Initial commit for second approach for multiple kvno. NOT TESTED!
This commit is contained in:

committed by
Nicolas Williams

parent
ed91d4c9e3
commit
fca53990e4
@@ -29,6 +29,7 @@ gen_files_hdb = \
|
||||
asn1_HDB_Ext_Lan_Manager_OWF.x \
|
||||
asn1_HDB_Ext_Password.x \
|
||||
asn1_HDB_Ext_Aliases.x \
|
||||
asn1_HDB_Ext_KeySet.x \
|
||||
asn1_HDB_extension.x \
|
||||
asn1_HDB_extensions.x \
|
||||
asn1_hdb_entry.x \
|
||||
|
@@ -105,7 +105,6 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
|
||||
krb5_principal enterprise_principal = NULL;
|
||||
krb5_data key, value;
|
||||
krb5_error_code ret;
|
||||
int code;
|
||||
|
||||
if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
|
||||
if (principal->name.name_string.len != 1) {
|
||||
@@ -125,43 +124,72 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
|
||||
hdb_principal2key(context, principal, &key);
|
||||
if (enterprise_principal)
|
||||
krb5_free_principal(context, enterprise_principal);
|
||||
code = db->hdb__get(context, db, key, &value);
|
||||
ret = db->hdb__get(context, db, key, &value);
|
||||
krb5_data_free(&key);
|
||||
if(code)
|
||||
return code;
|
||||
code = hdb_value2entry(context, &value, &entry->entry);
|
||||
if (code == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
|
||||
if(ret)
|
||||
return ret;
|
||||
ret = hdb_value2entry(context, &value, &entry->entry);
|
||||
if (ret == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
|
||||
krb5_data_free(&value);
|
||||
return HDB_ERR_NOENTRY;
|
||||
} else if (code == ASN1_BAD_ID) {
|
||||
} else if (ret == ASN1_BAD_ID) {
|
||||
hdb_entry_alias alias;
|
||||
|
||||
code = hdb_value2entry_alias(context, &value, &alias);
|
||||
if (code) {
|
||||
ret = hdb_value2entry_alias(context, &value, &alias);
|
||||
if (ret) {
|
||||
krb5_data_free(&value);
|
||||
return code;
|
||||
return ret;
|
||||
}
|
||||
hdb_principal2key(context, alias.principal, &key);
|
||||
krb5_data_free(&value);
|
||||
free_hdb_entry_alias(&alias);
|
||||
|
||||
code = db->hdb__get(context, db, key, &value);
|
||||
ret = db->hdb__get(context, db, key, &value);
|
||||
krb5_data_free(&key);
|
||||
if (code)
|
||||
return code;
|
||||
code = hdb_value2entry(context, &value, &entry->entry);
|
||||
if (code) {
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = hdb_value2entry(context, &value, &entry->entry);
|
||||
if (ret) {
|
||||
krb5_data_free(&value);
|
||||
return code;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
krb5_data_free(&value);
|
||||
if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
|
||||
code = hdb_unseal_keys (context, db, &entry->entry);
|
||||
if (code)
|
||||
if ((flags & HDB_F_KVNO_SPECIFIED) == 0 &&
|
||||
(flags & HDB_F_CURRENT_KVNO) == 0) {
|
||||
|
||||
/*
|
||||
* Decrypt all the old keys too, since we don't know which
|
||||
* the caller will need.
|
||||
*/
|
||||
ret = hdb_unseal_keys_kvno(context, db, 0, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
} else if ((flags & HDB_F_KVNO_SPECIFIED) != 0 &&
|
||||
kvno != entry->entry.kvno &&
|
||||
kvno < entry->entry.kvno &&
|
||||
kvno > 0) {
|
||||
|
||||
/* Decrypt the keys we were asked for, if not the current ones */
|
||||
ret = hdb_unseal_keys_kvno(context, db, kvno, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Always decrypt the current keys too */
|
||||
ret = hdb_unseal_keys(context, db, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return code;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
|
@@ -87,6 +87,14 @@ HDB-Ext-Aliases ::= SEQUENCE {
|
||||
aliases[1] SEQUENCE OF Principal -- all names, inc primary
|
||||
}
|
||||
|
||||
hdb_keyset ::= SEQUENCE {
|
||||
kvno[0] INTEGER (0..4294967295),
|
||||
replace-time[1] KerberosTime, -- time this key was replaced
|
||||
keys[2] SEQUENCE OF Key
|
||||
}
|
||||
|
||||
HDB-Ext-KeySet ::= SEQUENCE OF hdb_keyset
|
||||
|
||||
|
||||
HDB-extension ::= SEQUENCE {
|
||||
mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
|
||||
@@ -102,6 +110,7 @@ HDB-extension ::= SEQUENCE {
|
||||
aliases[6] HDB-Ext-Aliases,
|
||||
last-pw-change[7] KerberosTime,
|
||||
pkinit-cert[8] HDB-Ext-PKINIT-cert,
|
||||
hist-keys[9] HDB-Ext-KeySet,
|
||||
...
|
||||
},
|
||||
...
|
||||
@@ -109,11 +118,6 @@ HDB-extension ::= SEQUENCE {
|
||||
|
||||
HDB-extensions ::= SEQUENCE OF HDB-extension
|
||||
|
||||
hdb_keyset ::= SEQUENCE {
|
||||
kvno[1] INTEGER (0..4294967295),
|
||||
keys[0] SEQUENCE OF Key
|
||||
}
|
||||
|
||||
hdb_entry ::= SEQUENCE {
|
||||
principal[0] Principal OPTIONAL, -- this is optional only
|
||||
-- for compatibility with libkrb5
|
||||
|
@@ -168,13 +168,14 @@ hdb_unlock(int fd)
|
||||
void
|
||||
hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
|
||||
{
|
||||
size_t i;
|
||||
Key *k;
|
||||
int i;
|
||||
|
||||
if (ent->free_entry)
|
||||
(*ent->free_entry)(context, ent);
|
||||
|
||||
for(i = 0; i < ent->entry.keys.len; ++i) {
|
||||
Key *k = &ent->entry.keys.val[i];
|
||||
for(i = 0; i < ent->entry.keys.len; i++) {
|
||||
k = &ent->entry.keys.val[i];
|
||||
|
||||
memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
|
||||
}
|
||||
|
@@ -57,6 +57,7 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
|
||||
#define HDB_F_CANON 32 /* want canonicalition */
|
||||
#define HDB_F_ADMIN_DATA 64 /* want data that kdc don't use */
|
||||
#define HDB_F_KVNO_SPECIFIED 128 /* we want a particular KVNO */
|
||||
#define HDB_F_CURRENT_KVNO 256 /* we want the current KVNO */
|
||||
|
||||
/* hdb_capability_flags */
|
||||
#define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1
|
||||
|
@@ -39,7 +39,7 @@
|
||||
*/
|
||||
|
||||
void
|
||||
hdb_free_keys (krb5_context context, int len, Key *keys)
|
||||
hdb_free_keys(krb5_context context, int len, Key *keys)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -56,6 +56,19 @@ hdb_free_keys (krb5_context context, int len, Key *keys)
|
||||
free (keys);
|
||||
}
|
||||
|
||||
void
|
||||
hdb_free_keysets(krb5_context context, int len, hdb_keyset *keysets)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hdb_free_keys(context, keysets[i].keys.len, keysets[i].keys.val);
|
||||
keysets[i].keys.val = NULL;
|
||||
keysets[i].keys.len = 0;
|
||||
}
|
||||
free (keysets);
|
||||
}
|
||||
|
||||
/*
|
||||
* for each entry in `default_keys' try to parse it as a sequence
|
||||
* of etype:salttype:salt, syntax of this if something like:
|
||||
@@ -196,6 +209,60 @@ parse_key_set(krb5_context context, const char *key,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
krb5_error_code
|
||||
hdb_add_current_keys_to_history(krb5_context context, hdb_entry *entry)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
HDB_extension *ext;
|
||||
HDB_Ext_KeySet *hist_keys;
|
||||
hdb_keyset *tmp_keysets;
|
||||
int add = 0;
|
||||
|
||||
ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys);
|
||||
if (ext != NULL) {
|
||||
hist_keys = &ext->data.u.hist_keys;
|
||||
tmp_keysets = realloc(hist_keys->val,
|
||||
sizeof (*hist_keys->val) * (hist_keys->len + 1));
|
||||
if (tmp_keysets == NULL)
|
||||
return ENOMEM;
|
||||
hist_keys->val = tmp_keysets;
|
||||
memmove(&hist_keys->val[1], hist_keys->val,
|
||||
sizeof (*hist_keys->val) * hist_keys->len++);
|
||||
} else {
|
||||
add = 1;
|
||||
ext = calloc(1, sizeof (*ext));
|
||||
if (ext == NULL)
|
||||
return ENOMEM;
|
||||
ext->data.element = choice_HDB_extension_data_hist_keys;
|
||||
hist_keys = &ext->data.u.hist_keys;
|
||||
hist_keys->val = calloc(1, sizeof (*hist_keys->val));
|
||||
if (hist_keys->val == NULL) {
|
||||
free(hist_keys);
|
||||
return ENOMEM;
|
||||
}
|
||||
hist_keys->len = 1;
|
||||
}
|
||||
|
||||
hist_keys->val[0].keys.val = entry->keys.val;
|
||||
hist_keys->val[0].keys.len = entry->keys.len;
|
||||
hist_keys->val[0].kvno = entry->kvno;
|
||||
hist_keys->val[0].replace_time = time(NULL);
|
||||
|
||||
if (add) {
|
||||
ret = hdb_replace_extension(context, entry, ext);
|
||||
if (ret) {
|
||||
free_HDB_extension(ext);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* hdb_replace_extension() copies ext, so we have to free it */
|
||||
free_HDB_extension(ext);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static krb5_error_code
|
||||
add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
|
||||
krb5_enctype enctype, krb5_salt *salt)
|
||||
|
@@ -32,6 +32,7 @@
|
||||
*/
|
||||
|
||||
#include "hdb_locl.h"
|
||||
#include <assert.h>
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
@@ -479,6 +480,85 @@ hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
|
||||
return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_keys_kvno(krb5_context context, HDB *db, krb5_kvno kvno,
|
||||
hdb_entry *ent)
|
||||
{
|
||||
krb5_error_code ret = KRB5KRB_AP_ERR_NOKEY; /* XXX need a better code? */
|
||||
HDB_extension *tmp;
|
||||
HDB_Ext_KeySet *hist_keys;
|
||||
hdb_keyset *tmp_keys;
|
||||
Key *tmp_val;
|
||||
unsigned int tmp_len;
|
||||
krb5_kvno tmp_kvno;
|
||||
int i, k;
|
||||
|
||||
assert(kvno == 0 || kvno < ent->kvno);
|
||||
|
||||
tmp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
|
||||
if (tmp == NULL)
|
||||
return ret;
|
||||
|
||||
tmp_len = ent->keys.len;
|
||||
tmp_val = ent->keys.val;
|
||||
tmp_kvno = ent->kvno;
|
||||
|
||||
hist_keys = &tmp->data.u.hist_keys;
|
||||
|
||||
for (i = hist_keys->len - 1; i >= 0; i++) {
|
||||
if (kvno != 0 && hist_keys->val[i].kvno != kvno)
|
||||
continue;
|
||||
for (k = 0; k < hist_keys->val[i].keys.len; k++) {
|
||||
ret = hdb_unseal_key_mkey(context,
|
||||
&hist_keys->val[i].keys.val[k],
|
||||
db->hdb_master_key);
|
||||
if (ret)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (kvno == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* NOTE: What follows is a bit of an ugly hack.
|
||||
*
|
||||
* This is the keyset we're being asked for, so we add the
|
||||
* current keyset to the history, leave the one we were asked
|
||||
* for in the history, and pretend the one we were asked for is
|
||||
* also the current keyset.
|
||||
*
|
||||
* This is a bit of a defensive hack in case an entry fetched
|
||||
* this way ever gets modified then stored: if the keyset is not
|
||||
* changed we can detect this and put things back, else we won't
|
||||
* drop any keysets from history by accident.
|
||||
*
|
||||
* Note too that we only ever get called with a non-zero kvno
|
||||
* either in the KDC or in cases where we aren't changing the
|
||||
* HDB entry anyways, which is why this is just a defensive
|
||||
* hack. We also don't fetch specific kvnos in the dump case,
|
||||
* so there's no danger that we'll dump this entry and load it
|
||||
* again, repeatedly causing the history to grow boundelessly.
|
||||
*/
|
||||
tmp_keys = realloc(hist_keys->val,
|
||||
sizeof (*hist_keys->val) * (hist_keys->len + 1));
|
||||
if (tmp_keys == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
memmove(&tmp_keys[1], tmp_keys,
|
||||
sizeof (*hist_keys->val) * hist_keys->len++);
|
||||
tmp_keys[0].keys.len = ent->keys.len;
|
||||
tmp_keys[0].keys.val = ent->keys.val;
|
||||
tmp_keys[0].kvno = ent->kvno;
|
||||
tmp_keys[0].replace_time = time(NULL);
|
||||
i++;
|
||||
ent->keys.len = hist_keys->val[i].keys.len;
|
||||
ent->keys.val = hist_keys->val[i].keys.val;
|
||||
ent->kvno = kvno;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_key(krb5_context context, HDB *db, Key *k)
|
||||
{
|
||||
|
@@ -62,11 +62,12 @@ append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *s;
|
||||
int rc;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vasprintf(&s, fmt, ap);
|
||||
rc = vasprintf(&s, fmt, ap);
|
||||
va_end(ap);
|
||||
if(s == NULL) {
|
||||
if(rc < 0) {
|
||||
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
|
||||
return ENOMEM;
|
||||
}
|
||||
@@ -127,7 +128,7 @@ static krb5_error_code
|
||||
entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
|
||||
{
|
||||
char *p;
|
||||
size_t i;
|
||||
int i;
|
||||
krb5_error_code ret;
|
||||
|
||||
/* --- principal */
|
||||
@@ -234,7 +235,6 @@ entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
|
||||
} else
|
||||
append_string(context, sp, "-");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -88,6 +88,7 @@ main(int argc, char **argv)
|
||||
memset(&keyset, 0, sizeof(keyset));
|
||||
|
||||
keyset.kvno = kvno_integer;
|
||||
keyset.replace_time = time(NULL);
|
||||
|
||||
ret = hdb_generate_key_set_password(context, principal, password_str,
|
||||
&keyset.keys.val, &len);
|
||||
|
@@ -4,6 +4,7 @@ HEIMDAL_HDB_1.0 {
|
||||
global:
|
||||
encode_hdb_keyset;
|
||||
hdb_add_master_key;
|
||||
hdb_add_current_keys_to_history;
|
||||
hdb_check_db_format;
|
||||
hdb_clear_extension;
|
||||
hdb_clear_master_key;
|
||||
|
@@ -58,6 +58,10 @@ change(void *server_handle,
|
||||
if(ret)
|
||||
goto out;
|
||||
|
||||
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);
|
||||
@@ -170,6 +174,9 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
|
||||
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent);
|
||||
if(ret == HDB_ERR_NOENTRY)
|
||||
goto out;
|
||||
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
ret = _kadm5_set_keys2(context, &ent.entry, n_key_data, key_data);
|
||||
if(ret)
|
||||
goto out2;
|
||||
|
@@ -59,6 +59,10 @@ kadm5_s_randkey_principal(void *server_handle,
|
||||
if(ret)
|
||||
goto out;
|
||||
|
||||
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = _kadm5_set_keys_randomly (context,
|
||||
&ent.entry,
|
||||
new_keys,
|
||||
|
Reference in New Issue
Block a user