1/*	$NetBSD: login.c,v 1.104 2014/03/16 00:33:13 dholland 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("@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\
35 The Regents of the University of California.  All rights reserved.");
36#endif /* not lint */
37
38#ifndef lint
39#if 0
40static char sccsid[] = "@(#)login.c	8.4 (Berkeley) 4/2/94";
41#endif
42__RCSID("$NetBSD: login.c,v 1.104 2014/03/16 00:33:13 dholland Exp $");
43#endif /* not lint */
44
45/*
46 * login [ name ]
47 * login -h hostname	(for telnetd, etc.)
48 * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
49 */
50
51#include <sys/param.h>
52#include <sys/stat.h>
53#include <sys/time.h>
54#include <sys/resource.h>
55#include <sys/file.h>
56#include <sys/wait.h>
57#include <sys/socket.h>
58
59#include <err.h>
60#include <errno.h>
61#include <grp.h>
62#include <pwd.h>
63#include <setjmp.h>
64#include <signal.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <syslog.h>
69#include <time.h>
70#include <ttyent.h>
71#include <tzfile.h>
72#include <unistd.h>
73#include <sysexits.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 <krb5/com_err.h>
87#endif
88#ifdef LOGIN_CAP
89#include <login_cap.h>
90#endif
91#include <vis.h>
92
93#include "pathnames.h"
94#include "common.h"
95
96#ifdef KERBEROS5
97int login_krb5_forwardable_tgt = 0;
98static int login_krb5_get_tickets = 1;
99static int login_krb5_retain_ccache = 0;
100#endif
101
102static void	 checknologin(char *);
103#ifdef KERBEROS5
104int	 k5login(struct passwd *, char *, char *, char *);
105void	 k5destroy(void);
106int	 k5_read_creds(const char *);
107int	 k5_write_creds(void);
108#endif
109#if defined(KERBEROS5)
110static void	 dofork(void);
111#endif
112static void	 usage(void) __attribute__((__noreturn__));
113
114#define	TTYGRPNAME	"tty"		/* name of group to own ttys */
115
116#define DEFAULT_BACKOFF 3
117#define DEFAULT_RETRIES 10
118
119#if defined(KERBEROS5)
120int	has_ccache = 0;
121int	notickets = 1;
122extern krb5_context kcontext;
123extern int	have_forward;
124static char	*instance;
125extern char	*krb5tkfile_env;
126extern int	krb5_configured;
127#endif
128
129#if defined(KERBEROS5)
130#define	KERBEROS_CONFIGURED	krb5_configured
131#endif
132
133extern char **environ;
134
135int
136main(int argc, char *argv[])
137{
138	struct group *gr;
139	struct stat st;
140	int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
141	uid_t uid, saved_uid;
142	gid_t saved_gid, saved_gids[NGROUPS_MAX];
143	int nsaved_gids;
144#ifdef notdef
145	char *domain;
146#endif
147	char *p, *ttyn;
148	const char *pwprompt;
149	char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
150	char localhost[MAXHOSTNAMELEN + 1];
151	int need_chpass, require_chpass;
152	int login_retries = DEFAULT_RETRIES,
153	    login_backoff = DEFAULT_BACKOFF;
154	time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
155	char *loginname = NULL;
156#ifdef KERBEROS5
157	int Fflag;
158	krb5_error_code kerror;
159#endif
160#if defined(KERBEROS5)
161	int got_tickets = 0;
162#endif
163#ifdef LOGIN_CAP
164	char *shell = NULL;
165	login_cap_t *lc = NULL;
166#endif
167
168	tbuf[0] = '\0';
169	rval = 0;
170	pwprompt = NULL;
171	nested = NULL;
172	need_chpass = require_chpass = 0;
173
174	(void)signal(SIGALRM, timedout);
175	(void)alarm(timeout);
176	(void)signal(SIGQUIT, SIG_IGN);
177	(void)signal(SIGINT, SIG_IGN);
178	(void)setpriority(PRIO_PROCESS, 0, 0);
179
180	openlog("login", 0, LOG_AUTH);
181
182	/*
183	 * -p is used by getty to tell login not to destroy the environment
184	 * -f is used to skip a second login authentication
185	 * -h is used by other servers to pass the name of the remote host to
186	 *    login so that it may be placed in utmp/utmpx and wtmp/wtmpx
187	 * -a in addition to -h, a server may supply -a to pass the actual
188	 *    server address.
189	 * -s is used to force use of S/Key or equivalent.
190	 */
191	if (gethostname(localhost, sizeof(localhost)) < 0) {
192		syslog(LOG_ERR, "couldn't get local hostname: %m");
193		strcpy(hostname, "amnesiac");
194	}
195#ifdef notdef
196	domain = strchr(localhost, '.');
197#endif
198	localhost[sizeof(localhost) - 1] = '\0';
199
200	fflag = hflag = pflag = sflag = 0;
201	have_ss = 0;
202#ifdef KERBEROS5
203	Fflag = 0;
204	have_forward = 0;
205#endif
206	uid = getuid();
207	while ((ch = getopt(argc, argv, "a:Ffh:ps")) != -1)
208		switch (ch) {
209		case 'a':
210			if (uid)
211				errx(EXIT_FAILURE, "-a option: %s", strerror(EPERM));
212			decode_ss(optarg);
213#ifdef notdef
214			(void)sockaddr_snprintf(optarg,
215			    sizeof(struct sockaddr_storage), "%a", (void *)&ss);
216#endif
217			break;
218		case 'F':
219#ifdef KERBEROS5
220			Fflag = 1;
221#endif
222			/* FALLTHROUGH */
223		case 'f':
224			fflag = 1;
225			break;
226		case 'h':
227			if (uid)
228				errx(EXIT_FAILURE, "-h option: %s", strerror(EPERM));
229			hflag = 1;
230#ifdef notdef
231			if (domain && (p = strchr(optarg, '.')) != NULL &&
232			    strcasecmp(p, domain) == 0)
233				*p = '\0';
234#endif
235			hostname = optarg;
236			break;
237		case 'p':
238			pflag = 1;
239			break;
240		case 's':
241			sflag = 1;
242			break;
243		default:
244		case '?':
245			usage();
246			break;
247		}
248
249	setproctitle(NULL);
250	argc -= optind;
251	argv += optind;
252
253	if (*argv) {
254		username = loginname = *argv;
255		ask = 0;
256	} else
257		ask = 1;
258
259#ifdef F_CLOSEM
260	(void)fcntl(3, F_CLOSEM, 0);
261#else
262	for (cnt = getdtablesize(); cnt > 2; cnt--)
263		(void)close(cnt);
264#endif
265
266	ttyn = ttyname(STDIN_FILENO);
267	if (ttyn == NULL || *ttyn == '\0') {
268		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
269		ttyn = tname;
270	}
271	if ((tty = strstr(ttyn, "/pts/")) != NULL)
272		++tty;
273	else if ((tty = strrchr(ttyn, '/')) != NULL)
274		++tty;
275	else
276		tty = ttyn;
277
278	if (issetugid()) {
279		nested = strdup(user_from_uid(getuid(), 0));
280		if (nested == NULL) {
281			syslog(LOG_ERR, "strdup: %m");
282			sleepexit(EXIT_FAILURE);
283		}
284	}
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(KERBEROS5)
321		if (login_krb5_get_tickets)
322			k5destroy();
323#endif
324		if (ask) {
325			fflag = 0;
326			loginname = getloginname();
327		}
328		rootlogin = 0;
329#ifdef KERBEROS5
330		if ((instance = strchr(loginname, '/')) != NULL)
331			*instance++ = '\0';
332		else
333			instance = __UNCONST("");
334#endif
335		username = trimloginname(loginname);
336		/*
337		 * Note if trying multiple user names; log failures for
338		 * previous user name, but don't bother logging one failure
339		 * for nonexistent name (mistyped username).
340		 */
341		if (failures && strcmp(tbuf, username)) {
342			if (failures > (pwd ? 0 : 1))
343				badlogin(tbuf);
344			failures = 0;
345		}
346		(void)strlcpy(tbuf, username, sizeof(tbuf));
347
348		pwd = getpwnam(username);
349
350#ifdef LOGIN_CAP
351		/*
352		 * Establish the class now, before we might goto
353		 * within the next block. pwd can be NULL since it
354		 * falls back to the "default" class if it is.
355		 */
356		lc = login_getclass(pwd ? pwd->pw_class : NULL);
357#endif
358		/*
359		 * if we have a valid account name, and it doesn't have a
360		 * password, or the -f option was specified and the caller
361		 * is root or the caller isn't changing their uid, don't
362		 * authenticate.
363		 */
364		if (pwd) {
365			if (pwd->pw_uid == 0)
366				rootlogin = 1;
367
368			if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
369				/* already authenticated */
370#ifdef KERBEROS5
371				if (login_krb5_get_tickets && Fflag)
372					k5_read_creds(username);
373#endif
374				break;
375			} else if (pwd->pw_passwd[0] == '\0') {
376				/* pretend password okay */
377				rval = 0;
378				goto ttycheck;
379			}
380		}
381
382		fflag = 0;
383
384		(void)setpriority(PRIO_PROCESS, 0, -4);
385
386#ifdef SKEY
387		if (skey_haskey(username) == 0) {
388			static char skprompt[80];
389			const char *skinfo = skey_keyinfo(username);
390
391			(void)snprintf(skprompt, sizeof(skprompt),
392			    "Password [ %s ]:",
393			    skinfo ? skinfo : "error getting challenge");
394			pwprompt = skprompt;
395		} else
396#endif
397			pwprompt = "Password:";
398
399		p = getpass(pwprompt);
400
401		if (pwd == NULL) {
402			rval = 1;
403			goto skip;
404		}
405#ifdef KERBEROS5
406		if (login_krb5_get_tickets &&
407		    k5login(pwd, instance, localhost, p) == 0) {
408			rval = 0;
409			got_tickets = 1;
410		}
411#endif
412#if defined(KERBEROS5)
413		if (got_tickets)
414			goto skip;
415#endif
416#ifdef SKEY
417		if (skey_haskey(username) == 0 &&
418		    skey_passcheck(username, p) != -1) {
419			rval = 0;
420			goto skip;
421		}
422#endif
423		if (!sflag && *pwd->pw_passwd != '\0' &&
424		    !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
425			rval = 0;
426			require_chpass = 1;
427			goto skip;
428		}
429		rval = 1;
430
431	skip:
432		memset(p, 0, strlen(p));
433
434		(void)setpriority(PRIO_PROCESS, 0, 0);
435
436	ttycheck:
437		/*
438		 * If trying to log in as root without Kerberos,
439		 * but with insecure terminal, refuse the login attempt.
440		 */
441		if (pwd && !rval && rootlogin && !rootterm(tty)) {
442			(void)printf("Login incorrect or refused on this "
443			    "terminal.\n");
444			if (hostname)
445				syslog(LOG_NOTICE,
446				    "LOGIN %s REFUSED FROM %s ON TTY %s",
447				    pwd->pw_name, hostname, tty);
448			else
449				syslog(LOG_NOTICE,
450				    "LOGIN %s REFUSED ON TTY %s",
451				     pwd->pw_name, tty);
452			continue;
453		}
454
455		if (pwd && !rval)
456			break;
457
458		(void)printf("Login incorrect or refused on this "
459		    "terminal.\n");
460		failures++;
461		cnt++;
462		/*
463		 * We allow login_retries tries, but after login_backoff
464		 * we start backing off.  These default to 10 and 3
465		 * respectively.
466		 */
467		if (cnt > login_backoff) {
468			if (cnt >= login_retries) {
469				badlogin(username);
470				sleepexit(EXIT_FAILURE);
471			}
472			sleep((u_int)((cnt - login_backoff) * 5));
473		}
474	}
475
476	/* committed to login -- turn off timeout */
477	(void)alarm((u_int)0);
478
479	endpwent();
480
481	/* if user not super-user, check for disabled logins */
482#ifdef LOGIN_CAP
483	if (!login_getcapbool(lc, "ignorenologin", rootlogin))
484		checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
485#else
486	if (!rootlogin)
487		checknologin(NULL);
488#endif
489
490#ifdef LOGIN_CAP
491	quietlog = login_getcapbool(lc, "hushlogin", 0);
492#else
493	quietlog = 0;
494#endif
495	/* Temporarily give up special privileges so we can change */
496	/* into NFS-mounted homes that are exported for non-root */
497	/* access and have mode 7x0 */
498	saved_uid = geteuid();
499	saved_gid = getegid();
500	nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
501
502	(void)setegid(pwd->pw_gid);
503	initgroups(username, pwd->pw_gid);
504	(void)seteuid(pwd->pw_uid);
505
506	if (chdir(pwd->pw_dir) < 0) {
507#ifdef LOGIN_CAP
508		if (login_getcapbool(lc, "requirehome", 0)) {
509			(void)printf("Home directory %s required\n",
510			    pwd->pw_dir);
511			sleepexit(EXIT_FAILURE);
512		}
513#endif
514		(void)printf("No home directory %s!\n", pwd->pw_dir);
515		if (chdir("/") == -1)
516			exit(EXIT_FAILURE);
517		pwd->pw_dir = __UNCONST("/");
518		(void)printf("Logging in with home = \"/\".\n");
519	}
520
521	if (!quietlog)
522		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
523
524	/* regain special privileges */
525	(void)seteuid(saved_uid);
526	setgroups(nsaved_gids, saved_gids);
527	(void)setegid(saved_gid);
528
529#ifdef LOGIN_CAP
530	pw_warntime = login_getcaptime(lc, "password-warn",
531		_PASSWORD_WARNDAYS * SECSPERDAY,
532		_PASSWORD_WARNDAYS * SECSPERDAY);
533#endif
534
535	(void)gettimeofday(&now, NULL);
536	if (pwd->pw_expire) {
537		if (now.tv_sec >= pwd->pw_expire) {
538			(void)printf("Sorry -- your account has expired.\n");
539			sleepexit(EXIT_FAILURE);
540		} else if (pwd->pw_expire - now.tv_sec < pw_warntime &&
541		    !quietlog)
542			(void)printf("Warning: your account expires on %s",
543			    ctime(&pwd->pw_expire));
544	}
545	if (pwd->pw_change) {
546		if (pwd->pw_change == _PASSWORD_CHGNOW)
547			need_chpass = 1;
548		else if (now.tv_sec >= pwd->pw_change) {
549			(void)printf("Sorry -- your password has expired.\n");
550			sleepexit(EXIT_FAILURE);
551		} else if (pwd->pw_change - now.tv_sec < pw_warntime &&
552		    !quietlog)
553			(void)printf("Warning: your password expires on %s",
554			    ctime(&pwd->pw_change));
555
556	}
557	/* Nothing else left to fail -- really log in. */
558	update_db(quietlog, rootlogin, fflag);
559
560	(void)chown(ttyn, pwd->pw_uid,
561	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
562
563	if (ttyaction(ttyn, "login", pwd->pw_name))
564		(void)printf("Warning: ttyaction failed.\n");
565
566#if defined(KERBEROS5)
567	/* Fork so that we can call kdestroy */
568	if (! login_krb5_retain_ccache && has_ccache)
569		dofork();
570#endif
571
572	/* Destroy environment unless user has requested its preservation. */
573	if (!pflag)
574		environ = envinit;
575
576#ifdef LOGIN_CAP
577	if (nested == NULL && setusercontext(lc, pwd, pwd->pw_uid,
578	    LOGIN_SETLOGIN) != 0) {
579		syslog(LOG_ERR, "setusercontext failed");
580		exit(EXIT_FAILURE);
581	}
582	if (setusercontext(lc, pwd, pwd->pw_uid,
583	    (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETLOGIN))) != 0) {
584		syslog(LOG_ERR, "setusercontext failed");
585		exit(EXIT_FAILURE);
586	}
587#else
588	(void)setgid(pwd->pw_gid);
589
590	initgroups(username, pwd->pw_gid);
591
592	if (nested == NULL && setlogin(pwd->pw_name) < 0)
593		syslog(LOG_ERR, "setlogin() failure: %m");
594
595	/* Discard permissions last so can't get killed and drop core. */
596	if (rootlogin)
597		(void)setuid(0);
598	else
599		(void)setuid(pwd->pw_uid);
600#endif
601
602	if (*pwd->pw_shell == '\0')
603		pwd->pw_shell = __UNCONST(_PATH_BSHELL);
604#ifdef LOGIN_CAP
605	if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
606		if ((shell = strdup(shell)) == NULL) {
607			syslog(LOG_ERR, "Cannot alloc mem");
608			sleepexit(EXIT_FAILURE);
609		}
610		pwd->pw_shell = shell;
611	}
612#endif
613
614	(void)setenv("HOME", pwd->pw_dir, 1);
615	(void)setenv("SHELL", pwd->pw_shell, 1);
616	if (term[0] == '\0') {
617		const char *tt = stypeof(tty);
618#ifdef LOGIN_CAP
619		if (tt == NULL)
620			tt = login_getcapstr(lc, "term", NULL, NULL);
621#endif
622		/* unknown term -> "su" */
623		(void)strlcpy(term, tt != NULL ? tt : "su", sizeof(term));
624	}
625	(void)setenv("TERM", term, 0);
626	(void)setenv("LOGNAME", pwd->pw_name, 1);
627	(void)setenv("USER", pwd->pw_name, 1);
628
629#ifdef LOGIN_CAP
630	setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
631#else
632	(void)setenv("PATH", _PATH_DEFPATH, 0);
633#endif
634
635#ifdef KERBEROS5
636	if (krb5tkfile_env)
637		(void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
638#endif
639
640	/* If fflag is on, assume caller/authenticator has logged root login. */
641	if (rootlogin && fflag == 0) {
642		if (hostname)
643			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
644			    username, tty, hostname);
645		else
646			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
647			    username, tty);
648	}
649
650#if defined(KERBEROS5)
651	if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
652		(void)printf("Warning: no Kerberos tickets issued.\n");
653#endif
654
655	if (!quietlog) {
656		const char *fname;
657#ifdef LOGIN_CAP
658		fname = login_getcapstr(lc, "copyright", NULL, NULL);
659		if (fname != NULL && access(fname, F_OK) == 0)
660			motd(fname);
661		else
662#endif
663			(void)printf("%s", copyrightstr);
664
665#ifdef LOGIN_CAP
666		fname = login_getcapstr(lc, "welcome", NULL, NULL);
667		if (fname == NULL || access(fname, F_OK) != 0)
668#endif
669			fname = _PATH_MOTDFILE;
670		motd(fname);
671
672		(void)snprintf(tbuf,
673		    sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
674		if (stat(tbuf, &st) == 0 && st.st_size != 0)
675			(void)printf("You have %smail.\n",
676			    (st.st_mtime > st.st_atime) ? "new " : "");
677	}
678
679#ifdef LOGIN_CAP
680	login_close(lc);
681#endif
682
683	(void)signal(SIGALRM, SIG_DFL);
684	(void)signal(SIGQUIT, SIG_DFL);
685	(void)signal(SIGINT, SIG_DFL);
686	(void)signal(SIGTSTP, SIG_IGN);
687
688	tbuf[0] = '-';
689	(void)strlcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
690	    p + 1 : pwd->pw_shell, sizeof(tbuf) - 1);
691
692	/* Wait to change password until we're unprivileged */
693	if (need_chpass) {
694		if (!require_chpass)
695			(void)printf(
696"Warning: your password has expired. Please change it as soon as possible.\n");
697		else {
698			int	status;
699
700			(void)printf(
701		    "Your password has expired. Please choose a new one.\n");
702			switch (fork()) {
703			case -1:
704				warn("fork");
705				sleepexit(EXIT_FAILURE);
706			case 0:
707				execl(_PATH_BINPASSWD, "passwd", NULL);
708				_exit(EXIT_FAILURE);
709			default:
710				if (wait(&status) == -1 ||
711				    WEXITSTATUS(status))
712					sleepexit(EXIT_FAILURE);
713			}
714		}
715	}
716
717#ifdef KERBEROS5
718	if (login_krb5_get_tickets)
719		k5_write_creds();
720#endif
721	execlp(pwd->pw_shell, tbuf, NULL);
722	err(EXIT_FAILURE, "%s", pwd->pw_shell);
723}
724
725#if defined(KERBEROS5)
726/*
727 * This routine handles cleanup stuff, and the like.
728 * It exists only in the child process.
729 */
730static void
731dofork(void)
732{
733	pid_t child, wchild;
734
735	switch (child = fork()) {
736	case 0:
737		return; /* Child process */
738	case -1:
739		err(EXIT_FAILURE, "Can't fork");
740		/*NOTREACHED*/
741	default:
742		break;
743	}
744
745	/*
746	 * Setup stuff?  This would be things we could do in parallel
747	 * with login
748	 */
749	if (chdir("/") == -1)	/* Let's not keep the fs busy... */
750		err(EXIT_FAILURE, "Can't chdir to `/'");
751
752	/* If we're the parent, watch the child until it dies */
753	while ((wchild = wait(NULL)) != child)
754		if (wchild == -1)
755			err(EXIT_FAILURE, "Can't wait");
756
757	/* Cleanup stuff */
758	/* Run kdestroy to destroy tickets */
759	if (login_krb5_get_tickets)
760		k5destroy();
761
762	/* Leave */
763	exit(EXIT_SUCCESS);
764}
765#endif
766
767static void
768checknologin(char *fname)
769{
770	int fd, nchars;
771	char tbuf[8192];
772
773	if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
774		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
775			(void)write(fileno(stdout), tbuf, nchars);
776		sleepexit(EXIT_SUCCESS);
777	}
778}
779
780static void
781usage(void)
782{
783	(void)fprintf(stderr,
784	    "Usage: %s [-Ffps] [-a address] [-h hostname] [username]\n",
785	    getprogname());
786	exit(EXIT_FAILURE);
787}
788