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:
|
||||
*;
|
||||
|
@@ -44,6 +44,7 @@ static struct units acl_units[] = {
|
||||
{ "modify", KADM5_PRIV_MODIFY },
|
||||
{ "add", KADM5_PRIV_ADD },
|
||||
{ "get", KADM5_PRIV_GET },
|
||||
{ "get-keys", KADM5_PRIV_GET_KEYS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
@@ -177,6 +178,8 @@ check_flags (unsigned op,
|
||||
|
||||
if(res & KADM5_PRIV_GET)
|
||||
return KADM5_AUTH_GET;
|
||||
if(res & KADM5_PRIV_GET_KEYS)
|
||||
return KADM5_AUTH_GET_KEYS;
|
||||
if(res & KADM5_PRIV_ADD)
|
||||
return KADM5_AUTH_ADD;
|
||||
if(res & KADM5_PRIV_MODIFY)
|
||||
|
@@ -508,6 +508,7 @@ ad_get_cred(kadm5_ad_context *context, const char *password)
|
||||
static kadm5_ret_t
|
||||
kadm5_ad_chpass_principal(void *server_handle,
|
||||
krb5_principal principal,
|
||||
int keepold,
|
||||
const char *password)
|
||||
{
|
||||
kadm5_ad_context *context = server_handle;
|
||||
@@ -515,6 +516,9 @@ kadm5_ad_chpass_principal(void *server_handle,
|
||||
int result_code;
|
||||
kadm5_ret_t ret;
|
||||
|
||||
if (keepold)
|
||||
return KADM5_KEEPOLD_NOSUPP;
|
||||
|
||||
ret = ad_get_cred(context, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1224,14 +1228,21 @@ kadm5_ad_modify_principal(void *server_handle,
|
||||
#endif
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static kadm5_ret_t
|
||||
kadm5_ad_randkey_principal(void *server_handle,
|
||||
krb5_principal principal,
|
||||
krb5_boolean keepold,
|
||||
int n_ks_tuple,
|
||||
krb5_key_salt_tuple *ks_tuple,
|
||||
krb5_keyblock **keys,
|
||||
int *n_keys)
|
||||
{
|
||||
kadm5_ad_context *context = server_handle;
|
||||
|
||||
if (keepold)
|
||||
return KADM5_KEEPOLD_NOSUPP;
|
||||
|
||||
/*
|
||||
* random key
|
||||
*/
|
||||
@@ -1321,6 +1332,7 @@ kadm5_ad_rename_principal(void *server_handle,
|
||||
static kadm5_ret_t
|
||||
kadm5_ad_chpass_principal_with_key(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
int n_key_data,
|
||||
krb5_key_data *key_data)
|
||||
{
|
||||
@@ -1329,6 +1341,18 @@ kadm5_ad_chpass_principal_with_key(void *server_handle,
|
||||
return KADM5_RPC_ERROR;
|
||||
}
|
||||
|
||||
static kadm5_ret_t
|
||||
kadm5_ad_lock(void *server_handle)
|
||||
{
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
static kadm5_ret_t
|
||||
kadm5_ad_unlock(void *server_handle)
|
||||
{
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
static void
|
||||
set_funcs(kadm5_ad_context *c)
|
||||
{
|
||||
@@ -1345,6 +1369,8 @@ set_funcs(kadm5_ad_context *c)
|
||||
SET(c, modify_principal);
|
||||
SET(c, randkey_principal);
|
||||
SET(c, rename_principal);
|
||||
SET(c, lock);
|
||||
SET(c, unlock);
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
|
@@ -112,6 +112,11 @@ typedef struct {
|
||||
void* key_data_contents[2];/* Array of pointers */
|
||||
} krb5_key_data;
|
||||
|
||||
typedef struct _krb5_keysalt {
|
||||
int16_t type;
|
||||
krb5_data data; /* Length, data */
|
||||
} krb5_keysalt;
|
||||
|
||||
typedef struct _krb5_tl_data {
|
||||
struct _krb5_tl_data* tl_data_next;
|
||||
int16_t tl_data_type;
|
||||
@@ -129,6 +134,8 @@ typedef struct _krb5_tl_data {
|
||||
#define KRB5_TL_EXTENSION 0x0008
|
||||
#define KRB5_TL_PKINIT_ACL 0x0009
|
||||
#define KRB5_TL_ALIASES 0x000a
|
||||
#define KRB5_TL_HIST_KVNO_DIFF_CLNT 0x000b
|
||||
#define KRB5_TL_HIST_KVNO_DIFF_SVC 0x000c
|
||||
|
||||
typedef struct _kadm5_principal_ent_t {
|
||||
krb5_principal principal;
|
||||
@@ -193,12 +200,9 @@ typedef struct _kadm5_policy_ent_t {
|
||||
#define KADM5_PRIV_DELETE (1 << 3)
|
||||
#define KADM5_PRIV_LIST (1 << 4)
|
||||
#define KADM5_PRIV_CPW (1 << 5)
|
||||
#define KADM5_PRIV_GET_KEYS (1 << 6)
|
||||
#define KADM5_PRIV_ALL (KADM5_PRIV_GET | KADM5_PRIV_ADD | KADM5_PRIV_MODIFY | KADM5_PRIV_DELETE | KADM5_PRIV_LIST | KADM5_PRIV_CPW)
|
||||
|
||||
typedef struct {
|
||||
int XXX;
|
||||
}krb5_key_salt_tuple;
|
||||
|
||||
typedef struct _kadm5_config_params {
|
||||
uint32_t mask;
|
||||
|
||||
@@ -221,38 +225,4 @@ typedef krb5_error_code kadm5_ret_t;
|
||||
|
||||
#include "kadm5-protos.h"
|
||||
|
||||
#if 0
|
||||
/* unimplemented functions */
|
||||
kadm5_ret_t
|
||||
kadm5_decrypt_key(void *server_handle,
|
||||
kadm5_principal_ent_t entry, int32_t
|
||||
ktype, int32_t stype, int32_t
|
||||
kvno, krb5_keyblock *keyblock,
|
||||
krb5_keysalt *keysalt, int *kvnop);
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_create_policy(void *server_handle,
|
||||
kadm5_policy_ent_t policy, uint32_t mask);
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_delete_policy(void *server_handle, char *policy);
|
||||
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_modify_policy(void *server_handle,
|
||||
kadm5_policy_ent_t policy,
|
||||
uint32_t mask);
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_get_policy(void *server_handle, char *policy, kadm5_policy_ent_t ent);
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_get_policies(void *server_handle, char *exp,
|
||||
char ***pols, int *count);
|
||||
|
||||
void
|
||||
kadm5_free_policy_ent(kadm5_policy_ent_t policy);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __KADM5_ADMIN_H__ */
|
||||
|
@@ -38,6 +38,7 @@ RCSID("$Id$");
|
||||
kadm5_ret_t
|
||||
kadm5_c_chpass_principal(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
const char *password)
|
||||
{
|
||||
kadm5_client_context *context = server_handle;
|
||||
@@ -59,6 +60,7 @@ kadm5_c_chpass_principal(void *server_handle,
|
||||
krb5_store_int32(sp, kadm_chpass);
|
||||
krb5_store_principal(sp, princ);
|
||||
krb5_store_string(sp, password);
|
||||
krb5_store_int32(sp, keepold); /* extension */
|
||||
ret = _kadm5_client_send(context, sp);
|
||||
krb5_storage_free(sp);
|
||||
if (ret)
|
||||
@@ -82,6 +84,7 @@ kadm5_c_chpass_principal(void *server_handle,
|
||||
kadm5_ret_t
|
||||
kadm5_c_chpass_principal_with_key(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
int n_key_data,
|
||||
krb5_key_data *key_data)
|
||||
{
|
||||
@@ -107,6 +110,7 @@ kadm5_c_chpass_principal_with_key(void *server_handle,
|
||||
krb5_store_int32(sp, n_key_data);
|
||||
for (i = 0; i < n_key_data; ++i)
|
||||
kadm5_store_key_data (sp, &key_data[i]);
|
||||
krb5_store_int32(sp, keepold); /* extension */
|
||||
ret = _kadm5_client_send(context, sp);
|
||||
krb5_storage_free(sp);
|
||||
if (ret)
|
||||
|
@@ -38,6 +38,7 @@ RCSID("$Id$");
|
||||
static kadm5_ret_t
|
||||
change(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
const char *password,
|
||||
int cond)
|
||||
{
|
||||
@@ -49,15 +50,27 @@ change(void *server_handle,
|
||||
int existsp = 0;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
return ret;
|
||||
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);
|
||||
@@ -73,15 +86,20 @@ change(void *server_handle,
|
||||
|
||||
ret = _kadm5_set_keys(context, &ent.entry, password);
|
||||
if(ret) {
|
||||
_kadm5_free_keys (context->context, num_keys, keys);
|
||||
_kadm5_free_keys(context->context, num_keys, keys);
|
||||
goto out2;
|
||||
}
|
||||
_kadm5_free_keys(context->context, num_keys, keys);
|
||||
|
||||
if (cond)
|
||||
existsp = _kadm5_exists_keys (ent.entry.keys.val,
|
||||
ent.entry.keys.len,
|
||||
keys, num_keys);
|
||||
_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;
|
||||
@@ -89,12 +107,23 @@ change(void *server_handle,
|
||||
"Password reuse forbidden");
|
||||
goto out2;
|
||||
}
|
||||
}
|
||||
ent.entry.kvno++;
|
||||
|
||||
if (keepold) {
|
||||
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
} else {
|
||||
HDB_extension 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;
|
||||
}
|
||||
ent.entry.kvno++;
|
||||
|
||||
ret = _kadm5_set_modifier(context, &ent.entry);
|
||||
if(ret)
|
||||
@@ -118,7 +147,8 @@ change(void *server_handle,
|
||||
out2:
|
||||
hdb_free_entry(context->context, &ent);
|
||||
out:
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
return _kadm5_error_code(ret);
|
||||
}
|
||||
|
||||
@@ -131,9 +161,10 @@ out:
|
||||
kadm5_ret_t
|
||||
kadm5_s_chpass_principal_cond(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
const char *password)
|
||||
{
|
||||
return change (server_handle, princ, password, 1);
|
||||
return change (server_handle, princ, keepold, password, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -143,9 +174,10 @@ kadm5_s_chpass_principal_cond(void *server_handle,
|
||||
kadm5_ret_t
|
||||
kadm5_s_chpass_principal(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
const char *password)
|
||||
{
|
||||
return change (server_handle, princ, password, 0);
|
||||
return change (server_handle, princ, keepold, password, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -155,6 +187,7 @@ kadm5_s_chpass_principal(void *server_handle,
|
||||
kadm5_ret_t
|
||||
kadm5_s_chpass_principal_with_key(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
int n_key_data,
|
||||
krb5_key_data *key_data)
|
||||
{
|
||||
@@ -163,13 +196,20 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
|
||||
kadm5_ret_t ret;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
return ret;
|
||||
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, 0,
|
||||
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent);
|
||||
if(ret == HDB_ERR_NOENTRY)
|
||||
goto out;
|
||||
if (keepold) {
|
||||
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;
|
||||
@@ -181,9 +221,19 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
if (keepold) {
|
||||
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
} else {
|
||||
HDB_extension ext;
|
||||
|
||||
ext.data.element = choice_HDB_extension_data_hist_keys;
|
||||
ext.data.u.hist_keys.len = 0;
|
||||
ext.data.u.hist_keys.val = NULL;
|
||||
hdb_replace_extension(context->context, &ent.entry, &ext);
|
||||
}
|
||||
|
||||
|
||||
ret = context->db->hdb_store(context->context, context->db,
|
||||
HDB_F_REPLACE, &ent);
|
||||
@@ -199,6 +249,7 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
|
||||
out2:
|
||||
hdb_free_entry(context->context, &ent);
|
||||
out:
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
return _kadm5_error_code(ret);
|
||||
}
|
||||
|
@@ -42,7 +42,25 @@ kadm5_chpass_principal(void *server_handle,
|
||||
krb5_principal princ,
|
||||
const char *password)
|
||||
{
|
||||
return __CALL(chpass_principal, (server_handle, princ, password));
|
||||
return __CALL(chpass_principal, (server_handle, princ, 0, password));
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_chpass_principal_3(void *server_handle,
|
||||
krb5_principal princ,
|
||||
krb5_boolean keepold,
|
||||
int n_ks_tuple,
|
||||
krb5_key_salt_tuple *ks_tuple,
|
||||
const char *password)
|
||||
{
|
||||
/*
|
||||
* We should get around to implementing this... This can be useful
|
||||
* for, e.g., x-realm principals. For now we need the _3() to get
|
||||
* certain applications written to the kadm5 API to build and run.
|
||||
*/
|
||||
if (n_ks_tuple > 0)
|
||||
return KADM5_KS_TUPLE_NOSUPP;
|
||||
return __CALL(chpass_principal, (server_handle, princ, keepold, password));
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
@@ -52,7 +70,18 @@ kadm5_chpass_principal_with_key(void *server_handle,
|
||||
krb5_key_data *key_data)
|
||||
{
|
||||
return __CALL(chpass_principal_with_key,
|
||||
(server_handle, princ, n_key_data, key_data));
|
||||
(server_handle, princ, 0, n_key_data, key_data));
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_chpass_principal_with_key_3(void *server_handle,
|
||||
krb5_principal princ,
|
||||
int keepold,
|
||||
int n_key_data,
|
||||
krb5_key_data *key_data)
|
||||
{
|
||||
return __CALL(chpass_principal_with_key,
|
||||
(server_handle, princ, keepold, n_key_data, key_data));
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
@@ -92,6 +121,49 @@ kadm5_get_principal(void *server_handle,
|
||||
return __CALL(get_principal, (server_handle, princ, out, mask));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract decrypted keys from kadm5_principal_ent_t object. Mostly a
|
||||
* no-op for Heimdal because we fetch the entry with decrypted keys.
|
||||
* Sadly this is not fully a no-op, as we have to allocate a copy.
|
||||
*
|
||||
* @server_handle is the kadm5 handle
|
||||
* @entry is the HDB entry for the principal in question
|
||||
* @ktype is the enctype to get a key for, or -1 to get the first one
|
||||
* @stype is the salttype to get a key for, or -1 to get the first match
|
||||
* @kvno is the kvno to search for, or -1 to get the first match (highest kvno)
|
||||
* @keyblock is where the key will be placed
|
||||
* @keysalt, if not NULL, is where the salt will be placed
|
||||
* @kvnop, if not NULL, is where the selected kvno will be placed
|
||||
*/
|
||||
kadm5_ret_t
|
||||
kadm5_decrypt_key(void *server_handle,
|
||||
kadm5_principal_ent_t entry,
|
||||
int32_t ktype, int32_t stype,
|
||||
int32_t kvno, krb5_keyblock *keyblock,
|
||||
krb5_keysalt *keysalt, int *kvnop)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (kvno < 1 || stype != -1)
|
||||
return KADM5_DECRYPT_USAGE_NOSUPP;
|
||||
|
||||
for (i = 0; i < entry->n_key_data; i++) {
|
||||
if (ktype != entry->key_data[i].key_data_kvno)
|
||||
continue;
|
||||
|
||||
keyblock->keytype = ktype;
|
||||
keyblock->keyvalue.length = entry->key_data[i].key_data_length[0];
|
||||
keyblock->keyvalue.data = malloc(keyblock->keyvalue.length);
|
||||
if (keyblock->keyvalue.data == NULL)
|
||||
return ENOMEM;
|
||||
memcpy(keyblock->keyvalue.data,
|
||||
entry->key_data[i].key_data_contents[0],
|
||||
keyblock->keyvalue.length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_modify_principal(void *server_handle,
|
||||
kadm5_principal_ent_t princ,
|
||||
@@ -106,7 +178,21 @@ kadm5_randkey_principal(void *server_handle,
|
||||
krb5_keyblock **new_keys,
|
||||
int *n_keys)
|
||||
{
|
||||
return __CALL(randkey_principal, (server_handle, princ, new_keys, n_keys));
|
||||
return __CALL(randkey_principal, (server_handle, princ, FALSE, 0, NULL,
|
||||
new_keys, n_keys));
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_randkey_principal_3(void *server_handle,
|
||||
krb5_principal princ,
|
||||
krb5_boolean keepold,
|
||||
int n_ks_tuple,
|
||||
krb5_key_salt_tuple *ks_tuple,
|
||||
krb5_keyblock **new_keys,
|
||||
int *n_keys)
|
||||
{
|
||||
return __CALL(randkey_principal, (server_handle, princ, keepold,
|
||||
n_ks_tuple, ks_tuple, new_keys, n_keys));
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
@@ -132,3 +218,188 @@ kadm5_get_privs(void *server_handle,
|
||||
{
|
||||
return __CALL(get_privs, (server_handle, privs));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is allows the caller to set new keys for a principal.
|
||||
* This is a trivial wrapper around kadm5_setkey_principal_3().
|
||||
*/
|
||||
kadm5_ret_t
|
||||
kadm5_setkey_principal(void *server_handle,
|
||||
krb5_principal princ,
|
||||
krb5_keyblock *new_keys,
|
||||
int n_keys)
|
||||
{
|
||||
return kadm5_setkey_principal_3(server_handle, princ, 0, 0, NULL,
|
||||
new_keys, n_keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is allows the caller to set new keys for a principal.
|
||||
* This is a simple wrapper around kadm5_get_principal() and
|
||||
* kadm5_modify_principal().
|
||||
*/
|
||||
kadm5_ret_t
|
||||
kadm5_setkey_principal_3(void *server_handle,
|
||||
krb5_principal princ,
|
||||
krb5_boolean keepold,
|
||||
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
|
||||
krb5_keyblock *keyblocks,
|
||||
int n_keys)
|
||||
{
|
||||
kadm5_principal_ent_rec princ_ent;
|
||||
kadm5_ret_t ret;
|
||||
krb5_key_data *new_key_data = NULL;
|
||||
size_t i;
|
||||
|
||||
if (n_keys < 1)
|
||||
return EINVAL;
|
||||
if (n_ks_tuple > 0 && n_ks_tuple != n_keys)
|
||||
return KADM5_SETKEY3_ETYPE_MISMATCH;
|
||||
|
||||
ret = kadm5_get_principal(server_handle, princ, &princ_ent,
|
||||
KADM5_KVNO | KADM5_PRINCIPAL | KADM5_KEY_DATA);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (keepold) {
|
||||
new_key_data = malloc((n_keys + princ_ent.n_key_data) * sizeof(*new_key_data));
|
||||
if (new_key_data == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(&new_key_data[n_keys], &princ_ent.key_data[0],
|
||||
princ_ent.n_key_data * sizeof (princ_ent.key_data[0]));
|
||||
} else {
|
||||
new_key_data = malloc(n_keys * sizeof(*new_key_data));
|
||||
if (new_key_data == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
princ_ent.kvno++;
|
||||
for (i = 0; i < n_keys; i++) {
|
||||
new_key_data[i].key_data_ver = 2;
|
||||
|
||||
/* Key */
|
||||
new_key_data[i].key_data_kvno = princ_ent.kvno;
|
||||
new_key_data[i].key_data_type[0] = keyblocks[i].keytype;
|
||||
new_key_data[i].key_data_length[0] = keyblocks[i].keyvalue.length;
|
||||
new_key_data[i].key_data_contents[0] =
|
||||
malloc(keyblocks[i].keyvalue.length);
|
||||
if (new_key_data[i].key_data_contents[0] == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
memcpy(new_key_data[i].key_data_contents[0],
|
||||
keyblocks[i].keyvalue.data,
|
||||
keyblocks[i].keyvalue.length);
|
||||
|
||||
/*
|
||||
* Salt (but there's no salt, just salttype, which is kinda
|
||||
* silly -- what's the point of setkey_3() then, besides
|
||||
* keepold?!)
|
||||
*/
|
||||
new_key_data[i].key_data_type[1] = 0;
|
||||
if (n_ks_tuple > 0) {
|
||||
if (ks_tuple[i].ks_enctype != keyblocks[i].keytype)
|
||||
return KADM5_SETKEY3_ETYPE_MISMATCH;
|
||||
new_key_data[i].key_data_type[1] = ks_tuple[i].ks_salttype;
|
||||
}
|
||||
new_key_data[i].key_data_length[1] = 0;
|
||||
new_key_data[i].key_data_contents[1] = NULL;
|
||||
}
|
||||
|
||||
/* Free old keys */
|
||||
if (!keepold) {
|
||||
for (i = 0; i < princ_ent.n_key_data; i++) {
|
||||
free(princ_ent.key_data[i].key_data_contents[0]);
|
||||
free(princ_ent.key_data[i].key_data_contents[1]);
|
||||
}
|
||||
}
|
||||
free(princ_ent.key_data);
|
||||
princ_ent.key_data = new_key_data;
|
||||
princ_ent.n_key_data = n_keys + (keepold ? princ_ent.n_key_data : 0);
|
||||
new_key_data = NULL;
|
||||
|
||||
/* Modify the principal */
|
||||
ret = kadm5_modify_principal(server_handle, &princ_ent, KADM5_KVNO | KADM5_KEY_DATA);
|
||||
|
||||
out:
|
||||
if (new_key_data != NULL) {
|
||||
for (i = 0; i < n_keys; i++) {
|
||||
free(new_key_data[i].key_data_contents[0]);
|
||||
free(new_key_data[i].key_data_contents[1]);
|
||||
}
|
||||
free(new_key_data);
|
||||
}
|
||||
kadm5_free_principal_ent(server_handle, &princ_ent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_lock(void *server_handle)
|
||||
{
|
||||
return __CALL(lock, (server_handle));
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_unlock(void *server_handle)
|
||||
{
|
||||
return __CALL(unlock, (server_handle));
|
||||
}
|
||||
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_create_policy(void *server_handle,
|
||||
kadm5_policy_ent_t policy, long mask)
|
||||
{
|
||||
return KADM5_POLICY_OP_NOSUPP;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_delete_policy(void *server_handle, char *name)
|
||||
{
|
||||
return KADM5_POLICY_OP_NOSUPP;
|
||||
}
|
||||
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_modify_policy(void *server_handle, kadm5_policy_ent_t policy,
|
||||
uint32_t mask)
|
||||
{
|
||||
return KADM5_POLICY_OP_NOSUPP;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_get_policy(void *server_handle, char *policy, kadm5_policy_ent_t ent)
|
||||
{
|
||||
memset(ent, 0, sizeof (*ent));
|
||||
return KADM5_POLICY_OP_NOSUPP;
|
||||
}
|
||||
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_get_policies(void *server_handle, char *exp, char ***pols, int *count)
|
||||
{
|
||||
*count = 0;
|
||||
*pols = NULL;
|
||||
|
||||
return KADM5_POLICY_OP_NOSUPP;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_free_policy_ent(kadm5_policy_ent_t ent)
|
||||
{
|
||||
if (ent->policy)
|
||||
free(ent->policy);
|
||||
/*
|
||||
* Not clear if we should free ent or not. It might be an automatic
|
||||
* struct, so we don't free it for now, just in case.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,53 @@
|
||||
|
||||
RCSID("$Id$");
|
||||
|
||||
static kadm5_ret_t
|
||||
kadm5_s_lock(void *server_handle)
|
||||
{
|
||||
kadm5_server_context *context = server_handle;
|
||||
kadm5_ret_t ret;
|
||||
|
||||
if (context->keep_open) {
|
||||
/*
|
||||
* We open/close around every operation, but we retain the DB
|
||||
* open if the DB was locked with a prior call to kadm5_lock(),
|
||||
* so if it's open here that must be because the DB is locked.
|
||||
*/
|
||||
heim_assert(context->db->lock_count > 0,
|
||||
"Internal error in tracking HDB locks");
|
||||
return KADM5_ALREADY_LOCKED;
|
||||
}
|
||||
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = context->db->hdb_lock(context->context, context->db, HDB_WLOCK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
context->keep_open = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static kadm5_ret_t
|
||||
kadm5_s_unlock(void *server_handle)
|
||||
{
|
||||
kadm5_server_context *context = server_handle;
|
||||
kadm5_ret_t ret;
|
||||
|
||||
if (!context->keep_open)
|
||||
return KADM5_NOT_LOCKED;
|
||||
|
||||
(void) context->db->hdb_close(context->context, context->db);
|
||||
|
||||
context->keep_open = 0;
|
||||
ret = context->db->hdb_unlock(context->context, context->db);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
set_funcs(kadm5_server_context *c)
|
||||
{
|
||||
@@ -51,6 +98,8 @@ set_funcs(kadm5_server_context *c)
|
||||
SET(c, modify_principal);
|
||||
SET(c, randkey_principal);
|
||||
SET(c, rename_principal);
|
||||
SET(c, lock);
|
||||
SET(c, unlock);
|
||||
}
|
||||
|
||||
#ifndef NO_UNIX_SOCKETS
|
||||
|
@@ -65,6 +65,7 @@ create_principal(kadm5_server_context *context,
|
||||
kadm5_principal_ent_rec defrec, *defent;
|
||||
uint32_t def_mask;
|
||||
|
||||
memset(ent, 0, sizeof(*ent));
|
||||
if((mask & required_mask) != required_mask)
|
||||
return KADM5_BAD_MASK;
|
||||
if((mask & forbidden_mask))
|
||||
@@ -72,7 +73,6 @@ create_principal(kadm5_server_context *context,
|
||||
if((mask & KADM5_POLICY) && strcmp(princ->policy, "default"))
|
||||
/* XXX no real policies for now */
|
||||
return KADM5_UNK_POLICY;
|
||||
memset(ent, 0, sizeof(*ent));
|
||||
ret = krb5_copy_principal(context->context, princ->principal,
|
||||
&ent->entry.principal);
|
||||
if(ret)
|
||||
@@ -111,6 +111,12 @@ kadm5_s_create_principal_with_key(void *server_handle,
|
||||
hdb_entry_ex ent;
|
||||
kadm5_server_context *context = server_handle;
|
||||
|
||||
if ((mask & KADM5_KVNO) == 0) {
|
||||
/* create_principal() through _kadm5_setup_entry(), will need this */
|
||||
princ->kvno = 1;
|
||||
mask |= KADM5_KVNO;
|
||||
}
|
||||
|
||||
ret = create_principal(context, princ, mask, &ent,
|
||||
KADM5_PRINCIPAL | KADM5_KEY_DATA,
|
||||
KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
|
||||
@@ -121,18 +127,18 @@ kadm5_s_create_principal_with_key(void *server_handle,
|
||||
if(ret)
|
||||
goto out;
|
||||
|
||||
if ((mask & KADM5_KVNO) == 0)
|
||||
ent.entry.kvno = 1;
|
||||
|
||||
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
goto out;
|
||||
if (!context->keep_open) {
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
goto out;
|
||||
}
|
||||
ret = context->db->hdb_store(context->context, context->db, 0, &ent);
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (ret)
|
||||
goto out;
|
||||
kadm5_log_create (context, &ent.entry);
|
||||
@@ -153,6 +159,12 @@ kadm5_s_create_principal(void *server_handle,
|
||||
hdb_entry_ex ent;
|
||||
kadm5_server_context *context = server_handle;
|
||||
|
||||
if ((mask & KADM5_KVNO) == 0) {
|
||||
/* create_principal() through _kadm5_setup_entry(), will need this */
|
||||
princ->kvno = 1;
|
||||
mask |= KADM5_KVNO;
|
||||
}
|
||||
|
||||
ret = create_principal(context, princ, mask, &ent,
|
||||
KADM5_PRINCIPAL,
|
||||
KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
|
||||
@@ -163,9 +175,6 @@ kadm5_s_create_principal(void *server_handle,
|
||||
if(ret)
|
||||
goto out;
|
||||
|
||||
if ((mask & KADM5_KVNO) == 0)
|
||||
ent.entry.kvno = 1;
|
||||
|
||||
ent.entry.keys.len = 0;
|
||||
ent.entry.keys.val = NULL;
|
||||
|
||||
@@ -177,11 +186,14 @@ kadm5_s_create_principal(void *server_handle,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
goto out;
|
||||
if (!context->keep_open) {
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
goto out;
|
||||
}
|
||||
ret = context->db->hdb_store(context->context, context->db, 0, &ent);
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@@ -85,7 +85,8 @@ parse_file(krb5_context context, krb5_principal principal, int no_salt)
|
||||
size_t nkeys;
|
||||
Key *keys;
|
||||
|
||||
ret = hdb_generate_key_set(context, principal, &keys, &nkeys, no_salt);
|
||||
ret = hdb_generate_key_set(context, principal, 0, NULL, &keys, &nkeys,
|
||||
no_salt);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "hdb_generate_key_set");
|
||||
|
||||
|
@@ -43,10 +43,12 @@ kadm5_s_delete_principal(void *server_handle, krb5_principal princ)
|
||||
hdb_entry_ex ent;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret) {
|
||||
krb5_warn(context->context, ret, "opening database");
|
||||
return ret;
|
||||
if (!context->keep_open) {
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret) {
|
||||
krb5_warn(context->context, ret, "opening database");
|
||||
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);
|
||||
@@ -70,6 +72,7 @@ kadm5_s_delete_principal(void *server_handle, krb5_principal princ)
|
||||
out2:
|
||||
hdb_free_entry(context->context, &ent);
|
||||
out:
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
return _kadm5_error_code(ret);
|
||||
}
|
||||
|
@@ -178,8 +178,27 @@ _kadm5_setup_entry(kadm5_server_context *context,
|
||||
}
|
||||
}
|
||||
if(mask & KADM5_KVNO
|
||||
&& princ_mask & KADM5_KVNO)
|
||||
ent->entry.kvno = princ->kvno;
|
||||
&& princ_mask & KADM5_KVNO) {
|
||||
/*
|
||||
* For some reason kadmin's ank changes the kvno after calling
|
||||
* randkey. Now that we have key history, what are we to do
|
||||
* when we update kvno but not keys?!
|
||||
*
|
||||
* For now just clear the key history if the kvno changes.
|
||||
* Eventually we may want to search the key history for matching
|
||||
* keys and use those to replace the current key set (putting
|
||||
* the old current keyset in the history keysets list?!).
|
||||
*/
|
||||
if (ent->entry.kvno != princ->kvno &&
|
||||
(mask & princ_mask & KADM5_KEY_DATA)) {
|
||||
hdb_clear_extension(context->context, &ent->entry,
|
||||
choice_HDB_extension_data_hist_keys);
|
||||
princ->kvno = ent->entry.kvno;
|
||||
} else {
|
||||
/* _kadm5_set_keys2() expects this to have been done here */
|
||||
ent->entry.kvno = princ->kvno;
|
||||
}
|
||||
}
|
||||
if(mask & KADM5_MAX_RLIFE) {
|
||||
if(princ_mask & KADM5_MAX_RLIFE) {
|
||||
if(princ->max_renewable_life)
|
||||
|
@@ -85,10 +85,13 @@ kadm5_s_get_principals(void *server_handle,
|
||||
struct foreach_data d;
|
||||
kadm5_server_context *context = server_handle;
|
||||
kadm5_ret_t ret;
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret) {
|
||||
krb5_warn(context->context, ret, "opening database");
|
||||
return ret;
|
||||
|
||||
if (!context->keep_open) {
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret) {
|
||||
krb5_warn(context->context, ret, "opening database");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
d.exp = expression;
|
||||
{
|
||||
@@ -100,7 +103,8 @@ kadm5_s_get_principals(void *server_handle,
|
||||
d.princs = NULL;
|
||||
d.count = 0;
|
||||
ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA, foreach, &d);
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if(ret == 0)
|
||||
ret = add_princ(&d, NULL);
|
||||
if(ret == 0){
|
||||
|
@@ -32,6 +32,7 @@
|
||||
*/
|
||||
|
||||
#include "kadm5_locl.h"
|
||||
#include <assert.h>
|
||||
|
||||
RCSID("$Id$");
|
||||
|
||||
@@ -64,6 +65,57 @@ add_tl_data(kadm5_principal_ent_t ent, int16_t type,
|
||||
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
|
||||
_krb5_put_int(void *buffer, unsigned long value, size_t size); /* XXX */
|
||||
|
||||
static
|
||||
krb5_error_code
|
||||
copy_keyset_to_kadm5(kadm5_server_context *context, krb5_kvno kvno,
|
||||
size_t n_keys, Key *keys, krb5_salt *salt,
|
||||
kadm5_principal_ent_t out)
|
||||
{
|
||||
size_t i;
|
||||
Key *key;
|
||||
krb5_key_data *kd;
|
||||
krb5_data *sp;
|
||||
krb5_error_code ret = 0;
|
||||
|
||||
for (i = 0; i < n_keys; i++) {
|
||||
key = &keys[i];
|
||||
kd = &out->key_data[out->n_key_data];
|
||||
kd->key_data_ver = 2;
|
||||
kd->key_data_kvno = kvno;
|
||||
kd->key_data_type[0] = key->key.keytype;
|
||||
if(key->salt)
|
||||
kd->key_data_type[1] = key->salt->type;
|
||||
else
|
||||
kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
|
||||
/* setup key */
|
||||
kd->key_data_length[0] = key->key.keyvalue.length;
|
||||
kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
|
||||
if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
|
||||
kd->key_data_length[0]);
|
||||
/* setup salt */
|
||||
if(key->salt)
|
||||
sp = &key->salt->salt;
|
||||
else
|
||||
sp = &salt->saltvalue;
|
||||
kd->key_data_length[1] = sp->length;
|
||||
kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
|
||||
if(kd->key_data_length[1] != 0
|
||||
&& kd->key_data_contents[1] == NULL) {
|
||||
memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
|
||||
out->n_key_data++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_s_get_principal(void *server_handle,
|
||||
krb5_principal princ,
|
||||
@@ -75,12 +127,17 @@ kadm5_s_get_principal(void *server_handle,
|
||||
hdb_entry_ex ent;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
|
||||
if(ret)
|
||||
return ret;
|
||||
|
||||
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_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
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);
|
||||
|
||||
@@ -160,8 +217,21 @@ kadm5_s_get_principal(void *server_handle,
|
||||
if(mask & KADM5_FAIL_AUTH_COUNT)
|
||||
;
|
||||
#endif
|
||||
if(mask & KADM5_POLICY)
|
||||
out->policy = NULL;
|
||||
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;
|
||||
@@ -170,52 +240,38 @@ kadm5_s_get_principal(void *server_handle,
|
||||
}
|
||||
if(mask & KADM5_KEY_DATA){
|
||||
size_t i;
|
||||
Key *key;
|
||||
krb5_key_data *kd;
|
||||
size_t n_keys = ent.entry.keys.len;
|
||||
krb5_salt salt;
|
||||
krb5_data *sp;
|
||||
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);
|
||||
out->key_data = malloc(ent.entry.keys.len * sizeof(*out->key_data));
|
||||
if (out->key_data == NULL && ent.entry.keys.len != 0) {
|
||||
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;
|
||||
}
|
||||
for(i = 0; i < ent.entry.keys.len; i++){
|
||||
key = &ent.entry.keys.val[i];
|
||||
kd = &out->key_data[i];
|
||||
kd->key_data_ver = 2;
|
||||
kd->key_data_kvno = ent.entry.kvno;
|
||||
kd->key_data_type[0] = key->key.keytype;
|
||||
if(key->salt)
|
||||
kd->key_data_type[1] = key->salt->type;
|
||||
else
|
||||
kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
|
||||
/* setup key */
|
||||
kd->key_data_length[0] = key->key.keyvalue.length;
|
||||
kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
|
||||
if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
|
||||
kd->key_data_length[0]);
|
||||
/* setup salt */
|
||||
if(key->salt)
|
||||
sp = &key->salt->salt;
|
||||
else
|
||||
sp = &salt.saltvalue;
|
||||
kd->key_data_length[1] = sp->length;
|
||||
kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
|
||||
if(kd->key_data_length[1] != 0
|
||||
&& kd->key_data_contents[1] == NULL) {
|
||||
memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
|
||||
ret = ENOMEM;
|
||||
break;
|
||||
}
|
||||
memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
|
||||
out->n_key_data = i + 1;
|
||||
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);
|
||||
|
@@ -45,6 +45,18 @@
|
||||
|
||||
RCSID("$Id$");
|
||||
|
||||
static kadm5_ret_t
|
||||
kadm5_c_lock(void *server_handle)
|
||||
{
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
static kadm5_ret_t
|
||||
kadm5_c_unlock(void *server_handle)
|
||||
{
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
static void
|
||||
set_funcs(kadm5_client_context *c)
|
||||
{
|
||||
@@ -61,6 +73,8 @@ set_funcs(kadm5_client_context *c)
|
||||
SET(c, modify_principal);
|
||||
SET(c, randkey_principal);
|
||||
SET(c, rename_principal);
|
||||
SET(c, lock);
|
||||
SET(c, unlock);
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
|
@@ -57,3 +57,11 @@ error_code AUTH_CHANGEPW, "Operation requires `change-password' privilege"
|
||||
error_code BAD_TL_TYPE, "Invalid tagged data list element type"
|
||||
error_code MISSING_CONF_PARAMS, "Required parameters in kdc.conf missing"
|
||||
error_code BAD_SERVER_NAME, "Bad krb5 admin server hostname"
|
||||
error_code KS_TUPLE_NOSUPP, "Key/salt tuples not supported by this function"
|
||||
error_code SETKEY3_ETYPE_MISMATCH, "Key/salt tuples don't match keys"
|
||||
error_code DECRYPT_USAGE_NOSUPP, "Given usage of kadm5_decrypt() not supported"
|
||||
error_code POLICY_OP_NOSUPP, "Policy operations not supported"
|
||||
error_code KEEPOLD_NOSUPP, "Keep old keys option not supported"
|
||||
error_code AUTH_GET_KEYS, "Operation requires `get-keys' privilege"
|
||||
error_code ALREADY_LOCKED, "Database already locked"
|
||||
error_code NOT_LOCKED, "Database not locked"
|
||||
|
@@ -38,6 +38,7 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <roken.h>
|
||||
#include <heimbase.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@@ -63,16 +63,18 @@ _kadm5_init_keys (Key *keys, int len)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* return 1 if any key in `keys1, len1' exists in `keys2, len2'
|
||||
*/
|
||||
|
||||
int
|
||||
static int
|
||||
_kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
|
||||
{
|
||||
int i, j;
|
||||
size_t i, j;
|
||||
size_t optimize;
|
||||
|
||||
for (i = 0; i < len1; ++i) {
|
||||
optimize = 0;
|
||||
for (j = 0; j < len2; j++) {
|
||||
if ((keys1[i].salt != NULL && keys2[j].salt == NULL)
|
||||
|| (keys1[i].salt == NULL && keys2[j].salt != NULL))
|
||||
@@ -89,6 +91,7 @@ _kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
|
||||
}
|
||||
if (keys1[i].key.keytype != keys2[j].key.keytype)
|
||||
continue;
|
||||
optimize = 1;
|
||||
if (keys1[i].key.keyvalue.length != keys2[j].key.keyvalue.length)
|
||||
continue;
|
||||
if (memcmp (keys1[i].key.keyvalue.data, keys2[j].key.keyvalue.data,
|
||||
@@ -97,6 +100,33 @@ _kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimization: no need to check all of keys1[] if one there
|
||||
* was one key in keys2[] with matching enctype and salt but not
|
||||
* matching key. Assumption: all keys in keys1[] and keys2[]
|
||||
* are output by string2key.
|
||||
*/
|
||||
if (optimize)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* return 1 if any key in `keys1, len1' exists in hist_keys
|
||||
*/
|
||||
int
|
||||
_kadm5_exists_keys_hist(Key *keys1, int len1, HDB_Ext_KeySet *hist_keys)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < hist_keys->len; i++) {
|
||||
if (_kadm5_exists_keys(keys1, len1,
|
||||
hist_keys->val[i].keys.val,
|
||||
hist_keys->val[i].keys.len))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -585,7 +585,8 @@ kadm5_log_replay_modify (kadm5_server_context *context,
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ret = context->db->hdb_fetch_kvno(context->context, context->db,
|
||||
log_ent.entry.principal,
|
||||
HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
|
||||
HDB_F_DECRYPT|HDB_F_ALL_KVNOS|
|
||||
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (mask & KADM5_PRINC_EXPIRE_TIME) {
|
||||
@@ -698,6 +699,29 @@ kadm5_log_replay_modify (kadm5_server_context *context,
|
||||
size_t num;
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* We don't need to do anything about key history here because
|
||||
* we always log KADM5_TL_DATA when we change keys/passwords, so
|
||||
* the code below this will handle key history implicitly.
|
||||
* However, if we had to, the code to handle key history here
|
||||
* would look like this:
|
||||
*
|
||||
* HDB_extension *ext;
|
||||
* ...
|
||||
* ext = hdb_find_extension(&log_ent.entry,
|
||||
* choice_HDB_extension_data_hist_keys);
|
||||
* if (ext);
|
||||
* ret = hdb_replace_extension(context->context, &ent.entry, ext);
|
||||
* else
|
||||
* ret = hdb_clear_extension(context->context, &ent.entry,
|
||||
* choice_HDB_extension_data_hist_keys);
|
||||
*
|
||||
* Maybe we should do this here anyways, wasteful as it would
|
||||
* be, as a defensive programming measure? For now we heim_assert().
|
||||
*/
|
||||
heim_assert((mask & KADM5_TL_DATA),
|
||||
"Wouldn't log and replay key history");
|
||||
|
||||
for (i = 0; i < ent.entry.keys.len; ++i)
|
||||
free_Key(&ent.entry.keys.val[i]);
|
||||
free (ent.entry.keys.val);
|
||||
|
@@ -53,6 +53,38 @@ kadm5_store_key_data(krb5_storage *sp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_store_fake_key_data(krb5_storage *sp,
|
||||
krb5_key_data *key)
|
||||
{
|
||||
char buf[4] = {0};
|
||||
krb5_data c;
|
||||
|
||||
krb5_store_int32(sp, key->key_data_ver);
|
||||
krb5_store_int32(sp, key->key_data_kvno);
|
||||
krb5_store_int32(sp, key->key_data_type[0]);
|
||||
|
||||
/*
|
||||
* This is the key contents. We want it to be obvious to the client
|
||||
* (if it really did want the keys) that the key won't work.
|
||||
* 32-bit keys are no good for any enctype, so that should do.
|
||||
* Clients that didn't need keys will ignore this, and clients that
|
||||
* did want keys will either fail or they'll, say, create bogus
|
||||
* keytab entries that will subsequently fail to be useful.
|
||||
*/
|
||||
c.length = sizeof (buf);
|
||||
c.data = buf;
|
||||
memset(buf, 0xdeadcee5, sizeof (buf)); /* bad bad hexspeak for deadkeys */
|
||||
krb5_store_data(sp, c);
|
||||
|
||||
/* This is the salt -- no need to send garbage */
|
||||
krb5_store_int32(sp, key->key_data_type[1]);
|
||||
c.length = key->key_data_length[1];
|
||||
c.data = key->key_data_contents[1];
|
||||
krb5_store_data(sp, c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_ret_key_data(krb5_storage *sp,
|
||||
krb5_key_data *key)
|
||||
@@ -105,7 +137,7 @@ kadm5_ret_tl_data(krb5_storage *sp,
|
||||
static kadm5_ret_t
|
||||
store_principal_ent(krb5_storage *sp,
|
||||
kadm5_principal_ent_t princ,
|
||||
uint32_t mask)
|
||||
uint32_t mask, int wkeys)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -149,8 +181,12 @@ store_principal_ent(krb5_storage *sp,
|
||||
krb5_store_int32(sp, princ->fail_auth_count);
|
||||
if (mask & KADM5_KEY_DATA) {
|
||||
krb5_store_int32(sp, princ->n_key_data);
|
||||
for(i = 0; i < princ->n_key_data; i++)
|
||||
kadm5_store_key_data(sp, &princ->key_data[i]);
|
||||
for(i = 0; i < princ->n_key_data; i++) {
|
||||
if (wkeys)
|
||||
kadm5_store_key_data(sp, &princ->key_data[i]);
|
||||
else
|
||||
kadm5_store_fake_key_data(sp, &princ->key_data[i]);
|
||||
}
|
||||
}
|
||||
if (mask & KADM5_TL_DATA) {
|
||||
krb5_tl_data *tp;
|
||||
@@ -167,7 +203,14 @@ kadm5_ret_t
|
||||
kadm5_store_principal_ent(krb5_storage *sp,
|
||||
kadm5_principal_ent_t princ)
|
||||
{
|
||||
return store_principal_ent (sp, princ, ~0);
|
||||
return store_principal_ent (sp, princ, ~0, 1);
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
kadm5_store_principal_ent_nokeys(krb5_storage *sp,
|
||||
kadm5_principal_ent_t princ)
|
||||
{
|
||||
return store_principal_ent (sp, princ, ~0, 0);
|
||||
}
|
||||
|
||||
kadm5_ret_t
|
||||
@@ -176,7 +219,7 @@ kadm5_store_principal_ent_mask(krb5_storage *sp,
|
||||
uint32_t mask)
|
||||
{
|
||||
krb5_store_int32(sp, mask);
|
||||
return store_principal_ent (sp, princ, mask);
|
||||
return store_principal_ent (sp, princ, mask, 1);
|
||||
}
|
||||
|
||||
static kadm5_ret_t
|
||||
|
@@ -44,15 +44,19 @@ modify_principal(void *server_handle,
|
||||
kadm5_server_context *context = server_handle;
|
||||
hdb_entry_ex ent;
|
||||
kadm5_ret_t ret;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
|
||||
if((mask & forbidden_mask))
|
||||
return KADM5_BAD_MASK;
|
||||
if((mask & KADM5_POLICY) && strcmp(princ->policy, "default"))
|
||||
return KADM5_UNK_POLICY;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
return ret;
|
||||
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->principal, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
|
||||
if(ret)
|
||||
@@ -68,6 +72,21 @@ modify_principal(void *server_handle,
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
if((mask & KADM5_POLICY)) {
|
||||
HDB_extension ext;
|
||||
|
||||
ext.data.element = choice_HDB_extension_data_policy;
|
||||
ext.data.u.policy = strdup(princ->policy);
|
||||
if (ext.data.u.policy == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out2;
|
||||
}
|
||||
/* This calls free_HDB_extension(), freeing ext.data.u.policy */
|
||||
ret = hdb_replace_extension(context->context, &ent.entry, &ext);
|
||||
if (ret)
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = context->db->hdb_store(context->context, context->db,
|
||||
HDB_F_REPLACE, &ent);
|
||||
if (ret)
|
||||
@@ -80,7 +99,8 @@ modify_principal(void *server_handle,
|
||||
out2:
|
||||
hdb_free_entry(context->context, &ent);
|
||||
out:
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
return _kadm5_error_code(ret);
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@
|
||||
#define __kadm5_privatex_h__
|
||||
|
||||
struct kadm_func {
|
||||
kadm5_ret_t (*chpass_principal) (void *, krb5_principal, const char*);
|
||||
kadm5_ret_t (*chpass_principal) (void *, krb5_principal, int, const char*);
|
||||
kadm5_ret_t (*create_principal) (void*, kadm5_principal_ent_t,
|
||||
uint32_t, const char*);
|
||||
kadm5_ret_t (*delete_principal) (void*, krb5_principal);
|
||||
@@ -48,11 +48,14 @@ struct kadm_func {
|
||||
kadm5_ret_t (*get_principals) (void*, const char*, char***, int*);
|
||||
kadm5_ret_t (*get_privs) (void*, uint32_t*);
|
||||
kadm5_ret_t (*modify_principal) (void*, kadm5_principal_ent_t, uint32_t);
|
||||
kadm5_ret_t (*randkey_principal) (void*, krb5_principal,
|
||||
krb5_keyblock**, int*);
|
||||
kadm5_ret_t (*randkey_principal) (void*, krb5_principal, krb5_boolean, int,
|
||||
krb5_key_salt_tuple*, krb5_keyblock**,
|
||||
int*);
|
||||
kadm5_ret_t (*rename_principal) (void*, krb5_principal, krb5_principal);
|
||||
kadm5_ret_t (*chpass_principal_with_key) (void *, krb5_principal,
|
||||
kadm5_ret_t (*chpass_principal_with_key) (void *, krb5_principal, int,
|
||||
int, krb5_key_data *);
|
||||
kadm5_ret_t (*lock) (void *);
|
||||
kadm5_ret_t (*unlock) (void *);
|
||||
};
|
||||
|
||||
/* XXX should be integrated */
|
||||
@@ -89,6 +92,7 @@ typedef struct kadm5_server_context {
|
||||
/* */
|
||||
kadm5_config_params config;
|
||||
HDB *db;
|
||||
int keep_open;
|
||||
krb5_principal caller;
|
||||
unsigned acl_flags;
|
||||
kadm5_log_context log_context;
|
||||
|
@@ -38,14 +38,18 @@ RCSID("$Id$");
|
||||
kadm5_ret_t
|
||||
kadm5_c_randkey_principal(void *server_handle,
|
||||
krb5_principal princ,
|
||||
krb5_boolean keepold,
|
||||
int n_ks_tuple,
|
||||
krb5_key_salt_tuple *ks_tuple,
|
||||
krb5_keyblock **new_keys,
|
||||
int *n_keys)
|
||||
{
|
||||
kadm5_client_context *context = server_handle;
|
||||
kadm5_ret_t ret;
|
||||
krb5_storage *sp;
|
||||
unsigned char buf[1024];
|
||||
unsigned char buf[1536];
|
||||
int32_t tmp;
|
||||
size_t i;
|
||||
krb5_data reply;
|
||||
|
||||
ret = _kadm5_connect(server_handle);
|
||||
@@ -57,8 +61,37 @@ kadm5_c_randkey_principal(void *server_handle,
|
||||
krb5_clear_error_message(context->context);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE WELL: This message is extensible. It currently consists of:
|
||||
*
|
||||
* - opcode (kadm_randkey)
|
||||
* - principal name (princ)
|
||||
*
|
||||
* followed by optional items, each of which must be present if
|
||||
* there are any items following them that are also present:
|
||||
*
|
||||
* - keepold boolean (whether to delete old kvnos)
|
||||
* - number of key/salt type tuples
|
||||
* - array of {enctype, salttype}
|
||||
*
|
||||
* Eventually we may add:
|
||||
*
|
||||
* - opaque string2key parameters (salt, rounds, ...)
|
||||
*/
|
||||
krb5_store_int32(sp, kadm_randkey);
|
||||
krb5_store_principal(sp, princ);
|
||||
|
||||
if (keepold == TRUE || n_ks_tuple > 0)
|
||||
krb5_store_uint32(sp, keepold);
|
||||
if (n_ks_tuple > 0)
|
||||
krb5_store_uint32(sp, n_ks_tuple);
|
||||
for (i = 0; i < n_ks_tuple; i++) {
|
||||
krb5_store_int32(sp, ks_tuple[i].ks_enctype);
|
||||
krb5_store_int32(sp, ks_tuple[i].ks_salttype);
|
||||
}
|
||||
/* Future extensions go here */
|
||||
|
||||
ret = _kadm5_client_send(context, sp);
|
||||
krb5_storage_free(sp);
|
||||
if (ret)
|
||||
@@ -80,6 +113,10 @@ kadm5_c_randkey_principal(void *server_handle,
|
||||
int i;
|
||||
|
||||
krb5_ret_int32(sp, &tmp);
|
||||
if (tmp < 0) {
|
||||
ret = EOVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
k = malloc(tmp * sizeof(*k));
|
||||
if (k == NULL) {
|
||||
ret = ENOMEM;
|
||||
@@ -87,8 +124,10 @@ kadm5_c_randkey_principal(void *server_handle,
|
||||
}
|
||||
for(i = 0; i < tmp; i++)
|
||||
krb5_ret_keyblock(sp, &k[i]);
|
||||
*n_keys = tmp;
|
||||
*new_keys = k;
|
||||
if (n_keys && new_keys) {
|
||||
*n_keys = tmp;
|
||||
*new_keys = k;
|
||||
}
|
||||
}
|
||||
out:
|
||||
krb5_storage_free(sp);
|
||||
|
@@ -43,6 +43,9 @@ RCSID("$Id$");
|
||||
kadm5_ret_t
|
||||
kadm5_s_randkey_principal(void *server_handle,
|
||||
krb5_principal princ,
|
||||
krb5_boolean keepold,
|
||||
int n_ks_tuple,
|
||||
krb5_key_salt_tuple *ks_tuple,
|
||||
krb5_keyblock **new_keys,
|
||||
int *n_keys)
|
||||
{
|
||||
@@ -51,16 +54,26 @@ kadm5_s_randkey_principal(void *server_handle,
|
||||
kadm5_ret_t ret;
|
||||
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
return ret;
|
||||
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_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
|
||||
if(ret)
|
||||
goto out;
|
||||
|
||||
if (keepold) {
|
||||
ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = _kadm5_set_keys_randomly (context,
|
||||
&ent.entry,
|
||||
n_ks_tuple,
|
||||
ks_tuple,
|
||||
new_keys,
|
||||
n_keys);
|
||||
if (ret)
|
||||
@@ -74,9 +87,18 @@ kadm5_s_randkey_principal(void *server_handle,
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
if (keepold) {
|
||||
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
|
||||
if (ret)
|
||||
goto out2;
|
||||
} else {
|
||||
HDB_extension ext;
|
||||
|
||||
ext.data.element = choice_HDB_extension_data_hist_keys;
|
||||
ext.data.u.hist_keys.len = 0;
|
||||
ext.data.u.hist_keys.val = NULL;
|
||||
hdb_replace_extension(context->context, &ent.entry, &ext);
|
||||
}
|
||||
|
||||
ret = context->db->hdb_store(context->context, context->db,
|
||||
HDB_F_REPLACE, &ent);
|
||||
@@ -102,6 +124,7 @@ out3:
|
||||
out2:
|
||||
hdb_free_entry(context->context, &ent);
|
||||
out:
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
return _kadm5_error_code(ret);
|
||||
}
|
||||
|
@@ -48,13 +48,16 @@ kadm5_s_rename_principal(void *server_handle,
|
||||
memset(&ent, 0, sizeof(ent));
|
||||
if(krb5_principal_compare(context->context, source, target))
|
||||
return KADM5_DUP; /* XXX is this right? */
|
||||
ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
|
||||
if(ret)
|
||||
return ret;
|
||||
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,
|
||||
source, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
|
||||
if(ret){
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
goto out;
|
||||
}
|
||||
ret = _kadm5_set_modifier(context, &ent.entry);
|
||||
@@ -103,7 +106,8 @@ kadm5_s_rename_principal(void *server_handle,
|
||||
ret = context->db->hdb_remove(context->context, context->db, oldname);
|
||||
ent.entry.principal = oldname;
|
||||
out2:
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
if (!context->keep_open)
|
||||
context->db->hdb_close(context->context, context->db);
|
||||
hdb_free_entry(context->context, &ent);
|
||||
out:
|
||||
return _kadm5_error_code(ret);
|
||||
|
@@ -72,6 +72,23 @@ _kadm5_set_keys(kadm5_server_context *context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_Key(Key *k, Salt *s, krb5_key_data *kd, size_t kd_offset)
|
||||
{
|
||||
memset(k, 0, sizeof (*k)); /* sets mkvno and salt */
|
||||
k->key.keytype = kd[kd_offset].key_data_type[0];
|
||||
k->key.keyvalue.length = kd[kd_offset].key_data_length[0];
|
||||
k->key.keyvalue.data = kd[kd_offset].key_data_contents[0];
|
||||
|
||||
if(kd[kd_offset].key_data_ver == 2) {
|
||||
memset(s, 0, sizeof (*s));
|
||||
s->type = kd[kd_offset].key_data_type[1];
|
||||
s->salt.length = kd[kd_offset].key_data_length[1];
|
||||
s->salt.data = kd[kd_offset].key_data_contents[1];
|
||||
k->salt = s;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the keys of `ent' to (`n_key_data', `key_data')
|
||||
*/
|
||||
@@ -83,51 +100,161 @@ _kadm5_set_keys2(kadm5_server_context *context,
|
||||
krb5_key_data *key_data)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
int i;
|
||||
unsigned len;
|
||||
Key *keys;
|
||||
size_t i, k;
|
||||
HDB_extension ext;
|
||||
HDB_extension *extp = NULL;
|
||||
HDB_Ext_KeySet *hist_keys = &ext.data.u.hist_keys;
|
||||
Key key;
|
||||
Salt salt;
|
||||
Keys keys;
|
||||
hdb_keyset hkset;
|
||||
krb5_kvno kvno = -1;
|
||||
int one_key_set = 1;
|
||||
int replace_hist_keys = 0;
|
||||
|
||||
len = n_key_data;
|
||||
keys = malloc (len * sizeof(*keys));
|
||||
if (keys == NULL && len != 0)
|
||||
return ENOMEM;
|
||||
|
||||
_kadm5_init_keys (keys, len);
|
||||
|
||||
for(i = 0; i < n_key_data; i++) {
|
||||
keys[i].mkvno = NULL;
|
||||
keys[i].key.keytype = key_data[i].key_data_type[0];
|
||||
ret = krb5_data_copy(&keys[i].key.keyvalue,
|
||||
key_data[i].key_data_contents[0],
|
||||
key_data[i].key_data_length[0]);
|
||||
if(ret)
|
||||
goto out;
|
||||
if(key_data[i].key_data_ver == 2) {
|
||||
Salt *salt;
|
||||
|
||||
salt = calloc(1, sizeof(*salt));
|
||||
if(salt == NULL) {
|
||||
ret = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
keys[i].salt = salt;
|
||||
salt->type = key_data[i].key_data_type[1];
|
||||
krb5_data_copy(&salt->salt,
|
||||
key_data[i].key_data_contents[1],
|
||||
key_data[i].key_data_length[1]);
|
||||
} else
|
||||
keys[i].salt = NULL;
|
||||
if (n_key_data == 0) {
|
||||
/* Clear all keys! */
|
||||
ret = hdb_clear_extension(context->context, ent,
|
||||
choice_HDB_extension_data_hist_keys);
|
||||
if (ret)
|
||||
return ret;
|
||||
free_Keys(&ent->keys);
|
||||
return 0;
|
||||
}
|
||||
_kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
|
||||
ent->keys.len = len;
|
||||
ent->keys.val = keys;
|
||||
|
||||
memset(&keys, 0, sizeof (keys));
|
||||
memset(&hkset, 0, sizeof (hkset)); /* set set_time */
|
||||
ext.data.element = choice_HDB_extension_data_hist_keys;
|
||||
memset(hist_keys, 0, sizeof (*hist_keys));
|
||||
|
||||
for (i = 0; i < n_key_data; i++) {
|
||||
if (kvno != -1 && kvno != key_data[i].key_data_kvno) {
|
||||
one_key_set = 0;
|
||||
break;
|
||||
}
|
||||
kvno = key_data[i].key_data_kvno;
|
||||
}
|
||||
if (one_key_set) {
|
||||
/*
|
||||
* If we're updating KADM5_KEY_DATA with a single keyset then we
|
||||
* assume we must be setting the principal's kvno as well!
|
||||
*
|
||||
* Just have to be careful about old clients that might have
|
||||
* sent 0 as the kvno... This may seem ugly, but it's the price
|
||||
* of backwards compatibility with pre-multi-kvno kadmin clients
|
||||
* (besides, who's to say that updating KADM5_KEY_DATA requires
|
||||
* updating the entry's kvno?)
|
||||
*
|
||||
* Note that we do nothing special for the case where multiple
|
||||
* keysets are given but the entry's kvno is not set and not in
|
||||
* the given set of keysets. If this happens we'll just update
|
||||
* the key history only and leave the current keyset alone.
|
||||
*/
|
||||
if (kvno == 0) {
|
||||
/* Force kvno to 1 if it was 0; (ank would do this anyways) */
|
||||
if (ent->kvno == 0)
|
||||
ent->kvno = 1;
|
||||
/* Below we need key_data[*].kvno to be reasonable */
|
||||
for (i = 0; i < n_key_data; i++)
|
||||
key_data[i].key_data_kvno = ent->kvno;
|
||||
} else {
|
||||
/*
|
||||
* Or force the entry's kvno to match the one from the new,
|
||||
* singular keyset
|
||||
*/
|
||||
ent->kvno = kvno;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n_key_data; i++) {
|
||||
if (key_data[i].key_data_kvno == ent->kvno) {
|
||||
/* A current key; add to current key set */
|
||||
setup_Key(&key, &salt, key_data, i);
|
||||
ret = add_Keys(&keys, &key);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* This kvno is historical. Build an hdb_keyset for keys of
|
||||
* this enctype and add them to the new key history.
|
||||
*/
|
||||
for (k = 0; k < hist_keys->len; k++) {
|
||||
if (hist_keys->val[k].kvno == key_data[i].key_data_kvno)
|
||||
break;
|
||||
}
|
||||
if (hist_keys->len > k &&
|
||||
hist_keys->val[k].kvno == key_data[i].key_data_kvno)
|
||||
/* We've added all keys of this kvno already (see below) */
|
||||
continue;
|
||||
|
||||
memset(&hkset, 0, sizeof (hkset)); /* set set_time */
|
||||
hkset.kvno = key_data[i].key_data_kvno;
|
||||
for (k = 0; k < n_key_data; k++) {
|
||||
/* Find all keys of this kvno and add them to the new keyset */
|
||||
if (key_data[k].key_data_kvno != hkset.kvno)
|
||||
continue;
|
||||
|
||||
setup_Key(&key, &salt, key_data, k);
|
||||
ret = add_Keys(&hkset.keys, &key);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
ret = add_HDB_Ext_KeySet(hist_keys, &hkset);
|
||||
if (ret)
|
||||
goto out;
|
||||
replace_hist_keys = 1;
|
||||
}
|
||||
|
||||
if (replace_hist_keys)
|
||||
/* No key history given -> leave it alone */
|
||||
extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
|
||||
if (extp != NULL) {
|
||||
HDB_Ext_KeySet *old_hist_keys;
|
||||
|
||||
/*
|
||||
* Try to keep the very useful set_time values from the old hist
|
||||
* keys. kadm5 loses this info, so this heuristic is the best we
|
||||
* can do.
|
||||
*/
|
||||
old_hist_keys = &extp->data.u.hist_keys;
|
||||
for (i = 0; i < old_hist_keys->len; i++) {
|
||||
if (old_hist_keys->val[i].set_time == NULL)
|
||||
continue;
|
||||
for (k = 0; k < hist_keys->len; k++) {
|
||||
if (hist_keys->val[k].kvno != old_hist_keys->val[k].kvno)
|
||||
continue;
|
||||
hist_keys->val[k].set_time = old_hist_keys->val[k].set_time;
|
||||
old_hist_keys->val[k].set_time = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (replace_hist_keys) {
|
||||
/* If hist keys not given in key_data then don't blow away hist_keys */
|
||||
ret = hdb_replace_extension(context->context, ent, &ext);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A structure copy is more efficient here than this would be:
|
||||
*
|
||||
* copy_Keys(&keys, &ent->keys);
|
||||
* free_Keys(&keys);
|
||||
*
|
||||
* Of course, the above hdb_replace_extension() is not at all efficient...
|
||||
*/
|
||||
free_Keys(&ent->keys);
|
||||
ent->keys = keys;
|
||||
hdb_entry_set_pw_change_time(context->context, ent, 0);
|
||||
hdb_entry_clear_password(context->context, ent);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
_kadm5_free_keys (context->context, len, keys);
|
||||
|
||||
out:
|
||||
free_Keys(&keys);
|
||||
free_hdb_keyset(&hkset);
|
||||
free_HDB_extension(&ext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -196,6 +323,8 @@ is_des_key_p(int keytype)
|
||||
kadm5_ret_t
|
||||
_kadm5_set_keys_randomly (kadm5_server_context *context,
|
||||
hdb_entry *ent,
|
||||
int n_ks_tuple,
|
||||
krb5_key_salt_tuple *ks_tuple,
|
||||
krb5_keyblock **new_keys,
|
||||
int *n_keys)
|
||||
{
|
||||
@@ -206,7 +335,7 @@ _kadm5_set_keys_randomly (kadm5_server_context *context,
|
||||
Key *keys;
|
||||
|
||||
ret = hdb_generate_key_set(context->context, ent->principal,
|
||||
&keys, &num_keys, 1);
|
||||
n_ks_tuple, ks_tuple, &keys, &num_keys, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -263,8 +392,10 @@ out:
|
||||
_kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
|
||||
ent->keys.val = keys;
|
||||
ent->keys.len = num_keys;
|
||||
*new_keys = kblock;
|
||||
*n_keys = num_keys;
|
||||
if (n_keys && new_keys) {
|
||||
*new_keys = kblock;
|
||||
*n_keys = num_keys;
|
||||
}
|
||||
|
||||
hdb_entry_set_pw_change_time(context->context, ent, 0);
|
||||
hdb_entry_clear_password(context->context, ent);
|
||||
|
@@ -7,14 +7,22 @@ HEIMDAL_KAMD5_SERVER_1.0 {
|
||||
kadm5_add_passwd_quality_verifier;
|
||||
kadm5_check_password_quality;
|
||||
kadm5_chpass_principal;
|
||||
kadm5_chpass_principal_3;
|
||||
kadm5_chpass_principal_with_key;
|
||||
kadm5_chpass_principal_with_key_3;
|
||||
kadm5_create_policy;
|
||||
kadm5_create_principal;
|
||||
kadm5_delete_principal;
|
||||
kadm5_destroy;
|
||||
kadm5_decrypt_key;
|
||||
kadm5_delete_policy;
|
||||
kadm5_flush;
|
||||
kadm5_free_key_data;
|
||||
kadm5_free_name_list;
|
||||
kadm5_free_policy_ent;
|
||||
kadm5_free_principal_ent;
|
||||
kadm5_get_policy;
|
||||
kadm5_get_policies;
|
||||
kadm5_get_principal;
|
||||
kadm5_get_principals;
|
||||
kadm5_get_privs;
|
||||
@@ -24,18 +32,25 @@ HEIMDAL_KAMD5_SERVER_1.0 {
|
||||
kadm5_init_with_password_ctx;
|
||||
kadm5_init_with_skey;
|
||||
kadm5_init_with_skey_ctx;
|
||||
kadm5_lock;
|
||||
kadm5_modify_principal;
|
||||
kadm5_modify_policy;
|
||||
kadm5_randkey_principal;
|
||||
kadm5_randkey_principal_3;
|
||||
kadm5_rename_principal;
|
||||
kadm5_ret_key_data;
|
||||
kadm5_ret_principal_ent;
|
||||
kadm5_ret_principal_ent_mask;
|
||||
kadm5_ret_tl_data;
|
||||
kadm5_setup_passwd_quality_check;
|
||||
kadm5_setkey_principal;
|
||||
kadm5_setkey_principal_3;
|
||||
kadm5_store_key_data;
|
||||
kadm5_store_principal_ent;
|
||||
kadm5_store_principal_ent_mask;
|
||||
kadm5_store_principal_ent_nokeys;
|
||||
kadm5_store_tl_data;
|
||||
kadm5_unlock;
|
||||
kadm5_s_init_with_password_ctx;
|
||||
kadm5_s_init_with_password;
|
||||
kadm5_s_init_with_skey_ctx;
|
||||
|
@@ -878,6 +878,11 @@ typedef struct {
|
||||
typedef krb5_error_code
|
||||
(KRB5_CALLCONV * krb5_gic_process_last_req)(krb5_context, krb5_last_req_entry **, void *);
|
||||
|
||||
typedef struct {
|
||||
krb5_enctype ks_enctype;
|
||||
krb5int32 ks_salttype;
|
||||
}krb5_key_salt_tuple;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
Reference in New Issue
Block a user