]>
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 | { | |
245 | int err; | |
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 | ||
252 | if (ask->refcnt) | |
253 | return 0; | |
254 | ||
255 | psk = ask->parent; | |
256 | pask = alg_sk(ask->parent); | |
257 | tfm = pask->private; | |
258 | ||
259 | err = -ENOKEY; | |
260 | lock_sock(psk); | |
261 | if (!tfm->has_key) | |
262 | goto unlock; | |
263 | ||
264 | if (!pask->refcnt++) | |
265 | sock_hold(psk); | |
266 | ||
267 | ask->refcnt = 1; | |
268 | sock_put(psk); | |
269 | ||
270 | err = 0; | |
271 | ||
272 | unlock: | |
273 | release_sock(psk); | |
274 | ||
275 | return err; | |
276 | } | |
277 | ||
278 | static int hash_sendmsg_nokey(struct socket *sock, struct msghdr *msg, | |
279 | size_t size) | |
280 | { | |
281 | int err; | |
282 | ||
283 | err = hash_check_key(sock); | |
284 | if (err) | |
285 | return err; | |
286 | ||
287 | return hash_sendmsg(sock, msg, size); | |
288 | } | |
289 | ||
290 | static ssize_t hash_sendpage_nokey(struct socket *sock, struct page *page, | |
291 | int offset, size_t size, int flags) | |
292 | { | |
293 | int err; | |
294 | ||
295 | err = hash_check_key(sock); | |
296 | if (err) | |
297 | return err; | |
298 | ||
299 | return hash_sendpage(sock, page, offset, size, flags); | |
300 | } | |
301 | ||
302 | static int hash_recvmsg_nokey(struct socket *sock, struct msghdr *msg, | |
303 | size_t ignored, int flags) | |
304 | { | |
305 | int err; | |
306 | ||
307 | err = hash_check_key(sock); | |
308 | if (err) | |
309 | return err; | |
310 | ||
311 | return hash_recvmsg(sock, msg, ignored, flags); | |
312 | } | |
313 | ||
314 | static int hash_accept_nokey(struct socket *sock, struct socket *newsock, | |
315 | int flags) | |
316 | { | |
317 | int err; | |
318 | ||
319 | err = hash_check_key(sock); | |
320 | if (err) | |
321 | return err; | |
322 | ||
323 | return hash_accept(sock, newsock, flags); | |
324 | } | |
325 | ||
326 | static struct proto_ops algif_hash_ops_nokey = { | |
327 | .family = PF_ALG, | |
328 | ||
329 | .connect = sock_no_connect, | |
330 | .socketpair = sock_no_socketpair, | |
331 | .getname = sock_no_getname, | |
332 | .ioctl = sock_no_ioctl, | |
333 | .listen = sock_no_listen, | |
334 | .shutdown = sock_no_shutdown, | |
335 | .getsockopt = sock_no_getsockopt, | |
336 | .mmap = sock_no_mmap, | |
337 | .bind = sock_no_bind, | |
338 | .setsockopt = sock_no_setsockopt, | |
339 | .poll = sock_no_poll, | |
340 | ||
341 | .release = af_alg_release, | |
342 | .sendmsg = hash_sendmsg_nokey, | |
343 | .sendpage = hash_sendpage_nokey, | |
344 | .recvmsg = hash_recvmsg_nokey, | |
345 | .accept = hash_accept_nokey, | |
346 | }; | |
347 | ||
fe869cdb HX |
348 | static void *hash_bind(const char *name, u32 type, u32 mask) |
349 | { | |
6de62f15 HX |
350 | struct algif_hash_tfm *tfm; |
351 | struct crypto_ahash *hash; | |
352 | ||
353 | tfm = kzalloc(sizeof(*tfm), GFP_KERNEL); | |
354 | if (!tfm) | |
355 | return ERR_PTR(-ENOMEM); | |
356 | ||
357 | hash = crypto_alloc_ahash(name, type, mask); | |
358 | if (IS_ERR(hash)) { | |
359 | kfree(tfm); | |
360 | return ERR_CAST(hash); | |
361 | } | |
362 | ||
363 | tfm->hash = hash; | |
364 | ||
365 | return tfm; | |
fe869cdb HX |
366 | } |
367 | ||
368 | static void hash_release(void *private) | |
369 | { | |
6de62f15 HX |
370 | struct algif_hash_tfm *tfm = private; |
371 | ||
372 | crypto_free_ahash(tfm->hash); | |
373 | kfree(tfm); | |
fe869cdb HX |
374 | } |
375 | ||
376 | static int hash_setkey(void *private, const u8 *key, unsigned int keylen) | |
377 | { | |
6de62f15 HX |
378 | struct algif_hash_tfm *tfm = private; |
379 | int err; | |
380 | ||
381 | err = crypto_ahash_setkey(tfm->hash, key, keylen); | |
382 | tfm->has_key = !err; | |
383 | ||
384 | return err; | |
fe869cdb HX |
385 | } |
386 | ||
6de62f15 | 387 | static void hash_sock_destruct_common(struct sock *sk) |
fe869cdb HX |
388 | { |
389 | struct alg_sock *ask = alg_sk(sk); | |
390 | struct hash_ctx *ctx = ask->private; | |
391 | ||
79e88659 DB |
392 | sock_kzfree_s(sk, ctx->result, |
393 | crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req))); | |
fe869cdb | 394 | sock_kfree_s(sk, ctx, ctx->len); |
6de62f15 HX |
395 | } |
396 | ||
397 | static void hash_sock_destruct(struct sock *sk) | |
398 | { | |
399 | hash_sock_destruct_common(sk); | |
fe869cdb HX |
400 | af_alg_release_parent(sk); |
401 | } | |
402 | ||
6de62f15 HX |
403 | static void hash_release_parent_nokey(struct sock *sk) |
404 | { | |
405 | struct alg_sock *ask = alg_sk(sk); | |
406 | ||
407 | if (!ask->refcnt) { | |
408 | sock_put(ask->parent); | |
409 | return; | |
410 | } | |
411 | ||
412 | af_alg_release_parent(sk); | |
413 | } | |
414 | ||
415 | static void hash_sock_destruct_nokey(struct sock *sk) | |
416 | { | |
417 | hash_sock_destruct_common(sk); | |
418 | hash_release_parent_nokey(sk); | |
419 | } | |
420 | ||
421 | static int hash_accept_parent_common(void *private, struct sock *sk) | |
fe869cdb HX |
422 | { |
423 | struct hash_ctx *ctx; | |
424 | struct alg_sock *ask = alg_sk(sk); | |
6de62f15 HX |
425 | struct algif_hash_tfm *tfm = private; |
426 | struct crypto_ahash *hash = tfm->hash; | |
427 | unsigned len = sizeof(*ctx) + crypto_ahash_reqsize(hash); | |
428 | unsigned ds = crypto_ahash_digestsize(hash); | |
fe869cdb HX |
429 | |
430 | ctx = sock_kmalloc(sk, len, GFP_KERNEL); | |
431 | if (!ctx) | |
432 | return -ENOMEM; | |
433 | ||
434 | ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL); | |
435 | if (!ctx->result) { | |
436 | sock_kfree_s(sk, ctx, len); | |
437 | return -ENOMEM; | |
438 | } | |
439 | ||
440 | memset(ctx->result, 0, ds); | |
441 | ||
442 | ctx->len = len; | |
443 | ctx->more = 0; | |
444 | af_alg_init_completion(&ctx->completion); | |
445 | ||
446 | ask->private = ctx; | |
447 | ||
6de62f15 | 448 | ahash_request_set_tfm(&ctx->req, hash); |
fe869cdb HX |
449 | ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, |
450 | af_alg_complete, &ctx->completion); | |
451 | ||
452 | sk->sk_destruct = hash_sock_destruct; | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
6de62f15 HX |
457 | static int hash_accept_parent(void *private, struct sock *sk) |
458 | { | |
459 | struct algif_hash_tfm *tfm = private; | |
460 | ||
461 | if (!tfm->has_key && crypto_ahash_has_setkey(tfm->hash)) | |
462 | return -ENOKEY; | |
463 | ||
464 | return hash_accept_parent_common(private, sk); | |
465 | } | |
466 | ||
467 | static int hash_accept_parent_nokey(void *private, struct sock *sk) | |
468 | { | |
469 | int err; | |
470 | ||
471 | err = hash_accept_parent_common(private, sk); | |
472 | if (err) | |
473 | goto out; | |
474 | ||
475 | sk->sk_destruct = hash_sock_destruct_nokey; | |
476 | ||
477 | out: | |
478 | return err; | |
479 | } | |
480 | ||
fe869cdb HX |
481 | static const struct af_alg_type algif_type_hash = { |
482 | .bind = hash_bind, | |
483 | .release = hash_release, | |
484 | .setkey = hash_setkey, | |
485 | .accept = hash_accept_parent, | |
6de62f15 | 486 | .accept_nokey = hash_accept_parent_nokey, |
fe869cdb | 487 | .ops = &algif_hash_ops, |
6de62f15 | 488 | .ops_nokey = &algif_hash_ops_nokey, |
fe869cdb HX |
489 | .name = "hash", |
490 | .owner = THIS_MODULE | |
491 | }; | |
492 | ||
493 | static int __init algif_hash_init(void) | |
494 | { | |
495 | return af_alg_register_type(&algif_type_hash); | |
496 | } | |
497 | ||
498 | static void __exit algif_hash_exit(void) | |
499 | { | |
500 | int err = af_alg_unregister_type(&algif_type_hash); | |
501 | BUG_ON(err); | |
502 | } | |
503 | ||
504 | module_init(algif_hash_init); | |
505 | module_exit(algif_hash_exit); | |
506 | MODULE_LICENSE("GPL"); |