]>
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> | |
18 | #include "asymmetric_keys.h" | |
19 | ||
20 | MODULE_LICENSE("GPL"); | |
21 | ||
46c6f177 DH |
22 | static LIST_HEAD(asymmetric_key_parsers); |
23 | static DECLARE_RWSEM(asymmetric_key_parsers_sem); | |
24 | ||
b3426827 DK |
25 | /* |
26 | * Match asymmetric key id with partial match | |
27 | * @id: key id to match in a form "id:<id>" | |
28 | */ | |
29 | int asymmetric_keyid_match(const char *kid, const char *id) | |
30 | { | |
31 | size_t idlen, kidlen; | |
32 | ||
33 | if (!kid || !id) | |
34 | return 0; | |
35 | ||
36 | /* make it possible to use id as in the request: "id:<id>" */ | |
37 | if (strncmp(id, "id:", 3) == 0) | |
38 | id += 3; | |
39 | ||
40 | /* Anything after here requires a partial match on the ID string */ | |
41 | idlen = strlen(id); | |
42 | kidlen = strlen(kid); | |
43 | if (idlen > kidlen) | |
44 | return 0; | |
45 | ||
46 | kid += kidlen - idlen; | |
47 | if (strcasecmp(id, kid) != 0) | |
48 | return 0; | |
49 | ||
50 | return 1; | |
51 | } | |
ffb70f61 | 52 | EXPORT_SYMBOL_GPL(asymmetric_keyid_match); |
b3426827 | 53 | |
964f3b3b DH |
54 | /* |
55 | * Match asymmetric keys on (part of) their name | |
56 | * We have some shorthand methods for matching keys. We allow: | |
57 | * | |
58 | * "<desc>" - request a key by description | |
59 | * "id:<id>" - request a key matching the ID | |
60 | * "<subtype>:<id>" - request a key of a subtype | |
61 | */ | |
0c903ab6 DH |
62 | static bool asymmetric_key_cmp(const struct key *key, |
63 | const struct key_match_data *match_data) | |
964f3b3b DH |
64 | { |
65 | const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
46291959 | 66 | const char *description = match_data->raw_data; |
964f3b3b | 67 | const char *spec = description; |
b3426827 | 68 | const char *id; |
964f3b3b | 69 | ptrdiff_t speclen; |
964f3b3b DH |
70 | |
71 | if (!subtype || !spec || !*spec) | |
72 | return 0; | |
73 | ||
74 | /* See if the full key description matches as is */ | |
75 | if (key->description && strcmp(key->description, description) == 0) | |
76 | return 1; | |
77 | ||
78 | /* All tests from here on break the criterion description into a | |
79 | * specifier, a colon and then an identifier. | |
80 | */ | |
81 | id = strchr(spec, ':'); | |
82 | if (!id) | |
83 | return 0; | |
84 | ||
85 | speclen = id - spec; | |
86 | id++; | |
87 | ||
b3426827 DK |
88 | if (speclen == 2 && memcmp(spec, "id", 2) == 0) |
89 | return asymmetric_keyid_match(asymmetric_key_id(key), id); | |
964f3b3b DH |
90 | |
91 | if (speclen == subtype->name_len && | |
92 | memcmp(spec, subtype->name, speclen) == 0) | |
93 | return 1; | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
46291959 DH |
98 | /* |
99 | * Preparse the match criterion. If we don't set lookup_type and cmp, | |
100 | * the default will be an exact match on the key description. | |
101 | * | |
102 | * There are some specifiers for matching key IDs rather than by the key | |
103 | * description: | |
104 | * | |
105 | * "id:<id>" - request a key by any available ID | |
106 | * | |
107 | * These have to be searched by iteration rather than by direct lookup because | |
108 | * the key is hashed according to its description. | |
109 | */ | |
110 | static int asymmetric_key_match_preparse(struct key_match_data *match_data) | |
111 | { | |
112 | match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE; | |
c06cfb08 | 113 | match_data->cmp = asymmetric_key_cmp; |
46291959 DH |
114 | return 0; |
115 | } | |
116 | ||
117 | /* | |
118 | * Free the preparsed the match criterion. | |
119 | */ | |
120 | static void asymmetric_key_match_free(struct key_match_data *match_data) | |
121 | { | |
122 | } | |
123 | ||
964f3b3b DH |
124 | /* |
125 | * Describe the asymmetric key | |
126 | */ | |
127 | static void asymmetric_key_describe(const struct key *key, struct seq_file *m) | |
128 | { | |
129 | const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
130 | const char *kid = asymmetric_key_id(key); | |
131 | size_t n; | |
132 | ||
133 | seq_puts(m, key->description); | |
134 | ||
135 | if (subtype) { | |
136 | seq_puts(m, ": "); | |
137 | subtype->describe(key, m); | |
138 | ||
139 | if (kid) { | |
140 | seq_putc(m, ' '); | |
141 | n = strlen(kid); | |
142 | if (n <= 8) | |
143 | seq_puts(m, kid); | |
144 | else | |
145 | seq_puts(m, kid + n - 8); | |
146 | } | |
147 | ||
148 | seq_puts(m, " ["); | |
149 | /* put something here to indicate the key's capabilities */ | |
150 | seq_putc(m, ']'); | |
151 | } | |
152 | } | |
153 | ||
46c6f177 DH |
154 | /* |
155 | * Preparse a asymmetric payload to get format the contents appropriately for the | |
156 | * internal payload to cut down on the number of scans of the data performed. | |
157 | * | |
158 | * We also generate a proposed description from the contents of the key that | |
159 | * can be used to name the key if the user doesn't want to provide one. | |
160 | */ | |
161 | static int asymmetric_key_preparse(struct key_preparsed_payload *prep) | |
162 | { | |
163 | struct asymmetric_key_parser *parser; | |
164 | int ret; | |
165 | ||
166 | pr_devel("==>%s()\n", __func__); | |
167 | ||
168 | if (prep->datalen == 0) | |
169 | return -EINVAL; | |
170 | ||
171 | down_read(&asymmetric_key_parsers_sem); | |
172 | ||
173 | ret = -EBADMSG; | |
174 | list_for_each_entry(parser, &asymmetric_key_parsers, link) { | |
175 | pr_debug("Trying parser '%s'\n", parser->name); | |
176 | ||
177 | ret = parser->parse(prep); | |
178 | if (ret != -EBADMSG) { | |
179 | pr_debug("Parser recognised the format (ret %d)\n", | |
180 | ret); | |
181 | break; | |
182 | } | |
183 | } | |
184 | ||
185 | up_read(&asymmetric_key_parsers_sem); | |
186 | pr_devel("<==%s() = %d\n", __func__, ret); | |
187 | return ret; | |
188 | } | |
189 | ||
190 | /* | |
191 | * Clean up the preparse data | |
192 | */ | |
193 | static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) | |
194 | { | |
195 | struct asymmetric_key_subtype *subtype = prep->type_data[0]; | |
196 | ||
197 | pr_devel("==>%s()\n", __func__); | |
198 | ||
199 | if (subtype) { | |
fc7c70e0 | 200 | subtype->destroy(prep->payload[0]); |
46c6f177 DH |
201 | module_put(subtype->owner); |
202 | } | |
203 | kfree(prep->type_data[1]); | |
204 | kfree(prep->description); | |
205 | } | |
206 | ||
964f3b3b DH |
207 | /* |
208 | * dispose of the data dangling from the corpse of a asymmetric key | |
209 | */ | |
210 | static void asymmetric_key_destroy(struct key *key) | |
211 | { | |
212 | struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); | |
213 | if (subtype) { | |
214 | subtype->destroy(key->payload.data); | |
215 | module_put(subtype->owner); | |
216 | key->type_data.p[0] = NULL; | |
217 | } | |
218 | kfree(key->type_data.p[1]); | |
219 | key->type_data.p[1] = NULL; | |
220 | } | |
221 | ||
222 | struct key_type key_type_asymmetric = { | |
223 | .name = "asymmetric", | |
46c6f177 DH |
224 | .preparse = asymmetric_key_preparse, |
225 | .free_preparse = asymmetric_key_free_preparse, | |
6a09d17b | 226 | .instantiate = generic_key_instantiate, |
46291959 | 227 | .match_preparse = asymmetric_key_match_preparse, |
46291959 | 228 | .match_free = asymmetric_key_match_free, |
964f3b3b DH |
229 | .destroy = asymmetric_key_destroy, |
230 | .describe = asymmetric_key_describe, | |
231 | }; | |
232 | EXPORT_SYMBOL_GPL(key_type_asymmetric); | |
233 | ||
46c6f177 DH |
234 | /** |
235 | * register_asymmetric_key_parser - Register a asymmetric key blob parser | |
236 | * @parser: The parser to register | |
237 | */ | |
238 | int register_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
239 | { | |
240 | struct asymmetric_key_parser *cursor; | |
241 | int ret; | |
242 | ||
243 | down_write(&asymmetric_key_parsers_sem); | |
244 | ||
245 | list_for_each_entry(cursor, &asymmetric_key_parsers, link) { | |
246 | if (strcmp(cursor->name, parser->name) == 0) { | |
247 | pr_err("Asymmetric key parser '%s' already registered\n", | |
248 | parser->name); | |
249 | ret = -EEXIST; | |
250 | goto out; | |
251 | } | |
252 | } | |
253 | ||
254 | list_add_tail(&parser->link, &asymmetric_key_parsers); | |
255 | ||
256 | pr_notice("Asymmetric key parser '%s' registered\n", parser->name); | |
257 | ret = 0; | |
258 | ||
259 | out: | |
260 | up_write(&asymmetric_key_parsers_sem); | |
261 | return ret; | |
262 | } | |
263 | EXPORT_SYMBOL_GPL(register_asymmetric_key_parser); | |
264 | ||
265 | /** | |
266 | * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser | |
267 | * @parser: The parser to unregister | |
268 | */ | |
269 | void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser) | |
270 | { | |
271 | down_write(&asymmetric_key_parsers_sem); | |
272 | list_del(&parser->link); | |
273 | up_write(&asymmetric_key_parsers_sem); | |
274 | ||
275 | pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name); | |
276 | } | |
277 | EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser); | |
278 | ||
964f3b3b DH |
279 | /* |
280 | * Module stuff | |
281 | */ | |
282 | static int __init asymmetric_key_init(void) | |
283 | { | |
284 | return register_key_type(&key_type_asymmetric); | |
285 | } | |
286 | ||
287 | static void __exit asymmetric_key_cleanup(void) | |
288 | { | |
289 | unregister_key_type(&key_type_asymmetric); | |
290 | } | |
291 | ||
292 | module_init(asymmetric_key_init); | |
293 | module_exit(asymmetric_key_cleanup); |