/* $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;
}