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