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/xform_tcp.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  * SPDX-License-Identifier: BSD-3-Clause
    3  *
    4  * Copyright (c) 2003 Bruce M. Simpson <bms@spc.org>
    5  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  *
   11  * 1. Redistributions of source code must retain the above copyright
   12  *   notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *   notice, this list of conditions and the following disclaimer in the
   15  *   documentation and/or other materials provided with the distribution.
   16  * 3. The name of the author may not be used to endorse or promote products
   17  *   derived from this software without specific prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   29  */
   30 
   31 /* TCP MD5 Signature Option (RFC2385) */
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include "opt_inet.h"
   36 #include "opt_inet6.h"
   37 #include "opt_ipsec.h"
   38 
   39 #include <sys/param.h>
   40 #include <sys/systm.h>
   41 #include <sys/mbuf.h>
   42 #include <sys/lock.h>
   43 #include <sys/md5.h>
   44 #include <sys/rmlock.h>
   45 #include <sys/socket.h>
   46 #include <sys/sockopt.h>
   47 #include <sys/kernel.h>
   48 #include <sys/module.h>
   49 #include <sys/protosw.h>
   50 
   51 #include <netinet/in.h>
   52 #include <netinet/in_pcb.h>
   53 #include <netinet/in_systm.h>
   54 #include <netinet/ip.h>
   55 #include <netinet/ip_var.h>
   56 #include <netinet/tcp.h>
   57 #include <netinet/tcp_var.h>
   58 
   59 #include <net/vnet.h>
   60 
   61 #include <netipsec/ipsec.h>
   62 #include <netipsec/ipsec_support.h>
   63 #include <netipsec/xform.h>
   64 
   65 #ifdef INET6
   66 #include <netinet/ip6.h>
   67 #include <netipsec/ipsec6.h>
   68 #endif
   69 
   70 #include <netipsec/key.h>
   71 #include <netipsec/key_debug.h>
   72 
   73 #define TCP_SIGLEN      16      /* length of computed digest in bytes */
   74 #define TCP_KEYLEN_MIN  1       /* minimum length of TCP-MD5 key */
   75 #define TCP_KEYLEN_MAX  80      /* maximum length of TCP-MD5 key */
   76 
   77 static int
   78 tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
   79 {
   80         struct tcpcb *tp;
   81         int error, optval;
   82 
   83         if (sopt->sopt_name != TCP_MD5SIG) {
   84                 return (ENOPROTOOPT);
   85         }
   86 
   87         if (sopt->sopt_dir == SOPT_GET) {
   88                 INP_RLOCK(inp);
   89                 if (inp->inp_flags & INP_DROPPED) {
   90                         INP_RUNLOCK(inp);
   91                         return (ECONNRESET);
   92                 }
   93                 tp = intotcpcb(inp);
   94                 optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
   95                 INP_RUNLOCK(inp);
   96 
   97                 /* On success return with released INP_WLOCK */
   98                 return (sooptcopyout(sopt, &optval, sizeof(optval)));
   99         }
  100 
  101         error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
  102         if (error != 0)
  103                 return (error);
  104 
  105         /* INP_WLOCK_RECHECK */
  106         INP_WLOCK(inp);
  107         if (inp->inp_flags & INP_DROPPED) {
  108                 INP_WUNLOCK(inp);
  109                 return (ECONNRESET);
  110         }
  111         tp = intotcpcb(inp);
  112         if (optval > 0)
  113                 tp->t_flags |= TF_SIGNATURE;
  114         else
  115                 tp->t_flags &= ~TF_SIGNATURE;
  116 
  117         INP_WUNLOCK(inp);
  118         return (error);
  119 }
  120 
  121 /*
  122  * Callback function invoked by m_apply() to digest TCP segment data
  123  * contained within an mbuf chain.
  124  */
  125 static int
  126 tcp_signature_apply(void *fstate, void *data, u_int len)
  127 {
  128 
  129         MD5Update(fstate, (u_char *)data, len);
  130         return (0);
  131 }
  132 
  133 #ifdef INET
  134 static int
  135 ip_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
  136 {
  137         struct ippseudo ipp;
  138         struct ip *ip;
  139 
  140         ip = mtod(m, struct ip *);
  141         ipp.ippseudo_src.s_addr = ip->ip_src.s_addr;
  142         ipp.ippseudo_dst.s_addr = ip->ip_dst.s_addr;
  143         ipp.ippseudo_p = IPPROTO_TCP;
  144         ipp.ippseudo_pad = 0;
  145         ipp.ippseudo_len = htons(m->m_pkthdr.len - (ip->ip_hl << 2));
  146         MD5Update(ctx, (char *)&ipp, sizeof(ipp));
  147         return (ip->ip_hl << 2);
  148 }
  149 #endif
  150 
  151 #ifdef INET6
  152 static int
  153 ip6_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
  154 {
  155         struct ip6_pseudo {
  156                 struct in6_addr src, dst;
  157                 uint32_t len;
  158                 uint32_t nxt;
  159         } ip6p __aligned(4);
  160         struct ip6_hdr *ip6;
  161 
  162         ip6 = mtod(m, struct ip6_hdr *);
  163         ip6p.src = ip6->ip6_src;
  164         ip6p.dst = ip6->ip6_dst;
  165         ip6p.len = htonl(m->m_pkthdr.len - sizeof(*ip6)); /* XXX: ext headers */
  166         ip6p.nxt = htonl(IPPROTO_TCP);
  167         MD5Update(ctx, (char *)&ip6p, sizeof(ip6p));
  168         return (sizeof(*ip6));
  169 }
  170 #endif
  171 
  172 static int
  173 tcp_signature_compute(struct mbuf *m, struct tcphdr *th,
  174     struct secasvar *sav, u_char *buf)
  175 {
  176         MD5_CTX ctx;
  177         int len;
  178         u_short csum;
  179 
  180         MD5Init(&ctx);
  181          /* Step 1: Update MD5 hash with IP(v6) pseudo-header. */
  182         switch (sav->sah->saidx.dst.sa.sa_family) {
  183 #ifdef INET
  184         case AF_INET:
  185                 len = ip_pseudo_compute(m, &ctx);
  186                 break;
  187 #endif
  188 #ifdef INET6
  189         case AF_INET6:
  190                 len = ip6_pseudo_compute(m, &ctx);
  191                 break;
  192 #endif
  193         default:
  194                 return (EAFNOSUPPORT);
  195         }
  196         /*
  197          * Step 2: Update MD5 hash with TCP header, excluding options.
  198          * The TCP checksum must be set to zero.
  199          */
  200         csum = th->th_sum;
  201         th->th_sum = 0;
  202         MD5Update(&ctx, (char *)th, sizeof(struct tcphdr));
  203         th->th_sum = csum;
  204         /*
  205          * Step 3: Update MD5 hash with TCP segment data.
  206          * Use m_apply() to avoid an early m_pullup().
  207          */
  208         len += (th->th_off << 2);
  209         if (m->m_pkthdr.len - len > 0)
  210                 m_apply(m, len, m->m_pkthdr.len - len,
  211                     tcp_signature_apply, &ctx);
  212         /*
  213          * Step 4: Update MD5 hash with shared secret.
  214          */
  215         MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth));
  216         MD5Final(buf, &ctx);
  217         key_sa_recordxfer(sav, m);
  218         return (0);
  219 }
  220 
  221 static void
  222 setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
  223     union sockaddr_union *dst)
  224 {
  225         struct ip *ip;
  226 
  227         IPSEC_ASSERT(m->m_len >= sizeof(*ip), ("unexpected mbuf len"));
  228 
  229         ip = mtod(m, struct ip *);
  230         switch (ip->ip_v) {
  231 #ifdef INET
  232         case IPVERSION:
  233                 ipsec4_setsockaddrs(m, src, dst);
  234                 break;
  235 #endif
  236 #ifdef INET6
  237         case (IPV6_VERSION >> 4):
  238                 ipsec6_setsockaddrs(m, src, dst);
  239                 break;
  240 #endif
  241         default:
  242                 bzero(src, sizeof(*src));
  243                 bzero(dst, sizeof(*dst));
  244         }
  245 }
  246 
  247 /*
  248  * Compute TCP-MD5 hash of an *INBOUND* TCP segment.
  249  * Parameters:
  250  * m            pointer to head of mbuf chain
  251  * th           pointer to TCP header
  252  * buf          pointer to storage for computed MD5 digest
  253  *
  254  * Return 0 if successful, otherwise return error code.
  255  */
  256 static int
  257 tcp_ipsec_input(struct mbuf *m, struct tcphdr *th, u_char *buf)
  258 {
  259         char tmpdigest[TCP_SIGLEN];
  260         struct secasindex saidx;
  261         struct secasvar *sav;
  262 
  263         setsockaddrs(m, &saidx.src, &saidx.dst);
  264         saidx.proto = IPPROTO_TCP;
  265         saidx.mode = IPSEC_MODE_TCPMD5;
  266         saidx.reqid = 0;
  267         sav = key_allocsa_tcpmd5(&saidx);
  268         if (sav == NULL) {
  269                 KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
  270                 return (ENOENT);
  271         }
  272         if (buf == NULL) {
  273                 key_freesav(&sav);
  274                 KMOD_TCPSTAT_INC(tcps_sig_err_nosigopt);
  275                 return (EACCES);
  276         }
  277         /*
  278          * tcp_input() operates with TCP header fields in host
  279          * byte order. We expect them in network byte order.
  280          */
  281         tcp_fields_to_net(th);
  282         tcp_signature_compute(m, th, sav, tmpdigest);
  283         tcp_fields_to_host(th);
  284         key_freesav(&sav);
  285         if (bcmp(buf, tmpdigest, TCP_SIGLEN) != 0) {
  286                 KMOD_TCPSTAT_INC(tcps_sig_rcvbadsig);
  287                 return (EACCES);
  288         }
  289         KMOD_TCPSTAT_INC(tcps_sig_rcvgoodsig);
  290         return (0);
  291 }
  292 
  293 /*
  294  * Compute TCP-MD5 hash of an *OUTBOUND* TCP segment.
  295  * Parameters:
  296  * m            pointer to head of mbuf chain
  297  * th           pointer to TCP header
  298  * buf          pointer to storage for computed MD5 digest
  299  *
  300  * Return 0 if successful, otherwise return error code.
  301  */
  302 static int
  303 tcp_ipsec_output(struct mbuf *m, struct tcphdr *th, u_char *buf)
  304 {
  305         struct secasindex saidx;
  306         struct secasvar *sav;
  307 
  308         setsockaddrs(m, &saidx.src, &saidx.dst);
  309         saidx.proto = IPPROTO_TCP;
  310         saidx.mode = IPSEC_MODE_TCPMD5;
  311         saidx.reqid = 0;
  312         sav = key_allocsa_tcpmd5(&saidx);
  313         if (sav == NULL) {
  314                 KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
  315                 return (ENOENT);
  316         }
  317         tcp_signature_compute(m, th, sav, buf);
  318         key_freesav(&sav);
  319         return (0);
  320 }
  321 
  322 /*
  323  * Initialize a TCP-MD5 SA. Called when the SA is being set up.
  324  *
  325  * We don't need to set up the tdb prefixed fields, as we don't use the
  326  * opencrypto code; we just perform a key length check.
  327  *
  328  * XXX: Currently we have used single 'magic' SPI and need to still
  329  * support this.
  330  *
  331  * This allows per-host granularity without affecting the userland
  332  * interface, which is a simple socket option toggle switch,
  333  * TCP_SIGNATURE_ENABLE.
  334  *
  335  * To allow per-service granularity requires that we have a means
  336  * of mapping port to SPI. The mandated way of doing this is to
  337  * use SPD entries to specify packet flows which get the TCP-MD5
  338  * treatment, however the code to do this is currently unstable
  339  * and unsuitable for production use.
  340  *
  341  * Therefore we use this compromise in the meantime.
  342  */
  343 static int
  344 tcpsignature_init(struct secasvar *sav, struct xformsw *xsp)
  345 {
  346         int keylen;
  347 
  348         if (sav->alg_auth != SADB_X_AALG_TCP_MD5) {
  349                 DPRINTF(("%s: unsupported authentication algorithm %u\n",
  350                     __func__, sav->alg_auth));
  351                 return (EINVAL);
  352         }
  353         if (sav->key_auth == NULL) {
  354                 DPRINTF(("%s: no authentication key present\n", __func__));
  355                 return (EINVAL);
  356         }
  357         keylen = _KEYLEN(sav->key_auth);
  358         if ((keylen < TCP_KEYLEN_MIN) || (keylen > TCP_KEYLEN_MAX)) {
  359                 DPRINTF(("%s: invalid key length %u\n", __func__, keylen));
  360                 return (EINVAL);
  361         }
  362         sav->tdb_xform = xsp;
  363         return (0);
  364 }
  365 
  366 /*
  367  * Called when the SA is deleted.
  368  */
  369 static void
  370 tcpsignature_cleanup(struct secasvar *sav)
  371 {
  372 }
  373 
  374 static struct xformsw tcpsignature_xformsw = {
  375         .xf_type =      XF_TCPSIGNATURE,
  376         .xf_name =      "TCP-MD5",
  377         .xf_init =      tcpsignature_init,
  378         .xf_cleanup =   tcpsignature_cleanup,
  379 };
  380 
  381 static const struct tcpmd5_methods tcpmd5_methods = {
  382         .input = tcp_ipsec_input,
  383         .output = tcp_ipsec_output,
  384         .pcbctl = tcp_ipsec_pcbctl,
  385 };
  386 
  387 #ifndef KLD_MODULE
  388 /* TCP-MD5 support is build in the kernel */
  389 static const struct tcpmd5_support tcpmd5_ipsec = {
  390         .enabled = IPSEC_MODULE_ENABLED,
  391         .methods = &tcpmd5_methods
  392 };
  393 const struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
  394 #endif /* !KLD_MODULE */
  395 
  396 static int
  397 tcpmd5_modevent(module_t mod, int type, void *data)
  398 {
  399 
  400         switch (type) {
  401         case MOD_LOAD:
  402                 xform_attach(&tcpsignature_xformsw);
  403 #ifdef KLD_MODULE
  404                 tcpmd5_support_enable(&tcpmd5_methods);
  405 #endif
  406                 break;
  407         case MOD_UNLOAD:
  408 #ifdef KLD_MODULE
  409                 tcpmd5_support_disable();
  410 #endif
  411                 xform_detach(&tcpsignature_xformsw);
  412                 break;
  413         default:
  414                 return (EOPNOTSUPP);
  415         }
  416         return (0);
  417 }
  418 
  419 static moduledata_t tcpmd5_mod = {
  420         "tcpmd5",
  421         tcpmd5_modevent,
  422         0
  423 };
  424 
  425 DECLARE_MODULE(tcpmd5, tcpmd5_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
  426 MODULE_VERSION(tcpmd5, 1);
  427 #ifdef KLD_MODULE
  428 MODULE_DEPEND(tcpmd5, ipsec_support, 1, 1, 1);
  429 #endif

Cache object: d85c9c86e0d57ca6d5594608edde5457


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