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>
index | next in thread | previous in thread | raw e-mail
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
help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010119181359.9A71212685>
