Blame view

security/keys/key.c 28.3 KB
76181c134   David Howells   KEYS: Make reques...
1
  /* Basic authentication token and access key management
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
   *
69664cf16   David Howells   keys: don't gener...
3
   * Copyright (C) 2004-2008 Red Hat, Inc. All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
   * 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>
a7807a32b   Randy Dunlap   [PATCH] poison: a...
14
  #include <linux/poison.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  #include <linux/sched.h>
  #include <linux/slab.h>
29db91906   David Howells   [PATCH] Keys: Add...
17
  #include <linux/security.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include <linux/workqueue.h>
e51f6d343   Michael LeMay   [PATCH] keys: all...
19
  #include <linux/random.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
  #include <linux/err.h>
  #include "internal.h"
8bc16deab   David Howells   KEYS: Move the un...
22
  struct kmem_cache *key_jar;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
  struct rb_root		key_serial_tree; /* tree of keys indexed by serial */
  DEFINE_SPINLOCK(key_serial_lock);
  
  struct rb_root	key_user_tree; /* tree of quota records indexed by UID */
  DEFINE_SPINLOCK(key_user_lock);
0b77f5bfb   David Howells   keys: make the ke...
28
29
30
31
  unsigned int key_quota_root_maxkeys = 200;	/* root's key count quota */
  unsigned int key_quota_root_maxbytes = 20000;	/* root's key space quota */
  unsigned int key_quota_maxkeys = 200;		/* general key count quota */
  unsigned int key_quota_maxbytes = 20000;	/* general key space quota */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
  static LIST_HEAD(key_types_list);
  static DECLARE_RWSEM(key_types_sem);
973c9f4f4   David Howells   KEYS: Fix up comm...
34
  /* We serialise key instantiation and link */
76181c134   David Howells   KEYS: Make reques...
35
  DEFINE_MUTEX(key_construction_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
39
40
41
42
43
44
45
  #ifdef KEY_DEBUGGING
  void __key_check(const struct key *key)
  {
  	printk("__key_check: key %p {%08x} should be {%08x}
  ",
  	       key, key->magic, KEY_DEBUG_MAGIC);
  	BUG();
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
47
48
   * Get the key quota record for a user, allocating a new record if one doesn't
   * already exist.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
   */
9a56c2db4   Eric W. Biederman   userns: Convert s...
50
  struct key_user *key_user_lookup(kuid_t uid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
  {
  	struct key_user *candidate = NULL, *user;
  	struct rb_node *parent = NULL;
  	struct rb_node **p;
973c9f4f4   David Howells   KEYS: Fix up comm...
55
  try_again:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
  	p = &key_user_tree.rb_node;
  	spin_lock(&key_user_lock);
  
  	/* search the tree for a user record with a matching UID */
  	while (*p) {
  		parent = *p;
  		user = rb_entry(parent, struct key_user, node);
9a56c2db4   Eric W. Biederman   userns: Convert s...
63
  		if (uid_lt(uid, user->uid))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  			p = &(*p)->rb_left;
9a56c2db4   Eric W. Biederman   userns: Convert s...
65
  		else if (uid_gt(uid, user->uid))
1d1e97562   Serge E. Hallyn   keys: distinguish...
66
  			p = &(*p)->rb_right;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  		else
  			goto found;
  	}
  
  	/* if we get here, we failed to find a match in the tree */
  	if (!candidate) {
  		/* allocate a candidate user record if we don't already have
  		 * one */
  		spin_unlock(&key_user_lock);
  
  		user = NULL;
  		candidate = kmalloc(sizeof(struct key_user), GFP_KERNEL);
  		if (unlikely(!candidate))
  			goto out;
  
  		/* the allocation may have scheduled, so we need to repeat the
  		 * search lest someone else added the record whilst we were
  		 * asleep */
  		goto try_again;
  	}
  
  	/* if we get here, then the user record still hadn't appeared on the
  	 * second pass - so we use the candidate record */
  	atomic_set(&candidate->usage, 1);
  	atomic_set(&candidate->nkeys, 0);
  	atomic_set(&candidate->nikeys, 0);
  	candidate->uid = uid;
  	candidate->qnkeys = 0;
  	candidate->qnbytes = 0;
  	spin_lock_init(&candidate->lock);
76181c134   David Howells   KEYS: Make reques...
97
  	mutex_init(&candidate->cons_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
104
105
  
  	rb_link_node(&candidate->node, parent, p);
  	rb_insert_color(&candidate->node, &key_user_tree);
  	spin_unlock(&key_user_lock);
  	user = candidate;
  	goto out;
  
  	/* okay - we found a user record for this UID */
973c9f4f4   David Howells   KEYS: Fix up comm...
106
  found:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
  	atomic_inc(&user->usage);
  	spin_unlock(&key_user_lock);
a7f988ba3   Jesper Juhl   [PATCH] kfree cle...
109
  	kfree(candidate);
973c9f4f4   David Howells   KEYS: Fix up comm...
110
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  	return user;
a8b17ed01   David Howells   KEYS: Do some sty...
112
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
115
   * Dispose of a user structure
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
122
123
124
   */
  void key_user_put(struct key_user *user)
  {
  	if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
  		rb_erase(&user->node, &key_user_tree);
  		spin_unlock(&key_user_lock);
  
  		kfree(user);
  	}
a8b17ed01   David Howells   KEYS: Do some sty...
125
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
128
129
   * Allocate a serial number for a key.  These are assigned randomly to avoid
   * security issues through covert channel problems.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
134
   */
  static inline void key_alloc_serial(struct key *key)
  {
  	struct rb_node *parent, **p;
  	struct key *xkey;
e51f6d343   Michael LeMay   [PATCH] keys: all...
135
  	/* propose a random serial number and look for a hole for it in the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  	 * serial number tree */
e51f6d343   Michael LeMay   [PATCH] keys: all...
137
138
139
140
141
142
143
  	do {
  		get_random_bytes(&key->serial, sizeof(key->serial));
  
  		key->serial >>= 1; /* negative numbers are not permitted */
  	} while (key->serial < 3);
  
  	spin_lock(&key_serial_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144

9ad0830f3   David Howells   [PATCH] Keys: Fix...
145
  attempt_insertion:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  	parent = NULL;
  	p = &key_serial_tree.rb_node;
  
  	while (*p) {
  		parent = *p;
  		xkey = rb_entry(parent, struct key, serial_node);
  
  		if (key->serial < xkey->serial)
  			p = &(*p)->rb_left;
  		else if (key->serial > xkey->serial)
  			p = &(*p)->rb_right;
  		else
  			goto serial_exists;
  	}
9ad0830f3   David Howells   [PATCH] Keys: Fix...
160
161
162
163
164
165
166
  
  	/* we've found a suitable hole - arrange for this key to occupy it */
  	rb_link_node(&key->serial_node, parent, p);
  	rb_insert_color(&key->serial_node, &key_serial_tree);
  
  	spin_unlock(&key_serial_lock);
  	return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
  
  	/* we found a key with the proposed serial number - walk the tree from
  	 * that point looking for the next unused serial number */
e51f6d343   Michael LeMay   [PATCH] keys: all...
170
  serial_exists:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
  	for (;;) {
e51f6d343   Michael LeMay   [PATCH] keys: all...
172
  		key->serial++;
9ad0830f3   David Howells   [PATCH] Keys: Fix...
173
174
175
176
  		if (key->serial < 3) {
  			key->serial = 3;
  			goto attempt_insertion;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
  
  		parent = rb_next(parent);
  		if (!parent)
9ad0830f3   David Howells   [PATCH] Keys: Fix...
180
  			goto attempt_insertion;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
  
  		xkey = rb_entry(parent, struct key, serial_node);
  		if (key->serial < xkey->serial)
9ad0830f3   David Howells   [PATCH] Keys: Fix...
184
  			goto attempt_insertion;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  	}
a8b17ed01   David Howells   KEYS: Do some sty...
186
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187

973c9f4f4   David Howells   KEYS: Fix up comm...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  /**
   * key_alloc - Allocate a key of the specified type.
   * @type: The type of key to allocate.
   * @desc: The key description to allow the key to be searched out.
   * @uid: The owner of the new key.
   * @gid: The group ID for the new key's group permissions.
   * @cred: The credentials specifying UID namespace.
   * @perm: The permissions mask of the new key.
   * @flags: Flags specifying quota properties.
   *
   * Allocate a key of the specified type with the attributes given.  The key is
   * returned in an uninstantiated state and the caller needs to instantiate the
   * key before returning.
   *
   * The user's key count quota is updated to reflect the creation of the key and
   * the user's key data quota has the default for the key type reserved.  The
   * instantiation function should amend this as necessary.  If insufficient
   * quota is available, -EDQUOT will be returned.
   *
   * The LSM security modules can prevent a key being created, in which case
   * -EACCES will be returned.
   *
   * Returns a pointer to the new key if successful and an error code otherwise.
   *
   * Note that the caller needs to ensure the key type isn't uninstantiated.
   * Internally this can be done by locking key_types_sem.  Externally, this can
   * be done by either never unregistering the key type, or making sure
   * key_alloc() calls don't race with module unloading.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
217
   */
  struct key *key_alloc(struct key_type *type, const char *desc,
9a56c2db4   Eric W. Biederman   userns: Convert s...
218
  		      kuid_t uid, kgid_t gid, const struct cred *cred,
7e047ef5f   David Howells   [PATCH] keys: sor...
219
  		      key_perm_t perm, unsigned long flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
223
  {
  	struct key_user *user = NULL;
  	struct key *key;
  	size_t desclen, quotalen;
29db91906   David Howells   [PATCH] Keys: Add...
224
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
  
  	key = ERR_PTR(-EINVAL);
  	if (!desc || !*desc)
  		goto error;
b9fffa387   David Howells   KEYS: Add a key t...
229
230
231
232
233
234
235
  	if (type->vet_description) {
  		ret = type->vet_description(desc);
  		if (ret < 0) {
  			key = ERR_PTR(ret);
  			goto error;
  		}
  	}
16feef434   David Howells   KEYS: Consolidate...
236
237
  	desclen = strlen(desc);
  	quotalen = desclen + 1 + type->def_datalen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
  
  	/* get hold of the key tracking for this user */
9a56c2db4   Eric W. Biederman   userns: Convert s...
240
  	user = key_user_lookup(uid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
244
245
  	if (!user)
  		goto no_memory_1;
  
  	/* check that the user's quota permits allocation of another key and
  	 * its description */
7e047ef5f   David Howells   [PATCH] keys: sor...
246
  	if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
9a56c2db4   Eric W. Biederman   userns: Convert s...
247
  		unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?
0b77f5bfb   David Howells   keys: make the ke...
248
  			key_quota_root_maxkeys : key_quota_maxkeys;
9a56c2db4   Eric W. Biederman   userns: Convert s...
249
  		unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
0b77f5bfb   David Howells   keys: make the ke...
250
  			key_quota_root_maxbytes : key_quota_maxbytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  		spin_lock(&user->lock);
7e047ef5f   David Howells   [PATCH] keys: sor...
252
  		if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) {
0b77f5bfb   David Howells   keys: make the ke...
253
254
255
  			if (user->qnkeys + 1 >= maxkeys ||
  			    user->qnbytes + quotalen >= maxbytes ||
  			    user->qnbytes + quotalen < user->qnbytes)
7e047ef5f   David Howells   [PATCH] keys: sor...
256
257
  				goto no_quota;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
262
263
264
  
  		user->qnkeys++;
  		user->qnbytes += quotalen;
  		spin_unlock(&user->lock);
  	}
  
  	/* allocate and initialise the key and its description */
2480f57fb   David Howells   KEYS: Pre-clear s...
265
  	key = kmem_cache_zalloc(key_jar, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
269
  	if (!key)
  		goto no_memory_2;
  
  	if (desc) {
16feef434   David Howells   KEYS: Consolidate...
270
271
  		key->index_key.desc_len = desclen;
  		key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
  		if (!key->description)
  			goto no_memory_3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
276
  	}
  
  	atomic_set(&key->usage, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
  	init_rwsem(&key->sem);
7845bc396   David Howells   KEYS: Give key ty...
278
  	lockdep_set_class(&key->sem, &type->lock_class);
16feef434   David Howells   KEYS: Consolidate...
279
  	key->index_key.type = type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
  	key->user = user;
  	key->quotalen = quotalen;
  	key->datalen = type->def_datalen;
  	key->uid = uid;
  	key->gid = gid;
  	key->perm = perm;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286

7e047ef5f   David Howells   [PATCH] keys: sor...
287
  	if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
76d8aeabf   David Howells   [PATCH] keys: Dis...
288
  		key->flags |= 1 << KEY_FLAG_IN_QUOTA;
008643b86   David Howells   KEYS: Add a 'trus...
289
290
  	if (flags & KEY_ALLOC_TRUSTED)
  		key->flags |= 1 << KEY_FLAG_TRUSTED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
  #ifdef KEY_DEBUGGING
  	key->magic = KEY_DEBUG_MAGIC;
  #endif
29db91906   David Howells   [PATCH] Keys: Add...
295
  	/* let the security module know about the key */
d84f4f992   David Howells   CRED: Inaugurate ...
296
  	ret = security_key_alloc(key, cred, flags);
29db91906   David Howells   [PATCH] Keys: Add...
297
298
  	if (ret < 0)
  		goto security_error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
  	/* publish the key by giving it a serial number */
  	atomic_inc(&user->nkeys);
  	key_alloc_serial(key);
29db91906   David Howells   [PATCH] Keys: Add...
302
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
  	return key;
29db91906   David Howells   [PATCH] Keys: Add...
304
305
  security_error:
  	kfree(key->description);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  	kmem_cache_free(key_jar, key);
7e047ef5f   David Howells   [PATCH] keys: sor...
307
  	if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
313
  		spin_lock(&user->lock);
  		user->qnkeys--;
  		user->qnbytes -= quotalen;
  		spin_unlock(&user->lock);
  	}
  	key_user_put(user);
29db91906   David Howells   [PATCH] Keys: Add...
314
315
316
317
318
319
  	key = ERR_PTR(ret);
  	goto error;
  
  no_memory_3:
  	kmem_cache_free(key_jar, key);
  no_memory_2:
7e047ef5f   David Howells   [PATCH] keys: sor...
320
  	if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
29db91906   David Howells   [PATCH] Keys: Add...
321
322
323
324
325
326
327
  		spin_lock(&user->lock);
  		user->qnkeys--;
  		user->qnbytes -= quotalen;
  		spin_unlock(&user->lock);
  	}
  	key_user_put(user);
  no_memory_1:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
  	key = ERR_PTR(-ENOMEM);
  	goto error;
29db91906   David Howells   [PATCH] Keys: Add...
330
  no_quota:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
  	spin_unlock(&user->lock);
  	key_user_put(user);
  	key = ERR_PTR(-EDQUOT);
  	goto error;
a8b17ed01   David Howells   KEYS: Do some sty...
335
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
  EXPORT_SYMBOL(key_alloc);
973c9f4f4   David Howells   KEYS: Fix up comm...
337
338
339
340
341
342
343
344
345
346
  /**
   * key_payload_reserve - Adjust data quota reservation for the key's payload
   * @key: The key to make the reservation for.
   * @datalen: The amount of data payload the caller now wants.
   *
   * Adjust the amount of the owning user's key data quota that a key reserves.
   * If the amount is increased, then -EDQUOT may be returned if there isn't
   * enough free quota available.
   *
   * If successful, 0 is returned.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
   */
  int key_payload_reserve(struct key *key, size_t datalen)
  {
c5b60b5e6   Justin P. Mattock   security: whitesp...
350
  	int delta = (int)datalen - key->datalen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
  	int ret = 0;
  
  	key_check(key);
  
  	/* contemplate the quota adjustment */
76d8aeabf   David Howells   [PATCH] keys: Dis...
356
  	if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
9a56c2db4   Eric W. Biederman   userns: Convert s...
357
  		unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ?
0b77f5bfb   David Howells   keys: make the ke...
358
  			key_quota_root_maxbytes : key_quota_maxbytes;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
360
361
  		spin_lock(&key->user->lock);
  
  		if (delta > 0 &&
0b77f5bfb   David Howells   keys: make the ke...
362
363
  		    (key->user->qnbytes + delta >= maxbytes ||
  		     key->user->qnbytes + delta < key->user->qnbytes)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  			ret = -EDQUOT;
  		}
  		else {
  			key->user->qnbytes += delta;
  			key->quotalen += delta;
  		}
  		spin_unlock(&key->user->lock);
  	}
  
  	/* change the recorded data length if that didn't generate an error */
  	if (ret == 0)
  		key->datalen = datalen;
  
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
378
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  EXPORT_SYMBOL(key_payload_reserve);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
381
382
383
384
   * Instantiate a key and link it into the target keyring atomically.  Must be
   * called with the target keyring's semaphore writelocked.  The target key's
   * semaphore need not be locked as instantiation is serialised by
   * key_construction_mutex.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
   */
  static int __key_instantiate_and_link(struct key *key,
cf7f601c0   David Howells   KEYS: Add payload...
387
  				      struct key_preparsed_payload *prep,
3e30148c3   David Howells   [PATCH] Keys: Mak...
388
  				      struct key *keyring,
f70e2e061   David Howells   KEYS: Do prealloc...
389
  				      struct key *authkey,
b2a4df200   David Howells   KEYS: Expand the ...
390
  				      struct assoc_array_edit **_edit)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
398
  {
  	int ret, awaken;
  
  	key_check(key);
  	key_check(keyring);
  
  	awaken = 0;
  	ret = -EBUSY;
76181c134   David Howells   KEYS: Make reques...
399
  	mutex_lock(&key_construction_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
  
  	/* can't instantiate twice */
76d8aeabf   David Howells   [PATCH] keys: Dis...
402
  	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  		/* instantiate the key */
cf7f601c0   David Howells   KEYS: Add payload...
404
  		ret = key->type->instantiate(key, prep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
  
  		if (ret == 0) {
  			/* mark the key as being instantiated */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
408
  			atomic_inc(&key->user->nikeys);
76d8aeabf   David Howells   [PATCH] keys: Dis...
409
  			set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410

76d8aeabf   David Howells   [PATCH] keys: Dis...
411
  			if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  				awaken = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
  
  			/* and link it into the destination keyring */
  			if (keyring)
b2a4df200   David Howells   KEYS: Expand the ...
416
  				__key_link(key, _edit);
3e30148c3   David Howells   [PATCH] Keys: Mak...
417
418
  
  			/* disable the authorisation key */
d84f4f992   David Howells   CRED: Inaugurate ...
419
420
  			if (authkey)
  				key_revoke(authkey);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
422
  		}
  	}
76181c134   David Howells   KEYS: Make reques...
423
  	mutex_unlock(&key_construction_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
425
426
  
  	/* wake up anyone waiting for a key to be constructed */
  	if (awaken)
76181c134   David Howells   KEYS: Make reques...
427
  		wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
  
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
430
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431

973c9f4f4   David Howells   KEYS: Fix up comm...
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
  /**
   * key_instantiate_and_link - Instantiate a key and link it into the keyring.
   * @key: The key to instantiate.
   * @data: The data to use to instantiate the keyring.
   * @datalen: The length of @data.
   * @keyring: Keyring to create a link in on success (or NULL).
   * @authkey: The authorisation token permitting instantiation.
   *
   * Instantiate a key that's in the uninstantiated state using the provided data
   * and, if successful, link it in to the destination keyring if one is
   * supplied.
   *
   * If successful, 0 is returned, the authorisation token is revoked and anyone
   * waiting for the key is woken up.  If the key was already instantiated,
   * -EBUSY will be returned.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
449
450
   */
  int key_instantiate_and_link(struct key *key,
  			     const void *data,
  			     size_t datalen,
3e30148c3   David Howells   [PATCH] Keys: Mak...
451
  			     struct key *keyring,
d84f4f992   David Howells   CRED: Inaugurate ...
452
  			     struct key *authkey)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
453
  {
cf7f601c0   David Howells   KEYS: Add payload...
454
  	struct key_preparsed_payload prep;
b2a4df200   David Howells   KEYS: Expand the ...
455
  	struct assoc_array_edit *edit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
456
  	int ret;
cf7f601c0   David Howells   KEYS: Add payload...
457
458
459
460
461
462
463
464
465
  	memset(&prep, 0, sizeof(prep));
  	prep.data = data;
  	prep.datalen = datalen;
  	prep.quotalen = key->type->def_datalen;
  	if (key->type->preparse) {
  		ret = key->type->preparse(&prep);
  		if (ret < 0)
  			goto error;
  	}
f70e2e061   David Howells   KEYS: Do prealloc...
466
  	if (keyring) {
b2a4df200   David Howells   KEYS: Expand the ...
467
  		ret = __key_link_begin(keyring, &key->index_key, &edit);
f70e2e061   David Howells   KEYS: Do prealloc...
468
  		if (ret < 0)
cf7f601c0   David Howells   KEYS: Add payload...
469
  			goto error_free_preparse;
f70e2e061   David Howells   KEYS: Do prealloc...
470
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471

b2a4df200   David Howells   KEYS: Expand the ...
472
  	ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
  
  	if (keyring)
b2a4df200   David Howells   KEYS: Expand the ...
475
  		__key_link_end(keyring, &key->index_key, edit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476

cf7f601c0   David Howells   KEYS: Add payload...
477
478
479
480
  error_free_preparse:
  	if (key->type->preparse)
  		key->type->free_preparse(&prep);
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
482
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
  
  EXPORT_SYMBOL(key_instantiate_and_link);
973c9f4f4   David Howells   KEYS: Fix up comm...
485
  /**
fdd1b9458   David Howells   KEYS: Add a new k...
486
   * key_reject_and_link - Negatively instantiate a key and link it into the keyring.
973c9f4f4   David Howells   KEYS: Fix up comm...
487
488
   * @key: The key to instantiate.
   * @timeout: The timeout on the negative key.
fdd1b9458   David Howells   KEYS: Add a new k...
489
   * @error: The error to return when the key is hit.
973c9f4f4   David Howells   KEYS: Fix up comm...
490
491
492
493
   * @keyring: Keyring to create a link in on success (or NULL).
   * @authkey: The authorisation token permitting instantiation.
   *
   * Negatively instantiate a key that's in the uninstantiated state and, if
fdd1b9458   David Howells   KEYS: Add a new k...
494
495
496
   * successful, set its timeout and stored error and link it in to the
   * destination keyring if one is supplied.  The key and any links to the key
   * will be automatically garbage collected after the timeout expires.
973c9f4f4   David Howells   KEYS: Fix up comm...
497
498
   *
   * Negative keys are used to rate limit repeated request_key() calls by causing
fdd1b9458   David Howells   KEYS: Add a new k...
499
500
   * them to return the stored error code (typically ENOKEY) until the negative
   * key expires.
973c9f4f4   David Howells   KEYS: Fix up comm...
501
502
503
504
   *
   * If successful, 0 is returned, the authorisation token is revoked and anyone
   * waiting for the key is woken up.  If the key was already instantiated,
   * -EBUSY will be returned.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
   */
fdd1b9458   David Howells   KEYS: Add a new k...
506
  int key_reject_and_link(struct key *key,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
  			unsigned timeout,
fdd1b9458   David Howells   KEYS: Add a new k...
508
  			unsigned error,
3e30148c3   David Howells   [PATCH] Keys: Mak...
509
  			struct key *keyring,
d84f4f992   David Howells   CRED: Inaugurate ...
510
  			struct key *authkey)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
  {
b2a4df200   David Howells   KEYS: Expand the ...
512
  	struct assoc_array_edit *edit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  	struct timespec now;
f70e2e061   David Howells   KEYS: Do prealloc...
514
  	int ret, awaken, link_ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
519
520
521
522
  
  	key_check(key);
  	key_check(keyring);
  
  	awaken = 0;
  	ret = -EBUSY;
  
  	if (keyring)
b2a4df200   David Howells   KEYS: Expand the ...
523
  		link_ret = __key_link_begin(keyring, &key->index_key, &edit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524

76181c134   David Howells   KEYS: Make reques...
525
  	mutex_lock(&key_construction_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
  
  	/* can't instantiate twice */
76d8aeabf   David Howells   [PATCH] keys: Dis...
528
  	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
  		/* mark the key as being negatively instantiated */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
  		atomic_inc(&key->user->nikeys);
74792b000   David Howells   KEYS: Fix a race ...
531
532
  		key->type_data.reject_error = -error;
  		smp_wmb();
76d8aeabf   David Howells   [PATCH] keys: Dis...
533
534
  		set_bit(KEY_FLAG_NEGATIVE, &key->flags);
  		set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
  		now = current_kernel_time();
  		key->expiry = now.tv_sec + timeout;
c08ef808e   David Howells   KEYS: Fix garbage...
537
  		key_schedule_gc(key->expiry + key_gc_delay);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538

76d8aeabf   David Howells   [PATCH] keys: Dis...
539
  		if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
  			awaken = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
541

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542
543
544
  		ret = 0;
  
  		/* and link it into the destination keyring */
f70e2e061   David Howells   KEYS: Do prealloc...
545
  		if (keyring && link_ret == 0)
b2a4df200   David Howells   KEYS: Expand the ...
546
  			__key_link(key, &edit);
3e30148c3   David Howells   [PATCH] Keys: Mak...
547
548
  
  		/* disable the authorisation key */
d84f4f992   David Howells   CRED: Inaugurate ...
549
550
  		if (authkey)
  			key_revoke(authkey);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
551
  	}
76181c134   David Howells   KEYS: Make reques...
552
  	mutex_unlock(&key_construction_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
  
  	if (keyring)
b2a4df200   David Howells   KEYS: Expand the ...
555
  		__key_link_end(keyring, &key->index_key, edit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
  
  	/* wake up anyone waiting for a key to be constructed */
  	if (awaken)
76181c134   David Howells   KEYS: Make reques...
559
  		wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
560

f70e2e061   David Howells   KEYS: Do prealloc...
561
  	return ret == 0 ? link_ret : ret;
a8b17ed01   David Howells   KEYS: Do some sty...
562
  }
fdd1b9458   David Howells   KEYS: Add a new k...
563
  EXPORT_SYMBOL(key_reject_and_link);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564

973c9f4f4   David Howells   KEYS: Fix up comm...
565
566
567
568
569
570
571
  /**
   * key_put - Discard a reference to a key.
   * @key: The key to discard a reference from.
   *
   * Discard a reference to a key, and when all the references are gone, we
   * schedule the cleanup task to come and pull it out of the tree in process
   * context at some later time.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
573
574
575
576
577
578
   */
  void key_put(struct key *key)
  {
  	if (key) {
  		key_check(key);
  
  		if (atomic_dec_and_test(&key->usage))
3b07e9ca2   Tejun Heo   workqueue: deprec...
579
  			schedule_work(&key_gc_work);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
  	}
a8b17ed01   David Howells   KEYS: Do some sty...
581
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  EXPORT_SYMBOL(key_put);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
583
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
584
   * Find a key by its serial number.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
   */
  struct key *key_lookup(key_serial_t id)
  {
  	struct rb_node *n;
  	struct key *key;
  
  	spin_lock(&key_serial_lock);
  
  	/* search the tree for the specified key */
  	n = key_serial_tree.rb_node;
  	while (n) {
  		key = rb_entry(n, struct key, serial_node);
  
  		if (id < key->serial)
  			n = n->rb_left;
  		else if (id > key->serial)
  			n = n->rb_right;
  		else
  			goto found;
  	}
973c9f4f4   David Howells   KEYS: Fix up comm...
605
  not_found:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
  	key = ERR_PTR(-ENOKEY);
  	goto error;
973c9f4f4   David Howells   KEYS: Fix up comm...
608
  found:
5593122ee   David Howells   KEYS: Deal with d...
609
610
  	/* pretend it doesn't exist if it is awaiting deletion */
  	if (atomic_read(&key->usage) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
614
615
  		goto not_found;
  
  	/* this races with key_put(), but that doesn't matter since key_put()
  	 * doesn't actually change the key
  	 */
ccc3e6d9c   David Howells   KEYS: Define a __...
616
  	__key_get(key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617

973c9f4f4   David Howells   KEYS: Fix up comm...
618
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619
620
  	spin_unlock(&key_serial_lock);
  	return key;
a8b17ed01   David Howells   KEYS: Do some sty...
621
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
624
625
626
627
   * Find and lock the specified key type against removal.
   *
   * We return with the sem read-locked if successful.  If the type wasn't
   * available -ENOKEY is returned instead.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
   */
  struct key_type *key_type_lookup(const char *type)
  {
  	struct key_type *ktype;
  
  	down_read(&key_types_sem);
  
  	/* look up the key type to see if it's one of the registered kernel
  	 * types */
  	list_for_each_entry(ktype, &key_types_list, link) {
  		if (strcmp(ktype->name, type) == 0)
  			goto found_kernel_type;
  	}
  
  	up_read(&key_types_sem);
  	ktype = ERR_PTR(-ENOKEY);
973c9f4f4   David Howells   KEYS: Fix up comm...
644
  found_kernel_type:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
  	return ktype;
a8b17ed01   David Howells   KEYS: Do some sty...
646
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647

59e6b9c11   Bryan Schumaker   Created a functio...
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
  void key_set_timeout(struct key *key, unsigned timeout)
  {
  	struct timespec now;
  	time_t expiry = 0;
  
  	/* make the changes with the locks held to prevent races */
  	down_write(&key->sem);
  
  	if (timeout > 0) {
  		now = current_kernel_time();
  		expiry = now.tv_sec + timeout;
  	}
  
  	key->expiry = expiry;
  	key_schedule_gc(key->expiry + key_gc_delay);
  
  	up_write(&key->sem);
  }
  EXPORT_SYMBOL_GPL(key_set_timeout);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
668
   * Unlock a key type locked by key_type_lookup().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
670
671
672
   */
  void key_type_put(struct key_type *ktype)
  {
  	up_read(&key_types_sem);
a8b17ed01   David Howells   KEYS: Do some sty...
673
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
676
677
678
679
   * Attempt to update an existing key.
   *
   * The key is given to us with an incremented refcount that we need to discard
   * if we get an error.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680
   */
664cceb00   David Howells   [PATCH] Keys: Add...
681
  static inline key_ref_t __key_update(key_ref_t key_ref,
cf7f601c0   David Howells   KEYS: Add payload...
682
  				     struct key_preparsed_payload *prep)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
  {
664cceb00   David Howells   [PATCH] Keys: Add...
684
  	struct key *key = key_ref_to_ptr(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
686
687
  	int ret;
  
  	/* need write permission on the key to update it */
29db91906   David Howells   [PATCH] Keys: Add...
688
689
  	ret = key_permission(key_ref, KEY_WRITE);
  	if (ret < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
691
692
693
694
695
696
  		goto error;
  
  	ret = -EEXIST;
  	if (!key->type->update)
  		goto error;
  
  	down_write(&key->sem);
cf7f601c0   David Howells   KEYS: Add payload...
697
  	ret = key->type->update(key, prep);
76d8aeabf   David Howells   [PATCH] keys: Dis...
698
  	if (ret == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
  		/* updating a negative key instantiates it */
76d8aeabf   David Howells   [PATCH] keys: Dis...
700
  		clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
701
702
703
704
705
  
  	up_write(&key->sem);
  
  	if (ret < 0)
  		goto error;
664cceb00   David Howells   [PATCH] Keys: Add...
706
707
  out:
  	return key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708

664cceb00   David Howells   [PATCH] Keys: Add...
709
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
  	key_put(key);
664cceb00   David Howells   [PATCH] Keys: Add...
711
  	key_ref = ERR_PTR(ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712
  	goto out;
a8b17ed01   David Howells   KEYS: Do some sty...
713
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714

973c9f4f4   David Howells   KEYS: Fix up comm...
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
  /**
   * key_create_or_update - Update or create and instantiate a key.
   * @keyring_ref: A pointer to the destination keyring with possession flag.
   * @type: The type of key.
   * @description: The searchable description for the key.
   * @payload: The data to use to instantiate or update the key.
   * @plen: The length of @payload.
   * @perm: The permissions mask for a new key.
   * @flags: The quota flags for a new key.
   *
   * Search the destination keyring for a key of the same description and if one
   * is found, update it, otherwise create and instantiate a new one and create a
   * link to it from that keyring.
   *
   * If perm is KEY_PERM_UNDEF then an appropriate key permissions mask will be
   * concocted.
   *
   * Returns a pointer to the new key if successful, -ENODEV if the key type
   * wasn't available, -ENOTDIR if the keyring wasn't a keyring, -EACCES if the
   * caller isn't permitted to modify the keyring or the LSM did not permit
   * creation of the key.
   *
   * On success, the possession flag from the keyring ref will be tacked on to
   * the key ref before it is returned.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
   */
664cceb00   David Howells   [PATCH] Keys: Add...
740
741
742
743
744
  key_ref_t key_create_or_update(key_ref_t keyring_ref,
  			       const char *type,
  			       const char *description,
  			       const void *payload,
  			       size_t plen,
6b79ccb51   Arun Raghavan   keys: allow clien...
745
  			       key_perm_t perm,
7e047ef5f   David Howells   [PATCH] keys: sor...
746
  			       unsigned long flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
  {
16feef434   David Howells   KEYS: Consolidate...
748
749
750
  	struct keyring_index_key index_key = {
  		.description	= description,
  	};
cf7f601c0   David Howells   KEYS: Add payload...
751
  	struct key_preparsed_payload prep;
b2a4df200   David Howells   KEYS: Expand the ...
752
  	struct assoc_array_edit *edit;
d84f4f992   David Howells   CRED: Inaugurate ...
753
  	const struct cred *cred = current_cred();
664cceb00   David Howells   [PATCH] Keys: Add...
754
  	struct key *keyring, *key = NULL;
664cceb00   David Howells   [PATCH] Keys: Add...
755
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
758
  	/* look up the key type to see if it's one of the registered kernel
  	 * types */
16feef434   David Howells   KEYS: Consolidate...
759
760
  	index_key.type = key_type_lookup(type);
  	if (IS_ERR(index_key.type)) {
664cceb00   David Howells   [PATCH] Keys: Add...
761
  		key_ref = ERR_PTR(-ENODEV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
763
  		goto error;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
764
  	key_ref = ERR_PTR(-EINVAL);
16feef434   David Howells   KEYS: Consolidate...
765
766
  	if (!index_key.type->match || !index_key.type->instantiate ||
  	    (!index_key.description && !index_key.type->preparse))
cf7f601c0   David Howells   KEYS: Add payload...
767
  		goto error_put_type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
768

664cceb00   David Howells   [PATCH] Keys: Add...
769
770
771
  	keyring = key_ref_to_ptr(keyring_ref);
  
  	key_check(keyring);
c3a9d6541   David Howells   [Security] Keys: ...
772
773
  	key_ref = ERR_PTR(-ENOTDIR);
  	if (keyring->type != &key_type_keyring)
cf7f601c0   David Howells   KEYS: Add payload...
774
775
776
777
778
  		goto error_put_type;
  
  	memset(&prep, 0, sizeof(prep));
  	prep.data = payload;
  	prep.datalen = plen;
16feef434   David Howells   KEYS: Consolidate...
779
  	prep.quotalen = index_key.type->def_datalen;
008643b86   David Howells   KEYS: Add a 'trus...
780
  	prep.trusted = flags & KEY_ALLOC_TRUSTED;
16feef434   David Howells   KEYS: Consolidate...
781
782
  	if (index_key.type->preparse) {
  		ret = index_key.type->preparse(&prep);
cf7f601c0   David Howells   KEYS: Add payload...
783
784
785
786
  		if (ret < 0) {
  			key_ref = ERR_PTR(ret);
  			goto error_put_type;
  		}
16feef434   David Howells   KEYS: Consolidate...
787
788
  		if (!index_key.description)
  			index_key.description = prep.description;
cf7f601c0   David Howells   KEYS: Add payload...
789
  		key_ref = ERR_PTR(-EINVAL);
16feef434   David Howells   KEYS: Consolidate...
790
  		if (!index_key.description)
cf7f601c0   David Howells   KEYS: Add payload...
791
792
  			goto error_free_prep;
  	}
16feef434   David Howells   KEYS: Consolidate...
793
  	index_key.desc_len = strlen(index_key.description);
c3a9d6541   David Howells   [Security] Keys: ...
794

008643b86   David Howells   KEYS: Add a 'trus...
795
796
797
798
  	key_ref = ERR_PTR(-EPERM);
  	if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
  		goto error_free_prep;
  	flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
b2a4df200   David Howells   KEYS: Expand the ...
799
  	ret = __key_link_begin(keyring, &index_key, &edit);
cf7f601c0   David Howells   KEYS: Add payload...
800
801
802
803
  	if (ret < 0) {
  		key_ref = ERR_PTR(ret);
  		goto error_free_prep;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
804
805
806
  
  	/* if we're going to allocate a new key, we're going to have
  	 * to modify the keyring */
29db91906   David Howells   [PATCH] Keys: Add...
807
808
809
  	ret = key_permission(keyring_ref, KEY_WRITE);
  	if (ret < 0) {
  		key_ref = ERR_PTR(ret);
cf7f601c0   David Howells   KEYS: Add payload...
810
  		goto error_link_end;
29db91906   David Howells   [PATCH] Keys: Add...
811
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
812

1d9b7d97d   David Howells   [PATCH] Keys: Rep...
813
814
815
  	/* if it's possible to update this type of key, search for an existing
  	 * key of the same type and description in the destination keyring and
  	 * update that instead if possible
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
  	 */
16feef434   David Howells   KEYS: Consolidate...
817
  	if (index_key.type->update) {
b2a4df200   David Howells   KEYS: Expand the ...
818
819
  		key_ref = find_key_to_update(keyring_ref, &index_key);
  		if (key_ref)
1d9b7d97d   David Howells   [PATCH] Keys: Rep...
820
821
  			goto found_matching_key;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822

6b79ccb51   Arun Raghavan   keys: allow clien...
823
824
825
  	/* if the client doesn't provide, decide on the permissions we want */
  	if (perm == KEY_PERM_UNDEF) {
  		perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
96b5c8fea   David Howells   KEYS: Reduce init...
826
  		perm |= KEY_USR_VIEW;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827

16feef434   David Howells   KEYS: Consolidate...
828
  		if (index_key.type->read)
96b5c8fea   David Howells   KEYS: Reduce init...
829
  			perm |= KEY_POS_READ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830

16feef434   David Howells   KEYS: Consolidate...
831
832
  		if (index_key.type == &key_type_keyring ||
  		    index_key.type->update)
96b5c8fea   David Howells   KEYS: Reduce init...
833
  			perm |= KEY_POS_WRITE;
6b79ccb51   Arun Raghavan   keys: allow clien...
834
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
836
  
  	/* allocate a new key */
16feef434   David Howells   KEYS: Consolidate...
837
838
  	key = key_alloc(index_key.type, index_key.description,
  			cred->fsuid, cred->fsgid, cred, perm, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
  	if (IS_ERR(key)) {
e231c2ee6   David Howells   Convert ERR_PTR(P...
840
  		key_ref = ERR_CAST(key);
cf7f601c0   David Howells   KEYS: Add payload...
841
  		goto error_link_end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
844
  	}
  
  	/* instantiate it and link it into the target keyring */
b2a4df200   David Howells   KEYS: Expand the ...
845
  	ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &edit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
846
847
  	if (ret < 0) {
  		key_put(key);
664cceb00   David Howells   [PATCH] Keys: Add...
848
  		key_ref = ERR_PTR(ret);
cf7f601c0   David Howells   KEYS: Add payload...
849
  		goto error_link_end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
850
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
851
  	key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
cf7f601c0   David Howells   KEYS: Add payload...
852
  error_link_end:
b2a4df200   David Howells   KEYS: Expand the ...
853
  	__key_link_end(keyring, &index_key, edit);
cf7f601c0   David Howells   KEYS: Add payload...
854
  error_free_prep:
16feef434   David Howells   KEYS: Consolidate...
855
856
  	if (index_key.type->preparse)
  		index_key.type->free_preparse(&prep);
cf7f601c0   David Howells   KEYS: Add payload...
857
  error_put_type:
16feef434   David Howells   KEYS: Consolidate...
858
  	key_type_put(index_key.type);
cf7f601c0   David Howells   KEYS: Add payload...
859
  error:
664cceb00   David Howells   [PATCH] Keys: Add...
860
  	return key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
862
863
864
865
  
   found_matching_key:
  	/* we found a matching key, so we're going to try to update it
  	 * - we can drop the locks first as we have the key pinned
  	 */
b2a4df200   David Howells   KEYS: Expand the ...
866
  	__key_link_end(keyring, &index_key, edit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867

cf7f601c0   David Howells   KEYS: Add payload...
868
869
  	key_ref = __key_update(key_ref, &prep);
  	goto error_free_prep;
a8b17ed01   David Howells   KEYS: Do some sty...
870
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
  EXPORT_SYMBOL(key_create_or_update);
973c9f4f4   David Howells   KEYS: Fix up comm...
872
873
874
875
876
877
878
879
880
881
882
883
  /**
   * key_update - Update a key's contents.
   * @key_ref: The pointer (plus possession flag) to the key.
   * @payload: The data to be used to update the key.
   * @plen: The length of @payload.
   *
   * Attempt to update the contents of a key with the given payload data.  The
   * caller must be granted Write permission on the key.  Negative keys can be
   * instantiated by this method.
   *
   * Returns 0 on success, -EACCES if not permitted and -EOPNOTSUPP if the key
   * type does not support updating.  The key type may return other errors.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
   */
664cceb00   David Howells   [PATCH] Keys: Add...
885
  int key_update(key_ref_t key_ref, const void *payload, size_t plen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
886
  {
cf7f601c0   David Howells   KEYS: Add payload...
887
  	struct key_preparsed_payload prep;
664cceb00   David Howells   [PATCH] Keys: Add...
888
  	struct key *key = key_ref_to_ptr(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
893
  	int ret;
  
  	key_check(key);
  
  	/* the key must be writable */
29db91906   David Howells   [PATCH] Keys: Add...
894
895
  	ret = key_permission(key_ref, KEY_WRITE);
  	if (ret < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
896
897
898
899
  		goto error;
  
  	/* attempt to update it if supported */
  	ret = -EOPNOTSUPP;
cf7f601c0   David Howells   KEYS: Add payload...
900
901
  	if (!key->type->update)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902

cf7f601c0   David Howells   KEYS: Add payload...
903
904
905
906
907
908
909
910
  	memset(&prep, 0, sizeof(prep));
  	prep.data = payload;
  	prep.datalen = plen;
  	prep.quotalen = key->type->def_datalen;
  	if (key->type->preparse) {
  		ret = key->type->preparse(&prep);
  		if (ret < 0)
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
  	}
cf7f601c0   David Howells   KEYS: Add payload...
912
913
914
915
916
917
918
919
920
921
922
923
  	down_write(&key->sem);
  
  	ret = key->type->update(key, &prep);
  	if (ret == 0)
  		/* updating a negative key instantiates it */
  		clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
  
  	up_write(&key->sem);
  
  	if (key->type->preparse)
  		key->type->free_preparse(&prep);
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
925
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
  EXPORT_SYMBOL(key_update);
973c9f4f4   David Howells   KEYS: Fix up comm...
927
928
929
930
931
932
933
934
  /**
   * key_revoke - Revoke a key.
   * @key: The key to be revoked.
   *
   * Mark a key as being revoked and ask the type to free up its resources.  The
   * revocation timeout is set and the key and all its links will be
   * automatically garbage collected after key_gc_delay amount of time if they
   * are not manually dealt with first.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
936
937
   */
  void key_revoke(struct key *key)
  {
5d135440f   David Howells   KEYS: Add garbage...
938
939
  	struct timespec now;
  	time_t time;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940
  	key_check(key);
76181c134   David Howells   KEYS: Make reques...
941
942
943
944
945
946
947
948
  	/* make sure no one's trying to change or use the key when we mark it
  	 * - we tell lockdep that we might nest because we might be revoking an
  	 *   authorisation key whilst holding the sem on a key we've just
  	 *   instantiated
  	 */
  	down_write_nested(&key->sem, 1);
  	if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags) &&
  	    key->type->revoke)
04c567d93   David Howells   [PATCH] Keys: Fix...
949
  		key->type->revoke(key);
5d135440f   David Howells   KEYS: Add garbage...
950
951
952
953
954
  	/* set the death time to no more than the expiry time */
  	now = current_kernel_time();
  	time = now.tv_sec;
  	if (key->revoked_at == 0 || key->revoked_at > time) {
  		key->revoked_at = time;
c08ef808e   David Howells   KEYS: Fix garbage...
955
  		key_schedule_gc(key->revoked_at + key_gc_delay);
5d135440f   David Howells   KEYS: Add garbage...
956
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
  	up_write(&key->sem);
a8b17ed01   David Howells   KEYS: Do some sty...
958
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
959
  EXPORT_SYMBOL(key_revoke);
973c9f4f4   David Howells   KEYS: Fix up comm...
960
  /**
fd75815f7   David Howells   KEYS: Add invalid...
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
   * key_invalidate - Invalidate a key.
   * @key: The key to be invalidated.
   *
   * Mark a key as being invalidated and have it cleaned up immediately.  The key
   * is ignored by all searches and other operations from this point.
   */
  void key_invalidate(struct key *key)
  {
  	kenter("%d", key_serial(key));
  
  	key_check(key);
  
  	if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) {
  		down_write_nested(&key->sem, 1);
  		if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags))
  			key_schedule_gc_links();
  		up_write(&key->sem);
  	}
  }
  EXPORT_SYMBOL(key_invalidate);
  
  /**
973c9f4f4   David Howells   KEYS: Fix up comm...
983
984
985
986
987
988
   * register_key_type - Register a type of key.
   * @ktype: The new key type.
   *
   * Register a new key type.
   *
   * Returns 0 on success or -EEXIST if a type of this name already exists.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
990
991
992
993
   */
  int register_key_type(struct key_type *ktype)
  {
  	struct key_type *p;
  	int ret;
7845bc396   David Howells   KEYS: Give key ty...
994
  	memset(&ktype->lock_class, 0, sizeof(ktype->lock_class));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
996
997
998
999
1000
1001
1002
1003
1004
1005
  	ret = -EEXIST;
  	down_write(&key_types_sem);
  
  	/* disallow key types with the same name */
  	list_for_each_entry(p, &key_types_list, link) {
  		if (strcmp(p->name, ktype->name) == 0)
  			goto out;
  	}
  
  	/* store the type */
  	list_add(&ktype->link, &key_types_list);
1eb1bcf5b   David Howells   KEYS: Announce ke...
1006
1007
1008
  
  	pr_notice("Key type %s registered
  ", ktype->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009
  	ret = 0;
973c9f4f4   David Howells   KEYS: Fix up comm...
1010
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
1012
  	up_write(&key_types_sem);
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
1013
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014
  EXPORT_SYMBOL(register_key_type);
973c9f4f4   David Howells   KEYS: Fix up comm...
1015
1016
1017
1018
1019
1020
1021
  /**
   * unregister_key_type - Unregister a type of key.
   * @ktype: The key type.
   *
   * Unregister a key type and mark all the extant keys of this type as dead.
   * Those keys of this type are then destroyed to get rid of their payloads and
   * they and their links will be garbage collected as soon as possible.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
   */
  void unregister_key_type(struct key_type *ktype)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1025
  	down_write(&key_types_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1026
  	list_del_init(&ktype->link);
0c061b570   David Howells   KEYS: Correctly d...
1027
1028
  	downgrade_write(&key_types_sem);
  	key_gc_keytype(ktype);
1eb1bcf5b   David Howells   KEYS: Announce ke...
1029
1030
  	pr_notice("Key type %s unregistered
  ", ktype->name);
0c061b570   David Howells   KEYS: Correctly d...
1031
  	up_read(&key_types_sem);
a8b17ed01   David Howells   KEYS: Do some sty...
1032
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1033
  EXPORT_SYMBOL(unregister_key_type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1034
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
1035
   * Initialise the key management state.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036
1037
1038
1039
1040
   */
  void __init key_init(void)
  {
  	/* allocate a slab in which we can store keys */
  	key_jar = kmem_cache_create("key_jar", sizeof(struct key),
20c2df83d   Paul Mundt   mm: Remove slab d...
1041
  			0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042
1043
1044
1045
1046
  
  	/* add the special key types */
  	list_add_tail(&key_type_keyring.link, &key_types_list);
  	list_add_tail(&key_type_dead.link, &key_types_list);
  	list_add_tail(&key_type_user.link, &key_types_list);
9f6ed2ca2   Jeff Layton   keys: add a "logo...
1047
  	list_add_tail(&key_type_logon.link, &key_types_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
1049
1050
1051
1052
1053
1054
1055
  
  	/* record the root user tracking */
  	rb_link_node(&root_key_user.node,
  		     NULL,
  		     &key_user_tree.rb_node);
  
  	rb_insert_color(&root_key_user.node,
  			&key_user_tree);
a8b17ed01   David Howells   KEYS: Do some sty...
1056
  }