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