1 /* passwd.c --- verifying typed passwords with the OS.
2 * xscreensaver, Copyright (c) 1993-2014 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
17 #ifndef NO_LOCKING /* whole file */
29 # include <pwd.h> /* for getpwuid() */
36 #endif /* HAVE_SYSLOG */
38 #include <X11/Intrinsic.h>
40 #include "xscreensaver.h"
43 extern const char *blurb(void);
44 extern void check_for_leaks (const char *where);
56 #define countof(x) (sizeof((x))/sizeof(*(x)))
60 Bool (*init) (int argc, char **argv, Bool verbose_p);
61 Bool (*priv_init) (int argc, char **argv, Bool verbose_p);
62 Bool (*valid_p) (const char *typed_passwd, Bool verbose_p);
63 void (*try_unlock) (saver_info *si, Bool verbose_p,
64 Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
71 extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p);
72 extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
75 extern Bool pam_priv_init (int argc, char **argv, Bool verbose_p);
76 extern void pam_try_unlock (saver_info *si, Bool verbose_p,
77 Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
79 #ifdef PASSWD_HELPER_PROGRAM
80 extern Bool ext_priv_init (int argc, char **argv, Bool verbose_p);
81 extern Bool ext_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
83 extern Bool pwent_lock_init (int argc, char **argv, Bool verbose_p);
84 extern Bool pwent_priv_init (int argc, char **argv, Bool verbose_p);
85 extern Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
87 Bool lock_priv_init (int argc, char **argv, Bool verbose_p);
88 Bool lock_init (int argc, char **argv, Bool verbose_p);
89 Bool passwd_valid_p (const char *typed_passwd, Bool verbose_p);
91 /* The authorization methods to try, in order.
92 Note that the last one (the pwent version) is actually two auth methods,
93 since that code tries shadow passwords, and then non-shadow passwords.
94 (It's all in the same file since the APIs are randomly nearly-identical.)
96 struct auth_methods methods[] = {
98 { "PAM", 0, pam_priv_init, 0, pam_try_unlock,
101 # ifdef HAVE_KERBEROS
102 { "Kerberos", kerberos_lock_init, 0, kerberos_passwd_valid_p, 0,
105 # ifdef PASSWD_HELPER_PROGRAM
106 { "external", 0, ext_priv_init, ext_passwd_valid_p, 0,
109 { "normal", pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0,
115 lock_priv_init (int argc, char **argv, Bool verbose_p)
119 for (i = 0; i < countof(methods); i++)
121 if (!methods[i].priv_init)
122 methods[i].priv_initted_p = True;
124 methods[i].priv_initted_p = methods[i].priv_init (argc, argv,
127 if (methods[i].priv_initted_p)
130 fprintf (stderr, "%s: initialization of %s passwords failed.\n",
131 blurb(), methods[i].name);
138 lock_init (int argc, char **argv, Bool verbose_p)
142 for (i = 0; i < countof(methods); i++)
144 if (!methods[i].priv_initted_p) /* Bail if lock_priv_init failed. */
147 if (!methods[i].init)
148 methods[i].initted_p = True;
150 methods[i].initted_p = methods[i].init (argc, argv, verbose_p);
152 if (methods[i].initted_p)
155 fprintf (stderr, "%s: initialization of %s passwords failed.\n",
156 blurb(), methods[i].name);
162 /* A basic auth driver that simply prompts for a password then runs it through
163 * valid_p to determine whether the password is correct.
166 try_unlock_password(saver_info *si,
168 Bool (*valid_p)(const char *typed_passwd, Bool verbose_p))
170 struct auth_message message;
171 struct auth_response *response = NULL;
173 memset(&message, 0, sizeof(message));
176 fprintf(stderr, "%s: non-PAM password auth.\n", blurb());
178 /* Call the auth_conv function with "Password:", then feed
179 * the result into valid_p()
181 message.type = AUTH_MSGTYPE_PROMPT_NOECHO;
182 message.msg = "Password:";
184 si->unlock_cb(1, &message, &response, si);
189 if (valid_p (response->response, verbose_p))
190 si->unlock_state = ul_success; /* yay */
191 else if (si->unlock_state == ul_cancel ||
192 si->unlock_state == ul_time)
193 ; /* more specific failures ok */
195 si->unlock_state = ul_fail; /* generic failure */
197 if (response->response)
198 free(response->response);
203 /* Write a password failure to the system log.
206 do_syslog (saver_info *si, Bool verbose_p)
209 struct passwd *pw = getpwuid (getuid ());
210 char *d = DisplayString (si->dpy);
211 char *u = (pw && pw->pw_name ? pw->pw_name : "???");
219 # if defined(LOG_AUTHPRIV)
221 # elif defined(LOG_AUTH)
230 # define FMT "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\""
233 fprintf (stderr, "%s: syslog: " FMT "\n", blurb(),
234 si->unlock_failures, d, u);
236 openlog (progname, opt, fac);
237 syslog (LOG_NOTICE, FMT, si->unlock_failures, d, u);
240 # endif /* HAVE_SYSLOG */
246 * Runs through each authentication driver calling its try_unlock function.
247 * Called xss_authenticate() because AIX beat us to the name authenticate().
250 xss_authenticate(saver_info *si, Bool verbose_p)
254 si->unlock_state = ul_read;
256 for (i = 0; i < countof(methods); i++)
258 if (!methods[i].initted_p)
261 if (si->cached_passwd != NULL && methods[i].valid_p)
262 si->unlock_state = (methods[i].valid_p(si->cached_passwd, verbose_p) == True)
263 ? ul_success : ul_fail;
264 else if (methods[i].try_unlock != NULL)
265 methods[i].try_unlock(si, verbose_p, methods[i].valid_p);
266 else if (methods[i].valid_p)
267 try_unlock_password(si, verbose_p, methods[i].valid_p);
268 else /* Ze goggles, zey do nozing! */
269 fprintf(stderr, "%s: authentication method %s does nothing.\n",
270 blurb(), methods[i].name);
272 check_for_leaks (methods[i].name);
274 /* If password authentication failed, but the password was NULL
275 (meaning the user just hit RET) then treat that as "cancel".
276 This means that if the password is literally NULL, it will
277 work; but if not, then NULL passwords are treated as cancel.
279 if (si->unlock_state == ul_fail &&
283 fprintf (stderr, "%s: assuming null password means cancel.\n",
285 si->unlock_state = ul_cancel;
288 if (si->unlock_state == ul_success)
290 /* If we successfully authenticated by method N, but attempting
291 to authenticate by method N-1 failed, mention that (since if
292 an earlier authentication method fails and a later one succeeds,
293 something screwy is probably going on.)
295 if (verbose_p && i > 0)
297 for (j = 0; j < i; j++)
298 if (methods[j].initted_p)
300 "%s: authentication via %s failed.\n",
301 blurb(), methods[j].name);
303 "%s: authentication via %s succeeded.\n",
304 blurb(), methods[i].name);
306 goto DONE; /* Successfully authenticated! */
308 else if (si->unlock_state == ul_cancel ||
309 si->unlock_state == ul_time)
311 /* If any auth method gets a cancel or timeout, don't try the
312 next auth method! We're done! */
314 "%s: authentication via %s %s.\n",
315 blurb(), methods[i].name,
316 (si->unlock_state == ul_cancel
317 ? "cancelled" : "timed out"));
323 fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb());
325 if (si->unlock_state == ul_fail)
327 /* Note the time of the first failure */
328 if (si->unlock_failures == 0)
329 si->unlock_failure_time = time((time_t *) 0);
330 si->unlock_failures++;
331 do_syslog (si, verbose_p);
335 if (si->auth_finished_cb)
336 si->auth_finished_cb (si);
339 #endif /* NO_LOCKING -- whole file */