xref: /openbmc/linux/crypto/af_alg.c (revision 06869524)
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 
1503c8efc1SHerbert Xu #include <asm/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>
2403c8efc1SHerbert Xu 
2503c8efc1SHerbert Xu struct alg_type_list {
2603c8efc1SHerbert Xu 	const struct af_alg_type *type;
2703c8efc1SHerbert Xu 	struct list_head list;
2803c8efc1SHerbert Xu };
2903c8efc1SHerbert Xu 
3006869524SRandy Dunlap static atomic_long_t alg_memory_allocated;
3103c8efc1SHerbert Xu 
3203c8efc1SHerbert Xu static struct proto alg_proto = {
3303c8efc1SHerbert Xu 	.name			= "ALG",
3403c8efc1SHerbert Xu 	.owner			= THIS_MODULE,
3503c8efc1SHerbert Xu 	.memory_allocated	= &alg_memory_allocated,
3603c8efc1SHerbert Xu 	.obj_size		= sizeof(struct alg_sock),
3703c8efc1SHerbert Xu };
3803c8efc1SHerbert Xu 
3903c8efc1SHerbert Xu static LIST_HEAD(alg_types);
4003c8efc1SHerbert Xu static DECLARE_RWSEM(alg_types_sem);
4103c8efc1SHerbert Xu 
4203c8efc1SHerbert Xu static const struct af_alg_type *alg_get_type(const char *name)
4303c8efc1SHerbert Xu {
4403c8efc1SHerbert Xu 	const struct af_alg_type *type = ERR_PTR(-ENOENT);
4503c8efc1SHerbert Xu 	struct alg_type_list *node;
4603c8efc1SHerbert Xu 
4703c8efc1SHerbert Xu 	down_read(&alg_types_sem);
4803c8efc1SHerbert Xu 	list_for_each_entry(node, &alg_types, list) {
4903c8efc1SHerbert Xu 		if (strcmp(node->type->name, name))
5003c8efc1SHerbert Xu 			continue;
5103c8efc1SHerbert Xu 
5203c8efc1SHerbert Xu 		if (try_module_get(node->type->owner))
5303c8efc1SHerbert Xu 			type = node->type;
5403c8efc1SHerbert Xu 		break;
5503c8efc1SHerbert Xu 	}
5603c8efc1SHerbert Xu 	up_read(&alg_types_sem);
5703c8efc1SHerbert Xu 
5803c8efc1SHerbert Xu 	return type;
5903c8efc1SHerbert Xu }
6003c8efc1SHerbert Xu 
6103c8efc1SHerbert Xu int af_alg_register_type(const struct af_alg_type *type)
6203c8efc1SHerbert Xu {
6303c8efc1SHerbert Xu 	struct alg_type_list *node;
6403c8efc1SHerbert Xu 	int err = -EEXIST;
6503c8efc1SHerbert Xu 
6603c8efc1SHerbert Xu 	down_write(&alg_types_sem);
6703c8efc1SHerbert Xu 	list_for_each_entry(node, &alg_types, list) {
6803c8efc1SHerbert Xu 		if (!strcmp(node->type->name, type->name))
6903c8efc1SHerbert Xu 			goto unlock;
7003c8efc1SHerbert Xu 	}
7103c8efc1SHerbert Xu 
7203c8efc1SHerbert Xu 	node = kmalloc(sizeof(*node), GFP_KERNEL);
7303c8efc1SHerbert Xu 	err = -ENOMEM;
7403c8efc1SHerbert Xu 	if (!node)
7503c8efc1SHerbert Xu 		goto unlock;
7603c8efc1SHerbert Xu 
7703c8efc1SHerbert Xu 	type->ops->owner = THIS_MODULE;
7803c8efc1SHerbert Xu 	node->type = type;
7903c8efc1SHerbert Xu 	list_add(&node->list, &alg_types);
8003c8efc1SHerbert Xu 	err = 0;
8103c8efc1SHerbert Xu 
8203c8efc1SHerbert Xu unlock:
8303c8efc1SHerbert Xu 	up_write(&alg_types_sem);
8403c8efc1SHerbert Xu 
8503c8efc1SHerbert Xu 	return err;
8603c8efc1SHerbert Xu }
8703c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_register_type);
8803c8efc1SHerbert Xu 
8903c8efc1SHerbert Xu int af_alg_unregister_type(const struct af_alg_type *type)
9003c8efc1SHerbert Xu {
9103c8efc1SHerbert Xu 	struct alg_type_list *node;
9203c8efc1SHerbert Xu 	int err = -ENOENT;
9303c8efc1SHerbert Xu 
9403c8efc1SHerbert Xu 	down_write(&alg_types_sem);
9503c8efc1SHerbert Xu 	list_for_each_entry(node, &alg_types, list) {
9603c8efc1SHerbert Xu 		if (strcmp(node->type->name, type->name))
9703c8efc1SHerbert Xu 			continue;
9803c8efc1SHerbert Xu 
9903c8efc1SHerbert Xu 		list_del(&node->list);
10003c8efc1SHerbert Xu 		kfree(node);
10103c8efc1SHerbert Xu 		err = 0;
10203c8efc1SHerbert Xu 		break;
10303c8efc1SHerbert Xu 	}
10403c8efc1SHerbert Xu 	up_write(&alg_types_sem);
10503c8efc1SHerbert Xu 
10603c8efc1SHerbert Xu 	return err;
10703c8efc1SHerbert Xu }
10803c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_unregister_type);
10903c8efc1SHerbert Xu 
11003c8efc1SHerbert Xu static void alg_do_release(const struct af_alg_type *type, void *private)
11103c8efc1SHerbert Xu {
11203c8efc1SHerbert Xu 	if (!type)
11303c8efc1SHerbert Xu 		return;
11403c8efc1SHerbert Xu 
11503c8efc1SHerbert Xu 	type->release(private);
11603c8efc1SHerbert Xu 	module_put(type->owner);
11703c8efc1SHerbert Xu }
11803c8efc1SHerbert Xu 
11903c8efc1SHerbert Xu int af_alg_release(struct socket *sock)
12003c8efc1SHerbert Xu {
12103c8efc1SHerbert Xu 	if (sock->sk)
12203c8efc1SHerbert Xu 		sock_put(sock->sk);
12303c8efc1SHerbert Xu 	return 0;
12403c8efc1SHerbert Xu }
12503c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_release);
12603c8efc1SHerbert Xu 
12703c8efc1SHerbert Xu static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
12803c8efc1SHerbert Xu {
12903c8efc1SHerbert Xu 	struct sock *sk = sock->sk;
13003c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
13103c8efc1SHerbert Xu 	struct sockaddr_alg *sa = (void *)uaddr;
13203c8efc1SHerbert Xu 	const struct af_alg_type *type;
13303c8efc1SHerbert Xu 	void *private;
13403c8efc1SHerbert Xu 
13503c8efc1SHerbert Xu 	if (sock->state == SS_CONNECTED)
13603c8efc1SHerbert Xu 		return -EINVAL;
13703c8efc1SHerbert Xu 
13803c8efc1SHerbert Xu 	if (addr_len != sizeof(*sa))
13903c8efc1SHerbert Xu 		return -EINVAL;
14003c8efc1SHerbert Xu 
14103c8efc1SHerbert Xu 	sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
14203c8efc1SHerbert Xu 	sa->salg_name[sizeof(sa->salg_name) - 1] = 0;
14303c8efc1SHerbert Xu 
14403c8efc1SHerbert Xu 	type = alg_get_type(sa->salg_type);
14503c8efc1SHerbert Xu 	if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
14603c8efc1SHerbert Xu 		request_module("algif-%s", sa->salg_type);
14703c8efc1SHerbert Xu 		type = alg_get_type(sa->salg_type);
14803c8efc1SHerbert Xu 	}
14903c8efc1SHerbert Xu 
15003c8efc1SHerbert Xu 	if (IS_ERR(type))
15103c8efc1SHerbert Xu 		return PTR_ERR(type);
15203c8efc1SHerbert Xu 
15303c8efc1SHerbert Xu 	private = type->bind(sa->salg_name, sa->salg_feat, sa->salg_mask);
15403c8efc1SHerbert Xu 	if (IS_ERR(private)) {
15503c8efc1SHerbert Xu 		module_put(type->owner);
15603c8efc1SHerbert Xu 		return PTR_ERR(private);
15703c8efc1SHerbert Xu 	}
15803c8efc1SHerbert Xu 
15903c8efc1SHerbert Xu 	lock_sock(sk);
16003c8efc1SHerbert Xu 
16103c8efc1SHerbert Xu 	swap(ask->type, type);
16203c8efc1SHerbert Xu 	swap(ask->private, private);
16303c8efc1SHerbert Xu 
16403c8efc1SHerbert Xu 	release_sock(sk);
16503c8efc1SHerbert Xu 
16603c8efc1SHerbert Xu 	alg_do_release(type, private);
16703c8efc1SHerbert Xu 
16803c8efc1SHerbert Xu 	return 0;
16903c8efc1SHerbert Xu }
17003c8efc1SHerbert Xu 
17103c8efc1SHerbert Xu static int alg_setkey(struct sock *sk, char __user *ukey,
17203c8efc1SHerbert Xu 		      unsigned int keylen)
17303c8efc1SHerbert Xu {
17403c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
17503c8efc1SHerbert Xu 	const struct af_alg_type *type = ask->type;
17603c8efc1SHerbert Xu 	u8 *key;
17703c8efc1SHerbert Xu 	int err;
17803c8efc1SHerbert Xu 
17903c8efc1SHerbert Xu 	key = sock_kmalloc(sk, keylen, GFP_KERNEL);
18003c8efc1SHerbert Xu 	if (!key)
18103c8efc1SHerbert Xu 		return -ENOMEM;
18203c8efc1SHerbert Xu 
18303c8efc1SHerbert Xu 	err = -EFAULT;
18403c8efc1SHerbert Xu 	if (copy_from_user(key, ukey, keylen))
18503c8efc1SHerbert Xu 		goto out;
18603c8efc1SHerbert Xu 
18703c8efc1SHerbert Xu 	err = type->setkey(ask->private, key, keylen);
18803c8efc1SHerbert Xu 
18903c8efc1SHerbert Xu out:
19003c8efc1SHerbert Xu 	sock_kfree_s(sk, key, keylen);
19103c8efc1SHerbert Xu 
19203c8efc1SHerbert Xu 	return err;
19303c8efc1SHerbert Xu }
19403c8efc1SHerbert Xu 
19503c8efc1SHerbert Xu static int alg_setsockopt(struct socket *sock, int level, int optname,
19603c8efc1SHerbert Xu 			  char __user *optval, unsigned int optlen)
19703c8efc1SHerbert Xu {
19803c8efc1SHerbert Xu 	struct sock *sk = sock->sk;
19903c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
20003c8efc1SHerbert Xu 	const struct af_alg_type *type;
20103c8efc1SHerbert Xu 	int err = -ENOPROTOOPT;
20203c8efc1SHerbert Xu 
20303c8efc1SHerbert Xu 	lock_sock(sk);
20403c8efc1SHerbert Xu 	type = ask->type;
20503c8efc1SHerbert Xu 
20603c8efc1SHerbert Xu 	if (level != SOL_ALG || !type)
20703c8efc1SHerbert Xu 		goto unlock;
20803c8efc1SHerbert Xu 
20903c8efc1SHerbert Xu 	switch (optname) {
21003c8efc1SHerbert Xu 	case ALG_SET_KEY:
21103c8efc1SHerbert Xu 		if (sock->state == SS_CONNECTED)
21203c8efc1SHerbert Xu 			goto unlock;
21303c8efc1SHerbert Xu 		if (!type->setkey)
21403c8efc1SHerbert Xu 			goto unlock;
21503c8efc1SHerbert Xu 
21603c8efc1SHerbert Xu 		err = alg_setkey(sk, optval, optlen);
21703c8efc1SHerbert Xu 	}
21803c8efc1SHerbert Xu 
21903c8efc1SHerbert Xu unlock:
22003c8efc1SHerbert Xu 	release_sock(sk);
22103c8efc1SHerbert Xu 
22203c8efc1SHerbert Xu 	return err;
22303c8efc1SHerbert Xu }
22403c8efc1SHerbert Xu 
22503c8efc1SHerbert Xu int af_alg_accept(struct sock *sk, struct socket *newsock)
22603c8efc1SHerbert Xu {
22703c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
22803c8efc1SHerbert Xu 	const struct af_alg_type *type;
22903c8efc1SHerbert Xu 	struct sock *sk2;
23003c8efc1SHerbert Xu 	int err;
23103c8efc1SHerbert Xu 
23203c8efc1SHerbert Xu 	lock_sock(sk);
23303c8efc1SHerbert Xu 	type = ask->type;
23403c8efc1SHerbert Xu 
23503c8efc1SHerbert Xu 	err = -EINVAL;
23603c8efc1SHerbert Xu 	if (!type)
23703c8efc1SHerbert Xu 		goto unlock;
23803c8efc1SHerbert Xu 
23903c8efc1SHerbert Xu 	sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto);
24003c8efc1SHerbert Xu 	err = -ENOMEM;
24103c8efc1SHerbert Xu 	if (!sk2)
24203c8efc1SHerbert Xu 		goto unlock;
24303c8efc1SHerbert Xu 
24403c8efc1SHerbert Xu 	sock_init_data(newsock, sk2);
245507cad35SMiloslav Trmač 	sock_graft(sk2, newsock);
24603c8efc1SHerbert Xu 
24703c8efc1SHerbert Xu 	err = type->accept(ask->private, sk2);
24803c8efc1SHerbert Xu 	if (err) {
24903c8efc1SHerbert Xu 		sk_free(sk2);
25003c8efc1SHerbert Xu 		goto unlock;
25103c8efc1SHerbert Xu 	}
25203c8efc1SHerbert Xu 
25303c8efc1SHerbert Xu 	sk2->sk_family = PF_ALG;
25403c8efc1SHerbert Xu 
25503c8efc1SHerbert Xu 	sock_hold(sk);
25603c8efc1SHerbert Xu 	alg_sk(sk2)->parent = sk;
25703c8efc1SHerbert Xu 	alg_sk(sk2)->type = type;
25803c8efc1SHerbert Xu 
25903c8efc1SHerbert Xu 	newsock->ops = type->ops;
26003c8efc1SHerbert Xu 	newsock->state = SS_CONNECTED;
26103c8efc1SHerbert Xu 
26203c8efc1SHerbert Xu 	err = 0;
26303c8efc1SHerbert Xu 
26403c8efc1SHerbert Xu unlock:
26503c8efc1SHerbert Xu 	release_sock(sk);
26603c8efc1SHerbert Xu 
26703c8efc1SHerbert Xu 	return err;
26803c8efc1SHerbert Xu }
26903c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_accept);
27003c8efc1SHerbert Xu 
27103c8efc1SHerbert Xu static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
27203c8efc1SHerbert Xu {
27303c8efc1SHerbert Xu 	return af_alg_accept(sock->sk, newsock);
27403c8efc1SHerbert Xu }
27503c8efc1SHerbert Xu 
27603c8efc1SHerbert Xu static const struct proto_ops alg_proto_ops = {
27703c8efc1SHerbert Xu 	.family		=	PF_ALG,
27803c8efc1SHerbert Xu 	.owner		=	THIS_MODULE,
27903c8efc1SHerbert Xu 
28003c8efc1SHerbert Xu 	.connect	=	sock_no_connect,
28103c8efc1SHerbert Xu 	.socketpair	=	sock_no_socketpair,
28203c8efc1SHerbert Xu 	.getname	=	sock_no_getname,
28303c8efc1SHerbert Xu 	.ioctl		=	sock_no_ioctl,
28403c8efc1SHerbert Xu 	.listen		=	sock_no_listen,
28503c8efc1SHerbert Xu 	.shutdown	=	sock_no_shutdown,
28603c8efc1SHerbert Xu 	.getsockopt	=	sock_no_getsockopt,
28703c8efc1SHerbert Xu 	.mmap		=	sock_no_mmap,
28803c8efc1SHerbert Xu 	.sendpage	=	sock_no_sendpage,
28903c8efc1SHerbert Xu 	.sendmsg	=	sock_no_sendmsg,
29003c8efc1SHerbert Xu 	.recvmsg	=	sock_no_recvmsg,
29103c8efc1SHerbert Xu 	.poll		=	sock_no_poll,
29203c8efc1SHerbert Xu 
29303c8efc1SHerbert Xu 	.bind		=	alg_bind,
29403c8efc1SHerbert Xu 	.release	=	af_alg_release,
29503c8efc1SHerbert Xu 	.setsockopt	=	alg_setsockopt,
29603c8efc1SHerbert Xu 	.accept		=	alg_accept,
29703c8efc1SHerbert Xu };
29803c8efc1SHerbert Xu 
29903c8efc1SHerbert Xu static void alg_sock_destruct(struct sock *sk)
30003c8efc1SHerbert Xu {
30103c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
30203c8efc1SHerbert Xu 
30303c8efc1SHerbert Xu 	alg_do_release(ask->type, ask->private);
30403c8efc1SHerbert Xu }
30503c8efc1SHerbert Xu 
30603c8efc1SHerbert Xu static int alg_create(struct net *net, struct socket *sock, int protocol,
30703c8efc1SHerbert Xu 		      int kern)
30803c8efc1SHerbert Xu {
30903c8efc1SHerbert Xu 	struct sock *sk;
31003c8efc1SHerbert Xu 	int err;
31103c8efc1SHerbert Xu 
31203c8efc1SHerbert Xu 	if (sock->type != SOCK_SEQPACKET)
31303c8efc1SHerbert Xu 		return -ESOCKTNOSUPPORT;
31403c8efc1SHerbert Xu 	if (protocol != 0)
31503c8efc1SHerbert Xu 		return -EPROTONOSUPPORT;
31603c8efc1SHerbert Xu 
31703c8efc1SHerbert Xu 	err = -ENOMEM;
31803c8efc1SHerbert Xu 	sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto);
31903c8efc1SHerbert Xu 	if (!sk)
32003c8efc1SHerbert Xu 		goto out;
32103c8efc1SHerbert Xu 
32203c8efc1SHerbert Xu 	sock->ops = &alg_proto_ops;
32303c8efc1SHerbert Xu 	sock_init_data(sock, sk);
32403c8efc1SHerbert Xu 
32503c8efc1SHerbert Xu 	sk->sk_family = PF_ALG;
32603c8efc1SHerbert Xu 	sk->sk_destruct = alg_sock_destruct;
32703c8efc1SHerbert Xu 
32803c8efc1SHerbert Xu 	return 0;
32903c8efc1SHerbert Xu out:
33003c8efc1SHerbert Xu 	return err;
33103c8efc1SHerbert Xu }
33203c8efc1SHerbert Xu 
33303c8efc1SHerbert Xu static const struct net_proto_family alg_family = {
33403c8efc1SHerbert Xu 	.family	=	PF_ALG,
33503c8efc1SHerbert Xu 	.create	=	alg_create,
33603c8efc1SHerbert Xu 	.owner	=	THIS_MODULE,
33703c8efc1SHerbert Xu };
33803c8efc1SHerbert Xu 
33903c8efc1SHerbert Xu int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
34003c8efc1SHerbert Xu 		   int write)
34103c8efc1SHerbert Xu {
34203c8efc1SHerbert Xu 	unsigned long from = (unsigned long)addr;
34303c8efc1SHerbert Xu 	unsigned long npages;
34403c8efc1SHerbert Xu 	unsigned off;
34503c8efc1SHerbert Xu 	int err;
34603c8efc1SHerbert Xu 	int i;
34703c8efc1SHerbert Xu 
34803c8efc1SHerbert Xu 	err = -EFAULT;
34903c8efc1SHerbert Xu 	if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, addr, len))
35003c8efc1SHerbert Xu 		goto out;
35103c8efc1SHerbert Xu 
35203c8efc1SHerbert Xu 	off = from & ~PAGE_MASK;
35303c8efc1SHerbert Xu 	npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
35403c8efc1SHerbert Xu 	if (npages > ALG_MAX_PAGES)
35503c8efc1SHerbert Xu 		npages = ALG_MAX_PAGES;
35603c8efc1SHerbert Xu 
35703c8efc1SHerbert Xu 	err = get_user_pages_fast(from, npages, write, sgl->pages);
35803c8efc1SHerbert Xu 	if (err < 0)
35903c8efc1SHerbert Xu 		goto out;
36003c8efc1SHerbert Xu 
36103c8efc1SHerbert Xu 	npages = err;
36203c8efc1SHerbert Xu 	err = -EINVAL;
36303c8efc1SHerbert Xu 	if (WARN_ON(npages == 0))
36403c8efc1SHerbert Xu 		goto out;
36503c8efc1SHerbert Xu 
36603c8efc1SHerbert Xu 	err = 0;
36703c8efc1SHerbert Xu 
36803c8efc1SHerbert Xu 	sg_init_table(sgl->sg, npages);
36903c8efc1SHerbert Xu 
37003c8efc1SHerbert Xu 	for (i = 0; i < npages; i++) {
37103c8efc1SHerbert Xu 		int plen = min_t(int, len, PAGE_SIZE - off);
37203c8efc1SHerbert Xu 
37303c8efc1SHerbert Xu 		sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
37403c8efc1SHerbert Xu 
37503c8efc1SHerbert Xu 		off = 0;
37603c8efc1SHerbert Xu 		len -= plen;
37703c8efc1SHerbert Xu 		err += plen;
37803c8efc1SHerbert Xu 	}
37903c8efc1SHerbert Xu 
38003c8efc1SHerbert Xu out:
38103c8efc1SHerbert Xu 	return err;
38203c8efc1SHerbert Xu }
38303c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_make_sg);
38403c8efc1SHerbert Xu 
38503c8efc1SHerbert Xu void af_alg_free_sg(struct af_alg_sgl *sgl)
38603c8efc1SHerbert Xu {
38703c8efc1SHerbert Xu 	int i;
38803c8efc1SHerbert Xu 
38903c8efc1SHerbert Xu 	i = 0;
39003c8efc1SHerbert Xu 	do {
39103c8efc1SHerbert Xu 		put_page(sgl->pages[i]);
39203c8efc1SHerbert Xu 	} while (!sg_is_last(sgl->sg + (i++)));
39303c8efc1SHerbert Xu }
39403c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_free_sg);
39503c8efc1SHerbert Xu 
39603c8efc1SHerbert Xu int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
39703c8efc1SHerbert Xu {
39803c8efc1SHerbert Xu 	struct cmsghdr *cmsg;
39903c8efc1SHerbert Xu 
40003c8efc1SHerbert Xu 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
40103c8efc1SHerbert Xu 		if (!CMSG_OK(msg, cmsg))
40203c8efc1SHerbert Xu 			return -EINVAL;
40303c8efc1SHerbert Xu 		if (cmsg->cmsg_level != SOL_ALG)
40403c8efc1SHerbert Xu 			continue;
40503c8efc1SHerbert Xu 
40603c8efc1SHerbert Xu 		switch(cmsg->cmsg_type) {
40703c8efc1SHerbert Xu 		case ALG_SET_IV:
40803c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv)))
40903c8efc1SHerbert Xu 				return -EINVAL;
41003c8efc1SHerbert Xu 			con->iv = (void *)CMSG_DATA(cmsg);
41103c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen +
41203c8efc1SHerbert Xu 						      sizeof(*con->iv)))
41303c8efc1SHerbert Xu 				return -EINVAL;
41403c8efc1SHerbert Xu 			break;
41503c8efc1SHerbert Xu 
41603c8efc1SHerbert Xu 		case ALG_SET_OP:
41703c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
41803c8efc1SHerbert Xu 				return -EINVAL;
41903c8efc1SHerbert Xu 			con->op = *(u32 *)CMSG_DATA(cmsg);
42003c8efc1SHerbert Xu 			break;
42103c8efc1SHerbert Xu 
42203c8efc1SHerbert Xu 		default:
42303c8efc1SHerbert Xu 			return -EINVAL;
42403c8efc1SHerbert Xu 		}
42503c8efc1SHerbert Xu 	}
42603c8efc1SHerbert Xu 
42703c8efc1SHerbert Xu 	return 0;
42803c8efc1SHerbert Xu }
42903c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
43003c8efc1SHerbert Xu 
43103c8efc1SHerbert Xu int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
43203c8efc1SHerbert Xu {
43303c8efc1SHerbert Xu 	switch (err) {
43403c8efc1SHerbert Xu 	case -EINPROGRESS:
43503c8efc1SHerbert Xu 	case -EBUSY:
43603c8efc1SHerbert Xu 		wait_for_completion(&completion->completion);
43703c8efc1SHerbert Xu 		INIT_COMPLETION(completion->completion);
43803c8efc1SHerbert Xu 		err = completion->err;
43903c8efc1SHerbert Xu 		break;
44003c8efc1SHerbert Xu 	};
44103c8efc1SHerbert Xu 
44203c8efc1SHerbert Xu 	return err;
44303c8efc1SHerbert Xu }
44403c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
44503c8efc1SHerbert Xu 
44603c8efc1SHerbert Xu void af_alg_complete(struct crypto_async_request *req, int err)
44703c8efc1SHerbert Xu {
44803c8efc1SHerbert Xu 	struct af_alg_completion *completion = req->data;
44903c8efc1SHerbert Xu 
45003c8efc1SHerbert Xu 	completion->err = err;
45103c8efc1SHerbert Xu 	complete(&completion->completion);
45203c8efc1SHerbert Xu }
45303c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_complete);
45403c8efc1SHerbert Xu 
45503c8efc1SHerbert Xu static int __init af_alg_init(void)
45603c8efc1SHerbert Xu {
45703c8efc1SHerbert Xu 	int err = proto_register(&alg_proto, 0);
45803c8efc1SHerbert Xu 
45903c8efc1SHerbert Xu 	if (err)
46003c8efc1SHerbert Xu 		goto out;
46103c8efc1SHerbert Xu 
46203c8efc1SHerbert Xu 	err = sock_register(&alg_family);
46303c8efc1SHerbert Xu 	if (err != 0)
46403c8efc1SHerbert Xu 		goto out_unregister_proto;
46503c8efc1SHerbert Xu 
46603c8efc1SHerbert Xu out:
46703c8efc1SHerbert Xu 	return err;
46803c8efc1SHerbert Xu 
46903c8efc1SHerbert Xu out_unregister_proto:
47003c8efc1SHerbert Xu 	proto_unregister(&alg_proto);
47103c8efc1SHerbert Xu 	goto out;
47203c8efc1SHerbert Xu }
47303c8efc1SHerbert Xu 
47403c8efc1SHerbert Xu static void __exit af_alg_exit(void)
47503c8efc1SHerbert Xu {
47603c8efc1SHerbert Xu 	sock_unregister(PF_ALG);
47703c8efc1SHerbert Xu 	proto_unregister(&alg_proto);
47803c8efc1SHerbert Xu }
47903c8efc1SHerbert Xu 
48003c8efc1SHerbert Xu module_init(af_alg_init);
48103c8efc1SHerbert Xu module_exit(af_alg_exit);
48203c8efc1SHerbert Xu MODULE_LICENSE("GPL");
48303c8efc1SHerbert Xu MODULE_ALIAS_NETPROTO(AF_ALG);
484