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