/* passwd.c --- verifying typed passwords with the OS.
- * xscreensaver, Copyright (c) 1993-1997 Jamie Zawinski <jwz@netscape.com>
+ * xscreensaver, Copyright (c) 1993-2014 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 <time.h>
+#include <sys/time.h>
-#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 PRTYPE passwd_adjunct *
-# define PWPSLOT pwa_passwd
-# define GETPW getpwanam
-
-#elif defined(HAVE_HPUX_PASSWD)
-
-# include <hpsecurity.h>
-# include <prot.h>
-
-# define PRTYPE 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 */
+ int i, j;
- if (encrypted_user_passwd &&
- (s = (char *) crypt (typed_passwd, encrypted_user_passwd)) &&
- !strcmp (s, encrypted_user_passwd))
- return True;
+ si->unlock_state = ul_read;
- /* 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 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
+ 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 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;
+ }
+ }
- 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)
+ {
+ /* Note the time of the first failure */
+ if (si->unlock_failures == 0)
+ si->unlock_failure_time = time((time_t *) 0);
+ si->unlock_failures++;
+ do_syslog (si, verbose_p);
+ }
+DONE:
+ if (si->auth_finished_cb)
+ si->auth_finished_cb (si);
+}
#endif /* NO_LOCKING -- whole file */