99fdee08c62744b45d5df7a307dee208fa071613
[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 #include <X11/Intrinsic.h>
27
28 #include "auth.h"
29
30 extern const char *blurb(void);
31 extern void check_for_leaks (const char *where);
32
33
34 /* blargh */
35 #undef  Bool
36 #undef  True
37 #undef  False
38 #define Bool  int
39 #define True  1
40 #define False 0
41
42 #undef countof
43 #define countof(x) (sizeof((x))/sizeof(*(x)))
44
45 struct auth_methods {
46   const char *name;
47   Bool (*init) (int argc, char **argv, Bool verbose_p);
48   Bool (*priv_init) (int argc, char **argv, Bool verbose_p);
49   Bool (*valid_p) (const char *typed_passwd, Bool verbose_p);
50   void (*try_unlock) (saver_info *si, Bool verbose_p,
51                       Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
52   Bool initted_p;
53   Bool priv_initted_p;
54 };
55
56
57 #ifdef HAVE_KERBEROS
58 extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p);
59 extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
60 #endif
61 #ifdef HAVE_PAM
62 extern Bool pam_priv_init (int argc, char **argv, Bool verbose_p);
63 extern void pam_try_unlock (saver_info *si, Bool verbose_p,
64                             Bool (*valid_p)(const char *typed_passwd, Bool verbose_p));
65 #endif
66 #ifdef PASSWD_HELPER_PROGRAM
67 extern Bool ext_priv_init (int argc, char **argv, Bool verbose_p);
68 extern Bool ext_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
69 #endif
70 extern Bool pwent_lock_init (int argc, char **argv, Bool verbose_p);
71 extern Bool pwent_priv_init (int argc, char **argv, Bool verbose_p);
72 extern Bool pwent_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
73
74 Bool lock_priv_init (int argc, char **argv, Bool verbose_p);
75 Bool lock_init (int argc, char **argv, Bool verbose_p);
76 Bool passwd_valid_p (const char *typed_passwd, Bool verbose_p);
77
78 /* The authorization methods to try, in order.
79    Note that the last one (the pwent version) is actually two auth methods,
80    since that code tries shadow passwords, and then non-shadow passwords.
81    (It's all in the same file since the APIs are randomly nearly-identical.)
82  */
83 struct auth_methods methods[] = {
84 # ifdef HAVE_PAM
85   { "PAM",              0, pam_priv_init, 0, pam_try_unlock,
86                         False, False },
87 # endif
88 # ifdef HAVE_KERBEROS
89   { "Kerberos",         kerberos_lock_init, 0, kerberos_passwd_valid_p, 0,
90                         False, False },
91 # endif
92 # ifdef PASSWD_HELPER_PROGRAM
93   { "external",         0, ext_priv_init, ext_passwd_valid_p, 0,
94                         False, False },
95 # endif
96   { "normal",           pwent_lock_init, pwent_priv_init, pwent_passwd_valid_p, 0,
97                         False, False }
98 };
99
100
101 Bool
102 lock_priv_init (int argc, char **argv, Bool verbose_p)
103 {
104   int i;
105   Bool any_ok = False;
106   for (i = 0; i < countof(methods); i++)
107     {
108       if (!methods[i].priv_init)
109         methods[i].priv_initted_p = True;
110       else
111         methods[i].priv_initted_p = methods[i].priv_init (argc, argv,
112                                                           verbose_p);
113
114       if (methods[i].priv_initted_p)
115         any_ok = True;
116       else if (verbose_p)
117         fprintf (stderr, "%s: initialization of %s passwords failed.\n",
118                  blurb(), methods[i].name);
119     }
120   return any_ok;
121 }
122
123
124 Bool
125 lock_init (int argc, char **argv, Bool verbose_p)
126 {
127   int i;
128   Bool any_ok = False;
129   for (i = 0; i < countof(methods); i++)
130     {
131       if (!methods[i].priv_initted_p)   /* Bail if lock_priv_init failed. */
132         continue;
133
134       if (!methods[i].init)
135         methods[i].initted_p = True;
136       else
137         methods[i].initted_p = methods[i].init (argc, argv, verbose_p);
138
139       if (methods[i].initted_p)
140         any_ok = True;
141       else if (verbose_p)
142         fprintf (stderr, "%s: initialization of %s passwords failed.\n",
143                  blurb(), methods[i].name);
144     }
145   return any_ok;
146 }
147
148
149 /* A basic auth driver that simply prompts for a password then runs it through
150  * valid_p to determine whether the password is correct.
151  */
152 static void
153 try_unlock_password(saver_info *si,
154                    Bool verbose_p,
155                    Bool (*valid_p)(const char *typed_passwd, Bool verbose_p))
156 {
157   struct auth_message message;
158   struct auth_response *response = NULL;
159
160   memset(&message, 0, sizeof(message));
161
162   /* Call the auth_conv function with "Password:", then feed
163    * the result into valid_p()
164    */
165   message.type = AUTH_MSGTYPE_PROMPT_NOECHO;
166   message.msg = "Password:";
167
168   si->unlock_cb(1, &message, &response, si);
169
170   if (!response)
171     return;
172
173   si->unlock_state = valid_p(response->response, verbose_p) ? ul_success : ul_fail;
174
175   if (response->response)
176     free(response->response);
177   free(response);
178 }
179
180
181 /**
182  * Runs through each authentication driver calling its try_unlock function.
183  * Called xss_authenticate() because AIX beat us to the name authenticate().
184  */
185 void
186 xss_authenticate(saver_info *si, Bool verbose_p)
187 {
188   int i, j;
189
190   for (i = 0; i < countof(methods); i++)
191     {
192       if (!methods[i].initted_p)
193         continue;
194
195       if (si->cached_passwd != NULL && methods[i].valid_p)
196         si->unlock_state = (methods[i].valid_p(si->cached_passwd, verbose_p) == True)
197                            ? ul_success : ul_fail;
198       else if (methods[i].try_unlock != NULL)
199         methods[i].try_unlock(si, verbose_p, methods[i].valid_p);
200       else if (methods[i].valid_p)
201         try_unlock_password(si, verbose_p, methods[i].valid_p);
202       else /* Ze goggles, zey do nozing! */
203         fprintf(stderr, "%s: authentication method %s does nothing.\n",
204                 blurb(), methods[i].name);
205
206       check_for_leaks (methods[i].name);
207
208       if (si->unlock_state == ul_success)
209         {
210           /* If we successfully authenticated by method N, but attempting
211              to authenticate by method N-1 failed, mention that (since if
212              an earlier authentication method fails and a later one succeeds,
213              something screwy is probably going on.)
214            */
215           if (verbose_p && i > 0)
216             {
217               for (j = 0; j < i; j++)
218                 if (methods[j].initted_p)
219                     fprintf (stderr,
220                              "%s: authentication via %s failed.\n",
221                              blurb(), methods[j].name);
222               fprintf (stderr,
223                        "%s: authentication via %s succeeded.\n",
224                        blurb(), methods[i].name);
225             }
226           goto DONE;            /* Successfully authenticated! */
227         }
228     }
229
230   if (verbose_p)
231     fprintf(stderr, "%s: All authentication mechanisms failed.\n", blurb());
232
233 DONE:
234   if (si->auth_finished_cb)
235     si->auth_finished_cb (si);
236 }
237
238 #endif /* NO_LOCKING -- whole file */