Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 23 Jan 2001 13:46:11 -0500 (EST)
From:      "David J. MacKenzie" <djm@web.us.uu.net>
To:        freebsd-security@freebsd.org
Cc:        djm@web.us.uu.net
Subject:   PAM patches, iteration 4
Message-ID:  <20010123184611.E675046C7@dagger.web.us.uu.net>

next in thread | raw e-mail | index | archive | help
I've updated the docs to reflect PAM, cleaned up some error handling,
and included my patch to work around the pam_setcred() dispatch problem.
I also removed the non-logincap code path from su and rshd, since it's
already mandatory in login.  As before, this replaces my earlier patches.
I'd welcome having PAM experts examine them closely.  I think they're
ready for a wider audience.

--- ./contrib/libpam/doc/man/pam_setcred.3	1998/11/18 01:20:54	1.1
+++ ./contrib/libpam/doc/man/pam_setcred.3	2001/01/23 01:24:58
@@ -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
@@ -29,6 +29,23 @@
 .BR initgroups "(2) "
 (or equivalent) should have been performed.
 
+This function runs the
+.BI pam_sm_setcred()
+functions for all
+.BR auth
+modules defined for the current PAM service, even if a control flag
+of
+.BR sufficient
+or
+.BR requisite
+is present.  This is to ensure that a module whose
+.BI pam_sm_setcred()
+function returns
+.BR PAM_SUCCESS
+even if that module did not succeed in
+.BI pam_authenticate()
+does not prevent modules listed later from setting their credentials.
+It also appears to be the behavior of Solaris PAM.
 .SH "VALID FLAGS"
 .TP
 .BR PAM_ESTABLISH_CRED
--- ./contrib/libpam/libpam/pam_handlers.c	2001/01/22 20:19:52	1.1
+++ ./contrib/libpam/libpam/pam_handlers.c	2001/01/22 20:22:44
@@ -500,6 +500,8 @@
 #endif
     char *mod_full_path=NULL;
     servicefn func, func2;
+    int actions2buf[_PAM_RETURN_VALUES];
+    int *actions2 = actions;
     int success;
 
     D(("called."));
@@ -649,6 +651,19 @@
 	_sym = "_pam_sm_authenticate";
 	_sym2 = "_pam_sm_setcred";
 #endif
+	actions2 = actions2buf;
+	/* Always run the pam_sm_setcred for all listed auth modules.
+	   Otherwise, we can end up not running the pam_sm_setcred
+	   for auth module(s) that authenticated successfully,
+	   e.g. if an earlier auth module is "sufficient" and
+	   its authenticate fails but its setcred succeeds.
+	   This is also apparently what Solaris PAM does.  */
+	{
+                int i;
+                for (i = 0; i < _PAM_RETURN_VALUES; i++)
+                     actions2[i] = _PAM_ACTION_IGNORE;
+		actions2[PAM_SUCCESS] = _PAM_ACTION_OK;
+	}
 	break;
     case PAM_T_SESS:
 	handler_p = &the_handlers->open_session;
@@ -780,7 +795,7 @@
 
 	(*handler_p2)->must_fail = must_fail;        /* failure forced? */
 	(*handler_p2)->func = func2;
-	memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions));
+	memcpy((*handler_p2)->actions,actions2,sizeof((*handler_p2)->actions));
 	(*handler_p2)->argc = argc;
 	if (argv) {
 	    if (((*handler_p2)->argv = malloc(argvlen)) == NULL) {
--- ./libexec/rshd/Makefile	2001/01/17 00:04:57	1.1
+++ ./libexec/rshd/Makefile	2001/01/23 18:03:41
@@ -7,10 +7,15 @@
 
 #CFLAGS+= -DCRYPT
 
-# For login_cap handling
-CFLAGS+=-DLOGIN_CAP -Wall
+CFLAGS+= -Wall
 DPADD+=	${LIBUTIL}
 LDADD+= -lutil
+
+.if !defined(NOPAM)
+CFLAGS+= -DUSE_PAM
+DPADD+=	${LIBPAM}
+LDADD+=	${MINUSLPAM}
+.endif
 
 # IPv6 support
 CFLAGS+= -DINET6
--- ./libexec/rshd/rshd.8	2001/01/23 01:05:26	1.1
+++ ./libexec/rshd/rshd.8	2001/01/23 01:16:21
@@ -238,6 +238,16 @@
 .It Pa Ev $HOME /.rhosts
 .Sm on
 .It Pa /var/run/nologin
+.It Pa /etc/pam.conf
+if
+.Nm
+is configured with PAM support, it uses
+.Pa /etc/pam.conf
+entries with service name
+.Dq rsh .
+authentication modules requiring passwords (such as
+.Nm pam_unix )
+are not supported
 .El
 .Sh BUGS
 The authentication procedure used here assumes the integrity
--- ./libexec/rshd/rshd.c	2000/11/12 07:00:38	1.1
+++ ./libexec/rshd/rshd.c	2001/01/23 18:01:58
@@ -76,9 +76,21 @@
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
-#ifdef	LOGIN_CAP
 #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|LOG_AUTH, "pam_setcred: %s", pam_strerror(pamh, retcode)); \
+	if ((retcode = pam_close_session(pamh,0)) != PAM_SUCCESS) \
+		syslog(LOG_ERR|LOG_AUTH, "pam_close_session: %s", pam_strerror(pamh, retcode)); \
+	if ((retcode = pam_end(pamh, retcode)) != PAM_SUCCESS) \
+		syslog(LOG_ERR|LOG_AUTH, "pam_end: %s", pam_strerror(pamh, retcode)); \
+}
+#endif /* USE_PAM */
 
 /* wrapper for KAME-special getnameinfo() */
 #ifndef NI_WITHSCOPEID
@@ -188,6 +200,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");
+	return PAM_CONV_ERR;
+}
+#endif /* USE_PAM */
+
 char	username[20] = "USER=";
 char	homedir[64] = "HOME=";
 char	shell[64] = "SHELL=";
@@ -216,9 +242,12 @@
 	int rc;
 	int pv1[2], pv2[2];
 #endif
-#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);
@@ -229,7 +258,7 @@
 	    && af != AF_INET6
 #endif
 	    ) {
-		syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", af);
+		syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
 		exit(1);
 	}
 	err = getnameinfo((struct sockaddr *)fromp, fromp->su_len, numericname,
@@ -341,6 +370,56 @@
 
 	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|LOG_AUTH, "pam_start: %s", pam_strerror(pamh, retcode));
+		error("Login incorrect.\n");
+		exit(1);
+	}
+
+	retcode = pam_set_item (pamh, PAM_RUSER, remuser);
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_RUSER): %s", pam_strerror(pamh, retcode));
+		pam_end(pamh, retcode);
+		error("Login incorrect.\n");
+		exit(1);
+	}
+	retcode = pam_set_item (pamh, PAM_RHOST, fromhost);
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode));
+		pam_end(pamh, retcode);
+		error("Login incorrect.\n");
+		exit(1);
+	}
+	retcode = pam_set_item (pamh, PAM_TTY, "tty");
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR|LOG_AUTH, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode));
+		pam_end(pamh, retcode);
+		error("Login incorrect.\n");
+		exit(1);
+	}
+
+	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, "pam_get_item(PAM_USER): %s",
+			       pam_strerror(pamh, retcode));
+		retcode = pam_acct_mgmt(pamh, 0);
+	}
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'",
+		       remuser, fromhost, locuser, pam_strerror(pamh, retcode), cmdbuf);
+		pam_end(pamh, retcode);
+		error("Login incorrect.\n");
+		exit(1);
+	}
+#endif /* USE_PAM */
+
 	setpwent();
 	pwd = getpwnam(locuser);
 	if (pwd == NULL) {
@@ -349,13 +428,36 @@
 		    remuser, fromhost, locuser, cmdbuf);
 		if (errorstr == NULL)
 			errorstr = "Login incorrect.\n";
-		goto fail;
+		error(errorstr, fromhost);
+		exit(1);
+	}
+
+#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);
 	}
-#ifdef	LOGIN_CAP
+#endif /* USE_PAM */
+
 	lc = login_getpwclass(pwd);
-#endif
+	if (pwd->pw_uid)
+		auth_checknologin(lc);
+
 	if (chdir(pwd->pw_dir) < 0) {
-#ifdef	LOGIN_CAP
 		if (chdir("/") < 0 ||
 		    login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) {
 			syslog(LOG_INFO|LOG_AUTH,
@@ -364,44 +466,9 @@
 			error("No remote home directory.\n");
 			exit(0);
 		}
-#else
-		(void) chdir("/");
-#ifdef notdef
-		syslog(LOG_INFO|LOG_AUTH,
-		    "%s@%s as %s: no home directory. cmd='%.80s'",
-		    remuser, fromhost, locuser, cmdbuf);
-		error("No remote directory.\n");
-		exit(1);
-#endif
-#endif
 		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 +488,30 @@
 			exit(1);
 		}
 	}
-#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.
+	 */
+	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
+                syslog(LOG_ERR, "setusercontext: %m");
+		exit(1);
+	}
+
+#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 +535,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 +656,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 +676,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 +705,20 @@
 		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.1	2001/01/23 01:04:52	1.1
+++ ./usr.bin/login/login.1	2001/01/23 01:16:45
@@ -177,6 +177,13 @@
 makes login quieter
 .It Pa /etc/auth.conf
 configure authentication services
+.It Pa /etc/pam.conf
+if
+.Nm
+is configured with PAM support, it uses
+.Pa /etc/pam.conf
+entries with service name
+.Dq login
 .El
 .Sh SEE ALSO
 .Xr builtin 1 ,
--- ./usr.bin/login/login.c	2000/08/08 03:12:59	1.1
+++ ./usr.bin/login/login.c	2001/01/23 00:53:51
@@ -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;
@@ -677,12 +738,14 @@
 	if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) {
 		syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s",
 		    pam_strerror(pamh, e));
+		pam_end(pamh, e);
 		return -1;
 	}
 	if (hostname != NULL &&
 	    (e = pam_set_item(pamh, PAM_RHOST, full_hostname)) != PAM_SUCCESS) {
 		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
 		    pam_strerror(pamh, e));
+		pam_end(pamh, e);
 		return -1;
 	}
 	e = pam_authenticate(pamh, 0);
@@ -712,8 +775,8 @@
 			if (strcmp(username, tmpl_user) != 0)
 				pwd = getpwnam(tmpl_user);
 		} else
-			syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
-			    pam_strerror(pamh, e));
+			syslog(LOG_ERR, "pam_get_item(PAM_USER): %s",
+			       pam_strerror(pamh, e));
 		rval = 0;
 		break;
 
@@ -724,17 +787,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 +824,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/23 18:07:48
@@ -4,9 +4,18 @@
 PROG=	su
 SRCS=	su.c
 
-COPTS+=	-DLOGIN_CAP -DSKEY
-DPADD=	${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
-LDADD=	-lutil -lskey -lmd -lcrypt
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.if !defined(NOPAM)
+CFLAGS+= -DUSE_PAM
+DPADD+= ${LIBPAM}
+LDADD+= ${MINUSLPAM}
+.else
+COPTS+= -DSKEY
+DPADD+= ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
+LDADD+= -lskey -lmd -lcrypt
+.endif
 
 .if defined(WHEELSU)
 COPTS+=	-DWHEELSU
--- ./usr.bin/su/su.1	2001/01/23 01:02:52	1.1
+++ ./usr.bin/su/su.1	2001/01/23 01:16:33
@@ -173,6 +173,13 @@
 .Bl -tag -width /etc/auth.conf -compact
 .It Pa /etc/auth.conf
 configure authentication services
+.It Pa /etc/pam.conf
+if
+.Nm
+is configured with PAM support, it uses
+.Pa /etc/pam.conf
+entries with service name
+.Dq su
 .El
 .Sh SEE ALSO
 .Xr csh 1 ,
--- ./usr.bin/su/su.c	2000/02/24 21:06:21	1.1
+++ ./usr.bin/su/su.c	2001/01/23 18:05:14
@@ -60,36 +60,40 @@
 #include <syslog.h>
 #include <unistd.h>
 #include <libutil.h>
-
-#ifdef LOGIN_CAP
 #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_END { \
+	if ((retcode = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) { \
+		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode)); \
+	} \
+	if ((retcode = pam_end(pamh,retcode)) != PAM_SUCCESS) { \
+		syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, retcode)); \
+	} \
+}
+#else /* !USE_PAM */
 #ifdef	SKEY
 #include <skey.h>
 #endif
+#endif /* USE_PAM */
 
 #ifdef KERBEROS
 #include <openssl/des.h>
 #include <krb.h>
 #include <netdb.h>
 
-#ifdef LOGIN_CAP
 #define	ARGSTR	"-Kflmc:"
-#else
-#define	ARGSTR	"-Kflm"
-#endif
 
 static int kerberos(char *username, char *user, int uid, char *pword);
 static int koktologin(char *name, char *toname);
 
 int use_kerberos = 1;
 #else /* !KERBEROS */
-#ifdef LOGIN_CAP
 #define	ARGSTR	"-flmc:"
-#else
-#define	ARGSTR	"-flm"
-#endif
 #endif /* KERBEROS */
 
 char   *ontty __P((void));
@@ -107,17 +111,26 @@
 	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;
 	enum { UNSET, YES, NO } iscsh = UNSET;
-#ifdef LOGIN_CAP
 	login_cap_t *lc;
 	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
@@ -147,11 +160,9 @@
 			asme = 1;
 			asthem = 0;
 			break;
-#ifdef LOGIN_CAP
 		case 'c':
 			class = optarg;
 			break;
-#endif
 		case '?':
 		default:
 			usage();
@@ -161,8 +172,7 @@
 		user = argv[optind++];
 
 	if (strlen(user) > MAXLOGNAME - 1) {
-		(void)fprintf(stderr, "su: username too long.\n");
-		exit(1);
+		errx(1, "username too long");
 	}
 		
 	if (user == NULL)
@@ -189,7 +199,7 @@
 	if (errno)
 		prio = 0;
 	(void)setpriority(PRIO_PROCESS, 0, -2);
-	openlog("su", LOG_CONS, 0);
+	openlog("su", LOG_CONS, LOG_AUTH);
 
 	/* get current login name and shell */
 	ruid = getuid();
@@ -214,11 +224,67 @@
 		}
 	}
 
+#ifdef USE_PAM
+	retcode = pam_start("su", user, &conv, &pamh);
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode));
+		errx(1, "pam_start: %s", pam_strerror(pamh, retcode));
+	}
+
+	gethostname(myhost, sizeof(myhost));
+	retcode = pam_set_item(pamh, PAM_RHOST, myhost);
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode));
+		pam_end(pamh, retcode);
+		errx(1, "pam_set_item(PAM_RHOST): %s", pam_strerror(pamh, retcode));
+	}
+
+	mytty = ttyname(STDERR_FILENO);
+	if (!mytty)
+		mytty = "tty";
+	retcode = pam_set_item(pamh, PAM_TTY, mytty);
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode));
+		pam_end(pamh, retcode);
+		errx(1, "pam_set_item(PAM_TTY): %s", pam_strerror(pamh, retcode));
+	}
+
+	if (ruid) {
+		retcode = pam_authenticate(pamh, 0);
+		if (retcode != PAM_SUCCESS) {
+			syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, retcode));
+			pam_end(pamh, retcode);
+			errx(1, "Sorry");
+		}
+
+		if ((retcode = pam_get_item(pamh, PAM_USER, (const void **) &p)) == PAM_SUCCESS) {
+			user = p;
+		} else
+			syslog(LOG_ERR, "pam_get_item(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);
+			if (retcode != PAM_SUCCESS) {
+				syslog(LOG_ERR, "pam_chauthtok: %s", pam_strerror(pamh, retcode));
+				pam_end(pamh, retcode);
+				errx(1, "Sorry");
+			}
+		}
+		if (retcode != PAM_SUCCESS) {
+			syslog(LOG_ERR, "pam_acct_mgmt: %s", pam_strerror(pamh, retcode));
+			pam_end(pamh, retcode);
+			errx(1, "Sorry");
+		}
+	}
+
+#endif /* USE_PAM */
+
 	/* get target login information, default to root */
 	if ((pwd = getpwnam(user)) == NULL) {
 		errx(1, "unknown login: %s", user);
 	}
-#ifdef LOGIN_CAP
 	if (class==NULL) {
 		lc = login_getpwclass(pwd);
 	} else {
@@ -228,8 +294,8 @@
 		if (lc == NULL)
 			errx(1, "unknown class: %s", class);
 	}
-#endif
 
+#ifndef USE_PAM
 #ifdef WHEELSU
 	targetpass = strdup(pwd->pw_passwd);
 #endif /* WHEELSU */
@@ -280,18 +346,18 @@
 #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
-					{
-					fprintf(stderr, "Sorry\n");
+				{
 					syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty());
-					exit(1);
+					errx(1, "Sorry");
 				}
 			}
 #ifdef WHEELSU
@@ -301,17 +367,17 @@
 #endif /* WHEELSU */
 		}
 		if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) {
-			fprintf(stderr, "Sorry - account expired\n");
 			syslog(LOG_AUTH|LOG_WARNING,
 				"BAD SU %s to %s%s", username,
 				user, ontty());
-			exit(1);
+			errx(1, "Sorry - account expired");
 		}
 	}
+#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 +400,51 @@
 
 	(void)setpriority(PRIO_PROCESS, 0, prio);
 
-#ifdef LOGIN_CAP
+	/*
+	 * PAM modules might add supplementary groups in
+	 * pam_setcred(), so initialize them first.
+	 */
+	if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0)
+		err(1, "setusercontext");
+
+#ifdef USE_PAM
+	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+	if (retcode != PAM_SUCCESS) {
+		syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, retcode));
+	}
+
+	/*
+	 * 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 */
+
 	/* 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.
@@ -345,15 +453,6 @@
 		setwhat &= ~(LOGIN_SETPRIORITY|LOGIN_SETRESOURCES);
 	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
 
 	if (!asme) {
 		if (asthem) {
@@ -361,16 +460,9 @@
 #ifdef KERBEROS
 			k = getenv("KRBTKFILE");
 #endif
-			if ((cleanenv = calloc(20, sizeof(char*))) == NULL)
-				errx(1, "calloc");
-			cleanenv[0] = NULL;
-			environ = cleanenv;
-#ifdef LOGIN_CAP
+			environ = &cleanenv;
 			/* set the su'd user's environment & umask */
 			setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV);
-#else
-			(void)setenv("PATH", _PATH_DEFPATH, 1);
-#endif
 			if (p)
 				(void)setenv("TERM", p, 1);
 #ifdef KERBEROS
@@ -385,6 +477,17 @@
 		(void)setenv("HOME", pwd->pw_dir, 1);
 		(void)setenv("SHELL", shell, 1);
 	}
+
+	login_close(lc);
+
+#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";
@@ -396,20 +499,20 @@
 	*np = asthem ? "-su" : iscsh == YES ? "_su" : "su";
 
 	if (ruid != 0)
-		syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s",
+		syslog(LOG_NOTICE, "%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
 usage()
 {
-	(void)fprintf(stderr, "usage: su [%s] [login [args]]\n", ARGSTR);
-	exit(1);
+	errx(1, "usage: su [%s] [login [args]]", ARGSTR);
 }
 
 int
@@ -493,7 +596,7 @@
 			return (1);
 		}
 		warnx("kerberos: unable to su: %s", krb_err_txt[kerno]);
-		syslog(LOG_NOTICE|LOG_AUTH,
+		syslog(LOG_NOTICE,
 		    "BAD Kerberos SU: %s to %s%s: %s",
 		    username, user, ontty(), krb_err_txt[kerno]);
 		return (1);
@@ -520,13 +623,13 @@
 
 	if (kerno == KDC_PR_UNKNOWN) {
 		warnx("Warning: TGT not verified.");
-		syslog(LOG_NOTICE|LOG_AUTH,
+		syslog(LOG_NOTICE,
 		    "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
 		    username, user, ontty(), krb_err_txt[kerno],
 		    "rcmd", savehost);
 	} else if (kerno != KSUCCESS) {
 		warnx("Unable to use TGT: %s", krb_err_txt[kerno]);
-		syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s",
+		syslog(LOG_NOTICE, "failed su: %s to %s%s: %s",
 		    username, user, ontty(), krb_err_txt[kerno]);
 		dest_tkt();
 		return (1);
@@ -540,9 +643,9 @@
 
 		if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr,
 		    &authdata, "")) != KSUCCESS) {
-			warnx("kerberos: unable to verify rcmd ticket: %s\n",
+			warnx("kerberos: unable to verify rcmd ticket: %s",
 			    krb_err_txt[kerno]);
-			syslog(LOG_NOTICE|LOG_AUTH,
+			syslog(LOG_NOTICE,
 			    "failed su: %s to %s%s: %s", username,
 			     user, ontty(), krb_err_txt[kerno]);
 			dest_tkt();


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?20010123184611.E675046C7>