kdc: Add synthetic PKINIT principals option
This commit is contained in:
150
kdc/misc.c
150
kdc/misc.c
@@ -51,6 +51,76 @@ name_type_ok(krb5_context context,
|
||||
|
||||
struct timeval _kdc_now;
|
||||
|
||||
static krb5_error_code
|
||||
synthesize_hdb_close(krb5_context context, struct HDB *db)
|
||||
{
|
||||
(void) context;
|
||||
(void) db;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Synthesize an HDB entry suitable for PKINIT and only PKINIT.
|
||||
*/
|
||||
static krb5_error_code
|
||||
synthesize_client(krb5_context context,
|
||||
krb5_kdc_configuration *config,
|
||||
krb5_const_principal princ,
|
||||
HDB **db,
|
||||
hdb_entry_ex **h)
|
||||
{
|
||||
static HDB null_db;
|
||||
krb5_error_code ret;
|
||||
hdb_entry_ex *e;
|
||||
|
||||
/* Hope this works! */
|
||||
null_db.hdb_destroy = synthesize_hdb_close;
|
||||
null_db.hdb_close = synthesize_hdb_close;
|
||||
*db = &null_db;
|
||||
|
||||
ret = (e = calloc(1, sizeof(*e))) ? 0 : krb5_enomem(context);
|
||||
if (ret == 0) {
|
||||
e->entry.flags.client = 1;
|
||||
e->entry.flags.immutable = 1;
|
||||
e->entry.flags.virtual = 1;
|
||||
e->entry.flags.synthetic = 1;
|
||||
e->entry.flags.do_not_store = 1;
|
||||
e->entry.kvno = 1;
|
||||
e->entry.keys.len = 0;
|
||||
e->entry.keys.val = NULL;
|
||||
e->entry.created_by.time = time(NULL);
|
||||
e->entry.modified_by = NULL;
|
||||
e->entry.valid_start = NULL;
|
||||
e->entry.valid_end = NULL;
|
||||
e->entry.pw_end = NULL;
|
||||
e->entry.etypes = NULL;
|
||||
e->entry.generation = NULL;
|
||||
e->entry.extensions = NULL;
|
||||
}
|
||||
if (ret == 0)
|
||||
ret = (e->entry.max_renew = calloc(1, sizeof(e->entry.max_renew))) ?
|
||||
0 : krb5_enomem(context);
|
||||
if (ret == 0)
|
||||
ret = (e->entry.max_life = calloc(1, sizeof(e->entry.max_life))) ?
|
||||
0 : krb5_enomem(context);
|
||||
if (ret == 0)
|
||||
ret = krb5_copy_principal(context, princ, &e->entry.principal);
|
||||
if (ret == 0)
|
||||
ret = krb5_copy_principal(context, princ, &e->entry.created_by.principal);
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* We can't check OCSP in the TGS path, so we can't let tickets for
|
||||
* synthetic principals live very long.
|
||||
*/
|
||||
*(e->entry.max_renew) = config->synthetic_clients_max_renew;
|
||||
*(e->entry.max_life) = config->synthetic_clients_max_life;
|
||||
*h = e;
|
||||
} else {
|
||||
hdb_free_entry(context, e);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_db_fetch(krb5_context context,
|
||||
krb5_kdc_configuration *config,
|
||||
@@ -70,7 +140,7 @@ _kdc_db_fetch(krb5_context context,
|
||||
*h = NULL;
|
||||
|
||||
if (!name_type_ok(context, config, principal))
|
||||
goto out2;
|
||||
return HDB_ERR_NOENTRY;
|
||||
|
||||
flags |= HDB_F_DECRYPT;
|
||||
if (kvno_ptr != NULL && *kvno_ptr != 0) {
|
||||
@@ -102,6 +172,9 @@ _kdc_db_fetch(krb5_context context,
|
||||
for (i = 0; i < config->num_db; i++) {
|
||||
HDB *curdb = config->db[i];
|
||||
|
||||
if (db)
|
||||
*db = curdb;
|
||||
|
||||
ret = curdb->hdb_open(context, curdb, O_RDONLY, 0);
|
||||
if (ret) {
|
||||
const char *msg = krb5_get_error_message(context, ret);
|
||||
@@ -117,41 +190,54 @@ _kdc_db_fetch(krb5_context context,
|
||||
ret = hdb_fetch_kvno(context, curdb, princ, flags, 0, 0, kvno, ent);
|
||||
curdb->hdb_close(context, curdb);
|
||||
|
||||
switch (ret) {
|
||||
case HDB_ERR_WRONG_REALM:
|
||||
/*
|
||||
* the ent->entry.principal just contains hints for the client
|
||||
* to retry. This is important for enterprise principal routing
|
||||
* between trusts.
|
||||
*/
|
||||
/* fall through */
|
||||
case 0:
|
||||
if (db)
|
||||
*db = curdb;
|
||||
*h = ent;
|
||||
ent = NULL;
|
||||
goto out;
|
||||
if (ret == HDB_ERR_NOENTRY)
|
||||
continue; /* Check the other databases */
|
||||
|
||||
case HDB_ERR_NOENTRY:
|
||||
/* Check the other databases */
|
||||
continue;
|
||||
|
||||
default:
|
||||
/*
|
||||
* This is really important, because errors like
|
||||
* HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that
|
||||
* the RODC on which this code is running does not have
|
||||
* the key we need, and so a proxy to the KDC is required)
|
||||
* have specific meaning, and need to be propogated up.
|
||||
*/
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* This is really important, because errors like
|
||||
* HDB_ERR_NOT_FOUND_HERE (used to indicate to Samba that
|
||||
* the RODC on which this code is running does not have
|
||||
* the key we need, and so a proxy to the KDC is required)
|
||||
* have specific meaning, and need to be propogated up.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
out2:
|
||||
if (ret == HDB_ERR_NOENTRY) {
|
||||
krb5_set_error_message(context, ret, "no such entry found in hdb");
|
||||
switch (ret) {
|
||||
case HDB_ERR_WRONG_REALM:
|
||||
case 0:
|
||||
/*
|
||||
* the ent->entry.principal just contains hints for the client
|
||||
* to retry. This is important for enterprise principal routing
|
||||
* between trusts.
|
||||
*/
|
||||
*h = ent;
|
||||
ent = NULL;
|
||||
break;
|
||||
|
||||
case HDB_ERR_NOENTRY:
|
||||
if (db)
|
||||
*db = NULL;
|
||||
if ((flags & HDB_F_GET_CLIENT) && (flags & HDB_F_SYNTHETIC_OK) &&
|
||||
config->synthetic_clients) {
|
||||
ret = synthesize_client(context, config, principal, db, h);
|
||||
if (ret) {
|
||||
krb5_set_error_message(context, ret, "could not synthesize "
|
||||
"HDB client principal entry");
|
||||
ret = HDB_ERR_NOENTRY;
|
||||
krb5_prepend_error_message(context, ret, "no such entry found in hdb");
|
||||
}
|
||||
} else {
|
||||
krb5_set_error_message(context, ret, "no such entry found in hdb");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (db)
|
||||
*db = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
krb5_free_principal(context, enterprise_principal);
|
||||
free(ent);
|
||||
|
Reference in New Issue
Block a user