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