hdb: Derive pw_end for virtual services
We derive keysets for virtual host-based service principals, and that includes the `set_time` field of keys. But applications using the kadm5 API lose that information. Our httpkadmind wants to set a Cache-Control header with an appropriate max-age so that clients know when to re-fetch keytabs. We could extract some of the lib/hdb/common.c functions so that httpkadmind could re-create an HDB_entry from a kadm5 entry then compute the desired time, but ultimately we already have an appropriate field in the HDB_entry and kadm5_principal_ent_rec types: "password expiration". So let's set the `pw_end` of a virtual host-based service's HDB entry to the time when a client should next fetch the principal's keys, and we'll use that in httpkadmind as the `pw_expiration` field of the kadm5 entry type.
This commit is contained in:
@@ -856,6 +856,10 @@ derive_keys_for_kr(krb5_context context,
|
|||||||
* (t - krp->epoch < 0) is better than (krp->epoch < t), making us more
|
* (t - krp->epoch < 0) is better than (krp->epoch < t), making us more
|
||||||
* tolerant of signed 32-bit time_t here near 2038. Of course, we have
|
* tolerant of signed 32-bit time_t here near 2038. Of course, we have
|
||||||
* signed 32-bit time_t dragons elsewhere.
|
* signed 32-bit time_t dragons elsewhere.
|
||||||
|
*
|
||||||
|
* We don't need to check for n == 0 && rotation_period_offset < 0 because
|
||||||
|
* only derive_keys_for_current_kr() calls us with non-zero rotation period
|
||||||
|
* offsets, and it will never call us in that case.
|
||||||
*/
|
*/
|
||||||
if (t - krp->epoch < 0)
|
if (t - krp->epoch < 0)
|
||||||
return 0; /* This KR is not relevant yet */
|
return 0; /* This KR is not relevant yet */
|
||||||
@@ -864,6 +868,37 @@ derive_keys_for_kr(krb5_context context,
|
|||||||
set_time = krp->epoch + krp->period * n;
|
set_time = krp->epoch + krp->period * n;
|
||||||
kvno = krp->base_kvno + n;
|
kvno = krp->base_kvno + n;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since this principal is virtual, or has virtual keys, we're going to
|
||||||
|
* derive a "password expiration time" for it in order to help httpkadmind
|
||||||
|
* and other tools figure out when to request keys again.
|
||||||
|
*
|
||||||
|
* The kadm5 representation of principals does not include the set_time of
|
||||||
|
* keys/keysets, so we can't have httpkadmind derive a Cache-Control from
|
||||||
|
* that without adding yet another "TL data". Since adding TL data is a
|
||||||
|
* huge pain, we'll just use the `pw_end' field of `HDB_entry' to
|
||||||
|
* communicate when this principal's keys will change next.
|
||||||
|
*/
|
||||||
|
if (h->pw_end[0] == 0) {
|
||||||
|
KerberosTime used = (t - krp->epoch) % krp->period;
|
||||||
|
KerberosTime left = krp->period - used;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If `h->pw_end[0]' == 0 then this must be the current period of the
|
||||||
|
* current KR we're deriving keys for. See upstairs.
|
||||||
|
*
|
||||||
|
* If there's more than a quarter of this time period left, then we'll
|
||||||
|
* set `h->pw_end[0]' to one quarter before the end of this time
|
||||||
|
* period. Else we'll set it to 1/4 after (we'll be including the next
|
||||||
|
* set of derived keys, so there's no harm in waiting that long to
|
||||||
|
* refetch).
|
||||||
|
*/
|
||||||
|
if (left > krp->period >> 2)
|
||||||
|
h->pw_end[0] = set_time + krp->period - (krp->period >> 2);
|
||||||
|
else
|
||||||
|
h->pw_end[0] = set_time + krp->period + (krp->period >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do not waste cycles computing keys not wanted or needed.
|
* Do not waste cycles computing keys not wanted or needed.
|
||||||
@@ -1198,6 +1233,11 @@ derive_keys(krb5_context context,
|
|||||||
"because last key rotation period "
|
"because last key rotation period "
|
||||||
"marks deletion", p);
|
"marks deletion", p);
|
||||||
|
|
||||||
|
/* See `derive_keys_for_kr()' */
|
||||||
|
if (h->pw_end == NULL &&
|
||||||
|
(h->pw_end = calloc(1, sizeof(h->pw_end[0]))) == NULL)
|
||||||
|
ret = krb5_enomem(context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Derive and set in `h' its current kvno and current keys.
|
* Derive and set in `h' its current kvno and current keys.
|
||||||
*
|
*
|
||||||
@@ -1246,6 +1286,12 @@ derive_keys(krb5_context context,
|
|||||||
if (ret == 0 && *h->max_life > kr.val[current_kr].period >> 1)
|
if (ret == 0 && *h->max_life > kr.val[current_kr].period >> 1)
|
||||||
*h->max_life = kr.val[current_kr].period >> 1;
|
*h->max_life = kr.val[current_kr].period >> 1;
|
||||||
|
|
||||||
|
if (ret == 0 && h->pw_end[0] == 0)
|
||||||
|
/* Shouldn't happen */
|
||||||
|
h->pw_end[0] = kr.val[current_kr].epoch +
|
||||||
|
kr.val[current_kr].period *
|
||||||
|
(1 + (t - kr.val[current_kr].epoch) / kr.val[current_kr].period);
|
||||||
|
|
||||||
free_HDB_Ext_KeyRotation(&kr);
|
free_HDB_Ext_KeyRotation(&kr);
|
||||||
free_HDB_Ext_KeySet(&base_keys);
|
free_HDB_Ext_KeySet(&base_keys);
|
||||||
free(p);
|
free(p);
|
||||||
@@ -1730,11 +1776,22 @@ hdb_fetch_kvno(krb5_context context,
|
|||||||
hdb_entry *h)
|
hdb_entry *h)
|
||||||
{
|
{
|
||||||
krb5_error_code ret;
|
krb5_error_code ret;
|
||||||
|
krb5_timestamp now;
|
||||||
|
|
||||||
|
krb5_timeofday(context, &now);
|
||||||
|
|
||||||
flags |= kvno ? HDB_F_KVNO_SPECIFIED : 0; /* XXX is this needed */
|
flags |= kvno ? HDB_F_KVNO_SPECIFIED : 0; /* XXX is this needed */
|
||||||
if (t == 0)
|
ret = fetch_it(context, db, principal, flags, t ? t : now, etype, kvno, h);
|
||||||
krb5_timeofday(context, &t);
|
if (ret == 0 && t == 0 && h->flags.virtual &&
|
||||||
ret = fetch_it(context, db, principal, flags, t, etype, kvno, h);
|
h->pw_end && h->pw_end[0] < now) {
|
||||||
|
/*
|
||||||
|
* This shouldn't happen!
|
||||||
|
*
|
||||||
|
* Do not allow h->pw_end[0] to be in the past for virtual principals
|
||||||
|
* outside testing. This is just to prevent the AS/TGS from failing.
|
||||||
|
*/
|
||||||
|
h->pw_end[0] = now + 3600;
|
||||||
|
}
|
||||||
if (ret == HDB_ERR_NOENTRY)
|
if (ret == HDB_ERR_NOENTRY)
|
||||||
krb5_set_error_message(context, ret, "no such entry found in hdb");
|
krb5_set_error_message(context, ret, "no such entry found in hdb");
|
||||||
return ret;
|
return ret;
|
||||||
|
Reference in New Issue
Block a user