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