aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--Documentation/keys.txt19
-rw-r--r--include/linux/key.h5
-rw-r--r--security/keys/Makefile1
-rw-r--r--security/keys/gc.c193
-rw-r--r--security/keys/internal.h4
-rw-r--r--security/keys/key.c14
-rw-r--r--security/keys/keyctl.c1
-rw-r--r--security/keys/keyring.c85
-rw-r--r--security/keys/sysctl.c28
9 files changed, 344 insertions, 6 deletions
diff --git a/Documentation/keys.txt b/Documentation/keys.txt
index b56aacc1fff8..203487e9b1d8 100644
--- a/Documentation/keys.txt
+++ b/Documentation/keys.txt
@@ -26,7 +26,7 @@ This document has the following sections:
26 - Notes on accessing payload contents 26 - Notes on accessing payload contents
27 - Defining a key type 27 - Defining a key type
28 - Request-key callback service 28 - Request-key callback service
29 - Key access filesystem 29 - Garbage collection
30 30
31 31
32============ 32============
@@ -113,6 +113,9 @@ Each key has a number of attributes:
113 113
114 (*) Dead. The key's type was unregistered, and so the key is now useless. 114 (*) Dead. The key's type was unregistered, and so the key is now useless.
115 115
116Keys in the last three states are subject to garbage collection. See the
117section on "Garbage collection".
118
116 119
117==================== 120====================
118KEY SERVICE OVERVIEW 121KEY SERVICE OVERVIEW
@@ -1231,3 +1234,17 @@ by executing:
1231 1234
1232In this case, the program isn't required to actually attach the key to a ring; 1235In this case, the program isn't required to actually attach the key to a ring;
1233the rings are provided for reference. 1236the rings are provided for reference.
1237
1238
1239==================
1240GARBAGE COLLECTION
1241==================
1242
1243Dead keys (for which the type has been removed) will be automatically unlinked
1244from those keyrings that point to them and deleted as soon as possible by a
1245background garbage collector.
1246
1247Similarly, revoked and expired keys will be garbage collected, but only after a
1248certain amount of time has passed. This time is set as a number of seconds in:
1249
1250 /proc/sys/kernel/keys/gc_delay
diff --git a/include/linux/key.h b/include/linux/key.h
index e544f466d69a..33e0165de100 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -129,7 +129,10 @@ struct key {
129 struct rw_semaphore sem; /* change vs change sem */ 129 struct rw_semaphore sem; /* change vs change sem */
130 struct key_user *user; /* owner of this key */ 130 struct key_user *user; /* owner of this key */
131 void *security; /* security data for this key */ 131 void *security; /* security data for this key */
132 time_t expiry; /* time at which key expires (or 0) */ 132 union {
133 time_t expiry; /* time at which key expires (or 0) */
134 time_t revoked_at; /* time at which key was revoked */
135 };
133 uid_t uid; 136 uid_t uid;
134 gid_t gid; 137 gid_t gid;
135 key_perm_t perm; /* access permissions */ 138 key_perm_t perm; /* access permissions */
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 747a464943af..74d5447d7df7 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -3,6 +3,7 @@
3# 3#
4 4
5obj-y := \ 5obj-y := \
6 gc.o \
6 key.o \ 7 key.o \
7 keyring.o \ 8 keyring.o \
8 keyctl.o \ 9 keyctl.o \
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}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index a7252e7b2e05..fb830514c337 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -132,6 +132,10 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
132 132
133extern long join_session_keyring(const char *name); 133extern long join_session_keyring(const char *name);
134 134
135extern unsigned key_gc_delay;
136extern void keyring_gc(struct key *keyring, time_t limit);
137extern void key_schedule_gc(time_t expiry_at);
138
135/* 139/*
136 * check to see whether permission is granted to use a key in the desired way 140 * check to see whether permission is granted to use a key in the desired way
137 */ 141 */
diff --git a/security/keys/key.c b/security/keys/key.c
index bd9d2670e9c4..08531ad0f252 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -500,6 +500,7 @@ int key_negate_and_link(struct key *key,
500 set_bit(KEY_FLAG_INSTANTIATED, &key->flags); 500 set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
501 now = current_kernel_time(); 501 now = current_kernel_time();
502 key->expiry = now.tv_sec + timeout; 502 key->expiry = now.tv_sec + timeout;
503 key_schedule_gc(key->expiry);
503 504
504 if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) 505 if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
505 awaken = 1; 506 awaken = 1;
@@ -888,6 +889,9 @@ EXPORT_SYMBOL(key_update);
888 */ 889 */
889void key_revoke(struct key *key) 890void key_revoke(struct key *key)
890{ 891{
892 struct timespec now;
893 time_t time;
894
891 key_check(key); 895 key_check(key);
892 896
893 /* make sure no one's trying to change or use the key when we mark it 897 /* make sure no one's trying to change or use the key when we mark it
@@ -900,6 +904,14 @@ void key_revoke(struct key *key)
900 key->type->revoke) 904 key->type->revoke)
901 key->type->revoke(key); 905 key->type->revoke(key);
902 906
907 /* set the death time to no more than the expiry time */
908 now = current_kernel_time();
909 time = now.tv_sec;
910 if (key->revoked_at == 0 || key->revoked_at > time) {
911 key->revoked_at = time;
912 key_schedule_gc(key->revoked_at);
913 }
914
903 up_write(&key->sem); 915 up_write(&key->sem);
904 916
905} /* end key_revoke() */ 917} /* end key_revoke() */
@@ -984,6 +996,8 @@ void unregister_key_type(struct key_type *ktype)
984 spin_unlock(&key_serial_lock); 996 spin_unlock(&key_serial_lock);
985 up_write(&key_types_sem); 997 up_write(&key_types_sem);
986 998
999 key_schedule_gc(0);
1000
987} /* end unregister_key_type() */ 1001} /* end unregister_key_type() */
988 1002
989EXPORT_SYMBOL(unregister_key_type); 1003EXPORT_SYMBOL(unregister_key_type);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 1160b644dace..736d7800f97f 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1115,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
1115 } 1115 }
1116 1116
1117 key->expiry = expiry; 1117 key->expiry = expiry;
1118 key_schedule_gc(key->expiry);
1118 1119
1119 up_write(&key->sem); 1120 up_write(&key->sem);
1120 key_put(key); 1121 key_put(key);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 3dba81c2eba3..ac977f661a79 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1000,3 +1000,88 @@ static void keyring_revoke(struct key *keyring)
1000 } 1000 }
1001 1001
1002} /* end keyring_revoke() */ 1002} /* end keyring_revoke() */
1003
1004/*
1005 * Determine whether a key is dead
1006 */
1007static bool key_is_dead(struct key *key, time_t limit)
1008{
1009 return test_bit(KEY_FLAG_DEAD, &key->flags) ||
1010 (key->expiry > 0 && key->expiry <= limit);
1011}
1012
1013/*
1014 * Collect garbage from the contents of a keyring
1015 */
1016void keyring_gc(struct key *keyring, time_t limit)
1017{
1018 struct keyring_list *klist, *new;
1019 struct key *key;
1020 int loop, keep, max;
1021
1022 kenter("%x", key_serial(keyring));
1023
1024 down_write(&keyring->sem);
1025
1026 klist = keyring->payload.subscriptions;
1027 if (!klist)
1028 goto just_return;
1029
1030 /* work out how many subscriptions we're keeping */
1031 keep = 0;
1032 for (loop = klist->nkeys - 1; loop >= 0; loop--)
1033 if (!key_is_dead(klist->keys[loop], limit));
1034 keep++;
1035
1036 if (keep == klist->nkeys)
1037 goto just_return;
1038
1039 /* allocate a new keyring payload */
1040 max = roundup(keep, 4);
1041 new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
1042 GFP_KERNEL);
1043 if (!new)
1044 goto just_return;
1045 new->maxkeys = max;
1046 new->nkeys = 0;
1047 new->delkey = 0;
1048
1049 /* install the live keys
1050 * - must take care as expired keys may be updated back to life
1051 */
1052 keep = 0;
1053 for (loop = klist->nkeys - 1; loop >= 0; loop--) {
1054 key = klist->keys[loop];
1055 if (!key_is_dead(key, limit)) {
1056 if (keep >= max)
1057 goto discard_new;
1058 new->keys[keep++] = key_get(key);
1059 }
1060 }
1061 new->nkeys = keep;
1062
1063 /* adjust the quota */
1064 key_payload_reserve(keyring,
1065 sizeof(struct keyring_list) +
1066 KEYQUOTA_LINK_BYTES * keep);
1067
1068 if (keep == 0) {
1069 rcu_assign_pointer(keyring->payload.subscriptions, NULL);
1070 kfree(new);
1071 } else {
1072 rcu_assign_pointer(keyring->payload.subscriptions, new);
1073 }
1074
1075 up_write(&keyring->sem);
1076
1077 call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
1078 kleave(" [yes]");
1079 return;
1080
1081discard_new:
1082 new->nkeys = keep;
1083 keyring_clear_rcu_disposal(&new->rcu);
1084just_return:
1085 up_write(&keyring->sem);
1086 kleave(" [no]");
1087}
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
index b611d493c2d8..5e05dc09e2db 100644
--- a/security/keys/sysctl.c
+++ b/security/keys/sysctl.c
@@ -13,6 +13,8 @@
13#include <linux/sysctl.h> 13#include <linux/sysctl.h>
14#include "internal.h" 14#include "internal.h"
15 15
16static const int zero, one = 1, max = INT_MAX;
17
16ctl_table key_sysctls[] = { 18ctl_table key_sysctls[] = {
17 { 19 {
18 .ctl_name = CTL_UNNUMBERED, 20 .ctl_name = CTL_UNNUMBERED,
@@ -20,7 +22,9 @@ ctl_table key_sysctls[] = {
20 .data = &key_quota_maxkeys, 22 .data = &key_quota_maxkeys,
21 .maxlen = sizeof(unsigned), 23 .maxlen = sizeof(unsigned),
22 .mode = 0644, 24 .mode = 0644,
23 .proc_handler = &proc_dointvec, 25 .proc_handler = &proc_dointvec_minmax,
26 .extra1 = (void *) &one,
27 .extra2 = (void *) &max,
24 }, 28 },
25 { 29 {
26 .ctl_name = CTL_UNNUMBERED, 30 .ctl_name = CTL_UNNUMBERED,
@@ -28,7 +32,9 @@ ctl_table key_sysctls[] = {
28 .data = &key_quota_maxbytes, 32 .data = &key_quota_maxbytes,
29 .maxlen = sizeof(unsigned), 33 .maxlen = sizeof(unsigned),
30 .mode = 0644, 34 .mode = 0644,
31 .proc_handler = &proc_dointvec, 35 .proc_handler = &proc_dointvec_minmax,
36 .extra1 = (void *) &one,
37 .extra2 = (void *) &max,
32 }, 38 },
33 { 39 {
34 .ctl_name = CTL_UNNUMBERED, 40 .ctl_name = CTL_UNNUMBERED,
@@ -36,7 +42,9 @@ ctl_table key_sysctls[] = {
36 .data = &key_quota_root_maxkeys, 42 .data = &key_quota_root_maxkeys,
37 .maxlen = sizeof(unsigned), 43 .maxlen = sizeof(unsigned),
38 .mode = 0644, 44 .mode = 0644,
39 .proc_handler = &proc_dointvec, 45 .proc_handler = &proc_dointvec_minmax,
46 .extra1 = (void *) &one,
47 .extra2 = (void *) &max,
40 }, 48 },
41 { 49 {
42 .ctl_name = CTL_UNNUMBERED, 50 .ctl_name = CTL_UNNUMBERED,
@@ -44,7 +52,19 @@ ctl_table key_sysctls[] = {
44 .data = &key_quota_root_maxbytes, 52 .data = &key_quota_root_maxbytes,
45 .maxlen = sizeof(unsigned), 53 .maxlen = sizeof(unsigned),
46 .mode = 0644, 54 .mode = 0644,
47 .proc_handler = &proc_dointvec, 55 .proc_handler = &proc_dointvec_minmax,
56 .extra1 = (void *) &one,
57 .extra2 = (void *) &max,
58 },
59 {
60 .ctl_name = CTL_UNNUMBERED,
61 .procname = "gc_delay",
62 .data = &key_gc_delay,
63 .maxlen = sizeof(unsigned),
64 .mode = 0644,
65 .proc_handler = &proc_dointvec_minmax,
66 .extra1 = (void *) &zero,
67 .extra2 = (void *) &max,
48 }, 68 },
49 { .ctl_name = 0 } 69 { .ctl_name = 0 }
50}; 70};