#include "k5-int.h"
#include "kdb.h"
#include <stdio.h>
#include <errno.h>
int
krb5_db_get_key_data_kvno(krb5_context context, int count, krb5_key_data *data)
{
int i, kvno;
for (kvno = i = 0; i < count; i++) {
if (kvno < data[i].key_data_kvno) {
kvno = data[i].key_data_kvno;
}
}
return(kvno);
}
static void
cleanup_key_data(krb5_context context, int count, krb5_key_data *data)
{
int i;
if (data == NULL) return;
for (i = 0; i < count; i++)
krb5_dbe_free_key_data_contents(context, &data[i]);
free(data);
}
static krb5_error_code
preserve_one_old_key(krb5_context context, krb5_keyblock *mkey,
krb5_db_entry *dbent, krb5_key_data *old_kd,
krb5_key_data *new_kd)
{
krb5_error_code ret;
krb5_keyblock kb;
krb5_keysalt salt;
memset(new_kd, 0, sizeof(*new_kd));
ret = krb5_dbe_decrypt_key_data(context, mkey, old_kd, &kb, NULL);
if (ret == 0) {
*new_kd = *old_kd;
memset(old_kd, 0, sizeof(*old_kd));
krb5_free_keyblock_contents(context, &kb);
return 0;
}
ret = krb5_dbe_decrypt_key_data(context, NULL, old_kd, &kb, &salt);
if (ret)
return ret;
ret = krb5_dbe_encrypt_key_data(context, mkey, &kb, &salt,
old_kd->key_data_kvno, new_kd);
krb5_free_keyblock_contents(context, &kb);
krb5_free_data_contents(context, &salt.data);
return ret;
}
static krb5_error_code
preserve_old_keys(krb5_context context, krb5_keyblock *mkey,
krb5_db_entry *dbent, unsigned int keepold, int n_key_data,
krb5_key_data *key_data)
{
krb5_error_code ret;
krb5_kvno last_kvno = 0, kvno_changes = 0;
int i;
for (i = 0; i < n_key_data; i++) {
if (keepold > 1) {
if (i > 0 && key_data[i].key_data_kvno != last_kvno)
kvno_changes++;
if (kvno_changes >= keepold - 1)
break;
last_kvno = key_data[i].key_data_kvno;
}
ret = krb5_dbe_create_key_data(context, dbent);
if (ret)
return ret;
ret = preserve_one_old_key(context, mkey, dbent, &key_data[i],
&dbent->key_data[dbent->n_key_data - 1]);
if (ret)
return ret;
}
return 0;
}
static krb5_error_code
add_key_rnd(krb5_context context, krb5_keyblock *master_key,
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
krb5_db_entry *db_entry, int kvno)
{
krb5_keyblock key;
int i, j;
krb5_error_code retval;
krb5_key_data *kd_slot;
for (i = 0; i < ks_tuple_count; i++) {
krb5_boolean similar;
similar = 0;
for (j = 0; j < i; j++) {
if ((retval = krb5_c_enctype_compare(context,
ks_tuple[i].ks_enctype,
ks_tuple[j].ks_enctype,
&similar)))
return(retval);
if (similar)
break;
}
if (similar)
continue;
if ((retval = krb5_dbe_create_key_data(context, db_entry)))
return retval;
kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
&key)))
return retval;
retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL,
kvno, kd_slot);
krb5_free_keyblock_contents(context, &key);
if( retval )
return retval;
}
return 0;
}
static krb5_error_code
make_random_salt(krb5_context context, krb5_keysalt *salt_out)
{
krb5_error_code retval;
unsigned char rndbuf[8];
krb5_data salt, rnd = make_data(rndbuf, sizeof(rndbuf));
unsigned int i;
retval = krb5_c_random_make_octets(context, &rnd);
if (retval)
return retval;
retval = alloc_data(&salt, sizeof(rndbuf) * 2);
if (retval)
return retval;
for (i = 0; i < sizeof(rndbuf); i++) {
salt.data[i * 2] = 0x40 | (rndbuf[i] >> 4);
salt.data[i * 2 + 1] = 0x40 | (rndbuf[i] & 0xf);
}
salt_out->type = KRB5_KDB_SALTTYPE_SPECIAL;
salt_out->data = salt;
return 0;
}
static krb5_error_code
add_key_pwd(krb5_context context, krb5_keyblock *master_key,
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
const char *passwd, krb5_db_entry *db_entry, int kvno)
{
krb5_error_code retval;
krb5_keysalt key_salt;
krb5_keyblock key;
krb5_data pwd;
int i, j;
krb5_key_data *kd_slot;
for (i = 0; i < ks_tuple_count; i++) {
krb5_boolean similar;
similar = 0;
for (j = 0; j < i; j++) {
if ((retval = krb5_c_enctype_compare(context,
ks_tuple[i].ks_enctype,
ks_tuple[j].ks_enctype,
&similar)))
return(retval);
if (similar)
break;
}
if (j < i)
continue;
if ((retval = krb5_dbe_create_key_data(context, db_entry)))
return(retval);
kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
switch (key_salt.type = ks_tuple[i].ks_salttype) {
case KRB5_KDB_SALTTYPE_ONLYREALM: {
krb5_data * saltdata;
if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
db_entry->princ), &saltdata)))
return(retval);
key_salt.data = *saltdata;
free(saltdata);
}
break;
case KRB5_KDB_SALTTYPE_NOREALM:
if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
&key_salt.data)))
return(retval);
break;
case KRB5_KDB_SALTTYPE_NORMAL:
if ((retval = krb5_principal2salt(context, db_entry->princ,
&key_salt.data)))
return(retval);
break;
case KRB5_KDB_SALTTYPE_SPECIAL:
retval = make_random_salt(context, &key_salt);
if (retval)
return retval;
break;
default:
return(KRB5_KDB_BAD_SALTTYPE);
}
pwd = string2data((char *)passwd);
retval = krb5_c_string_to_key_with_params(context,
ks_tuple[i].ks_enctype,
&pwd, &key_salt.data,
NULL, &key);
if (retval) {
free(key_salt.data.data);
return retval;
}
retval = krb5_dbe_encrypt_key_data(context, master_key, &key,
(const krb5_keysalt *)&key_salt,
kvno, kd_slot);
if (key_salt.data.data)
free(key_salt.data.data);
free(key.contents);
if( retval )
return retval;
}
return 0;
}
static krb5_error_code
rekey(krb5_context context, krb5_keyblock *mkey, krb5_key_salt_tuple *ks_tuple,
int ks_tuple_count, const char *password, int new_kvno,
unsigned int keepold, krb5_db_entry *db_entry)
{
krb5_error_code ret;
krb5_key_data *key_data;
int n_key_data, old_kvno;
n_key_data = db_entry->n_key_data;
key_data = db_entry->key_data;
db_entry->n_key_data = 0;
db_entry->key_data = NULL;
old_kvno = krb5_db_get_key_data_kvno(context, n_key_data, key_data);
if (new_kvno < old_kvno + 1)
new_kvno = old_kvno + 1;
if (new_kvno == (1 << 16))
new_kvno = 1;
if (password != NULL) {
ret = add_key_pwd(context, mkey, ks_tuple, ks_tuple_count, password,
db_entry, new_kvno);
} else {
ret = add_key_rnd(context, mkey, ks_tuple, ks_tuple_count, db_entry,
new_kvno);
}
if (ret) {
cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
db_entry->n_key_data = n_key_data;
db_entry->key_data = key_data;
return ret;
}
if (keepold > 0) {
ret = preserve_old_keys(context, mkey, db_entry, keepold, n_key_data,
key_data);
}
cleanup_key_data(context, n_key_data, key_data);
return ret;
}
krb5_error_code
krb5_dbe_crk(krb5_context context, krb5_keyblock *mkey,
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
unsigned int keepold, krb5_db_entry *dbent)
{
return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, keepold,
dbent);
}
krb5_error_code
krb5_dbe_ark(krb5_context context, krb5_keyblock *mkey,
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
krb5_db_entry *dbent)
{
return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, 2, dbent);
}
krb5_error_code
krb5_dbe_def_cpw(krb5_context context, krb5_keyblock *mkey,
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
char *password, int new_kvno, unsigned int keepold,
krb5_db_entry *dbent)
{
return rekey(context, mkey, ks_tuple, ks_tuple_count, password, new_kvno,
keepold, dbent);
}
krb5_error_code
krb5_dbe_apw(krb5_context context, krb5_keyblock *mkey,
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, char *password,
krb5_db_entry *dbent)
{
return rekey(context, mkey, ks_tuple, ks_tuple_count, password, 0, 2,
dbent);
}