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 | ||
7901c1a8 DH |
37 | /** |
38 | * asymmetric_key_generate_id: Construct an asymmetric key ID | |
39 | * @val_1: First binary blob | |
40 | * @len_1: Length of first binary blob | |
41 | * @val_2: Second binary blob | |
42 | * @len_2: Length of second binary blob | |
43 | * | |
44 | * Construct an asymmetric key ID from a pair of binary blobs. | |
45 | */ | |
46 | struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, | |
47 | size_t len_1, | |
48 | const void *val_2, | |
49 | size_t len_2) | |
50 | { | |
51 | struct asymmetric_key_id *kid; | |
52 | ||
53 | kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2, | |
54 | GFP_KERNEL); | |
55 | if (!kid) | |
56 | return ERR_PTR(-ENOMEM); | |
57 | kid->len = len_1 + len_2; | |
58 | memcpy(kid->data, val_1, len_1); | |
59 | memcpy(kid->data + len_1, val_2, len_2); | |
60 | return kid; | |
61 | } | |
62 | EXPORT_SYMBOL_GPL(asymmetric_key_generate_id); | |
63 | ||
64 | /** | |
65 | * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same. | |
66 | * @kid_1, @kid_2: The key IDs to compare | |
67 | */ | |
68 | bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1, | |
69 | const struct asymmetric_key_id *kid2) | |
70 | { | |
71 | if (!kid1 || !kid2) | |
72 | return false; | |
73 | if (kid1->len != kid2->len) | |
74 | return false; | |
75 | return memcmp(kid1->data, kid2->data, kid1->len) == 0; | |
76 | } | |
77 | EXPORT_SYMBOL_GPL(asymmetric_key_id_same); | |
78 | ||
f1b731db DK |
79 | /** |
80 | * asymmetric_key_id_partial - Return true if two asymmetric keys IDs | |
81 | * partially match | |
82 | * @kid_1, @kid_2: The key IDs to compare | |
83 | */ | |
84 | bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1, | |
85 | const struct asymmetric_key_id *kid2) | |
86 | { | |
87 | if (!kid1 || !kid2) | |
88 | return false; | |
89 | if (kid1->len < kid2->len) | |
90 | return false; | |
91 | return memcmp(kid1->data + (kid1->len - kid2->len), | |
92 | kid2->data, kid2->len) == 0; | |
93 | } | |
94 | EXPORT_SYMBOL_GPL(asymmetric_key_id_partial); | |
95 | ||
7901c1a8 DH |
96 | /** |
97 | * asymmetric_match_key_ids - Search asymmetric key IDs | |
98 | * @kids: The list of key IDs to check | |
99 | * @match_id: The key ID we're looking for | |
f1b731db | 100 | * @match: The match function to use |
7901c1a8 | 101 | */ |
f1b731db DK |
102 | static bool asymmetric_match_key_ids( |
103 | const struct asymmetric_key_ids *kids, | |
104 | const struct asymmetric_key_id *match_id, | |
105 | bool (*match)(const struct asymmetric_key_id *kid1, | |
106 | const struct asymmetric_key_id *kid2)) | |
7901c1a8 | 107 | { |
f1b731db DK |
108 | int i; |
109 | ||
7901c1a8 DH |
110 | if (!kids || !match_id) |
111 | return false; | |
f1b731db DK |
112 | for (i = 0; i < ARRAY_SIZE(kids->id); i++) |
113 | if (match(kids->id[i], match_id)) | |
114 | return true; | |
7901c1a8 DH |
115 | return false; |
116 | } | |
7901c1a8 | 117 | |
f2b3dee4 MZ |
118 | /* helper function can be called directly with pre-allocated memory */ |
119 | inline int __asymmetric_key_hex_to_key_id(const char *id, | |
120 | struct asymmetric_key_id *match_id, | |
121 | size_t hexlen) | |
122 | { | |
123 | match_id->len = hexlen; | |
124 | return hex2bin(match_id->data, id, hexlen); | |
125 | } | |
126 | ||
7901c1a8 DH |
127 | /** |
128 | * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID. | |
129 | * @id: The ID as a hex string. | |
130 | */ | |
131 | struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id) | |
132 | { | |
133 | struct asymmetric_key_id *match_id; | |
f2b3dee4 | 134 | size_t asciihexlen; |
d1ac5540 | 135 | int ret; |
7901c1a8 DH |
136 | |
137 | if (!*id) | |
138 | return ERR_PTR(-EINVAL); | |
f2b3dee4 MZ |
139 | asciihexlen = strlen(id); |
140 | if (asciihexlen & 1) | |
7901c1a8 DH |
141 | return ERR_PTR(-EINVAL); |
142 | ||
f2b3dee4 | 143 | match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2, |
7901c1a8 DH |
144 | GFP_KERNEL); |
145 | if (!match_id) | |
146 | return ERR_PTR(-ENOMEM); | |
f2b3dee4 | 147 | ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2); |
d1ac5540 DH |
148 | if (ret < 0) { |
149 | kfree(match_id); | |
150 | return ERR_PTR(-EINVAL); | |
151 | } | |
7901c1a8 DH |
152 | return match_id; |
153 | } | |
154 | ||
b3426827 | 155 | /* |
f1b731db | 156 | * Match asymmetric keys by an exact match on an ID. |
964f3b3b | 157 | */ |
0c903ab6 DH |
158 | static bool asymmetric_key_cmp(const struct key *key, |
159 | const struct key_match_data *match_data) | |
964f3b3b | 160 | { |
46963b77 DH |
161 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
162 | const struct asymmetric_key_id *match_id = match_data->preparsed; | |
964f3b3b | 163 | |
f1b731db DK |
164 | return asymmetric_match_key_ids(kids, match_id, |
165 | asymmetric_key_id_same); | |
166 | } | |
167 | ||
168 | /* | |
169 | * Match asymmetric keys by a partial match on an IDs. | |
170 | */ | |
171 | static bool asymmetric_key_cmp_partial(const struct key *key, | |
172 | const struct key_match_data *match_data) | |
173 | { | |
174 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); | |
175 | const struct asymmetric_key_id *match_id = match_data->preparsed; | |
176 | ||
177 | return asymmetric_match_key_ids(kids, match_id, | |
178 | asymmetric_key_id_partial); | |
964f3b3b DH |
179 | } |
180 | ||
46291959 DH |
181 | /* |
182 | * Preparse the match criterion. If we don't set lookup_type and cmp, | |
183 | * the default will be an exact match on the key description. | |
184 | * | |
185 | * There are some specifiers for matching key IDs rather than by the key | |
186 | * description: | |
187 | * | |
f1b731db DK |
188 | * "id:<id>" - find a key by partial match on any available ID |
189 | * "ex:<id>" - find a key by exact match on any available ID | |
46291959 DH |
190 | * |
191 | * These have to be searched by iteration rather than by direct lookup because | |
192 | * the key is hashed according to its description. | |
193 | */ | |
194 | static int asymmetric_key_match_preparse(struct key_match_data *match_data) | |
195 | { | |
46963b77 DH |
196 | struct asymmetric_key_id *match_id; |
197 | const char *spec = match_data->raw_data; | |
198 | const char *id; | |
f1b731db DK |
199 | bool (*cmp)(const struct key *, const struct key_match_data *) = |
200 | asymmetric_key_cmp; | |
46963b77 DH |
201 | |
202 | if (!spec || !*spec) | |
203 | return -EINVAL; | |
204 | if (spec[0] == 'i' && | |
205 | spec[1] == 'd' && | |
206 | spec[2] == ':') { | |
207 | id = spec + 3; | |
f1b731db DK |
208 | cmp = asymmetric_key_cmp_partial; |
209 | } else if (spec[0] == 'e' && | |
210 | spec[1] == 'x' && | |
211 | spec[2] == ':') { | |
212 | id = spec + 3; | |
46963b77 DH |
213 | } else { |
214 | goto default_match; | |
215 | } | |
216 | ||
217 | match_id = asymmetric_key_hex_to_key_id(id); | |
40b50e80 DK |
218 | if (IS_ERR(match_id)) |
219 | return PTR_ERR(match_id); | |
46963b77 DH |
220 | |
221 | match_data->preparsed = match_id; | |
f1b731db | 222 | match_data->cmp = cmp; |
46963b77 DH |
223 | match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; |
224 | return 0; | |
225 | ||
226 | default_match: | |
46291959 DH |
227 | return 0; |
228 | } | |
229 | ||
230 | /* | |
231 | * Free the preparsed the match criterion. | |
232 | */ | |
233 | static void asymmetric_key_match_free(struct key_match_data *match_data) | |
234 | { | |
46963b77 | 235 | kfree(match_data->preparsed); |
46291959 DH |
236 | } |
237 | ||
964f3b3b DH |
238 | /* |
239 | * Describe the asymmetric key | |
240 | */ | |
241 | static void asymmetric_key_describe(const struct key *key, struct seq_file *m) | |
242 | { | |
243 | const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
46963b77 DH |
244 | const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); |
245 | const struct asymmetric_key_id *kid; | |
246 | const unsigned char *p; | |
247 | int n; | |
964f3b3b DH |
248 | |
249 | seq_puts(m, key->description); | |
250 | ||
251 | if (subtype) { | |
252 | seq_puts(m, ": "); | |
253 | subtype->describe(key, m); | |
254 | ||
d4016589 DK |
255 | if (kids && kids->id[1]) { |
256 | kid = kids->id[1]; | |
964f3b3b | 257 | seq_putc(m, ' '); |
46963b77 DH |
258 | n = kid->len; |
259 | p = kid->data; | |
d4016589 DK |
260 | if (n > 4) { |
261 | p += n - 4; | |
262 | n = 4; | |
46963b77 DH |
263 | } |
264 | seq_printf(m, "%*phN", n, p); | |
964f3b3b DH |
265 | } |
266 | ||
267 | seq_puts(m, " ["); | |
268 | /* put something here to indicate the key's capabilities */ | |
269 | seq_putc(m, ']'); | |
270 | } | |
271 | } | |
272 | ||
46c6f177 DH |
273 | /* |
274 | * Preparse a asymmetric payload to get format the contents appropriately for the | |
275 | * internal payload to cut down on the number of scans of the data performed. | |
276 | * | |
277 | * We also generate a proposed description from the contents of the key that | |
278 | * can be used to name the key if the user doesn't want to provide one. | |
279 | */ | |
280 | static int asymmetric_key_preparse(struct key_preparsed_payload *prep) | |
281 | { | |
282 | struct asymmetric_key_parser *parser; | |
283 | int ret; | |
284 | ||
285 | pr_devel("==>%s()\n", __func__); | |
286 | ||
287 | if (prep->datalen == 0) | |
288 | return -EINVAL; | |
289 | ||
290 | down_read(&asymmetric_key_parsers_sem); | |
291 | ||
292 | ret = -EBADMSG; | |
293 | list_for_each_entry(parser, &asymmetric_key_parsers, link) { | |
294 | pr_debug("Trying parser '%s'\n", parser->name); | |
295 | ||
296 | ret = parser->parse(prep); | |
297 | if (ret != -EBADMSG) { | |
298 | pr_debug("Parser recognised the format (ret %d)\n", | |
299 | ret); | |
300 | break; | |
301 | } | |
302 | } | |
303 | ||
304 | up_read(&asymmetric_key_parsers_sem); | |
305 | pr_devel("<==%s() = %d\n", __func__, ret); | |
306 | return ret; | |
307 | } | |
308 | ||
146aa8b1 DH |
309 | /* |
310 | * Clean up the key ID list | |
311 | */ | |
312 | static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids) | |
313 | { | |
314 | int i; | |
315 | ||
316 | if (kids) { | |
317 | for (i = 0; i < ARRAY_SIZE(kids->id); i++) | |
318 | kfree(kids->id[i]); | |
319 | kfree(kids); | |
320 | } | |
321 | } | |
322 | ||
46c6f177 DH |
323 | /* |
324 | * Clean up the preparse data | |
325 | */ | |
326 | static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) | |
327 | { | |
146aa8b1 DH |
328 | struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype]; |
329 | struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids]; | |
46c6f177 DH |
330 | |
331 | pr_devel("==>%s()\n", __func__); | |
332 | ||
333 | if (subtype) { | |
146aa8b1 | 334 | subtype->destroy(prep->payload.data[asym_crypto]); |
46c6f177 DH |
335 | module_put(subtype->owner); |
336 | } | |
146aa8b1 | 337 | asymmetric_key_free_kids(kids); |
46c6f177 DH |
338 | kfree(prep->description); |
339 | } | |
340 | ||
964f3b3b DH |
341 | /* |
342 | * dispose of the data dangling from the corpse of a asymmetric key | |
343 | */ | |
344 | static void asymmetric_key_destroy(struct key *key) | |
345 | { | |
346 | struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
146aa8b1 DH |
347 | struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; |
348 | void *data = key->payload.data[asym_crypto]; | |
349 | ||
350 | key->payload.data[asym_crypto] = NULL; | |
351 | key->payload.data[asym_subtype] = NULL; | |
352 | key->payload.data[asym_key_ids] = NULL; | |
46963b77 | 353 | |
964f3b3b | 354 | if (subtype) { |
146aa8b1 | 355 | subtype->destroy(data); |
964f3b3b | 356 | module_put(subtype->owner); |
964f3b3b | 357 | } |
46963b77 | 358 | |
146aa8b1 | 359 | asymmetric_key_free_kids(kids); |
964f3b3b DH |
360 | } |
361 | ||
362 | struct key_type key_type_asymmetric = { | |
363 | .name = "asymmetric", | |
46c6f177 DH |
364 | .preparse = asymmetric_key_preparse, |
365 | .free_preparse = asymmetric_key_free_preparse, | |
6a09d17b | 366 | .instantiate = generic_key_instantiate, |
46291959 | 367 | .match_preparse = asymmetric_key_match_preparse, |
46291959 | 368 | .match_free = asymmetric_key_match_free, |
964f3b3b DH |
369 | .destroy = asymmetric_key_destroy, |
370 | .describe = asymmetric_key_describe, | |
371 | }; | |
372 | EXPORT_SYMBOL_GPL(key_type_asymmetric); | |
373 | ||
46c6f177 DH |
374 | /** |
375 | * register_asymmetric_key_parser - Register a asymmetric key blob parser | |
376 | * @parser: The parser to register | |
377 | */ | |
378 | int register_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
379 | { | |
380 | struct asymmetric_key_parser *cursor; | |
381 | int ret; | |
382 | ||
383 | down_write(&asymmetric_key_parsers_sem); | |
384 | ||
385 | list_for_each_entry(cursor, &asymmetric_key_parsers, link) { | |
386 | if (strcmp(cursor->name, parser->name) == 0) { | |
387 | pr_err("Asymmetric key parser '%s' already registered\n", | |
388 | parser->name); | |
389 | ret = -EEXIST; | |
390 | goto out; | |
391 | } | |
392 | } | |
393 | ||
394 | list_add_tail(&parser->link, &asymmetric_key_parsers); | |
395 | ||
396 | pr_notice("Asymmetric key parser '%s' registered\n", parser->name); | |
397 | ret = 0; | |
398 | ||
399 | out: | |
400 | up_write(&asymmetric_key_parsers_sem); | |
401 | return ret; | |
402 | } | |
403 | EXPORT_SYMBOL_GPL(register_asymmetric_key_parser); | |
404 | ||
405 | /** | |
406 | * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser | |
407 | * @parser: The parser to unregister | |
408 | */ | |
409 | void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
410 | { | |
411 | down_write(&asymmetric_key_parsers_sem); | |
412 | list_del(&parser->link); | |
413 | up_write(&asymmetric_key_parsers_sem); | |
414 | ||
415 | pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name); | |
416 | } | |
417 | EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser); | |
418 | ||
964f3b3b DH |
419 | /* |
420 | * Module stuff | |
421 | */ | |
422 | static int __init asymmetric_key_init(void) | |
423 | { | |
424 | return register_key_type(&key_type_asymmetric); | |
425 | } | |
426 | ||
427 | static void __exit asymmetric_key_cleanup(void) | |
428 | { | |
429 | unregister_key_type(&key_type_asymmetric); | |
430 | } | |
431 | ||
432 | module_init(asymmetric_key_init); | |
433 | module_exit(asymmetric_key_cleanup); |