~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to crypto/algif_hash.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

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