Package: libpam-modules
Version: 1.1.8-3.6
Severity: normal

Dear Maintainer,

pam_exec is currently not handling SIGCHLD in the same way that other
pam modules do when using fork (see for example how pam_unix reset
SIGCHLD to default before fork and restore SIGCHLD to the old value
after wait).

By not resetting SIGCHLD, you might encounter a race conditions when the
process that is calling pam actually sets up a signal handler that runs
wait (to retrieve pids of its children).

I was able to always reproduce this behaviour with a fresh Debian
installation (minimal set of packages) and:

- install a trivial pam_exec line, for example append in
  /etc/pam.d/common-account

- sets up an at job for the near future

- verify the behaviour of the atd process

- you should see that it fails and in the log:

/var/log/auth.log:
Oct 13 21:47:00 d9test atd[766]: pam_exec(atd:account): waitpid returns with 
-1: No child processes

and a strace of atd reveals what happens (atd has pid 487, the new
process that is generated at 21:47 has pid 766)
(see the output of "strace -tt -f -p 487")

- atd (pid=487) forks at exactly 21:47 (it's the job that has been
  setup) producing pid=766
- pam stack runs, pam_exec fork/exec the process that is defined in
  /etc/pam.d (in this example /bin/true), pid=767
- pid=767 exits immediately with exit=0
- pid=766 receives SIGCHLD and you can clearly see that there's a signal
  handler that collects pid=767
- after the signal handler, there's another wait call (pam_exec's) that
  cannot find this child and therefore exit with exit=1

487   21:46:24.691254 restart_syscall(<... resuming interrupted nanosleep ...>) 
= 0
487   21:47:00.039083 stat(".", {st_mode=S_IFDIR|S_ISVTX|0770, st_size=4096, 
...}) = 0
[...]
487   21:47:00.040078 clone(child_stack=NULL, 
flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, 
child_tidptr=0x7fad11926f50) = 766
766   21:47:00.040313 set_robust_list(0x7fad11926f60, 24) = 0
[...]
766   21:47:00.060342 clone(child_stack=NULL, 
flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, 
child_tidptr=0x7fad11926f50) = 767
767   21:47:00.060535 set_robust_list(0x7fad11926f60, 24 <unfinished ...>
[...]
767   21:47:00.158848 execve("/bin/true", ["/bin/true"], [/* 3 vars */]) = 0
[...]
767   21:47:00.160283 exit_group(0)     = ?
767   21:47:00.160345 +++ exited with 0 +++
766   21:47:00.160366 <... read resumed> "", 4096) = 0
766   21:47:00.160412 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, 
si_pid=767, si_uid=1, si_status=0, si_utime=0, si_stime=0} ---
766   21:47:00.160462 wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 
WNOHANG, NULL) = 767
766   21:47:00.160549 wait4(-1, 0x7ffe3a24a914, WNOHANG, NULL) = -1 ECHILD (No 
child processes)
766   21:47:00.160607 rt_sigreturn({mask=[]}) = 0
766   21:47:00.160668 close(7)          = 0
766   21:47:00.160719 wait4(767, 0x7ffe3a24aff8, 0, NULL) = -1 ECHILD (No child 
processes)
[...]
766   21:47:00.163397 exit_group(1)     = ?
766   21:47:00.163533 +++ exited with 1 +++
[...]

A simple fix (that works for me) could be to do what pam_unix does:
reset to SIG_DFL before fork and then restore the signal at wait.
(in pam_unix there's also a check of option UNIX_NOREAP, it might be a
good idea to also introduce this in pam_exec).

--- pam_exec.c.orig
+++ pam_exec.c
@@ -48,6 +48,7 @@
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <signal.h>
 
 
 #define PAM_SM_AUTH
@@ -212,6 +213,10 @@
     return PAM_SERVICE_ERR;
   }
 
+  memset(&newsa, '\0', sizeof(newsa));
+  newsa.sa_handler = SIG_DFL;
+  sigaction(SIGCHLD, &newsa, &oldsa);
+
   pid = fork();
   if (pid == -1)
     return PAM_SYSTEM_ERR;
@@ -258,6 +263,7 @@
 
       while ((retval = waitpid (pid, &status, 0)) == -1 &&
             errno == EINTR);
+      sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
       if (retval == (pid_t)-1)
        {
          pam_syslog (pamh, LOG_ERR, "waitpid returns with -1: %m");



Regards, Leonardo.


-- System Information:
Debian Release: 9.5
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 4.9.0-8-amd64 (SMP w/6 CPU cores)
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), 
LANGUAGE=en_GB:en (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)

Versions of packages libpam-modules depends on:
ii  debconf [debconf-2.0]  1.5.61
ii  libaudit1              1:2.6.7-2
ii  libc6                  2.24-11+deb9u3
ii  libdb5.3               5.3.28-12+deb9u1
ii  libpam-modules-bin     1.1.8-3.6
ii  libpam0g               1.1.8-3.6
ii  libselinux1            2.6-3+b3

libpam-modules recommends no packages.

libpam-modules suggests no packages.

-- debconf information:
  libpam-modules/disable-screensaver:

Reply via email to