Blame view
security/keys/gc.c
5.21 KB
5d135440f
|
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 |
/* Key garbage collector * * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public Licence * as published by the Free Software Foundation; either version * 2 of the Licence, or (at your option) any later version. */ #include <linux/module.h> #include <keys/keyring-type.h> #include "internal.h" /* * Delay between key revocation/expiry in seconds */ unsigned key_gc_delay = 5 * 60; /* * Reaper */ static void key_gc_timer_func(unsigned long); static void key_garbage_collector(struct work_struct *); static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0); static DECLARE_WORK(key_gc_work, key_garbage_collector); static key_serial_t key_gc_cursor; /* the last key the gc considered */ |
c08ef808e
|
29 |
static bool key_gc_again; |
5d135440f
|
30 31 |
static unsigned long key_gc_executing; static time_t key_gc_next_run = LONG_MAX; |
c08ef808e
|
32 |
static time_t key_gc_new_timer; |
5d135440f
|
33 34 35 36 37 38 39 40 41 42 43 |
/* * Schedule a garbage collection run * - precision isn't particularly important */ void key_schedule_gc(time_t gc_at) { unsigned long expires; time_t now = current_kernel_time().tv_sec; kenter("%ld", gc_at - now); |
c08ef808e
|
44 |
if (gc_at <= now) { |
5d135440f
|
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
schedule_work(&key_gc_work); } else if (gc_at < key_gc_next_run) { expires = jiffies + (gc_at - now) * HZ; mod_timer(&key_gc_timer, expires); } } /* * The garbage collector timer kicked off */ static void key_gc_timer_func(unsigned long data) { kenter(""); key_gc_next_run = LONG_MAX; schedule_work(&key_gc_work); } /* * Garbage collect pointers from a keyring * - return true if we altered the keyring */ static bool key_gc_keyring(struct key *keyring, time_t limit) |
ee18d64c1
|
67 |
__releases(key_serial_lock) |
5d135440f
|
68 69 70 71 72 73 74 75 76 77 78 |
{ struct keyring_list *klist; struct key *key; int loop; kenter("%x", key_serial(keyring)); if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) goto dont_gc; /* scan the keyring looking for dead keys */ |
cf8304e8f
|
79 80 |
rcu_read_lock(); klist = rcu_dereference(keyring->payload.subscriptions); |
5d135440f
|
81 |
if (!klist) |
cf8304e8f
|
82 |
goto unlock_dont_gc; |
5d135440f
|
83 84 85 86 87 88 89 |
for (loop = klist->nkeys - 1; loop >= 0; loop--) { key = klist->keys[loop]; if (test_bit(KEY_FLAG_DEAD, &key->flags) || (key->expiry > 0 && key->expiry <= limit)) goto do_gc; } |
cf8304e8f
|
90 91 |
unlock_dont_gc: rcu_read_unlock(); |
5d135440f
|
92 93 94 95 96 |
dont_gc: kleave(" = false"); return false; do_gc: |
cf8304e8f
|
97 |
rcu_read_unlock(); |
5d135440f
|
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
key_gc_cursor = keyring->serial; key_get(keyring); spin_unlock(&key_serial_lock); keyring_gc(keyring, limit); key_put(keyring); kleave(" = true"); return true; } /* * Garbage collector for keys * - this involves scanning the keyrings for dead, expired and revoked keys * that have overstayed their welcome */ static void key_garbage_collector(struct work_struct *work) { struct rb_node *rb; key_serial_t cursor; struct key *key, *xkey; |
c08ef808e
|
117 |
time_t new_timer = LONG_MAX, limit, now; |
5d135440f
|
118 |
|
c08ef808e
|
119 120 |
now = current_kernel_time().tv_sec; kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now); |
5d135440f
|
121 122 |
if (test_and_set_bit(0, &key_gc_executing)) { |
c08ef808e
|
123 124 |
key_schedule_gc(current_kernel_time().tv_sec + 1); kleave(" [busy; deferring]"); |
5d135440f
|
125 126 |
return; } |
c08ef808e
|
127 |
limit = now; |
5d135440f
|
128 129 130 131 132 133 |
if (limit > key_gc_delay) limit -= key_gc_delay; else limit = key_gc_delay; spin_lock(&key_serial_lock); |
c08ef808e
|
134 135 136 137 138 |
if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) { spin_unlock(&key_serial_lock); clear_bit(0, &key_gc_executing); return; } |
5d135440f
|
139 140 141 142 |
cursor = key_gc_cursor; if (cursor < 0) cursor = 0; |
c08ef808e
|
143 144 145 146 |
if (cursor > 0) new_timer = key_gc_new_timer; else key_gc_again = false; |
5d135440f
|
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
/* find the first key above the cursor */ key = NULL; rb = key_serial_tree.rb_node; while (rb) { xkey = rb_entry(rb, struct key, serial_node); if (cursor < xkey->serial) { key = xkey; rb = rb->rb_left; } else if (cursor > xkey->serial) { rb = rb->rb_right; } else { rb = rb_next(rb); if (!rb) goto reached_the_end; key = rb_entry(rb, struct key, serial_node); break; } } if (!key) goto reached_the_end; /* trawl through the keys looking for keyrings */ for (;;) { |
606531c31
|
172 |
if (key->expiry > limit && key->expiry < new_timer) { |
c08ef808e
|
173 |
kdebug("will expire %x in %ld", |
606531c31
|
174 |
key_serial(key), key->expiry - limit); |
5d135440f
|
175 |
new_timer = key->expiry; |
c08ef808e
|
176 |
} |
5d135440f
|
177 178 |
if (key->type == &key_type_keyring && |
c08ef808e
|
179 180 181 182 |
key_gc_keyring(key, limit)) /* the gc had to release our lock so that the keyring * could be modified, so we have to get it again */ goto gc_released_our_lock; |
5d135440f
|
183 184 |
rb = rb_next(&key->serial_node); |
c08ef808e
|
185 186 |
if (!rb) goto reached_the_end; |
5d135440f
|
187 188 |
key = rb_entry(rb, struct key, serial_node); } |
c08ef808e
|
189 190 191 192 |
gc_released_our_lock: kdebug("gc_released_our_lock"); key_gc_new_timer = new_timer; key_gc_again = true; |
5d135440f
|
193 |
clear_bit(0, &key_gc_executing); |
c08ef808e
|
194 195 |
schedule_work(&key_gc_work); kleave(" [continue]"); |
5d135440f
|
196 |
return; |
c08ef808e
|
197 |
/* when we reach the end of the run, we set the timer for the next one */ |
5d135440f
|
198 |
reached_the_end: |
c08ef808e
|
199 200 201 |
kdebug("reached_the_end"); spin_unlock(&key_serial_lock); key_gc_new_timer = new_timer; |
5d135440f
|
202 |
key_gc_cursor = 0; |
c08ef808e
|
203 204 205 206 207 208 209 210 211 212 213 214 215 |
clear_bit(0, &key_gc_executing); if (key_gc_again) { /* there may have been a key that expired whilst we were * scanning, so if we discarded any links we should do another * scan */ new_timer = now + 1; key_schedule_gc(new_timer); } else if (new_timer < LONG_MAX) { new_timer += key_gc_delay; key_schedule_gc(new_timer); } kleave(" [end]"); |
5d135440f
|
216 |
} |