X-Git-Url: https://git.saurik.com/apple/ipsec.git/blobdiff_plain/476121220b14176dcbf5f70f47b9ef8e38f8b389..ed5ea7fc1f63ae1e11ea74854408b785d896577f:/ipsec-tools/racoon/isakmp_frag.c diff --git a/ipsec-tools/racoon/isakmp_frag.c b/ipsec-tools/racoon/isakmp_frag.c index af19b91..304e825 100644 --- a/ipsec-tools/racoon/isakmp_frag.c +++ b/ipsec-tools/racoon/isakmp_frag.c @@ -41,7 +41,9 @@ #include #include +#ifdef HAVE_OPENSSL #include +#endif #include #include @@ -77,6 +79,9 @@ #include "handler.h" #include "isakmp_frag.h" #include "strnames.h" +#include "nattraversal.h" +#include "grabmyaddr.h" +#include "localconf.h" int isakmp_sendfrags(iph1, buf) @@ -95,6 +100,20 @@ isakmp_sendfrags(iph1, buf) unsigned int fragnum = 0; size_t len; int etype; +#ifdef ENABLE_NATT + size_t extralen = NON_ESP_MARKER_USE(iph1)? NON_ESP_MARKER_LEN : 0; +#else + size_t extralen = 0; +#endif + int s; + vchar_t *vbuf; + + + /* select the socket to be sent */ + s = getsockmyaddr(iph1->local); + if (s == -1){ + return -1; + } /* * Catch the exchange type for later: the fragments and the @@ -108,7 +127,7 @@ isakmp_sendfrags(iph1, buf) * First compute the maximum data length that will fit in it */ max_datalen = ISAKMP_FRAG_MAXLEN - - (sizeof(*hdr) + sizeof(*fraghdr) + sizeof(trailer)); + (sizeof(*hdr) + sizeof(*fraghdr)); sdata = buf->v; len = buf->l; @@ -121,9 +140,7 @@ isakmp_sendfrags(iph1, buf) else datalen = len; - fraglen = sizeof(*hdr) - + sizeof(*fraghdr) - + datalen; + fraglen = sizeof(*hdr) + sizeof(*fraghdr) + datalen; if ((frag = vmalloc(fraglen)) == NULL) { plog(LLV_ERROR, LOCATION, NULL, @@ -136,7 +153,7 @@ isakmp_sendfrags(iph1, buf) hdr->etype = etype; fraghdr = (struct isakmp_frag *)(hdr + 1); - fraghdr->unknown0 = htons(0); + fraghdr->unknown0 = 0; fraghdr->len = htons(fraglen - sizeof(*hdr)); fraghdr->unknown1 = htons(1); fraghdr->index = fragnum; @@ -148,8 +165,28 @@ isakmp_sendfrags(iph1, buf) data = (caddr_t)(fraghdr + 1); memcpy(data, sdata, datalen); - if (isakmp_send(iph1, frag) < 0) { - plog(LLV_ERROR, LOCATION, iph1->remote, "isakmp_send failed\n"); +#ifdef ENABLE_NATT + /* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker) + must added just before the packet itself. For this we must + allocate a new buffer and release it at the end. */ + if (extralen) { + if ((vbuf = vmalloc(frag->l + extralen)) == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "%s: vbuf allocation failed\n", __FUNCTION__); + vfree(frag); + return -1; + } + *(u_int32_t *)vbuf->v = 0; // non-esp marker + memcpy(vbuf->v + extralen, frag->v, frag->l); + vfree(frag); + frag = vbuf; + } +#endif + + if (sendfromto(s, frag->v, frag->l, + iph1->local, iph1->remote, lcconf->count_persend) == -1) { + plog(LLV_ERROR, LOCATION, NULL, "%s: sendfromto failed\n", __FUNCTION__); + vfree(frag); return -1; } @@ -158,7 +195,10 @@ isakmp_sendfrags(iph1, buf) len -= datalen; sdata += datalen; } - + + plog(LLV_DEBUG2, LOCATION, NULL, + "%s: processed %d fragments\n", __FUNCTION__, fragnum); + return fragnum; } @@ -167,10 +207,11 @@ vendorid_frag_cap(gen) struct isakmp_gen *gen; { int *hp; + int hashlen_bytes = eay_md5_hashlen() >> 3; hp = (int *)(gen + 1); - return ntohl(hp[MD5_DIGEST_LENGTH / sizeof(*hp)]); + return ntohl(hp[hashlen_bytes / sizeof(*hp)]); } int @@ -205,6 +246,13 @@ isakmp_frag_extract(iph1, msg) return -1; } + if (ntohs(frag->len) < sizeof(*frag)) { + plog(LLV_ERROR, LOCATION, NULL, + "invalid Frag, frag-len %d\n", + ntohs(frag->len)); + return -1; + } + if ((buf = vmalloc(ntohs(frag->len) - sizeof(*frag))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return -1; @@ -215,6 +263,7 @@ isakmp_frag_extract(iph1, msg) vfree(buf); return -1; } + bzero(item, sizeof(*item)); data = (char *)(frag + 1); memcpy(buf->v, data, buf->l); @@ -223,6 +272,7 @@ isakmp_frag_extract(iph1, msg) item->frag_last = (frag->flags & ISAKMP_FRAG_LAST); item->frag_next = NULL; item->frag_packet = buf; + item->frag_id = ntohs(frag->unknown1); /* Look for the last frag while inserting the new item in the chain */ if (item->frag_last) @@ -232,14 +282,29 @@ isakmp_frag_extract(iph1, msg) iph1->frag_chain = item; } else { struct isakmp_frag_item *current; + int dup = 0; current = iph1->frag_chain; + if (!current->frag_next && current->frag_last) { + last_frag = current->frag_num; + } while (current->frag_next) { if (current->frag_last) - last_frag = item->frag_num; + last_frag = current->frag_num; + if (current->frag_num == item->frag_num) { + dup = 1; + } current = current->frag_next; } - current->frag_next = item; + // avoid duplicates + if (!dup) { + current->frag_next = item; + } else { + racoon_free(item); + vfree(buf); + item = NULL; + buf = NULL; + } } /* If we saw the last frag, check if the chain is complete */ @@ -259,7 +324,10 @@ isakmp_frag_extract(iph1, msg) if (item != NULL) /* It is complete */ return 1; } - + + plog(LLV_DEBUG2, LOCATION, NULL, + "%s: processed %d fragments\n", __FUNCTION__, last_frag); + return 0; } @@ -270,7 +338,7 @@ isakmp_frag_reassembly(iph1) struct isakmp_frag_item *item; size_t len = 0; vchar_t *buf = NULL; - int frag_count = 0; + int frag_count = 0, frag_max = 0; int i; char *data; @@ -281,6 +349,9 @@ isakmp_frag_reassembly(iph1) do { frag_count++; + if (item->frag_num > frag_max && item->frag_last) { + frag_max = item->frag_num; + } len += item->frag_packet->l; item = item->frag_next; } while (item != NULL); @@ -291,7 +362,7 @@ isakmp_frag_reassembly(iph1) } data = buf->v; - for (i = 1; i <= frag_count; i++) { + for (i = 1; i <= frag_max; i++) { item = iph1->frag_chain; do { if (item->frag_num == i) @@ -304,15 +375,19 @@ isakmp_frag_reassembly(iph1) "Missing fragment #%d\n", i); vfree(buf); buf = NULL; - goto out; + return buf; } memcpy(data, item->frag_packet->v, item->frag_packet->l); data += item->frag_packet->l; } + plog(LLV_DEBUG2, LOCATION, NULL, + "%s: processed %d fragments\n", __FUNCTION__, frag_count); + out: item = iph1->frag_chain; - do { + + while (item != NULL) { struct isakmp_frag_item *next_item; next_item = item->frag_next; @@ -321,7 +396,7 @@ out: racoon_free(item); item = next_item; - } while (item != NULL); + } iph1->frag_chain = NULL; @@ -335,10 +410,11 @@ isakmp_frag_addcap(buf, cap) { int *capp; size_t len; + int hashlen_bytes = eay_md5_hashlen() >> 3; /* If the capability has not been added, add room now */ len = buf->l; - if (len == MD5_DIGEST_LENGTH) { + if (len == hashlen_bytes) { if ((buf = vrealloc(buf, len + sizeof(cap))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); @@ -348,9 +424,122 @@ isakmp_frag_addcap(buf, cap) *capp = htonl(0); } - capp = (int *)(buf->v + MD5_DIGEST_LENGTH); + capp = (int *)(buf->v + hashlen_bytes); *capp |= htonl(cap); return buf; } +int +sendfragsfromto(s, buf, local, remote, count_persend, frag_flags) + int s; + vchar_t *buf; + struct sockaddr *local; + struct sockaddr *remote; + int count_persend; + u_int32_t frag_flags; +{ + struct isakmp *main_hdr; + struct isakmp *hdr; + struct isakmp_frag *fraghdr; + caddr_t data; + caddr_t sdata; + size_t datalen; + size_t max_datalen; + size_t fraglen; + vchar_t *frag; + unsigned int trailer; + unsigned int fragnum = 0; + size_t len; +#ifdef ENABLE_NATT + size_t extralen = (frag_flags & FRAG_PUT_NON_ESP_MARKER)? NON_ESP_MARKER_LEN : 0; +#else + size_t extralen = 0; +#endif + + /* + * fragmented packet must have the same exchange type (amongst other fields in the header). + */ + main_hdr = (struct isakmp *)buf->v; + + /* + * We want to send a a packet smaller than ISAKMP_FRAG_MAXLEN + * First compute the maximum data length that will fit in it + */ + max_datalen = ISAKMP_FRAG_MAXLEN - + (sizeof(*main_hdr) + sizeof(*fraghdr)); + + sdata = buf->v; + len = buf->l; + + while (len > 0) { + fragnum++; + + if (len > max_datalen) + datalen = max_datalen; + else + datalen = len; + + fraglen = sizeof(*hdr) + sizeof(*fraghdr) + datalen; + + if ((frag = vmalloc(fraglen)) == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "Cannot allocate memory\n"); + return -1; + } + + hdr = (struct isakmp *)frag->v; + bcopy(main_hdr, hdr, sizeof(*hdr)); + hdr->len = htonl(frag->l); + hdr->np = ISAKMP_NPTYPE_FRAG; + + fraghdr = (struct isakmp_frag *)(hdr + 1); + fraghdr->unknown0 = 0; + fraghdr->len = htons(fraglen - sizeof(*hdr)); + fraghdr->unknown1 = htons(1); + fraghdr->index = fragnum; + if (len == datalen) + fraghdr->flags = ISAKMP_FRAG_LAST; + else + fraghdr->flags = 0; + + data = (caddr_t)(fraghdr + 1); + memcpy(data, sdata, datalen); + +#ifdef ENABLE_NATT + /* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker) + must added just before the packet itself. For this we must + allocate a new buffer and release it at the end. */ + if (extralen) { + vchar_t *vbuf; + + if ((vbuf = vmalloc(frag->l + extralen)) == NULL) { + plog(LLV_ERROR, LOCATION, NULL, + "%s: vbuf allocation failed\n", __FUNCTION__); + vfree(frag); + return -1; + } + *(u_int32_t *)vbuf->v = 0; // non-esp marker + memcpy(vbuf->v + extralen, frag->v, frag->l); + vfree(frag); + frag = vbuf; + } +#endif + + if (sendfromto(s, frag->v, frag->l, local, remote, count_persend) == -1) { + plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n"); + vfree(frag); + return -1; + } + + vfree(frag); + + len -= datalen; + sdata += datalen; + } + + plog(LLV_DEBUG2, LOCATION, NULL, + "%s: processed %d fragments\n", __FUNCTION__, fragnum); + + return fragnum; +}