-/* $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
#include <netinet/in.h>
#include <arpa/inet.h>
+#ifdef HAVE_OPENSSL
#include <openssl/md5.h>
+#endif
#include <stdlib.h>
#include <stdio.h>
#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;
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
* 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;
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;
}
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;
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;
}
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;
}
* 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);
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)
} 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;
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;
}
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;
}
-