Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 19 Jan 2001 13:13:59 -0500
From:      "David J. MacKenzie" <djm@web.us.uu.net>
To:        freebsd-security@FreeBSD.ORG
Cc:        djm@web.us.uu.net
Subject:   improved: PAM support for login, rshd, and su 
Message-ID:  <20010119181359.9A71212685@jenkins.web.us.uu.net>
In-Reply-To: Message from "David J. MacKenzie" <djm@web.us.uu.net>  of "Thu, 18 Jan 2001 10:09:22 EST." <20010118150923.111FB3E5B@catapult.web.us.uu.net> 

next in thread | previous in thread | raw e-mail | index | archive | help
In reviewing the patches I sent to this list recently,
I realized that I had neglected to add support for PAM 
"template users" to rshd and su.  I also noticed that rshd didn't
use auth_checknologin() when LOGIN_CAP was defined (not my bug,
but I have fixed it).  And I failed to add dependencies for
LIBPAM to the Makefiles.

Here is a revised version of my patches, which supercedes
the previous one.  The patches to login didn't change.

--- ./libexec/rshd/Makefile	2001/01/17 00:04:57	1.1
+++ ./libexec/rshd/Makefile	2001/01/19 17:33:26
@@ -8,9 +8,9 @@
 #CFLAGS+= -DCRYPT
 
 # For login_cap handling
-CFLAGS+=-DLOGIN_CAP -Wall
-DPADD+=	${LIBUTIL}
-LDADD+= -lutil
+CFLAGS+=-DLOGIN_CAP -DUSE_PAM -Wall
+DPADD+=	${LIBUTIL} ${LIBPAM}
+LDADD+= -lutil -lpam
 
 # IPv6 support
 CFLAGS+= -DINET6
--- ./libexec/rshd/rshd.c	2000/11/12 07:00:38	1.1
+++ ./libexec/rshd/rshd.c	2001/01/19 17:58:29
@@ -80,6 +80,12 @@
 #include <login_cap.h>
 #endif
 
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+static pam_handle_t *pamh;
+#endif /* USE_PAM */
+
 /* wrapper for KAME-special getnameinfo() */
 #ifndef NI_WITHSCOPEID
 #define	NI_WITHSCOPEID	0
@@ -219,6 +225,10 @@
 #ifdef	LOGIN_CAP
 	login_cap_t *lc;
 #endif
+#ifdef USE_PAM
+	static struct pam_conv conv = { misc_conv, NULL };
+	int retcode;
+#endif /* USE_PAM */
 
 	(void) signal(SIGINT, SIG_DFL);
 	(void) signal(SIGQUIT, SIG_DFL);
@@ -341,6 +351,43 @@
 
 	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, &cp)) == PAM_SUCCESS) {
+			strncpy(locuser, cp, sizeof(locuser));
+		} 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) {
+		retcode = pam_open_session(pamh,0);
+	}
+	if (retcode == PAM_SUCCESS) {
+		retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+		if (retcode != PAM_SUCCESS)
+			pam_close_session(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 +396,42 @@
 		    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 */
+
+#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 +455,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];
@@ -569,6 +623,10 @@
 			    (doencrypt && FD_ISSET(pv1[0], &readfrom)) ||
 #endif
 			    FD_ISSET(pv[0], &readfrom));
+#ifdef USE_PAM
+			pam_close_session(pamh, 0);
+			pam_end(pamh, PAM_SUCCESS);
+#endif /* USE_PAM */
 			exit(0);
 		}
 		setpgrp(0, getpid());
--- ./usr.bin/login/login.c	2000/08/08 03:12:59	1.1
+++ ./usr.bin/login/login.c	2001/01/18 03:24:07
@@ -81,6 +81,7 @@
 #ifndef NO_PAM
 #include <security/pam_appl.h>
 #include <security/pam_misc.h>
+#include <sys/wait.h>
 #endif
 
 #include "pathnames.h"
@@ -106,6 +107,7 @@
 
 #ifndef NO_PAM
 static int auth_pam __P((void));
+pam_handle_t *pamh = NULL;
 #endif
 static int auth_traditional __P((void));
 extern void login __P((struct utmp *));
@@ -150,6 +152,10 @@
 	char tname[sizeof(_PATH_TTY) + 10];
 	char *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);
@@ -548,6 +554,35 @@
 	if (!pflag)
 		environ = envinit;
 
+#ifndef NO_PAM
+	if (pamh) {
+		/*
+		 * We must fork() before setuid() because we need to call
+		 * pam_close_session() as root.
+		 */
+		pid = fork();
+		if (pid < 0) {
+			err(1, "fork");
+			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));
+			exit(0);
+		} else if (pid) {
+			/* parent - wait for child to finish, then cleanup session */
+			wait(NULL);
+			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));
+			exit(0);
+		} else {
+			if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
+				syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
+		}
+	}
+#endif /* NO_PAM */
+
 	/*
 	 * We don't need to be root anymore, so
 	 * set the user and session context
@@ -562,6 +597,17 @@
 		exit(1);
 	}
 
+#ifndef NO_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 /* NO_PAM */
+
 	(void)setenv("SHELL", pwd->pw_shell, 1);
 	(void)setenv("HOME", pwd->pw_dir, 1);
 	if (term != NULL && *term != '\0')
@@ -663,7 +709,6 @@
 static int
 auth_pam()
 {
-	pam_handle_t *pamh = NULL;
 	const char *tmpl_user;
 	const void *item;
 	int rval;
@@ -724,13 +769,36 @@
 		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;
+		} else if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
+			syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
+			rval = -1;
+		} else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+			syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
+			rval = -1;
+			pam_close_session(pamh, 0);
+		}
+	}
+
+	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;
 }
@@ -745,7 +813,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/19 17:41:13
@@ -4,9 +4,9 @@
 PROG=	su
 SRCS=	su.c
 
-COPTS+=	-DLOGIN_CAP -DSKEY
-DPADD=	${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT}
-LDADD=	-lutil -lskey -lmd -lcrypt
+COPTS+=	-DLOGIN_CAP -DSKEY -DUSE_PAM
+DPADD=	${LIBUTIL} ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBPAM}
+LDADD=	-lutil -lskey -lmd -lcrypt -lpam
 
 .if defined(WHEELSU)
 COPTS+=	-DWHEELSU
--- ./usr.bin/su/su.c	2000/02/24 21:06:21	1.1
+++ ./usr.bin/su/su.c	2001/01/19 17:48:55
@@ -65,6 +65,20 @@
 #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 { retcode = pam_close_session(pamh,0); \
+		    pam_end(pamh,retcode); }
+#endif /* USE_PAM */
+
 #ifdef	SKEY
 #include <skey.h>
 #endif
@@ -107,8 +121,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 +131,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 +239,28 @@
 		}
 	}
 
+#ifdef USE_PAM
+	retcode = pam_start("su", user, &conv, &pamh);
+	PAM_FAIL_CHECK;
+
+	if (ruid) {
+		retcode = pam_authenticate(pamh, 0);
+		PAM_FAIL_CHECK;
+
+		if ((retcode = pam_get_item(pamh, PAM_USER, &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 +277,7 @@
 	}
 #endif
 
+#ifndef USE_PAM
 #ifdef WHEELSU
 	targetpass = strdup(pwd->pw_passwd);
 #endif /* WHEELSU */
@@ -280,11 +328,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,6 +357,7 @@
 			exit(1);
 		}
 	}
+#endif /* USE_PAM */
 
 	if (asme) {
 		/* if asme and non-standard target shell, must be root */
@@ -334,6 +384,60 @@
 
 	(void)setpriority(PRIO_PROCESS, 0, prio);
 
+#ifdef USE_PAM
+	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;
+
+	retcode = pam_open_session(pamh, 0);
+	PAM_FAIL_CHECK;
+
+	retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+	PAM_FAIL_CHECK;
+
+	env = (const char * const *)pam_getenvlist(pamh);
+	if (env != NULL) {
+		for (i=0; env[i]; i++)
+			putenv(env[i]);
+	}
+
+	/*
+	 * We must fork() before setuid() because we need to call
+	 * pam_close_session() 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:
+	    pam_end(pamh, retcode);
+#endif /* USE_PAM */
+
 #ifdef LOGIN_CAP
 	/* Set everything now except the environment & umask */
 	setwhat = LOGIN_SETUSER|LOGIN_SETGROUP|LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
@@ -361,10 +465,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);
@@ -403,6 +504,9 @@
 
 	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?20010119181359.9A71212685>