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