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: