1/* login.c - Start a session on the system. 2 * 3 * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com> 4 * 5 * No support for PAM/securetty/selinux/login script/issue/utmp 6 * Relies on libcrypt for hash calculation. 7 8USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT)) 9 10config LOGIN 11 bool "login" 12 default y 13 depends on TOYBOX_SHADOW 14 help 15 usage: login [-p] [-h host] [-f USERNAME] [USERNAME] 16 17 Log in as a user, prompting for username and password if necessary. 18 19 -p Preserve environment 20 -h The name of the remote host for this login 21 -f login as USERNAME without authentication 22*/ 23 24#define FOR_login 25#include "toys.h" 26 27GLOBALS( 28 char *h, *f; 29 30 int login_timeout, login_fail_timeout; 31) 32 33static void login_timeout_handler(int sig __attribute__((unused))) 34{ 35 printf("\nLogin timed out after %d seconds.\n", TT.login_timeout); 36 xexit(); 37} 38 39void login_main(void) 40{ 41 int hh = FLAG(h), count, tty = tty_fd(); 42 char *username, *pass = 0, *ss; 43 struct passwd *pwd = 0; 44 45 // we read user/password from stdin, but tty can be stderr? 46 if (tty == -1) error_exit("no tty"); 47 48 openlog("login", LOG_PID | LOG_CONS, LOG_AUTH); 49 xsignal(SIGALRM, login_timeout_handler); 50 51 if (TT.f) username = TT.f; 52 else username = *toys.optargs; 53 for (count = 0; count < 3; count++) { 54 alarm(TT.login_timeout = 60); 55 tcflush(0, TCIFLUSH); 56 57 if (!username) { 58 if (gethostname(toybuf, sizeof(toybuf)-1)) *toybuf = 0; 59 printf("%s%slogin: ", *toybuf ? toybuf : "", *toybuf ? " " : ""); 60 fflush(stdout); 61 62 if(!fgets(toybuf, sizeof(toybuf)-1, stdin)) xexit(); 63 64 // Remove trailing \n and so on 65 for (ss = toybuf; *ss; ss++) if (*ss<=' ' || *ss==':') break; 66 *ss = 0; 67 if (!*(username = toybuf)) { 68 username = 0; 69 continue; 70 } 71 } 72 73 // If user exists and isn't locked 74 if ((pwd = getpwnam(username))) { 75 // Pre-authenticated or passwordless 76 if (TT.f || !*pwd->pw_passwd) break; 77 78 // fetch shadow password if necessary 79 if (*(pass = pwd->pw_passwd) == 'x') { 80 struct spwd *spwd = getspnam (username); 81 82 if (spwd) pass = spwd->sp_pwdp; 83 } 84 } else if (TT.f) error_exit("bad -f '%s'", TT.f); 85 86 // Verify password. (Prompt for password _before_ checking disable state.) 87 if (!read_password(toybuf, sizeof(toybuf), "Password: ")) { 88 int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss); 89 90 // password go bye-bye now. 91 memset(toybuf, 0, sizeof(toybuf)); 92 if (x) break; 93 } 94 95 syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name, 96 ttyname(tty), hh ? "from " : "", hh ? TT.h : ""); 97 98 sleep(3); 99 puts("Login incorrect"); 100 101 username = 0; 102 pwd = 0; 103 } 104 105 alarm(0); 106 if (!pwd) error_exit("max retries (3)"); 107 108 // Check twice because "this file exists" is a security test, and in 109 // theory filehandle exhaustion or other error could make open/read fail. 110 if (pwd->pw_uid && !access("/etc/nologin", R_OK)) { 111 ss = readfile("/etc/nologin", toybuf, sizeof(toybuf)); 112 puts ((ss && *ss) ? ss : "nologin"); 113 free(ss); 114 toys.exitval = 1; 115 116 return; 117 } 118 119 if (fchown(tty, pwd->pw_uid, pwd->pw_gid) || fchmod(tty, 0600)) 120 printf("can't claim tty"); 121 xsetuser(pwd); 122 reset_env(pwd, !FLAG(p)); 123 124 // Message of the day 125 if ((ss = readfile("/etc/motd", 0, 0))) puts(ss); 126 127 syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name, 128 ttyname(tty), hh ? "from" : "", hh ? TT.h : ""); 129 130 // not using xexec(), login calls absolute path from filesystem so must exec() 131 execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0); 132 perror_exit("exec shell '%s'", pwd->pw_shell); 133} 134