#include "k5-int.h"
#include <sys/time.h>
#include <kadm5/admin.h>
#include <kdb.h>
#include "server_internal.h"
#include <krb5/kadm5_hook_plugin.h>
#ifdef USE_VALGRIND
#include <valgrind/memcheck.h>
#else
#define VALGRIND_CHECK_DEFINED(LVALUE) ((void)0)
#endif
extern krb5_principal master_princ;
extern krb5_principal hist_princ;
extern krb5_keyblock master_keyblock;
extern krb5_db_entry master_db;
static int decrypt_key_data(krb5_context context,
int n_key_data, krb5_key_data *key_data,
krb5_keyblock **keyblocks, int *n_keys);
kadm5_ret_t
krb5_copy_key_data_contents(krb5_context context, krb5_key_data *from,
krb5_key_data *to)
{
int i, idx;
*to = *from;
idx = (from->key_data_ver == 1 ? 1 : 2);
for (i = 0; i < idx; i++) {
if ( from->key_data_length[i] ) {
to->key_data_contents[i] = malloc(from->key_data_length[i]);
if (to->key_data_contents[i] == NULL) {
for (i = 0; i < idx; i++)
zapfree(to->key_data_contents[i], to->key_data_length[i]);
return ENOMEM;
}
memcpy(to->key_data_contents[i], from->key_data_contents[i],
from->key_data_length[i]);
}
}
return 0;
}
static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
{
krb5_tl_data *n;
n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
if (n == NULL)
return NULL;
n->tl_data_contents = malloc(tl->tl_data_length);
if (n->tl_data_contents == NULL) {
free(n);
return NULL;
}
memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
n->tl_data_type = tl->tl_data_type;
n->tl_data_length = tl->tl_data_length;
n->tl_data_next = NULL;
return n;
}
static void
cleanup_key_data(krb5_context context, int count, krb5_key_data *data)
{
int i;
for (i = 0; i < count; i++)
krb5_free_key_data_contents(context, &data[i]);
free(data);
}
static krb5_boolean
ks_tuple_present(int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
krb5_key_salt_tuple *looking_for)
{
int i;
for (i = 0; i < n_ks_tuple; i++) {
if (ks_tuple[i].ks_enctype == looking_for->ks_enctype &&
ks_tuple[i].ks_salttype == looking_for->ks_salttype)
return TRUE;
}
return FALSE;
}
static kadm5_ret_t
get_policy(kadm5_server_handle_t handle, const char *name,
kadm5_policy_ent_t policy_out, krb5_boolean *have_pol_out)
{
kadm5_ret_t ret;
*have_pol_out = FALSE;
if (name == NULL)
return 0;
ret = kadm5_get_policy(handle->lhandle, (char *)name, policy_out);
if (ret == 0)
*have_pol_out = TRUE;
return (ret == KADM5_UNK_POLICY) ? 0 : ret;
}
static kadm5_ret_t
apply_keysalt_policy(kadm5_server_handle_t handle, const char *policy,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
int *new_n_kstp, krb5_key_salt_tuple **new_kstp)
{
kadm5_ret_t ret;
kadm5_policy_ent_rec polent;
krb5_boolean have_polent;
int ak_n_ks_tuple = 0;
int new_n_ks_tuple = 0;
krb5_key_salt_tuple *ak_ks_tuple = NULL;
krb5_key_salt_tuple *new_ks_tuple = NULL;
krb5_key_salt_tuple *subset;
int i, m;
if (new_n_kstp != NULL) {
*new_n_kstp = 0;
*new_kstp = NULL;
}
memset(&polent, 0, sizeof(polent));
ret = get_policy(handle, policy, &polent, &have_polent);
if (ret)
goto cleanup;
if (polent.allowed_keysalts == NULL) {
if (n_ks_tuple == 0) {
n_ks_tuple = handle->params.num_keysalts;
ks_tuple = handle->params.keysalts;
}
new_ks_tuple = malloc(n_ks_tuple * sizeof(*new_ks_tuple));
if (new_ks_tuple == NULL) {
ret = ENOMEM;
goto cleanup;
}
memcpy(new_ks_tuple, ks_tuple, n_ks_tuple * sizeof(*new_ks_tuple));
new_n_ks_tuple = n_ks_tuple;
ret = 0;
goto cleanup;
}
ret = krb5_string_to_keysalts(polent.allowed_keysalts,
",",
NULL,
0,
&ak_ks_tuple,
&ak_n_ks_tuple);
if (ret)
goto cleanup;
for (i = 0; i < n_ks_tuple; i++) {
if (!ks_tuple_present(ak_n_ks_tuple, ak_ks_tuple, &ks_tuple[i])) {
ret = KADM5_BAD_KEYSALTS;
goto cleanup;
}
}
if (n_ks_tuple == 0) {
new_n_ks_tuple = ak_n_ks_tuple;
new_ks_tuple = ak_ks_tuple;
ak_ks_tuple = NULL;
goto cleanup;
}
subset = calloc(n_ks_tuple, sizeof(*subset));
if (subset == NULL) {
ret = ENOMEM;
goto cleanup;
}
for (m = 0, i = 0; i < ak_n_ks_tuple && m < n_ks_tuple; i++) {
if (ks_tuple_present(n_ks_tuple, ks_tuple, &ak_ks_tuple[i]))
subset[m++] = ak_ks_tuple[i];
}
new_ks_tuple = subset;
new_n_ks_tuple = m;
ret = 0;
cleanup:
if (have_polent)
kadm5_free_policy_ent(handle->lhandle, &polent);
free(ak_ks_tuple);
if (new_n_kstp != NULL) {
*new_n_kstp = new_n_ks_tuple;
*new_kstp = new_ks_tuple;
} else {
free(new_ks_tuple);
}
return ret;
}
static void
check_1_6_dummy(kadm5_principal_ent_t entry, long mask,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char **passptr)
{
int i;
char *password = *passptr;
if (password == NULL || !(mask & KADM5_ATTRIBUTES) ||
!(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX))
return;
for (i = 0; (unsigned char) password[i] == i + 1; i++);
if (password[i] != '\0' || i != 255)
return;
*passptr = NULL;
}
static int
count_new_keys(int n_key_data, krb5_key_data *key_data)
{
int n;
for (n = 1; n < n_key_data; n++) {
if (key_data[n - 1].key_data_kvno != key_data[n].key_data_kvno)
return n;
}
return n_key_data;
}
kadm5_ret_t
kadm5_create_principal(void *server_handle,
kadm5_principal_ent_t entry, long mask,
char *password)
{
return
kadm5_create_principal_3(server_handle, entry, mask,
0, NULL, password);
}
kadm5_ret_t
kadm5_create_principal_3(void *server_handle,
kadm5_principal_ent_t entry, long mask,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
char *password)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
kadm5_policy_ent_rec polent;
krb5_boolean have_polent = FALSE;
krb5_timestamp now;
krb5_tl_data *tl_data_tail;
unsigned int ret;
kadm5_server_handle_t handle = server_handle;
krb5_keyblock *act_mkey;
krb5_kvno act_kvno;
int new_n_ks_tuple = 0, i;
krb5_key_salt_tuple *new_ks_tuple = NULL;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
check_1_6_dummy(entry, mask, n_ks_tuple, ks_tuple, &password);
if (entry == NULL)
return EINVAL;
if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
(mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
(mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
(mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
(mask & KADM5_FAIL_AUTH_COUNT))
return KADM5_BAD_MASK;
if ((mask & KADM5_KEY_DATA) && entry->n_key_data != 0)
return KADM5_BAD_MASK;
if((mask & KADM5_POLICY) && entry->policy == NULL)
return KADM5_BAD_MASK;
if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
return KADM5_BAD_MASK;
if((mask & ~ALL_PRINC_MASK))
return KADM5_BAD_MASK;
if (mask & KADM5_TL_DATA) {
for (tl_data_tail = entry->tl_data; tl_data_tail != NULL;
tl_data_tail = tl_data_tail->tl_data_next) {
if (tl_data_tail->tl_data_type < 256)
return KADM5_BAD_TL_TYPE;
}
}
ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
switch(ret) {
case KADM5_UNK_PRINC:
break;
case 0:
kdb_free_entry(handle, kdb, &adb);
return KADM5_DUP;
default:
return ret;
}
kdb = calloc(1, sizeof(*kdb));
if (kdb == NULL)
return ENOMEM;
kdb->mask = mask | KADM5_KEY_DATA | KADM5_PRINCIPAL;
memset(&adb, 0, sizeof(osa_princ_ent_rec));
if ((mask & KADM5_POLICY)) {
ret = get_policy(handle, entry->policy, &polent, &have_polent);
if (ret)
goto cleanup;
}
if (password) {
ret = passwd_check(handle, password, have_polent ? &polent : NULL,
entry->principal);
if (ret)
goto cleanup;
}
if ((ret = krb5_timeofday(handle->context, &now)))
goto cleanup;
kdb->magic = KRB5_KDB_MAGIC_NUMBER;
kdb->len = KRB5_KDB_V1_BASE_LENGTH;
if ((mask & KADM5_ATTRIBUTES))
kdb->attributes = entry->attributes;
else
kdb->attributes = handle->params.flags;
if ((mask & KADM5_MAX_LIFE))
kdb->max_life = entry->max_life;
else
kdb->max_life = handle->params.max_life;
if (mask & KADM5_MAX_RLIFE)
kdb->max_renewable_life = entry->max_renewable_life;
else
kdb->max_renewable_life = handle->params.max_rlife;
if ((mask & KADM5_PRINC_EXPIRE_TIME))
kdb->expiration = entry->princ_expire_time;
else
kdb->expiration = handle->params.expiration;
kdb->pw_expiration = 0;
if (mask & KADM5_PW_EXPIRATION) {
kdb->pw_expiration = entry->pw_expiration;
} else if (have_polent && polent.pw_max_life) {
kdb->mask |= KADM5_PW_EXPIRATION;
kdb->pw_expiration = ts_incr(now, polent.pw_max_life);
}
kdb->last_success = 0;
kdb->last_failed = 0;
kdb->fail_auth_count = 0;
ret = krb5_copy_principal(handle->context, entry->principal, &kdb->princ);
if (ret)
goto cleanup;
if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
goto cleanup;
if (mask & KADM5_TL_DATA) {
for (tl_data_tail = entry->tl_data; tl_data_tail;
tl_data_tail = tl_data_tail->tl_data_next)
{
ret = krb5_dbe_update_tl_data(handle->context, kdb, tl_data_tail);
if( ret )
goto cleanup;
}
}
ret = apply_keysalt_policy(handle, entry->policy, n_ks_tuple, ks_tuple,
&new_n_ks_tuple, &new_ks_tuple);
if (ret)
goto cleanup;
ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey);
if (ret)
goto cleanup;
if (mask & KADM5_KEY_DATA) {
assert(entry->n_key_data == 0);
} else if (password) {
ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple,
new_n_ks_tuple, password,
(mask & KADM5_KVNO)?entry->kvno:1,
FALSE, kdb);
} else {
ret = krb5_dbe_crk(handle->context, &master_keyblock,
new_ks_tuple, new_n_ks_tuple, FALSE, kdb);
if (mask & KADM5_KVNO) {
for (i = 0; i < kdb->n_key_data; i++)
kdb->key_data[i].key_data_kvno = entry->kvno;
}
}
if (ret)
goto cleanup;
ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
if (ret)
goto cleanup;
ret = k5_kadm5_hook_create(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, entry, mask,
new_n_ks_tuple, new_ks_tuple, password);
if (ret)
goto cleanup;
adb.admin_history_kvno = INITIAL_HIST_KVNO;
if (mask & KADM5_POLICY) {
adb.aux_attributes = KADM5_POLICY;
adb.policy = entry->policy;
}
ret = kdb_put_entry(handle, kdb, &adb);
(void) k5_kadm5_hook_create(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask,
new_n_ks_tuple, new_ks_tuple, password);
cleanup:
free(new_ks_tuple);
krb5_db_free_principal(handle->context, kdb);
if (have_polent)
(void) kadm5_free_policy_ent(handle->lhandle, &polent);
return ret;
}
kadm5_ret_t
kadm5_delete_principal(void *server_handle, krb5_principal principal)
{
unsigned int ret;
kadm5_server_handle_t handle = server_handle;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
if (principal == NULL)
return EINVAL;
if (krb5_principal_compare(handle->context, principal, master_princ))
return KADM5_PROTECT_PRINCIPAL;
ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, principal);
if (ret)
return ret;
ret = kdb_delete_entry(handle, principal);
if (ret == 0)
(void) k5_kadm5_hook_remove(handle->context,
handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, principal);
return ret;
}
kadm5_ret_t
kadm5_modify_principal(void *server_handle,
kadm5_principal_ent_t entry, long mask)
{
int ret, ret2, i;
kadm5_policy_ent_rec pol;
krb5_boolean have_pol = FALSE;
krb5_db_entry *kdb;
krb5_tl_data *tl_data_orig;
osa_princ_ent_rec adb;
kadm5_server_handle_t handle = server_handle;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
if(entry == NULL)
return EINVAL;
if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
(mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
(mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
(mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
(mask & KADM5_LAST_FAILED))
return KADM5_BAD_MASK;
if((mask & ~ALL_PRINC_MASK))
return KADM5_BAD_MASK;
if((mask & KADM5_POLICY) && entry->policy == NULL)
return KADM5_BAD_MASK;
if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
return KADM5_BAD_MASK;
if (mask & KADM5_TL_DATA) {
tl_data_orig = entry->tl_data;
while (tl_data_orig) {
if (tl_data_orig->tl_data_type < 256)
return KADM5_BAD_TL_TYPE;
tl_data_orig = tl_data_orig->tl_data_next;
}
}
ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
if (ret)
return(ret);
kdb->mask = mask;
if ((mask & KADM5_POLICY)) {
ret = get_policy(handle, entry->policy, &pol, &have_pol);
if (ret)
goto done;
adb.aux_attributes |= KADM5_POLICY;
if (adb.policy)
free(adb.policy);
adb.policy = strdup(entry->policy);
}
if (mask & KADM5_PW_EXPIRATION) {
kdb->pw_expiration = entry->pw_expiration;
} else if (have_pol) {
kdb->mask |= KADM5_PW_EXPIRATION;
if (pol.pw_max_life) {
ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
&kdb->pw_expiration);
if (ret)
goto done;
kdb->pw_expiration = ts_incr(kdb->pw_expiration, pol.pw_max_life);
} else {
kdb->pw_expiration = 0;
}
}
if ((mask & KADM5_POLICY_CLR) && (adb.aux_attributes & KADM5_POLICY)) {
free(adb.policy);
adb.policy = NULL;
adb.aux_attributes &= ~KADM5_POLICY;
kdb->pw_expiration = 0;
}
if ((mask & KADM5_ATTRIBUTES))
kdb->attributes = entry->attributes;
if ((mask & KADM5_MAX_LIFE))
kdb->max_life = entry->max_life;
if ((mask & KADM5_PRINC_EXPIRE_TIME))
kdb->expiration = entry->princ_expire_time;
if (mask & KADM5_MAX_RLIFE)
kdb->max_renewable_life = entry->max_renewable_life;
if((mask & KADM5_KVNO)) {
for (i = 0; i < kdb->n_key_data; i++)
kdb->key_data[i].key_data_kvno = entry->kvno;
}
if (mask & KADM5_TL_DATA) {
krb5_tl_data *tl;
for (tl = entry->tl_data; tl;
tl = tl->tl_data_next)
{
ret = krb5_dbe_update_tl_data(handle->context, kdb, tl);
if( ret )
{
goto done;
}
}
}
if (mask & KADM5_FAIL_AUTH_COUNT) {
if (entry->fail_auth_count != 0) {
ret = KADM5_BAD_SERVER_PARAMS;
goto done;
}
kdb->fail_auth_count = 0;
}
ret = k5_kadm5_hook_modify(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, entry, mask);
if (ret)
goto done;
ret = kdb_put_entry(handle, kdb, &adb);
if (ret) goto done;
(void) k5_kadm5_hook_modify(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, entry, mask);
ret = KADM5_OK;
done:
if (have_pol) {
ret2 = kadm5_free_policy_ent(handle->lhandle, &pol);
ret = ret ? ret : ret2;
}
kdb_free_entry(handle, kdb, &adb);
return ret;
}
kadm5_ret_t
kadm5_rename_principal(void *server_handle,
krb5_principal source, krb5_principal target)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
krb5_error_code ret;
kadm5_server_handle_t handle = server_handle;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
if (source == NULL || target == NULL)
return EINVAL;
if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
kdb_free_entry(handle, kdb, &adb);
return(KADM5_DUP);
}
ret = k5_kadm5_hook_rename(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, source, target);
if (ret)
return ret;
ret = krb5_db_rename_principal(handle->context, source, target);
if (ret)
return ret;
ret = kdb_get_entry(handle, target, &kdb, &adb);
if (ret)
return ret;
kdb->mask = 0;
ret = kdb_put_entry(handle, kdb, &adb);
kdb_free_entry(handle, kdb, &adb);
if (ret)
return ret;
(void) k5_kadm5_hook_rename(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, source, target);
return 0;
}
kadm5_ret_t
kadm5_get_principal(void *server_handle, krb5_principal principal,
kadm5_principal_ent_t entry,
long in_mask)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
krb5_error_code ret = 0;
long mask;
int i;
kadm5_server_handle_t handle = server_handle;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
mask = in_mask;
memset(entry, 0, sizeof(*entry));
if (principal == NULL)
return EINVAL;
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return ret;
if ((mask & KADM5_POLICY) &&
adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
if ((entry->policy = strdup(adb.policy)) == NULL) {
ret = ENOMEM;
goto done;
}
}
if (mask & KADM5_AUX_ATTRIBUTES)
entry->aux_attributes = adb.aux_attributes;
if ((mask & KADM5_PRINCIPAL) &&
(ret = krb5_copy_principal(handle->context, kdb->princ,
&entry->principal))) {
goto done;
}
if (mask & KADM5_PRINC_EXPIRE_TIME)
entry->princ_expire_time = kdb->expiration;
if ((mask & KADM5_LAST_PWD_CHANGE) &&
(ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
&(entry->last_pwd_change)))) {
goto done;
}
if (mask & KADM5_PW_EXPIRATION)
entry->pw_expiration = kdb->pw_expiration;
if (mask & KADM5_MAX_LIFE)
entry->max_life = kdb->max_life;
if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
ret = krb5_dbe_lookup_mod_princ_data(handle->context, kdb,
&(entry->mod_date),
&(entry->mod_name));
if (ret) {
goto done;
}
if (! (mask & KADM5_MOD_TIME))
entry->mod_date = 0;
if (! (mask & KADM5_MOD_NAME)) {
krb5_free_principal(handle->context, entry->mod_name);
entry->mod_name = NULL;
}
}
if (mask & KADM5_ATTRIBUTES)
entry->attributes = kdb->attributes;
if (mask & KADM5_KVNO)
for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++)
if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno)
entry->kvno = kdb->key_data[i].key_data_kvno;
if (mask & KADM5_MKVNO) {
ret = krb5_dbe_get_mkvno(handle->context, kdb, &entry->mkvno);
if (ret)
goto done;
}
if (mask & KADM5_MAX_RLIFE)
entry->max_renewable_life = kdb->max_renewable_life;
if (mask & KADM5_LAST_SUCCESS)
entry->last_success = kdb->last_success;
if (mask & KADM5_LAST_FAILED)
entry->last_failed = kdb->last_failed;
if (mask & KADM5_FAIL_AUTH_COUNT)
entry->fail_auth_count = kdb->fail_auth_count;
if (mask & KADM5_TL_DATA) {
krb5_tl_data *tl, *tl2;
entry->tl_data = NULL;
tl = kdb->tl_data;
while (tl) {
if (tl->tl_data_type > 255) {
if ((tl2 = dup_tl_data(tl)) == NULL) {
ret = ENOMEM;
goto done;
}
tl2->tl_data_next = entry->tl_data;
entry->tl_data = tl2;
entry->n_tl_data++;
}
tl = tl->tl_data_next;
}
}
if (mask & KADM5_KEY_DATA) {
entry->n_key_data = kdb->n_key_data;
if(entry->n_key_data) {
entry->key_data = k5calloc(entry->n_key_data,
sizeof(krb5_key_data), &ret);
if (entry->key_data == NULL)
goto done;
} else
entry->key_data = NULL;
for (i = 0; i < entry->n_key_data; i++)
ret = krb5_copy_key_data_contents(handle->context,
&kdb->key_data[i],
&entry->key_data[i]);
if (ret)
goto done;
}
ret = KADM5_OK;
done:
if (ret && entry->principal) {
krb5_free_principal(handle->context, entry->principal);
entry->principal = NULL;
}
kdb_free_entry(handle, kdb, &adb);
return ret;
}
static kadm5_ret_t
check_pw_reuse(krb5_context context,
krb5_keyblock *hist_keyblocks,
int n_new_key_data, krb5_key_data *new_key_data,
unsigned int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
{
unsigned int x, y, z;
krb5_keyblock newkey, histkey, *kb;
krb5_key_data *key_data;
krb5_error_code ret;
assert (n_new_key_data >= 0);
for (x = 0; x < (unsigned) n_new_key_data; x++) {
if (new_key_data[x].key_data_kvno != new_key_data[0].key_data_kvno)
break;
ret = krb5_dbe_decrypt_key_data(context, NULL, &(new_key_data[x]),
&newkey, NULL);
if (ret)
return(ret);
for (y = 0; y < n_pw_hist_data; y++) {
for (z = 0; z < (unsigned int) pw_hist_data[y].n_key_data; z++) {
for (kb = hist_keyblocks; kb->enctype != 0; kb++) {
key_data = &pw_hist_data[y].key_data[z];
ret = krb5_dbe_decrypt_key_data(context, kb, key_data,
&histkey, NULL);
if (ret)
continue;
if (newkey.length == histkey.length &&
newkey.enctype == histkey.enctype &&
memcmp(newkey.contents, histkey.contents,
histkey.length) == 0) {
krb5_free_keyblock_contents(context, &histkey);
krb5_free_keyblock_contents(context, &newkey);
return KADM5_PASS_REUSE;
}
krb5_free_keyblock_contents(context, &histkey);
}
}
}
krb5_free_keyblock_contents(context, &newkey);
}
return(0);
}
static void
free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
{
int i;
for (i = 0; i < hist->n_key_data; i++)
krb5_free_key_data_contents(context, &hist->key_data[i]);
free(hist->key_data);
}
static
int create_history_entry(krb5_context context,
krb5_keyblock *hist_key, int n_key_data,
krb5_key_data *key_data, osa_pw_hist_ent *hist_out)
{
int i;
krb5_error_code ret = 0;
krb5_keyblock key;
krb5_keysalt salt;
krb5_ui_2 kvno;
osa_pw_hist_ent hist;
hist_out->key_data = NULL;
hist_out->n_key_data = 0;
if (n_key_data < 0)
return EINVAL;
memset(&key, 0, sizeof(key));
memset(&hist, 0, sizeof(hist));
if (n_key_data == 0)
goto cleanup;
hist.key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret);
if (hist.key_data == NULL)
goto cleanup;
kvno = key_data[0].key_data_kvno;
for (i = 0; i < n_key_data; i++) {
if (key_data[i].key_data_kvno < kvno)
break;
ret = krb5_dbe_decrypt_key_data(context, NULL,
&key_data[i], &key,
&salt);
if (ret)
goto cleanup;
ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
key_data[i].key_data_kvno,
&hist.key_data[hist.n_key_data]);
if (ret)
goto cleanup;
hist.n_key_data++;
krb5_free_keyblock_contents(context, &key);
}
*hist_out = hist;
hist.n_key_data = 0;
hist.key_data = NULL;
cleanup:
krb5_free_keyblock_contents(context, &key);
free_history_entry(context, &hist);
return ret;
}
static kadm5_ret_t add_to_history(krb5_context context,
krb5_kvno hist_kvno,
osa_princ_ent_t adb,
kadm5_policy_ent_t pol,
osa_pw_hist_ent *pw)
{
osa_pw_hist_ent *histp;
uint32_t nhist;
unsigned int i, knext, nkeys;
nhist = pol->pw_history_num;
if (nhist <= 1)
return 0;
if (adb->admin_history_kvno != hist_kvno) {
free(adb->old_keys);
adb->old_keys = NULL;
adb->old_key_len = 0;
adb->old_key_next = 0;
adb->admin_history_kvno = hist_kvno;
}
nkeys = adb->old_key_len;
knext = adb->old_key_next;
if (nkeys + 1 < nhist) {
if (adb->old_keys == NULL) {
adb->old_keys = (osa_pw_hist_ent *)
malloc((nkeys + 1) * sizeof (osa_pw_hist_ent));
} else {
adb->old_keys = (osa_pw_hist_ent *)
realloc(adb->old_keys,
(nkeys + 1) * sizeof (osa_pw_hist_ent));
}
if (adb->old_keys == NULL)
return(ENOMEM);
memset(&adb->old_keys[nkeys], 0, sizeof(osa_pw_hist_ent));
nkeys = ++adb->old_key_len;
for (i = nkeys - 1; i > knext; i--) {
adb->old_keys[i] = adb->old_keys[i - 1];
}
memset(&adb->old_keys[knext], 0, sizeof(osa_pw_hist_ent));
} else if (nkeys + 1 > nhist) {
int j;
osa_pw_hist_t tmp;
tmp = (osa_pw_hist_ent *)
malloc((nhist - 1) * sizeof (osa_pw_hist_ent));
if (tmp == NULL)
return ENOMEM;
for (i = 0; i < nhist - 1; i++) {
j = (i + nkeys + knext - (nhist - 1)) % nkeys;
tmp[i] = adb->old_keys[j];
}
for (i = 0; i < nkeys - (nhist - 1); i++) {
j = (i + nkeys + knext) % nkeys;
histp = &adb->old_keys[j];
for (j = 0; j < histp->n_key_data; j++) {
krb5_free_key_data_contents(context, &histp->key_data[j]);
}
free(histp->key_data);
}
free(adb->old_keys);
adb->old_keys = tmp;
nkeys = adb->old_key_len = nhist - 1;
knext = adb->old_key_next = 0;
}
if (knext + 1 > nkeys)
knext = adb->old_key_next = 0;
histp = &adb->old_keys[knext];
for (i = 0; i < (unsigned int) histp->n_key_data; i++)
krb5_free_key_data_contents(context, &histp->key_data[i]);
free(histp->key_data);
adb->old_keys[knext] = *pw;
if (++adb->old_key_next == nhist - 1)
adb->old_key_next = 0;
return(0);
}
kadm5_ret_t
kadm5_chpass_principal(void *server_handle,
krb5_principal principal, char *password)
{
return
kadm5_chpass_principal_3(server_handle, principal, FALSE,
0, NULL, password);
}
kadm5_ret_t
kadm5_chpass_principal_3(void *server_handle,
krb5_principal principal, unsigned int keepold,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
char *password)
{
krb5_timestamp now;
kadm5_policy_ent_rec pol;
osa_princ_ent_rec adb;
krb5_db_entry *kdb;
int ret, ret2, hist_added;
krb5_boolean have_pol = FALSE;
kadm5_server_handle_t handle = server_handle;
osa_pw_hist_ent hist;
krb5_keyblock *act_mkey, *hist_keyblocks = NULL;
krb5_kvno act_kvno, hist_kvno;
int new_n_ks_tuple = 0;
krb5_key_salt_tuple *new_ks_tuple = NULL;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
hist_added = 0;
memset(&hist, 0, sizeof(hist));
if (principal == NULL || password == NULL)
return EINVAL;
if ((krb5_principal_compare(handle->context,
principal, hist_princ)) == TRUE)
return KADM5_PROTECT_PRINCIPAL;
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT |
KADM5_PW_EXPIRATION;
ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
&new_n_ks_tuple, &new_ks_tuple);
if (ret)
goto done;
if ((adb.aux_attributes & KADM5_POLICY)) {
ret = get_policy(handle, adb.policy, &pol, &have_pol);
if (ret)
goto done;
}
if (have_pol) {
ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
if (ret)
goto done;
ret = create_history_entry(handle->context, &hist_keyblocks[0],
kdb->n_key_data, kdb->key_data, &hist);
if (ret == KRB5_BAD_ENCTYPE)
ret = KADM5_BAD_HIST_KEY;
if (ret)
goto done;
}
if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
principal)))
goto done;
ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey);
if (ret)
goto done;
ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple,
password, 0 ,
keepold, kdb);
if (ret)
goto done;
ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
if (ret)
goto done;
kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
ret = krb5_timeofday(handle->context, &now);
if (ret)
goto done;
kdb->pw_expiration = 0;
if (have_pol) {
ret = check_pw_reuse(handle->context, hist_keyblocks,
kdb->n_key_data, kdb->key_data,
1, &hist);
if (ret)
goto done;
if (pol.pw_history_num > 1) {
if (adb.admin_history_kvno == hist_kvno) {
ret = check_pw_reuse(handle->context, hist_keyblocks,
kdb->n_key_data, kdb->key_data,
adb.old_key_len, adb.old_keys);
if (ret)
goto done;
}
if (hist.n_key_data > 0) {
ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
&hist);
if (ret)
goto done;
hist_added = 1;
}
}
if (pol.pw_max_life)
kdb->pw_expiration = ts_incr(now, pol.pw_max_life);
}
ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
if (ret)
goto done;
kdb->fail_auth_count = 0;
if (hist_added)
kdb->mask |= KADM5_KEY_HIST;
ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
new_n_ks_tuple, new_ks_tuple, password);
if (ret)
goto done;
if ((ret = kdb_put_entry(handle, kdb, &adb)))
goto done;
(void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, principal,
keepold, new_n_ks_tuple, new_ks_tuple, password);
ret = KADM5_OK;
done:
free(new_ks_tuple);
if (!hist_added && hist.key_data)
free_history_entry(handle->context, &hist);
kdb_free_entry(handle, kdb, &adb);
kdb_free_keyblocks(handle, hist_keyblocks);
if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
&& !ret)
ret = ret2;
return ret;
}
kadm5_ret_t
kadm5_randkey_principal(void *server_handle,
krb5_principal principal,
krb5_keyblock **keyblocks,
int *n_keys)
{
return
kadm5_randkey_principal_3(server_handle, principal,
FALSE, 0, NULL,
keyblocks, n_keys);
}
kadm5_ret_t
kadm5_randkey_principal_3(void *server_handle,
krb5_principal principal,
unsigned int keepold,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **keyblocks,
int *n_keys)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
krb5_timestamp now;
kadm5_policy_ent_rec pol;
int ret, n_new_keys;
krb5_boolean have_pol = FALSE;
kadm5_server_handle_t handle = server_handle;
krb5_keyblock *act_mkey;
krb5_kvno act_kvno;
int new_n_ks_tuple = 0;
krb5_key_salt_tuple *new_ks_tuple = NULL;
if (keyblocks)
*keyblocks = NULL;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
if (principal == NULL)
return EINVAL;
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT |
KADM5_PW_EXPIRATION;
ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
&new_n_ks_tuple, &new_ks_tuple);
if (ret)
goto done;
if (krb5_principal_compare(handle->context, principal, hist_princ)) {
if (keepold) {
ret = KADM5_PROTECT_PRINCIPAL;
goto done;
}
new_n_ks_tuple = 1;
}
ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey);
if (ret)
goto done;
ret = krb5_dbe_crk(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple,
keepold, kdb);
if (ret)
goto done;
ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
if (ret)
goto done;
kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
ret = krb5_timeofday(handle->context, &now);
if (ret)
goto done;
if ((adb.aux_attributes & KADM5_POLICY)) {
ret = get_policy(handle, adb.policy, &pol, &have_pol);
if (ret)
goto done;
}
kdb->pw_expiration = 0;
if (have_pol && pol.pw_max_life)
kdb->pw_expiration = ts_incr(now, pol.pw_max_life);
ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
if (ret)
goto done;
kdb->fail_auth_count = 0;
if (keyblocks) {
n_new_keys = count_new_keys(kdb->n_key_data, kdb->key_data);
ret = decrypt_key_data(handle->context, n_new_keys, kdb->key_data,
keyblocks, n_keys);
if (ret)
goto done;
}
ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
new_n_ks_tuple, new_ks_tuple, NULL);
if (ret)
goto done;
if ((ret = kdb_put_entry(handle, kdb, &adb)))
goto done;
(void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, principal,
keepold, new_n_ks_tuple, new_ks_tuple, NULL);
ret = KADM5_OK;
done:
free(new_ks_tuple);
kdb_free_entry(handle, kdb, &adb);
if (have_pol)
kadm5_free_policy_ent(handle->lhandle, &pol);
return ret;
}
kadm5_ret_t
kadm5_setkey_principal(void *server_handle,
krb5_principal principal,
krb5_keyblock *keyblocks,
int n_keys)
{
return
kadm5_setkey_principal_3(server_handle, principal,
FALSE, 0, NULL,
keyblocks, n_keys);
}
kadm5_ret_t
kadm5_setkey_principal_3(void *server_handle,
krb5_principal principal,
unsigned int keepold,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
krb5_keyblock *keyblocks,
int n_keys)
{
kadm5_key_data *key_data;
kadm5_ret_t ret;
int i;
if (keyblocks == NULL)
return EINVAL;
if (n_ks_tuple) {
if (n_ks_tuple != n_keys)
return KADM5_SETKEY3_ETYPE_MISMATCH;
for (i = 0; i < n_ks_tuple; i++) {
if (ks_tuple[i].ks_enctype != keyblocks[i].enctype)
return KADM5_SETKEY3_ETYPE_MISMATCH;
}
}
key_data = calloc(n_keys, sizeof(kadm5_key_data));
if (key_data == NULL)
return ENOMEM;
for (i = 0; i < n_keys; i++) {
key_data[i].key = keyblocks[i];
key_data[i].salt.type =
n_ks_tuple ? ks_tuple[i].ks_salttype : KRB5_KDB_SALTTYPE_NORMAL;
}
ret = kadm5_setkey_principal_4(server_handle, principal, keepold,
key_data, n_keys);
free(key_data);
return ret;
}
static kadm5_ret_t
make_ks_from_key_data(krb5_context context, kadm5_key_data *key_data,
int n_key_data, krb5_key_salt_tuple **out)
{
int i;
krb5_key_salt_tuple *ks;
*out = NULL;
ks = calloc(n_key_data, sizeof(*ks));
if (ks == NULL)
return ENOMEM;
for (i = 0; i < n_key_data; i++) {
ks[i].ks_enctype = key_data[i].key.enctype;
ks[i].ks_salttype = key_data[i].salt.type;
}
*out = ks;
return 0;
}
kadm5_ret_t
kadm5_setkey_principal_4(void *server_handle, krb5_principal principal,
unsigned int keepold, kadm5_key_data *key_data,
int n_key_data)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
krb5_timestamp now;
kadm5_policy_ent_rec pol;
krb5_key_data *new_key_data = NULL;
int i, j, ret, n_new_key_data = 0;
krb5_kvno kvno;
krb5_boolean similar, have_pol = FALSE;
kadm5_server_handle_t handle = server_handle;
krb5_keyblock *act_mkey;
krb5_key_salt_tuple *ks_from_keys = NULL;
CHECK_HANDLE(server_handle);
krb5_clear_error_message(handle->context);
if (principal == NULL || key_data == NULL || n_key_data == 0)
return EINVAL;
if (hist_princ != NULL &&
krb5_principal_compare(handle->context, principal, hist_princ))
return KADM5_PROTECT_PRINCIPAL;
kvno = key_data[0].kvno;
for (i = 1; i < n_key_data; i++) {
if (key_data[i].kvno != kvno)
return KADM5_SETKEY_BAD_KVNO;
}
ret = kdb_get_entry(handle, principal, &kdb, &adb);
if (ret)
return ret;
kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES | KADM5_FAIL_AUTH_COUNT |
KADM5_PW_EXPIRATION;
if (kvno == 0) {
for (i = 0; i < kdb->n_key_data; i++) {
if (kdb->key_data[i].key_data_kvno > kvno)
kvno = kdb->key_data[i].key_data_kvno;
}
kvno++;
} else if (keepold) {
for (i = 0; i < kdb->n_key_data; i++) {
if (kdb->key_data[i].key_data_kvno == kvno) {
ret = KADM5_SETKEY_BAD_KVNO;
goto done;
}
}
}
ret = make_ks_from_key_data(handle->context, key_data, n_key_data,
&ks_from_keys);
if (ret)
goto done;
ret = apply_keysalt_policy(handle, adb.policy, n_key_data, ks_from_keys,
NULL, NULL);
free(ks_from_keys);
if (ret)
goto done;
for (i = 0; i < n_key_data; i++) {
for (j = i + 1; j < n_key_data; j++) {
ret = krb5_c_enctype_compare(handle->context,
key_data[i].key.enctype,
key_data[j].key.enctype,
&similar);
if (ret)
goto done;
if (similar) {
if (key_data[i].salt.type == key_data[j].salt.type) {
ret = KADM5_SETKEY_DUP_ENCTYPES;
goto done;
}
}
}
}
n_new_key_data = n_key_data + (keepold ? kdb->n_key_data : 0);
new_key_data = calloc(n_new_key_data, sizeof(krb5_key_data));
if (new_key_data == NULL) {
n_new_key_data = 0;
ret = ENOMEM;
goto done;
}
n_new_key_data = 0;
for (i = 0; i < n_key_data; i++) {
ret = kdb_get_active_mkey(handle, NULL, &act_mkey);
if (ret)
goto done;
ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
&key_data[i].key, &key_data[i].salt,
kvno, &new_key_data[i]);
if (ret)
goto done;
n_new_key_data++;
}
if (keepold) {
memcpy(new_key_data + n_new_key_data, kdb->key_data,
kdb->n_key_data * sizeof(krb5_key_data));
memset(kdb->key_data, 0, kdb->n_key_data * sizeof(krb5_key_data));
krb5_dbe_sort_key_data(new_key_data, n_new_key_data);
}
cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
kdb->key_data = new_key_data;
kdb->n_key_data = n_new_key_data;
new_key_data = NULL;
n_new_key_data = 0;
kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
ret = krb5_timeofday(handle->context, &now);
if (ret)
goto done;
if (adb.aux_attributes & KADM5_POLICY) {
ret = get_policy(handle, adb.policy, &pol, &have_pol);
if (ret)
goto done;
}
kdb->pw_expiration = 0;
if (have_pol && pol.pw_max_life)
kdb->pw_expiration = ts_incr(now, pol.pw_max_life);
ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
if (ret)
goto done;
kdb->fail_auth_count = 0;
ret = kdb_put_entry(handle, kdb, &adb);
if (ret)
goto done;
ret = KADM5_OK;
done:
cleanup_key_data(handle->context, n_new_key_data, new_key_data);
kdb_free_entry(handle, kdb, &adb);
if (have_pol)
kadm5_free_policy_ent(handle->lhandle, &pol);
return ret;
}
kadm5_ret_t
kadm5_get_principal_keys(void *server_handle ,
krb5_principal principal ,
krb5_kvno kvno ,
kadm5_key_data **key_data_out ,
int *n_key_data_out )
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
kadm5_ret_t ret;
kadm5_server_handle_t handle = server_handle;
kadm5_key_data *key_data = NULL;
int i, nkeys = 0;
if (principal == NULL || key_data_out == NULL || n_key_data_out == NULL)
return EINVAL;
CHECK_HANDLE(server_handle);
if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
return(ret);
key_data = calloc(kdb->n_key_data, sizeof(kadm5_key_data));
if (key_data == NULL) {
ret = ENOMEM;
goto done;
}
for (i = 0, nkeys = 0; i < kdb->n_key_data; i++) {
if (kvno != 0 && kvno != kdb->key_data[i].key_data_kvno)
continue;
key_data[nkeys].kvno = kdb->key_data[i].key_data_kvno;
ret = krb5_dbe_decrypt_key_data(handle->context, NULL,
&kdb->key_data[i],
&key_data[nkeys].key,
&key_data[nkeys].salt);
if (ret)
goto done;
nkeys++;
}
*n_key_data_out = nkeys;
*key_data_out = key_data;
key_data = NULL;
nkeys = 0;
ret = KADM5_OK;
done:
kdb_free_entry(handle, kdb, &adb);
kadm5_free_kadm5_key_data(handle->context, nkeys, key_data);
return ret;
}
static int decrypt_key_data(krb5_context context,
int n_key_data, krb5_key_data *key_data,
krb5_keyblock **keyblocks, int *n_keys)
{
krb5_keyblock *keys;
int ret, i;
keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
if (keys == NULL)
return ENOMEM;
memset(keys, 0, n_key_data*sizeof(krb5_keyblock));
for (i = 0; i < n_key_data; i++) {
ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &keys[i],
NULL);
if (ret) {
for (; i >= 0; i--)
krb5_free_keyblock_contents(context, &keys[i]);
free(keys);
return ret;
}
}
*keyblocks = keys;
if (n_keys)
*n_keys = n_key_data;
return 0;
}
kadm5_ret_t kadm5_decrypt_key(void *server_handle,
kadm5_principal_ent_t entry, krb5_int32
ktype, krb5_int32 stype, krb5_int32
kvno, krb5_keyblock *keyblock,
krb5_keysalt *keysalt, int *kvnop)
{
kadm5_server_handle_t handle = server_handle;
krb5_db_entry dbent;
krb5_key_data *key_data;
krb5_keyblock *mkey_ptr;
int ret;
CHECK_HANDLE(server_handle);
if (entry->n_key_data == 0 || entry->key_data == NULL)
return EINVAL;
dbent.n_key_data = entry->n_key_data;
dbent.key_data = entry->key_data;
if ((ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
stype, kvno, &key_data)))
return ret;
dbent.tl_data = entry->tl_data;
if ((ret = krb5_dbe_find_mkey(handle->context, &dbent, &mkey_ptr))) {
if (krb5_db_fetch_mkey_list(handle->context, master_princ,
&master_keyblock) == 0) {
if ((ret = krb5_dbe_find_mkey(handle->context, &dbent,
&mkey_ptr))) {
return ret;
}
} else {
return ret;
}
}
if ((ret = krb5_dbe_decrypt_key_data(handle->context, NULL, key_data,
keyblock, keysalt)))
return ret;
if (ktype != -1)
keyblock->enctype = ktype;
if (kvnop)
*kvnop = key_data->key_data_kvno;
return KADM5_OK;
}
kadm5_ret_t
kadm5_purgekeys(void *server_handle,
krb5_principal principal,
int keepkvno)
{
kadm5_server_handle_t handle = server_handle;
kadm5_ret_t ret;
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
krb5_key_data *old_keydata;
int n_old_keydata;
int i, j, k;
CHECK_HANDLE(server_handle);
if (principal == NULL)
return EINVAL;
ret = kdb_get_entry(handle, principal, &kdb, &adb);
if (ret)
return(ret);
if (keepkvno <= 0) {
keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
kdb->key_data);
}
old_keydata = kdb->key_data;
n_old_keydata = kdb->n_key_data;
kdb->n_key_data = 0;
kdb->key_data = calloc(n_old_keydata, sizeof(krb5_key_data));
if (kdb->key_data == NULL) {
ret = ENOMEM;
goto done;
}
memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
for (i = 0, j = 0; i < n_old_keydata; i++) {
if (old_keydata[i].key_data_kvno < keepkvno)
continue;
kdb->key_data[j] = old_keydata[i];
for (k = 0; k < old_keydata[i].key_data_ver; k++) {
old_keydata[i].key_data_contents[k] = NULL;
}
j++;
}
kdb->n_key_data = j;
cleanup_key_data(handle->context, n_old_keydata, old_keydata);
kdb->mask = KADM5_KEY_DATA;
ret = kdb_put_entry(handle, kdb, &adb);
if (ret)
goto done;
done:
kdb_free_entry(handle, kdb, &adb);
return ret;
}
kadm5_ret_t
kadm5_get_strings(void *server_handle, krb5_principal principal,
krb5_string_attr **strings_out, int *count_out)
{
kadm5_server_handle_t handle = server_handle;
kadm5_ret_t ret;
krb5_db_entry *kdb = NULL;
*strings_out = NULL;
*count_out = 0;
CHECK_HANDLE(server_handle);
if (principal == NULL)
return EINVAL;
ret = kdb_get_entry(handle, principal, &kdb, NULL);
if (ret)
return ret;
ret = krb5_dbe_get_strings(handle->context, kdb, strings_out, count_out);
kdb_free_entry(handle, kdb, NULL);
return ret;
}
kadm5_ret_t
kadm5_set_string(void *server_handle, krb5_principal principal,
const char *key, const char *value)
{
kadm5_server_handle_t handle = server_handle;
kadm5_ret_t ret;
krb5_db_entry *kdb;
osa_princ_ent_rec adb;
CHECK_HANDLE(server_handle);
if (principal == NULL || key == NULL)
return EINVAL;
ret = kdb_get_entry(handle, principal, &kdb, &adb);
if (ret)
return ret;
ret = krb5_dbe_set_string(handle->context, kdb, key, value);
if (ret)
goto done;
kdb->mask = KADM5_TL_DATA;
ret = kdb_put_entry(handle, kdb, &adb);
done:
kdb_free_entry(handle, kdb, &adb);
return ret;
}
kadm5_ret_t
kadm5_create_alias(void *server_handle, krb5_principal alias,
krb5_principal target)
{
krb5_db_entry *kdb;
osa_princ_ent_rec adb = { 0 };
krb5_error_code ret;
kadm5_server_handle_t handle = server_handle;
CHECK_HANDLE(server_handle);
if (alias == NULL || target == NULL)
return EINVAL;
if (!krb5_realm_compare(handle->context, alias, target))
return KADM5_ALIAS_REALM;
ret = kdb_get_entry(handle, alias, &kdb, NULL);
if (!ret) {
kdb_free_entry(handle, kdb, NULL);
return KADM5_DUP;
}
ret = k5_kadm5_hook_alias(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_PRECOMMIT, alias, target);
if (ret)
return ret;
ret = krb5_dbe_make_alias_entry(handle->context, alias, target, &kdb);
if (ret)
return ret;
ret = kdb_put_entry(handle, kdb, &adb);
krb5_db_free_principal(handle->context, kdb);
if (ret)
return ret;
(void) k5_kadm5_hook_alias(handle->context, handle->hook_handles,
KADM5_HOOK_STAGE_POSTCOMMIT, alias, target);
return 0;
}