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:
Love Hörnquist Åstrand
2011-07-24 15:41:36 -07:00
58 changed files with 2136 additions and 404 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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__ */

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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");

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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){

View File

@@ -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);

View File

@@ -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

View File

@@ -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"

View File

@@ -38,6 +38,7 @@
#include <config.h>
#include <roken.h>
#include <heimbase.h>
#include <stdio.h>
#include <stdlib.h>

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;