From e191597e6bbf03e920e1b42f44ac65faaddedf51 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Thu, 7 Mar 2019 21:04:25 -0600 Subject: Add reset_env() and make su and login use it. Do long-delayed login cleanup. --- lib/lib.c | 35 +++++++++++++++++++++++++++++ lib/lib.h | 1 + toys/lsb/su.c | 65 +++++++++++++++++++++++++++++++----------------------- toys/other/login.c | 61 ++++++++++++-------------------------------------- 4 files changed, 87 insertions(+), 75 deletions(-) diff --git a/lib/lib.c b/lib/lib.c index 54bfc8c4..d1210f49 100644 --- a/lib/lib.c +++ b/lib/lib.c @@ -122,6 +122,7 @@ ssize_t readall(int fd, void *buf, size_t len) ssize_t writeall(int fd, void *buf, size_t len) { size_t count = 0; + while (countpw_dir)) { + perror_msg("chdir %s", p->pw_dir); + xchdir("/"); + } + } else { + char **ev1, **ev2; + + // remove LD_*, IFS, ENV, and BASH_ENV from environment + for (ev1 = ev2 = environ;;) { + while (*ev2 && (strstart(ev2, "LD_") || strstart(ev2, "IFS=") || + strstart(ev2, "ENV=") || strstart(ev2, "BASH_ENV="))) ev2++; + if (!(*ev1++ = *ev2++)) break; + } + } + + setenv("PATH", _PATH_DEFPATH, 1); + setenv("HOME", p->pw_dir, 1); + setenv("SHELL", p->pw_shell, 1); + setenv("USER", p->pw_name, 1); + setenv("LOGNAME", p->pw_name, 1); +} diff --git a/lib/lib.h b/lib/lib.h index bdabd4a2..87f4150e 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -259,6 +259,7 @@ void do_lines(int fd, char delim, void (*call)(char **pline, long len)); long environ_bytes(); long long millitime(void); char *format_iso_time(char *buf, size_t len, struct timespec *ts); +void reset_env(struct passwd *p, int clear); #define HR_SPACE 1 // Space between number and units #define HR_B 2 // Use "B" for single byte units diff --git a/toys/lsb/su.c b/toys/lsb/su.c index 0bffe782..c48a0c9f 100644 --- a/toys/lsb/su.c +++ b/toys/lsb/su.c @@ -4,22 +4,34 @@ * * See http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/su.html * TODO: log su attempts + * TODO: suid support + * Supports undocumented compatibility options: -m synonym for -p, - for -l -USE_SU(NEWTOY(su, "lmpc:s:", TOYFLAG_BIN|TOYFLAG_ROOTONLY)) +USE_SU(NEWTOY(su, "^lmpu:g:c:s:[!lmp]", TOYFLAG_BIN|TOYFLAG_ROOTONLY)) config SU bool "su" default y depends on TOYBOX_SHADOW help - usage: su [-lmp] [-c CMD] [-s SHELL] [USER [ARGS...]] + usage: su [-lp] [-u UID] [-g GID,...] [-s SHELL] [-c CMD] [USER [COMMAND...]] - Switch to user (or root) and run shell (with optional command line). + Switch user, prompting for password of new user when not run as root. - -s Shell to use - -c Command to pass to shell with -c - -l Login shell - -(m|p) Preserve environment + With one argument, switch to USER and run user's shell from /etc/passwd. + With no arguments, USER is root. If COMMAND line provided after USER, + exec() it as new USER (bypasing shell). If -u or -g specified, first + argument (if any) isn't USER (it's COMMAND). + + first argument is USER name to switch to (which must exist). + Non-root users are prompted for new user's password. + + -s Shell to use (default is user's shell from /etc/passwd) + -c Command line to pass to -s shell (ala sh -c "CMD") + -l Reset environment as if new login. + -u Switch to UID instead of USER + -g Switch to GID (only root allowed, can be comma separated list) + -p Preserve environment (except for $PATH and $IFS) */ #define FOR_su @@ -53,6 +65,8 @@ void su_main() if (*toys.optargs) name = *(toys.optargs++); else name = "root"; + loggit(name, 0); + if (!(shp = getspnam(name))) perror_exit("no '%s'", name); if (getuid()) { if (*shp->sp_pwdp != '$') goto deny; @@ -61,32 +75,26 @@ void su_main() memset(toybuf, 0, sizeof(toybuf)); if (!passhash || strcmp(passhash, shp->sp_pwdp)) goto deny; } + closelog(); - up = xgetpwnam(name); - xsetuser(up); + xsetuser(up = xgetpwnam(name)); + + if (FLAG(m)||FLAG(p)) { + unsetenv("IFS"); + setenv("PATH", _PATH_DEFPATHS, 1); + } else reset_env(up, FLAG(l)); argv = argu = xmalloc(sizeof(char *)*(toys.optc + 4)); *(argv++) = TT.s ? TT.s : up->pw_shell; + loggit(name, *argu); + + if (FLAG(m)||FLAG(p)) { + unsetenv("IFS"); + setenv("PATH", _PATH_DEFPATHS, 1); + } else reset_env(up, FLAG(l)); - if (toys.optflags & FLAG_l) { - int i; - char *stuff[] = {snapshot_env("TERM"), snapshot_env("DISPLAY"), - snapshot_env("COLORTERM"), snapshot_env("XAUTHORITY")}; - - clearenv(); - for (i=0; i < ARRAY_LEN(stuff); i++) if (stuff[i]) putenv(stuff[i]); - *(argv++) = "-l"; - xchdir(up->pw_dir); - } else unsetenv("IFS"); - setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1); - if (!(toys.optflags & (FLAG_m|FLAG_p))) { - setenv("HOME", up->pw_dir, 1); - setenv("SHELL", up->pw_shell, 1); - setenv("USER", up->pw_name, 1); - setenv("LOGNAME", up->pw_name, 1); - } else unsetenv("IFS"); - - if (toys.optflags & FLAG_c) { + if (FLAG(l)) *(argv++) = "-l"; + if (FLAG(c)) { *(argv++) = "-c"; *(argv++) = TT.c; } @@ -94,6 +102,7 @@ void su_main() xexec(argu); deny: + syslog(LOG_NOTICE, "No.", getusername(getuid()), name); puts("No."); toys.exitval = 1; } diff --git a/toys/other/login.c b/toys/other/login.c index 76e1f345..9bd6cc95 100644 --- a/toys/other/login.c +++ b/toys/other/login.c @@ -4,9 +4,6 @@ * * No support for PAM/securetty/selinux/login script/issue/utmp * Relies on libcrypt for hash calculation. - * - * TODO: this command predates "pending" but needs cleanup. It #defines - * random stuff, calls exit() form a signal handler... yeah. USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT)) @@ -36,24 +33,17 @@ GLOBALS( static void login_timeout_handler(int sig __attribute__((unused))) { printf("\nLogin timed out after %d seconds.\n", TT.login_timeout); - exit(0); + xexit(); } void login_main(void) { - char *forbid[] = { - "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD", - "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH", - "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL" - }; - int hh = FLAG(h), count, tty; - char uu[33], *username, *pass = 0, *ss; + int hh = FLAG(h), count, tty = tty_fd(); + char *username, *pass = 0, *ss; struct passwd *pwd = 0; - for (tty=0; tty<3; tty++) if (isatty(tty)) break; - if (tty == 3) error_exit("no tty"); - - for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]); + // we read user/password from stdin, but tty can be stderr? + if (tty == -1) error_exit("no tty"); openlog("login", LOG_PID | LOG_CONS, LOG_AUTH); xsignal(SIGALRM, login_timeout_handler); @@ -65,27 +55,23 @@ void login_main(void) tcflush(0, TCIFLUSH); if (!username) { - int i; - - memset(username = uu, 0, sizeof(uu)); - gethostname(uu, sizeof(uu)-1); - printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : ""); + if (gethostname(toybuf, sizeof(toybuf)-1)) *toybuf = 0; + printf("%s%slogin: ", *toybuf ? toybuf : "", *toybuf ? " " : ""); fflush(stdout); - if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1); + if(!fgets(toybuf, sizeof(toybuf)-1, stdin)) xexit(); // Remove trailing \n and so on - for (i = 0; ipw_passwd != '!' && *pwd->pw_passwd != '*') { - + if ((pwd = getpwnam(username))) { // Pre-authenticated or passwordless if (TT.f || !*pwd->pw_passwd) break; @@ -117,9 +103,6 @@ void login_main(void) } alarm(0); - // This had password data in it, and we reuse for motd below - memset(toybuf, 0, sizeof(toybuf)); - if (!pwd) error_exit("max retries (3)"); // Check twice because "this file exists" is a security test, and in @@ -136,26 +119,10 @@ void login_main(void) if (fchown(tty, pwd->pw_uid, pwd->pw_gid) || fchmod(tty, 0600)) printf("can't claim tty"); xsetuser(pwd); - - if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir); - - if (!FLAG(p)) { - char *term = getenv("TERM"); - - clearenv(); - if (term) setenv("TERM", term, 1); - } - - setenv("USER", pwd->pw_name, 1); - setenv("LOGNAME", pwd->pw_name, 1); - setenv("HOME", pwd->pw_dir, 1); - setenv("SHELL", pwd->pw_shell, 1); + reset_env(pwd, FLAG(p)); // Message of the day - if ((ss = readfile("/etc/motd", 0, 0))) { - puts(ss); - free(ss); - } + if ((ss = readfile("/etc/motd", 0, 0))) puts(ss); syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name, ttyname(tty), hh ? "from" : "", hh ? TT.h : ""); -- cgit v1.2.3