/* login.c - Start a session on the system. * * Copyright 2012 Elie De Brauwer * * No support for PAM/securetty/selinux/login script/issue/utmp * Relies on libcrypt for hash calculation. USE_LOGIN(NEWTOY(login, ">1f:ph:", TOYFLAG_BIN|TOYFLAG_NEEDROOT)) config LOGIN bool "login" default y depends on TOYBOX_SHADOW help usage: login [-p] [-h host] [-f USERNAME] [USERNAME] Log in as a user, prompting for username and password if necessary. -p Preserve environment -h The name of the remote host for this login -f login as USERNAME without authentication */ #define FOR_login #include "toys.h" GLOBALS( char *h, *f; int login_timeout, login_fail_timeout; ) static void login_timeout_handler(int sig __attribute__((unused))) { printf("\nLogin timed out after %d seconds.\n", TT.login_timeout); xexit(); } void login_main(void) { int hh = FLAG(h), count, tty = tty_fd(); char *username, *pass = 0, *ss; struct passwd *pwd = 0; // we read user/password from stdin, but tty can be stderr? if (tty == -1) error_exit("no tty"); openlog("login", LOG_PID | LOG_CONS, LOG_AUTH); xsignal(SIGALRM, login_timeout_handler); if (TT.f) username = TT.f; else username = *toys.optargs; for (count = 0; count < 3; count++) { alarm(TT.login_timeout = 60); tcflush(0, TCIFLUSH); if (!username) { if (gethostname(toybuf, sizeof(toybuf)-1)) *toybuf = 0; printf("%s%slogin: ", *toybuf ? toybuf : "", *toybuf ? " " : ""); fflush(stdout); if(!fgets(toybuf, sizeof(toybuf)-1, stdin)) xexit(); // Remove trailing \n and so on for (ss = toybuf; *ss; ss++) if (*ss<=' ' || *ss==':') break; *ss = 0; if (!*(username = toybuf)) { username = 0; continue; } } // If user exists and isn't locked if ((pwd = getpwnam(username))) { // Pre-authenticated or passwordless if (TT.f || !*pwd->pw_passwd) break; // fetch shadow password if necessary if (*(pass = pwd->pw_passwd) == 'x') { struct spwd *spwd = getspnam (username); if (spwd) pass = spwd->sp_pwdp; } } else if (TT.f) error_exit("bad -f '%s'", TT.f); // Verify password. (Prompt for password _before_ checking disable state.) if (!read_password(toybuf, sizeof(toybuf), "Password: ")) { int x = pass && (ss = crypt(toybuf, pass)) && !strcmp(pass, ss); // password go bye-bye now. memset(toybuf, 0, sizeof(toybuf)); if (x) break; } syslog(LOG_WARNING, "invalid password for '%s' on %s %s%s", pwd->pw_name, ttyname(tty), hh ? "from " : "", hh ? TT.h : ""); sleep(3); puts("Login incorrect"); username = 0; pwd = 0; } alarm(0); if (!pwd) error_exit("max retries (3)"); // Check twice because "this file exists" is a security test, and in // theory filehandle exhaustion or other error could make open/read fail. if (pwd->pw_uid && !access("/etc/nologin", R_OK)) { ss = readfile("/etc/nologin", toybuf, sizeof(toybuf)); puts ((ss && *ss) ? ss : "nologin"); free(ss); toys.exitval = 1; return; } if (fchown(tty, pwd->pw_uid, pwd->pw_gid) || fchmod(tty, 0600)) printf("can't claim tty"); xsetuser(pwd); reset_env(pwd, !FLAG(p)); // Message of the day if ((ss = readfile("/etc/motd", 0, 0))) puts(ss); syslog(LOG_INFO, "%s logged in on %s %s %s", pwd->pw_name, ttyname(tty), hh ? "from" : "", hh ? TT.h : ""); // not using xexec(), login calls absolute path from filesystem so must exec() execl(pwd->pw_shell, xmprintf("-%s", pwd->pw_shell), (char *)0); perror_exit("exec shell '%s'", pwd->pw_shell); }