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:
Roland C. Dowdeswell
2019-06-12 18:33:10 +01:00
committed by Roland C. Dowdeswell
parent d6337ebdce
commit 366b787917
3 changed files with 187 additions and 10 deletions

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;