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