/* * Fill out a krb5_db_entry princ entry struct given a LDAP message containing * the results of a principal search of the directory. */ krb5_error_code populate_krb5_db_entry(krb5_context context, krb5_ldap_context *ldap_context, LDAP *ld, LDAPMessage *ent, krb5_const_principal princ, krb5_db_entry *entry) { krb5_error_code ret; unsigned int mask = 0; int val, i, pcount, objtype; krb5_boolean attr_present; krb5_kvno mkvno = 0; krb5_timestamp lastpwdchange, unlock_time; char *policydn = NULL, *pwdpolicydn = NULL, *polname = NULL, *user = NULL; char *tktpolname = NULL, *dn = NULL, **link_references = NULL; char **pnvalues = NULL, **ocvalues = NULL, **a2d2 = NULL; struct berval **ber_key_data = NULL, **ber_tl_data = NULL; krb5_tl_data userinfo_tl_data = { NULL }, **endp, *tl; osa_princ_ent_rec princ_ent; memset(&princ_ent, 0, sizeof(princ_ent)); ret = krb5_copy_principal(context, princ, &entry->princ); if (ret) goto cleanup; /* get the associated directory user information */ pnvalues = ldap_get_values(ld, ent, "krbprincipalname"); if (pnvalues != NULL) { ret = krb5_unparse_name(context, princ, &user); if (ret) goto cleanup; pcount = 0; for (i = 0; pnvalues[i] != NULL; i++) { if (strcasecmp(pnvalues[i], user) == 0) { pcount = ldap_count_values(pnvalues); break; } } dn = ldap_get_dn(ld, ent); if (dn == NULL) { ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &ret); ret = set_ldap_error(context, ret, 0); goto cleanup; } ocvalues = ldap_get_values(ld, ent, "objectclass"); if (ocvalues != NULL) { for (i = 0; ocvalues[i] != NULL; i++) { if (strcasecmp(ocvalues[i], "krbprincipal") == 0) { objtype = KDB_STANDALONE_PRINCIPAL_OBJECT; ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE, &objtype); if (ret) goto cleanup; break; } } } /* Add principalcount, DN and principaltype user information to * tl_data */ ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount); if (ret) goto cleanup; ret = store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, dn); if (ret) goto cleanup; } ret = get_time(ld, ent, "krbLastSuccessfulAuth", &entry->last_success, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_LAST_SUCCESS_ATTR; ret = get_time(ld, ent, "krbLastFailedAuth", &entry->last_failed, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_LAST_FAILED_ATTR; if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount", &val) == 0) { entry->fail_auth_count = val; mask |= KDB_FAIL_AUTH_COUNT_ATTR; } if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &val) == 0) { entry->max_life = val; mask |= KDB_MAX_LIFE_ATTR; } if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", &val) == 0) { entry->max_renewable_life = val; mask |= KDB_MAX_RLIFE_ATTR; } if (krb5_ldap_get_value(ld, ent, "krbticketflags", &val) == 0) { entry->attributes = val; mask |= KDB_TKT_FLAGS_ATTR; } ret = get_time(ld, ent, "krbprincipalexpiration", &entry->expiration, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_PRINC_EXPIRE_TIME_ATTR; ret = get_time(ld, ent, "krbpasswordexpiration", &entry->pw_expiration, &attr_present); if (ret) goto cleanup; if (attr_present) mask |= KDB_PWD_EXPIRE_TIME_ATTR; ret = krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn, &attr_present); if (ret) goto cleanup; if (attr_present) { mask |= KDB_POL_REF_ATTR; /* Ensure that the policy is inside the realm container. */ ret = krb5_ldap_policydn_to_name(context, policydn, &tktpolname); if (ret) goto cleanup; } ret = krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn, &attr_present); if (ret) goto cleanup; if (attr_present) { mask |= KDB_PWD_POL_REF_ATTR; /* Ensure that the policy is inside the realm container. */ ret = krb5_ldap_policydn_to_name(context, pwdpolicydn, &polname); if (ret) goto cleanup; princ_ent.policy = polname; princ_ent.aux_attributes |= KADM5_POLICY; } ber_key_data = ldap_get_values_len(ld, ent, "krbpwdhistory"); if (ber_key_data != NULL) { mask |= KDB_PWD_HISTORY_ATTR; ret = krb5_decode_histkey(context, ber_key_data, &princ_ent); if (ret) goto cleanup; ldap_value_free_len(ber_key_data); } if (princ_ent.aux_attributes) { ret = krb5_update_tl_kadm_data(context, entry, &princ_ent); if (ret) goto cleanup; } ber_key_data = ldap_get_values_len(ld, ent, "krbprincipalkey"); if (ber_key_data != NULL) { mask |= KDB_SECRET_KEY_ATTR; ret = krb5_decode_krbsecretkey(context, entry, ber_key_data, &mkvno); if (ret) goto cleanup; if (mkvno != 0) { ret = krb5_dbe_update_mkvno(context, entry, mkvno); if (ret) goto cleanup; } } ret = get_time(ld, ent, "krbLastPwdChange", &lastpwdchange, &attr_present); if (ret) goto cleanup; if (attr_present) { ret = krb5_dbe_update_last_pwd_change(context, entry, lastpwdchange); if (ret) goto cleanup; mask |= KDB_LAST_PWD_CHANGE_ATTR; } ret = get_time(ld, ent, "krbLastAdminUnlock", &unlock_time, &attr_present); if (ret) goto cleanup; if (attr_present) { ret = krb5_dbe_update_last_admin_unlock(context, entry, unlock_time); if (ret) goto cleanup; mask |= KDB_LAST_ADMIN_UNLOCK_ATTR; } a2d2 = ldap_get_values(ld, ent, "krbAllowedToDelegateTo"); if (a2d2 != NULL) { for (endp = &entry->tl_data; *endp; endp = &(*endp)->tl_data_next); for (i = 0; a2d2[i] != NULL; i++) { tl = k5alloc(sizeof(*tl), &ret); if (tl == NULL) goto cleanup; tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL; tl->tl_data_length = strlen(a2d2[i]); tl->tl_data_contents = (unsigned char *)strdup(a2d2[i]); if (tl->tl_data_contents == NULL) { ret = ENOMEM; free(tl); goto cleanup; } tl->tl_data_next = NULL; *endp = tl; endp = &tl->tl_data_next; } } link_references = ldap_get_values(ld, ent, "krbobjectreferences"); if (link_references != NULL) { for (i = 0; link_references[i] != NULL; i++) { ret = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN, link_references[i]); if (ret) goto cleanup; } } ber_tl_data = ldap_get_values_len(ld, ent, "krbExtraData"); if (ber_tl_data != NULL) { for (i = 0; ber_tl_data[i] != NULL; i++) { ret = berval2tl_data(ber_tl_data[i], &tl); if (ret) goto cleanup; ret = krb5_dbe_update_tl_data(context, entry, tl); free(tl->tl_data_contents); free(tl); if (ret) goto cleanup; } mask |= KDB_EXTRA_DATA_ATTR; } /* Auth indicators from krbPrincipalAuthInd will replace those from * krbExtraData. */ ret = get_ldap_auth_ind(context, ld, ent, entry, &mask); if (ret) goto cleanup; /* Update the mask of attributes present on the directory object to the * tl_data. */ ret = store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask); if (ret) goto cleanup; ret = krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data); if (ret) goto cleanup; ret = krb5_read_tkt_policy(context, ldap_context, entry, tktpolname); if (ret) goto cleanup; /* For compatibility with DB2 principals. */ entry->len = KRB5_KDB_V1_BASE_LENGTH; cleanup: ldap_memfree(dn); ldap_value_free_len(ber_key_data); ldap_value_free_len(ber_tl_data); ldap_value_free(pnvalues); ldap_value_free(ocvalues); ldap_value_free(link_references); ldap_value_free(a2d2); free(userinfo_tl_data.tl_data_contents); free(pwdpolicydn); free(polname); free(tktpolname); free(policydn); krb5_free_unparsed_name(context, user); free_princ_ent_contents(&princ_ent); return ret; }
krb5_error_code add_new_mkey(krb5_context context, krb5_db_entry *master_entry, krb5_keyblock *new_mkey, krb5_kvno use_mkvno) { krb5_error_code retval = 0; int old_key_data_count, i; krb5_kvno new_mkey_kvno; krb5_key_data tmp_key_data; krb5_mkey_aux_node *mkey_aux_data_head = NULL, **mkey_aux_data; krb5_keylist_node *keylist_node; krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(context); /* do this before modifying master_entry key_data */ new_mkey_kvno = get_next_kvno(context, master_entry); /* verify the requested mkvno if not 0 is the one that would be used here. */ if (use_mkvno != 0 && new_mkey_kvno != use_mkvno) return (KRB5_KDB_KVNONOMATCH); old_key_data_count = master_entry->n_key_data; /* alloc enough space to hold new and existing key_data */ /* * The encrypted key is malloc'ed by krb5_dbe_encrypt_key_data and * krb5_key_data key_data_contents is a pointer to this key. Using some * logic from master_key_convert(). */ master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * (old_key_data_count + 1)); if (master_entry->key_data == NULL) return (ENOMEM); memset(master_entry->key_data, 0, sizeof(krb5_key_data) * (old_key_data_count + 1)); master_entry->n_key_data = old_key_data_count + 1; /* Note, mkey does not have salt */ /* add new mkey encrypted with itself to mkey princ entry */ if ((retval = krb5_dbe_encrypt_key_data(context, new_mkey, new_mkey, NULL, (int) new_mkey_kvno, &master_entry->key_data[0]))) { return (retval); } /* the mvkno should be that of the newest mkey */ if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) { krb5_free_key_data_contents(context, &master_entry->key_data[0]); return (retval); } /* * Need to decrypt old keys with the current mkey which is in the global * master_keyblock and encrypt those keys with the latest mkey. And while * the old keys are being decrypted, use those to create the * KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of * the older mkeys. * * The new mkey is followed by existing keys. * * First, set up for creating a krb5_mkey_aux_node list which will be used * to update the mkey aux data for the mkey princ entry. */ mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node)); if (mkey_aux_data_head == NULL) { retval = ENOMEM; goto clean_n_exit; } memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node)); mkey_aux_data = &mkey_aux_data_head; for (keylist_node = master_keylist, i = 1; keylist_node != NULL; keylist_node = keylist_node->next, i++) { /* * Create a list of krb5_mkey_aux_node nodes. One node contains the new * mkey encrypted by an old mkey and the old mkey's kvno (one node per * old mkey). */ if (*mkey_aux_data == NULL) { /* *mkey_aux_data points to next field of previous node */ *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node)); if (*mkey_aux_data == NULL) { retval = ENOMEM; goto clean_n_exit; } memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node)); } memset(&tmp_key_data, 0, sizeof(tmp_key_data)); /* encrypt the new mkey with the older mkey */ retval = krb5_dbe_encrypt_key_data(context, &keylist_node->keyblock, new_mkey, NULL, (int) new_mkey_kvno, &tmp_key_data); if (retval) goto clean_n_exit; (*mkey_aux_data)->latest_mkey = tmp_key_data; (*mkey_aux_data)->mkey_kvno = keylist_node->kvno; mkey_aux_data = &((*mkey_aux_data)->next); /* * Store old key in master_entry keydata past the new mkey */ retval = krb5_dbe_encrypt_key_data(context, new_mkey, &keylist_node->keyblock, NULL, (int) keylist_node->kvno, &master_entry->key_data[i]); if (retval) goto clean_n_exit; } assert(i == old_key_data_count + 1); if ((retval = krb5_dbe_update_mkey_aux(context, master_entry, mkey_aux_data_head))) { goto clean_n_exit; } master_entry->mask |= KADM5_KEY_DATA; clean_n_exit: krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head); return (retval); }