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
   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