diff options
Diffstat (limited to 'security/keys/request_key.c')
-rw-r--r-- | security/keys/request_key.c | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/security/keys/request_key.c b/security/keys/request_key.c new file mode 100644 index 000000000000..9705b1aeba5d --- /dev/null +++ b/security/keys/request_key.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /* request_key.c: request a key from userspace | ||
2 | * | ||
3 | * Copyright (C) 2004 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 License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/kmod.h> | ||
15 | #include <linux/err.h> | ||
16 | #include "internal.h" | ||
17 | |||
18 | struct key_construction { | ||
19 | struct list_head link; /* link in construction queue */ | ||
20 | struct key *key; /* key being constructed */ | ||
21 | }; | ||
22 | |||
23 | /* when waiting for someone else's keys, you get added to this */ | ||
24 | DECLARE_WAIT_QUEUE_HEAD(request_key_conswq); | ||
25 | |||
26 | /*****************************************************************************/ | ||
27 | /* | ||
28 | * request userspace finish the construction of a key | ||
29 | * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>" | ||
30 | * - if callout_info is an empty string, it'll be rendered as a "-" instead | ||
31 | */ | ||
32 | static int call_request_key(struct key *key, | ||
33 | const char *op, | ||
34 | const char *callout_info) | ||
35 | { | ||
36 | struct task_struct *tsk = current; | ||
37 | unsigned long flags; | ||
38 | key_serial_t prkey, sskey; | ||
39 | char *argv[10], *envp[3], uid_str[12], gid_str[12]; | ||
40 | char key_str[12], keyring_str[3][12]; | ||
41 | int i; | ||
42 | |||
43 | /* record the UID and GID */ | ||
44 | sprintf(uid_str, "%d", current->fsuid); | ||
45 | sprintf(gid_str, "%d", current->fsgid); | ||
46 | |||
47 | /* we say which key is under construction */ | ||
48 | sprintf(key_str, "%d", key->serial); | ||
49 | |||
50 | /* we specify the process's default keyrings */ | ||
51 | sprintf(keyring_str[0], "%d", | ||
52 | tsk->thread_keyring ? tsk->thread_keyring->serial : 0); | ||
53 | |||
54 | prkey = 0; | ||
55 | if (tsk->signal->process_keyring) | ||
56 | prkey = tsk->signal->process_keyring->serial; | ||
57 | |||
58 | sskey = 0; | ||
59 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
60 | if (tsk->signal->session_keyring) | ||
61 | sskey = tsk->signal->session_keyring->serial; | ||
62 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
63 | |||
64 | |||
65 | if (!sskey) | ||
66 | sskey = tsk->user->session_keyring->serial; | ||
67 | |||
68 | sprintf(keyring_str[1], "%d", prkey); | ||
69 | sprintf(keyring_str[2], "%d", sskey); | ||
70 | |||
71 | /* set up a minimal environment */ | ||
72 | i = 0; | ||
73 | envp[i++] = "HOME=/"; | ||
74 | envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | ||
75 | envp[i] = NULL; | ||
76 | |||
77 | /* set up the argument list */ | ||
78 | i = 0; | ||
79 | argv[i++] = "/sbin/request-key"; | ||
80 | argv[i++] = (char *) op; | ||
81 | argv[i++] = key_str; | ||
82 | argv[i++] = uid_str; | ||
83 | argv[i++] = gid_str; | ||
84 | argv[i++] = keyring_str[0]; | ||
85 | argv[i++] = keyring_str[1]; | ||
86 | argv[i++] = keyring_str[2]; | ||
87 | argv[i++] = callout_info[0] ? (char *) callout_info : "-"; | ||
88 | argv[i] = NULL; | ||
89 | |||
90 | /* do it */ | ||
91 | return call_usermodehelper(argv[0], argv, envp, 1); | ||
92 | |||
93 | } /* end call_request_key() */ | ||
94 | |||
95 | /*****************************************************************************/ | ||
96 | /* | ||
97 | * call out to userspace for the key | ||
98 | * - called with the construction sem held, but the sem is dropped here | ||
99 | * - we ignore program failure and go on key status instead | ||
100 | */ | ||
101 | static struct key *__request_key_construction(struct key_type *type, | ||
102 | const char *description, | ||
103 | const char *callout_info) | ||
104 | { | ||
105 | struct key_construction cons; | ||
106 | struct timespec now; | ||
107 | struct key *key; | ||
108 | int ret, negative; | ||
109 | |||
110 | /* create a key and add it to the queue */ | ||
111 | key = key_alloc(type, description, | ||
112 | current->fsuid, current->fsgid, KEY_USR_ALL, 0); | ||
113 | if (IS_ERR(key)) | ||
114 | goto alloc_failed; | ||
115 | |||
116 | write_lock(&key->lock); | ||
117 | key->flags |= KEY_FLAG_USER_CONSTRUCT; | ||
118 | write_unlock(&key->lock); | ||
119 | |||
120 | cons.key = key; | ||
121 | list_add_tail(&cons.link, &key->user->consq); | ||
122 | |||
123 | /* we drop the construction sem here on behalf of the caller */ | ||
124 | up_write(&key_construction_sem); | ||
125 | |||
126 | /* make the call */ | ||
127 | ret = call_request_key(key, "create", callout_info); | ||
128 | if (ret < 0) | ||
129 | goto request_failed; | ||
130 | |||
131 | /* if the key wasn't instantiated, then we want to give an error */ | ||
132 | ret = -ENOKEY; | ||
133 | if (!(key->flags & KEY_FLAG_INSTANTIATED)) | ||
134 | goto request_failed; | ||
135 | |||
136 | down_write(&key_construction_sem); | ||
137 | list_del(&cons.link); | ||
138 | up_write(&key_construction_sem); | ||
139 | |||
140 | /* also give an error if the key was negatively instantiated */ | ||
141 | check_not_negative: | ||
142 | if (key->flags & KEY_FLAG_NEGATIVE) { | ||
143 | key_put(key); | ||
144 | key = ERR_PTR(-ENOKEY); | ||
145 | } | ||
146 | |||
147 | out: | ||
148 | return key; | ||
149 | |||
150 | request_failed: | ||
151 | /* it wasn't instantiated | ||
152 | * - remove from construction queue | ||
153 | * - mark the key as dead | ||
154 | */ | ||
155 | negative = 0; | ||
156 | down_write(&key_construction_sem); | ||
157 | |||
158 | list_del(&cons.link); | ||
159 | |||
160 | write_lock(&key->lock); | ||
161 | key->flags &= ~KEY_FLAG_USER_CONSTRUCT; | ||
162 | |||
163 | /* check it didn't get instantiated between the check and the down */ | ||
164 | if (!(key->flags & KEY_FLAG_INSTANTIATED)) { | ||
165 | key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; | ||
166 | negative = 1; | ||
167 | } | ||
168 | |||
169 | write_unlock(&key->lock); | ||
170 | up_write(&key_construction_sem); | ||
171 | |||
172 | if (!negative) | ||
173 | goto check_not_negative; /* surprisingly, the key got | ||
174 | * instantiated */ | ||
175 | |||
176 | /* set the timeout and store in the session keyring if we can */ | ||
177 | now = current_kernel_time(); | ||
178 | key->expiry = now.tv_sec + key_negative_timeout; | ||
179 | |||
180 | if (current->signal->session_keyring) { | ||
181 | unsigned long flags; | ||
182 | struct key *keyring; | ||
183 | |||
184 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
185 | keyring = current->signal->session_keyring; | ||
186 | atomic_inc(&keyring->usage); | ||
187 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
188 | |||
189 | key_link(keyring, key); | ||
190 | key_put(keyring); | ||
191 | } | ||
192 | |||
193 | key_put(key); | ||
194 | |||
195 | /* notify anyone who was waiting */ | ||
196 | wake_up_all(&request_key_conswq); | ||
197 | |||
198 | key = ERR_PTR(ret); | ||
199 | goto out; | ||
200 | |||
201 | alloc_failed: | ||
202 | up_write(&key_construction_sem); | ||
203 | goto out; | ||
204 | |||
205 | } /* end __request_key_construction() */ | ||
206 | |||
207 | /*****************************************************************************/ | ||
208 | /* | ||
209 | * call out to userspace to request the key | ||
210 | * - we check the construction queue first to see if an appropriate key is | ||
211 | * already being constructed by userspace | ||
212 | */ | ||
213 | static struct key *request_key_construction(struct key_type *type, | ||
214 | const char *description, | ||
215 | struct key_user *user, | ||
216 | const char *callout_info) | ||
217 | { | ||
218 | struct key_construction *pcons; | ||
219 | struct key *key, *ckey; | ||
220 | |||
221 | DECLARE_WAITQUEUE(myself, current); | ||
222 | |||
223 | /* see if there's such a key under construction already */ | ||
224 | down_write(&key_construction_sem); | ||
225 | |||
226 | list_for_each_entry(pcons, &user->consq, link) { | ||
227 | ckey = pcons->key; | ||
228 | |||
229 | if (ckey->type != type) | ||
230 | continue; | ||
231 | |||
232 | if (type->match(ckey, description)) | ||
233 | goto found_key_under_construction; | ||
234 | } | ||
235 | |||
236 | /* see about getting userspace to construct the key */ | ||
237 | key = __request_key_construction(type, description, callout_info); | ||
238 | error: | ||
239 | return key; | ||
240 | |||
241 | /* someone else has the same key under construction | ||
242 | * - we want to keep an eye on their key | ||
243 | */ | ||
244 | found_key_under_construction: | ||
245 | atomic_inc(&ckey->usage); | ||
246 | up_write(&key_construction_sem); | ||
247 | |||
248 | /* wait for the key to be completed one way or another */ | ||
249 | add_wait_queue(&request_key_conswq, &myself); | ||
250 | |||
251 | for (;;) { | ||
252 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
253 | if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT)) | ||
254 | break; | ||
255 | schedule(); | ||
256 | } | ||
257 | |||
258 | set_current_state(TASK_RUNNING); | ||
259 | remove_wait_queue(&request_key_conswq, &myself); | ||
260 | |||
261 | /* we'll need to search this process's keyrings to see if the key is | ||
262 | * now there since we can't automatically assume it's also available | ||
263 | * there */ | ||
264 | key_put(ckey); | ||
265 | ckey = NULL; | ||
266 | |||
267 | key = NULL; /* request a retry */ | ||
268 | goto error; | ||
269 | |||
270 | } /* end request_key_construction() */ | ||
271 | |||
272 | /*****************************************************************************/ | ||
273 | /* | ||
274 | * request a key | ||
275 | * - search the process's keyrings | ||
276 | * - check the list of keys being created or updated | ||
277 | * - call out to userspace for a key if requested (supplementary info can be | ||
278 | * passed) | ||
279 | */ | ||
280 | struct key *request_key(struct key_type *type, | ||
281 | const char *description, | ||
282 | const char *callout_info) | ||
283 | { | ||
284 | struct key_user *user; | ||
285 | struct key *key; | ||
286 | |||
287 | /* search all the process keyrings for a key */ | ||
288 | key = search_process_keyrings_aux(type, description, type->match); | ||
289 | |||
290 | if (PTR_ERR(key) == -EAGAIN) { | ||
291 | /* the search failed, but the keyrings were searchable, so we | ||
292 | * should consult userspace if we can */ | ||
293 | key = ERR_PTR(-ENOKEY); | ||
294 | if (!callout_info) | ||
295 | goto error; | ||
296 | |||
297 | /* - get hold of the user's construction queue */ | ||
298 | user = key_user_lookup(current->fsuid); | ||
299 | if (!user) { | ||
300 | key = ERR_PTR(-ENOMEM); | ||
301 | goto error; | ||
302 | } | ||
303 | |||
304 | for (;;) { | ||
305 | /* ask userspace (returns NULL if it waited on a key | ||
306 | * being constructed) */ | ||
307 | key = request_key_construction(type, description, | ||
308 | user, callout_info); | ||
309 | if (key) | ||
310 | break; | ||
311 | |||
312 | /* someone else made the key we want, so we need to | ||
313 | * search again as it might now be available to us */ | ||
314 | key = search_process_keyrings_aux(type, description, | ||
315 | type->match); | ||
316 | if (PTR_ERR(key) != -EAGAIN) | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | key_user_put(user); | ||
321 | } | ||
322 | |||
323 | error: | ||
324 | return key; | ||
325 | |||
326 | } /* end request_key() */ | ||
327 | |||
328 | EXPORT_SYMBOL(request_key); | ||
329 | |||
330 | /*****************************************************************************/ | ||
331 | /* | ||
332 | * validate a key | ||
333 | */ | ||
334 | int key_validate(struct key *key) | ||
335 | { | ||
336 | struct timespec now; | ||
337 | int ret = 0; | ||
338 | |||
339 | if (key) { | ||
340 | /* check it's still accessible */ | ||
341 | ret = -EKEYREVOKED; | ||
342 | if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD)) | ||
343 | goto error; | ||
344 | |||
345 | /* check it hasn't expired */ | ||
346 | ret = 0; | ||
347 | if (key->expiry) { | ||
348 | now = current_kernel_time(); | ||
349 | if (now.tv_sec >= key->expiry) | ||
350 | ret = -EKEYEXPIRED; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | error: | ||
355 | return ret; | ||
356 | |||
357 | } /* end key_validate() */ | ||
358 | |||
359 | EXPORT_SYMBOL(key_validate); | ||