kdb_cpw.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* lib/kdb/kdb_cpw.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
*/
/*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "k5-int.h"
#include "krb5/adm.h"
#include <stdio.h>
#include <errno.h>
static int
get_key_data_kvno(context, count, data)
krb5_context context;
int count;
krb5_key_data * data;
{
int i, kvno;
/* Find last key version number */
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(context, count, data)
krb5_context context;
int count;
krb5_key_data * data;
{
int i, j;
for (i = 0; i < count; i++) {
for (j = 0; j < data[i].key_data_ver; j++) {
if (data[i].key_data_length[j]) {
free(data[i].key_data_contents[j]);
}
}
}
free(data);
}
static krb5_error_code
add_key_rnd(context, master_key, ks_tuple, ks_tuple_count, db_entry, kvno)
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_principal krbtgt_princ;
krb5_keyblock key;
krb5_db_entry krbtgt_entry;
krb5_key_data * krbtgt_kdata;
krb5_boolean more;
int max_kvno, one, i, j;
krb5_error_code retval;
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, &krbtgt_entry,
&one, &more);
krb5_free_principal(context, krbtgt_princ); /* don't need it anymore */
if (retval)
return(retval);
if ((one > 1) || (more)) {
krb5_db_free_principal(context, &krbtgt_entry, one);
return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
}
if (!one)
return KRB5_KDB_NOENTRY;
/* 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;
}
}
for (i = 0; i < ks_tuple_count; i++) {
krb5_boolean similar;
similar = 0;
/*
* We could use krb5_keysalt_iterate to replace this loop, or use
* krb5_keysalt_is_present for the loop below, but we want to avoid
* circular library dependencies.
*/
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))
goto add_key_rnd_err;
/* 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
necessary. */
/* make new key */
if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
&key)))
goto add_key_rnd_err;
retval = krb5_dbekd_encrypt_key_data(context, master_key,
&key, NULL, kvno,
&db_entry->key_data[db_entry->n_key_data-1]);
krb5_free_keyblock_contents(context, &key);
if (retval)
goto add_key_rnd_err;
}
add_key_rnd_err:
krb5_db_free_principal(context, &krbtgt_entry, one);
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 = 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));
}
} else {
cleanup_key_data(context, key_data_count, key_data);
}
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 = 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);
}
/*
* Add key_data for a krb5_db_entry
* If passwd is NULL the assumes that the caller wants a random password.
*/
static krb5_error_code
add_key_pwd(context, master_key, ks_tuple, ks_tuple_count, passwd,
db_entry, kvno)
krb5_context context;
krb5_keyblock * master_key;
krb5_key_salt_tuple * ks_tuple;
int ks_tuple_count;
char * passwd;
krb5_db_entry * db_entry;
int kvno;
{
krb5_error_code retval;
krb5_keysalt key_salt;
krb5_keyblock key;
krb5_data pwd;
krb5_boolean found;
int i, j;
retval = 0;
for (i = 0; i < ks_tuple_count; i++) {
krb5_boolean similar;
similar = 0;
/*
* We could use krb5_keysalt_iterate to replace this loop, or use
* krb5_keysalt_is_present for the loop below, but we want to avoid
* circular library dependencies.
*/
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 &&
(ks_tuple[j].ks_salttype == ks_tuple[i].ks_salttype))
break;
}
if (j < i)
continue;
if (retval = krb5_dbe_create_key_data(context, db_entry))
return(retval);
/* Convert password string to key using appropriate salt */
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;
krb5_xfree(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_V4:
key_salt.data.length = 0;
key_salt.data.data = 0;
break;
case KRB5_KDB_SALTTYPE_AFS3: {
#if 0
krb5_data * saltdata;
if (retval = krb5_copy_data(context, krb5_princ_realm(context,
db_entry->princ), &saltdata))
return(retval);
key_salt.data = *saltdata;
key_salt.data.length = -1; /*length actually used below...*/
krb5_xfree(saltdata);
#else
/* Why do we do this? Well, the afs_mit_string_to_key needs to
use strlen, and the realm is not NULL terminated.... */
int slen = (*krb5_princ_realm(context,db_entry->princ)).length;
if(!(key_salt.data.data = (char *) malloc(slen+1)))
return ENOMEM;
key_salt.data.data[slen] = 0;
memcpy((char *)key_salt.data.data,
(char *)(*krb5_princ_realm(context,db_entry->princ)).data,
slen);
key_salt.data.length = -1; /*length actually used below...*/
#endif
}
break;
default:
return(KRB5_KDB_BAD_SALTTYPE);
}
pwd.data = passwd;
pwd.length = strlen(passwd);
memset(&key, 0, sizeof (krb5_keyblock));
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);
}
if (key_salt.data.length == -1)
key_salt.data.length =
krb5_princ_realm(context, db_entry->princ)->length;
if (retval = krb5_dbekd_encrypt_key_data(context, master_key, &key,
(const krb5_keysalt *)&key_salt,
kvno, &db_entry->key_data[db_entry->n_key_data-1])) {
if (key_salt.data.data)
free(key_salt.data.data);
krb5_free_keyblock_contents(context, &key);
return(retval);
}
if (key_salt.data.data)
free(key_salt.data.data);
krb5_free_keyblock_contents(context, &key);
}
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_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;
{
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 = 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. if the requested kvno is too small,
increment the old kvno */
if (new_kvno < old_kvno+1)
new_kvno = old_kvno+1;
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));
}
} else {
cleanup_key_data(context, key_data_count, key_data);
}
return(retval);
}
/*
* Add password 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_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;
{
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 = 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);
}