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