Home | History | Annotate | Line # | Download | only in login
login.c revision 1.54
      1 /*     $NetBSD: login.c,v 1.54 2000/02/14 03:21:02 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.54 2000/02/14 03:21:02 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 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 
    551 	/* Destroy environment unless user has requested its preservation. */
    552 	if (!pflag)
    553 		environ = envinit;
    554 
    555 #ifdef LOGIN_CAP
    556 	if (setusercontext(lc, pwd, pwd->pw_uid,
    557 	    LOGIN_SETALL & ~LOGIN_SETPATH) != 0) {
    558 		syslog(LOG_ERR, "setusercontext failed");
    559 		exit(1);
    560 	}
    561 #else
    562 	(void)setgid(pwd->pw_gid);
    563 
    564 	initgroups(username, pwd->pw_gid);
    565 
    566 	if (setlogin(pwd->pw_name) < 0)
    567 		syslog(LOG_ERR, "setlogin() failure: %m");
    568 
    569 	/* Discard permissions last so can't get killed and drop core. */
    570 	if (rootlogin)
    571 		(void)setuid(0);
    572 	else
    573 		(void)setuid(pwd->pw_uid);
    574 #endif
    575 
    576 	if (*pwd->pw_shell == '\0')
    577 		pwd->pw_shell = _PATH_BSHELL;
    578 #ifdef LOGIN_CAP
    579 	if((shell = login_getcapstr(lc, "shell", NULL, NULL))) {
    580 		if(!(shell = strdup(shell))) {
    581                 	syslog(LOG_NOTICE, "Cannot alloc mem");
    582                 	sleepexit(1);
    583 		}
    584 		pwd->pw_shell = shell;
    585 	}
    586 #endif
    587 
    588 	(void)setenv("HOME", pwd->pw_dir, 1);
    589 	(void)setenv("SHELL", pwd->pw_shell, 1);
    590 	if (term[0] == '\0') {
    591 		char *tt = (char *) stypeof(tty);
    592 #ifdef LOGIN_CAP
    593 		if(!tt)
    594 			tt = login_getcapstr(lc, "term", NULL, NULL);
    595 #endif
    596 		/* unknown term -> "su" */
    597 		(void)strncpy(term, tt ? tt : "su", sizeof(term));
    598 		}
    599 	(void)setenv("TERM", term, 0);
    600 	(void)setenv("LOGNAME", pwd->pw_name, 1);
    601 	(void)setenv("USER", pwd->pw_name, 1);
    602 
    603 #ifdef LOGIN_CAP
    604 	setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
    605 #else
    606 	(void)setenv("PATH", _PATH_DEFPATH, 0);
    607 #endif
    608 
    609 #ifdef KERBEROS
    610 	if (krbtkfile_env)
    611 		(void)setenv("KRBTKFILE", krbtkfile_env, 1);
    612 #endif
    613 #ifdef KERBEROS5
    614 	if (krbtkfile_env)
    615 		(void)setenv("KRB5CCNAME", krbtkfile_env, 1);
    616 #endif
    617 
    618 	if (tty[sizeof("tty")-1] == 'd')
    619 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
    620 
    621 	/* If fflag is on, assume caller/authenticator has logged root login. */
    622 	if (rootlogin && fflag == 0) {
    623 		if (hostname)
    624 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
    625 			    username, tty, hostname);
    626 		else
    627 			syslog(LOG_NOTICE,
    628 				"ROOT LOGIN (%s) ON %s", username, tty);
    629 	}
    630 
    631 #if defined(KERBEROS) || defined(KERBEROS5)
    632 	if (!quietlog && notickets == 1)
    633 		(void)printf("Warning: no Kerberos tickets issued.\n");
    634 #endif
    635 
    636 	if (!quietlog) {
    637 		char *fname;
    638 #ifdef LOGIN_CAP
    639 		fname = login_getcapstr(lc, "copyright", NULL, NULL);
    640 		if (fname && access(fname, F_OK) == 0)
    641 			motd(fname);
    642 		else
    643 			(void)printf(copyrightstr);
    644 #else
    645 		(void)printf(copyrightstr);
    646 #endif
    647 
    648 #ifdef LOGIN_CAP
    649                 fname = login_getcapstr(lc, "welcome", NULL, NULL);
    650                 if (fname == NULL || access(fname, F_OK) != 0)
    651 #endif
    652                         fname = _PATH_MOTDFILE;
    653                 motd(fname);
    654 
    655 		(void)snprintf(tbuf,
    656 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
    657 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
    658 			(void)printf("You have %smail.\n",
    659 			    (st.st_mtime > st.st_atime) ? "new " : "");
    660 	}
    661 
    662 #ifdef LOGIN_CAP
    663 	login_close(lc);
    664 #endif
    665 
    666 	(void)signal(SIGALRM, SIG_DFL);
    667 	(void)signal(SIGQUIT, SIG_DFL);
    668 	(void)signal(SIGINT, SIG_DFL);
    669 	(void)signal(SIGTSTP, SIG_IGN);
    670 
    671 	tbuf[0] = '-';
    672 	(void)strncpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
    673 	    p + 1 : pwd->pw_shell, sizeof(tbuf) - 2);
    674 
    675 	/* Wait to change password until we're unprivileged */
    676 	if (need_chpass) {
    677 		if (!require_chpass)
    678 			(void)printf(
    679 "Warning: your password has expired. Please change it as soon as possible.\n");
    680 		else {
    681 			int	status;
    682 
    683 			(void)printf(
    684 		    "Your password has expired. Please choose a new one.\n");
    685 			switch (fork()) {
    686 			case -1:
    687 				warn("fork");
    688 				sleepexit(1);
    689 			case 0:
    690 				execl(_PATH_BINPASSWD, "passwd", 0);
    691 				_exit(1);
    692 			default:
    693 				if (wait(&status) == -1 ||
    694 				    WEXITSTATUS(status))
    695 					sleepexit(1);
    696 			}
    697 		}
    698 	}
    699 
    700 #ifdef KERBEROS5
    701 	k5_write_creds();
    702 #endif
    703 	execlp(pwd->pw_shell, tbuf, 0);
    704 	err(1, "%s", pwd->pw_shell);
    705 }
    706 
    707 #if defined(KERBEROS) || defined(KERBEROS5)
    708 #define	NBUFSIZ		(MAXLOGNAME + 1 + 5)	/* .root suffix */
    709 #else
    710 #define	NBUFSIZ		(MAXLOGNAME + 1)
    711 #endif
    712 
    713 #if defined(KERBEROS) || defined(KERBEROS5)
    714 /*
    715  * This routine handles cleanup stuff, and the like.
    716  * It exists only in the child process.
    717  */
    718 #include <sys/wait.h>
    719 void
    720 dofork()
    721 {
    722 	int child;
    723 
    724 	if (!(child = fork()))
    725 		return; /* Child process */
    726 
    727 	/* Setup stuff?  This would be things we could do in parallel with login */
    728 	(void) chdir("/");	/* Let's not keep the fs busy... */
    729 
    730 	/* If we're the parent, watch the child until it dies */
    731 	while (wait(0) != child)
    732 		;
    733 
    734 	/* Cleanup stuff */
    735 	/* Run kdestroy to destroy tickets */
    736 	kdestroy();
    737 
    738 	/* Leave */
    739 	exit(0);
    740 }
    741 #endif
    742 
    743 void
    744 getloginname()
    745 {
    746 	int ch;
    747 	char *p;
    748 	static char nbuf[NBUFSIZ];
    749 
    750 	for (;;) {
    751 		(void)printf("login: ");
    752 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
    753 			if (ch == EOF) {
    754 				badlogin(username);
    755 				exit(0);
    756 			}
    757 			if (p < nbuf + (NBUFSIZ - 1))
    758 				*p++ = ch;
    759 		}
    760 		if (p > nbuf) {
    761 			if (nbuf[0] == '-')
    762 				(void)fprintf(stderr,
    763 				    "login names may not start with '-'.\n");
    764 			else {
    765 				*p = '\0';
    766 				username = nbuf;
    767 				break;
    768 			}
    769 		}
    770 	}
    771 }
    772 
    773 int
    774 rootterm(ttyn)
    775 	char *ttyn;
    776 {
    777 	struct ttyent *t;
    778 
    779 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
    780 }
    781 
    782 jmp_buf motdinterrupt;
    783 
    784 void
    785 motd(fname)
    786 	char *fname;
    787 {
    788 	int fd, nchars;
    789 	sig_t oldint;
    790 	char tbuf[8192];
    791 
    792 	if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
    793 		return;
    794 	oldint = signal(SIGINT, sigint);
    795 	if (setjmp(motdinterrupt) == 0)
    796 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    797 			(void)write(fileno(stdout), tbuf, nchars);
    798 	(void)signal(SIGINT, oldint);
    799 	(void)close(fd);
    800 }
    801 
    802 /* ARGSUSED */
    803 void
    804 sigint(signo)
    805 	int signo;
    806 {
    807 
    808 	longjmp(motdinterrupt, 1);
    809 }
    810 
    811 /* ARGSUSED */
    812 void
    813 timedout(signo)
    814 	int signo;
    815 {
    816 
    817 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
    818 	exit(0);
    819 }
    820 
    821 void
    822 checknologin(fname)
    823 	char *fname;
    824 {
    825 	int fd, nchars;
    826 	char tbuf[8192];
    827 
    828 	if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
    829 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    830 			(void)write(fileno(stdout), tbuf, nchars);
    831 		sleepexit(0);
    832 	}
    833 }
    834 
    835 void
    836 dolastlog(quiet)
    837 	int quiet;
    838 {
    839 	struct lastlog ll;
    840 	int fd;
    841 
    842 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
    843 		(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
    844 		if (!quiet) {
    845 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
    846 			    ll.ll_time != 0) {
    847 				(void)printf("Last login: %.*s ",
    848 				    24, (char *)ctime(&ll.ll_time));
    849 				if (*ll.ll_host != '\0')
    850 					(void)printf("from %.*s\n",
    851 					    (int)sizeof(ll.ll_host),
    852 					    ll.ll_host);
    853 				else
    854 					(void)printf("on %.*s\n",
    855 					    (int)sizeof(ll.ll_line),
    856 					    ll.ll_line);
    857 			}
    858 			(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
    859 			    SEEK_SET);
    860 		}
    861 		memset((void *)&ll, 0, sizeof(ll));
    862 		(void)time(&ll.ll_time);
    863 		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
    864 		if (hostname)
    865 			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
    866 		(void)write(fd, (char *)&ll, sizeof(ll));
    867 		(void)close(fd);
    868 	}
    869 }
    870 
    871 void
    872 badlogin(name)
    873 	char *name;
    874 {
    875 	if (failures == 0)
    876 		return;
    877 	if (hostname) {
    878 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
    879 		    failures, failures > 1 ? "S" : "", hostname);
    880 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    881 		    "%d LOGIN FAILURE%s FROM %s, %s",
    882 		    failures, failures > 1 ? "S" : "", hostname, name);
    883 	} else {
    884 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
    885 		    failures, failures > 1 ? "S" : "", tty);
    886 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    887 		    "%d LOGIN FAILURE%s ON %s, %s",
    888 		    failures, failures > 1 ? "S" : "", tty, name);
    889 	}
    890 }
    891 
    892 const char *
    893 stypeof(ttyid)
    894 	const char *ttyid;
    895 {
    896 	struct ttyent *t;
    897 
    898 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
    899 }
    900 
    901 void
    902 sleepexit(eval)
    903 	int eval;
    904 {
    905 
    906 	(void)sleep(5);
    907 	exit(eval);
    908 }
    909