Home | History | Annotate | Line # | Download | only in login
login.c revision 1.72
      1 /*     $NetBSD: login.c,v 1.72 2003/01/01 00:00:14 mycroft Exp $       */
      2 
      3 /*-
      4  * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 __COPYRIGHT(
     39 "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
     40 	The Regents of the University of California.  All rights reserved.\n");
     41 #endif /* not lint */
     42 
     43 #ifndef lint
     44 #if 0
     45 static char sccsid[] = "@(#)login.c	8.4 (Berkeley) 4/2/94";
     46 #endif
     47 __RCSID("$NetBSD: login.c,v 1.72 2003/01/01 00:00:14 mycroft Exp $");
     48 #endif /* not lint */
     49 
     50 /*
     51  * login [ name ]
     52  * login -h hostname	(for telnetd, etc.)
     53  * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
     54  */
     55 
     56 #include <sys/param.h>
     57 #include <sys/stat.h>
     58 #include <sys/time.h>
     59 #include <sys/resource.h>
     60 #include <sys/file.h>
     61 #include <sys/wait.h>
     62 #include <sys/socket.h>
     63 
     64 #include <err.h>
     65 #include <errno.h>
     66 #include <grp.h>
     67 #include <pwd.h>
     68 #include <setjmp.h>
     69 #include <signal.h>
     70 #include <stdio.h>
     71 #include <stdlib.h>
     72 #include <string.h>
     73 #include <syslog.h>
     74 #include <time.h>
     75 #include <ttyent.h>
     76 #include <tzfile.h>
     77 #include <unistd.h>
     78 #ifdef SUPPORT_UTMP
     79 #include <utmp.h>
     80 #endif
     81 #ifdef SUPPORT_UTMPX
     82 #include <utmpx.h>
     83 #endif
     84 #include <util.h>
     85 #ifdef SKEY
     86 #include <skey.h>
     87 #endif
     88 #ifdef KERBEROS5
     89 #include <krb5/krb5.h>
     90 #include <com_err.h>
     91 #endif
     92 #ifdef LOGIN_CAP
     93 #include <login_cap.h>
     94 #endif
     95 
     96 #include "pathnames.h"
     97 
     98 #ifdef KERBEROS5
     99 int login_krb5_get_tickets = 1;
    100 int login_krb4_get_tickets = 0;
    101 int login_krb5_forwardable_tgt = 0;
    102 int login_krb5_retain_ccache = 0;
    103 #endif
    104 
    105 void	 badlogin __P((char *));
    106 void	 checknologin __P((char *));
    107 #ifdef SUPPORT_UTMP
    108 static void	 doutmp __P((void));
    109 static void	 dolastlog __P((int));
    110 #endif
    111 #ifdef SUPPORT_UTMPX
    112 static void	 doutmpx __P((void));
    113 static void	 dolastlogx __P((int));
    114 #endif
    115 static void	 update_db __P((int));
    116 void	 getloginname __P((void));
    117 int	 main __P((int, char *[]));
    118 void	 motd __P((char *));
    119 int	 rootterm __P((char *));
    120 void	 sigint __P((int));
    121 void	 sleepexit __P((int));
    122 const	 char *stypeof __P((const char *));
    123 void	 timedout __P((int));
    124 #if defined(KERBEROS)
    125 int	 klogin __P((struct passwd *, char *, char *, char *));
    126 void	 kdestroy __P((void));
    127 #endif
    128 #ifdef KERBEROS5
    129 int	 k5login __P((struct passwd *, char *, char *, char *));
    130 void	 k5destroy __P((void));
    131 int	 k5_read_creds __P((char*));
    132 int	 k5_write_creds __P((void));
    133 #endif
    134 #if defined(KERBEROS) || defined(KERBEROS5)
    135 void	 dofork __P((void));
    136 #endif
    137 
    138 #define	TTYGRPNAME	"tty"		/* name of group to own ttys */
    139 
    140 #define DEFAULT_BACKOFF 3
    141 #define DEFAULT_RETRIES 10
    142 
    143 /*
    144  * This bounds the time given to login.  Not a define so it can
    145  * be patched on machines where it's too small.
    146  */
    147 u_int	timeout = 300;
    148 
    149 #if defined(KERBEROS) || defined(KERBEROS5)
    150 int	notickets = 1;
    151 char	*instance;
    152 int	has_ccache = 0;
    153 #endif
    154 #ifdef KERBEROS
    155 extern char	*krbtkfile_env;
    156 extern int	krb_configured;
    157 #endif
    158 #ifdef KERBEROS5
    159 extern krb5_context kcontext;
    160 extern int	have_forward;
    161 extern char	*krb5tkfile_env;
    162 extern int	krb5_configured;
    163 #endif
    164 
    165 #if defined(KERBEROS) && defined(KERBEROS5)
    166 #define	KERBEROS_CONFIGURED	(krb_configured || krb5_configured)
    167 #elif defined(KERBEROS)
    168 #define	KERBEROS_CONFIGURED	krb_configured
    169 #elif defined(KERBEROS5)
    170 #define	KERBEROS_CONFIGURED	krb5_configured
    171 #endif
    172 
    173 struct	passwd *pwd;
    174 int	failures;
    175 char	term[64], *envinit[1], *hostname, *username, *tty, *nested;
    176 struct timeval now;
    177 struct sockaddr_storage ss;
    178 
    179 static const char copyrightstr[] = "\
    180 Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003\n\
    181 \tThe NetBSD Foundation, Inc.  All rights reserved.\n\
    182 Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994\n\
    183 \tThe Regents of the University of California.  All rights reserved.\n\n";
    184 
    185 int
    186 main(argc, argv)
    187 	int argc;
    188 	char *argv[];
    189 {
    190 	extern char **environ;
    191 	struct group *gr;
    192 	struct stat st;
    193 	int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
    194 	int Fflag;
    195 	uid_t uid, saved_uid;
    196 	gid_t saved_gid, saved_gids[NGROUPS_MAX];
    197 	int nsaved_gids;
    198 	char *domain, *p, *ttyn, *pwprompt;
    199 	const char *salt;
    200 	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
    201 	char localhost[MAXHOSTNAMELEN + 1];
    202 	int need_chpass, require_chpass;
    203 	int login_retries = DEFAULT_RETRIES,
    204 	    login_backoff = DEFAULT_BACKOFF;
    205 	time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
    206 #ifdef KERBEROS5
    207 	krb5_error_code kerror;
    208 #endif
    209 #if defined(KERBEROS) || defined(KERBEROS5)
    210 	int got_tickets = 0;
    211 #endif
    212 #ifdef LOGIN_CAP
    213 	char *shell = NULL;
    214 	login_cap_t *lc = NULL;
    215 #endif
    216 
    217 	tbuf[0] = '\0';
    218 	rval = 0;
    219 	pwprompt = NULL;
    220 	nested = NULL;
    221 	need_chpass = require_chpass = 0;
    222 
    223 	(void)signal(SIGALRM, timedout);
    224 	(void)alarm(timeout);
    225 	(void)signal(SIGQUIT, SIG_IGN);
    226 	(void)signal(SIGINT, SIG_IGN);
    227 	(void)setpriority(PRIO_PROCESS, 0, 0);
    228 
    229 	openlog("login", 0, LOG_AUTH);
    230 
    231 	/*
    232 	 * -p is used by getty to tell login not to destroy the environment
    233 	 * -f is used to skip a second login authentication
    234 	 * -h is used by other servers to pass the name of the remote host to
    235 	 *    login so that it may be placed in utmp/utmpx and wtmp/wtmpx
    236 	 * -s is used to force use of S/Key or equivalent.
    237 	 */
    238 	domain = NULL;
    239 	if (gethostname(localhost, sizeof(localhost)) < 0)
    240 		syslog(LOG_ERR, "couldn't get local hostname: %m");
    241 	else
    242 		domain = strchr(localhost, '.');
    243 	localhost[sizeof(localhost) - 1] = '\0';
    244 
    245 	Fflag = fflag = hflag = pflag = sflag = 0;
    246 #ifdef KERBEROS5
    247 	have_forward = 0;
    248 #endif
    249 	uid = getuid();
    250 	while ((ch = getopt(argc, argv, "Ffh:ps")) != -1)
    251 		switch (ch) {
    252 		case 'F':
    253 			Fflag = 1;
    254 			/* FALLTHROUGH */
    255 		case 'f':
    256 			fflag = 1;
    257 			break;
    258 		case 'h':
    259 			if (uid)
    260 				errx(1, "-h option: %s", strerror(EPERM));
    261 			hflag = 1;
    262 			if (domain && (p = strchr(optarg, '.')) != NULL &&
    263 			    strcasecmp(p, domain) == 0)
    264 				*p = 0;
    265 			hostname = optarg;
    266 			break;
    267 		case 'p':
    268 			pflag = 1;
    269 			break;
    270 		case 's':
    271 			sflag = 1;
    272 			break;
    273 		default:
    274 		case '?':
    275 			(void)fprintf(stderr,
    276 			    "usage: login [-fps] [-h hostname] [username]\n");
    277 			exit(1);
    278 		}
    279 	argc -= optind;
    280 	argv += optind;
    281 
    282 	if (*argv) {
    283 		username = *argv;
    284 		ask = 0;
    285 	} else
    286 		ask = 1;
    287 
    288 	for (cnt = getdtablesize(); cnt > 2; cnt--)
    289 		(void)close(cnt);
    290 
    291 	ttyn = ttyname(STDIN_FILENO);
    292 	if (ttyn == NULL || *ttyn == '\0') {
    293 		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
    294 		ttyn = tname;
    295 	}
    296 	if ((tty = strrchr(ttyn, '/')) != NULL)
    297 		++tty;
    298 	else
    299 		tty = ttyn;
    300 
    301 	if (issetugid()) {
    302 		nested = strdup(user_from_uid(getuid(), 0));
    303 		if (nested == NULL) {
    304                 	syslog(LOG_ERR, "strdup: %m");
    305                 	sleepexit(1);
    306 		}
    307 	}
    308 
    309 #ifdef LOGIN_CAP
    310 	/* Get "login-retries" and "login-backoff" from default class */
    311 	if ((lc = login_getclass(NULL)) != NULL) {
    312 		login_retries = (int)login_getcapnum(lc, "login-retries",
    313 		    DEFAULT_RETRIES, DEFAULT_RETRIES);
    314 		login_backoff = (int)login_getcapnum(lc, "login-backoff",
    315 		    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
    316 		login_close(lc);
    317 		lc = NULL;
    318 	}
    319 #endif
    320 
    321 #ifdef KERBEROS5
    322 	kerror = krb5_init_context(&kcontext);
    323 	if (kerror) {
    324 		/*
    325 		 * If Kerberos is not configured, that is, we are
    326 		 * not using Kerberos, do not log the error message.
    327 		 * However, if Kerberos is configured,  and the
    328 		 * context init fails for some other reason, we need
    329 		 * to issue a no tickets warning to the user when the
    330 		 * login succeeds.
    331 		 */
    332 		if (kerror != ENXIO) {	/* XXX NetBSD-local Heimdal hack */
    333 			syslog(LOG_NOTICE,
    334 			    "%s when initializing Kerberos context",
    335 			    error_message(kerror));
    336 			krb5_configured = 1;
    337 		}
    338 		login_krb5_get_tickets = 0;
    339 	}
    340 #endif /* KERBEROS5 */
    341 
    342 	for (cnt = 0;; ask = 1) {
    343 #if defined(KERBEROS)
    344 	        kdestroy();
    345 #endif
    346 #if defined(KERBEROS5)
    347 		if (login_krb5_get_tickets)
    348 			k5destroy();
    349 #endif
    350 		if (ask) {
    351 			fflag = 0;
    352 			getloginname();
    353 		}
    354 		rootlogin = 0;
    355 #ifdef KERBEROS
    356 		if ((instance = strchr(username, '.')) != NULL)
    357 			*instance++ = '\0';
    358 		else
    359 			instance = "";
    360 #endif
    361 #ifdef KERBEROS5
    362 		if ((instance = strchr(username, '/')) != NULL)
    363 			*instance++ = '\0';
    364 		else
    365 			instance = "";
    366 #endif
    367 		if (strlen(username) > MAXLOGNAME)
    368 			username[MAXLOGNAME] = '\0';
    369 
    370 		/*
    371 		 * Note if trying multiple user names; log failures for
    372 		 * previous user name, but don't bother logging one failure
    373 		 * for nonexistent name (mistyped username).
    374 		 */
    375 		if (failures && strcmp(tbuf, username)) {
    376 			if (failures > (pwd ? 0 : 1))
    377 				badlogin(tbuf);
    378 			failures = 0;
    379 		}
    380 		(void)strlcpy(tbuf, username, sizeof(tbuf));
    381 
    382 		if ((pwd = getpwnam(username)) != NULL)
    383 			salt = pwd->pw_passwd;
    384 		else
    385 			salt = "xx";
    386 
    387 #ifdef LOGIN_CAP
    388 		/*
    389 		 * Establish the class now, before we might goto
    390 		 * within the next block. pwd can be NULL since it
    391 		 * falls back to the "default" class if it is.
    392 		 */
    393 		lc = login_getclass(pwd ? pwd->pw_class : NULL);
    394 #endif
    395 		/*
    396 		 * if we have a valid account name, and it doesn't have a
    397 		 * password, or the -f option was specified and the caller
    398 		 * is root or the caller isn't changing their uid, don't
    399 		 * authenticate.
    400 		 */
    401 		if (pwd) {
    402 			if (pwd->pw_uid == 0)
    403 				rootlogin = 1;
    404 
    405 			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
    406 				/* already authenticated */
    407 #ifdef KERBEROS5
    408 				if (login_krb5_get_tickets && Fflag)
    409 					k5_read_creds(username);
    410 #endif
    411 				break;
    412 			} else if (pwd->pw_passwd[0] == '\0') {
    413 				/* pretend password okay */
    414 				rval = 0;
    415 				goto ttycheck;
    416 			}
    417 		}
    418 
    419 		fflag = 0;
    420 
    421 		(void)setpriority(PRIO_PROCESS, 0, -4);
    422 
    423 #ifdef SKEY
    424 		if (skey_haskey(username) == 0) {
    425 			static char skprompt[80];
    426 			const char *skinfo = skey_keyinfo(username);
    427 
    428 			(void)snprintf(skprompt, sizeof(skprompt)-1,
    429 			    "Password [%s]:",
    430 			    skinfo ? skinfo : "error getting challenge");
    431 			pwprompt = skprompt;
    432 		} else
    433 #endif
    434 			pwprompt = "Password:";
    435 
    436 		p = getpass(pwprompt);
    437 
    438 		if (pwd == NULL) {
    439 			rval = 1;
    440 			goto skip;
    441 		}
    442 #ifdef KERBEROS
    443 		if (
    444 #ifdef KERBEROS5
    445 		    /* allow a user to get both krb4 and krb5 tickets, if
    446 		     * desired.  If krb5 is compiled in, the default action
    447 		     * is to ignore krb4 and get krb5 tickets, but the user
    448 		     * can override this in the krb5.conf. */
    449 		    login_krb4_get_tickets &&
    450 #endif
    451 		    klogin(pwd, instance, localhost, p) == 0) {
    452 			rval = 0;
    453 			got_tickets = 1;
    454 		}
    455 #endif
    456 #ifdef KERBEROS5
    457 		if (login_krb5_get_tickets &&
    458 		    k5login(pwd, instance, localhost, p) == 0) {
    459 			rval = 0;
    460 			got_tickets = 1;
    461 		}
    462 #endif
    463 #if defined(KERBEROS) || defined(KERBEROS5)
    464 		if (got_tickets)
    465 			goto skip;
    466 #endif
    467 #ifdef SKEY
    468 		if (skey_haskey(username) == 0 &&
    469 		    skey_passcheck(username, p) != -1) {
    470 			rval = 0;
    471 			goto skip;
    472 		}
    473 #endif
    474 		if (!sflag && *pwd->pw_passwd != '\0' &&
    475 		    !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
    476 			rval = 0;
    477 			require_chpass = 1;
    478 			goto skip;
    479 		}
    480 		rval = 1;
    481 
    482 	skip:
    483 		memset(p, 0, strlen(p));
    484 
    485 		(void)setpriority(PRIO_PROCESS, 0, 0);
    486 
    487 	ttycheck:
    488 		/*
    489 		 * If trying to log in as root without Kerberos,
    490 		 * but with insecure terminal, refuse the login attempt.
    491 		 */
    492 		if (pwd && !rval && rootlogin && !rootterm(tty)) {
    493 			(void)fprintf(stderr,
    494 			    "%s login refused on this terminal.\n",
    495 			    pwd->pw_name);
    496 			if (hostname)
    497 				syslog(LOG_NOTICE,
    498 				    "LOGIN %s REFUSED FROM %s ON TTY %s",
    499 				    pwd->pw_name, hostname, tty);
    500 			else
    501 				syslog(LOG_NOTICE,
    502 				    "LOGIN %s REFUSED ON TTY %s",
    503 				     pwd->pw_name, tty);
    504 			continue;
    505 		}
    506 
    507 		if (pwd && !rval)
    508 			break;
    509 
    510 		(void)printf("Login incorrect\n");
    511 		failures++;
    512 		cnt++;
    513 		/* we allow 10 tries, but after 3 we start backing off */
    514 		if (cnt > login_backoff) {
    515 			if (cnt >= login_retries) {
    516 				badlogin(username);
    517 				sleepexit(1);
    518 			}
    519 			sleep((u_int)((cnt - 3) * 5));
    520 		}
    521 	}
    522 
    523 	/* committed to login -- turn off timeout */
    524 	(void)alarm((u_int)0);
    525 
    526 	endpwent();
    527 
    528 	/* if user not super-user, check for disabled logins */
    529 #ifdef LOGIN_CAP
    530         if (!login_getcapbool(lc, "ignorenologin", rootlogin))
    531 		checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
    532 #else
    533         if (!rootlogin)
    534                 checknologin(NULL);
    535 #endif
    536 
    537 #ifdef LOGIN_CAP
    538         quietlog = login_getcapbool(lc, "hushlogin", 0);
    539 #else
    540         quietlog = 0;
    541 #endif
    542 	/* Temporarily give up special privileges so we can change */
    543 	/* into NFS-mounted homes that are exported for non-root */
    544 	/* access and have mode 7x0 */
    545 	saved_uid = geteuid();
    546 	saved_gid = getegid();
    547 	nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
    548 
    549 	(void)setegid(pwd->pw_gid);
    550 	initgroups(username, pwd->pw_gid);
    551 	(void)seteuid(pwd->pw_uid);
    552 
    553 	if (chdir(pwd->pw_dir) < 0) {
    554 #ifdef LOGIN_CAP
    555                 if (login_getcapbool(lc, "requirehome", 0)) {
    556 			(void)printf("Home directory %s required\n",
    557 			    pwd->pw_dir);
    558                         sleepexit(1);
    559 		}
    560 #endif
    561 		(void)printf("No home directory %s!\n", pwd->pw_dir);
    562 		if (chdir("/"))
    563 			exit(0);
    564 		pwd->pw_dir = "/";
    565 		(void)printf("Logging in with home = \"/\".\n");
    566 	}
    567 
    568 	if (!quietlog)
    569 		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
    570 
    571 	/* regain special privileges */
    572 	(void)seteuid(saved_uid);
    573 	setgroups(nsaved_gids, saved_gids);
    574 	(void)setegid(saved_gid);
    575 
    576 #ifdef LOGIN_CAP
    577         pw_warntime = login_getcaptime(lc, "password-warn",
    578             _PASSWORD_WARNDAYS * SECSPERDAY,
    579             _PASSWORD_WARNDAYS * SECSPERDAY);
    580 #endif
    581 
    582 	(void)gettimeofday(&now, (struct timezone *)NULL);
    583 	if (pwd->pw_expire) {
    584 		if (now.tv_sec >= pwd->pw_expire) {
    585 			(void)printf("Sorry -- your account has expired.\n");
    586 			sleepexit(1);
    587 		} else if (pwd->pw_expire - now.tv_sec < pw_warntime &&
    588 		    !quietlog)
    589 			(void)printf("Warning: your account expires on %s",
    590 			    ctime(&pwd->pw_expire));
    591 	}
    592 	if (pwd->pw_change) {
    593 		if (pwd->pw_change == _PASSWORD_CHGNOW)
    594 			need_chpass = 1;
    595 		else if (now.tv_sec >= pwd->pw_change) {
    596 			(void)printf("Sorry -- your password has expired.\n");
    597 			sleepexit(1);
    598 		} else if (pwd->pw_change - now.tv_sec < pw_warntime &&
    599 		    !quietlog)
    600 			(void)printf("Warning: your password expires on %s",
    601 			    ctime(&pwd->pw_change));
    602 
    603 	}
    604 	/* Nothing else left to fail -- really log in. */
    605 	update_db(quietlog);
    606 
    607 	(void)chown(ttyn, pwd->pw_uid,
    608 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
    609 
    610 	if (ttyaction(ttyn, "login", pwd->pw_name))
    611 		(void)printf("Warning: ttyaction failed.\n");
    612 
    613 #if defined(KERBEROS) || defined(KERBEROS5)
    614 	/* Fork so that we can call kdestroy */
    615 	if (
    616 #ifdef KERBEROS5
    617 	    ! login_krb5_retain_ccache &&
    618 #endif
    619 	    has_ccache)
    620 		dofork();
    621 #endif
    622 
    623 	/* Destroy environment unless user has requested its preservation. */
    624 	if (!pflag)
    625 		environ = envinit;
    626 
    627 #ifdef LOGIN_CAP
    628 	if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
    629 	    LOGIN_SETLOGIN) != 0) {
    630 		syslog(LOG_ERR, "setusercontext failed");
    631 		exit(1);
    632 	}
    633 	if (setusercontext(lc, pwd, pwd->pw_uid,
    634 	    (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETLOGIN))) != 0) {
    635 		syslog(LOG_ERR, "setusercontext failed");
    636 		exit(1);
    637 	}
    638 #else
    639 	(void)setgid(pwd->pw_gid);
    640 
    641 	initgroups(username, pwd->pw_gid);
    642 
    643 	if (nested == NULL && setlogin(pwd->pw_name) < 0)
    644 		syslog(LOG_ERR, "setlogin() failure: %m");
    645 
    646 	/* Discard permissions last so can't get killed and drop core. */
    647 	if (rootlogin)
    648 		(void)setuid(0);
    649 	else
    650 		(void)setuid(pwd->pw_uid);
    651 #endif
    652 
    653 	if (*pwd->pw_shell == '\0')
    654 		pwd->pw_shell = _PATH_BSHELL;
    655 #ifdef LOGIN_CAP
    656 	if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
    657 		if ((shell = strdup(shell)) == NULL) {
    658                 	syslog(LOG_ERR, "Cannot alloc mem");
    659                 	sleepexit(1);
    660 		}
    661 		pwd->pw_shell = shell;
    662 	}
    663 #endif
    664 
    665 	(void)setenv("HOME", pwd->pw_dir, 1);
    666 	(void)setenv("SHELL", pwd->pw_shell, 1);
    667 	if (term[0] == '\0') {
    668 		char *tt = (char *)stypeof(tty);
    669 #ifdef LOGIN_CAP
    670 		if (tt == NULL)
    671 			tt = login_getcapstr(lc, "term", NULL, NULL);
    672 #endif
    673 		/* unknown term -> "su" */
    674 		(void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term));
    675 	}
    676 	(void)setenv("TERM", term, 0);
    677 	(void)setenv("LOGNAME", pwd->pw_name, 1);
    678 	(void)setenv("USER", pwd->pw_name, 1);
    679 
    680 #ifdef LOGIN_CAP
    681 	setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
    682 #else
    683 	(void)setenv("PATH", _PATH_DEFPATH, 0);
    684 #endif
    685 
    686 #ifdef KERBEROS
    687 	if (krbtkfile_env)
    688 		(void)setenv("KRBTKFILE", krbtkfile_env, 1);
    689 #endif
    690 #ifdef KERBEROS5
    691 	if (krb5tkfile_env)
    692 		(void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
    693 #endif
    694 
    695 	if (tty[sizeof("tty")-1] == 'd')
    696 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
    697 
    698 	/* If fflag is on, assume caller/authenticator has logged root login. */
    699 	if (rootlogin && fflag == 0) {
    700 		if (hostname)
    701 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
    702 			    username, tty, hostname);
    703 		else
    704 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
    705 			    username, tty);
    706 	}
    707 
    708 #if defined(KERBEROS) || defined(KERBEROS5)
    709 	if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
    710 		(void)printf("Warning: no Kerberos tickets issued.\n");
    711 #endif
    712 
    713 	if (!quietlog) {
    714 		char *fname;
    715 #ifdef LOGIN_CAP
    716 		fname = login_getcapstr(lc, "copyright", NULL, NULL);
    717 		if (fname != NULL && access(fname, F_OK) == 0)
    718 			motd(fname);
    719 		else
    720 #endif
    721 			(void)printf("%s", copyrightstr);
    722 
    723 #ifdef LOGIN_CAP
    724                 fname = login_getcapstr(lc, "welcome", NULL, NULL);
    725                 if (fname == NULL || access(fname, F_OK) != 0)
    726 #endif
    727                         fname = _PATH_MOTDFILE;
    728                 motd(fname);
    729 
    730 		(void)snprintf(tbuf,
    731 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
    732 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
    733 			(void)printf("You have %smail.\n",
    734 			    (st.st_mtime > st.st_atime) ? "new " : "");
    735 	}
    736 
    737 #ifdef LOGIN_CAP
    738 	login_close(lc);
    739 #endif
    740 
    741 	(void)signal(SIGALRM, SIG_DFL);
    742 	(void)signal(SIGQUIT, SIG_DFL);
    743 	(void)signal(SIGINT, SIG_DFL);
    744 	(void)signal(SIGTSTP, SIG_IGN);
    745 
    746 	tbuf[0] = '-';
    747 	(void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
    748 	    p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
    749 
    750 	/* Wait to change password until we're unprivileged */
    751 	if (need_chpass) {
    752 		if (!require_chpass)
    753 			(void)printf(
    754 "Warning: your password has expired. Please change it as soon as possible.\n");
    755 		else {
    756 			int	status;
    757 
    758 			(void)printf(
    759 		    "Your password has expired. Please choose a new one.\n");
    760 			switch (fork()) {
    761 			case -1:
    762 				warn("fork");
    763 				sleepexit(1);
    764 			case 0:
    765 				execl(_PATH_BINPASSWD, "passwd", 0);
    766 				_exit(1);
    767 			default:
    768 				if (wait(&status) == -1 ||
    769 				    WEXITSTATUS(status))
    770 					sleepexit(1);
    771 			}
    772 		}
    773 	}
    774 
    775 #ifdef KERBEROS5
    776 	if (login_krb5_get_tickets)
    777 		k5_write_creds();
    778 #endif
    779 	execlp(pwd->pw_shell, tbuf, 0);
    780 	err(1, "%s", pwd->pw_shell);
    781 }
    782 
    783 #if defined(KERBEROS) || defined(KERBEROS5)
    784 #define	NBUFSIZ		(MAXLOGNAME + 1 + 5)	/* .root suffix */
    785 #else
    786 #define	NBUFSIZ		(MAXLOGNAME + 1)
    787 #endif
    788 
    789 #if defined(KERBEROS) || defined(KERBEROS5)
    790 /*
    791  * This routine handles cleanup stuff, and the like.
    792  * It exists only in the child process.
    793  */
    794 #include <sys/wait.h>
    795 void
    796 dofork()
    797 {
    798 	int child;
    799 
    800 	if (!(child = fork()))
    801 		return; /* Child process */
    802 
    803 	/*
    804 	 * Setup stuff?  This would be things we could do in parallel
    805 	 * with login
    806 	 */
    807 	(void)chdir("/");	/* Let's not keep the fs busy... */
    808 
    809 	/* If we're the parent, watch the child until it dies */
    810 	while (wait(0) != child)
    811 		;
    812 
    813 	/* Cleanup stuff */
    814 	/* Run kdestroy to destroy tickets */
    815 #ifdef KERBEROS
    816 	kdestroy();
    817 #endif
    818 #ifdef KERBEROS5
    819 	if (login_krb5_get_tickets)
    820 		k5destroy();
    821 #endif
    822 
    823 	/* Leave */
    824 	exit(0);
    825 }
    826 #endif
    827 
    828 void
    829 getloginname()
    830 {
    831 	int ch;
    832 	char *p;
    833 	static char nbuf[NBUFSIZ];
    834 
    835 	for (;;) {
    836 		(void)printf("login: ");
    837 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
    838 			if (ch == EOF) {
    839 				badlogin(username);
    840 				exit(0);
    841 			}
    842 			if (p < nbuf + (NBUFSIZ - 1))
    843 				*p++ = ch;
    844 		}
    845 		if (p > nbuf) {
    846 			if (nbuf[0] == '-')
    847 				(void)fprintf(stderr,
    848 				    "login names may not start with '-'.\n");
    849 			else {
    850 				*p = '\0';
    851 				username = nbuf;
    852 				break;
    853 			}
    854 		}
    855 	}
    856 }
    857 
    858 int
    859 rootterm(ttyn)
    860 	char *ttyn;
    861 {
    862 	struct ttyent *t;
    863 
    864 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
    865 }
    866 
    867 jmp_buf motdinterrupt;
    868 
    869 void
    870 motd(fname)
    871 	char *fname;
    872 {
    873 	int fd, nchars;
    874 	sig_t oldint;
    875 	char tbuf[8192];
    876 
    877 	if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
    878 		return;
    879 	oldint = signal(SIGINT, sigint);
    880 	if (setjmp(motdinterrupt) == 0)
    881 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    882 			(void)write(fileno(stdout), tbuf, nchars);
    883 	(void)signal(SIGINT, oldint);
    884 	(void)close(fd);
    885 }
    886 
    887 /* ARGSUSED */
    888 void
    889 sigint(signo)
    890 	int signo;
    891 {
    892 
    893 	longjmp(motdinterrupt, 1);
    894 }
    895 
    896 /* ARGSUSED */
    897 void
    898 timedout(signo)
    899 	int signo;
    900 {
    901 
    902 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
    903 	exit(0);
    904 }
    905 
    906 void
    907 checknologin(fname)
    908 	char *fname;
    909 {
    910 	int fd, nchars;
    911 	char tbuf[8192];
    912 
    913 	if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
    914 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    915 			(void)write(fileno(stdout), tbuf, nchars);
    916 		sleepexit(0);
    917 	}
    918 }
    919 
    920 static void
    921 update_db(int quietlog)
    922 {
    923 	if (nested != NULL) {
    924 		if (hostname != NULL)
    925 			syslog(LOG_NOTICE, "%s to %s on tty %s from %s",
    926 			    nested, pwd->pw_name, tty, hostname);
    927 		else
    928 			syslog(LOG_NOTICE, "%s to %s on tty %s", nested,
    929 			    pwd->pw_name, tty);
    930 
    931 		return;
    932 	}
    933 	if (hostname != NULL) {
    934 		socklen_t len = sizeof(ss);
    935 		(void)getpeername(STDIN_FILENO, (struct sockaddr *)&ss, &len);
    936 	}
    937 	(void)gettimeofday(&now, NULL);
    938 #ifdef SUPPORT_UTMPX
    939 	doutmpx();
    940 	dolastlogx(quietlog);
    941 	quietlog = 1;
    942 #endif
    943 #ifdef SUPPORT_UTMP
    944 	doutmp();
    945 	dolastlog(quietlog);
    946 #endif
    947 }
    948 
    949 #ifdef SUPPORT_UTMPX
    950 static void
    951 doutmpx()
    952 {
    953 	struct utmpx utmpx;
    954 	char *t;
    955 
    956 	memset((void *)&utmpx, 0, sizeof(utmpx));
    957 	utmpx.ut_tv = now;
    958 	(void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name));
    959 	if (hostname) {
    960 		(void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host));
    961 		utmpx.ut_ss = ss;
    962 	}
    963 	(void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line));
    964 	utmpx.ut_type = USER_PROCESS;
    965 	utmpx.ut_pid = getpid();
    966 	t = tty + strlen(tty);
    967 	if (t - tty >= sizeof(utmpx.ut_id)) {
    968 	    (void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id),
    969 		sizeof(utmpx.ut_id));
    970 	} else {
    971 	    (void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id));
    972 	}
    973 	if (pututxline(&utmpx) == NULL)
    974 		syslog(LOG_NOTICE, "Cannot update utmpx %m");
    975 	endutxent();
    976 	if (updwtmpx(_PATH_WTMPX, &utmpx) != 0)
    977 		syslog(LOG_NOTICE, "Cannot update wtmpx %m");
    978 }
    979 
    980 static void
    981 dolastlogx(quiet)
    982 	int quiet;
    983 {
    984 	struct lastlogx ll;
    985 	if (getlastlogx(pwd->pw_uid, &ll) != NULL) {
    986 		time_t t = (time_t)ll.ll_tv.tv_sec;
    987 		(void)printf("Last login: %.24s ", ctime(&t));
    988 		if (*ll.ll_host != '\0')
    989 			(void)printf("from %.*s ",
    990 			    (int)sizeof(ll.ll_host),
    991 			    ll.ll_host);
    992 		(void)printf("on %.*s\n",
    993 		    (int)sizeof(ll.ll_line),
    994 		    ll.ll_line);
    995 	}
    996 	ll.ll_tv = now;
    997 	(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
    998 	if (hostname) {
    999 		(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
   1000 		ll.ll_ss = ss;
   1001 	}
   1002 	if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0)
   1003 		syslog(LOG_NOTICE, "Cannot update lastlogx %m");
   1004 }
   1005 #endif
   1006 
   1007 #ifdef SUPPORT_UTMP
   1008 static void
   1009 doutmp()
   1010 {
   1011 	struct utmp utmp;
   1012 
   1013 	(void)memset((void *)&utmp, 0, sizeof(utmp));
   1014 	utmp.ut_time = now.tv_sec;
   1015 	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
   1016 	if (hostname)
   1017 		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
   1018 	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
   1019 	login(&utmp);
   1020 }
   1021 
   1022 static void
   1023 dolastlog(quiet)
   1024 	int quiet;
   1025 {
   1026 	struct lastlog ll;
   1027 	int fd;
   1028 
   1029 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
   1030 		(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
   1031 		if (!quiet) {
   1032 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
   1033 			    ll.ll_time != 0) {
   1034 				(void)printf("Last login: %.24s ",
   1035 				    ctime(&ll.ll_time));
   1036 				if (*ll.ll_host != '\0')
   1037 					(void)printf("from %.*s ",
   1038 					    (int)sizeof(ll.ll_host),
   1039 					    ll.ll_host);
   1040 				(void)printf("on %.*s\n",
   1041 				    (int)sizeof(ll.ll_line), ll.ll_line);
   1042 			}
   1043 			(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
   1044 			    SEEK_SET);
   1045 		}
   1046 		memset((void *)&ll, 0, sizeof(ll));
   1047 		ll.ll_time = now.tv_sec;
   1048 		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
   1049 		if (hostname)
   1050 			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
   1051 		(void)write(fd, (char *)&ll, sizeof(ll));
   1052 		(void)close(fd);
   1053 	}
   1054 }
   1055 #endif
   1056 
   1057 void
   1058 badlogin(name)
   1059 	char *name;
   1060 {
   1061 
   1062 	if (failures == 0)
   1063 		return;
   1064 	if (hostname) {
   1065 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
   1066 		    failures, failures > 1 ? "S" : "", hostname);
   1067 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
   1068 		    "%d LOGIN FAILURE%s FROM %s, %s",
   1069 		    failures, failures > 1 ? "S" : "", hostname, name);
   1070 	} else {
   1071 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
   1072 		    failures, failures > 1 ? "S" : "", tty);
   1073 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
   1074 		    "%d LOGIN FAILURE%s ON %s, %s",
   1075 		    failures, failures > 1 ? "S" : "", tty, name);
   1076 	}
   1077 }
   1078 
   1079 const char *
   1080 stypeof(ttyid)
   1081 	const char *ttyid;
   1082 {
   1083 	struct ttyent *t;
   1084 
   1085 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
   1086 }
   1087 
   1088 void
   1089 sleepexit(eval)
   1090 	int eval;
   1091 {
   1092 
   1093 	(void)sleep(5);
   1094 	exit(eval);
   1095 }
   1096