login.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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. USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT))
  8. config LOGIN
  9. bool "login"
  10. default y
  11. depends on TOYBOX_SHADOW
  12. help
  13. usage: login [-p] [-h host] [-f USERNAME] [USERNAME]
  14. Log in as a user, prompting for username and password if necessary.
  15. -p Preserve environment
  16. -h The name of the remote host for this login
  17. -f login as USERNAME without authentication
  18. */
  19. #define FOR_login
  20. #include "toys.h"
  21. GLOBALS(
  22. char *h, *f;
  23. int login_timeout, login_fail_timeout;
  24. )
  25. static void login_timeout_handler(int sig __attribute__((unused)))
  26. {
  27. printf("\nLogin timed out after %d seconds.\n", TT.login_timeout);
  28. xexit();
  29. }
  30. void login_main(void)
  31. {
  32. int hh = FLAG(h), count, tty = tty_fd();
  33. char *username, *pass = 0, *ss;
  34. struct passwd *pwd = 0;
  35. // we read user/password from stdin, but tty can be stderr?
  36. if (tty == -1) error_exit("no tty");
  37. openlog("login", LOG_PID | LOG_CONS, LOG_AUTH);
  38. xsignal(SIGALRM, login_timeout_handler);
  39. if (TT.f) username = TT.f;
  40. else username = *toys.optargs;
  41. for (count = 0; count < 3; count++) {
  42. alarm(TT.login_timeout = 60);
  43. tcflush(0, TCIFLUSH);
  44. if (!username) {
  45. if (gethostname(toybuf, sizeof(toybuf)-1)) *toybuf = 0;
  46. printf("%s%slogin: ", *toybuf ? toybuf : "", *toybuf ? " " : "");
  47. fflush(stdout);
  48. if(!fgets(toybuf, sizeof(toybuf)-1, stdin)) xexit();
  49. // Remove trailing \n and so on
  50. for (ss = toybuf; *ss; ss++) if (*ss<=' ' || *ss==':') break;
  51. *ss = 0;
  52. if (!*(username = toybuf)) {
  53. username = 0;
  54. continue;
  55. }
  56. }
  57. // If user exists and isn't locked
  58. if ((pwd = getpwnam(username))) {
  59. // Pre-authenticated or passwordless
  60. if (TT.f || !*pwd->pw_passwd) break;
  61. // fetch shadow password if necessary
  62. if (*(pass = pwd->pw_passwd) == 'x') {
  63. struct spwd *spwd = getspnam (username);
  64. if (spwd) pass = spwd->sp_pwdp;
  65. }
  66. } else if (TT.f) error_exit("bad -f '%s'", TT.f);
  67. // Verify password. (Prompt for password _before_ checking disable state.)
  68. if (!read_password(toybuf, sizeof(toybuf), "Password: ")) {
  69. int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss);
  70. // password go bye-bye now.
  71. memset(toybuf, 0, sizeof(toybuf));
  72. if (x) break;
  73. }
  74. syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name,
  75. ttyname(tty), hh ? "from " : "", hh ? TT.h : "");
  76. sleep(3);
  77. puts("Login incorrect");
  78. username = 0;
  79. pwd = 0;
  80. }
  81. alarm(0);
  82. if (!pwd) error_exit("max retries (3)");
  83. // Check twice because "this file exists" is a security test, and in
  84. // theory filehandle exhaustion or other error could make open/read fail.
  85. if (pwd->pw_uid && !access("/etc/nologin", R_OK)) {
  86. ss = readfile("/etc/nologin", toybuf, sizeof(toybuf));
  87. puts ((ss && *ss) ? ss : "nologin");
  88. free(ss);
  89. toys.exitval = 1;
  90. return;
  91. }
  92. if (fchown(tty, pwd->pw_uid, pwd->pw_gid) || fchmod(tty, 0600))
  93. printf("can't claim tty");
  94. xsetuser(pwd);
  95. reset_env(pwd, !FLAG(p));
  96. // Message of the day
  97. if ((ss = readfile("/etc/motd", 0, 0))) puts(ss);
  98. syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name,
  99. ttyname(tty), hh ? "from" : "", hh ? TT.h : "");
  100. // not using xexec(), login calls absolute path from filesystem so must exec()
  101. execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0);
  102. perror_exit("exec shell '%s'", pwd->pw_shell);
  103. }