Commit | Line | Data |
---|---|---|
fe869cdb HX |
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 | ||
6de62f15 HX |
37 | struct algif_hash_tfm { |
38 | struct crypto_ahash *hash; | |
39 | bool has_key; | |
40 | }; | |
41 | ||
1b784140 YX |
42 | static int hash_sendmsg(struct socket *sock, struct msghdr *msg, |
43 | size_t ignored) | |
fe869cdb HX |
44 | { |
45 | int limit = ALG_MAX_PAGES * PAGE_SIZE; | |
46 | struct sock *sk = sock->sk; | |
47 | struct alg_sock *ask = alg_sk(sk); | |
48 | struct hash_ctx *ctx = ask->private; | |
fe869cdb HX |
49 | long copied = 0; |
50 | int err; | |
51 | ||
52 | if (limit > sk->sk_sndbuf) | |
53 | limit = sk->sk_sndbuf; | |
54 | ||
55 | lock_sock(sk); | |
56 | if (!ctx->more) { | |
57 | err = crypto_ahash_init(&ctx->req); | |
58 | if (err) | |
59 | goto unlock; | |
60 | } | |
61 | ||
62 | ctx->more = 0; | |
63 | ||
01e97e65 AV |
64 | while (msg_data_left(msg)) { |
65 | int len = msg_data_left(msg); | |
fe869cdb | 66 | |
1d10eb2f AV |
67 | if (len > limit) |
68 | len = limit; | |
fe869cdb | 69 | |
1d10eb2f AV |
70 | len = af_alg_make_sg(&ctx->sgl, &msg->msg_iter, len); |
71 | if (len < 0) { | |
72 | err = copied ? 0 : len; | |
73 | goto unlock; | |
74 | } | |
fe869cdb | 75 | |
1d10eb2f | 76 | ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL, len); |
fe869cdb | 77 | |
1d10eb2f AV |
78 | err = af_alg_wait_for_completion(crypto_ahash_update(&ctx->req), |
79 | &ctx->completion); | |
80 | af_alg_free_sg(&ctx->sgl); | |
81 | if (err) | |
82 | goto unlock; | |
fe869cdb | 83 | |
1d10eb2f AV |
84 | copied += len; |
85 | iov_iter_advance(&msg->msg_iter, len); | |
fe869cdb HX |
86 | } |
87 | ||
88 | err = 0; | |
89 | ||
90 | ctx->more = msg->msg_flags & MSG_MORE; | |
91 | if (!ctx->more) { | |
92 | ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); | |
93 | err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), | |
94 | &ctx->completion); | |
95 | } | |
96 | ||
97 | unlock: | |
98 | release_sock(sk); | |
99 | ||
100 | return err ?: copied; | |
101 | } | |
102 | ||
103 | static ssize_t hash_sendpage(struct socket *sock, struct page *page, | |
104 | int offset, size_t size, int flags) | |
105 | { | |
106 | struct sock *sk = sock->sk; | |
107 | struct alg_sock *ask = alg_sk(sk); | |
108 | struct hash_ctx *ctx = ask->private; | |
109 | int err; | |
110 | ||
d3f7d56a SL |
111 | if (flags & MSG_SENDPAGE_NOTLAST) |
112 | flags |= MSG_MORE; | |
113 | ||
fe869cdb HX |
114 | lock_sock(sk); |
115 | sg_init_table(ctx->sgl.sg, 1); | |
116 | sg_set_page(ctx->sgl.sg, page, size, offset); | |
117 | ||
118 | ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, ctx->result, size); | |
119 | ||
120 | if (!(flags & MSG_MORE)) { | |
121 | if (ctx->more) | |
122 | err = crypto_ahash_finup(&ctx->req); | |
123 | else | |
124 | err = crypto_ahash_digest(&ctx->req); | |
125 | } else { | |
126 | if (!ctx->more) { | |
127 | err = crypto_ahash_init(&ctx->req); | |
128 | if (err) | |
129 | goto unlock; | |
130 | } | |
131 | ||
132 | err = crypto_ahash_update(&ctx->req); | |
133 | } | |
134 | ||
135 | err = af_alg_wait_for_completion(err, &ctx->completion); | |
136 | if (err) | |
137 | goto unlock; | |
138 | ||
139 | ctx->more = flags & MSG_MORE; | |
140 | ||
141 | unlock: | |
142 | release_sock(sk); | |
143 | ||
144 | return err ?: size; | |
145 | } | |
146 | ||
1b784140 YX |
147 | static int hash_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, |
148 | int flags) | |
fe869cdb HX |
149 | { |
150 | struct sock *sk = sock->sk; | |
151 | struct alg_sock *ask = alg_sk(sk); | |
152 | struct hash_ctx *ctx = ask->private; | |
153 | unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req)); | |
154 | int err; | |
155 | ||
156 | if (len > ds) | |
157 | len = ds; | |
158 | else if (len < ds) | |
159 | msg->msg_flags |= MSG_TRUNC; | |
160 | ||
161 | lock_sock(sk); | |
162 | if (ctx->more) { | |
163 | ctx->more = 0; | |
164 | ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); | |
165 | err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), | |
166 | &ctx->completion); | |
167 | if (err) | |
168 | goto unlock; | |
169 | } | |
170 | ||
7eab8d9e | 171 | err = memcpy_to_msg(msg, ctx->result, len); |
fe869cdb HX |
172 | |
173 | unlock: | |
174 | release_sock(sk); | |
175 | ||
176 | return err ?: len; | |
177 | } | |
178 | ||
179 | static int hash_accept(struct socket *sock, struct socket *newsock, int flags) | |
180 | { | |
181 | struct sock *sk = sock->sk; | |
182 | struct alg_sock *ask = alg_sk(sk); | |
183 | struct hash_ctx *ctx = ask->private; | |
184 | struct ahash_request *req = &ctx->req; | |
185 | char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))]; | |
186 | struct sock *sk2; | |
187 | struct alg_sock *ask2; | |
188 | struct hash_ctx *ctx2; | |
4afa5f96 | 189 | bool more; |
fe869cdb HX |
190 | int err; |
191 | ||
4afa5f96 HX |
192 | lock_sock(sk); |
193 | more = ctx->more; | |
194 | err = more ? crypto_ahash_export(req, state) : 0; | |
195 | release_sock(sk); | |
196 | ||
fe869cdb HX |
197 | if (err) |
198 | return err; | |
199 | ||
200 | err = af_alg_accept(ask->parent, newsock); | |
201 | if (err) | |
202 | return err; | |
203 | ||
204 | sk2 = newsock->sk; | |
205 | ask2 = alg_sk(sk2); | |
206 | ctx2 = ask2->private; | |
4afa5f96 HX |
207 | ctx2->more = more; |
208 | ||
209 | if (!more) | |
210 | return err; | |
fe869cdb HX |
211 | |
212 | err = crypto_ahash_import(&ctx2->req, state); | |
213 | if (err) { | |
214 | sock_orphan(sk2); | |
215 | sock_put(sk2); | |
216 | } | |
217 | ||
218 | return err; | |
219 | } | |
220 | ||
221 | static struct proto_ops algif_hash_ops = { | |
222 | .family = PF_ALG, | |
223 | ||
224 | .connect = sock_no_connect, | |
225 | .socketpair = sock_no_socketpair, | |
226 | .getname = sock_no_getname, | |
227 | .ioctl = sock_no_ioctl, | |
228 | .listen = sock_no_listen, | |
229 | .shutdown = sock_no_shutdown, | |
230 | .getsockopt = sock_no_getsockopt, | |
231 | .mmap = sock_no_mmap, | |
232 | .bind = sock_no_bind, | |
233 | .setsockopt = sock_no_setsockopt, | |
234 | .poll = sock_no_poll, | |
235 | ||
236 | .release = af_alg_release, | |
237 | .sendmsg = hash_sendmsg, | |
238 | .sendpage = hash_sendpage, | |
239 | .recvmsg = hash_recvmsg, | |
240 | .accept = hash_accept, | |
241 | }; | |
242 | ||
6de62f15 HX |
243 | static int hash_check_key(struct socket *sock) |
244 | { | |
ad46d7e3 | 245 | int err = 0; |
6de62f15 HX |
246 | struct sock *psk; |
247 | struct alg_sock *pask; | |
248 | struct algif_hash_tfm *tfm; | |
249 | struct sock *sk = sock->sk; | |
250 | struct alg_sock *ask = alg_sk(sk); | |
251 | ||
ad46d7e3 | 252 | lock_sock(sk); |
6de62f15 | 253 | if (ask->refcnt) |
ad46d7e3 | 254 | goto unlock_child; |
6de62f15 HX |
255 | |
256 | psk = ask->parent; | |
257 | pask = alg_sk(ask->parent); | |
258 | tfm = pask->private; | |
259 | ||
260 | err = -ENOKEY; | |
ad46d7e3 | 261 | lock_sock_nested(psk, SINGLE_DEPTH_NESTING); |
6de62f15 HX |
262 | if (!tfm->has_key) |
263 | goto unlock; | |
264 | ||
265 | if (!pask->refcnt++) | |
266 | sock_hold(psk); | |
267 | ||
268 | ask->refcnt = 1; | |
269 | sock_put(psk); | |
270 | ||
271 | err = 0; | |
272 | ||
273 | unlock: | |
274 | release_sock(psk); | |
ad46d7e3 HX |
275 | unlock_child: |
276 | release_sock(sk); | |
6de62f15 HX |
277 | |
278 | return err; | |
279 | } | |
280 | ||
281 | static int hash_sendmsg_nokey(struct socket *sock, struct msghdr *msg, | |
282 | size_t size) | |
283 | { | |
284 | int err; | |
285 | ||
286 | err = hash_check_key(sock); | |
287 | if (err) | |
288 | return err; | |
289 | ||
290 | return hash_sendmsg(sock, msg, size); | |
291 | } | |
292 | ||
293 | static ssize_t hash_sendpage_nokey(struct socket *sock, struct page *page, | |
294 | int offset, size_t size, int flags) | |
295 | { | |
296 | int err; | |
297 | ||
298 | err = hash_check_key(sock); | |
299 | if (err) | |
300 | return err; | |
301 | ||
302 | return hash_sendpage(sock, page, offset, size, flags); | |
303 | } | |
304 | ||
305 | static int hash_recvmsg_nokey(struct socket *sock, struct msghdr *msg, | |
306 | size_t ignored, int flags) | |
307 | { | |
308 | int err; | |
309 | ||
310 | err = hash_check_key(sock); | |
311 | if (err) | |
312 | return err; | |
313 | ||
314 | return hash_recvmsg(sock, msg, ignored, flags); | |
315 | } | |
316 | ||
317 | static int hash_accept_nokey(struct socket *sock, struct socket *newsock, | |
318 | int flags) | |
319 | { | |
320 | int err; | |
321 | ||
322 | err = hash_check_key(sock); | |
323 | if (err) | |
324 | return err; | |
325 | ||
326 | return hash_accept(sock, newsock, flags); | |
327 | } | |
328 | ||
329 | static struct proto_ops algif_hash_ops_nokey = { | |
330 | .family = PF_ALG, | |
331 | ||
332 | .connect = sock_no_connect, | |
333 | .socketpair = sock_no_socketpair, | |
334 | .getname = sock_no_getname, | |
335 | .ioctl = sock_no_ioctl, | |
336 | .listen = sock_no_listen, | |
337 | .shutdown = sock_no_shutdown, | |
338 | .getsockopt = sock_no_getsockopt, | |
339 | .mmap = sock_no_mmap, | |
340 | .bind = sock_no_bind, | |
341 | .setsockopt = sock_no_setsockopt, | |
342 | .poll = sock_no_poll, | |
343 | ||
344 | .release = af_alg_release, | |
345 | .sendmsg = hash_sendmsg_nokey, | |
346 | .sendpage = hash_sendpage_nokey, | |
347 | .recvmsg = hash_recvmsg_nokey, | |
348 | .accept = hash_accept_nokey, | |
349 | }; | |
350 | ||
fe869cdb HX |
351 | static void *hash_bind(const char *name, u32 type, u32 mask) |
352 | { | |
6de62f15 HX |
353 | struct algif_hash_tfm *tfm; |
354 | struct crypto_ahash *hash; | |
355 | ||
356 | tfm = kzalloc(sizeof(*tfm), GFP_KERNEL); | |
357 | if (!tfm) | |
358 | return ERR_PTR(-ENOMEM); | |
359 | ||
360 | hash = crypto_alloc_ahash(name, type, mask); | |
361 | if (IS_ERR(hash)) { | |
362 | kfree(tfm); | |
363 | return ERR_CAST(hash); | |
364 | } | |
365 | ||
366 | tfm->hash = hash; | |
367 | ||
368 | return tfm; | |
fe869cdb HX |
369 | } |
370 | ||
371 | static void hash_release(void *private) | |
372 | { | |
6de62f15 HX |
373 | struct algif_hash_tfm *tfm = private; |
374 | ||
375 | crypto_free_ahash(tfm->hash); | |
376 | kfree(tfm); | |
fe869cdb HX |
377 | } |
378 | ||
379 | static int hash_setkey(void *private, const u8 *key, unsigned int keylen) | |
380 | { | |
6de62f15 HX |
381 | struct algif_hash_tfm *tfm = private; |
382 | int err; | |
383 | ||
384 | err = crypto_ahash_setkey(tfm->hash, key, keylen); | |
385 | tfm->has_key = !err; | |
386 | ||
387 | return err; | |
fe869cdb HX |
388 | } |
389 | ||
f1d84af1 | 390 | static void hash_sock_destruct(struct sock *sk) |
fe869cdb HX |
391 | { |
392 | struct alg_sock *ask = alg_sk(sk); | |
393 | struct hash_ctx *ctx = ask->private; | |
394 | ||
79e88659 DB |
395 | sock_kzfree_s(sk, ctx->result, |
396 | crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req))); | |
fe869cdb | 397 | sock_kfree_s(sk, ctx, ctx->len); |
6de62f15 HX |
398 | af_alg_release_parent(sk); |
399 | } | |
400 | ||
f1d84af1 | 401 | static int hash_accept_parent_nokey(void *private, struct sock *sk) |
fe869cdb HX |
402 | { |
403 | struct hash_ctx *ctx; | |
404 | struct alg_sock *ask = alg_sk(sk); | |
6de62f15 HX |
405 | struct algif_hash_tfm *tfm = private; |
406 | struct crypto_ahash *hash = tfm->hash; | |
407 | unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(hash); | |
408 | unsigned ds = crypto_ahash_digestsize(hash); | |
fe869cdb HX |
409 | |
410 | ctx = sock_kmalloc(sk, len, GFP_KERNEL); | |
411 | if (!ctx) | |
412 | return -ENOMEM; | |
413 | ||
414 | ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL); | |
415 | if (!ctx->result) { | |
416 | sock_kfree_s(sk, ctx, len); | |
417 | return -ENOMEM; | |
418 | } | |
419 | ||
420 | memset(ctx->result, 0, ds); | |
421 | ||
422 | ctx->len = len; | |
423 | ctx->more = 0; | |
424 | af_alg_init_completion(&ctx->completion); | |
425 | ||
426 | ask->private = ctx; | |
427 | ||
6de62f15 | 428 | ahash_request_set_tfm(&ctx->req, hash); |
fe869cdb HX |
429 | ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, |
430 | af_alg_complete, &ctx->completion); | |
431 | ||
432 | sk->sk_destruct = hash_sock_destruct; | |
433 | ||
434 | return 0; | |
435 | } | |
436 | ||
6de62f15 HX |
437 | static int hash_accept_parent(void *private, struct sock *sk) |
438 | { | |
439 | struct algif_hash_tfm *tfm = private; | |
440 | ||
441 | if (!tfm->has_key && crypto_ahash_has_setkey(tfm->hash)) | |
442 | return -ENOKEY; | |
443 | ||
f1d84af1 | 444 | return hash_accept_parent_nokey(private, sk); |
6de62f15 HX |
445 | } |
446 | ||
fe869cdb HX |
447 | static const struct af_alg_type algif_type_hash = { |
448 | .bind = hash_bind, | |
449 | .release = hash_release, | |
450 | .setkey = hash_setkey, | |
451 | .accept = hash_accept_parent, | |
6de62f15 | 452 | .accept_nokey = hash_accept_parent_nokey, |
fe869cdb | 453 | .ops = &algif_hash_ops, |
6de62f15 | 454 | .ops_nokey = &algif_hash_ops_nokey, |
fe869cdb HX |
455 | .name = "hash", |
456 | .owner = THIS_MODULE | |
457 | }; | |
458 | ||
459 | static int __init algif_hash_init(void) | |
460 | { | |
461 | return af_alg_register_type(&algif_type_hash); | |
462 | } | |
463 | ||
464 | static void __exit algif_hash_exit(void) | |
465 | { | |
466 | int err = af_alg_unregister_type(&algif_type_hash); | |
467 | BUG_ON(err); | |
468 | } | |
469 | ||
470 | module_init(algif_hash_init); | |
471 | module_exit(algif_hash_exit); | |
472 | MODULE_LICENSE("GPL"); |