diff options
Diffstat (limited to 'crypto/algif_hash.c')
-rw-r--r-- | crypto/algif_hash.c | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c new file mode 100644 index 000000000000..62122a1a2f7a --- /dev/null +++ b/crypto/algif_hash.c | |||
@@ -0,0 +1,319 @@ | |||
1 | /* | ||
2 | * algif_hash: User-space interface for hash algorithms | ||
3 | * | ||
4 | * This file provides the user-space API for hash algorithms. | ||
5 | * | ||
6 | * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the Free | ||
10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
11 | * any later version. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <crypto/hash.h> | ||
16 | #include <crypto/if_alg.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/net.h> | ||
22 | #include <net/sock.h> | ||
23 | |||
24 | struct hash_ctx { | ||
25 | struct af_alg_sgl sgl; | ||
26 | |||
27 | u8 *result; | ||
28 | |||
29 | struct af_alg_completion completion; | ||
30 | |||
31 | unsigned int len; | ||
32 | bool more; | ||
33 | |||
34 | struct ahash_request req; | ||
35 | }; | ||
36 | |||
37 | static int hash_sendmsg(struct kiocb *unused, struct socket *sock, | ||
38 | struct msghdr *msg, size_t ignored) | ||
39 | { | ||
40 | int limit = ALG_MAX_PAGES * PAGE_SIZE; | ||
41 | struct sock *sk = sock->sk; | ||
42 | struct alg_sock *ask = alg_sk(sk); | ||
43 | struct hash_ctx *ctx = ask->private; | ||
44 | unsigned long iovlen; | ||
45 | struct iovec *iov; | ||
46 | long copied = 0; | ||
47 | int err; | ||
48 | |||
49 | if (limit > sk->sk_sndbuf) | ||
50 | limit = sk->sk_sndbuf; | ||
51 | |||
52 | lock_sock(sk); | ||
53 | if (!ctx->more) { | ||
54 | err = crypto_ahash_init(&ctx->req); | ||
55 | if (err) | ||
56 | goto unlock; | ||
57 | } | ||
58 | |||
59 | ctx->more = 0; | ||
60 | |||
61 | for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0; | ||
62 | iovlen--, iov++) { | ||
63 | unsigned long seglen = iov->iov_len; | ||
64 | char __user *from = iov->iov_base; | ||
65 | |||
66 | while (seglen) { | ||
67 | int len = min_t(unsigned long, seglen, limit); | ||
68 | int newlen; | ||
69 | |||
70 | newlen = af_alg_make_sg(&ctx->sgl, from, len, 0); | ||
71 | if (newlen < 0) | ||
72 | goto unlock; | ||
73 | |||
74 | ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL, | ||
75 | newlen); | ||
76 | |||
77 | err = af_alg_wait_for_completion( | ||
78 | crypto_ahash_update(&ctx->req), | ||
79 | &ctx->completion); | ||
80 | |||
81 | af_alg_free_sg(&ctx->sgl); | ||
82 | |||
83 | if (err) | ||
84 | goto unlock; | ||
85 | |||
86 | seglen -= newlen; | ||
87 | from += newlen; | ||
88 | copied += newlen; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | err = 0; | ||
93 | |||
94 | ctx->more = msg->msg_flags & MSG_MORE; | ||
95 | if (!ctx->more) { | ||
96 | ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); | ||
97 | err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), | ||
98 | &ctx->completion); | ||
99 | } | ||
100 | |||
101 | unlock: | ||
102 | release_sock(sk); | ||
103 | |||
104 | return err ?: copied; | ||
105 | } | ||
106 | |||
107 | static ssize_t hash_sendpage(struct socket *sock, struct page *page, | ||
108 | int offset, size_t size, int flags) | ||
109 | { | ||
110 | struct sock *sk = sock->sk; | ||
111 | struct alg_sock *ask = alg_sk(sk); | ||
112 | struct hash_ctx *ctx = ask->private; | ||
113 | int err; | ||
114 | |||
115 | lock_sock(sk); | ||
116 | sg_init_table(ctx->sgl.sg, 1); | ||
117 | sg_set_page(ctx->sgl.sg, page, size, offset); | ||
118 | |||
119 | ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size); | ||
120 | |||
121 | if (!(flags & MSG_MORE)) { | ||
122 | if (ctx->more) | ||
123 | err = crypto_ahash_finup(&ctx->req); | ||
124 | else | ||
125 | err = crypto_ahash_digest(&ctx->req); | ||
126 | } else { | ||
127 | if (!ctx->more) { | ||
128 | err = crypto_ahash_init(&ctx->req); | ||
129 | if (err) | ||
130 | goto unlock; | ||
131 | } | ||
132 | |||
133 | err = crypto_ahash_update(&ctx->req); | ||
134 | } | ||
135 | |||
136 | err = af_alg_wait_for_completion(err, &ctx->completion); | ||
137 | if (err) | ||
138 | goto unlock; | ||
139 | |||
140 | ctx->more = flags & MSG_MORE; | ||
141 | |||
142 | unlock: | ||
143 | release_sock(sk); | ||
144 | |||
145 | return err ?: size; | ||
146 | } | ||
147 | |||
148 | static int hash_recvmsg(struct kiocb *unused, struct socket *sock, | ||
149 | struct msghdr *msg, size_t len, int flags) | ||
150 | { | ||
151 | struct sock *sk = sock->sk; | ||
152 | struct alg_sock *ask = alg_sk(sk); | ||
153 | struct hash_ctx *ctx = ask->private; | ||
154 | unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)); | ||
155 | int err; | ||
156 | |||
157 | if (len > ds) | ||
158 | len = ds; | ||
159 | else if (len < ds) | ||
160 | msg->msg_flags |= MSG_TRUNC; | ||
161 | |||
162 | lock_sock(sk); | ||
163 | if (ctx->more) { | ||
164 | ctx->more = 0; | ||
165 | ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); | ||
166 | err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), | ||
167 | &ctx->completion); | ||
168 | if (err) | ||
169 | goto unlock; | ||
170 | } | ||
171 | |||
172 | err = memcpy_toiovec(msg->msg_iov, ctx->result, len); | ||
173 | |||
174 | unlock: | ||
175 | release_sock(sk); | ||
176 | |||
177 | return err ?: len; | ||
178 | } | ||
179 | |||
180 | static int hash_accept(struct socket *sock, struct socket *newsock, int flags) | ||
181 | { | ||
182 | struct sock *sk = sock->sk; | ||
183 | struct alg_sock *ask = alg_sk(sk); | ||
184 | struct hash_ctx *ctx = ask->private; | ||
185 | struct ahash_request *req = &ctx->req; | ||
186 | char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))]; | ||
187 | struct sock *sk2; | ||
188 | struct alg_sock *ask2; | ||
189 | struct hash_ctx *ctx2; | ||
190 | int err; | ||
191 | |||
192 | err = crypto_ahash_export(req, state); | ||
193 | if (err) | ||
194 | return err; | ||
195 | |||
196 | err = af_alg_accept(ask->parent, newsock); | ||
197 | if (err) | ||
198 | return err; | ||
199 | |||
200 | sk2 = newsock->sk; | ||
201 | ask2 = alg_sk(sk2); | ||
202 | ctx2 = ask2->private; | ||
203 | ctx2->more = 1; | ||
204 | |||
205 | err = crypto_ahash_import(&ctx2->req, state); | ||
206 | if (err) { | ||
207 | sock_orphan(sk2); | ||
208 | sock_put(sk2); | ||
209 | } | ||
210 | |||
211 | return err; | ||
212 | } | ||
213 | |||
214 | static struct proto_ops algif_hash_ops = { | ||
215 | .family = PF_ALG, | ||
216 | |||
217 | .connect = sock_no_connect, | ||
218 | .socketpair = sock_no_socketpair, | ||
219 | .getname = sock_no_getname, | ||
220 | .ioctl = sock_no_ioctl, | ||
221 | .listen = sock_no_listen, | ||
222 | .shutdown = sock_no_shutdown, | ||
223 | .getsockopt = sock_no_getsockopt, | ||
224 | .mmap = sock_no_mmap, | ||
225 | .bind = sock_no_bind, | ||
226 | .setsockopt = sock_no_setsockopt, | ||
227 | .poll = sock_no_poll, | ||
228 | |||
229 | .release = af_alg_release, | ||
230 | .sendmsg = hash_sendmsg, | ||
231 | .sendpage = hash_sendpage, | ||
232 | .recvmsg = hash_recvmsg, | ||
233 | .accept = hash_accept, | ||
234 | }; | ||
235 | |||
236 | static void *hash_bind(const char *name, u32 type, u32 mask) | ||
237 | { | ||
238 | return crypto_alloc_ahash(name, type, mask); | ||
239 | } | ||
240 | |||
241 | static void hash_release(void *private) | ||
242 | { | ||
243 | crypto_free_ahash(private); | ||
244 | } | ||
245 | |||
246 | static int hash_setkey(void *private, const u8 *key, unsigned int keylen) | ||
247 | { | ||
248 | return crypto_ahash_setkey(private, key, keylen); | ||
249 | } | ||
250 | |||
251 | static void hash_sock_destruct(struct sock *sk) | ||
252 | { | ||
253 | struct alg_sock *ask = alg_sk(sk); | ||
254 | struct hash_ctx *ctx = ask->private; | ||
255 | |||
256 | sock_kfree_s(sk, ctx->result, | ||
257 | crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req))); | ||
258 | sock_kfree_s(sk, ctx, ctx->len); | ||
259 | af_alg_release_parent(sk); | ||
260 | } | ||
261 | |||
262 | static int hash_accept_parent(void *private, struct sock *sk) | ||
263 | { | ||
264 | struct hash_ctx *ctx; | ||
265 | struct alg_sock *ask = alg_sk(sk); | ||
266 | unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private); | ||
267 | unsigned ds = crypto_ahash_digestsize(private); | ||
268 | |||
269 | ctx = sock_kmalloc(sk, len, GFP_KERNEL); | ||
270 | if (!ctx) | ||
271 | return -ENOMEM; | ||
272 | |||
273 | ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL); | ||
274 | if (!ctx->result) { | ||
275 | sock_kfree_s(sk, ctx, len); | ||
276 | return -ENOMEM; | ||
277 | } | ||
278 | |||
279 | memset(ctx->result, 0, ds); | ||
280 | |||
281 | ctx->len = len; | ||
282 | ctx->more = 0; | ||
283 | af_alg_init_completion(&ctx->completion); | ||
284 | |||
285 | ask->private = ctx; | ||
286 | |||
287 | ahash_request_set_tfm(&ctx->req, private); | ||
288 | ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, | ||
289 | af_alg_complete, &ctx->completion); | ||
290 | |||
291 | sk->sk_destruct = hash_sock_destruct; | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static const struct af_alg_type algif_type_hash = { | ||
297 | .bind = hash_bind, | ||
298 | .release = hash_release, | ||
299 | .setkey = hash_setkey, | ||
300 | .accept = hash_accept_parent, | ||
301 | .ops = &algif_hash_ops, | ||
302 | .name = "hash", | ||
303 | .owner = THIS_MODULE | ||
304 | }; | ||
305 | |||
306 | static int __init algif_hash_init(void) | ||
307 | { | ||
308 | return af_alg_register_type(&algif_type_hash); | ||
309 | } | ||
310 | |||
311 | static void __exit algif_hash_exit(void) | ||
312 | { | ||
313 | int err = af_alg_unregister_type(&algif_type_hash); | ||
314 | BUG_ON(err); | ||
315 | } | ||
316 | |||
317 | module_init(algif_hash_init); | ||
318 | module_exit(algif_hash_exit); | ||
319 | MODULE_LICENSE("GPL"); | ||