]>
Commit | Line | Data |
---|---|---|
cce9e06d HX |
1 | /* |
2 | * Cryptographic API for algorithms (i.e., low-level API). | |
3 | * | |
4 | * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the Free | |
8 | * Software Foundation; either version 2 of the License, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | */ | |
12 | ||
6bfd4809 | 13 | #include <linux/err.h> |
cce9e06d HX |
14 | #include <linux/errno.h> |
15 | #include <linux/init.h> | |
16 | #include <linux/kernel.h> | |
4cc7720c | 17 | #include <linux/list.h> |
cce9e06d HX |
18 | #include <linux/module.h> |
19 | #include <linux/string.h> | |
20 | ||
21 | #include "internal.h" | |
22 | ||
4cc7720c HX |
23 | static LIST_HEAD(crypto_template_list); |
24 | ||
492e2b63 | 25 | void crypto_larval_error(const char *name, u32 type, u32 mask) |
2825982d HX |
26 | { |
27 | struct crypto_alg *alg; | |
28 | ||
29 | down_read(&crypto_alg_sem); | |
492e2b63 | 30 | alg = __crypto_alg_lookup(name, type, mask); |
2825982d HX |
31 | up_read(&crypto_alg_sem); |
32 | ||
33 | if (alg) { | |
34 | if (crypto_is_larval(alg)) { | |
35 | struct crypto_larval *larval = (void *)alg; | |
36 | complete(&larval->completion); | |
37 | } | |
38 | crypto_mod_put(alg); | |
39 | } | |
40 | } | |
41 | EXPORT_SYMBOL_GPL(crypto_larval_error); | |
42 | ||
cce9e06d HX |
43 | static inline int crypto_set_driver_name(struct crypto_alg *alg) |
44 | { | |
45 | static const char suffix[] = "-generic"; | |
46 | char *driver_name = alg->cra_driver_name; | |
47 | int len; | |
48 | ||
49 | if (*driver_name) | |
50 | return 0; | |
51 | ||
52 | len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME); | |
53 | if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME) | |
54 | return -ENAMETOOLONG; | |
55 | ||
56 | memcpy(driver_name + len, suffix, sizeof(suffix)); | |
57 | return 0; | |
58 | } | |
59 | ||
4cc7720c | 60 | static int crypto_check_alg(struct crypto_alg *alg) |
cce9e06d | 61 | { |
cce9e06d HX |
62 | if (alg->cra_alignmask & (alg->cra_alignmask + 1)) |
63 | return -EINVAL; | |
64 | ||
65 | if (alg->cra_alignmask & alg->cra_blocksize) | |
66 | return -EINVAL; | |
67 | ||
68 | if (alg->cra_blocksize > PAGE_SIZE / 8) | |
69 | return -EINVAL; | |
70 | ||
71 | if (alg->cra_priority < 0) | |
72 | return -EINVAL; | |
cce9e06d | 73 | |
4cc7720c HX |
74 | return crypto_set_driver_name(alg); |
75 | } | |
76 | ||
6bfd4809 HX |
77 | static void crypto_destroy_instance(struct crypto_alg *alg) |
78 | { | |
79 | struct crypto_instance *inst = (void *)alg; | |
80 | struct crypto_template *tmpl = inst->tmpl; | |
81 | ||
82 | tmpl->free(inst); | |
83 | crypto_tmpl_put(tmpl); | |
84 | } | |
85 | ||
86 | static void crypto_remove_spawns(struct list_head *spawns, | |
87 | struct list_head *list) | |
88 | { | |
89 | struct crypto_spawn *spawn, *n; | |
90 | ||
91 | list_for_each_entry_safe(spawn, n, spawns, list) { | |
92 | struct crypto_instance *inst = spawn->inst; | |
93 | struct crypto_template *tmpl = inst->tmpl; | |
94 | ||
95 | list_del_init(&spawn->list); | |
96 | spawn->alg = NULL; | |
97 | ||
98 | if (crypto_is_dead(&inst->alg)) | |
99 | continue; | |
100 | ||
101 | inst->alg.cra_flags |= CRYPTO_ALG_DEAD; | |
102 | if (!tmpl || !crypto_tmpl_get(tmpl)) | |
103 | continue; | |
104 | ||
105 | crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, &inst->alg); | |
106 | list_move(&inst->alg.cra_list, list); | |
107 | hlist_del(&inst->list); | |
108 | inst->alg.cra_destroy = crypto_destroy_instance; | |
109 | ||
110 | if (!list_empty(&inst->alg.cra_users)) { | |
111 | if (&n->list == spawns) | |
112 | n = list_entry(inst->alg.cra_users.next, | |
113 | typeof(*n), list); | |
114 | __list_splice(&inst->alg.cra_users, spawns->prev); | |
115 | } | |
116 | } | |
117 | } | |
118 | ||
119 | static int __crypto_register_alg(struct crypto_alg *alg, | |
120 | struct list_head *list) | |
4cc7720c HX |
121 | { |
122 | struct crypto_alg *q; | |
6bfd4809 HX |
123 | int ret = -EAGAIN; |
124 | ||
125 | if (crypto_is_dead(alg)) | |
126 | goto out; | |
127 | ||
128 | INIT_LIST_HEAD(&alg->cra_users); | |
129 | ||
130 | ret = -EEXIST; | |
4cc7720c | 131 | |
2825982d | 132 | atomic_set(&alg->cra_refcnt, 1); |
cce9e06d | 133 | list_for_each_entry(q, &crypto_alg_list, cra_list) { |
4cc7720c | 134 | if (q == alg) |
cce9e06d | 135 | goto out; |
6bfd4809 HX |
136 | |
137 | if (crypto_is_moribund(q)) | |
138 | continue; | |
139 | ||
140 | if (crypto_is_larval(q)) { | |
2825982d HX |
141 | struct crypto_larval *larval = (void *)q; |
142 | ||
6bfd4809 HX |
143 | if (strcmp(alg->cra_name, q->cra_name) && |
144 | strcmp(alg->cra_driver_name, q->cra_name)) | |
145 | continue; | |
146 | ||
147 | if (larval->adult) | |
148 | continue; | |
492e2b63 HX |
149 | if ((q->cra_flags ^ alg->cra_flags) & larval->mask) |
150 | continue; | |
2825982d HX |
151 | if (!crypto_mod_get(alg)) |
152 | continue; | |
6bfd4809 | 153 | |
2825982d HX |
154 | larval->adult = alg; |
155 | complete(&larval->completion); | |
6bfd4809 | 156 | continue; |
2825982d | 157 | } |
6bfd4809 HX |
158 | |
159 | if (strcmp(alg->cra_name, q->cra_name)) | |
160 | continue; | |
161 | ||
162 | if (strcmp(alg->cra_driver_name, q->cra_driver_name) && | |
163 | q->cra_priority > alg->cra_priority) | |
164 | continue; | |
165 | ||
166 | crypto_remove_spawns(&q->cra_users, list); | |
cce9e06d HX |
167 | } |
168 | ||
169 | list_add(&alg->cra_list, &crypto_alg_list); | |
2825982d HX |
170 | |
171 | crypto_notify(CRYPTO_MSG_ALG_REGISTER, alg); | |
4cc7720c | 172 | ret = 0; |
2825982d | 173 | |
cce9e06d | 174 | out: |
cce9e06d HX |
175 | return ret; |
176 | } | |
4cc7720c | 177 | |
6bfd4809 HX |
178 | static void crypto_remove_final(struct list_head *list) |
179 | { | |
180 | struct crypto_alg *alg; | |
181 | struct crypto_alg *n; | |
182 | ||
183 | list_for_each_entry_safe(alg, n, list, cra_list) { | |
184 | list_del_init(&alg->cra_list); | |
185 | crypto_alg_put(alg); | |
186 | } | |
187 | } | |
188 | ||
4cc7720c HX |
189 | int crypto_register_alg(struct crypto_alg *alg) |
190 | { | |
6bfd4809 | 191 | LIST_HEAD(list); |
4cc7720c HX |
192 | int err; |
193 | ||
194 | err = crypto_check_alg(alg); | |
195 | if (err) | |
196 | return err; | |
197 | ||
198 | down_write(&crypto_alg_sem); | |
6bfd4809 | 199 | err = __crypto_register_alg(alg, &list); |
4cc7720c HX |
200 | up_write(&crypto_alg_sem); |
201 | ||
6bfd4809 | 202 | crypto_remove_final(&list); |
4cc7720c HX |
203 | return err; |
204 | } | |
cce9e06d HX |
205 | EXPORT_SYMBOL_GPL(crypto_register_alg); |
206 | ||
6bfd4809 HX |
207 | static int crypto_remove_alg(struct crypto_alg *alg, struct list_head *list) |
208 | { | |
209 | if (unlikely(list_empty(&alg->cra_list))) | |
210 | return -ENOENT; | |
211 | ||
212 | alg->cra_flags |= CRYPTO_ALG_DEAD; | |
213 | ||
214 | crypto_notify(CRYPTO_MSG_ALG_UNREGISTER, alg); | |
215 | list_del_init(&alg->cra_list); | |
216 | crypto_remove_spawns(&alg->cra_users, list); | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
cce9e06d HX |
221 | int crypto_unregister_alg(struct crypto_alg *alg) |
222 | { | |
6bfd4809 HX |
223 | int ret; |
224 | LIST_HEAD(list); | |
cce9e06d HX |
225 | |
226 | down_write(&crypto_alg_sem); | |
6bfd4809 | 227 | ret = crypto_remove_alg(alg, &list); |
cce9e06d HX |
228 | up_write(&crypto_alg_sem); |
229 | ||
230 | if (ret) | |
231 | return ret; | |
232 | ||
233 | BUG_ON(atomic_read(&alg->cra_refcnt) != 1); | |
234 | if (alg->cra_destroy) | |
235 | alg->cra_destroy(alg); | |
236 | ||
6bfd4809 | 237 | crypto_remove_final(&list); |
cce9e06d HX |
238 | return 0; |
239 | } | |
240 | EXPORT_SYMBOL_GPL(crypto_unregister_alg); | |
241 | ||
4cc7720c HX |
242 | int crypto_register_template(struct crypto_template *tmpl) |
243 | { | |
244 | struct crypto_template *q; | |
245 | int err = -EEXIST; | |
246 | ||
247 | down_write(&crypto_alg_sem); | |
248 | ||
249 | list_for_each_entry(q, &crypto_template_list, list) { | |
250 | if (q == tmpl) | |
251 | goto out; | |
252 | } | |
253 | ||
254 | list_add(&tmpl->list, &crypto_template_list); | |
2825982d | 255 | crypto_notify(CRYPTO_MSG_TMPL_REGISTER, tmpl); |
4cc7720c HX |
256 | err = 0; |
257 | out: | |
258 | up_write(&crypto_alg_sem); | |
259 | return err; | |
260 | } | |
261 | EXPORT_SYMBOL_GPL(crypto_register_template); | |
262 | ||
263 | void crypto_unregister_template(struct crypto_template *tmpl) | |
264 | { | |
265 | struct crypto_instance *inst; | |
266 | struct hlist_node *p, *n; | |
267 | struct hlist_head *list; | |
6bfd4809 | 268 | LIST_HEAD(users); |
4cc7720c HX |
269 | |
270 | down_write(&crypto_alg_sem); | |
271 | ||
272 | BUG_ON(list_empty(&tmpl->list)); | |
273 | list_del_init(&tmpl->list); | |
274 | ||
275 | list = &tmpl->instances; | |
276 | hlist_for_each_entry(inst, p, list, list) { | |
6bfd4809 HX |
277 | int err = crypto_remove_alg(&inst->alg, &users); |
278 | BUG_ON(err); | |
4cc7720c HX |
279 | } |
280 | ||
2825982d HX |
281 | crypto_notify(CRYPTO_MSG_TMPL_UNREGISTER, tmpl); |
282 | ||
4cc7720c HX |
283 | up_write(&crypto_alg_sem); |
284 | ||
285 | hlist_for_each_entry_safe(inst, p, n, list, list) { | |
286 | BUG_ON(atomic_read(&inst->alg.cra_refcnt) != 1); | |
287 | tmpl->free(inst); | |
288 | } | |
6bfd4809 | 289 | crypto_remove_final(&users); |
4cc7720c HX |
290 | } |
291 | EXPORT_SYMBOL_GPL(crypto_unregister_template); | |
292 | ||
293 | static struct crypto_template *__crypto_lookup_template(const char *name) | |
294 | { | |
295 | struct crypto_template *q, *tmpl = NULL; | |
296 | ||
297 | down_read(&crypto_alg_sem); | |
298 | list_for_each_entry(q, &crypto_template_list, list) { | |
299 | if (strcmp(q->name, name)) | |
300 | continue; | |
301 | if (unlikely(!crypto_tmpl_get(q))) | |
302 | continue; | |
303 | ||
304 | tmpl = q; | |
305 | break; | |
306 | } | |
307 | up_read(&crypto_alg_sem); | |
308 | ||
309 | return tmpl; | |
310 | } | |
311 | ||
312 | struct crypto_template *crypto_lookup_template(const char *name) | |
313 | { | |
314 | return try_then_request_module(__crypto_lookup_template(name), name); | |
315 | } | |
316 | EXPORT_SYMBOL_GPL(crypto_lookup_template); | |
317 | ||
318 | int crypto_register_instance(struct crypto_template *tmpl, | |
319 | struct crypto_instance *inst) | |
320 | { | |
6bfd4809 | 321 | LIST_HEAD(list); |
4cc7720c HX |
322 | int err = -EINVAL; |
323 | ||
324 | if (inst->alg.cra_destroy) | |
325 | goto err; | |
326 | ||
327 | err = crypto_check_alg(&inst->alg); | |
328 | if (err) | |
329 | goto err; | |
330 | ||
331 | inst->alg.cra_module = tmpl->module; | |
332 | ||
333 | down_write(&crypto_alg_sem); | |
334 | ||
6bfd4809 | 335 | err = __crypto_register_alg(&inst->alg, &list); |
4cc7720c HX |
336 | if (err) |
337 | goto unlock; | |
338 | ||
339 | hlist_add_head(&inst->list, &tmpl->instances); | |
340 | inst->tmpl = tmpl; | |
341 | ||
342 | unlock: | |
343 | up_write(&crypto_alg_sem); | |
344 | ||
6bfd4809 HX |
345 | crypto_remove_final(&list); |
346 | ||
4cc7720c HX |
347 | err: |
348 | return err; | |
349 | } | |
350 | EXPORT_SYMBOL_GPL(crypto_register_instance); | |
351 | ||
6bfd4809 HX |
352 | int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg, |
353 | struct crypto_instance *inst) | |
354 | { | |
355 | int err = -EAGAIN; | |
356 | ||
357 | spawn->inst = inst; | |
358 | ||
359 | down_write(&crypto_alg_sem); | |
360 | if (!crypto_is_moribund(alg)) { | |
361 | list_add(&spawn->list, &alg->cra_users); | |
362 | spawn->alg = alg; | |
363 | err = 0; | |
364 | } | |
365 | up_write(&crypto_alg_sem); | |
366 | ||
367 | return err; | |
368 | } | |
369 | EXPORT_SYMBOL_GPL(crypto_init_spawn); | |
370 | ||
371 | void crypto_drop_spawn(struct crypto_spawn *spawn) | |
372 | { | |
373 | down_write(&crypto_alg_sem); | |
374 | list_del(&spawn->list); | |
375 | up_write(&crypto_alg_sem); | |
376 | } | |
377 | EXPORT_SYMBOL_GPL(crypto_drop_spawn); | |
378 | ||
379 | struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn) | |
380 | { | |
381 | struct crypto_alg *alg; | |
382 | struct crypto_alg *alg2; | |
383 | struct crypto_tfm *tfm; | |
384 | ||
385 | down_read(&crypto_alg_sem); | |
386 | alg = spawn->alg; | |
387 | alg2 = alg; | |
388 | if (alg2) | |
389 | alg2 = crypto_mod_get(alg2); | |
390 | up_read(&crypto_alg_sem); | |
391 | ||
392 | if (!alg2) { | |
393 | if (alg) | |
394 | crypto_shoot_alg(alg); | |
395 | return ERR_PTR(-EAGAIN); | |
396 | } | |
397 | ||
398 | tfm = __crypto_alloc_tfm(alg, 0); | |
399 | if (IS_ERR(tfm)) | |
400 | crypto_mod_put(alg); | |
401 | ||
402 | return tfm; | |
403 | } | |
404 | EXPORT_SYMBOL_GPL(crypto_spawn_tfm); | |
405 | ||
2825982d HX |
406 | int crypto_register_notifier(struct notifier_block *nb) |
407 | { | |
408 | return blocking_notifier_chain_register(&crypto_chain, nb); | |
409 | } | |
410 | EXPORT_SYMBOL_GPL(crypto_register_notifier); | |
411 | ||
412 | int crypto_unregister_notifier(struct notifier_block *nb) | |
413 | { | |
414 | return blocking_notifier_chain_unregister(&crypto_chain, nb); | |
415 | } | |
416 | EXPORT_SYMBOL_GPL(crypto_unregister_notifier); | |
417 | ||
cce9e06d HX |
418 | static int __init crypto_algapi_init(void) |
419 | { | |
420 | crypto_init_proc(); | |
421 | return 0; | |
422 | } | |
423 | ||
424 | static void __exit crypto_algapi_exit(void) | |
425 | { | |
426 | crypto_exit_proc(); | |
427 | } | |
428 | ||
429 | module_init(crypto_algapi_init); | |
430 | module_exit(crypto_algapi_exit); | |
431 | ||
432 | MODULE_LICENSE("GPL"); | |
433 | MODULE_DESCRIPTION("Cryptographic algorithms API"); |