Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 20 Jan 2001 23:58:23 -0500
From:      "David J. MacKenzie" <djm@web.us.uu.net>
To:        freebsd-security@freebsd.org
Cc:        djm@web.us.uu.net
Subject:   PAM patch, iteration 3
Message-ID:  <20010121045823.59DB212686@jenkins.web.us.uu.net>

next in thread | raw e-mail | index | archive | help
After reading the RFC and considering my code some more, I've made
more fixes to it, including initialization of supplementary groups
and environment variables, and several other fixes.  As before,
this patch replaces the previous two that I sent out.  It was
generated against -stable.

The rshd with PAM and without Kerberos won't be very useful
until FreeBSD imports the pam_rhosts module from Linux-PAM.
I don't think the rcmd/rsh protocol provides a way to prompt
the remote user for a password.

login was alone in using NO_PAM instead of USE_PAM for the #ifdef.
sshd used USE_PAM and I used that in the other utilities as well,
so I changed login to match.  I think it's easier to understand
that way.

The Linux-PAM manual, the RFC example, and the Solaris man page all
indicate that this patch to pam_setcred.3 is correct.

--- ./contrib/libpam/doc/man/pam_setcred.3	1998/11/18 01:20:54	1.1
+++ ./contrib/libpam/doc/man/pam_setcred.3	2001/01/21 00:08:26
@@ -16,7 +16,7 @@
 
 This function is used to establish, maintain and delete the
 credentials of a user. It should be called after a user has been
-authenticated and before a session is opened for the user (with
+authenticated and after a session is opened for the user (with
 .BR pam_open_session "(3))."
 
 It should be noted that credentials come in many forms. Examples
--- ./libexec/rshd/Makefile	2001/01/17 00:04:57	1.1
+++ ./libexec/rshd/Makefile	2001/01/21 04:45:35
@@ -12,6 +12,12 @@
 DPADD+=	${LIBUTIL}
 LDADD+= -lutil
 
+.if !defined(NOPAM)
+CFLAGS+= -DUSE_PAM
+DPADD+=	${LIBPAM}
+LDADD+=	${MINUSLPAM}
+.endif
+
 # IPv6 support
 CFLAGS+= -DINET6
 
--- ./libexec/rshd/rshd.c	2000/11/12 07:00:38	1.1
+++ ./libexec/rshd/rshd.c	2001/01/21 04:25:51
@@ -80,6 +80,20 @@
 #include <login_cap.h>
 #endif
 
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#include <sys/wait.h>
+static pam_handle_t *pamh;
+#define PAM_END { \
+	if ((retcode = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
+		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode)); \
+	if ((retcode = pam_close_session(pamh,0)) != PAM_SUCCESS) \
+		syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, retcode)); \
+	if ((retcode = pam_end(pamh, retcode)) != PAM_SUCCESS) \
+		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, retcode)); \
+}
+#endif /* USE_PAM */
+
 /* wrapper for KAME-special getnameinfo() */
 #ifndef NI_WITHSCOPEID
 #define	NI_WITHSCOPEID	0
@@ -188,6 +202,20 @@
 	return(0);
 }
 
+#ifdef USE_PAM
+/*
+ * We can't have a conversation with the client over the rsh connection.
+ * You must use auth methods that don't require one, like pam_rhosts.
+ */
+
+int null_conv(int num_msg, const struct pam_message **msg,
+              struct pam_response **resp, void *appdata_ptr)
+{
+	syslog(LOG_ERR, "PAM conversation is not supported\n");
+	return PAM_CONV_ERR;
+}
+#endif /* USE_PAM */
+
 char	username[20] = "USER=";
 char	homedir[64] = "HOME=";
 char	shell[64] = "SHELL=";
@@ -219,6 +247,11 @@
 #ifdef	LOGIN_CAP
 	login_cap_t *lc;
 #endif
+#ifdef USE_PAM
+	static struct pam_conv conv = { null_conv, NULL };
+	int retcode, i;
+	const char * const *env;
+#endif /* USE_PAM */
 
 	(void) signal(SIGINT, SIG_DFL);
 	(void) signal(SIGQUIT, SIG_DFL);
@@ -341,6 +374,36 @@
 
 	getstr(locuser, sizeof(locuser), "locuser");
 	getstr(cmdbuf, sizeof(cmdbuf), "command");
+
+#ifdef USE_PAM
+	retcode = pam_start("rsh", locuser, &conv, &pamh);
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR, "pam_start: %s\n", pam_strerror(pamh, retcode));
+		exit(1);
+	}
+	pam_set_item (pamh, PAM_RUSER, remuser);
+	pam_set_item (pamh, PAM_RHOST, fromhost);
+	pam_set_item (pamh, PAM_TTY, "tty");
+
+	retcode = pam_authenticate(pamh, 0);
+	if (retcode == PAM_SUCCESS) {
+		if ((retcode = pam_get_item(pamh, PAM_USER, (const void **) &cp)) == PAM_SUCCESS) {
+			strncpy(locuser, cp, sizeof(locuser));
+			locuser[sizeof(locuser) - 1] = '\0';
+		} else
+			syslog(LOG_ERR|LOG_AUTH, "Couldn't get PAM_USER: %s",
+			       pam_strerror(pamh, retcode));
+		retcode = pam_acct_mgmt(pamh, 0);
+	}
+	if (retcode != PAM_SUCCESS) {
+		pam_end(pamh, retcode);
+		syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'",
+		       remuser, fromhost, locuser, pam_strerror(pamh, retcode), cmdbuf);
+		error("Login incorrect.\n");
+		exit(1);
+	}
+#endif /* USE_PAM */
+
 	setpwent();
 	pwd = getpwnam(locuser);
 	if (pwd == NULL) {
@@ -349,11 +412,42 @@
 		    remuser, fromhost, locuser, cmdbuf);
 		if (errorstr == NULL)
 			errorstr = "Login incorrect.\n";
-		goto fail;
+		error(errorstr, fromhost);
+		exit(1);
 	}
-#ifdef	LOGIN_CAP
+
+#ifndef USE_PAM
+	if (errorstr ||
+	    (pwd->pw_expire && time(NULL) >= pwd->pw_expire) ||
+	    iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0,
+			remuser, locuser) < 0) {
+		if (__rcmd_errstr)
+			syslog(LOG_INFO|LOG_AUTH,
+			       "%s@%s as %s: permission denied (%s). cmd='%.80s'",
+			       remuser, fromhost, locuser, __rcmd_errstr,
+			       cmdbuf);
+		else
+			syslog(LOG_INFO|LOG_AUTH,
+			       "%s@%s as %s: permission denied. cmd='%.80s'",
+			       remuser, fromhost, locuser, cmdbuf);
+		if (errorstr == NULL)
+			errorstr = "Login incorrect.\n";
+		error(errorstr, fromhost);
+		exit(1);
+	}
+#endif /* USE_PAM */
+
+#ifdef LOGIN_CAP
 	lc = login_getpwclass(pwd);
-#endif
+	if (pwd->pw_uid)
+		auth_checknologin(lc);
+#else /* !LOGIN_CAP */
+	if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
+		error("Logins currently disabled.\n");
+		exit(1);
+	}
+#endif /* LOGIN_CAP */
+
 	if (chdir(pwd->pw_dir) < 0) {
 #ifdef	LOGIN_CAP
 		if (chdir("/") < 0 ||
@@ -377,30 +471,6 @@
 		pwd->pw_dir = "/";
 	}
 
-		if (errorstr ||
-		    (pwd->pw_expire && time(NULL) >= pwd->pw_expire) ||
-		    iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0,
-				 remuser, locuser) < 0) {
-			if (__rcmd_errstr)
-				syslog(LOG_INFO|LOG_AUTH,
-			    "%s@%s as %s: permission denied (%s). cmd='%.80s'",
-				    remuser, fromhost, locuser, __rcmd_errstr,
-				    cmdbuf);
-			else
-				syslog(LOG_INFO|LOG_AUTH,
-			    "%s@%s as %s: permission denied. cmd='%.80s'",
-				    remuser, fromhost, locuser, cmdbuf);
-fail:
-			if (errorstr == NULL)
-				errorstr = "Login incorrect.\n";
-			error(errorstr, fromhost);
-			exit(1);
-		}
-
-	if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) {
-		error("Logins currently disabled.\n");
-		exit(1);
-	}
 #ifdef	LOGIN_CAP
 	if (lc != NULL && fromp->su_family == AF_INET) {	/*XXX*/
 		char	remote_ip[MAXHOSTNAMELEN];
@@ -421,13 +491,36 @@
 			exit(1);
 		}
 	}
-#endif	/* !LOGIN_CAP */
+#endif	/* LOGIN_CAP */
 #if	BSD > 43
 	/* before fork, while we're session leader */
 	if (setlogin(pwd->pw_name) < 0)
 		syslog(LOG_ERR, "setlogin() failed: %m");
 #endif
 
+	/*
+	 * PAM modules might add supplementary groups in
+	 * pam_setcred(), so initialize them first.
+	 * But we need to open the session as root.
+	 */
+#ifdef	LOGIN_CAP
+	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
+                syslog(LOG_ERR, "setusercontext: %m");
+		exit(1);
+	}
+#else /* !LOGIN_CAP */
+	(void) setgid((gid_t)pwd->pw_gid);
+	initgroups(pwd->pw_name, pwd->pw_gid);
+#endif /* LOGIN_CAP */
+
+#ifdef USE_PAM
+	if ((retcode = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
+		syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, retcode));
+	} else if ((retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode));
+	}
+#endif /* USE_PAM */
+
 	(void) write(STDERR_FILENO, "\0", 1);
 	sent_null = 1;
 
@@ -451,6 +544,9 @@
 		pid = fork();
 		if (pid == -1)  {
 			error("Can't fork; try again.\n");
+#ifdef USE_PAM
+			PAM_END;
+#endif /* USE_PAM */
 			exit(1);
 		}
 		if (pid) {
@@ -569,6 +665,9 @@
 			    (doencrypt && FD_ISSET(pv1[0], &readfrom)) ||
 #endif
 			    FD_ISSET(pv[0], &readfrom));
+#ifdef USE_PAM
+			PAM_END;
+#endif /* USE_PAM */
 			exit(0);
 		}
 		setpgrp(0, getpid());
@@ -586,6 +685,23 @@
 		dup2(pv[1], 2);
 		close(pv[1]);
 	}
+#ifdef USE_PAM
+	else {
+		pid = fork();
+		if (pid == -1)  {
+			error("Can't fork; try again.\n");
+			PAM_END;
+			exit(1);
+		}
+		if (pid) {
+			/* Parent. */
+			wait(NULL);
+			PAM_END;
+			exit(0);
+		}
+	}
+#endif /* USE_PAM */
+
 	if (*pwd->pw_shell == '\0')
 		pwd->pw_shell = _PATH_BSHELL;
 	environ = envinit;
@@ -598,17 +714,24 @@
 		cp++;
 	else
 		cp = pwd->pw_shell;
+
 #ifdef	LOGIN_CAP
-	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) {
+	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~LOGIN_SETGROUP) != 0) {
                 syslog(LOG_ERR, "setusercontext: %m");
 		exit(1);
 	}
 	login_close(lc);
 #else
-	(void) setgid((gid_t)pwd->pw_gid);
-	initgroups(pwd->pw_name, pwd->pw_gid);
 	(void) setuid((uid_t)pwd->pw_uid);
 #endif
+#ifdef USE_PAM
+	env = (const char * const *)pam_getenvlist(pamh);
+	if (env != NULL) {
+		for (i=0; env[i]; i++)
+			putenv(env[i]);
+	}
+#endif /* USE_PAM */
+
 	endpwent();
 	if (log_success || pwd->pw_uid == 0) {
 		    syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
--- ./usr.bin/login/Makefile	2001/01/21 04:44:21	1.1
+++ ./usr.bin/login/Makefile	2001/01/21 04:44:45
@@ -11,9 +11,8 @@
 DPADD=	${LIBUTIL} ${LIBCRYPT}
 LDADD=	-lutil -lcrypt
 
-.if defined(NOPAM)
-CFLAGS+= -DNO_PAM
-.else
+.if !defined(NOPAM)
+CFLAGS+= -DUSE_PAM
 DPADD+=	${LIBPAM}
 LDADD+=	${MINUSLPAM}
 .endif
--- ./usr.bin/login/login.c	2000/08/08 03:12:59	1.1
+++ ./usr.bin/login/login.c	2001/01/21 04:44:15
@@ -78,10 +78,11 @@
 #include <unistd.h>
 #include <utmp.h>
 
-#ifndef NO_PAM
+#ifdef USE_PAM
 #include <security/pam_appl.h>
 #include <security/pam_misc.h>
-#endif
+#include <sys/wait.h>
+#endif /* USE_PAM */
 
 #include "pathnames.h"
 
@@ -104,9 +105,18 @@
 int	 login_access __P((char *, char *));
 void     login_fbtab __P((char *, uid_t, gid_t));
 
-#ifndef NO_PAM
+#ifdef USE_PAM
 static int auth_pam __P((void));
-#endif
+pam_handle_t *pamh = NULL;
+#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 /* USE_PAM */
 static int auth_traditional __P((void));
 extern void login __P((struct utmp *));
 static void usage __P((void));
@@ -150,6 +160,10 @@
 	char tname[sizeof(_PATH_TTY) + 10];
 	char *shell = NULL;
 	login_cap_t *lc = NULL;
+#ifdef USE_PAM
+	pid_t pid;
+	int e;
+#endif /* USE_PAM */
 
 	(void)signal(SIGQUIT, SIG_IGN);
 	(void)signal(SIGINT, SIG_IGN);
@@ -309,19 +323,19 @@
 
 		(void)setpriority(PRIO_PROCESS, 0, -4);
 
-#ifndef NO_PAM
+#ifdef USE_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 */
+#endif /* USE_PAM */
 			rval = auth_traditional();
 
 		(void)setpriority(PRIO_PROCESS, 0, 0);
 
-#ifndef NO_PAM
+#ifdef USE_PAM
 		/*
 		 * PAM authentication may have changed "pwd" to the
 		 * entry for the template user.  Check again to see if
@@ -329,7 +343,7 @@
 		 */
 		if (pwd != NULL && pwd->pw_uid == 0)
 			rootlogin = 1;
-#endif /* NO_PAM */
+#endif /* USE_PAM */
 
 	ttycheck:
 		/*
@@ -549,6 +563,43 @@
 		environ = envinit;
 
 	/*
+	 * 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);
+	}
+
+#ifdef USE_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));
+		}
+
+		/*
+		 * We must fork() before setuid() because we need to call
+		 * pam_close_session() as root.
+		 */
+		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, e)) != PAM_SUCCESS)
+				syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
+		}
+	}
+#endif /* USE_PAM */
+
+	/*
 	 * We don't need to be root anymore, so
 	 * set the user and session context
 	 */
@@ -557,7 +608,7 @@
 		exit(1);
 	}
 	if (setusercontext(lc, pwd, pwd->pw_uid,
-	    LOGIN_SETALL & ~LOGIN_SETLOGIN) != 0) {
+	    LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) {
                 syslog(LOG_ERR, "setusercontext() failed - exiting");
 		exit(1);
 	}
@@ -573,6 +624,17 @@
 	(void)setenv("USER", username, 1);
 	(void)setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0);
 
+#ifdef USE_PAM
+	if (pamh) {
+		const char * const *env = (const char * const *)pam_getenvlist(pamh);
+		int i;
+		if (env != NULL) {
+			for (i=0; env[i]; i++)
+				putenv(env[i]);
+		}
+	}
+#endif /* USE_PAM */
+
 	if (!quietlog) {
 		char	*cw;
 
@@ -652,7 +714,7 @@
 	return rval;
 }
 
-#ifndef NO_PAM
+#ifdef USE_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
@@ -663,7 +725,6 @@
 static int
 auth_pam()
 {
-	pam_handle_t *pamh = NULL;
 	const char *tmpl_user;
 	const void *item;
 	int rval;
@@ -724,17 +785,33 @@
 		break;
 
 	default:
-		syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e));
+		syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
 		rval = -1;
 		break;
 	}
-	if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
-		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
-		rval = -1;
+
+	if (rval != -1) {
+		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));
+				rval = -1;
+			}
+		} else if (e != PAM_SUCCESS) {
+			rval = 1;
+		}
+	}
+
+	if (rval == -1) {
+		if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
+			syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
+		}
+		pamh = NULL;
 	}
 	return rval;
 }
-#endif /* NO_PAM */
+#endif /* USE_PAM */
 
 static void
 usage()
@@ -745,7 +822,7 @@
 
 /*
  * Allow for authentication style and/or kerberos instance
- * */
+ */
 
 #define	NBUFSIZ		UT_NAMESIZE + 64
 
--- ./usr.bin/su/Makefile	2001/01/16 21:33:47	1.1
+++ ./usr.bin/su/Makefile	2001/01/21 04:45:12
@@ -8,6 +8,12 @@
 DPADD=	${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
 LDADD=	-lutil -lskey -lmd -lcrypt
 
+.if !defined(NOPAM)
+CFLAGS+= -DUSE_PAM
+DPADD+=	${LIBPAM}
+LDADD+=	${MINUSLPAM}
+.endif
+
 .if defined(WHEELSU)
 COPTS+=	-DWHEELSU
 .endif
--- ./usr.bin/su/su.c	2000/02/24 21:06:21	1.1
+++ ./usr.bin/su/su.c	2001/01/21 04:27:04
@@ -65,6 +65,30 @@
 #include <login_cap.h>
 #endif
 
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+#include <signal.h>
+#include <sys/wait.h>
+#define PAM_FAIL_CHECK \
+	if (retcode != PAM_SUCCESS) { \
+		fprintf(stderr, "su: PAM error: %s\n", pam_strerror(pamh, retcode)); \
+		syslog(LOG_ERR, "PAM error: %s", pam_strerror(pamh, retcode)); \
+		pam_end(pamh, retcode); \
+		exit(1); \
+	}
+#define PAM_END { \
+	if ((retcode = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) { \
+		fprintf(stderr, "su: PAM error: %s\n", pam_strerror(pamh, retcode)); \
+		syslog(LOG_ERR, "PAM error: %s", pam_strerror(pamh, retcode)); \
+	} \
+	if ((retcode = pam_end(pamh,retcode)) != PAM_SUCCESS) { \
+		fprintf(stderr, "su: PAM error: %s\n", pam_strerror(pamh, retcode)); \
+		syslog(LOG_ERR, "PAM error: %s", pam_strerror(pamh, retcode)); \
+	} \
+}
+#endif /* USE_PAM */
+
 #ifdef	SKEY
 #include <skey.h>
 #endif
@@ -107,8 +131,7 @@
 	char *targetpass;
 	int iswheelsu;
 #endif /* WHEELSU */
-	char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np;
-	struct group *gr;
+	char *p, *user, *shell=NULL, *username, *cleanenv = NULL, **nargv, **np;
 	uid_t ruid;
 	gid_t gid;
 	int asme, ch, asthem, fastlogin, prio, i;
@@ -118,6 +141,18 @@
 	char *class=NULL;
 	int setwhat;
 #endif
+#ifdef USE_PAM
+	int retcode;
+	pam_handle_t *pamh = NULL;
+	struct pam_conv conv = { misc_conv, NULL };
+	char myhost[MAXHOSTNAMELEN + 1], *mytty;
+	int statusp=0;
+	int child_pid, child_pgrp, ret_pid;
+	const char * const *env;
+#else /* !USE_PAM */
+	char **g;
+	struct group *gr;
+#endif /* USE_PAM */
 #ifdef KERBEROS
 	char *k;
 #endif
@@ -214,6 +249,38 @@
 		}
 	}
 
+#ifdef USE_PAM
+	retcode = pam_start("su", user, &conv, &pamh);
+	PAM_FAIL_CHECK;
+
+	gethostname(myhost, sizeof(myhost));
+	retcode = pam_set_item(pamh, PAM_RHOST, myhost);
+	PAM_FAIL_CHECK;
+
+	mytty = ttyname(STDERR_FILENO);
+	if (!mytty)
+		mytty = "tty";
+	retcode = pam_set_item(pamh, PAM_TTY, mytty);
+	PAM_FAIL_CHECK;
+
+	if (ruid) {
+		retcode = pam_authenticate(pamh, 0);
+		PAM_FAIL_CHECK;
+
+		if ((retcode = pam_get_item(pamh, PAM_USER, (const void **) &p)) == PAM_SUCCESS) {
+			user = p;
+		} else
+			syslog(LOG_ERR|LOG_AUTH, "Couldn't get PAM_USER: %s",
+			       pam_strerror(pamh, retcode));
+
+		retcode = pam_acct_mgmt(pamh, 0);
+		if (retcode == PAM_NEW_AUTHTOK_REQD)
+			retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
+		PAM_FAIL_CHECK;
+	}
+
+#endif /* USE_PAM */
+
 	/* get target login information, default to root */
 	if ((pwd = getpwnam(user)) == NULL) {
 		errx(1, "unknown login: %s", user);
@@ -230,6 +297,7 @@
 	}
 #endif
 
+#ifndef USE_PAM
 #ifdef WHEELSU
 	targetpass = strdup(pwd->pw_passwd);
 #endif /* WHEELSU */
@@ -280,11 +348,12 @@
 #ifdef WHEELSU
 			      || (iswheelsu && !strcmp(targetpass, crypt(p,targetpass)))
 #endif /* WHEELSU */
-			      )) {
-#else
+			      ))
+#else /* !SKEY */
 			p = getpass("Password:");
-			if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
-#endif
+			if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd)))
+#endif /* SKEY */
+				{
 #ifdef KERBEROS
 	    			if (!use_kerberos || (use_kerberos && kerberos(username, user, pwd->pw_uid, p)))
 #endif
@@ -308,10 +377,11 @@
 			exit(1);
 		}
 	}
+#endif /* USE_PAM */
 
 	if (asme) {
 		/* if asme and non-standard target shell, must be root */
-		if (!chshell(pwd->pw_shell) && ruid)
+		if (ruid && !chshell(pwd->pw_shell))
 			errx(1, "permission denied (shell).");
 	} else if (pwd->pw_shell && *pwd->pw_shell) {
 		shell = pwd->pw_shell;
@@ -334,9 +404,57 @@
 
 	(void)setpriority(PRIO_PROCESS, 0, prio);
 
+	/*
+	 * PAM modules might add supplementary groups in
+	 * pam_setcred(), so initialize them first.
+	 */
+#ifdef LOGIN_CAP
+	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
+		err(1, "setusercontext");
+#else
+	if (setgid(pwd->pw_gid) < 0)
+		err(1, "setgid");
+	if (initgroups(user, pwd->pw_gid))
+		errx(1, "initgroups failed");
+#endif
+
+#ifdef USE_PAM
+	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+	PAM_FAIL_CHECK;
+
+	/*
+	 * We must fork() before setuid() because we need to call
+	 * pam_setcred(pamh, PAM_DELETE_CRED) as root.
+	 */
+
+	statusp = 1;
+	switch ((child_pid = fork())) {
+	default:
+            while ((ret_pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) {
+		if (WIFSTOPPED(statusp)) {
+		    child_pgrp = tcgetpgrp(1);
+		    kill(getpid(), SIGSTOP);
+		    tcsetpgrp(1, child_pgrp);
+		    kill(child_pid, SIGCONT); 
+		    statusp = 1;
+		    continue;
+		}
+		break;
+            }
+	    if (ret_pid == -1)
+		err(1, "waitpid");
+	    PAM_END;
+	    exit(statusp);
+	case -1:
+	    err(1, "fork");
+	    PAM_END;
+	    exit (1);
+	case 0:
+#endif /* USE_PAM */
+
 #ifdef LOGIN_CAP
 	/* Set everything now except the environment & umask */
-	setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
+	setwhat = LOGIN_SETUSER|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
 	/*
 	 * Don't touch resource/priority settings if -m has been
 	 * used or -l and -c hasn't, and we're not su'ing to root.
@@ -346,11 +464,6 @@
 	if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0)
 		err(1, "setusercontext");
 #else
-	/* set permissions */
-	if (setgid(pwd->pw_gid) < 0)
-		err(1, "setgid");
-	if (initgroups(user, pwd->pw_gid))
-		errx(1, "initgroups failed");
 	if (setuid(pwd->pw_uid) < 0)
 		err(1, "setuid");
 #endif
@@ -361,10 +474,7 @@
 #ifdef KERBEROS
 			k = getenv("KRBTKFILE");
 #endif
-			if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
-				errx(1, "calloc");
-			cleanenv[0] = NULL;
-			environ = cleanenv;
+			environ = &cleanenv;
 #ifdef LOGIN_CAP
 			/* set the su'd user's environment & umask */
 			setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
@@ -385,6 +495,19 @@
 		(void)setenv("HOME", pwd->pw_dir, 1);
 		(void)setenv("SHELL", shell, 1);
 	}
+
+#ifdef LOGIN_CAP
+	login_close(lc);
+#endif /* LOGIN_CAP */
+
+#ifdef USE_PAM
+	env = (const char * const *)pam_getenvlist(pamh);
+	if (env != NULL) {
+		for (i=0; env[i]; i++)
+			putenv(env[i]);
+	}
+#endif /* USE_PAM */
+
 	if (iscsh == YES) {
 		if (fastlogin)
 			*np-- = "-f";
@@ -399,10 +522,11 @@
 		syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
 		    username, user, ontty());
 
-	login_close(lc);
-
 	execv(shell, np);
 	err(1, "%s", shell);
+#ifdef USE_PAM
+	}
+#endif /* USE_PAM */
 }
 
 static void


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?20010121045823.59DB212686>