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