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