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