summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpam/Makefile.am2
-rw-r--r--libpam/include/security/pam_modutil.h13
-rw-r--r--libpam/libpam.map5
-rw-r--r--libpam/pam_modutil_sanitize.c175
-rw-r--r--modules/pam_exec/pam_exec.c34
-rw-r--r--modules/pam_mkhomedir/pam_mkhomedir.c15
-rw-r--r--modules/pam_unix/pam_unix_acct.c23
-rw-r--r--modules/pam_unix/pam_unix_passwd.c20
-rw-r--r--modules/pam_unix/support.c20
-rw-r--r--modules/pam_unix/support.h2
-rw-r--r--modules/pam_xauth/pam_xauth.c26
11 files changed, 251 insertions, 84 deletions
diff --git a/libpam/Makefile.am b/libpam/Makefile.am
index 417ca779..685a797d 100644
--- a/libpam/Makefile.am
+++ b/libpam/Makefile.am
@@ -43,4 +43,4 @@ libpam_la_SOURCES = pam_account.c pam_auth.c pam_data.c pam_delay.c \
pam_modutil_cleanup.c pam_modutil_getpwnam.c pam_modutil_ioloop.c \
pam_modutil_getgrgid.c pam_modutil_getpwuid.c pam_modutil_getgrnam.c \
pam_modutil_getspnam.c pam_modutil_getlogin.c pam_modutil_ingroup.c \
- pam_modutil_priv.c
+ pam_modutil_priv.c pam_modutil_sanitize.c
diff --git a/libpam/include/security/pam_modutil.h b/libpam/include/security/pam_modutil.h
index 8087ba15..4ce8c552 100644
--- a/libpam/include/security/pam_modutil.h
+++ b/libpam/include/security/pam_modutil.h
@@ -129,6 +129,19 @@ extern int PAM_NONNULL((1,2))
pam_modutil_regain_priv(pam_handle_t *pamh,
struct pam_modutil_privs *p);
+enum pam_modutil_redirect_fd {
+ PAM_MODUTIL_IGNORE_FD, /* do not redirect */
+ PAM_MODUTIL_PIPE_FD, /* redirect to a pipe */
+ PAM_MODUTIL_NULL_FD, /* redirect to /dev/null */
+};
+
+/* redirect standard descriptors, close all other descriptors. */
+extern int PAM_NONNULL((1))
+pam_modutil_sanitize_helper_fds(pam_handle_t *pamh,
+ enum pam_modutil_redirect_fd redirect_stdin,
+ enum pam_modutil_redirect_fd redirect_stdout,
+ enum pam_modutil_redirect_fd redirect_stderr);
+
#ifdef __cplusplus
}
#endif
diff --git a/libpam/libpam.map b/libpam/libpam.map
index b0885d65..d6835b47 100644
--- a/libpam/libpam.map
+++ b/libpam/libpam.map
@@ -67,3 +67,8 @@ LIBPAM_MODUTIL_1.1.3 {
pam_modutil_drop_priv;
pam_modutil_regain_priv;
} LIBPAM_MODUTIL_1.1;
+
+LIBPAM_MODUTIL_1.1.9 {
+ global:
+ pam_modutil_sanitize_helper_fds;
+} LIBPAM_MODUTIL_1.1.3;
diff --git a/libpam/pam_modutil_sanitize.c b/libpam/pam_modutil_sanitize.c
new file mode 100644
index 00000000..65f85d01
--- /dev/null
+++ b/libpam/pam_modutil_sanitize.c
@@ -0,0 +1,175 @@
+/*
+ * This file implements the following functions:
+ * pam_modutil_sanitize_helper_fds:
+ * redirects standard descriptors, closes all other descriptors.
+ */
+
+#include "pam_modutil_private.h"
+#include <security/pam_ext.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/resource.h>
+
+/*
+ * Creates a pipe, closes its write end, redirects fd to its read end.
+ * Returns fd on success, -1 otherwise.
+ */
+static int
+redirect_in_pipe(pam_handle_t *pamh, int fd, const char *name)
+{
+ int in[2];
+
+ if (pipe(in) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
+ return -1;
+ }
+
+ close(in[1]);
+
+ if (in[0] == fd)
+ return fd;
+
+ if (dup2(in[0], fd) != fd) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name);
+ fd = -1;
+ }
+
+ close(in[0]);
+ return fd;
+}
+
+/*
+ * Creates a pipe, closes its read end, redirects fd to its write end.
+ * Returns fd on success, -1 otherwise.
+ */
+static int
+redirect_out_pipe(pam_handle_t *pamh, int fd, const char *name)
+{
+ int out[2];
+
+ if (pipe(out) < 0) {
+ pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
+ return -1;
+ }
+
+ close(out[0]);
+
+ if (out[1] == fd)
+ return fd;
+
+ if (dup2(out[1], fd) != fd) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name);
+ fd = -1;
+ }
+
+ close(out[1]);
+ return fd;
+}
+
+/*
+ * Opens /dev/null for writing, redirects fd there.
+ * Returns fd on success, -1 otherwise.
+ */
+static int
+redirect_out_null(pam_handle_t *pamh, int fd, const char *name)
+{
+ int null = open("/dev/null", O_WRONLY);
+
+ if (null < 0) {
+ pam_syslog(pamh, LOG_ERR, "open of %s failed: %m", "/dev/null");
+ return -1;
+ }
+
+ if (null == fd)
+ return fd;
+
+ if (dup2(null, fd) != fd) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name);
+ fd = -1;
+ }
+
+ close(null);
+ return fd;
+}
+
+static int
+redirect_out(pam_handle_t *pamh, enum pam_modutil_redirect_fd mode,
+ int fd, const char *name)
+{
+ switch (mode) {
+ case PAM_MODUTIL_PIPE_FD:
+ if (redirect_out_pipe(pamh, fd, name) < 0)
+ return -1;
+ break;
+ case PAM_MODUTIL_NULL_FD:
+ if (redirect_out_null(pamh, fd, name) < 0)
+ return -1;
+ break;
+ case PAM_MODUTIL_IGNORE_FD:
+ break;
+ }
+ return fd;
+}
+
+/* Closes all descriptors after stderr. */
+static void
+close_fds(void)
+{
+ /*
+ * An arbitrary upper limit for the maximum file descriptor number
+ * returned by RLIMIT_NOFILE.
+ */
+ const int MAX_FD_NO = 65535;
+
+ /* The lower limit is the same as for _POSIX_OPEN_MAX. */
+ const int MIN_FD_NO = 20;
+
+ int fd;
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) || rlim.rlim_max > MAX_FD_NO)
+ fd = MAX_FD_NO;
+ else if (rlim.rlim_max < MIN_FD_NO)
+ fd = MIN_FD_NO;
+ else
+ fd = rlim.rlim_max - 1;
+
+ for (; fd > STDERR_FILENO; --fd)
+ close(fd);
+}
+
+int
+pam_modutil_sanitize_helper_fds(pam_handle_t *pamh,
+ enum pam_modutil_redirect_fd stdin_mode,
+ enum pam_modutil_redirect_fd stdout_mode,
+ enum pam_modutil_redirect_fd stderr_mode)
+{
+ if (stdin_mode != PAM_MODUTIL_IGNORE_FD &&
+ redirect_in_pipe(pamh, STDIN_FILENO, "stdin") < 0) {
+ return -1;
+ }
+
+ if (redirect_out(pamh, stdout_mode, STDOUT_FILENO, "stdout") < 0)
+ return -1;
+
+ /*
+ * If stderr should not be ignored and
+ * redirect mode for stdout and stderr are the same,
+ * optimize by redirecting stderr to stdout.
+ */
+ if (stderr_mode != PAM_MODUTIL_IGNORE_FD &&
+ stdout_mode == stderr_mode) {
+ if (dup2(STDOUT_FILENO, STDERR_FILENO) != STDERR_FILENO) {
+ pam_syslog(pamh, LOG_ERR,
+ "dup2 of %s failed: %m", "stderr");
+ return -1;
+ }
+ } else {
+ if (redirect_out(pamh, stderr_mode, STDERR_FILENO, "stderr") < 0)
+ return -1;
+ }
+
+ close_fds();
+ return 0;
+}
diff --git a/modules/pam_exec/pam_exec.c b/modules/pam_exec/pam_exec.c
index b56e4b26..12c44444 100644
--- a/modules/pam_exec/pam_exec.c
+++ b/modules/pam_exec/pam_exec.c
@@ -302,6 +302,10 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
char **envlist, **tmp;
int envlen, nitems;
char *envstr;
+ enum pam_modutil_redirect_fd redirect_stdin =
+ expose_authtok ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_PIPE_FD;
+ enum pam_modutil_redirect_fd redirect_stdout =
+ (use_stdout || logfile) ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_NULL_FD;
/* First, move all the pipes off of stdin, stdout, and stderr, to ensure
* that calls to dup2 won't close them. */
@@ -330,18 +334,6 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
_exit (err);
}
}
- else
- {
- close (STDIN_FILENO);
-
- /* New stdin. */
- if ((i = open ("/dev/null", O_RDWR)) < 0)
- {
- int err = errno;
- pam_syslog (pamh, LOG_ERR, "open of /dev/null failed: %m");
- _exit (err);
- }
- }
/* Set up stdout. */
@@ -374,26 +366,18 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
free (buffer);
}
}
- else
- {
- close (STDOUT_FILENO);
- if ((i = open ("/dev/null", O_RDWR)) < 0)
- {
- int err = errno;
- pam_syslog (pamh, LOG_ERR, "open of /dev/null failed: %m");
- _exit (err);
- }
- }
- if (dup2 (STDOUT_FILENO, STDERR_FILENO) == -1)
+ if ((use_stdout || logfile) &&
+ dup2 (STDOUT_FILENO, STDERR_FILENO) == -1)
{
int err = errno;
pam_syslog (pamh, LOG_ERR, "dup2 failed: %m");
_exit (err);
}
- for (i = 3; i < sysconf (_SC_OPEN_MAX); i++)
- close (i);
+ if (pam_modutil_sanitize_helper_fds(pamh, redirect_stdin,
+ redirect_stdout, redirect_stdout) < 0)
+ _exit(1);
if (call_setuid)
if (setuid (geteuid ()) == -1)
diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c
index a867a738..c9220897 100644
--- a/modules/pam_mkhomedir/pam_mkhomedir.c
+++ b/modules/pam_mkhomedir/pam_mkhomedir.c
@@ -58,8 +58,6 @@
#include <security/pam_modutil.h>
#include <security/pam_ext.h>
-#define MAX_FD_NO 10000
-
/* argument parsing */
#define MKHOMEDIR_DEBUG 020 /* be verbose about things */
#define MKHOMEDIR_QUIET 040 /* keep quiet about things */
@@ -131,18 +129,13 @@ create_homedir (pam_handle_t *pamh, options_t *opt,
/* fork */
child = fork();
if (child == 0) {
- int i;
- struct rlimit rlim;
static char *envp[] = { NULL };
const char *args[] = { NULL, NULL, NULL, NULL, NULL };
- if (getrlimit(RLIMIT_NOFILE, &rlim)==0) {
- if (rlim.rlim_max >= MAX_FD_NO)
- rlim.rlim_max = MAX_FD_NO;
- for (i=0; i < (int)rlim.rlim_max; i++) {
- close(i);
- }
- }
+ if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
+ PAM_MODUTIL_PIPE_FD,
+ PAM_MODUTIL_PIPE_FD) < 0)
+ _exit(PAM_SYSTEM_ERR);
/* exec the mkhomedir helper */
args[0] = MKHOMEDIR_HELPER;
diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c
index dc505e73..27998451 100644
--- a/modules/pam_unix/pam_unix_acct.c
+++ b/modules/pam_unix/pam_unix_acct.c
@@ -98,24 +98,21 @@ int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
/* fork */
child = fork();
if (child == 0) {
- int i=0;
- struct rlimit rlim;
static char *envp[] = { NULL };
const char *args[] = { NULL, NULL, NULL, NULL };
- /* reopen stdout as pipe */
- dup2(fds[1], STDOUT_FILENO);
-
/* XXX - should really tidy up PAM here too */
- if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
- if (rlim.rlim_max >= MAX_FD_NO)
- rlim.rlim_max = MAX_FD_NO;
- for (i=0; i < (int)rlim.rlim_max; i++) {
- if (i != STDOUT_FILENO) {
- close(i);
- }
- }
+ /* reopen stdout as pipe */
+ if (dup2(fds[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdout");
+ _exit(PAM_AUTHINFO_UNAVAIL);
+ }
+
+ if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD,
+ PAM_MODUTIL_IGNORE_FD,
+ PAM_MODUTIL_PIPE_FD) < 0) {
+ _exit(PAM_AUTHINFO_UNAVAIL);
}
if (geteuid() == 0) {
diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c
index 5f3a3db3..606071ea 100644
--- a/modules/pam_unix/pam_unix_passwd.c
+++ b/modules/pam_unix/pam_unix_passwd.c
@@ -201,8 +201,6 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const
/* fork */
child = fork();
if (child == 0) {
- int i=0;
- struct rlimit rlim;
static char *envp[] = { NULL };
const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
char buffer[16];
@@ -210,15 +208,15 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const
/* XXX - should really tidy up PAM here too */
/* reopen stdin as pipe */
- dup2(fds[0], STDIN_FILENO);
-
- if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
- if (rlim.rlim_max >= MAX_FD_NO)
- rlim.rlim_max = MAX_FD_NO;
- for (i=0; i < (int)rlim.rlim_max; i++) {
- if (i != STDIN_FILENO)
- close(i);
- }
+ if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
+ _exit(PAM_AUTHINFO_UNAVAIL);
+ }
+
+ if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
+ PAM_MODUTIL_PIPE_FD,
+ PAM_MODUTIL_PIPE_FD) < 0) {
+ _exit(PAM_AUTHINFO_UNAVAIL);
}
/* exec binary helper */
diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c
index 3a849c81..fdb45c20 100644
--- a/modules/pam_unix/support.c
+++ b/modules/pam_unix/support.c
@@ -564,23 +564,21 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
/* fork */
child = fork();
if (child == 0) {
- int i=0;
- struct rlimit rlim;
static char *envp[] = { NULL };
const char *args[] = { NULL, NULL, NULL, NULL };
/* XXX - should really tidy up PAM here too */
/* reopen stdin as pipe */
- dup2(fds[0], STDIN_FILENO);
-
- if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
- if (rlim.rlim_max >= MAX_FD_NO)
- rlim.rlim_max = MAX_FD_NO;
- for (i=0; i < (int)rlim.rlim_max; i++) {
- if (i != STDIN_FILENO)
- close(i);
- }
+ if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) {
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
+ _exit(PAM_AUTHINFO_UNAVAIL);
+ }
+
+ if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
+ PAM_MODUTIL_PIPE_FD,
+ PAM_MODUTIL_PIPE_FD) < 0) {
+ _exit(PAM_AUTHINFO_UNAVAIL);
}
if (geteuid() == 0) {
diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h
index 6f5b2eb6..cd6ddb76 100644
--- a/modules/pam_unix/support.h
+++ b/modules/pam_unix/support.h
@@ -141,8 +141,6 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
-#define MAX_FD_NO 2000000
-
/* use this to free strings. ESPECIALLY password strings */
#define _pam_delete(xx) \
diff --git a/modules/pam_xauth/pam_xauth.c b/modules/pam_xauth/pam_xauth.c
index c7ce55ab..2be43513 100644
--- a/modules/pam_xauth/pam_xauth.c
+++ b/modules/pam_xauth/pam_xauth.c
@@ -128,7 +128,6 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output,
/* We're the child. */
size_t j;
const char *args[10];
- int maxopened;
/* Drop privileges. */
if (setgid(gid) == -1)
{
@@ -150,19 +149,26 @@ run_coprocess(pam_handle_t *pamh, const char *input, char **output,
(unsigned long) geteuid ());
_exit (err);
}
- /* Initialize the argument list. */
- memset(args, 0, sizeof(args));
/* Set the pipe descriptors up as stdin and stdout, and close
* everything else, including the original values for the
* descriptors. */
- dup2(ipipe[0], STDIN_FILENO);
- dup2(opipe[1], STDOUT_FILENO);
- maxopened = (int)sysconf(_SC_OPEN_MAX);
- for (i = 0; i < maxopened; i++) {
- if ((i != STDIN_FILENO) && (i != STDOUT_FILENO)) {
- close(i);
- }
+ if (dup2(ipipe[0], STDIN_FILENO) != STDIN_FILENO) {
+ int err = errno;
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
+ _exit(err);
}
+ if (dup2(opipe[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ int err = errno;
+ pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdout");
+ _exit(err);
+ }
+ if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
+ PAM_MODUTIL_IGNORE_FD,
+ PAM_MODUTIL_NULL_FD) < 0) {
+ _exit(1);
+ }
+ /* Initialize the argument list. */
+ memset(args, 0, sizeof(args));
/* Convert the varargs list into a regular array of strings. */
va_start(ap, command);
args[0] = command;