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