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