Initial commit for second approach for multiple kvno. NOT TESTED!

This commit is contained in:
Nicolas Williams
2011-03-25 16:00:49 -05:00
committed by Nicolas Williams
parent ed91d4c9e3
commit fca53990e4
12 changed files with 227 additions and 32 deletions

View File

@@ -29,6 +29,7 @@ gen_files_hdb = \
asn1_HDB_Ext_Lan_Manager_OWF.x \ asn1_HDB_Ext_Lan_Manager_OWF.x \
asn1_HDB_Ext_Password.x \ asn1_HDB_Ext_Password.x \
asn1_HDB_Ext_Aliases.x \ asn1_HDB_Ext_Aliases.x \
asn1_HDB_Ext_KeySet.x \
asn1_HDB_extension.x \ asn1_HDB_extension.x \
asn1_HDB_extensions.x \ asn1_HDB_extensions.x \
asn1_hdb_entry.x \ asn1_hdb_entry.x \

View File

@@ -105,7 +105,6 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
krb5_principal enterprise_principal = NULL; krb5_principal enterprise_principal = NULL;
krb5_data key, value; krb5_data key, value;
krb5_error_code ret; krb5_error_code ret;
int code;
if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) { if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
if (principal->name.name_string.len != 1) { 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); hdb_principal2key(context, principal, &key);
if (enterprise_principal) if (enterprise_principal)
krb5_free_principal(context, 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); krb5_data_free(&key);
if(code) if(ret)
return code; return ret;
code = hdb_value2entry(context, &value, &entry->entry); ret = hdb_value2entry(context, &value, &entry->entry);
if (code == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) { if (ret == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
krb5_data_free(&value); krb5_data_free(&value);
return HDB_ERR_NOENTRY; return HDB_ERR_NOENTRY;
} else if (code == ASN1_BAD_ID) { } else if (ret == ASN1_BAD_ID) {
hdb_entry_alias alias; hdb_entry_alias alias;
code = hdb_value2entry_alias(context, &value, &alias); ret = hdb_value2entry_alias(context, &value, &alias);
if (code) { if (ret) {
krb5_data_free(&value); krb5_data_free(&value);
return code; return ret;
} }
hdb_principal2key(context, alias.principal, &key); hdb_principal2key(context, alias.principal, &key);
krb5_data_free(&value); krb5_data_free(&value);
free_hdb_entry_alias(&alias); 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); krb5_data_free(&key);
if (code) if (ret)
return code; return ret;
code = hdb_value2entry(context, &value, &entry->entry); ret = hdb_value2entry(context, &value, &entry->entry);
if (code) { if (ret) {
krb5_data_free(&value); krb5_data_free(&value);
return code; return ret;
} }
} }
krb5_data_free(&value); krb5_data_free(&value);
if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
code = hdb_unseal_keys (context, db, &entry->entry); if ((flags & HDB_F_KVNO_SPECIFIED) == 0 &&
if (code) (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); hdb_free_entry(context, entry);
return ret;
}
} }
return code;
return ret;
} }
static krb5_error_code static krb5_error_code

View File

@@ -87,6 +87,14 @@ HDB-Ext-Aliases ::= SEQUENCE {
aliases[1] SEQUENCE OF Principal -- all names, inc primary 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 { HDB-extension ::= SEQUENCE {
mandatory[0] BOOLEAN, -- kdc MUST understand this extension, mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
@@ -102,6 +110,7 @@ HDB-extension ::= SEQUENCE {
aliases[6] HDB-Ext-Aliases, aliases[6] HDB-Ext-Aliases,
last-pw-change[7] KerberosTime, last-pw-change[7] KerberosTime,
pkinit-cert[8] HDB-Ext-PKINIT-cert, 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-extensions ::= SEQUENCE OF HDB-extension
hdb_keyset ::= SEQUENCE {
kvno[1] INTEGER (0..4294967295),
keys[0] SEQUENCE OF Key
}
hdb_entry ::= SEQUENCE { hdb_entry ::= SEQUENCE {
principal[0] Principal OPTIONAL, -- this is optional only principal[0] Principal OPTIONAL, -- this is optional only
-- for compatibility with libkrb5 -- for compatibility with libkrb5

View File

@@ -168,13 +168,14 @@ hdb_unlock(int fd)
void void
hdb_free_entry(krb5_context context, hdb_entry_ex *ent) hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
{ {
size_t i; Key *k;
int i;
if (ent->free_entry) if (ent->free_entry)
(*ent->free_entry)(context, ent); (*ent->free_entry)(context, ent);
for(i = 0; i < ent->entry.keys.len; ++i) { for(i = 0; i < ent->entry.keys.len; i++) {
Key *k = &ent->entry.keys.val[i]; k = &ent->entry.keys.val[i];
memset (k->key.keyvalue.data, 0, k->key.keyvalue.length); memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
} }

View File

@@ -57,6 +57,7 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
#define HDB_F_CANON 32 /* want canonicalition */ #define HDB_F_CANON 32 /* want canonicalition */
#define HDB_F_ADMIN_DATA 64 /* want data that kdc don't use */ #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_KVNO_SPECIFIED 128 /* we want a particular KVNO */
#define HDB_F_CURRENT_KVNO 256 /* we want the current KVNO */
/* hdb_capability_flags */ /* hdb_capability_flags */
#define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1 #define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1

View File

@@ -39,7 +39,7 @@
*/ */
void void
hdb_free_keys (krb5_context context, int len, Key *keys) hdb_free_keys(krb5_context context, int len, Key *keys)
{ {
int i; int i;
@@ -56,6 +56,19 @@ hdb_free_keys (krb5_context context, int len, Key *keys)
free (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 * for each entry in `default_keys' try to parse it as a sequence
* of etype:salttype:salt, syntax of this if something like: * 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; 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 static krb5_error_code
add_enctype_to_key_set(Key **key_set, size_t *nkeyset, add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
krb5_enctype enctype, krb5_salt *salt) krb5_enctype enctype, krb5_salt *salt)

View File

@@ -32,6 +32,7 @@
*/ */
#include "hdb_locl.h" #include "hdb_locl.h"
#include <assert.h>
#ifndef O_BINARY #ifndef O_BINARY
#define O_BINARY 0 #define O_BINARY 0
#endif #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); 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 krb5_error_code
hdb_unseal_key(krb5_context context, HDB *db, Key *k) hdb_unseal_key(krb5_context context, HDB *db, Key *k)
{ {

View File

@@ -62,11 +62,12 @@ append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...)
{ {
krb5_error_code ret; krb5_error_code ret;
char *s; char *s;
int rc;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vasprintf(&s, fmt, ap); rc = vasprintf(&s, fmt, ap);
va_end(ap); va_end(ap);
if(s == NULL) { if(rc < 0) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM; return ENOMEM;
} }
@@ -127,7 +128,7 @@ static krb5_error_code
entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent) entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
{ {
char *p; char *p;
size_t i; int i;
krb5_error_code ret; krb5_error_code ret;
/* --- principal */ /* --- principal */
@@ -234,7 +235,6 @@ entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
} else } else
append_string(context, sp, "-"); append_string(context, sp, "-");
return 0; return 0;
} }

View File

@@ -88,6 +88,7 @@ main(int argc, char **argv)
memset(&keyset, 0, sizeof(keyset)); memset(&keyset, 0, sizeof(keyset));
keyset.kvno = kvno_integer; keyset.kvno = kvno_integer;
keyset.replace_time = time(NULL);
ret = hdb_generate_key_set_password(context, principal, password_str, ret = hdb_generate_key_set_password(context, principal, password_str,
&keyset.keys.val, &len); &keyset.keys.val, &len);

View File

@@ -4,6 +4,7 @@ HEIMDAL_HDB_1.0 {
global: global:
encode_hdb_keyset; encode_hdb_keyset;
hdb_add_master_key; hdb_add_master_key;
hdb_add_current_keys_to_history;
hdb_check_db_format; hdb_check_db_format;
hdb_clear_extension; hdb_clear_extension;
hdb_clear_master_key; hdb_clear_master_key;

View File

@@ -58,6 +58,10 @@ change(void *server_handle,
if(ret) if(ret)
goto out; 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) { if (context->db->hdb_capability_flags & HDB_CAP_F_HANDLE_PASSWORDS) {
ret = context->db->hdb_password(context->context, context->db, ret = context->db->hdb_password(context->context, context->db,
&ent, password, cond); &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); HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent);
if(ret == HDB_ERR_NOENTRY) if(ret == HDB_ERR_NOENTRY)
goto out; 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); ret = _kadm5_set_keys2(context, &ent.entry, n_key_data, key_data);
if(ret) if(ret)
goto out2; goto out2;

View File

@@ -59,6 +59,10 @@ kadm5_s_randkey_principal(void *server_handle,
if(ret) if(ret)
goto out; goto out;
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
if (ret)
goto out2;
ret = _kadm5_set_keys_randomly (context, ret = _kadm5_set_keys_randomly (context,
&ent.entry, &ent.entry,
new_keys, new_keys,