aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2010-10-19 09:12:39 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2010-11-19 04:47:57 -0500
commit03c8efc1ffeb6b82a22c1af8dd908af349563314 (patch)
treea2538f6c5151ca92aadac3d52d9703d39d254584
parentc2f9bff5ace07fbea03a53c6c3253f6c3a81e9f9 (diff)
crypto: af_alg - User-space interface for Crypto API
This patch creates the backbone of the user-space interface for the Crypto API, through a new socket family AF_ALG. Each session corresponds to one or more connections obtained from that socket. The number depends on the number of inputs/outputs of that particular type of operation. For most types there will be a s ingle connection/file descriptor that is used for both input and output. AEAD is one of the few that require two inputs. Each algorithm type will provide its own implementation that plugs into af_alg. They're keyed using a string such as "skcipher" or "hash". IOW this patch only contains the boring bits that is required to hold everything together. Thakns to Miloslav Trmac for reviewing this and contributing fixes and improvements. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Acked-by: David S. Miller <davem@davemloft.net> Tested-by: Martin Willi <martin@strongswan.org>
-rw-r--r--crypto/Kconfig3
-rw-r--r--crypto/Makefile1
-rw-r--r--crypto/af_alg.c482
-rw-r--r--include/crypto/if_alg.h92
-rw-r--r--include/linux/if_alg.h40
5 files changed, 618 insertions, 0 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig
index e4bac29a32e7..357e3caf4cbe 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -841,6 +841,9 @@ config CRYPTO_ANSI_CPRNG
841 ANSI X9.31 A.2.4. Note that this option must be enabled if 841 ANSI X9.31 A.2.4. Note that this option must be enabled if
842 CRYPTO_FIPS is selected 842 CRYPTO_FIPS is selected
843 843
844config CRYPTO_USER_API
845 tristate
846
844source "drivers/crypto/Kconfig" 847source "drivers/crypto/Kconfig"
845 848
846endif # if CRYPTO 849endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 423b7de61f93..0b1319734bc0 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_CRYPTO_RNG2) += krng.o
85obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o 85obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
86obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o 86obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o
87obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o 87obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
88obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
88 89
89# 90#
90# generic algorithms and the async_tx api 91# generic algorithms and the async_tx api
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
new file mode 100644
index 000000000000..cabed0e9c9d8
--- /dev/null
+++ b/crypto/af_alg.c
@@ -0,0 +1,482 @@
1/*
2 * af_alg: User-space algorithm interface
3 *
4 * This file provides the user-space API for 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 <asm/atomic.h>
16#include <crypto/if_alg.h>
17#include <linux/crypto.h>
18#include <linux/init.h>
19#include <linux/kernel.h>
20#include <linux/list.h>
21#include <linux/module.h>
22#include <linux/net.h>
23#include <linux/rwsem.h>
24
25struct alg_type_list {
26 const struct af_alg_type *type;
27 struct list_head list;
28};
29
30static atomic_t alg_memory_allocated;
31
32static struct proto alg_proto = {
33 .name = "ALG",
34 .owner = THIS_MODULE,
35 .memory_allocated = &alg_memory_allocated,
36 .obj_size = sizeof(struct alg_sock),
37};
38
39static LIST_HEAD(alg_types);
40static DECLARE_RWSEM(alg_types_sem);
41
42static const struct af_alg_type *alg_get_type(const char *name)
43{
44 const struct af_alg_type *type = ERR_PTR(-ENOENT);
45 struct alg_type_list *node;
46
47 down_read(&alg_types_sem);
48 list_for_each_entry(node, &alg_types, list) {
49 if (strcmp(node->type->name, name))
50 continue;
51
52 if (try_module_get(node->type->owner))
53 type = node->type;
54 break;
55 }
56 up_read(&alg_types_sem);
57
58 return type;
59}
60
61int af_alg_register_type(const struct af_alg_type *type)
62{
63 struct alg_type_list *node;
64 int err = -EEXIST;
65
66 down_write(&alg_types_sem);
67 list_for_each_entry(node, &alg_types, list) {
68 if (!strcmp(node->type->name, type->name))
69 goto unlock;
70 }
71
72 node = kmalloc(sizeof(*node), GFP_KERNEL);
73 err = -ENOMEM;
74 if (!node)
75 goto unlock;
76
77 type->ops->owner = THIS_MODULE;
78 node->type = type;
79 list_add(&node->list, &alg_types);
80 err = 0;
81
82unlock:
83 up_write(&alg_types_sem);
84
85 return err;
86}
87EXPORT_SYMBOL_GPL(af_alg_register_type);
88
89int af_alg_unregister_type(const struct af_alg_type *type)
90{
91 struct alg_type_list *node;
92 int err = -ENOENT;
93
94 down_write(&alg_types_sem);
95 list_for_each_entry(node, &alg_types, list) {
96 if (strcmp(node->type->name, type->name))
97 continue;
98
99 list_del(&node->list);
100 kfree(node);
101 err = 0;
102 break;
103 }
104 up_write(&alg_types_sem);
105
106 return err;
107}
108EXPORT_SYMBOL_GPL(af_alg_unregister_type);
109
110static void alg_do_release(const struct af_alg_type *type, void *private)
111{
112 if (!type)
113 return;
114
115 type->release(private);
116 module_put(type->owner);
117}
118
119int af_alg_release(struct socket *sock)
120{
121 if (sock->sk)
122 sock_put(sock->sk);
123 return 0;
124}
125EXPORT_SYMBOL_GPL(af_alg_release);
126
127static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
128{
129 struct sock *sk = sock->sk;
130 struct alg_sock *ask = alg_sk(sk);
131 struct sockaddr_alg *sa = (void *)uaddr;
132 const struct af_alg_type *type;
133 void *private;
134
135 if (sock->state == SS_CONNECTED)
136 return -EINVAL;
137
138 if (addr_len != sizeof(*sa))
139 return -EINVAL;
140
141 sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
142 sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
143
144 type = alg_get_type(sa->salg_type);
145 if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
146 request_module("algif-%s", sa->salg_type);
147 type = alg_get_type(sa->salg_type);
148 }
149
150 if (IS_ERR(type))
151 return PTR_ERR(type);
152
153 private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
154 if (IS_ERR(private)) {
155 module_put(type->owner);
156 return PTR_ERR(private);
157 }
158
159 lock_sock(sk);
160
161 swap(ask->type, type);
162 swap(ask->private, private);
163
164 release_sock(sk);
165
166 alg_do_release(type, private);
167
168 return 0;
169}
170
171static int alg_setkey(struct sock *sk, char __user *ukey,
172 unsigned int keylen)
173{
174 struct alg_sock *ask = alg_sk(sk);
175 const struct af_alg_type *type = ask->type;
176 u8 *key;
177 int err;
178
179 key = sock_kmalloc(sk, keylen, GFP_KERNEL);
180 if (!key)
181 return -ENOMEM;
182
183 err = -EFAULT;
184 if (copy_from_user(key, ukey, keylen))
185 goto out;
186
187 err = type->setkey(ask->private, key, keylen);
188
189out:
190 sock_kfree_s(sk, key, keylen);
191
192 return err;
193}
194
195static int alg_setsockopt(struct socket *sock, int level, int optname,
196 char __user *optval, unsigned int optlen)
197{
198 struct sock *sk = sock->sk;
199 struct alg_sock *ask = alg_sk(sk);
200 const struct af_alg_type *type;
201 int err = -ENOPROTOOPT;
202
203 lock_sock(sk);
204 type = ask->type;
205
206 if (level != SOL_ALG || !type)
207 goto unlock;
208
209 switch (optname) {
210 case ALG_SET_KEY:
211 if (sock->state == SS_CONNECTED)
212 goto unlock;
213 if (!type->setkey)
214 goto unlock;
215
216 err = alg_setkey(sk, optval, optlen);
217 }
218
219unlock:
220 release_sock(sk);
221
222 return err;
223}
224
225int af_alg_accept(struct sock *sk, struct socket *newsock)
226{
227 struct alg_sock *ask = alg_sk(sk);
228 const struct af_alg_type *type;
229 struct sock *sk2;
230 int err;
231
232 lock_sock(sk);
233 type = ask->type;
234
235 err = -EINVAL;
236 if (!type)
237 goto unlock;
238
239 sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto);
240 err = -ENOMEM;
241 if (!sk2)
242 goto unlock;
243
244 sock_init_data(newsock, sk2);
245
246 err = type->accept(ask->private, sk2);
247 if (err) {
248 sk_free(sk2);
249 goto unlock;
250 }
251
252 sk2->sk_family = PF_ALG;
253
254 sock_hold(sk);
255 alg_sk(sk2)->parent = sk;
256 alg_sk(sk2)->type = type;
257
258 newsock->ops = type->ops;
259 newsock->state = SS_CONNECTED;
260
261 err = 0;
262
263unlock:
264 release_sock(sk);
265
266 return err;
267}
268EXPORT_SYMBOL_GPL(af_alg_accept);
269
270static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
271{
272 return af_alg_accept(sock->sk, newsock);
273}
274
275static const struct proto_ops alg_proto_ops = {
276 .family = PF_ALG,
277 .owner = THIS_MODULE,
278
279 .connect = sock_no_connect,
280 .socketpair = sock_no_socketpair,
281 .getname = sock_no_getname,
282 .ioctl = sock_no_ioctl,
283 .listen = sock_no_listen,
284 .shutdown = sock_no_shutdown,
285 .getsockopt = sock_no_getsockopt,
286 .mmap = sock_no_mmap,
287 .sendpage = sock_no_sendpage,
288 .sendmsg = sock_no_sendmsg,
289 .recvmsg = sock_no_recvmsg,
290 .poll = sock_no_poll,
291
292 .bind = alg_bind,
293 .release = af_alg_release,
294 .setsockopt = alg_setsockopt,
295 .accept = alg_accept,
296};
297
298static void alg_sock_destruct(struct sock *sk)
299{
300 struct alg_sock *ask = alg_sk(sk);
301
302 alg_do_release(ask->type, ask->private);
303}
304
305static int alg_create(struct net *net, struct socket *sock, int protocol,
306 int kern)
307{
308 struct sock *sk;
309 int err;
310
311 if (sock->type != SOCK_SEQPACKET)
312 return -ESOCKTNOSUPPORT;
313 if (protocol != 0)
314 return -EPROTONOSUPPORT;
315
316 err = -ENOMEM;
317 sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto);
318 if (!sk)
319 goto out;
320
321 sock->ops = &alg_proto_ops;
322 sock_init_data(sock, sk);
323
324 sk->sk_family = PF_ALG;
325 sk->sk_destruct = alg_sock_destruct;
326
327 return 0;
328out:
329 return err;
330}
331
332static const struct net_proto_family alg_family = {
333 .family = PF_ALG,
334 .create = alg_create,
335 .owner = THIS_MODULE,
336};
337
338int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
339 int write)
340{
341 unsigned long from = (unsigned long)addr;
342 unsigned long npages;
343 unsigned off;
344 int err;
345 int i;
346
347 err = -EFAULT;
348 if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, addr, len))
349 goto out;
350
351 off = from & ~PAGE_MASK;
352 npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
353 if (npages > ALG_MAX_PAGES)
354 npages = ALG_MAX_PAGES;
355
356 err = get_user_pages_fast(from, npages, write, sgl->pages);
357 if (err < 0)
358 goto out;
359
360 npages = err;
361 err = -EINVAL;
362 if (WARN_ON(npages == 0))
363 goto out;
364
365 err = 0;
366
367 sg_init_table(sgl->sg, npages);
368
369 for (i = 0; i < npages; i++) {
370 int plen = min_t(int, len, PAGE_SIZE - off);
371
372 sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
373
374 off = 0;
375 len -= plen;
376 err += plen;
377 }
378
379out:
380 return err;
381}
382EXPORT_SYMBOL_GPL(af_alg_make_sg);
383
384void af_alg_free_sg(struct af_alg_sgl *sgl)
385{
386 int i;
387
388 i = 0;
389 do {
390 put_page(sgl->pages[i]);
391 } while (!sg_is_last(sgl->sg + (i++)));
392}
393EXPORT_SYMBOL_GPL(af_alg_free_sg);
394
395int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
396{
397 struct cmsghdr *cmsg;
398
399 for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
400 if (!CMSG_OK(msg, cmsg))
401 return -EINVAL;
402 if (cmsg->cmsg_level != SOL_ALG)
403 continue;
404
405 switch(cmsg->cmsg_type) {
406 case ALG_SET_IV:
407 if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv)))
408 return -EINVAL;
409 con->iv = (void *)CMSG_DATA(cmsg);
410 if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen +
411 sizeof(*con->iv)))
412 return -EINVAL;
413 break;
414
415 case ALG_SET_OP:
416 if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
417 return -EINVAL;
418 con->op = *(u32 *)CMSG_DATA(cmsg);
419 break;
420
421 default:
422 return -EINVAL;
423 }
424 }
425
426 return 0;
427}
428EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
429
430int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
431{
432 switch (err) {
433 case -EINPROGRESS:
434 case -EBUSY:
435 wait_for_completion(&completion->completion);
436 INIT_COMPLETION(completion->completion);
437 err = completion->err;
438 break;
439 };
440
441 return err;
442}
443EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
444
445void af_alg_complete(struct crypto_async_request *req, int err)
446{
447 struct af_alg_completion *completion = req->data;
448
449 completion->err = err;
450 complete(&completion->completion);
451}
452EXPORT_SYMBOL_GPL(af_alg_complete);
453
454static int __init af_alg_init(void)
455{
456 int err = proto_register(&alg_proto, 0);
457
458 if (err)
459 goto out;
460
461 err = sock_register(&alg_family);
462 if (err != 0)
463 goto out_unregister_proto;
464
465out:
466 return err;
467
468out_unregister_proto:
469 proto_unregister(&alg_proto);
470 goto out;
471}
472
473static void __exit af_alg_exit(void)
474{
475 sock_unregister(PF_ALG);
476 proto_unregister(&alg_proto);
477}
478
479module_init(af_alg_init);
480module_exit(af_alg_exit);
481MODULE_LICENSE("GPL");
482MODULE_ALIAS_NETPROTO(AF_ALG);
diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h
new file mode 100644
index 000000000000..c5813c87de06
--- /dev/null
+++ b/include/crypto/if_alg.h
@@ -0,0 +1,92 @@
1/*
2 * if_alg: User-space algorithm interface
3 *
4 * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 */
12
13#ifndef _CRYPTO_IF_ALG_H
14#define _CRYPTO_IF_ALG_H
15
16#include <linux/compiler.h>
17#include <linux/completion.h>
18#include <linux/if_alg.h>
19#include <linux/types.h>
20#include <net/sock.h>
21
22#define ALG_MAX_PAGES 16
23
24struct crypto_async_request;
25
26struct alg_sock {
27 /* struct sock must be the first member of struct alg_sock */
28 struct sock sk;
29
30 struct sock *parent;
31
32 const struct af_alg_type *type;
33 void *private;
34};
35
36struct af_alg_completion {
37 struct completion completion;
38 int err;
39};
40
41struct af_alg_control {
42 struct af_alg_iv *iv;
43 int op;
44};
45
46struct af_alg_type {
47 void *(*bind)(const char *name, u32 type, u32 mask);
48 void (*release)(void *private);
49 int (*setkey)(void *private, const u8 *key, unsigned int keylen);
50 int (*accept)(void *private, struct sock *sk);
51
52 struct proto_ops *ops;
53 struct module *owner;
54 char name[14];
55};
56
57struct af_alg_sgl {
58 struct scatterlist sg[ALG_MAX_PAGES];
59 struct page *pages[ALG_MAX_PAGES];
60};
61
62int af_alg_register_type(const struct af_alg_type *type);
63int af_alg_unregister_type(const struct af_alg_type *type);
64
65int af_alg_release(struct socket *sock);
66int af_alg_accept(struct sock *sk, struct socket *newsock);
67
68int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
69 int write);
70void af_alg_free_sg(struct af_alg_sgl *sgl);
71
72int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con);
73
74int af_alg_wait_for_completion(int err, struct af_alg_completion *completion);
75void af_alg_complete(struct crypto_async_request *req, int err);
76
77static inline struct alg_sock *alg_sk(struct sock *sk)
78{
79 return (struct alg_sock *)sk;
80}
81
82static inline void af_alg_release_parent(struct sock *sk)
83{
84 sock_put(alg_sk(sk)->parent);
85}
86
87static inline void af_alg_init_completion(struct af_alg_completion *completion)
88{
89 init_completion(&completion->completion);
90}
91
92#endif /* _CRYPTO_IF_ALG_H */
diff --git a/include/linux/if_alg.h b/include/linux/if_alg.h
new file mode 100644
index 000000000000..0f9acce5b1ff
--- /dev/null
+++ b/include/linux/if_alg.h
@@ -0,0 +1,40 @@
1/*
2 * if_alg: User-space algorithm interface
3 *
4 * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 */
12
13#ifndef _LINUX_IF_ALG_H
14#define _LINUX_IF_ALG_H
15
16#include <linux/types.h>
17
18struct sockaddr_alg {
19 __u16 salg_family;
20 __u8 salg_type[14];
21 __u32 salg_feat;
22 __u32 salg_mask;
23 __u8 salg_name[64];
24};
25
26struct af_alg_iv {
27 __u32 ivlen;
28 __u8 iv[0];
29};
30
31/* Socket options */
32#define ALG_SET_KEY 1
33#define ALG_SET_IV 2
34#define ALG_SET_OP 3
35
36/* Operations */
37#define ALG_OP_DECRYPT 0
38#define ALG_OP_ENCRYPT 1
39
40#endif /* _LINUX_IF_ALG_H */