d9d159d7b8733c65e68dc82b5196a3d020b232fc
[oweals/cde.git] / cde / lib / pam / pam_modules / dce / dce_authenticate.c
1 /* $XConsortium: dce_authenticate.c /main/5 1996/05/09 04:26:26 drk $ */
2 /*
3  * Copyright (c) 1995, by Sun Microsystems, Inc.
4  * All rights reserved.
5  */
6
7 #ident  "@(#)dce_authenticate.c 1.34     96/02/14 SMI"
8
9 #include <dce/nbase.h>
10 #include <dce/sec_login.h>
11 #include <dce/dce_error.h>
12 #include <security/pam_appl.h>
13 #include <security/pam_modules.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <sys/types.h>
18 #include <rpc/des_crypt.h>
19 #include <pwd.h>
20 #include <syslog.h>
21 #include <libintl.h>
22
23 #include "pam_impl.h"
24 #include "utils.h"
25
26 #ifdef XFN_MAPPING
27 #include "xfn_mapping.h"
28 #endif /* XFN_MAPPING */
29
30 #define SLEEPTIME       4
31
32 /* maxmimum DCE_PASSWD_LENGTH. We need to pick something for 
33  *  __pam_get_authtok to use.
34  */
35
36 #define DCE_PASSWD_LENGTH 256
37
38 static int
39 attempt_dce_login(
40         void *pamh,
41         dce_module_data_t       *dsd,
42         error_status_t          *st,
43         char                    *user,
44         char                    *dce_pass
45 );
46
47 void
48 dce_cleanup(
49         pam_handle_t *pamh,
50         void *data,
51         int pam_status
52 );
53
54 /*
55  * pam_sm_authenticate          - Authenticate user
56  */
57
58 int
59 pam_sm_authenticate(
60         pam_handle_t            *pamh,
61         int                     flags,
62         int                     argc,
63         const char              **argv)
64 {
65         char                    *user;
66         int                     err, result = PAM_AUTH_ERR;
67         char                    messages[1][PAM_MAX_MSG_SIZE];
68         error_status_t          st;
69         char                    *defpass;
70         int                     num_msg = 0;
71         int                     debug = 0;
72         int                     warn = 1;
73         int                     passwd_flag = 0;
74         int                     try_first_pass = 0;
75         int                     use_first_pass = 0;
76         int                     ignore = 0;
77         int                     invalid_user = 0;
78
79 #ifdef XFN_MAPPING
80         int                     try_mapped_pass = 0;
81         int                     use_mapped_pass = 0;
82 #endif
83         int                     i;
84         char                    *firstpass = NULL, *password = NULL;
85         uid_t                   pw_uid;
86         dce_module_data_t       *dsd = NULL;
87
88         for (i = 0; i < argc; i++) {
89                 if (strcmp(argv[i], "debug") == 0)
90                         debug = 1;
91                 else if (strcmp(argv[i], "try_first_pass") == 0) {
92                         if (!passwd_flag) {
93                                 try_first_pass = 1;
94                                 passwd_flag = 1;
95                         }
96                 } else if (strcmp(argv[i], "use_first_pass") == 0) {
97                         if (!passwd_flag) {
98                                 use_first_pass = 1;
99                                 passwd_flag = 1;
100                         }
101 #ifdef XFN_MAPPING
102                 } else if (strcmp(argv[i], "try_mapped_pass") == 0) {
103                         if (!passwd_flag) {
104                                 try_mapped_pass = 1;
105                                 passwd_flag = 1;
106                         }
107                 } else if (strcmp(argv[i], "use_mapped_pass") == 0) {
108                         if (!passwd_flag) {
109                                 use_mapped_pass = 1;
110                                 passwd_flag = 1;
111                         }
112 #endif
113                 } else if (strcmp(argv[i], "nowarn") == 0) {
114                         warn = 0;
115                 } else {
116                         syslog(LOG_ERR, "illegal module option %s", argv[i]);
117                 }
118         }
119
120         if (flags & PAM_SILENT) warn = 0;
121
122         if (debug)
123                 syslog(LOG_DEBUG, "DCE pam_sm_authenticate");
124
125         err = pam_get_user(pamh, &user, NULL);
126
127         if (err != PAM_SUCCESS)
128                 return (err);
129
130         if (user == NULL || !user[0])
131                 return (PAM_AUTH_ERR);
132
133         /* Don't bother to authenticate root in DCE */
134
135         if (strcmp(user, "root") == 0)
136                 ignore = 1;
137
138         /* make sure a password entry exists for this user */
139         /* we also need the uid for XFN */
140
141         if (!get_pw_uid(user, &pw_uid)) {
142                 invalid_user = 1;
143         }
144
145         if (pam_get_data(pamh, DCE_DATA, (void**)&dsd) != PAM_SUCCESS ||
146                 dsd == NULL) {
147
148                 dsd = calloc(1, sizeof (dce_module_data_t));
149                 if (dsd == NULL) {
150                         result = PAM_BUF_ERR;
151                         goto out;
152                 }
153
154                 if ((err = pam_set_data(pamh, DCE_DATA, dsd, &dce_cleanup))
155                         != PAM_SUCCESS) {
156                                 free(dsd);
157                                 result = err;
158                                 goto out;
159                 }
160
161         } else {
162                 if (dsd->login_context != sec_login_default_handle) {
163                         error_status_t st;
164                         sec_login_purge_context(&dsd->login_context, &st);
165                 }
166         }
167
168         dsd->login_context = sec_login_default_handle;
169         dsd->auth_status = PAM_AUTH_ERR;
170         dsd->debug = debug;
171         dsd->warn = warn;
172         dsd->reset_passwd = 0;
173         dsd->passwd_expired = 0;
174         dsd->auth_src = sec_login_auth_src_network;
175
176         /* see if a legitimate DCE user */
177
178         if (!sec_login_setup_identity((unsigned char *)user,
179                         sec_login_no_flags, &dsd->login_context, &st)) {
180                 if (debug) {
181                         dce_error_string_t text;
182                         syslog(LOG_DEBUG,
183                                 "PAM: DCE sec_login_setup_identity: %s",
184                         get_dce_error_message(st, text));
185                 }
186                 if (st == sec_rgy_object_not_found) {
187                         /* mask the unknown user case */
188                         invalid_user = 1;
189                 } else {
190                         result = PAM_AUTH_ERR;
191                         goto out;
192                 }
193         }
194
195         err = pam_get_item(pamh, PAM_AUTHTOK, (void **) &firstpass);
196
197         if (err != PAM_SUCCESS && (use_first_pass || use_mapped_pass)) {
198                 result = PAM_AUTH_ERR;
199                 if (debug)
200                         syslog(LOG_DEBUG, "PAM: DCE goto out!");
201                 goto out;
202         }
203
204         if (firstpass != NULL && (ignore || invalid_user))
205                 goto out;
206
207 #ifdef XFN_MAPPING
208
209         if (firstpass == NULL) {
210                 if (use_mapped_pass) goto out;
211         } else if (try_mapped_pass || use_mapped_pass) {
212                 char dcepass[MAP_PASSLEN+1];
213                 uid_t saved_uid;
214                 int got_mapped_pass;
215
216                 saved_uid = geteuid();
217
218                 if (saved_uid != pw_uid && getuid() == 0 &&
219                                 seteuid(pw_uid) < 0) {
220                         syslog(LOG_ERR,
221                                 "xfn_get_mapped_passwd: seteuid: %m");
222                         /* continue since we might be able to get mapping */
223                 }
224
225                 got_mapped_pass = xfn_get_mapped_password(
226                         debug ? XFN_MAP_DEBUG : 0, user, DCE_XFN_PASS_ATTR,
227                                 firstpass, dcepass, sizeof (dcepass));
228
229                 if (geteuid() != saved_uid && seteuid(saved_uid) < 0) {
230                         syslog(LOG_ERR,
231                                 "xfn_get_mapped_passwd seteuid restore: %m");
232                         /* XXX:  what should we do here? */
233                 }
234
235                 if (got_mapped_pass) {
236
237                         result =
238                             attempt_dce_login(pamh, dsd, &st, user, dcepass);
239
240                         memset(dcepass, 0, MAP_PASSLEN);
241
242                         if (result == PAM_SUCCESS ||
243                                 st == sec_login_s_acct_invalid) goto out;
244                 }
245                 if (use_mapped_pass) goto out;
246         }
247 #endif
248
249         if (firstpass == NULL) {
250                 if (use_first_pass) goto out;
251         } else if (use_first_pass || try_first_pass) {
252
253                 result = attempt_dce_login(pamh, dsd, &st,
254                                                 user, firstpass);
255
256                 if (result == PAM_SUCCESS ||
257                         st == sec_login_s_acct_invalid ||
258                         use_first_pass) {
259                         goto out;
260                 }
261         }
262
263         /*
264          * Get the password from the user
265          */
266
267         if (debug)
268                 syslog(LOG_DEBUG, "DCE pam_sm_auth prompting for password");
269
270         if (firstpass == NULL &&
271                         !(try_first_pass||try_mapped_pass||invalid_user))
272                 (void) sprintf(messages[0], (const char *) PAM_MSG(pamh, 10,
273                         "Password: "));
274         else
275                 (void) sprintf(messages[0], (const char *) PAM_MSG(pamh, 11,
276                         "DCE Password: "));
277
278         num_msg = 1;
279
280         err = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
281                         DCE_PASSWD_LENGTH,messages[0], &password);
282         if (debug)
283                 syslog(LOG_DEBUG, "DCE __pam_get_authtok = %d", err);
284
285         if (err != PAM_SUCCESS) {
286                 result = err;
287                 goto out;
288         }
289
290         if (password == NULL) {
291                 /* Need a password to proceed */
292                 result = PAM_AUTH_ERR;
293                 goto out;
294         }
295
296         if (firstpass == NULL) {
297                 /* this is the first password, stash it away */
298                 pam_set_item(pamh, PAM_AUTHTOK, password);
299         }
300
301         /* one last ditch attempt to login to DCE */
302
303         if (invalid_user || ignore)
304                 goto out;
305
306         result = attempt_dce_login(pamh, dsd, &st, user, password);
307
308 #ifdef XFN_MAPPING
309         /* we had to prompt for DCE password, so attempt to */
310         /* update mapping iff we got a good DCE password */
311
312         if (try_mapped_pass && result == PAM_SUCCESS && firstpass) {
313                 uid_t saved_uid;
314
315                 saved_uid = geteuid();
316
317                 if (saved_uid != pw_uid && seteuid(pw_uid) < 0) {
318                         syslog(LOG_ERR, "xfn_get_mapped_passwd: seteuid: %m");
319                         goto out;
320                 }
321
322                 xfn_update_mapped_password(debug ? XFN_MAP_DEBUG : 0,
323                         user, DCE_XFN_PASS_ATTR, firstpass, password);
324
325                 if (geteuid() != saved_uid && seteuid(saved_uid) < 0) {
326                         syslog(LOG_ERR,
327                         "xfn_get_mapped_passwd seteuid restore: %m");
328                         /* XXX:  what should we do here? */
329                 }
330         }
331 #endif
332
333 out:
334
335         if (ignore) {
336                 result = PAM_IGNORE;
337         }
338
339         if (password != NULL)
340                 memset(password, 0, strlen(password));
341
342         if (invalid_user)
343                 result = PAM_USER_UNKNOWN;
344
345         if (dsd)
346                 dsd->auth_status = result;
347
348         return (result);
349 }
350
351 static int
352 attempt_dce_login(
353         void *pamh,
354         dce_module_data_t       *dsd,
355         error_status_t          *st,
356         char *user,
357         char *dce_pass)
358 {
359         sec_passwd_rec_t        passwd_rec;
360         boolean32               login_valid = 0;
361         error_status_t          set_st;
362
363         /* have to strdup password because the call clears it */
364         passwd_rec.key.tagged_union.plain = (idl_char *) strdup(dce_pass);
365         if (passwd_rec.key.tagged_union.plain == NULL) {
366                 return (PAM_BUF_ERR);
367         }
368         passwd_rec.key.key_type = sec_passwd_plain;
369         passwd_rec.pepper = NULL;
370         passwd_rec.version_number = sec_passwd_c_version_none;
371
372         login_valid = sec_login_valid_and_cert_ident(dsd->login_context,
373                 &passwd_rec, &dsd->reset_passwd, &dsd->auth_src, st);
374
375         if (dsd->debug) {
376                 dce_error_string_t text;
377
378                 syslog(LOG_DEBUG, "sec_login_valid_and_cert_ident: %s",
379                                 get_dce_error_message(*st, text));
380         }
381
382         if (*st == sec_login_s_acct_invalid && dsd->warn) {
383                 char messages[1][PAM_MAX_MSG_SIZE];
384
385                 sprintf(messages[0],
386                 PAM_MSG(pamh, 12, "Error: Your DCE Account has expired.\n"));
387                 __pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL);
388         }
389
390         /* the call to sec_login_valid_and_cert_ident already zeros */
391         /* out the password, so we just free it */
392
393         free(passwd_rec.key.tagged_union.plain);
394
395         return (login_valid ? PAM_SUCCESS : PAM_AUTH_ERR);
396 }
397
398 void
399 dce_cleanup(
400         pam_handle_t *pamh,
401         void *data,
402         int pam_status)
403 {
404         error_status_t  st;
405         dce_module_data_t *dsd = (dce_module_data_t *) data;
406         int status;
407
408         if (dsd->debug) {
409                 syslog(LOG_DEBUG, "DCE dce_cleanup pam_sm_auth_status(%d)",
410                         dsd->auth_status);
411         }
412
413         if (!dsd->login_context != sec_login_default_handle) {
414                 free(dsd);
415                 return;
416         }
417
418         /* if pam_end as PAM_SUCCESS, clean up based on value in */
419         /* auth_status, otherwise just purge the context */
420
421         if (pam_status == PAM_SUCCESS) {
422                 pam_sec_login_free_context(dsd->auth_status,
423                         &dsd->login_context, &st);
424         } else {
425                 sec_login_purge_context(&dsd->login_context, &st);
426                 if (dsd->debug) {
427                         dce_error_string_t text;
428                         syslog(LOG_DEBUG, "sec_login_purge_context: %s",
429                                 get_dce_error_message(st, text));
430                 }
431         }
432
433         free(dsd);
434 }