diff options
Diffstat (limited to 'toys')
-rw-r--r-- | toys/other/login.c | 246 |
1 files changed, 94 insertions, 152 deletions
diff --git a/toys/other/login.c b/toys/other/login.c index b728286b..c44a8879 100644 --- a/toys/other/login.c +++ b/toys/other/login.c @@ -8,30 +8,28 @@ * TODO: this command predates "pending" but needs cleanup. It #defines * random stuff, calls exit() form a signal handler... yeah. -USE_LOGIN(NEWTOY(login, ">1fph:", TOYFLAG_BIN)) +USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT)) config LOGIN bool "login" default y depends on TOYBOX_SHADOW help - usage: login [-p] [-h host] [[-f] username] + usage: login [-p] [-h host] [-f USERNAME] [USERNAME] - Establish a new session with the system. + Log in as a user, prompting for username and password if necessary. -p Preserve environment -h The name of the remote host for this login - -f Do not perform authentication + -f login as USERNAME without authentication */ #define FOR_login #include "toys.h" -#define USER_NAME_MAX_SIZE 32 -#define HOSTNAME_SIZE 32 - GLOBALS( char *hostname; + char *username; int login_timeout, login_fail_timeout; ) @@ -42,184 +40,128 @@ static void login_timeout_handler(int sig __attribute__((unused))) exit(0); } -static 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", NULL -}; - -int verify_password(char * pwd) -{ - char *pass; - - if (read_password(toybuf, sizeof(toybuf), "Password: ")) return 1; - if (!pwd) return 1; - if (pwd[0] == '!' || pwd[0] == '*') return 1; - - pass = crypt(toybuf, pwd); - if (pass && !strcmp(pass, pwd)) return 0; - - return 1; -} - -void read_user(char * buff, int size) -{ - char hostname[HOSTNAME_SIZE+1]; - int i = 0; - - hostname[HOSTNAME_SIZE] = 0; - if (!gethostname(hostname, HOSTNAME_SIZE)) fputs(hostname, stdout); - - fputs(" login: ", stdout); - fflush(stdout); - - do { - int c = getchar(); - if (c == EOF) exit(EXIT_FAILURE); - *buff = c; - } while (isblank(*buff)); - - if (*buff != '\n') if(!fgets(&buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1); - - while(i<HOSTNAME_SIZE-1 && isgraph(buff[i])) i++; - buff[i] = 0; -} - -void handle_nologin(void) -{ - int fd = open("/etc/nologin", O_RDONLY); - int size; - - if (fd == -1) return; - - size = readall(fd, toybuf,sizeof(toybuf)-1); - toybuf[size] = 0; - if (!size) puts("System closed for routine maintenance\n"); - else puts(toybuf); - - close(fd); - fflush(stdout); - exit(1); -} - -void handle_motd(void) -{ - int fd = open("/etc/motd", O_RDONLY); - int size; - if (fd == -1) return; - - size = readall(fd, toybuf,sizeof(toybuf)-1); - toybuf[size] = 0; - puts(toybuf); - - close(fd); - fflush(stdout); -} - -void spawn_shell(const char *shell) -{ - const char * exec_name = strrchr(shell,'/'); - if (exec_name) exec_name++; - else exec_name = shell; - - snprintf(toybuf,sizeof(toybuf)-1, "-%s", shell); - execl(shell, toybuf, NULL); - error_exit("Failed to spawn shell"); -} - -void setup_environment(const struct passwd *pwd, int clear_env) -{ - if (chdir(pwd->pw_dir)) printf("bad home dir: %s\n", pwd->pw_dir); - - if (clear_env) { - const 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); -} - void login_main(void) { - int f_flag = toys.optflags & FLAG_f; - int h_flag = toys.optflags & FLAG_h; - char username[33], *pass = NULL, **ss; - struct passwd * pwd = NULL; - struct spwd * spwd = NULL; - int auth_fail_cnt = 0; - - if (f_flag && toys.optc != 1) error_exit("-f requires username"); + 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 = toys.optflags&FLAG_h, count, tty; + char uu[33], *username, *pass = 0, *ss; + struct passwd *pwd = 0; - if (geteuid()) error_exit("not root"); + for (tty=0; tty<3; tty++) if (isatty(tty)) break; + if (tty == 3) error_exit("no tty"); - if (!isatty(0) || !isatty(1) || !isatty(2)) error_exit("no tty"); + for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]); openlog("login", LOG_PID | LOG_CONS, LOG_AUTH); xsignal(SIGALRM, login_timeout_handler); - alarm(TT.login_timeout = 60); - for (ss = forbid; *ss; ss++) unsetenv(*ss); - - while (1) { + if (TT.username) username = TT.username; + else username = *toys.optargs; + for (count = 0; count < 3; count++) { + alarm(TT.login_timeout = 60); tcflush(0, TCIFLUSH); - username[sizeof(username)-1] = 0; - if (*toys.optargs) xstrncpy(username, *toys.optargs, sizeof(username)); - else { - read_user(username, sizeof(username)); - if (!*username) continue; + if (!username) { + int i; + + memset(username = uu, 0, sizeof(uu)); + gethostname(uu, sizeof(uu)-1); + printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : ""); + fflush(stdout); + + if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1); + + // Remove trailing \n and so on + for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0; + if (!*uu) { + username = 0; + continue; + } } + // If user exists and isn't locked pwd = getpwnam(username); - if (!pwd) goto query_pass; // Non-existing user + if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') { - if (pwd->pw_passwd[0] == '!' || pwd->pw_passwd[0] == '*') - goto query_pass; // Locked account + // Pre-authenticated or passwordless + if (TT.username || !*pwd->pw_passwd) break; - if (f_flag) break; // Pre-authenticated + // fetch shadow password if necessary + if (*(pass = pwd->pw_passwd) == 'x') { + struct spwd *spwd = getspnam (username); - if (!pwd->pw_passwd[0]) break; // Password-less account + if (spwd) pass = spwd->sp_pwdp; + } + } else if (TT.username) error_exit("bad -f '%s'", TT.username); - pass = pwd->pw_passwd; - if (pwd->pw_passwd[0] == 'x') { - spwd = getspnam (username); - if (spwd) pass = spwd->sp_pwdp; - } + // Verify password. (Prompt for password _before_ checking disable state.) + if (!read_password(toybuf, sizeof(toybuf), "Password: ")) { + int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss); -query_pass: - if (!verify_password(pass)) break; + // password go bye-bye now. + memset(toybuf, 0, sizeof(toybuf)); + if (x) break; + } - f_flag = 0; - syslog(LOG_WARNING, "invalid password for '%s' on %s %s %s", username, - ttyname(0), h_flag?"from":"", h_flag?TT.hostname:""); + syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name, + ttyname(tty), hh ? "from " : "", hh ? TT.hostname : ""); sleep(3); puts("Login incorrect"); - if (++auth_fail_cnt == 3) - error_exit("Maximum number of tries exceeded (3)\n"); - - *username = 0; + username = 0; pwd = 0; - spwd = 0; } 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)"); - if (pwd->pw_uid) handle_nologin(); + // Check twice because "this file exists" is a security test, and in + // theory filehandle exhaustion or other error could make open/read fail. + if (pwd->pw_uid && !access("/etc/nologin", R_OK)) { + ss = readfile("/etc/nologin", toybuf, sizeof(toybuf)); + puts ((ss && *ss) ? ss : "nologin"); + free(ss); + toys.exitval = 1; + + return; + } xsetuser(pwd); - setup_environment(pwd, !(toys.optflags & FLAG_p)); + if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir); + + if (!(toys.optflags&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); - handle_motd(); + // Message of the day + if ((ss = readfile("/etc/motd", 0, 0))) { + puts(ss); + free(ss); + } syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name, - ttyname(0), h_flag?"from":"", h_flag?TT.hostname:""); + ttyname(tty), hh ? "from" : "", hh ? TT.hostname : ""); - spawn_shell(pwd->pw_shell); + // can't xexec here because name doesn't match argv[0] + snprintf(toybuf, sizeof(toybuf)-1, "-%s", ss = basename_r(pwd->pw_shell)); + toy_exec((char *[]){toybuf, 0}); + execl(ss, toybuf, NULL); + error_exit("Failed to spawn shell"); } |