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>