login.c revision 76786
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
3423246Swosch#if 0
351590Srgrimesstatic char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3823246Swosch#endif
391590Srgrimes
401590Srgrimes#ifndef lint
4127605Scharnier#if 0
421590Srgrimesstatic char sccsid[] = "@(#)login.c	8.4 (Berkeley) 4/2/94";
4327605Scharnier#endif
4427605Scharnierstatic const char rcsid[] =
4550477Speter  "$FreeBSD: head/usr.bin/login/login.c 76786 2001-05-18 04:55:16Z obrien $";
461590Srgrimes#endif /* not lint */
471590Srgrimes
481590Srgrimes/*
491590Srgrimes * login [ name ]
501590Srgrimes * login -h hostname	(for telnetd, etc.)
511590Srgrimes * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
521590Srgrimes */
531590Srgrimes
5423246Swosch#include <sys/copyright.h>
551590Srgrimes#include <sys/param.h>
561590Srgrimes#include <sys/stat.h>
5757339Sshin#include <sys/socket.h>
581590Srgrimes#include <sys/time.h>
591590Srgrimes#include <sys/resource.h>
601590Srgrimes#include <sys/file.h>
6116423Sache#include <netinet/in.h>
6216423Sache#include <arpa/inet.h>
631590Srgrimes
641590Srgrimes#include <err.h>
651590Srgrimes#include <errno.h>
661590Srgrimes#include <grp.h>
6740102Smarkm#include <libutil.h>
6841079Sjdp#include <login_cap.h>
6916423Sache#include <netdb.h>
701590Srgrimes#include <pwd.h>
711590Srgrimes#include <setjmp.h>
721590Srgrimes#include <signal.h>
731590Srgrimes#include <stdio.h>
741590Srgrimes#include <stdlib.h>
751590Srgrimes#include <string.h>
761590Srgrimes#include <syslog.h>
771590Srgrimes#include <ttyent.h>
781590Srgrimes#include <unistd.h>
791590Srgrimes#include <utmp.h>
801590Srgrimes
8174874Smarkm#ifdef USE_PAM
8241279Sjdp#include <security/pam_appl.h>
8341279Sjdp#include <security/pam_misc.h>
8474874Smarkm#include <sys/wait.h>
8574874Smarkm#endif /* USE_PAM */
863702Spst
871590Srgrimes#include "pathnames.h"
881590Srgrimes
8957339Sshin/* wrapper for KAME-special getnameinfo() */
9057339Sshin#ifndef NI_WITHSCOPEID
9157339Sshin#define	NI_WITHSCOPEID	0
9257339Sshin#endif
9357339Sshin
941590Srgrimesvoid	 badlogin __P((char *));
951590Srgrimesvoid	 checknologin __P((void));
961590Srgrimesvoid	 dolastlog __P((int));
971590Srgrimesvoid	 getloginname __P((void));
9821528Sdavidnvoid	 motd __P((char *));
991590Srgrimesint	 rootterm __P((char *));
1001590Srgrimesvoid	 sigint __P((int));
1011590Srgrimesvoid	 sleepexit __P((int));
10223985Sdavidnvoid	 refused __P((char *,char *,int));
1031590Srgrimeschar	*stypeof __P((char *));
1041590Srgrimesvoid	 timedout __P((int));
10529922Smarkmint	 login_access __P((char *, char *));
1062224Sguidovoid     login_fbtab __P((char *, uid_t, gid_t));
1071590Srgrimes
10874874Smarkm#ifdef USE_PAM
10941279Sjdpstatic int auth_pam __P((void));
11072215Snectarstatic int export_pam_environment __P((void));
11172215Snectarstatic int ok_to_export __P((const char *));
11274874Smarkm
11374874Smarkmstatic pam_handle_t *pamh = NULL;
11474874Smarkmstatic char **environ_pam;
11574874Smarkm
11674874Smarkm#define PAM_END { \
11774874Smarkm	if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
11874874Smarkm		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); \
11974874Smarkm	if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) \
12074874Smarkm		syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); \
12174874Smarkm	if ((e = pam_end(pamh, e)) != PAM_SUCCESS) \
12274874Smarkm		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); \
12374874Smarkm}
12474874Smarkm#endif /* USE_PAM */
12541279Sjdpstatic int auth_traditional __P((void));
1261590Srgrimesextern void login __P((struct utmp *));
12727605Scharnierstatic void usage __P((void));
1281590Srgrimes
1291590Srgrimes#define	TTYGRPNAME	"tty"		/* name of group to own ttys */
13023985Sdavidn#define	DEFAULT_BACKOFF	3
13123985Sdavidn#define	DEFAULT_RETRIES	10
13276786Sobrien#define	DEFAULT_PROMPT		"login: "
13376786Sobrien#define	DEFAULT_PASSWD_PROMPT	"Password:"
1341590Srgrimes
1351590Srgrimes/*
1361590Srgrimes * This bounds the time given to login.  Not a define so it can
1371590Srgrimes * be patched on machines where it's too small.
1381590Srgrimes */
1391590Srgrimesu_int	timeout = 300;
1401590Srgrimes
14142272Seivind/* Buffer for signal handling of timeout */
14242272Seivindjmp_buf timeout_buf;
14342272Seivind
1441590Srgrimesstruct	passwd *pwd;
1451590Srgrimesint	failures;
14676786Sobrienchar	*term, *envinit[1], *hostname, *passwd_prompt, *prompt, *tty, *username;
14716423Sachechar    full_hostname[MAXHOSTNAMELEN];
1481590Srgrimes
1491590Srgrimesint
1501590Srgrimesmain(argc, argv)
1511590Srgrimes	int argc;
1521590Srgrimes	char *argv[];
1531590Srgrimes{
1541590Srgrimes	extern char **environ;
1551590Srgrimes	struct group *gr;
1561590Srgrimes	struct stat st;
1571590Srgrimes	struct timeval tp;
1581590Srgrimes	struct utmp utmp;
15923985Sdavidn	int rootok, retries, backoff;
1601590Srgrimes	int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
1614878Sugen	int changepass;
16223985Sdavidn	time_t warntime;
16335559Speter	uid_t uid, euid;
16446007Sache	gid_t egid;
16545431Sbrian	char *p, *ttyn;
16642272Seivind	char tbuf[MAXPATHLEN + 2];
16742272Seivind	char tname[sizeof(_PATH_TTY) + 10];
16823985Sdavidn	char *shell = NULL;
16921528Sdavidn	login_cap_t *lc = NULL;
17074874Smarkm#ifdef USE_PAM
17174874Smarkm	pid_t pid;
17274874Smarkm	int e;
17374874Smarkm#endif /* USE_PAM */
1741590Srgrimes
17542272Seivind	(void)signal(SIGQUIT, SIG_IGN);
17642272Seivind	(void)signal(SIGINT, SIG_IGN);
17742272Seivind	if (setjmp(timeout_buf)) {
17842272Seivind		if (failures)
17942272Seivind			badlogin(tbuf);
18042272Seivind		(void)fprintf(stderr,
18142272Seivind			      "Login timed out after %d seconds\n", timeout);
18242272Seivind		exit(0);
18342272Seivind	}
1841590Srgrimes	(void)signal(SIGALRM, timedout);
1851590Srgrimes	(void)alarm(timeout);
1861590Srgrimes	(void)setpriority(PRIO_PROCESS, 0, 0);
1871590Srgrimes
1881590Srgrimes	openlog("login", LOG_ODELAY, LOG_AUTH);
1891590Srgrimes
1901590Srgrimes	/*
1911590Srgrimes	 * -p is used by getty to tell login not to destroy the environment
1921590Srgrimes	 * -f is used to skip a second login authentication
1931590Srgrimes	 * -h is used by other servers to pass the name of the remote
1941590Srgrimes	 *    host to login so that it may be placed in utmp and wtmp
1951590Srgrimes	 */
1963205Spst	*full_hostname = '\0';
19723985Sdavidn	term = NULL;
1981590Srgrimes
1991590Srgrimes	fflag = hflag = pflag = 0;
2001590Srgrimes	uid = getuid();
20135557Speter	euid = geteuid();
20246007Sache	egid = getegid();
20324360Simp	while ((ch = getopt(argc, argv, "fh:p")) != -1)
2041590Srgrimes		switch (ch) {
2051590Srgrimes		case 'f':
2061590Srgrimes			fflag = 1;
2071590Srgrimes			break;
2081590Srgrimes		case 'h':
2091590Srgrimes			if (uid)
2101590Srgrimes				errx(1, "-h option: %s", strerror(EPERM));
2111590Srgrimes			hflag = 1;
2123205Spst			strncpy(full_hostname, optarg, sizeof(full_hostname)-1);
21336559Samurai
21445431Sbrian			trimdomain(optarg, UT_HOSTSIZE);
21536559Samurai
21616423Sache			if (strlen(optarg) > UT_HOSTSIZE) {
21757339Sshin				struct addrinfo hints, *res;
21857339Sshin				int ga_err;
21957339Sshin
22057339Sshin				memset(&hints, 0, sizeof(hints));
22157339Sshin				hints.ai_family = AF_UNSPEC;
22257339Sshin				ga_err = getaddrinfo(optarg, NULL, &hints,
22357339Sshin						    &res);
22457339Sshin				if (ga_err == 0) {
22557339Sshin					char hostbuf[MAXHOSTNAMELEN];
22616423Sache
22757339Sshin					getnameinfo(res->ai_addr,
22857339Sshin						    res->ai_addrlen,
22957339Sshin						    hostbuf,
23057339Sshin						    sizeof(hostbuf), NULL, 0,
23157339Sshin						    NI_NUMERICHOST|
23257339Sshin						    NI_WITHSCOPEID);
23357339Sshin					optarg = strdup(hostbuf);
23416423Sache				} else
23516423Sache					optarg = "invalid hostname";
23657339Sshin				if (res != NULL)
23757339Sshin					freeaddrinfo(res);
23816423Sache			}
2391590Srgrimes			hostname = optarg;
2401590Srgrimes			break;
2411590Srgrimes		case 'p':
2421590Srgrimes			pflag = 1;
2431590Srgrimes			break;
2441590Srgrimes		case '?':
2451590Srgrimes		default:
2461590Srgrimes			if (!uid)
2471590Srgrimes				syslog(LOG_ERR, "invalid flag %c", ch);
24827605Scharnier			usage();
2491590Srgrimes		}
2501590Srgrimes	argc -= optind;
2511590Srgrimes	argv += optind;
2521590Srgrimes
2531590Srgrimes	if (*argv) {
2541590Srgrimes		username = *argv;
2551590Srgrimes		ask = 0;
2561590Srgrimes	} else
2571590Srgrimes		ask = 1;
2581590Srgrimes
2591590Srgrimes	for (cnt = getdtablesize(); cnt > 2; cnt--)
2601590Srgrimes		(void)close(cnt);
2611590Srgrimes
2621590Srgrimes	ttyn = ttyname(STDIN_FILENO);
2631590Srgrimes	if (ttyn == NULL || *ttyn == '\0') {
2641590Srgrimes		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
2651590Srgrimes		ttyn = tname;
2661590Srgrimes	}
26723985Sdavidn	if ((tty = strrchr(ttyn, '/')) != NULL)
2681590Srgrimes		++tty;
2691590Srgrimes	else
2701590Srgrimes		tty = ttyn;
2711590Srgrimes
27223985Sdavidn	/*
27323985Sdavidn	 * Get "login-retries" & "login-backoff" from default class
27423985Sdavidn	 */
27523985Sdavidn	lc = login_getclass(NULL);
27676786Sobrien	prompt = login_getcapstr(lc, "prompt", DEFAULT_PROMPT, DEFAULT_PROMPT);
27776786Sobrien	passwd_prompt = login_getcapstr(lc, "passwd_prompt",
27876786Sobrien	    DEFAULT_PASSWD_PROMPT, DEFAULT_PASSWD_PROMPT);
27923985Sdavidn	retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES);
28023985Sdavidn	backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF);
28123985Sdavidn	login_close(lc);
28223985Sdavidn	lc = NULL;
28321528Sdavidn
2841590Srgrimes	for (cnt = 0;; ask = 1) {
2851590Srgrimes		if (ask) {
2861590Srgrimes			fflag = 0;
2871590Srgrimes			getloginname();
2881590Srgrimes		}
2891590Srgrimes		rootlogin = 0;
29021528Sdavidn		rootok = rootterm(tty); /* Default (auth may change) */
29121528Sdavidn
2921590Srgrimes		if (strlen(username) > UT_NAMESIZE)
2931590Srgrimes			username[UT_NAMESIZE] = '\0';
2941590Srgrimes
2951590Srgrimes		/*
2961590Srgrimes		 * Note if trying multiple user names; log failures for
2971590Srgrimes		 * previous user name, but don't bother logging one failure
2981590Srgrimes		 * for nonexistent name (mistyped username).
2991590Srgrimes		 */
3001590Srgrimes		if (failures && strcmp(tbuf, username)) {
3011590Srgrimes			if (failures > (pwd ? 0 : 1))
3021590Srgrimes				badlogin(tbuf);
3031590Srgrimes		}
30427605Scharnier		(void)strncpy(tbuf, username, sizeof tbuf-1);
30527605Scharnier		tbuf[sizeof tbuf-1] = '\0';
3061590Srgrimes
30741279Sjdp		pwd = getpwnam(username);
30823985Sdavidn
30923985Sdavidn		/*
3101590Srgrimes		 * if we have a valid account name, and it doesn't have a
3111590Srgrimes		 * password, or the -f option was specified and the caller
3121590Srgrimes		 * is root or the caller isn't changing their uid, don't
3131590Srgrimes		 * authenticate.
3141590Srgrimes		 */
31521528Sdavidn		if (pwd != NULL) {
3163205Spst			if (pwd->pw_uid == 0)
3173205Spst				rootlogin = 1;
3183205Spst
31923985Sdavidn			if (fflag && (uid == (uid_t)0 ||
32023985Sdavidn				      uid == (uid_t)pwd->pw_uid)) {
3213205Spst				/* already authenticated */
3223205Spst				break;
3233205Spst			} else if (pwd->pw_passwd[0] == '\0') {
32424321Sdavidn				if (!rootlogin || rootok) {
32524251Sdavidn					/* pretend password okay */
32624251Sdavidn					rval = 0;
32724251Sdavidn					goto ttycheck;
32824251Sdavidn				}
3293205Spst			}
3303205Spst		}
3313205Spst
3321590Srgrimes		fflag = 0;
3331590Srgrimes
3341590Srgrimes		(void)setpriority(PRIO_PROCESS, 0, -4);
3351590Srgrimes
33674874Smarkm#ifdef USE_PAM
33741279Sjdp		/*
33841279Sjdp		 * Try to authenticate using PAM.  If a PAM system error
33941279Sjdp		 * occurs, perhaps because of a botched configuration,
34041279Sjdp		 * then fall back to using traditional Unix authentication.
34141279Sjdp		 */
34241279Sjdp		if ((rval = auth_pam()) == -1)
34374874Smarkm#endif /* USE_PAM */
34441279Sjdp			rval = auth_traditional();
34523985Sdavidn
34641279Sjdp		(void)setpriority(PRIO_PROCESS, 0, 0);
34723985Sdavidn
34874874Smarkm#ifdef USE_PAM
34941279Sjdp		/*
35041279Sjdp		 * PAM authentication may have changed "pwd" to the
35141279Sjdp		 * entry for the template user.  Check again to see if
35241279Sjdp		 * this is a root login after all.
35341279Sjdp		 */
35441279Sjdp		if (pwd != NULL && pwd->pw_uid == 0)
35541279Sjdp			rootlogin = 1;
35674874Smarkm#endif /* USE_PAM */
3571590Srgrimes
3583205Spst	ttycheck:
3591590Srgrimes		/*
3601590Srgrimes		 * If trying to log in as root without Kerberos,
3611590Srgrimes		 * but with insecure terminal, refuse the login attempt.
3621590Srgrimes		 */
36324222Sdavidn		if (pwd && !rval) {
36424222Sdavidn			if (rootlogin && !rootok)
36524222Sdavidn				refused(NULL, "NOROOT", 0);
36624222Sdavidn			else	/* valid password & authenticated */
36724222Sdavidn				break;
3681590Srgrimes		}
3691590Srgrimes
3701590Srgrimes		(void)printf("Login incorrect\n");
3711590Srgrimes		failures++;
37223985Sdavidn
37323985Sdavidn		/*
37423985Sdavidn		 * we allow up to 'retry' (10) tries,
37523985Sdavidn		 * but after 'backoff' (3) we start backing off
37623985Sdavidn		 */
37723985Sdavidn		if (++cnt > backoff) {
37823985Sdavidn			if (cnt >= retries) {
3791590Srgrimes				badlogin(username);
3801590Srgrimes				sleepexit(1);
3811590Srgrimes			}
38238374Sjkoshy			sleep((u_int)((cnt - backoff) * 5));
3831590Srgrimes		}
3841590Srgrimes	}
3851590Srgrimes
3861590Srgrimes	/* committed to login -- turn off timeout */
3871590Srgrimes	(void)alarm((u_int)0);
3881590Srgrimes
3891590Srgrimes	endpwent();
3901590Srgrimes
39141279Sjdp	/*
39241279Sjdp	 * Establish the login class.
39341279Sjdp	 */
39441279Sjdp	lc = login_getpwclass(pwd);
39541279Sjdp
3961590Srgrimes	/* if user not super-user, check for disabled logins */
3971590Srgrimes	if (!rootlogin)
39821528Sdavidn		auth_checknologin(lc);
3991590Srgrimes
40021528Sdavidn	quietlog = login_getcapbool(lc, "hushlogin", 0);
40146007Sache	/* Switching needed for NFS with root access disabled */
40246007Sache	(void)setegid(pwd->pw_gid);
40335557Speter	(void)seteuid(rootlogin ? 0 : pwd->pw_uid);
40421528Sdavidn	if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
40526021Spst		if (login_getcapbool(lc, "requirehome", 0))
40623985Sdavidn			refused("Home directory not available", "HOMEDIR", 1);
40724485Sdavidn		if (chdir("/") < 0)
40823985Sdavidn			refused("Cannot find root directory", "ROOTDIR", 1);
40923985Sdavidn		if (!quietlog || *pwd->pw_dir)
41023985Sdavidn			printf("No home directory.\nLogging in with home = \"/\".\n");
41157546Sache		pwd->pw_dir = "/";
4121590Srgrimes	}
41335557Speter	(void)seteuid(euid);
41446007Sache	(void)setegid(egid);
41521528Sdavidn	if (!quietlog)
41621528Sdavidn		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
4171590Srgrimes
4181590Srgrimes	if (pwd->pw_change || pwd->pw_expire)
4191590Srgrimes		(void)gettimeofday(&tp, (struct timezone *)NULL);
4202532Sjkh
42130564Sjoerg#define DEFAULT_WARN  (2L * 7L * 86400L)  /* Two weeks */
42221528Sdavidn
42376710Seric
42476710Seric	warntime = login_getcaptime(lc, "warnexpire",
42576710Seric				    DEFAULT_WARN, DEFAULT_WARN);
42676710Seric
42776710Seric	if (pwd->pw_expire) {
42876710Seric		if (tp.tv_sec >= pwd->pw_expire) {
42976710Seric			refused("Sorry -- your account has expired",
43076710Seric				"EXPIRED", 1);
43176710Seric		} else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
43276710Seric		    (void)printf("Warning: your account expires on %s",
43376710Seric				 ctime(&pwd->pw_expire));
43476710Seric	}
43576710Seric
43623985Sdavidn	warntime = login_getcaptime(lc, "warnpassword",
43723985Sdavidn				    DEFAULT_WARN, DEFAULT_WARN);
43823985Sdavidn
4394878Sugen	changepass=0;
44021528Sdavidn	if (pwd->pw_change) {
4411590Srgrimes		if (tp.tv_sec >= pwd->pw_change) {
4421590Srgrimes			(void)printf("Sorry -- your password has expired.\n");
4434878Sugen			changepass=1;
44423985Sdavidn			syslog(LOG_INFO,
44523985Sdavidn			       "%s Password expired - forcing change",
44623985Sdavidn			       pwd->pw_name);
44723985Sdavidn		} else if (pwd->pw_change - tp.tv_sec < warntime && !quietlog)
44823985Sdavidn		    (void)printf("Warning: your password expires on %s",
44923985Sdavidn				 ctime(&pwd->pw_change));
45023985Sdavidn	}
45123985Sdavidn
45221528Sdavidn	if (lc != NULL) {
45321528Sdavidn		if (hostname) {
45457339Sshin			struct addrinfo hints, *res;
45557339Sshin			int ga_err;
45621528Sdavidn
45757339Sshin			memset(&hints, 0, sizeof(hints));
45857339Sshin			hints.ai_family = AF_UNSPEC;
45957339Sshin			ga_err = getaddrinfo(full_hostname, NULL, &hints,
46057339Sshin					     &res);
46157339Sshin			if (ga_err == 0) {
46257339Sshin				char hostbuf[MAXHOSTNAMELEN];
46357339Sshin
46457339Sshin				getnameinfo(res->ai_addr, res->ai_addrlen,
46557339Sshin					    hostbuf, sizeof(hostbuf), NULL, 0,
46657339Sshin					    NI_NUMERICHOST|NI_WITHSCOPEID);
46757339Sshin				optarg = strdup(hostbuf);
46857339Sshin			} else
46921528Sdavidn				optarg = NULL;
47057339Sshin			if (res != NULL)
47157339Sshin				freeaddrinfo(res);
47223985Sdavidn			if (!auth_hostok(lc, full_hostname, optarg))
47323985Sdavidn				refused("Permission denied", "HOST", 1);
47421528Sdavidn		}
47521528Sdavidn
47623985Sdavidn		if (!auth_ttyok(lc, tty))
47723985Sdavidn			refused("Permission denied", "TTY", 1);
47821528Sdavidn
47923985Sdavidn		if (!auth_timeok(lc, time(NULL)))
48023985Sdavidn			refused("Logins not available right now", "TIME", 1);
48121528Sdavidn	}
48223985Sdavidn        shell=login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
48323985Sdavidn	if (*pwd->pw_shell == '\0')
48423985Sdavidn		pwd->pw_shell = _PATH_BSHELL;
48523985Sdavidn	if (*shell == '\0')   /* Not overridden */
48623985Sdavidn		shell = pwd->pw_shell;
48723985Sdavidn	if ((shell = strdup(shell)) == NULL) {
48823985Sdavidn		syslog(LOG_NOTICE, "memory allocation error");
48923985Sdavidn		sleepexit(1);
49023985Sdavidn	}
49121528Sdavidn
49221528Sdavidn#ifdef LOGIN_ACCESS
49323985Sdavidn	if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
49423985Sdavidn		refused("Permission denied", "ACCESS", 1);
49521528Sdavidn#endif /* LOGIN_ACCESS */
49621528Sdavidn
4971590Srgrimes	/* Nothing else left to fail -- really log in. */
4981590Srgrimes	memset((void *)&utmp, 0, sizeof(utmp));
4991590Srgrimes	(void)time(&utmp.ut_time);
5001590Srgrimes	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
5011590Srgrimes	if (hostname)
5021590Srgrimes		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
5031590Srgrimes	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
5041590Srgrimes	login(&utmp);
5051590Srgrimes
5061590Srgrimes	dolastlog(quietlog);
5071590Srgrimes
5082224Sguido	/*
5092224Sguido	 * Set device protections, depending on what terminal the
5102224Sguido	 * user is logged in. This feature is used on Suns to give
5112224Sguido	 * console users better privacy.
5122224Sguido	 */
5132224Sguido	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
5142224Sguido
51550124Simp	/*
51650124Simp	 * Clear flags of the tty.  None should be set, and when the
51750124Simp	 * user sets them otherwise, this can cause the chown to fail.
51850124Simp	 * Since it isn't clear that flags are useful on character
51950124Simp	 * devices, we just clear them.
52050124Simp	 */
52159621Ssheldonh	if (chflags(ttyn, 0) && errno != EOPNOTSUPP)
52250124Simp		syslog(LOG_ERR, "chmod(%s): %m", ttyn);
52350124Simp	if (chown(ttyn, pwd->pw_uid,
52450124Simp	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
52550124Simp		syslog(LOG_ERR, "chmod(%s): %m", ttyn);
5261590Srgrimes
52750124Simp
52823985Sdavidn	/*
52923985Sdavidn	 * Preserve TERM if it happens to be already set.
53023985Sdavidn	 */
53124222Sdavidn	if ((term = getenv("TERM")) != NULL)
53224222Sdavidn		term = strdup(term);
5331590Srgrimes
53423985Sdavidn	/*
53523985Sdavidn	 * Exclude cons/vt/ptys only, assume dialup otherwise
53623985Sdavidn	 * TODO: Make dialup tty determination a library call
53723985Sdavidn	 * for consistency (finger etc.)
53823985Sdavidn	 */
53924894Sdavidn	if (hostname==NULL && isdialuptty(tty))
5401590Srgrimes		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
5411590Srgrimes
5423205Spst#ifdef LOGALL
5433205Spst	/*
5443205Spst	 * Syslog each successful login, so we don't have to watch hundreds
5453205Spst	 * of wtmp or lastlogin files.
5463205Spst	 */
54723985Sdavidn	if (hostname)
54823985Sdavidn		syslog(LOG_INFO, "login from %s on %s as %s",
54923985Sdavidn		       full_hostname, tty, pwd->pw_name);
55023985Sdavidn	else
55123985Sdavidn		syslog(LOG_INFO, "login on %s as %s",
55223985Sdavidn		       tty, pwd->pw_name);
55321528Sdavidn#endif
55421528Sdavidn
55523985Sdavidn	/*
55623985Sdavidn	 * If fflag is on, assume caller/authenticator has logged root login.
55723985Sdavidn	 */
55823985Sdavidn	if (rootlogin && fflag == 0)
55923985Sdavidn	{
56023985Sdavidn		if (hostname)
56123985Sdavidn			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
56223985Sdavidn			       username, tty, full_hostname);
56323985Sdavidn		else
56423985Sdavidn			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
56523985Sdavidn			       username, tty);
56623985Sdavidn	}
56723985Sdavidn
56823985Sdavidn	/*
56923985Sdavidn	 * Destroy environment unless user has requested its preservation.
57023985Sdavidn	 * We need to do this before setusercontext() because that may
57123985Sdavidn	 * set or reset some environment variables.
57223985Sdavidn	 */
57321528Sdavidn	if (!pflag)
57421528Sdavidn		environ = envinit;
57521528Sdavidn
57674874Smarkm#ifdef USE_PAM
57723985Sdavidn	/*
57872215Snectar	 * Add any environmental variables that the
57972215Snectar	 * PAM modules may have set.
58072215Snectar	 */
58174874Smarkm	if (pamh) {
58274874Smarkm		environ_pam = pam_getenvlist(pamh);
58374874Smarkm		if (environ_pam)
58474874Smarkm			export_pam_environment();
58574874Smarkm	}
58674874Smarkm#endif /* USE_PAM */
58772215Snectar
58872215Snectar	/*
58974874Smarkm	 * PAM modules might add supplementary groups during pam_setcred().
59074874Smarkm	 */
59174874Smarkm	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
59274874Smarkm                syslog(LOG_ERR, "setusercontext() failed - exiting");
59374874Smarkm		exit(1);
59474874Smarkm	}
59574874Smarkm
59674874Smarkm#ifdef USE_PAM
59774874Smarkm	if (pamh) {
59874874Smarkm		if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
59974874Smarkm			syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
60074874Smarkm		} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
60174874Smarkm			syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
60274874Smarkm		}
60374874Smarkm
60474874Smarkm		/*
60574874Smarkm		 * We must fork() before setuid() because we need to call
60674874Smarkm		 * pam_close_session() as root.
60774874Smarkm		 */
60874874Smarkm		pid = fork();
60974874Smarkm		if (pid < 0) {
61074874Smarkm			err(1, "fork");
61174874Smarkm			PAM_END;
61274874Smarkm			exit(0);
61374874Smarkm		} else if (pid) {
61474874Smarkm			/* parent - wait for child to finish, then cleanup session */
61574874Smarkm			wait(NULL);
61674874Smarkm			PAM_END;
61774874Smarkm			exit(0);
61874874Smarkm		} else {
61974874Smarkm			if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
62074874Smarkm				syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
62174874Smarkm		}
62274874Smarkm	}
62374874Smarkm#endif /* USE_PAM */
62474874Smarkm
62574874Smarkm	/*
62623985Sdavidn	 * We don't need to be root anymore, so
62721528Sdavidn	 * set the user and session context
62821528Sdavidn	 */
62941279Sjdp	if (setlogin(username) != 0) {
63041279Sjdp                syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
63141279Sjdp		exit(1);
63241279Sjdp	}
63341279Sjdp	if (setusercontext(lc, pwd, pwd->pw_uid,
63474874Smarkm	    LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
63521528Sdavidn                syslog(LOG_ERR, "setusercontext() failed - exiting");
63621528Sdavidn		exit(1);
6373205Spst	}
63821528Sdavidn
63923148Sache	(void)setenv("SHELL", pwd->pw_shell, 1);
64021528Sdavidn	(void)setenv("HOME", pwd->pw_dir, 1);
64123985Sdavidn	if (term != NULL && *term != '\0')
64221528Sdavidn		(void)setenv("TERM", term, 1);	/* Preset overrides */
64321528Sdavidn	else {
64423985Sdavidn		(void)setenv("TERM", stypeof(tty), 0);	/* Fallback doesn't */
64521528Sdavidn	}
64641279Sjdp	(void)setenv("LOGNAME", username, 1);
64741279Sjdp	(void)setenv("USER", username, 1);
64821528Sdavidn	(void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
64921528Sdavidn
6501590Srgrimes	if (!quietlog) {
65123985Sdavidn		char	*cw;
65223985Sdavidn
65323985Sdavidn		cw = login_getcapstr(lc, "copyright", NULL, NULL);
65421528Sdavidn		if (cw != NULL && access(cw, F_OK) == 0)
65521528Sdavidn			motd(cw);
65621528Sdavidn		else
65723985Sdavidn		    (void)printf("%s\n\t%s %s\n",
65823985Sdavidn	"Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
65923985Sdavidn	"The Regents of the University of California. ",
66023985Sdavidn	"All rights reserved.");
66123985Sdavidn
66223985Sdavidn		(void)printf("\n");
66323985Sdavidn
66421528Sdavidn		cw = login_getcapstr(lc, "welcome", NULL, NULL);
66521528Sdavidn		if (cw == NULL || access(cw, F_OK) != 0)
66621528Sdavidn			cw = _PATH_MOTDFILE;
66721528Sdavidn		motd(cw);
66823985Sdavidn
66921528Sdavidn		cw = getenv("MAIL");	/* $MAIL may have been set by class */
67021528Sdavidn		if (cw != NULL) {
67121528Sdavidn			strncpy(tbuf, cw, sizeof(tbuf));
67221528Sdavidn			tbuf[sizeof(tbuf)-1] = '\0';
67321528Sdavidn		} else
67423985Sdavidn			snprintf(tbuf, sizeof(tbuf), "%s/%s",
67523985Sdavidn				 _PATH_MAILDIR, pwd->pw_name);
6761590Srgrimes		if (stat(tbuf, &st) == 0 && st.st_size != 0)
67723985Sdavidn			(void)printf("You have %smail.\n",
67823985Sdavidn				     (st.st_mtime > st.st_atime) ? "new " : "");
6791590Srgrimes	}
6801590Srgrimes
68121528Sdavidn	login_close(lc);
6823205Spst
6831590Srgrimes	(void)signal(SIGALRM, SIG_DFL);
6841590Srgrimes	(void)signal(SIGQUIT, SIG_DFL);
6851590Srgrimes	(void)signal(SIGINT, SIG_DFL);
6861590Srgrimes	(void)signal(SIGTSTP, SIG_IGN);
6871590Srgrimes
6884878Sugen	if (changepass) {
68921528Sdavidn		if (system(_PATH_CHPASS) != 0)
6904878Sugen			sleepexit(1);
6914878Sugen	}
6924878Sugen
69323985Sdavidn	/*
69423985Sdavidn	 * Login shells have a leading '-' in front of argv[0]
69523985Sdavidn	 */
69623985Sdavidn	tbuf[0] = '-';
69723985Sdavidn	(void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell);
69823985Sdavidn
69921528Sdavidn	execlp(shell, tbuf, 0);
70021528Sdavidn	err(1, "%s", shell);
7011590Srgrimes}
7021590Srgrimes
70341279Sjdpstatic int
70441279Sjdpauth_traditional()
70541279Sjdp{
70641279Sjdp	int rval;
70741279Sjdp	char *p;
70841279Sjdp	char *ep;
70941279Sjdp	char *salt;
71041279Sjdp
71141279Sjdp	rval = 1;
71241279Sjdp	salt = pwd != NULL ? pwd->pw_passwd : "xx";
71341279Sjdp
71476786Sobrien	p = getpass(passwd_prompt);
71541279Sjdp	ep = crypt(p, salt);
71641279Sjdp
71741279Sjdp	if (pwd) {
71841279Sjdp		if (!p[0] && pwd->pw_passwd[0])
71941279Sjdp			ep = ":";
72041279Sjdp		if (strcmp(ep, pwd->pw_passwd) == 0)
72141279Sjdp			rval = 0;
72241279Sjdp	}
72341279Sjdp
72441279Sjdp	/* clear entered password */
72541279Sjdp	memset(p, 0, strlen(p));
72641279Sjdp	return rval;
72741279Sjdp}
72841279Sjdp
72974874Smarkm#ifdef USE_PAM
73041279Sjdp/*
73141279Sjdp * Attempt to authenticate the user using PAM.  Returns 0 if the user is
73241279Sjdp * authenticated, or 1 if not authenticated.  If some sort of PAM system
73341279Sjdp * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
73441279Sjdp * function returns -1.  This can be used as an indication that we should
73541279Sjdp * fall back to a different authentication mechanism.
73641279Sjdp */
73741279Sjdpstatic int
73841279Sjdpauth_pam()
73941279Sjdp{
74041279Sjdp	const char *tmpl_user;
74141279Sjdp	const void *item;
74241279Sjdp	int rval;
74341279Sjdp	int e;
74441279Sjdp	static struct pam_conv conv = { misc_conv, NULL };
74541279Sjdp
74641279Sjdp	if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) {
74741279Sjdp		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
74841279Sjdp		return -1;
74941279Sjdp	}
75041279Sjdp	if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
75141279Sjdp		syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
75241279Sjdp		    pam_strerror(pamh, e));
75341279Sjdp		return -1;
75441279Sjdp	}
75541279Sjdp	if (hostname != NULL &&
75641279Sjdp	    (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
75741279Sjdp		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
75841279Sjdp		    pam_strerror(pamh, e));
75941279Sjdp		return -1;
76041279Sjdp	}
76141279Sjdp	e = pam_authenticate(pamh, 0);
76241279Sjdp	switch (e) {
76341279Sjdp
76441279Sjdp	case PAM_SUCCESS:
76541279Sjdp		/*
76641279Sjdp		 * With PAM we support the concept of a "template"
76741279Sjdp		 * user.  The user enters a login name which is
76841279Sjdp		 * authenticated by PAM, usually via a remote service
76941279Sjdp		 * such as RADIUS or TACACS+.  If authentication
77041279Sjdp		 * succeeds, a different but related "template" name
77141279Sjdp		 * is used for setting the credentials, shell, and
77241279Sjdp		 * home directory.  The name the user enters need only
77341279Sjdp		 * exist on the remote authentication server, but the
77441279Sjdp		 * template name must be present in the local password
77541279Sjdp		 * database.
77641279Sjdp		 *
77741279Sjdp		 * This is supported by two various mechanisms in the
77841279Sjdp		 * individual modules.  However, from the application's
77941279Sjdp		 * point of view, the template user is always passed
78041279Sjdp		 * back as a changed value of the PAM_USER item.
78141279Sjdp		 */
78241279Sjdp		if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
78341279Sjdp		    PAM_SUCCESS) {
78441279Sjdp			tmpl_user = (const char *) item;
78541279Sjdp			if (strcmp(username, tmpl_user) != 0)
78641279Sjdp				pwd = getpwnam(tmpl_user);
78741279Sjdp		} else
78841279Sjdp			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
78941279Sjdp			    pam_strerror(pamh, e));
79041279Sjdp		rval = 0;
79141279Sjdp		break;
79241279Sjdp
79341279Sjdp	case PAM_AUTH_ERR:
79441279Sjdp	case PAM_USER_UNKNOWN:
79541279Sjdp	case PAM_MAXTRIES:
79641279Sjdp		rval = 1;
79741279Sjdp		break;
79841279Sjdp
79941279Sjdp	default:
80074874Smarkm		syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
80141279Sjdp		rval = -1;
80241279Sjdp		break;
80341279Sjdp	}
80474874Smarkm
80574874Smarkm	if (rval == 0) {
80674874Smarkm		e = pam_acct_mgmt(pamh, 0);
80774874Smarkm		if (e == PAM_NEW_AUTHTOK_REQD) {
80874874Smarkm			e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
80974874Smarkm			if (e != PAM_SUCCESS) {
81074874Smarkm				syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, e));
81174874Smarkm				rval = 1;
81274874Smarkm			}
81374874Smarkm		} else if (e != PAM_SUCCESS) {
81474874Smarkm			rval = 1;
81574874Smarkm		}
81641279Sjdp	}
81774874Smarkm
81874874Smarkm	if (rval != 0) {
81974874Smarkm		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
82074874Smarkm			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
82174874Smarkm		}
82274874Smarkm		pamh = NULL;
82374874Smarkm	}
82441279Sjdp	return rval;
82541279Sjdp}
82672215Snectar
82772215Snectarstatic int
82872215Snectarexport_pam_environment()
82972215Snectar{
83072215Snectar	char	**pp;
83172215Snectar
83272215Snectar	for (pp = environ_pam; *pp != NULL; pp++) {
83372215Snectar		if (ok_to_export(*pp))
83472215Snectar			(void) putenv(*pp);
83572215Snectar		free(*pp);
83672215Snectar	}
83772215Snectar	return PAM_SUCCESS;
83872215Snectar}
83972215Snectar
84072215Snectar/*
84172215Snectar * Sanity checks on PAM environmental variables:
84272215Snectar * - Make sure there is an '=' in the string.
84372215Snectar * - Make sure the string doesn't run on too long.
84472215Snectar * - Do not export certain variables.  This list was taken from the
84572215Snectar *   Solaris pam_putenv(3) man page.
84672215Snectar */
84772215Snectarstatic int
84872215Snectarok_to_export(s)
84972215Snectar	const char *s;
85072215Snectar{
85172215Snectar	static const char *noexport[] = {
85272215Snectar		"SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
85372215Snectar		"IFS", "PATH", NULL
85472215Snectar	};
85572215Snectar	const char **pp;
85672215Snectar	size_t n;
85772215Snectar
85872215Snectar	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
85972215Snectar		return 0;
86072215Snectar	if (strncmp(s, "LD_", 3) == 0)
86172215Snectar		return 0;
86272215Snectar	for (pp = noexport; *pp != NULL; pp++) {
86372215Snectar		n = strlen(*pp);
86472215Snectar		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
86572215Snectar			return 0;
86672215Snectar	}
86772215Snectar	return 1;
86872215Snectar}
86974874Smarkm#endif /* USE_PAM */
87041279Sjdp
87127605Scharnierstatic void
87227605Scharnierusage()
87327605Scharnier{
87427605Scharnier	(void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
87527605Scharnier	exit(1);
87627605Scharnier}
8771590Srgrimes
87823985Sdavidn/*
87923985Sdavidn * Allow for authentication style and/or kerberos instance
88074874Smarkm */
88121528Sdavidn
88221528Sdavidn#define	NBUFSIZ		UT_NAMESIZE + 64
88321528Sdavidn
8841590Srgrimesvoid
8851590Srgrimesgetloginname()
8861590Srgrimes{
8871590Srgrimes	int ch;
8881590Srgrimes	char *p;
8891590Srgrimes	static char nbuf[NBUFSIZ];
8901590Srgrimes
8911590Srgrimes	for (;;) {
89276786Sobrien		(void)printf(prompt);
8931590Srgrimes		for (p = nbuf; (ch = getchar()) != '\n'; ) {
8941590Srgrimes			if (ch == EOF) {
8951590Srgrimes				badlogin(username);
8961590Srgrimes				exit(0);
8971590Srgrimes			}
8981590Srgrimes			if (p < nbuf + (NBUFSIZ - 1))
8991590Srgrimes				*p++ = ch;
9001590Srgrimes		}
90159645Ssheldonh		if (p > nbuf) {
9021590Srgrimes			if (nbuf[0] == '-')
9031590Srgrimes				(void)fprintf(stderr,
9041590Srgrimes				    "login names may not start with '-'.\n");
9051590Srgrimes			else {
9061590Srgrimes				*p = '\0';
9071590Srgrimes				username = nbuf;
9081590Srgrimes				break;
9091590Srgrimes			}
91059645Ssheldonh		}
9111590Srgrimes	}
9121590Srgrimes}
9131590Srgrimes
9141590Srgrimesint
9151590Srgrimesrootterm(ttyn)
9161590Srgrimes	char *ttyn;
9171590Srgrimes{
9181590Srgrimes	struct ttyent *t;
91923985Sdavidn
9201590Srgrimes	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
9211590Srgrimes}
9221590Srgrimes
92321528Sdavidnvolatile int motdinterrupt;
9241590Srgrimes
92521528Sdavidn/* ARGSUSED */
9261590Srgrimesvoid
92721528Sdavidnsigint(signo)
92821528Sdavidn	int signo;
9291590Srgrimes{
93021528Sdavidn	motdinterrupt = 1;
93121528Sdavidn}
93221528Sdavidn
93321528Sdavidnvoid
93421528Sdavidnmotd(motdfile)
93521528Sdavidn	char *motdfile;
93621528Sdavidn{
9371590Srgrimes	int fd, nchars;
9381590Srgrimes	sig_t oldint;
93921528Sdavidn	char tbuf[256];
9401590Srgrimes
94121528Sdavidn	if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
9421590Srgrimes		return;
94321528Sdavidn	motdinterrupt = 0;
9441590Srgrimes	oldint = signal(SIGINT, sigint);
94521528Sdavidn	while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
94621528Sdavidn		(void)write(fileno(stdout), tbuf, nchars);
9471590Srgrimes	(void)signal(SIGINT, oldint);
9481590Srgrimes	(void)close(fd);
9491590Srgrimes}
9501590Srgrimes
9511590Srgrimes/* ARGSUSED */
9521590Srgrimesvoid
9531590Srgrimestimedout(signo)
9541590Srgrimes	int signo;
9551590Srgrimes{
95642272Seivind	longjmp(timeout_buf, signo);
9571590Srgrimes}
9581590Srgrimes
9591590Srgrimes
9601590Srgrimesvoid
9611590Srgrimesdolastlog(quiet)
9621590Srgrimes	int quiet;
9631590Srgrimes{
9641590Srgrimes	struct lastlog ll;
9651590Srgrimes	int fd;
9661590Srgrimes
9671590Srgrimes	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
9681590Srgrimes		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
9691590Srgrimes		if (!quiet) {
9701590Srgrimes			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
9711590Srgrimes			    ll.ll_time != 0) {
9721590Srgrimes				(void)printf("Last login: %.*s ",
9731590Srgrimes				    24-5, (char *)ctime(&ll.ll_time));
9741590Srgrimes				if (*ll.ll_host != '\0')
9751590Srgrimes					(void)printf("from %.*s\n",
9761590Srgrimes					    (int)sizeof(ll.ll_host),
9771590Srgrimes					    ll.ll_host);
9781590Srgrimes				else
9791590Srgrimes					(void)printf("on %.*s\n",
9801590Srgrimes					    (int)sizeof(ll.ll_line),
9811590Srgrimes					    ll.ll_line);
9821590Srgrimes			}
9831590Srgrimes			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
9841590Srgrimes		}
9851590Srgrimes		memset((void *)&ll, 0, sizeof(ll));
9861590Srgrimes		(void)time(&ll.ll_time);
9871590Srgrimes		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
9881590Srgrimes		if (hostname)
9891590Srgrimes			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
9901590Srgrimes		(void)write(fd, (char *)&ll, sizeof(ll));
9911590Srgrimes		(void)close(fd);
9921590Srgrimes	}
9931590Srgrimes}
9941590Srgrimes
9951590Srgrimesvoid
9961590Srgrimesbadlogin(name)
9971590Srgrimes	char *name;
9981590Srgrimes{
9991590Srgrimes
10001590Srgrimes	if (failures == 0)
10011590Srgrimes		return;
10021590Srgrimes	if (hostname) {
10031590Srgrimes		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
100416423Sache		    failures, failures > 1 ? "S" : "", full_hostname);
10051590Srgrimes		syslog(LOG_AUTHPRIV|LOG_NOTICE,
10061590Srgrimes		    "%d LOGIN FAILURE%s FROM %s, %s",
100716423Sache		    failures, failures > 1 ? "S" : "", full_hostname, name);
10081590Srgrimes	} else {
10091590Srgrimes		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
10101590Srgrimes		    failures, failures > 1 ? "S" : "", tty);
10111590Srgrimes		syslog(LOG_AUTHPRIV|LOG_NOTICE,
10121590Srgrimes		    "%d LOGIN FAILURE%s ON %s, %s",
10131590Srgrimes		    failures, failures > 1 ? "S" : "", tty, name);
10141590Srgrimes	}
101542272Seivind	failures = 0;
10161590Srgrimes}
10171590Srgrimes
10181590Srgrimes#undef	UNKNOWN
10191590Srgrimes#define	UNKNOWN	"su"
10201590Srgrimes
10211590Srgrimeschar *
10221590Srgrimesstypeof(ttyid)
10231590Srgrimes	char *ttyid;
10241590Srgrimes{
102523985Sdavidn
10261590Srgrimes	struct ttyent *t;
102723985Sdavidn
10281590Srgrimes	return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
10291590Srgrimes}
10301590Srgrimes
10311590Srgrimesvoid
103223985Sdavidnrefused(msg, rtype, lout)
103323985Sdavidn	char *msg;
103423985Sdavidn	char *rtype;
103523985Sdavidn	int lout;
103623985Sdavidn{
103723985Sdavidn
103823985Sdavidn	if (msg != NULL)
103923985Sdavidn	    printf("%s.\n", msg);
104023985Sdavidn	if (hostname)
104123985Sdavidn		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
104223985Sdavidn		       pwd->pw_name, rtype, full_hostname, tty);
104323985Sdavidn	else
104423985Sdavidn		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
104523985Sdavidn		       pwd->pw_name, rtype, tty);
104623985Sdavidn	if (lout)
104723985Sdavidn		sleepexit(1);
104823985Sdavidn}
104923985Sdavidn
105023985Sdavidnvoid
10511590Srgrimessleepexit(eval)
10521590Srgrimes	int eval;
10531590Srgrimes{
105423985Sdavidn
10551590Srgrimes	(void)sleep(5);
10561590Srgrimes	exit(eval);
10571590Srgrimes}
1058