17aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley/* login.c - Start a session on the system.
20b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer *
30b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
40b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer *
50b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer * No support for PAM/securetty/selinux/login script/issue/utmp
60b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer * Relies on libcrypt for hash calculation.
7f033f8607f156464747abe57487c1f6226f94001Rob Landley *
8f033f8607f156464747abe57487c1f6226f94001Rob Landley * TODO: this command predates "pending" but needs cleanup. It #defines
9f033f8607f156464747abe57487c1f6226f94001Rob Landley * random stuff, calls exit() form a signal handler... yeah.
100b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
113aa1d18b727ded4415d8a5f34f90a0726213aa7fRob LandleyUSE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
120b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
130b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwerconfig LOGIN
147aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  bool "login"
157aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  default y
1646ddf0e34b03f7711a9c80f7a70dc8cbf732f782Isaac Dunham  depends on TOYBOX_SHADOW
177aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  help
183aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    usage: login [-p] [-h host] [-f USERNAME] [USERNAME]
197aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
203aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    Log in as a user, prompting for username and password if necessary.
21afba5b8efdf1bac2c02ca787840a2be053c800f7Rob Landley
227aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    -p	Preserve environment
237aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    -h	The name of the remote host for this login
243aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    -f	login as USERNAME without authentication
250b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer*/
260b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
27c0e56edaf256adb6c60c5a052525a1ffbb927901Rob Landley#define FOR_login
280b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer#include "toys.h"
290b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
30c0e56edaf256adb6c60c5a052525a1ffbb927901Rob LandleyGLOBALS(
317aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  char *hostname;
323aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  char *username;
332109b158f496b3ecccea34c960b45f7aeba6679eRob Landley
342109b158f496b3ecccea34c960b45f7aeba6679eRob Landley  int login_timeout, login_fail_timeout;
350b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer)
360b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
370b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwerstatic void login_timeout_handler(int sig __attribute__((unused)))
380b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer{
392109b158f496b3ecccea34c960b45f7aeba6679eRob Landley  printf("\nLogin timed out after %d seconds.\n", TT.login_timeout);
407aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  exit(0);
410b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer}
420b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
430b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwervoid login_main(void)
440b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer{
453aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  char *forbid[] = {
463aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    "BASH_ENV", "ENV", "HOME", "IFS", "LD_LIBRARY_PATH", "LD_PRELOAD",
473aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    "LD_TRACE_LOADED_OBJECTS", "LD_BIND_NOW", "LD_AOUT_LIBRARY_PATH",
483aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    "LD_AOUT_PRELOAD", "LD_NOWARN", "LD_KEEPDIR", "SHELL"
493aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  };
503aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  int hh = toys.optflags&FLAG_h, count, tty;
513aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  char uu[33], *username, *pass = 0, *ss;
523aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  struct passwd *pwd = 0;
530b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
543aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  for (tty=0; tty<3; tty++) if (isatty(tty)) break;
553aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  if (tty == 3) error_exit("no tty");
560b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
573aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  for (count = 0; count < ARRAY_LEN(forbid); count++) unsetenv(forbid[count]);
580b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
597aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
60c776bde13bb2767db2943bc3b02df737a465c035Rob Landley  xsignal(SIGALRM, login_timeout_handler);
61c5e41c7d39523516c023a8def5b13907aac53cf1Rob Landley
623aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  if (TT.username) username = TT.username;
633aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  else username = *toys.optargs;
643aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  for (count = 0; count < 3; count++) {
653aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    alarm(TT.login_timeout = 60);
667aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    tcflush(0, TCIFLUSH);
670b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
683aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    if (!username) {
693aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      int i;
703aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
713aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      memset(username = uu, 0, sizeof(uu));
723aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      gethostname(uu, sizeof(uu)-1);
733aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      printf("%s%slogin: ", *uu ? uu : "", *uu ? " " : "");
743aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      fflush(stdout);
753aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
763aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      if(!fgets(uu, sizeof(uu)-1, stdin)) _exit(1);
773aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
783aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      // Remove trailing \n and so on
793aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      for (i = 0; i<sizeof(uu); i++) if (uu[i]<=' ' || uu[i]==':') uu[i]=0;
803aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      if (!*uu) {
813aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley        username = 0;
823aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley        continue;
833aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      }
847aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    }
850b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
863aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    // If user exists and isn't locked
877aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    pwd = getpwnam(username);
883aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    if (pwd && *pwd->pw_passwd != '!' && *pwd->pw_passwd != '*') {
890b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
903aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      // Pre-authenticated or passwordless
913aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      if (TT.username || !*pwd->pw_passwd) break;
920b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
933aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      // fetch shadow password if necessary
943aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      if (*(pass = pwd->pw_passwd) == 'x') {
953aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley        struct spwd *spwd = getspnam (username);
960b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
973aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley        if (spwd) pass = spwd->sp_pwdp;
983aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      }
993aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    } else if (TT.username) error_exit("bad -f '%s'", TT.username);
1000b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1013aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    // Verify password. (Prompt for password _before_ checking disable state.)
1023aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    if (!read_password(toybuf, sizeof(toybuf), "Password: ")) {
1033aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss);
1040b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1053aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      // password go bye-bye now.
1063aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      memset(toybuf, 0, sizeof(toybuf));
1073aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      if (x) break;
1083aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    }
1090b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1103aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name,
1113aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley      ttyname(tty), hh ? "from " : "", hh ? TT.hostname : "");
1120b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1132109b158f496b3ecccea34c960b45f7aeba6679eRob Landley    sleep(3);
1147aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    puts("Login incorrect");
1150b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1163aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    username = 0;
1172109b158f496b3ecccea34c960b45f7aeba6679eRob Landley    pwd = 0;
1187aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  }
1190b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1207aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  alarm(0);
1213aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  // This had password data in it, and we reuse for motd below
1223aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  memset(toybuf, 0, sizeof(toybuf));
1233aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
1243aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  if (!pwd) error_exit("max retries (3)");
1250b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1263aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  // Check twice because "this file exists" is a security test, and in
1273aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  // theory filehandle exhaustion or other error could make open/read fail.
1283aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  if (pwd->pw_uid && !access("/etc/nologin", R_OK)) {
1293aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    ss = readfile("/etc/nologin", toybuf, sizeof(toybuf));
1303aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    puts ((ss && *ss) ? ss : "nologin");
1313aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    free(ss);
1323aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    toys.exitval = 1;
1333aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
1343aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    return;
1353aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  }
1360b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
137afba5b8efdf1bac2c02ca787840a2be053c800f7Rob Landley  xsetuser(pwd);
1380b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1393aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  if (chdir(pwd->pw_dir)) printf("bad $HOME: %s\n", pwd->pw_dir);
1403aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
1413aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  if (!(toys.optflags&FLAG_p)) {
1423aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    char *term = getenv("TERM");
1433aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
1443aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    clearenv();
1453aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    if (term) setenv("TERM", term, 1);
1463aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  }
1473aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley
1483aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  setenv("USER", pwd->pw_name, 1);
1493aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  setenv("LOGNAME", pwd->pw_name, 1);
1503aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  setenv("HOME", pwd->pw_dir, 1);
1513aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  setenv("SHELL", pwd->pw_shell, 1);
1520b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1533aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  // Message of the day
1543aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  if ((ss = readfile("/etc/motd", 0, 0))) {
1553aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    puts(ss);
1563aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    free(ss);
1573aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley  }
1580b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1597aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
1603aa1d18b727ded4415d8a5f34f90a0726213aa7fRob Landley    ttyname(tty), hh ? "from" : "", hh ? TT.hostname : "");
1610b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer
1627d6af77804adc069a83e8566250f868a6cb9786eRob Landley  // not using xexec(), login calls absolute path from filesystem so must exec()
1637d6af77804adc069a83e8566250f868a6cb9786eRob Landley  execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0);
1647d6af77804adc069a83e8566250f868a6cb9786eRob Landley  perror_exit("exec shell '%s'", pwd->pw_shell);
1650b11a16626867a362ed9aff3950650af94d1cfa7Elie De Brauwer}
166