Home | History | Annotate | Line # | Download | only in login
login.c revision 1.60
      1 /*     $NetBSD: login.c,v 1.60 2000/08/02 05:58:36 thorpej 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.60 2000/08/02 05:58:36 thorpej Exp $");
     48 #endif /* not lint */
     49 
     50 /*
     51  * login [ name ]
     52  * login -h hostname	(for telnetd, etc.)
     53  * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
     54  */
     55 
     56 #include <sys/param.h>
     57 #include <sys/stat.h>
     58 #include <sys/time.h>
     59 #include <sys/resource.h>
     60 #include <sys/file.h>
     61 #include <sys/wait.h>
     62 
     63 #include <err.h>
     64 #include <errno.h>
     65 #include <grp.h>
     66 #include <pwd.h>
     67 #include <setjmp.h>
     68 #include <signal.h>
     69 #include <stdio.h>
     70 #include <stdlib.h>
     71 #include <string.h>
     72 #include <syslog.h>
     73 #include <time.h>
     74 #include <ttyent.h>
     75 #include <tzfile.h>
     76 #include <unistd.h>
     77 #include <utmp.h>
     78 #include <util.h>
     79 #ifdef SKEY
     80 #include <skey.h>
     81 #endif
     82 #ifdef KERBEROS5
     83 #include <krb5/krb5.h>
     84 #include <com_err.h>
     85 #endif
     86 #ifdef LOGIN_CAP
     87 #include <login_cap.h>
     88 #endif
     89 
     90 #include "pathnames.h"
     91 
     92 #ifdef KERBEROS5
     93 int login_krb5_get_tickets = 1;
     94 int login_krb4_get_tickets = 0;
     95 int login_krb5_forwardable_tgt = 0;
     96 int login_krb5_retain_ccache = 0;
     97 #endif
     98 
     99 void	 badlogin __P((char *));
    100 void	 checknologin __P((char *));
    101 void	 dolastlog __P((int));
    102 void	 getloginname __P((void));
    103 int	 main __P((int, char *[]));
    104 void	 motd __P((char *));
    105 int	 rootterm __P((char *));
    106 void	 sigint __P((int));
    107 void	 sleepexit __P((int));
    108 const	 char *stypeof __P((const char *));
    109 void	 timedout __P((int));
    110 #if defined(KERBEROS)
    111 int	 klogin __P((struct passwd *, char *, char *, char *));
    112 void	 kdestroy __P((void));
    113 #endif
    114 #ifdef KERBEROS5
    115 int	 k5login __P((struct passwd *, char *, char *, char *));
    116 void	 k5destroy __P((void));
    117 int	 k5_read_creds __P((char*));
    118 int	 k5_write_creds __P((void));
    119 #endif
    120 #if defined(KERBEROS) || defined(KERBEROS5)
    121 void	 dofork __P((void));
    122 #endif
    123 
    124 #define	TTYGRPNAME	"tty"		/* name of group to own ttys */
    125 
    126 #define DEFAULT_BACKOFF 3
    127 #define DEFAULT_RETRIES 10
    128 
    129 /*
    130  * This bounds the time given to login.  Not a define so it can
    131  * be patched on machines where it's too small.
    132  */
    133 u_int	timeout = 300;
    134 
    135 #if defined(KERBEROS) || defined(KERBEROS5)
    136 int	notickets = 1;
    137 char	*instance;
    138 int	has_ccache = 0;
    139 #endif
    140 #ifdef KERBEROS
    141 extern char	*krbtkfile_env;
    142 extern int	krb_configured;
    143 #endif
    144 #ifdef KERBEROS5
    145 extern krb5_context kcontext;
    146 extern int	have_forward;
    147 extern char	*krb5tkfile_env;
    148 extern int	krb5_configured;
    149 #endif
    150 
    151 #if defined(KERBEROS) && defined(KERBEROS5)
    152 #define	KERBEROS_CONFIGURED	(krb_configured || krb5_configured)
    153 #elif defined(KERBEROS)
    154 #define	KERBEROS_CONFIGURED	krb_configured
    155 #elif defined(KERBEROS5)
    156 #define	KERBEROS_CONFIGURED	krb5_configured
    157 #endif
    158 
    159 struct	passwd *pwd;
    160 int	failures;
    161 char	term[64], *envinit[1], *hostname, *username, *tty;
    162 
    163 static const char copyrightstr[] = "\
    164 Copyright (c) 1996, 1997, 1998, 1999, 2000\n\
    165 \tThe NetBSD Foundation, Inc.  All rights reserved.\n\
    166 Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994\n\
    167 \tThe Regents of the University of California.  All rights reserved.\n\n";
    168 
    169 int
    170 main(argc, argv)
    171 	int argc;
    172 	char *argv[];
    173 {
    174 	extern char **environ;
    175 	struct group *gr;
    176 	struct stat st;
    177 	struct timeval tp;
    178 	struct utmp utmp;
    179 	int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
    180 	int Fflag;
    181 	uid_t uid, saved_uid;
    182 	gid_t saved_gid, saved_gids[NGROUPS_MAX];
    183 	int nsaved_gids;
    184 	char *domain, *p, *ttyn, *pwprompt;
    185 	const char *salt;
    186 	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
    187 	char localhost[MAXHOSTNAMELEN + 1];
    188 	int need_chpass, require_chpass;
    189 	int login_retries = DEFAULT_RETRIES,
    190 	    login_backoff = DEFAULT_BACKOFF;
    191 	time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
    192 #ifdef KERBEROS5
    193 	krb5_error_code kerror;
    194 #endif
    195 #if defined(KERBEROS) || defined(KERBEROS5)
    196 	int got_tickets = 0;
    197 #endif
    198 #ifdef LOGIN_CAP
    199 	char *shell = NULL;
    200 	login_cap_t *lc = NULL;
    201 #endif
    202 
    203 	tbuf[0] = '\0';
    204 	rval = 0;
    205 	pwprompt = NULL;
    206 	need_chpass = require_chpass = 0;
    207 
    208 	(void)signal(SIGALRM, timedout);
    209 	(void)alarm(timeout);
    210 	(void)signal(SIGQUIT, SIG_IGN);
    211 	(void)signal(SIGINT, SIG_IGN);
    212 	(void)setpriority(PRIO_PROCESS, 0, 0);
    213 
    214 	openlog("login", LOG_ODELAY, LOG_AUTH);
    215 
    216 	/*
    217 	 * -p is used by getty to tell login not to destroy the environment
    218 	 * -f is used to skip a second login authentication
    219 	 * -h is used by other servers to pass the name of the remote
    220 	 *    host to login so that it may be placed in utmp and wtmp
    221 	 * -s is used to force use of S/Key or equivalent.
    222 	 */
    223 	domain = NULL;
    224 	if (gethostname(localhost, sizeof(localhost)) < 0)
    225 		syslog(LOG_ERR, "couldn't get local hostname: %m");
    226 	else
    227 		domain = strchr(localhost, '.');
    228 	localhost[sizeof(localhost) - 1] = '\0';
    229 
    230 	Fflag = fflag = hflag = pflag = sflag = 0;
    231 #ifdef KERBEROS5
    232 	have_forward = 0;
    233 #endif
    234 	uid = getuid();
    235 	while ((ch = getopt(argc, argv, "Ffh:ps")) != -1)
    236 		switch (ch) {
    237 		case 'F':
    238 			Fflag = 1;
    239 			/* FALLTHROUGH */
    240 		case 'f':
    241 			fflag = 1;
    242 			break;
    243 		case 'h':
    244 			if (uid)
    245 				errx(1, "-h option: %s", strerror(EPERM));
    246 			hflag = 1;
    247 			if (domain && (p = strchr(optarg, '.')) != NULL &&
    248 			    strcasecmp(p, domain) == 0)
    249 				*p = 0;
    250 			hostname = optarg;
    251 			break;
    252 		case 'p':
    253 			pflag = 1;
    254 			break;
    255 		case 's':
    256 			sflag = 1;
    257 			break;
    258 		default:
    259 		case '?':
    260 			(void)fprintf(stderr,
    261 			    "usage: login [-fps] [-h hostname] [username]\n");
    262 			exit(1);
    263 		}
    264 	argc -= optind;
    265 	argv += optind;
    266 
    267 	if (*argv) {
    268 		username = *argv;
    269 		ask = 0;
    270 	} else
    271 		ask = 1;
    272 
    273 	for (cnt = getdtablesize(); cnt > 2; cnt--)
    274 		(void)close(cnt);
    275 
    276 	ttyn = ttyname(STDIN_FILENO);
    277 	if (ttyn == NULL || *ttyn == '\0') {
    278 		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
    279 		ttyn = tname;
    280 	}
    281 	if ((tty = strrchr(ttyn, '/')) != NULL)
    282 		++tty;
    283 	else
    284 		tty = ttyn;
    285 
    286 #ifdef LOGIN_CAP
    287 	/* Get "login-retries" and "login-backoff" from default class */
    288 	if ((lc = login_getclass(NULL)) != NULL) {
    289 		login_retries = (int)login_getcapnum(lc, "login-retries",
    290 		    DEFAULT_RETRIES, DEFAULT_RETRIES);
    291 		login_backoff = (int)login_getcapnum(lc, "login-backoff",
    292 		    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
    293 		login_close(lc);
    294 		lc = NULL;
    295 	}
    296 #endif
    297 
    298 #ifdef KERBEROS5
    299 	kerror = krb5_init_context(&kcontext);
    300 	if (kerror) {
    301 		syslog(LOG_NOTICE, "%s when initializing Kerberos context",
    302 		    error_message(kerror));
    303 		login_krb5_get_tickets = 0;
    304 	}
    305 #endif KERBEROS5
    306 
    307 	for (cnt = 0;; ask = 1) {
    308 #if defined(KERBEROS)
    309 	        kdestroy();
    310 #endif
    311 #if defined(KERBEROS5)
    312 		if (login_krb5_get_tickets)
    313 			k5destroy();
    314 #endif
    315 		if (ask) {
    316 			fflag = 0;
    317 			getloginname();
    318 		}
    319 		rootlogin = 0;
    320 #ifdef KERBEROS
    321 		if ((instance = strchr(username, '.')) != NULL)
    322 			*instance++ = '\0';
    323 		else
    324 			instance = "";
    325 #endif
    326 #ifdef KERBEROS5
    327 		if ((instance = strchr(username, '/')) != NULL)
    328 			*instance++ = '\0';
    329 		else
    330 			instance = "";
    331 #endif
    332 		if (strlen(username) > MAXLOGNAME)
    333 			username[MAXLOGNAME] = '\0';
    334 
    335 		/*
    336 		 * Note if trying multiple user names; log failures for
    337 		 * previous user name, but don't bother logging one failure
    338 		 * for nonexistent name (mistyped username).
    339 		 */
    340 		if (failures && strcmp(tbuf, username)) {
    341 			if (failures > (pwd ? 0 : 1))
    342 				badlogin(tbuf);
    343 			failures = 0;
    344 		}
    345 		(void)strncpy(tbuf, username, sizeof(tbuf) - 1);
    346 		tbuf[sizeof(tbuf) - 1] = '\0';
    347 
    348 		if ((pwd = getpwnam(username)) != NULL)
    349 			salt = pwd->pw_passwd;
    350 		else
    351 			salt = "xx";
    352 
    353 #ifdef LOGIN_CAP
    354 		/*
    355 		 * Establish the class now, before we might goto
    356 		 * within the next block. pwd can be NULL since it
    357 		 * falls back to the "default" class if it is.
    358 		 */
    359 		lc = login_getclass(pwd ? pwd->pw_class : NULL);
    360 #endif
    361 		/*
    362 		 * if we have a valid account name, and it doesn't have a
    363 		 * password, or the -f option was specified and the caller
    364 		 * is root or the caller isn't changing their uid, don't
    365 		 * authenticate.
    366 		 */
    367 		if (pwd) {
    368 			if (pwd->pw_uid == 0)
    369 				rootlogin = 1;
    370 
    371 			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
    372 				/* already authenticated */
    373 #ifdef KERBEROS5
    374 				if (login_krb5_get_tickets && Fflag)
    375 					k5_read_creds(username);
    376 #endif
    377 				break;
    378 			} else if (pwd->pw_passwd[0] == '\0') {
    379 				/* pretend password okay */
    380 				rval = 0;
    381 				goto ttycheck;
    382 			}
    383 		}
    384 
    385 		fflag = 0;
    386 
    387 		(void)setpriority(PRIO_PROCESS, 0, -4);
    388 
    389 #ifdef SKEY
    390 		if (skey_haskey(username) == 0) {
    391 			static char skprompt[80];
    392 			const char *skinfo = skey_keyinfo(username);
    393 
    394 			(void)snprintf(skprompt, sizeof(skprompt)-1,
    395 			    "Password [%s]:",
    396 			    skinfo ? skinfo : "error getting challenge");
    397 			pwprompt = skprompt;
    398 		} else
    399 #endif
    400 			pwprompt = "Password:";
    401 
    402 		p = getpass(pwprompt);
    403 
    404 		if (pwd == NULL) {
    405 			rval = 1;
    406 			goto skip;
    407 		}
    408 #ifdef KERBEROS
    409 		if (
    410 #ifdef KERBEROS5
    411 		    /* allow a user to get both krb4 and krb5 tickets, if
    412 		     * desired.  If krb5 is compiled in, the default action
    413 		     * is to ignore krb4 and get krb5 tickets, but the user
    414 		     * can override this in the krb5.conf. */
    415 		    login_krb4_get_tickets &&
    416 #endif
    417 		    klogin(pwd, instance, localhost, p) == 0) {
    418 			rval = 0;
    419 			got_tickets = 1;
    420 		}
    421 #endif
    422 #ifdef KERBEROS5
    423 		if (login_krb5_get_tickets &&
    424 		    k5login(pwd, instance, localhost, p) == 0) {
    425 			rval = 0;
    426 			got_tickets = 1;
    427 		}
    428 #endif
    429 #if defined(KERBEROS) || defined(KERBEROS5)
    430 		if (got_tickets)
    431 			goto skip;
    432 #endif
    433 #ifdef SKEY
    434 		if (skey_haskey(username) == 0 &&
    435 		    skey_passcheck(username, p) != -1) {
    436 			rval = 0;
    437 			goto skip;
    438 		}
    439 #endif
    440 		if (!sflag && *pwd->pw_passwd != '\0' &&
    441 		    !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
    442 			rval = 0;
    443 			require_chpass = 1;
    444 			goto skip;
    445 		}
    446 		rval = 1;
    447 
    448 	skip:
    449 		memset(p, 0, strlen(p));
    450 
    451 		(void)setpriority(PRIO_PROCESS, 0, 0);
    452 
    453 	ttycheck:
    454 		/*
    455 		 * If trying to log in as root without Kerberos,
    456 		 * but with insecure terminal, refuse the login attempt.
    457 		 */
    458 		if (pwd && !rval && rootlogin && !rootterm(tty)) {
    459 			(void)fprintf(stderr,
    460 			    "%s login refused on this terminal.\n",
    461 			    pwd->pw_name);
    462 			if (hostname)
    463 				syslog(LOG_NOTICE,
    464 				    "LOGIN %s REFUSED FROM %s ON TTY %s",
    465 				    pwd->pw_name, hostname, tty);
    466 			else
    467 				syslog(LOG_NOTICE,
    468 				    "LOGIN %s REFUSED ON TTY %s",
    469 				     pwd->pw_name, tty);
    470 			continue;
    471 		}
    472 
    473 		if (pwd && !rval)
    474 			break;
    475 
    476 		(void)printf("Login incorrect\n");
    477 		failures++;
    478 		cnt++;
    479 		/* we allow 10 tries, but after 3 we start backing off */
    480 		if (cnt > login_backoff) {
    481 			if (cnt >= login_retries) {
    482 				badlogin(username);
    483 				sleepexit(1);
    484 			}
    485 			sleep((u_int)((cnt - 3) * 5));
    486 		}
    487 	}
    488 
    489 	/* committed to login -- turn off timeout */
    490 	(void)alarm((u_int)0);
    491 
    492 	endpwent();
    493 
    494 	/* if user not super-user, check for disabled logins */
    495 #ifdef LOGIN_CAP
    496         if (!login_getcapbool(lc, "ignorenologin", rootlogin))
    497 		checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
    498 #else
    499         if (!rootlogin)
    500                 checknologin(NULL);
    501 #endif
    502 
    503 #ifdef LOGIN_CAP
    504         quietlog = login_getcapbool(lc, "hushlogin", 0);
    505 #else
    506         quietlog = 0;
    507 #endif
    508 	/* Temporarily give up special privileges so we can change */
    509 	/* into NFS-mounted homes that are exported for non-root */
    510 	/* access and have mode 7x0 */
    511 	saved_uid = geteuid();
    512 	saved_gid = getegid();
    513 	nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
    514 
    515 	(void)setegid(pwd->pw_gid);
    516 	initgroups(username, pwd->pw_gid);
    517 	(void)seteuid(pwd->pw_uid);
    518 
    519 	if (chdir(pwd->pw_dir) < 0) {
    520 #ifdef LOGIN_CAP
    521                 if (login_getcapbool(lc, "requirehome", 0)) {
    522 			(void)printf("Home directory %s required\n",
    523 			    pwd->pw_dir);
    524                         sleepexit(1);
    525 		}
    526 #endif
    527 		(void)printf("No home directory %s!\n", pwd->pw_dir);
    528 		if (chdir("/"))
    529 			exit(0);
    530 		pwd->pw_dir = "/";
    531 		(void)printf("Logging in with home = \"/\".\n");
    532 	}
    533 
    534 	if (!quietlog)
    535 		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
    536 
    537 	/* regain special privileges */
    538 	(void)seteuid(saved_uid);
    539 	setgroups(nsaved_gids, saved_gids);
    540 	(void)setegid(saved_gid);
    541 
    542 #ifdef LOGIN_CAP
    543         pw_warntime = login_getcaptime(lc, "password-warn",
    544             _PASSWORD_WARNDAYS * SECSPERDAY,
    545             _PASSWORD_WARNDAYS * SECSPERDAY);
    546 #endif
    547 
    548 	if (pwd->pw_change || pwd->pw_expire)
    549 		(void)gettimeofday(&tp, (struct timezone *)NULL);
    550 	if (pwd->pw_expire) {
    551 		if (tp.tv_sec >= pwd->pw_expire) {
    552 			(void)printf("Sorry -- your account has expired.\n");
    553 			sleepexit(1);
    554 		} else if (pwd->pw_expire - tp.tv_sec < pw_warntime &&
    555 		    !quietlog)
    556 			(void)printf("Warning: your account expires on %s",
    557 			    ctime(&pwd->pw_expire));
    558 	}
    559 	if (pwd->pw_change) {
    560 		if (pwd->pw_change == _PASSWORD_CHGNOW)
    561 			need_chpass = 1;
    562 		else if (tp.tv_sec >= pwd->pw_change) {
    563 			(void)printf("Sorry -- your password has expired.\n");
    564 			sleepexit(1);
    565 		} else if (pwd->pw_change - tp.tv_sec < pw_warntime &&
    566 		    !quietlog)
    567 			(void)printf("Warning: your password expires on %s",
    568 			    ctime(&pwd->pw_change));
    569 
    570 	}
    571 	/* Nothing else left to fail -- really log in. */
    572 	memset((void *)&utmp, 0, sizeof(utmp));
    573 	(void)time(&utmp.ut_time);
    574 	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
    575 	if (hostname)
    576 		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
    577 	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
    578 	login(&utmp);
    579 
    580 	dolastlog(quietlog);
    581 
    582 	(void)chown(ttyn, pwd->pw_uid,
    583 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
    584 
    585 	if (ttyaction(ttyn, "login", pwd->pw_name))
    586 		(void)printf("Warning: ttyaction failed.\n");
    587 
    588 #if defined(KERBEROS) || defined(KERBEROS5)
    589 	/* Fork so that we can call kdestroy */
    590 	if (
    591 #ifdef KERBEROS5
    592 	    ! login_krb5_retain_ccache &&
    593 #endif
    594 	    has_ccache)
    595 		dofork();
    596 #endif
    597 
    598 	/* Destroy environment unless user has requested its preservation. */
    599 	if (!pflag)
    600 		environ = envinit;
    601 
    602 #ifdef LOGIN_CAP
    603 	if (setusercontext(lc, pwd, pwd->pw_uid,
    604 	    LOGIN_SETALL & ~LOGIN_SETPATH) != 0) {
    605 		syslog(LOG_ERR, "setusercontext failed");
    606 		exit(1);
    607 	}
    608 #else
    609 	(void)setgid(pwd->pw_gid);
    610 
    611 	initgroups(username, pwd->pw_gid);
    612 
    613 	if (setlogin(pwd->pw_name) < 0)
    614 		syslog(LOG_ERR, "setlogin() failure: %m");
    615 
    616 	/* Discard permissions last so can't get killed and drop core. */
    617 	if (rootlogin)
    618 		(void)setuid(0);
    619 	else
    620 		(void)setuid(pwd->pw_uid);
    621 #endif
    622 
    623 	if (*pwd->pw_shell == '\0')
    624 		pwd->pw_shell = _PATH_BSHELL;
    625 #ifdef LOGIN_CAP
    626 	if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
    627 		if ((shell = strdup(shell)) == NULL) {
    628                 	syslog(LOG_NOTICE, "Cannot alloc mem");
    629                 	sleepexit(1);
    630 		}
    631 		pwd->pw_shell = shell;
    632 	}
    633 #endif
    634 
    635 	(void)setenv("HOME", pwd->pw_dir, 1);
    636 	(void)setenv("SHELL", pwd->pw_shell, 1);
    637 	if (term[0] == '\0') {
    638 		char *tt = (char *)stypeof(tty);
    639 #ifdef LOGIN_CAP
    640 		if (tt == NULL)
    641 			tt = login_getcapstr(lc, "term", NULL, NULL);
    642 #endif
    643 		/* unknown term -> "su" */
    644 		(void)strncpy(term, tt != NULL ? tt : "su", sizeof(term));
    645 	}
    646 	(void)setenv("TERM", term, 0);
    647 	(void)setenv("LOGNAME", pwd->pw_name, 1);
    648 	(void)setenv("USER", pwd->pw_name, 1);
    649 
    650 #ifdef LOGIN_CAP
    651 	setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
    652 #else
    653 	(void)setenv("PATH", _PATH_DEFPATH, 0);
    654 #endif
    655 
    656 #ifdef KERBEROS
    657 	if (krbtkfile_env)
    658 		(void)setenv("KRBTKFILE", krbtkfile_env, 1);
    659 #endif
    660 #ifdef KERBEROS5
    661 	if (krb5tkfile_env)
    662 		(void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
    663 #endif
    664 
    665 	if (tty[sizeof("tty")-1] == 'd')
    666 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
    667 
    668 	/* If fflag is on, assume caller/authenticator has logged root login. */
    669 	if (rootlogin && fflag == 0) {
    670 		if (hostname)
    671 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
    672 			    username, tty, hostname);
    673 		else
    674 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
    675 			    username, tty);
    676 	}
    677 
    678 #if defined(KERBEROS) || defined(KERBEROS5)
    679 	if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
    680 		(void)printf("Warning: no Kerberos tickets issued.\n");
    681 #endif
    682 
    683 	if (!quietlog) {
    684 		char *fname;
    685 #ifdef LOGIN_CAP
    686 		fname = login_getcapstr(lc, "copyright", NULL, NULL);
    687 		if (fname != NULL && access(fname, F_OK) == 0)
    688 			motd(fname);
    689 		else
    690 #endif
    691 			(void)printf(copyrightstr);
    692 
    693 #ifdef LOGIN_CAP
    694                 fname = login_getcapstr(lc, "welcome", NULL, NULL);
    695                 if (fname == NULL || access(fname, F_OK) != 0)
    696 #endif
    697                         fname = _PATH_MOTDFILE;
    698                 motd(fname);
    699 
    700 		(void)snprintf(tbuf,
    701 		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
    702 		if (stat(tbuf, &st) == 0 && st.st_size != 0)
    703 			(void)printf("You have %smail.\n",
    704 			    (st.st_mtime > st.st_atime) ? "new " : "");
    705 	}
    706 
    707 #ifdef LOGIN_CAP
    708 	login_close(lc);
    709 #endif
    710 
    711 	(void)signal(SIGALRM, SIG_DFL);
    712 	(void)signal(SIGQUIT, SIG_DFL);
    713 	(void)signal(SIGINT, SIG_DFL);
    714 	(void)signal(SIGTSTP, SIG_IGN);
    715 
    716 	tbuf[0] = '-';
    717 	(void)strncpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
    718 	    p + 1 : pwd->pw_shell, sizeof(tbuf) - 2);
    719 
    720 	/* Wait to change password until we're unprivileged */
    721 	if (need_chpass) {
    722 		if (!require_chpass)
    723 			(void)printf(
    724 "Warning: your password has expired. Please change it as soon as possible.\n");
    725 		else {
    726 			int	status;
    727 
    728 			(void)printf(
    729 		    "Your password has expired. Please choose a new one.\n");
    730 			switch (fork()) {
    731 			case -1:
    732 				warn("fork");
    733 				sleepexit(1);
    734 			case 0:
    735 				execl(_PATH_BINPASSWD, "passwd", 0);
    736 				_exit(1);
    737 			default:
    738 				if (wait(&status) == -1 ||
    739 				    WEXITSTATUS(status))
    740 					sleepexit(1);
    741 			}
    742 		}
    743 	}
    744 
    745 #ifdef KERBEROS5
    746 	if (login_krb5_get_tickets)
    747 		k5_write_creds();
    748 #endif
    749 	execlp(pwd->pw_shell, tbuf, 0);
    750 	err(1, "%s", pwd->pw_shell);
    751 }
    752 
    753 #if defined(KERBEROS) || defined(KERBEROS5)
    754 #define	NBUFSIZ		(MAXLOGNAME + 1 + 5)	/* .root suffix */
    755 #else
    756 #define	NBUFSIZ		(MAXLOGNAME + 1)
    757 #endif
    758 
    759 #if defined(KERBEROS) || defined(KERBEROS5)
    760 /*
    761  * This routine handles cleanup stuff, and the like.
    762  * It exists only in the child process.
    763  */
    764 #include <sys/wait.h>
    765 void
    766 dofork()
    767 {
    768 	int child;
    769 
    770 	if (!(child = fork()))
    771 		return; /* Child process */
    772 
    773 	/*
    774 	 * Setup stuff?  This would be things we could do in parallel
    775 	 * with login
    776 	 */
    777 	(void)chdir("/");	/* Let's not keep the fs busy... */
    778 
    779 	/* If we're the parent, watch the child until it dies */
    780 	while (wait(0) != child)
    781 		;
    782 
    783 	/* Cleanup stuff */
    784 	/* Run kdestroy to destroy tickets */
    785 #ifdef KERBEROS
    786 	kdestroy();
    787 #endif
    788 #ifdef KERBEROS5
    789 	if (login_krb5_get_tickets)
    790 		k5destroy();
    791 #endif
    792 
    793 	/* Leave */
    794 	exit(0);
    795 }
    796 #endif
    797 
    798 void
    799 getloginname()
    800 {
    801 	int ch;
    802 	char *p;
    803 	static char nbuf[NBUFSIZ];
    804 
    805 	for (;;) {
    806 		(void)printf("login: ");
    807 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
    808 			if (ch == EOF) {
    809 				badlogin(username);
    810 				exit(0);
    811 			}
    812 			if (p < nbuf + (NBUFSIZ - 1))
    813 				*p++ = ch;
    814 		}
    815 		if (p > nbuf) {
    816 			if (nbuf[0] == '-')
    817 				(void)fprintf(stderr,
    818 				    "login names may not start with '-'.\n");
    819 			else {
    820 				*p = '\0';
    821 				username = nbuf;
    822 				break;
    823 			}
    824 		}
    825 	}
    826 }
    827 
    828 int
    829 rootterm(ttyn)
    830 	char *ttyn;
    831 {
    832 	struct ttyent *t;
    833 
    834 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
    835 }
    836 
    837 jmp_buf motdinterrupt;
    838 
    839 void
    840 motd(fname)
    841 	char *fname;
    842 {
    843 	int fd, nchars;
    844 	sig_t oldint;
    845 	char tbuf[8192];
    846 
    847 	if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
    848 		return;
    849 	oldint = signal(SIGINT, sigint);
    850 	if (setjmp(motdinterrupt) == 0)
    851 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    852 			(void)write(fileno(stdout), tbuf, nchars);
    853 	(void)signal(SIGINT, oldint);
    854 	(void)close(fd);
    855 }
    856 
    857 /* ARGSUSED */
    858 void
    859 sigint(signo)
    860 	int signo;
    861 {
    862 
    863 	longjmp(motdinterrupt, 1);
    864 }
    865 
    866 /* ARGSUSED */
    867 void
    868 timedout(signo)
    869 	int signo;
    870 {
    871 
    872 	(void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
    873 	exit(0);
    874 }
    875 
    876 void
    877 checknologin(fname)
    878 	char *fname;
    879 {
    880 	int fd, nchars;
    881 	char tbuf[8192];
    882 
    883 	if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
    884 		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
    885 			(void)write(fileno(stdout), tbuf, nchars);
    886 		sleepexit(0);
    887 	}
    888 }
    889 
    890 void
    891 dolastlog(quiet)
    892 	int quiet;
    893 {
    894 	struct lastlog ll;
    895 	int fd;
    896 
    897 	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
    898 		(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
    899 		if (!quiet) {
    900 			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
    901 			    ll.ll_time != 0) {
    902 				(void)printf("Last login: %.*s ",
    903 				    24, (char *)ctime(&ll.ll_time));
    904 				if (*ll.ll_host != '\0')
    905 					(void)printf("from %.*s\n",
    906 					    (int)sizeof(ll.ll_host),
    907 					    ll.ll_host);
    908 				else
    909 					(void)printf("on %.*s\n",
    910 					    (int)sizeof(ll.ll_line),
    911 					    ll.ll_line);
    912 			}
    913 			(void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
    914 			    SEEK_SET);
    915 		}
    916 		memset((void *)&ll, 0, sizeof(ll));
    917 		(void)time(&ll.ll_time);
    918 		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
    919 		if (hostname)
    920 			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
    921 		(void)write(fd, (char *)&ll, sizeof(ll));
    922 		(void)close(fd);
    923 	}
    924 }
    925 
    926 void
    927 badlogin(name)
    928 	char *name;
    929 {
    930 
    931 	if (failures == 0)
    932 		return;
    933 	if (hostname) {
    934 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
    935 		    failures, failures > 1 ? "S" : "", hostname);
    936 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    937 		    "%d LOGIN FAILURE%s FROM %s, %s",
    938 		    failures, failures > 1 ? "S" : "", hostname, name);
    939 	} else {
    940 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
    941 		    failures, failures > 1 ? "S" : "", tty);
    942 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
    943 		    "%d LOGIN FAILURE%s ON %s, %s",
    944 		    failures, failures > 1 ? "S" : "", tty, name);
    945 	}
    946 }
    947 
    948 const char *
    949 stypeof(ttyid)
    950 	const char *ttyid;
    951 {
    952 	struct ttyent *t;
    953 
    954 	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
    955 }
    956 
    957 void
    958 sleepexit(eval)
    959 	int eval;
    960 {
    961 
    962 	(void)sleep(5);
    963 	exit(eval);
    964 }
    965