Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / pam / pam_modules / dce / dce_password.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: dce_password.c /main/5 1996/05/09 04:26:43 drk $ */
24 /*
25  * Copyright (c) 1995, by Sun Microsystems, Inc.
26  * All rights reserved.
27  */
28
29 #ident  "@(#)dce_password.c 1.22     96/02/14 SMI"
30
31 #include <security/pam_appl.h>
32 #include <security/pam_modules.h>
33 #include <syslog.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <pwd.h>
39
40 #include <dce/acct.h>
41 #include <dce/uuid.h>
42 #include <dce/binding.h>
43 #include <dce/sec_login.h>
44 #include <dce/dce_error.h>
45
46 #include "utils.h"
47
48 #include "pam_impl.h"
49
50 #ifdef XFN_MAPPING
51 #include "xfn_mapping.h"
52 #endif
53
54 static int
55 dce_changepw(
56         char *account_name,
57         char *old_pass,
58         char *new_pass);
59
60 static char *
61 get_passwd(
62         pam_handle_t *pamh,
63         char *prompt);
64
65 /*
66  * XXX: This module is NOT finished!
67  *
68  */
69 int
70 pam_sm_chauthtok(
71         pam_handle_t            *pamh,
72         int                     flags,
73         int                     argc,
74         const char              **argv)
75 {
76         char                    *user;
77         int                     err, result = PAM_AUTH_ERR;
78         char                    *newpass = NULL, *vnewpass = NULL;
79         char                    *oldpass = NULL;
80         int                     try_first_pass = 0;
81         int                     use_first_pass = 0;
82         char                    *firstpass = NULL;
83
84 #ifdef XFN_MAPPING
85         int                     try_mapped_pass = 0;
86         int                     use_mapped_pass = 0;
87         uid_t                   saved_uid;
88 #endif
89         int                     i;
90         int                     debug = 0;
91         uid_t                   pw_uid;
92         dce_module_data_t       *dsd = NULL;
93         error_status_t          status;
94
95         if (debug)
96                 syslog(LOG_DEBUG, "DCE Authentication\n");
97
98         for (i = 0; i < argc; i++) {
99                 if (strcmp(argv[i], "debug") == 0)
100                         debug = 1;
101                 else if (strcmp(argv[i], "try_first_pass") == 0)
102                         try_first_pass = 1;
103                 else if (strcmp(argv[i], "use_first_pass") == 0)
104                         use_first_pass = 1;
105 #ifdef XFN_MAPPING
106                 else if (strcmp(argv[i], "try_mapped_pass") == 0)
107                         try_mapped_pass = 1;
108                 else if (strcmp(argv[i], "use_mapped_pass") == 0)
109                         use_mapped_pass = 1;
110 #endif
111                 else
112                         syslog(LOG_DEBUG, "illegal scheme option %s", argv[i]);
113         }
114
115
116         if (flags & PAM_PRELIM_CHECK) {
117
118                 /* try and bind to registry master of local cell */
119
120                 sec_rgy_handle_t        rh = sec_rgy_default_handle;
121
122                 sec_rgy_site_open_update(NULL, &rh, &status);
123                 if (status == error_status_ok) {
124                         sec_rgy_site_close(rh, &status);
125                         return (PAM_SUCCESS);
126                 }
127                 return (PAM_AUTHTOK_ERR);
128         }
129
130         /* make sure PAM framework is telling us to update passwords */
131         if (!(flags & PAM_UPDATE_AUTHTOK)) {
132                 syslog(LOG_ERR, "dce pam_sm_chauthtok: bad flags: %d", flags);
133                 return (PAM_SYSTEM_ERR);
134         }
135
136
137         if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
138                 if (pam_get_data(pamh, DCE_DATA, (void **)&dsd)
139                                                         == PAM_SUCCESS) {
140                         if (!dsd->passwd_expired)
141                                 return (PAM_IGNORE);
142                 }
143         }
144
145         if ((err = pam_get_item(pamh, PAM_USER, (void **) &user)) < 0)
146                 return (err);
147
148         /* Don't bother to handle root in DCE */
149
150         if (strcmp(user, "root") == 0)
151                 return (PAM_IGNORE);
152
153 #ifdef XFN_MAPPING
154         if (use_mapped_pass || try_mapped_pass) {
155                 int got_mapped = 0, updated_mapped = 0;
156                 char dcepass[MAP_PASSLEN+1];
157
158                 if ((err = pam_get_item(pamh, PAM_AUTHTOK,
159                                         (void **) &newpass)) < 0)
160                         return (err);
161                 if ((err = pam_get_item(pamh, PAM_OLDAUTHTOK,
162                                         (void **) &oldpass)) < 0)
163                         return (err);
164
165                 if (!get_pw_uid(user, &pw_uid)) {
166                         return (PAM_AUTHTOK_ERR);
167                 }
168
169                 /* XXX: need to seteuid */
170
171                 saved_uid = geteuid();
172
173                 if (saved_uid != pw_uid && getuid() == 0 &&
174                                 seteuid(pw_uid) < 0) {
175                         syslog(LOG_ERR,
176                                 "xfn_get_mapped_passwd: seteuid: %m");
177                         return (PAM_AUTHTOK_ERR);
178                 }
179
180                 got_mapped = xfn_get_mapped_password(0, user,
181                         DCE_XFN_PASS_ATTR, oldpass, dcepass, sizeof (dcepass));
182
183                 if (got_mapped) {
184
185                         updated_mapped = xfn_update_mapped_password(
186                                 0, user, DCE_XFN_PASS_ATTR, newpass,
187                                                 dcepass);
188
189                         memset(dcepass, 0, sizeof (dcepass));
190
191                 } else {
192                         /* probably should prompt for DCE password */
193                         /* and attempt to update it */
194                 }
195
196                 if (geteuid() != saved_uid && seteuid(saved_uid) < 0) {
197                         syslog(LOG_ERR,
198                                 "xfn_get_mapped_passwd seteuid restore: %m");
199                         /* XXX:  what should we do here? */
200                 }
201
202                 if (use_mapped_pass) {
203                         if (updated_mapped)
204                                 return (PAM_SUCCESS);
205                         else
206                                 return (PAM_AUTHTOK_ERR);
207                 }
208
209                 return (PAM_AUTHTOK_ERR);
210         }
211 #endif /* XFN_MAPPING */
212
213         if (try_first_pass || use_first_pass) {
214
215                 if ((err = pam_get_item(pamh, PAM_AUTHTOK,
216                                         (void **) &newpass)) < 0)
217                         return (err);
218                 if ((err = pam_get_item(pamh, PAM_OLDAUTHTOK,
219                                         (void **) &oldpass)) < 0)
220                         return (err);
221
222                 result = dce_changepw(user, oldpass, newpass);
223
224                 if (result == PAM_SUCCESS) {
225                 /* might need to update PAM_OLDAUTHTOK and PAM_NEWAUTHTOK */
226                         goto out;
227                 }
228
229                 /* assume we need to prompt for old DCE password? */
230
231                 oldpass = get_passwd(pamh, PAM_MSG(pamh, 20,
232                                                 "Old DCE password: "));
233
234                 if (oldpass == NULL || oldpass[0] == '\0') {
235                         /* Need a password to proceed */
236                         result = PAM_AUTHTOK_ERR;
237                         goto out;
238                 }
239
240                 result = dce_changepw(user, oldpass, newpass);
241
242                 if (result == PAM_SUCCESS) {
243                 /* might need to update PAM_OLDAUTHTOK and PAM_NEWAUTHTOK */
244                 }
245                 goto out;
246         }
247
248
249         /* prompt for both old and new passwords */
250
251         if (firstpass == NULL && !(try_first_pass||try_mapped_pass))
252                 oldpass = get_passwd(pamh, PAM_MSG(pamh, 20,
253                                                 "Old DCE password: "));
254         else
255                 oldpass = get_passwd(pamh, PAM_MSG(pamh, 20,
256                                                 "Old DCE password: "));
257
258         if (oldpass == NULL || oldpass[0] == '\0') {
259                 /* Need a password to proceed */
260                 result = PAM_AUTHTOK_ERR;
261                 goto out;
262         }
263
264         if (firstpass == NULL && !(try_first_pass||try_mapped_pass))
265                 newpass = get_passwd(pamh, PAM_MSG(pamh, 21,
266                                                 "New DCE password: "));
267         else
268                 newpass = get_passwd(pamh, PAM_MSG(pamh, 21,
269                                                 "New DCE password: "));
270
271         if (newpass == NULL || newpass[0] == '\0') {
272                 /* Need a password to proceed */
273                 result = PAM_AUTHTOK_ERR;
274                 goto out;
275         }
276
277         if (firstpass == NULL && !(try_first_pass||try_mapped_pass))
278                 vnewpass = get_passwd(pamh,
279                                         PAM_MSG(pamh, 22,
280                                         "Re-enter new DCE password: "));
281         else
282                 vnewpass = get_passwd(pamh,
283                                         PAM_MSG(pamh, 22,
284                                         "Re-enter new DCE password: "));
285
286         if (vnewpass == NULL || vnewpass[0] == '\0') {
287                 /* Need a password to proceed */
288                 result = PAM_AUTHTOK_ERR;
289                 goto out;
290         }
291
292         if (strcmp(newpass, vnewpass)) {
293                 result = PAM_AUTHTOK_ERR;
294                 goto out;
295         }
296
297         result = dce_changepw(user, oldpass, newpass);
298
299         if (result == PAM_SUCCESS) {
300                 /* might need to update PAM_OLDAUTHTOK and PAM_NEWAUTHTOK) */
301         }
302 out:
303
304         if (oldpass) {
305                 memset(oldpass, 0, strlen(oldpass));
306                 free(oldpass);
307         }
308
309         if (newpass) {
310                 memset(newpass, 0, strlen(newpass));
311                 free(newpass);
312         }
313
314
315         if (vnewpass) {
316                 memset(vnewpass, 0, strlen(vnewpass));
317                 free(vnewpass);
318         }
319
320         return (result);
321 }
322
323 static int
324 dce_changepw(
325         char *account_name,
326         char *old_pass,
327         char *new_pass)
328 {
329         error_status_t          status;
330         sec_rgy_handle_t        rgy_handle = sec_rgy_default_handle;
331         sec_rgy_login_name_t    name_key;
332         sec_rgy_cursor_t        account_cursor;
333         sec_rgy_login_name_t    name_result;
334         sec_rgy_sid_t           id_sid;
335         sec_rgy_unix_sid_t      unix_sid;
336         sec_rgy_acct_key_t      key_parts;
337         sec_rgy_acct_user_t     user_part;
338         sec_rgy_acct_admin_t    admin_part;
339         sec_passwd_rec_t        new_key, caller_key;
340         sec_passwd_version_t    new_key_version;
341         sec_login_handle_t      login_context = sec_login_default_handle;
342         sec_passwd_rec_t        passwd_rec;
343         boolean32               reset_pass = 0, login_valid = 0;
344         sec_login_auth_src_t    auth_src;
345         char                    *env;
346         static char              *krb5 = "KRB5CCNAME";
347         char                    *krb5_value = NULL;
348
349         /* stash away the value of KRB5CCNAME, if there is one set */
350         env = getenv(krb5);
351         if (env) {
352                 krb5_value = malloc(strlen(krb5)+1+strlen(env)+1);
353                 if (krb5_value)
354                         sprintf(krb5_value, "%s=%s", krb5, env);
355                 else {
356                         status = sec_login_s_no_current_context;
357                         goto out;
358                 }
359         }
360
361         /* first we a get a login context. A future version should */
362         /* check and see if there is a current context with the */
363         /* same name as the user we are setting */
364
365         if (!sec_login_setup_identity((unsigned_char_p_t) account_name,
366                 sec_login_no_flags, &login_context, &status)) {
367                         goto out;
368         }
369
370         /* have to strdup password because the call clears it */
371         passwd_rec.key.tagged_union.plain =
372                         (idl_char *) strdup(old_pass);
373         if (passwd_rec.key.tagged_union.plain == NULL) {
374                 status = sec_login_s_no_current_context;
375                 goto out;
376         }
377
378         passwd_rec.key.key_type = sec_passwd_plain;
379         passwd_rec.pepper = NULL;
380         passwd_rec.version_number = sec_passwd_c_version_none;
381
382         login_valid = sec_login_validate_identity(login_context,
383                     &passwd_rec, &reset_pass, &auth_src, &status);
384
385         free(passwd_rec.key.tagged_union.plain); /* already memset */
386
387         if (!login_valid)
388                 goto out;
389
390         sec_login_set_context(login_context, &status);
391
392         if (status != error_status_ok)
393                 goto out;
394
395         /* we now have a context with the same account as the one */
396         /* we are trying to change the password on. Lets talk to the */
397         /* registry... */
398
399         sec_rgy_site_open(NULL, &rgy_handle, &status);
400
401         if (status != error_status_ok) {
402                 goto out;
403         }
404
405         caller_key.key.key_type = sec_passwd_plain;
406         caller_key.pepper = NULL;
407         caller_key.version_number = sec_passwd_c_version_none;
408         caller_key.key.tagged_union.plain = (idl_char *) old_pass;
409
410         sec_rgy_cursor_reset(&account_cursor);
411
412         strcpy((char *)name_key.pname, account_name);
413         name_key.gname[0] = '\0';
414         name_key.oname[0] = '\0';
415
416         /* need to do this in order to get the org and group for the */
417         /* account. All we want is name_result... */
418
419         sec_rgy_acct_lookup(
420                 rgy_handle,
421                 &name_key,
422                 &account_cursor,
423                 &name_result,
424                 &id_sid,
425                 &unix_sid,
426                 &key_parts,
427                 &user_part,
428                 &admin_part,
429                 &status);
430
431         if (status != error_status_ok)
432                 goto out;
433
434         caller_key.key.key_type = sec_passwd_plain;
435         caller_key.pepper = NULL;
436         caller_key.version_number = sec_passwd_c_version_none;
437         caller_key.key.tagged_union.plain = (idl_char *) old_pass;
438
439         new_key.key.key_type = sec_passwd_plain;
440         new_key.pepper = NULL;
441         new_key.version_number = sec_passwd_c_version_none;
442         new_key.key.tagged_union.plain = (idl_char *) new_pass;
443
444         /* rock and roll. lets try to update the password... */
445
446         sec_rgy_acct_passwd(
447                 rgy_handle,
448                 &name_result,
449                 &caller_key,
450                 &new_key,
451                 sec_passwd_des,
452                 &new_key_version,
453                 &status);
454
455 out:
456         /* restore the old KRB5CCNAME value */
457         if (krb5_value) {
458                 putenv(krb5_value);
459         }
460
461         if (rgy_handle != sec_rgy_default_handle) {
462                 error_status_t  st; /* don't trash status */
463
464                 sec_rgy_site_close(rgy_handle, &st);
465         }
466
467         if (login_context != sec_login_default_handle) {
468                 error_status_t  st; /* don't trash status */
469
470                 sec_login_purge_context(&login_context, &st);
471         }
472
473         if (status == error_status_ok) {
474                 return (PAM_SUCCESS);
475         } else {
476                 return (PAM_AUTHTOK_ERR);
477         }
478 }
479
480
481
482 static char *
483 get_passwd(
484         pam_handle_t *pamh,
485         char *prompt)
486 {
487         int                     err;
488         char                    *p;
489
490         err = __pam_get_authtok(pamh, PAM_PROMPT, 0, 256, prompt, &p);
491
492         if (err != PAM_SUCCESS) {
493                 return (NULL);
494         }
495
496         return (p);
497 }