]>
Commit | Line | Data |
---|---|---|
964f3b3b DH |
1 | /* Asymmetric public-key cryptography key type |
2 | * | |
3 | * See Documentation/security/asymmetric-keys.txt | |
4 | * | |
5 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | |
6 | * Written by David Howells (dhowells@redhat.com) | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public Licence | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the Licence, or (at your option) any later version. | |
12 | */ | |
13 | #include <keys/asymmetric-subtype.h> | |
46c6f177 | 14 | #include <keys/asymmetric-parser.h> |
99db4435 | 15 | #include <crypto/public_key.h> |
964f3b3b DH |
16 | #include <linux/seq_file.h> |
17 | #include <linux/module.h> | |
18 | #include <linux/slab.h> | |
7901c1a8 | 19 | #include <linux/ctype.h> |
97d3aa0f | 20 | #include <keys/system_keyring.h> |
964f3b3b DH |
21 | #include "asymmetric_keys.h" |
22 | ||
23 | MODULE_LICENSE("GPL"); | |
24 | ||
99db4435 DH |
25 | const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = { |
26 | [VERIFYING_MODULE_SIGNATURE] = "mod sig", | |
27 | [VERIFYING_FIRMWARE_SIGNATURE] = "firmware sig", | |
28 | [VERIFYING_KEXEC_PE_SIGNATURE] = "kexec PE sig", | |
29 | [VERIFYING_KEY_SIGNATURE] = "key sig", | |
30 | [VERIFYING_KEY_SELF_SIGNATURE] = "key self sig", | |
31 | [VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig", | |
32 | }; | |
33 | EXPORT_SYMBOL_GPL(key_being_used_for); | |
34 | ||
46c6f177 DH |
35 | static LIST_HEAD(asymmetric_key_parsers); |
36 | static DECLARE_RWSEM(asymmetric_key_parsers_sem); | |
37 | ||
983023f2 | 38 | /** |
9eb02989 | 39 | * find_asymmetric_key - Find a key by ID. |
983023f2 | 40 | * @keyring: The keys to search. |
9eb02989 DH |
41 | * @id_0: The first ID to look for or NULL. |
42 | * @id_1: The second ID to look for or NULL. | |
983023f2 DH |
43 | * @partial: Use partial match if true, exact if false. |
44 | * | |
45 | * Find a key in the given keyring by identifier. The preferred identifier is | |
9eb02989 DH |
46 | * the id_0 and the fallback identifier is the id_1. If both are given, the |
47 | * lookup is by the former, but the latter must also match. | |
983023f2 | 48 | */ |
9eb02989 DH |
49 | struct key *find_asymmetric_key(struct key *keyring, |
50 | const struct asymmetric_key_id *id_0, | |
51 | const struct asymmetric_key_id *id_1, | |
52 | bool partial) | |
983023f2 DH |
53 | { |
54 | struct key *key; | |
55 | key_ref_t ref; | |
56 | const char *lookup; | |
57 | char *req, *p; | |
58 | int len; | |
59 | ||
b3811d36 CYL |
60 | BUG_ON(!id_0 && !id_1); |
61 | ||
9eb02989 DH |
62 | if (id_0) { |
63 | lookup = id_0->data; | |
64 | len = id_0->len; | |
983023f2 | 65 | } else { |
9eb02989 DH |
66 | lookup = id_1->data; |
67 | len = id_1->len; | |
983023f2 DH |
68 | } |
69 | ||
70 | /* Construct an identifier "id:<keyid>". */ | |
71 | p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); | |
72 | if (!req) | |
73 | return ERR_PTR(-ENOMEM); | |
74 | ||
75 | if (partial) { | |
76 | *p++ = 'i'; | |
77 | *p++ = 'd'; | |
78 | } else { | |
79 | *p++ = 'e'; | |
80 | *p++ = 'x'; | |
81 | } | |
82 | *p++ = ':'; | |
83 | p = bin2hex(p, lookup, len); | |
84 | *p = 0; | |
85 | ||
86 | pr_debug("Look up: \"%s\"\n", req); | |
87 | ||
88 | ref = keyring_search(make_key_ref(keyring, 1), | |
89 | &key_type_asymmetric, req); | |
90 | if (IS_ERR(ref)) | |
91 | pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); | |
92 | kfree(req); | |
93 | ||
94 | if (IS_ERR(ref)) { | |
95 | switch (PTR_ERR(ref)) { | |
96 | /* Hide some search errors */ | |
97 | case -EACCES: | |
98 | case -ENOTDIR: | |
99 | case -EAGAIN: | |
100 | return ERR_PTR(-ENOKEY); | |
101 | default: | |
102 | return ERR_CAST(ref); | |
103 | } | |
104 | } | |
105 | ||
106 | key = key_ref_to_ptr(ref); | |
9eb02989 | 107 | if (id_0 && id_1) { |
983023f2 | 108 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
9eb02989 | 109 | |
6a6d2a77 | 110 | if (!kids->id[1]) { |
9eb02989 | 111 | pr_debug("First ID matches, but second is missing\n"); |
983023f2 DH |
112 | goto reject; |
113 | } | |
9eb02989 DH |
114 | if (!asymmetric_key_id_same(id_1, kids->id[1])) { |
115 | pr_debug("First ID matches, but second does not\n"); | |
983023f2 DH |
116 | goto reject; |
117 | } | |
118 | } | |
119 | ||
120 | pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); | |
121 | return key; | |
122 | ||
123 | reject: | |
124 | key_put(key); | |
125 | return ERR_PTR(-EKEYREJECTED); | |
126 | } | |
9eb02989 | 127 | EXPORT_SYMBOL_GPL(find_asymmetric_key); |
983023f2 | 128 | |
7901c1a8 DH |
129 | /** |
130 | * asymmetric_key_generate_id: Construct an asymmetric key ID | |
131 | * @val_1: First binary blob | |
132 | * @len_1: Length of first binary blob | |
133 | * @val_2: Second binary blob | |
134 | * @len_2: Length of second binary blob | |
135 | * | |
136 | * Construct an asymmetric key ID from a pair of binary blobs. | |
137 | */ | |
138 | struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, | |
139 | size_t len_1, | |
140 | const void *val_2, | |
141 | size_t len_2) | |
142 | { | |
143 | struct asymmetric_key_id *kid; | |
144 | ||
145 | kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2, | |
146 | GFP_KERNEL); | |
147 | if (!kid) | |
148 | return ERR_PTR(-ENOMEM); | |
149 | kid->len = len_1 + len_2; | |
150 | memcpy(kid->data, val_1, len_1); | |
151 | memcpy(kid->data + len_1, val_2, len_2); | |
152 | return kid; | |
153 | } | |
154 | EXPORT_SYMBOL_GPL(asymmetric_key_generate_id); | |
155 | ||
156 | /** | |
157 | * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same. | |
158 | * @kid_1, @kid_2: The key IDs to compare | |
159 | */ | |
160 | bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, | |
161 | const struct asymmetric_key_id *kid2) | |
162 | { | |
163 | if (!kid1 || !kid2) | |
164 | return false; | |
165 | if (kid1->len != kid2->len) | |
166 | return false; | |
167 | return memcmp(kid1->data, kid2->data, kid1->len) == 0; | |
168 | } | |
169 | EXPORT_SYMBOL_GPL(asymmetric_key_id_same); | |
170 | ||
f1b731db DK |
171 | /** |
172 | * asymmetric_key_id_partial - Return true if two asymmetric keys IDs | |
173 | * partially match | |
174 | * @kid_1, @kid_2: The key IDs to compare | |
175 | */ | |
176 | bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1, | |
177 | const struct asymmetric_key_id *kid2) | |
178 | { | |
179 | if (!kid1 || !kid2) | |
180 | return false; | |
181 | if (kid1->len < kid2->len) | |
182 | return false; | |
183 | return memcmp(kid1->data + (kid1->len - kid2->len), | |
184 | kid2->data, kid2->len) == 0; | |
185 | } | |
186 | EXPORT_SYMBOL_GPL(asymmetric_key_id_partial); | |
187 | ||
7901c1a8 DH |
188 | /** |
189 | * asymmetric_match_key_ids - Search asymmetric key IDs | |
190 | * @kids: The list of key IDs to check | |
191 | * @match_id: The key ID we're looking for | |
f1b731db | 192 | * @match: The match function to use |
7901c1a8 | 193 | */ |
f1b731db DK |
194 | static bool asymmetric_match_key_ids( |
195 | const struct asymmetric_key_ids *kids, | |
196 | const struct asymmetric_key_id *match_id, | |
197 | bool (*match)(const struct asymmetric_key_id *kid1, | |
198 | const struct asymmetric_key_id *kid2)) | |
7901c1a8 | 199 | { |
f1b731db DK |
200 | int i; |
201 | ||
7901c1a8 DH |
202 | if (!kids || !match_id) |
203 | return false; | |
f1b731db DK |
204 | for (i = 0; i < ARRAY_SIZE(kids->id); i++) |
205 | if (match(kids->id[i], match_id)) | |
206 | return true; | |
7901c1a8 DH |
207 | return false; |
208 | } | |
7901c1a8 | 209 | |
f2b3dee4 MZ |
210 | /* helper function can be called directly with pre-allocated memory */ |
211 | inline int __asymmetric_key_hex_to_key_id(const char *id, | |
212 | struct asymmetric_key_id *match_id, | |
213 | size_t hexlen) | |
214 | { | |
215 | match_id->len = hexlen; | |
216 | return hex2bin(match_id->data, id, hexlen); | |
217 | } | |
218 | ||
7901c1a8 DH |
219 | /** |
220 | * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID. | |
221 | * @id: The ID as a hex string. | |
222 | */ | |
223 | struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) | |
224 | { | |
225 | struct asymmetric_key_id *match_id; | |
f2b3dee4 | 226 | size_t asciihexlen; |
d1ac5540 | 227 | int ret; |
7901c1a8 DH |
228 | |
229 | if (!*id) | |
230 | return ERR_PTR(-EINVAL); | |
f2b3dee4 MZ |
231 | asciihexlen = strlen(id); |
232 | if (asciihexlen & 1) | |
7901c1a8 DH |
233 | return ERR_PTR(-EINVAL); |
234 | ||
f2b3dee4 | 235 | match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2, |
7901c1a8 DH |
236 | GFP_KERNEL); |
237 | if (!match_id) | |
238 | return ERR_PTR(-ENOMEM); | |
f2b3dee4 | 239 | ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2); |
d1ac5540 DH |
240 | if (ret < 0) { |
241 | kfree(match_id); | |
242 | return ERR_PTR(-EINVAL); | |
243 | } | |
7901c1a8 DH |
244 | return match_id; |
245 | } | |
246 | ||
b3426827 | 247 | /* |
f1b731db | 248 | * Match asymmetric keys by an exact match on an ID. |
964f3b3b | 249 | */ |
0c903ab6 DH |
250 | static bool asymmetric_key_cmp(const struct key *key, |
251 | const struct key_match_data *match_data) | |
964f3b3b | 252 | { |
46963b77 DH |
253 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
254 | const struct asymmetric_key_id *match_id = match_data->preparsed; | |
964f3b3b | 255 | |
f1b731db DK |
256 | return asymmetric_match_key_ids(kids, match_id, |
257 | asymmetric_key_id_same); | |
258 | } | |
259 | ||
260 | /* | |
261 | * Match asymmetric keys by a partial match on an IDs. | |
262 | */ | |
263 | static bool asymmetric_key_cmp_partial(const struct key *key, | |
264 | const struct key_match_data *match_data) | |
265 | { | |
266 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); | |
267 | const struct asymmetric_key_id *match_id = match_data->preparsed; | |
268 | ||
269 | return asymmetric_match_key_ids(kids, match_id, | |
270 | asymmetric_key_id_partial); | |
964f3b3b DH |
271 | } |
272 | ||
46291959 DH |
273 | /* |
274 | * Preparse the match criterion. If we don't set lookup_type and cmp, | |
275 | * the default will be an exact match on the key description. | |
276 | * | |
277 | * There are some specifiers for matching key IDs rather than by the key | |
278 | * description: | |
279 | * | |
f1b731db DK |
280 | * "id:<id>" - find a key by partial match on any available ID |
281 | * "ex:<id>" - find a key by exact match on any available ID | |
46291959 DH |
282 | * |
283 | * These have to be searched by iteration rather than by direct lookup because | |
284 | * the key is hashed according to its description. | |
285 | */ | |
286 | static int asymmetric_key_match_preparse(struct key_match_data *match_data) | |
287 | { | |
46963b77 DH |
288 | struct asymmetric_key_id *match_id; |
289 | const char *spec = match_data->raw_data; | |
290 | const char *id; | |
f1b731db DK |
291 | bool (*cmp)(const struct key *, const struct key_match_data *) = |
292 | asymmetric_key_cmp; | |
46963b77 DH |
293 | |
294 | if (!spec || !*spec) | |
295 | return -EINVAL; | |
296 | if (spec[0] == 'i' && | |
297 | spec[1] == 'd' && | |
298 | spec[2] == ':') { | |
299 | id = spec + 3; | |
f1b731db DK |
300 | cmp = asymmetric_key_cmp_partial; |
301 | } else if (spec[0] == 'e' && | |
302 | spec[1] == 'x' && | |
303 | spec[2] == ':') { | |
304 | id = spec + 3; | |
46963b77 DH |
305 | } else { |
306 | goto default_match; | |
307 | } | |
308 | ||
309 | match_id = asymmetric_key_hex_to_key_id(id); | |
40b50e80 DK |
310 | if (IS_ERR(match_id)) |
311 | return PTR_ERR(match_id); | |
46963b77 DH |
312 | |
313 | match_data->preparsed = match_id; | |
f1b731db | 314 | match_data->cmp = cmp; |
46963b77 DH |
315 | match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; |
316 | return 0; | |
317 | ||
318 | default_match: | |
46291959 DH |
319 | return 0; |
320 | } | |
321 | ||
322 | /* | |
323 | * Free the preparsed the match criterion. | |
324 | */ | |
325 | static void asymmetric_key_match_free(struct key_match_data *match_data) | |
326 | { | |
46963b77 | 327 | kfree(match_data->preparsed); |
46291959 DH |
328 | } |
329 | ||
964f3b3b DH |
330 | /* |
331 | * Describe the asymmetric key | |
332 | */ | |
333 | static void asymmetric_key_describe(const struct key *key, struct seq_file *m) | |
334 | { | |
335 | const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
46963b77 DH |
336 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
337 | const struct asymmetric_key_id *kid; | |
338 | const unsigned char *p; | |
339 | int n; | |
964f3b3b DH |
340 | |
341 | seq_puts(m, key->description); | |
342 | ||
343 | if (subtype) { | |
344 | seq_puts(m, ": "); | |
345 | subtype->describe(key, m); | |
346 | ||
d4016589 DK |
347 | if (kids && kids->id[1]) { |
348 | kid = kids->id[1]; | |
964f3b3b | 349 | seq_putc(m, ' '); |
46963b77 DH |
350 | n = kid->len; |
351 | p = kid->data; | |
d4016589 DK |
352 | if (n > 4) { |
353 | p += n - 4; | |
354 | n = 4; | |
46963b77 DH |
355 | } |
356 | seq_printf(m, "%*phN", n, p); | |
964f3b3b DH |
357 | } |
358 | ||
359 | seq_puts(m, " ["); | |
360 | /* put something here to indicate the key's capabilities */ | |
361 | seq_putc(m, ']'); | |
362 | } | |
363 | } | |
364 | ||
46c6f177 DH |
365 | /* |
366 | * Preparse a asymmetric payload to get format the contents appropriately for the | |
367 | * internal payload to cut down on the number of scans of the data performed. | |
368 | * | |
369 | * We also generate a proposed description from the contents of the key that | |
370 | * can be used to name the key if the user doesn't want to provide one. | |
371 | */ | |
372 | static int asymmetric_key_preparse(struct key_preparsed_payload *prep) | |
373 | { | |
374 | struct asymmetric_key_parser *parser; | |
375 | int ret; | |
376 | ||
377 | pr_devel("==>%s()\n", __func__); | |
378 | ||
379 | if (prep->datalen == 0) | |
380 | return -EINVAL; | |
381 | ||
382 | down_read(&asymmetric_key_parsers_sem); | |
383 | ||
384 | ret = -EBADMSG; | |
385 | list_for_each_entry(parser, &asymmetric_key_parsers, link) { | |
386 | pr_debug("Trying parser '%s'\n", parser->name); | |
387 | ||
388 | ret = parser->parse(prep); | |
389 | if (ret != -EBADMSG) { | |
390 | pr_debug("Parser recognised the format (ret %d)\n", | |
391 | ret); | |
392 | break; | |
393 | } | |
394 | } | |
395 | ||
396 | up_read(&asymmetric_key_parsers_sem); | |
397 | pr_devel("<==%s() = %d\n", __func__, ret); | |
398 | return ret; | |
399 | } | |
400 | ||
146aa8b1 DH |
401 | /* |
402 | * Clean up the key ID list | |
403 | */ | |
404 | static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids) | |
405 | { | |
406 | int i; | |
407 | ||
408 | if (kids) { | |
409 | for (i = 0; i < ARRAY_SIZE(kids->id); i++) | |
410 | kfree(kids->id[i]); | |
411 | kfree(kids); | |
412 | } | |
413 | } | |
414 | ||
46c6f177 DH |
415 | /* |
416 | * Clean up the preparse data | |
417 | */ | |
418 | static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) | |
419 | { | |
146aa8b1 DH |
420 | struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype]; |
421 | struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids]; | |
46c6f177 DH |
422 | |
423 | pr_devel("==>%s()\n", __func__); | |
424 | ||
425 | if (subtype) { | |
3b764563 DH |
426 | subtype->destroy(prep->payload.data[asym_crypto], |
427 | prep->payload.data[asym_auth]); | |
46c6f177 DH |
428 | module_put(subtype->owner); |
429 | } | |
146aa8b1 | 430 | asymmetric_key_free_kids(kids); |
46c6f177 DH |
431 | kfree(prep->description); |
432 | } | |
433 | ||
964f3b3b DH |
434 | /* |
435 | * dispose of the data dangling from the corpse of a asymmetric key | |
436 | */ | |
437 | static void asymmetric_key_destroy(struct key *key) | |
438 | { | |
439 | struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
146aa8b1 DH |
440 | struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; |
441 | void *data = key->payload.data[asym_crypto]; | |
3b764563 | 442 | void *auth = key->payload.data[asym_auth]; |
146aa8b1 DH |
443 | |
444 | key->payload.data[asym_crypto] = NULL; | |
445 | key->payload.data[asym_subtype] = NULL; | |
446 | key->payload.data[asym_key_ids] = NULL; | |
3b764563 | 447 | key->payload.data[asym_auth] = NULL; |
46963b77 | 448 | |
964f3b3b | 449 | if (subtype) { |
3b764563 | 450 | subtype->destroy(data, auth); |
964f3b3b | 451 | module_put(subtype->owner); |
964f3b3b | 452 | } |
46963b77 | 453 | |
146aa8b1 | 454 | asymmetric_key_free_kids(kids); |
964f3b3b DH |
455 | } |
456 | ||
97d3aa0f MM |
457 | static struct key_restriction *asymmetric_restriction_alloc( |
458 | key_restrict_link_func_t check, | |
459 | struct key *key) | |
460 | { | |
461 | struct key_restriction *keyres = | |
462 | kzalloc(sizeof(struct key_restriction), GFP_KERNEL); | |
463 | ||
464 | if (!keyres) | |
465 | return ERR_PTR(-ENOMEM); | |
466 | ||
467 | keyres->check = check; | |
468 | keyres->key = key; | |
469 | keyres->keytype = &key_type_asymmetric; | |
470 | ||
471 | return keyres; | |
472 | } | |
473 | ||
474 | /* | |
475 | * look up keyring restrict functions for asymmetric keys | |
476 | */ | |
477 | static struct key_restriction *asymmetric_lookup_restriction( | |
478 | const char *restriction) | |
479 | { | |
7e3c4d22 MM |
480 | char *restrict_method; |
481 | char *parse_buf; | |
482 | char *next; | |
483 | struct key_restriction *ret = ERR_PTR(-EINVAL); | |
484 | ||
97d3aa0f MM |
485 | if (strcmp("builtin_trusted", restriction) == 0) |
486 | return asymmetric_restriction_alloc( | |
487 | restrict_link_by_builtin_trusted, NULL); | |
488 | ||
489 | if (strcmp("builtin_and_secondary_trusted", restriction) == 0) | |
490 | return asymmetric_restriction_alloc( | |
491 | restrict_link_by_builtin_and_secondary_trusted, NULL); | |
492 | ||
7e3c4d22 MM |
493 | parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL); |
494 | if (!parse_buf) | |
495 | return ERR_PTR(-ENOMEM); | |
496 | ||
497 | next = parse_buf; | |
498 | restrict_method = strsep(&next, ":"); | |
499 | ||
500 | if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) { | |
8e323a02 | 501 | char *key_text; |
7e3c4d22 MM |
502 | key_serial_t serial; |
503 | struct key *key; | |
8e323a02 MM |
504 | key_restrict_link_func_t link_fn = |
505 | restrict_link_by_key_or_keyring; | |
506 | bool allow_null_key = false; | |
7e3c4d22 | 507 | |
8e323a02 MM |
508 | key_text = strsep(&next, ":"); |
509 | ||
510 | if (next) { | |
511 | if (strcmp(next, "chain") != 0) | |
512 | goto out; | |
513 | ||
514 | link_fn = restrict_link_by_key_or_keyring_chain; | |
515 | allow_null_key = true; | |
516 | } | |
7e3c4d22 | 517 | |
8e323a02 | 518 | if (kstrtos32(key_text, 0, &serial) < 0) |
7e3c4d22 | 519 | goto out; |
8e323a02 MM |
520 | |
521 | if ((serial == 0) && allow_null_key) { | |
522 | key = NULL; | |
523 | } else { | |
524 | key = key_lookup(serial); | |
525 | if (IS_ERR(key)) { | |
526 | ret = ERR_CAST(key); | |
527 | goto out; | |
528 | } | |
7e3c4d22 MM |
529 | } |
530 | ||
8e323a02 | 531 | ret = asymmetric_restriction_alloc(link_fn, key); |
7e3c4d22 MM |
532 | if (IS_ERR(ret)) |
533 | key_put(key); | |
534 | } | |
535 | ||
536 | out: | |
537 | kfree(parse_buf); | |
538 | return ret; | |
97d3aa0f MM |
539 | } |
540 | ||
964f3b3b | 541 | struct key_type key_type_asymmetric = { |
97d3aa0f MM |
542 | .name = "asymmetric", |
543 | .preparse = asymmetric_key_preparse, | |
544 | .free_preparse = asymmetric_key_free_preparse, | |
545 | .instantiate = generic_key_instantiate, | |
546 | .match_preparse = asymmetric_key_match_preparse, | |
547 | .match_free = asymmetric_key_match_free, | |
548 | .destroy = asymmetric_key_destroy, | |
549 | .describe = asymmetric_key_describe, | |
550 | .lookup_restriction = asymmetric_lookup_restriction, | |
964f3b3b DH |
551 | }; |
552 | EXPORT_SYMBOL_GPL(key_type_asymmetric); | |
553 | ||
46c6f177 DH |
554 | /** |
555 | * register_asymmetric_key_parser - Register a asymmetric key blob parser | |
556 | * @parser: The parser to register | |
557 | */ | |
558 | int register_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
559 | { | |
560 | struct asymmetric_key_parser *cursor; | |
561 | int ret; | |
562 | ||
563 | down_write(&asymmetric_key_parsers_sem); | |
564 | ||
565 | list_for_each_entry(cursor, &asymmetric_key_parsers, link) { | |
566 | if (strcmp(cursor->name, parser->name) == 0) { | |
567 | pr_err("Asymmetric key parser '%s' already registered\n", | |
568 | parser->name); | |
569 | ret = -EEXIST; | |
570 | goto out; | |
571 | } | |
572 | } | |
573 | ||
574 | list_add_tail(&parser->link, &asymmetric_key_parsers); | |
575 | ||
576 | pr_notice("Asymmetric key parser '%s' registered\n", parser->name); | |
577 | ret = 0; | |
578 | ||
579 | out: | |
580 | up_write(&asymmetric_key_parsers_sem); | |
581 | return ret; | |
582 | } | |
583 | EXPORT_SYMBOL_GPL(register_asymmetric_key_parser); | |
584 | ||
585 | /** | |
586 | * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser | |
587 | * @parser: The parser to unregister | |
588 | */ | |
589 | void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
590 | { | |
591 | down_write(&asymmetric_key_parsers_sem); | |
592 | list_del(&parser->link); | |
593 | up_write(&asymmetric_key_parsers_sem); | |
594 | ||
595 | pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name); | |
596 | } | |
597 | EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser); | |
598 | ||
964f3b3b DH |
599 | /* |
600 | * Module stuff | |
601 | */ | |
602 | static int __init asymmetric_key_init(void) | |
603 | { | |
604 | return register_key_type(&key_type_asymmetric); | |
605 | } | |
606 | ||
607 | static void __exit asymmetric_key_cleanup(void) | |
608 | { | |
609 | unregister_key_type(&key_type_asymmetric); | |
610 | } | |
611 | ||
612 | module_init(asymmetric_key_init); | |
613 | module_exit(asymmetric_key_cleanup); |