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