Blame view

security/keys/keyctl.c 29.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /* keyctl.c: userspace keyctl operations
   *
3e30148c3   David Howells   [PATCH] Keys: Mak...
3
   * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   * 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 License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/sched.h>
  #include <linux/slab.h>
  #include <linux/syscalls.h>
  #include <linux/keyctl.h>
  #include <linux/fs.h>
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
19
  #include <linux/capability.h>
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
20
  #include <linux/string.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  #include <linux/err.h>
38bbca6b6   David Howells   keys: increase th...
22
  #include <linux/vmalloc.h>
70a5bb72b   David Howells   keys: add keyctl ...
23
  #include <linux/security.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
  #include <asm/uaccess.h>
  #include "internal.h"
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  static int key_get_type_from_user(char *type,
  				  const char __user *_type,
  				  unsigned len)
  {
  	int ret;
  
  	ret = strncpy_from_user(type, _type, len);
  
  	if (ret < 0)
  		return -EFAULT;
  
  	if (ret == 0 || ret >= len)
  		return -EINVAL;
  
  	if (type[0] == '.')
  		return -EPERM;
  
  	type[len - 1] = '\0';
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
50
51
52
53
54
  /*****************************************************************************/
  /*
   * extract the description of a new key from userspace and either add it as a
   * new key to the specified keyring or update a matching key in that keyring
   * - the keyring must be writable
   * - returns the new key's serial number
   * - implements add_key()
   */
1e7bfb213   Heiko Carstens   [CVE-2009-0029] S...
55
56
57
58
59
  SYSCALL_DEFINE5(add_key, const char __user *, _type,
  		const char __user *, _description,
  		const void __user *, _payload,
  		size_t, plen,
  		key_serial_t, ringid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
  {
664cceb00   David Howells   [PATCH] Keys: Add...
61
  	key_ref_t keyring_ref, key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
  	char type[32], *description;
  	void *payload;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
64
  	long ret;
38bbca6b6   David Howells   keys: increase th...
65
  	bool vm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  
  	ret = -EINVAL;
38bbca6b6   David Howells   keys: increase th...
68
  	if (plen > 1024 * 1024 - 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
  		goto error;
  
  	/* draw all the data into kernel space */
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
72
  	ret = key_get_type_from_user(type, _type, sizeof(type));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  	if (ret < 0)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75

0cb409d98   Davi Arnaut   [PATCH] strndup_u...
76
77
78
  	description = strndup_user(_description, PAGE_SIZE);
  	if (IS_ERR(description)) {
  		ret = PTR_ERR(description);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  		goto error;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
80
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
  
  	/* pull the payload in if one was supplied */
  	payload = NULL;
38bbca6b6   David Howells   keys: increase th...
84
  	vm = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
  	if (_payload) {
  		ret = -ENOMEM;
  		payload = kmalloc(plen, GFP_KERNEL);
38bbca6b6   David Howells   keys: increase th...
88
89
90
91
92
93
94
95
  		if (!payload) {
  			if (plen <= PAGE_SIZE)
  				goto error2;
  			vm = true;
  			payload = vmalloc(plen);
  			if (!payload)
  				goto error2;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
101
102
  
  		ret = -EFAULT;
  		if (copy_from_user(payload, _payload, plen) != 0)
  			goto error3;
  	}
  
  	/* find the target keyring (which must be writable) */
8bbf4976b   David Howells   KEYS: Alter use o...
103
  	keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
104
105
  	if (IS_ERR(keyring_ref)) {
  		ret = PTR_ERR(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
  		goto error3;
  	}
  
  	/* create or update the requested key and add it to the target
  	 * keyring */
664cceb00   David Howells   [PATCH] Keys: Add...
111
  	key_ref = key_create_or_update(keyring_ref, type, description,
6b79ccb51   Arun Raghavan   keys: allow clien...
112
113
  				       payload, plen, KEY_PERM_UNDEF,
  				       KEY_ALLOC_IN_QUOTA);
664cceb00   David Howells   [PATCH] Keys: Add...
114
115
116
  	if (!IS_ERR(key_ref)) {
  		ret = key_ref_to_ptr(key_ref)->serial;
  		key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
  	}
  	else {
664cceb00   David Howells   [PATCH] Keys: Add...
119
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
121
  	key_ref_put(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
   error3:
38bbca6b6   David Howells   keys: increase th...
123
124
125
126
  	if (!vm)
  		kfree(payload);
  	else
  		vfree(payload);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
   error2:
  	kfree(description);
   error:
  	return ret;
  
  } /* end sys_add_key() */
  
  /*****************************************************************************/
  /*
   * search the process keyrings for a matching key
   * - nested keyrings may also be searched if they have Search permission
   * - if a key is found, it will be attached to the destination keyring if
   *   there's one specified
   * - /sbin/request-key will be invoked if _callout_info is non-NULL
   *   - the _callout_info string will be passed to /sbin/request-key
   *   - if the _callout_info string is empty, it will be rendered as "-"
   * - implements request_key()
   */
1e7bfb213   Heiko Carstens   [CVE-2009-0029] S...
145
146
147
148
  SYSCALL_DEFINE4(request_key, const char __user *, _type,
  		const char __user *, _description,
  		const char __user *, _callout_info,
  		key_serial_t, destringid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
  {
  	struct key_type *ktype;
664cceb00   David Howells   [PATCH] Keys: Add...
151
152
  	struct key *key;
  	key_ref_t dest_ref;
4a38e122e   David Howells   keys: allow the c...
153
  	size_t callout_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  	char type[32], *description, *callout_info;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
155
  	long ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
  
  	/* pull the type into kernel space */
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
158
  	ret = key_get_type_from_user(type, _type, sizeof(type));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
  	if (ret < 0)
  		goto error;
1260f801b   David Howells   [PATCH] Keys: Fix...
161

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
  	/* pull the description into kernel space */
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
163
164
165
  	description = strndup_user(_description, PAGE_SIZE);
  	if (IS_ERR(description)) {
  		ret = PTR_ERR(description);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  		goto error;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
167
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
  
  	/* pull the callout info into kernel space */
  	callout_info = NULL;
4a38e122e   David Howells   keys: allow the c...
171
  	callout_len = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  	if (_callout_info) {
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
173
174
175
  		callout_info = strndup_user(_callout_info, PAGE_SIZE);
  		if (IS_ERR(callout_info)) {
  			ret = PTR_ERR(callout_info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  			goto error2;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
177
  		}
4a38e122e   David Howells   keys: allow the c...
178
  		callout_len = strlen(callout_info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
180
181
  	}
  
  	/* get the destination keyring if specified */
664cceb00   David Howells   [PATCH] Keys: Add...
182
  	dest_ref = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
  	if (destringid) {
8bbf4976b   David Howells   KEYS: Alter use o...
184
  		dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
185
186
  		if (IS_ERR(dest_ref)) {
  			ret = PTR_ERR(dest_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
188
189
190
191
192
193
194
195
196
197
198
  			goto error3;
  		}
  	}
  
  	/* find the key type */
  	ktype = key_type_lookup(type);
  	if (IS_ERR(ktype)) {
  		ret = PTR_ERR(ktype);
  		goto error4;
  	}
  
  	/* do the search */
4a38e122e   David Howells   keys: allow the c...
199
200
  	key = request_key_and_link(ktype, description, callout_info,
  				   callout_len, NULL, key_ref_to_ptr(dest_ref),
7e047ef5f   David Howells   [PATCH] keys: sor...
201
  				   KEY_ALLOC_IN_QUOTA);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
  	if (IS_ERR(key)) {
  		ret = PTR_ERR(key);
  		goto error5;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  	ret = key->serial;
3e30148c3   David Howells   [PATCH] Keys: Mak...
207
   	key_put(key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
   error5:
  	key_type_put(ktype);
   error4:
664cceb00   David Howells   [PATCH] Keys: Add...
211
  	key_ref_put(dest_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
   error3:
  	kfree(callout_info);
   error2:
  	kfree(description);
   error:
  	return ret;
  
  } /* end sys_request_key() */
  
  /*****************************************************************************/
  /*
   * get the ID of the specified process keyring
   * - the keyring must have search permission to be found
   * - implements keyctl(KEYCTL_GET_KEYRING_ID)
   */
  long keyctl_get_keyring_ID(key_serial_t id, int create)
  {
664cceb00   David Howells   [PATCH] Keys: Add...
229
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
231
  	key_ref = lookup_user_key(id, create, 0, KEY_SEARCH);
664cceb00   David Howells   [PATCH] Keys: Add...
232
233
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
  		goto error;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
236
237
  	ret = key_ref_to_ptr(key_ref)->serial;
  	key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
242
243
244
245
246
247
248
249
250
   error:
  	return ret;
  
  } /* end keyctl_get_keyring_ID() */
  
  /*****************************************************************************/
  /*
   * join the session keyring
   * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING)
   */
  long keyctl_join_session_keyring(const char __user *_name)
  {
  	char *name;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
251
  	long ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
  
  	/* fetch the name from userspace */
  	name = NULL;
  	if (_name) {
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
256
257
258
  		name = strndup_user(_name, PAGE_SIZE);
  		if (IS_ERR(name)) {
  			ret = PTR_ERR(name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  			goto error;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
260
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
  	}
  
  	/* join the session */
  	ret = join_session_keyring(name);
0d54ee1c7   Vegard Nossum   security: introdu...
265
  	kfree(name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
   error:
  	return ret;
  
  } /* end keyctl_join_session_keyring() */
  
  /*****************************************************************************/
  /*
   * update a key's data payload
   * - the key must be writable
   * - implements keyctl(KEYCTL_UPDATE)
   */
  long keyctl_update_key(key_serial_t id,
  		       const void __user *_payload,
  		       size_t plen)
  {
664cceb00   David Howells   [PATCH] Keys: Add...
282
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  	void *payload;
  	long ret;
  
  	ret = -EINVAL;
  	if (plen > PAGE_SIZE)
  		goto error;
  
  	/* pull the payload in if one was supplied */
  	payload = NULL;
  	if (_payload) {
  		ret = -ENOMEM;
  		payload = kmalloc(plen, GFP_KERNEL);
  		if (!payload)
  			goto error;
  
  		ret = -EFAULT;
  		if (copy_from_user(payload, _payload, plen) != 0)
  			goto error2;
  	}
  
  	/* find the target key (which must be writable) */
8bbf4976b   David Howells   KEYS: Alter use o...
304
  	key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
305
306
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
  		goto error2;
  	}
  
  	/* update the key */
664cceb00   David Howells   [PATCH] Keys: Add...
311
  	ret = key_update(key_ref, payload, plen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312

664cceb00   David Howells   [PATCH] Keys: Add...
313
  	key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
   error2:
  	kfree(payload);
   error:
  	return ret;
  
  } /* end keyctl_update_key() */
  
  /*****************************************************************************/
  /*
   * revoke a key
   * - the key must be writable
   * - implements keyctl(KEYCTL_REVOKE)
   */
  long keyctl_revoke_key(key_serial_t id)
  {
664cceb00   David Howells   [PATCH] Keys: Add...
329
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
331
  	key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
332
333
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
  		goto error;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
336
  	key_revoke(key_ref_to_ptr(key_ref));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
  	ret = 0;
664cceb00   David Howells   [PATCH] Keys: Add...
338
  	key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
   error:
1260f801b   David Howells   [PATCH] Keys: Fix...
340
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
345
346
347
348
349
350
351
  
  } /* end keyctl_revoke_key() */
  
  /*****************************************************************************/
  /*
   * clear the specified process keyring
   * - the keyring must be writable
   * - implements keyctl(KEYCTL_CLEAR)
   */
  long keyctl_keyring_clear(key_serial_t ringid)
  {
664cceb00   David Howells   [PATCH] Keys: Add...
352
  	key_ref_t keyring_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
354
  	keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
355
356
  	if (IS_ERR(keyring_ref)) {
  		ret = PTR_ERR(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
358
  		goto error;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
359
  	ret = keyring_clear(key_ref_to_ptr(keyring_ref));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360

664cceb00   David Howells   [PATCH] Keys: Add...
361
  	key_ref_put(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
363
364
365
366
367
368
369
370
371
372
373
374
375
   error:
  	return ret;
  
  } /* end keyctl_keyring_clear() */
  
  /*****************************************************************************/
  /*
   * link a key into a keyring
   * - the keyring must be writable
   * - the key must be linkable
   * - implements keyctl(KEYCTL_LINK)
   */
  long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
  {
664cceb00   David Howells   [PATCH] Keys: Add...
376
  	key_ref_t keyring_ref, key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
378
  	keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
379
380
  	if (IS_ERR(keyring_ref)) {
  		ret = PTR_ERR(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
  		goto error;
  	}
8bbf4976b   David Howells   KEYS: Alter use o...
383
  	key_ref = lookup_user_key(id, 1, 0, KEY_LINK);
664cceb00   David Howells   [PATCH] Keys: Add...
384
385
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
  		goto error2;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
388
  	ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389

664cceb00   David Howells   [PATCH] Keys: Add...
390
  	key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
   error2:
664cceb00   David Howells   [PATCH] Keys: Add...
392
  	key_ref_put(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
394
395
396
397
398
399
400
401
402
403
404
405
406
   error:
  	return ret;
  
  } /* end keyctl_keyring_link() */
  
  /*****************************************************************************/
  /*
   * unlink the first attachment of a key from a keyring
   * - the keyring must be writable
   * - we don't need any permissions on the key
   * - implements keyctl(KEYCTL_UNLINK)
   */
  long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
  {
664cceb00   David Howells   [PATCH] Keys: Add...
407
  	key_ref_t keyring_ref, key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
409
  	keyring_ref = lookup_user_key(ringid, 0, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
410
411
  	if (IS_ERR(keyring_ref)) {
  		ret = PTR_ERR(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
  		goto error;
  	}
8bbf4976b   David Howells   KEYS: Alter use o...
414
  	key_ref = lookup_user_key(id, 0, 0, 0);
664cceb00   David Howells   [PATCH] Keys: Add...
415
416
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
  		goto error2;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
419
  	ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420

664cceb00   David Howells   [PATCH] Keys: Add...
421
  	key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
   error2:
664cceb00   David Howells   [PATCH] Keys: Add...
423
  	key_ref_put(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
   error:
  	return ret;
  
  } /* end keyctl_keyring_unlink() */
  
  /*****************************************************************************/
  /*
   * describe a user key
   * - the key must have view permission
   * - if there's a buffer, we place up to buflen bytes of data into it
   * - unless there's an error, we return the amount of description available,
   *   irrespective of how much we may have copied
   * - the description is formatted thus:
   *	type;uid;gid;perm;description<NUL>
   * - implements keyctl(KEYCTL_DESCRIBE)
   */
  long keyctl_describe_key(key_serial_t keyid,
  			 char __user *buffer,
  			 size_t buflen)
  {
3e30148c3   David Howells   [PATCH] Keys: Mak...
444
  	struct key *key, *instkey;
664cceb00   David Howells   [PATCH] Keys: Add...
445
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
  	char *tmpbuf;
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
448
  	key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
664cceb00   David Howells   [PATCH] Keys: Add...
449
  	if (IS_ERR(key_ref)) {
3e30148c3   David Howells   [PATCH] Keys: Mak...
450
451
  		/* viewing a key under construction is permitted if we have the
  		 * authorisation token handy */
664cceb00   David Howells   [PATCH] Keys: Add...
452
  		if (PTR_ERR(key_ref) == -EACCES) {
3e30148c3   David Howells   [PATCH] Keys: Mak...
453
454
455
  			instkey = key_get_instantiation_authkey(keyid);
  			if (!IS_ERR(instkey)) {
  				key_put(instkey);
8bbf4976b   David Howells   KEYS: Alter use o...
456
  				key_ref = lookup_user_key(keyid,
664cceb00   David Howells   [PATCH] Keys: Add...
457
458
  							  0, 1, 0);
  				if (!IS_ERR(key_ref))
3e30148c3   David Howells   [PATCH] Keys: Mak...
459
460
461
  					goto okay;
  			}
  		}
664cceb00   David Howells   [PATCH] Keys: Add...
462
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
464
  		goto error;
  	}
3e30148c3   David Howells   [PATCH] Keys: Mak...
465
  okay:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
470
  	/* calculate how much description we're going to return */
  	ret = -ENOMEM;
  	tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
  	if (!tmpbuf)
  		goto error2;
664cceb00   David Howells   [PATCH] Keys: Add...
471
  	key = key_ref_to_ptr(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
  	ret = snprintf(tmpbuf, PAGE_SIZE - 1,
664cceb00   David Howells   [PATCH] Keys: Add...
473
474
475
476
477
478
479
  		       "%s;%d;%d;%08x;%s",
  		       key_ref_to_ptr(key_ref)->type->name,
  		       key_ref_to_ptr(key_ref)->uid,
  		       key_ref_to_ptr(key_ref)->gid,
  		       key_ref_to_ptr(key_ref)->perm,
  		       key_ref_to_ptr(key_ref)->description ?
  		       key_ref_to_ptr(key_ref)->description : ""
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  		       );
  
  	/* include a NUL char at the end of the data */
  	if (ret > PAGE_SIZE - 1)
  		ret = PAGE_SIZE - 1;
  	tmpbuf[ret] = 0;
  	ret++;
  
  	/* consider returning the data */
  	if (buffer && buflen > 0) {
  		if (buflen > ret)
  			buflen = ret;
  
  		if (copy_to_user(buffer, tmpbuf, buflen) != 0)
  			ret = -EFAULT;
  	}
  
  	kfree(tmpbuf);
   error2:
664cceb00   David Howells   [PATCH] Keys: Add...
499
  	key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
   error:
  	return ret;
  
  } /* end keyctl_describe_key() */
  
  /*****************************************************************************/
  /*
   * search the specified keyring for a matching key
   * - the start keyring must be searchable
   * - nested keyrings may also be searched if they are searchable
   * - only keys with search permission may be found
   * - if a key is found, it will be attached to the destination keyring if
   *   there's one specified
   * - implements keyctl(KEYCTL_SEARCH)
   */
  long keyctl_keyring_search(key_serial_t ringid,
  			   const char __user *_type,
  			   const char __user *_description,
  			   key_serial_t destringid)
  {
  	struct key_type *ktype;
664cceb00   David Howells   [PATCH] Keys: Add...
521
  	key_ref_t keyring_ref, key_ref, dest_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522
  	char type[32], *description;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
523
  	long ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
  
  	/* pull the type and description into kernel space */
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
526
  	ret = key_get_type_from_user(type, _type, sizeof(type));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
  	if (ret < 0)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529

0cb409d98   Davi Arnaut   [PATCH] strndup_u...
530
531
532
  	description = strndup_user(_description, PAGE_SIZE);
  	if (IS_ERR(description)) {
  		ret = PTR_ERR(description);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
  		goto error;
0cb409d98   Davi Arnaut   [PATCH] strndup_u...
534
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
  
  	/* get the keyring at which to begin the search */
8bbf4976b   David Howells   KEYS: Alter use o...
537
  	keyring_ref = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
664cceb00   David Howells   [PATCH] Keys: Add...
538
539
  	if (IS_ERR(keyring_ref)) {
  		ret = PTR_ERR(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
542
543
  		goto error2;
  	}
  
  	/* get the destination keyring if specified */
664cceb00   David Howells   [PATCH] Keys: Add...
544
  	dest_ref = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
  	if (destringid) {
8bbf4976b   David Howells   KEYS: Alter use o...
546
  		dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
664cceb00   David Howells   [PATCH] Keys: Add...
547
548
  		if (IS_ERR(dest_ref)) {
  			ret = PTR_ERR(dest_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549
550
551
552
553
554
555
556
557
558
559
560
  			goto error3;
  		}
  	}
  
  	/* find the key type */
  	ktype = key_type_lookup(type);
  	if (IS_ERR(ktype)) {
  		ret = PTR_ERR(ktype);
  		goto error4;
  	}
  
  	/* do the search */
664cceb00   David Howells   [PATCH] Keys: Add...
561
562
563
  	key_ref = keyring_search(keyring_ref, ktype, description);
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
569
570
571
  
  		/* treat lack or presence of a negative key the same */
  		if (ret == -EAGAIN)
  			ret = -ENOKEY;
  		goto error5;
  	}
  
  	/* link the resulting key to the destination keyring if we can */
664cceb00   David Howells   [PATCH] Keys: Add...
572
  	if (dest_ref) {
29db91906   David Howells   [PATCH] Keys: Add...
573
574
  		ret = key_permission(key_ref, KEY_LINK);
  		if (ret < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
  			goto error6;
664cceb00   David Howells   [PATCH] Keys: Add...
576
  		ret = key_link(key_ref_to_ptr(dest_ref), key_ref_to_ptr(key_ref));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
  		if (ret < 0)
  			goto error6;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
580
  	ret = key_ref_to_ptr(key_ref)->serial;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
  
   error6:
664cceb00   David Howells   [PATCH] Keys: Add...
583
  	key_ref_put(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
585
586
   error5:
  	key_type_put(ktype);
   error4:
664cceb00   David Howells   [PATCH] Keys: Add...
587
  	key_ref_put(dest_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
   error3:
664cceb00   David Howells   [PATCH] Keys: Add...
589
  	key_ref_put(keyring_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
594
595
596
597
598
   error2:
  	kfree(description);
   error:
  	return ret;
  
  } /* end keyctl_keyring_search() */
  
  /*****************************************************************************/
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
600
601
602
603
604
605
606
607
608
   * read a user key's payload
   * - the keyring must be readable or the key must be searchable from the
   *   process's keyrings
   * - if there's a buffer, we place up to buflen bytes of data into it
   * - unless there's an error, we return the amount of data in the key,
   *   irrespective of how much we may have copied
   * - implements keyctl(KEYCTL_READ)
   */
  long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
  {
664cceb00   David Howells   [PATCH] Keys: Add...
609
610
  	struct key *key;
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
  	long ret;
  
  	/* find the key first */
8bbf4976b   David Howells   KEYS: Alter use o...
614
  	key_ref = lookup_user_key(keyid, 0, 0, 0);
664cceb00   David Howells   [PATCH] Keys: Add...
615
616
617
  	if (IS_ERR(key_ref)) {
  		ret = -ENOKEY;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
618
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
619
620
621
  	key = key_ref_to_ptr(key_ref);
  
  	/* see if we can read it directly */
29db91906   David Howells   [PATCH] Keys: Add...
622
623
  	ret = key_permission(key_ref, KEY_READ);
  	if (ret == 0)
664cceb00   David Howells   [PATCH] Keys: Add...
624
  		goto can_read_key;
29db91906   David Howells   [PATCH] Keys: Add...
625
626
  	if (ret != -EACCES)
  		goto error;
664cceb00   David Howells   [PATCH] Keys: Add...
627
628
629
630
631
632
633
634
635
  
  	/* we can't; see if it's searchable from this process's keyrings
  	 * - we automatically take account of the fact that it may be
  	 *   dangling off an instantiation key
  	 */
  	if (!is_key_possessed(key_ref)) {
  		ret = -EACCES;
  		goto error2;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
  
  	/* the key is probably readable - now try to read it */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
   can_read_key:
  	ret = key_validate(key);
  	if (ret == 0) {
  		ret = -EOPNOTSUPP;
  		if (key->type->read) {
  			/* read the data with the semaphore held (since we
  			 * might sleep) */
  			down_read(&key->sem);
  			ret = key->type->read(key, buffer, buflen);
  			up_read(&key->sem);
  		}
  	}
  
   error2:
  	key_put(key);
   error:
  	return ret;
  
  } /* end keyctl_read_key() */
  
  /*****************************************************************************/
  /*
   * change the ownership of a key
   * - the keyring owned by the changer
   * - if the uid or gid is -1, then that parameter is not changed
   * - implements keyctl(KEYCTL_CHOWN)
   */
  long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
  {
5801649d8   Fredrik Tolf   [PATCH] keys: let...
667
  	struct key_user *newowner, *zapowner = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
  	struct key *key;
664cceb00   David Howells   [PATCH] Keys: Add...
669
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
672
673
674
  	long ret;
  
  	ret = 0;
  	if (uid == (uid_t) -1 && gid == (gid_t) -1)
  		goto error;
8bbf4976b   David Howells   KEYS: Alter use o...
675
  	key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
664cceb00   David Howells   [PATCH] Keys: Add...
676
677
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
678
679
  		goto error;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
680
  	key = key_ref_to_ptr(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
  	/* make the changes with the locks held to prevent chown/chown races */
  	ret = -EACCES;
  	down_write(&key->sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
686
687
  
  	if (!capable(CAP_SYS_ADMIN)) {
  		/* only the sysadmin can chown a key to some other UID */
  		if (uid != (uid_t) -1 && key->uid != uid)
5801649d8   Fredrik Tolf   [PATCH] keys: let...
688
  			goto error_put;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
690
691
692
  
  		/* only the sysadmin can set the key's GID to a group other
  		 * than one of those that the current process subscribes to */
  		if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid))
5801649d8   Fredrik Tolf   [PATCH] keys: let...
693
  			goto error_put;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
  	}
5801649d8   Fredrik Tolf   [PATCH] keys: let...
695
  	/* change the UID */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
  	if (uid != (uid_t) -1 && uid != key->uid) {
5801649d8   Fredrik Tolf   [PATCH] keys: let...
697
  		ret = -ENOMEM;
1d1e97562   Serge E. Hallyn   keys: distinguish...
698
  		newowner = key_user_lookup(uid, current_user_ns());
5801649d8   Fredrik Tolf   [PATCH] keys: let...
699
700
701
702
703
  		if (!newowner)
  			goto error_put;
  
  		/* transfer the quota burden to the new user */
  		if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
0b77f5bfb   David Howells   keys: make the ke...
704
705
706
707
  			unsigned maxkeys = (uid == 0) ?
  				key_quota_root_maxkeys : key_quota_maxkeys;
  			unsigned maxbytes = (uid == 0) ?
  				key_quota_root_maxbytes : key_quota_maxbytes;
5801649d8   Fredrik Tolf   [PATCH] keys: let...
708
  			spin_lock(&newowner->lock);
0b77f5bfb   David Howells   keys: make the ke...
709
710
711
712
  			if (newowner->qnkeys + 1 >= maxkeys ||
  			    newowner->qnbytes + key->quotalen >= maxbytes ||
  			    newowner->qnbytes + key->quotalen <
  			    newowner->qnbytes)
5801649d8   Fredrik Tolf   [PATCH] keys: let...
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
  				goto quota_overrun;
  
  			newowner->qnkeys++;
  			newowner->qnbytes += key->quotalen;
  			spin_unlock(&newowner->lock);
  
  			spin_lock(&key->user->lock);
  			key->user->qnkeys--;
  			key->user->qnbytes -= key->quotalen;
  			spin_unlock(&key->user->lock);
  		}
  
  		atomic_dec(&key->user->nkeys);
  		atomic_inc(&newowner->nkeys);
  
  		if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
  			atomic_dec(&key->user->nikeys);
  			atomic_inc(&newowner->nikeys);
  		}
  
  		zapowner = key->user;
  		key->user = newowner;
  		key->uid = uid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
739
740
741
742
  	}
  
  	/* change the GID */
  	if (gid != (gid_t) -1)
  		key->gid = gid;
  
  	ret = 0;
5801649d8   Fredrik Tolf   [PATCH] keys: let...
743
  error_put:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
744
745
  	up_write(&key->sem);
  	key_put(key);
5801649d8   Fredrik Tolf   [PATCH] keys: let...
746
747
748
  	if (zapowner)
  		key_user_put(zapowner);
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
  	return ret;
5801649d8   Fredrik Tolf   [PATCH] keys: let...
750
751
752
753
754
  quota_overrun:
  	spin_unlock(&newowner->lock);
  	zapowner = newowner;
  	ret = -EDQUOT;
  	goto error_put;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
758
759
760
761
762
763
764
765
  } /* end keyctl_chown_key() */
  
  /*****************************************************************************/
  /*
   * change the permission mask on a key
   * - the keyring owned by the changer
   * - implements keyctl(KEYCTL_SETPERM)
   */
  long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
  {
  	struct key *key;
664cceb00   David Howells   [PATCH] Keys: Add...
766
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
768
769
  	long ret;
  
  	ret = -EINVAL;
664cceb00   David Howells   [PATCH] Keys: Add...
770
  	if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
  		goto error;
8bbf4976b   David Howells   KEYS: Alter use o...
772
  	key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
664cceb00   David Howells   [PATCH] Keys: Add...
773
774
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775
776
  		goto error;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
777
  	key = key_ref_to_ptr(key_ref);
76d8aeabf   David Howells   [PATCH] keys: Dis...
778
  	/* make the changes with the locks held to prevent chown/chmod races */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
  	ret = -EACCES;
  	down_write(&key->sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781

76d8aeabf   David Howells   [PATCH] keys: Dis...
782
  	/* if we're not the sysadmin, we can only change a key that we own */
47d804bfa   David Howells   CRED: Wrap task c...
783
  	if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) {
76d8aeabf   David Howells   [PATCH] keys: Dis...
784
785
786
  		key->perm = perm;
  		ret = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
  	up_write(&key->sem);
  	key_put(key);
76d8aeabf   David Howells   [PATCH] keys: Dis...
790
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
793
  	return ret;
  
  } /* end keyctl_setperm_key() */
8bbf4976b   David Howells   KEYS: Alter use o...
794
795
796
797
798
799
800
801
  /*
   * get the destination keyring for instantiation
   */
  static long get_instantiation_keyring(key_serial_t ringid,
  				      struct request_key_auth *rka,
  				      struct key **_dest_keyring)
  {
  	key_ref_t dkref;
eca1bf5b4   David Howells   KEYS: Fix variabl...
802
  	*_dest_keyring = NULL;
8bbf4976b   David Howells   KEYS: Alter use o...
803
  	/* just return a NULL pointer if we weren't asked to make a link */
eca1bf5b4   David Howells   KEYS: Fix variabl...
804
  	if (ringid == 0)
8bbf4976b   David Howells   KEYS: Alter use o...
805
  		return 0;
8bbf4976b   David Howells   KEYS: Alter use o...
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
  
  	/* if a specific keyring is nominated by ID, then use that */
  	if (ringid > 0) {
  		dkref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
  		if (IS_ERR(dkref))
  			return PTR_ERR(dkref);
  		*_dest_keyring = key_ref_to_ptr(dkref);
  		return 0;
  	}
  
  	if (ringid == KEY_SPEC_REQKEY_AUTH_KEY)
  		return -EINVAL;
  
  	/* otherwise specify the destination keyring recorded in the
  	 * authorisation key (any KEY_SPEC_*_KEYRING) */
  	if (ringid >= KEY_SPEC_REQUESTOR_KEYRING) {
  		*_dest_keyring = rka->dest_keyring;
  		return 0;
  	}
  
  	return -ENOKEY;
  }
d84f4f992   David Howells   CRED: Inaugurate ...
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
  /*
   * change the request_key authorisation key on the current process
   */
  static int keyctl_change_reqkey_auth(struct key *key)
  {
  	struct cred *new;
  
  	new = prepare_creds();
  	if (!new)
  		return -ENOMEM;
  
  	key_put(new->request_key_auth);
  	new->request_key_auth = key_get(key);
  
  	return commit_creds(new);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
845
846
847
848
849
850
851
852
853
  /*****************************************************************************/
  /*
   * instantiate the key with the specified payload, and, if one is given, link
   * the key into the keyring
   */
  long keyctl_instantiate_key(key_serial_t id,
  			    const void __user *_payload,
  			    size_t plen,
  			    key_serial_t ringid)
  {
d84f4f992   David Howells   CRED: Inaugurate ...
854
  	const struct cred *cred = current_cred();
3e30148c3   David Howells   [PATCH] Keys: Mak...
855
  	struct request_key_auth *rka;
8bbf4976b   David Howells   KEYS: Alter use o...
856
  	struct key *instkey, *dest_keyring;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
857
858
  	void *payload;
  	long ret;
38bbca6b6   David Howells   keys: increase th...
859
  	bool vm = false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860

d84f4f992   David Howells   CRED: Inaugurate ...
861
  	kenter("%d,,%zu,%d", id, plen, ringid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
  	ret = -EINVAL;
38bbca6b6   David Howells   keys: increase th...
863
  	if (plen > 1024 * 1024 - 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
  		goto error;
b5f545c88   David Howells   [PATCH] keys: Per...
865
866
867
  	/* the appropriate instantiation authorisation key must have been
  	 * assumed before calling this */
  	ret = -EPERM;
d84f4f992   David Howells   CRED: Inaugurate ...
868
  	instkey = cred->request_key_auth;
b5f545c88   David Howells   [PATCH] keys: Per...
869
870
871
872
873
874
  	if (!instkey)
  		goto error;
  
  	rka = instkey->payload.data;
  	if (rka->target_key->serial != id)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
877
878
879
880
  	/* pull the payload in if one was supplied */
  	payload = NULL;
  
  	if (_payload) {
  		ret = -ENOMEM;
  		payload = kmalloc(plen, GFP_KERNEL);
38bbca6b6   David Howells   keys: increase th...
881
882
883
884
885
886
887
888
  		if (!payload) {
  			if (plen <= PAGE_SIZE)
  				goto error;
  			vm = true;
  			payload = vmalloc(plen);
  			if (!payload)
  				goto error;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
893
  
  		ret = -EFAULT;
  		if (copy_from_user(payload, _payload, plen) != 0)
  			goto error2;
  	}
3e30148c3   David Howells   [PATCH] Keys: Mak...
894
895
  	/* find the destination keyring amongst those belonging to the
  	 * requesting task */
8bbf4976b   David Howells   KEYS: Alter use o...
896
897
898
  	ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
  	if (ret < 0)
  		goto error2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
900
  
  	/* instantiate the key and link it into a keyring */
3e30148c3   David Howells   [PATCH] Keys: Mak...
901
  	ret = key_instantiate_and_link(rka->target_key, payload, plen,
8bbf4976b   David Howells   KEYS: Alter use o...
902
  				       dest_keyring, instkey);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903

8bbf4976b   David Howells   KEYS: Alter use o...
904
  	key_put(dest_keyring);
b5f545c88   David Howells   [PATCH] keys: Per...
905
906
907
  
  	/* discard the assumed authority if it's just been disabled by
  	 * instantiation of the key */
d84f4f992   David Howells   CRED: Inaugurate ...
908
909
  	if (ret == 0)
  		keyctl_change_reqkey_auth(NULL);
b5f545c88   David Howells   [PATCH] keys: Per...
910
911
  
  error2:
38bbca6b6   David Howells   keys: increase th...
912
913
914
915
  	if (!vm)
  		kfree(payload);
  	else
  		vfree(payload);
b5f545c88   David Howells   [PATCH] keys: Per...
916
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
918
919
920
921
922
923
924
925
926
927
  	return ret;
  
  } /* end keyctl_instantiate_key() */
  
  /*****************************************************************************/
  /*
   * negatively instantiate the key with the given timeout (in seconds), and, if
   * one is given, link the key into the keyring
   */
  long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
  {
d84f4f992   David Howells   CRED: Inaugurate ...
928
  	const struct cred *cred = current_cred();
3e30148c3   David Howells   [PATCH] Keys: Mak...
929
  	struct request_key_auth *rka;
8bbf4976b   David Howells   KEYS: Alter use o...
930
  	struct key *instkey, *dest_keyring;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931
  	long ret;
d84f4f992   David Howells   CRED: Inaugurate ...
932
  	kenter("%d,%u,%d", id, timeout, ringid);
b5f545c88   David Howells   [PATCH] keys: Per...
933
934
935
  	/* the appropriate instantiation authorisation key must have been
  	 * assumed before calling this */
  	ret = -EPERM;
d84f4f992   David Howells   CRED: Inaugurate ...
936
  	instkey = cred->request_key_auth;
b5f545c88   David Howells   [PATCH] keys: Per...
937
  	if (!instkey)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
939

3e30148c3   David Howells   [PATCH] Keys: Mak...
940
  	rka = instkey->payload.data;
b5f545c88   David Howells   [PATCH] keys: Per...
941
942
  	if (rka->target_key->serial != id)
  		goto error;
3e30148c3   David Howells   [PATCH] Keys: Mak...
943

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
944
945
  	/* find the destination keyring if present (which must also be
  	 * writable) */
8bbf4976b   David Howells   KEYS: Alter use o...
946
947
948
  	ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
  	if (ret < 0)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
950
  
  	/* instantiate the key and link it into a keyring */
664cceb00   David Howells   [PATCH] Keys: Add...
951
  	ret = key_negate_and_link(rka->target_key, timeout,
8bbf4976b   David Howells   KEYS: Alter use o...
952
  				  dest_keyring, instkey);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953

8bbf4976b   David Howells   KEYS: Alter use o...
954
  	key_put(dest_keyring);
b5f545c88   David Howells   [PATCH] keys: Per...
955
956
957
  
  	/* discard the assumed authority if it's just been disabled by
  	 * instantiation of the key */
d84f4f992   David Howells   CRED: Inaugurate ...
958
959
  	if (ret == 0)
  		keyctl_change_reqkey_auth(NULL);
b5f545c88   David Howells   [PATCH] keys: Per...
960
961
  
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
963
964
965
966
967
  	return ret;
  
  } /* end keyctl_negate_key() */
  
  /*****************************************************************************/
  /*
3e30148c3   David Howells   [PATCH] Keys: Mak...
968
969
970
971
972
   * set the default keyring in which request_key() will cache keys
   * - return the old setting
   */
  long keyctl_set_reqkey_keyring(int reqkey_defl)
  {
d84f4f992   David Howells   CRED: Inaugurate ...
973
974
975
976
977
978
979
980
981
982
983
  	struct cred *new;
  	int ret, old_setting;
  
  	old_setting = current_cred_xxx(jit_keyring);
  
  	if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE)
  		return old_setting;
  
  	new = prepare_creds();
  	if (!new)
  		return -ENOMEM;
3e30148c3   David Howells   [PATCH] Keys: Mak...
984
985
986
  
  	switch (reqkey_defl) {
  	case KEY_REQKEY_DEFL_THREAD_KEYRING:
d84f4f992   David Howells   CRED: Inaugurate ...
987
  		ret = install_thread_keyring_to_cred(new);
3e30148c3   David Howells   [PATCH] Keys: Mak...
988
  		if (ret < 0)
d84f4f992   David Howells   CRED: Inaugurate ...
989
  			goto error;
3e30148c3   David Howells   [PATCH] Keys: Mak...
990
991
992
  		goto set;
  
  	case KEY_REQKEY_DEFL_PROCESS_KEYRING:
d84f4f992   David Howells   CRED: Inaugurate ...
993
994
995
996
997
998
999
  		ret = install_process_keyring_to_cred(new);
  		if (ret < 0) {
  			if (ret != -EEXIST)
  				goto error;
  			ret = 0;
  		}
  		goto set;
3e30148c3   David Howells   [PATCH] Keys: Mak...
1000
1001
1002
1003
1004
  
  	case KEY_REQKEY_DEFL_DEFAULT:
  	case KEY_REQKEY_DEFL_SESSION_KEYRING:
  	case KEY_REQKEY_DEFL_USER_KEYRING:
  	case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
d84f4f992   David Howells   CRED: Inaugurate ...
1005
1006
  	case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
  		goto set;
3e30148c3   David Howells   [PATCH] Keys: Mak...
1007
1008
  
  	case KEY_REQKEY_DEFL_NO_CHANGE:
3e30148c3   David Howells   [PATCH] Keys: Mak...
1009
1010
  	case KEY_REQKEY_DEFL_GROUP_KEYRING:
  	default:
d84f4f992   David Howells   CRED: Inaugurate ...
1011
1012
  		ret = -EINVAL;
  		goto error;
3e30148c3   David Howells   [PATCH] Keys: Mak...
1013
  	}
d84f4f992   David Howells   CRED: Inaugurate ...
1014
1015
1016
1017
1018
1019
1020
  set:
  	new->jit_keyring = reqkey_defl;
  	commit_creds(new);
  	return old_setting;
  error:
  	abort_creds(new);
  	return -EINVAL;
3e30148c3   David Howells   [PATCH] Keys: Mak...
1021
1022
1023
1024
  } /* end keyctl_set_reqkey_keyring() */
  
  /*****************************************************************************/
  /*
017679c4d   David Howells   [PATCH] keys: Per...
1025
1026
1027
1028
1029
1030
1031
1032
1033
   * set or clear the timeout for a key
   */
  long keyctl_set_timeout(key_serial_t id, unsigned timeout)
  {
  	struct timespec now;
  	struct key *key;
  	key_ref_t key_ref;
  	time_t expiry;
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
1034
  	key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
017679c4d   David Howells   [PATCH] keys: Per...
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
  	if (IS_ERR(key_ref)) {
  		ret = PTR_ERR(key_ref);
  		goto error;
  	}
  
  	key = key_ref_to_ptr(key_ref);
  
  	/* make the changes with the locks held to prevent races */
  	down_write(&key->sem);
  
  	expiry = 0;
  	if (timeout > 0) {
  		now = current_kernel_time();
  		expiry = now.tv_sec + timeout;
  	}
  
  	key->expiry = expiry;
  
  	up_write(&key->sem);
  	key_put(key);
  
  	ret = 0;
  error:
  	return ret;
  
  } /* end keyctl_set_timeout() */
  
  /*****************************************************************************/
  /*
b5f545c88   David Howells   [PATCH] keys: Per...
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
   * assume the authority to instantiate the specified key
   */
  long keyctl_assume_authority(key_serial_t id)
  {
  	struct key *authkey;
  	long ret;
  
  	/* special key IDs aren't permitted */
  	ret = -EINVAL;
  	if (id < 0)
  		goto error;
  
  	/* we divest ourselves of authority if given an ID of 0 */
  	if (id == 0) {
d84f4f992   David Howells   CRED: Inaugurate ...
1078
  		ret = keyctl_change_reqkey_auth(NULL);
b5f545c88   David Howells   [PATCH] keys: Per...
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
  		goto error;
  	}
  
  	/* attempt to assume the authority temporarily granted to us whilst we
  	 * instantiate the specified key
  	 * - the authorisation key must be in the current task's keyrings
  	 *   somewhere
  	 */
  	authkey = key_get_instantiation_authkey(id);
  	if (IS_ERR(authkey)) {
  		ret = PTR_ERR(authkey);
  		goto error;
  	}
d84f4f992   David Howells   CRED: Inaugurate ...
1092
1093
1094
1095
  	ret = keyctl_change_reqkey_auth(authkey);
  	if (ret < 0)
  		goto error;
  	key_put(authkey);
b5f545c88   David Howells   [PATCH] keys: Per...
1096

d84f4f992   David Howells   CRED: Inaugurate ...
1097
  	ret = authkey->serial;
b5f545c88   David Howells   [PATCH] keys: Per...
1098
1099
1100
1101
  error:
  	return ret;
  
  } /* end keyctl_assume_authority() */
70a5bb72b   David Howells   keys: add keyctl ...
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
  /*
   * get the security label of a key
   * - the key must grant us view permission
   * - if there's a buffer, we place up to buflen bytes of data into it
   * - unless there's an error, we return the amount of information available,
   *   irrespective of how much we may have copied (including the terminal NUL)
   * - implements keyctl(KEYCTL_GET_SECURITY)
   */
  long keyctl_get_security(key_serial_t keyid,
  			 char __user *buffer,
  			 size_t buflen)
  {
  	struct key *key, *instkey;
  	key_ref_t key_ref;
  	char *context;
  	long ret;
8bbf4976b   David Howells   KEYS: Alter use o...
1118
  	key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
70a5bb72b   David Howells   keys: add keyctl ...
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
  	if (IS_ERR(key_ref)) {
  		if (PTR_ERR(key_ref) != -EACCES)
  			return PTR_ERR(key_ref);
  
  		/* viewing a key under construction is also permitted if we
  		 * have the authorisation token handy */
  		instkey = key_get_instantiation_authkey(keyid);
  		if (IS_ERR(instkey))
  			return PTR_ERR(key_ref);
  		key_put(instkey);
8bbf4976b   David Howells   KEYS: Alter use o...
1129
  		key_ref = lookup_user_key(keyid, 0, 1, 0);
70a5bb72b   David Howells   keys: add keyctl ...
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
  		if (IS_ERR(key_ref))
  			return PTR_ERR(key_ref);
  	}
  
  	key = key_ref_to_ptr(key_ref);
  	ret = security_key_getsecurity(key, &context);
  	if (ret == 0) {
  		/* if no information was returned, give userspace an empty
  		 * string */
  		ret = 1;
  		if (buffer && buflen > 0 &&
  		    copy_to_user(buffer, "", 1) != 0)
  			ret = -EFAULT;
  	} else if (ret > 0) {
  		/* return as much data as there's room for */
  		if (buffer && buflen > 0) {
  			if (buflen > ret)
  				buflen = ret;
  
  			if (copy_to_user(buffer, context, buflen) != 0)
  				ret = -EFAULT;
  		}
  
  		kfree(context);
  	}
  
  	key_ref_put(key_ref);
  	return ret;
  }
b5f545c88   David Howells   [PATCH] keys: Per...
1159
1160
  /*****************************************************************************/
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1161
1162
   * the key control system call
   */
938bb9f5e   Heiko Carstens   [CVE-2009-0029] S...
1163
1164
  SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
  		unsigned long, arg4, unsigned long, arg5)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
  {
  	switch (option) {
  	case KEYCTL_GET_KEYRING_ID:
  		return keyctl_get_keyring_ID((key_serial_t) arg2,
  					     (int) arg3);
  
  	case KEYCTL_JOIN_SESSION_KEYRING:
  		return keyctl_join_session_keyring((const char __user *) arg2);
  
  	case KEYCTL_UPDATE:
  		return keyctl_update_key((key_serial_t) arg2,
  					 (const void __user *) arg3,
  					 (size_t) arg4);
  
  	case KEYCTL_REVOKE:
  		return keyctl_revoke_key((key_serial_t) arg2);
  
  	case KEYCTL_DESCRIBE:
  		return keyctl_describe_key((key_serial_t) arg2,
  					   (char __user *) arg3,
  					   (unsigned) arg4);
  
  	case KEYCTL_CLEAR:
  		return keyctl_keyring_clear((key_serial_t) arg2);
  
  	case KEYCTL_LINK:
  		return keyctl_keyring_link((key_serial_t) arg2,
  					   (key_serial_t) arg3);
  
  	case KEYCTL_UNLINK:
  		return keyctl_keyring_unlink((key_serial_t) arg2,
  					     (key_serial_t) arg3);
  
  	case KEYCTL_SEARCH:
  		return keyctl_keyring_search((key_serial_t) arg2,
  					     (const char __user *) arg3,
  					     (const char __user *) arg4,
  					     (key_serial_t) arg5);
  
  	case KEYCTL_READ:
  		return keyctl_read_key((key_serial_t) arg2,
  				       (char __user *) arg3,
  				       (size_t) arg4);
  
  	case KEYCTL_CHOWN:
  		return keyctl_chown_key((key_serial_t) arg2,
  					(uid_t) arg3,
  					(gid_t) arg4);
  
  	case KEYCTL_SETPERM:
  		return keyctl_setperm_key((key_serial_t) arg2,
  					  (key_perm_t) arg3);
  
  	case KEYCTL_INSTANTIATE:
  		return keyctl_instantiate_key((key_serial_t) arg2,
  					      (const void __user *) arg3,
  					      (size_t) arg4,
  					      (key_serial_t) arg5);
  
  	case KEYCTL_NEGATE:
  		return keyctl_negate_key((key_serial_t) arg2,
  					 (unsigned) arg3,
  					 (key_serial_t) arg4);
3e30148c3   David Howells   [PATCH] Keys: Mak...
1228
1229
  	case KEYCTL_SET_REQKEY_KEYRING:
  		return keyctl_set_reqkey_keyring(arg2);
017679c4d   David Howells   [PATCH] keys: Per...
1230
1231
1232
  	case KEYCTL_SET_TIMEOUT:
  		return keyctl_set_timeout((key_serial_t) arg2,
  					  (unsigned) arg3);
b5f545c88   David Howells   [PATCH] keys: Per...
1233
1234
  	case KEYCTL_ASSUME_AUTHORITY:
  		return keyctl_assume_authority((key_serial_t) arg2);
70a5bb72b   David Howells   keys: add keyctl ...
1235
1236
  	case KEYCTL_GET_SECURITY:
  		return keyctl_get_security((key_serial_t) arg2,
90bd49ab6   James Morris   keys: fix sparse ...
1237
  					   (char __user *) arg3,
70a5bb72b   David Howells   keys: add keyctl ...
1238
  					   (size_t) arg4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1239
1240
1241
1242
1243
  	default:
  		return -EOPNOTSUPP;
  	}
  
  } /* end sys_keyctl() */