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