[PATCH] uml: fix fault handler on write
[linux-2.6.git] / 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  *
7  * Portions derived from Cryptoapi, by Alexander Kjeldaas <astor@fast.no>
8  * and Nettle, by Niels Möller.
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 2 of the License, or (at your option) 
13  * any later version.
14  *
15  */
16
17 #include <linux/compiler.h>
18 #include <linux/init.h>
19 #include <linux/crypto.h>
20 #include <linux/errno.h>
21 #include <linux/kmod.h>
22 #include <linux/rwsem.h>
23 #include <linux/slab.h>
24 #include "internal.h"
25
26 LIST_HEAD(crypto_alg_list);
27 DECLARE_RWSEM(crypto_alg_sem);
28
29 static inline int crypto_alg_get(struct crypto_alg *alg)
30 {
31         return try_module_get(alg->cra_module);
32 }
33
34 static inline void crypto_alg_put(struct crypto_alg *alg)
35 {
36         module_put(alg->cra_module);
37 }
38
39 static struct crypto_alg *crypto_alg_lookup(const char *name)
40 {
41         struct crypto_alg *q, *alg = NULL;
42
43         if (!name)
44                 return NULL;
45         
46         down_read(&crypto_alg_sem);
47         
48         list_for_each_entry(q, &crypto_alg_list, cra_list) {
49                 if (!(strcmp(q->cra_name, name))) {
50                         if (crypto_alg_get(q))
51                                 alg = q;
52                         break;
53                 }
54         }
55         
56         up_read(&crypto_alg_sem);
57         return alg;
58 }
59
60 /* A far more intelligent version of this is planned.  For now, just
61  * try an exact match on the name of the algorithm. */
62 static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name)
63 {
64         return try_then_request_module(crypto_alg_lookup(name), name);
65 }
66
67 static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags)
68 {
69         tfm->crt_flags = flags & CRYPTO_TFM_REQ_MASK;
70         flags &= ~CRYPTO_TFM_REQ_MASK;
71         
72         switch (crypto_tfm_alg_type(tfm)) {
73         case CRYPTO_ALG_TYPE_CIPHER:
74                 return crypto_init_cipher_flags(tfm, flags);
75                 
76         case CRYPTO_ALG_TYPE_DIGEST:
77                 return crypto_init_digest_flags(tfm, flags);
78                 
79         case CRYPTO_ALG_TYPE_COMPRESS:
80                 return crypto_init_compress_flags(tfm, flags);
81         
82         default:
83                 break;
84         }
85         
86         BUG();
87         return -EINVAL;
88 }
89
90 static int crypto_init_ops(struct crypto_tfm *tfm)
91 {
92         switch (crypto_tfm_alg_type(tfm)) {
93         case CRYPTO_ALG_TYPE_CIPHER:
94                 return crypto_init_cipher_ops(tfm);
95                 
96         case CRYPTO_ALG_TYPE_DIGEST:
97                 return crypto_init_digest_ops(tfm);
98                 
99         case CRYPTO_ALG_TYPE_COMPRESS:
100                 return crypto_init_compress_ops(tfm);
101         
102         default:
103                 break;
104         }
105         
106         BUG();
107         return -EINVAL;
108 }
109
110 static void crypto_exit_ops(struct crypto_tfm *tfm)
111 {
112         switch (crypto_tfm_alg_type(tfm)) {
113         case CRYPTO_ALG_TYPE_CIPHER:
114                 crypto_exit_cipher_ops(tfm);
115                 break;
116                 
117         case CRYPTO_ALG_TYPE_DIGEST:
118                 crypto_exit_digest_ops(tfm);
119                 break;
120                 
121         case CRYPTO_ALG_TYPE_COMPRESS:
122                 crypto_exit_compress_ops(tfm);
123                 break;
124         
125         default:
126                 BUG();
127                 
128         }
129 }
130
131 static unsigned int crypto_ctxsize(struct crypto_alg *alg, int flags)
132 {
133         unsigned int len;
134
135         switch (alg->cra_flags & CRYPTO_ALG_TYPE_MASK) {
136         default:
137                 BUG();
138
139         case CRYPTO_ALG_TYPE_CIPHER:
140                 len = crypto_cipher_ctxsize(alg, flags);
141                 break;
142                 
143         case CRYPTO_ALG_TYPE_DIGEST:
144                 len = crypto_digest_ctxsize(alg, flags);
145                 break;
146                 
147         case CRYPTO_ALG_TYPE_COMPRESS:
148                 len = crypto_compress_ctxsize(alg, flags);
149                 break;
150         }
151
152         return len + alg->cra_alignmask;
153 }
154
155 struct crypto_tfm *crypto_alloc_tfm(const char *name, u32 flags)
156 {
157         struct crypto_tfm *tfm = NULL;
158         struct crypto_alg *alg;
159         unsigned int tfm_size;
160
161         alg = crypto_alg_mod_lookup(name);
162         if (alg == NULL)
163                 goto out;
164
165         tfm_size = sizeof(*tfm) + crypto_ctxsize(alg, flags);
166         tfm = kmalloc(tfm_size, GFP_KERNEL);
167         if (tfm == NULL)
168                 goto out_put;
169
170         memset(tfm, 0, tfm_size);
171         
172         tfm->__crt_alg = alg;
173         
174         if (crypto_init_flags(tfm, flags))
175                 goto out_free_tfm;
176                 
177         if (crypto_init_ops(tfm)) {
178                 crypto_exit_ops(tfm);
179                 goto out_free_tfm;
180         }
181
182         goto out;
183
184 out_free_tfm:
185         kfree(tfm);
186         tfm = NULL;
187 out_put:
188         crypto_alg_put(alg);
189 out:
190         return tfm;
191 }
192
193 void crypto_free_tfm(struct crypto_tfm *tfm)
194 {
195         struct crypto_alg *alg;
196         int size;
197
198         if (unlikely(!tfm))
199                 return;
200
201         alg = tfm->__crt_alg;
202         size = sizeof(*tfm) + alg->cra_ctxsize;
203
204         crypto_exit_ops(tfm);
205         crypto_alg_put(alg);
206         memset(tfm, 0, size);
207         kfree(tfm);
208 }
209
210 int crypto_register_alg(struct crypto_alg *alg)
211 {
212         int ret = 0;
213         struct crypto_alg *q;
214
215         if (alg->cra_alignmask & (alg->cra_alignmask + 1))
216                 return -EINVAL;
217
218         if (alg->cra_alignmask > PAGE_SIZE)
219                 return -EINVAL;
220         
221         down_write(&crypto_alg_sem);
222         
223         list_for_each_entry(q, &crypto_alg_list, cra_list) {
224                 if (!(strcmp(q->cra_name, alg->cra_name))) {
225                         ret = -EEXIST;
226                         goto out;
227                 }
228         }
229         
230         list_add_tail(&alg->cra_list, &crypto_alg_list);
231 out:    
232         up_write(&crypto_alg_sem);
233         return ret;
234 }
235
236 int crypto_unregister_alg(struct crypto_alg *alg)
237 {
238         int ret = -ENOENT;
239         struct crypto_alg *q;
240         
241         BUG_ON(!alg->cra_module);
242         
243         down_write(&crypto_alg_sem);
244         list_for_each_entry(q, &crypto_alg_list, cra_list) {
245                 if (alg == q) {
246                         list_del(&alg->cra_list);
247                         ret = 0;
248                         goto out;
249                 }
250         }
251 out:    
252         up_write(&crypto_alg_sem);
253         return ret;
254 }
255
256 int crypto_alg_available(const char *name, u32 flags)
257 {
258         int ret = 0;
259         struct crypto_alg *alg = crypto_alg_mod_lookup(name);
260         
261         if (alg) {
262                 crypto_alg_put(alg);
263                 ret = 1;
264         }
265         
266         return ret;
267 }
268
269 static int __init init_crypto(void)
270 {
271         printk(KERN_INFO "Initializing Cryptographic API\n");
272         crypto_init_proc();
273         return 0;
274 }
275
276 __initcall(init_crypto);
277
278 EXPORT_SYMBOL_GPL(crypto_register_alg);
279 EXPORT_SYMBOL_GPL(crypto_unregister_alg);
280 EXPORT_SYMBOL_GPL(crypto_alloc_tfm);
281 EXPORT_SYMBOL_GPL(crypto_free_tfm);
282 EXPORT_SYMBOL_GPL(crypto_alg_available);