Home | History | Annotate | Line # | Download | only in login
login.c revision 1.67
      1 /*     $NetBSD: login.c,v 1.67 2002/07/27 22:58:07 christos 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.67 2002/07/27 22:58:07 christos 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;
    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\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 	need_chpass = require_chpass = 0;
    221 
    222 	(void)signal(SIGALRM, timedout);
    223 	(void)alarm(timeout);
    224 	(void)signal(SIGQUIT, SIG_IGN);
    225 	(void)signal(SIGINT, SIG_IGN);
    226 	(void)setpriority(PRIO_PROCESS, 0, 0);
    227 
    228 	openlog("login", 0, LOG_AUTH);
    229 
    230 	/*
    231 	 * -p is used by getty to tell login not to destroy the environment
    232 	 * -f is used to skip a second login authentication
    233 	 * -h is used by other servers to pass the name of the remote host to
    234 	 *    login so that it may be placed in utmp/utmpx and wtmp/wtmpx
    235 	 * -s is used to force use of S/Key or equivalent.
    236 	 */
    237 	domain = NULL;
    238 	if (gethostname(localhost, sizeof(localhost)) < 0)
    239 		syslog(LOG_ERR, "couldn't get local hostname: %m");
    240 	else
    241 		domain = strchr(localhost, '.');
    242 	localhost[sizeof(localhost) - 1] = '\0';
    243 
    244 	Fflag = fflag = hflag = pflag = sflag = 0;
    245 #ifdef KERBEROS5
    246 	have_forward = 0;
    247 #endif
    248 	uid = getuid();
    249 	while ((ch = getopt(argc, argv, "Ffh:ps")) != -1)
    250 		switch (ch) {
    251 		case 'F':
    252 			Fflag = 1;
    253 			/* FALLTHROUGH */
    254 		case 'f':
    255 			fflag = 1;
    256 			break;
    257 		case 'h':
    258 			if (uid)
    259 				errx(1, "-h option: %s", strerror(EPERM));
    260 			hflag = 1;
    261 			if (domain && (p = strchr(optarg, '.')) != NULL &&
    262 			    strcasecmp(p, domain) == 0)
    263 				*p = 0;
    264 			hostname = optarg;
    265 			break;
    266 		case 'p':
    267 			pflag = 1;
    268 			break;
    269 		case 's':
    270 			sflag = 1;
    271 			break;
    272 		default:
    273 		case '?':
    274 			(void)fprintf(stderr,
    275 			    "usage: login [-fps] [-h hostname] [username]\n");
    276 			exit(1);
    277 		}
    278 	argc -= optind;
    279 	argv += optind;
    280 
    281 	if (*argv) {
    282 		username = *argv;
    283 		ask = 0;
    284 	} else
    285 		ask = 1;
    286 
    287 	for (cnt = getdtablesize(); cnt > 2; cnt--)
    288 		(void)close(cnt);
    289 
    290 	ttyn = ttyname(STDIN_FILENO);
    291 	if (ttyn == NULL || *ttyn == '\0') {
    292 		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
    293 		ttyn = tname;
    294 	}
    295 	if ((tty = strrchr(ttyn, '/')) != NULL)
    296 		++tty;
    297 	else
    298 		tty = ttyn;
    299 
    300 #ifdef LOGIN_CAP
    301 	/* Get "login-retries" and "login-backoff" from default class */
    302 	if ((lc = login_getclass(NULL)) != NULL) {
    303 		login_retries = (int)login_getcapnum(lc, "login-retries",
    304 		    DEFAULT_RETRIES, DEFAULT_RETRIES);
    305 		login_backoff = (int)login_getcapnum(lc, "login-backoff",
    306 		    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
    307 		login_close(lc);
    308 		lc = NULL;
    309 	}
    310 #endif
    311 
    312 #ifdef KERBEROS5
    313 	kerror = krb5_init_context(&kcontext);
    314 	if (kerror) {
    315 		/*
    316 		 * If Kerberos is not configured, that is, we are
    317 		 * not using Kerberos, do not log the error message.
    318 		 * However, if Kerberos is configured,  and the
    319 		 * context init fails for some other reason, we need
    320 		 * to issue a no tickets warning to the user when the
    321 		 * login succeeds.
    322 		 */
    323 		if (kerror != ENXIO) {	/* XXX NetBSD-local Heimdal hack */
    324 			syslog(LOG_NOTICE,
    325 			    "%s when initializing Kerberos context",
    326 			    error_message(kerror));
    327 			krb5_configured = 1;
    328 		}
    329 		login_krb5_get_tickets = 0;
    330 	}
    331 #endif /* KERBEROS5 */
    332 
    333 	for (cnt = 0;; ask = 1) {
    334 #if defined(KERBEROS)
    335 	        kdestroy();
    336 #endif
    337 #if defined(KERBEROS5)
    338 		if (login_krb5_get_tickets)
    339 			k5destroy();
    340 #endif
    341 		if (ask) {
    342 			fflag = 0;
    343 			getloginname();
    344 		}
    345 		rootlogin = 0;
    346 #ifdef KERBEROS
    347 		if ((instance = strchr(username, '.')) != NULL)
    348 			*instance++ = '\0';
    349 		else
    350 			instance = "";
    351 #endif
    352 #ifdef KERBEROS5
    353 		if ((instance = strchr(username, '/')) != NULL)
    354 			*instance++ = '\0';
    355 		else
    356 			instance = "";
    357 #endif
    358 		if (strlen(username) > MAXLOGNAME)
    359 			username[MAXLOGNAME] = '\0';
    360 
    361 		/*
    362 		 * Note if trying multiple user names; log failures for
    363 		 * previous user name, but don't bother logging one failure
    364 		 * for nonexistent name (mistyped username).
    365 		 */
    366 		if (failures && strcmp(tbuf, username)) {
    367 			if (failures > (pwd ? 0 : 1))
    368 				badlogin(tbuf);
    369 			failures = 0;
    370 		}
    371 		(void)strncpy(tbuf, username, sizeof(tbuf) - 1);
    372 		tbuf[sizeof(tbuf) - 1] = '\0';
    373 
    374 		if ((pwd = getpwnam(username)) != NULL)
    375 			salt = pwd->pw_passwd;
    376 		else
    377 			salt = "xx";
    378 
    379 #ifdef LOGIN_CAP
    380 		/*
    381 		 * Establish the class now, before we might goto
    382 		 * within the next block. pwd can be NULL since it
    383 		 * falls back to the "default" class if it is.
    384 		 */
    385 		lc = login_getclass(pwd ? pwd->pw_class : NULL);
    386 #endif
    387 		/*
    388 		 * if we have a valid account name, and it doesn't have a
    389 		 * password, or the -f option was specified and the caller
    390 		 * is root or the caller isn't changing their uid, don't
    391 		 * authenticate.
    392 		 */
    393 		if (pwd) {
    394 			if (pwd->pw_uid == 0)
    395 				rootlogin = 1;
    396 
    397 			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
    398 				/* already authenticated */
    399 #ifdef KERBEROS5
    400 				if (login_krb5_get_tickets && Fflag)
    401 					k5_read_creds(username);
    402 #endif
    403 				break;
    404 			} else if (pwd->pw_passwd[0] == '\0') {
    405 				/* pretend password okay */
    406 				rval = 0;
    407 				goto ttycheck;
    408 			}
    409 		}
    410 
    411 		fflag = 0;
    412 
    413 		(void)setpriority(PRIO_PROCESS, 0, -4);
    414 
    415 #ifdef SKEY
    416 		if (skey_haskey(username) == 0) {
    417 			static char skprompt[80];
    418 			const char *skinfo = skey_keyinfo(username);
    419 
    420 			(void)snprintf(skprompt, sizeof(skprompt)-1,
    421 			    "Password [%s]:",
    422 			    skinfo ? skinfo : "error getting challenge");
    423 			pwprompt = skprompt;
    424 		} else
    425 #endif
    426 			pwprompt = "Password:";
    427 
    428 		p = getpass(pwprompt);
    429 
    430 		if (pwd == NULL) {
    431 			rval = 1;
    432 			goto skip;
    433 		}
    434 #ifdef KERBEROS
    435 		if (
    436 #ifdef KERBEROS5
    437 		    /* allow a user to get both krb4 and krb5 tickets, if
    438 		     * desired.  If krb5 is compiled in, the default action
    439 		     * is to ignore krb4 and get krb5 tickets, but the user
    440 		     * can override this in the krb5.conf. */
    441 		    login_krb4_get_tickets &&
    442 #endif
    443 		    klogin(pwd, instance, localhost, p) == 0) {
    444 			rval = 0;
    445 			got_tickets = 1;
    446 		}
    447 #endif
    448 #ifdef KERBEROS5
    449 		if (login_krb5_get_tickets &&
    450 		    k5login(pwd, instance, localhost, p) == 0) {
    451 			rval = 0;
    452 			got_tickets = 1;
    453 		}
    454 #endif
    455 #if defined(KERBEROS) || defined(KERBEROS5)
    456 		if (got_tickets)
    457 			goto skip;
    458 #endif
    459 #ifdef SKEY
    460 		if (skey_haskey(username) == 0 &&
    461 		    skey_passcheck(username, p) != -1) {
    462 			rval = 0;
    463 			goto skip;
    464 		}
    465 #endif
    466 		if (!sflag && *pwd->pw_passwd != '\0' &&
    467 		    !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
    468 			rval = 0;
    469 			require_chpass = 1;
    470 			goto skip;
    471 		}
    472 		rval = 1;
    473 
    474 	skip:
    475 		memset(p, 0, strlen(p));
    476 
    477 		(void)setpriority(PRIO_PROCESS, 0, 0);
    478 
    479 	ttycheck:
    480 		/*
    481 		 * If trying to log in as root without Kerberos,
    482 		 * but with insecure terminal, refuse the login attempt.
    483 		 */
    484 		if (pwd && !rval && rootlogin && !rootterm(tty)) {
    485 			(void)fprintf(stderr,
    486 			    "%s login refused on this terminal.\n",
    487 			    pwd->pw_name);
    488 			if (hostname)
    489 				syslog(LOG_NOTICE,
    490 				    "LOGIN %s REFUSED FROM %s ON TTY %s",
    491 				    pwd->pw_name, hostname, tty);
    492 			else
    493 				syslog(LOG_NOTICE,
    494 				    "LOGIN %s REFUSED ON TTY %s",
    495 				     pwd->pw_name, tty);
    496 			continue;
    497 		}
    498 
    499 		if (pwd && !rval)
    500 			break;
    501 
    502 		(void)printf("Login incorrect\n");
    503 		failures++;
    504 		cnt++;
    505 		/* we allow 10 tries, but after 3 we start backing off */
    506 		if (cnt > login_backoff) {
    507 			if (cnt >= login_retries) {
    508 				badlogin(username);
    509 				sleepexit(1);
    510 			}
    511 			sleep((u_int)((cnt - 3) * 5));
    512 		}
    513 	}
    514 
    515 	/* committed to login -- turn off timeout */
    516 	(void)alarm((u_int)0);
    517 
    518 	endpwent();
    519 
    520 	/* if user not super-user, check for disabled logins */
    521 #ifdef LOGIN_CAP
    522         if (!login_getcapbool(lc, "ignorenologin", rootlogin))
    523 		checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
    524 #else
    525         if (!rootlogin)
    526                 checknologin(NULL);
    527 #endif
    528 
    529 #ifdef LOGIN_CAP
    530         quietlog = login_getcapbool(lc, "hushlogin", 0);
    531 #else
    532         quietlog = 0;
    533 #endif
    534 	/* Temporarily give up special privileges so we can change */
    535 	/* into NFS-mounted homes that are exported for non-root */
    536 	/* access and have mode 7x0 */
    537 	saved_uid = geteuid();
    538 	saved_gid = getegid();
    539 	nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
    540 
    541 	(void)setegid(pwd->pw_gid);
    542 	initgroups(username, pwd->pw_gid);
    543 	(void)seteuid(pwd->pw_uid);
    544 
    545 	if (chdir(pwd->pw_dir) < 0) {
    546 #ifdef LOGIN_CAP
    547                 if (login_getcapbool(lc, "requirehome", 0)) {
    548 			(void)printf("Home directory %s required\n",
    549 			    pwd->pw_dir);
    550                         sleepexit(1);
    551 		}
    552 #endif
    553 		(void)printf("No home directory %s!\n", pwd->pw_dir);
    554 		if (chdir("/"))
    555 			exit(0);
    556 		pwd->pw_dir = "/";
    557 		(void)printf("Logging in with home = \"/\".\n");
    558 	}
    559 
    560 	if (!quietlog)
    561 		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
    562 
    563 	/* regain special privileges */
    564 	(void)seteuid(saved_uid);
    565 	setgroups(nsaved_gids, saved_gids);
    566 	(void)setegid(saved_gid);
    567 
    568 #ifdef LOGIN_CAP
    569         pw_warntime = login_getcaptime(lc, "password-warn",
    570             _PASSWORD_WARNDAYS * SECSPERDAY,
    571             _PASSWORD_WARNDAYS * SECSPERDAY);
    572 #endif
    573 
    574 	(void)gettimeofday(&now, (struct timezone *)NULL);
    575 	if (pwd->pw_expire) {
    576 		if (now.tv_sec >= pwd->pw_expire) {
    577 			(void)printf("Sorry -- your account has expired.\n");
    578 			sleepexit(1);
    579 		} else if (pwd->pw_expire - now.tv_sec < pw_warntime &&
    580 		    !quietlog)
    581 			(void)printf("Warning: your account expires on %s",
    582 			    ctime(&pwd->pw_expire));
    583 	}
    584 	if (pwd->pw_change) {
    585 		if (pwd->pw_change == _PASSWORD_CHGNOW)
    586 			need_chpass = 1;
    587 		else if (now.tv_sec >= pwd->pw_change) {
    588 			(void)printf("Sorry -- your password has expired.\n");
    589 			sleepexit(1);
    590 		} else if (pwd->pw_change - now.tv_sec < pw_warntime &&
    591 		    !quietlog)
    592 			(void)printf("Warning: your password expires on %s",
    593 			    ctime(&pwd->pw_change));
    594 
    595 	}
    596 	/* Nothing else left to fail -- really log in. */
    597 	update_db(quietlog);
    598 
    599 	(void)chown(ttyn, pwd->pw_uid,
    600 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
    601 
    602 	if (ttyaction(ttyn, "login", pwd->pw_name))
    603 		(void)printf("Warning: ttyaction failed.\n");
    604 
    605 #if defined(KERBEROS) || defined(KERBEROS5)
    606 	/* Fork so that we can call kdestroy */
    607 	if (
    608 #ifdef KERBEROS5
    609 	    ! login_krb5_retain_ccache &&
    610 #endif
    611 	    has_ccache)
    612 		dofork();
    613 #endif
    614 
    615 	/* Destroy environment unless user has requested its preservation. */
    616 	if (!pflag)
    617 		environ = envinit;
    618 
    619 #ifdef LOGIN_CAP
    620 	if (setusercontext(lc, pwd, pwd->pw_uid,
    621 	    LOGIN_SETALL & ~LOGIN_SETPATH) != 0) {
    622 		syslog(LOG_ERR, "setusercontext failed");
    623 		exit(1);
    624 	}
    625 #else
    626 	(void)setgid(pwd->pw_gid);
    627 
    628 	initgroups(username, pwd->pw_gid);
    629 
    630 	if (setlogin(pwd->pw_name) < 0)
    631 		syslog(LOG_ERR, "setlogin() failure: %m");
    632 
    633 	/* Discard permissions last so can't get killed and drop core. */
    634 	if (rootlogin)
    635 		(void)setuid(0);
    636 	else
    637 		(void)setuid(pwd->pw_uid);
    638 #endif
    639 
    640 	if (*pwd->pw_shell == '\0')
    641 		pwd->pw_shell = _PATH_BSHELL;
    642 #ifdef LOGIN_CAP
    643 	if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
    644 		if ((shell = strdup(shell)) == NULL) {
    645                 	syslog(LOG_ERR, "Cannot alloc mem");
    646                 	sleepexit(1);
    647 		}
    648 		pwd->pw_shell = shell;
    649 	}
    650 #endif
    651 
    652 	(void)setenv("HOME", pwd->pw_dir, 1);
    653 	(void)setenv("SHELL", pwd->pw_shell, 1);
    654 	if (term[0] == '\0') {
    655 		char *tt = (char *)stypeof(tty);
    656 #ifdef LOGIN_CAP
    657 		if (tt == NULL)
    658 			tt = login_getcapstr(lc, "term", NULL, NULL);
    659 #endif
    660 		/* unknown term -> "su" */
    661 		(void)strncpy(term, tt != NULL ? tt : "su", sizeof(term));
    662 	}
    663 	(void)setenv("TERM", term, 0);
    664 	(void)setenv("LOGNAME", pwd->pw_name, 1);
    665 	(void)setenv("USER", pwd->pw_name, 1);
    666 
    667 #ifdef LOGIN_CAP
    668 	setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
    669 #else
    670 	(void)setenv("PATH", _PATH_DEFPATH, 0);
    671 #endif
    672 
    673 #ifdef KERBEROS
    674 	if (krbtkfile_env)
    675 		(void)setenv("KRBTKFILE", krbtkfile_env, 1);
    676 #endif
    677 #ifdef KERBEROS5
    678 	if (krb5tkfile_env)
    679 		(void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
    680 #endif
    681 
    682 	if (tty[sizeof("tty")-1] == 'd')
    683 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
    684 
    685 	/* If fflag is on, assume caller/authenticator has logged root login. */
    686 	if (rootlogin && fflag == 0) {
    687 		if (hostname)
    688 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
    689 			    username, tty, hostname);
    690 		else
    691 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
    692 			    username, tty);
    693 	}
    694 
    695 #if defined(KERBEROS) || defined(KERBEROS5)
    696 	if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
    697 		(void)printf("Warning: no Kerberos tickets issued.\n");
    698 #endif
    699 
    700 	if (!quietlog) {
    701 		char *fname;
    702 #ifdef LOGIN_CAP
    703 		fname = login_getcapstr(lc, "copyright", NULL, NULL);
    704 		if (fname != NULL && access(fname, F_OK) == 0)
    705 			motd(fname);
    706 		else
    707 #endif
    708 			(void)printf(copyrightstr);
    709 
    710 #ifdef LOGIN_CAP
    711                 fname = login_getcapstr(lc, "welcome", NULL, NULL);
    712                 if (fname == NULL || access(fname, F_OK) != 0)
    713 #endif
    714                         fname = _PATH_MOTDFILE;
    715                 motd(fname);
    716 
    717 		(void)snprintf(tbuf,
    718 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
    719 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
    720 			(void)printf("You have %smail.\n",
    721 			    (st.st_mtime > st.st_atime) ? "new " : "");
    722 	}
    723 
    724 #ifdef LOGIN_CAP
    725 	login_close(lc);
    726 #endif
    727 
    728 	(void)signal(SIGALRM, SIG_DFL);
    729 	(void)signal(SIGQUIT, SIG_DFL);
    730 	(void)signal(SIGINT, SIG_DFL);
    731 	(void)signal(SIGTSTP, SIG_IGN);
    732 
    733 	tbuf[0] = '-';
    734 	(void)strncpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
    735 	    p + 1 : pwd->pw_shell, sizeof(tbuf) - 2);
    736 
    737 	/* Wait to change password until we're unprivileged */
    738 	if (need_chpass) {
    739 		if (!require_chpass)
    740 			(void)printf(
    741 "Warning: your password has expired. Please change it as soon as possible.\n");
    742 		else {
    743 			int	status;
    744 
    745 			(void)printf(
    746 		    "Your password has expired. Please choose a new one.\n");
    747 			switch (fork()) {
    748 			case -1:
    749 				warn("fork");
    750 				sleepexit(1);
    751 			case 0:
    752 				execl(_PATH_BINPASSWD, "passwd", 0);
    753 				_exit(1);
    754 			default:
    755 				if (wait(&status) == -1 ||
    756 				    WEXITSTATUS(status))
    757 					sleepexit(1);
    758 			}
    759 		}
    760 	}
    761 
    762 #ifdef KERBEROS5
    763 	if (login_krb5_get_tickets)
    764 		k5_write_creds();
    765 #endif
    766 	execlp(pwd->pw_shell, tbuf, 0);
    767 	err(1, "%s", pwd->pw_shell);
    768 }
    769 
    770 #if defined(KERBEROS) || defined(KERBEROS5)
    771 #define	NBUFSIZ		(MAXLOGNAME + 1 + 5)	/* .root suffix */
    772 #else
    773 #define	NBUFSIZ		(MAXLOGNAME + 1)
    774 #endif
    775 
    776 #if defined(KERBEROS) || defined(KERBEROS5)
    777 /*
    778  * This routine handles cleanup stuff, and the like.
    779  * It exists only in the child process.
    780  */
    781 #include <sys/wait.h>
    782 void
    783 dofork()
    784 {
    785 	int child;
    786 
    787 	if (!(child = fork()))
    788 		return; /* Child process */
    789 
    790 	/*
    791 	 * Setup stuff?  This would be things we could do in parallel
    792 	 * with login
    793 	 */
    794 	(void)chdir("/");	/* Let's not keep the fs busy... */
    795 
    796 	/* If we're the parent, watch the child until it dies */
    797 	while (wait(0) != child)
    798 		;
    799 
    800 	/* Cleanup stuff */
    801 	/* Run kdestroy to destroy tickets */
    802 #ifdef KERBEROS
    803 	kdestroy();
    804 #endif
    805 #ifdef KERBEROS5
    806 	if (login_krb5_get_tickets)
    807 		k5destroy();
    808 #endif
    809 
    810 	/* Leave */
    811 	exit(0);
    812 }
    813 #endif
    814 
    815 void
    816 getloginname()
    817 {
    818 	int ch;
    819 	char *p;
    820 	static char nbuf[NBUFSIZ];
    821 
    822 	for (;;) {
    823 		(void)printf("login: ");
    824 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
    825 			if (ch == EOF) {
    826 				badlogin(username);
    827 				exit(0);
    828 			}
    829 			if (p < nbuf + (NBUFSIZ - 1))
    830 				*p++ = ch;
    831 		}
    832 		if (p > nbuf) {
    833 			if (nbuf[0] == '-')
    834 				(void)fprintf(stderr,
    835 				    "login names may not start with '-'.\n");
    836 			else {
    837 				*p = '\0';
    838 				username = nbuf;
    839 				break;
    840 			}
    841 		}
    842 	}
    843 }
    844 
    845 int
    846 rootterm(ttyn)
    847 	char *ttyn;
    848 {
    849 	struct ttyent *t;
    850 
    851 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
    852 }
    853 
    854 jmp_buf motdinterrupt;
    855 
    856 void
    857 motd(fname)
    858 	char *fname;
    859 {
    860 	int fd, nchars;
    861 	sig_t oldint;
    862 	char tbuf[8192];
    863 
    864 	if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
    865 		return;
    866 	oldint = signal(SIGINT, sigint);
    867 	if (setjmp(motdinterrupt) == 0)
    868 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    869 			(void)write(fileno(stdout), tbuf, nchars);
    870 	(void)signal(SIGINT, oldint);
    871 	(void)close(fd);
    872 }
    873 
    874 /* ARGSUSED */
    875 void
    876 sigint(signo)
    877 	int signo;
    878 {
    879 
    880 	longjmp(motdinterrupt, 1);
    881 }
    882 
    883 /* ARGSUSED */
    884 void
    885 timedout(signo)
    886 	int signo;
    887 {
    888 
    889 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
    890 	exit(0);
    891 }
    892 
    893 void
    894 checknologin(fname)
    895 	char *fname;
    896 {
    897 	int fd, nchars;
    898 	char tbuf[8192];
    899 
    900 	if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
    901 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    902 			(void)write(fileno(stdout), tbuf, nchars);
    903 		sleepexit(0);
    904 	}
    905 }
    906 
    907 static void
    908 update_db(int quietlog)
    909 {
    910 	if (hostname != NULL) {
    911 		socklen_t len = sizeof(ss);
    912 		(void)getpeername(STDIN_FILENO, (struct sockaddr *)&ss, &len);
    913 	}
    914 	printf("from host %s\n", hostname);
    915 	(void)gettimeofday(&now, NULL);
    916 #ifdef SUPPORT_UTMPX
    917 	doutmpx();
    918 	dolastlogx(quietlog);
    919 	quietlog = 1;
    920 #endif
    921 #ifdef SUPPORT_UTMP
    922 	doutmp();
    923 	dolastlog(quietlog);
    924 #endif
    925 }
    926 
    927 #ifdef SUPPORT_UTMPX
    928 static void
    929 doutmpx()
    930 {
    931 	struct utmpx utmpx;
    932 	char *t;
    933 
    934 	memset((void *)&utmpx, 0, sizeof(utmpx));
    935 	utmpx.ut_tv = now;
    936 	(void)strncpy(utmpx.ut_name, username, sizeof(utmpx.ut_name));
    937 	if (hostname) {
    938 		(void)strncpy(utmpx.ut_host, hostname, sizeof(utmpx.ut_host));
    939 		utmpx.ut_ss = ss;
    940 	}
    941 	(void)strncpy(utmpx.ut_line, tty, sizeof(utmpx.ut_line));
    942 	utmpx.ut_type = USER_PROCESS;
    943 	utmpx.ut_pid = getpid();
    944 	t = tty + strlen(tty);
    945 	if (t - tty >= sizeof(utmpx.ut_id)) {
    946 	    (void)strncpy(utmpx.ut_id, t - sizeof(utmpx.ut_id),
    947 		sizeof(utmpx.ut_id));
    948 	} else {
    949 	    (void)strncpy(utmpx.ut_id, tty, sizeof(utmpx.ut_id));
    950 	}
    951 	if (pututxline(&utmpx) == NULL)
    952 		syslog(LOG_NOTICE, "Cannot update utmpx %m");
    953 	endutxent();
    954 	if (updwtmpx(_PATH_WTMPX, &utmpx) != 0)
    955 		syslog(LOG_NOTICE, "Cannot update wtmpx %m");
    956 }
    957 
    958 static void
    959 dolastlogx(quiet)
    960 	int quiet;
    961 {
    962 	struct lastlogx ll;
    963 	if (getlastlogx(pwd->pw_uid, &ll) != NULL) {
    964 		time_t t = (time_t)ll.ll_tv.tv_sec;
    965 		(void)printf("Last login: %.24s ", ctime(&t));
    966 		if (*ll.ll_host != '\0')
    967 			(void)printf("from %.*s ",
    968 			    (int)sizeof(ll.ll_host),
    969 			    ll.ll_host);
    970 		(void)printf("on %.*s\n",
    971 		    (int)sizeof(ll.ll_line),
    972 		    ll.ll_line);
    973 	}
    974 	ll.ll_tv = now;
    975 	(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
    976 	if (hostname) {
    977 		(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
    978 		ll.ll_ss = ss;
    979 	}
    980 	if (updlastlogx(_PATH_LASTLOGX, pwd->pw_uid, &ll) != 0)
    981 		syslog(LOG_NOTICE, "Cannot update lastlogx %m");
    982 }
    983 #endif
    984 
    985 #ifdef SUPPORT_UTMP
    986 static void
    987 doutmp()
    988 {
    989 	struct utmp utmp;
    990 
    991 	(void)memset((void *)&utmp, 0, sizeof(utmp));
    992 	utmp.ut_time = now.tv_sec;
    993 	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
    994 	if (hostname)
    995 		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
    996 	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
    997 	login(&utmp);
    998 }
    999 
   1000 static void
   1001 dolastlog(quiet)
   1002 	int quiet;
   1003 {
   1004 	struct lastlog ll;
   1005 	int fd;
   1006 
   1007 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
   1008 		(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
   1009 		if (!quiet) {
   1010 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
   1011 			    ll.ll_time != 0) {
   1012 				(void)printf("Last login: %.24s ",
   1013 				    ctime(&ll.ll_time));
   1014 				if (*ll.ll_host != '\0')
   1015 					(void)printf("from %.*s ",
   1016 					    (int)sizeof(ll.ll_host),
   1017 					    ll.ll_host);
   1018 				(void)printf("on %.*s\n",
   1019 				    (int)sizeof(ll.ll_line), ll.ll_line);
   1020 			}
   1021 			(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
   1022 			    SEEK_SET);
   1023 		}
   1024 		memset((void *)&ll, 0, sizeof(ll));
   1025 		ll.ll_time = now.tv_sec;
   1026 		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
   1027 		if (hostname)
   1028 			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
   1029 		(void)write(fd, (char *)&ll, sizeof(ll));
   1030 		(void)close(fd);
   1031 	}
   1032 }
   1033 #endif
   1034 
   1035 void
   1036 badlogin(name)
   1037 	char *name;
   1038 {
   1039 
   1040 	if (failures == 0)
   1041 		return;
   1042 	if (hostname) {
   1043 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
   1044 		    failures, failures > 1 ? "S" : "", hostname);
   1045 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
   1046 		    "%d LOGIN FAILURE%s FROM %s, %s",
   1047 		    failures, failures > 1 ? "S" : "", hostname, name);
   1048 	} else {
   1049 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
   1050 		    failures, failures > 1 ? "S" : "", tty);
   1051 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
   1052 		    "%d LOGIN FAILURE%s ON %s, %s",
   1053 		    failures, failures > 1 ? "S" : "", tty, name);
   1054 	}
   1055 }
   1056 
   1057 const char *
   1058 stypeof(ttyid)
   1059 	const char *ttyid;
   1060 {
   1061 	struct ttyent *t;
   1062 
   1063 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
   1064 }
   1065 
   1066 void
   1067 sleepexit(eval)
   1068 	int eval;
   1069 {
   1070 
   1071 	(void)sleep(5);
   1072 	exit(eval);
   1073 }
   1074