xref: /openbmc/linux/crypto/af_alg.c (revision 3f69cc60)
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;
7937766586SHerbert Xu 	if (type->ops_nokey)
8037766586SHerbert Xu 		type->ops_nokey->owner = THIS_MODULE;
8103c8efc1SHerbert Xu 	node->type = type;
8203c8efc1SHerbert Xu 	list_add(&node->list, &alg_types);
8303c8efc1SHerbert Xu 	err = 0;
8403c8efc1SHerbert Xu 
8503c8efc1SHerbert Xu unlock:
8603c8efc1SHerbert Xu 	up_write(&alg_types_sem);
8703c8efc1SHerbert Xu 
8803c8efc1SHerbert Xu 	return err;
8903c8efc1SHerbert Xu }
9003c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_register_type);
9103c8efc1SHerbert Xu 
9203c8efc1SHerbert Xu int af_alg_unregister_type(const struct af_alg_type *type)
9303c8efc1SHerbert Xu {
9403c8efc1SHerbert Xu 	struct alg_type_list *node;
9503c8efc1SHerbert Xu 	int err = -ENOENT;
9603c8efc1SHerbert Xu 
9703c8efc1SHerbert Xu 	down_write(&alg_types_sem);
9803c8efc1SHerbert Xu 	list_for_each_entry(node, &alg_types, list) {
9903c8efc1SHerbert Xu 		if (strcmp(node->type->name, type->name))
10003c8efc1SHerbert Xu 			continue;
10103c8efc1SHerbert Xu 
10203c8efc1SHerbert Xu 		list_del(&node->list);
10303c8efc1SHerbert Xu 		kfree(node);
10403c8efc1SHerbert Xu 		err = 0;
10503c8efc1SHerbert Xu 		break;
10603c8efc1SHerbert Xu 	}
10703c8efc1SHerbert Xu 	up_write(&alg_types_sem);
10803c8efc1SHerbert Xu 
10903c8efc1SHerbert Xu 	return err;
11003c8efc1SHerbert Xu }
11103c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_unregister_type);
11203c8efc1SHerbert Xu 
11303c8efc1SHerbert Xu static void alg_do_release(const struct af_alg_type *type, void *private)
11403c8efc1SHerbert Xu {
11503c8efc1SHerbert Xu 	if (!type)
11603c8efc1SHerbert Xu 		return;
11703c8efc1SHerbert Xu 
11803c8efc1SHerbert Xu 	type->release(private);
11903c8efc1SHerbert Xu 	module_put(type->owner);
12003c8efc1SHerbert Xu }
12103c8efc1SHerbert Xu 
12203c8efc1SHerbert Xu int af_alg_release(struct socket *sock)
12303c8efc1SHerbert Xu {
12403c8efc1SHerbert Xu 	if (sock->sk)
12503c8efc1SHerbert Xu 		sock_put(sock->sk);
12603c8efc1SHerbert Xu 	return 0;
12703c8efc1SHerbert Xu }
12803c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_release);
12903c8efc1SHerbert Xu 
130c840ac6aSHerbert Xu void af_alg_release_parent(struct sock *sk)
131c840ac6aSHerbert Xu {
132c840ac6aSHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
133a6a48c56SHerbert Xu 	unsigned int nokey = ask->nokey_refcnt;
134a6a48c56SHerbert Xu 	bool last = nokey && !ask->refcnt;
135c840ac6aSHerbert Xu 
136c840ac6aSHerbert Xu 	sk = ask->parent;
137c840ac6aSHerbert Xu 	ask = alg_sk(sk);
138c840ac6aSHerbert Xu 
139c840ac6aSHerbert Xu 	lock_sock(sk);
140a6a48c56SHerbert Xu 	ask->nokey_refcnt -= nokey;
141a6a48c56SHerbert Xu 	if (!last)
142c840ac6aSHerbert Xu 		last = !--ask->refcnt;
143c840ac6aSHerbert Xu 	release_sock(sk);
144c840ac6aSHerbert Xu 
145c840ac6aSHerbert Xu 	if (last)
146c840ac6aSHerbert Xu 		sock_put(sk);
147c840ac6aSHerbert Xu }
148c840ac6aSHerbert Xu EXPORT_SYMBOL_GPL(af_alg_release_parent);
149c840ac6aSHerbert Xu 
15003c8efc1SHerbert Xu static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
15103c8efc1SHerbert Xu {
15215539de5SHerbert Xu 	const u32 forbidden = CRYPTO_ALG_INTERNAL;
15303c8efc1SHerbert Xu 	struct sock *sk = sock->sk;
15403c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
15503c8efc1SHerbert Xu 	struct sockaddr_alg *sa = (void *)uaddr;
15603c8efc1SHerbert Xu 	const struct af_alg_type *type;
15703c8efc1SHerbert Xu 	void *private;
158c840ac6aSHerbert Xu 	int err;
15903c8efc1SHerbert Xu 
16003c8efc1SHerbert Xu 	if (sock->state == SS_CONNECTED)
16103c8efc1SHerbert Xu 		return -EINVAL;
16203c8efc1SHerbert Xu 
1633f69cc60SHerbert Xu 	if (addr_len < sizeof(*sa))
16403c8efc1SHerbert Xu 		return -EINVAL;
16503c8efc1SHerbert Xu 
16603c8efc1SHerbert Xu 	sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
1673f69cc60SHerbert Xu 	sa->salg_name[sizeof(sa->salg_name) + addr_len - sizeof(*sa) - 1] = 0;
16803c8efc1SHerbert Xu 
16903c8efc1SHerbert Xu 	type = alg_get_type(sa->salg_type);
17003c8efc1SHerbert Xu 	if (IS_ERR(type) && PTR_ERR(type) == -ENOENT) {
17103c8efc1SHerbert Xu 		request_module("algif-%s", sa->salg_type);
17203c8efc1SHerbert Xu 		type = alg_get_type(sa->salg_type);
17303c8efc1SHerbert Xu 	}
17403c8efc1SHerbert Xu 
17503c8efc1SHerbert Xu 	if (IS_ERR(type))
17603c8efc1SHerbert Xu 		return PTR_ERR(type);
17703c8efc1SHerbert Xu 
17815539de5SHerbert Xu 	private = type->bind(sa->salg_name,
17915539de5SHerbert Xu 			     sa->salg_feat & ~forbidden,
18015539de5SHerbert Xu 			     sa->salg_mask & ~forbidden);
18103c8efc1SHerbert Xu 	if (IS_ERR(private)) {
18203c8efc1SHerbert Xu 		module_put(type->owner);
18303c8efc1SHerbert Xu 		return PTR_ERR(private);
18403c8efc1SHerbert Xu 	}
18503c8efc1SHerbert Xu 
186c840ac6aSHerbert Xu 	err = -EBUSY;
18703c8efc1SHerbert Xu 	lock_sock(sk);
188a6a48c56SHerbert Xu 	if (ask->refcnt | ask->nokey_refcnt)
189c840ac6aSHerbert Xu 		goto unlock;
19003c8efc1SHerbert Xu 
19103c8efc1SHerbert Xu 	swap(ask->type, type);
19203c8efc1SHerbert Xu 	swap(ask->private, private);
19303c8efc1SHerbert Xu 
194c840ac6aSHerbert Xu 	err = 0;
195c840ac6aSHerbert Xu 
196c840ac6aSHerbert Xu unlock:
19703c8efc1SHerbert Xu 	release_sock(sk);
19803c8efc1SHerbert Xu 
19903c8efc1SHerbert Xu 	alg_do_release(type, private);
20003c8efc1SHerbert Xu 
201c840ac6aSHerbert Xu 	return err;
20203c8efc1SHerbert Xu }
20303c8efc1SHerbert Xu 
20403c8efc1SHerbert Xu static int alg_setkey(struct sock *sk, char __user *ukey,
20503c8efc1SHerbert Xu 		      unsigned int keylen)
20603c8efc1SHerbert Xu {
20703c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
20803c8efc1SHerbert Xu 	const struct af_alg_type *type = ask->type;
20903c8efc1SHerbert Xu 	u8 *key;
21003c8efc1SHerbert Xu 	int err;
21103c8efc1SHerbert Xu 
21203c8efc1SHerbert Xu 	key = sock_kmalloc(sk, keylen, GFP_KERNEL);
21303c8efc1SHerbert Xu 	if (!key)
21403c8efc1SHerbert Xu 		return -ENOMEM;
21503c8efc1SHerbert Xu 
21603c8efc1SHerbert Xu 	err = -EFAULT;
21703c8efc1SHerbert Xu 	if (copy_from_user(key, ukey, keylen))
21803c8efc1SHerbert Xu 		goto out;
21903c8efc1SHerbert Xu 
22003c8efc1SHerbert Xu 	err = type->setkey(ask->private, key, keylen);
22103c8efc1SHerbert Xu 
22203c8efc1SHerbert Xu out:
223ad202c8cSStephan Mueller 	sock_kzfree_s(sk, key, keylen);
22403c8efc1SHerbert Xu 
22503c8efc1SHerbert Xu 	return err;
22603c8efc1SHerbert Xu }
22703c8efc1SHerbert Xu 
22803c8efc1SHerbert Xu static int alg_setsockopt(struct socket *sock, int level, int optname,
22903c8efc1SHerbert Xu 			  char __user *optval, unsigned int optlen)
23003c8efc1SHerbert Xu {
23103c8efc1SHerbert Xu 	struct sock *sk = sock->sk;
23203c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
23303c8efc1SHerbert Xu 	const struct af_alg_type *type;
234c840ac6aSHerbert Xu 	int err = -EBUSY;
23503c8efc1SHerbert Xu 
23603c8efc1SHerbert Xu 	lock_sock(sk);
237c840ac6aSHerbert Xu 	if (ask->refcnt)
238c840ac6aSHerbert Xu 		goto unlock;
239c840ac6aSHerbert Xu 
24003c8efc1SHerbert Xu 	type = ask->type;
24103c8efc1SHerbert Xu 
242c840ac6aSHerbert Xu 	err = -ENOPROTOOPT;
24303c8efc1SHerbert Xu 	if (level != SOL_ALG || !type)
24403c8efc1SHerbert Xu 		goto unlock;
24503c8efc1SHerbert Xu 
24603c8efc1SHerbert Xu 	switch (optname) {
24703c8efc1SHerbert Xu 	case ALG_SET_KEY:
24803c8efc1SHerbert Xu 		if (sock->state == SS_CONNECTED)
24903c8efc1SHerbert Xu 			goto unlock;
25003c8efc1SHerbert Xu 		if (!type->setkey)
25103c8efc1SHerbert Xu 			goto unlock;
25203c8efc1SHerbert Xu 
25303c8efc1SHerbert Xu 		err = alg_setkey(sk, optval, optlen);
25425fb8638SStephan Mueller 		break;
25525fb8638SStephan Mueller 	case ALG_SET_AEAD_AUTHSIZE:
25625fb8638SStephan Mueller 		if (sock->state == SS_CONNECTED)
25725fb8638SStephan Mueller 			goto unlock;
25825fb8638SStephan Mueller 		if (!type->setauthsize)
25925fb8638SStephan Mueller 			goto unlock;
26025fb8638SStephan Mueller 		err = type->setauthsize(ask->private, optlen);
26103c8efc1SHerbert Xu 	}
26203c8efc1SHerbert Xu 
26303c8efc1SHerbert Xu unlock:
26403c8efc1SHerbert Xu 	release_sock(sk);
26503c8efc1SHerbert Xu 
26603c8efc1SHerbert Xu 	return err;
26703c8efc1SHerbert Xu }
26803c8efc1SHerbert Xu 
269cdfbabfbSDavid Howells int af_alg_accept(struct sock *sk, struct socket *newsock, bool kern)
27003c8efc1SHerbert Xu {
27103c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
27203c8efc1SHerbert Xu 	const struct af_alg_type *type;
27303c8efc1SHerbert Xu 	struct sock *sk2;
2746a935170SHerbert Xu 	unsigned int nokey;
27503c8efc1SHerbert Xu 	int err;
27603c8efc1SHerbert Xu 
27703c8efc1SHerbert Xu 	lock_sock(sk);
27803c8efc1SHerbert Xu 	type = ask->type;
27903c8efc1SHerbert Xu 
28003c8efc1SHerbert Xu 	err = -EINVAL;
28103c8efc1SHerbert Xu 	if (!type)
28203c8efc1SHerbert Xu 		goto unlock;
28303c8efc1SHerbert Xu 
284cdfbabfbSDavid Howells 	sk2 = sk_alloc(sock_net(sk), PF_ALG, GFP_KERNEL, &alg_proto, kern);
28503c8efc1SHerbert Xu 	err = -ENOMEM;
28603c8efc1SHerbert Xu 	if (!sk2)
28703c8efc1SHerbert Xu 		goto unlock;
28803c8efc1SHerbert Xu 
28903c8efc1SHerbert Xu 	sock_init_data(newsock, sk2);
290507cad35SMiloslav Trmač 	sock_graft(sk2, newsock);
2914c63f83cSMilan Broz 	security_sk_clone(sk, sk2);
29203c8efc1SHerbert Xu 
29303c8efc1SHerbert Xu 	err = type->accept(ask->private, sk2);
29437766586SHerbert Xu 
29537766586SHerbert Xu 	nokey = err == -ENOKEY;
29637766586SHerbert Xu 	if (nokey && type->accept_nokey)
29737766586SHerbert Xu 		err = type->accept_nokey(ask->private, sk2);
29837766586SHerbert Xu 
299a383292cSHerbert Xu 	if (err)
30003c8efc1SHerbert Xu 		goto unlock;
30103c8efc1SHerbert Xu 
30203c8efc1SHerbert Xu 	sk2->sk_family = PF_ALG;
30303c8efc1SHerbert Xu 
30437766586SHerbert Xu 	if (nokey || !ask->refcnt++)
30503c8efc1SHerbert Xu 		sock_hold(sk);
306a6a48c56SHerbert Xu 	ask->nokey_refcnt += nokey;
30703c8efc1SHerbert Xu 	alg_sk(sk2)->parent = sk;
30803c8efc1SHerbert Xu 	alg_sk(sk2)->type = type;
3096a935170SHerbert Xu 	alg_sk(sk2)->nokey_refcnt = nokey;
31003c8efc1SHerbert Xu 
31103c8efc1SHerbert Xu 	newsock->ops = type->ops;
31203c8efc1SHerbert Xu 	newsock->state = SS_CONNECTED;
31303c8efc1SHerbert Xu 
31437766586SHerbert Xu 	if (nokey)
31537766586SHerbert Xu 		newsock->ops = type->ops_nokey;
31637766586SHerbert Xu 
31703c8efc1SHerbert Xu 	err = 0;
31803c8efc1SHerbert Xu 
31903c8efc1SHerbert Xu unlock:
32003c8efc1SHerbert Xu 	release_sock(sk);
32103c8efc1SHerbert Xu 
32203c8efc1SHerbert Xu 	return err;
32303c8efc1SHerbert Xu }
32403c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_accept);
32503c8efc1SHerbert Xu 
326cdfbabfbSDavid Howells static int alg_accept(struct socket *sock, struct socket *newsock, int flags,
327cdfbabfbSDavid Howells 		      bool kern)
32803c8efc1SHerbert Xu {
329cdfbabfbSDavid Howells 	return af_alg_accept(sock->sk, newsock, kern);
33003c8efc1SHerbert Xu }
33103c8efc1SHerbert Xu 
33203c8efc1SHerbert Xu static const struct proto_ops alg_proto_ops = {
33303c8efc1SHerbert Xu 	.family		=	PF_ALG,
33403c8efc1SHerbert Xu 	.owner		=	THIS_MODULE,
33503c8efc1SHerbert Xu 
33603c8efc1SHerbert Xu 	.connect	=	sock_no_connect,
33703c8efc1SHerbert Xu 	.socketpair	=	sock_no_socketpair,
33803c8efc1SHerbert Xu 	.getname	=	sock_no_getname,
33903c8efc1SHerbert Xu 	.ioctl		=	sock_no_ioctl,
34003c8efc1SHerbert Xu 	.listen		=	sock_no_listen,
34103c8efc1SHerbert Xu 	.shutdown	=	sock_no_shutdown,
34203c8efc1SHerbert Xu 	.getsockopt	=	sock_no_getsockopt,
34303c8efc1SHerbert Xu 	.mmap		=	sock_no_mmap,
34403c8efc1SHerbert Xu 	.sendpage	=	sock_no_sendpage,
34503c8efc1SHerbert Xu 	.sendmsg	=	sock_no_sendmsg,
34603c8efc1SHerbert Xu 	.recvmsg	=	sock_no_recvmsg,
34703c8efc1SHerbert Xu 	.poll		=	sock_no_poll,
34803c8efc1SHerbert Xu 
34903c8efc1SHerbert Xu 	.bind		=	alg_bind,
35003c8efc1SHerbert Xu 	.release	=	af_alg_release,
35103c8efc1SHerbert Xu 	.setsockopt	=	alg_setsockopt,
35203c8efc1SHerbert Xu 	.accept		=	alg_accept,
35303c8efc1SHerbert Xu };
35403c8efc1SHerbert Xu 
35503c8efc1SHerbert Xu static void alg_sock_destruct(struct sock *sk)
35603c8efc1SHerbert Xu {
35703c8efc1SHerbert Xu 	struct alg_sock *ask = alg_sk(sk);
35803c8efc1SHerbert Xu 
35903c8efc1SHerbert Xu 	alg_do_release(ask->type, ask->private);
36003c8efc1SHerbert Xu }
36103c8efc1SHerbert Xu 
36203c8efc1SHerbert Xu static int alg_create(struct net *net, struct socket *sock, int protocol,
36303c8efc1SHerbert Xu 		      int kern)
36403c8efc1SHerbert Xu {
36503c8efc1SHerbert Xu 	struct sock *sk;
36603c8efc1SHerbert Xu 	int err;
36703c8efc1SHerbert Xu 
36803c8efc1SHerbert Xu 	if (sock->type != SOCK_SEQPACKET)
36903c8efc1SHerbert Xu 		return -ESOCKTNOSUPPORT;
37003c8efc1SHerbert Xu 	if (protocol != 0)
37103c8efc1SHerbert Xu 		return -EPROTONOSUPPORT;
37203c8efc1SHerbert Xu 
37303c8efc1SHerbert Xu 	err = -ENOMEM;
37411aa9c28SEric W. Biederman 	sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto, kern);
37503c8efc1SHerbert Xu 	if (!sk)
37603c8efc1SHerbert Xu 		goto out;
37703c8efc1SHerbert Xu 
37803c8efc1SHerbert Xu 	sock->ops = &alg_proto_ops;
37903c8efc1SHerbert Xu 	sock_init_data(sock, sk);
38003c8efc1SHerbert Xu 
38103c8efc1SHerbert Xu 	sk->sk_family = PF_ALG;
38203c8efc1SHerbert Xu 	sk->sk_destruct = alg_sock_destruct;
38303c8efc1SHerbert Xu 
38403c8efc1SHerbert Xu 	return 0;
38503c8efc1SHerbert Xu out:
38603c8efc1SHerbert Xu 	return err;
38703c8efc1SHerbert Xu }
38803c8efc1SHerbert Xu 
38903c8efc1SHerbert Xu static const struct net_proto_family alg_family = {
39003c8efc1SHerbert Xu 	.family	=	PF_ALG,
39103c8efc1SHerbert Xu 	.create	=	alg_create,
39203c8efc1SHerbert Xu 	.owner	=	THIS_MODULE,
39303c8efc1SHerbert Xu };
39403c8efc1SHerbert Xu 
3951d10eb2fSAl Viro int af_alg_make_sg(struct af_alg_sgl *sgl, struct iov_iter *iter, int len)
39603c8efc1SHerbert Xu {
3971d10eb2fSAl Viro 	size_t off;
3981d10eb2fSAl Viro 	ssize_t n;
3991d10eb2fSAl Viro 	int npages, i;
40003c8efc1SHerbert Xu 
4011d10eb2fSAl Viro 	n = iov_iter_get_pages(iter, sgl->pages, len, ALG_MAX_PAGES, &off);
4021d10eb2fSAl Viro 	if (n < 0)
4031d10eb2fSAl Viro 		return n;
40403c8efc1SHerbert Xu 
4059399f0c5SLinus Torvalds 	npages = (off + n + PAGE_SIZE - 1) >> PAGE_SHIFT;
40603c8efc1SHerbert Xu 	if (WARN_ON(npages == 0))
4071d10eb2fSAl Viro 		return -EINVAL;
40866db3739STadeusz Struk 	/* Add one extra for linking */
40966db3739STadeusz Struk 	sg_init_table(sgl->sg, npages + 1);
41003c8efc1SHerbert Xu 
4111d10eb2fSAl Viro 	for (i = 0, len = n; i < npages; i++) {
41203c8efc1SHerbert Xu 		int plen = min_t(int, len, PAGE_SIZE - off);
41303c8efc1SHerbert Xu 
41403c8efc1SHerbert Xu 		sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
41503c8efc1SHerbert Xu 
41603c8efc1SHerbert Xu 		off = 0;
41703c8efc1SHerbert Xu 		len -= plen;
41803c8efc1SHerbert Xu 	}
41966db3739STadeusz Struk 	sg_mark_end(sgl->sg + npages - 1);
42066db3739STadeusz Struk 	sgl->npages = npages;
42166db3739STadeusz Struk 
4221d10eb2fSAl Viro 	return n;
42303c8efc1SHerbert Xu }
42403c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_make_sg);
42503c8efc1SHerbert Xu 
42666db3739STadeusz Struk void af_alg_link_sg(struct af_alg_sgl *sgl_prev, struct af_alg_sgl *sgl_new)
42766db3739STadeusz Struk {
42866db3739STadeusz Struk 	sg_unmark_end(sgl_prev->sg + sgl_prev->npages - 1);
42966db3739STadeusz Struk 	sg_chain(sgl_prev->sg, sgl_prev->npages + 1, sgl_new->sg);
43066db3739STadeusz Struk }
431bd507520STadeusz Struk EXPORT_SYMBOL_GPL(af_alg_link_sg);
43266db3739STadeusz Struk 
43303c8efc1SHerbert Xu void af_alg_free_sg(struct af_alg_sgl *sgl)
43403c8efc1SHerbert Xu {
43503c8efc1SHerbert Xu 	int i;
43603c8efc1SHerbert Xu 
43766db3739STadeusz Struk 	for (i = 0; i < sgl->npages; i++)
43803c8efc1SHerbert Xu 		put_page(sgl->pages[i]);
43903c8efc1SHerbert Xu }
44003c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_free_sg);
44103c8efc1SHerbert Xu 
44203c8efc1SHerbert Xu int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
44303c8efc1SHerbert Xu {
44403c8efc1SHerbert Xu 	struct cmsghdr *cmsg;
44503c8efc1SHerbert Xu 
446f95b414eSGu Zheng 	for_each_cmsghdr(cmsg, msg) {
44703c8efc1SHerbert Xu 		if (!CMSG_OK(msg, cmsg))
44803c8efc1SHerbert Xu 			return -EINVAL;
44903c8efc1SHerbert Xu 		if (cmsg->cmsg_level != SOL_ALG)
45003c8efc1SHerbert Xu 			continue;
45103c8efc1SHerbert Xu 
45203c8efc1SHerbert Xu 		switch (cmsg->cmsg_type) {
45303c8efc1SHerbert Xu 		case ALG_SET_IV:
45403c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv)))
45503c8efc1SHerbert Xu 				return -EINVAL;
45603c8efc1SHerbert Xu 			con->iv = (void *)CMSG_DATA(cmsg);
45703c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen +
45803c8efc1SHerbert Xu 						      sizeof(*con->iv)))
45903c8efc1SHerbert Xu 				return -EINVAL;
46003c8efc1SHerbert Xu 			break;
46103c8efc1SHerbert Xu 
46203c8efc1SHerbert Xu 		case ALG_SET_OP:
46303c8efc1SHerbert Xu 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
46403c8efc1SHerbert Xu 				return -EINVAL;
46503c8efc1SHerbert Xu 			con->op = *(u32 *)CMSG_DATA(cmsg);
46603c8efc1SHerbert Xu 			break;
46703c8efc1SHerbert Xu 
468af8e8073SStephan Mueller 		case ALG_SET_AEAD_ASSOCLEN:
469af8e8073SStephan Mueller 			if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
470af8e8073SStephan Mueller 				return -EINVAL;
471af8e8073SStephan Mueller 			con->aead_assoclen = *(u32 *)CMSG_DATA(cmsg);
472af8e8073SStephan Mueller 			break;
473af8e8073SStephan Mueller 
47403c8efc1SHerbert Xu 		default:
47503c8efc1SHerbert Xu 			return -EINVAL;
47603c8efc1SHerbert Xu 		}
47703c8efc1SHerbert Xu 	}
47803c8efc1SHerbert Xu 
47903c8efc1SHerbert Xu 	return 0;
48003c8efc1SHerbert Xu }
48103c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
48203c8efc1SHerbert Xu 
48303c8efc1SHerbert Xu int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
48403c8efc1SHerbert Xu {
48503c8efc1SHerbert Xu 	switch (err) {
48603c8efc1SHerbert Xu 	case -EINPROGRESS:
48703c8efc1SHerbert Xu 	case -EBUSY:
48803c8efc1SHerbert Xu 		wait_for_completion(&completion->completion);
48916735d02SWolfram Sang 		reinit_completion(&completion->completion);
49003c8efc1SHerbert Xu 		err = completion->err;
49103c8efc1SHerbert Xu 		break;
49203c8efc1SHerbert Xu 	};
49303c8efc1SHerbert Xu 
49403c8efc1SHerbert Xu 	return err;
49503c8efc1SHerbert Xu }
49603c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
49703c8efc1SHerbert Xu 
49803c8efc1SHerbert Xu void af_alg_complete(struct crypto_async_request *req, int err)
49903c8efc1SHerbert Xu {
50003c8efc1SHerbert Xu 	struct af_alg_completion *completion = req->data;
50103c8efc1SHerbert Xu 
5027e77bdebSRabin Vincent 	if (err == -EINPROGRESS)
5037e77bdebSRabin Vincent 		return;
5047e77bdebSRabin Vincent 
50503c8efc1SHerbert Xu 	completion->err = err;
50603c8efc1SHerbert Xu 	complete(&completion->completion);
50703c8efc1SHerbert Xu }
50803c8efc1SHerbert Xu EXPORT_SYMBOL_GPL(af_alg_complete);
50903c8efc1SHerbert Xu 
51003c8efc1SHerbert Xu static int __init af_alg_init(void)
51103c8efc1SHerbert Xu {
51203c8efc1SHerbert Xu 	int err = proto_register(&alg_proto, 0);
51303c8efc1SHerbert Xu 
51403c8efc1SHerbert Xu 	if (err)
51503c8efc1SHerbert Xu 		goto out;
51603c8efc1SHerbert Xu 
51703c8efc1SHerbert Xu 	err = sock_register(&alg_family);
51803c8efc1SHerbert Xu 	if (err != 0)
51903c8efc1SHerbert Xu 		goto out_unregister_proto;
52003c8efc1SHerbert Xu 
52103c8efc1SHerbert Xu out:
52203c8efc1SHerbert Xu 	return err;
52303c8efc1SHerbert Xu 
52403c8efc1SHerbert Xu out_unregister_proto:
52503c8efc1SHerbert Xu 	proto_unregister(&alg_proto);
52603c8efc1SHerbert Xu 	goto out;
52703c8efc1SHerbert Xu }
52803c8efc1SHerbert Xu 
52903c8efc1SHerbert Xu static void __exit af_alg_exit(void)
53003c8efc1SHerbert Xu {
53103c8efc1SHerbert Xu 	sock_unregister(PF_ALG);
53203c8efc1SHerbert Xu 	proto_unregister(&alg_proto);
53303c8efc1SHerbert Xu }
53403c8efc1SHerbert Xu 
53503c8efc1SHerbert Xu module_init(af_alg_init);
53603c8efc1SHerbert Xu module_exit(af_alg_exit);
53703c8efc1SHerbert Xu MODULE_LICENSE("GPL");
53803c8efc1SHerbert Xu MODULE_ALIAS_NETPROTO(AF_ALG);
539