2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
23 /* $XConsortium: unix_update_authtok_nisplus.c /main/5 1996/05/09 04:36:35 drk $ */
26 * Copyright (c) 1992-1995, by Sun Microsystems, Inc.
27 * All rights reserved.
30 #ident "@(#)unix_update_authtok_nisplus.c 1.55 96/02/02 SMI"
32 #include "unix_headers.h"
35 static int update_attr(pam_handle_t *, char *, char **, char *,
36 int, char **, char **,
37 struct passwd *, int, int,
39 static int talk_to_npd(pam_handle_t *, char *, char **, char *,
40 char *, char *, char *,
41 struct passwd *, int, int,
43 nis_result *, int, int);
44 static char *reencrypt_secret(char *, char *, char *);
45 static nis_error revert2oldpasswd(char *, nis_result *);
48 update_authtok_nisplus(
52 char *data[], /* Depending on field: it can store */
53 /* encrypted new passwd or new */
55 char *old, /* old passwd: clear version */
56 char *oldrpc, /* old rpc passwd: clear version */
57 char *new, /* new passwd: clear version */
58 int opwcmd, /* old passwd cmd: nispasswd */
59 struct passwd *nisplus_pwd,
62 nis_result *passwd_res,
67 char tmpcryptsecret[HEXKEYBYTES+KEYCHECKSUMSIZE+1];
68 char *newcryptsecret = NULL;
72 char mname[NIS_MAXNAMELEN];
78 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
80 char *gecos = NULL, *shell = NULL;
84 if ((rc = pam_get_item(pamh, PAM_SERVICE, (void **)&prognamep))
86 (rc = pam_get_item(pamh, PAM_USER, (void **)&usrname))
91 * Passwd is setuid program. We want the real user to send out
92 * any nis+ requests. The correct identity should have been set
93 * in ck_perm() when checking privilege.
97 "the effective uid while updating NIS+ passwd is %d",
101 if (opwcmd == FALSE) {
103 * Attempt to let NIS+ NPD do the password update.
104 * If the passwd entry is not present (in passwd_res)
105 * try NPD for the local domain.
106 * If the passwd entry is present call NPD in the
107 * domain the passwd entry resides in.
108 * NPD wants only the domainname so strip off
109 * the org_dir portion of the passwd directory.
111 if (passwd_res == NULL || passwd_res->status != NIS_SUCCESS) {
114 * Should never get here; ck_perm() should fail.
116 * It is a waste of time to try NPD for some values
117 * of passwd_res->status; additional checks advised
118 * if ever it is possible to get here.
120 rc = talk_to_npd(pamh, field, data, domain, usrname,
121 old, new, nisplus_pwd, failover, privileged,
122 &shell, &gecos, passwd_res, debug, nowarn);
124 pwd_domain = NIS_RES_OBJECT(passwd_res)->zo_domain;
125 if (strcmp(nis_leaf_of(pwd_domain), "org_dir") == 0) {
126 pwd_domain = nis_domain_of(
127 NIS_RES_OBJECT(passwd_res)->zo_domain);
130 rc = talk_to_npd(pamh, field, data, pwd_domain, usrname,
131 old, new, nisplus_pwd, failover, privileged,
132 &shell, &gecos, passwd_res, debug, nowarn);
135 if (rc == PAM_SUCCESS || rc == PAM_NISPLUS_PARTIAL_SUCCESS) {
136 sprintf(messages[0], PAM_MSG(pamh, 108,
137 "NIS+ password information changed for %s"),
139 (void) __pam_display_msg(pamh, PAM_TEXT_INFO,
142 if (rc == PAM_SUCCESS) {
143 sprintf(messages[0], PAM_MSG(pamh, 109,
144 "NIS+ credential information changed for %s"),
146 (void) __pam_display_msg(
147 pamh, PAM_TEXT_INFO, 1,
150 return (PAM_SUCCESS);
152 /* failover to use old protocol */
155 "Failed to use new passwd update protocol");
158 * There are two reasons we will get here:
159 * 1. passwd, shell, gecos update failed (true failover)
160 * 2. we are updating passwd attrs other than the above
161 * three attrs. In this case, rc is equal to PAM_PERM_DENIED
162 * (i.e. attrs not supported by new protocol)
167 if (strcmp(field, "passwd") == 0) {
169 * Old style nisplus update
171 * Obtain the old aging information. And modify, if need be,
172 * on top. At least the lstchg field needs to be changed.
174 /* old protocol requires user credential info */
175 if (cred_res == NULL || cred_res->status != NIS_SUCCESS) {
176 syslog(LOG_ERR, "%s%s: %s", prognamep, NISPLUS_MSG,
177 "Failover: user credential is required.");
178 return (PAM_AUTHTOK_RECOVERY_ERR);
181 nisplus_populate_age(NIS_RES_OBJECT(passwd_res), &sp);
183 (void) memcpy(tmpcryptsecret, curcryptsecret,
184 HEXKEYBYTES + KEYCHECKSUMSIZE + 1);
186 /* same user check? */
187 if ((!privileged) && (newcryptsecret = reencrypt_secret
188 (tmpcryptsecret, oldrpc, new)) == NULL) {
189 sprintf(messages[0], " ");
190 sprintf(messages[1], PAM_MSG(pamh, 110,
191 "Unable to reencrypt NIS+ credentials for %s;"),
193 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
195 return (PAM_AUTHTOK_RECOVERY_ERR);
198 /* update passwd at server */
199 (void) memset((char *)ecol, 0, sizeof (ecol));
200 ecol[1].ec_value.ec_value_val = *data;
201 ecol[1].ec_value.ec_value_len = strlen(*data) + 1;
202 ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED;
204 /* update last change field */
205 sp.sp_lstchg = DAY_NOW;
206 if (sp.sp_max == 0) {
207 /* passwd was forced to changed: turn off aging */
212 /* prepare shadow column */
213 if (sp.sp_expire == -1) {
214 sprintf(shadow, "%ld:%ld:%ld:%ld:%ld::%lu",
222 sprintf(shadow, "%ld:%ld:%ld:%ld:%ld:%ld:%lu",
231 ecol[7].ec_value.ec_value_val = shadow;
232 ecol[7].ec_value.ec_value_len = strlen(shadow) + 1;
233 ecol[7].ec_flags = EN_CRYPT|EN_MODIFIED;
236 * build entry based on the one we got back from the server
238 eobj = nis_clone_object(NIS_RES_OBJECT(passwd_res), NULL);
240 syslog(LOG_ERR, "%s%s: %s", prognamep, NISPLUS_MSG,
241 "clone object failed");
242 return (PAM_AUTHTOK_RECOVERY_ERR);
244 eobj->EN_data.en_cols.en_cols_val = ecol;
245 eobj->EN_data.en_cols.en_cols_len = 8;
247 /* strlen("[name=],.") + null + "." = 11 */
248 if ((strlen(usrname) +
249 strlen(NIS_RES_OBJECT(passwd_res)->zo_name) +
250 strlen(NIS_RES_OBJECT(passwd_res)->zo_domain) + 11) >
251 (size_t) NIS_MAXNAMELEN) {
252 syslog(LOG_ERR, "%s%s: %s", prognamep, NISPLUS_MSG,
253 "NIS+ name too long");
254 return (PAM_BUF_ERR);
256 sprintf(mname, "[name=%s],%s.%s", usrname,
257 NIS_RES_OBJECT(passwd_res)->zo_name,
258 NIS_RES_OBJECT(passwd_res)->zo_domain);
259 if (mname[strlen(mname) - 1] != '.')
260 (void) strcat(mname, ".");
261 mres = nis_modify_entry(mname, eobj, 0);
264 * It is possible that we have permission to modify the
265 * encrypted password but not the shadow column in the
266 * NIS+ table. In this case, we should try updating only
267 * the password field and not the aging stuff (lstchg).
268 * With the current NIS+ passwd table format, this would
269 * be the case most of the times.
271 if (mres->status == NIS_PERMISSION) {
272 ecol[7].ec_flags = 0;
273 mres = nis_modify_entry(mname, eobj, 0);
274 if (mres->status != NIS_SUCCESS) {
275 sprintf(messages[0], PAM_MSG(pamh, 111,
276 "%s%s: Password information update failed"),
277 prognamep, NISPLUS_MSG);
278 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
281 (void) nis_freeresult(mres);
282 return (PAM_AUTHTOK_RECOVERY_ERR);
285 /* set column stuff to NULL so that we can free eobj */
286 eobj->EN_data.en_cols.en_cols_val = NULL;
287 eobj->EN_data.en_cols.en_cols_len = 0;
288 (void) nis_destroy_object(eobj);
289 (void) nis_freeresult(mres);
291 sprintf(messages[0], PAM_MSG(pamh, 112,
292 "NIS+ password information changed for %s"),
294 (void) __pam_display_msg(pamh, PAM_TEXT_INFO,
298 sprintf(messages[0], " ");
299 sprintf(messages[1], PAM_MSG(pamh, 113,
300 "The NIS+ credential information for %s will not be changed."),
302 sprintf(messages[2], PAM_MSG(pamh, 114,
303 "User %s must do the following to update his/her"), usrname);
304 sprintf(messages[3], PAM_MSG(pamh, 115,
305 "credential information:"));
306 sprintf(messages[4], PAM_MSG(pamh, 116,
307 "Use NEW passwd for login and OLD passwd for keylogin."));
308 sprintf(messages[5], PAM_MSG(pamh, 117,
309 "Use \"chkey -p\" to reencrypt the credentials with the"));
310 sprintf(messages[6], PAM_MSG(pamh, 118,
311 "new login passwd."));
312 sprintf(messages[7], PAM_MSG(pamh, 119,
313 "The user must keylogin explicitly after their next login."),
315 sprintf(messages[8], " ");
316 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
319 return (PAM_SUCCESS);
322 /* update cred at server */
323 (void) memset((char *)ecol, 0, sizeof (ecol));
324 ecol[4].ec_value.ec_value_val = newcryptsecret;
325 ecol[4].ec_value.ec_value_len = strlen(newcryptsecret) + 1;
326 ecol[4].ec_flags = EN_CRYPT|EN_MODIFIED;
327 eobj = nis_clone_object(NIS_RES_OBJECT(cred_res), NULL);
329 syslog(LOG_ERR, "%s%s: %s", prognamep, NISPLUS_MSG,
330 "clone object failed");
331 return (PAM_AUTHTOK_RECOVERY_ERR);
333 eobj->EN_data.en_cols.en_cols_val = ecol;
334 eobj->EN_data.en_cols.en_cols_len = 5;
337 * Now, if one were stupid enough to run nispasswd as/for root
338 * on some machine, it would have looked up and modified
339 * the password entry for "root" in passwd.org_dir. Now,
340 * should we really apply this new password to the cred
341 * entry for "<machinename>.<domainname>" ?
343 * POLICY: NO. We have no way of identifying a root user in
344 * NIS+ passwd table for each root@machinename. We do not
345 * allow the one password for [name=root], passwd.org_dir
346 * to apply to all "<machinename>.<domainname>" principals.
347 * If somebody let a root entry in passwd table, it probably
348 * has modify permissions for a distinguished NIS+ principal
349 * which we let be associated only with NIS+ principal
350 * root.<domainname>. Does this make any sense ?
353 /* strlen("[cname=.,auth_type=DES],.") + null + "." = 26 */
354 if ((strlen(ENTRY_VAL(NIS_RES_OBJECT(cred_res), 0)) +
355 strlen(NIS_RES_OBJECT(cred_res)->zo_name) +
356 strlen(NIS_RES_OBJECT(cred_res)->zo_domain) + 26) >
357 (size_t) NIS_MAXNAMELEN) {
358 syslog(LOG_ERR, "%s%s: %s", prognamep, NISPLUS_MSG,
359 "NIS+ name too long");
360 return (PAM_BUF_ERR);
362 sprintf(mname, "[cname=%s,auth_type=DES],%s.%s",
363 ENTRY_VAL(NIS_RES_OBJECT(cred_res), 0),
364 NIS_RES_OBJECT(cred_res)->zo_name,
365 NIS_RES_OBJECT(cred_res)->zo_domain);
366 if (mname[strlen(mname) - 1] != '.')
367 (void) strcat(mname, ".");
368 mres = nis_modify_entry(mname, eobj, 0);
369 if (mres->status != NIS_SUCCESS) {
371 /* attempt to revert back to the old passwd */
372 niserr = revert2oldpasswd(usrname, passwd_res);
374 if (niserr != NIS_SUCCESS) {
375 sprintf(messages[0], "");
376 sprintf(messages[1], PAM_MSG(pamh, 120,
377 "WARNING: Could not reencrypt NIS+ credentials for %s;"),
379 sprintf(messages[2], PAM_MSG(pamh, 121,
380 "login and keylogin passwords differ."));
381 sprintf(messages[3], PAM_MSG(pamh, 122,
382 "Use NEW passwd for login and OLD passwd for keylogin."));
383 sprintf(messages[4], PAM_MSG(pamh, 117,
384 "Use \"chkey -p\" to reencrypt the credentials with the"));
385 sprintf(messages[5], PAM_MSG(pamh, 118,
386 "new login passwd."));
387 sprintf(messages[6], PAM_MSG(pamh, 123,
388 "You must keylogin explicitly after your next login."));
389 sprintf(messages[7], "");
390 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
393 return (PAM_AUTHTOK_RECOVERY_ERR);
396 sprintf(messages[0], PAM_MSG(pamh, 124,
397 "%s%s: couldn't change password for %s."),
398 prognamep, NISPLUS_MSG, usrname);
399 sprintf(messages[1], PAM_MSG(pamh, 125,
400 "Reason: failed to update the cred table with reencrypted credentials."));
401 sprintf(messages[2], PAM_MSG(pamh, 126,
402 "Please notify your System Administrator."));
403 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
406 (void) nis_freeresult(mres);
407 return (PAM_AUTHTOK_RECOVERY_ERR);
409 /* set column stuff to NULL so that we can free eobj */
410 eobj->EN_data.en_cols.en_cols_val = NULL;
411 eobj->EN_data.en_cols.en_cols_len = 0;
412 (void) nis_destroy_object(eobj);
413 (void) nis_freeresult(mres);
415 sprintf(messages[0], PAM_MSG(pamh, 109,
416 "NIS+ credential information changed for %s"),
418 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
421 return (update_attr(pamh, field, data, usrname, 1, &shell,
422 &gecos, nisplus_pwd, failover, privileged,
423 passwd_res, nowarn));
425 return (PAM_SUCCESS);
430 * The function uses the new protocol to update passwd attributes via
434 talk_to_npd(pam_handle_t *pamh, char *field, char **data, char *domain,
435 char *user, char *oldpass, char *newpass,
436 struct passwd *nisplus_pwd, int failover, int privileged,
437 char **shell, char **gecos,
438 nis_result *passwd_res, int debug, int nowarn)
441 char *old_passwd = NULL;
442 char srv_pubkey[HEXKEYBYTES + 1];
443 char u_pubkey[HEXKEYBYTES + 1];
444 char u_seckey[HEXKEYBYTES + 1];
446 unsigned long ident = 0, randval = 0;
447 int error = 0, status, srv_keysize = HEXKEYBYTES + 1;
448 int retcode = PAM_SYSTEM_ERR;
449 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
450 nispasswd_error *errlist = NULL;
451 nispasswd_error *p = NULL;
453 if (user == NULL || domain == NULL || *domain == '\0') {
454 retcode = PAM_AUTHTOK_RECOVERY_ERR;
459 syslog(LOG_DEBUG, "domain=%s, user=%s", domain, user);
462 * Let's do a quick check whether the attrs are really of interest.
463 * We don't want to prompt for user passwd which is sure not to be
466 retcode = update_attr(pamh, field, data, NULL, 0, NULL, NULL,
467 nisplus_pwd, failover, privileged,
469 if (retcode != PAM_SUCCESS)
472 if (oldpass == NULL) {
474 * This is possible from unix_set_authtokattr().
475 * Old passwd is required to change any attributes.
476 * This is imposed by new protocol to support users
477 * without credentials.
480 retcode = __pam_get_authtok(pamh, PAM_PROMPT, 0, PASSWORD_LEN,
481 PAM_MSG(pamh, 63, "Enter login(NIS+) password: "),
483 if (retcode != PAM_SUCCESS)
486 old_passwd = strdup(oldpass);
487 if (old_passwd == NULL) {
488 retcode = PAM_BUF_ERR;
493 /* get gecos, shell and other */
494 retcode = update_attr(pamh, field, data, user, 0, shell, gecos,
495 nisplus_pwd, failover, privileged,
497 if (retcode != PAM_SUCCESS)
500 if (npd_makeclnthandle(domain, &clnt, srv_pubkey, srv_keysize) ==
503 "Couldn't make a client handle to NIS+ password daemon");
504 retcode = PAM_AUTHTOK_RECOVERY_ERR;
508 /* again: doesn't need to generate a new pair of keys */
509 /* generate a key-pair for this user */
510 (void) __gen_dhkeys(u_pubkey, u_seckey, old_passwd);
513 * get the common des key from the servers' pubkey and
514 * the users secret key
516 if (__get_cmnkey(srv_pubkey, u_seckey, &deskey) == FALSE) {
517 syslog(LOG_ALERT, "Couldn't get a common DES key");
518 retcode = PAM_AUTHTOK_RECOVERY_ERR;
522 status = nispasswd_auth(user, domain, old_passwd, u_pubkey, &deskey,
523 clnt, &ident, &randval, &error);
524 if (status == NPD_FAILED) {
528 "Password update daemon is not running with NIS+ master server");
529 retcode = PAM_AUTHTOK_RECOVERY_ERR;
532 syslog(LOG_ALERT, "NIS+ system error");
533 retcode = PAM_AUTHTOK_RECOVERY_ERR;
535 case NPD_IDENTINVALID:
536 syslog(LOG_ALERT, "NIS+ identifier invalid");
537 retcode = PAM_AUTHTOK_RECOVERY_ERR;
539 case NPD_PASSINVALID:
540 syslog(LOG_ALERT, "NIS+ password invalid");
541 retcode = PAM_AUTHTOK_RECOVERY_ERR;
543 case NPD_NOSUCHENTRY:
544 syslog(LOG_ALERT, "No NIS+ password entry for %s",
546 retcode = PAM_AUTHTOK_RECOVERY_ERR;
549 syslog(LOG_ALERT, "NIS+ error");
550 retcode = PAM_AUTHTOK_RECOVERY_ERR;
552 case NPD_CKGENFAILED:
554 "Couldn't generate a common DES key");
555 retcode = PAM_AUTHTOK_RECOVERY_ERR;
558 syslog(LOG_ALERT, "No NIS+ password for %s", user);
559 retcode = PAM_AUTHTOK_RECOVERY_ERR;
562 syslog(LOG_ALERT, "NIS+ passwd has not aged enough");
563 retcode = PAM_AUTHTOK_RECOVERY_ERR;
566 syslog(LOG_ALERT, "No shadow password information");
567 retcode = PAM_AUTHTOK_RECOVERY_ERR;
570 syslog(LOG_ALERT, "NIS+ fatal error: %d", error);
571 retcode = PAM_AUTHTOK_RECOVERY_ERR;
575 if (status == NPD_TRYAGAIN) {
577 * call nispasswd_auth() again after getting another
578 * passwd. Note that ident is now non-zero.
582 "status=tryagain; ident=%ld, randval=%ld",
586 memset(old_passwd, 0, strlen(old_passwd));
590 /* wrong passwd: get auth token again */
591 sprintf(messages[0], PAM_MSG(pamh, 127,
592 "NIS+ Password incorrect: try again"));
593 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
596 retcode = __pam_get_authtok(pamh, PAM_PROMPT, 0, PASSWORD_LEN,
597 PAM_MSG(pamh, 63, "Enter login(NIS+) password: "),
599 if (retcode != PAM_SUCCESS)
604 if (status == NPD_SUCCESS) {
605 /* send the new passwd & other changes */
608 "status=success; ident=%ld, randval=%ld",
610 if (newpass == NULL) {
612 * This is possible from unix_set_authtokattr().
613 * Just use the same passwd so that we have a
614 * meaningful passwd field.
616 newpass = old_passwd;
619 /* gecos and shell could be NULL if we just change passwd */
620 status = nispasswd_pass(clnt, ident, randval, &deskey,
621 newpass, *gecos, *shell, &error, &errlist);
623 if (status == NPD_FAILED) {
624 sprintf(messages[0], PAM_MSG(pamh, 128,
625 "NIS+ password information update failed \
626 while talking to NIS+ passwd daemon"));
627 (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
630 syslog(LOG_DEBUG, "error=%d", error);
631 retcode = PAM_AUTHTOK_RECOVERY_ERR;
635 * WHAT SHOULD BE DONE FOR THE PARTIAL SUCCESS CASE ??
636 * I'll just print out some messages
638 if (status == NPD_PARTIALSUCCESS) {
640 "Password information is partially updated.");
641 for (p = errlist; p != NULL; p = p->next) {
642 if (p->npd_field == NPD_GECOS) {
643 sprintf(messages[0], PAM_MSG(pamh, 129,
644 "GECOS information was not updated: check NIS+ permissions."));
645 (void) __pam_display_msg(pamh,
648 } else if (p->npd_field == NPD_SHELL) {
649 sprintf(messages[0], PAM_MSG(pamh, 130,
650 "SHELL information was not updated: check NIS+ permissions."));
651 (void) __pam_display_msg(pamh,
654 } else if (p->npd_field == NPD_SECRETKEY) {
655 sprintf(messages[0], PAM_MSG(pamh, 131,
656 "NIS+ Credential information was not updated."));
657 (void) __pam_display_msg(pamh,
662 /* check for collision with PAM_* return code */
663 (void) __npd_free_errlist(errlist);
664 retcode = PAM_NISPLUS_PARTIAL_SUCCESS;
667 (void) __npd_free_errlist(errlist);
669 retcode = PAM_SUCCESS;
672 memset(old_passwd, 0, strlen(old_passwd));
680 update_attr(pam_handle_t *pamh, char *field, char **data, char *usrname,
681 int opwcmd, char **sh_p, char **gecos_p,
682 struct passwd *nisplus_pwd, int failover, int privileged,
683 nis_result *passwd_res, int nowarn)
688 char mname[NIS_MAXNAMELEN];
689 struct spwd sp; /* new attr values in here */
694 static char lkstring[] = "*LK*"; /* ??? in header */
695 int flag = 0; /* any change in shadow column */
696 char **data_p = data;
701 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
703 if (strcmp(field, "attr") == 0) {
705 * Obtain the old aging information. And modify, if need be,
709 nisplus_populate_age(NIS_RES_OBJECT(passwd_res), &sp);
711 (void) memset((char *)ecol, 0, sizeof (ecol));
712 while (*data != NULL) {
713 /* AUTHTOK_DEL: not applicable */
715 /* check attribute: AUTHTOK_LK */
716 if ((value = attr_match("AUTHTOK_LK", *data))
718 /* new update protocol doesn't support this */
720 return (PAM_PERM_DENIED);
722 if (strcmp(value, "1") == 0) {
724 ecol[1].ec_value.ec_value_val =
726 ecol[1].ec_value.ec_value_len =
727 strlen(&lkstring[0]) + 1;
728 ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED;
731 ("AUTHTOK_EXP", data_p))) {
732 sp.sp_lstchg = DAY_NOW;
740 /* check attribute: AUTHTOK_EXP */
741 if ((value = attr_match("AUTHTOK_EXP", *data))
743 /* new update protocol doesn't support this */
745 return (PAM_PERM_DENIED);
747 if (strcmp(value, "1") == 0) {
748 /* expire password */
749 sp.sp_lstchg = (long) 0;
756 /* check attribute: AUTHTOK_MAXAGE */
757 if ((value = attr_match("AUTHTOK_MAXAGE", *data))
759 /* new update protocol doesn't support this */
761 return (PAM_PERM_DENIED);
764 maxdate = (int)atol(value);
766 ("AUTHTOK_MINAGE", data_p)) &&
769 if (maxdate == -1) { /* turn off aging */
772 } else if (sp.sp_max == -1)
773 sp.sp_lstchg = DAY_NOW;
781 /* check attribute: AUTHTOK_MINAGE */
782 if ((value = attr_match("AUTHTOK_MINAGE", *data))
784 /* new update protocol doesn't support this */
786 return (PAM_PERM_DENIED);
789 mindate = (int)atol(value);
791 ("AUTHTOK_MAXAGE", data_p)) &&
792 sp.sp_max == -1 && mindate != -1)
793 return (PAM_AUTHTOK_ERR);
800 /* check attribute: AUTHTOK_WARNDATE */
801 if ((value = attr_match("AUTHTOK_WARNDATE", *data))
803 /* new update protocol doesn't support this */
805 return (PAM_PERM_DENIED);
808 warndate = (int)atol(value);
809 if (sp.sp_max == -1 && warndate != -1)
810 return (PAM_AUTHTOK_ERR);
811 sp.sp_warn = warndate;
817 if ((value = attr_match("AUTHTOK_SHELL", *data))
819 /* see if quick check */
821 return (PAM_SUCCESS);
823 if (nisplus_pwd == NULL && opwcmd) {
825 sprintf(messages[0], PAM_MSG(pamh, 132,
827 (void) __pam_display_msg(pamh,
831 return (PAM_AUTHTOK_RECOVERY_ERR);
835 * If failover, we already got the shell info
836 * in "shell". Don't ask again.
841 newsh = getloginshell(pamh,
842 nisplus_pwd->pw_shell,
845 /* if NULL, shell unchanged */
847 return (PAM_SUCCESS);
849 if (opwcmd || failover) {
850 ecol[6].ec_value.ec_value_val = newsh;
851 ecol[6].ec_value.ec_value_len =
853 ecol[6].ec_flags = EN_MODIFIED;
860 if ((value = attr_match("AUTHTOK_HOMEDIR", *data))
862 /* new update protocol doesn't support this */
864 return (PAM_PERM_DENIED);
867 if (nisplus_pwd == NULL) {
869 sprintf(messages[0], PAM_MSG(pamh, 132,
871 (void) __pam_display_msg(pamh,
875 return (PAM_AUTHTOK_RECOVERY_ERR);
877 newhome = gethomedir(pamh, nisplus_pwd->pw_dir,
879 /* if NULL, homedir unchanged */
881 return (PAM_SUCCESS);
882 ecol[5].ec_value.ec_value_val = newhome;
883 ecol[5].ec_value.ec_value_len =
885 ecol[5].ec_flags = EN_MODIFIED;
890 if ((value = attr_match("AUTHTOK_GECOS", *data))
892 /* see if quick check */
894 return (PAM_SUCCESS);
896 /* finger information */
897 if (nisplus_pwd == NULL && opwcmd) {
899 sprintf(messages[0], PAM_MSG(pamh, 132,
901 (void) __pam_display_msg(pamh,
905 return (PAM_AUTHTOK_RECOVERY_ERR);
910 newgecos = getfingerinfo(pamh,
911 nisplus_pwd->pw_gecos, nowarn);
914 /* if NULL, gecos unchanged */
915 if (newgecos == NULL)
916 return (PAM_SUCCESS);
918 if (opwcmd || failover) {
919 ecol[4].ec_value.ec_value_val =
921 ecol[4].ec_value.ec_value_len =
922 strlen(newgecos) + 1;
923 ecol[4].ec_flags = EN_MODIFIED;
932 return (PAM_SUCCESS);
934 if (flag && opwcmd) {
935 /* prepare shadow column */
936 if (sp.sp_expire == -1) {
937 sprintf(shadow, "%ld:%ld:%ld:%ld:%ld::%lu",
945 sprintf(shadow, "%ld:%ld:%ld:%ld:%ld:%ld:%lu",
954 ecol[7].ec_value.ec_value_val = shadow;
955 ecol[7].ec_value.ec_value_len = strlen(shadow) + 1;
956 ecol[7].ec_flags = EN_CRYPT|EN_MODIFIED;
959 if (opwcmd || failover) {
960 eobj = nis_clone_object(NIS_RES_OBJECT(passwd_res),
963 syslog(LOG_ERR, "NIS+ clone object failed");
964 return (PAM_AUTHTOK_RECOVERY_ERR);
966 eobj->EN_data.en_cols.en_cols_val = ecol;
967 eobj->EN_data.en_cols.en_cols_len = 8;
969 /* strlen("[name=],.") + null + "." = 17 */
970 if ((strlen(usrname) +
971 strlen(NIS_RES_OBJECT(passwd_res)->zo_name) +
972 strlen(NIS_RES_OBJECT(passwd_res)->zo_domain) +
973 17) > (size_t) NIS_MAXNAMELEN) {
974 syslog(LOG_ERR, "NIS+ name too long");
975 return (PAM_BUF_ERR);
977 sprintf(mname, "[name=%s],%s.%s", usrname,
978 NIS_RES_OBJECT(passwd_res)->zo_name,
979 NIS_RES_OBJECT(passwd_res)->zo_domain);
980 if (mname[strlen(mname) - 1] != '.')
981 (void) strcat(mname, ".");
982 mres = nis_modify_entry(mname, eobj, 0);
983 if (mres->status != NIS_SUCCESS) {
984 sprintf(messages[0], PAM_MSG(pamh, 133,
985 "NIS+ password information update failed (update_attr)"));
986 (void) __pam_display_msg(pamh,
987 PAM_ERROR_MSG, 1, messages, NULL);
989 return (PAM_AUTHTOK_RECOVERY_ERR);
992 sprintf(messages[0], PAM_MSG(pamh, 112,
993 "NIS+ password information changed for %s"),
995 (void) __pam_display_msg(pamh, PAM_TEXT_INFO,
999 return (PAM_SUCCESS);
1003 * Return reencrypted secret key.
1004 * The first two if statements should always succeed as these tests
1005 * are also carried out in getnewpasswd().
1008 reencrypt_secret(char *oldsecret, char *oldpass, char *newpass)
1010 static char crypt[HEXKEYBYTES + KEYCHECKSUMSIZE + 1];
1012 if (xdecrypt(oldsecret, oldpass) == 0)
1013 return (NULL); /* cbc_crypt failed */
1015 if (memcmp(oldsecret, &(oldsecret[HEXKEYBYTES]), KEYCHECKSUMSIZE) != 0)
1016 return (NULL); /* didn't really decrypt */
1018 (void) memcpy(crypt, oldsecret, HEXKEYBYTES);
1019 (void) memcpy(crypt + HEXKEYBYTES, oldsecret, KEYCHECKSUMSIZE);
1020 crypt[HEXKEYBYTES + KEYCHECKSUMSIZE] = 0;
1022 if (xencrypt(crypt, newpass) == 0)
1023 return (NULL); /* cbc_crypt encryption failed */
1030 * Revert back to the old passwd
1033 revert2oldpasswd(char *usrname, nis_result *passwd_res)
1038 char mname[NIS_MAXNAMELEN];
1043 (void) memset((char *) ecol, 0, sizeof (ecol));
1048 ecol[1].ec_value.ec_value_val =
1049 ENTRY_VAL(NIS_RES_OBJECT(passwd_res), 1);
1050 ecol[1].ec_value.ec_value_len =
1051 ENTRY_LEN(NIS_RES_OBJECT(passwd_res), 1);
1052 ecol[1].ec_flags = EN_CRYPT|EN_MODIFIED;
1055 * build entry based on the global "passwd_res"
1057 eobj = nis_clone_object(NIS_RES_OBJECT(passwd_res), NULL);
1059 return (NIS_SYSTEMERROR);
1060 eobj->EN_data.en_cols.en_cols_val = ecol;
1061 eobj->EN_data.en_cols.en_cols_len = 8;
1063 sprintf(mname, "[name=%s],%s.%s", usrname,
1064 NIS_RES_OBJECT(passwd_res)->zo_name,
1065 NIS_RES_OBJECT(passwd_res)->zo_domain);
1067 mres = nis_modify_entry(mname, eobj, 0);
1068 return (mres->status);
1071 #endif /* PAM_NISPLUS */