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

Cache object: 37c9ea386a658ad4d98f6efee340c31f


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