The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/crypto/af_alg.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 <linux/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 
   25 struct alg_type_list {
   26         const struct af_alg_type *type;
   27         struct list_head list;
   28 };
   29 
   30 static atomic_long_t alg_memory_allocated;
   31 
   32 static 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 
   39 static LIST_HEAD(alg_types);
   40 static DECLARE_RWSEM(alg_types_sem);
   41 
   42 static 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 
   61 int 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 
   82 unlock:
   83         up_write(&alg_types_sem);
   84 
   85         return err;
   86 }
   87 EXPORT_SYMBOL_GPL(af_alg_register_type);
   88 
   89 int 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 }
  108 EXPORT_SYMBOL_GPL(af_alg_unregister_type);
  109 
  110 static 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 
  119 int af_alg_release(struct socket *sock)
  120 {
  121         if (sock->sk)
  122                 sock_put(sock->sk);
  123         return 0;
  124 }
  125 EXPORT_SYMBOL_GPL(af_alg_release);
  126 
  127 static 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 
  171 static 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 
  189 out:
  190         sock_kfree_s(sk, key, keylen);
  191 
  192         return err;
  193 }
  194 
  195 static 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 
  219 unlock:
  220         release_sock(sk);
  221 
  222         return err;
  223 }
  224 
  225 int 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         sock_graft(sk2, newsock);
  246 
  247         err = type->accept(ask->private, sk2);
  248         if (err) {
  249                 sk_free(sk2);
  250                 goto unlock;
  251         }
  252 
  253         sk2->sk_family = PF_ALG;
  254 
  255         sock_hold(sk);
  256         alg_sk(sk2)->parent = sk;
  257         alg_sk(sk2)->type = type;
  258 
  259         newsock->ops = type->ops;
  260         newsock->state = SS_CONNECTED;
  261 
  262         err = 0;
  263 
  264 unlock:
  265         release_sock(sk);
  266 
  267         return err;
  268 }
  269 EXPORT_SYMBOL_GPL(af_alg_accept);
  270 
  271 static int alg_accept(struct socket *sock, struct socket *newsock, int flags)
  272 {
  273         return af_alg_accept(sock->sk, newsock);
  274 }
  275 
  276 static const struct proto_ops alg_proto_ops = {
  277         .family         =       PF_ALG,
  278         .owner          =       THIS_MODULE,
  279 
  280         .connect        =       sock_no_connect,
  281         .socketpair     =       sock_no_socketpair,
  282         .getname        =       sock_no_getname,
  283         .ioctl          =       sock_no_ioctl,
  284         .listen         =       sock_no_listen,
  285         .shutdown       =       sock_no_shutdown,
  286         .getsockopt     =       sock_no_getsockopt,
  287         .mmap           =       sock_no_mmap,
  288         .sendpage       =       sock_no_sendpage,
  289         .sendmsg        =       sock_no_sendmsg,
  290         .recvmsg        =       sock_no_recvmsg,
  291         .poll           =       sock_no_poll,
  292 
  293         .bind           =       alg_bind,
  294         .release        =       af_alg_release,
  295         .setsockopt     =       alg_setsockopt,
  296         .accept         =       alg_accept,
  297 };
  298 
  299 static void alg_sock_destruct(struct sock *sk)
  300 {
  301         struct alg_sock *ask = alg_sk(sk);
  302 
  303         alg_do_release(ask->type, ask->private);
  304 }
  305 
  306 static int alg_create(struct net *net, struct socket *sock, int protocol,
  307                       int kern)
  308 {
  309         struct sock *sk;
  310         int err;
  311 
  312         if (sock->type != SOCK_SEQPACKET)
  313                 return -ESOCKTNOSUPPORT;
  314         if (protocol != 0)
  315                 return -EPROTONOSUPPORT;
  316 
  317         err = -ENOMEM;
  318         sk = sk_alloc(net, PF_ALG, GFP_KERNEL, &alg_proto);
  319         if (!sk)
  320                 goto out;
  321 
  322         sock->ops = &alg_proto_ops;
  323         sock_init_data(sock, sk);
  324 
  325         sk->sk_family = PF_ALG;
  326         sk->sk_destruct = alg_sock_destruct;
  327 
  328         return 0;
  329 out:
  330         return err;
  331 }
  332 
  333 static const struct net_proto_family alg_family = {
  334         .family =       PF_ALG,
  335         .create =       alg_create,
  336         .owner  =       THIS_MODULE,
  337 };
  338 
  339 int af_alg_make_sg(struct af_alg_sgl *sgl, void __user *addr, int len,
  340                    int write)
  341 {
  342         unsigned long from = (unsigned long)addr;
  343         unsigned long npages;
  344         unsigned off;
  345         int err;
  346         int i;
  347 
  348         err = -EFAULT;
  349         if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, addr, len))
  350                 goto out;
  351 
  352         off = from & ~PAGE_MASK;
  353         npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
  354         if (npages > ALG_MAX_PAGES)
  355                 npages = ALG_MAX_PAGES;
  356 
  357         err = get_user_pages_fast(from, npages, write, sgl->pages);
  358         if (err < 0)
  359                 goto out;
  360 
  361         npages = err;
  362         err = -EINVAL;
  363         if (WARN_ON(npages == 0))
  364                 goto out;
  365 
  366         err = 0;
  367 
  368         sg_init_table(sgl->sg, npages);
  369 
  370         for (i = 0; i < npages; i++) {
  371                 int plen = min_t(int, len, PAGE_SIZE - off);
  372 
  373                 sg_set_page(sgl->sg + i, sgl->pages[i], plen, off);
  374 
  375                 off = 0;
  376                 len -= plen;
  377                 err += plen;
  378         }
  379 
  380 out:
  381         return err;
  382 }
  383 EXPORT_SYMBOL_GPL(af_alg_make_sg);
  384 
  385 void af_alg_free_sg(struct af_alg_sgl *sgl)
  386 {
  387         int i;
  388 
  389         i = 0;
  390         do {
  391                 put_page(sgl->pages[i]);
  392         } while (!sg_is_last(sgl->sg + (i++)));
  393 }
  394 EXPORT_SYMBOL_GPL(af_alg_free_sg);
  395 
  396 int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con)
  397 {
  398         struct cmsghdr *cmsg;
  399 
  400         for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
  401                 if (!CMSG_OK(msg, cmsg))
  402                         return -EINVAL;
  403                 if (cmsg->cmsg_level != SOL_ALG)
  404                         continue;
  405 
  406                 switch(cmsg->cmsg_type) {
  407                 case ALG_SET_IV:
  408                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(*con->iv)))
  409                                 return -EINVAL;
  410                         con->iv = (void *)CMSG_DATA(cmsg);
  411                         if (cmsg->cmsg_len < CMSG_LEN(con->iv->ivlen +
  412                                                       sizeof(*con->iv)))
  413                                 return -EINVAL;
  414                         break;
  415 
  416                 case ALG_SET_OP:
  417                         if (cmsg->cmsg_len < CMSG_LEN(sizeof(u32)))
  418                                 return -EINVAL;
  419                         con->op = *(u32 *)CMSG_DATA(cmsg);
  420                         break;
  421 
  422                 default:
  423                         return -EINVAL;
  424                 }
  425         }
  426 
  427         return 0;
  428 }
  429 EXPORT_SYMBOL_GPL(af_alg_cmsg_send);
  430 
  431 int af_alg_wait_for_completion(int err, struct af_alg_completion *completion)
  432 {
  433         switch (err) {
  434         case -EINPROGRESS:
  435         case -EBUSY:
  436                 wait_for_completion(&completion->completion);
  437                 INIT_COMPLETION(completion->completion);
  438                 err = completion->err;
  439                 break;
  440         };
  441 
  442         return err;
  443 }
  444 EXPORT_SYMBOL_GPL(af_alg_wait_for_completion);
  445 
  446 void af_alg_complete(struct crypto_async_request *req, int err)
  447 {
  448         struct af_alg_completion *completion = req->data;
  449 
  450         completion->err = err;
  451         complete(&completion->completion);
  452 }
  453 EXPORT_SYMBOL_GPL(af_alg_complete);
  454 
  455 static int __init af_alg_init(void)
  456 {
  457         int err = proto_register(&alg_proto, 0);
  458 
  459         if (err)
  460                 goto out;
  461 
  462         err = sock_register(&alg_family);
  463         if (err != 0)
  464                 goto out_unregister_proto;
  465 
  466 out:
  467         return err;
  468 
  469 out_unregister_proto:
  470         proto_unregister(&alg_proto);
  471         goto out;
  472 }
  473 
  474 static void __exit af_alg_exit(void)
  475 {
  476         sock_unregister(PF_ALG);
  477         proto_unregister(&alg_proto);
  478 }
  479 
  480 module_init(af_alg_init);
  481 module_exit(af_alg_exit);
  482 MODULE_LICENSE("GPL");
  483 MODULE_ALIAS_NETPROTO(AF_ALG);

Cache object: 584180d8c3752afc74cd7d4b1dac81a9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.