Blob Blame History Raw
commit 29e60c5b7ac0980606971afc6fd6028bcf0c7f0f
Author: Simo Sorce <simo@redhat.com>
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);