Home | History | Annotate | Line # | Download | only in login
login.c revision 1.48
      1 /*     $NetBSD: login.c,v 1.48 2000/01/13 06:17:56 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.48 2000/01/13 06:17:56 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 10
    116 #define DEFAULT_RETRIES 3
    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 	char *shell = NULL;
    169 #ifdef KERBEROS5
    170 	krb5_error_code kerror;
    171 #endif
    172 #ifdef LOGIN_CAP
    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 		if (pwd != NULL)
    331                 	lc = login_getclass(pwd->pw_class);
    332 #endif
    333 
    334 		/*
    335 		 * if we have a valid account name, and it doesn't have a
    336 		 * password, or the -f option was specified and the caller
    337 		 * is root or the caller isn't changing their uid, don't
    338 		 * authenticate.
    339 		 */
    340 		if (pwd) {
    341 			if (pwd->pw_uid == 0)
    342 				rootlogin = 1;
    343 
    344 			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
    345 				/* already authenticated */
    346 #ifdef KERBEROS5
    347 				if (Fflag)
    348 					k5_read_creds(username);
    349 #endif
    350 				break;
    351 			} else if (pwd->pw_passwd[0] == '\0') {
    352 				/* pretend password okay */
    353 				rval = 0;
    354 				goto ttycheck;
    355 			}
    356 		}
    357 
    358 		fflag = 0;
    359 
    360 		(void)setpriority(PRIO_PROCESS, 0, -4);
    361 
    362 #ifdef SKEY
    363 		if (skey_haskey(username) == 0) {
    364 			static char skprompt[80];
    365 			char *skinfo = skey_keyinfo(username);
    366 
    367 			(void)snprintf(skprompt, sizeof(skprompt)-1,
    368 			    "Password [%s]:",
    369 			    skinfo ? skinfo : "error getting challenge");
    370 			pwprompt = skprompt;
    371 		} else
    372 #endif
    373 			pwprompt = "Password:";
    374 
    375 		p = getpass(pwprompt);
    376 
    377 		if (pwd == NULL) {
    378 			rval = 1;
    379 			goto skip;
    380 		}
    381 #ifdef KERBEROS
    382 		if (klogin(pwd, instance, localhost, p) == 0) {
    383 			rval = 0;
    384 			goto skip;
    385 		}
    386 #endif
    387 #ifdef KERBEROS5
    388 		if (klogin(pwd, instance, localhost, p) == 0) {
    389 			rval = 0;
    390 			goto skip;
    391 		}
    392 #endif
    393 #ifdef SKEY
    394 		if (skey_haskey(username) == 0 &&
    395 		    skey_passcheck(username, p) != -1) {
    396 			rval = 0;
    397 			goto skip;
    398 		}
    399 #endif
    400 		if (!sflag && *pwd->pw_passwd != '\0' &&
    401 		    !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
    402 			rval = 0;
    403 			require_chpass = 1;
    404 			goto skip;
    405 		}
    406 		rval = 1;
    407 
    408 	skip:
    409 		memset(p, 0, strlen(p));
    410 
    411 		(void)setpriority(PRIO_PROCESS, 0, 0);
    412 
    413 	ttycheck:
    414 		/*
    415 		 * If trying to log in as root without Kerberos,
    416 		 * but with insecure terminal, refuse the login attempt.
    417 		 */
    418 		if (pwd && !rval && rootlogin && !rootterm(tty)) {
    419 			(void)fprintf(stderr,
    420 			    "%s login refused on this terminal.\n",
    421 			    pwd->pw_name);
    422 			if (hostname)
    423 				syslog(LOG_NOTICE,
    424 				    "LOGIN %s REFUSED FROM %s ON TTY %s",
    425 				    pwd->pw_name, hostname, tty);
    426 			else
    427 				syslog(LOG_NOTICE,
    428 				    "LOGIN %s REFUSED ON TTY %s",
    429 				     pwd->pw_name, tty);
    430 			continue;
    431 		}
    432 
    433 		if (pwd && !rval)
    434 			break;
    435 
    436 		(void)printf("Login incorrect\n");
    437 		failures++;
    438 		cnt++;
    439 		/* we allow 10 tries, but after 3 we start backing off */
    440 		if (cnt > login_backoff || cnt >= login_retries) {
    441 			if (cnt >= login_retries) {
    442 				badlogin(username);
    443 				sleepexit(1);
    444 			}
    445 			sleep((u_int)((cnt - 3) * 5));
    446 		}
    447 	}
    448 
    449 	/* committed to login -- turn off timeout */
    450 	(void)alarm((u_int)0);
    451 
    452 	endpwent();
    453 
    454 	/* if user not super-user, check for disabled logins */
    455 #ifdef LOGIN_CAP
    456         if (!rootlogin || login_getcapbool(lc, "ignorenologin", 0)) {
    457 		char *fname;
    458 
    459 		fname = login_getcapstr(lc, "nologin", NULL, NULL);
    460                 checknologin(fname);
    461 	}
    462 #else
    463         if (!rootlogin)
    464                 checknologin(NULL);
    465 #endif
    466 
    467 #ifdef LOGIN_CAP
    468         quietlog = login_getcapbool(lc, "hushlogin", 0);
    469 #else
    470         quietlog = 0;
    471 #endif
    472 	/* Temporarily give up special privileges so we can change */
    473 	/* into NFS-mounted homes that are exported for non-root */
    474 	/* access and have mode 7x0 */
    475 	saved_uid = geteuid();
    476 	saved_gid = getegid();
    477 	nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
    478 
    479 	(void)setegid(pwd->pw_gid);
    480 	initgroups(username, pwd->pw_gid);
    481 	(void)seteuid(pwd->pw_uid);
    482 
    483 	if (chdir(pwd->pw_dir) < 0) {
    484 #ifdef LOGIN_CAP
    485                 if (login_getcapbool(lc, "requirehome", 0)) {
    486 			(void) printf("Home directory %s required\n", pwd->pw_dir);
    487                         sleepexit(1);
    488 		}
    489 #endif
    490 		(void)printf("No home directory %s!\n", pwd->pw_dir);
    491 		if (chdir("/"))
    492 			exit(0);
    493 		pwd->pw_dir = "/";
    494 		(void)printf("Logging in with home = \"/\".\n");
    495 	}
    496 
    497 	if(!quietlog)
    498 		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
    499 
    500 	/* regain special privileges */
    501 	(void)seteuid(saved_uid);
    502 	setgroups(nsaved_gids, saved_gids);
    503 	(void)setegid(saved_gid);
    504 
    505 #ifdef LOGIN_CAP
    506         pw_warntime = login_getcaptime(lc, "password-warn",
    507                                     _PASSWORD_WARNDAYS * SECSPERDAY,
    508                                     _PASSWORD_WARNDAYS * SECSPERDAY);
    509 #endif
    510 
    511 	if (pwd->pw_change || pwd->pw_expire)
    512 		(void)gettimeofday(&tp, (struct timezone *)NULL);
    513 	if (pwd->pw_expire) {
    514 		if (tp.tv_sec >= pwd->pw_expire) {
    515 			(void)printf("Sorry -- your account has expired.\n");
    516 			sleepexit(1);
    517 		} else if (pwd->pw_expire - tp.tv_sec < pw_warntime &&
    518 			   !quietlog)
    519 			(void)printf("Warning: your account expires on %s",
    520 			    ctime(&pwd->pw_expire));
    521 	}
    522 	if (pwd->pw_change) {
    523 		if (pwd->pw_change == _PASSWORD_CHGNOW)
    524 			need_chpass = 1;
    525 		else if (tp.tv_sec >= pwd->pw_change) {
    526 			(void)printf("Sorry -- your password has expired.\n");
    527 			sleepexit(1);
    528 		} else if (pwd->pw_change - tp.tv_sec < pw_warntime &&
    529 			   !quietlog)
    530 			(void)printf("Warning: your password expires on %s",
    531 			    ctime(&pwd->pw_change));
    532 
    533 	}
    534 	/* Nothing else left to fail -- really log in. */
    535 	memset((void *)&utmp, 0, sizeof(utmp));
    536 	(void)time(&utmp.ut_time);
    537 	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
    538 	if (hostname)
    539 		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
    540 	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
    541 	login(&utmp);
    542 
    543 	dolastlog(quietlog);
    544 
    545 	(void)chown(ttyn, pwd->pw_uid,
    546 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
    547 
    548 	if (ttyaction(ttyn, "login", pwd->pw_name))
    549 		(void)printf("Warning: ttyaction failed.\n");
    550 
    551 #if defined(KERBEROS) || defined(KERBEROS5)
    552 	/* Fork so that we can call kdestroy */
    553 	if (krbtkfile_env)
    554 		dofork();
    555 #endif
    556 #ifdef LOGIN_CAP
    557 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~LOGIN_SETPATH)
    558 	    != 0) {
    559 		syslog(LOG_ERR, "setusercontext failed");
    560 		exit(1);
    561 	}
    562 #else
    563 	(void)setgid(pwd->pw_gid);
    564 
    565 	initgroups(username, pwd->pw_gid);
    566 
    567 	if (setlogin(pwd->pw_name) < 0)
    568 		syslog(LOG_ERR, "setlogin() failure: %m");
    569 
    570 	/* Discard permissions last so can't get killed and drop core. */
    571 	if (rootlogin)
    572 		(void)setuid(0);
    573 	else
    574 		(void)setuid(pwd->pw_uid);
    575 
    576 #endif
    577 
    578 	if (*pwd->pw_shell == '\0')
    579 		pwd->pw_shell = _PATH_BSHELL;
    580 #ifdef LOGIN_CAP
    581 	if((shell = login_getcapstr(lc, "shell", NULL, NULL))) {
    582 		if(!(shell = strdup(shell))) {
    583                 	syslog(LOG_NOTICE, "Cannot alloc mem");
    584                 	sleepexit(1);
    585 		}
    586 		pwd->pw_shell = shell;
    587 	}
    588 #endif
    589 
    590 	/* Destroy environment unless user has requested its preservation. */
    591 	if (!pflag)
    592 		environ = envinit;
    593 	(void)setenv("HOME", pwd->pw_dir, 1);
    594 	(void)setenv("SHELL", pwd->pw_shell, 1);
    595 	if (term[0] == '\0') {
    596 		char *tt = (char *) stypeof(tty);
    597 #ifdef LOGIN_CAP
    598 		if(!tt)
    599 			tt = login_getcapstr(lc, "term", NULL, NULL);
    600 #endif
    601 		/* unknown term -> "su" */
    602 		(void)strncpy(term, tt ? tt : "su", sizeof(term));
    603 		}
    604 	(void)setenv("TERM", term, 0);
    605 	(void)setenv("LOGNAME", pwd->pw_name, 1);
    606 	(void)setenv("USER", pwd->pw_name, 1);
    607 #ifdef LOGIN_CAP
    608 	setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
    609 #else
    610 	(void)setenv("PATH", _PATH_DEFPATH, 0);
    611 #endif
    612 #ifdef KERBEROS
    613 	if (krbtkfile_env)
    614 		(void)setenv("KRBTKFILE", krbtkfile_env, 1);
    615 #endif
    616 #ifdef KERBEROS5
    617 	if (krbtkfile_env)
    618 		(void)setenv("KRB5CCNAME", krbtkfile_env, 1);
    619 #endif
    620 
    621 	if (tty[sizeof("tty")-1] == 'd')
    622 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
    623 
    624 	/* If fflag is on, assume caller/authenticator has logged root login. */
    625 	if (rootlogin && fflag == 0) {
    626 		if (hostname)
    627 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
    628 			    username, tty, hostname);
    629 		else
    630 			syslog(LOG_NOTICE,
    631 				"ROOT LOGIN (%s) ON %s", username, tty);
    632 	}
    633 
    634 #if defined(KERBEROS) || defined(KERBEROS5)
    635 	if (!quietlog && notickets == 1)
    636 		(void)printf("Warning: no Kerberos tickets issued.\n");
    637 #endif
    638 
    639 	if (!quietlog) {
    640 		char    *fname;
    641 #ifdef LOGIN_CAP
    642 
    643 		fname = login_getcapstr(lc, "copyright", NULL, NULL);
    644 		if (fname && access(fname, F_OK) == 0)
    645 			motd(fname);
    646 		else
    647 #endif
    648 			(void)printf(copyrightstr);
    649 
    650 #ifdef LOGIN_CAP
    651                 fname = login_getcapstr(lc, "welcome", NULL, NULL);
    652                 if (fname == NULL || access(fname, F_OK) != 0)
    653 #endif
    654                         fname = _PATH_MOTDFILE;
    655                 motd(fname);
    656 
    657 		(void)snprintf(tbuf,
    658 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
    659 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
    660 			(void)printf("You have %smail.\n",
    661 			    (st.st_mtime > st.st_atime) ? "new " : "");
    662 	}
    663 
    664 #ifdef LOGIN_CAP
    665 	login_close(lc);
    666 #endif
    667 
    668 	(void)signal(SIGALRM, SIG_DFL);
    669 	(void)signal(SIGQUIT, SIG_DFL);
    670 	(void)signal(SIGINT, SIG_DFL);
    671 	(void)signal(SIGTSTP, SIG_IGN);
    672 
    673 	tbuf[0] = '-';
    674 	(void)strncpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
    675 	    p + 1 : pwd->pw_shell, sizeof(tbuf) - 2);
    676 
    677 	/* Wait to change password until we're unprivileged */
    678 	if (need_chpass) {
    679 		if (!require_chpass)
    680 			(void)printf(
    681 "Warning: your password has expired. Please change it as soon as possible.\n");
    682 		else {
    683 			int	status;
    684 
    685 			(void)printf(
    686 		    "Your password has expired. Please choose a new one.\n");
    687 			switch (fork()) {
    688 			case -1:
    689 				warn("fork");
    690 				sleepexit(1);
    691 			case 0:
    692 				execl(_PATH_BINPASSWD, "passwd", 0);
    693 				_exit(1);
    694 			default:
    695 				if (wait(&status) == -1 ||
    696 				    WEXITSTATUS(status))
    697 					sleepexit(1);
    698 			}
    699 		}
    700 	}
    701 
    702 #ifdef KERBEROS5
    703 	k5_write_creds();
    704 #endif
    705 	execlp(pwd->pw_shell, tbuf, 0);
    706 	err(1, "%s", pwd->pw_shell);
    707 }
    708 
    709 #if defined(KERBEROS) || defined(KERBEROS5)
    710 #define	NBUFSIZ		(MAXLOGNAME + 1 + 5)	/* .root suffix */
    711 #else
    712 #define	NBUFSIZ		(MAXLOGNAME + 1)
    713 #endif
    714 
    715 #if defined(KERBEROS) || defined(KERBEROS5)
    716 /*
    717  * This routine handles cleanup stuff, and the like.
    718  * It exists only in the child process.
    719  */
    720 #include <sys/wait.h>
    721 void
    722 dofork()
    723 {
    724 	int child;
    725 
    726 	if (!(child = fork()))
    727 		return; /* Child process */
    728 
    729 	/* Setup stuff?  This would be things we could do in parallel with login */
    730 	(void) chdir("/");	/* Let's not keep the fs busy... */
    731 
    732 	/* If we're the parent, watch the child until it dies */
    733 	while (wait(0) != child)
    734 		;
    735 
    736 	/* Cleanup stuff */
    737 	/* Run kdestroy to destroy tickets */
    738 	kdestroy();
    739 
    740 	/* Leave */
    741 	exit(0);
    742 }
    743 #endif
    744 
    745 void
    746 getloginname()
    747 {
    748 	int ch;
    749 	char *p;
    750 	static char nbuf[NBUFSIZ];
    751 
    752 	for (;;) {
    753 		(void)printf("login: ");
    754 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
    755 			if (ch == EOF) {
    756 				badlogin(username);
    757 				exit(0);
    758 			}
    759 			if (p < nbuf + (NBUFSIZ - 1))
    760 				*p++ = ch;
    761 		}
    762 		if (p > nbuf) {
    763 			if (nbuf[0] == '-')
    764 				(void)fprintf(stderr,
    765 				    "login names may not start with '-'.\n");
    766 			else {
    767 				*p = '\0';
    768 				username = nbuf;
    769 				break;
    770 			}
    771 		}
    772 	}
    773 }
    774 
    775 int
    776 rootterm(ttyn)
    777 	char *ttyn;
    778 {
    779 	struct ttyent *t;
    780 
    781 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
    782 }
    783 
    784 jmp_buf motdinterrupt;
    785 
    786 void
    787 motd(fname)
    788 	char *fname;
    789 {
    790 	int fd, nchars;
    791 	sig_t oldint;
    792 	char tbuf[8192];
    793 
    794 	if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
    795 		return;
    796 	oldint = signal(SIGINT, sigint);
    797 	if (setjmp(motdinterrupt) == 0)
    798 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    799 			(void)write(fileno(stdout), tbuf, nchars);
    800 	(void)signal(SIGINT, oldint);
    801 	(void)close(fd);
    802 }
    803 
    804 /* ARGSUSED */
    805 void
    806 sigint(signo)
    807 	int signo;
    808 {
    809 
    810 	longjmp(motdinterrupt, 1);
    811 }
    812 
    813 /* ARGSUSED */
    814 void
    815 timedout(signo)
    816 	int signo;
    817 {
    818 
    819 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
    820 	exit(0);
    821 }
    822 
    823 void
    824 checknologin(fname)
    825 	char *fname;
    826 {
    827 	int fd, nchars;
    828 	char tbuf[8192];
    829 
    830 	if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
    831 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    832 			(void)write(fileno(stdout), tbuf, nchars);
    833 		sleepexit(0);
    834 	}
    835 }
    836 
    837 void
    838 dolastlog(quiet)
    839 	int quiet;
    840 {
    841 	struct lastlog ll;
    842 	int fd;
    843 
    844 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
    845 		(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
    846 		if (!quiet) {
    847 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
    848 			    ll.ll_time != 0) {
    849 				(void)printf("Last login: %.*s ",
    850 				    24, (char *)ctime(&ll.ll_time));
    851 				if (*ll.ll_host != '\0')
    852 					(void)printf("from %.*s\n",
    853 					    (int)sizeof(ll.ll_host),
    854 					    ll.ll_host);
    855 				else
    856 					(void)printf("on %.*s\n",
    857 					    (int)sizeof(ll.ll_line),
    858 					    ll.ll_line);
    859 			}
    860 			(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
    861 			    SEEK_SET);
    862 		}
    863 		memset((void *)&ll, 0, sizeof(ll));
    864 		(void)time(&ll.ll_time);
    865 		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
    866 		if (hostname)
    867 			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
    868 		(void)write(fd, (char *)&ll, sizeof(ll));
    869 		(void)close(fd);
    870 	}
    871 }
    872 
    873 void
    874 badlogin(name)
    875 	char *name;
    876 {
    877 	if (failures == 0)
    878 		return;
    879 	if (hostname) {
    880 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
    881 		    failures, failures > 1 ? "S" : "", hostname);
    882 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    883 		    "%d LOGIN FAILURE%s FROM %s, %s",
    884 		    failures, failures > 1 ? "S" : "", hostname, name);
    885 	} else {
    886 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
    887 		    failures, failures > 1 ? "S" : "", tty);
    888 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    889 		    "%d LOGIN FAILURE%s ON %s, %s",
    890 		    failures, failures > 1 ? "S" : "", tty, name);
    891 	}
    892 }
    893 
    894 const char *
    895 stypeof(ttyid)
    896 	const char *ttyid;
    897 {
    898 	struct ttyent *t;
    899 
    900 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
    901 }
    902 
    903 void
    904 sleepexit(eval)
    905 	int eval;
    906 {
    907 
    908 	(void)sleep(5);
    909 	exit(eval);
    910 }
    911