1 /* login.c - Start a session on the system. 2 * 3 * Copyright 2012 Elie De Brauwer <eliedebrauwer (at) gmail.com> 4 * 5 * No support for PAM/securetty/selinux/login script/issue/utmp 6 * Relies on libcrypt for hash calculation. 7 * 8 * TODO: this command predates "pending" but needs cleanup. It #defines 9 * random stuff, calls exit() form a signal handler... yeah. 10 11 USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT)) 12 13 config LOGIN 14 bool "login" 15 default y 16 depends on TOYBOX_SHADOW 17 help 18 usage: login [-p] [-h host] [-f USERNAME] [USERNAME] 19 20 Log in as a user, prompting for username and password if necessary. 21 22 -p Preserve environment 23 -h The name of the remote host for this login 24 -f login as USERNAME without authentication 25 */ 26 27 #define FOR_login 28 #include "toys.h" 29 30 GLOBALS( 31 char *hostname; 32 char *username; 33 34 int login_timeout, login_fail_timeout; 35 ) 36 37 static void login_timeout_handler(int sig __attribute__((unused))) 38 { 39 printf("\nLogin timed out after %d seconds.\n", TT.login_timeout); 40 exit(0); 41 } 42 43 void login_main(void) 44 { 45 char *forbid[] = { 46 "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD", 47 "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH", 48 "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL" 49 }; 50 int hh = toys.optflags&FLAG_h, count, tty; 51 char uu[33], *username, *pass = 0, *ss; 52 struct passwd *pwd = 0; 53 54 for (tty=0; tty<3; tty++) if (isatty(tty)) break; 55 if (tty == 3) error_exit("no tty"); 56 57 for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]); 58 59 openlog("login", LOG_PID | LOG_CONS, LOG_AUTH); 60 xsignal(SIGALRM, login_timeout_handler); 61 62 if (TT.username) username = TT.username; 63 else username = *toys.optargs; 64 for (count = 0; count < 3; count++) { 65 alarm(TT.login_timeout = 60); 66 tcflush(0, TCIFLUSH); 67 68 if (!username) { 69 int i; 70 71 memset(username = uu, 0, sizeof(uu)); 72 gethostname(uu, sizeof(uu)-1); 73 printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : ""); 74 fflush(stdout); 75 76 if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1); 77 78 // Remove trailing \n and so on 79 for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0; 80 if (!*uu) { 81 username = 0; 82 continue; 83 } 84 } 85 86 // If user exists and isn't locked 87 pwd = getpwnam(username); 88 if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') { 89 90 // Pre-authenticated or passwordless 91 if (TT.username || !*pwd->pw_passwd) break; 92 93 // fetch shadow password if necessary 94 if (*(pass = pwd->pw_passwd) == 'x') { 95 struct spwd *spwd = getspnam (username); 96 97 if (spwd) pass = spwd->sp_pwdp; 98 } 99 } else if (TT.username) error_exit("bad -f '%s'", TT.username); 100 101 // Verify password. (Prompt for password _before_ checking disable state.) 102 if (!read_password(toybuf, sizeof(toybuf), "Password: ")) { 103 int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss); 104 105 // password go bye-bye now. 106 memset(toybuf, 0, sizeof(toybuf)); 107 if (x) break; 108 } 109 110 syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name, 111 ttyname(tty), hh ? "from " : "", hh ? TT.hostname : ""); 112 113 sleep(3); 114 puts("Login incorrect"); 115 116 username = 0; 117 pwd = 0; 118 } 119 120 alarm(0); 121 // This had password data in it, and we reuse for motd below 122 memset(toybuf, 0, sizeof(toybuf)); 123 124 if (!pwd) error_exit("max retries (3)"); 125 126 // Check twice because "this file exists" is a security test, and in 127 // theory filehandle exhaustion or other error could make open/read fail. 128 if (pwd->pw_uid && !access("/etc/nologin", R_OK)) { 129 ss = readfile("/etc/nologin", toybuf, sizeof(toybuf)); 130 puts ((ss && *ss) ? ss : "nologin"); 131 free(ss); 132 toys.exitval = 1; 133 134 return; 135 } 136 137 xsetuser(pwd); 138 139 if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir); 140 141 if (!(toys.optflags&FLAG_p)) { 142 char *term = getenv("TERM"); 143 144 clearenv(); 145 if (term) setenv("TERM", term, 1); 146 } 147 148 setenv("USER", pwd->pw_name, 1); 149 setenv("LOGNAME", pwd->pw_name, 1); 150 setenv("HOME", pwd->pw_dir, 1); 151 setenv("SHELL", pwd->pw_shell, 1); 152 153 // Message of the day 154 if ((ss = readfile("/etc/motd", 0, 0))) { 155 puts(ss); 156 free(ss); 157 } 158 159 syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name, 160 ttyname(tty), hh ? "from" : "", hh ? TT.hostname : ""); 161 162 // not using xexec(), login calls absolute path from filesystem so must exec() 163 execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0); 164 perror_exit("exec shell '%s'", pwd->pw_shell); 165 } 166