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