X-Git-Url: https://git.saurik.com/apple/ipsec.git/blobdiff_plain/52b7d2ce06d68d0a9160d16f6e7c08c21c149d0d..ef0ae40ec078a7d5126612e50fbbbe09acb80a48:/ipsec-tools/racoon/isakmp_frag.c diff --git a/ipsec-tools/racoon/isakmp_frag.c b/ipsec-tools/racoon/isakmp_frag.c index e728c74..ee24b93 100644 --- a/ipsec-tools/racoon/isakmp_frag.c +++ b/ipsec-tools/racoon/isakmp_frag.c @@ -1,4 +1,6 @@ -/* $Id: isakmp_frag.c,v 1.4 2004/11/13 17:31:36 manubsd Exp $ */ +/* $NetBSD: isakmp_frag.c,v 1.4 2006/09/09 16:22:09 manu Exp $ */ + +/* Id: isakmp_frag.c,v 1.4 2004/11/13 17:31:36 manubsd Exp */ /* * Copyright (C) 2004 Emmanuel Dreyfus @@ -39,7 +41,9 @@ #include #include +#ifdef HAVE_OPENSSL #include +#endif #include #include @@ -75,10 +79,14 @@ #include "handler.h" #include "isakmp_frag.h" #include "strnames.h" +#include "nattraversal.h" +#include "grabmyaddr.h" +#include "localconf.h" +#include "crypto_openssl.h" int isakmp_sendfrags(iph1, buf) - struct ph1handle *iph1; + phase1_handle_t *iph1; vchar_t *buf; { struct isakmp *hdr; @@ -89,10 +97,23 @@ isakmp_sendfrags(iph1, buf) size_t max_datalen; size_t fraglen; vchar_t *frag; - unsigned int trailer; 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((struct sockaddr *)iph1->local); + if (s == -1){ + return -1; + } /* * Catch the exchange type for later: the fragments and the @@ -106,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; @@ -119,12 +140,10 @@ 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, + plog(ASL_LEVEL_ERR, "Cannot allocate memory\n"); return -1; } @@ -134,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; @@ -146,17 +165,40 @@ isakmp_sendfrags(iph1, buf) data = (caddr_t)(fraghdr + 1); memcpy(data, sdata, datalen); - if (isakmp_send(iph1, frag) < 0) { - plog(LLV_ERROR, LOCATION, NULL, "isakmp_send failed\n"); - return -1; +#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(ASL_LEVEL_ERR, + "%s: vbuf allocation failed\n", __FUNCTION__); + vfree(frag); + return -1; + } + *ALIGNED_CAST(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(ASL_LEVEL_ERR, "%s: sendfromto failed\n", __FUNCTION__); + vfree(frag); + return -1; + } + vfree(frag); len -= datalen; sdata += datalen; } - + + plog(ASL_LEVEL_DEBUG, + "%s: processed %d fragments\n", __FUNCTION__, fragnum); + return fragnum; } @@ -165,28 +207,28 @@ vendorid_frag_cap(gen) struct isakmp_gen *gen; { int *hp; + int hashlen_bytes = eay_md5_hashlen() >> 3; - hp = (int *)(gen + 1); + hp = ALIGNED_CAST(int *)(gen + 1); - return ntohl(hp[MD5_DIGEST_LENGTH / sizeof(*hp)]); + return ntohl(hp[hashlen_bytes / sizeof(*hp)]); } int isakmp_frag_extract(iph1, msg) - struct ph1handle *iph1; + phase1_handle_t *iph1; vchar_t *msg; { struct isakmp *isakmp; struct isakmp_frag *frag; struct isakmp_frag_item *item; vchar_t *buf; - size_t len; int last_frag = 0; char *data; int i; if (msg->l < sizeof(*isakmp) + sizeof(*frag)) { - plog(LLV_ERROR, LOCATION, NULL, "Message too short\n"); + plog(ASL_LEVEL_ERR, "Message too short\n"); return -1; } @@ -197,22 +239,31 @@ isakmp_frag_extract(iph1, msg) * frag->len is the frag payload data plus the frag payload header, * whose size is sizeof(*frag) */ - if (msg->l < sizeof(*isakmp) + ntohs(frag->len)) { - plog(LLV_ERROR, LOCATION, NULL, "Fragment too short\n"); + if (msg->l < sizeof(*isakmp) + ntohs(frag->len) || + ntohs(frag->len) < sizeof(*frag) + 1) { + plog(ASL_LEVEL_ERR, "Fragment too short\n"); + return -1; + } + + if (ntohs(frag->len) < sizeof(*frag)) { + plog(ASL_LEVEL_ERR, + "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"); + plog(ASL_LEVEL_ERR, "Cannot allocate memory\n"); return -1; } if ((item = racoon_malloc(sizeof(*item))) == NULL) { - plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); + plog(ASL_LEVEL_ERR, "Cannot allocate memory\n"); vfree(buf); return -1; } - + bzero(item, sizeof(*item)); + data = (char *)(frag + 1); memcpy(buf->v, data, buf->l); @@ -220,75 +271,82 @@ isakmp_frag_extract(iph1, msg) item->frag_last = (frag->flags & ISAKMP_FRAG_LAST); item->frag_next = NULL; item->frag_packet = buf; - - /* Look for the last frag while inserting the new item in the chain */ - if (item->frag_last) - last_frag = item->frag_num; - - if (iph1->frag_chain == NULL) { - iph1->frag_chain = item; - } else { - struct isakmp_frag_item *current; - - current = iph1->frag_chain; - while (current->frag_next) { - if (current->frag_last) - last_frag = item->frag_num; - current = current->frag_next; - } - current->frag_next = item; - } - - /* If we saw the last frag, check if the chain is complete */ - if (last_frag != 0) { - for (i = 1; i <= last_frag; i++) { - item = iph1->frag_chain; - do { - if (item->frag_num == i) - break; - item = item->frag_next; - } while (item != NULL); - - if (item == NULL) /* Not found */ - break; - } - - if (item != NULL) /* It is complete */ - return 1; - } - - return 0; + item->frag_id = ntohs(frag->unknown1); + + plog(ASL_LEVEL_DEBUG, + "%s: received fragment #%d frag ID=%d last frag=%d\n", + __FUNCTION__, item->frag_num, item->frag_id, item->frag_last); + + /* Insert if new and find the last frag num if present */ + struct isakmp_frag_item *current; + + last_frag = (item->frag_last ? item->frag_num : 0); + current = iph1->frag_chain; + while (current) { + if (current->frag_num == item->frag_num) { // duplicate? + vfree(item->frag_packet); + racoon_free(item); + return 0; // already have it + } + if (current->frag_last) + last_frag = current->frag_num; + current = current->frag_next; + } + /* no dup - insert it */ + item->frag_next = iph1->frag_chain; + iph1->frag_chain = item; + + /* Check if the chain is complete */ + if (last_frag == 0) + return 0; /* if last_frag not found - chain is not complete */ + for (i = 1; i <= last_frag; i++) { + current = iph1->frag_chain; + while (current) { + if (current->frag_num == i) + break; + current = current->frag_next; + }; + if (!current) + return 0; /* chain not complete */ + } + + plog(ASL_LEVEL_DEBUG, + "%s: processed fragment %d\n", __FUNCTION__, frag->index); + return 1; /* chain is complete */ } vchar_t * isakmp_frag_reassembly(iph1) - struct ph1handle *iph1; + phase1_handle_t *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; if ((item = iph1->frag_chain) == NULL) { - plog(LLV_ERROR, LOCATION, NULL, "No fragment to reassemble\n"); + plog(ASL_LEVEL_ERR, "No fragment to reassemble\n"); goto out; } 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); if ((buf = vmalloc(len)) == NULL) { - plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); + plog(ASL_LEVEL_ERR, "Cannot allocate memory\n"); goto out; } 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) @@ -297,19 +355,23 @@ isakmp_frag_reassembly(iph1) } while (item != NULL); if (item == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "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(ASL_LEVEL_DEBUG, + "%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; @@ -318,10 +380,11 @@ out: racoon_free(item); item = next_item; - } while (item != NULL); + } iph1->frag_chain = NULL; + //plogdump(ASL_LEVEL_DEBUG, buf->v, buf->l, "re-assembled fragements:\n"); return buf; } @@ -330,24 +393,138 @@ isakmp_frag_addcap(buf, cap) vchar_t *buf; int cap; { - int *capp; + int val, *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, + plog(ASL_LEVEL_ERR, "Cannot allocate memory\n"); return NULL; } - capp = (int *)(buf->v + len); - *capp = htonl(0); + val = 0; + memcpy(buf->v + len, &val, sizeof(val)); // Wcast_lign fix - copy instead of assign for unaligned move + } + capp = (int *)(void*)(buf->v + hashlen_bytes); // Wcast_lign fix - copy instead of assign for unaligned move + memcpy(&val, capp, sizeof(val)); + val |= htonl(cap); + memcpy(capp, &val, sizeof(val)); + + return buf; +} + +int +sendfragsfromto(s, buf, local, remote, count_persend, frag_flags) + int s; + vchar_t *buf; + struct sockaddr_storage *local; + struct sockaddr_storage *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 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(ASL_LEVEL_ERR, + "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(ASL_LEVEL_ERR, + "%s: vbuf allocation failed\n", __FUNCTION__); + vfree(frag); + return -1; + } + *ALIGNED_CAST(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(ASL_LEVEL_ERR, "sendfromto failed\n"); + vfree(frag); + return -1; + } + + vfree(frag); + + len -= datalen; + sdata += datalen; } - capp = (int *)(buf->v + MD5_DIGEST_LENGTH); - *capp |= htonl(cap); + plog(ASL_LEVEL_DEBUG, + "%s: processed %d fragments\n", __FUNCTION__, fragnum); - return buf; + return fragnum; } -