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/netipsec/ipsec_pcb.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  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  *
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include "opt_inet.h"
   31 #include "opt_inet6.h"
   32 #include "opt_ipsec.h"
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/kernel.h>
   37 #include <sys/lock.h>
   38 #include <sys/malloc.h>
   39 #include <sys/mbuf.h>
   40 #include <sys/priv.h>
   41 #include <sys/socket.h>
   42 #include <sys/sockopt.h>
   43 #include <sys/syslog.h>
   44 #include <sys/proc.h>
   45 
   46 #include <netinet/in.h>
   47 #include <netinet/in_pcb.h>
   48 
   49 #include <netipsec/ipsec.h>
   50 #include <netipsec/ipsec6.h>
   51 #include <netipsec/ipsec_support.h>
   52 #include <netipsec/key.h>
   53 #include <netipsec/key_debug.h>
   54 
   55 MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy");
   56 
   57 static void
   58 ipsec_setsockaddrs_inpcb(struct inpcb *inp, union sockaddr_union *src,
   59     union sockaddr_union *dst, u_int dir)
   60 {
   61 
   62 #ifdef INET6
   63         if (inp->inp_vflag & INP_IPV6) {
   64                 struct sockaddr_in6 *sin6;
   65 
   66                 bzero(&src->sin6, sizeof(src->sin6));
   67                 bzero(&dst->sin6, sizeof(dst->sin6));
   68                 src->sin6.sin6_family = AF_INET6;
   69                 src->sin6.sin6_len = sizeof(struct sockaddr_in6);
   70                 dst->sin6.sin6_family = AF_INET6;
   71                 dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
   72 
   73                 if (dir == IPSEC_DIR_OUTBOUND)
   74                         sin6 = &src->sin6;
   75                 else
   76                         sin6 = &dst->sin6;
   77                 sin6->sin6_addr = inp->in6p_laddr;
   78                 sin6->sin6_port = inp->inp_lport;
   79                 if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_laddr)) {
   80                         /* XXXAE: use in6p_zoneid */
   81                         sin6->sin6_addr.s6_addr16[1] = 0;
   82                         sin6->sin6_scope_id = ntohs(
   83                             inp->in6p_laddr.s6_addr16[1]);
   84                 }
   85 
   86                 if (dir == IPSEC_DIR_OUTBOUND)
   87                         sin6 = &dst->sin6;
   88                 else
   89                         sin6 = &src->sin6;
   90                 sin6->sin6_addr = inp->in6p_faddr;
   91                 sin6->sin6_port = inp->inp_fport;
   92                 if (IN6_IS_SCOPE_LINKLOCAL(&inp->in6p_faddr)) {
   93                         /* XXXAE: use in6p_zoneid */
   94                         sin6->sin6_addr.s6_addr16[1] = 0;
   95                         sin6->sin6_scope_id = ntohs(
   96                             inp->in6p_faddr.s6_addr16[1]);
   97                 }
   98         }
   99 #endif
  100 #ifdef INET
  101         if (inp->inp_vflag & INP_IPV4) {
  102                 struct sockaddr_in *sin;
  103 
  104                 bzero(&src->sin, sizeof(src->sin));
  105                 bzero(&dst->sin, sizeof(dst->sin));
  106                 src->sin.sin_family = AF_INET;
  107                 src->sin.sin_len = sizeof(struct sockaddr_in);
  108                 dst->sin.sin_family = AF_INET;
  109                 dst->sin.sin_len = sizeof(struct sockaddr_in);
  110 
  111                 if (dir == IPSEC_DIR_OUTBOUND)
  112                         sin = &src->sin;
  113                 else
  114                         sin = &dst->sin;
  115                 sin->sin_addr = inp->inp_laddr;
  116                 sin->sin_port = inp->inp_lport;
  117 
  118                 if (dir == IPSEC_DIR_OUTBOUND)
  119                         sin = &dst->sin;
  120                 else
  121                         sin = &src->sin;
  122                 sin->sin_addr = inp->inp_faddr;
  123                 sin->sin_port = inp->inp_fport;
  124         }
  125 #endif
  126 }
  127 
  128 void
  129 ipsec_setspidx_inpcb(struct inpcb *inp, struct secpolicyindex *spidx,
  130     u_int dir)
  131 {
  132 
  133         ipsec_setsockaddrs_inpcb(inp, &spidx->src, &spidx->dst, dir);
  134 #ifdef INET6
  135         if (inp->inp_vflag & INP_IPV6) {
  136                 spidx->prefs = sizeof(struct in6_addr) << 3;
  137                 spidx->prefd = sizeof(struct in6_addr) << 3;
  138         }
  139 #endif
  140 #ifdef INET
  141         if (inp->inp_vflag & INP_IPV4) {
  142                 spidx->prefs = sizeof(struct in_addr) << 3;
  143                 spidx->prefd = sizeof(struct in_addr) << 3;
  144         }
  145 #endif
  146         spidx->ul_proto = IPPROTO_TCP; /* XXX: currently only TCP uses this */
  147         spidx->dir = dir;
  148         KEYDBG(IPSEC_DUMP,
  149             printf("%s: ", __func__); kdebug_secpolicyindex(spidx, NULL));
  150 }
  151 
  152 /* Initialize PCB policy. */
  153 int
  154 ipsec_init_pcbpolicy(struct inpcb *inp)
  155 {
  156 
  157         IPSEC_ASSERT(inp != NULL, ("null inp"));
  158         IPSEC_ASSERT(inp->inp_sp == NULL, ("inp_sp already initialized"));
  159 
  160         inp->inp_sp = malloc(sizeof(struct inpcbpolicy), M_IPSEC_INPCB,
  161             M_NOWAIT | M_ZERO);
  162         if (inp->inp_sp == NULL)
  163                 return (ENOBUFS);
  164         return (0);
  165 }
  166 
  167 /* Delete PCB policy. */
  168 int
  169 ipsec_delete_pcbpolicy(struct inpcb *inp)
  170 {
  171 
  172         if (inp->inp_sp == NULL)
  173                 return (0);
  174 
  175         if (inp->inp_sp->sp_in != NULL)
  176                 key_freesp(&inp->inp_sp->sp_in);
  177 
  178         if (inp->inp_sp->sp_out != NULL)
  179                 key_freesp(&inp->inp_sp->sp_out);
  180 
  181         free(inp->inp_sp, M_IPSEC_INPCB);
  182         inp->inp_sp = NULL;
  183         return (0);
  184 }
  185 
  186 /* Deep-copy a policy in PCB. */
  187 static struct secpolicy *
  188 ipsec_deepcopy_pcbpolicy(struct secpolicy *src)
  189 {
  190         struct secpolicy *dst;
  191         int i;
  192 
  193         if (src == NULL)
  194                 return (NULL);
  195 
  196         IPSEC_ASSERT(src->state == IPSEC_SPSTATE_PCB, ("SP isn't PCB"));
  197 
  198         dst = key_newsp();
  199         if (dst == NULL)
  200                 return (NULL);
  201 
  202         /* spidx is not copied here */
  203         dst->policy = src->policy;
  204         dst->state = src->state;
  205         dst->priority = src->priority;
  206         /* Do not touch the refcnt field. */
  207 
  208         /* Copy IPsec request chain. */
  209         for (i = 0; i < src->tcount; i++) {
  210                 dst->req[i] = ipsec_newisr();
  211                 if (dst->req[i] == NULL) {
  212                         key_freesp(&dst);
  213                         return (NULL);
  214                 }
  215                 bcopy(src->req[i], dst->req[i], sizeof(struct ipsecrequest));
  216                 dst->tcount++;
  217         }
  218         KEYDBG(IPSEC_DUMP,
  219             printf("%s: copied SP(%p) -> SP(%p)\n", __func__, src, dst);
  220             kdebug_secpolicy(dst));
  221         return (dst);
  222 }
  223 
  224 /*
  225  * Copy IPsec policy from old INPCB into new.
  226  * It is expected that new INPCB has not configured policies.
  227  */
  228 int
  229 ipsec_copy_pcbpolicy(struct inpcb *old, struct inpcb *new)
  230 {
  231         struct secpolicy *sp;
  232 
  233         /*
  234          * old->inp_sp can be NULL if PCB was created when an IPsec
  235          * support was unavailable. This is not an error, we don't have
  236          * policies in this PCB, so nothing to copy.
  237          */
  238         if (old->inp_sp == NULL)
  239                 return (0);
  240 
  241         IPSEC_ASSERT(new->inp_sp != NULL, ("new inp_sp is NULL"));
  242         IPSEC_ASSERT((new->inp_sp->flags & (
  243             INP_INBOUND_POLICY | INP_OUTBOUND_POLICY)) == 0,
  244             ("new PCB already has configured policies"));
  245         INP_WLOCK_ASSERT(new);
  246         INP_LOCK_ASSERT(old);
  247 
  248         if (old->inp_sp->flags & INP_INBOUND_POLICY) {
  249                 sp = ipsec_deepcopy_pcbpolicy(old->inp_sp->sp_in);
  250                 if (sp == NULL)
  251                         return (ENOBUFS);
  252                 ipsec_setspidx_inpcb(new, &sp->spidx, IPSEC_DIR_INBOUND);
  253                 if (new->inp_sp->sp_in != NULL)
  254                         key_freesp(&new->inp_sp->sp_in);
  255                 new->inp_sp->sp_in = sp;
  256                 new->inp_sp->flags |= INP_INBOUND_POLICY;
  257         }
  258         if (old->inp_sp->flags & INP_OUTBOUND_POLICY) {
  259                 sp = ipsec_deepcopy_pcbpolicy(old->inp_sp->sp_out);
  260                 if (sp == NULL)
  261                         return (ENOBUFS);
  262                 ipsec_setspidx_inpcb(new, &sp->spidx, IPSEC_DIR_OUTBOUND);
  263                 if (new->inp_sp->sp_out != NULL)
  264                         key_freesp(&new->inp_sp->sp_out);
  265                 new->inp_sp->sp_out = sp;
  266                 new->inp_sp->flags |= INP_OUTBOUND_POLICY;
  267         }
  268         return (0);
  269 }
  270 
  271 static int
  272 ipsec_set_pcbpolicy(struct inpcb *inp, struct ucred *cred,
  273     void *request, size_t len)
  274 {
  275         struct sadb_x_policy *xpl;
  276         struct secpolicy **spp, *newsp;
  277         int error, flags;
  278 
  279         xpl = (struct sadb_x_policy *)request;
  280         /* Select direction. */
  281         switch (xpl->sadb_x_policy_dir) {
  282         case IPSEC_DIR_INBOUND:
  283         case IPSEC_DIR_OUTBOUND:
  284                 break;
  285         default:
  286                 ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
  287                         xpl->sadb_x_policy_dir));
  288                 return (EINVAL);
  289         }
  290         /*
  291          * Privileged sockets are allowed to set own security policy
  292          * and configure IPsec bypass. Unprivileged sockets only can
  293          * have ENTRUST policy.
  294          */
  295         switch (xpl->sadb_x_policy_type) {
  296         case IPSEC_POLICY_IPSEC:
  297         case IPSEC_POLICY_BYPASS:
  298                 if (cred != NULL &&
  299                     priv_check_cred(cred, PRIV_NETINET_IPSEC, 0) != 0)
  300                         return (EACCES);
  301                 /* Allocate new SP entry. */
  302                 newsp = key_msg2sp(xpl, len, &error);
  303                 if (newsp == NULL)
  304                         return (error);
  305                 newsp->state = IPSEC_SPSTATE_PCB;
  306                 newsp->spidx.ul_proto = IPSEC_ULPROTO_ANY;
  307 #ifdef INET
  308                 if (inp->inp_vflag & INP_IPV4) {
  309                         newsp->spidx.src.sin.sin_family =
  310                             newsp->spidx.dst.sin.sin_family = AF_INET;
  311                         newsp->spidx.src.sin.sin_len =
  312                             newsp->spidx.dst.sin.sin_len =
  313                             sizeof(struct sockaddr_in);
  314                 }
  315 #endif
  316 #ifdef INET6
  317                 if (inp->inp_vflag & INP_IPV6) {
  318                         newsp->spidx.src.sin6.sin6_family =
  319                             newsp->spidx.dst.sin6.sin6_family = AF_INET6;
  320                         newsp->spidx.src.sin6.sin6_len =
  321                             newsp->spidx.dst.sin6.sin6_len =
  322                             sizeof(struct sockaddr_in6);
  323                 }
  324 #endif
  325                 break;
  326         case IPSEC_POLICY_ENTRUST:
  327                 /* We just use NULL pointer for ENTRUST policy */
  328                 newsp = NULL;
  329                 break;
  330         default:
  331                 /* Other security policy types aren't allowed for PCB */
  332                 return (EINVAL);
  333         }
  334 
  335         INP_WLOCK(inp);
  336         if (xpl->sadb_x_policy_dir == IPSEC_DIR_INBOUND) {
  337                 spp = &inp->inp_sp->sp_in;
  338                 flags = INP_INBOUND_POLICY;
  339         } else {
  340                 spp = &inp->inp_sp->sp_out;
  341                 flags = INP_OUTBOUND_POLICY;
  342         }
  343         /* Clear old SP and set new SP. */
  344         if (*spp != NULL)
  345                 key_freesp(spp);
  346         *spp = newsp;
  347         KEYDBG(IPSEC_DUMP,
  348             printf("%s: new SP(%p)\n", __func__, newsp));
  349         if (newsp == NULL)
  350                 inp->inp_sp->flags &= ~flags;
  351         else {
  352                 inp->inp_sp->flags |= flags;
  353                 KEYDBG(IPSEC_DUMP, kdebug_secpolicy(newsp));
  354         }
  355         INP_WUNLOCK(inp);
  356         return (0);
  357 }
  358 
  359 static int
  360 ipsec_get_pcbpolicy(struct inpcb *inp, void *request, size_t *len)
  361 {
  362         struct sadb_x_policy *xpl;
  363         struct secpolicy *sp;
  364         int error, flags;
  365 
  366         xpl = (struct sadb_x_policy *)request;
  367 
  368         INP_RLOCK(inp);
  369         flags = inp->inp_sp->flags;
  370         /* Select direction. */
  371         switch (xpl->sadb_x_policy_dir) {
  372         case IPSEC_DIR_INBOUND:
  373                 sp = inp->inp_sp->sp_in;
  374                 flags &= INP_INBOUND_POLICY;
  375                 break;
  376         case IPSEC_DIR_OUTBOUND:
  377                 sp = inp->inp_sp->sp_out;
  378                 flags &= INP_OUTBOUND_POLICY;
  379                 break;
  380         default:
  381                 INP_RUNLOCK(inp);
  382                 ipseclog((LOG_ERR, "%s: invalid direction=%u\n", __func__,
  383                         xpl->sadb_x_policy_dir));
  384                 return (EINVAL);
  385         }
  386 
  387         if (flags == 0) {
  388                 /* Return ENTRUST policy */
  389                 INP_RUNLOCK(inp);
  390                 xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
  391                 xpl->sadb_x_policy_type = IPSEC_POLICY_ENTRUST;
  392                 xpl->sadb_x_policy_id = 0;
  393                 xpl->sadb_x_policy_priority = 0;
  394                 xpl->sadb_x_policy_len = PFKEY_UNIT64(sizeof(*xpl));
  395                 *len = sizeof(*xpl);
  396                 return (0);
  397         }
  398 
  399         IPSEC_ASSERT(sp != NULL,
  400             ("sp is NULL, but flags is 0x%04x", inp->inp_sp->flags));
  401 
  402         key_addref(sp);
  403         INP_RUNLOCK(inp);
  404         error = key_sp2msg(sp, request, len);
  405         key_freesp(&sp);
  406         if (error == EINVAL)
  407                 return (error);
  408         /*
  409          * We return "success", but user should check *len.
  410          * *len will be set to size of valid data and
  411          * sadb_x_policy_len will contain needed size.
  412          */
  413         return (0);
  414 }
  415 
  416 /* Handle socket option control request for PCB */
  417 static int
  418 ipsec_control_pcbpolicy(struct inpcb *inp, struct sockopt *sopt)
  419 {
  420         void *optdata;
  421         size_t optlen;
  422         int error;
  423 
  424         if (inp->inp_sp == NULL)
  425                 return (ENOPROTOOPT);
  426 
  427         /* Limit maximum request size to PAGE_SIZE */
  428         optlen = sopt->sopt_valsize;
  429         if (optlen < sizeof(struct sadb_x_policy) || optlen > PAGE_SIZE)
  430                 return (EINVAL);
  431 
  432         optdata = malloc(optlen, M_TEMP, sopt->sopt_td ? M_WAITOK: M_NOWAIT);
  433         if (optdata == NULL)
  434                 return (ENOBUFS);
  435         /*
  436          * We need a hint from the user, what policy is requested - input
  437          * or output? User should specify it in the buffer, even for
  438          * setsockopt().
  439          */
  440         error = sooptcopyin(sopt, optdata, optlen, optlen);
  441         if (error == 0) {
  442                 if (sopt->sopt_dir == SOPT_SET)
  443                         error = ipsec_set_pcbpolicy(inp,
  444                             sopt->sopt_td ? sopt->sopt_td->td_ucred: NULL,
  445                             optdata, optlen);
  446                 else {
  447                         error = ipsec_get_pcbpolicy(inp, optdata, &optlen);
  448                         if (error == 0)
  449                                 error = sooptcopyout(sopt, optdata, optlen);
  450                 }
  451         }
  452         free(optdata, M_TEMP);
  453         return (error);
  454 }
  455 
  456 #ifdef INET
  457 /*
  458  * IPSEC_PCBCTL() method implementation for IPv4.
  459  */
  460 int
  461 ipsec4_pcbctl(struct inpcb *inp, struct sockopt *sopt)
  462 {
  463 
  464         if (sopt->sopt_name != IP_IPSEC_POLICY)
  465                 return (ENOPROTOOPT);
  466         return (ipsec_control_pcbpolicy(inp, sopt));
  467 }
  468 #endif
  469 
  470 #ifdef INET6
  471 /*
  472  * IPSEC_PCBCTL() method implementation for IPv6.
  473  */
  474 int
  475 ipsec6_pcbctl(struct inpcb *inp, struct sockopt *sopt)
  476 {
  477 
  478         if (sopt->sopt_name != IPV6_IPSEC_POLICY)
  479                 return (ENOPROTOOPT);
  480         return (ipsec_control_pcbpolicy(inp, sopt));
  481 }
  482 #endif
  483 

Cache object: 63c3ba61455290361ab0bb12f4fc37e5


[ 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.