aboutsummaryrefslogtreecommitdiffstats
path: root/security/keys/gc.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-09-02 04:14:00 -0400
committerJames Morris <jmorris@namei.org>2009-09-02 07:29:11 -0400
commit5d135440faf7db8d566de0c6fab36b16cf9cfc3b (patch)
treed9c022e73ed51dfe5729fde9a97150cb64b68196 /security/keys/gc.c
parentf041ae2f99d49adc914153a34a2d0e14e4389d90 (diff)
KEYS: Add garbage collection for dead, revoked and expired keys. [try #6]
Add garbage collection for dead, revoked and expired keys. This involved erasing all links to such keys from keyrings that point to them. At that point, the key will be deleted in the normal manner. Keyrings from which garbage collection occurs are shrunk and their quota consumption reduced as appropriate. Dead keys (for which the key type has been removed) will be garbage collected immediately. Revoked and expired keys will hang around for a number of seconds, as set in /proc/sys/kernel/keys/gc_delay before being automatically removed. The default is 5 minutes. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/keys/gc.c')
-rw-r--r--security/keys/gc.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/security/keys/gc.c b/security/keys/gc.c
new file mode 100644
index 000000000000..44adc325e15c
--- /dev/null
+++ b/security/keys/gc.c
@@ -0,0 +1,193 @@
1/* Key garbage collector
2 *
3 * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <keys/keyring-type.h>
14#include "internal.h"
15
16/*
17 * Delay between key revocation/expiry in seconds
18 */
19unsigned key_gc_delay = 5 * 60;
20
21/*
22 * Reaper
23 */
24static void key_gc_timer_func(unsigned long);
25static void key_garbage_collector(struct work_struct *);
26static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
27static DECLARE_WORK(key_gc_work, key_garbage_collector);
28static key_serial_t key_gc_cursor; /* the last key the gc considered */
29static unsigned long key_gc_executing;
30static time_t key_gc_next_run = LONG_MAX;
31
32/*
33 * Schedule a garbage collection run
34 * - precision isn't particularly important
35 */
36void key_schedule_gc(time_t gc_at)
37{
38 unsigned long expires;
39 time_t now = current_kernel_time().tv_sec;
40
41 kenter("%ld", gc_at - now);
42
43 gc_at += key_gc_delay;
44
45 if (now >= gc_at) {
46 schedule_work(&key_gc_work);
47 } else if (gc_at < key_gc_next_run) {
48 expires = jiffies + (gc_at - now) * HZ;
49 mod_timer(&key_gc_timer, expires);
50 }
51}
52
53/*
54 * The garbage collector timer kicked off
55 */
56static void key_gc_timer_func(unsigned long data)
57{
58 kenter("");
59 key_gc_next_run = LONG_MAX;
60 schedule_work(&key_gc_work);
61}
62
63/*
64 * Garbage collect pointers from a keyring
65 * - return true if we altered the keyring
66 */
67static bool key_gc_keyring(struct key *keyring, time_t limit)
68{
69 struct keyring_list *klist;
70 struct key *key;
71 int loop;
72
73 kenter("%x", key_serial(keyring));
74
75 if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
76 goto dont_gc;
77
78 /* scan the keyring looking for dead keys */
79 klist = rcu_dereference(keyring->payload.subscriptions);
80 if (!klist)
81 goto dont_gc;
82
83 for (loop = klist->nkeys - 1; loop >= 0; loop--) {
84 key = klist->keys[loop];
85 if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
86 (key->expiry > 0 && key->expiry <= limit))
87 goto do_gc;
88 }
89
90dont_gc:
91 kleave(" = false");
92 return false;
93
94do_gc:
95 key_gc_cursor = keyring->serial;
96 key_get(keyring);
97 spin_unlock(&key_serial_lock);
98 keyring_gc(keyring, limit);
99 key_put(keyring);
100 kleave(" = true");
101 return true;
102}
103
104/*
105 * Garbage collector for keys
106 * - this involves scanning the keyrings for dead, expired and revoked keys
107 * that have overstayed their welcome
108 */
109static void key_garbage_collector(struct work_struct *work)
110{
111 struct rb_node *rb;
112 key_serial_t cursor;
113 struct key *key, *xkey;
114 time_t new_timer = LONG_MAX, limit;
115
116 kenter("");
117
118 if (test_and_set_bit(0, &key_gc_executing)) {
119 key_schedule_gc(current_kernel_time().tv_sec);
120 return;
121 }
122
123 limit = current_kernel_time().tv_sec;
124 if (limit > key_gc_delay)
125 limit -= key_gc_delay;
126 else
127 limit = key_gc_delay;
128
129 spin_lock(&key_serial_lock);
130
131 if (RB_EMPTY_ROOT(&key_serial_tree))
132 goto reached_the_end;
133
134 cursor = key_gc_cursor;
135 if (cursor < 0)
136 cursor = 0;
137
138 /* find the first key above the cursor */
139 key = NULL;
140 rb = key_serial_tree.rb_node;
141 while (rb) {
142 xkey = rb_entry(rb, struct key, serial_node);
143 if (cursor < xkey->serial) {
144 key = xkey;
145 rb = rb->rb_left;
146 } else if (cursor > xkey->serial) {
147 rb = rb->rb_right;
148 } else {
149 rb = rb_next(rb);
150 if (!rb)
151 goto reached_the_end;
152 key = rb_entry(rb, struct key, serial_node);
153 break;
154 }
155 }
156
157 if (!key)
158 goto reached_the_end;
159
160 /* trawl through the keys looking for keyrings */
161 for (;;) {
162 if (key->expiry > 0 && key->expiry < new_timer)
163 new_timer = key->expiry;
164
165 if (key->type == &key_type_keyring &&
166 key_gc_keyring(key, limit)) {
167 /* the gc ate our lock */
168 schedule_work(&key_gc_work);
169 goto no_unlock;
170 }
171
172 rb = rb_next(&key->serial_node);
173 if (!rb) {
174 key_gc_cursor = 0;
175 break;
176 }
177 key = rb_entry(rb, struct key, serial_node);
178 }
179
180out:
181 spin_unlock(&key_serial_lock);
182no_unlock:
183 clear_bit(0, &key_gc_executing);
184 if (new_timer < LONG_MAX)
185 key_schedule_gc(new_timer);
186
187 kleave("");
188 return;
189
190reached_the_end:
191 key_gc_cursor = 0;
192 goto out;
193}