1 /* passwd.c --- verifying typed passwords with the OS.
2 * xscreensaver, Copyright (c) 1993-2004 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 */
27 # include <pwd.h> /* for getpwuid() */
34 #endif /* HAVE_SYSLOG */
36 #include <X11/Intrinsic.h>
38 #include "xscreensaver.h"
41 extern const char *blurb(void);
42 extern void check_for_leaks (const char *where);
54 #define countof(x) (sizeof((x))/sizeof(*(x)))
58 Bool (*init) (int argc, char **argv, Bool verbose_p);
59 Bool (*priv_init) (int argc, char **argv, Bool verbose_p);
60 Bool (*valid_p) (const char *typed_passwd, Bool verbose_p);
61 void (*try_unlock) (saver_info *si, Bool verbose_p,
62 Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
69 extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p);
70 extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
73 extern Bool pam_priv_init (int argc, char **argv, Bool verbose_p);
74 extern void pam_try_unlock (saver_info *si, Bool verbose_p,
75 Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
77 #ifdef PASSWD_HELPER_PROGRAM
78 extern Bool ext_priv_init (int argc, char **argv, Bool verbose_p);
79 extern Bool ext_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
81 extern Bool pwent_lock_init (int argc, char **argv, Bool verbose_p);
82 extern Bool pwent_priv_init (int argc, char **argv, Bool verbose_p);
83 extern Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
85 Bool lock_priv_init (int argc, char **argv, Bool verbose_p);
86 Bool lock_init (int argc, char **argv, Bool verbose_p);
87 Bool passwd_valid_p (const char *typed_passwd, Bool verbose_p);
89 /* The authorization methods to try, in order.
90 Note that the last one (the pwent version) is actually two auth methods,
91 since that code tries shadow passwords, and then non-shadow passwords.
92 (It's all in the same file since the APIs are randomly nearly-identical.)
94 struct auth_methods methods[] = {
96 { "PAM", 0, pam_priv_init, 0, pam_try_unlock,
100 { "Kerberos", kerberos_lock_init, 0, kerberos_passwd_valid_p, 0,
103 # ifdef PASSWD_HELPER_PROGRAM
104 { "external", 0, ext_priv_init, ext_passwd_valid_p, 0,
107 { "normal", pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0,
113 lock_priv_init (int argc, char **argv, Bool verbose_p)
117 for (i = 0; i < countof(methods); i++)
119 if (!methods[i].priv_init)
120 methods[i].priv_initted_p = True;
122 methods[i].priv_initted_p = methods[i].priv_init (argc, argv,
125 if (methods[i].priv_initted_p)
128 fprintf (stderr, "%s: initialization of %s passwords failed.\n",
129 blurb(), methods[i].name);
136 lock_init (int argc, char **argv, Bool verbose_p)
140 for (i = 0; i < countof(methods); i++)
142 if (!methods[i].priv_initted_p) /* Bail if lock_priv_init failed. */
145 if (!methods[i].init)
146 methods[i].initted_p = True;
148 methods[i].initted_p = methods[i].init (argc, argv, verbose_p);
150 if (methods[i].initted_p)
153 fprintf (stderr, "%s: initialization of %s passwords failed.\n",
154 blurb(), methods[i].name);
160 /* A basic auth driver that simply prompts for a password then runs it through
161 * valid_p to determine whether the password is correct.
164 try_unlock_password(saver_info *si,
166 Bool (*valid_p)(const char *typed_passwd, Bool verbose_p))
168 struct auth_message message;
169 struct auth_response *response = NULL;
171 memset(&message, 0, sizeof(message));
174 fprintf(stderr, "%s: non-PAM password auth.\n", blurb());
176 /* Call the auth_conv function with "Password:", then feed
177 * the result into valid_p()
179 message.type = AUTH_MSGTYPE_PROMPT_NOECHO;
180 message.msg = "Password:";
182 si->unlock_cb(1, &message, &response, si);
187 if (valid_p (response->response, verbose_p))
188 si->unlock_state = ul_success; /* yay */
189 else if (si->unlock_state == ul_cancel ||
190 si->unlock_state == ul_time)
191 ; /* more specific failures ok */
193 si->unlock_state = ul_fail; /* generic failure */
195 if (response->response)
196 free(response->response);
201 /* Write a password failure to the system log.
204 do_syslog (saver_info *si, Bool verbose_p)
207 struct passwd *pw = getpwuid (getuid ());
208 char *d = DisplayString (si->dpy);
209 char *u = (pw && pw->pw_name ? pw->pw_name : "???");
217 # if defined(LOG_AUTHPRIV)
219 # elif defined(LOG_AUTH)
228 # define FMT "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\""
231 fprintf (stderr, "%s: syslog: " FMT "\n", blurb(),
232 si->unlock_failures, d, u);
234 openlog (progname, opt, fac);
235 syslog (LOG_NOTICE, FMT, si->unlock_failures, d, u);
238 # endif /* HAVE_SYSLOG */
244 * Runs through each authentication driver calling its try_unlock function.
245 * Called xss_authenticate() because AIX beat us to the name authenticate().
248 xss_authenticate(saver_info *si, Bool verbose_p)
252 si->unlock_state = ul_read;
254 for (i = 0; i < countof(methods); i++)
256 if (!methods[i].initted_p)
259 if (si->cached_passwd != NULL && methods[i].valid_p)
260 si->unlock_state = (methods[i].valid_p(si->cached_passwd, verbose_p) == True)
261 ? ul_success : ul_fail;
262 else if (methods[i].try_unlock != NULL)
263 methods[i].try_unlock(si, verbose_p, methods[i].valid_p);
264 else if (methods[i].valid_p)
265 try_unlock_password(si, verbose_p, methods[i].valid_p);
266 else /* Ze goggles, zey do nozing! */
267 fprintf(stderr, "%s: authentication method %s does nothing.\n",
268 blurb(), methods[i].name);
270 check_for_leaks (methods[i].name);
272 /* If password authentication failed, but the password was NULL
273 (meaning the user just hit RET) then treat that as "cancel".
274 This means that if the password is literally NULL, it will
275 work; but if not, then NULL passwords are treated as cancel.
277 if (si->unlock_state == ul_fail &&
281 fprintf (stderr, "%s: assuming null password means cancel.\n",
283 si->unlock_state = ul_cancel;
286 if (si->unlock_state == ul_success)
288 /* If we successfully authenticated by method N, but attempting
289 to authenticate by method N-1 failed, mention that (since if
290 an earlier authentication method fails and a later one succeeds,
291 something screwy is probably going on.)
293 if (verbose_p && i > 0)
295 for (j = 0; j < i; j++)
296 if (methods[j].initted_p)
298 "%s: authentication via %s failed.\n",
299 blurb(), methods[j].name);
301 "%s: authentication via %s succeeded.\n",
302 blurb(), methods[i].name);
304 goto DONE; /* Successfully authenticated! */
306 else if (si->unlock_state == ul_cancel ||
307 si->unlock_state == ul_time)
309 /* If any auth method gets a cancel or timeout, don't try the
310 next auth method! We're done! */
312 "%s: authentication via %s %s.\n",
313 blurb(), methods[i].name,
314 (si->unlock_state == ul_cancel
315 ? "cancelled" : "timed out"));
321 fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb());
323 if (si->unlock_state == ul_fail)
325 si->unlock_failures++;
326 do_syslog (si, verbose_p);
330 if (si->auth_finished_cb)
331 si->auth_finished_cb (si);
334 #endif /* NO_LOCKING -- whole file */