mrrl-logincaps

MRRL version of logincaps
git clone https://ccx.te2000.cz/git/mrrl-logincaps
Log | Files | Refs

login.c (7024B)


      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  *
      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 #include <assert.h>
     28 
     29 #define FOR_login
     30 #include "toys.h"
     31 
     32 #ifndef LOGINEXEC_PATH
     33 #define LOGINEXEC_PATH "/etc/loginexec"
     34 #endif
     35 
     36 GLOBALS(
     37   char *hostname;
     38   char *username;
     39 
     40   int login_timeout, login_fail_timeout;
     41 )
     42 
     43 static void login_timeout_handler(int sig __attribute__((unused)))
     44 {
     45   printf("\nLogin timed out after %d seconds.\n", TT.login_timeout);
     46   exit(0);
     47 }
     48 
     49 unsigned char login_hashcmp(const char *s1, const char *s2)
     50 {
     51   // constant time string comparison
     52   unsigned char result = 0;
     53   do {
     54     result |= (*s1) ^ (*s2);
     55   } while(*(s1++) && *(s2++));
     56   return result;
     57 }
     58 
     59 void login_main(void)
     60 {
     61   char *forbid[] = {
     62     "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
     63     "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
     64     "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL"
     65   };
     66   int hh = toys.optflags&FLAG_h, count, tty;
     67   int pipefd[2] = {-1, -1};
     68   int child_pid;
     69   char uu[33], *username, *pass = 0, *ss;
     70   struct passwd *pwd = 0;
     71   char *loginexec = 0;
     72   unsigned char use_loginexec = 0;
     73 
     74   for (tty=0; tty<3; tty++) if (isatty(tty)) break;
     75   if (tty == 3) error_exit("no tty");
     76 
     77   for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]);
     78 
     79   openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
     80   xsignal(SIGALRM, login_timeout_handler);
     81 
     82   if (TT.username) username = TT.username;
     83   else username = *toys.optargs;
     84   for (count = 0; count < 3; count++) {
     85     alarm(TT.login_timeout = 60);
     86     tcflush(0, TCIFLUSH);
     87 
     88     if (!username) {
     89       int i;
     90 
     91       memset(username = uu, 0, sizeof(uu));
     92       gethostname(uu, sizeof(uu)-1);
     93       printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : "");
     94       fflush(stdout);
     95 
     96       if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1);
     97 
     98       // Remove trailing \n and so on
     99       for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0;
    100       if (!*uu) {
    101         username = 0;
    102         continue;
    103       }
    104     }
    105 
    106     // If user exists and isn't locked
    107     pwd = getpwnam(username);
    108     if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') {
    109 
    110       // Pre-authenticated or passwordless
    111       if (TT.username || !*pwd->pw_passwd) break;
    112 
    113       // fetch shadow password if necessary
    114       if (*(pass = pwd->pw_passwd) == 'x') {
    115         struct spwd *spwd = getspnam (username);
    116 
    117         if (spwd) pass = spwd->sp_pwdp;
    118       }
    119 
    120       loginexec = xmprintf("%s/loginexec", pwd->pw_dir);
    121       use_loginexec = !access(loginexec, X_OK);
    122 
    123     } else if (TT.username) error_exit("bad -f '%s'", TT.username);
    124 
    125 
    126     // Verify password. (Prompt for password _before_ checking disable state.)
    127     if (!read_password(toybuf, sizeof(toybuf), "Password: ")) {
    128       int x = pass && (ss = crypt(toybuf, pass)) && !login_hashcmp(pass, ss);
    129 
    130       if (x && use_loginexec) {
    131         if(!pipe(pipefd)) {
    132           if((child_pid = fork())) {
    133             if(child_pid == -1) {
    134               /* error */
    135               perror("fork");
    136               close(pipefd[0]);
    137               close(pipefd[1]);
    138               pipefd[0] = pipefd[1] = -1;
    139             } else {
    140               /* parent */
    141               close(pipefd[1]);
    142             }
    143           } else {
    144             /* child */
    145             dup2(pipefd[1], 1);
    146             xprintf("%s\n", toybuf);
    147             exit(0);
    148           }
    149         } else {
    150           /* creating pipe failed */
    151           perror("pipe");
    152           pipefd[0] = pipefd[1] = -1;
    153         }
    154         if (!access(LOGINEXEC_PATH, X_OK)) {
    155           if (!(toys.optflags&FLAG_p)) {
    156             char *term = getenv("TERM");
    157 
    158             clearenv();
    159             if (term) setenv("TERM", term, 1);
    160           }
    161 
    162           setenv("USER", pwd->pw_name, 1);
    163           setenv("LOGNAME", pwd->pw_name, 1);
    164           setenv("HOME", pwd->pw_dir, 1);
    165           setenv("SHELL", pwd->pw_shell, 1);
    166           if(pipefd[0] >= 0) {
    167             setenv("LOGINPASS_FD", xmprintf("%d", pipefd[0]), 1);
    168           }
    169           /* TODO */
    170           /* set UID, GID and GIDLIST env variables */
    171 
    172           alarm(0); // remove pending SIGALRM for stale login
    173 
    174           execl(LOGINEXEC_PATH, LOGINEXEC_PATH, loginexec, (char *)0);
    175         } else {
    176           syslog(LOG_WARNING, "login executable '%s' unavailable when attempting to log in as '%s' on %s %s%s",
    177             LOGINEXEC_PATH,
    178             pwd ? pwd->pw_name : "UNKNOWN",
    179             ttyname(tty),
    180             hh ? "from " : "", hh ? TT.hostname : "");
    181 
    182           puts("global loginexec unavailable, using user shell");
    183           use_loginexec = 0;
    184         }
    185       }
    186       // password go bye-bye now.
    187       memset(toybuf, 0, sizeof(toybuf));
    188       if (x) break;
    189     }
    190 
    191     syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", 
    192       pwd ? pwd->pw_name : "UNKNOWN",
    193       ttyname(tty), hh ? "from " : "", hh ? TT.hostname : "");
    194 
    195     sleep(3);
    196     puts("Login incorrect");
    197 
    198     username = 0;
    199     pwd = 0;
    200   }
    201 
    202   alarm(0);
    203   // This had password data in it, and we reuse for motd below
    204   memset(toybuf, 0, sizeof(toybuf));
    205 
    206   if (!pwd) error_exit("max retries (3)");
    207 
    208   // Check twice because "this file exists" is a security test, and in
    209   // theory filehandle exhaustion or other error could make open/read fail.
    210   if (pwd->pw_uid && !access("/etc/nologin", R_OK)) {
    211     ss = readfile("/etc/nologin", toybuf, sizeof(toybuf));
    212     puts ((ss && *ss) ? ss : "nologin");
    213     free(ss);
    214     toys.exitval = 1;
    215 
    216     return;
    217   }
    218 
    219   xsetuser(pwd);
    220 
    221   if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir);
    222 
    223   if (!(toys.optflags&FLAG_p)) {
    224     char *term = getenv("TERM");
    225 
    226     clearenv();
    227     if (term) setenv("TERM", term, 1);
    228   }
    229 
    230   setenv("USER", pwd->pw_name, 1);
    231   setenv("LOGNAME", pwd->pw_name, 1);
    232   setenv("HOME", pwd->pw_dir, 1);
    233   setenv("SHELL", pwd->pw_shell, 1);
    234   if(pipefd[0] >= 0) {
    235     setenv("LOGINPASS_FD", xmprintf("%d", pipefd[0]), 1);
    236   }
    237 
    238   // Message of the day
    239   if ((ss = readfile("/etc/motd", 0, 0))) {
    240     puts(ss);
    241     free(ss);
    242   }
    243 
    244   syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
    245     ttyname(tty), hh ? "from" : "", hh ? TT.hostname : "");
    246 
    247   assert(!use_loginexec);
    248   // not using xexec(), login calls absolute path from filesystem so must exec()
    249   execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0);
    250   perror_exit("exec shell '%s'", pwd->pw_shell);
    251 }