commit 29e60c5b7ac0980606971afc6fd6028bcf0c7f0f Author: Simo Sorce Date: Fri Nov 15 16:36:05 2013 -0500 Set expiration time on keys and keyrings By setting the timeout based on the credetial's timeout we let the system automatically cleanup expired credentials. [ghudson@mit.edu: simplified code slightly] ticket: 7769 (new) target_version: 1.12 tags: pullup diff --git a/src/lib/krb5/ccache/cc_keyring.c b/src/lib/krb5/ccache/cc_keyring.c index 2192fa5..1a0f1df 100644 --- a/src/lib/krb5/ccache/cc_keyring.c +++ b/src/lib/krb5/ccache/cc_keyring.c @@ -818,21 +818,68 @@ cleanup: static krb5_error_code add_cred_key(const char *name, const void *payload, size_t plen, - key_serial_t cache_id, krb5_boolean legacy_type) + key_serial_t cache_id, krb5_boolean legacy_type, + key_serial_t *key_out) { key_serial_t key; + *key_out = -1; if (!legacy_type) { /* Try the preferred cred key type; fall back if no kernel support. */ key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id); - if (key != -1) + if (key != -1) { + *key_out = key; return 0; - else if (errno != EINVAL && errno != ENODEV) + } else if (errno != EINVAL && errno != ENODEV) { return errno; + } } /* Use the user key type. */ key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id); - return (key == -1) ? errno : 0; + if (key == -1) + return errno; + *key_out = key; + return 0; +} + +static void +update_keyring_expiration(krb5_context context, krb5_ccache id) +{ + krb5_krcc_data *d = (krb5_krcc_data *)id->data; + krb5_cc_cursor cursor; + krb5_creds creds; + krb5_timestamp now, endtime = 0; + unsigned int timeout; + + /* + * We have no way to know what is the actual timeout set on the keyring. + * We also cannot keep track of it in a local variable as another process + * can always modify the keyring independently, so just always enumerate + * all keys and find out the highest endtime time. + */ + + /* Find the maximum endtime of all creds in the cache. */ + if (krb5_krcc_start_seq_get(context, id, &cursor) != 0) + return; + for (;;) { + if (krb5_krcc_next_cred(context, id, &cursor, &creds) != 0) + break; + if (creds.times.endtime > endtime) + endtime = creds.times.endtime; + krb5_free_cred_contents(context, &creds); + } + (void)krb5_krcc_end_seq_get(context, id, &cursor); + + if (endtime == 0) /* No creds with end times */ + return; + + if (krb5_timeofday(context, &now) != 0) + return; + + /* Setting the timeout to zero would reset the timeout, so we set it to one + * second instead if creds are already expired. */ + timeout = (endtime > now) ? endtime - now : 1; + (void)keyctl_set_timeout(d->cache_id, timeout); } /* @@ -1497,6 +1544,8 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds) char *payload = NULL; unsigned int payloadlen; char *keyname = NULL; + key_serial_t cred_key; + krb5_timestamp now; DEBUG_PRINT(("krb5_krcc_store: entered\n")); @@ -1523,12 +1572,24 @@ krb5_krcc_store(krb5_context context, krb5_ccache id, krb5_creds * creds) DEBUG_PRINT(("krb5_krcc_store: adding new key '%s' to keyring %d\n", keyname, d->cache_id)); kret = add_cred_key(keyname, payload, payloadlen, d->cache_id, - d->is_legacy_type); + d->is_legacy_type, &cred_key); if (kret) goto errout; krb5_krcc_update_change_time(d); + /* Set appropriate timeouts on cache keys. */ + kret = krb5_timeofday(context, &now); + if (kret) + goto errout; + + if (creds->times.endtime > now) + (void)keyctl_set_timeout(cred_key, creds->times.endtime - now); + + update_keyring_expiration(context, id); + + kret = KRB5_OK; + errout: if (keyname) krb5_free_unparsed_name(context, keyname);