From http://www.jwz.org/xscreensaver/xscreensaver-5.29.tar.gz
[xscreensaver] / driver / passwd.c
index 99fdee08c62744b45d5df7a307dee208fa071613..fd42c5521351da99fa65a2584e8debe429bd7789 100644 (file)
 # include <unistd.h>
 #endif
 
+#ifndef VMS
+# include <pwd.h>              /* for getpwuid() */
+#else /* VMS */
+# include "vms-pwd.h"
+#endif /* VMS */
+
+#ifdef HAVE_SYSLOG
+# include <syslog.h>
+#endif /* HAVE_SYSLOG */
+
 #include <X11/Intrinsic.h>
 
+#include "xscreensaver.h"
 #include "auth.h"
 
 extern const char *blurb(void);
@@ -159,6 +170,9 @@ try_unlock_password(saver_info *si,
 
   memset(&message, 0, sizeof(message));
 
+  if (verbose_p)
+    fprintf(stderr, "%s: non-PAM password auth.\n", blurb());
+
   /* Call the auth_conv function with "Password:", then feed
    * the result into valid_p()
    */
@@ -170,7 +184,13 @@ try_unlock_password(saver_info *si,
   if (!response)
     return;
 
-  si->unlock_state = valid_p(response->response, verbose_p) ? ul_success : ul_fail;
+  if (valid_p (response->response, verbose_p))
+    si->unlock_state = ul_success;            /* yay */
+  else if (si->unlock_state == ul_cancel ||
+           si->unlock_state == ul_time)
+    ;                                         /* more specific failures ok */
+  else
+    si->unlock_state = ul_fail;                       /* generic failure */
 
   if (response->response)
     free(response->response);
@@ -178,6 +198,48 @@ try_unlock_password(saver_info *si,
 }
 
 
+/* Write a password failure to the system log.
+ */
+static void
+do_syslog (saver_info *si, Bool verbose_p)
+{
+# ifdef HAVE_SYSLOG
+  struct passwd *pw = getpwuid (getuid ());
+  char *d = DisplayString (si->dpy);
+  char *u = (pw && pw->pw_name ? pw->pw_name : "???");
+  int opt = 0;
+  int fac = 0;
+
+#  ifdef LOG_PID
+  opt = LOG_PID;
+#  endif
+
+#  if defined(LOG_AUTHPRIV)
+  fac = LOG_AUTHPRIV;
+#  elif defined(LOG_AUTH)
+  fac = LOG_AUTH;
+#  else
+  fac = LOG_DAEMON;
+#  endif
+
+  if (!d) d = "";
+
+#  undef FMT
+#  define FMT "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\""
+
+  if (verbose_p)
+    fprintf (stderr, "%s: syslog: " FMT "\n", blurb(), 
+             si->unlock_failures, d, u);
+
+  openlog (progname, opt, fac);
+  syslog (LOG_NOTICE, FMT, si->unlock_failures, d, u);
+  closelog ();
+
+# endif /* HAVE_SYSLOG */
+}
+
+
+
 /**
  * Runs through each authentication driver calling its try_unlock function.
  * Called xss_authenticate() because AIX beat us to the name authenticate().
@@ -187,6 +249,8 @@ xss_authenticate(saver_info *si, Bool verbose_p)
 {
   int i, j;
 
+  si->unlock_state = ul_read;
+
   for (i = 0; i < countof(methods); i++)
     {
       if (!methods[i].initted_p)
@@ -205,6 +269,20 @@ xss_authenticate(saver_info *si, Bool verbose_p)
 
       check_for_leaks (methods[i].name);
 
+      /* If password authentication failed, but the password was NULL
+         (meaning the user just hit RET) then treat that as "cancel".
+         This means that if the password is literally NULL, it will
+         work; but if not, then NULL passwords are treated as cancel.
+       */
+      if (si->unlock_state == ul_fail &&
+          si->cached_passwd &&
+          !*si->cached_passwd)
+        {
+          fprintf (stderr, "%s: assuming null password means cancel.\n",
+                   blurb());
+          si->unlock_state = ul_cancel;
+        }
+
       if (si->unlock_state == ul_success)
         {
           /* If we successfully authenticated by method N, but attempting
@@ -225,11 +303,29 @@ xss_authenticate(saver_info *si, Bool verbose_p)
             }
           goto DONE;           /* Successfully authenticated! */
         }
+      else if (si->unlock_state == ul_cancel ||
+               si->unlock_state == ul_time)
+        {
+          /* If any auth method gets a cancel or timeout, don't try the
+             next auth method!  We're done! */
+          fprintf (stderr,
+                   "%s: authentication via %s %s.\n",
+                       blurb(), methods[i].name,
+                   (si->unlock_state == ul_cancel
+                    ? "cancelled" : "timed out"));
+          goto DONE;
+        }
     }
 
   if (verbose_p)
     fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb());
 
+  if (si->unlock_state == ul_fail)
+    {
+      si->unlock_failures++;
+      do_syslog (si, verbose_p);
+    }
+
 DONE:
   if (si->auth_finished_cb)
     si->auth_finished_cb (si);