xref: /openbmc/linux/crypto/af_alg.c (revision 9399f0c5)
103c8efc1SHerbert Xu /*
203c8efc1SHerbert Xu  * af_alg: User-space algorithm interface
303c8efc1SHerbert Xu  *
403c8efc1SHerbert Xu  * This file provides the user-space API for algorithms.
503c8efc1SHerbert Xu  *
603c8efc1SHerbert Xu  * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>
703c8efc1SHerbert Xu  *
803c8efc1SHerbert Xu  * This program is free software; you can redistribute it and/or modify it
903c8efc1SHerbert Xu  * under the terms of the GNU General Public License as published by the Free
1003c8efc1SHerbert Xu  * Software Foundation; either version 2 of the License, or (at your option)
1103c8efc1SHerbert Xu  * any later version.
1203c8efc1SHerbert Xu  *
1303c8efc1SHerbert Xu  */
1403c8efc1SHerbert Xu 
1560063497SArun Sharma #include <linux/atomic.h>
1603c8efc1SHerbert Xu #include <crypto/if_alg.h>
1703c8efc1SHerbert Xu #include <linux/crypto.h>
1803c8efc1SHerbert Xu #include <linux/init.h>
1903c8efc1SHerbert Xu #include <linux/kernel.h>
2003c8efc1SHerbert Xu #include <linux/list.h>
2103c8efc1SHerbert Xu #include <linux/module.h>
2203c8efc1SHerbert Xu #include <linux/net.h>
2303c8efc1SHerbert Xu #include <linux/rwsem.h>
244c63f83cSMilan Broz #include <linux/security.h>
2503c8efc1SHerbert Xu 
2603c8efc1SHerbert Xu struct alg_type_list {
2703c8efc1SHerbert Xu 	const struct af_alg_type *type;
2803c8efc1SHerbert Xu 	struct list_head list;
2903c8efc1SHerbert Xu };
3003c8efc1SHerbert Xu 
3106869524SRandy Dunlap static atomic_long_t alg_memory_allocated;
3203c8efc1SHerbert Xu 
3303c8efc1SHerbert Xu static struct proto alg_proto = {
3403c8efc1SHerbert Xu 	.name			= "ALG",
3503c8efc1SHerbert Xu 	.owner			= THIS_MODULE,
3603c8efc1SHerbert Xu 	.memory_allocated	= &alg_memory_allocated,
3703c8efc1SHerbert Xu 	.obj_size		= sizeof(struct alg_sock),
3803c8efc1SHerbert Xu };
3903c8efc1SHerbert Xu 
4003c8efc1SHerbert Xu static LIST_HEAD(alg_types);
4103c8efc1SHerbert Xu static DECLARE_RWSEM(alg_types_sem);
4203c8efc1SHerbert Xu 
4303c8efc1SHerbert Xu static const struct af_alg_type *alg_get_type(const char *name)
4403c8efc1SHerbert Xu {
4503c8efc1SHerbert Xu 	const struct af_alg_type *type = ERR_PTR(-ENOENT);
4603c8efc1SHerbert Xu 	struct alg_type_list *node;
4703c8efc1SHerbert Xu 
4803c8efc1SHerbert Xu 	down_read(&alg_types_sem);
4903c8efc1SHerbert Xu 	list_for_each_entry(node, &alg_types, list) {
5003c8efc1SHerbert Xu 		if (strcmp(node->type->name, name))
5103c8efc1SHerbert Xu 			continue;
5203c8efc1SHerbert Xu 
5303c8efc1SHerbert Xu 		if (try_module_get(node->type->owner))
5403c8efc1SHerbert Xu 			type = node->type;
5503c8efc1SHerbert Xu 		break;
5603c8efc1SHerbert Xu 	}
5703c8efc1SHerbert Xu 	up_read(&alg_types_sem);
5803c8efc1SHerbert Xu 
5903c8efc1SHerbert Xu 	return type;
6003c8efc1SHerbert Xu }
6103c8efc1SHerbert Xu 
6203c8efc1SHerbert Xu int af_alg_register_type(const struct af_alg_type *type)
6303c8efc1SHerbert Xu {
6403c8efc1SHerbert Xu 	struct alg_type_list *node;
6503c8efc1SHerbert Xu 	int err = -EEXIST;
6603c8efc1SHerbert Xu 
6703c8efc1SHerbert Xu 	down_write(&alg_types_sem);
6803c8efc1SHerbert Xu 	list_for_each_entry(node, &alg_types, list) {
6903c8efc1SHerbert Xu 		if (!strcmp(node->type->name, type->name))
7003c8efc1SHerbert Xu 			goto unlock;
7103c8efc1SHerbert Xu 	}
7203c8efc1SHerbert Xu 
7303c8efc1SHerbert Xu 	node = kmalloc(sizeof(*node), GFP_KERNEL);
7403c8efc1SHerbert Xu 	err = -ENOMEM;
7503c8efc1SHerbert Xu 	if (!node)
7603c8efc1SHerbert Xu 		goto unlock;
7703c8efc1SHerbert Xu 
7803c8efc1SHerbert Xu 	type->ops->owner = THIS_MODULE;
7903c8efc1SHerbert Xu 	node->type = type;
8003c8efc1SHerbert Xu 	list_add(&node->list, &alg_types);
8103c8efc1SHerbert Xu 	err = 0;
8203c8efc1SHerbert Xu 
8303c8efc1SHerbert Xu unlock:
8403c8efc1SHerbert Xu 	up_write(&alg_types_sem);
8503c8efc1SHerbert Xu 
8603c8efc1SHerbert Xu 	return err;
8703c8efc1SHerbert Xu }
8803c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_register_type);
8903c8efc1SHerbert Xu 
9003c8efc1SHerbert Xu int af_alg_unregister_type(const struct af_alg_type *type)
9103c8efc1SHerbert Xu {
9203c8efc1SHerbert Xu 	struct alg_type_list *node;
9303c8efc1SHerbert Xu 	int err = -ENOENT;
9403c8efc1SHerbert Xu 
9503c8efc1SHerbert Xu 	down_write(&alg_types_sem);
9603c8efc1SHerbert Xu 	list_for_each_entry(node, &alg_types, list) {
9703c8efc1SHerbert Xu 		if (strcmp(node->type->name, type->name))
9803c8efc1SHerbert Xu 			continue;
9903c8efc1SHerbert Xu 
10003c8efc1SHerbert Xu 		list_del(&node->list);
10103c8efc1SHerbert Xu 		kfree(node);
10203c8efc1SHerbert Xu 		err = 0;
10303c8efc1SHerbert Xu 		break;
10403c8efc1SHerbert Xu 	}
10503c8efc1SHerbert Xu 	up_write(&alg_types_sem);
10603c8efc1SHerbert Xu 
10703c8efc1SHerbert Xu 	return err;
10803c8efc1SHerbert Xu }
10903c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_unregister_type);
11003c8efc1SHerbert Xu 
11103c8efc1SHerbert Xu static void alg_do_release(const struct af_alg_type *type, void *private)
11203c8efc1SHerbert Xu {
11303c8efc1SHerbert Xu 	if (!type)
11403c8efc1SHerbert Xu 		return;
11503c8efc1SHerbert Xu 
11603c8efc1SHerbert Xu 	type->release(private);
11703c8efc1SHerbert Xu 	module_put(type->owner);
11803c8efc1SHerbert Xu }
11903c8efc1SHerbert Xu 
12003c8efc1SHerbert Xu int af_alg_release(struct socket *sock)
12103c8efc1SHerbert Xu {
12203c8efc1SHerbert Xu 	if (sock->sk)
12303c8efc1SHerbert Xu 		sock_put(sock->sk);
12403c8efc1SHerbert Xu 	return 0;
12503c8efc1SHerbert Xu }
12603c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_release);
12703c8efc1SHerbert Xu 
12803c8efc1SHerbert Xu static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
12903c8efc1SHerbert Xu {
13003c8efc1SHerbert Xu 	struct sock *sk = sock->sk;
13103c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
13203c8efc1SHerbert Xu 	struct sockaddr_alg *sa = (void *)uaddr;
13303c8efc1SHerbert Xu 	const struct af_alg_type *type;
13403c8efc1SHerbert Xu 	void *private;
13503c8efc1SHerbert Xu 
13603c8efc1SHerbert Xu 	if (sock->state == SS_CONNECTED)
13703c8efc1SHerbert Xu 		return -EINVAL;
13803c8efc1SHerbert Xu 
13903c8efc1SHerbert Xu 	if (addr_len != sizeof(*sa))
14003c8efc1SHerbert Xu 		return -EINVAL;
14103c8efc1SHerbert Xu 
14203c8efc1SHerbert Xu 	sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
14303c8efc1SHerbert Xu 	sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
14403c8efc1SHerbert Xu 
14503c8efc1SHerbert Xu 	type = alg_get_type(sa->salg_type);
14603c8efc1SHerbert Xu 	if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
14703c8efc1SHerbert Xu 		request_module("algif-%s", sa->salg_type);
14803c8efc1SHerbert Xu 		type = alg_get_type(sa->salg_type);
14903c8efc1SHerbert Xu 	}
15003c8efc1SHerbert Xu 
15103c8efc1SHerbert Xu 	if (IS_ERR(type))
15203c8efc1SHerbert Xu 		return PTR_ERR(type);
15303c8efc1SHerbert Xu 
15403c8efc1SHerbert Xu 	private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
15503c8efc1SHerbert Xu 	if (IS_ERR(private)) {
15603c8efc1SHerbert Xu 		module_put(type->owner);
15703c8efc1SHerbert Xu 		return PTR_ERR(private);
15803c8efc1SHerbert Xu 	}
15903c8efc1SHerbert Xu 
16003c8efc1SHerbert Xu 	lock_sock(sk);
16103c8efc1SHerbert Xu 
16203c8efc1SHerbert Xu 	swap(ask->type, type);
16303c8efc1SHerbert Xu 	swap(ask->private, private);
16403c8efc1SHerbert Xu 
16503c8efc1SHerbert Xu 	release_sock(sk);
16603c8efc1SHerbert Xu 
16703c8efc1SHerbert Xu 	alg_do_release(type, private);
16803c8efc1SHerbert Xu 
16903c8efc1SHerbert Xu 	return 0;
17003c8efc1SHerbert Xu }
17103c8efc1SHerbert Xu 
17203c8efc1SHerbert Xu static int alg_setkey(struct sock *sk, char __user *ukey,
17303c8efc1SHerbert Xu 		      unsigned int keylen)
17403c8efc1SHerbert Xu {
17503c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
17603c8efc1SHerbert Xu 	const struct af_alg_type *type = ask->type;
17703c8efc1SHerbert Xu 	u8 *key;
17803c8efc1SHerbert Xu 	int err;
17903c8efc1SHerbert Xu 
18003c8efc1SHerbert Xu 	key = sock_kmalloc(sk, keylen, GFP_KERNEL);
18103c8efc1SHerbert Xu 	if (!key)
18203c8efc1SHerbert Xu 		return -ENOMEM;
18303c8efc1SHerbert Xu 
18403c8efc1SHerbert Xu 	err = -EFAULT;
18503c8efc1SHerbert Xu 	if (copy_from_user(key, ukey, keylen))
18603c8efc1SHerbert Xu 		goto out;
18703c8efc1SHerbert Xu 
18803c8efc1SHerbert Xu 	err = type->setkey(ask->private, key, keylen);
18903c8efc1SHerbert Xu 
19003c8efc1SHerbert Xu out:
19103c8efc1SHerbert Xu 	sock_kfree_s(sk, key, keylen);
19203c8efc1SHerbert Xu 
19303c8efc1SHerbert Xu 	return err;
19403c8efc1SHerbert Xu }
19503c8efc1SHerbert Xu 
19603c8efc1SHerbert Xu static int alg_setsockopt(struct socket *sock, int level, int optname,
19703c8efc1SHerbert Xu 			  char __user *optval, unsigned int optlen)
19803c8efc1SHerbert Xu {
19903c8efc1SHerbert Xu 	struct sock *sk = sock->sk;
20003c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
20103c8efc1SHerbert Xu 	const struct af_alg_type *type;
20203c8efc1SHerbert Xu 	int err = -ENOPROTOOPT;
20303c8efc1SHerbert Xu 
20403c8efc1SHerbert Xu 	lock_sock(sk);
20503c8efc1SHerbert Xu 	type = ask->type;
20603c8efc1SHerbert Xu 
20703c8efc1SHerbert Xu 	if (level != SOL_ALG || !type)
20803c8efc1SHerbert Xu 		goto unlock;
20903c8efc1SHerbert Xu 
21003c8efc1SHerbert Xu 	switch (optname) {
21103c8efc1SHerbert Xu 	case ALG_SET_KEY:
21203c8efc1SHerbert Xu 		if (sock->state == SS_CONNECTED)
21303c8efc1SHerbert Xu 			goto unlock;
21403c8efc1SHerbert Xu 		if (!type->setkey)
21503c8efc1SHerbert Xu 			goto unlock;
21603c8efc1SHerbert Xu 
21703c8efc1SHerbert Xu 		err = alg_setkey(sk, optval, optlen);
21803c8efc1SHerbert Xu 	}
21903c8efc1SHerbert Xu 
22003c8efc1SHerbert Xu unlock:
22103c8efc1SHerbert Xu 	release_sock(sk);
22203c8efc1SHerbert Xu 
22303c8efc1SHerbert Xu 	return err;
22403c8efc1SHerbert Xu }
22503c8efc1SHerbert Xu 
22603c8efc1SHerbert Xu int af_alg_accept(struct sock *sk, struct socket *newsock)
22703c8efc1SHerbert Xu {
22803c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
22903c8efc1SHerbert Xu 	const struct af_alg_type *type;
23003c8efc1SHerbert Xu 	struct sock *sk2;
23103c8efc1SHerbert Xu 	int err;
23203c8efc1SHerbert Xu 
23303c8efc1SHerbert Xu 	lock_sock(sk);
23403c8efc1SHerbert Xu 	type = ask->type;
23503c8efc1SHerbert Xu 
23603c8efc1SHerbert Xu 	err = -EINVAL;
23703c8efc1SHerbert Xu 	if (!type)
23803c8efc1SHerbert Xu 		goto unlock;
23903c8efc1SHerbert Xu 
24003c8efc1SHerbert Xu 	sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto);
24103c8efc1SHerbert Xu 	err = -ENOMEM;
24203c8efc1SHerbert Xu 	if (!sk2)
24303c8efc1SHerbert Xu 		goto unlock;
24403c8efc1SHerbert Xu 
24503c8efc1SHerbert Xu 	sock_init_data(newsock, sk2);
246507cad35SMiloslav Trmač 	sock_graft(sk2, newsock);
2474c63f83cSMilan Broz 	security_sk_clone(sk, sk2);
24803c8efc1SHerbert Xu 
24903c8efc1SHerbert Xu 	err = type->accept(ask->private, sk2);
25003c8efc1SHerbert Xu 	if (err) {
25103c8efc1SHerbert Xu 		sk_free(sk2);
25203c8efc1SHerbert Xu 		goto unlock;
25303c8efc1SHerbert Xu 	}
25403c8efc1SHerbert Xu 
25503c8efc1SHerbert Xu 	sk2->sk_family = PF_ALG;
25603c8efc1SHerbert Xu 
25703c8efc1SHerbert Xu 	sock_hold(sk);
25803c8efc1SHerbert Xu 	alg_sk(sk2)->parent = sk;
25903c8efc1SHerbert Xu 	alg_sk(sk2)->type = type;
26003c8efc1SHerbert Xu 
26103c8efc1SHerbert Xu 	newsock->ops = type->ops;
26203c8efc1SHerbert Xu 	newsock->state = SS_CONNECTED;
26303c8efc1SHerbert Xu 
26403c8efc1SHerbert Xu 	err = 0;
26503c8efc1SHerbert Xu 
26603c8efc1SHerbert Xu unlock:
26703c8efc1SHerbert Xu 	release_sock(sk);
26803c8efc1SHerbert Xu 
26903c8efc1SHerbert Xu 	return err;
27003c8efc1SHerbert Xu }
27103c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_accept);
27203c8efc1SHerbert Xu 
27303c8efc1SHerbert Xu static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
27403c8efc1SHerbert Xu {
27503c8efc1SHerbert Xu 	return af_alg_accept(sock->sk, newsock);
27603c8efc1SHerbert Xu }
27703c8efc1SHerbert Xu 
27803c8efc1SHerbert Xu static const struct proto_ops alg_proto_ops = {
27903c8efc1SHerbert Xu 	.family		=	PF_ALG,
28003c8efc1SHerbert Xu 	.owner		=	THIS_MODULE,
28103c8efc1SHerbert Xu 
28203c8efc1SHerbert Xu 	.connect	=	sock_no_connect,
28303c8efc1SHerbert Xu 	.socketpair	=	sock_no_socketpair,
28403c8efc1SHerbert Xu 	.getname	=	sock_no_getname,
28503c8efc1SHerbert Xu 	.ioctl		=	sock_no_ioctl,
28603c8efc1SHerbert Xu 	.listen		=	sock_no_listen,
28703c8efc1SHerbert Xu 	.shutdown	=	sock_no_shutdown,
28803c8efc1SHerbert Xu 	.getsockopt	=	sock_no_getsockopt,
28903c8efc1SHerbert Xu 	.mmap		=	sock_no_mmap,
29003c8efc1SHerbert Xu 	.sendpage	=	sock_no_sendpage,
29103c8efc1SHerbert Xu 	.sendmsg	=	sock_no_sendmsg,
29203c8efc1SHerbert Xu 	.recvmsg	=	sock_no_recvmsg,
29303c8efc1SHerbert Xu 	.poll		=	sock_no_poll,
29403c8efc1SHerbert Xu 
29503c8efc1SHerbert Xu 	.bind		=	alg_bind,
29603c8efc1SHerbert Xu 	.release	=	af_alg_release,
29703c8efc1SHerbert Xu 	.setsockopt	=	alg_setsockopt,
29803c8efc1SHerbert Xu 	.accept		=	alg_accept,
29903c8efc1SHerbert Xu };
30003c8efc1SHerbert Xu 
30103c8efc1SHerbert Xu static void alg_sock_destruct(struct sock *sk)
30203c8efc1SHerbert Xu {
30303c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
30403c8efc1SHerbert Xu 
30503c8efc1SHerbert Xu 	alg_do_release(ask->type, ask->private);
30603c8efc1SHerbert Xu }
30703c8efc1SHerbert Xu 
30803c8efc1SHerbert Xu static int alg_create(struct net *net, struct socket *sock, int protocol,
30903c8efc1SHerbert Xu 		      int kern)
31003c8efc1SHerbert Xu {
31103c8efc1SHerbert Xu 	struct sock *sk;
31203c8efc1SHerbert Xu 	int err;
31303c8efc1SHerbert Xu 
31403c8efc1SHerbert Xu 	if (sock->type != SOCK_SEQPACKET)
31503c8efc1SHerbert Xu 		return -ESOCKTNOSUPPORT;
31603c8efc1SHerbert Xu 	if (protocol != 0)
31703c8efc1SHerbert Xu 		return -EPROTONOSUPPORT;
31803c8efc1SHerbert Xu 
31903c8efc1SHerbert Xu 	err = -ENOMEM;
32003c8efc1SHerbert Xu 	sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto);
32103c8efc1SHerbert Xu 	if (!sk)
32203c8efc1SHerbert Xu 		goto out;
32303c8efc1SHerbert Xu 
32403c8efc1SHerbert Xu 	sock->ops = &alg_proto_ops;
32503c8efc1SHerbert Xu 	sock_init_data(sock, sk);
32603c8efc1SHerbert Xu 
32703c8efc1SHerbert Xu 	sk->sk_family = PF_ALG;
32803c8efc1SHerbert Xu 	sk->sk_destruct = alg_sock_destruct;
32903c8efc1SHerbert Xu 
33003c8efc1SHerbert Xu 	return 0;
33103c8efc1SHerbert Xu out:
33203c8efc1SHerbert Xu 	return err;
33303c8efc1SHerbert Xu }
33403c8efc1SHerbert Xu 
33503c8efc1SHerbert Xu static const struct net_proto_family alg_family = {
33603c8efc1SHerbert Xu 	.family	=	PF_ALG,
33703c8efc1SHerbert Xu 	.create	=	alg_create,
33803c8efc1SHerbert Xu 	.owner	=	THIS_MODULE,
33903c8efc1SHerbert Xu };
34003c8efc1SHerbert Xu 
3411d10eb2fSAl Viro int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len)
34203c8efc1SHerbert Xu {
3431d10eb2fSAl Viro 	size_t off;
3441d10eb2fSAl Viro 	ssize_t n;
3451d10eb2fSAl Viro 	int npages, i;
34603c8efc1SHerbert Xu 
3471d10eb2fSAl Viro 	n = iov_iter_get_pages(iter, sgl->pages, len, ALG_MAX_PAGES, &off);
3481d10eb2fSAl Viro 	if (n < 0)
3491d10eb2fSAl Viro 		return n;
35003c8efc1SHerbert Xu 
3519399f0c5SLinus Torvalds 	npages = (off + n + PAGE_SIZE - 1) >> PAGE_SHIFT;
35203c8efc1SHerbert Xu 	if (WARN_ON(npages == 0))
3531d10eb2fSAl Viro 		return -EINVAL;
35403c8efc1SHerbert Xu 
35503c8efc1SHerbert Xu 	sg_init_table(sgl->sg, npages);
35603c8efc1SHerbert Xu 
3571d10eb2fSAl Viro 	for (i = 0, len = n; i < npages; i++) {
35803c8efc1SHerbert Xu 		int plen = min_t(int, len, PAGE_SIZE - off);
35903c8efc1SHerbert Xu 
36003c8efc1SHerbert Xu 		sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
36103c8efc1SHerbert Xu 
36203c8efc1SHerbert Xu 		off = 0;
36303c8efc1SHerbert Xu 		len -= plen;
36403c8efc1SHerbert Xu 	}
3651d10eb2fSAl Viro 	return n;
36603c8efc1SHerbert Xu }
36703c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_make_sg);
36803c8efc1SHerbert Xu 
36903c8efc1SHerbert Xu void af_alg_free_sg(struct af_alg_sgl *sgl)
37003c8efc1SHerbert Xu {
37103c8efc1SHerbert Xu 	int i;
37203c8efc1SHerbert Xu 
37303c8efc1SHerbert Xu 	i = 0;
37403c8efc1SHerbert Xu 	do {
37503c8efc1SHerbert Xu 		put_page(sgl->pages[i]);
37603c8efc1SHerbert Xu 	} while (!sg_is_last(sgl->sg + (i++)));
37703c8efc1SHerbert Xu }
37803c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_free_sg);
37903c8efc1SHerbert Xu 
38003c8efc1SHerbert Xu int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
38103c8efc1SHerbert Xu {
38203c8efc1SHerbert Xu 	struct cmsghdr *cmsg;
38303c8efc1SHerbert Xu 
384f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
38503c8efc1SHerbert Xu 		if (!CMSG_OK(msg, cmsg))
38603c8efc1SHerbert Xu 			return -EINVAL;
38703c8efc1SHerbert Xu 		if (cmsg->cmsg_level != SOL_ALG)
38803c8efc1SHerbert Xu 			continue;
38903c8efc1SHerbert Xu 
39003c8efc1SHerbert Xu 		switch(cmsg->cmsg_type) {
39103c8efc1SHerbert Xu 		case ALG_SET_IV:
39203c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv)))
39303c8efc1SHerbert Xu 				return -EINVAL;
39403c8efc1SHerbert Xu 			con->iv = (void *)CMSG_DATA(cmsg);
39503c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen +
39603c8efc1SHerbert Xu 						      sizeof(*con->iv)))
39703c8efc1SHerbert Xu 				return -EINVAL;
39803c8efc1SHerbert Xu 			break;
39903c8efc1SHerbert Xu 
40003c8efc1SHerbert Xu 		case ALG_SET_OP:
40103c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
40203c8efc1SHerbert Xu 				return -EINVAL;
40303c8efc1SHerbert Xu 			con->op = *(u32 *)CMSG_DATA(cmsg);
40403c8efc1SHerbert Xu 			break;
40503c8efc1SHerbert Xu 
406af8e8073SStephan Mueller 		case ALG_SET_AEAD_ASSOCLEN:
407af8e8073SStephan Mueller 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
408af8e8073SStephan Mueller 				return -EINVAL;
409af8e8073SStephan Mueller 			con->aead_assoclen = *(u32 *)CMSG_DATA(cmsg);
410af8e8073SStephan Mueller 			break;
411af8e8073SStephan Mueller 
41203c8efc1SHerbert Xu 		default:
41303c8efc1SHerbert Xu 			return -EINVAL;
41403c8efc1SHerbert Xu 		}
41503c8efc1SHerbert Xu 	}
41603c8efc1SHerbert Xu 
41703c8efc1SHerbert Xu 	return 0;
41803c8efc1SHerbert Xu }
41903c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
42003c8efc1SHerbert Xu 
42103c8efc1SHerbert Xu int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
42203c8efc1SHerbert Xu {
42303c8efc1SHerbert Xu 	switch (err) {
42403c8efc1SHerbert Xu 	case -EINPROGRESS:
42503c8efc1SHerbert Xu 	case -EBUSY:
42603c8efc1SHerbert Xu 		wait_for_completion(&completion->completion);
42716735d02SWolfram Sang 		reinit_completion(&completion->completion);
42803c8efc1SHerbert Xu 		err = completion->err;
42903c8efc1SHerbert Xu 		break;
43003c8efc1SHerbert Xu 	};
43103c8efc1SHerbert Xu 
43203c8efc1SHerbert Xu 	return err;
43303c8efc1SHerbert Xu }
43403c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
43503c8efc1SHerbert Xu 
43603c8efc1SHerbert Xu void af_alg_complete(struct crypto_async_request *req, int err)
43703c8efc1SHerbert Xu {
43803c8efc1SHerbert Xu 	struct af_alg_completion *completion = req->data;
43903c8efc1SHerbert Xu 
4407e77bdebSRabin Vincent 	if (err == -EINPROGRESS)
4417e77bdebSRabin Vincent 		return;
4427e77bdebSRabin Vincent 
44303c8efc1SHerbert Xu 	completion->err = err;
44403c8efc1SHerbert Xu 	complete(&completion->completion);
44503c8efc1SHerbert Xu }
44603c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_complete);
44703c8efc1SHerbert Xu 
44803c8efc1SHerbert Xu static int __init af_alg_init(void)
44903c8efc1SHerbert Xu {
45003c8efc1SHerbert Xu 	int err = proto_register(&alg_proto, 0);
45103c8efc1SHerbert Xu 
45203c8efc1SHerbert Xu 	if (err)
45303c8efc1SHerbert Xu 		goto out;
45403c8efc1SHerbert Xu 
45503c8efc1SHerbert Xu 	err = sock_register(&alg_family);
45603c8efc1SHerbert Xu 	if (err != 0)
45703c8efc1SHerbert Xu 		goto out_unregister_proto;
45803c8efc1SHerbert Xu 
45903c8efc1SHerbert Xu out:
46003c8efc1SHerbert Xu 	return err;
46103c8efc1SHerbert Xu 
46203c8efc1SHerbert Xu out_unregister_proto:
46303c8efc1SHerbert Xu 	proto_unregister(&alg_proto);
46403c8efc1SHerbert Xu 	goto out;
46503c8efc1SHerbert Xu }
46603c8efc1SHerbert Xu 
46703c8efc1SHerbert Xu static void __exit af_alg_exit(void)
46803c8efc1SHerbert Xu {
46903c8efc1SHerbert Xu 	sock_unregister(PF_ALG);
47003c8efc1SHerbert Xu 	proto_unregister(&alg_proto);
47103c8efc1SHerbert Xu }
47203c8efc1SHerbert Xu 
47303c8efc1SHerbert Xu module_init(af_alg_init);
47403c8efc1SHerbert Xu module_exit(af_alg_exit);
47503c8efc1SHerbert Xu MODULE_LICENSE("GPL");
47603c8efc1SHerbert Xu MODULE_ALIAS_NETPROTO(AF_ALG);
477