=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/login/login.c,v retrieving revision 1.35 retrieving revision 1.36 diff -u -r1.35 -r1.36 --- src/usr.bin/login/login.c 2000/10/14 20:33:13 1.35 +++ src/usr.bin/login/login.c 2001/05/29 21:39:26 1.36 @@ -1,4 +1,4 @@ -/* $OpenBSD: login.c,v 1.35 2000/10/14 20:33:13 miod Exp $ */ +/* $OpenBSD: login.c,v 1.36 2001/05/29 21:39:26 millert Exp $ */ /* $NetBSD: login.c,v 1.13 1996/05/15 23:50:16 jtc Exp $ */ /*- @@ -33,6 +33,39 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +/*- + * Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Berkeley Software Design, + * Inc. + * 4. The name of Berkeley Software Design, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI $From: login.c,v 2.28 1999/09/08 22:35:36 prb Exp $ + */ #ifndef lint static char copyright[] = @@ -44,7 +77,7 @@ #if 0 static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94"; #endif -static char rcsid[] = "$OpenBSD: login.c,v 1.35 2000/10/14 20:33:13 miod Exp $"; +static char rcsid[] = "$OpenBSD: login.c,v 1.36 2001/05/29 21:39:26 millert Exp $"; #endif /* not lint */ /* @@ -54,6 +87,7 @@ */ #include +#include #include #include #include @@ -64,9 +98,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -76,28 +112,25 @@ #include #include #include -#include +#include +#ifdef KERBEROS +#include +#endif + #include "pathnames.h" void badlogin __P((char *)); -void checknologin __P((void)); void dolastlog __P((int)); void getloginname __P((void)); void motd __P((void)); +void quickexit __P((int)); int rootterm __P((char *)); void sigint __P((int)); void sighup __P((int)); void sleepexit __P((int)); char *stypeof __P((char *)); void timedout __P((int)); -int pwcheck __P((char *, char *, char *, char *)); -#if defined(KERBEROS) || defined(KERBEROS5) -int klogin __P((struct passwd *, char *, char *, char *)); -void kdestroy __P((void)); -void dofork __P((void)); -void kgettokens __P((char *)); -#endif extern int check_failedlogin __P((uid_t)); extern void log_failedlogin __P((uid_t, char *, char *, char *)); @@ -111,17 +144,13 @@ */ u_int timeout = 300; -#if defined(KERBEROS) || defined(KERBEROS5) -int notickets = 1; -char *instance; -char *krbtkfile_env; -int authok; -#endif - -struct passwd *pwd; +struct passwd *pwd; login_cap_t *lc = NULL; +auth_session_t *as = NULL; int failures; +int needbanner = 1; char term[64], *hostname, *tty; +char *style; char *username = NULL, *rusername = NULL; int @@ -130,26 +159,49 @@ char *argv[]; { extern char **environ; + struct addrinfo *ai, hints; struct group *gr; + struct rlimit cds, scds; struct stat st; - struct timeval tp; struct utmp utmp; - int ask, ch, cnt, fflag, hflag, pflag, uflag, quietlog, rootlogin, rval; + quad_t expire, warning; uid_t uid; - char *domain, *p, *salt, *ttyn, *shell; + int ask, ch, cnt, fflag, pflag, quietlog, rootlogin, lastchance; + int error, homeless, needto, authok; + char *domain, *p, *ttyn, *shell, *fullname, *instance; + char *lipaddr, *script, *ripaddr, *style, *type, *fqdn, *copyright; char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; char localhost[MAXHOSTNAMELEN]; (void)signal(SIGALRM, timedout); - (void)alarm(timeout); + if (argc > 1) { + needto = 0; + (void)alarm(timeout); + } else + needto = 1; (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGINT, SIG_IGN); - (void)signal(SIGHUP, sighup); + (void)signal(SIGHUP, SIG_IGN); (void)setpriority(PRIO_PROCESS, 0, 0); openlog("login", LOG_ODELAY, LOG_AUTH); + fqdn = lipaddr = ripaddr = fullname = NULL; + /* + * Since login deals with sensitive information, turn off coredumps. + */ + if (getrlimit(RLIMIT_CORE, &scds) < 0) { + syslog(LOG_ERR, "couldn't get core dump size: %m"); + scds.rlim_cur = scds.rlim_max = QUAD_MIN; + } + cds.rlim_cur = cds.rlim_max = 0; + if (setrlimit(RLIMIT_CORE, &cds) < 0) { + syslog(LOG_ERR, "couldn't set core dump size to 0: %m"); + scds.rlim_cur = scds.rlim_max = QUAD_MIN; + } + + /* * -p is used by getty to tell login not to destroy the environment * -f is used to skip a second login authentication * -h is used by other servers to pass the name of the remote @@ -166,38 +218,84 @@ domain = localhost; } - fflag = hflag = pflag = 0; + if ((as = auth_open()) == NULL) { + syslog(LOG_ERR, "%m"); + err(1, NULL); + } + + fflag = pflag = 0; uid = getuid(); - while ((ch = getopt(argc, argv, "fh:u:p")) != -1) + while ((ch = getopt(argc, argv, "fh:pu:L:R:")) != -1) switch (ch) { case 'f': fflag = 1; break; case 'h': - if (uid) - errx(1, "-h option: %s", strerror(EPERM)); - hflag = 1; + if (uid) { + warn("-h option: %s", strerror(EPERM)); + quickexit(1); + } + if ((fqdn = strdup(optarg)) == NULL) { + warn(NULL); + quickexit(1); + } + auth_setoption(as, "fqdn", fqdn); if (domain && (p = strchr(optarg, '.')) && strcasecmp(p+1, domain) == 0) *p = 0; hostname = optarg; + auth_setoption(as, "hostname", hostname); break; + case 'L': + if (uid) { + warnx("-L option: %s", strerror(EPERM)); + quickexit(1); + } + if (lipaddr) { + warnx("duplicate -L option"); + quickexit(1); + } + lipaddr = optarg; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(lipaddr, NULL, &hints, &ai); + if (!error) { + strlcpy(localhost, ai->ai_canonname, + sizeof(localhost)); + freeaddrinfo(ai); + } else + strlcpy(localhost, lipaddr, sizeof(localhost)); + auth_setoption(as, "local_addr", lipaddr); + break; case 'p': pflag = 1; break; + case 'R': + if (uid) { + warnx("-R option: %s", strerror(EPERM)); + quickexit(1); + } + if (ripaddr) { + warnx("duplicate -R option"); + quickexit(1); + } + ripaddr = optarg; + auth_setoption(as, "remote_addr", ripaddr); + break; case 'u': - if (uid) - errx(1, "-u option: %s", strerror(EPERM)); - uflag = 1; + if (uid) { + warnx("-u option: %s", strerror(EPERM)); + quickexit(1); + } rusername = optarg; break; - case '?': default: if (!uid) syslog(LOG_ERR, "invalid flag %c", ch); (void)fprintf(stderr, - "usage: login [-fp] [-h hostname] [username]\n"); - exit(1); + "usage: login [-fp] [-h hostname] [-L lipaddr] [-R ripaddr] [username]\n"); + quickexit(1); } argc -= optind; argv += optind; @@ -221,40 +319,106 @@ else tty = ttyn; - for (cnt = 0;; ask = 1) { -#if defined(KERBEROS) || defined(KERBEROS5) - kdestroy(); - authok = 0; +#ifdef notyet + /* XXX - we don't (yet) support per-tty auth stuff */ + /* BSDi uses a ttys.conf file but we could just overload /etc/ttys */ + /* + * Classify the attempt. + * By default we use the value in the ttys file. + * If there is a classify script we run that as + * + * classify [-f] [username] + */ + if (type = getttyauth(tty)) + auth_setoption(as, "auth_type", type); #endif + + /* get the default login class */ + if ((lc = login_getclass(0)) == NULL) { /* get the default class */ + warnx("Failure to retrieve default class"); + quickexit(1); + } + if ((script = login_getcapstr(lc, "classify", NULL, NULL)) != NULL) { + unsetenv("AUTH_TYPE"); + unsetenv("REMOTE_NAME"); + if (script[0] != '/') { + syslog(LOG_ERR, "Invalid classify script: %s", script); + warnx("Classification failure"); + quickexit(1); + } + shell = strrchr(script, '/') + 1; + auth_setstate(as, AUTH_OKAY); + auth_call(as, script, shell, + fflag ? "-f" : username, fflag ? username : 0, 0); + if (!(auth_getstate(as) & AUTH_ALLOW)) + quickexit(1); + auth_setenv(as); + if ((p = getenv("AUTH_TYPE")) != NULL && + strncmp(p, "auth-", 5) == 0) + type = p; + if ((p = getenv("REMOTE_NAME")) != NULL) + hostname = p; + /* + * we may have changed some values, reset them + */ + auth_clroptions(as); + if (type) + auth_setoption(as, "auth_type", type); + if (fqdn) + auth_setoption(as, "fqdn", fqdn); + if (hostname) + auth_setoption(as, "hostname", hostname); + if (lipaddr) + auth_setoption(as, "local_addr", lipaddr); + if (ripaddr) + auth_setoption(as, "remote_addr", ripaddr); + } + + /* + * Request the things like the approval script print things + * to stdout (in particular, the nologins files) + */ + auth_setitem(as, AUTHV_INTERACTIVE, "True"); + + for (cnt = 0;; ask = 1) { + /* + * Clean up our current authentication session. + * Options are not cleared so we need to clear any + * we might set below. + */ + auth_clean(as); + auth_clroption(as, "style"); + auth_clroption(as, "lastchance"); + + lastchance = 0; + if (ask) { fflag = 0; getloginname(); } + if (needto) { + needto = 0; + alarm(timeout); + } + if ((style = strchr(username, ':')) != NULL) + *style++ = '\0'; + if (fullname) + free(fullname); + if (auth_setitem(as, AUTHV_NAME, username) < 0 || + (fullname = strdup(username)) == NULL) { + syslog(LOG_ERR, "%m"); + warn(NULL); + quickexit(1); + } rootlogin = 0; - -#if defined(KERBEROS) || defined(KERBEROS5) - /* - * Why should anyone with a root instance be able - * to be root here? - */ - instance = ""; -#endif -#ifdef KERBEROS + /* XXX - kerb5 uses a '/' not a '.' ??? */ if ((instance = strchr(username, '.')) != NULL) { if (strncmp(instance, ".root", 5) == 0) rootlogin = 1; *instance++ = '\0'; } else instance = ""; -#endif -#ifdef KERBEROS5 - if ((instance = strchr(username, '/')) != NULL) { - if (strncmp(instance, "/root", 5) == 0) - rootlogin = 1; - *instance++ = '\0'; - } else - instance = ""; -#endif + if (strlen(username) > UT_NAMESIZE) username[UT_NAMESIZE] = '\0'; @@ -268,120 +432,116 @@ badlogin(tbuf); failures = 0; } - (void)strlcpy(tbuf, username, sizeof tbuf); + (void)strlcpy(tbuf, username, sizeof(tbuf)); - if ((pwd = getpwnam(username))) - salt = pwd->pw_passwd; - else - salt = "xx"; - lc = login_getclass(pwd ? pwd->pw_class : LOGIN_DEFCLASS); - if (!lc) - err(1, "unable to get login class"); + if ((pwd = getpwnam(username)) != NULL && + auth_setpwd(as, pwd) < 0) { + syslog(LOG_ERR, "%m"); + warn(NULL); + quickexit(1); + } - /* - * If we have a valid account name, and it doesn't have a - * password, or the -f option was specified and the caller - * is root or the caller isn't changing their uid, don't - * authenticate. - */ - if (pwd) { - if (pwd->pw_uid == 0) - rootlogin = 1; + lc = login_getclass(pwd ? pwd->pw_class : NULL); - if (fflag && (uid == 0 || uid == pwd->pw_uid)) { - /* already authenticated */ - break; - } else if (pwd->pw_passwd[0] == '\0') { - /* pretend password okay */ - rval = 0; -#if defined(KERBEROS) || defined(KERBEROS5) - authok = 1; -#endif - goto ttycheck; - } - } + if (!lc) + goto failed; - fflag = 0; + style = login_getstyle(lc, style, type); - (void)setpriority(PRIO_PROCESS, 0, -4); + if (!style) + goto failed; - p = getpass("Password:"); + /* + * Turn off the fflag if we have an an invalid user + * or we are not root and we are trying to change uids. + */ + if (!pwd || (uid && uid != pwd->pw_uid)) + fflag = 0; - if (pwd) { -#if defined(KERBEROS) || defined(KERBEROS5) - rval = klogin(pwd, instance, localhost, p); - if (rval != 0 && rootlogin && pwd->pw_uid != 0) - rootlogin = 0; - if (rval == 1) { - /* Fall back on password file. */ - if (pwd->pw_uid != 0) - rootlogin = 0; - rval = pwcheck(username, p, salt, pwd->pw_passwd); - } - if (rval == 0) - authok = 1; -#else - rval = pwcheck(username, p, salt, pwd->pw_passwd); -#endif - } else { -#ifdef SKEY - if (strcasecmp(p, "s/key") == 0) - (void)skey_authenticate(username); - else -#endif - { - useconds_t us; + if (pwd && pwd->pw_uid == 0) + rootlogin = 1; - /* - * Sleep between 1 and 3 seconds - * to emulate a crypt. - */ - us = arc4random() % 3000000; - usleep(us); + /* + * If we do not have the force flag authenticate the user + */ + if (fflag) + authok = AUTH_SECURE; + else { + lastchance = + login_getcaptime(lc, "password-dead", 0, 0) != 0; + if (lastchance) + auth_setoption(as, "lastchance", "yes"); + /* + * Once we start asking for a password + * we want to log a failure on a hup. + */ + signal(SIGHUP, sighup); + auth_verify(as, style, NULL, lc->lc_class, NULL); + authok = auth_getstate(as); + /* + * If their password expired and it has not been + * too long since then, give the user one last + * chance to change their password + */ + if ((authok & AUTH_PWEXPIRED) && lastchance) { + authok = AUTH_OKAY; + } else + lastchance = 0; + if ((authok & AUTH_ALLOW) == 0) + goto failed; + if (auth_setoption(as, "style", style) < 0) { + syslog(LOG_ERR, "%m"); + warn(NULL); + quickexit(1); } - rval = 1; } - memset(p, 0, strlen(p)); - - (void)setpriority(PRIO_PROCESS, 0, 0); - - ttycheck: /* - * If trying to log in as root without Kerberos, - * but with insecure terminal, refuse the login attempt. + * explicitly reject users without password file entries */ -#if defined(KERBEROS) || defined(KERBEROS5) - if (authok == 1) -#endif - /* if logging in as root, user must be on a secure tty */ - if (pwd && rval == 0 && (!rootlogin || rootterm(tty))) - break; + if (pwd == 0) + goto failed; + authok &= AUTH_SECURE; + /* - * We don't want to give out info to an attacker trying - * to guess root's password so we always say "login refused" - * in that case, not "Login incorrect". + * If trying to log in as root on an insecure terminal, + * refuse the login attempt unless the authentication + * style explicitly says a root login is okay. */ + if (authok == 0 && pwd && rootlogin && !rootterm(tty)) + goto failed; + + if (fflag) { + type = 0; + style = "forced"; + } + break; + +failed: + if (authok & AUTH_SILENT) + quickexit(0); if (rootlogin && !rootterm(tty)) { - (void)fprintf(stderr, - "%s login refused on this terminal.\n", - pwd ? pwd->pw_name : "root"); + warnx("%s login refused on this terminal.", + fullname); if (hostname) syslog(LOG_NOTICE, "LOGIN %s REFUSED FROM %s%s%s ON TTY %s", - pwd ? pwd->pw_name : "root", - rusername ? rusername : "", + fullname, rusername ? rusername : "", rusername ? "@" : "", hostname, tty); else syslog(LOG_NOTICE, "LOGIN %s REFUSED ON TTY %s", - pwd ? pwd->pw_name : "root", tty); - } else - (void)printf("Login incorrect\n"); + fullname, tty); + } else { + if (!as || (p = auth_getvalue(as, "errormsg")) == NULL) + p = "Login incorrect"; + (void)printf("%s\n", p); + } failures++; if (pwd) log_failedlogin(pwd->pw_uid, hostname, rusername, tty); /* we allow 10 tries, but after 3 we start backing off */ + /* XXX - should be configurable */ if (++cnt > 3) { if (cnt >= 10) { badlogin(username); @@ -392,90 +552,19 @@ } /* committed to login -- turn off timeout */ - (void)alarm((u_int)0); + (void)alarm(0); endpwent(); - /* if user not super-user, check for disabled logins */ - if (!rootlogin) - checknologin(); - - setegid(pwd->pw_gid); - seteuid(pwd->pw_uid); - - if (chdir(pwd->pw_dir) < 0) { - (void)printf("No home directory %s!\n", pwd->pw_dir); - if (login_getcapbool(lc, "requirehome", 0)) - exit(1); - if (chdir("/")) - exit(0); - pwd->pw_dir = "/"; - (void)printf("Logging in with home = \"/\".\n"); - } - shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); if (*shell == '\0') shell = _PATH_BSHELL; else if (strlen(shell) >= MAXPATHLEN) { syslog(LOG_ERR, "shell path too long: %s", shell); warnx("invalid shell"); - sleepexit(1); + quickexit(1); } - quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) || - login_getcapbool(lc, "hushlogin", 0) || - (access(_PATH_HUSHLOGIN, F_OK) == 0)); - - seteuid(0); - setegid(0); /* XXX use a saved gid instead? */ - - if (pwd->pw_change || pwd->pw_expire) - (void)gettimeofday(&tp, (struct timezone *)NULL); - if (pwd->pw_expire) { - if (tp.tv_sec >= pwd->pw_expire) { - (void)printf("Sorry -- your account has expired.\n"); - sleepexit(1); - } else if (!quietlog &&pwd->pw_expire - tp.tv_sec < - login_getcaptime(lc, "expire-warn", - 2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY)) - (void)printf("Warning: your account expires on %s", - ctime(&pwd->pw_expire)); - } - if (pwd->pw_change) { - if (tp.tv_sec >= pwd->pw_change) { - (void)printf("Sorry -- your password has expired.\n"); - sleepexit(1); - } else if (!quietlog && pwd->pw_change - tp.tv_sec < - login_getcaptime(lc, "password-warn", - 2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY)) - (void)printf("Warning: your password expires on %s", - ctime(&pwd->pw_change)); - } - - /* Nothing else left to fail -- really log in. */ - (void)signal(SIGHUP, SIG_DFL); - memset((void *)&utmp, 0, sizeof(utmp)); - (void)time(&utmp.ut_time); - (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); - if (hostname) - (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); - (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); - login(&utmp); - - if (!quietlog) - (void)check_failedlogin(pwd->pw_uid); - dolastlog(quietlog); - - login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); - - (void)chown(ttyn, pwd->pw_uid, - (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); -#if defined(KERBEROS) || defined(KERBEROS5) - /* Fork so that we can call kdestroy */ - if (krbtkfile_env) - dofork(); -#endif - /* Destroy environment unless user has requested its preservation. */ if (!pflag) { if ((environ = calloc(1, sizeof (char *))) == NULL) @@ -496,7 +585,7 @@ if (setenv("HOME", pwd->pw_dir, 1) == -1 || setenv("SHELL", shell, 1) == -1) { warn("unable to setenv()"); - exit(1); + quickexit(1); } if (term[0] == '\0') (void)strlcpy(term, stypeof(tty), sizeof(term)); @@ -504,36 +593,85 @@ setenv("LOGNAME", pwd->pw_name, 1) == -1 || setenv("USER", pwd->pw_name, 1) == -1) { warn("unable to setenv()"); - exit(1); + quickexit(1); } if (hostname) { if (setenv("REMOTEHOST", hostname, 1) == -1) { warn("unable to setenv()"); - exit(1); + quickexit(1); } } if (rusername) { if (setenv("REMOTEUSER", rusername, 1) == -1) { warn("unable to setenv()"); - exit(1); + quickexit(1); } } -#ifdef KERBEROS - if (krbtkfile_env) { - if (setenv("KRBTKFILE", krbtkfile_env, 1) == -1) { - warn("unable to setenv()"); - exit(1); - } + + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) { + warn("unable to set user context"); + quickexit(1); } -#endif -#ifdef KERBEROS5 - if (krbtkfile_env) { - if (setenv("KRB5CCNAME", krbtkfile_env, 1) == -1) { - warn("unable to setenv()"); - exit(1); + auth_setenv(as); + + /* if user not super-user, check for disabled logins */ + if (!rootlogin) + auth_checknologin(lc); + + setegid(pwd->pw_gid); + seteuid(pwd->pw_uid); + + homeless = chdir(pwd->pw_dir); + if (homeless) { + if (login_getcapbool(lc, "requirehome", 0)) { + (void)printf("No home directory %s!\n", pwd->pw_dir); + quickexit(1); } + if (chdir("/")) + quickexit(0); } -#endif + + quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) || + login_getcapbool(lc, "hushlogin", 0) || + (access(_PATH_HUSHLOGIN, F_OK) == 0)); + + seteuid(0); + setegid(0); /* XXX use a saved gid instead? */ + + if ((p = auth_getvalue(as, "warnmsg")) != NULL) + (void)printf("WARNING: %s\n\n", p); + + expire = auth_check_expire(as); + if (expire < 0) { + (void)printf("Sorry -- your account has expired.\n"); + quickexit(1); + } else if (expire > 0 && !quietlog) { + warning = login_getcaptime(lc, "expire-warn", + 2 * DAYSPERWEEK * SECSPERDAY, 2 * DAYSPERWEEK * SECSPERDAY); + if (expire < warning) + (void)printf("Warning: your account expires on %s", + ctime(&pwd->pw_expire)); + } + + /* Nothing else left to fail -- really log in. */ + (void)signal(SIGHUP, SIG_DFL); + memset(&utmp, 0, sizeof(utmp)); + (void)time(&utmp.ut_time); + (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); + if (hostname) + (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); + (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); + login(&utmp); + + if (!quietlog) + (void)check_failedlogin(pwd->pw_uid); + dolastlog(quietlog); + + login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); + + (void)chown(ttyn, pwd->pw_uid, + (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); + /* If fflag is on, assume caller/authenticator has logged root login. */ if (rootlogin && fflag == 0) { if (hostname) @@ -544,11 +682,6 @@ syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); } -#if defined(KERBEROS) || defined(KERBEROS5) - if (!quietlog && notickets == 1) - (void)printf("Warning: no Kerberos tickets issued.\n"); -#endif - if (!quietlog) { #if 0 (void)printf("%s\n\t%s %s\n\n", @@ -556,6 +689,9 @@ "The Regents of the University of California. ", "All rights reserved."); #endif + if ((copyright = + login_getcapstr(lc, "copyright", NULL, NULL)) != NULL) + auth_cat(copyright); motd(); (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name); @@ -566,74 +702,73 @@ (void)signal(SIGALRM, SIG_DFL); (void)signal(SIGQUIT, SIG_DFL); + (void)signal(SIGHUP, SIG_DFL); (void)signal(SIGINT, SIG_DFL); (void)signal(SIGTSTP, SIG_IGN); tbuf[0] = '-'; (void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ? - p + 1 : shell, sizeof tbuf - 1); + p + 1 : shell, sizeof(tbuf) - 1); - /* Discard permissions last so can't get killed and drop core. */ - if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL)) { + if ((scds.rlim_cur != QUAD_MIN || scds.rlim_max != QUAD_MIN) && + setrlimit(RLIMIT_CORE, &scds) < 0) + syslog(LOG_ERR, "couldn't reset core dump size: %m"); + + if (lastchance) + (void)printf("WARNING: Your password has expired. You must change your password, now!\n"); + + if (setusercontext(lc, pwd, pwd->pw_uid, + LOGIN_SETALL & ~LOGIN_SETPATH) < 0) { warn("unable to set user context"); - exit(1); + quickexit(1); } + if (homeless) { + (void)printf("No home directory %s!\n", pwd->pw_dir); + (void)printf("Logging in with home = \"/\".\n"); + (void)setenv("HOME", "/", 1); + } + + if (auth_approval(as, lc, NULL, "login") == 0) { + if (auth_getstate(as) & AUTH_EXPIRED) + (void)printf("Sorry -- your account has expired.\n"); + else + (void)printf("approval failure\n"); + quickexit(1); + } + + /* + * The last thing we do is discard all of the open file descriptors. + * Last because the C library may have some open. + * + * XXX + * Assume that stdin, stdout and stderr are 0, 1 and 2, and that + * STDERR_FILENO is 2. + */ + for (cnt = getdtablesize(); cnt > STDERR_FILENO; cnt--) + (void)close(cnt); + + /* + * Close the authentication session, make sure it is marked + * as okay so no files are removed. + */ + auth_setstate(as, AUTH_OKAY); + auth_close(as); + #ifdef KERBEROS - kgettokens(pwd->pw_dir); + kgettokens(pwd->pw_dir); #endif execlp(shell, tbuf, 0); err(1, "%s", shell); } -int -pwcheck(user, p, salt, passwd) - char *user, *p, *salt, *passwd; -{ -#ifdef SKEY - if (strcasecmp(p, "s/key") == 0) - return skey_authenticate(user); -#endif - return strcmp(crypt(p, salt), passwd); -} - -#if defined(KERBEROS) || defined(KERBEROS5) -#define NBUFSIZ (UT_NAMESIZE + 1 + 5) /* .root suffix */ -#else -#define NBUFSIZ (UT_NAMESIZE + 1) -#endif - -#if defined(KERBEROS) || defined(KERBEROS5) /* - * This routine handles cleanup stuff, and the like. - * It exists only in the child process. + * Allow for a '.' and 16 characters for any instance as well as + * space for a ':' and 16 charcters defining the authentication type. */ -#include -void -dofork() -{ - int child; +#define NBUFSIZ (UT_NAMESIZE + 1 + 16 + 1 + 16) - if (!(child = fork())) - return; /* Child process */ - - /* Setup stuff? This would be things we could do in parallel with login */ - (void) chdir("/"); /* Let's not keep the fs busy... */ - - /* If we're the parent, watch the child until it dies */ - while (wait(0) != child) - ; - - /* Cleanup stuff */ - /* Run kdestroy to destroy tickets */ - kdestroy(); - - /* Leave */ - exit(0); -} -#endif - void getloginname() { @@ -646,7 +781,7 @@ for (p = nbuf; (ch = getchar()) != '\n'; ) { if (ch == EOF) { badlogin(username); - exit(0); + quickexit(0); } if (p < nbuf + (NBUFSIZ - 1)) *p++ = ch; @@ -670,6 +805,7 @@ { struct ttyent *t; + /* XXX - stash output of getttynam() elsewhere */ return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); } @@ -713,24 +849,6 @@ } void -checknologin() -{ - int fd, nchars; - char *nologin; - char tbuf[8192]; - - if (!login_getcapbool(lc, "ignorenologin", 0)) { - nologin = login_getcapstr(lc, "nologin", _PATH_NOLOGIN, - _PATH_NOLOGIN); - if ((fd = open(nologin, O_RDONLY, 0)) >= 0) { - while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) - (void)write(fileno(stdout), tbuf, nchars); - sleepexit(0); - } - } -} - -void dolastlog(quiet) int quiet; { @@ -807,11 +925,22 @@ sleepexit(eval) int eval; { + auth_close(as); (void)sleep(5); exit(eval); } void +quickexit(eval) + int eval; +{ + if (as) + auth_close(as); + exit(eval); +} + + +void sighup(signum) int signum; { @@ -819,3 +948,21 @@ badlogin(username); exit(0); } + +#ifdef KERBEROS +void +kgettokens(homedir) + char *homedir; +{ + + /* buy AFS-tokens for homedir */ + if (k_hasafs()) { + char cell[128]; + k_setpag(); + if (k_afs_cell_of_file(homedir, + cell, sizeof(cell)) == 0) + krb_afslog(cell, 0); + krb_afslog(0, 0); + } +} +#endif