http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.34.tar.gz
[xscreensaver] / driver / passwd-pam.c
index 0d14e262313ea8471661d85632d71f78860c110e..324e31fd197188e3e87b5c9192893744f7951124 100644 (file)
@@ -1,7 +1,7 @@
 /* passwd-pam.c --- verifying typed passwords with PAM
  * (Pluggable Authentication Modules.)
  * written by Bill Nottingham <notting@redhat.com> (and jwz) for
- * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1993-2001 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
@@ -55,6 +55,8 @@ extern char *blurb(void);
 
 #include <sys/stat.h>
 
+extern void block_sigchld (void);
+extern void unblock_sigchld (void);
 
 /* blargh */
 #undef  Bool
@@ -67,6 +69,13 @@ extern char *blurb(void);
 #undef countof
 #define countof(x) (sizeof((x))/sizeof(*(x)))
 
+/* Some time between Red Hat 4.2 and 7.0, the words were transposed 
+   in the various PAM_x_CRED macro names.  Yay!
+ */
+#ifndef  PAM_REFRESH_CRED
+# define PAM_REFRESH_CRED PAM_CRED_REFRESH
+#endif
+
 static int pam_conversation (int nmsgs,
                              const struct pam_message **msg,
                              struct pam_response **resp,
@@ -150,6 +159,9 @@ struct pam_closure {
 /* On SunOS 5.6, the `pam_conv.appdata_ptr' slot seems to be ignored, and
    the `closure' argument to pc.conv always comes in as random garbage.
    So we get around this by using a global variable instead.  Shoot me!
+
+   (I've been told this is bug 4092227, and is fixed in Solaris 7.)
+   (I've also been told that it's fixed in Solaris 2.6 by patch 106257-05.)
  */
 static void *suns_pam_implementation_blows = 0;
 
@@ -208,14 +220,46 @@ pam_passwd_valid_p (const char *typed_passwd, Bool verbose_p)
   }
 
   /* Try to authenticate as the current user.
+     We must turn off our SIGCHLD handler for the duration of the call to
+     pam_authenticate(), because in some cases, the underlying PAM code
+     will do this:
+
+        1: fork a setuid subprocess to do some dirty work;
+        2: read a response from that subprocess;
+        3: waitpid(pid, ...) on that subprocess.
+
+    If we (the ignorant parent process) have a SIGCHLD handler, then there's
+    a race condition between steps 2 and 3: if the subprocess exits before
+    waitpid() was called, then our SIGCHLD handler fires, and gets notified
+    of the subprocess death; then PAM's call to waitpid() fails, because the
+    process has already been reaped.
+
+    I consider this a bug in PAM, since the caller should be able to have
+    whatever signal handlers it wants -- the PAM documentation doesn't say
+    "oh by the way, if you use PAM, you can't use SIGCHLD."
    */
+
   PAM_NO_DELAY(pamh);
+
+  block_sigchld();
   status = pam_authenticate (pamh, 0);
+  unblock_sigchld();
+
   if (verbose_p)
     fprintf (stderr, "%s:   pam_authenticate (...) ==> %d (%s)\n",
              blurb(), status, PAM_STRERROR(pamh, status));
   if (status == PAM_SUCCESS)  /* Win! */
-    goto DONE;
+    {
+      /* Each time we successfully authenticate, refresh credentials,
+         for Kerberos/AFS/DCE/etc.  If this fails, just ignore that
+         failure and blunder along; it shouldn't matter.
+       */
+      int status2 = pam_setcred (pamh, PAM_REFRESH_CRED);
+      if (verbose_p)
+        fprintf (stderr, "%s:   pam_setcred (...) ==> %d (%s)\n",
+                 blurb(), status2, PAM_STRERROR(pamh, status2));
+      goto DONE;
+    }
 
   /* If that didn't work, set the user to root, and try to authenticate again.
    */