]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netinet6/ah_output.c
xnu-2782.30.5.tar.gz
[apple/xnu.git] / bsd / netinet6 / ah_output.c
index e8c81b01f50979176a644e403c7138a1ea597b81..18391b1873d1de6d9622c374f9f67eb9b0b953ad 100644 (file)
@@ -1,4 +1,33 @@
-/*     $KAME: ah_output.c,v 1.17 2000/03/09 08:54:48 itojun Exp $      */
+/*
+ * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ * 
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+/*     $FreeBSD: src/sys/netinet6/ah_output.c,v 1.1.2.3 2001/07/03 11:01:49 ume Exp $  */
+/*     $KAME: ah_output.c,v 1.30 2001/02/21 00:50:53 itojun Exp $      */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -34,9 +63,6 @@
  */
 
 #define _IP_VHL
-#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
-#include "opt_inet.h"
-#endif
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #endif
 
 #include <netinet6/ipsec.h>
+#if INET6
+#include <netinet6/ipsec6.h>
+#endif
 #include <netinet6/ah.h>
+#if INET6
+#include <netinet6/ah6.h>
+#endif
 #include <netkey/key.h>
 #include <netkey/keydb.h>
-#include <netkey/key_debug.h>
 
 #include <net/net_osdep.h>
 
-static struct in_addr *ah4_finaldst __P((struct mbuf *));
+#if INET
+static struct in_addr *ah4_finaldst(struct mbuf *);
+#endif
+
+extern lck_mtx_t *sadb_mutex;
 
 /*
  * compute AH header size.
@@ -85,8 +120,6 @@ size_t
 ah_hdrsiz(isr)
        struct ipsecrequest *isr;
 {
-       struct ah_algorithm *algo;
-       size_t hdrsiz;
 
        /* sanity check */
        if (isr == NULL)
@@ -95,33 +128,46 @@ ah_hdrsiz(isr)
        if (isr->saidx.proto != IPPROTO_AH)
                panic("unsupported mode passed to ah_hdrsiz");
 
-       if (isr->sav == NULL)
-               goto estimate;
-       if (isr->sav->state != SADB_SASTATE_MATURE
-        && isr->sav->state != SADB_SASTATE_DYING)
-               goto estimate;
+#if 0
+       {
 
-       /* we need transport mode AH. */
-       algo = &ah_algorithms[isr->sav->alg_auth];
-       if (!algo)
-               goto estimate;
+               lck_mtx_lock(sadb_mutex);
+               const struct ah_algorithm *algo;
+               size_t hdrsiz;
 
-       /*
-        * XXX
-        * right now we don't calcurate the padding size.  simply
-        * treat the padding size as constant, for simplicity.
-        *
-        * XXX variable size padding support
-        */
-       hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
-       if (isr->sav->flags & SADB_X_EXT_OLD)
-               hdrsiz += sizeof(struct ah);
-       else
-               hdrsiz += sizeof(struct newah);
+               /*%%%%% this needs to change - no sav in ipsecrequest any more */
+               if (isr->sav == NULL)
+                       goto estimate;
+               if (isr->sav->state != SADB_SASTATE_MATURE
+                && isr->sav->state != SADB_SASTATE_DYING)
+                       goto estimate;
+       
+               /* we need transport mode AH. */
+               algo = ah_algorithm_lookup(isr->sav->alg_auth);
+               if (!algo)
+                       goto estimate;
+       
+               /*
+                * XXX
+                * right now we don't calcurate the padding size.  simply
+                * treat the padding size as constant, for simplicity.
+                *
+                * XXX variable size padding support
+                */
+               hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
+               if (isr->sav->flags & SADB_X_EXT_OLD)
+                       hdrsiz += sizeof(struct ah);
+               else
+                       hdrsiz += sizeof(struct newah);
+       
+               lck_mtx_unlock(sadb_mutex);
+               return hdrsiz;
+       }
 
-       return hdrsiz;
+estimate:
+#endif
 
-    estimate:
+    //lck_mtx_unlock(sadb_mutex);
        /* ASSUMING:
         *      sizeof(struct newah) > sizeof(struct ah).
         *      16 = (16 + 3) & ~(4 - 1).
@@ -129,6 +175,7 @@ ah_hdrsiz(isr)
        return sizeof(struct newah) + 16;
 }
 
+#if INET
 /*
  * Modify the packet so that it includes the authentication data.
  * The mbuf passed must start with IPv4 header.
@@ -137,12 +184,11 @@ ah_hdrsiz(isr)
  * the function does not modify m.
  */
 int
-ah4_output(m, isr)
+ah4_output(m, sav)
        struct mbuf *m;
-       struct ipsecrequest *isr;
+       struct secasvar *sav;
 {
-       struct secasvar *sav = isr->sav;
-       struct ah_algorithm *algo;
+       const struct ah_algorithm *algo;
        u_int32_t spi;
        u_char *ahdrpos;
        u_char *ahsumpos = NULL;
@@ -150,26 +196,31 @@ ah4_output(m, isr)
        size_t plen = 0;        /*AH payload size in bytes*/
        size_t ahlen = 0;       /*plen + sizeof(ah)*/
        struct ip *ip;
-       struct in_addr dst;
+       struct in_addr dst = { 0 };
        struct in_addr *finaldst;
        int error;
 
        /* sanity checks */
        if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
-               struct ip *ip;
-
                ip = mtod(m, struct ip *);
                ipseclog((LOG_DEBUG, "ah4_output: internal error: "
                        "sav->replay is null: %x->%x, SPI=%u\n",
                        (u_int32_t)ntohl(ip->ip_src.s_addr),
                        (u_int32_t)ntohl(ip->ip_dst.s_addr),
                        (u_int32_t)ntohl(sav->spi)));
-               ipsecstat.out_inval++;
+               IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
                m_freem(m);
                return EINVAL;
        }
 
-       algo = &ah_algorithms[sav->alg_auth];
+       algo = ah_algorithm_lookup(sav->alg_auth);
+       if (!algo) {
+               ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
+                   "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
+               IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
+               m_freem(m);
+               return EINVAL;
+       }
        spi = sav->spi;
 
        /*
@@ -226,7 +277,7 @@ ah4_output(m, isr)
        if (sav->flags & SADB_X_EXT_OLD) {
                struct ah *ahdr;
 
-               ahdr = (struct ah *)ahdrpos;
+               ahdr = (struct ah *)(void *)ahdrpos;
                ahsumpos = (u_char *)(ahdr + 1);
                ahdr->ah_len = plen >> 2;
                ahdr->ah_nxt = ip->ip_p;
@@ -236,7 +287,7 @@ ah4_output(m, isr)
        } else {
                struct newah *ahdr;
 
-               ahdr = (struct newah *)ahdrpos;
+               ahdr = (struct newah *)(void *)ahdrpos;
                ahsumpos = (u_char *)(ahdr + 1);
                ahdr->ah_len = (plen >> 2) + 1; /* plus one for seq# */
                ahdr->ah_nxt = ip->ip_p;
@@ -248,12 +299,14 @@ ah4_output(m, isr)
                                ipseclog((LOG_WARNING,
                                    "replay counter overflowed. %s\n",
                                    ipsec_logsastr(sav)));
-                               ipsecstat.out_inval++;
+                               IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
                                m_freem(m);
                                return EINVAL;
                        }
                }
+               lck_mtx_lock(sadb_mutex);
                sav->replay->count++;
+               lck_mtx_unlock(sadb_mutex);
                /*
                 * XXX sequence number must not be cycled, if the SA is
                 * installed by IKE daemon.
@@ -270,7 +323,7 @@ ah4_output(m, isr)
                ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
        else {
                ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
-               ipsecstat.out_inval++;
+               IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
                m_freem(m);
                return EMSGSIZE;
        }
@@ -292,12 +345,13 @@ ah4_output(m, isr)
         * calcurate the checksum, based on security association
         * and the algorithm specified.
         */
-       error = ah4_calccksum(m, (caddr_t)ahsumpos, algo, sav);
+       error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
        if (error) {
                ipseclog((LOG_ERR,
                    "error after ah4_calccksum, called from ah4_output"));
+               m_freem(m);
                m = NULL;
-               ipsecstat.out_inval++;
+               IPSEC_STAT_INCREMENT(ipsecstat.out_inval);
                return error;
        }
 
@@ -305,22 +359,27 @@ ah4_output(m, isr)
                ip = mtod(m, struct ip *);      /*just to make sure*/
                ip->ip_dst.s_addr = dst.s_addr;
        }
+       lck_mtx_lock(sadb_stat_mutex);
        ipsecstat.out_success++;
        ipsecstat.out_ahhist[sav->alg_auth]++;
+       lck_mtx_unlock(sadb_stat_mutex);
        key_sa_recordxfer(sav, m);
 
        return 0;
 }
+#endif
 
 /* Calculate AH length */
 int
 ah_hdrlen(sav)
        struct secasvar *sav;
 {
-       struct ah_algorithm *algo;
+       const struct ah_algorithm *algo;
        int plen, ahlen;
        
-       algo = &ah_algorithms[sav->alg_auth];
+       algo = ah_algorithm_lookup(sav->alg_auth);
+       if (!algo)
+               return 0;
        if (sav->flags & SADB_X_EXT_OLD) {
                /* RFC 1826 */
                plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1);   /*XXX pad to 8byte?*/
@@ -339,16 +398,15 @@ ah_hdrlen(sav)
  * Fill in the Authentication Header and calculate checksum.
  */
 int
-ah6_output(m, nexthdrp, md, isr)
+ah6_output(m, nexthdrp, md, sav)
        struct mbuf *m;
        u_char *nexthdrp;
        struct mbuf *md;
-       struct ipsecrequest *isr;
+       struct secasvar *sav;
 {
        struct mbuf *mprev;
        struct mbuf *mah;
-       struct secasvar *sav = isr->sav;
-       struct ah_algorithm *algo;
+       const struct ah_algorithm *algo;
        u_int32_t spi;
        u_char *ahsumpos = NULL;
        size_t plen;    /*AH payload size in bytes*/
@@ -404,14 +462,21 @@ ah6_output(m, nexthdrp, md, isr)
 
        if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
                ipseclog((LOG_DEBUG, "ah6_output: internal error: "
-                       "sav->replay is null: SPI=%u\n",
-                       (u_int32_t)ntohl(sav->spi)));
-               ipsec6stat.out_inval++;
+                         "sav->replay is null: SPI=%u\n",
+                         (u_int32_t)ntohl(sav->spi)));
+               IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
                m_freem(m);
                return EINVAL;
        }
 
-       algo = &ah_algorithms[sav->alg_auth];
+       algo = ah_algorithm_lookup(sav->alg_auth);
+       if (!algo) {
+               ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
+                   "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
+               IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
+               m_freem(m);
+               return EINVAL;
+       }
        spi = sav->spi;
 
        /*
@@ -442,14 +507,16 @@ ah6_output(m, nexthdrp, md, isr)
                        if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
                                /* XXX Is it noisy ? */
                                ipseclog((LOG_WARNING,
-                                   "replay counter overflowed. %s\n",
+                                    "replay counter overflowed. %s\n",
                                    ipsec_logsastr(sav)));
-                               ipsecstat.out_inval++;
+                               IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
                                m_freem(m);
                                return EINVAL;
                        }
                }
+               lck_mtx_lock(sadb_mutex);
                sav->replay->count++;
+               lck_mtx_unlock(sadb_mutex);
                /*
                 * XXX sequence number must not be cycled, if the SA is
                 * installed by IKE daemon.
@@ -462,20 +529,21 @@ ah6_output(m, nexthdrp, md, isr)
         * calcurate the checksum, based on security association
         * and the algorithm specified.
         */
-       error = ah6_calccksum(m, (caddr_t)ahsumpos, algo, sav);
+       error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
        if (error) {
-               ipsec6stat.out_inval++;
+               IPSEC_STAT_INCREMENT(ipsec6stat.out_inval);
                m_freem(m);
        } else {
-               ipsec6stat.out_success++;
+               IPSEC_STAT_INCREMENT(ipsec6stat.out_success);
                key_sa_recordxfer(sav, m);
        }
-       ipsec6stat.out_ahhist[sav->alg_auth]++;
+       IPSEC_STAT_INCREMENT(ipsec6stat.out_ahhist[sav->alg_auth]);
 
        return(error);
 }
 #endif
 
+#if INET
 /*
  * Find the final destination if there is loose/strict source routing option.
  * Returns NULL if there's no source routing options.
@@ -522,6 +590,15 @@ ah4_finaldst(m)
        q = (u_char *)(ip + 1);
        i = 0;
        while (i < optlen) {
+               if (i + IPOPT_OPTVAL >= optlen)
+                       return NULL;
+               if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
+                   q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
+                   i + IPOPT_OLEN < optlen)
+                       ;
+               else
+                       return NULL;
+
                switch (q[i + IPOPT_OPTVAL]) {
                case IPOPT_EOL:
                        i = optlen;     /* bye */
@@ -531,8 +608,8 @@ ah4_finaldst(m)
                        break;
                case IPOPT_LSRR:
                case IPOPT_SSRR:
-                       if (q[i + IPOPT_OLEN] <= 0
-                        || optlen - i < q[i + IPOPT_OLEN]) {
+                       if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
+                           optlen - i < q[i + IPOPT_OLEN]) {
                                ipseclog((LOG_ERR,
                                    "ip_finaldst: invalid IP option "
                                    "(code=%02x len=%02x)\n",
@@ -540,10 +617,10 @@ ah4_finaldst(m)
                                return NULL;
                        }
                        i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
-                       return (struct in_addr *)(q + i);
+                       return (struct in_addr *)(void *)(q + i);
                default:
-                       if (q[i + IPOPT_OLEN] <= 0
-                        || optlen - i < q[i + IPOPT_OLEN]) {
+                       if (q[i + IPOPT_OLEN] < 2 ||
+                           optlen - i < q[i + IPOPT_OLEN]) {
                                ipseclog((LOG_ERR,
                                    "ip_finaldst: invalid IP option "
                                    "(code=%02x len=%02x)\n",
@@ -556,3 +633,4 @@ ah4_finaldst(m)
        }
        return NULL;
 }
+#endif