/** * This function adds an HDB entry's current keyset to the entry's key * history. The current keyset is left alone; the caller is responsible * for freeing it. * * @param context Context * @param entry HDB entry */ krb5_error_code hdb_add_current_keys_to_history(krb5_context context, hdb_entry *entry) { krb5_boolean replace = FALSE; krb5_error_code ret; HDB_extension *ext; HDB_Ext_KeySet *keys; hdb_keyset newkeyset; time_t newtime; if (entry->keys.len == 0) return 0; /* nothing to do */ ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys); if (ext == NULL) { replace = TRUE; ext = calloc(1, sizeof (*ext)); if (ext == NULL) return krb5_enomem(context); ext->data.element = choice_HDB_extension_data_hist_keys; } keys = &ext->data.u.hist_keys; ext->mandatory = FALSE; /* * Copy in newest old keyset */ ret = hdb_entry_get_pw_change_time(entry, &newtime); if (ret) goto out; memset(&newkeyset, 0, sizeof(newkeyset)); newkeyset.keys = entry->keys; newkeyset.kvno = entry->kvno; newkeyset.set_time = &newtime; ret = add_HDB_Ext_KeySet(keys, &newkeyset); if (ret) goto out; if (replace) { /* hdb_replace_extension() deep-copies ext; what a waste */ ret = hdb_replace_extension(context, entry, ext); if (ret) goto out; } ret = hdb_prune_keys(context, entry); if (ret) goto out; out: if (replace && ext) { free_HDB_extension(ext); free(ext); } return ret; }
/** * This function prunes an HDB entry's keys that are too old to have been used * to mint still valid tickets (based on the entry's maximum ticket lifetime). * * @param context Context * @param entry HDB entry */ krb5_error_code hdb_prune_keys(krb5_context context, hdb_entry *entry) { HDB_extension *ext; HDB_Ext_KeySet *keys; size_t nelem; ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys); if (ext == NULL) return 0; keys = &ext->data.u.hist_keys; nelem = keys->len; /* Optionally drop key history for keys older than now - max_life */ if (entry->max_life != NULL && nelem > 0 && krb5_config_get_bool_default(context, NULL, FALSE, "kadmin", "prune-key-history", NULL)) { hdb_keyset *elem; time_t ceiling = time(NULL) - *entry->max_life; time_t keep_time = 0; size_t i; /* * Compute most recent key timestamp that predates the current time * by at least the entry's maximum ticket lifetime. */ for (i = 0; i < nelem; ++i) { elem = &keys->val[i]; if (elem->set_time && *elem->set_time < ceiling && (keep_time == 0 || *elem->set_time > keep_time)) keep_time = *elem->set_time; } /* Drop obsolete entries */ if (keep_time) { for (i = 0; i < nelem; /* see below */) { elem = &keys->val[i]; if (elem->set_time && *elem->set_time < keep_time) { remove_HDB_Ext_KeySet(keys, i); /* * Removing the i'th element shifts the tail down, continue * at same index with reduced upper bound. */ --nelem; continue; } ++i; } } } return 0; }
/** * This function changes an hdb_entry's kvno, swapping the current key * set with a historical keyset. If no historical keys are found then * an error is returned (the caller can still set entry->kvno directly). * * @param context krb5_context * @param new_kvno New kvno for the entry * @param entry hdb_entry to modify */ krb5_error_code hdb_change_kvno(krb5_context context, krb5_kvno new_kvno, hdb_entry *entry) { HDB_extension ext; HDB_extension *extp; hdb_keyset keyset; HDB_Ext_KeySet *hist_keys; size_t i; int found = 0; krb5_error_code ret; if (entry->kvno == new_kvno) return 0; extp = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys); if (extp == NULL) { memset(&ext, 0, sizeof (ext)); ext.data.element = choice_HDB_extension_data_hist_keys; extp = &ext; } memset(&keyset, 0, sizeof (keyset)); hist_keys = &extp->data.u.hist_keys; for (i = 0; i < hist_keys->len; i++) { if (hist_keys->val[i].kvno == new_kvno) { found = 1; ret = copy_hdb_keyset(&hist_keys->val[i], &keyset); if (ret) goto out; ret = remove_HDB_Ext_KeySet(hist_keys, i); if (ret) goto out; break; } } if (!found) return HDB_ERR_KVNO_NOT_FOUND; ret = hdb_add_current_keys_to_history(context, entry); if (ret) goto out; /* Note: we do nothing with keyset.set_time */ entry->kvno = new_kvno; entry->keys = keyset.keys; /* shortcut */ memset(&keyset.keys, 0, sizeof (keyset.keys)); out: free_hdb_keyset(&keyset); return ret; }
krb5_error_code hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a) { const HDB_extension *ext; ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases); if (ext) *a = &ext->data.u.aliases; else *a = NULL; return 0; }
krb5_error_code hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t) { const HDB_extension *ext; ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change); if (ext) *t = ext->data.u.last_pw_change; else *t = 0; return 0; }
krb5_error_code hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a) { const HDB_extension *ext; ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash); if (ext) *a = &ext->data.u.pkinit_cert_hash; else *a = NULL; return 0; }
krb5_error_code hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry, const HDB_Ext_Constrained_delegation_acl **a) { const HDB_extension *ext; ext = hdb_find_extension(entry, choice_HDB_extension_data_allowed_to_delegate_to); if (ext) *a = &ext->data.u.allowed_to_delegate_to; else *a = NULL; return 0; }
/** * This function adds a key to an HDB entry's key history. * * @param context Context * @param entry HDB entry * @param kvno Key version number of the key to add to the history * @param key The Key to add */ krb5_error_code hdb_add_history_key(krb5_context context, hdb_entry *entry, krb5_kvno kvno, Key *key) { size_t i; hdb_keyset keyset; HDB_Ext_KeySet *hist_keys; HDB_extension ext; HDB_extension *extp; krb5_error_code ret; memset(&keyset, 0, sizeof (keyset)); memset(&ext, 0, sizeof (ext)); extp = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys); if (extp == NULL) { ext.data.element = choice_HDB_extension_data_hist_keys; extp = &ext; } extp->mandatory = FALSE; hist_keys = &extp->data.u.hist_keys; for (i = 0; i < hist_keys->len; i++) { if (hist_keys->val[i].kvno == kvno) { ret = add_Keys(&hist_keys->val[i].keys, key); goto out; } } keyset.kvno = kvno; ret = add_Keys(&keyset.keys, key); if (ret) goto out; ret = add_HDB_Ext_KeySet(hist_keys, &keyset); if (ret) goto out; if (extp == &ext) { ret = hdb_replace_extension(context, entry, &ext); if (ret) goto out; } out: free_hdb_keyset(&keyset); free_HDB_extension(&ext); return ret; }
const Keys * hdb_kvno2keys(krb5_context context, const hdb_entry *e, krb5_kvno kvno) { HDB_Ext_KeySet *hist_keys; HDB_extension *extp; size_t i; if (kvno == 0) return &e->keys; extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys); if (extp == NULL) return 0; hist_keys = &extp->data.u.hist_keys; for (i = 0; i < hist_keys->len; i++) { if (hist_keys->val[i].kvno == kvno) return &hist_keys->val[i].keys; } return NULL; }
kadm5_ret_t kadm5_s_get_principal(void *server_handle, krb5_principal princ, kadm5_principal_ent_t out, uint32_t mask) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; memset(&ent, 0, sizeof(ent)); if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0); if(ret) return ret; } ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_DECRYPT|HDB_F_ALL_KVNOS| HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if (!context->keep_open) context->db->hdb_close(context->context, context->db); if(ret) return _kadm5_error_code(ret); memset(out, 0, sizeof(*out)); if(mask & KADM5_PRINCIPAL) ret = krb5_copy_principal(context->context, ent.entry.principal, &out->principal); if(ret) goto out; if(mask & KADM5_PRINC_EXPIRE_TIME && ent.entry.valid_end) out->princ_expire_time = *ent.entry.valid_end; if(mask & KADM5_PW_EXPIRATION && ent.entry.pw_end) out->pw_expiration = *ent.entry.pw_end; if(mask & KADM5_LAST_PWD_CHANGE) hdb_entry_get_pw_change_time(&ent.entry, &out->last_pwd_change); if(mask & KADM5_ATTRIBUTES){ out->attributes |= ent.entry.flags.postdate ? 0 : KRB5_KDB_DISALLOW_POSTDATED; out->attributes |= ent.entry.flags.forwardable ? 0 : KRB5_KDB_DISALLOW_FORWARDABLE; out->attributes |= ent.entry.flags.initial ? KRB5_KDB_DISALLOW_TGT_BASED : 0; out->attributes |= ent.entry.flags.renewable ? 0 : KRB5_KDB_DISALLOW_RENEWABLE; out->attributes |= ent.entry.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE; out->attributes |= ent.entry.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0; out->attributes |= ent.entry.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0; out->attributes |= ent.entry.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR; out->attributes |= ent.entry.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0; out->attributes |= ent.entry.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0; out->attributes |= ent.entry.flags.trusted_for_delegation ? KRB5_KDB_TRUSTED_FOR_DELEGATION : 0; out->attributes |= ent.entry.flags.allow_kerberos4 ? KRB5_KDB_ALLOW_KERBEROS4 : 0; out->attributes |= ent.entry.flags.allow_digest ? KRB5_KDB_ALLOW_DIGEST : 0; } if(mask & KADM5_MAX_LIFE) { if(ent.entry.max_life) out->max_life = *ent.entry.max_life; else out->max_life = INT_MAX; } if(mask & KADM5_MOD_TIME) { if(ent.entry.modified_by) out->mod_date = ent.entry.modified_by->time; else out->mod_date = ent.entry.created_by.time; } if(mask & KADM5_MOD_NAME) { if(ent.entry.modified_by) { if (ent.entry.modified_by->principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.modified_by->principal, &out->mod_name); } else if(ent.entry.created_by.principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.created_by.principal, &out->mod_name); else out->mod_name = NULL; } if(ret) goto out; if(mask & KADM5_KVNO) out->kvno = ent.entry.kvno; if(mask & KADM5_MKVNO) { size_t n; out->mkvno = 0; /* XXX */ for(n = 0; n < ent.entry.keys.len; n++) if(ent.entry.keys.val[n].mkvno) { out->mkvno = *ent.entry.keys.val[n].mkvno; /* XXX this isn't right */ break; } } #if 0 /* XXX implement */ if(mask & KADM5_AUX_ATTRIBUTES) ; if(mask & KADM5_LAST_SUCCESS) ; if(mask & KADM5_LAST_FAILED) ; if(mask & KADM5_FAIL_AUTH_COUNT) ; #endif if(mask & KADM5_POLICY) { HDB_extension *ext; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_policy); if (ext == NULL) { out->policy = strdup("default"); /* It's OK if we retun NULL instead of "default" */ } else { out->policy = strdup(ext->data.u.policy); if (out->policy == NULL) { ret = ENOMEM; goto out; } } } if(mask & KADM5_MAX_RLIFE) { if(ent.entry.max_renew) out->max_renewable_life = *ent.entry.max_renew; else out->max_renewable_life = INT_MAX; } if(mask & KADM5_KEY_DATA){ size_t i; size_t n_keys = ent.entry.keys.len; krb5_salt salt; HDB_extension *ext; HDB_Ext_KeySet *hist_keys = NULL; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys); if (ext != NULL) hist_keys = &ext->data.u.hist_keys; krb5_get_pw_salt(context->context, ent.entry.principal, &salt); for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) n_keys += hist_keys->val[i].keys.len; out->key_data = malloc(n_keys * sizeof(*out->key_data)); if (out->key_data == NULL && n_keys != 0) { ret = ENOMEM; goto out; } out->n_key_data = 0; ret = copy_keyset_to_kadm5(context, ent.entry.kvno, ent.entry.keys.len, ent.entry.keys.val, &salt, out); if (ret) goto out; for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) { ret = copy_keyset_to_kadm5(context, hist_keys->val[i].kvno, hist_keys->val[i].keys.len, hist_keys->val[i].keys.val, &salt, out); if (ret) goto out; } krb5_free_salt(context->context, salt); assert( out->n_key_data == n_keys ); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } if(mask & KADM5_TL_DATA) { time_t last_pw_expire; const HDB_Ext_PKINIT_acl *acl; const HDB_Ext_Aliases *aliases; ret = hdb_entry_get_pw_change_time(&ent.entry, &last_pw_expire); if (ret == 0 && last_pw_expire) { unsigned char buf[4]; _krb5_put_int(buf, last_pw_expire, sizeof(buf)); ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf)); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } /* * If the client was allowed to get key data, let it have the * password too. */ if(mask & KADM5_KEY_DATA) { heim_utf8_string pw; ret = hdb_entry_get_password(context->context, context->db, &ent.entry, &pw); if (ret == 0) { ret = add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1); free(pw); } krb5_clear_error_message(context->context); } ret = hdb_entry_get_pkinit_acl(&ent.entry, &acl); if (ret == 0 && acl) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length, acl, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } ret = hdb_entry_get_aliases(&ent.entry, &aliases); if (ret == 0 && aliases) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length, aliases, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } } out: hdb_free_entry(context->context, &ent); return _kadm5_error_code(ret); }
krb5_error_code hdb_replace_extension(krb5_context context, hdb_entry *entry, const HDB_extension *ext) { HDB_extension *ext2; HDB_extension *es; int ret; ext2 = NULL; if (entry->extensions == NULL) { entry->extensions = calloc(1, sizeof(*entry->extensions)); if (entry->extensions == NULL) { krb5_set_error_string(context, "malloc: out of memory"); return ENOMEM; } } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) { ext2 = hdb_find_extension(entry, ext->data.element); } else { /* * This is an unknown extention, and we are asked to replace a * possible entry in `entry' that is of the same type. This * might seem impossible, but ASN.1 CHOICE comes to our * rescue. The first tag in each branch in the CHOICE is * unique, so just find the element in the list that have the * same tag was we are putting into the list. */ Der_class replace_class, list_class; Der_type replace_type, list_type; unsigned int replace_tag, list_tag; size_t size; int i; ret = der_get_tag(ext->data.u.asn1_ellipsis.data, ext->data.u.asn1_ellipsis.length, &replace_class, &replace_type, &replace_tag, &size); if (ret) { krb5_set_error_string(context, "hdb: failed to decode " "replacement hdb extention"); return ret; } for (i = 0; i < entry->extensions->len; i++) { HDB_extension *ext3 = &entry->extensions->val[i]; if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis) continue; ret = der_get_tag(ext3->data.u.asn1_ellipsis.data, ext3->data.u.asn1_ellipsis.length, &list_class, &list_type, &list_tag, &size); if (ret) { krb5_set_error_string(context, "hdb: failed to decode " "present hdb extention"); return ret; } if (MAKE_TAG(replace_class,replace_type,replace_type) == MAKE_TAG(list_class,list_type,list_type)) { ext2 = ext3; break; } } } if (ext2) { free_HDB_extension(ext2); ret = copy_HDB_extension(ext, ext2); if (ret) krb5_set_error_string(context, "hdb: failed to copy replacement " "hdb extention"); return ret; } es = realloc(entry->extensions->val, (entry->extensions->len+1)*sizeof(entry->extensions->val[0])); if (es == NULL) { krb5_set_error_string(context, "malloc: out of memory"); return ENOMEM; } entry->extensions->val = es; ret = copy_HDB_extension(ext, &entry->extensions->val[entry->extensions->len]); if (ret == 0) entry->extensions->len++; else krb5_set_error_string(context, "hdb: failed to copy new extension"); return ret; }
int hdb_entry_get_password(krb5_context context, HDB *db, const hdb_entry *entry, char **p) { HDB_extension *ext; char *str; int ret; ext = hdb_find_extension(entry, choice_HDB_extension_data_password); if (ext) { heim_utf8_string str; heim_octet_string pw; if (db->hdb_master_key_set && ext->data.u.password.mkvno) { hdb_master_key key; key = _hdb_find_master_key(ext->data.u.password.mkvno, db->hdb_master_key); if (key == NULL) { krb5_set_error_string(context, "master key %d missing", *ext->data.u.password.mkvno); return HDB_ERR_NO_MKEY; } ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY, ext->data.u.password.password.data, ext->data.u.password.password.length, &pw); } else { ret = der_copy_octet_string(&ext->data.u.password.password, &pw); } if (ret) { krb5_clear_error_string(context); return ret; } str = pw.data; if (str[pw.length - 1] != '\0') { krb5_set_error_string(context, "password malformated"); return EINVAL; } *p = strdup(str); der_free_octet_string(&pw); if (*p == NULL) { krb5_set_error_string(context, "malloc: out of memory"); return ENOMEM; } return 0; } ret = krb5_unparse_name(context, entry->principal, &str); if (ret == 0) { krb5_set_error_string(context, "no password attributefor %s", str); free(str); } else krb5_clear_error_string(context); return ENOENT; }
static kadm5_ret_t change(void *server_handle, krb5_principal princ, int keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, const char *password, int cond) { kadm5_server_context *context = server_handle; hdb_entry_ex ent; kadm5_ret_t ret; Key *keys; size_t num_keys; int existsp = 0; memset(&ent, 0, sizeof(ent)); if (!context->keep_open) { ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0); if(ret) return ret; } ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); if(ret) goto out; if (keepold || cond) { /* * We save these for now so we can handle password history checking; * we handle keepold further below. */ ret = hdb_add_current_keys_to_history(context->context, &ent.entry); if (ret) goto out; } if (context->db->hdb_capability_flags & HDB_CAP_F_HANDLE_PASSWORDS) { ret = context->db->hdb_password(context->context, context->db, &ent, password, cond); if (ret) goto out2; } else { num_keys = ent.entry.keys.len; keys = ent.entry.keys.val; ent.entry.keys.len = 0; ent.entry.keys.val = NULL; ret = _kadm5_set_keys(context, &ent.entry, n_ks_tuple, ks_tuple, password); if(ret) { _kadm5_free_keys(context->context, num_keys, keys); goto out2; } _kadm5_free_keys(context->context, num_keys, keys); if (cond) { HDB_extension *ext; ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys); if (ext != NULL) existsp = _kadm5_exists_keys_hist(ent.entry.keys.val, ent.entry.keys.len, &ext->data.u.hist_keys); } if (existsp) { ret = KADM5_PASS_REUSE; krb5_set_error_message(context->context, ret, "Password reuse forbidden"); goto out2; } } ent.entry.kvno++; ent.entry.flags.require_pwchange = 0; if (keepold) { ret = hdb_seal_keys(context->context, context->db, &ent.entry); if (ret) goto out2; } else { HDB_extension ext; memset(&ext, 0, sizeof (ext)); ext.data.element = choice_HDB_extension_data_hist_keys; ext.data.u.hist_keys.len = 0; ext.data.u.hist_keys.val = NULL; ret = hdb_replace_extension(context->context, &ent.entry, &ext); if (ret) goto out2; } ret = _kadm5_set_modifier(context, &ent.entry); if(ret) goto out2; ret = _kadm5_bump_pw_expire(context, &ent.entry); if (ret) goto out2; ret = context->db->hdb_store(context->context, context->db, HDB_F_REPLACE, &ent); if (ret) goto out2; kadm5_log_modify (context, &ent.entry, KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME | KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION | KADM5_TL_DATA); out2: hdb_free_entry(context->context, &ent); out: if (!context->keep_open) context->db->hdb_close(context->context, context->db); return _kadm5_error_code(ret); }
krb5_error_code entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent) { krb5_error_code ret; ssize_t sz; size_t i, k; size_t num_tl_data = 0; size_t num_key_data = 0; char *p; HDB_Ext_KeySet *hist_keys = NULL; HDB_extension *extp; time_t last_pw_chg = 0; time_t exp = 0; time_t pwexp = 0; unsigned int max_life = 0; unsigned int max_renew = 0; if (ent->modified_by) num_tl_data++; ret = hdb_entry_get_pw_change_time(ent, &last_pw_chg); if (ret) return ret; if (last_pw_chg) num_tl_data++; extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys); if (extp) hist_keys = &extp->data.u.hist_keys; for (i = 0; i < ent->keys.len;i++) { if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 || ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5) continue; num_key_data++; } if (hist_keys) { for (i = 0; i < hist_keys->len; i++) { /* * MIT uses the highest kvno as the current kvno instead of * tracking kvno separately, so we can't dump keysets with kvno * higher than the entry's kvno. */ if (hist_keys->val[i].kvno >= ent->kvno) continue; for (k = 0; k < hist_keys->val[i].keys.len; k++) { if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 || ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5) continue; num_key_data++; } } } ret = krb5_unparse_name(context, ent->principal, &p); if (ret) return ret; sz = append_string(context, sp, "princ\t38\t%u\t%u\t%u\t0\t%s\t%d", strlen(p), num_tl_data, num_key_data, p, flags_to_attr(ent->flags)); free(p); if (sz == -1) return ENOMEM; if (ent->max_life) max_life = *ent->max_life; if (ent->max_renew) max_renew = *ent->max_renew; if (ent->valid_end) exp = *ent->valid_end; if (ent->pw_end) pwexp = *ent->pw_end; sz = append_string(context, sp, "\t%u\t%u\t%u\t%u\t0\t0\t0", max_life, max_renew, exp, pwexp); if (sz == -1) return ENOMEM; /* Dump TL data we know: last pw chg and modified_by */ #define mit_KRB5_TL_LAST_PWD_CHANGE 1 #define mit_KRB5_TL_MOD_PRINC 2 if (last_pw_chg) { krb5_data d; time_t val; unsigned char *ptr; ptr = (unsigned char *)&last_pw_chg; val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); d.data = &val; d.length = sizeof (last_pw_chg); sz = append_string(context, sp, "\t%u\t%u\t", mit_KRB5_TL_LAST_PWD_CHANGE, d.length); if (sz == -1) return ENOMEM; sz = append_hex(context, sp, 1, 1, &d); if (sz == -1) return ENOMEM; } if (ent->modified_by) { krb5_data d; unsigned int val; size_t plen; unsigned char *ptr; char *modby_p; ptr = (unsigned char *)&ent->modified_by->time; val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); d.data = &val; d.length = sizeof (ent->modified_by->time); ret = krb5_unparse_name(context, ent->modified_by->principal, &modby_p); if (ret) return ret; plen = strlen(modby_p); sz = append_string(context, sp, "\t%u\t%u\t", mit_KRB5_TL_MOD_PRINC, d.length + plen + 1 /* NULL counted */); if (sz == -1) return ENOMEM; sz = append_hex(context, sp, 1, 1, &d); if (sz == -1) { free(modby_p); return ENOMEM; } d.data = modby_p; d.length = plen + 1; sz = append_hex(context, sp, 1, 1, &d); free(modby_p); if (sz == -1) return ENOMEM; } /* * Dump keys (remembering to not include any with kvno higher than * the entry's because MIT doesn't track entry kvno separately from * the entry's keys -- max kvno is it) */ for (i = 0; i < ent->keys.len; i++) { if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 || ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5) continue; sz = append_mit_key(context, sp, ent->principal, ent->kvno, &ent->keys.val[i]); if (sz == -1) return ENOMEM; } for (i = 0; hist_keys && i < ent->kvno; i++) { size_t m; /* dump historical keys */ for (k = 0; k < hist_keys->len; k++) { if (hist_keys->val[k].kvno != ent->kvno - i) continue; for (m = 0; m < hist_keys->val[k].keys.len; m++) { if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 || ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5) continue; sz = append_mit_key(context, sp, ent->principal, hist_keys->val[k].kvno, &hist_keys->val[k].keys.val[m]); if (sz == -1) return ENOMEM; } } } sz = append_string(context, sp, "\t-1;"); /* "extra data" */ if (sz == -1) return ENOMEM; return 0; }