http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / driver / passwd.c
index b0a4b8ad10d8af638aa9a800cd3cf86ed4527c20..066bc81f9c608d6a0178c5be12605b30556efaaf 100644 (file)
@@ -1,5 +1,5 @@
 /* passwd.c --- verifying typed passwords with the OS.
- * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@netscape.com>
+ * xscreensaver, Copyright (c) 1993-2004 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
 
 #ifndef NO_LOCKING  /* whole file */
 
+#include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
 #ifndef VMS
-# include <pwd.h>
+# include <pwd.h>              /* for getpwuid() */
 #else /* VMS */
 # include "vms-pwd.h"
 #endif /* VMS */
 
+#ifdef HAVE_SYSLOG
+# include <syslog.h>
+#endif /* HAVE_SYSLOG */
 
-#ifdef __bsdi__
-# include <sys/param.h>
-# if _BSDI_VERSION >= 199608
-#  define BSD_AUTH
-# endif
-#endif /* __bsdi__ */
-
-
-#if defined(HAVE_SHADOW_PASSWD)              /* passwds live in /etc/shadow */
-
-#   include <shadow.h>
-#   define PWTYPE   struct spwd *
-#   define PWPSLOT  sp_pwdp
-#   define GETPW    getspnam
-
-#elif defined(HAVE_ENHANCED_PASSWD)      /* passwds live in /tcb/files/auth/ */
-                                     /* M.Matsumoto <matsu@yao.sharp.co.jp> */
-#   include <sys/security.h>
-#   include <prot.h>
+#include <X11/Intrinsic.h>
 
-#   define PWTYPE   struct pr_passwd *
-#   define PWPSLOT  ufld.fd_encrypt
-#   define GETPW    getprpwnam
+#include "xscreensaver.h"
+#include "auth.h"
 
-#elif defined(HAVE_ADJUNCT_PASSWD)
-
-#   include <sys/label.h>
-#   include <sys/audit.h>
-#   include <pwdadj.h>
-
-#   define PWTYPE   struct passwd_adjunct *
-#   define PWPSLOT  pwa_passwd
-#   define GETPW    getpwanam
-
-#elif defined(HAVE_HPUX_PASSWD)
-
-#   include <hpsecurity.h>
-#   include <prot.h>
-
-#   define PWTYPE   struct s_passwd *
-#   define PWPSLOT  pw_passwd
-#   define GETPW    getspwnam
-#   define crypt    bigcrypt
-
-#endif
+extern const char *blurb(void);
+extern void check_for_leaks (const char *where);
 
 
 /* blargh */
 #define True  1
 #define False 0
 
+#undef countof
+#define countof(x) (sizeof((x))/sizeof(*(x)))
 
-extern char *progname;
+struct auth_methods {
+  const char *name;
+  Bool (*init) (int argc, char **argv, Bool verbose_p);
+  Bool (*priv_init) (int argc, char **argv, Bool verbose_p);
+  Bool (*valid_p) (const char *typed_passwd, Bool verbose_p);
+  void (*try_unlock) (saver_info *si, Bool verbose_p,
+                     Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
+  Bool initted_p;
+  Bool priv_initted_p;
+};
 
-static char *encrypted_root_passwd = 0;
-static char *encrypted_user_passwd = 0;
 
-#ifdef VMS
-# define ROOT "SYSTEM"
-#else
-# define ROOT "root"
+#ifdef HAVE_KERBEROS
+extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p);
+extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
 #endif
+#ifdef HAVE_PAM
+extern Bool pam_priv_init (int argc, char **argv, Bool verbose_p);
+extern void pam_try_unlock (saver_info *si, Bool verbose_p,
+                           Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
+#endif
+#ifdef PASSWD_HELPER_PROGRAM
+extern Bool ext_priv_init (int argc, char **argv, Bool verbose_p);
+extern Bool ext_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
+#endif
+extern Bool pwent_lock_init (int argc, char **argv, Bool verbose_p);
+extern Bool pwent_priv_init (int argc, char **argv, Bool verbose_p);
+extern Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
+
+Bool lock_priv_init (int argc, char **argv, Bool verbose_p);
+Bool lock_init (int argc, char **argv, Bool verbose_p);
+Bool passwd_valid_p (const char *typed_passwd, Bool verbose_p);
+
+/* The authorization methods to try, in order.
+   Note that the last one (the pwent version) is actually two auth methods,
+   since that code tries shadow passwords, and then non-shadow passwords.
+   (It's all in the same file since the APIs are randomly nearly-identical.)
+ */
+struct auth_methods methods[] = {
+# ifdef HAVE_PAM
+  { "PAM",              0, pam_priv_init, 0, pam_try_unlock,
+                        False, False },
+# endif
+# ifdef HAVE_KERBEROS
+  { "Kerberos",         kerberos_lock_init, 0, kerberos_passwd_valid_p, 0,
+                        False, False },
+# endif
+# ifdef PASSWD_HELPER_PROGRAM
+  { "external",                0, ext_priv_init, ext_passwd_valid_p, 0,
+                       False, False },
+# endif
+  { "normal",           pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0,
+                        False, False }
+};
 
 
-
-#ifndef VMS
-
-static char *
-user_name (void)
+Bool
+lock_priv_init (int argc, char **argv, Bool verbose_p)
 {
-  /* I think that just checking $USER here is not the best idea. */
-
-  const char *u = 0;
-
-  /* It has been reported that getlogin() returns the wrong user id on some
-     very old SGI systems...  And I've seen it return the string "rlogin"
-     sometimes!  Screw it, using getpwuid() should be enough...
-   */
-/* u = (char *) getlogin ();
- */
-
-  /* getlogin() fails if not attached to a terminal; in that case, use
-     getpwuid().  (Note that in this case, we're not doing shadow stuff, since
-     all we're interested in is the name, not the password.  So that should
-     still work.  Right?) */
-  if (!u || !*u)
+  int i;
+  Bool any_ok = False;
+  for (i = 0; i < countof(methods); i++)
     {
-      struct passwd *p = getpwuid (getuid ());
-      u = (p ? p->pw_name : 0);
+      if (!methods[i].priv_init)
+        methods[i].priv_initted_p = True;
+      else
+        methods[i].priv_initted_p = methods[i].priv_init (argc, argv,
+                                                          verbose_p);
+
+      if (methods[i].priv_initted_p)
+        any_ok = True;
+      else if (verbose_p)
+        fprintf (stderr, "%s: initialization of %s passwords failed.\n",
+                 blurb(), methods[i].name);
     }
-
-  return (u ? strdup(u) : 0);
+  return any_ok;
 }
 
-#else  /* VMS */
 
-static char *
-user_name (void)
+Bool
+lock_init (int argc, char **argv, Bool verbose_p)
 {
-  char *u = getenv("USER");
-  return (u ? strdup(u) : 0);
+  int i;
+  Bool any_ok = False;
+  for (i = 0; i < countof(methods); i++)
+    {
+      if (!methods[i].priv_initted_p)  /* Bail if lock_priv_init failed. */
+        continue;
+
+      if (!methods[i].init)
+        methods[i].initted_p = True;
+      else
+        methods[i].initted_p = methods[i].init (argc, argv, verbose_p);
+
+      if (methods[i].initted_p)
+        any_ok = True;
+      else if (verbose_p)
+        fprintf (stderr, "%s: initialization of %s passwords failed.\n",
+                 blurb(), methods[i].name);
+    }
+  return any_ok;
 }
 
-#endif /* VMS */
 
-
-static Bool
-passwd_known_p (const char *pw)
+/* A basic auth driver that simply prompts for a password then runs it through
+ * valid_p to determine whether the password is correct.
+ */
+static void
+try_unlock_password(saver_info *si,
+                  Bool verbose_p,
+                  Bool (*valid_p)(const char *typed_passwd, Bool verbose_p))
 {
-  return (pw &&
-         pw[0] != '*' &&       /* This would be sensible...         */
-         strlen(pw) > 4);      /* ...but this is what Solaris does. */
-}
+  struct auth_message message;
+  struct auth_response *response = NULL;
 
+  memset(&message, 0, sizeof(message));
 
-static char *
-get_encrypted_passwd(const char *user)
-{
-  if (user && *user)
-    {
-#ifdef PWTYPE
-      {                                        /* First check the shadow passwords. */
-       PWTYPE p = GETPW((char *) user);
-       if (p && passwd_known_p (p->PWPSLOT))
-         return strdup(p->PWPSLOT);
-      }
-#endif
-      {                                        /* Check non-shadow passwords too. */
-       struct passwd *p = getpwnam(user);
-       if (p && passwd_known_p (p->pw_passwd))
-         return strdup(p->pw_passwd);
-      }
-    }
+  if (verbose_p)
+    fprintf(stderr, "%s: non-PAM password auth.\n", blurb());
 
-  fprintf (stderr, "%s: couldn't get password of \"%s\"\n",
-          progname, (user ? user : "(null)"));
+  /* Call the auth_conv function with "Password:", then feed
+   * the result into valid_p()
+   */
+  message.type = AUTH_MSGTYPE_PROMPT_NOECHO;
+  message.msg = "Password:";
 
-  return 0;
-}
+  si->unlock_cb(1, &message, &response, si);
 
+  if (!response)
+    return;
 
+  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 */
 
-/* This has to be called before we've changed our effective user ID,
-   because it might need priveleges to get at the encrypted passwords.
-   Returns false if we weren't able to get any passwords, and therefore,
-   locking isn't possible.  (It will also have written to stderr.)
- */
+  if (response->response)
+    free(response->response);
+  free(response);
+}
 
-#ifndef VMS
 
-Bool
-lock_init (int argc, char **argv)
+/* Write a password failure to the system log.
+ */
+static void
+do_syslog (saver_info *si, Bool verbose_p)
 {
-  char *u;
-
-#ifdef HAVE_ENHANCED_PASSWD
-  set_auth_parameters(argc, argv);
-  check_auth_parameters();
-#endif /* HAVE_DEC_ENHANCED */
-
-  u = user_name();
-  encrypted_user_passwd = get_encrypted_passwd(u);
-  encrypted_root_passwd = get_encrypted_passwd(ROOT);
-  if (u) free (u);
-
-  if (encrypted_user_passwd)
-    return True;
-  else
-    return False;
+# 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 */
 }
 
 
-/* This can be called at any time, and says whether the typed password
-   belongs to either the logged in user (real uid, not effective); or
-   to root.
+
+/**
+ * Runs through each authentication driver calling its try_unlock function.
+ * Called xss_authenticate() because AIX beat us to the name authenticate().
  */
-Bool
-passwd_valid_p (const char *typed_passwd)
+void
+xss_authenticate(saver_info *si, Bool verbose_p)
 {
-  char *s = 0;  /* note that on some systems, crypt() may return null */
-
-  if (encrypted_user_passwd &&
-      (s = (char *) crypt (typed_passwd, encrypted_user_passwd)) &&
-      !strcmp (s, encrypted_user_passwd))
-    return True;
+  int i, j;
 
-  /* do not allow root to have a null password. */
-  else if (typed_passwd[0] &&
-          encrypted_root_passwd &&
-          (s = (char *) crypt (typed_passwd, encrypted_root_passwd)) &&
-          !strcmp (s, encrypted_root_passwd))
-    return True;
+  for (i = 0; i < countof(methods); i++)
+    {
+      if (!methods[i].initted_p)
+        continue;
+
+      if (si->cached_passwd != NULL && methods[i].valid_p)
+       si->unlock_state = (methods[i].valid_p(si->cached_passwd, verbose_p) == True)
+                          ? ul_success : ul_fail;
+      else if (methods[i].try_unlock != NULL)
+        methods[i].try_unlock(si, verbose_p, methods[i].valid_p);
+      else if (methods[i].valid_p)
+        try_unlock_password(si, verbose_p, methods[i].valid_p);
+      else /* Ze goggles, zey do nozing! */
+        fprintf(stderr, "%s: authentication method %s does nothing.\n",
+                blurb(), methods[i].name);
+
+      check_for_leaks (methods[i].name);
+
+      if (si->unlock_state == ul_success)
+        {
+          /* If we successfully authenticated by method N, but attempting
+             to authenticate by method N-1 failed, mention that (since if
+             an earlier authentication method fails and a later one succeeds,
+             something screwy is probably going on.)
+           */
+          if (verbose_p && i > 0)
+            {
+              for (j = 0; j < i; j++)
+                if (methods[j].initted_p)
+                    fprintf (stderr,
+                             "%s: authentication via %s failed.\n",
+                             blurb(), methods[j].name);
+              fprintf (stderr,
+                       "%s: authentication via %s succeeded.\n",
+                       blurb(), methods[i].name);
+            }
+          goto DONE;           /* Successfully authenticated! */
+        }
+    }
 
-  else
-    return False;
-}
+  if (verbose_p)
+    fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb());
 
-#else  /* VMS */
-Bool lock_init (int argc, char **argv) { return True; }
-#endif /* VMS */
+  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);
+}
 
 #endif /* NO_LOCKING -- whole file */