Skip site navigation (1)Skip section navigation (2)
Date:      24 Jan 2002 18:22:45 +0100
From:      Dag-Erling Smorgrav <des@ofug.org>
To:        security@freebsd.org
Cc:        markm@freebsd.org
Subject:   login(1) PAMification
Message-ID:  <xzpofjjbr4q.fsf@flood.ping.uio.no>

next in thread | raw e-mail | index | archive | help
--=-=-=

See the attached patch.

DES
-- 
Dag-Erling Smorgrav - des@ofug.org


--=-=-=
Content-Type: text/x-patch
Content-Disposition: attachment; filename=login.diff

? etc/pam.d/passwd
? lib/libpam/modules/check.sh
? usr.bin/login/login.c.no_pam
? usr.bin/login/login.c.pamref
? usr.bin/login/login.c.ref
Index: etc/pam.d/login
===================================================================
RCS file: /home/ncvs/src/etc/pam.d/login,v
retrieving revision 1.6
diff -u -r1.6 login
--- etc/pam.d/login	21 Jan 2002 18:51:24 -0000	1.6
+++ etc/pam.d/login	24 Jan 2002 17:19:32 -0000
@@ -6,6 +6,7 @@
 
 # auth
 auth		required	pam_nologin.so	no_warn
+auth		sufficient	pam_self.so	no_warn
 auth		sufficient	pam_opie.so	no_warn no_fake_prompts
 auth		requisite	pam_opieaccess.so	no_warn
 #auth		sufficient	pam_kerberosIV.so	no_warn try_first_pass
@@ -16,12 +17,14 @@
 # account
 #account	required	pam_kerberosIV.so
 #account	required	pam_krb5.so
+account		required	pam_login_access.so
 account		required	pam_unix.so
 
 # session
 #session	required	pam_kerberosIV.so
 #session	required	pam_krb5.so
 #session	required	pam_ssh.so
+session		required	pam_lastlog.so
 session		required	pam_unix.so
 
 # password
Index: etc/pam.d/sshd
===================================================================
RCS file: /home/ncvs/src/etc/pam.d/sshd,v
retrieving revision 1.2
diff -u -r1.2 sshd
--- etc/pam.d/sshd	5 Dec 2001 21:26:00 -0000	1.2
+++ etc/pam.d/sshd	24 Jan 2002 17:19:32 -0000
@@ -9,9 +9,11 @@
 auth		required	pam_unix.so	no_warn try_first_pass
 
 # account
+account		required	pam_login_access.so
 account		required	pam_unix.so
 
 # session
+session		required	pam_lastlog.so
 session		required	pam_permit.so
 
 # password
Index: etc/pam.d/su
===================================================================
RCS file: /home/ncvs/src/etc/pam.d/su,v
retrieving revision 1.6
diff -u -r1.6 su
--- etc/pam.d/su	21 Jan 2002 18:51:24 -0000	1.6
+++ etc/pam.d/su	24 Jan 2002 17:19:32 -0000
@@ -6,6 +6,7 @@
 
 # auth
 auth		sufficient	pam_rootok.so	no_warn
+auth		sufficient	pam_self.so	no_warn
 auth		requisite	pam_wheel.so	no_warn auth_as_self noroot_ok
 #auth		sufficient	pam_kerberosIV.so	no_warn
 #auth		sufficient	pam_krb5.so	no_warn try_first_pass auth_as_self
Index: lib/libpam/libpam/Makefile
===================================================================
RCS file: /home/ncvs/src/lib/libpam/libpam/Makefile,v
retrieving revision 1.25
diff -u -r1.25 Makefile
--- lib/libpam/libpam/Makefile	23 Jan 2002 15:54:08 -0000	1.25
+++ lib/libpam/libpam/Makefile	24 Jan 2002 17:19:32 -0000
@@ -78,6 +78,8 @@
 .if defined(MAKE_KERBEROS5) && !defined(NOCRYPT) && !defined(NO_OPENSSL)
 STATIC_MODULES+= ${MODOBJDIR}/pam_krb5/libpam_krb5.a
 .endif
+STATIC_MODULES+= ${MODOBJDIR}/pam_lastlog/libpam_lastlog.a
+STATIC_MODULES+= ${MODOBJDIR}/pam_login_access/libpam_login_access.a
 STATIC_MODULES+= ${MODOBJDIR}/pam_nologin/libpam_nologin.a
 STATIC_MODULES+= ${MODOBJDIR}/pam_opie/libpam_opie.a
 STATIC_MODULES+= ${MODOBJDIR}/pam_opieaccess/libpam_opieaccess.a
Index: lib/libpam/modules/Makefile
===================================================================
RCS file: /home/ncvs/src/lib/libpam/modules/Makefile,v
retrieving revision 1.16
diff -u -r1.16 Makefile
--- lib/libpam/modules/Makefile	21 Jan 2002 13:43:52 -0000	1.16
+++ lib/libpam/modules/Makefile	24 Jan 2002 17:19:32 -0000
@@ -32,6 +32,8 @@
 .if defined(MAKE_KERBEROS5) && !defined(NOCRYPT) && !defined(NO_OPENSSL)
 SUBDIR+=	pam_krb5
 .endif
+SUBDIR+=	pam_lastlog
+SUBDIR+=	pam_login_access
 SUBDIR+=	pam_nologin
 SUBDIR+=	pam_opie
 SUBDIR+=	pam_opieaccess
Index: lib/libpam/modules/pam_unix/pam_unix.c
===================================================================
RCS file: /home/ncvs/src/lib/libpam/modules/pam_unix/pam_unix.c,v
retrieving revision 1.16
diff -u -r1.16 pam_unix.c
--- lib/libpam/modules/pam_unix/pam_unix.c	19 Jan 2002 18:29:49 -0000	1.16
+++ lib/libpam/modules/pam_unix/pam_unix.c	24 Jan 2002 17:19:32 -0000
@@ -1,6 +1,13 @@
 /*-
  * Copyright 1998 Juniper Networks, Inc.
  * All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technologies, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software was developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -10,6 +17,9 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -27,15 +37,21 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD: src/lib/libpam/modules/pam_unix/pam_unix.c,v 1.16 2002/01/19 18:29:49 des Exp $");
 
-#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
 #include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #ifdef YP
 #include <rpc/rpc.h>
 #include <rpcsvc/yp_prot.h>
 #include <rpcsvc/ypclnt.h>
 #include <rpcsvc/yppasswd.h>
 #endif
+
 #include <login_cap.h>
+#include <netdb.h>
 #include <pwd.h>
 #include <stdlib.h>
 #include <string.h>
@@ -68,7 +84,12 @@
 #define DEFAULT_WARN		(2L * 7L * 86400L)  /* Two weeks */
 #define	MAX_TRIES		3
 
-enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_NULLOK, PAM_OPT_LOCAL_PASS, PAM_OPT_NIS_PASS };
+enum {
+	PAM_OPT_AUTH_AS_SELF	= PAM_OPT_STD_MAX,
+	PAM_OPT_NULLOK,
+	PAM_OPT_LOCAL_PASS,
+	PAM_OPT_NIS_PASS
+};
 
 static struct opttab other_options[] = {
 	{ "auth_as_self",	PAM_OPT_AUTH_AS_SELF },
@@ -198,13 +219,15 @@
 PAM_EXTERN int
 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
 {
+	struct addrinfo hints, *res;
 	struct options options;
-	struct passwd *pw;
+	struct passwd *pwd;
 	struct timeval tp;
 	login_cap_t *lc;
 	time_t warntime;
 	int retval;
-	const char *user;
+	const char *rhost, *tty, *user;
+	char rhostip[MAXHOSTNAMELEN];
 	char buf[128];
 
 	pam_std_option(&options, other_options, argc, argv);
@@ -212,53 +235,100 @@
 	PAM_LOG("Options processed");
 
 	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
-	if (retval != PAM_SUCCESS || user == NULL)
-		/* some implementations return PAM_SUCCESS here */
-		PAM_RETURN(PAM_USER_UNKNOWN);
-
-	pw = getpwnam(user);
-	if (pw == NULL)
-		PAM_RETURN(PAM_USER_UNKNOWN);
+	if (retval != PAM_SUCCESS)
+		PAM_RETURN(retval);
+
+	if (user == NULL || (pwd = getpwnam(user)) == NULL)
+		PAM_RETURN(PAM_SERVICE_ERR);
 
 	PAM_LOG("Got user: %s", user);
 
-	retval = PAM_SUCCESS;
-	lc = login_getpwclass(pw);
+	retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost);
+	if (retval != PAM_SUCCESS)
+		PAM_RETURN(retval);
 
-	if (pw->pw_change || pw->pw_expire)
-		gettimeofday(&tp, NULL);
+	retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty);
+	if (retval != PAM_SUCCESS)
+		PAM_RETURN(retval);
 
-	warntime = login_getcaptime(lc, "warnpassword", DEFAULT_WARN,
-	    DEFAULT_WARN);
+	if (*pwd->pw_passwd == '\0' &&
+	    (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0)
+		return (PAM_NEW_AUTHTOK_REQD);
+	    
+	lc = login_getpwclass(pwd);
+	if (lc == NULL) {
+		PAM_LOG("Unable to get login class for user %s", user);
+		return (PAM_SERVICE_ERR);
+	}
 
 	PAM_LOG("Got login_cap");
 
-	if (pw->pw_change) {
-		if (tp.tv_sec >= pw->pw_change)
-			/* some implementations return PAM_AUTHTOK_EXPIRED */
+	if (pwd->pw_change || pwd->pw_expire)
+		gettimeofday(&tp, NULL);
+
+	/*
+	 * Check pw_expire before pw_change - no point in letting the
+	 * user change the password on an expired account.
+	 */
+	
+	if (pwd->pw_expire) {
+		warntime = login_getcaptime(lc, "warnexpire",
+		    DEFAULT_WARN, DEFAULT_WARN);
+		if (tp.tv_sec >= pwd->pw_expire) {
+			login_close(lc);
+			PAM_RETURN(PAM_ACCT_EXPIRED);
+		} else if (pwd->pw_expire - tp.tv_sec < warntime &&
+		    (flags & PAM_SILENT) == 0) {
+			snprintf(buf, sizeof(buf),
+			    "Warning: your account expires on %s",
+			    ctime(&pwd->pw_expire));
+			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
+		}
+	}
+
+	retval = PAM_SUCCESS;
+	if (pwd->pw_change) {
+		warntime = login_getcaptime(lc, "warnpassword",
+		    DEFAULT_WARN, DEFAULT_WARN);
+		if (tp.tv_sec >= pwd->pw_change) {
 			retval = PAM_NEW_AUTHTOK_REQD;
-		else if (pw->pw_change - tp.tv_sec < warntime) {
+		} else if (pwd->pw_change - tp.tv_sec < warntime &&
+		    (flags & PAM_SILENT) == 0) {
 			snprintf(buf, sizeof(buf),
 			    "Warning: your password expires on %s",
-			    ctime(&pw->pw_change));
+			    ctime(&pwd->pw_change));
 			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
 		}
 	}
 
-	warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN,
-	    DEFAULT_WARN);
+	/*
+	 * From here on, we must leave retval untouched (unless we
+	 * know we're going to fail), because we need to remember
+	 * whether we're supposed to return PAM_SUCCESS or
+	 * PAM_NEW_AUTHTOK_REQD.
+	 */
 
-	if (pw->pw_expire) {
-		if (tp.tv_sec >= pw->pw_expire)
-			retval = PAM_ACCT_EXPIRED;
-		else if (pw->pw_expire - tp.tv_sec < warntime) {
-			snprintf(buf, sizeof(buf),
-			    "Warning: your account expires on %s",
-			    ctime(&pw->pw_expire));
-			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
+	if (rhost) {
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_family = AF_UNSPEC;
+		if (getaddrinfo(rhost, NULL, &hints, &res) == 0) {
+			getnameinfo(res->ai_addr, res->ai_addrlen,
+			    rhostip, sizeof(rhostip), NULL, 0,
+			    NI_NUMERICHOST|NI_WITHSCOPEID);
 		}
+		if (res != NULL)
+			freeaddrinfo(res);
 	}
 
+	/*
+	 * Check host / tty / time-of-day restrictions
+	 */
+	
+	if (!auth_hostok(lc, rhost, rhostip) ||
+	    !auth_ttyok(lc, tty) ||
+	    !auth_timeok(lc, time(NULL)))
+		retval = PAM_AUTH_ERR;
+	
 	login_close(lc);
 
 	PAM_RETURN(retval);
Index: usr.bin/login/Makefile
===================================================================
RCS file: /home/ncvs/src/usr.bin/login/Makefile,v
retrieving revision 1.39
diff -u -r1.39 Makefile
--- usr.bin/login/Makefile	1 Dec 2001 19:48:59 -0000	1.39
+++ usr.bin/login/Makefile	24 Jan 2002 17:19:32 -0000
@@ -2,11 +2,11 @@
 # $FreeBSD: src/usr.bin/login/Makefile,v 1.39 2001/12/01 19:48:59 bde Exp $
 
 PROG=	login
-SRCS=	login.c login_access.c login_fbtab.c
+SRCS=	login.c login_fbtab.c
 MAN=	login.1 login.access.5
 
-CFLAGS+=-DLOGIN_ACCESS -DLOGALL
-WARNS?=	2
+CFLAGS+=-DLOGALL
+WARNS?=	4
 NO_WERROR=
 
 DPADD=	${LIBUTIL} ${LIBCRYPT} ${LIBPAM}
Index: usr.bin/login/login.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/login/login.c,v
retrieving revision 1.78
diff -u -r1.78 login.c
--- usr.bin/login/login.c	21 Jan 2002 16:19:38 -0000	1.78
+++ usr.bin/login/login.c	24 Jan 2002 17:19:32 -0000
@@ -79,12 +79,9 @@
 #include <syslog.h>
 #include <ttyent.h>
 #include <unistd.h>
-#include <utmp.h>
 
-#ifndef NO_PAM
 #include <security/pam_appl.h>
 #include <security/pam_misc.h>
-#endif
 
 #include "login.h"
 #include "pathnames.h"
@@ -94,97 +91,105 @@
 #define	NI_WITHSCOPEID	0
 #endif
 
-static int	auth_traditional __P((void));
-static void	badlogin __P((char *));
-static void	dolastlog __P((int));
-static void	getloginname __P((void));
-static void	motd __P((const char *));
-static void	refused __P((const char *,const char *,int));
-static int	rootterm __P((char *));
-static void	sigint __P((int));
-static void	sleepexit __P((int));
-static const char *stypeof __P((char *));
-static void	timedout __P((int));
-static void	usage __P((void));
-
-#ifndef NO_PAM
-static int	auth_pam __P((void));
-static int	export_pam_environment __P((void));
-static int	ok_to_export __P((const char *));
-
-static pam_handle_t *pamh = NULL;
-static char	**environ_pam;
-
-#define PAM_END { \
-	if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
-		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); \
-	if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) \
-		syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); \
-	if ((e = pam_end(pamh, e)) != PAM_SUCCESS) \
-		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); \
-}
-#endif /* NO_PAM */
+static int		 auth_pam(void);
+static void		 bail(int, int);
+static int		 export(const char *);
+static void		 export_pam_environment(void);
+static int		 motd(const char *);
+static int		 rootterm(char *);
+static void		 badlogin(char *);
+static char		*getloginname(void);
+static void		 pam_syslog(const char *);
+static void		 pam_cleanup(void);
+static void		 refused(const char *, const char *, int);
+static const char	*stypeof(char *);
+static void		 sigint(int);
+static void		 timedout(int);
+static void		 usage(void);
 
 #define	TTYGRPNAME		"tty"			/* group to own ttys */
 #define	DEFAULT_BACKOFF		3
 #define	DEFAULT_RETRIES		10
 #define	DEFAULT_PROMPT		"login: "
 #define	DEFAULT_PASSWD_PROMPT	"Password:"
-#define	INVALID_HOST		"invalid hostname"
-#define	UNKNOWN			"su"
+#define	TERM_UNKNOWN		"su"
 #define	DEFAULT_WARN		(2L * 7L * 86400L)	/* Two weeks */
-#define	NBUFSIZ			UT_NAMESIZE + 64
+#define NO_SLEEP_EXIT		0
+#define SLEEP_EXIT		5
 
 /*
  * This bounds the time given to login.  Not a define so it can
  * be patched on machines where it's too small.
  */
-u_int	timeout = 300;
+static u_int		timeout = 300;
 
 /* Buffer for signal handling of timeout */
-jmp_buf timeout_buf;
+static jmp_buf		 timeout_buf;
+
+struct passwd		*pwd;
+static int		 failures;
+
+static char		*envinit[1];	/* empty environment list */
+
+/*
+ * Command line flags and arguments
+ */
+static int		 fflag;		/* -f: do not perform authentication */
+static int		 hflag;		/* -h: login from remote host */
+static char		*hostname;	/* hostname from command line */
+static int		 pflag;		/* -p: preserve environment */
+
+/*
+ * User name
+ */
+static char		*username;	/* user name */
+static char		*olduser;	/* previous user name */
+
+/*
+ * Prompts
+ */
+static char		 default_prompt[] = DEFAULT_PROMPT;
+static char		*prompt;
+static char		 default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
+static char		*passwd_prompt;
+
+static char		*tty;
 
-struct	passwd *pwd;
-int	failures;
-char	*term, *envinit[1], *hostname, *passwd_prompt, *prompt, *tty, *username;
-char    full_hostname[MAXHOSTNAMELEN];
+/*
+ * PAM data
+ */
+static pam_handle_t	*pamh = NULL;
+static struct pam_conv	 pamc = { misc_conv, NULL };
+static int		 pam_err;
+static int		 pam_silent = PAM_SILENT;
+static int		 pam_cred_established;
+static int		 pam_session_established;
 
 int
-main(argc, argv)
-	int argc;
-	char *argv[];
+main(int argc, char *argv[])
 {
 	struct group *gr;
 	struct stat st;
-	struct timeval tp;
-	struct utmp utmp;
 	int rootok, retries, backoff;
-	int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
-	time_t warntime;
+	int ask, ch, cnt, quietlog, rootlogin, rval;
 	uid_t uid, euid;
 	gid_t egid;
+	char *term;
 	char *p, *ttyn;
-	char tbuf[MAXPATHLEN + 2];
 	char tname[sizeof(_PATH_TTY) + 10];
-	char *shell = NULL;
-	static char default_prompt[] = DEFAULT_PROMPT;
-	static char default_passwd_prompt[] = DEFAULT_PASSWD_PROMPT;
-	static char invalid_host[] = INVALID_HOST;
+	char *arg0, *shell = NULL;
 	login_cap_t *lc = NULL;
-#ifndef NO_PAM
 	pid_t pid;
-	int e;
-#endif /* NO_PAM */
 
 	(void)signal(SIGQUIT, SIG_IGN);
 	(void)signal(SIGINT, SIG_IGN);
 	(void)signal(SIGHUP, SIG_IGN);
 	if (setjmp(timeout_buf)) {
 		if (failures)
-			badlogin(tbuf);
+			badlogin(username);
 		(void)fprintf(stderr, "Login timed out after %d seconds\n",
 		    timeout);
-		exit(0);
+		bail(NO_SLEEP_EXIT, 0);
 	}
 	(void)signal(SIGALRM, timedout);
 	(void)alarm(timeout);
@@ -192,63 +197,22 @@
 
 	openlog("login", LOG_ODELAY, LOG_AUTH);
 
-	/*
-	 * -p is used by getty to tell login not to destroy the environment
-	 * -f is used to skip a second login authentication
-	 * -h is used by other servers to pass the name of the remote
-	 *    host to login so that it may be placed in utmp and wtmp
-	 */
-	*full_hostname = '\0';
-	term = NULL;
-
-	fflag = hflag = pflag = 0;
 	uid = getuid();
 	euid = geteuid();
 	egid = getegid();
+
 	while ((ch = getopt(argc, argv, "fh:p")) != -1)
 		switch (ch) {
 		case 'f':
 			fflag = 1;
 			break;
 		case 'h':
-			if (uid)
+			if (uid != 0)
 				errx(1, "-h option: %s", strerror(EPERM));
-			hflag = 1;
-			if (strlcpy(full_hostname, optarg,
-			    sizeof(full_hostname)) >= sizeof(full_hostname))
+			if (strlen(optarg) >= MAXHOSTNAMELEN)
 				errx(1, "-h option: %s: exceeds maximum "
 				    "hostname size", optarg);
-
-			trimdomain(optarg, UT_HOSTSIZE);
-
-			if (strlen(optarg) > UT_HOSTSIZE) {
-				struct addrinfo hints, *res;
-				int ga_err;
-				
-				memset(&hints, 0, sizeof(hints));
-				hints.ai_family = AF_UNSPEC;
-				ga_err = getaddrinfo(optarg, NULL, &hints,
-				    &res);
-				if (ga_err == 0) {
-					char hostbuf[MAXHOSTNAMELEN];
-
-					getnameinfo(res->ai_addr,
-					    res->ai_addrlen,
-					    hostbuf,
-					    sizeof(hostbuf), NULL, 0,
-					    NI_NUMERICHOST|
-					    NI_WITHSCOPEID);
-					optarg = strdup(hostbuf);
-					if (optarg == NULL) {
-						syslog(LOG_NOTICE,
-						    "strdup(): %m");
-						sleepexit(1);
-					}
-				} else
-					optarg = invalid_host;
-				if (res != NULL)
-					freeaddrinfo(res);
-			}
+			hflag = 1;
 			hostname = optarg;
 			break;
 		case 'p':
@@ -256,22 +220,28 @@
 			break;
 		case '?':
 		default:
-			if (!uid)
+			if (uid == 0)
 				syslog(LOG_ERR, "invalid flag %c", ch);
 			usage();
 		}
 	argc -= optind;
 	argv += optind;
 
-	if (*argv) {
-		username = *argv;
+	if (argc > 0) {
+		username = strdup(*argv);
+		if (username == NULL)
+			err(1, "strdup()");
 		ask = 0;
-	} else
+	} else {
 		ask = 1;
+	}
 
 	for (cnt = getdtablesize(); cnt > 2; cnt--)
 		(void)close(cnt);
 
+	/*
+	 * Get current TTY
+	 */
 	ttyn = ttyname(STDIN_FILENO);
 	if (ttyn == NULL || *ttyn == '\0') {
 		(void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
@@ -286,110 +256,107 @@
 	 * Get "login-retries" & "login-backoff" from default class
 	 */
 	lc = login_getclass(NULL);
-	prompt = login_getcapstr(lc, "prompt", default_prompt, default_prompt);
+	prompt = login_getcapstr(lc, "prompt",
+	    default_prompt, default_prompt);
 	passwd_prompt = login_getcapstr(lc, "passwd_prompt",
 	    default_passwd_prompt, default_passwd_prompt);
-	retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES,
-	    DEFAULT_RETRIES);
-	backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF,
-	    DEFAULT_BACKOFF);
+	retries = login_getcapnum(lc, "login-retries",
+	    DEFAULT_RETRIES, DEFAULT_RETRIES);
+	backoff = login_getcapnum(lc, "login-backoff",
+	    DEFAULT_BACKOFF, DEFAULT_BACKOFF);
 	login_close(lc);
 	lc = NULL;
 
+	/*
+	 * Try to authenticate the user until we succeed or time out.
+	 */
 	for (cnt = 0;; ask = 1) {
 		if (ask) {
 			fflag = 0;
-			getloginname();
+			if (olduser != NULL)
+				free(olduser);
+			olduser = username;
+			username = getloginname();
 		}
 		rootlogin = 0;
 		rootok = rootterm(tty); /* Default (auth may change) */
 
-		if (strlen(username) > UT_NAMESIZE)
-			username[UT_NAMESIZE] = '\0';
-
 		/*
 		 * Note if trying multiple user names; log failures for
 		 * previous user name, but don't bother logging one failure
 		 * for nonexistent name (mistyped username).
 		 */
-		if (failures && strcmp(tbuf, username)) {
+		if (failures && strcmp(olduser, username) != 0) {
 			if (failures > (pwd ? 0 : 1))
-				badlogin(tbuf);
+				badlogin(olduser);
 		}
-		(void)strlcpy(tbuf, username, sizeof(tbuf));
+		olduser = username;
 
+		/*
+		 * Load the PAM policy and set some variables
+		 */
+		pam_err = pam_start("login", username, &pamc, &pamh);
+		if (pam_err != PAM_SUCCESS) {
+			pam_syslog("pam_start()");
+			bail(NO_SLEEP_EXIT, 1);
+		}
+		pam_err = pam_set_item(pamh, PAM_TTY, tty);
+		if (pam_err != PAM_SUCCESS) {
+			pam_syslog("pam_set_item(PAM_TTY)");
+			bail(NO_SLEEP_EXIT, 1);
+		}
+		pam_err = pam_set_item(pamh, PAM_RHOST, hostname);
+		if (pam_err != PAM_SUCCESS) {
+			pam_syslog("pam_set_item(PAM_RHOST)");
+			bail(NO_SLEEP_EXIT, 1);
+		}
+		
 		pwd = getpwnam(username);
+		if (pwd != NULL && pwd->pw_uid == 0)
+			rootlogin = 1;
 
 		/*
-		 * if we have a valid account name, and it doesn't have a
-		 * password, or the -f option was specified and the caller
-		 * is root or the caller isn't changing their uid, don't
+		 * If the -f option was specified and the caller is
+		 * root or the caller isn't changing their uid, don't
 		 * authenticate.
 		 */
-		if (pwd != NULL) {
-			if (pwd->pw_uid == 0)
-				rootlogin = 1;
-
-			if (fflag && (uid == (uid_t)0 ||
-			    uid == (uid_t)pwd->pw_uid)) {
-				/* already authenticated */
-				break;
-			} else if (pwd->pw_passwd[0] == '\0') {
-				if (!rootlogin || rootok) {
-					/* pretend password okay */
-					rval = 0;
-					goto ttycheck;
-				}
-			}
+		if (pwd != NULL && fflag &&
+		    (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) {
+			/* already authenticated */
+			rval = 0;
+			goto ttycheck;
 		}
 
 		fflag = 0;
 
 		(void)setpriority(PRIO_PROCESS, 0, -4);
 
-#ifndef NO_PAM
-		/*
-		 * Try to authenticate using PAM.  If a PAM system error
-		 * occurs, perhaps because of a botched configuration,
-		 * then fall back to using traditional Unix authentication.
-		 */
-		if ((rval = auth_pam()) == -1)
-#endif /* NO_PAM */
-			rval = auth_traditional();
+		rval = auth_pam();
 
 		(void)setpriority(PRIO_PROCESS, 0, 0);
 
-		/*
-		 * PAM authentication may have changed "pwd" to the
-		 * entry for the template user.  Check again to see if
-		 * this is a root login after all.
-		 */
-		if (pwd != NULL && pwd->pw_uid == 0)
-			rootlogin = 1;
-
 	ttycheck:
-		/*
-		 * If trying to log in as root without Kerberos,
-		 * but with insecure terminal, refuse the login attempt.
-		 */
-		if (pwd && !rval) {
-			if (rootlogin && !rootok)
+		if (pwd && rval == 0) {
+			/* deny root logins on insecure terminals */
+			if (pwd->pw_uid == 0 && !rootok)
 				refused(NULL, "NOROOT", 0);
 			else	/* valid password & authenticated */
 				break;
 		}
 
+		pam_cleanup();
+		
 		(void)printf("Login incorrect\n");
 		failures++;
 
 		/*
-		 * we allow up to 'retry' (10) tries,
-		 * but after 'backoff' (3) we start backing off
+		 * Allow up to 'retry' (10) attempts, but start
+		 * backing off after 'backoff' (3) attempts.
 		 */
 		if (++cnt > backoff) {
 			if (cnt >= retries) {
 				badlogin(username);
-				sleepexit(1);
+				bail(SLEEP_EXIT, 1);
 			}
 			sleep((u_int)((cnt - backoff) * 5));
 		}
@@ -407,6 +374,7 @@
 	lc = login_getpwclass(pwd);
 
 	quietlog = login_getcapbool(lc, "hushlogin", 0);
+	
 	/*
 	 * Switching needed for NFS with root access disabled.
 	 *
@@ -426,92 +394,28 @@
 		pwd->pw_dir = strdup("/");
 		if (pwd->pw_dir == NULL) {
 			syslog(LOG_NOTICE, "strdup(): %m");
-			sleepexit(1);
+			bail(SLEEP_EXIT, 1);
 		}
 	}
 	(void)seteuid(euid);
 	(void)setegid(egid);
 	if (!quietlog)
 		quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
-
-	if (pwd->pw_change || pwd->pw_expire)
-		(void)gettimeofday(&tp, (struct timezone *)NULL);
-
-	warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN,
-	    DEFAULT_WARN);
-
-	if (pwd->pw_expire) {
-		if (tp.tv_sec >= pwd->pw_expire) {
-			refused("Sorry -- your account has expired", "EXPIRED",
-			    1);
-		} else if (pwd->pw_expire - tp.tv_sec < warntime && !quietlog)
-			(void)printf("Warning: your account expires on %s",
-			    ctime(&pwd->pw_expire));
-	}
-
-	if (lc != NULL) {
-		if (hostname) {
-			struct addrinfo hints, *res;
-			int ga_err;
-
-			memset(&hints, 0, sizeof(hints));
-			hints.ai_family = AF_UNSPEC;
-			ga_err = getaddrinfo(full_hostname, NULL, &hints,
-					     &res);
-			if (ga_err == 0) {
-				char hostbuf[MAXHOSTNAMELEN];
-
-				getnameinfo(res->ai_addr, res->ai_addrlen,
-				    hostbuf, sizeof(hostbuf), NULL, 0,
-				    NI_NUMERICHOST|NI_WITHSCOPEID);
-				if ((optarg = strdup(hostbuf)) == NULL) {
-					syslog(LOG_NOTICE, "strdup(): %m");
-					sleepexit(1);
-				}
-			} else
-				optarg = NULL;
-			if (res != NULL)
-				freeaddrinfo(res);
-			if (!auth_hostok(lc, full_hostname, optarg))
-				refused("Permission denied", "HOST", 1);
-		}
-
-		if (!auth_ttyok(lc, tty))
-			refused("Permission denied", "TTY", 1);
-
-		if (!auth_timeok(lc, time(NULL)))
-			refused("Logins not available right now", "TIME", 1);
-	}
+	
         shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
 	if (*pwd->pw_shell == '\0')
 		pwd->pw_shell = strdup(_PATH_BSHELL);
 	if (pwd->pw_shell == NULL) {
 		syslog(LOG_NOTICE, "strdup(): %m");
-		sleepexit(1);
+		bail(SLEEP_EXIT, 1);
 	}
 	if (*shell == '\0')   /* Not overridden */
 		shell = pwd->pw_shell;
 	if ((shell = strdup(shell)) == NULL) {
 		syslog(LOG_NOTICE, "strdup(): %m");
-		sleepexit(1);
+		bail(SLEEP_EXIT, 1);
 	}
 
-#ifdef LOGIN_ACCESS
-	if (login_access(pwd->pw_name, hostname ? full_hostname : tty) == 0)
-		refused("Permission denied", "ACCESS", 1);
-#endif /* LOGIN_ACCESS */
-
-	/* Nothing else left to fail -- really log in. */
-	memset((void *)&utmp, 0, sizeof(utmp));
-	(void)time(&utmp.ut_time);
-	(void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
-	if (hostname)
-		(void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
-	(void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
-	login(&utmp);
-
-	dolastlog(quietlog);
-
 	/*
 	 * Set device protections, depending on what terminal the
 	 * user is logged in. This feature is used on Suns to give
@@ -525,126 +429,116 @@
 	 * Since it isn't clear that flags are useful on character
 	 * devices, we just clear them.
 	 */
-	if (chflags(ttyn, 0) && errno != EOPNOTSUPP)
-		syslog(LOG_ERR, "chmod(%s): %m", ttyn);
-	if (chown(ttyn, pwd->pw_uid,
+	if (ttyn != tname && chflags(ttyn, 0) && errno != EOPNOTSUPP)
+		syslog(LOG_ERR, "chflags(%s): %m", ttyn);
+	if (ttyn != tname && chown(ttyn, pwd->pw_uid,
 	    (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid))
 		syslog(LOG_ERR, "chmod(%s): %m", ttyn);
 
-
-	/*
-	 * Preserve TERM if it happens to be already set.
-	 */
-	if ((term = getenv("TERM")) != NULL) {
-		if ((term = strdup(term)) == NULL) {
-			syslog(LOG_NOTICE,
-			    "strdup(): %m");
-			sleepexit(1);
-		}
-	}
-
 	/*
 	 * Exclude cons/vt/ptys only, assume dialup otherwise
 	 * TODO: Make dialup tty determination a library call
 	 * for consistency (finger etc.)
 	 */
-	if (hostname==NULL && isdialuptty(tty))
+	if (hflag && isdialuptty(tty))
 		syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
 
 #ifdef LOGALL
 	/*
-	 * Syslog each successful login, so we don't have to watch hundreds
-	 * of wtmp or lastlogin files.
+	 * Syslog each successful login, so we don't have to watch
+	 * hundreds of wtmp or lastlogin files.
 	 */
-	if (hostname)
+	if (hflag)
 		syslog(LOG_INFO, "login from %s on %s as %s",
-		       full_hostname, tty, pwd->pw_name);
+		       hostname, tty, pwd->pw_name);
 	else
 		syslog(LOG_INFO, "login on %s as %s",
 		       tty, pwd->pw_name);
 #endif
 
 	/*
-	 * If fflag is on, assume caller/authenticator has logged root login.
+	 * If fflag is on, assume caller/authenticator has logged root
+	 * login.
 	 */
-	if (rootlogin && fflag == 0)
-	{
-		if (hostname)
+	if (rootlogin && fflag == 0) {
+		if (hflag)
 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
-			    username, tty, full_hostname);
+			    username, tty, hostname);
 		else
 			syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
 			    username, tty);
 	}
 
 	/*
-	 * Destroy environment unless user has requested its preservation.
-	 * We need to do this before setusercontext() because that may
-	 * set or reset some environment variables.
+	 * Destroy environment unless user has requested its
+	 * preservation - but preserve TERM in all cases
 	 */
+	term = getenv("TERM");
 	if (!pflag)
 		environ = envinit;
+	if (term != NULL)
+		setenv("TERM", term, 0);
 
 	/*
 	 * PAM modules might add supplementary groups during pam_setcred().
 	 */
 	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
                 syslog(LOG_ERR, "setusercontext() failed - exiting");
-		exit(1);
+		bail(NO_SLEEP_EXIT, 1);
 	}
 
-#ifndef NO_PAM
-	if (pamh) {
-		if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
-			syslog(LOG_ERR, "pam_open_session: %s",
-			    pam_strerror(pamh, e));
-		} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED))
-		    != PAM_SUCCESS) {
-			syslog(LOG_ERR, "pam_setcred: %s",
-			    pam_strerror(pamh, e));
-		}
-
-	        /*
-	         * Add any environmental variables that the
-	         * PAM modules may have set.
-		 * Call *after* opening session!
-		 */
-		if (pamh) {
-		  environ_pam = pam_getenvlist(pamh);
-		  if (environ_pam)
-			export_pam_environment();
-		}
+	pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED);
+	if (pam_err != PAM_SUCCESS) {
+		pam_syslog("pam_setcred()");
+		bail(NO_SLEEP_EXIT, 1);
+	}
+	pam_cred_established = 1;
+	
+	pam_err = pam_open_session(pamh, pam_silent);
+	if (pam_err != PAM_SUCCESS) {
+		pam_syslog("pam_open_session()");
+		bail(NO_SLEEP_EXIT, 1);
+	}
+	pam_session_established = 1;
 
+	/*
+	 * We must fork() before setuid() because we need to call
+	 * pam_close_session() as root.
+	 */
+	pid = fork();
+	if (pid < 0) {
+		err(1, "fork");
+	} else if (pid != 0) {
 		/*
-		 * We must fork() before setuid() because we need to call
-		 * pam_close_session() as root.
+		 * Parent: wait for child to finish, then clean up
+		 * session.
 		 */
-		pid = fork();
-		if (pid < 0) {
-			err(1, "fork");
-			PAM_END;
-			exit(0);
-		} else if (pid) {
-			/* parent - wait for child to finish, then cleanup
-			   session */
-			wait(NULL);
-			PAM_END;
-			exit(0);
-		} else {
-			if ((e = pam_end(pamh, PAM_DATA_SILENT)) != PAM_SUCCESS)
-				syslog(LOG_ERR, "pam_end: %s",
-				    pam_strerror(pamh, e));
-		}
+		wait(NULL);
+		bail(NO_SLEEP_EXIT, 0);
 	}
-#endif /* NO_PAM */
 
 	/*
-	 * We don't need to be root anymore, so
-	 * set the user and session context
+	 * NOTICE: We are now in the child process!
+	 */
+	
+	/*
+	 * Add any environment variables the PAM modules may have set.
+	 */
+	export_pam_environment();
+
+	/*
+	 * We're done with PAM now; our parent will deal with the rest.
+	 */
+	pam_end(pamh, PAM_DATA_SILENT);
+	pamh = NULL;
+
+	/*
+	 * We don't need to be root anymore, so set the login name and
+	 * the UID.
 	 */
 	if (setlogin(username) != 0) {
                 syslog(LOG_ERR, "setlogin(%s): %m - exiting", username);
-		exit(1);
+		bail(NO_SLEEP_EXIT, 1);
 	}
 	if (setusercontext(lc, pwd, pwd->pw_uid,
 	    LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
@@ -654,45 +548,38 @@
 
 	(void)setenv("SHELL", pwd->pw_shell, 1);
 	(void)setenv("HOME", pwd->pw_dir, 1);
-	if (term != NULL && *term != '\0')
-		(void)setenv("TERM", term, 1);		/* Preset overrides */
-	else {
-		(void)setenv("TERM", stypeof(tty), 0);	/* Fallback doesn't */
-	}
+	(void)setenv("TERM", stypeof(tty), 0);
 	(void)setenv("LOGNAME", username, 1);
 	(void)setenv("USER", username, 1);
 	(void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
 
 	if (!quietlog) {
-		const char *cw;
+		char *cw;
 
 		cw = login_getcapstr(lc, "copyright", NULL, NULL);
-		if (cw != NULL && access(cw, F_OK) == 0)
-			motd(cw);
-		else
-		    (void)printf("%s\n\t%s %s\n",
-	"Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
-	"The Regents of the University of California. ",
-	"All rights reserved.");
+		if (cw == NULL || motd(cw) == -1)
+			(void)printf("%s", copyright);
 
 		(void)printf("\n");
 
 		cw = login_getcapstr(lc, "welcome", NULL, NULL);
-		if (cw == NULL || access(cw, F_OK) != 0)
-			cw = _PATH_MOTDFILE;
-		motd(cw);
+		if (cw != NULL && access(cw, F_OK) == 0)
+			motd(cw);
+		else
+			motd(_PATH_MOTDFILE);
 
 		if (login_getcapbool(lc, "nocheckmail", 0) == 0) {
 			/* $MAIL may have been set by class. */
 			cw = getenv("MAIL");
-			if (cw != NULL)
-				strlcpy(tbuf, cw, sizeof(tbuf));
-			else
-				snprintf(tbuf, sizeof(tbuf), "%s/%s",
+			if (cw == NULL) {
+				asprintf(&cw, "%s/%s",
 				    _PATH_MAILDIR, pwd->pw_name);
-			if (stat(tbuf, &st) == 0 && st.st_size != 0)
+			}
+			if (cw && stat(cw, &st) == 0 && st.st_size != 0)
 				(void)printf("You have %smail.\n",
 				    (st.st_mtime > st.st_atime) ? "new " : "");
+			if (getenv("MAIL") == NULL)
+				free(cw);
 		}
 	}
 
@@ -706,45 +593,23 @@
 	/*
 	 * Login shells have a leading '-' in front of argv[0]
 	 */
-	if ((u_int)snprintf(tbuf, sizeof(tbuf), "-%s",
-	    (p = strrchr(pwd->pw_shell, '/')) ? p + 1 : pwd->pw_shell) >=
-	    sizeof(tbuf)) {
+	p = strrchr(pwd->pw_shell, '/');
+	if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) {
 		syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size",
 		    username);
 		errx(1, "shell exceeds maximum pathname size");
+	} else if (arg0 == NULL) {
+		err(1, "asprintf()");
 	}
 
-	execlp(shell, tbuf, (char *)0);
+	execlp(shell, arg0, (char *)0);
 	err(1, "%s", shell);
+	
+	/*
+	 * That's it, folks!
+	 */
 }
 
-static int
-auth_traditional()
-{
-	int rval;
-	char *p;
-	const char *ep;
-	const char *salt;
-
-	rval = 1;
-	salt = pwd != NULL ? pwd->pw_passwd : "xx";
-
-	p = getpass(passwd_prompt);
-	ep = crypt(p, salt);
-
-	if (pwd) {
-		if (!p[0] && pwd->pw_passwd[0])
-			ep = ":";
-		if (strcmp(ep, pwd->pw_passwd) == 0)
-			rval = 0;
-	}
-
-	/* clear entered password */
-	memset(p, 0, strlen(p));
-	return rval;
-}
-
-#ifndef NO_PAM
 /*
  * Attempt to authenticate the user using PAM.  Returns 0 if the user is
  * authenticated, or 1 if not authenticated.  If some sort of PAM system
@@ -753,32 +618,14 @@
  * fall back to a different authentication mechanism.
  */
 static int
-auth_pam()
+auth_pam(void)
 {
 	const char *tmpl_user;
 	const void *item;
 	int rval;
-	int e;
-	static struct pam_conv conv = { misc_conv, NULL };
 
-	if ((e = pam_start("login", username, &conv, &pamh)) != PAM_SUCCESS) {
-		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e));
-		return -1;
-	}
-	if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
-		syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
-		    pam_strerror(pamh, e));
-		return -1;
-	}
-	if (hostname == NULL)
-		gethostname(full_hostname, sizeof full_hostname);
-	if ((e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
-		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
-		    pam_strerror(pamh, e));
-		return -1;
-	}
-	e = pam_authenticate(pamh, 0);
-	switch (e) {
+	pam_err = pam_authenticate(pamh, pam_silent);
+	switch (pam_err) {
 
 	case PAM_SUCCESS:
 		/*
@@ -798,14 +645,14 @@
 		 * point of view, the template user is always passed
 		 * back as a changed value of the PAM_USER item.
 		 */
-		if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
-		    PAM_SUCCESS) {
-			tmpl_user = (const char *) item;
+		pam_err = pam_get_item(pamh, PAM_USER, &item);
+		if (pam_err == PAM_SUCCESS) {
+			tmpl_user = (const char *)item;
 			if (strcmp(username, tmpl_user) != 0)
 				pwd = getpwnam(tmpl_user);
-		} else
-			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
-			    pam_strerror(pamh, e));
+		} else {
+			pam_syslog("pam_get_item(PAM_USER)");
+		}
 		rval = 0;
 		break;
 
@@ -816,57 +663,66 @@
 		break;
 
 	default:
-		syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
+		pam_syslog("pam_authenticate()");
 		rval = -1;
 		break;
 	}
 
 	if (rval == 0) {
-		e = pam_acct_mgmt(pamh, 0);
-		if (e == PAM_NEW_AUTHTOK_REQD) {
-			e = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
-			if (e != PAM_SUCCESS) {
-				syslog(LOG_ERR, "pam_chauthtok: %s",
-				    pam_strerror(pamh, e));
+		pam_err = pam_acct_mgmt(pamh, pam_silent);
+		switch (pam_err) {
+		case PAM_SUCCESS:
+			break;
+		case PAM_NEW_AUTHTOK_REQD:
+			pam_err = pam_chauthtok(pamh,
+			    pam_silent|PAM_CHANGE_EXPIRED_AUTHTOK);
+			if (pam_err != PAM_SUCCESS) {
+				pam_syslog("pam_chauthtok()");
 				rval = 1;
 			}
-		} else if (e != PAM_SUCCESS) {
+			break;
+		default:
+			pam_syslog("pam_acct_mgmt()");
 			rval = 1;
+			break;
 		}
 	}
 
 	if (rval != 0) {
-		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
-			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
-		}
+		pam_end(pamh, pam_err);
 		pamh = NULL;
 	}
-	return rval;
+	return (rval);
 }
 
-static int
+/*
+ * Export any environment variables PAM modules may have set
+ */
+static void
 export_pam_environment()
 {
-	char	**pp;
+	char **pam_env;
+	char **pp;
 
-	for (pp = environ_pam; *pp != NULL; pp++) {
-		if (ok_to_export(*pp))
-			(void) putenv(*pp);
-		free(*pp);
+	pam_env = pam_getenvlist(pamh);
+	if (pam_env != NULL) {
+		for (pp = pam_env; *pp != NULL; pp++) {
+			(void)export(*pp);
+			free(*pp);
+		}
 	}
-	return PAM_SUCCESS;
 }
 
 /*
- * Sanity checks on PAM environmental variables:
+ * Perform sanity checks on an environment variable:
  * - Make sure there is an '=' in the string.
  * - Make sure the string doesn't run on too long.
  * - Do not export certain variables.  This list was taken from the
  *   Solaris pam_putenv(3) man page.
+ * Then export it.
  */
 static int
-ok_to_export(s)
-	const char *s;
+export(const char *s)
 {
 	static const char *noexport[] = {
 		"SHELL", "HOME", "LOGNAME", "MAIL", "CDPATH",
@@ -876,17 +732,17 @@
 	size_t n;
 
 	if (strlen(s) > 1024 || strchr(s, '=') == NULL)
-		return 0;
+		return (0);
 	if (strncmp(s, "LD_", 3) == 0)
-		return 0;
+		return (0);
 	for (pp = noexport; *pp != NULL; pp++) {
 		n = strlen(*pp);
 		if (s[n] == '=' && strncmp(s, *pp, n) == 0)
-			return 0;
+			return (0);
 	}
-	return 1;
+	(void)putenv(s);
+	return (1);
 }
-#endif /* NO_PAM */
 
 static void
 usage()
@@ -897,135 +753,114 @@
 }
 
 /*
- * Allow for authentication style and/or kerberos instance
+ * Prompt user and read login name from stdin.
  */
-
-void
+static char *
 getloginname()
 {
+	char *nbuf, *p;
 	int ch;
-	char *p;
-	static char nbuf[NBUFSIZ];
 
-	for (;;) {
+	nbuf = malloc(MAXLOGNAME);
+	if (nbuf == NULL)
+		err(1, "malloc()");
+	do {
 		(void)printf("%s", prompt);
 		for (p = nbuf; (ch = getchar()) != '\n'; ) {
 			if (ch == EOF) {
 				badlogin(username);
-				exit(0);
+				bail(NO_SLEEP_EXIT, 0);
 			}
-			if (p < nbuf + (NBUFSIZ - 1))
+			if (p < nbuf + MAXLOGNAME - 1)
 				*p++ = ch;
 		}
-		if (p > nbuf) {
-			if (nbuf[0] == '-')
-				(void)fprintf(stderr,
-				    "login names may not start with '-'.\n");
-			else {
-				*p = '\0';
-				username = nbuf;
-				break;
-			}
-		}
+	} while (p == nbuf);
+	
+	*p = '\0';
+	if (nbuf[0] == '-') {
+		pam_silent = 0;
+		memmove(nbuf, nbuf + 1, strlen(nbuf));
+	} else {
+		pam_silent = PAM_SILENT;
 	}
+	return nbuf;
 }
 
-int
-rootterm(ttyn)
-	char *ttyn;
+/*
+ * Verify that root is allowed to log in on this terminal.  This
+ * requires the terminal to be known and secure.
+ */
+static int
+rootterm(char *ttyn)
 {
 	struct ttyent *t;
 
 	return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
 }
 
-volatile int motdinterrupt;
 
-void
-sigint(signo)
-	int signo __unused;
+/*
+ * SIGINT handler for motd().
+ */
+static volatile int motdinterrupt;
+static void
+sigint(int signo __unused)
 {
 	motdinterrupt = 1;
 }
 
-void
-motd(motdfile)
-	const char *motdfile;
+/*
+ * Display the contents of a file (such as /etc/motd).
+ */
+static int
+motd(const char *motdfile)
 {
-	int fd, nchars;
 	sig_t oldint;
-	char tbuf[256];
+	FILE *f;
+	int ch;
 
-	if ((fd = open(motdfile, O_RDONLY, 0)) < 0)
-		return;
+	if ((f = fopen(motdfile, "r")) == NULL)
+		return (-1);
 	motdinterrupt = 0;
 	oldint = signal(SIGINT, sigint);
-	while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 && !motdinterrupt)
-		(void)write(fileno(stdout), tbuf, nchars);
-	(void)signal(SIGINT, oldint);
-	(void)close(fd);
+	while ((ch = fgetc(f)) != EOF && !motdinterrupt)
+		putchar(ch);
+	signal(SIGINT, oldint);
+	if (ch != EOF || ferror(f)) {
+		fclose(f);
+		return (-1);
+	}
+	fclose(f);
+	return (0);
 }
 
-/* ARGSUSED */
-void
-timedout(signo)
-	int signo;
+/*
+ * SIGALRM handler, to enforce login prompt timeout.
+ *
+ * XXX This can potentially confuse the hell out of PAM.  We should
+ * XXX instead implement a conversation function that returns
+ * XXX PAM_CONV_ERR when interrupted by a signal, and have the signal
+ * XXX handler just set a flag.
+ */
+static void
+timedout(int signo __unused)
 {
 
 	longjmp(timeout_buf, signo);
 }
 
-
-void
-dolastlog(quiet)
-	int quiet;
-{
-	struct lastlog ll;
-	int fd;
-
-	if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
-		(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
-		if (!quiet) {
-			if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
-			    ll.ll_time != 0) {
-				(void)printf("Last login: %.*s ",
-				    24-5, (char *)ctime(&ll.ll_time));
-				if (*ll.ll_host != '\0')
-					(void)printf("from %.*s\n",
-					    (int)sizeof(ll.ll_host),
-					    ll.ll_host);
-				else
-					(void)printf("on %.*s\n",
-					    (int)sizeof(ll.ll_line),
-					    ll.ll_line);
-			}
-			(void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
-		}
-		memset((void *)&ll, 0, sizeof(ll));
-		(void)time(&ll.ll_time);
-		(void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
-		if (hostname)
-			(void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
-		(void)write(fd, (char *)&ll, sizeof(ll));
-		(void)close(fd);
-	} else {
-		syslog(LOG_ERR, "cannot open %s: %m", _PATH_LASTLOG);
-	}
-}
-
 void
-badlogin(name)
-	char *name;
+badlogin(char *name)
 {
 
 	if (failures == 0)
 		return;
-	if (hostname) {
+	if (hflag) {
 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
-		    failures, failures > 1 ? "S" : "", full_hostname);
+		    failures, failures > 1 ? "S" : "", hostname);
 		syslog(LOG_AUTHPRIV|LOG_NOTICE,
 		    "%d LOGIN FAILURE%s FROM %s, %s",
-		    failures, failures > 1 ? "S" : "", full_hostname, name);
+		    failures, failures > 1 ? "S" : "", hostname, name);
 	} else {
 		syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
 		    failures, failures > 1 ? "S" : "", tty);
@@ -1037,8 +872,7 @@
 }
 
 const char *
-stypeof(ttyid)
-	char *ttyid;
+stypeof(char *ttyid)
 {
 	struct ttyent *t;
 
@@ -1047,33 +881,67 @@
 		if (t != NULL && t->ty_type != NULL)
 			return (t->ty_type);
 	}
-	return (UNKNOWN);
+	return (TERM_UNKNOWN);
 }
 
 void
-refused(msg, rtype, lout)
-	const char *msg;
-	const char *rtype;
-	int lout;
+refused(const char *msg, const char *rtype, int lout)
 {
 
 	if (msg != NULL)
 	    printf("%s.\n", msg);
-	if (hostname)
+	if (hflag)
 		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) FROM %s ON TTY %s",
-		    pwd->pw_name, rtype, full_hostname, tty);
+		    pwd->pw_name, rtype, hostname, tty);
 	else
 		syslog(LOG_NOTICE, "LOGIN %s REFUSED (%s) ON TTY %s",
 		    pwd->pw_name, rtype, tty);
 	if (lout)
-		sleepexit(1);
+		bail(SLEEP_EXIT, 1);
 }
 
+/*
+ * Log a PAM error
+ */
+void
+pam_syslog(const char *msg)
+{
+	syslog(LOG_ERR, "%s: %s", msg, pam_strerror(pamh, pam_err));
+}
+
+/*
+ * Shut down PAM
+ */
+void
+pam_cleanup()
+{
+
+	if (pamh != NULL) {
+		if (pam_session_established) {
+			pam_err = pam_close_session(pamh, 0);
+			if (pam_err != PAM_SUCCESS)
+				pam_syslog("pam_close_session()");
+		}
+		pam_session_established = 0;
+		if (pam_cred_established) {
+			pam_err = pam_setcred(pamh, pam_silent|PAM_DELETE_CRED);
+			if (pam_err != PAM_SUCCESS)
+				pam_syslog("pam_setcred()");
+		}
+		pam_cred_established = 0;
+		pam_end(pamh, pam_err);
+		pamh = NULL;
+	}
+}
+
+/*
+ * Exit, optionally after sleeping a few seconds
+ */
 void
-sleepexit(eval)
-	int eval;
+bail(int sec, int eval)
 {
 
-	(void)sleep(5);
+	pam_cleanup();
+	(void)sleep(sec);
 	exit(eval);
 }
Index: usr.bin/login/login.h
===================================================================
RCS file: /home/ncvs/src/usr.bin/login/login.h,v
retrieving revision 1.2
diff -u -r1.2 login.h
--- usr.bin/login/login.h	1 Dec 2001 21:12:04 -0000	1.2
+++ usr.bin/login/login.h	24 Jan 2002 17:19:32 -0000
@@ -25,7 +25,6 @@
  * $FreeBSD: src/usr.bin/login/login.h,v 1.2 2001/12/01 21:12:04 markm Exp $
  */
 
-int	login_access __P((char *, char *));
 void	login_fbtab __P((char *, uid_t, gid_t));
 
 extern	char **environ;
Index: usr.bin/login/login_access.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/login/login_access.c,v
retrieving revision 1.8
diff -u -r1.8 login_access.c
--- usr.bin/login/login_access.c	10 Dec 2001 21:13:05 -0000	1.8
+++ usr.bin/login/login_access.c	24 Jan 2002 17:19:32 -0000
@@ -40,18 +40,19 @@
 #define YES             1
 #define NO              0
 
-static int	from_match __P((char *, char *));
-static int	list_match __P((char *, char *, int (*)(char *, char *)));
-static int	netgroup_match __P((char *, char *, char *));
-static int	string_match __P((char *, char *));
-static int	user_match __P((char *, char *));
+static int	from_match __P((const char *, const char *));
+static int	list_match __P((char *, const char *,
+				int (*)(const char *, const char *)));
+static int	netgroup_match __P((const char *, const char *, const char *));
+static int	string_match __P((const char *, const char *));
+static int	user_match __P((const char *, const char *));
 
 /* login_access - match username/group and host/tty with access control file */
 
 int
 login_access(user, from)
-char   *user;
-char   *from;
+const char   *user;
+const char   *from;
 {
     FILE   *fp;
     char    line[BUFSIZ];
@@ -111,9 +112,9 @@
 /* list_match - match an item against a list of tokens with exceptions */
 
 static int list_match(list, item, match_fn)
-char   *list;
-char   *item;
-int   (*match_fn) __P((char *, char *));
+char         *list;
+const char   *item;
+int         (*match_fn) __P((const char *, const char *));
 {
     char   *tok;
     int     match = NO;
@@ -145,9 +146,9 @@
 /* netgroup_match - match group against machine or user */
 
 static int netgroup_match(group, machine, user)
-char   *group __unused;
-char   *machine __unused;
-char   *user __unused;
+const char   *group __unused;
+const char   *machine __unused;
+const char   *user __unused;
 {
     syslog(LOG_ERR, "NIS netgroup support not configured");
     return 0;
@@ -156,8 +157,8 @@
 /* user_match - match a username against one token */
 
 static int user_match(tok, string)
-char   *tok;
-char   *string;
+const char   *tok;
+const char   *string;
 {
     struct group *group;
     int     i;
@@ -183,8 +184,8 @@
 /* from_match - match a host or tty against a list of tokens */
 
 static int from_match(tok, string)
-char   *tok;
-char   *string;
+const char   *tok;
+const char   *string;
 {
     int     tok_len;
     int     str_len;
@@ -219,8 +220,8 @@
 /* string_match - match a string against one token */
 
 static int string_match(tok, string)
-char   *tok;
-char   *string;
+const char   *tok;
+const char   *string;
 {
 
     /*

--=-=-=--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?xzpofjjbr4q.fsf>