Merge pull request #12 from nicowilliams/krb5_admin_patches_2nd
Krb5 admin patches 2nd This has all the patches needed for krb5_admind to build and pass most tests, that includes: - more kadm5 API compatibility (including very basic profile functionality) - multi-kvno support (useful for key rollovers) (a test for this is included in tests/db/check-kdc) Unfinished: - password history (currently uses key history, needs to be separated and use digests) - policies (only default policy allowed) - mit kdb changes not tested yet Signed-off-by: Love Hörnquist Åstrand <lha@h5l.org>
This commit is contained in:
@@ -29,11 +29,13 @@ 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 \
|
||||
asn1_hdb_entry_alias.x \
|
||||
asn1_hdb_keyset.x
|
||||
asn1_hdb_keyset.x \
|
||||
asn1_Keys.x
|
||||
|
||||
CLEANFILES = $(BUILT_SOURCES) $(gen_files_hdb) \
|
||||
hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.c*
|
||||
@@ -121,7 +123,7 @@ $(srcdir)/hdb-private.h:
|
||||
$(gen_files_hdb) hdb_asn1.hx hdb_asn1-priv.hx: hdb_asn1_files
|
||||
|
||||
hdb_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/hdb.asn1
|
||||
$(ASN1_COMPILE) $(srcdir)/hdb.asn1 hdb_asn1
|
||||
$(ASN1_COMPILE) --sequence=HDB-Ext-KeySet --sequence=Keys $(srcdir)/hdb.asn1 hdb_asn1
|
||||
|
||||
test_dbinfo_LIBS = libhdb.la
|
||||
|
||||
|
@@ -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,74 @@ _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_DECRYPT) && (flags & HDB_F_ALL_KVNOS)) {
|
||||
/* Decrypt the current keys */
|
||||
ret = hdb_unseal_keys(context, db, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
/* Decrypt the key history too */
|
||||
ret = hdb_unseal_keys_kvno(context, db, 0, flags, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
} else if ((flags & HDB_F_DECRYPT)) {
|
||||
if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->entry.kvno) {
|
||||
/* Decrypt the current keys */
|
||||
ret = hdb_unseal_keys(context, db, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if ((flags & HDB_F_ALL_KVNOS))
|
||||
kvno = 0;
|
||||
/*
|
||||
* Find and decrypt the keys from the history that we want,
|
||||
* and swap them with the current keys
|
||||
*/
|
||||
ret = hdb_unseal_keys_kvno(context, db, kvno, flags, &entry->entry);
|
||||
if (ret) {
|
||||
hdb_free_entry(context, entry);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return code;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
@@ -284,6 +314,8 @@ _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
|
||||
krb5_data key, value;
|
||||
int code;
|
||||
|
||||
if (entry->entry.flags.do_not_store)
|
||||
return HDB_ERR_MISUSE;
|
||||
/* check if new aliases already is used */
|
||||
code = hdb_check_aliases(context, db, entry);
|
||||
if (code)
|
||||
|
22
lib/hdb/db.c
22
lib/hdb/db.c
@@ -65,12 +65,24 @@ DB_lock(krb5_context context, HDB *db, int operation)
|
||||
{
|
||||
DB *d = (DB*)db->hdb_db;
|
||||
int fd = (*d->fd)(d);
|
||||
krb5_error_code ret;
|
||||
|
||||
if (db->lock_count > 0) {
|
||||
db->lock_count++;
|
||||
if (db->lock_type == HDB_WLOCK || db->lock_type == operation)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(fd < 0) {
|
||||
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
|
||||
"Can't lock database: %s", db->hdb_name);
|
||||
return HDB_ERR_CANT_LOCK_DB;
|
||||
}
|
||||
return hdb_lock(fd, operation);
|
||||
ret = hdb_lock(fd, operation);
|
||||
if (ret)
|
||||
return ret;
|
||||
db->lock_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
@@ -78,6 +90,14 @@ DB_unlock(krb5_context context, HDB *db)
|
||||
{
|
||||
DB *d = (DB*)db->hdb_db;
|
||||
int fd = (*d->fd)(d);
|
||||
|
||||
if (db->lock_count > 1) {
|
||||
db->lock_count--;
|
||||
return 0;
|
||||
}
|
||||
heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
|
||||
db->lock_count--;
|
||||
|
||||
if(fd < 0) {
|
||||
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
|
||||
"Can't unlock database: %s", db->hdb_name);
|
||||
|
@@ -75,9 +75,21 @@ DB_lock(krb5_context context, HDB *db, int operation)
|
||||
{
|
||||
DB *d = (DB*)db->hdb_db;
|
||||
int fd;
|
||||
krb5_error_code ret;
|
||||
|
||||
if (db->lock_count > 1) {
|
||||
db->lock_count++;
|
||||
if (db->lock_count == HDB_WLOCK || db->lock_count == operation)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((*d->fd)(d, &fd))
|
||||
return HDB_ERR_CANT_LOCK_DB;
|
||||
return hdb_lock(fd, operation);
|
||||
ret = hdb_lock(fd, operation);
|
||||
if (ret)
|
||||
return ret;
|
||||
db->lock_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
@@ -85,6 +97,14 @@ DB_unlock(krb5_context context, HDB *db)
|
||||
{
|
||||
DB *d = (DB*)db->hdb_db;
|
||||
int fd;
|
||||
|
||||
if (db->lock_count > 1) {
|
||||
db->lock_count--;
|
||||
return 0;
|
||||
}
|
||||
heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
|
||||
db->lock_count--;
|
||||
|
||||
if ((*d->fd)(d, &fd))
|
||||
return HDB_ERR_CANT_LOCK_DB;
|
||||
return hdb_unlock(fd);
|
||||
|
@@ -432,3 +432,67 @@ hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
hdb_entry_get_kvno_diff_clnt(const hdb_entry *entry)
|
||||
{
|
||||
const HDB_extension *ext;
|
||||
|
||||
ext = hdb_find_extension(entry,
|
||||
choice_HDB_extension_data_hist_kvno_diff_clnt);
|
||||
if (ext)
|
||||
return ext->data.u.hist_kvno_diff_clnt;
|
||||
return 1;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_entry_set_kvno_diff_clnt(krb5_context context, hdb_entry *entry,
|
||||
unsigned int diff)
|
||||
{
|
||||
HDB_extension ext;
|
||||
|
||||
if (diff > 16384)
|
||||
return EINVAL;
|
||||
ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
|
||||
ext.data.u.hist_kvno_diff_clnt = diff;
|
||||
return hdb_replace_extension(context, entry, &ext);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_entry_clear_kvno_diff_clnt(krb5_context context, hdb_entry *entry)
|
||||
{
|
||||
return hdb_clear_extension(context, entry,
|
||||
choice_HDB_extension_data_hist_kvno_diff_clnt);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
hdb_entry_get_kvno_diff_svc(const hdb_entry *entry)
|
||||
{
|
||||
const HDB_extension *ext;
|
||||
|
||||
ext = hdb_find_extension(entry,
|
||||
choice_HDB_extension_data_hist_kvno_diff_svc);
|
||||
if (ext)
|
||||
return ext->data.u.hist_kvno_diff_svc;
|
||||
return 1024; /* max_life effectively provides a better default */
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_entry_set_kvno_diff_svc(krb5_context context, hdb_entry *entry,
|
||||
unsigned int diff)
|
||||
{
|
||||
HDB_extension ext;
|
||||
|
||||
if (diff > 16384)
|
||||
return EINVAL;
|
||||
ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
|
||||
ext.data.u.hist_kvno_diff_svc = diff;
|
||||
return hdb_replace_extension(context, entry, &ext);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_entry_clear_kvno_diff_svc(krb5_context context, hdb_entry *entry)
|
||||
{
|
||||
return hdb_clear_extension(context, entry,
|
||||
choice_HDB_extension_data_hist_kvno_diff_svc);
|
||||
}
|
||||
|
@@ -196,12 +196,226 @@ fix_salt(krb5_context context, hdb_entry *ent, int key_num)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs a pointer to a Key or array of @key_count Keys
|
||||
* where the caller may place Keys.
|
||||
*
|
||||
* @param context Context
|
||||
* @param entry HDB entry
|
||||
* @param kvno kvno of the keys to be added
|
||||
* @param is_hist Whether the keys will be historical keys or current keys
|
||||
* @param key_count Size of array of keys to set. MUST be zero if !is_hist.
|
||||
* @param out Pointer to Key * variable where to put the resulting Key *
|
||||
*
|
||||
* See three call sites below for more information.
|
||||
*/
|
||||
static krb5_error_code
|
||||
get_entry_key_location(krb5_context context, hdb_entry *entry, krb5_kvno kvno,
|
||||
krb5_boolean is_hist, size_t key_count, Key **out)
|
||||
{
|
||||
HDB_extension ext;
|
||||
HDB_Ext_KeySet *hist_keys;
|
||||
hdb_keyset *keyset = NULL;
|
||||
size_t keyset_count = 0;
|
||||
Key *k = NULL;
|
||||
size_t i;
|
||||
krb5_error_code ret;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if (!is_hist) {
|
||||
Key *tmp;
|
||||
|
||||
/* Extend current keyset */
|
||||
tmp = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
|
||||
if (tmp == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
entry->keys.val = tmp;
|
||||
|
||||
/* k points to current Key */
|
||||
k = &entry->keys.val[entry->keys.len];
|
||||
|
||||
memset(k, 0, sizeof(*k));
|
||||
entry->keys.len += 1;
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Find a history keyset and extend it or extend the history keyset */
|
||||
memset(&ext, 0, sizeof (ext));
|
||||
ext.data.element = choice_HDB_extension_data_hist_keys;
|
||||
hist_keys = &ext.data.u.hist_keys;
|
||||
|
||||
/* hdb_replace_extension() makes a copy of ext */
|
||||
ret = hdb_replace_extension(context, entry, &ext);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < hist_keys->len; i++) {
|
||||
if (hist_keys->val[i].kvno == kvno) {
|
||||
/* We're adding a key to an existing history keyset */
|
||||
keyset = &hist_keys->val[i];
|
||||
if ((keyset->keys.len % 8) == 0) {
|
||||
Key *tmp;
|
||||
|
||||
/* We're adding the 9th, 17th, ... key to the set */
|
||||
tmp = realloc(keyset->keys.val,
|
||||
(keyset->keys.len + 8) * sizeof (*tmp));
|
||||
if (tmp == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyset == NULL) {
|
||||
/* We're adding the first key of a new history keyset */
|
||||
if (hist_keys->val == NULL) {
|
||||
if (key_count == 0)
|
||||
keyset_count = 8; /* There's not that many enctypes */
|
||||
else
|
||||
keyset_count = key_count;
|
||||
hist_keys->val = calloc(keyset_count,
|
||||
sizeof (*hist_keys->val));
|
||||
if (hist_keys->val == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
keyset = &hist_keys->val[0];
|
||||
} else if (hist_keys->len == keyset_count) {
|
||||
hdb_keyset *tmp;
|
||||
|
||||
keyset_count *= 2;
|
||||
tmp = realloc(hist_keys->val,
|
||||
keyset_count * sizeof (*hist_keys->val));
|
||||
if (tmp == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
hist_keys->val = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
k = &keyset->keys.val[keyset->keys.len];
|
||||
|
||||
if (key_count != 0)
|
||||
keyset->keys.len += key_count;
|
||||
|
||||
done:
|
||||
memset(k, 0, sizeof (*k));
|
||||
k->mkvno = malloc(sizeof(*k->mkvno));
|
||||
if (k->mkvno == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
*k->mkvno = 1;
|
||||
*out = k;
|
||||
|
||||
out:
|
||||
if (ret && !is_hist)
|
||||
entry->keys.len--;
|
||||
if (is_hist)
|
||||
free_HDB_extension(&ext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function takes a key from a krb5_storage from an MIT KDB encoded
|
||||
* entry and places it in the given Key object.
|
||||
*
|
||||
* @param context Context
|
||||
* @param entry HDB entry
|
||||
* @param sp krb5_storage with current offset set to the beginning of a
|
||||
* key
|
||||
* @param version See comments in caller body for the backstory on this
|
||||
* @param k Key * to load the key into
|
||||
*/
|
||||
static krb5_error_code
|
||||
mdb_keyvalue2key(krb5_context context, hdb_entry *entry, krb5_storage *sp, uint16_t version, Key *k)
|
||||
{
|
||||
size_t i;
|
||||
uint16_t u16, type;
|
||||
krb5_error_code ret;
|
||||
|
||||
for (i = 0; i < version; i++) {
|
||||
CHECK(ret = krb5_ret_uint16(sp, &type));
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
if (i == 0) {
|
||||
/* This "version" means we have a key */
|
||||
k->key.keytype = type;
|
||||
if (u16 < 2) {
|
||||
ret = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* MIT stores keys encrypted keys as {16-bit length
|
||||
* of plaintext key, {encrypted key}}. The reason
|
||||
* for this is that the Kerberos cryptosystem is not
|
||||
* length-preserving. Heimdal's approach is to
|
||||
* truncate the plaintext to the expected length of
|
||||
* the key given its enctype, so we ignore this
|
||||
* 16-bit length-of-plaintext-key field.
|
||||
*/
|
||||
krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
|
||||
k->key.keyvalue.length = u16 - 2; /* adjust cipher len */
|
||||
k->key.keyvalue.data = malloc(k->key.keyvalue.length);
|
||||
krb5_storage_read(sp, k->key.keyvalue.data,
|
||||
k->key.keyvalue.length);
|
||||
} else if (i == 1) {
|
||||
/* This "version" means we have a salt */
|
||||
k->salt = calloc(1, sizeof(*k->salt));
|
||||
if (k->salt == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
k->salt->type = type;
|
||||
if (u16 != 0) {
|
||||
k->salt->salt.data = malloc(u16);
|
||||
if (k->salt->salt.data == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
k->salt->salt.length = u16;
|
||||
krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
|
||||
}
|
||||
fix_salt(context, entry, entry->keys.len - 1);
|
||||
} else {
|
||||
/*
|
||||
* Whatever this "version" might be, we skip it
|
||||
*
|
||||
* XXX A krb5.conf parameter requesting that we log
|
||||
* about strangeness like this, or return an error
|
||||
* from here, might be nice.
|
||||
*/
|
||||
krb5_storage_seek(sp, u16, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
free_Key(k);
|
||||
memset(k, 0, sizeof (*k));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function parses an MIT krb5 encoded KDB entry and fills in the
|
||||
* given HDB entry with it.
|
||||
*/
|
||||
static krb5_error_code
|
||||
mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_storage *sp;
|
||||
Key *k;
|
||||
krb5_kvno key_kvno;
|
||||
uint32_t u32;
|
||||
uint16_t u16, num_keys, num_tl;
|
||||
size_t i, j;
|
||||
@@ -328,125 +542,63 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
|
||||
for (i = 0; i < num_keys; i++) {
|
||||
int keep = 0;
|
||||
uint16_t version;
|
||||
void *ptr;
|
||||
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
version = u16;
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
key_kvno = u16;
|
||||
|
||||
/*
|
||||
* First time through, and until we find one matching key,
|
||||
* entry->kvno == 0.
|
||||
*/
|
||||
if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) {
|
||||
keep = 1;
|
||||
entry->kvno = u16;
|
||||
if ((entry->kvno < key_kvno) && (kvno == 0 || kvno == key_kvno)) {
|
||||
/*
|
||||
* Found a higher kvno than earlier, so free the old highest
|
||||
* kvno keys.
|
||||
*
|
||||
* XXX Of course, we actually want to extract the old kvnos
|
||||
* as well, for some of the kadm5 APIs. We shouldn't free
|
||||
* these keys, but keep them elsewhere.
|
||||
* Found a higher kvno than earlier, we aren't looking for
|
||||
* any particular kvno, so save the previously saved keys as
|
||||
* historical keys.
|
||||
*/
|
||||
keep = 1;
|
||||
|
||||
/* Get an array of Keys to save the current keyset into */
|
||||
ret = get_entry_key_location(context, entry, entry->kvno, TRUE,
|
||||
entry->keys.len, &k);
|
||||
|
||||
for (j = 0; j < entry->keys.len; j++)
|
||||
copy_Key(&entry->keys.val[j], &k[j]);
|
||||
|
||||
/* Change the entry's current kvno */
|
||||
entry->kvno = key_kvno;
|
||||
|
||||
for (j = 0; j < entry->keys.len; j++)
|
||||
free_Key(&entry->keys.val[j]);
|
||||
free(entry->keys.val);
|
||||
entry->keys.len = 0;
|
||||
entry->keys.val = NULL;
|
||||
} else if (entry->kvno == u16)
|
||||
} else if (entry->kvno == key_kvno)
|
||||
/* Accumulate keys */
|
||||
keep = 1;
|
||||
|
||||
if (keep) {
|
||||
Key *k;
|
||||
|
||||
ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
|
||||
if (ptr == NULL) {
|
||||
ret = ENOMEM;
|
||||
ret = get_entry_key_location(context, entry, key_kvno,
|
||||
FALSE, 0, &k);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
entry->keys.val = ptr;
|
||||
|
||||
/* k points to current Key */
|
||||
k = &entry->keys.val[entry->keys.len];
|
||||
|
||||
memset(k, 0, sizeof(*k));
|
||||
entry->keys.len += 1;
|
||||
|
||||
k->mkvno = malloc(sizeof(*k->mkvno));
|
||||
if (k->mkvno == NULL) {
|
||||
ret = ENOMEM;
|
||||
ret = mdb_keyvalue2key(context, entry, sp, version, k);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
*k->mkvno = 1;
|
||||
|
||||
for (j = 0; j < version; j++) {
|
||||
uint16_t type;
|
||||
CHECK(ret = krb5_ret_uint16(sp, &type));
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
if (j == 0) {
|
||||
/* This "version" means we have a key */
|
||||
k->key.keytype = type;
|
||||
if (u16 < 2) {
|
||||
ret = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* MIT stores keys encrypted keys as {16-bit length
|
||||
* of plaintext key, {encrypted key}}. The reason
|
||||
* for this is that the Kerberos cryptosystem is not
|
||||
* length-preserving. Heimdal's approach is to
|
||||
* truncate the plaintext to the expected length of
|
||||
* the key given its enctype, so we ignore this
|
||||
* 16-bit length-of-plaintext-key field.
|
||||
*/
|
||||
krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
|
||||
k->key.keyvalue.length = u16 - 2; /* adjust cipher len */
|
||||
k->key.keyvalue.data = malloc(k->key.keyvalue.length);
|
||||
krb5_storage_read(sp, k->key.keyvalue.data,
|
||||
k->key.keyvalue.length);
|
||||
} else if (j == 1) {
|
||||
/* This "version" means we have a salt */
|
||||
k->salt = calloc(1, sizeof(*k->salt));
|
||||
if (k->salt == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
k->salt->type = type;
|
||||
if (u16 != 0) {
|
||||
k->salt->salt.data = malloc(u16);
|
||||
if (k->salt->salt.data == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
k->salt->salt.length = u16;
|
||||
krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
|
||||
}
|
||||
fix_salt(context, entry, entry->keys.len - 1);
|
||||
} else {
|
||||
/*
|
||||
* Whatever this "version" might be, we skip it
|
||||
*
|
||||
* XXX A krb5.conf parameter requesting that we log
|
||||
* about strangeness like this, or return an error
|
||||
* from here, might be nice.
|
||||
*/
|
||||
krb5_storage_seek(sp, u16, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* XXX For now we skip older kvnos, but we should extract
|
||||
* them...
|
||||
* them... XXX Finish.
|
||||
*/
|
||||
for (j = 0; j < version; j++) {
|
||||
/* enctype */
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
/* encrypted key (or plaintext salt) */
|
||||
CHECK(ret = krb5_ret_uint16(sp, &u16));
|
||||
krb5_storage_seek(sp, u16, SEEK_CUR);
|
||||
}
|
||||
ret = get_entry_key_location(context, entry, key_kvno, TRUE, 0, &k);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = mdb_keyvalue2key(context, entry, sp, version, k);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,12 +648,24 @@ mdb_lock(krb5_context context, HDB *db, int operation)
|
||||
{
|
||||
DB *d = (DB*)db->hdb_db;
|
||||
int fd = (*d->fd)(d);
|
||||
krb5_error_code ret;
|
||||
|
||||
if (db->lock_count > 1) {
|
||||
db->lock_count++;
|
||||
if (db->lock_type == HDB_WLOCK || db->lock_count == operation)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(fd < 0) {
|
||||
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
|
||||
"Can't lock database: %s", db->hdb_name);
|
||||
return HDB_ERR_CANT_LOCK_DB;
|
||||
}
|
||||
return hdb_lock(fd, operation);
|
||||
ret = hdb_lock(fd, operation);
|
||||
if (ret)
|
||||
return ret;
|
||||
db->lock_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
@@ -509,6 +673,14 @@ mdb_unlock(krb5_context context, HDB *db)
|
||||
{
|
||||
DB *d = (DB*)db->hdb_db;
|
||||
int fd = (*d->fd)(d);
|
||||
|
||||
if (db->lock_count > 1) {
|
||||
db->lock_count--;
|
||||
return 0;
|
||||
}
|
||||
heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
|
||||
db->lock_count--;
|
||||
|
||||
if(fd < 0) {
|
||||
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
|
||||
"Can't unlock database: %s", db->hdb_name);
|
||||
@@ -581,19 +753,25 @@ static krb5_error_code
|
||||
mdb_rename(krb5_context context, HDB *db, const char *new_name)
|
||||
{
|
||||
int ret;
|
||||
char *old, *new;
|
||||
char *old = NULL;
|
||||
char *new = NULL;
|
||||
|
||||
asprintf(&old, "%s.db", db->hdb_name);
|
||||
asprintf(&new, "%s.db", new_name);
|
||||
if (asprintf(&old, "%s.db", db->hdb_name) < 0)
|
||||
goto out;
|
||||
if (asprintf(&new, "%s.db", new_name) < 0)
|
||||
goto out;
|
||||
ret = rename(old, new);
|
||||
free(old);
|
||||
free(new);
|
||||
if(ret)
|
||||
return errno;
|
||||
goto out;
|
||||
|
||||
free(db->hdb_name);
|
||||
db->hdb_name = strdup(new_name);
|
||||
return 0;
|
||||
errno = 0;
|
||||
|
||||
out:
|
||||
free(old);
|
||||
free(new);
|
||||
return errno;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
@@ -732,8 +910,7 @@ mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
|
||||
char *fn;
|
||||
krb5_error_code ret;
|
||||
|
||||
asprintf(&fn, "%s.db", db->hdb_name);
|
||||
if (fn == NULL) {
|
||||
if (asprintf(&fn, "%s.db", db->hdb_name) < 0) {
|
||||
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
|
||||
return ENOMEM;
|
||||
}
|
||||
|
@@ -46,8 +46,9 @@ HDBFlags ::= BIT STRING {
|
||||
trusted-for-delegation(14), -- Trusted to print forwardabled tickets
|
||||
allow-kerberos4(15), -- Allow Kerberos 4 requests
|
||||
allow-digest(16), -- Allow digest requests
|
||||
locked-out(17) -- Account is locked out,
|
||||
locked-out(17), -- Account is locked out,
|
||||
-- authentication will be denied
|
||||
do-not-store(31) -- Not to be modified and stored in HDB
|
||||
}
|
||||
|
||||
GENERATION ::= SEQUENCE {
|
||||
@@ -87,6 +88,17 @@ HDB-Ext-Aliases ::= SEQUENCE {
|
||||
aliases[1] SEQUENCE OF Principal -- all names, inc primary
|
||||
}
|
||||
|
||||
Keys ::= SEQUENCE OF Key
|
||||
|
||||
hdb_keyset ::= SEQUENCE {
|
||||
kvno[0] INTEGER (0..4294967295),
|
||||
keys[1] Keys,
|
||||
set-time[2] KerberosTime OPTIONAL, -- time this keyset was created/set
|
||||
...
|
||||
}
|
||||
|
||||
HDB-Ext-KeySet ::= SEQUENCE OF hdb_keyset
|
||||
|
||||
|
||||
HDB-extension ::= SEQUENCE {
|
||||
mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
|
||||
@@ -102,6 +114,10 @@ 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,
|
||||
hist-kvno-diff-clnt[10] INTEGER (0..4294967295),
|
||||
hist-kvno-diff-svc[11] INTEGER (0..4294967295),
|
||||
policy[12] UTF8String,
|
||||
...
|
||||
},
|
||||
...
|
||||
@@ -109,16 +125,11 @@ 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
|
||||
kvno[1] INTEGER (0..4294967295),
|
||||
keys[2] SEQUENCE OF Key,
|
||||
keys[2] Keys,
|
||||
created-by[3] Event,
|
||||
modified-by[4] Event OPTIONAL,
|
||||
valid-start[5] KerberosTime OPTIONAL,
|
||||
|
@@ -168,13 +168,14 @@ hdb_unlock(int fd)
|
||||
void
|
||||
hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
|
||||
{
|
||||
Key *k;
|
||||
size_t 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,10 @@ 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 */
|
||||
#define HDB_F_LIVE_CLNT_KVNOS 512 /* we want all live keys for pre-auth */
|
||||
#define HDB_F_LIVE_SVC_KVNOS 1024 /* we want all live keys for tix */
|
||||
#define HDB_F_ALL_KVNOS 2048 /* we want all the keys, live or not */
|
||||
|
||||
/* hdb_capability_flags */
|
||||
#define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1
|
||||
@@ -102,6 +106,8 @@ typedef struct HDB{
|
||||
hdb_master_key hdb_master_key;
|
||||
int hdb_openp;
|
||||
int hdb_capability_flags;
|
||||
int lock_count;
|
||||
int lock_type;
|
||||
/**
|
||||
* Open (or create) the a Kerberos database.
|
||||
*
|
||||
|
@@ -26,5 +26,6 @@ error_code NO_MKEY, "No correct master key"
|
||||
error_code MANDATORY_OPTION, "Entry contains unknown mandatory extension"
|
||||
error_code NO_WRITE_SUPPORT, "HDB backend doesn't contain write support"
|
||||
error_code NOT_FOUND_HERE, "The secret for this entry is not replicated to this database"
|
||||
error_code MISUSE, "Incorrect use of the API"
|
||||
|
||||
end
|
||||
|
@@ -36,6 +36,9 @@
|
||||
#ifndef __HDB_LOCL_H__
|
||||
#define __HDB_LOCL_H__
|
||||
|
||||
#include <assert.h>
|
||||
#include <heimbase.h>
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
140
lib/hdb/keys.c
140
lib/hdb/keys.c
@@ -39,9 +39,9 @@
|
||||
*/
|
||||
|
||||
void
|
||||
hdb_free_keys (krb5_context context, int len, Key *keys)
|
||||
hdb_free_keys(krb5_context context, int len, Key *keys)
|
||||
{
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
free(keys[i].mkvno);
|
||||
@@ -196,6 +196,81 @@ parse_key_set(krb5_context context, const char *key,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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_error_code ret;
|
||||
HDB_extension *ext;
|
||||
HDB_Ext_KeySet *hist_keys;
|
||||
hdb_keyset *tmp_keysets;
|
||||
size_t i;
|
||||
size_t replace = 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 {
|
||||
replace = 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.len = 0;
|
||||
hist_keys->val[0].keys.val = calloc(entry->keys.len,
|
||||
sizeof (*hist_keys->val[0].keys.val));
|
||||
for (i = 0; i < entry->keys.len; i++, hist_keys->val[0].keys.len++) {
|
||||
ret = copy_Key(&entry->keys.val[i], &hist_keys->val[0].keys.val[i]);
|
||||
if (ret) {
|
||||
free_HDB_extension(ext);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
hist_keys->val[0].kvno = entry->kvno;
|
||||
hist_keys->val[0].set_time = malloc(sizeof (*hist_keys->val[0].set_time));
|
||||
if (hist_keys->val[0].set_time == NULL) {
|
||||
free_HDB_extension(ext);
|
||||
return ENOMEM;
|
||||
}
|
||||
(void) hdb_entry_get_pw_change_time(entry, hist_keys->val[0].set_time);
|
||||
|
||||
if (replace) {
|
||||
/* hdb_replace_extension() deep-copies ext; what a waste */
|
||||
ret = hdb_replace_extension(context, entry, ext);
|
||||
if (ret) {
|
||||
free_HDB_extension(ext);
|
||||
return ret;
|
||||
}
|
||||
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)
|
||||
@@ -243,6 +318,50 @@ add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
krb5_error_code
|
||||
ks_tuple2str(krb5_context context, int n_ks_tuple,
|
||||
krb5_key_salt_tuple *ks_tuple, char ***ks_tuple_strs)
|
||||
{
|
||||
size_t i;
|
||||
char **ksnames;
|
||||
char *ename, *sname;
|
||||
krb5_error_code rc = KRB5_PROG_ETYPE_NOSUPP;
|
||||
|
||||
*ks_tuple_strs = NULL;
|
||||
if (n_ks_tuple < 1)
|
||||
return 0;
|
||||
|
||||
if ((ksnames = calloc(n_ks_tuple, sizeof (*ksnames))) == NULL)
|
||||
return (errno);
|
||||
|
||||
for (i = 0; i < n_ks_tuple; i++) {
|
||||
if (krb5_enctype_to_string(context, ks_tuple[i].ks_enctype, &ename))
|
||||
goto out;
|
||||
if (krb5_salttype_to_string(context, ks_tuple[i].ks_enctype,
|
||||
ks_tuple[i].ks_salttype, &sname))
|
||||
goto out;
|
||||
|
||||
if (asprintf(&ksnames[i], "%s:%s", ename, sname) == -1) {
|
||||
rc = errno;
|
||||
free(ename);
|
||||
free(sname);
|
||||
goto out;
|
||||
}
|
||||
free(ename);
|
||||
free(sname);
|
||||
}
|
||||
|
||||
*ks_tuple_strs = ksnames;
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
for (i = 0; i < n_ks_tuple; i++)
|
||||
free(ksnames[i]);
|
||||
free(ksnames);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the `key_set' from the [kadmin]default_keys statement. If
|
||||
* `no_salt' is set, salt is not important (and will not be set) since
|
||||
@@ -251,12 +370,15 @@ add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
|
||||
|
||||
krb5_error_code
|
||||
hdb_generate_key_set(krb5_context context, krb5_principal principal,
|
||||
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
|
||||
Key **ret_key_set, size_t *nkeyset, int no_salt)
|
||||
{
|
||||
char **ktypes, **kp;
|
||||
char **ktypes = NULL;
|
||||
char **kp;
|
||||
krb5_error_code ret;
|
||||
Key *k, *key_set;
|
||||
size_t i, j;
|
||||
char **ks_tuple_strs;
|
||||
static const char *default_keytypes[] = {
|
||||
"aes256-cts-hmac-sha1-96:pw-salt",
|
||||
"des3-cbc-sha1:pw-salt",
|
||||
@@ -264,16 +386,18 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal,
|
||||
NULL
|
||||
};
|
||||
|
||||
ktypes = krb5_config_get_strings(context, NULL, "kadmin",
|
||||
"default_keys", NULL);
|
||||
if ((ret = ks_tuple2str(context, n_ks_tuple, ks_tuple, &ks_tuple_strs)))
|
||||
return ret;
|
||||
|
||||
if (ks_tuple_strs == NULL)
|
||||
ktypes = krb5_config_get_strings(context, NULL, "kadmin",
|
||||
"default_keys", NULL);
|
||||
if (ktypes == NULL)
|
||||
ktypes = (char **)(intptr_t)default_keytypes;
|
||||
|
||||
*ret_key_set = key_set = NULL;
|
||||
*nkeyset = 0;
|
||||
|
||||
ret = 0;
|
||||
|
||||
for(kp = ktypes; kp && *kp; kp++) {
|
||||
const char *p;
|
||||
krb5_salt salt;
|
||||
@@ -366,7 +490,7 @@ hdb_generate_key_set_password(krb5_context context,
|
||||
krb5_error_code ret;
|
||||
size_t i;
|
||||
|
||||
ret = hdb_generate_key_set(context, principal,
|
||||
ret = hdb_generate_key_set(context, principal, 0, NULL,
|
||||
keys, num_keys, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
148
lib/hdb/mkey.c
148
lib/hdb/mkey.c
@@ -479,6 +479,131 @@ 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,
|
||||
unsigned flags, hdb_entry *ent)
|
||||
{
|
||||
krb5_error_code ret = HDB_ERR_NOENTRY;
|
||||
HDB_extension *ext;
|
||||
HDB_Ext_KeySet *hist_keys;
|
||||
Key *tmp_val;
|
||||
time_t tmp_set_time;
|
||||
unsigned int tmp_len;
|
||||
unsigned int kvno_diff = 0;
|
||||
krb5_kvno tmp_kvno;
|
||||
size_t i, k;
|
||||
int exclude_dead = 0;
|
||||
KerberosTime now = 0;
|
||||
time_t *set_time;
|
||||
|
||||
if (kvno == 0)
|
||||
ret = 0;
|
||||
|
||||
if ((flags & HDB_F_LIVE_CLNT_KVNOS) || (flags & HDB_F_LIVE_SVC_KVNOS)) {
|
||||
exclude_dead = 1;
|
||||
now = time(NULL);
|
||||
if (HDB_F_LIVE_CLNT_KVNOS)
|
||||
kvno_diff = hdb_entry_get_kvno_diff_clnt(ent);
|
||||
else
|
||||
kvno_diff = hdb_entry_get_kvno_diff_svc(ent);
|
||||
}
|
||||
|
||||
ext = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
|
||||
if (ext == NULL)
|
||||
return ret;
|
||||
|
||||
/* For swapping; see below */
|
||||
tmp_len = ent->keys.len;
|
||||
tmp_val = ent->keys.val;
|
||||
tmp_kvno = ent->kvno;
|
||||
(void) hdb_entry_get_pw_change_time(ent, &tmp_set_time);
|
||||
|
||||
hist_keys = &ext->data.u.hist_keys;
|
||||
|
||||
for (i = 0; i < hist_keys->len; i++) {
|
||||
if (kvno != 0 && hist_keys->val[i].kvno != kvno)
|
||||
continue;
|
||||
|
||||
if (exclude_dead &&
|
||||
((ent->max_life != NULL &&
|
||||
hist_keys->val[i].set_time != NULL &&
|
||||
(*hist_keys->val[i].set_time) < (now - (*ent->max_life))) ||
|
||||
(hist_keys->val[i].kvno < kvno &&
|
||||
(kvno - hist_keys->val[i].kvno) > kvno_diff)))
|
||||
/*
|
||||
* The KDC may want to to check for this keyset's set_time
|
||||
* is within the TGS principal's max_life, say. But we stop
|
||||
* here.
|
||||
*/
|
||||
continue;
|
||||
|
||||
/* Either the keys we want, or all the keys */
|
||||
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 kvno == 0 we might not want to bail here! E.g., if we
|
||||
* no longer have the right master key, so just ignore this.
|
||||
*
|
||||
* We could filter out keys that we can't decrypt here
|
||||
* because of HDB_ERR_NO_MKEY. However, it seems safest to
|
||||
* filter them out only where necessary, say, in kadm5.
|
||||
*/
|
||||
if (ret && kvno != 0)
|
||||
return ret;
|
||||
if (ret && ret != HDB_ERR_NO_MKEY)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (kvno == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* What follows is a bit of a hack.
|
||||
*
|
||||
* This is the keyset we're being asked for, but it's not the
|
||||
* current keyset. 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.
|
||||
*/
|
||||
set_time = malloc(sizeof (*set_time));
|
||||
if (set_time == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
/* Swap key sets */
|
||||
ent->kvno = hist_keys->val[i].kvno;
|
||||
ent->keys.val = hist_keys->val[i].keys.val;
|
||||
ent->keys.len = hist_keys->val[i].keys.len;
|
||||
if (hist_keys->val[i].set_time != NULL)
|
||||
/* Sloppy, but the callers we expect won't care */
|
||||
(void) hdb_entry_set_pw_change_time(context, ent,
|
||||
*hist_keys->val[i].set_time);
|
||||
hist_keys->val[i].kvno = tmp_kvno;
|
||||
hist_keys->val[i].keys.val = tmp_val;
|
||||
hist_keys->val[i].keys.len = tmp_len;
|
||||
if (hist_keys->val[i].set_time != NULL)
|
||||
/* Sloppy, but the callers we expect won't care */
|
||||
*hist_keys->val[i].set_time = tmp_set_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
hdb_unseal_key(krb5_context context, HDB *db, Key *k)
|
||||
{
|
||||
@@ -526,14 +651,31 @@ hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
|
||||
krb5_error_code
|
||||
hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < ent->keys.len; i++){
|
||||
krb5_error_code ret;
|
||||
HDB_extension *ext;
|
||||
HDB_Ext_KeySet *hist_keys;
|
||||
size_t i, k;
|
||||
krb5_error_code ret;
|
||||
|
||||
for(i = 0; i < ent->keys.len; i++){
|
||||
ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ext = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
|
||||
if (ext == NULL)
|
||||
return 0;
|
||||
hist_keys = &ext->data.u.hist_keys;
|
||||
|
||||
for (i = 0; i < hist_keys->len; i++) {
|
||||
for (k = 0; k < hist_keys->val[i].keys.len; k++) {
|
||||
ret = hdb_seal_key_mkey(context, &hist_keys->val[i].keys.val[k],
|
||||
mkey);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -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,10 @@ main(int argc, char **argv)
|
||||
memset(&keyset, 0, sizeof(keyset));
|
||||
|
||||
keyset.kvno = kvno_integer;
|
||||
keyset.set_time = malloc(sizeof (*keyset.set_time));
|
||||
if (keyset.set_time == NULL)
|
||||
errx(1, "couldn't allocate set_time field of keyset");
|
||||
*keyset.set_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;
|
||||
@@ -74,33 +75,42 @@ HEIMDAL_HDB_1.0 {
|
||||
hdb_kt_ops;
|
||||
|
||||
# some random bits needed for libkadm
|
||||
HDBFlags2int;
|
||||
add_HDB_Ext_KeySet;
|
||||
add_Keys;
|
||||
asn1_HDBFlags_units;
|
||||
copy_Event;
|
||||
copy_HDB_extensions;
|
||||
copy_Key;
|
||||
copy_Keys;
|
||||
copy_Salt;
|
||||
decode_HDB_Ext_Aliases;
|
||||
decode_HDB_Ext_PKINIT_acl;
|
||||
decode_HDB_extension;
|
||||
decode_HDB_Ext_PKINIT_acl;
|
||||
decode_Key;
|
||||
decode_Keys;
|
||||
encode_HDB_Ext_Aliases;
|
||||
encode_HDB_Ext_PKINIT_acl;
|
||||
encode_HDB_extension;
|
||||
encode_HDB_Ext_PKINIT_acl;
|
||||
encode_Key;
|
||||
encode_Keys;
|
||||
free_Event;
|
||||
free_hdb_entry;
|
||||
free_HDB_Ext_Aliases;
|
||||
free_HDB_Ext_PKINIT_acl;
|
||||
free_HDB_extension;
|
||||
free_HDB_extensions;
|
||||
free_HDB_Ext_PKINIT_acl;
|
||||
free_hdb_keyset;
|
||||
free_Key;
|
||||
free_Keys;
|
||||
free_Salt;
|
||||
free_hdb_entry;
|
||||
HDBFlags2int;
|
||||
int2HDBFlags;
|
||||
length_HDB_Ext_Aliases;
|
||||
length_HDB_Ext_PKINIT_acl;
|
||||
length_HDB_extension;
|
||||
length_HDB_Ext_PKINIT_acl;
|
||||
length_Key;
|
||||
length_Keys;
|
||||
remove_Keys;
|
||||
|
||||
local:
|
||||
*;
|
||||
|
Reference in New Issue
Block a user