aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-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
7 files changed, 322 insertions, 4 deletions
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};