commit 2cbf96e394f7a7decbae2f8a6829b8cddd03536b from: Chris Cappuccio via: GitHub date: Wed May 31 15:44:25 2023 UTC Merge pull request #136 from stspdotname/enable-root make the 'enable' command switch to the root user 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; }