Blame view

security/keys/gc.c 5.21 KB
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Fix garbage...
29
  static bool key_gc_again;
5d135440f   David Howells   KEYS: Add garbage...
30
31
  static unsigned long key_gc_executing;
  static time_t key_gc_next_run = LONG_MAX;
c08ef808e   David Howells   KEYS: Fix garbage...
32
  static time_t key_gc_new_timer;
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Fix garbage...
44
  	if (gc_at <= now) {
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Add a keyct...
67
  	__releases(key_serial_lock)
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Fix RCU han...
79
80
  	rcu_read_lock();
  	klist = rcu_dereference(keyring->payload.subscriptions);
5d135440f   David Howells   KEYS: Add garbage...
81
  	if (!klist)
cf8304e8f   David Howells   KEYS: Fix RCU han...
82
  		goto unlock_dont_gc;
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Fix RCU han...
90
91
  unlock_dont_gc:
  	rcu_read_unlock();
5d135440f   David Howells   KEYS: Add garbage...
92
93
94
95
96
  dont_gc:
  	kleave(" = false");
  	return false;
  
  do_gc:
cf8304e8f   David Howells   KEYS: Fix RCU han...
97
  	rcu_read_unlock();
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Fix garbage...
117
  	time_t new_timer = LONG_MAX, limit, now;
5d135440f   David Howells   KEYS: Add garbage...
118

c08ef808e   David Howells   KEYS: Fix garbage...
119
120
  	now = current_kernel_time().tv_sec;
  	kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
5d135440f   David Howells   KEYS: Add garbage...
121
122
  
  	if (test_and_set_bit(0, &key_gc_executing)) {
c08ef808e   David Howells   KEYS: Fix garbage...
123
124
  		key_schedule_gc(current_kernel_time().tv_sec + 1);
  		kleave(" [busy; deferring]");
5d135440f   David Howells   KEYS: Add garbage...
125
126
  		return;
  	}
c08ef808e   David Howells   KEYS: Fix garbage...
127
  	limit = now;
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Fix garbage...
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   David Howells   KEYS: Add garbage...
139
140
141
142
  
  	cursor = key_gc_cursor;
  	if (cursor < 0)
  		cursor = 0;
c08ef808e   David Howells   KEYS: Fix garbage...
143
144
145
146
  	if (cursor > 0)
  		new_timer = key_gc_new_timer;
  	else
  		key_gc_again = false;
5d135440f   David Howells   KEYS: Add garbage...
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   David Howells   KEYS: Have the ga...
172
  		if (key->expiry > limit && key->expiry < new_timer) {
c08ef808e   David Howells   KEYS: Fix garbage...
173
  			kdebug("will expire %x in %ld",
606531c31   David Howells   KEYS: Have the ga...
174
  			       key_serial(key), key->expiry - limit);
5d135440f   David Howells   KEYS: Add garbage...
175
  			new_timer = key->expiry;
c08ef808e   David Howells   KEYS: Fix garbage...
176
  		}
5d135440f   David Howells   KEYS: Add garbage...
177
178
  
  		if (key->type == &key_type_keyring &&
c08ef808e   David Howells   KEYS: Fix garbage...
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   David Howells   KEYS: Add garbage...
183
184
  
  		rb = rb_next(&key->serial_node);
c08ef808e   David Howells   KEYS: Fix garbage...
185
186
  		if (!rb)
  			goto reached_the_end;
5d135440f   David Howells   KEYS: Add garbage...
187
188
  		key = rb_entry(rb, struct key, serial_node);
  	}
c08ef808e   David Howells   KEYS: Fix garbage...
189
190
191
192
  gc_released_our_lock:
  	kdebug("gc_released_our_lock");
  	key_gc_new_timer = new_timer;
  	key_gc_again = true;
5d135440f   David Howells   KEYS: Add garbage...
193
  	clear_bit(0, &key_gc_executing);
c08ef808e   David Howells   KEYS: Fix garbage...
194
195
  	schedule_work(&key_gc_work);
  	kleave(" [continue]");
5d135440f   David Howells   KEYS: Add garbage...
196
  	return;
c08ef808e   David Howells   KEYS: Fix garbage...
197
  	/* when we reach the end of the run, we set the timer for the next one */
5d135440f   David Howells   KEYS: Add garbage...
198
  reached_the_end:
c08ef808e   David Howells   KEYS: Fix garbage...
199
200
201
  	kdebug("reached_the_end");
  	spin_unlock(&key_serial_lock);
  	key_gc_new_timer = new_timer;
5d135440f   David Howells   KEYS: Add garbage...
202
  	key_gc_cursor = 0;
c08ef808e   David Howells   KEYS: Fix garbage...
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   David Howells   KEYS: Add garbage...
216
  }