We provide a "derived key" mechanism to allow wildcard princs
In order to support certain use cases, we implement a mechanism to allow wildcard principals to be defined and for the KDC to issue tickets for said principals by deriving a key for them from a cluster master entry in the HDB. The way that this works is we defined an entry of the form: WELLKNOWN/DERIVED-KEY/KRB5-CRYPTO-PRFPLUS/<hostname>@REALM When reading from the Kerberos DB, if we can't find an entry for what looks like a hostbased principal, then we will attempt to search for a principal of the above form chopping name components off the front as we search. If we find an entry, then we derive keys for it by using krb5_crypto_prfplus() with the entry's key and the principal name of the request.
This commit is contained in:

committed by
Roland C. Dowdeswell

parent
d6337ebdce
commit
366b787917
@@ -68,6 +68,8 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
|
||||
c->db = NULL;
|
||||
c->num_db = 0;
|
||||
c->logf = NULL;
|
||||
c->enable_derived_keys = FALSE;
|
||||
c->derived_keys_ndots = 2;
|
||||
|
||||
c->num_kdc_processes =
|
||||
krb5_config_get_int_default(context, NULL, c->num_kdc_processes,
|
||||
@@ -261,6 +263,14 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
|
||||
0,
|
||||
"kdc", "pkinit_dh_min_bits", NULL);
|
||||
|
||||
c->enable_derived_keys =
|
||||
krb5_config_get_bool_default(context, NULL, c->enable_derived_keys,
|
||||
"kdc", "enable_derived_keys", NULL);
|
||||
|
||||
c->derived_keys_ndots =
|
||||
krb5_config_get_int_default(context, NULL, c->derived_keys_ndots,
|
||||
"kdc", "derived_keys_ndots", NULL);
|
||||
|
||||
*config = c;
|
||||
|
||||
return 0;
|
||||
|
@@ -96,6 +96,9 @@ typedef struct krb5_kdc_configuration {
|
||||
const char *kx509_template;
|
||||
const char *kx509_ca;
|
||||
|
||||
krb5_boolean enable_derived_keys;
|
||||
int derived_keys_ndots;
|
||||
|
||||
} krb5_kdc_configuration;
|
||||
|
||||
struct krb5_kdc_service {
|
||||
|
184
kdc/misc.c
184
kdc/misc.c
@@ -49,6 +49,173 @@ name_type_ok(krb5_context context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
log_princ(krb5_context context, krb5_kdc_configuration *config, int lvl,
|
||||
const char *fmt, krb5_const_principal princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *princstr;
|
||||
|
||||
ret = krb5_unparse_name(context, princ, &princstr);
|
||||
if (ret) {
|
||||
kdc_log(context, config, 0, "log_princ: ENOMEM");
|
||||
return;
|
||||
}
|
||||
kdc_log(context, config, lvl, fmt, princstr);
|
||||
free(princstr);
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_derive_the_keys(krb5_context context, krb5_kdc_configuration *config,
|
||||
krb5_const_principal princ, krb5uint32 kvno, hdb_entry_ex *h)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_crypto crypto = NULL;
|
||||
krb5_data in;
|
||||
size_t i;
|
||||
char *princstr = NULL;
|
||||
const char *errmsg = NULL;
|
||||
|
||||
ret = krb5_unparse_name(context, princ, &princstr);
|
||||
if (ret) {
|
||||
errmsg = "krb5_unparse_name failed";
|
||||
goto bail;
|
||||
}
|
||||
|
||||
in.data = princstr;
|
||||
in.length = strlen(in.data);
|
||||
|
||||
for (i = 0; i < h->entry.keys.len; i++) {
|
||||
krb5_enctype etype = h->entry.keys.val[i].key.keytype;
|
||||
krb5_keyblock *keyptr = &h->entry.keys.val[i].key;
|
||||
krb5_data rnd;
|
||||
size_t len;
|
||||
|
||||
kdc_log(context, config, 8, " etype=%d", etype);
|
||||
|
||||
errmsg = "Failed to init crypto";
|
||||
ret = krb5_crypto_init(context, keyptr, 0, &crypto);
|
||||
if (ret)
|
||||
goto bail;
|
||||
|
||||
errmsg = "Failed to determine keysize";
|
||||
ret = krb5_enctype_keysize(context, etype, &len);
|
||||
if (ret)
|
||||
goto bail;
|
||||
|
||||
errmsg = "krb5_crypto_prfplus() failed";
|
||||
ret = krb5_crypto_prfplus(context, crypto, &in, len, &rnd);
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
crypto = NULL;
|
||||
if (ret)
|
||||
goto bail;
|
||||
|
||||
errmsg = "krb5_random_to_key() failed";
|
||||
krb5_free_keyblock_contents(context, keyptr);
|
||||
ret = krb5_random_to_key(context, etype, rnd.data, rnd.length, keyptr);
|
||||
krb5_data_free(&rnd);
|
||||
if (ret)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
bail:
|
||||
if (ret) {
|
||||
const char *msg = krb5_get_error_message(context, ret);
|
||||
kdc_log(context, config, 0, "%s: %s", errmsg, msg);
|
||||
krb5_free_error_message(context, msg);
|
||||
}
|
||||
if (crypto)
|
||||
krb5_crypto_destroy(context, crypto);
|
||||
free(princstr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
_fetch_it(krb5_context context, krb5_kdc_configuration *config, HDB *db,
|
||||
krb5_const_principal princ, unsigned flags, krb5uint32 kvno,
|
||||
hdb_entry_ex *ent)
|
||||
{
|
||||
krb5_principal tmpprinc;
|
||||
krb5_error_code ret;
|
||||
char *host = NULL;
|
||||
char *tmp;
|
||||
const char *realm = NULL;
|
||||
int is_derived_key = 0;
|
||||
size_t ndots = 0;
|
||||
size_t hdots;
|
||||
|
||||
flags |= HDB_F_DECRYPT;
|
||||
|
||||
if (config->enable_derived_keys) {
|
||||
if (krb5_principal_get_num_comp(context, princ) == 2) {
|
||||
realm = krb5_principal_get_realm(context, princ);
|
||||
host = strdup(krb5_principal_get_comp_string(context, princ, 1));
|
||||
if (!host)
|
||||
return krb5_enomem(context);
|
||||
|
||||
/* Strip the :port */
|
||||
tmp = strchr(host, ':');
|
||||
if (tmp) {
|
||||
*tmp++ = '\0';
|
||||
if (strchr(tmp, ':')) {
|
||||
kdc_log(context, config, 7, "Strange host instance, "
|
||||
"port %s contains a colon (``:'')", tmp);
|
||||
free(host);
|
||||
host = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ndots = config->derived_keys_ndots;
|
||||
|
||||
for (hdots = 0, tmp = host; tmp && *tmp; tmp++)
|
||||
if (*tmp == '.')
|
||||
hdots++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* XXXrcd: should we exclude certain principals from this
|
||||
* muckery? E.g. host? krbtgt?
|
||||
*/
|
||||
|
||||
krb5_copy_principal(context, princ, &tmpprinc);
|
||||
|
||||
tmp = host;
|
||||
for (;;) {
|
||||
log_princ(context, config, 7, "Looking up %s", tmpprinc);
|
||||
ret = db->hdb_fetch_kvno(context, db, tmpprinc, flags, kvno, ent);
|
||||
|
||||
if (ret != HDB_ERR_NOENTRY)
|
||||
break;
|
||||
|
||||
if (!tmp || !*tmp || hdots < ndots)
|
||||
break;
|
||||
|
||||
is_derived_key = 1;
|
||||
krb5_free_principal(context, tmpprinc);
|
||||
krb5_build_principal(context, &tmpprinc, strlen(realm), realm,
|
||||
"WELLKNOWN", "DERIVED-KEY", "KRB5-CRYPTO-PRFPLUS", tmp, NULL);
|
||||
|
||||
tmp = strchr(tmp, '.');
|
||||
if (!tmp)
|
||||
break;
|
||||
tmp++;
|
||||
hdots--;
|
||||
}
|
||||
|
||||
if (ret == 0 && is_derived_key) {
|
||||
kdc_log(context, config, 7, "Deriving keys:");
|
||||
log_princ(context, config, 7, " for %s", princ);
|
||||
log_princ(context, config, 7, " from %s", tmpprinc);
|
||||
_derive_the_keys(context, config, princ, kvno, ent);
|
||||
}
|
||||
|
||||
free(host);
|
||||
krb5_free_principal(context, tmpprinc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct timeval _kdc_now;
|
||||
|
||||
krb5_error_code
|
||||
@@ -99,7 +266,9 @@ _kdc_db_fetch(krb5_context context,
|
||||
}
|
||||
|
||||
for (i = 0; i < config->num_db; i++) {
|
||||
ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0);
|
||||
HDB *curdb = config->db[i];
|
||||
|
||||
ret = curdb->hdb_open(context, curdb, O_RDONLY, 0);
|
||||
if (ret) {
|
||||
const char *msg = krb5_get_error_message(context, ret);
|
||||
kdc_log(context, config, 0, "Failed to open database: %s", msg);
|
||||
@@ -108,16 +277,11 @@ _kdc_db_fetch(krb5_context context,
|
||||
}
|
||||
|
||||
princ = principal;
|
||||
if (!(config->db[i]->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal)
|
||||
if (!(curdb->hdb_capability_flags & HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL) && enterprise_principal)
|
||||
princ = enterprise_principal;
|
||||
|
||||
ret = config->db[i]->hdb_fetch_kvno(context,
|
||||
config->db[i],
|
||||
princ,
|
||||
flags | HDB_F_DECRYPT,
|
||||
kvno,
|
||||
ent);
|
||||
config->db[i]->hdb_close(context, config->db[i]);
|
||||
ret = _fetch_it(context, config, curdb, princ, flags, kvno, ent);
|
||||
curdb->hdb_close(context, curdb);
|
||||
|
||||
switch (ret) {
|
||||
case HDB_ERR_WRONG_REALM:
|
||||
@@ -129,7 +293,7 @@ _kdc_db_fetch(krb5_context context,
|
||||
/* fall through */
|
||||
case 0:
|
||||
if (db)
|
||||
*db = config->db[i];
|
||||
*db = curdb;
|
||||
*h = ent;
|
||||
ent = NULL;
|
||||
goto out;
|
||||
|
Reference in New Issue
Block a user