Commit Diff


commit - 25e1b3c7a65c61732e655a97f1596a52f28a3424
commit + 2cbf96e394f7a7decbae2f8a6829b8cddd03536b
blob - bf26e0f35483d84fb4e011938121109ca0717c72
blob + cb93dae29f883f57ee923b60a332edcaf8749fa6
--- Makefile
+++ Makefile
@@ -9,6 +9,8 @@ PROG= nsh
 DEBUG?=-O0 -g
 .endif
 
+NSH_REXEC_PATH?=/usr/local/bin/nsh
+
 .if make(install)
 DESTDIR?=/usr/local
 BINDIR?=/bin
@@ -19,7 +21,7 @@ MANDIR?=/man/man
 #CFLAGS=-O -DDHCPLEASES=\"/flash/dhcpd.leases\" -Wmissing-prototypes -Wformat -Wall -Wpointer-arith -Wbad-function-cast #-W
 CFLAGS?=-O
 CFLAGS+=-Wmissing-prototypes -Wformat -Wall -Wbad-function-cast -I/usr/local/include #-W -Wpointer-arith
-CPPFLAGS+=-DNSH_VERSION=${NSH_VERSION}
+CPPFLAGS+=-DNSH_VERSION=${NSH_VERSION}  -DNSH_REXEC_PATH=${NSH_REXEC_PATH}
 
 SRCS=arp.c compile.c main.c genget.c commands.c stats.c kroute.c
 SRCS+=ctl.c show.c if.c version.c route.c conf.c complete.c ieee80211.c
blob - 28c7350a7c3e5500d0fad8a789c320416b51c08e
blob + eebb73667f75dda726d8e045bee3e25554254690
--- commands.c
+++ commands.c
@@ -86,7 +86,6 @@ size_t	cursor_argo;			/* offset of cursor margv[cursor
 
 pid_t	child;
 
-static int	quit(void);
 static int	disable(void);
 static int	doverbose(int, char**);
 static int	doediting(int, char**);
@@ -173,8 +172,12 @@ void sigalarm(int blahfart)
 int
 quit(void)
 {
-	printf("%% Session terminated.\n");
-	exit(0);
+	if (privexec) {
+		exit(NSH_REXEC_EXIT_CODE_QUIT);
+	} else {
+		printf("%% Session terminated.\n");
+		exit(0);
+	}
 	return 0;
 }
 
@@ -2606,6 +2609,10 @@ cmdargs_output(char *cmd, char *arg[], int stdoutfd, i
 int
 disable(void)
 {
+	if (privexec) {
+		exit(0);
+		return 0;
+	}
 	priv = 0;
 	config_mode = 0;
 	return 0;
blob - f87dcc248a9328234f8eb72469036d383f529e0f
blob + 0be9d4e399ef96e55a54cac6c8dd39ed4b847d08
--- externs.h
+++ externs.h
@@ -6,11 +6,14 @@
 #error "NSH_VERSION is undefined"
 #endif
 
-#define NSH_STRINGIFY_VERSION(x) #x
-#define NSH_STRINGVAL_VERSION(x) NSH_STRINGIFY_VERSION(x)
+#define NSH_STRINGIFY(x) #x
+#define NSH_STRINGVAL(x) NSH_STRINGIFY(x)
 
-#define NSH_VERSION_STR NSH_STRINGVAL_VERSION(NSH_VERSION)
+#define NSH_VERSION_STR NSH_STRINGVAL(NSH_VERSION)
 
+#define NSH_REXEC_PATH_STR		NSH_STRINGVAL(NSH_REXEC_PATH)
+#define NSH_REXEC_EXIT_CODE_QUIT	42
+
 #define NO_ARG(x)	(strcasecmp(x, "no") == 0) /* absolute "no" */
 
 #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0])) /* sys/param.h */
@@ -31,6 +34,7 @@ extern int editing;		/* is command line editing mode o
 extern int config_mode;		/* are we in comfig mode? */
 extern int bridge;		/* are we in bridge mode (or interface mode?) */
 extern int priv;		/* privileged mode or not? */
+extern int privexec;		/* process was started in privileged mode? */
 extern pid_t pid;		/* process id of nsh */
 extern int cli_rtable;		/* environment rtable */
 
@@ -158,10 +162,13 @@ extern char metricnames[];
 #define DIFF		"/usr/bin/diff"
 #define REBOOT		"/sbin/reboot"
 #define HALT		"/sbin/halt"
+#define SU		"/usr/bin/su"
+#define DOAS		"/usr/bin/doas"
 #define SAVESCRIPT	"/usr/local/bin/save.sh"
 #ifndef DHCPLEASES
 #define DHCPLEASES	"/var/db/dhcpd.leases"
 #endif
+int quit(void);
 void command(void);
 char **step_optreq(char **, char **, int, char **, int);
 int argvtostring(int, char **, char *, int);
blob - 6b3aefcbe9ed0aca92fbaa37f049f21d07a5a939
blob + 9ba61d37637b0bb62d90fcd2821475b4bcd990ab
--- main.c
+++ main.c
@@ -39,7 +39,7 @@ jmp_buf toplevel;
 char *vers = NSH_VERSION_STR;
 int bridge = 0;		/* bridge mode for interface() */
 int verbose = 0;	/* verbose mode */
-int priv = 0, cli_rtable = 0;
+int priv = 0, privexec = 0, cli_rtable = 0;
 int editing = 1, config_mode = 0;;
 pid_t pid;
 
@@ -61,17 +61,22 @@ main(int argc, char *argv[])
 
 	setlocale(LC_CTYPE, "");
 
-	if(getuid() != 0)
-		printf("%% Functionality limited without root privilege.\n");
-
 	pid = getpid();
 
-	while ((ch = getopt(argc, argv, "c:i:v")) != -1)
+	while ((ch = getopt(argc, argv, "c:ei:v")) != -1)
 		switch (ch) {
 		case 'c':
 			cflag = 1;
 			strlcpy(rc, optarg, PATH_MAX);
 			break;
+		case 'e':
+			if (getuid() != 0) {
+				fprintf(stderr, "%s: Use of -e option requires "
+				    "root privileges.\n", getprogname());
+				exit(1);
+			}
+			privexec = 1;
+			break;
 		case 'i':
 			iflag = 1;
 			strlcpy(rc, optarg, PATH_MAX);
@@ -83,6 +88,12 @@ main(int argc, char *argv[])
 			usage();
 		}
 
+
+	if (getuid() != 0) {
+		printf("%% Functionality is limited without root privileges.\n"
+		    "%% The 'enable' command will switch to the root user.\n");
+	}
+
 	argc -= optind;
 	argv += optind;
 	if (cflag && iflag)
@@ -92,7 +103,8 @@ main(int argc, char *argv[])
 	if (iflag)
 		rmtemp(SQ3DBFILE);
 
-	printf("%% NSH v%s\n", vers);
+	if (!privexec)
+		printf("%% NSH v%s\n", vers);
 
 	/* create temporal tables (if they aren't already there) */
 	if (db_create_table_rtables() < 0)
@@ -146,6 +158,13 @@ main(int argc, char *argv[])
 
 		exit(0);
 	}
+	if (privexec) {
+		/*
+		 * We start out in privileged mode.
+		 * We are already running as root as per -e option handling.
+		 */
+		priv = 1;
+	}
 
 	top = setjmp(toplevel) == 0;
 	if (top) {
blob - 54ea3c3deb08da424263112ddae92e7895e9f1ee
blob + af71bd01fd67ae20381615194fddba516473b430
--- passwd.c
+++ passwd.c
@@ -161,8 +161,8 @@ secretusage(void)
 /*
  * enable privileged mode
  */
-int
-enable(int argc, char **argv)
+static int
+enable_passwd(int argc, char **argv)
 {
 	char *p, *cpass;
 	char salt[_PASSWORD_LEN];
@@ -171,14 +171,10 @@ enable(int argc, char **argv)
 	switch (argc) {
 
 	case 1:
-		if (priv == 1)
-			return 0;
-
 		/* try to read pass */
 		if (!(read_pass(pass, sizeof(pass)))) {
 			if (errno == ENOENT) {
 				/* no password file, so enable */
-				priv = 1;
 				return 1;
 			} else {
 				/* cant read password file */
@@ -187,15 +183,14 @@ enable(int argc, char **argv)
 				return 0;
 			}
 		}
-		p = getpass("Password:");
+		p = getpass("Privileged Mode Secret:");
 		if (p == NULL || *p == '\0')
 			return 0;
 
 		if (strcmp(crypt(p, pass), pass) == 0) {
-			priv = 1;
 			return 1;
 		} else {
-			printf("%% Password incorrect\n");
+			printf("%% Secret incorrect\n");
 			return 0;
 		}
 
@@ -205,11 +200,11 @@ enable(int argc, char **argv)
 			printf("%% enable\t\t\t\tEnable privileged mode\n");
 			printf("%% enable ?\t\t\t\tPrint help information\n");
 			secretusage();
-			return 1;
+			return 0;
 		} else {
 			if (isprefix(argv[1], "secret")) {
 				secretusage();
-				return 1;
+				return 0;
 			}
 			printf("%% Invalid argument: %s\n", argv[1]);
 			return 0;
@@ -231,11 +226,11 @@ enable(int argc, char **argv)
 		}
 
 		if (strlen(argv[2]) < 8) {
-			printf("%% Password too short; at least 8 characters required\n");
+			printf("%% Secret too short; at least 8 characters required\n");
 			return 0;
 		}
 		if (strlen(argv[2]) > _PASSWORD_LEN) {
-			printf("%% Password too long; at most %d characters allowed\n",
+			printf("%% Secret too long; at most %d characters allowed\n",
 			    _PASSWORD_LEN);
 			return 0;
 		}
@@ -247,7 +242,8 @@ enable(int argc, char **argv)
 			printf("%% crypt failed\n");
 			return 0;
 		}
-		return(write_pass(cpass));
+		write_pass(cpass);
+		return 0;
 
 	case 4:
 		if (!isprefix(argv[1], "secret")) {
@@ -274,11 +270,73 @@ enable(int argc, char **argv)
 
 		/* set crypted pass */
 		strlcpy(pass, argv[3], sizeof(pass));
-		return (write_pass(pass));
+		write_pass(pass);
+		return 0;
 
 	default:
 		printf("%% Too many arguments\n");
+		return 0;
+	}
+
+	return 0;
+}
+
+int
+enable(int argc, char **argv)
+{
+	char *doas_argv[] = {
+		DOAS, NSH_REXEC_PATH_STR, "-e", NULL
+	};
+	char *su_argv[] = {
+		SU, "root", "-c", NSH_REXEC_PATH_STR " -e", NULL
+		
+	};
+	int exit_code;
+
+	if (argc != 1)
+		return enable_passwd(argc, argv);
+
+	if (priv == 1 || !enable_passwd(argc, argv))
 		return 0;
+	
+	if (getuid() == 0) {
+		priv = 1;
+		return 0;
 	}
 
+	/*
+	 * Start an nsh child process in privileged mode.
+	 * The 'priv' flag will remain at zero in our own process.
+	 */
+	printf("%% Obtaining root privileges via %s\n", DOAS);
+	exit_code = cmdargs(doas_argv[0], doas_argv);
+	if (exit_code == 0)
+		return 0;
+	else if (exit_code == NSH_REXEC_EXIT_CODE_QUIT) {
+		/* The child exited due to a 'quit' command. */
+		quit();
+	}
+
+	/*
+	 * XXX We cannot differentiate a doas exit code of 1 from an
+	 * nsh exit code of 1. Under normal circumstances nsh will exit
+	 * with code zero. Just assume that doas failed to run the
+	 * command if we get here and retry with su.
+	 */
+
+	printf("%% Obtaining root privileges via %s\n", SU);
+	exit_code = cmdargs(su_argv[0], su_argv);
+
+	if (exit_code == -1 || exit_code == 127) {
+		printf("%% Entering privileged mode failed: "
+		    "Could not re-execute nsh\n");
+	} else if (exit_code == NSH_REXEC_EXIT_CODE_QUIT) {
+		/* The child exited due to a 'quit' command. */
+		quit();
+	} else if (exit_code) {
+		printf("%% Privileged mode child process exited "
+		    "with error code %d\n", exit_code);
+	}
+
+	return 0;
 }