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