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