http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.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   for (i = 0; i < countof(methods); i++)
253     {
254       if (!methods[i].initted_p)
255         continue;
256
257       if (si->cached_passwd != NULL && methods[i].valid_p)
258         si->unlock_state = (methods[i].valid_p(si->cached_passwd, verbose_p) == True)
259                            ? ul_success : ul_fail;
260       else if (methods[i].try_unlock != NULL)
261         methods[i].try_unlock(si, verbose_p, methods[i].valid_p);
262       else if (methods[i].valid_p)
263         try_unlock_password(si, verbose_p, methods[i].valid_p);
264       else /* Ze goggles, zey do nozing! */
265         fprintf(stderr, "%s: authentication method %s does nothing.\n",
266                 blurb(), methods[i].name);
267
268       check_for_leaks (methods[i].name);
269
270       if (si->unlock_state == ul_success)
271         {
272           /* If we successfully authenticated by method N, but attempting
273              to authenticate by method N-1 failed, mention that (since if
274              an earlier authentication method fails and a later one succeeds,
275              something screwy is probably going on.)
276            */
277           if (verbose_p && i > 0)
278             {
279               for (j = 0; j < i; j++)
280                 if (methods[j].initted_p)
281                     fprintf (stderr,
282                              "%s: authentication via %s failed.\n",
283                              blurb(), methods[j].name);
284               fprintf (stderr,
285                        "%s: authentication via %s succeeded.\n",
286                        blurb(), methods[i].name);
287             }
288           goto DONE;            /* Successfully authenticated! */
289         }
290     }
291
292   if (verbose_p)
293     fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb());
294
295   if (si->unlock_state == ul_fail)
296     {
297       si->unlock_failures++;
298       do_syslog (si, verbose_p);
299     }
300
301 DONE:
302   if (si->auth_finished_cb)
303     si->auth_finished_cb (si);
304 }
305
306 #endif /* NO_LOCKING -- whole file */