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