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