]> git.saurik.com Git - apple/ipsec.git/blobdiff - ipsec-tools/racoon/ipsec_doi.c
ipsec-332.100.1.tar.gz
[apple/ipsec.git] / ipsec-tools / racoon / ipsec_doi.c
index 8fe1460b81510892746a85bf33216dcdbae96280..5b6934dcf4e9a585444546da515ba96168ce6d32 100644 (file)
@@ -34,6 +34,7 @@
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/socket.h>
+#include <arpa/inet.h>
 
 #include <netinet/in.h>
 
@@ -86,7 +87,6 @@
 #ifdef ENABLE_NATT
 #include "nattraversal.h"
 #endif
-#include "ikev2_rfc.h"
 
 #ifdef ENABLE_HYBRID
 static int switch_authmethod(int);
@@ -96,7 +96,6 @@ int verbose_proposal_check = 1;
 
 static vchar_t *get_ph1approval (phase1_handle_t *, struct prop_pair **);
 void print_ph1mismatched (struct prop_pair *, struct isakmpsa *);
-static int t2isakmpsa (struct isakmp_pl_t *, struct isakmpsa *);
 static int cmp_aproppair_i (struct prop_pair *, struct prop_pair *);
 static struct prop_pair *get_ph2approval (phase2_handle_t *,
        struct prop_pair **);
@@ -133,11 +132,8 @@ static int (*check_transform[]) (int) = {
        check_trns_ipcomp,      /* IPSECDOI_PROTO_IPCOMP */
 };
 
-static int check_attr_isakmp (struct isakmp_pl_t *);
 static int check_attr_ah (struct isakmp_pl_t *);
 static int check_attr_esp (struct isakmp_pl_t *);
-static int check_attr_ipsec (int, struct isakmp_pl_t *);
-static int check_attr_ipcomp (struct isakmp_pl_t *);
 static int (*check_attributes[]) (struct isakmp_pl_t *) = {
        0,
        check_attr_isakmp,      /* IPSECDOI_PROTO_ISAKMP */
@@ -372,7 +368,6 @@ get_ph1approvalx(p, proposal, sap, check_level)
                    tsap->encklen == s->encklen &&
                        tsap->version == s->version) {
                        switch(check_level) {
-                       case PROP_CHECK_IKEV2:
                        case PROP_CHECK_OBEY:
                                goto found;
                                break;
@@ -534,7 +529,7 @@ print_ph1mismatched(p, proposal)
 /*
  * get ISAKMP data attributes
  */
-static int
+int
 t2isakmpsa(trns, sa)
        struct isakmp_pl_t *trns;
        struct isakmpsa *sa;
@@ -561,6 +556,12 @@ t2isakmpsa(trns, sa)
                goto err;
 
        while (tlen > 0) {
+               if (tlen < sizeof(struct isakmp_data)) {
+                       plog(ASL_LEVEL_ERR,
+                                "t2isakmpsa invalid length of isakmp data, expected %zu actual %d\n",
+                                sizeof(struct isakmp_data), tlen);
+                       goto err;
+               }
 
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
@@ -584,10 +585,10 @@ t2isakmpsa(trns, sa)
                                p = (u_char *)&d->lorv;
                        } else {        /*TLV*/
                                len = ntohs(d->lorv);
-                               if (len > tlen) {
-                                       plog(ASL_LEVEL_ERR, 
-                                                "invalid ISAKMP-SA attr, attr-len %d, overall-len %d\n",
-                                                len, tlen);
+                               if ((len + sizeof(struct isakmp_data)) > tlen) {
+                                       plog(ASL_LEVEL_ERR,
+                                                "invalid ISAKMP-SA attr(%d), attr-len %d, overall-len %lu\n",
+                                                type, len, (tlen - sizeof(struct isakmp_data)));
                                        return -1;
                                }
                                p = (u_char *)(d + 1);
@@ -641,6 +642,14 @@ t2isakmpsa(trns, sa)
                                sa->dhgrp->gen1 = 0;
                                if (len > 4)
                                        return -1;
+
+                               if ((len + sizeof(struct isakmp_data)) > tlen) {
+                                       plog(ASL_LEVEL_ERR,
+                                                "invalid ISAKMP-SA attr - OAKLEY_ATTR_GRP_GEN_ONE, attr-len %d, overall-len %lu\n",
+                                                len, (tlen - sizeof(struct isakmp_data)));
+                                       return -1;
+                               }
+
                                memcpy(&sa->dhgrp->gen1, d + 1, len);
                                sa->dhgrp->gen1 = ntohl(sa->dhgrp->gen1);
                        }
@@ -655,6 +664,14 @@ t2isakmpsa(trns, sa)
                                sa->dhgrp->gen2 = 0;
                                if (len > 4)
                                        return -1;
+
+                               if ((len + sizeof(struct isakmp_data)) > tlen) {
+                                       plog(ASL_LEVEL_ERR,
+                                                "invalid ISAKMP-SA attr - OAKLEY_ATTR_GRP_GEN_TWO, attr-len %d, overall-len %lu\n",
+                                                len, (tlen - sizeof(struct isakmp_data)));
+                                       return -1;
+                               }
+
                                memcpy(&sa->dhgrp->gen2, d + 1, len);
                                sa->dhgrp->gen2 = ntohl(sa->dhgrp->gen2);
                        }
@@ -750,6 +767,13 @@ t2isakmpsa(trns, sa)
                        d = (struct isakmp_data *)((char *)d + sizeof(*d));
                } else {
                        tlen -= (sizeof(*d) + ntohs(d->lorv));
+                       if (tlen < 0) {
+                               plog(ASL_LEVEL_ERR,
+                                        "t2isakmpsa: packet too short - attr length %u for type %d\n",
+                                        ntohs(d->lorv), type);
+                               return -1;
+                       }
+
                        d = (struct isakmp_data *)((char *)d + sizeof(*d) + ntohs(d->lorv));
                }
        }
@@ -1074,11 +1098,17 @@ get_ph2approvalx(iph2, pp)
        }
        /* no proposal matching */
 err:
-       flushsaprop(pr0);
+       if (pr0 != NULL) {
+               flushsaprop(pr0);
+               pr0 = NULL;
+       }
        return NULL;
 
 found:
-       flushsaprop(pr0);
+       if (pr0 != NULL) {
+               flushsaprop(pr0);
+               pr0 = NULL;
+       }
        plog(ASL_LEVEL_DEBUG, "matched\n");
        iph2->approval = pr;
 
@@ -1245,6 +1275,14 @@ get_proppair(sa, mode)
                        goto bad;
                }
 
+               if (pa->len < sizeof(struct isakmp_pl_p)) {
+                       plog(ASL_LEVEL_ERR,
+                                "get_proppair invalid length of proposal, expected %lu actual %d\n",
+                                sizeof(struct isakmp_pl_p), pa->len);
+                       vfree(pbuf);
+                       goto bad;
+               }
+
                prop = (struct isakmp_pl_p *)pa->ptr;
                proplen = pa->len;
 
@@ -1272,6 +1310,14 @@ get_proppair(sa, mode)
                if (check_spi_size(prop->proto_id, prop->spi_size) < 0)
                        continue;
 
+               if (pa->len < (sizeof(struct isakmp_pl_p) + prop->spi_size)) {
+                       plog(ASL_LEVEL_ERR,
+                                "get_proppair invalid length of proposal spi size, expected %u actual %zu\n",
+                                prop->spi_size, (pa->len - sizeof(struct isakmp_pl_p)));
+                       vfree(pbuf);
+                       goto bad;
+               }
+
                /* get transform */
                if (get_transform(prop, pair, &num_p) < 0) {
                        vfree(pbuf);
@@ -1390,6 +1436,13 @@ get_transform(prop, pair, num_p)
                        break;
                }
 
+               if (pa->len < sizeof(struct isakmp_pl_t)) {
+                       plog(ASL_LEVEL_ERR,
+                                "get_transform invalid length of transform, expected %lu actual %d\n",
+                                sizeof(struct isakmp_pl_t), pa->len);
+                       break;
+               }
+
                trns = (struct isakmp_pl_t *)pa->ptr;
                trnslen = pa->len;
 
@@ -1957,7 +2010,7 @@ check_trns_ipcomp(t_id)
 /*
  * check data attributes in IKE.
  */
-static int
+int
 check_attr_isakmp(trns)
        struct isakmp_pl_t *trns;
 {
@@ -1970,6 +2023,13 @@ check_attr_isakmp(trns)
        d = (struct isakmp_data *)((caddr_t)trns + sizeof(struct isakmp_pl_t));
 
        while (tlen > 0) {
+               if (tlen < sizeof(struct isakmp_data)) {
+                       plog(ASL_LEVEL_ERR,
+                                "check_attr_isakmp invalid length of isakmp data, expected %zu actual %d\n",
+                                sizeof(struct isakmp_data), tlen);
+                       return -1;
+               }
+
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);
@@ -2148,6 +2208,12 @@ check_attr_isakmp(trns)
                        tlen -= (sizeof(*d) + lorv);
                        d = (struct isakmp_data *)((char *)d
                                + sizeof(*d) + lorv);
+                       if (tlen < 0) {
+                               plog(ASL_LEVEL_ERR,
+                                        "check_attr_isakmp: packet too short - attr length %u for type %d\n",
+                                        lorv, type);
+                               return -1;
+                       }
                }
        }
 
@@ -2171,7 +2237,7 @@ check_attr_esp(trns)
        return check_attr_ipsec(IPSECDOI_PROTO_IPSEC_ESP, trns);
 }
 
-static int
+int
 check_attr_ipsec(proto_id, trns)
        int proto_id;
        struct isakmp_pl_t *trns;
@@ -2187,6 +2253,13 @@ check_attr_ipsec(proto_id, trns)
        memset(attrseen, 0, sizeof(attrseen));
 
        while (tlen > 0) {
+               if (tlen < sizeof(struct isakmp_data)) {
+                       plog(ASL_LEVEL_ERR,
+                                "check_attr_ipsec invalid length of isakmp data, expected %zu actual %d\n",
+                                sizeof(struct isakmp_data), tlen);
+                       return -1;
+               }
+
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);
@@ -2362,6 +2435,12 @@ ahmismatch:
                        tlen -= (sizeof(*d) + lorv);
                        d = (struct isakmp_data *)((caddr_t)d
                                + sizeof(*d) + lorv);
+                       if (tlen < 0) {
+                               plog(ASL_LEVEL_ERR,
+                                        "check_attr_ipsec: packet too short - attr length %u for type %d\n",
+                                        lorv, type);
+                               return -1;
+                       }
                }
        }
 
@@ -2383,7 +2462,7 @@ ahmismatch:
        return 0;
 }
 
-static int
+int
 check_attr_ipcomp(trns)
        struct isakmp_pl_t *trns;
 {
@@ -2398,6 +2477,13 @@ check_attr_ipcomp(trns)
        memset(attrseen, 0, sizeof(attrseen));
 
        while (tlen > 0) {
+               if (tlen < sizeof(struct isakmp_data)) {
+                       plog(ASL_LEVEL_ERR,
+                                "check_attr_ipcomp: invalid length of isakmp data, expected %zu actual %d\n",
+                                sizeof(struct isakmp_data), tlen);
+                       return -1;
+               }
+
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
                lorv = ntohs(d->lorv);
@@ -2513,6 +2599,12 @@ check_attr_ipcomp(trns)
                        tlen -= (sizeof(*d) + lorv);
                        d = (struct isakmp_data *)((caddr_t)d
                                + sizeof(*d) + lorv);
+                       if (tlen < 0) {
+                               plog(ASL_LEVEL_ERR,
+                                        "check_attr_ipcomp: packet too short - attr length %u for type %d\n",
+                                        lorv, type);
+                               return -1;
+                       }
                }
        }
 
@@ -2567,8 +2659,6 @@ ipsecdoi_setph1proposal (phase1_handle_t *iph1)
                (ALIGNED_CAST(struct ipsecdoi_sa_b *)mysa->v)->sit = htonl(props->rmconf->sittype);
         
                (void)setph1prop(iph1, mysa->v + sizeof(struct ipsecdoi_sa_b));
-       } else {
-               (void)setph1prop(iph1, mysa->v);
        }
     
        return mysa;
@@ -2579,7 +2669,6 @@ setph1prop (phase1_handle_t *iph1,
                        caddr_t buf)
 {
     struct isakmpsa *props = iph1->rmconf->proposal;
-    unsigned int version = iph1->version;
     
        struct isakmp_pl_p *prop = NULL;
        struct isakmpsa *s = NULL;
@@ -2587,10 +2676,7 @@ setph1prop (phase1_handle_t *iph1,
        u_int8_t *np_t; /* pointer next trns type in previous header */
        int trns_num;
        caddr_t p = buf;
-       u_int16_t tmplen;
     int spi_size = 0;
-    cookie_t *my_cookie = (iph1->side == INITIATOR) ? &iph1->index.i_ck : &iph1->index.r_ck;
-    
 
        proplen = sizeof(*prop) + spi_size;
        if (buf) {
@@ -2965,7 +3051,6 @@ setph2proposal0(iph2, pp, pr)
        return p;
 }
 
-
 /*
  * create phase2 proposal from policy configuration.
  * NOT INCLUDING isakmp general header of SA payload.
@@ -3007,16 +3092,14 @@ ipsecdoi_setph2proposal(phase2_handle_t *iph2, int return_sa)
        for (a = proposal; a; a = a->next) {
                for (b = a->head; b; b = b->next) {
             if (b->proto_id == IPSECDOI_PROTO_IPCOMP) {
-                // %%%%% todo - IKEv2 uses ipcomp notification
                 // skip this - not specified in the SA
                 // Need to set this in iph2 ???
                 continue;
             }
             // IKEv1 sends encode mode in SA - uses diferent codes when NATT being used
-            // IKEv2 does not send encode mode in SA
 #ifdef ENABLE_NATT
             if (iph2->ph1->natt_flags & NAT_DETECTED) {
-                plog (ASL_LEVEL_INFO, "NAT detected -> UDP encapsulation\n");
+                plog (ASL_LEVEL_NOTICE, "NAT detected -> UDP encapsulation\n");
                 b->udp_encap = 1;
                 if (iph2->version == ISAKMP_VERSION_NUMBER_IKEV1) {
                     int udp_diff = iph2->ph1->natt_options->mode_udp_diff;
@@ -3672,6 +3755,7 @@ ipsecdoi_setid1(iph1)
        vchar_t *ret = NULL;
        struct ipsecdoi_id_b id_b;
        vchar_t *ident = NULL;
+       struct sockaddr_in v4_address;
        struct sockaddr_storage *ipid = NULL;
 
        /* init */
@@ -3753,6 +3837,19 @@ ipsecdoi_setid1(iph1)
                if (ipid == NULL)
                        ipid = iph1->local;
 
+               {
+                       if (ipid->ss_family == AF_INET6 &&
+                               iph1->nat64_prefix.length) {
+                               memset(&v4_address, 0, sizeof(v4_address));
+                               v4_address.sin_len = sizeof(struct sockaddr_in);
+                               v4_address.sin_family = AF_INET;
+                               v4_address.sin_port = ((struct sockaddr_in6 *)ipid)->sin6_port;
+                               v4_address.sin_addr.s_addr = 0;
+
+                               ipid = ALIGNED_CAST(struct sockaddr_storage *)&v4_address;
+                       }
+               }
+
                /* use IP address */
                switch (ipid->ss_family) {
                case AF_INET:
@@ -3981,8 +4078,22 @@ ipsecdoi_setid2(iph2)
                return -1;
        }
 
-       iph2->id = ipsecdoi_sockaddr2id(&sp->spidx.src,
-                                       sp->spidx.prefs, sp->spidx.ul_proto);
+       struct sockaddr_in local_v4_address;
+       struct sockaddr_storage *srcaddr = &sp->spidx.src;
+       u_int8_t prefs = sp->spidx.prefs;
+       if (sp->spidx.dst.ss_family == AF_INET6 &&
+               iph2->nat64_prefix.length) {
+               memset(&local_v4_address, 0, sizeof(local_v4_address));
+               local_v4_address.sin_len = sizeof(struct sockaddr_in);
+               local_v4_address.sin_family = AF_INET;
+               local_v4_address.sin_port = ((struct sockaddr_in6 *)&sp->spidx.src)->sin6_port;
+               // Setting a fixed IPv4 address to avoid FATAL-ID issue with 0.0.0.0 IPv4 address
+               inet_pton(AF_INET, "192.168.2.2", &local_v4_address.sin_addr);
+               srcaddr = ALIGNED_CAST(struct sockaddr_storage *)&local_v4_address;
+               prefs = 32;
+       }
+       iph2->id = ipsecdoi_sockaddr2id(srcaddr,
+                                       prefs, sp->spidx.ul_proto);
        if (iph2->id == NULL) {
                plog(ASL_LEVEL_ERR, 
                        "failed to get ID for %s\n",
@@ -4005,8 +4116,22 @@ ipsecdoi_setid2(iph2)
                         s_ipsecdoi_ident((ALIGNED_CAST(struct ipsecdoi_id_b *)iph2->id->v)->type));
 
        /* remote side */
-       iph2->id_p = ipsecdoi_sockaddr2id(&sp->spidx.dst,
-                               sp->spidx.prefd, sp->spidx.ul_proto);
+       struct sockaddr_in v4_address;
+       struct sockaddr_storage *dstaddr = &sp->spidx.dst;
+       u_int8_t prefd = sp->spidx.prefd;
+       if (sp->spidx.dst.ss_family == AF_INET6 &&
+               iph2->nat64_prefix.length) {
+               memset(&v4_address, 0, sizeof(v4_address));
+               v4_address.sin_len = sizeof(struct sockaddr_in);
+               v4_address.sin_family = AF_INET;
+               v4_address.sin_port = ((struct sockaddr_in6 *)&sp->spidx.dst)->sin6_port;
+               nw_nat64_extract_v4(&iph2->nat64_prefix, &((struct sockaddr_in6 *)&sp->spidx.dst)->sin6_addr, &v4_address.sin_addr);
+
+               dstaddr = ALIGNED_CAST(struct sockaddr_storage *)&v4_address;
+               prefd = 32;
+       }
+       iph2->id_p = ipsecdoi_sockaddr2id(dstaddr,
+                                                                         prefd, sp->spidx.ul_proto);
        if (iph2->id_p == NULL) {
                plog(ASL_LEVEL_ERR, 
                        "failed to get ID for %s\n",
@@ -4014,7 +4139,7 @@ ipsecdoi_setid2(iph2)
                VPTRINIT(iph2->id);
                return -1;
        }
-       plogdump(ASL_LEVEL_DEBUG, iph2->id->v, iph2->id->l, "use remote ID type %s\n",
+       plogdump(ASL_LEVEL_DEBUG, iph2->id_p->v, iph2->id_p->l, "use remote ID type %s\n",
                         s_ipsecdoi_ident((ALIGNED_CAST(struct ipsecdoi_id_b *)iph2->id_p->v)->type));
 
        return 0;
@@ -4045,9 +4170,13 @@ ipsecdoi_sockaddr2id(saddr, prefixlen, ul_proto)
                if (prefixlen == (sizeof(struct in_addr) << 3)) {
                        type = IPSECDOI_ID_IPV4_ADDR;
                        len2 = 0;
-               } else {
+               } else if (prefixlen < (sizeof(struct in_addr) << 3)) {
                        type = IPSECDOI_ID_IPV4_ADDR_SUBNET;
                        len2 = sizeof(struct in_addr);
+               } else {
+                       plog(ASL_LEVEL_ERR,
+                               "invalid prefix length: %d.\n", prefixlen);
+                       return NULL;
                }
                sa = (caddr_t)&((struct sockaddr_in *)(saddr))->sin_addr;
                port = ((struct sockaddr_in *)(saddr))->sin_port;
@@ -4058,9 +4187,13 @@ ipsecdoi_sockaddr2id(saddr, prefixlen, ul_proto)
                if (prefixlen == (sizeof(struct in6_addr) << 3)) {
                        type = IPSECDOI_ID_IPV6_ADDR;
                        len2 = 0;
-               } else {
+               } else if (prefixlen < (sizeof(struct in6_addr) << 3)) {
                        type = IPSECDOI_ID_IPV6_ADDR_SUBNET;
                        len2 = sizeof(struct in6_addr);
+               } else {
+                       plog(ASL_LEVEL_ERR,
+                               "invalid prefix length: %d.\n", prefixlen);
+                       return NULL;
                }
                sa = (caddr_t)&((struct sockaddr_in6 *)(saddr))->sin6_addr;
                port = ((struct sockaddr_in6 *)(saddr))->sin6_port;
@@ -4550,6 +4683,12 @@ ipsecdoi_t2satrns(t, pp, pr, tr)
        tr->authtype = IPSECDOI_ATTR_AUTH_NONE;
 
        while (tlen > 0) {
+               if (tlen < sizeof(struct isakmp_data)) {
+                       plog(ASL_LEVEL_ERR,
+                                "ipsecdoi_t2satrns invalid length of isakmp data, expected %zu actual %d\n",
+                                sizeof(struct isakmp_data), tlen);
+                       return -1;
+               }
 
                type = ntohs(d->type) & ~ISAKMP_GEN_MASK;
                flag = ntohs(d->type) & ISAKMP_GEN_MASK;
@@ -4601,6 +4740,12 @@ ipsecdoi_t2satrns(t, pp, pr, tr)
                                memcpy(ld_buf->v, &d->lorv, sizeof(d->lorv));
                        } else {
                                int len = ntohs(d->lorv);
+                               if ((len + sizeof(struct isakmp_data)) > tlen) {
+                                       plog(ASL_LEVEL_ERR,
+                                                "invalid IPsec attr(%d), attr-len %d, overall-len %lu\n",
+                                                type, len, (tlen - sizeof(struct isakmp_data)));
+                                       return -1;
+                               }
                                /* i.e. ISAKMP_GEN_TLV */
                                ld_buf = vmalloc(len);
                                if (ld_buf == NULL) {
@@ -4721,6 +4866,12 @@ ipsecdoi_t2satrns(t, pp, pr, tr)
                } else {
                        tlen -= (sizeof(*d) + ntohs(d->lorv));
                        d = (struct isakmp_data *)((caddr_t)d + sizeof(*d) + ntohs(d->lorv));
+                       if (tlen < 0) {
+                               plog(ASL_LEVEL_ERR,
+                                        "ipsecdoi_t2satrns: packet too short - attr length %u for type %d\n",
+                                        ntohs(d->lorv), type);
+                               goto end;
+                       }
                }
        }