Package: libpam-modules
Version: 1.1.3-7
Severity: wishlist
Tags: patch

The attached patches implement two new options for the pam_exec module.

Patch 1 adds a "stdout" option, which shows the stdout (and stderr) of
the executed command via pam_info.  For instance, adding the following
line to /etc/pam.d/login right before the line for pam_motd:
session optional pam_exec.so stdout /usr/bin/seq 5
will print five lines (numbered 1-5) at the start and end of the
session.  In order to implement this option without breaking the
existing support for the expose_authtok option, I had to
reorganize the file descriptor handling to move the loop that closes all 
unwanted
file descriptors below all the code that sets up stdin/stdout/stderr,
and add some new code before that setup to ensure that none of the pipes
ended up on stdin/stdout/stderr where they might get closed by dup2.

Patch 2 adds a "type" option, which causes pam_exec to only execute the
command when the PAM module type matches the given type.  In particular,
this makes it possible to run only at the start or end of a session,
without having to write a separate wrapper script to check the PAM_TYPE
environment variable.  For example, adding the following to
/etc/pam.d/login right before the line for pam_motd:
session optional pam_exec.so type=open_session /bin/sleep 5
will sleep for 5 seconds at login time, but not at logout time,
demonstrating that the option works.

Together, these options make it possible to show dynamically generated
output at the start of a PAM session.  For example, the following
pam_exec invocation has the same effect as the current
dynamically-generated first line of the motd:
session optional pam_exec.so type=open_session stdout /bin/uname -snrvm

CCing Roger Leigh, who plans to use this functionality to replace the
current dynamically generated motd.

- Josh Triplett
=== modified file 'modules/pam_exec/pam_exec.8.xml'
--- modules/pam_exec/pam_exec.8.xml	2010-10-19 22:24:34 +0000
+++ modules/pam_exec/pam_exec.8.xml	2012-04-23 12:01:40 +0000
@@ -31,6 +31,9 @@
         quiet
       </arg>
       <arg choice="opt">
+        stdout
+      </arg>
+      <arg choice="opt">
         log=<replaceable>file</replaceable>
       </arg>
       <arg choice="plain">
@@ -119,6 +122,17 @@
 
         <varlistentry>
           <term>
+            <option>stdout</option>
+          </term>
+          <listitem>
+            <para>
+              Shows the output of the command.
+            </para>
+          </listitem>
+       </varlistentry>
+
+        <varlistentry>
+          <term>
             <option>quiet</option>
           </term>
           <listitem>
@@ -236,7 +250,8 @@
   <refsect1 id='pam_exec-author'>
     <title>AUTHOR</title>
       <para>
-        pam_exec was written by Thorsten Kukuk &lt;ku...@thkukuk.de&gt;.
+        pam_exec was written by Thorsten Kukuk &lt;ku...@thkukuk.de&gt; and
+        Josh Triplett &lt;j...@joshtriplett.org&gt;.
       </para>
   </refsect1>
 

=== modified file 'modules/pam_exec/pam_exec.c'
--- modules/pam_exec/pam_exec.c	2009-04-03 07:36:22 +0000
+++ modules/pam_exec/pam_exec.c	2012-04-23 12:01:40 +0000
@@ -72,6 +72,24 @@
   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 @@
   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,6 +121,8 @@
 
       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 (strcasecmp (argv[optargc], "seteuid") == 0)
@@ -164,6 +187,21 @@
 	}
     }
 
+  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 +236,21 @@
         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 +298,23 @@
       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 +324,10 @@
 	      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 +338,23 @@
 	    }
 	}
 
-      /* 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 +371,7 @@
 	}
       else
 	{
-	  /* New stdout/stderr.  */
+	  close (STDOUT_FILENO);
 	  if ((i = open ("/dev/null", O_RDWR)) < 0)
 	    {
 	      int err = errno;
@@ -306,13 +380,16 @@
 	    }
 	}
 
-      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)
 	  {

=== modified file 'modules/pam_exec/pam_exec.8.xml'
--- modules/pam_exec/pam_exec.8.xml	2012-04-23 12:01:40 +0000
+++ modules/pam_exec/pam_exec.8.xml	2012-04-23 12:33:04 +0000
@@ -36,6 +36,9 @@
       <arg choice="opt">
         log=<replaceable>file</replaceable>
       </arg>
+      <arg choice="opt">
+        type=<replaceable>type</replaceable>
+      </arg>
       <arg choice="plain">
        <replaceable>command</replaceable>
       </arg>
@@ -122,6 +125,17 @@
 
         <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>
@@ -208,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>

=== modified file 'modules/pam_exec/pam_exec.c'
--- modules/pam_exec/pam_exec.c	2012-04-23 12:01:40 +0000
+++ modules/pam_exec/pam_exec.c	2012-04-23 12:33:04 +0000
@@ -125,6 +125,11 @@
 	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)

Reply via email to