]>
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> |
964f3b3b DH |
20 | #include "asymmetric_keys.h" |
21 | ||
22 | MODULE_LICENSE("GPL"); | |
23 | ||
99db4435 DH |
24 | const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = { |
25 | [VERIFYING_MODULE_SIGNATURE] = "mod sig", | |
26 | [VERIFYING_FIRMWARE_SIGNATURE] = "firmware sig", | |
27 | [VERIFYING_KEXEC_PE_SIGNATURE] = "kexec PE sig", | |
28 | [VERIFYING_KEY_SIGNATURE] = "key sig", | |
29 | [VERIFYING_KEY_SELF_SIGNATURE] = "key self sig", | |
30 | [VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig", | |
31 | }; | |
32 | EXPORT_SYMBOL_GPL(key_being_used_for); | |
33 | ||
46c6f177 DH |
34 | static LIST_HEAD(asymmetric_key_parsers); |
35 | static DECLARE_RWSEM(asymmetric_key_parsers_sem); | |
36 | ||
983023f2 | 37 | /** |
9eb02989 | 38 | * find_asymmetric_key - Find a key by ID. |
983023f2 | 39 | * @keyring: The keys to search. |
9eb02989 DH |
40 | * @id_0: The first ID to look for or NULL. |
41 | * @id_1: The second ID to look for or NULL. | |
983023f2 DH |
42 | * @partial: Use partial match if true, exact if false. |
43 | * | |
44 | * Find a key in the given keyring by identifier. The preferred identifier is | |
9eb02989 DH |
45 | * the id_0 and the fallback identifier is the id_1. If both are given, the |
46 | * lookup is by the former, but the latter must also match. | |
983023f2 | 47 | */ |
9eb02989 DH |
48 | struct key *find_asymmetric_key(struct key *keyring, |
49 | const struct asymmetric_key_id *id_0, | |
50 | const struct asymmetric_key_id *id_1, | |
51 | bool partial) | |
983023f2 DH |
52 | { |
53 | struct key *key; | |
54 | key_ref_t ref; | |
55 | const char *lookup; | |
56 | char *req, *p; | |
57 | int len; | |
58 | ||
9eb02989 DH |
59 | if (id_0) { |
60 | lookup = id_0->data; | |
61 | len = id_0->len; | |
983023f2 | 62 | } else { |
9eb02989 DH |
63 | lookup = id_1->data; |
64 | len = id_1->len; | |
983023f2 DH |
65 | } |
66 | ||
67 | /* Construct an identifier "id:<keyid>". */ | |
68 | p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); | |
69 | if (!req) | |
70 | return ERR_PTR(-ENOMEM); | |
71 | ||
72 | if (partial) { | |
73 | *p++ = 'i'; | |
74 | *p++ = 'd'; | |
75 | } else { | |
76 | *p++ = 'e'; | |
77 | *p++ = 'x'; | |
78 | } | |
79 | *p++ = ':'; | |
80 | p = bin2hex(p, lookup, len); | |
81 | *p = 0; | |
82 | ||
83 | pr_debug("Look up: \"%s\"\n", req); | |
84 | ||
85 | ref = keyring_search(make_key_ref(keyring, 1), | |
86 | &key_type_asymmetric, req); | |
87 | if (IS_ERR(ref)) | |
88 | pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); | |
89 | kfree(req); | |
90 | ||
91 | if (IS_ERR(ref)) { | |
92 | 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 | return ERR_CAST(ref); | |
100 | } | |
101 | } | |
102 | ||
103 | key = key_ref_to_ptr(ref); | |
9eb02989 | 104 | if (id_0 && id_1) { |
983023f2 | 105 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
9eb02989 DH |
106 | |
107 | if (!kids->id[0]) { | |
108 | pr_debug("First ID matches, but second is missing\n"); | |
983023f2 DH |
109 | goto reject; |
110 | } | |
9eb02989 DH |
111 | if (!asymmetric_key_id_same(id_1, kids->id[1])) { |
112 | pr_debug("First ID matches, but second does not\n"); | |
983023f2 DH |
113 | goto reject; |
114 | } | |
115 | } | |
116 | ||
117 | pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); | |
118 | return key; | |
119 | ||
120 | reject: | |
121 | key_put(key); | |
122 | return ERR_PTR(-EKEYREJECTED); | |
123 | } | |
9eb02989 | 124 | EXPORT_SYMBOL_GPL(find_asymmetric_key); |
983023f2 | 125 | |
7901c1a8 DH |
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 | 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 | kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2, | |
143 | GFP_KERNEL); | |
144 | if (!kid) | |
145 | return ERR_PTR(-ENOMEM); | |
146 | kid->len = len_1 + len_2; | |
147 | memcpy(kid->data, val_1, len_1); | |
148 | memcpy(kid->data + len_1, val_2, len_2); | |
149 | 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 | bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, | |
158 | const struct asymmetric_key_id *kid2) | |
159 | { | |
160 | if (!kid1 || !kid2) | |
161 | return false; | |
162 | if (kid1->len != kid2->len) | |
163 | return false; | |
164 | return memcmp(kid1->data, kid2->data, kid1->len) == 0; | |
165 | } | |
166 | EXPORT_SYMBOL_GPL(asymmetric_key_id_same); | |
167 | ||
f1b731db DK |
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 | bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1, | |
174 | const struct asymmetric_key_id *kid2) | |
175 | { | |
176 | if (!kid1 || !kid2) | |
177 | return false; | |
178 | if (kid1->len < kid2->len) | |
179 | return false; | |
180 | return memcmp(kid1->data + (kid1->len - kid2->len), | |
181 | kid2->data, kid2->len) == 0; | |
182 | } | |
183 | EXPORT_SYMBOL_GPL(asymmetric_key_id_partial); | |
184 | ||
7901c1a8 DH |
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 | |
f1b731db | 189 | * @match: The match function to use |
7901c1a8 | 190 | */ |
f1b731db DK |
191 | 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)) | |
7901c1a8 | 196 | { |
f1b731db DK |
197 | int i; |
198 | ||
7901c1a8 DH |
199 | if (!kids || !match_id) |
200 | return false; | |
f1b731db DK |
201 | for (i = 0; i < ARRAY_SIZE(kids->id); i++) |
202 | if (match(kids->id[i], match_id)) | |
203 | return true; | |
7901c1a8 DH |
204 | return false; |
205 | } | |
7901c1a8 | 206 | |
f2b3dee4 MZ |
207 | /* helper function can be called directly with pre-allocated memory */ |
208 | inline int __asymmetric_key_hex_to_key_id(const char *id, | |
209 | struct asymmetric_key_id *match_id, | |
210 | size_t hexlen) | |
211 | { | |
212 | match_id->len = hexlen; | |
213 | return hex2bin(match_id->data, id, hexlen); | |
214 | } | |
215 | ||
7901c1a8 DH |
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 | struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) | |
221 | { | |
222 | struct asymmetric_key_id *match_id; | |
f2b3dee4 | 223 | size_t asciihexlen; |
d1ac5540 | 224 | int ret; |
7901c1a8 DH |
225 | |
226 | if (!*id) | |
227 | return ERR_PTR(-EINVAL); | |
f2b3dee4 MZ |
228 | asciihexlen = strlen(id); |
229 | if (asciihexlen & 1) | |
7901c1a8 DH |
230 | return ERR_PTR(-EINVAL); |
231 | ||
f2b3dee4 | 232 | match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2, |
7901c1a8 DH |
233 | GFP_KERNEL); |
234 | if (!match_id) | |
235 | return ERR_PTR(-ENOMEM); | |
f2b3dee4 | 236 | ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2); |
d1ac5540 DH |
237 | if (ret < 0) { |
238 | kfree(match_id); | |
239 | return ERR_PTR(-EINVAL); | |
240 | } | |
7901c1a8 DH |
241 | return match_id; |
242 | } | |
243 | ||
b3426827 | 244 | /* |
f1b731db | 245 | * Match asymmetric keys by an exact match on an ID. |
964f3b3b | 246 | */ |
0c903ab6 DH |
247 | static bool asymmetric_key_cmp(const struct key *key, |
248 | const struct key_match_data *match_data) | |
964f3b3b | 249 | { |
46963b77 DH |
250 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
251 | const struct asymmetric_key_id *match_id = match_data->preparsed; | |
964f3b3b | 252 | |
f1b731db DK |
253 | 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 | 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 | const struct asymmetric_key_id *match_id = match_data->preparsed; | |
265 | ||
266 | return asymmetric_match_key_ids(kids, match_id, | |
267 | asymmetric_key_id_partial); | |
964f3b3b DH |
268 | } |
269 | ||
46291959 DH |
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 | * | |
f1b731db DK |
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 | |
46291959 DH |
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 | static int asymmetric_key_match_preparse(struct key_match_data *match_data) | |
284 | { | |
46963b77 DH |
285 | struct asymmetric_key_id *match_id; |
286 | const char *spec = match_data->raw_data; | |
287 | const char *id; | |
f1b731db DK |
288 | bool (*cmp)(const struct key *, const struct key_match_data *) = |
289 | asymmetric_key_cmp; | |
46963b77 DH |
290 | |
291 | if (!spec || !*spec) | |
292 | return -EINVAL; | |
293 | if (spec[0] == 'i' && | |
294 | spec[1] == 'd' && | |
295 | spec[2] == ':') { | |
296 | id = spec + 3; | |
f1b731db DK |
297 | cmp = asymmetric_key_cmp_partial; |
298 | } else if (spec[0] == 'e' && | |
299 | spec[1] == 'x' && | |
300 | spec[2] == ':') { | |
301 | id = spec + 3; | |
46963b77 DH |
302 | } else { |
303 | goto default_match; | |
304 | } | |
305 | ||
306 | match_id = asymmetric_key_hex_to_key_id(id); | |
40b50e80 DK |
307 | if (IS_ERR(match_id)) |
308 | return PTR_ERR(match_id); | |
46963b77 DH |
309 | |
310 | match_data->preparsed = match_id; | |
f1b731db | 311 | match_data->cmp = cmp; |
46963b77 DH |
312 | match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; |
313 | return 0; | |
314 | ||
315 | default_match: | |
46291959 DH |
316 | return 0; |
317 | } | |
318 | ||
319 | /* | |
320 | * Free the preparsed the match criterion. | |
321 | */ | |
322 | static void asymmetric_key_match_free(struct key_match_data *match_data) | |
323 | { | |
46963b77 | 324 | kfree(match_data->preparsed); |
46291959 DH |
325 | } |
326 | ||
964f3b3b DH |
327 | /* |
328 | * Describe the asymmetric key | |
329 | */ | |
330 | 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); | |
46963b77 DH |
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; | |
964f3b3b DH |
337 | |
338 | seq_puts(m, key->description); | |
339 | ||
340 | if (subtype) { | |
341 | seq_puts(m, ": "); | |
342 | subtype->describe(key, m); | |
343 | ||
d4016589 DK |
344 | if (kids && kids->id[1]) { |
345 | kid = kids->id[1]; | |
964f3b3b | 346 | seq_putc(m, ' '); |
46963b77 DH |
347 | n = kid->len; |
348 | p = kid->data; | |
d4016589 DK |
349 | if (n > 4) { |
350 | p += n - 4; | |
351 | n = 4; | |
46963b77 DH |
352 | } |
353 | seq_printf(m, "%*phN", n, p); | |
964f3b3b DH |
354 | } |
355 | ||
356 | seq_puts(m, " ["); | |
357 | /* put something here to indicate the key's capabilities */ | |
358 | seq_putc(m, ']'); | |
359 | } | |
360 | } | |
361 | ||
46c6f177 DH |
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 | 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 | if (prep->datalen == 0) | |
377 | return -EINVAL; | |
378 | ||
379 | down_read(&asymmetric_key_parsers_sem); | |
380 | ||
381 | ret = -EBADMSG; | |
382 | list_for_each_entry(parser, &asymmetric_key_parsers, link) { | |
383 | pr_debug("Trying parser '%s'\n", parser->name); | |
384 | ||
385 | ret = parser->parse(prep); | |
386 | if (ret != -EBADMSG) { | |
387 | pr_debug("Parser recognised the format (ret %d)\n", | |
388 | ret); | |
389 | break; | |
390 | } | |
391 | } | |
392 | ||
393 | up_read(&asymmetric_key_parsers_sem); | |
394 | pr_devel("<==%s() = %d\n", __func__, ret); | |
395 | return ret; | |
396 | } | |
397 | ||
146aa8b1 DH |
398 | /* |
399 | * Clean up the key ID list | |
400 | */ | |
401 | static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids) | |
402 | { | |
403 | int i; | |
404 | ||
405 | if (kids) { | |
406 | for (i = 0; i < ARRAY_SIZE(kids->id); i++) | |
407 | kfree(kids->id[i]); | |
408 | kfree(kids); | |
409 | } | |
410 | } | |
411 | ||
46c6f177 DH |
412 | /* |
413 | * Clean up the preparse data | |
414 | */ | |
415 | static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) | |
416 | { | |
146aa8b1 DH |
417 | struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype]; |
418 | struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids]; | |
46c6f177 DH |
419 | |
420 | pr_devel("==>%s()\n", __func__); | |
421 | ||
422 | if (subtype) { | |
3b764563 DH |
423 | subtype->destroy(prep->payload.data[asym_crypto], |
424 | prep->payload.data[asym_auth]); | |
46c6f177 DH |
425 | module_put(subtype->owner); |
426 | } | |
146aa8b1 | 427 | asymmetric_key_free_kids(kids); |
46c6f177 DH |
428 | kfree(prep->description); |
429 | } | |
430 | ||
964f3b3b DH |
431 | /* |
432 | * dispose of the data dangling from the corpse of a asymmetric key | |
433 | */ | |
434 | static void asymmetric_key_destroy(struct key *key) | |
435 | { | |
436 | struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
146aa8b1 DH |
437 | struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; |
438 | void *data = key->payload.data[asym_crypto]; | |
3b764563 | 439 | void *auth = key->payload.data[asym_auth]; |
146aa8b1 DH |
440 | |
441 | key->payload.data[asym_crypto] = NULL; | |
442 | key->payload.data[asym_subtype] = NULL; | |
443 | key->payload.data[asym_key_ids] = NULL; | |
3b764563 | 444 | key->payload.data[asym_auth] = NULL; |
46963b77 | 445 | |
964f3b3b | 446 | if (subtype) { |
3b764563 | 447 | subtype->destroy(data, auth); |
964f3b3b | 448 | module_put(subtype->owner); |
964f3b3b | 449 | } |
46963b77 | 450 | |
146aa8b1 | 451 | asymmetric_key_free_kids(kids); |
964f3b3b DH |
452 | } |
453 | ||
454 | struct key_type key_type_asymmetric = { | |
455 | .name = "asymmetric", | |
46c6f177 DH |
456 | .preparse = asymmetric_key_preparse, |
457 | .free_preparse = asymmetric_key_free_preparse, | |
6a09d17b | 458 | .instantiate = generic_key_instantiate, |
46291959 | 459 | .match_preparse = asymmetric_key_match_preparse, |
46291959 | 460 | .match_free = asymmetric_key_match_free, |
964f3b3b DH |
461 | .destroy = asymmetric_key_destroy, |
462 | .describe = asymmetric_key_describe, | |
463 | }; | |
464 | EXPORT_SYMBOL_GPL(key_type_asymmetric); | |
465 | ||
46c6f177 DH |
466 | /** |
467 | * register_asymmetric_key_parser - Register a asymmetric key blob parser | |
468 | * @parser: The parser to register | |
469 | */ | |
470 | int register_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
471 | { | |
472 | struct asymmetric_key_parser *cursor; | |
473 | int ret; | |
474 | ||
475 | down_write(&asymmetric_key_parsers_sem); | |
476 | ||
477 | list_for_each_entry(cursor, &asymmetric_key_parsers, link) { | |
478 | if (strcmp(cursor->name, parser->name) == 0) { | |
479 | pr_err("Asymmetric key parser '%s' already registered\n", | |
480 | parser->name); | |
481 | ret = -EEXIST; | |
482 | goto out; | |
483 | } | |
484 | } | |
485 | ||
486 | list_add_tail(&parser->link, &asymmetric_key_parsers); | |
487 | ||
488 | pr_notice("Asymmetric key parser '%s' registered\n", parser->name); | |
489 | ret = 0; | |
490 | ||
491 | out: | |
492 | up_write(&asymmetric_key_parsers_sem); | |
493 | return ret; | |
494 | } | |
495 | EXPORT_SYMBOL_GPL(register_asymmetric_key_parser); | |
496 | ||
497 | /** | |
498 | * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser | |
499 | * @parser: The parser to unregister | |
500 | */ | |
501 | void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
502 | { | |
503 | down_write(&asymmetric_key_parsers_sem); | |
504 | list_del(&parser->link); | |
505 | up_write(&asymmetric_key_parsers_sem); | |
506 | ||
507 | pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name); | |
508 | } | |
509 | EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser); | |
510 | ||
964f3b3b DH |
511 | /* |
512 | * Module stuff | |
513 | */ | |
514 | static int __init asymmetric_key_init(void) | |
515 | { | |
516 | return register_key_type(&key_type_asymmetric); | |
517 | } | |
518 | ||
519 | static void __exit asymmetric_key_cleanup(void) | |
520 | { | |
521 | unregister_key_type(&key_type_asymmetric); | |
522 | } | |
523 | ||
524 | module_init(asymmetric_key_init); | |
525 | module_exit(asymmetric_key_cleanup); |