toybox/toys/other/login.c
<<
>>
Prefs
   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
  11USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
  12
  13config 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
  30GLOBALS(
  31  char *hostname;
  32  char *username;
  33
  34  int login_timeout, login_fail_timeout;
  35)
  36
  37static 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
  43void 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