summaryrefslogtreecommitdiffstats
path: root/krb5-master-keyring-expiration.patch
blob: 2c58cb05ffe612a2eca4079e553201ef8ceaaa35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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);