diff options
Diffstat (limited to 'crypto/api.c')
-rw-r--r-- | crypto/api.c | 122 |
1 files changed, 110 insertions, 12 deletions
diff --git a/crypto/api.c b/crypto/api.c index c922090b4842..5a0d6a17cfd7 100644 --- a/crypto/api.c +++ b/crypto/api.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
19 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
20 | #include <linux/kmod.h> | 20 | #include <linux/kmod.h> |
21 | #include <linux/param.h> | ||
21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
22 | #include <linux/string.h> | 23 | #include <linux/string.h> |
23 | #include "internal.h" | 24 | #include "internal.h" |
@@ -27,6 +28,9 @@ EXPORT_SYMBOL_GPL(crypto_alg_list); | |||
27 | DECLARE_RWSEM(crypto_alg_sem); | 28 | DECLARE_RWSEM(crypto_alg_sem); |
28 | EXPORT_SYMBOL_GPL(crypto_alg_sem); | 29 | EXPORT_SYMBOL_GPL(crypto_alg_sem); |
29 | 30 | ||
31 | BLOCKING_NOTIFIER_HEAD(crypto_chain); | ||
32 | EXPORT_SYMBOL_GPL(crypto_chain); | ||
33 | |||
30 | static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg) | 34 | static inline struct crypto_alg *crypto_alg_get(struct crypto_alg *alg) |
31 | { | 35 | { |
32 | atomic_inc(&alg->cra_refcnt); | 36 | atomic_inc(&alg->cra_refcnt); |
@@ -39,27 +43,24 @@ static inline void crypto_alg_put(struct crypto_alg *alg) | |||
39 | alg->cra_destroy(alg); | 43 | alg->cra_destroy(alg); |
40 | } | 44 | } |
41 | 45 | ||
42 | static struct crypto_alg *crypto_mod_get(struct crypto_alg *alg) | 46 | struct crypto_alg *crypto_mod_get(struct crypto_alg *alg) |
43 | { | 47 | { |
44 | return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL; | 48 | return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL; |
45 | } | 49 | } |
50 | EXPORT_SYMBOL_GPL(crypto_mod_get); | ||
46 | 51 | ||
47 | static void crypto_mod_put(struct crypto_alg *alg) | 52 | void crypto_mod_put(struct crypto_alg *alg) |
48 | { | 53 | { |
49 | crypto_alg_put(alg); | 54 | crypto_alg_put(alg); |
50 | module_put(alg->cra_module); | 55 | module_put(alg->cra_module); |
51 | } | 56 | } |
57 | EXPORT_SYMBOL_GPL(crypto_mod_put); | ||
52 | 58 | ||
53 | static struct crypto_alg *crypto_alg_lookup(const char *name) | 59 | struct crypto_alg *__crypto_alg_lookup(const char *name) |
54 | { | 60 | { |
55 | struct crypto_alg *q, *alg = NULL; | 61 | struct crypto_alg *q, *alg = NULL; |
56 | int best = -1; | 62 | int best = -2; |
57 | 63 | ||
58 | if (!name) | ||
59 | return NULL; | ||
60 | |||
61 | down_read(&crypto_alg_sem); | ||
62 | |||
63 | list_for_each_entry(q, &crypto_alg_list, cra_list) { | 64 | list_for_each_entry(q, &crypto_alg_list, cra_list) { |
64 | int exact, fuzzy; | 65 | int exact, fuzzy; |
65 | 66 | ||
@@ -79,16 +80,113 @@ static struct crypto_alg *crypto_alg_lookup(const char *name) | |||
79 | if (exact) | 80 | if (exact) |
80 | break; | 81 | break; |
81 | } | 82 | } |
82 | 83 | ||
84 | return alg; | ||
85 | } | ||
86 | EXPORT_SYMBOL_GPL(__crypto_alg_lookup); | ||
87 | |||
88 | static void crypto_larval_destroy(struct crypto_alg *alg) | ||
89 | { | ||
90 | struct crypto_larval *larval = (void *)alg; | ||
91 | |||
92 | BUG_ON(!crypto_is_larval(alg)); | ||
93 | if (larval->adult) | ||
94 | crypto_mod_put(larval->adult); | ||
95 | kfree(larval); | ||
96 | } | ||
97 | |||
98 | static struct crypto_alg *crypto_larval_alloc(const char *name) | ||
99 | { | ||
100 | struct crypto_alg *alg; | ||
101 | struct crypto_larval *larval; | ||
102 | |||
103 | larval = kzalloc(sizeof(*larval), GFP_KERNEL); | ||
104 | if (!larval) | ||
105 | return NULL; | ||
106 | |||
107 | larval->alg.cra_flags = CRYPTO_ALG_LARVAL; | ||
108 | larval->alg.cra_priority = -1; | ||
109 | larval->alg.cra_destroy = crypto_larval_destroy; | ||
110 | |||
111 | atomic_set(&larval->alg.cra_refcnt, 2); | ||
112 | strlcpy(larval->alg.cra_name, name, CRYPTO_MAX_ALG_NAME); | ||
113 | init_completion(&larval->completion); | ||
114 | |||
115 | down_write(&crypto_alg_sem); | ||
116 | alg = __crypto_alg_lookup(name); | ||
117 | if (!alg) { | ||
118 | alg = &larval->alg; | ||
119 | list_add(&alg->cra_list, &crypto_alg_list); | ||
120 | } | ||
121 | up_write(&crypto_alg_sem); | ||
122 | |||
123 | if (alg != &larval->alg) | ||
124 | kfree(larval); | ||
125 | |||
126 | return alg; | ||
127 | } | ||
128 | |||
129 | static void crypto_larval_kill(struct crypto_alg *alg) | ||
130 | { | ||
131 | struct crypto_larval *larval = (void *)alg; | ||
132 | |||
133 | down_write(&crypto_alg_sem); | ||
134 | list_del(&alg->cra_list); | ||
135 | up_write(&crypto_alg_sem); | ||
136 | complete(&larval->completion); | ||
137 | crypto_alg_put(alg); | ||
138 | } | ||
139 | |||
140 | static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg) | ||
141 | { | ||
142 | struct crypto_larval *larval = (void *)alg; | ||
143 | |||
144 | wait_for_completion_interruptible_timeout(&larval->completion, 60 * HZ); | ||
145 | alg = larval->adult; | ||
146 | if (alg && !crypto_mod_get(alg)) | ||
147 | alg = NULL; | ||
148 | crypto_mod_put(&larval->alg); | ||
149 | |||
150 | return alg; | ||
151 | } | ||
152 | |||
153 | static struct crypto_alg *crypto_alg_lookup(const char *name) | ||
154 | { | ||
155 | struct crypto_alg *alg; | ||
156 | |||
157 | if (!name) | ||
158 | return NULL; | ||
159 | |||
160 | down_read(&crypto_alg_sem); | ||
161 | alg = __crypto_alg_lookup(name); | ||
83 | up_read(&crypto_alg_sem); | 162 | up_read(&crypto_alg_sem); |
163 | |||
84 | return alg; | 164 | return alg; |
85 | } | 165 | } |
86 | 166 | ||
87 | /* A far more intelligent version of this is planned. For now, just | 167 | /* A far more intelligent version of this is planned. For now, just |
88 | * try an exact match on the name of the algorithm. */ | 168 | * try an exact match on the name of the algorithm. */ |
89 | static inline struct crypto_alg *crypto_alg_mod_lookup(const char *name) | 169 | static struct crypto_alg *crypto_alg_mod_lookup(const char *name) |
90 | { | 170 | { |
91 | return try_then_request_module(crypto_alg_lookup(name), name); | 171 | struct crypto_alg *alg; |
172 | struct crypto_alg *larval; | ||
173 | |||
174 | alg = try_then_request_module(crypto_alg_lookup(name), name); | ||
175 | if (alg) | ||
176 | return crypto_is_larval(alg) ? crypto_larval_wait(alg) : alg; | ||
177 | |||
178 | larval = crypto_larval_alloc(name); | ||
179 | if (!larval || !crypto_is_larval(larval)) | ||
180 | return larval; | ||
181 | |||
182 | if (crypto_notify(CRYPTO_MSG_ALG_REQUEST, larval) == NOTIFY_STOP) | ||
183 | alg = crypto_larval_wait(larval); | ||
184 | else { | ||
185 | crypto_mod_put(larval); | ||
186 | alg = NULL; | ||
187 | } | ||
188 | crypto_larval_kill(larval); | ||
189 | return alg; | ||
92 | } | 190 | } |
93 | 191 | ||
94 | static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags) | 192 | static int crypto_init_flags(struct crypto_tfm *tfm, u32 flags) |