/* $NetBSD: isakmp_frag.c,v 1.4.6.1 2009/04/22 11:25:35 tteras Exp $ */ /* Id: isakmp_frag.c,v 1.4 2004/11/13 17:31:36 manubsd Exp */ /* * Copyright (C) 2004 Emmanuel Dreyfus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include <sys/types.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/queue.h> #include <netinet/in.h> #include <arpa/inet.h> #include <openssl/md5.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <string.h> #include <errno.h> #if TIME_WITH_SYS_TIME # include <sys/time.h> # include <time.h> #else # if HAVE_SYS_TIME_H # include <sys/time.h> # else # include <time.h> # endif #endif #include <netdb.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <ctype.h> #include "var.h" #include "misc.h" #include "vmbuf.h" #include "plog.h" #include "sockmisc.h" #include "schedule.h" #include "debug.h" #include "isakmp_var.h" #include "isakmp.h" #include "handler.h" #include "isakmp_frag.h" #include "strnames.h" int isakmp_sendfrags(iph1, buf) struct ph1handle *iph1; vchar_t *buf; { 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; int etype; /* * Catch the exchange type for later: the fragments and the * fragmented packet must have the same exchange type. */ hdr = (struct isakmp *)buf->v; etype = hdr->etype; /* * 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(*hdr) + sizeof(*fraghdr) + sizeof(trailer)); 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; } set_isakmp_header1(frag, iph1, ISAKMP_NPTYPE_FRAG); hdr = (struct isakmp *)frag->v; hdr->etype = etype; fraghdr = (struct isakmp_frag *)(hdr + 1); fraghdr->unknown0 = htons(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); if (isakmp_send(iph1, frag) < 0) { plog(LLV_ERROR, LOCATION, NULL, "isakmp_send failed\n"); return -1; } vfree(frag); len -= datalen; sdata += datalen; } return fragnum; } unsigned int vendorid_frag_cap(gen) struct isakmp_gen *gen; { int *hp; hp = (int *)(gen + 1); return ntohl(hp[MD5_DIGEST_LENGTH / sizeof(*hp)]); } int isakmp_frag_extract(iph1, msg) struct ph1handle *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"); return -1; } isakmp = (struct isakmp *)msg->v; frag = (struct isakmp_frag *)(isakmp + 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) || ntohs(frag->len) < sizeof(*frag) + 1) { plog(LLV_ERROR, LOCATION, NULL, "Fragment too short\n"); return -1; } if ((buf = vmalloc(ntohs(frag->len) - sizeof(*frag))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return -1; } if ((item = racoon_malloc(sizeof(*item))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); vfree(buf); return -1; } data = (char *)(frag + 1); memcpy(buf->v, data, buf->l); item->frag_num = frag->index; 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; } vchar_t * isakmp_frag_reassembly(iph1) struct ph1handle *iph1; { struct isakmp_frag_item *item; size_t len = 0; vchar_t *buf = NULL; int frag_count = 0; int i; char *data; if ((item = iph1->frag_chain) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "No fragment to reassemble\n"); goto out; } do { frag_count++; 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"); goto out; } data = buf->v; for (i = 1; i <= frag_count; i++) { item = iph1->frag_chain; do { if (item->frag_num == i) break; item = item->frag_next; } while (item != NULL); if (item == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Missing fragment #%d\n", i); vfree(buf); buf = NULL; goto out; } memcpy(data, item->frag_packet->v, item->frag_packet->l); data += item->frag_packet->l; } out: item = iph1->frag_chain; do { struct isakmp_frag_item *next_item; next_item = item->frag_next; vfree(item->frag_packet); racoon_free(item); item = next_item; } while (item != NULL); iph1->frag_chain = NULL; return buf; } vchar_t * isakmp_frag_addcap(buf, cap) vchar_t *buf; int cap; { int *capp; size_t len; /* If the capability has not been added, add room now */ len = buf->l; if (len == MD5_DIGEST_LENGTH) { if ((buf = vrealloc(buf, len + sizeof(cap))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "Cannot allocate memory\n"); return NULL; } capp = (int *)(buf->v + len); *capp = htonl(0); } capp = (int *)(buf->v + MD5_DIGEST_LENGTH); *capp |= htonl(cap); return buf; }