ni_status ni2_mergedirprop(void *domain, ni_id *dir, const ni_name key, ni_namelist values) { /* mergeprop given a directory rather than a pathname */ ni_status ret; ni_property p; ni_namelist nl; ni_index where, whereval; int i; /* need to be talking to the master */ ni_needwrite(domain, 1); /* fetch list of property keys from directory */ NI_INIT(&nl); ret = ni_listprops(domain, dir, &nl); if (ret != NI_OK) { return ret; } /* check for existing property with this key */ where = ni_namelist_match(nl, key); ni_namelist_free(&nl); /* if property doesn't exist, create it */ if (where == NI_INDEX_NULL) { NI_INIT(&p); p.nip_name = ni_name_dup(key); p.nip_val = ni_namelist_dup(values); ret = ni_createprop(domain, dir, p, NI_INDEX_NULL); ni_prop_free(&p); return ret; } /* property exists: replace the existing values */ /* fetch existing namelist for this property */ NI_INIT(&nl); ret = ni_readprop(domain, dir, where, &nl); if (ret != NI_OK) { return ret; } /* merge new values */ for (i = 0; i < values.ni_namelist_len; i++) { whereval = ni_namelist_match(nl, values.ni_namelist_val[i]); if (whereval == NI_INDEX_NULL) { ni_namelist_insert(&nl, values.ni_namelist_val[i], NI_INDEX_NULL); } } /* write the new list back */ ret = ni_writeprop(domain, dir, where, nl); ni_namelist_free(&nl); return ret; }
ni_status ni2_createdirprop(void *domain, ni_id *dir, const ni_name key, ni_namelist values) { /* createprop given a directory rather than a pathname */ ni_status ret; ni_property p; ni_namelist nl; ni_index where; /* need to be talking to the master */ ni_needwrite(domain, 1); /* fetch list of property keys from directory */ NI_INIT(&nl); ret = ni_listprops(domain, dir, &nl); if (ret != NI_OK) { return ret; } /* check for existing property with this key */ where = ni_namelist_match(nl, key); ni_namelist_free(&nl); /* if property doesn't exist, create it */ if (where == NI_INDEX_NULL) { NI_INIT(&p); p.nip_name = ni_name_dup(key); p.nip_val = ni_namelist_dup(values); ret = ni_createprop(domain, dir, p, NI_INDEX_NULL); ni_prop_free(&p); return ret; } /* property exists: replace the existing values */ ret = ni_writeprop(domain, dir, where, values); return ret; }
/* * Change a user's password in NetInfo. */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { char *oldHash, *newHash; char *oldPassword = NULL, *newPassword = NULL; void *d; int status, isroot, tries, maxTries; int options = 0; int amChangingExpiredPassword; ni_id dir; ni_proplist pl; ni_property p; ni_namelist nl; int ni_uid, uid, secure, minlen, lifetime; ni_index where; struct pam_conv *appconv; struct pam_message msg, *pmsg; struct pam_response *resp; const char *cmiscptr = NULL; char *uname; char salt[9]; int i; amChangingExpiredPassword = flags & PAM_CHANGE_EXPIRED_AUTHTOK; status = pam_get_item(pamh, PAM_CONV, (void **) &appconv); if (status != PAM_SUCCESS) return status; status = pam_get_item(pamh, PAM_USER, (void **) &uname); if (status != PAM_SUCCESS) return status; if (uname == NULL) return PAM_USER_UNKNOWN; status = pam_get_item(pamh, PAM_OLDAUTHTOK, (void **) &oldPassword); if (status != PAM_SUCCESS) { return status; } if (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL) || pam_test_option(&options, PAM_OPT_TRY_FIRST_PASS, NULL)) { if (pam_get_item(pamh, PAM_AUTHTOK, (void **) &newPassword) != PAM_SUCCESS) newPassword = NULL; if (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL) && newPassword == NULL) return PAM_AUTHTOK_RECOVER_ERR; } d = domain_for_user(uname, NULL, &dir); if (d == (void *) NULL) { syslog(LOG_ERR, "user %s not found in NetInfo", uname); return PAM_USER_UNKNOWN; } /* * These should be configurable in NetInfo. */ secure = secure_passwords(); maxTries = secure ? 3 : 5; minlen = secure ? 8 : 5; /* * Read the passwd and uid from NetInfo. */ status = ni_lookupprop(d, &dir, "passwd", &nl); if (status == NI_NOPROP) nl.ni_namelist_len = 0; else if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo read failed: %s", ni_error(status)); return netinfo2PamStatus(status); } oldHash = NULL; if (nl.ni_namelist_len > 0) oldHash = nl.ni_namelist_val[0]; status = ni_lookupprop(d, &dir, "uid", &nl); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo read failed: %s", ni_error(status)); return netinfo2PamStatus(status); } ni_uid = -2; if (nl.ni_namelist_len > 0) ni_uid = atoi(nl.ni_namelist_val[0]); /* * See if I'm uid 0 on the master host for the user's NetInfo domain. */ isroot = is_root_on_master(d); uid = getuid(); if (isroot) { if (flags & PAM_PRELIM_CHECK) { /* Don't need old password. */ return PAM_SUCCESS; } } else if (uid != ni_uid) { ni_free(d); return PAM_PERM_DENIED; } if (flags & PAM_PRELIM_CHECK) { /* * If we are not root, we should verify the old * password. */ char *encrypted; if (oldPassword != NULL && (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL) || pam_test_option(&options, PAM_OPT_TRY_FIRST_PASS, NULL))) { encrypted = crypt(oldPassword, oldHash); if (oldPassword[0] == '\0' && oldHash != '\0') encrypted = ":"; status = strcmp(encrypted, oldHash) == 0 ? PAM_SUCCESS : PAM_AUTH_ERR; if (status != PAM_SUCCESS) { if (pam_test_option(&options, PAM_OPT_USE_FIRST_PASS, NULL)) sendConversationMessage(appconv, "NetInfo password incorrect", PAM_ERROR_MSG, &options); else sendConversationMessage(appconv, "NetInfo password incorrect: try again", PAM_ERROR_MSG, &options); } else { ni_free(d); return PAM_SUCCESS; } } tries = 0; while (oldPassword == NULL && tries++ < maxTries) { pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = OLD_PASSWORD_PROMPT; resp = NULL; status = appconv->conv(1, (struct pam_message **) & pmsg, &resp, appconv->appdata_ptr); if (status != PAM_SUCCESS) { ni_free(d); return status; } oldPassword = resp->resp; free(resp); encrypted = crypt(oldPassword, oldHash); if (oldPassword[0] == '\0' && oldHash != '\0') encrypted = ":"; status = strcmp(encrypted, oldHash) == 0 ? PAM_SUCCESS : PAM_AUTH_ERR; if (status != PAM_SUCCESS) { int abortMe = 0; if (oldPassword != NULL && oldPassword[0] == '\0') abortMe = 1; _pam_overwrite(oldPassword); _pam_drop(oldPassword); if (!amChangingExpiredPassword & abortMe) { sendConversationMessage(appconv, "Password change aborted", PAM_ERROR_MSG, &options); ni_free(d); return PAM_AUTHTOK_RECOVER_ERR; } else { sendConversationMessage(appconv, "NetInfo password incorrect: try again", PAM_ERROR_MSG, &options); } } } if (oldPassword == NULL) { status = PAM_MAXTRIES; } (void) pam_set_item(pamh, PAM_OLDAUTHTOK, oldPassword); ni_free(d); return status; } /* PAM_PRELIM_CHECK */ status = PAM_ABORT; tries = 0; while (newPassword == NULL && tries++ < maxTries) { pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = NEW_PASSWORD_PROMPT; resp = NULL; status = appconv->conv(1, &pmsg, &resp, appconv->appdata_ptr); if (status != PAM_SUCCESS) { ni_free(d); return status; } newPassword = resp->resp; free(resp); if (newPassword[0] == '\0') { free(newPassword); newPassword = NULL; } if (newPassword != NULL) { if (isroot == 0) { if (oldPassword != NULL && !strcmp(oldPassword, newPassword)) { cmiscptr = "Passwords must differ"; newPassword = NULL; } else if (strlen(newPassword) < minlen) { cmiscptr = "Password too short"; newPassword = NULL; } } } else { ni_free(d); return PAM_AUTHTOK_RECOVER_ERR; } if (cmiscptr == NULL) { /* get password again */ char *miscptr; pmsg = &msg; msg.msg_style = PAM_PROMPT_ECHO_OFF; msg.msg = AGAIN_PASSWORD_PROMPT; resp = NULL; status = appconv->conv(1, &pmsg, &resp, appconv->appdata_ptr); if (status != PAM_SUCCESS) { ni_free(d); return status; } miscptr = resp->resp; free(resp); if (miscptr[0] == '\0') { free(miscptr); miscptr = NULL; } if (miscptr == NULL) { if (!amChangingExpiredPassword) { sendConversationMessage(appconv, "Password change aborted", PAM_ERROR_MSG, &options); ni_free(d); return PAM_AUTHTOK_RECOVER_ERR; } } else if (!strcmp(newPassword, miscptr)) { miscptr = NULL; break; } sendConversationMessage(appconv, "You must enter the same password", PAM_ERROR_MSG, &options); miscptr = NULL; newPassword = NULL; } else { sendConversationMessage(appconv, cmiscptr, PAM_ERROR_MSG, &options); cmiscptr = NULL; newPassword = NULL; } } if (cmiscptr != NULL || newPassword == NULL) { ni_free(d); return PAM_MAXTRIES; } /* * Lock onto the master server. */ ni_needwrite(d, 1); /* * Authenticate if necessary */ if (isroot == 0) { ni_setuser(d, uname); ni_setpassword(d, oldPassword); } /* * Create a random salt */ srandom((int) time((time_t *) NULL)); salt[0] = saltchars[random() % strlen(saltchars)]; salt[1] = saltchars[random() % strlen(saltchars)]; salt[2] = '\0'; newHash = crypt(newPassword, salt); /* * Change the password in NetInfo. */ status = ni_read(d, &dir, &pl); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo read failed: %s", ni_error(status)); return netinfo2PamStatus(status); } p.nip_name = "passwd"; p.nip_val.ni_namelist_len = 1; p.nip_val.ni_namelist_val = (ni_name *) malloc(sizeof(ni_name)); p.nip_val.ni_namelist_val[0] = newHash; where = ni_proplist_match(pl, p.nip_name, NULL); if (where == NI_INDEX_NULL) status = ni_createprop(d, &dir, p, NI_INDEX_NULL); else status = ni_writeprop(d, &dir, where, p.nip_val); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo write property \"passwd\" failed: %s", ni_error(status)); return netinfo2PamStatus(status); } /* * Now, update "change" property. If this fails, we've still * updated the password... perhaps the user should be alerted * of this. */ lifetime = password_lifetime(); if (lifetime > 0) { struct timeval tp; char change[64]; where = ni_proplist_match(pl, "change", NULL); gettimeofday(&tp, NULL); tp.tv_sec += lifetime; snprintf(change, sizeof(change), "%ld", tp.tv_sec); p.nip_name = "change"; p.nip_val.ni_namelist_len = 1; p.nip_val.ni_namelist_val[0] = change; if (where == NI_INDEX_NULL) status = ni_createprop(d, &dir, p, NI_INDEX_NULL); else status = ni_writeprop(d, &dir, where, p.nip_val); if (status != NI_OK) { ni_free(d); syslog(LOG_ERR, "NetInfo write property \"change\" failed: %s", ni_error(status)); return netinfo2PamStatus(status); } } free(p.nip_val.ni_namelist_val); ni_free(d); /* tell lookupd to invalidate its cache */ { int i, proc = -1; unit lookup_buf[MAX_INLINE_UNITS]; #ifdef __NeXT__ port_t port; #else mach_port_t port; #endif port = _lookupd_port(0); (void) _lookup_link(port, "_invalidatecache", &proc); (void) _lookup_one(port, proc, NULL, 0, lookup_buf, &i); } return PAM_SUCCESS; }