1/*-
2 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technologies, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 * Portions copyright (c) 1999-2007 Apple Inc.  All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 *    must display the following acknowledgement:
23 *	This product includes software developed by the University of
24 *	California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 *    may be used to endorse or promote products derived from this software
27 *    without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42#if 0
43#ifndef lint
44static char sccsid[] = "@(#)login.c	8.4 (Berkeley) 4/2/94";
45#endif
46#endif
47
48#include <sys/cdefs.h>
49__FBSDID("$FreeBSD: src/usr.bin/login/login.c,v 1.106 2007/07/04 00:00:40 scf Exp $");
50
51/*
52 * login [ name ]
53 * login -h hostname	(for telnetd, etc.)
54 * login -f name	(for pre-authenticated login: datakit, xterm, etc.)
55 */
56
57#ifndef __APPLE__
58#include <sys/copyright.h>
59#endif
60#ifdef __APPLE__
61#include <TargetConditionals.h>
62#endif
63#include <sys/param.h>
64#include <sys/file.h>
65#include <sys/stat.h>
66#include <sys/time.h>
67#include <sys/resource.h>
68#include <sys/wait.h>
69
70#include <err.h>
71#include <errno.h>
72#include <grp.h>
73#ifdef __APPLE__
74#include <util.h>
75#else
76#include <libutil.h>
77#endif
78#ifdef LOGIN_CAP
79#include <login_cap.h>
80#endif
81#include <pwd.h>
82#include <setjmp.h>
83#include <signal.h>
84#include <stdio.h>
85#include <stdlib.h>
86#include <string.h>
87#include <syslog.h>
88#include <ttyent.h>
89#include <unistd.h>
90#ifdef __APPLE__
91#include <utmpx.h>
92#ifdef USE_PAM
93#else /* !USE_PAM */
94#ifndef _UTX_USERSIZE
95#define _UTX_USERSIZE MAXLOGNAME
96#endif
97#endif /* USE_PAM */
98#endif /* __APPLE__ */
99
100#include <sys/types.h>
101#include <sys/socket.h>
102#include <netinet/in.h>
103#include <arpa/inet.h>
104#include <netdb.h>
105
106#ifdef USE_BSM_AUDIT
107#include <bsm/libbsm.h>
108#include <bsm/audit.h>
109#include <bsm/audit_session.h>
110#include <bsm/audit_uevents.h>
111#endif
112
113#ifdef __APPLE__
114#include <mach/mach_types.h>
115#include <mach/task.h>
116#include <mach/mach_init.h>
117#include <servers/bootstrap.h>
118
119#include <sys/file.h>
120#include <tzfile.h>
121#endif /* __APPLE__ */
122
123#ifdef USE_PAM
124#include <security/pam_appl.h>
125#include <security/openpam.h>
126#endif /* USE_PAM */
127
128#include "login.h"
129#include "pathnames.h"
130
131#ifdef USE_PAM
132static int		 auth_pam(int skip_auth);
133#endif /* USE_PAM */
134static void		 bail(int, int);
135#ifdef USE_PAM
136static int		 export(const char *);
137static void		 export_pam_environment(void);
138#endif /* USE_PAM */
139static int		 motd(const char *);
140static void		 badlogin(char *);
141static char		*getloginname(void);
142#ifdef USE_PAM
143static void		 pam_syslog(const char *);
144static void		 pam_cleanup(void);
145#endif /* USE_PAM */
146static void		 refused(const char *, const char *, int);
147static const char	*stypeof(char *);
148static void		 sigint(int);
149static void		 timedout(int);
150static void		 usage(void);
151
152#ifdef __APPLE__
153static void		 dolastlog(int);
154static void		 handle_sighup(int);
155
156#ifndef USE_PAM
157static void		 checknologin(void);
158static int		 rootterm(const char *);
159#endif /* !USE_PAM */
160#endif /* __APPLE__ */
161
162#define	TTYGRPNAME		"tty"			/* group to own ttys */
163#define	DEFAULT_BACKOFF		3
164#define	DEFAULT_RETRIES		10
165#define	DEFAULT_PROMPT		"login: "
166#define	DEFAULT_PASSWD_PROMPT	"Password:"
167#define	TERM_UNKNOWN		"su"
168#define	DEFAULT_WARN		(2L * 7L * 86400L)	/* Two weeks */
169#define NO_SLEEP_EXIT		0
170#define SLEEP_EXIT		5
171
172/*
173 * This bounds the time given to login.  Not a define so it can
174 * be patched on machines where it's too small.
175 */
176static u_int		timeout = 300;
177
178/* Buffer for signal handling of timeout */
179static jmp_buf		 timeout_buf;
180
181struct passwd		*pwd;
182static int		 failures;
183
184static char		*envinit[1];	/* empty environment list */
185
186/*
187 * Command line flags and arguments
188 */
189static int		 fflag;		/* -f: do not perform authentication */
190#ifdef __APPLE__
191static int		 lflag;		/*   -l: login session to the commmand that follows username */
192#endif
193static int		 hflag;		/* -h: login from remote host */
194static char		*hostname;	/* hostname from command line */
195static int		 pflag;		/* -p: preserve environment */
196
197/*
198 * User name
199 */
200static char		*username;	/* user name */
201static char		*olduser;	/* previous user name */
202
203/*
204 * Prompts
205 */
206static char		 default_prompt[] = DEFAULT_PROMPT;
207static const char	*prompt;
208static char		 default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
209static const char	*passwd_prompt;
210
211static char		*tty;
212
213/*
214 * PAM data
215 */
216#ifdef USE_PAM
217static pam_handle_t	*pamh = NULL;
218static struct pam_conv	 pamc = { openpam_ttyconv, NULL };
219static int		 pam_err;
220static int		 pam_silent = PAM_SILENT;
221static int		 pam_cred_established;
222static int		 pam_session_established;
223#endif /* USE_PAM */
224
225#ifdef __APPLE__
226pid_t pid;
227
228#ifdef USE_PAM
229static struct lastlogx lastlog;
230#endif /* USE_PAM */
231
232#ifdef USE_BSM_AUDIT
233extern au_tid_addr_t tid;
234#endif /* USE_BSM_AUDIT */
235#endif /* __APPLE__ */
236
237int
238main(int argc, char *argv[])
239{
240	struct group *gr;
241	struct stat st;
242	int retries, backoff;
243	int ask, ch, cnt, quietlog = 0, rootlogin, rval;
244	uid_t uid, euid;
245	gid_t egid;
246	char *term;
247	char *p, *ttyn;
248	char tname[sizeof(_PATH_TTY) + 10];
249	char *arg0;
250	const char *tp;
251#ifdef __APPLE__
252	int prio;
253#ifdef USE_PAM
254	const char *name = "login";	/* PAM config */
255#else
256	struct utmpx utmp;
257#endif /* USE_PAM */
258	const char *shell = NULL;
259#endif /* !__APPLE__ */
260#ifdef LOGIN_CAP
261	login_cap_t *lc = NULL;
262	login_cap_t *lc_user = NULL;
263#endif /* LOGIN_CAP */
264#ifndef __APPLE__
265	pid_t pid;
266#endif
267#ifdef USE_BSM_AUDIT
268	char auditsuccess = 1;
269#endif
270
271	(void)signal(SIGQUIT, SIG_IGN);
272	(void)signal(SIGINT, SIG_IGN);
273	(void)signal(SIGHUP, SIG_IGN);
274	if (setjmp(timeout_buf)) {
275		if (failures)
276			badlogin(username);
277		(void)fprintf(stderr, "Login timed out after %d seconds\n",
278		    timeout);
279		bail(NO_SLEEP_EXIT, 0);
280	}
281	(void)signal(SIGALRM, timedout);
282	(void)alarm(timeout);
283#ifdef __APPLE__
284	prio = getpriority(PRIO_PROCESS, 0);
285#endif
286	(void)setpriority(PRIO_PROCESS, 0, 0);
287
288	openlog("login", LOG_ODELAY, LOG_AUTH);
289
290	uid = getuid();
291	euid = geteuid();
292	egid = getegid();
293
294#ifdef __APPLE__
295	while ((ch = getopt(argc, argv, "1fh:lpq")) != -1)
296#else
297	while ((ch = getopt(argc, argv, "fh:p")) != -1)
298#endif
299		switch (ch) {
300		case 'f':
301			fflag = 1;
302			break;
303		case 'h':
304			if (uid != 0)
305				errx(1, "-h option: %s", strerror(EPERM));
306			if (strlen(optarg) >= MAXHOSTNAMELEN)
307				errx(1, "-h option: %s: exceeds maximum "
308				    "hostname size", optarg);
309			hflag = 1;
310			hostname = optarg;
311			break;
312		case 'p':
313			pflag = 1;
314			break;
315#ifdef __APPLE__
316		case '1':
317			break;
318		case 'l':
319			lflag = 1;
320			break;
321		case 'q':
322			quietlog = 1;
323			break;
324#endif
325		case '?':
326		default:
327			if (uid == 0)
328				syslog(LOG_ERR, "invalid flag %c", ch);
329			usage();
330		}
331	argc -= optind;
332	argv += optind;
333
334	if (argc > 0) {
335		username = strdup(*argv);
336		if (username == NULL)
337			err(1, "strdup()");
338		ask = 0;
339#ifdef __APPLE__
340		argv++;
341#endif /* __APPLE__ */
342	} else {
343		ask = 1;
344	}
345
346#ifndef __APPLE__
347	setproctitle("-%s", getprogname());
348#endif /* !__APPLE__ */
349
350	for (cnt = getdtablesize(); cnt > 2; cnt--)
351		(void)close(cnt);
352
353	/*
354	 * Get current TTY
355	 */
356	ttyn = ttyname(STDIN_FILENO);
357	if (ttyn == NULL || *ttyn == '\0') {
358		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
359		ttyn = tname;
360	}
361	if ((tty = strrchr(ttyn, '/')) != NULL)
362		++tty;
363	else
364		tty = ttyn;
365
366#ifdef LOGIN_CAP
367	/*
368	 * Get "login-retries" & "login-backoff" from default class
369	 */
370	lc = login_getclass(NULL);
371	prompt = login_getcapstr(lc, "login_prompt",
372	    default_prompt, default_prompt);
373	passwd_prompt = login_getcapstr(lc, "passwd_prompt",
374	    default_passwd_prompt, default_passwd_prompt);
375	retries = login_getcapnum(lc, "login-retries",
376	    DEFAULT_RETRIES, DEFAULT_RETRIES);
377	backoff = login_getcapnum(lc, "login-backoff",
378	    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
379	login_close(lc);
380	lc = NULL;
381#else /* !LOGIN_CAP */
382	prompt = default_prompt;
383	passwd_prompt = default_passwd_prompt;
384	retries = DEFAULT_RETRIES;
385	backoff = DEFAULT_BACKOFF;
386#endif /* !LOGIN_CAP */
387
388#ifdef __APPLE__
389#ifdef USE_BSM_AUDIT
390	/* Set the terminal id */
391	au_tid_t old_tid;
392	audit_set_terminal_id(&old_tid);
393	tid.at_type = AU_IPv4;
394	tid.at_addr[0] = old_tid.machine;
395	if (fstat(STDIN_FILENO, &st) < 0) {
396		fprintf(stderr, "login: Unable to stat terminal\n");
397		au_login_fail("Unable to stat terminal", 1);
398		exit(-1);
399	}
400	if (S_ISCHR(st.st_mode)) {
401		tid.at_port = st.st_rdev;
402	} else {
403		tid.at_port = 0;
404	}
405#endif /* USE_BSM_AUDIT */
406#endif /* __APPLE__ */
407
408	/*
409	 * Try to authenticate the user until we succeed or time out.
410	 */
411	for (cnt = 0;; ask = 1) {
412		if (ask) {
413			fflag = 0;
414			if (olduser != NULL)
415				free(olduser);
416			olduser = username;
417			username = getloginname();
418		}
419		rootlogin = 0;
420
421#ifdef __APPLE__
422		if (strlen(username) > _UTX_USERSIZE)
423			username[_UTX_USERSIZE] = '\0';
424#endif /* __APPLE__ */
425
426		/*
427		 * Note if trying multiple user names; log failures for
428		 * previous user name, but don't bother logging one failure
429		 * for nonexistent name (mistyped username).
430		 */
431		if (failures && strcmp(olduser, username) != 0) {
432			if (failures > (pwd ? 0 : 1))
433				badlogin(olduser);
434		}
435
436#ifdef __APPLE__
437#ifdef USE_PAM
438	/* get lastlog info before PAM make a new entry */
439	if (!quietlog)
440		getlastlogxbyname(username, &lastlog);
441#endif /* USE_PAM */
442#endif /* __APPLE__ */
443
444		pwd = getpwnam(username);
445
446#ifdef USE_PAM
447		/*
448		 * Load the PAM policy and set some variables
449		 */
450#ifdef __APPLE__
451		if (fflag && (pwd != NULL) && (pwd->pw_uid == uid)) {
452			name = "login.term";
453		}
454#endif
455		pam_err = pam_start(name, username, &pamc, &pamh);
456		if (pam_err != PAM_SUCCESS) {
457			pam_syslog("pam_start()");
458#ifdef USE_BSM_AUDIT
459			au_login_fail("PAM Error", 1);
460#endif
461			bail(NO_SLEEP_EXIT, 1);
462		}
463		pam_err = pam_set_item(pamh, PAM_TTY, tty);
464		if (pam_err != PAM_SUCCESS) {
465			pam_syslog("pam_set_item(PAM_TTY)");
466#ifdef USE_BSM_AUDIT
467			au_login_fail("PAM Error", 1);
468#endif
469			bail(NO_SLEEP_EXIT, 1);
470		}
471		pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
472		if (pam_err != PAM_SUCCESS) {
473			pam_syslog("pam_set_item(PAM_RHOST)");
474#ifdef USE_BSM_AUDIT
475			au_login_fail("PAM Error", 1);
476#endif
477			bail(NO_SLEEP_EXIT, 1);
478		}
479#endif /* USE_PAM */
480
481		if (pwd != NULL && pwd->pw_uid == 0)
482			rootlogin = 1;
483
484		/*
485		 * If the -f option was specified and the caller is
486		 * root or the caller isn't changing their uid, don't
487		 * authenticate.
488		 */
489		if (pwd != NULL && fflag &&
490		    (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) {
491#ifdef USE_PAM
492			rval = auth_pam(fflag);
493#else
494			rval = 0;
495#endif /* USE_PAM */
496#ifdef USE_BSM_AUDIT
497			auditsuccess = 0; /* opened a terminal window only */
498#endif
499
500#ifdef __APPLE__
501#ifndef USE_PAM
502		/* If the account doesn't have a password, authenticate. */
503		} else if (pwd != NULL && pwd->pw_passwd[0] == '\0') {
504			rval = 0;
505#endif /* !USE_PAM */
506#endif /* __APPLE__ */
507		} else if( pwd ) {
508			fflag = 0;
509			(void)setpriority(PRIO_PROCESS, 0, -4);
510#ifdef USE_PAM
511			rval = auth_pam(fflag);
512#else
513		{
514			char* salt = pwd->pw_passwd;
515			char* p = getpass(passwd_prompt);
516			rval = strcmp(crypt(p, salt), salt);
517			memset(p, 0, strlen(p));
518		}
519#endif
520			(void)setpriority(PRIO_PROCESS, 0, 0);
521		} else {
522			rval = -1;
523		}
524
525#ifdef __APPLE__
526#ifndef USE_PAM
527		/*
528		 * If trying to log in as root but with insecure terminal,
529		 * refuse the login attempt.
530		 */
531		if (pwd && rootlogin && !rootterm(tty)) {
532			refused("root login refused on this terminal", "ROOTTERM", 0);
533#ifdef USE_BSM_AUDIT
534			au_login_fail("Login refused on terminal", 0);
535#endif
536			continue;
537		}
538#endif /* !USE_PAM */
539#endif /* __APPLE__ */
540
541		if (pwd && rval == 0)
542			break;
543
544#ifdef USE_PAM
545		pam_cleanup();
546#endif /* USE_PAM */
547
548		/*
549		 * We are not exiting here, but this corresponds to a failed
550		 * login event, so set exitstatus to 1.
551		 */
552#ifdef USE_BSM_AUDIT
553		au_login_fail("Login incorrect", 1);
554#endif
555
556		(void)printf("Login incorrect\n");
557		failures++;
558
559		pwd = NULL;
560
561		/*
562		 * Allow up to 'retry' (10) attempts, but start
563		 * backing off after 'backoff' (3) attempts.
564		 */
565		if (++cnt > backoff) {
566			if (cnt >= retries) {
567				badlogin(username);
568				bail(SLEEP_EXIT, 1);
569			}
570			sleep((u_int)((cnt - backoff) * 5));
571		}
572	}
573
574	/* committed to login -- turn off timeout */
575	(void)alarm((u_int)0);
576	(void)signal(SIGHUP, SIG_DFL);
577
578	endpwent();
579
580#ifdef __APPLE__
581	if (!pwd) {
582		fprintf(stderr, "login: Unable to find user: %s\n", username);
583		exit(1);
584	}
585
586#ifndef USE_PAM
587	/* if user not super-user, check for disabled logins */
588	if (!rootlogin)
589		checknologin();
590#endif /* !USE_PAM */
591#endif /* APPLE */
592
593#ifdef USE_BSM_AUDIT
594	/* Audit successful login. */
595	if (auditsuccess)
596		au_login_success(fflag);
597#endif
598
599#ifdef LOGIN_CAP
600	/*
601	 * Establish the login class.
602	 */
603	lc = login_getpwclass(pwd);
604	lc_user = login_getuserclass(pwd);
605
606	if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0)))
607		quietlog = login_getcapbool(lc, "hushlogin", 0);
608#endif /* LOGIN_CAP */
609
610#ifndef __APPLE__
611	/*
612	 * Switching needed for NFS with root access disabled.
613	 *
614	 * XXX: This change fails to modify the additional groups for the
615	 * process, and as such, may restrict rights normally granted
616	 * through those groups.
617	 */
618	(void)setegid(pwd->pw_gid);
619	(void)seteuid(rootlogin ? 0 : pwd->pw_uid);
620
621	if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
622#ifdef LOGIN_CAP
623		if (login_getcapbool(lc, "requirehome", 0))
624			refused("Home directory not available", "HOMEDIR", 1);
625#endif /* LOGIN_CAP */
626		if (chdir("/") < 0)
627			refused("Cannot find root directory", "ROOTDIR", 1);
628		if (!quietlog || *pwd->pw_dir)
629			printf("No home directory.\nLogging in with home = \"/\".\n");
630		pwd->pw_dir = strdup("/");
631		if (pwd->pw_dir == NULL) {
632			syslog(LOG_NOTICE, "strdup(): %m");
633			bail(SLEEP_EXIT, 1);
634		}
635	}
636
637	(void)seteuid(euid);
638	(void)setegid(egid);
639#endif /* !__APPLE__ */
640	if (!quietlog) {
641		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
642#ifdef USE_PAM
643		if (!quietlog)
644			pam_silent = 0;
645#endif /* USE_PAM */
646	}
647
648#ifdef __APPLE__
649	/* Nothing else left to fail -- really log in. */
650#ifndef USE_PAM
651	memset((void *)&utmp, 0, sizeof(utmp));
652	(void)gettimeofday(&utmp.ut_tv, NULL);
653	(void)strncpy(utmp.ut_user, username, sizeof(utmp.ut_user));
654	if (hostname)
655		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
656	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
657	utmp.ut_type = USER_PROCESS | UTMPX_AUTOFILL_MASK;
658	utmp.ut_pid = getpid();
659	pututxline(&utmp);
660#endif /* USE_PAM */
661
662	shell = "";
663#endif /* !__APPLE__ */
664#ifdef LOGIN_CAP
665	shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
666#endif /* !LOGIN_CAP */
667	if (*pwd->pw_shell == '\0')
668		pwd->pw_shell = strdup(_PATH_BSHELL);
669	if (pwd->pw_shell == NULL) {
670		syslog(LOG_NOTICE, "strdup(): %m");
671		bail(SLEEP_EXIT, 1);
672	}
673
674#if defined(__APPLE__) && TARGET_OS_EMBEDDED
675	/* on embedded, allow a shell to live in /var/debug_mount/bin/sh */
676#define _PATH_DEBUGSHELL	"/var/debug_mount/bin/sh"
677        if (stat(pwd->pw_shell, &st) != 0) {
678        	if (stat(_PATH_DEBUGSHELL, &st) == 0) {
679        		pwd->pw_shell = strdup(_PATH_DEBUGSHELL);
680        	}
681        }
682#endif
683
684	if (*shell == '\0')   /* Not overridden */
685		shell = pwd->pw_shell;
686	if ((shell = strdup(shell)) == NULL) {
687		syslog(LOG_NOTICE, "strdup(): %m");
688		bail(SLEEP_EXIT, 1);
689	}
690
691#ifdef __APPLE__
692	dolastlog(quietlog);
693#endif
694
695#ifndef __APPLE__
696	/*
697	 * Set device protections, depending on what terminal the
698	 * user is logged in. This feature is used on Suns to give
699	 * console users better privacy.
700	 */
701	login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
702#endif /* !__APPLE__ */
703
704	/*
705	 * Clear flags of the tty.  None should be set, and when the
706	 * user sets them otherwise, this can cause the chown to fail.
707	 * Since it isn't clear that flags are useful on character
708	 * devices, we just clear them.
709	 *
710	 * We don't log in the case of EOPNOTSUPP because dev might be
711	 * on NFS, which doesn't support chflags.
712	 *
713	 * We don't log in the EROFS because that means that /dev is on
714	 * a read only file system and we assume that the permissions there
715	 * are sane.
716	 */
717	if (ttyn != tname && chflags(ttyn, 0))
718#ifdef __APPLE__
719		if (errno != EOPNOTSUPP && errno != ENOTSUP && errno != EROFS)
720#else
721		if (errno != EOPNOTSUPP && errno != EROFS)
722#endif
723			syslog(LOG_ERR, "chflags(%s): %m", ttyn);
724	if (ttyn != tname && chown(ttyn, pwd->pw_uid,
725	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
726		if (errno != EROFS)
727			syslog(LOG_ERR, "chown(%s): %m", ttyn);
728
729#ifdef __APPLE__
730	(void)chmod(ttyn, 0620);
731#endif /* __APPLE__ */
732
733#ifndef __APPLE__
734	/*
735	 * Exclude cons/vt/ptys only, assume dialup otherwise
736	 * TODO: Make dialup tty determination a library call
737	 * for consistency (finger etc.)
738	 */
739	if (hflag && isdialuptty(tty))
740		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
741#endif /* !__APPLE__ */
742
743#ifdef LOGALL
744	/*
745	 * Syslog each successful login, so we don't have to watch
746	 * hundreds of wtmp or lastlogin files.
747	 */
748	if (hflag)
749		syslog(LOG_INFO, "login from %s on %s as %s",
750		       hostname, tty, pwd->pw_name);
751	else
752		syslog(LOG_INFO, "login on %s as %s",
753		       tty, pwd->pw_name);
754#endif
755
756	/*
757	 * If fflag is on, assume caller/authenticator has logged root
758	 * login.
759	 */
760	if (rootlogin && fflag == 0) {
761		if (hflag)
762			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
763			    username, tty, hostname);
764		else
765			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
766			    username, tty);
767	}
768
769	/*
770	 * Destroy environment unless user has requested its
771	 * preservation - but preserve TERM in all cases
772	 */
773	term = getenv("TERM");
774	if (!pflag)
775		environ = envinit;
776	if (term != NULL)
777		setenv("TERM", term, 0);
778
779#ifndef __APPLE__
780	/*
781	 * PAM modules might add supplementary groups during pam_setcred().
782	 */
783	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
784		syslog(LOG_ERR, "setusercontext() failed - exiting");
785		bail(NO_SLEEP_EXIT, 1);
786	}
787#endif /* !__APPLE__ */
788#ifdef USE_PAM
789	if (!fflag) {
790		pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
791		if (pam_err != PAM_SUCCESS) {
792			pam_syslog("pam_setcred()");
793			bail(NO_SLEEP_EXIT, 1);
794		}
795		pam_cred_established = 1;
796	}
797
798	pam_err = pam_open_session(pamh, pam_silent);
799	if (pam_err != PAM_SUCCESS) {
800		pam_syslog("pam_open_session()");
801		bail(NO_SLEEP_EXIT, 1);
802	}
803	pam_session_established = 1;
804#endif /* USE_PAM */
805
806#ifdef __APPLE__
807	/* <rdar://problem/5377791>
808	   Install a signal handler that will forward SIGHUP to the
809	   child and process group.  The parent should not exit on
810	   SIGHUP so that the tty ownership can be reset. */
811	(void)signal(SIGHUP, handle_sighup);
812#endif /* __APPLE__ */
813
814	/*
815	 * We must fork() before setuid() because we need to call
816	 * pam_close_session() as root.
817	 */
818	pid = fork();
819	if (pid < 0) {
820		err(1, "fork");
821	} else if (pid != 0) {
822		/*
823		 * Parent: wait for child to finish, then clean up
824		 * session.
825		 */
826		int status;
827#ifndef __APPLE__
828		setproctitle("-%s [pam]", getprogname());
829#endif /* !__APPLE__ */
830#ifdef __APPLE__
831		/* Our SIGHUP handler may interrupt the wait */
832		int res;
833		do {
834			res = waitpid(pid, &status, 0);
835		} while (res == -1 && errno == EINTR);
836#else
837		waitpid(pid, &status, 0);
838#endif
839#ifdef __APPLE__
840		chown(ttyn, 0, 0);
841		chmod(ttyn, 0666);
842#endif /* __APPLE__ */
843		bail(NO_SLEEP_EXIT, 0);
844	}
845
846	/*
847	 * NOTICE: We are now in the child process!
848	 */
849
850#ifdef __APPLE__
851	/* Restore the default SIGHUP handler for the child. */
852	(void)signal(SIGHUP, SIG_DFL);
853#endif /* __APPLE__ */
854
855#ifdef USE_PAM
856	/*
857	 * Add any environment variables the PAM modules may have set.
858	 */
859	export_pam_environment();
860
861	/*
862	 * We're done with PAM now; our parent will deal with the rest.
863	 */
864	pam_end(pamh, 0);
865	pamh = NULL;
866#endif /* USE_PAM */
867
868	/*
869	 * We don't need to be root anymore, so set the login name and
870	 * the UID.
871	 */
872	if (setlogin(username) != 0) {
873		syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
874		bail(NO_SLEEP_EXIT, 1);
875	}
876#ifdef __APPLE__
877	/* <rdar://problem/6041650> restore process priority if not changing uids */
878	if (uid == (uid_t)pwd->pw_uid) {
879		(void)setpriority(PRIO_PROCESS, 0, prio);
880	}
881
882	(void)setgid(pwd->pw_gid);
883	if (initgroups(username, pwd->pw_gid) == -1)
884		syslog(LOG_ERR, "login: initgroups() failed");
885	(void) setuid(rootlogin ? 0 : pwd->pw_uid);
886#else /* !__APPLE__ */
887	if (setusercontext(lc, pwd, pwd->pw_uid,
888	    LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
889		syslog(LOG_ERR, "setusercontext() failed - exiting");
890		exit(1);
891	}
892#endif /* !__APPLE__ */
893
894#ifdef __APPLE__
895	/* We test for the home directory after pam_open_session(3)
896	 * as the home directory may have been mounted by a session
897	 * module, and after changing uid as the home directory may
898	 * be NFS with root access disabled. */
899	if (!lflag) {
900		/* First do a stat in case the homedir is automounted */
901		stat(pwd->pw_dir,&st);
902		if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) {
903			printf("No home directory: %s\n", pwd->pw_dir);
904			if (chdir("/") < 0) {
905				refused("Cannot find root directory", "ROOTDIR", 0);
906				exit(1);
907			}
908			pwd->pw_dir = strdup("/");
909			if (pwd->pw_dir == NULL) {
910				syslog(LOG_NOTICE, "strdup(): %m");
911				exit(1);
912			}
913		}
914	}
915#endif /* __APPLE__ */
916	if (pwd->pw_shell) {
917		(void)setenv("SHELL", pwd->pw_shell, 1);
918	} else {
919		syslog(LOG_ERR, "pwd->pw_shell not set - exiting");
920		bail(NO_SLEEP_EXIT, 1);
921	}
922	if (pwd->pw_dir) {
923		(void)setenv("HOME", pwd->pw_dir, 1);
924	} else {
925		(void)setenv("HOME", "/", 1);
926	}
927	/* Overwrite "term" from login.conf(5) for any known TERM */
928	if (term == NULL && (tp = stypeof(tty)) != NULL)
929		(void)setenv("TERM", tp, 1);
930	else
931		(void)setenv("TERM", TERM_UNKNOWN, 0);
932	(void)setenv("LOGNAME", username, 1);
933	(void)setenv("USER", username, 1);
934	(void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
935
936#ifdef __APPLE__
937	/* Re-enable crash reporter */
938	do {
939		kern_return_t kr;
940		mach_port_t bp, ep, mts;
941		thread_state_flavor_t flavor = 0;
942
943#if defined(__ppc__)
944		flavor = PPC_THREAD_STATE64;
945#elif defined(__i386__) || defined(__x86_64__)
946		flavor = x86_THREAD_STATE;
947#elif defined(__arm__)
948		flavor = ARM_THREAD_STATE;
949#else
950#error unsupported architecture
951#endif
952
953		mts = mach_task_self();
954
955		kr = task_get_bootstrap_port(mts, &bp);
956		if (kr != KERN_SUCCESS) {
957		  syslog(LOG_ERR, "task_get_bootstrap_port() failure: %s (%d)",
958			bootstrap_strerror(kr), kr);
959		  break;
960		}
961
962		const char* bs = "com.apple.ReportCrash";
963		kr = bootstrap_look_up(bp, (char*)bs, &ep);
964		if (kr != KERN_SUCCESS) {
965		  syslog(LOG_ERR, "bootstrap_look_up(%s) failure: %s (%d)",
966			bs, bootstrap_strerror(kr), kr);
967		  break;
968		}
969
970		kr = task_set_exception_ports(mts, EXC_MASK_CRASH, ep, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, flavor);
971		if (kr != KERN_SUCCESS) {
972		  syslog(LOG_ERR, "task_set_exception_ports() failure: %d", kr);
973		  break;
974		}
975	} while (0);
976#endif /* __APPLE__ */
977
978	if (!quietlog) {
979#ifdef LOGIN_CAP
980		const char *cw;
981
982		cw = login_getcapstr(lc, "copyright", NULL, NULL);
983		if (cw == NULL || motd(cw) == -1)
984			(void)printf("%s", copyright);
985
986		(void)printf("\n");
987
988		cw = login_getcapstr(lc, "welcome", NULL, NULL);
989		if (cw != NULL && access(cw, F_OK) == 0)
990			motd(cw);
991		else
992			motd(_PATH_MOTDFILE);
993
994		if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 &&
995		    login_getcapbool(lc, "nocheckmail", 0) == 0) {
996#else /* !LOGIN_CAP */
997		motd(_PATH_MOTDFILE);
998		{
999#endif /* !LOGIN_CAP */
1000			char *cx;
1001
1002			/* $MAIL may have been set by class. */
1003			cx = getenv("MAIL");
1004			if (cx == NULL) {
1005				asprintf(&cx, "%s/%s",
1006				    _PATH_MAILDIR, pwd->pw_name);
1007			}
1008			if (cx && stat(cx, &st) == 0 && st.st_size != 0)
1009				(void)printf("You have %smail.\n",
1010				    (st.st_mtime > st.st_atime) ? "new " : "");
1011			if (getenv("MAIL") == NULL)
1012				free(cx);
1013		}
1014	}
1015
1016#ifdef LOGIN_CAP
1017	login_close(lc_user);
1018	login_close(lc);
1019#endif /* LOGIN_CAP */
1020
1021	(void)signal(SIGALRM, SIG_DFL);
1022	(void)signal(SIGQUIT, SIG_DFL);
1023	(void)signal(SIGINT, SIG_DFL);
1024	(void)signal(SIGTSTP, SIG_IGN);
1025
1026#ifdef __APPLE__
1027	if (fflag && *argv) pwd->pw_shell = *argv;
1028#endif /* __APPLE__ */
1029
1030	/*
1031	 * Login shells have a leading '-' in front of argv[0]
1032	 */
1033	p = strrchr(pwd->pw_shell, '/');
1034#ifdef __APPLE__
1035	if (asprintf(&arg0, "%s%s", lflag ? "" : "-", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
1036#else /* __APPLE__ */
1037	if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
1038#endif /* __APPLE__ */
1039		syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
1040		    username);
1041		errx(1, "shell exceeds maximum pathname size");
1042	} else if (arg0 == NULL) {
1043		err(1, "asprintf()");
1044	}
1045
1046#ifdef __APPLE__
1047	if (fflag && *argv) {
1048		*argv = arg0;
1049		execvp(pwd->pw_shell, argv);
1050		err(1, "%s", arg0);
1051	}
1052#endif /* __APPLE__ */
1053	execlp(shell, arg0, (char *)0);
1054	err(1, "%s", shell);
1055
1056	/*
1057	 * That's it, folks!
1058	 */
1059}
1060
1061#ifdef USE_PAM
1062/*
1063 * Attempt to authenticate the user using PAM.  Returns 0 if the user is
1064 * authenticated, or 1 if not authenticated.  If some sort of PAM system
1065 * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
1066 * function returns -1.  This can be used as an indication that we should
1067 * fall back to a different authentication mechanism.
1068 */
1069static int
1070auth_pam(int skip_auth)
1071{
1072	const char *tmpl_user;
1073	const void *item;
1074	int rval;
1075
1076	rval = 0;
1077
1078	if (skip_auth == 0)
1079	{
1080		pam_err = pam_authenticate(pamh, pam_silent);
1081		switch (pam_err) {
1082
1083		case PAM_SUCCESS:
1084			/*
1085			 * With PAM we support the concept of a "template"
1086			 * user.  The user enters a login name which is
1087			 * authenticated by PAM, usually via a remote service
1088			 * such as RADIUS or TACACS+.  If authentication
1089			 * succeeds, a different but related "template" name
1090			 * is used for setting the credentials, shell, and
1091			 * home directory.  The name the user enters need only
1092			 * exist on the remote authentication server, but the
1093			 * template name must be present in the local password
1094			 * database.
1095			 *
1096			 * This is supported by two various mechanisms in the
1097			 * individual modules.  However, from the application's
1098			 * point of view, the template user is always passed
1099			 * back as a changed value of the PAM_USER item.
1100			 */
1101			pam_err = pam_get_item(pamh, PAM_USER, &item);
1102			if (pam_err == PAM_SUCCESS) {
1103				tmpl_user = (const char *)item;
1104				if (strcmp(username, tmpl_user) != 0)
1105					pwd = getpwnam(tmpl_user);
1106			} else {
1107				pam_syslog("pam_get_item(PAM_USER)");
1108			}
1109			rval = 0;
1110			break;
1111
1112		case PAM_AUTH_ERR:
1113		case PAM_USER_UNKNOWN:
1114		case PAM_MAXTRIES:
1115			rval = 1;
1116			break;
1117
1118		default:
1119			pam_syslog("pam_authenticate()");
1120			rval = -1;
1121			break;
1122		}
1123	}
1124
1125	if (rval == 0) {
1126		pam_err = pam_acct_mgmt(pamh, pam_silent);
1127		switch (pam_err) {
1128		case PAM_SUCCESS:
1129			break;
1130		case PAM_NEW_AUTHTOK_REQD:
1131			if (skip_auth == 0)
1132			{
1133				pam_err = pam_chauthtok(pamh,
1134				    pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
1135				if (pam_err != PAM_SUCCESS) {
1136					pam_syslog("pam_chauthtok()");
1137					rval = 1;
1138				}
1139			}
1140			else
1141			{
1142				pam_syslog("pam_acct_mgmt()");
1143			}
1144			break;
1145		default:
1146			pam_syslog("pam_acct_mgmt()");
1147			rval = 1;
1148			break;
1149		}
1150	}
1151
1152	if (rval != 0) {
1153		pam_end(pamh, pam_err);
1154		pamh = NULL;
1155	}
1156	return (rval);
1157}
1158
1159/*
1160 * Export any environment variables PAM modules may have set
1161 */
1162static void
1163export_pam_environment()
1164{
1165	char **pam_env;
1166	char **pp;
1167
1168	pam_env = pam_getenvlist(pamh);
1169	if (pam_env != NULL) {
1170		for (pp = pam_env; *pp != NULL; pp++) {
1171			(void)export(*pp);
1172			free(*pp);
1173		}
1174	}
1175}
1176
1177/*
1178 * Perform sanity checks on an environment variable:
1179 * - Make sure there is an '=' in the string.
1180 * - Make sure the string doesn't run on too long.
1181 * - Do not export certain variables.  This list was taken from the
1182 *   Solaris pam_putenv(3) man page.
1183 * Then export it.
1184 */
1185static int
1186export(const char *s)
1187{
1188	static const char *noexport[] = {
1189		"SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
1190		"IFS", "PATH", NULL
1191	};
1192	char *p;
1193	const char **pp;
1194	size_t n;
1195
1196	if (strlen(s) > 1024 || (p = strchr(s, '=')) == NULL)
1197		return (0);
1198	if (strncmp(s, "LD_", 3) == 0)
1199		return (0);
1200	for (pp = noexport; *pp != NULL; pp++) {
1201		n = strlen(*pp);
1202		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
1203			return (0);
1204	}
1205	*p = '\0';
1206	(void)setenv(s, p + 1, 1);
1207	*p = '=';
1208	return (1);
1209}
1210#endif /* USE_PAM */
1211
1212static void
1213usage()
1214{
1215#ifdef __APPLE__
1216	(void)fprintf(stderr, "usage: login [-pq] [-h hostname] [username]\n");
1217	(void)fprintf(stderr, "       login -f [-lpq] [-h hostname] [username [prog [arg ...]]]\n");
1218#else
1219	(void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n");
1220#endif
1221	exit(1);
1222}
1223
1224/*
1225 * Prompt user and read login name from stdin.
1226 */
1227static char *
1228getloginname()
1229{
1230	char *nbuf, *p;
1231	int ch;
1232
1233	nbuf = malloc(MAXLOGNAME);
1234	if (nbuf == NULL)
1235		err(1, "malloc()");
1236	do {
1237		(void)printf("%s", prompt);
1238		for (p = nbuf; (ch = getchar()) != '\n'; ) {
1239			if (ch == EOF) {
1240				badlogin(username);
1241				bail(NO_SLEEP_EXIT, 0);
1242			}
1243			if (p < nbuf + MAXLOGNAME - 1)
1244				*p++ = ch;
1245		}
1246	} while (p == nbuf);
1247
1248	*p = '\0';
1249	if (nbuf[0] == '-') {
1250#ifdef USE_PAM
1251		pam_silent = 0;
1252#endif /* USE_PAM */
1253		memmove(nbuf, nbuf + 1, strlen(nbuf));
1254	} else {
1255#ifdef USE_PAM
1256		pam_silent = PAM_SILENT;
1257#endif /* USE_PAM */
1258	}
1259	return nbuf;
1260}
1261
1262#ifdef __APPLE__
1263#ifndef USE_PAM
1264static int
1265rootterm(const char* ttyn)
1266{
1267	struct ttyent *t;
1268	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
1269}
1270#endif /* !USE_PAM */
1271#endif /* __APPLE__ */
1272
1273/*
1274 * SIGINT handler for motd().
1275 */
1276static volatile int motdinterrupt;
1277static void
1278sigint(int signo __unused)
1279{
1280	motdinterrupt = 1;
1281}
1282
1283/*
1284 * Display the contents of a file (such as /etc/motd).
1285 */
1286static int
1287motd(const char *motdfile)
1288{
1289	sig_t oldint;
1290	FILE *f;
1291	int ch;
1292
1293	if ((f = fopen(motdfile, "r")) == NULL)
1294		return (-1);
1295	motdinterrupt = 0;
1296	oldint = signal(SIGINT, sigint);
1297	while ((ch = fgetc(f)) != EOF && !motdinterrupt)
1298		putchar(ch);
1299	signal(SIGINT, oldint);
1300	if (ch != EOF || ferror(f)) {
1301		fclose(f);
1302		return (-1);
1303	}
1304	fclose(f);
1305	return (0);
1306}
1307
1308/*
1309 * SIGHUP handler
1310 * Forwards the SIGHUP to the child process and current process group.
1311 */
1312static void
1313handle_sighup(int signo)
1314{
1315	if (pid > 0) {
1316		/* close the controlling terminal */
1317		close(STDIN_FILENO);
1318		close(STDOUT_FILENO);
1319		close(STDERR_FILENO);
1320		/* Ignore SIGHUP to avoid tail-recursion on signaling
1321		   the current process group (of which we are a member). */
1322		(void)signal(SIGHUP, SIG_IGN);
1323		/* Forward the signal to the current process group. */
1324		(void)kill(0, signo);
1325		/* Forward the signal to the child if not a member of the current
1326		 * process group <rdar://problem/6244808>. */
1327		if (getpgid(pid) != getpgrp()) {
1328			(void)kill(pid, signo);
1329		}
1330	}
1331}
1332
1333/*
1334 * SIGALRM handler, to enforce login prompt timeout.
1335 *
1336 * XXX This can potentially confuse the hell out of PAM.  We should
1337 * XXX instead implement a conversation function that returns
1338 * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
1339 * XXX handler just set a flag.
1340 */
1341static void
1342timedout(int signo __unused)
1343{
1344
1345	longjmp(timeout_buf, signo);
1346}
1347
1348#ifdef __APPLE__
1349#ifndef USE_PAM
1350void
1351checknologin()
1352{
1353	int fd, nchars;
1354	char tbuf[8192];
1355
1356	if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
1357		while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
1358			(void)write(fileno(stdout), tbuf, nchars);
1359#ifdef USE_BSM_AUDIT
1360		au_login_fail("No login", 0);
1361#endif
1362		sleep(5);
1363		exit(0);
1364	}
1365}
1366#endif /* !USE_PAM */
1367
1368void
1369dolastlog(quiet)
1370	int quiet;
1371{
1372#ifdef USE_PAM
1373	if (quiet)
1374		return;
1375	if (*lastlog.ll_line) {
1376		(void)printf("Last login: %.*s ",
1377		    24-5, (char *)ctime(&lastlog.ll_tv.tv_sec));
1378		if (*lastlog.ll_host != '\0')
1379			(void)printf("from %.*s\n",
1380			    (int)sizeof(lastlog.ll_host),
1381			    lastlog.ll_host);
1382		else
1383			(void)printf("on %.*s\n",
1384			    (int)sizeof(lastlog.ll_line),
1385			    lastlog.ll_line);
1386	}
1387#else /* !USE_PAM */
1388	struct lastlogx ll;
1389
1390	if(!quiet && getlastlogx(pwd->pw_uid, &ll) != NULL) {
1391		(void)printf("Last login: %.*s ",
1392				24-5, (char *)ctime(&ll.ll_tv.tv_sec));
1393		if (*ll.ll_host != '\0')
1394			(void)printf("from %.*s\n",
1395					(int)sizeof(ll.ll_host),
1396					ll.ll_host);
1397		else
1398			(void)printf("on %.*s\n",
1399					(int)sizeof(ll.ll_line),
1400					ll.ll_line);
1401	}
1402#endif /* USE_PAM */
1403}
1404#endif /* __APPLE__ */
1405
1406static void
1407badlogin(char *name)
1408{
1409
1410	if (failures == 0)
1411		return;
1412	if (hflag) {
1413		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
1414		    failures, failures > 1 ? "S" : "", hostname);
1415		syslog(LOG_AUTHPRIV|LOG_NOTICE,
1416		    "%d LOGIN FAILURE%s FROM %s, %s",
1417		    failures, failures > 1 ? "S" : "", hostname, name);
1418	} else {
1419		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
1420		    failures, failures > 1 ? "S" : "", tty);
1421		syslog(LOG_AUTHPRIV|LOG_NOTICE,
1422		    "%d LOGIN FAILURE%s ON %s, %s",
1423		    failures, failures > 1 ? "S" : "", tty, name);
1424	}
1425	failures = 0;
1426}
1427
1428const char *
1429stypeof(char *ttyid)
1430{
1431	struct ttyent *t;
1432
1433	if (ttyid != NULL && *ttyid != '\0') {
1434		t = getttynam(ttyid);
1435		if (t != NULL && t->ty_type != NULL)
1436			return (t->ty_type);
1437	}
1438	return (NULL);
1439}
1440
1441static void
1442refused(const char *msg, const char *rtype, int lout)
1443{
1444
1445	if (msg != NULL)
1446	    printf("%s.\n", msg);
1447	if (hflag)
1448		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
1449		    pwd->pw_name, rtype, hostname, tty);
1450	else
1451		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
1452		    pwd->pw_name, rtype, tty);
1453	if (lout)
1454		bail(SLEEP_EXIT, 1);
1455}
1456
1457#ifdef USE_PAM
1458/*
1459 * Log a PAM error
1460 */
1461static void
1462pam_syslog(const char *msg)
1463{
1464	syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
1465}
1466
1467/*
1468 * Shut down PAM
1469 */
1470static void
1471pam_cleanup()
1472{
1473
1474	if (pamh != NULL) {
1475		if (pam_session_established) {
1476			pam_err = pam_close_session(pamh, 0);
1477			if (pam_err != PAM_SUCCESS)
1478				pam_syslog("pam_close_session()");
1479		}
1480		pam_session_established = 0;
1481		if (pam_cred_established) {
1482			pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
1483			if (pam_err != PAM_SUCCESS)
1484				pam_syslog("pam_setcred()");
1485		}
1486		pam_cred_established = 0;
1487		pam_end(pamh, pam_err);
1488		pamh = NULL;
1489	}
1490}
1491#endif /* USE_PAM */
1492
1493/*
1494 * Exit, optionally after sleeping a few seconds
1495 */
1496void
1497bail(int sec, int eval)
1498{
1499
1500#ifdef USE_PAM
1501	pam_cleanup();
1502#endif /* USE_PAM */
1503#ifdef USE_BSM_AUDIT
1504	if (pwd != NULL)
1505		audit_logout();
1506#endif
1507	(void)sleep(sec);
1508	exit(eval);
1509}
1510