[CRYPTO] api: Add crypto_alg reference counting
[safe/jmp/linux-2.6] / crypto / api.c
1 /*
2  * Scatterlist Cryptographic API.
3  *
4  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
5  * Copyright (c) 2002 David S. Miller (davem@redhat.com)
6  * Copyright (c) 2005 Herbert Xu <herbert@gondor.apana.org.au>
7  *
8  * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
9  * and Nettle, by Niels Möller.
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU General Public License as published by the Free
13  * Software Foundation; either version 2 of the License, or (at your option) 
14  * any later version.
15  *
16  */
17
18 #include <linux/compiler.h>
19 #include <linux/init.h>
20 #include <linux/crypto.h>
21 #include <linux/errno.h>
22 #include <linux/kernel.h>
23 #include <linux/kmod.h>
24 #include <linux/rwsem.h>
25 #include <linux/slab.h>
26 #include <linux/string.h>
27 #include "internal.h"
28
29 LIST_HEAD(crypto_alg_list);
30 DECLARE_RWSEM(crypto_alg_sem);
31
32 static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg)
33 {
34         atomic_inc(&alg->cra_refcnt);
35         return alg;
36 }
37
38 static inline void crypto_alg_put(struct crypto_alg *alg)
39 {
40         if (atomic_dec_and_test(&alg->cra_refcnt) && alg->cra_destroy)
41                 alg->cra_destroy(alg);
42 }
43
44 static struct crypto_alg *crypto_mod_get(struct crypto_alg *alg)
45 {
46         return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL;
47 }
48
49 static void crypto_mod_put(struct crypto_alg *alg)
50 {
51         crypto_alg_put(alg);
52         module_put(alg->cra_module);
53 }
54
55 static struct crypto_alg *crypto_alg_lookup(const char *name)
56 {
57         struct crypto_alg *q, *alg = NULL;
58         int best = -1;
59
60         if (!name)
61                 return NULL;
62         
63         down_read(&crypto_alg_sem);
64         
65         list_for_each_entry(q, &crypto_alg_list, cra_list) {
66                 int exact, fuzzy;
67
68                 exact = !strcmp(q->cra_driver_name, name);
69                 fuzzy = !strcmp(q->cra_name, name);
70                 if (!exact && !(fuzzy && q->cra_priority > best))
71                         continue;
72
73                 if (unlikely(!crypto_mod_get(q)))
74                         continue;
75
76                 best = q->cra_priority;
77                 if (alg)
78                         crypto_mod_put(alg);
79                 alg = q;
80
81                 if (exact)
82                         break;
83         }
84         
85         up_read(&crypto_alg_sem);
86         return alg;
87 }
88
89 /* A far more intelligent version of this is planned.  For now, just
90  * try an exact match on the name of the algorithm. */
91 static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name)
92 {
93         return try_then_request_module(crypto_alg_lookup(name), name);
94 }
95
96 static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags)
97 {
98         tfm->crt_flags = flags & CRYPTO_TFM_REQ_MASK;
99         flags &= ~CRYPTO_TFM_REQ_MASK;
100         
101         switch (crypto_tfm_alg_type(tfm)) {
102         case CRYPTO_ALG_TYPE_CIPHER:
103                 return crypto_init_cipher_flags(tfm, flags);
104                 
105         case CRYPTO_ALG_TYPE_DIGEST:
106                 return crypto_init_digest_flags(tfm, flags);
107                 
108         case CRYPTO_ALG_TYPE_COMPRESS:
109                 return crypto_init_compress_flags(tfm, flags);
110         
111         default:
112                 break;
113         }
114         
115         BUG();
116         return -EINVAL;
117 }
118
119 static int crypto_init_ops(struct crypto_tfm *tfm)
120 {
121         switch (crypto_tfm_alg_type(tfm)) {
122         case CRYPTO_ALG_TYPE_CIPHER:
123                 return crypto_init_cipher_ops(tfm);
124                 
125         case CRYPTO_ALG_TYPE_DIGEST:
126                 return crypto_init_digest_ops(tfm);
127                 
128         case CRYPTO_ALG_TYPE_COMPRESS:
129                 return crypto_init_compress_ops(tfm);
130         
131         default:
132                 break;
133         }
134         
135         BUG();
136         return -EINVAL;
137 }
138
139 static void crypto_exit_ops(struct crypto_tfm *tfm)
140 {
141         switch (crypto_tfm_alg_type(tfm)) {
142         case CRYPTO_ALG_TYPE_CIPHER:
143                 crypto_exit_cipher_ops(tfm);
144                 break;
145                 
146         case CRYPTO_ALG_TYPE_DIGEST:
147                 crypto_exit_digest_ops(tfm);
148                 break;
149                 
150         case CRYPTO_ALG_TYPE_COMPRESS:
151                 crypto_exit_compress_ops(tfm);
152                 break;
153         
154         default:
155                 BUG();
156                 
157         }
158 }
159
160 static unsigned int crypto_ctxsize(struct crypto_alg *alg, int flags)
161 {
162         unsigned int len;
163
164         switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
165         default:
166                 BUG();
167
168         case CRYPTO_ALG_TYPE_CIPHER:
169                 len = crypto_cipher_ctxsize(alg, flags);
170                 break;
171                 
172         case CRYPTO_ALG_TYPE_DIGEST:
173                 len = crypto_digest_ctxsize(alg, flags);
174                 break;
175                 
176         case CRYPTO_ALG_TYPE_COMPRESS:
177                 len = crypto_compress_ctxsize(alg, flags);
178                 break;
179         }
180
181         return len + (alg->cra_alignmask & ~(crypto_tfm_ctx_alignment() - 1));
182 }
183
184 struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
185 {
186         struct crypto_tfm *tfm = NULL;
187         struct crypto_alg *alg;
188         unsigned int tfm_size;
189
190         alg = crypto_alg_mod_lookup(name);
191         if (alg == NULL)
192                 goto out;
193
194         tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
195         tfm = kzalloc(tfm_size, GFP_KERNEL);
196         if (tfm == NULL)
197                 goto out_put;
198
199         tfm->__crt_alg = alg;
200         
201         if (crypto_init_flags(tfm, flags))
202                 goto out_free_tfm;
203                 
204         if (crypto_init_ops(tfm))
205                 goto out_free_tfm;
206
207         if (alg->cra_init && alg->cra_init(tfm))
208                 goto cra_init_failed;
209
210         goto out;
211
212 cra_init_failed:
213         crypto_exit_ops(tfm);
214 out_free_tfm:
215         kfree(tfm);
216         tfm = NULL;
217 out_put:
218         crypto_mod_put(alg);
219 out:
220         return tfm;
221 }
222
223 void crypto_free_tfm(struct crypto_tfm *tfm)
224 {
225         struct crypto_alg *alg;
226         int size;
227
228         if (unlikely(!tfm))
229                 return;
230
231         alg = tfm->__crt_alg;
232         size = sizeof(*tfm) + alg->cra_ctxsize;
233
234         if (alg->cra_exit)
235                 alg->cra_exit(tfm);
236         crypto_exit_ops(tfm);
237         crypto_mod_put(alg);
238         memset(tfm, 0, size);
239         kfree(tfm);
240 }
241
242 static inline int crypto_set_driver_name(struct crypto_alg *alg)
243 {
244         static const char suffix[] = "-generic";
245         char *driver_name = alg->cra_driver_name;
246         int len;
247
248         if (*driver_name)
249                 return 0;
250
251         len = strlcpy(driver_name, alg->cra_name, CRYPTO_MAX_ALG_NAME);
252         if (len + sizeof(suffix) > CRYPTO_MAX_ALG_NAME)
253                 return -ENAMETOOLONG;
254
255         memcpy(driver_name + len, suffix, sizeof(suffix));
256         return 0;
257 }
258
259 int crypto_register_alg(struct crypto_alg *alg)
260 {
261         int ret;
262         struct crypto_alg *q;
263
264         if (alg->cra_alignmask & (alg->cra_alignmask + 1))
265                 return -EINVAL;
266
267         if (alg->cra_alignmask & alg->cra_blocksize)
268                 return -EINVAL;
269
270         if (alg->cra_blocksize > PAGE_SIZE / 8)
271                 return -EINVAL;
272
273         if (alg->cra_priority < 0)
274                 return -EINVAL;
275         
276         ret = crypto_set_driver_name(alg);
277         if (unlikely(ret))
278                 return ret;
279
280         down_write(&crypto_alg_sem);
281         
282         list_for_each_entry(q, &crypto_alg_list, cra_list) {
283                 if (q == alg) {
284                         ret = -EEXIST;
285                         goto out;
286                 }
287         }
288         
289         list_add(&alg->cra_list, &crypto_alg_list);
290         atomic_set(&alg->cra_refcnt, 1);
291 out:    
292         up_write(&crypto_alg_sem);
293         return ret;
294 }
295
296 int crypto_unregister_alg(struct crypto_alg *alg)
297 {
298         int ret = -ENOENT;
299         struct crypto_alg *q;
300         
301         down_write(&crypto_alg_sem);
302         list_for_each_entry(q, &crypto_alg_list, cra_list) {
303                 if (alg == q) {
304                         list_del(&alg->cra_list);
305                         ret = 0;
306                         goto out;
307                 }
308         }
309 out:    
310         up_write(&crypto_alg_sem);
311
312         if (ret)
313                 return ret;
314
315         BUG_ON(atomic_read(&alg->cra_refcnt) != 1);
316         if (alg->cra_destroy)
317                 alg->cra_destroy(alg);
318
319         return 0;
320 }
321
322 int crypto_alg_available(const char *name, u32 flags)
323 {
324         int ret = 0;
325         struct crypto_alg *alg = crypto_alg_mod_lookup(name);
326         
327         if (alg) {
328                 crypto_mod_put(alg);
329                 ret = 1;
330         }
331         
332         return ret;
333 }
334
335 static int __init init_crypto(void)
336 {
337         printk(KERN_INFO "Initializing Cryptographic API\n");
338         crypto_init_proc();
339         return 0;
340 }
341
342 __initcall(init_crypto);
343
344 EXPORT_SYMBOL_GPL(crypto_register_alg);
345 EXPORT_SYMBOL_GPL(crypto_unregister_alg);
346 EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
347 EXPORT_SYMBOL_GPL(crypto_free_tfm);
348 EXPORT_SYMBOL_GPL(crypto_alg_available);