summaryrefslogtreecommitdiff
path: root/modules/pam_exec
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_exec')
-rw-r--r--modules/pam_exec/pam_exec.8.xml34
-rw-r--r--modules/pam_exec/pam_exec.c108
2 files changed, 127 insertions, 15 deletions
diff --git a/modules/pam_exec/pam_exec.8.xml b/modules/pam_exec/pam_exec.8.xml
index 4dc2a19d..23793668 100644
--- a/modules/pam_exec/pam_exec.8.xml
+++ b/modules/pam_exec/pam_exec.8.xml
@@ -31,8 +31,14 @@
quiet
</arg>
<arg choice="opt">
+ stdout
+ </arg>
+ <arg choice="opt">
log=<replaceable>file</replaceable>
</arg>
+ <arg choice="opt">
+ type=<replaceable>type</replaceable>
+ </arg>
<arg choice="plain">
<replaceable>command</replaceable>
</arg>
@@ -119,6 +125,28 @@
<varlistentry>
<term>
+ <option>type=<replaceable>type</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Only run the command if the module type matches the given type.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>stdout</option>
+ </term>
+ <listitem>
+ <para>
+ Per default the output of the executed command is written to <filename>/dev/null</filename>. With this option, the stdout output of the executed command is redirected to the calling application. It's in the responsibility of this application what happens with the output. The <option>log</option> option is ignored.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
<option>quiet</option>
</term>
<listitem>
@@ -194,7 +222,8 @@
<listitem>
<para>
<function>pam_setcred</function> was called, which
- does not execute the command.
+ does not execute the command. Or, the value given for the type=
+ parameter did not match the module type.
</para>
</listitem>
</varlistentry>
@@ -236,7 +265,8 @@
<refsect1 id='pam_exec-author'>
<title>AUTHOR</title>
<para>
- pam_exec was written by Thorsten Kukuk &lt;kukuk@thkukuk.de&gt;.
+ pam_exec was written by Thorsten Kukuk &lt;kukuk@thkukuk.de&gt; and
+ Josh Triplett &lt;josh@joshtriplett.org&gt;.
</para>
</refsect1>
diff --git a/modules/pam_exec/pam_exec.c b/modules/pam_exec/pam_exec.c
index 8b37e95e..b56e4b26 100644
--- a/modules/pam_exec/pam_exec.c
+++ b/modules/pam_exec/pam_exec.c
@@ -72,6 +72,24 @@ static struct {
ENV_ITEM(PAM_RUSER),
};
+/* move_fd_to_non_stdio copies the given file descriptor to something other
+ * than stdin, stdout, or stderr. Assumes that the caller will close all
+ * unwanted fds after calling. */
+static int
+move_fd_to_non_stdio (pam_handle_t *pamh, int fd)
+{
+ while (fd < 3)
+ {
+ fd = dup(fd);
+ if (fd == -1)
+ {
+ int err = errno;
+ pam_syslog (pamh, LOG_ERR, "dup failed: %m");
+ _exit (err);
+ }
+ }
+ return fd;
+}
static int
call_exec (const char *pam_type, pam_handle_t *pamh,
@@ -81,11 +99,14 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
int call_setuid = 0;
int quiet = 0;
int expose_authtok = 0;
+ int use_stdout = 0;
int optargc;
const char *logfile = NULL;
const char *authtok = NULL;
pid_t pid;
int fds[2];
+ int stdout_fds[2];
+ FILE *stdout_file = NULL;
if (argc < 1) {
pam_syslog (pamh, LOG_ERR,
@@ -100,8 +121,15 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
if (strcasecmp (argv[optargc], "debug") == 0)
debug = 1;
+ else if (strcasecmp (argv[optargc], "stdout") == 0)
+ use_stdout = 1;
else if (strncasecmp (argv[optargc], "log=", 4) == 0)
logfile = &argv[optargc][4];
+ else if (strncasecmp (argv[optargc], "type=", 5) == 0)
+ {
+ if (strcmp (pam_type, &argv[optargc][5]) != 0)
+ return PAM_IGNORE;
+ }
else if (strcasecmp (argv[optargc], "seteuid") == 0)
call_setuid = 1;
else if (strcasecmp (argv[optargc], "quiet") == 0)
@@ -164,6 +192,21 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
}
}
+ if (use_stdout)
+ {
+ if (pipe(stdout_fds) != 0)
+ {
+ pam_syslog (pamh, LOG_ERR, "Could not create pipe: %m");
+ return PAM_SYSTEM_ERR;
+ }
+ stdout_file = fdopen(stdout_fds[0], "r");
+ if (!stdout_file)
+ {
+ pam_syslog (pamh, LOG_ERR, "Could not fdopen pipe: %m");
+ return PAM_SYSTEM_ERR;
+ }
+ }
+
if (optargc >= argc) {
pam_syslog (pamh, LOG_ERR, "No path given as argument");
return PAM_SERVICE_ERR;
@@ -198,6 +241,21 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
close(fds[1]);
}
+ if (use_stdout)
+ {
+ char buf[4096];
+ close(stdout_fds[1]);
+ while (fgets(buf, sizeof(buf), stdout_file) != NULL)
+ {
+ size_t len;
+ len = strlen(buf);
+ if (buf[len-1] == '\n')
+ buf[len-1] = '\0';
+ pam_info(pamh, "%s", buf);
+ }
+ fclose(stdout_file);
+ }
+
while ((retval = waitpid (pid, &status, 0)) == -1 &&
errno == EINTR);
if (retval == (pid_t)-1)
@@ -245,6 +303,23 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
int envlen, nitems;
char *envstr;
+ /* First, move all the pipes off of stdin, stdout, and stderr, to ensure
+ * that calls to dup2 won't close them. */
+
+ if (expose_authtok)
+ {
+ fds[0] = move_fd_to_non_stdio(pamh, fds[0]);
+ close(fds[1]);
+ }
+
+ if (use_stdout)
+ {
+ stdout_fds[1] = move_fd_to_non_stdio(pamh, stdout_fds[1]);
+ close(stdout_fds[0]);
+ }
+
+ /* Set up stdin. */
+
if (expose_authtok)
{
/* reopen stdin as pipe */
@@ -254,17 +329,10 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
pam_syslog (pamh, LOG_ERR, "dup2 of STDIN failed: %m");
_exit (err);
}
-
- for (i = 0; i < sysconf (_SC_OPEN_MAX); i++)
- {
- if (i != STDIN_FILENO)
- close (i);
- }
}
else
{
- for (i = 0; i < sysconf (_SC_OPEN_MAX); i++)
- close (i);
+ close (STDIN_FILENO);
/* New stdin. */
if ((i = open ("/dev/null", O_RDWR)) < 0)
@@ -275,12 +343,23 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
}
}
- /* New stdout and stderr. */
- if (logfile)
+ /* Set up stdout. */
+
+ if (use_stdout)
+ {
+ if (dup2(stdout_fds[1], STDOUT_FILENO) == -1)
+ {
+ int err = errno;
+ pam_syslog (pamh, LOG_ERR, "dup2 to stdout failed: %m");
+ _exit (err);
+ }
+ }
+ else if (logfile)
{
time_t tm = time (NULL);
char *buffer = NULL;
+ close (STDOUT_FILENO);
if ((i = open (logfile, O_CREAT|O_APPEND|O_WRONLY,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1)
{
@@ -297,7 +376,7 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
}
else
{
- /* New stdout/stderr. */
+ close (STDOUT_FILENO);
if ((i = open ("/dev/null", O_RDWR)) < 0)
{
int err = errno;
@@ -306,13 +385,16 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
}
}
- if (dup (i) == -1)
+ if (dup2 (STDOUT_FILENO, STDERR_FILENO) == -1)
{
int err = errno;
- pam_syslog (pamh, LOG_ERR, "dup failed: %m");
+ pam_syslog (pamh, LOG_ERR, "dup2 failed: %m");
_exit (err);
}
+ for (i = 3; i < sysconf (_SC_OPEN_MAX); i++)
+ close (i);
+
if (call_setuid)
if (setuid (geteuid ()) == -1)
{