Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 20 Jan 2001 13:03:23 -0500 (EST)
From:      Robert Watson <rwatson@FreeBSD.ORG>
To:        "David J. MacKenzie" <djm@web.us.uu.net>
Cc:        freebsd-security@FreeBSD.ORG
Subject:   Re: improved: PAM support for login, rshd, and su 
Message-ID:  <Pine.NEB.3.96L.1010120130122.5539A-100000@fledge.watson.org>
In-Reply-To: <20010119181359.9A71212685@jenkins.web.us.uu.net>

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

So, at one point recently I sent e-mail to freebsd-arch proposing that we
eliminate the #ifdef for LOGIN_CAP.  I asked if anyone was actually not
using the login.conf stuff in their configuration but haven't found any
examples yet.  Having two code paths for all sensitive authorization and
authentication code really makes a mess of things, and also means that
login.conf can't be used as a comprehensive source of policy.  In the
TrustedBSD MAC implementation, I currently maintain MAC labeling
information in login.conf per-user-class, mandating using of LOGIN_CAP,
and also making the use of PAM to manage security contexts very desirable. 
Would anyone object to removing the LOGIN_CAP ifdef?  Are there any
negative implications to this that I am not aware of?

Robert N M Watson             FreeBSD Core Team, TrustedBSD Project
robert@fledge.watson.org      NAI Labs, Safeport Network Services

On Fri, 19 Jan 2001, David J. MacKenzie wrote:

> 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
> 



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?Pine.NEB.3.96L.1010120130122.5539A-100000>