From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / driver / passwd.c
1 /* passwd.c --- verifying typed passwords with the OS.
2  * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski <jwz@jwz.org>
3  *
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 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #ifndef NO_LOCKING  /* whole file */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #ifdef HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25
26 #ifndef VMS
27 # include <pwd.h>               /* for getpwuid() */
28 #else /* VMS */
29 # include "vms-pwd.h"
30 #endif /* VMS */
31
32 #ifdef HAVE_SYSLOG
33 # include <syslog.h>
34 #endif /* HAVE_SYSLOG */
35
36 #include <X11/Intrinsic.h>
37
38 #include "xscreensaver.h"
39 #include "auth.h"
40
41 extern const char *blurb(void);
42 extern void check_for_leaks (const char *where);
43
44
45 /* blargh */
46 #undef  Bool
47 #undef  True
48 #undef  False
49 #define Bool  int
50 #define True  1
51 #define False 0
52
53 #undef countof
54 #define countof(x) (sizeof((x))/sizeof(*(x)))
55
56 struct auth_methods {
57   const char *name;
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));
63   Bool initted_p;
64   Bool priv_initted_p;
65 };
66
67
68 #ifdef HAVE_KERBEROS
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);
71 #endif
72 #ifdef HAVE_PAM
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));
76 #endif
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);
80 #endif
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);
84
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);
88
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.)
93  */
94 struct auth_methods methods[] = {
95 # ifdef HAVE_PAM
96   { "PAM",              0, pam_priv_init, 0, pam_try_unlock,
97                         False, False },
98 # endif
99 # ifdef HAVE_KERBEROS
100   { "Kerberos",         kerberos_lock_init, 0, kerberos_passwd_valid_p, 0,
101                         False, False },
102 # endif
103 # ifdef PASSWD_HELPER_PROGRAM
104   { "external",         0, ext_priv_init, ext_passwd_valid_p, 0,
105                         False, False },
106 # endif
107   { "normal",           pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0,
108                         False, False }
109 };
110
111
112 Bool
113 lock_priv_init (int argc, char **argv, Bool verbose_p)
114 {
115   int i;
116   Bool any_ok = False;
117   for (i = 0; i < countof(methods); i++)
118     {
119       if (!methods[i].priv_init)
120         methods[i].priv_initted_p = True;
121       else
122         methods[i].priv_initted_p = methods[i].priv_init (argc, argv,
123                                                           verbose_p);
124
125       if (methods[i].priv_initted_p)
126         any_ok = True;
127       else if (verbose_p)
128         fprintf (stderr, "%s: initialization of %s passwords failed.\n",
129                  blurb(), methods[i].name);
130     }
131   return any_ok;
132 }
133
134
135 Bool
136 lock_init (int argc, char **argv, Bool verbose_p)
137 {
138   int i;
139   Bool any_ok = False;
140   for (i = 0; i < countof(methods); i++)
141     {
142       if (!methods[i].priv_initted_p)   /* Bail if lock_priv_init failed. */
143         continue;
144
145       if (!methods[i].init)
146         methods[i].initted_p = True;
147       else
148         methods[i].initted_p = methods[i].init (argc, argv, verbose_p);
149
150       if (methods[i].initted_p)
151         any_ok = True;
152       else if (verbose_p)
153         fprintf (stderr, "%s: initialization of %s passwords failed.\n",
154                  blurb(), methods[i].name);
155     }
156   return any_ok;
157 }
158
159
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.
162  */
163 static void
164 try_unlock_password(saver_info *si,
165                    Bool verbose_p,
166                    Bool (*valid_p)(const char *typed_passwd, Bool verbose_p))
167 {
168   struct auth_message message;
169   struct auth_response *response = NULL;
170
171   memset(&message, 0, sizeof(message));
172
173   if (verbose_p)
174     fprintf(stderr, "%s: non-PAM password auth.\n", blurb());
175
176   /* Call the auth_conv function with "Password:", then feed
177    * the result into valid_p()
178    */
179   message.type = AUTH_MSGTYPE_PROMPT_NOECHO;
180   message.msg = "Password:";
181
182   si->unlock_cb(1, &message, &response, si);
183
184   if (!response)
185     return;
186
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 */
192   else
193     si->unlock_state = ul_fail;                /* generic failure */
194
195   if (response->response)
196     free(response->response);
197   free(response);
198 }
199
200
201 /* Write a password failure to the system log.
202  */
203 static void
204 do_syslog (saver_info *si, Bool verbose_p)
205 {
206 # ifdef HAVE_SYSLOG
207   struct passwd *pw = getpwuid (getuid ());
208   char *d = DisplayString (si->dpy);
209   char *u = (pw && pw->pw_name ? pw->pw_name : "???");
210   int opt = 0;
211   int fac = 0;
212
213 #  ifdef LOG_PID
214   opt = LOG_PID;
215 #  endif
216
217 #  if defined(LOG_AUTHPRIV)
218   fac = LOG_AUTHPRIV;
219 #  elif defined(LOG_AUTH)
220   fac = LOG_AUTH;
221 #  else
222   fac = LOG_DAEMON;
223 #  endif
224
225   if (!d) d = "";
226
227 #  undef FMT
228 #  define FMT "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\""
229
230   if (verbose_p)
231     fprintf (stderr, "%s: syslog: " FMT "\n", blurb(), 
232              si->unlock_failures, d, u);
233
234   openlog (progname, opt, fac);
235   syslog (LOG_NOTICE, FMT, si->unlock_failures, d, u);
236   closelog ();
237
238 # endif /* HAVE_SYSLOG */
239 }
240
241
242
243 /**
244  * Runs through each authentication driver calling its try_unlock function.
245  * Called xss_authenticate() because AIX beat us to the name authenticate().
246  */
247 void
248 xss_authenticate(saver_info *si, Bool verbose_p)
249 {
250   int i, j;
251
252   si->unlock_state = ul_read;
253
254   for (i = 0; i < countof(methods); i++)
255     {
256       if (!methods[i].initted_p)
257         continue;
258
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);
269
270       check_for_leaks (methods[i].name);
271
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.
276        */
277       if (si->unlock_state == ul_fail &&
278           si->cached_passwd &&
279           !*si->cached_passwd)
280         {
281           fprintf (stderr, "%s: assuming null password means cancel.\n",
282                    blurb());
283           si->unlock_state = ul_cancel;
284         }
285
286       if (si->unlock_state == ul_success)
287         {
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.)
292            */
293           if (verbose_p && i > 0)
294             {
295               for (j = 0; j < i; j++)
296                 if (methods[j].initted_p)
297                     fprintf (stderr,
298                              "%s: authentication via %s failed.\n",
299                              blurb(), methods[j].name);
300               fprintf (stderr,
301                        "%s: authentication via %s succeeded.\n",
302                        blurb(), methods[i].name);
303             }
304           goto DONE;            /* Successfully authenticated! */
305         }
306       else if (si->unlock_state == ul_cancel ||
307                si->unlock_state == ul_time)
308         {
309           /* If any auth method gets a cancel or timeout, don't try the
310              next auth method!  We're done! */
311           fprintf (stderr,
312                    "%s: authentication via %s %s.\n",
313                        blurb(), methods[i].name,
314                    (si->unlock_state == ul_cancel
315                     ? "cancelled" : "timed out"));
316           goto DONE;
317         }
318     }
319
320   if (verbose_p)
321     fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb());
322
323   if (si->unlock_state == ul_fail)
324     {
325       si->unlock_failures++;
326       do_syslog (si, verbose_p);
327     }
328
329 DONE:
330   if (si->auth_finished_cb)
331     si->auth_finished_cb (si);
332 }
333
334 #endif /* NO_LOCKING -- whole file */