/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/kdb/kdb_cpw.c */
/*
- * Copyright 1995, 2009 by the Massachusetts Institute of Technology.
+ * Copyright 1995, 2009, 2014 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
#include <stdio.h>
#include <errno.h>
+enum save { DISCARD_ALL, KEEP_LAST_KVNO, KEEP_ALL };
+
int
krb5_db_get_key_data_kvno(context, count, data)
krb5_context context;
int count;
krb5_key_data * data;
{
- int i, j;
+ int i;
/* If data is NULL, count is always 0 */
if (data == NULL) return;
- for (i = 0; i < count; i++) {
- for (j = 0; j < data[i].key_data_ver; j++) {
- if (data[i].key_data_length[j]) {
- krb5_db_free(context, data[i].key_data_contents[j]);
- }
- }
+ for (i = 0; i < count; i++)
+ krb5_dbe_free_key_data_contents(context, &data[i]);
+ free(data);
+}
+
+/* Transfer key data from old_kd to new_kd, making sure that new_kd is
+ * encrypted with mkey. May steal from old_kd and zero it out. */
+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) {
+ /* old_kd is already encrypted in mkey, so just move it. */
+ *new_kd = *old_kd;
+ memset(old_kd, 0, sizeof(*old_kd));
+ krb5_free_keyblock_contents(context, &kb);
+ return 0;
+ }
+
+ /* Decrypt and re-encrypt old_kd using mkey. */
+ 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;
+}
+
+/*
+ * Add key_data to dbent, making sure that each entry is encrypted in mkey. If
+ * kvno is non-zero, preserve only keys of that kvno. May steal some elements
+ * from key_data and zero them out.
+ */
+static krb5_error_code
+preserve_old_keys(krb5_context context, krb5_keyblock *mkey,
+ krb5_db_entry *dbent, int kvno, int n_key_data,
+ krb5_key_data *key_data)
+{
+ krb5_error_code ret;
+ int i;
+
+ for (i = 0; i < n_key_data; i++) {
+ if (kvno != 0 && key_data[i].key_data_kvno != kvno)
+ continue;
+ 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;
}
- krb5_db_free(context, data);
+ return 0;
}
static krb5_error_code
krb5_db_entry * db_entry;
int kvno;
{
- krb5_principal krbtgt_princ;
krb5_keyblock key;
- krb5_db_entry *krbtgt_entry;
- int max_kvno, i, j, k;
+ int i, j;
krb5_error_code retval;
- krb5_key_data tmp_key_data;
- krb5_key_data *tptr;
-
- memset( &tmp_key_data, 0, sizeof(tmp_key_data));
-
-
- retval = krb5_build_principal_ext(context, &krbtgt_princ,
- db_entry->princ->realm.length,
- db_entry->princ->realm.data,
- KRB5_TGS_NAME_SIZE,
- KRB5_TGS_NAME,
- db_entry->princ->realm.length,
- db_entry->princ->realm.data,
- 0);
- if (retval)
- return retval;
-
- /* Get tgt from database */
- retval = krb5_db_get_principal(context, krbtgt_princ, 0, &krbtgt_entry);
- krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
- if (retval)
- return(retval);
-
- /* Get max kvno */
- for (max_kvno = j = 0; j < krbtgt_entry->n_key_data; j++) {
- if (max_kvno < krbtgt_entry->key_data[j].key_data_kvno) {
- max_kvno = krbtgt_entry->key_data[j].key_data_kvno;
- }
- }
+ krb5_key_data *kd_slot;
for (i = 0; i < ks_tuple_count; i++) {
krb5_boolean similar;
continue;
if ((retval = krb5_dbe_create_key_data(context, db_entry)))
- goto add_key_rnd_err;
+ return retval;
+ kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
/* there used to be code here to extract the old key, and derive
a new key from it. Now that there's a unified prng, that isn't
/* make new key */
if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
&key)))
- goto add_key_rnd_err;
-
+ return retval;
- /* db library will free this. Since, its a so, it could actually be using different memory management
- function. So, its better if the memory is allocated by the db's malloc. So, a temporary memory is used
- here which will later be copied to the db_entry */
retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL,
- kvno, &tmp_key_data);
+ kvno, kd_slot);
krb5_free_keyblock_contents(context, &key);
if( retval )
- goto add_key_rnd_err;
-
- tptr = &db_entry->key_data[db_entry->n_key_data-1];
-
- tptr->key_data_ver = tmp_key_data.key_data_ver;
- tptr->key_data_kvno = tmp_key_data.key_data_kvno;
-
- for( k = 0; k < tmp_key_data.key_data_ver; k++ )
- {
- tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
- tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
- if( tmp_key_data.key_data_contents[k] )
- {
- tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
- if( tptr->key_data_contents[k] == NULL )
- {
- cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
- db_entry->key_data = NULL;
- db_entry->n_key_data = 0;
- retval = ENOMEM;
- goto add_key_rnd_err;
- }
- memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
-
- memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
- free( tmp_key_data.key_data_contents[k] );
- tmp_key_data.key_data_contents[k] = NULL;
- }
- }
-
- }
-
-add_key_rnd_err:
- krb5_db_free_principal(context, krbtgt_entry);
-
- for( i = 0; i < tmp_key_data.key_data_ver; i++ )
- {
- if( tmp_key_data.key_data_contents[i] )
- {
- memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
- free( tmp_key_data.key_data_contents[i] );
- }
- }
- return(retval);
-}
-
-/*
- * Change random key for a krb5_db_entry
- * Assumes the max kvno
- *
- * As a side effect all old keys are nuked if keepold is false.
- */
-krb5_error_code
-krb5_dbe_crk(context, master_key, ks_tuple, ks_tuple_count, keepold, db_entry)
- krb5_context context;
- krb5_keyblock * master_key;
- krb5_key_salt_tuple * ks_tuple;
- int ks_tuple_count;
- krb5_boolean keepold;
- krb5_db_entry * db_entry;
-{
- int key_data_count;
- int n_new_key_data;
- krb5_key_data * key_data;
- krb5_error_code retval;
- int kvno;
- int i;
-
- /* First save the old keydata */
- kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
- db_entry->key_data);
- key_data_count = db_entry->n_key_data;
- key_data = db_entry->key_data;
- db_entry->key_data = NULL;
- db_entry->n_key_data = 0;
-
- /* increment the kvno */
- kvno++;
-
- retval = add_key_rnd(context, master_key, ks_tuple,
- ks_tuple_count, db_entry, kvno);
- if (retval) {
- cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
- db_entry->n_key_data = key_data_count;
- db_entry->key_data = key_data;
- } else if (keepold) {
- n_new_key_data = db_entry->n_key_data;
- for (i = 0; i < key_data_count; i++) {
- retval = krb5_dbe_create_key_data(context, db_entry);
- if (retval) {
- cleanup_key_data(context, db_entry->n_key_data,
- db_entry->key_data);
- break;
- }
- db_entry->key_data[i+n_new_key_data] = key_data[i];
- memset(&key_data[i], 0, sizeof(krb5_key_data));
- }
- krb5_db_free(context, key_data); /* we moved the cotents to new memory. But, the original block which contained the data */
- } else {
- cleanup_key_data(context, key_data_count, key_data);
+ return retval;
}
- return(retval);
-}
-/*
- * Add random key for a krb5_db_entry
- * Assumes the max kvno
- *
- * As a side effect all old keys older than the max kvno are nuked.
- */
-krb5_error_code
-krb5_dbe_ark(context, master_key, ks_tuple, ks_tuple_count, db_entry)
- krb5_context context;
- krb5_keyblock * master_key;
- krb5_key_salt_tuple * ks_tuple;
- int ks_tuple_count;
- krb5_db_entry * db_entry;
-{
- int key_data_count;
- krb5_key_data * key_data;
- krb5_error_code retval;
- int kvno;
- int i;
-
- /* First save the old keydata */
- kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
- db_entry->key_data);
- key_data_count = db_entry->n_key_data;
- key_data = db_entry->key_data;
- db_entry->key_data = NULL;
- db_entry->n_key_data = 0;
-
- /* increment the kvno */
- kvno++;
-
- if ((retval = add_key_rnd(context, master_key, ks_tuple,
- ks_tuple_count, db_entry, kvno))) {
- cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
- db_entry->n_key_data = key_data_count;
- db_entry->key_data = key_data;
- } else {
- /* Copy keys with key_data_kvno == kvno - 1 ( = old kvno ) */
- for (i = 0; i < key_data_count; i++) {
- if (key_data[i].key_data_kvno == (kvno - 1)) {
- if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
- cleanup_key_data(context, db_entry->n_key_data,
- db_entry->key_data);
- break;
- }
- /* We should decrypt/re-encrypt the data to use the same mkvno*/
- db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
- memset(&key_data[i], 0, sizeof(krb5_key_data));
- }
- }
- cleanup_key_data(context, key_data_count, key_data);
- }
- return(retval);
+ return 0;
}
/* Construct a random explicit salt. */
krb5_keyblock * master_key;
krb5_key_salt_tuple * ks_tuple;
int ks_tuple_count;
- char * passwd;
+ const char * passwd;
krb5_db_entry * db_entry;
int kvno;
{
krb5_keysalt key_salt;
krb5_keyblock key;
krb5_data pwd;
- int i, j, k;
- krb5_key_data tmp_key_data;
- krb5_key_data *tptr;
-
- memset( &tmp_key_data, 0, sizeof(tmp_key_data));
-
- retval = 0;
+ krb5_data afs_params = string2data("\1"), *s2k_params;
+ int i, j;
+ krb5_key_data *kd_slot;
for (i = 0; i < ks_tuple_count; i++) {
krb5_boolean similar;
similar = 0;
+ s2k_params = NULL;
/*
* We could use krb5_keysalt_iterate to replace this loop, or use
if ((retval = krb5_dbe_create_key_data(context, db_entry)))
return(retval);
+ kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
/* Convert password string to key using appropriate salt */
switch (key_salt.type = ks_tuple[i].ks_salttype) {
key_salt.data.data = 0;
break;
case KRB5_KDB_SALTTYPE_AFS3:
- /* The afs_mit_string_to_key needs to use strlen, and the
- realm field is not (necessarily) NULL terminated. */
- retval = krb5int_copy_data_contents_add0(context,
- krb5_princ_realm(context,
- db_entry->princ),
- &key_salt.data);
+ retval = krb5int_copy_data_contents(context,
+ &db_entry->princ->realm,
+ &key_salt.data);
if (retval)
return retval;
- key_salt.data.length = SALT_TYPE_AFS_LENGTH; /*length actually used below...*/
+ s2k_params = &afs_params;
break;
case KRB5_KDB_SALTTYPE_SPECIAL:
retval = make_random_salt(context, &key_salt);
return(KRB5_KDB_BAD_SALTTYPE);
}
- pwd.data = passwd;
- pwd.length = strlen(passwd);
+ pwd = string2data((char *)passwd);
- /* AFS string to key will happen here */
- if ((retval = krb5_c_string_to_key(context, ks_tuple[i].ks_enctype,
- &pwd, &key_salt.data, &key))) {
- if (key_salt.data.data)
- free(key_salt.data.data);
- return(retval);
+ retval = krb5_c_string_to_key_with_params(context,
+ ks_tuple[i].ks_enctype,
+ &pwd, &key_salt.data,
+ s2k_params, &key);
+ if (retval) {
+ free(key_salt.data.data);
+ return retval;
}
- if (key_salt.data.length == SALT_TYPE_AFS_LENGTH)
- key_salt.data.length =
- krb5_princ_realm(context, db_entry->princ)->length;
-
- /* memory allocation to be done by db. So, use temporary block and later copy
- it to the memory allocated by db */
retval = krb5_dbe_encrypt_key_data(context, master_key, &key,
(const krb5_keysalt *)&key_salt,
- kvno, &tmp_key_data);
+ kvno, kd_slot);
if (key_salt.data.data)
free(key_salt.data.data);
free(key.contents);
if( retval )
return retval;
+ }
- tptr = &db_entry->key_data[db_entry->n_key_data-1];
-
- tptr->key_data_ver = tmp_key_data.key_data_ver;
- tptr->key_data_kvno = tmp_key_data.key_data_kvno;
-
- for( k = 0; k < tmp_key_data.key_data_ver; k++ )
- {
- tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
- tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
- if( tmp_key_data.key_data_contents[k] )
- {
- tptr->key_data_contents[k] = krb5_db_alloc(context, NULL, tmp_key_data.key_data_length[k]);
- if( tptr->key_data_contents[k] == NULL )
- {
- cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
- db_entry->key_data = NULL;
- db_entry->n_key_data = 0;
- retval = ENOMEM;
- goto add_key_pwd_err;
- }
- memcpy( tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);
-
- memset( tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
- free( tmp_key_data.key_data_contents[k] );
- tmp_key_data.key_data_contents[k] = NULL;
- }
- }
+ 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,
+ enum save savekeys, krb5_db_entry *db_entry)
+{
+ krb5_error_code ret;
+ krb5_key_data *key_data;
+ int n_key_data, old_kvno, save_kvno;
+
+ /* Save aside the old key data. */
+ 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;
+
+ /* Make sure the new kvno is greater than the old largest kvno. */
+ 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;
+ /* Wrap from 65535 to 1; we can only store 16-bit kvno values in key_data,
+ * and we assign special meaning to kvno 0. */
+ if (new_kvno == (1 << 16))
+ new_kvno = 1;
+
+ /* Add new keys to the front of the list. */
+ 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);
}
-add_key_pwd_err:
- for( i = 0; i < tmp_key_data.key_data_ver; i++ )
- {
- if( tmp_key_data.key_data_contents[i] )
- {
- memset( tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
- free( tmp_key_data.key_data_contents[i] );
- }
+ 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;
}
- return(retval);
+ /* Possibly add some or all of the old keys to the back of the list. May
+ * steal from and zero out some of the old key data entries. */
+ if (savekeys != DISCARD_ALL) {
+ save_kvno = (savekeys == KEEP_LAST_KVNO) ? old_kvno : 0;
+ ret = preserve_old_keys(context, mkey, db_entry, save_kvno, n_key_data,
+ key_data);
+ }
+
+ /* Free any old key data entries not stolen and zeroed out above. */
+ cleanup_key_data(context, n_key_data, key_data);
+ return ret;
}
/*
- * Change password for a krb5_db_entry
+ * Change random key for a krb5_db_entry
* Assumes the max kvno
*
* As a side effect all old keys are nuked if keepold is false.
*/
krb5_error_code
-krb5_dbe_def_cpw(context, master_key, ks_tuple, ks_tuple_count, passwd,
- new_kvno, keepold, db_entry)
- krb5_context context;
- krb5_keyblock * master_key;
- krb5_key_salt_tuple * ks_tuple;
- int ks_tuple_count;
- char * passwd;
- int new_kvno;
- krb5_boolean keepold;
- krb5_db_entry * db_entry;
+krb5_dbe_crk(krb5_context context, krb5_keyblock *mkey,
+ krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
+ krb5_boolean keepold, krb5_db_entry *dbent)
{
- int key_data_count;
- int n_new_key_data;
- krb5_key_data * key_data;
- krb5_error_code retval;
- int old_kvno;
- int i;
-
- /* First save the old keydata */
- old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
- db_entry->key_data);
- key_data_count = db_entry->n_key_data;
- key_data = db_entry->key_data;
- db_entry->key_data = NULL;
- db_entry->n_key_data = 0;
+ return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0,
+ keepold ? KEEP_ALL : DISCARD_ALL, dbent);
+}
- /* increment the kvno. if the requested kvno is too small,
- increment the old kvno */
- if (new_kvno < old_kvno+1)
- new_kvno = old_kvno+1;
+/*
+ * Add random key for a krb5_db_entry
+ * Assumes the max kvno
+ *
+ * As a side effect all old keys older than the max kvno are nuked.
+ */
+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,
+ KEEP_LAST_KVNO, dbent);
+}
- retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
- passwd, db_entry, new_kvno);
- if (retval) {
- cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
- db_entry->n_key_data = key_data_count;
- db_entry->key_data = key_data;
- } else if (keepold) {
- n_new_key_data = db_entry->n_key_data;
- for (i = 0; i < key_data_count; i++) {
- retval = krb5_dbe_create_key_data(context, db_entry);
- if (retval) {
- cleanup_key_data(context, db_entry->n_key_data,
- db_entry->key_data);
- break;
- }
- db_entry->key_data[i+n_new_key_data] = key_data[i];
- memset(&key_data[i], 0, sizeof(krb5_key_data));
- }
- krb5_db_free( context, key_data );
- } else {
- cleanup_key_data(context, key_data_count, key_data);
- }
- return(retval);
+/*
+ * Change password for a krb5_db_entry
+ * Assumes the max kvno
+ *
+ * As a side effect all old keys are nuked if keepold is false.
+ */
+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, krb5_boolean keepold,
+ krb5_db_entry *dbent)
+{
+ return rekey(context, mkey, ks_tuple, ks_tuple_count, password, new_kvno,
+ keepold ? KEEP_ALL : DISCARD_ALL, dbent);
}
/*
* As a side effect all old keys older than the max kvno are nuked.
*/
krb5_error_code
-krb5_dbe_apw(context, master_key, ks_tuple, ks_tuple_count, passwd, db_entry)
- krb5_context context;
- krb5_keyblock * master_key;
- krb5_key_salt_tuple * ks_tuple;
- int ks_tuple_count;
- char * passwd;
- krb5_db_entry * db_entry;
+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)
{
- int key_data_count;
- krb5_key_data * key_data;
- krb5_error_code retval;
- int old_kvno, new_kvno;
- int i;
-
- /* First save the old keydata */
- old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
- db_entry->key_data);
- key_data_count = db_entry->n_key_data;
- key_data = db_entry->key_data;
- db_entry->key_data = NULL;
- db_entry->n_key_data = 0;
-
- /* increment the kvno */
- new_kvno = old_kvno+1;
-
- if ((retval = add_key_pwd(context, master_key, ks_tuple, ks_tuple_count,
- passwd, db_entry, new_kvno))) {
- cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
- db_entry->n_key_data = key_data_count;
- db_entry->key_data = key_data;
- } else {
- /* Copy keys with key_data_kvno == old_kvno */
- for (i = 0; i < key_data_count; i++) {
- if (key_data[i].key_data_kvno == old_kvno) {
- if ((retval = krb5_dbe_create_key_data(context, db_entry))) {
- cleanup_key_data(context, db_entry->n_key_data,
- db_entry->key_data);
- break;
- }
- /* We should decrypt/re-encrypt the data to use the same mkvno*/
- db_entry->key_data[db_entry->n_key_data - 1] = key_data[i];
- memset(&key_data[i], 0, sizeof(krb5_key_data));
- }
- }
- cleanup_key_data(context, key_data_count, key_data);
- }
- return(retval);
+ return rekey(context, mkey, ks_tuple, ks_tuple_count, password, 0,
+ KEEP_LAST_KVNO, dbent);
}