Home | History | Annotate | Download | only in other
      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