]> git.ipfire.org Git - people/arne_f/kernel.git/blob - crypto/algif_hash.c
MIPS: KVM: Add missing gfn range check
[people/arne_f/kernel.git] / crypto / algif_hash.c
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 = af_alg_wait_for_completion(crypto_ahash_init(&ctx->req),
55 &ctx->completion);
56 if (err)
57 goto unlock;
58 }
59
60 ctx->more = 0;
61
62 for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
63 iovlen--, iov++) {
64 unsigned long seglen = iov->iov_len;
65 char __user *from = iov->iov_base;
66
67 while (seglen) {
68 int len = min_t(unsigned long, seglen, limit);
69 int newlen;
70
71 newlen = af_alg_make_sg(&ctx->sgl, from, len, 0);
72 if (newlen < 0) {
73 err = copied ? 0 : newlen;
74 goto unlock;
75 }
76
77 ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL,
78 newlen);
79
80 err = af_alg_wait_for_completion(
81 crypto_ahash_update(&ctx->req),
82 &ctx->completion);
83
84 af_alg_free_sg(&ctx->sgl);
85
86 if (err)
87 goto unlock;
88
89 seglen -= newlen;
90 from += newlen;
91 copied += newlen;
92 }
93 }
94
95 err = 0;
96
97 ctx->more = msg->msg_flags & MSG_MORE;
98 if (!ctx->more) {
99 ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
100 err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
101 &ctx->completion);
102 }
103
104 unlock:
105 release_sock(sk);
106
107 return err ?: copied;
108 }
109
110 static ssize_t hash_sendpage(struct socket *sock, struct page *page,
111 int offset, size_t size, int flags)
112 {
113 struct sock *sk = sock->sk;
114 struct alg_sock *ask = alg_sk(sk);
115 struct hash_ctx *ctx = ask->private;
116 int err;
117
118 if (flags & MSG_SENDPAGE_NOTLAST)
119 flags |= MSG_MORE;
120
121 lock_sock(sk);
122 sg_init_table(ctx->sgl.sg, 1);
123 sg_set_page(ctx->sgl.sg, page, size, offset);
124
125 ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size);
126
127 if (!(flags & MSG_MORE)) {
128 if (ctx->more)
129 err = crypto_ahash_finup(&ctx->req);
130 else
131 err = crypto_ahash_digest(&ctx->req);
132 } else {
133 if (!ctx->more) {
134 err = crypto_ahash_init(&ctx->req);
135 err = af_alg_wait_for_completion(err, &ctx->completion);
136 if (err)
137 goto unlock;
138 }
139
140 err = crypto_ahash_update(&ctx->req);
141 }
142
143 err = af_alg_wait_for_completion(err, &ctx->completion);
144 if (err)
145 goto unlock;
146
147 ctx->more = flags & MSG_MORE;
148
149 unlock:
150 release_sock(sk);
151
152 return err ?: size;
153 }
154
155 static int hash_recvmsg(struct kiocb *unused, struct socket *sock,
156 struct msghdr *msg, size_t len, int flags)
157 {
158 struct sock *sk = sock->sk;
159 struct alg_sock *ask = alg_sk(sk);
160 struct hash_ctx *ctx = ask->private;
161 unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));
162 int err;
163
164 if (len > ds)
165 len = ds;
166 else if (len < ds)
167 msg->msg_flags |= MSG_TRUNC;
168
169 lock_sock(sk);
170 if (ctx->more) {
171 ctx->more = 0;
172 ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);
173 err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req),
174 &ctx->completion);
175 if (err)
176 goto unlock;
177 }
178
179 err = memcpy_toiovec(msg->msg_iov, ctx->result, len);
180
181 unlock:
182 release_sock(sk);
183
184 return err ?: len;
185 }
186
187 static int hash_accept(struct socket *sock, struct socket *newsock, int flags)
188 {
189 struct sock *sk = sock->sk;
190 struct alg_sock *ask = alg_sk(sk);
191 struct hash_ctx *ctx = ask->private;
192 struct ahash_request *req = &ctx->req;
193 char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))];
194 struct sock *sk2;
195 struct alg_sock *ask2;
196 struct hash_ctx *ctx2;
197 bool more;
198 int err;
199
200 lock_sock(sk);
201 more = ctx->more;
202 err = more ? crypto_ahash_export(req, state) : 0;
203 release_sock(sk);
204
205 if (err)
206 return err;
207
208 err = af_alg_accept(ask->parent, newsock);
209 if (err)
210 return err;
211
212 sk2 = newsock->sk;
213 ask2 = alg_sk(sk2);
214 ctx2 = ask2->private;
215 ctx2->more = more;
216
217 if (!more)
218 return err;
219
220 err = crypto_ahash_import(&ctx2->req, state);
221 if (err) {
222 sock_orphan(sk2);
223 sock_put(sk2);
224 }
225
226 return err;
227 }
228
229 static struct proto_ops algif_hash_ops = {
230 .family = PF_ALG,
231
232 .connect = sock_no_connect,
233 .socketpair = sock_no_socketpair,
234 .getname = sock_no_getname,
235 .ioctl = sock_no_ioctl,
236 .listen = sock_no_listen,
237 .shutdown = sock_no_shutdown,
238 .getsockopt = sock_no_getsockopt,
239 .mmap = sock_no_mmap,
240 .bind = sock_no_bind,
241 .setsockopt = sock_no_setsockopt,
242 .poll = sock_no_poll,
243
244 .release = af_alg_release,
245 .sendmsg = hash_sendmsg,
246 .sendpage = hash_sendpage,
247 .recvmsg = hash_recvmsg,
248 .accept = hash_accept,
249 };
250
251 static void *hash_bind(const char *name, u32 type, u32 mask)
252 {
253 return crypto_alloc_ahash(name, type, mask);
254 }
255
256 static void hash_release(void *private)
257 {
258 crypto_free_ahash(private);
259 }
260
261 static int hash_setkey(void *private, const u8 *key, unsigned int keylen)
262 {
263 return crypto_ahash_setkey(private, key, keylen);
264 }
265
266 static void hash_sock_destruct(struct sock *sk)
267 {
268 struct alg_sock *ask = alg_sk(sk);
269 struct hash_ctx *ctx = ask->private;
270
271 sock_kfree_s(sk, ctx->result,
272 crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)));
273 sock_kfree_s(sk, ctx, ctx->len);
274 af_alg_release_parent(sk);
275 }
276
277 static int hash_accept_parent(void *private, struct sock *sk)
278 {
279 struct hash_ctx *ctx;
280 struct alg_sock *ask = alg_sk(sk);
281 unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(private);
282 unsigned ds = crypto_ahash_digestsize(private);
283
284 ctx = sock_kmalloc(sk, len, GFP_KERNEL);
285 if (!ctx)
286 return -ENOMEM;
287
288 ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL);
289 if (!ctx->result) {
290 sock_kfree_s(sk, ctx, len);
291 return -ENOMEM;
292 }
293
294 memset(ctx->result, 0, ds);
295
296 ctx->len = len;
297 ctx->more = 0;
298 af_alg_init_completion(&ctx->completion);
299
300 ask->private = ctx;
301
302 ahash_request_set_tfm(&ctx->req, private);
303 ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,
304 af_alg_complete, &ctx->completion);
305
306 sk->sk_destruct = hash_sock_destruct;
307
308 return 0;
309 }
310
311 static const struct af_alg_type algif_type_hash = {
312 .bind = hash_bind,
313 .release = hash_release,
314 .setkey = hash_setkey,
315 .accept = hash_accept_parent,
316 .ops = &algif_hash_ops,
317 .name = "hash",
318 .owner = THIS_MODULE
319 };
320
321 static int __init algif_hash_init(void)
322 {
323 return af_alg_register_type(&algif_type_hash);
324 }
325
326 static void __exit algif_hash_exit(void)
327 {
328 int err = af_alg_unregister_type(&algif_type_hash);
329 BUG_ON(err);
330 }
331
332 module_init(algif_hash_init);
333 module_exit(algif_hash_exit);
334 MODULE_LICENSE("GPL");