Blame view

security/keys/key.c 29.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);
738c5d190   Steve Dickson   KEYS: Increase ro...
28
29
  unsigned int key_quota_root_maxkeys = 1000000;	/* root's key count quota */
  unsigned int key_quota_root_maxbytes = 25000000; /* root's key space quota */
0b77f5bfb   David Howells   keys: make the ke...
30
31
  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
  	if (!key)
  		goto no_memory_2;
5057975ae   Dan Carpenter   KEYS: remove a bo...
268
269
270
271
  	key->index_key.desc_len = desclen;
  	key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
  	if (!key->description)
  		goto no_memory_3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
  
  	atomic_set(&key->usage, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
  	init_rwsem(&key->sem);
7845bc396   David Howells   KEYS: Give key ty...
275
  	lockdep_set_class(&key->sem, &type->lock_class);
16feef434   David Howells   KEYS: Consolidate...
276
  	key->index_key.type = type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
280
281
282
  	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
283

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

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

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

973c9f4f4   David Howells   KEYS: Fix up comm...
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
  /**
   * 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
449
450
451
452
   */
  int key_instantiate_and_link(struct key *key,
  			     const void *data,
  			     size_t datalen,
3e30148c3   David Howells   [PATCH] Keys: Mak...
453
  			     struct key *keyring,
d84f4f992   David Howells   CRED: Inaugurate ...
454
  			     struct key *authkey)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  {
cf7f601c0   David Howells   KEYS: Add payload...
456
  	struct key_preparsed_payload prep;
b2a4df200   David Howells   KEYS: Expand the ...
457
  	struct assoc_array_edit *edit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  	int ret;
cf7f601c0   David Howells   KEYS: Add payload...
459
460
461
462
  	memset(&prep, 0, sizeof(prep));
  	prep.data = data;
  	prep.datalen = datalen;
  	prep.quotalen = key->type->def_datalen;
7dfa0ca6a   David Howells   KEYS: Allow expir...
463
  	prep.expiry = TIME_T_MAX;
cf7f601c0   David Howells   KEYS: Add payload...
464
465
466
467
468
  	if (key->type->preparse) {
  		ret = key->type->preparse(&prep);
  		if (ret < 0)
  			goto error;
  	}
f70e2e061   David Howells   KEYS: Do prealloc...
469
  	if (keyring) {
b2a4df200   David Howells   KEYS: Expand the ...
470
  		ret = __key_link_begin(keyring, &key->index_key, &edit);
f70e2e061   David Howells   KEYS: Do prealloc...
471
  		if (ret < 0)
4d8c0250b   David Howells   KEYS: Call ->free...
472
  			goto error;
f70e2e061   David Howells   KEYS: Do prealloc...
473
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474

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

4d8c0250b   David Howells   KEYS: Call ->free...
480
  error:
cf7f601c0   David Howells   KEYS: Add payload...
481
482
  	if (key->type->preparse)
  		key->type->free_preparse(&prep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
484
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
  
  EXPORT_SYMBOL(key_instantiate_and_link);
973c9f4f4   David Howells   KEYS: Fix up comm...
487
  /**
fdd1b9458   David Howells   KEYS: Add a new k...
488
   * key_reject_and_link - Negatively instantiate a key and link it into the keyring.
973c9f4f4   David Howells   KEYS: Fix up comm...
489
490
   * @key: The key to instantiate.
   * @timeout: The timeout on the negative key.
fdd1b9458   David Howells   KEYS: Add a new k...
491
   * @error: The error to return when the key is hit.
973c9f4f4   David Howells   KEYS: Fix up comm...
492
493
494
495
   * @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...
496
497
498
   * 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...
499
500
   *
   * Negative keys are used to rate limit repeated request_key() calls by causing
fdd1b9458   David Howells   KEYS: Add a new k...
501
502
   * them to return the stored error code (typically ENOKEY) until the negative
   * key expires.
973c9f4f4   David Howells   KEYS: Fix up comm...
503
504
505
506
   *
   * 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
507
   */
fdd1b9458   David Howells   KEYS: Add a new k...
508
  int key_reject_and_link(struct key *key,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
  			unsigned timeout,
fdd1b9458   David Howells   KEYS: Add a new k...
510
  			unsigned error,
3e30148c3   David Howells   [PATCH] Keys: Mak...
511
  			struct key *keyring,
d84f4f992   David Howells   CRED: Inaugurate ...
512
  			struct key *authkey)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  {
b2a4df200   David Howells   KEYS: Expand the ...
514
  	struct assoc_array_edit *edit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
  	struct timespec now;
f70e2e061   David Howells   KEYS: Do prealloc...
516
  	int ret, awaken, link_ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
518
519
520
521
522
523
524
  
  	key_check(key);
  	key_check(keyring);
  
  	awaken = 0;
  	ret = -EBUSY;
  
  	if (keyring)
b2a4df200   David Howells   KEYS: Expand the ...
525
  		link_ret = __key_link_begin(keyring, &key->index_key, &edit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526

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

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

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

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

973c9f4f4   David Howells   KEYS: Fix up comm...
567
568
569
570
571
572
573
  /**
   * 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
574
575
576
577
578
579
580
   */
  void key_put(struct key *key)
  {
  	if (key) {
  		key_check(key);
  
  		if (atomic_dec_and_test(&key->usage))
3b07e9ca2   Tejun Heo   workqueue: deprec...
581
  			schedule_work(&key_gc_work);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
  	}
a8b17ed01   David Howells   KEYS: Do some sty...
583
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
584
  EXPORT_SYMBOL(key_put);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
586
   * Find a key by its serial number.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
   */
  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...
607
  not_found:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
  	key = ERR_PTR(-ENOKEY);
  	goto error;
973c9f4f4   David Howells   KEYS: Fix up comm...
610
  found:
5593122ee   David Howells   KEYS: Deal with d...
611
612
  	/* pretend it doesn't exist if it is awaiting deletion */
  	if (atomic_read(&key->usage) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
615
616
617
  		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 __...
618
  	__key_get(key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
619

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
626
627
628
629
   * 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
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
   */
  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...
646
  found_kernel_type:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
  	return ktype;
a8b17ed01   David Howells   KEYS: Do some sty...
648
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649

59e6b9c11   Bryan Schumaker   Created a functio...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
  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
669
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
670
   * Unlock a key type locked by key_type_lookup().
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
672
673
674
   */
  void key_type_put(struct key_type *ktype)
  {
  	up_read(&key_types_sem);
a8b17ed01   David Howells   KEYS: Do some sty...
675
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
676

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

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

973c9f4f4   David Howells   KEYS: Fix up comm...
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
  /**
   * 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
741
   */
664cceb00   David Howells   [PATCH] Keys: Add...
742
743
744
745
746
  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...
747
  			       key_perm_t perm,
7e047ef5f   David Howells   [PATCH] keys: sor...
748
  			       unsigned long flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
  {
16feef434   David Howells   KEYS: Consolidate...
750
751
752
  	struct keyring_index_key index_key = {
  		.description	= description,
  	};
cf7f601c0   David Howells   KEYS: Add payload...
753
  	struct key_preparsed_payload prep;
b2a4df200   David Howells   KEYS: Expand the ...
754
  	struct assoc_array_edit *edit;
d84f4f992   David Howells   CRED: Inaugurate ...
755
  	const struct cred *cred = current_cred();
664cceb00   David Howells   [PATCH] Keys: Add...
756
  	struct key *keyring, *key = NULL;
664cceb00   David Howells   [PATCH] Keys: Add...
757
  	key_ref_t key_ref;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
758
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
  	/* look up the key type to see if it's one of the registered kernel
  	 * types */
16feef434   David Howells   KEYS: Consolidate...
761
762
  	index_key.type = key_type_lookup(type);
  	if (IS_ERR(index_key.type)) {
664cceb00   David Howells   [PATCH] Keys: Add...
763
  		key_ref = ERR_PTR(-ENODEV);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
764
765
  		goto error;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
766
  	key_ref = ERR_PTR(-EINVAL);
c06cfb08b   David Howells   KEYS: Remove key_...
767
  	if (!index_key.type->instantiate ||
16feef434   David Howells   KEYS: Consolidate...
768
  	    (!index_key.description && !index_key.type->preparse))
cf7f601c0   David Howells   KEYS: Add payload...
769
  		goto error_put_type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770

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

008643b86   David Howells   KEYS: Add a 'trus...
798
799
800
801
  	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 ...
802
  	ret = __key_link_begin(keyring, &index_key, &edit);
cf7f601c0   David Howells   KEYS: Add payload...
803
804
805
806
  	if (ret < 0) {
  		key_ref = ERR_PTR(ret);
  		goto error_free_prep;
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
807
808
809
  
  	/* if we're going to allocate a new key, we're going to have
  	 * to modify the keyring */
f5895943d   David Howells   KEYS: Move the fl...
810
  	ret = key_permission(keyring_ref, KEY_NEED_WRITE);
29db91906   David Howells   [PATCH] Keys: Add...
811
812
  	if (ret < 0) {
  		key_ref = ERR_PTR(ret);
cf7f601c0   David Howells   KEYS: Add payload...
813
  		goto error_link_end;
29db91906   David Howells   [PATCH] Keys: Add...
814
  	}
664cceb00   David Howells   [PATCH] Keys: Add...
815

1d9b7d97d   David Howells   [PATCH] Keys: Rep...
816
817
818
  	/* 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
819
  	 */
16feef434   David Howells   KEYS: Consolidate...
820
  	if (index_key.type->update) {
b2a4df200   David Howells   KEYS: Expand the ...
821
822
  		key_ref = find_key_to_update(keyring_ref, &index_key);
  		if (key_ref)
1d9b7d97d   David Howells   [PATCH] Keys: Rep...
823
824
  			goto found_matching_key;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825

6b79ccb51   Arun Raghavan   keys: allow clien...
826
827
828
  	/* 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...
829
  		perm |= KEY_USR_VIEW;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
830

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

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

cf7f601c0   David Howells   KEYS: Add payload...
871
872
  	key_ref = __key_update(key_ref, &prep);
  	goto error_free_prep;
a8b17ed01   David Howells   KEYS: Do some sty...
873
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
  EXPORT_SYMBOL(key_create_or_update);
973c9f4f4   David Howells   KEYS: Fix up comm...
875
876
877
878
879
880
881
882
883
884
885
886
  /**
   * 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
887
   */
664cceb00   David Howells   [PATCH] Keys: Add...
888
  int key_update(key_ref_t key_ref, const void *payload, size_t plen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
  {
cf7f601c0   David Howells   KEYS: Add payload...
890
  	struct key_preparsed_payload prep;
664cceb00   David Howells   [PATCH] Keys: Add...
891
  	struct key *key = key_ref_to_ptr(key_ref);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
892
893
894
895
896
  	int ret;
  
  	key_check(key);
  
  	/* the key must be writable */
f5895943d   David Howells   KEYS: Move the fl...
897
  	ret = key_permission(key_ref, KEY_NEED_WRITE);
29db91906   David Howells   [PATCH] Keys: Add...
898
  	if (ret < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
899
900
901
902
  		goto error;
  
  	/* attempt to update it if supported */
  	ret = -EOPNOTSUPP;
cf7f601c0   David Howells   KEYS: Add payload...
903
904
  	if (!key->type->update)
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
905

cf7f601c0   David Howells   KEYS: Add payload...
906
907
908
909
  	memset(&prep, 0, sizeof(prep));
  	prep.data = payload;
  	prep.datalen = plen;
  	prep.quotalen = key->type->def_datalen;
7dfa0ca6a   David Howells   KEYS: Allow expir...
910
  	prep.expiry = TIME_T_MAX;
cf7f601c0   David Howells   KEYS: Add payload...
911
912
913
914
  	if (key->type->preparse) {
  		ret = key->type->preparse(&prep);
  		if (ret < 0)
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
  	}
cf7f601c0   David Howells   KEYS: Add payload...
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);
4d8c0250b   David Howells   KEYS: Call ->free...
924
  error:
cf7f601c0   David Howells   KEYS: Add payload...
925
926
  	if (key->type->preparse)
  		key->type->free_preparse(&prep);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
927
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
928
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
  EXPORT_SYMBOL(key_update);
973c9f4f4   David Howells   KEYS: Fix up comm...
930
931
932
933
934
935
936
937
  /**
   * 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
938
939
940
   */
  void key_revoke(struct key *key)
  {
5d135440f   David Howells   KEYS: Add garbage...
941
942
  	struct timespec now;
  	time_t time;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
  	key_check(key);
76181c134   David Howells   KEYS: Make reques...
944
945
946
947
948
949
950
951
  	/* 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...
952
  		key->type->revoke(key);
5d135440f   David Howells   KEYS: Add garbage...
953
954
955
956
957
  	/* 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...
958
  		key_schedule_gc(key->revoked_at + key_gc_delay);
5d135440f   David Howells   KEYS: Add garbage...
959
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
  	up_write(&key->sem);
a8b17ed01   David Howells   KEYS: Do some sty...
961
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
  EXPORT_SYMBOL(key_revoke);
973c9f4f4   David Howells   KEYS: Fix up comm...
963
  /**
fd75815f7   David Howells   KEYS: Add invalid...
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
   * 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);
  
  /**
6a09d17bb   David Howells   KEYS: Provide a g...
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
   * generic_key_instantiate - Simple instantiation of a key from preparsed data
   * @key: The key to be instantiated
   * @prep: The preparsed data to load.
   *
   * Instantiate a key from preparsed data.  We assume we can just copy the data
   * in directly and clear the old pointers.
   *
   * This can be pointed to directly by the key type instantiate op pointer.
   */
  int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
  {
  	int ret;
  
  	pr_devel("==>%s()
  ", __func__);
  
  	ret = key_payload_reserve(key, prep->quotalen);
  	if (ret == 0) {
  		key->type_data.p[0] = prep->type_data[0];
  		key->type_data.p[1] = prep->type_data[1];
fc7c70e0b   David Howells   KEYS: struct key_...
1006
1007
  		rcu_assign_keypointer(key, prep->payload[0]);
  		key->payload.data2[1] = prep->payload[1];
6a09d17bb   David Howells   KEYS: Provide a g...
1008
1009
  		prep->type_data[0] = NULL;
  		prep->type_data[1] = NULL;
fc7c70e0b   David Howells   KEYS: struct key_...
1010
1011
  		prep->payload[0] = NULL;
  		prep->payload[1] = NULL;
6a09d17bb   David Howells   KEYS: Provide a g...
1012
1013
1014
1015
1016
1017
1018
1019
  	}
  	pr_devel("<==%s() = %d
  ", __func__, ret);
  	return ret;
  }
  EXPORT_SYMBOL(generic_key_instantiate);
  
  /**
973c9f4f4   David Howells   KEYS: Fix up comm...
1020
1021
1022
1023
1024
1025
   * 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
1026
1027
1028
1029
1030
   */
  int register_key_type(struct key_type *ktype)
  {
  	struct key_type *p;
  	int ret;
7845bc396   David Howells   KEYS: Give key ty...
1031
  	memset(&ktype->lock_class, 0, sizeof(ktype->lock_class));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
  	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...
1043
1044
1045
  
  	pr_notice("Key type %s registered
  ", ktype->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1046
  	ret = 0;
973c9f4f4   David Howells   KEYS: Fix up comm...
1047
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
1049
  	up_write(&key_types_sem);
  	return ret;
a8b17ed01   David Howells   KEYS: Do some sty...
1050
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
  EXPORT_SYMBOL(register_key_type);
973c9f4f4   David Howells   KEYS: Fix up comm...
1052
1053
1054
1055
1056
1057
1058
  /**
   * 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
1059
1060
1061
   */
  void unregister_key_type(struct key_type *ktype)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  	down_write(&key_types_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1063
  	list_del_init(&ktype->link);
0c061b570   David Howells   KEYS: Correctly d...
1064
1065
  	downgrade_write(&key_types_sem);
  	key_gc_keytype(ktype);
1eb1bcf5b   David Howells   KEYS: Announce ke...
1066
1067
  	pr_notice("Key type %s unregistered
  ", ktype->name);
0c061b570   David Howells   KEYS: Correctly d...
1068
  	up_read(&key_types_sem);
a8b17ed01   David Howells   KEYS: Do some sty...
1069
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
  EXPORT_SYMBOL(unregister_key_type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1071
  /*
973c9f4f4   David Howells   KEYS: Fix up comm...
1072
   * Initialise the key management state.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073
1074
1075
1076
1077
   */
  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...
1078
  			0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1079
1080
1081
1082
1083
  
  	/* 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...
1084
  	list_add_tail(&key_type_logon.link, &key_types_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1085
1086
1087
1088
1089
1090
1091
1092
  
  	/* 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...
1093
  }