691 lines
15 KiB
C
691 lines
15 KiB
C
/*
|
|
* Copyright (C) 2008 Martin Willi
|
|
* HSR Hochschule fuer Technik Rapperswil
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <string.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "ha_message.h"
|
|
|
|
#include <daemon.h>
|
|
|
|
#define ALLOCATION_BLOCK 64
|
|
|
|
typedef struct private_ha_message_t private_ha_message_t;
|
|
|
|
/**
|
|
* Private data of an ha_message_t object.
|
|
*/
|
|
struct private_ha_message_t {
|
|
|
|
/**
|
|
* Public ha_message_t interface.
|
|
*/
|
|
ha_message_t public;
|
|
|
|
/**
|
|
* Allocated size of buf
|
|
*/
|
|
size_t allocated;
|
|
|
|
/**
|
|
* Buffer containing encoded data
|
|
*/
|
|
chunk_t buf;
|
|
};
|
|
|
|
ENUM(ha_message_type_names, HA_IKE_ADD, HA_IKE_IV,
|
|
"IKE_ADD",
|
|
"IKE_UPDATE",
|
|
"IKE_MID_INITIATOR",
|
|
"IKE_MID_RESPONDER",
|
|
"IKE_DELETE",
|
|
"CHILD_ADD",
|
|
"CHILD_DELETE",
|
|
"SEGMENT_DROP",
|
|
"SEGMENT_TAKE",
|
|
"STATUS",
|
|
"RESYNC",
|
|
"IKE_IV",
|
|
);
|
|
|
|
typedef struct ike_sa_id_encoding_t ike_sa_id_encoding_t;
|
|
|
|
/**
|
|
* Encoding if an ike_sa_id_t
|
|
*/
|
|
struct ike_sa_id_encoding_t {
|
|
uint8_t ike_version;
|
|
uint64_t initiator_spi;
|
|
uint64_t responder_spi;
|
|
uint8_t initiator;
|
|
} __attribute__((packed));
|
|
|
|
typedef struct identification_encoding_t identification_encoding_t;
|
|
|
|
/**
|
|
* Encoding of a identification_t
|
|
*/
|
|
struct identification_encoding_t {
|
|
uint8_t type;
|
|
uint8_t len;
|
|
char encoding[];
|
|
} __attribute__((packed));
|
|
|
|
typedef struct host_encoding_t host_encoding_t;
|
|
|
|
/**
|
|
* encoding of a host_t
|
|
*/
|
|
struct host_encoding_t {
|
|
uint16_t port;
|
|
uint8_t family;
|
|
char encoding[];
|
|
} __attribute__((packed));
|
|
|
|
typedef struct ts_encoding_t ts_encoding_t;
|
|
|
|
/**
|
|
* encoding of a traffic_selector_t
|
|
*/
|
|
struct ts_encoding_t {
|
|
uint8_t type;
|
|
uint8_t protocol;
|
|
uint16_t from_port;
|
|
uint16_t to_port;
|
|
uint8_t dynamic;
|
|
char encoding[];
|
|
} __attribute__((packed));
|
|
|
|
METHOD(ha_message_t, get_type, ha_message_type_t,
|
|
private_ha_message_t *this)
|
|
{
|
|
return this->buf.ptr[1];
|
|
}
|
|
|
|
/**
|
|
* check for space in buffer, increase if necessary
|
|
*/
|
|
static void check_buf(private_ha_message_t *this, size_t len)
|
|
{
|
|
int increased = 0;
|
|
|
|
while (this->buf.len + len > this->allocated)
|
|
{ /* double size */
|
|
this->allocated += ALLOCATION_BLOCK;
|
|
increased++;
|
|
}
|
|
if (increased)
|
|
{
|
|
this->buf.ptr = realloc(this->buf.ptr, this->allocated);
|
|
}
|
|
}
|
|
|
|
METHOD(ha_message_t, add_attribute, void,
|
|
private_ha_message_t *this, ha_message_attribute_t attribute, ...)
|
|
{
|
|
size_t len;
|
|
va_list args;
|
|
|
|
check_buf(this, sizeof(uint8_t));
|
|
this->buf.ptr[this->buf.len] = attribute;
|
|
this->buf.len += sizeof(uint8_t);
|
|
|
|
va_start(args, attribute);
|
|
switch (attribute)
|
|
{
|
|
/* ike_sa_id_t* */
|
|
case HA_IKE_ID:
|
|
case HA_IKE_REKEY_ID:
|
|
{
|
|
ike_sa_id_encoding_t *enc;
|
|
ike_sa_id_t *id;
|
|
|
|
id = va_arg(args, ike_sa_id_t*);
|
|
check_buf(this, sizeof(ike_sa_id_encoding_t));
|
|
enc = (ike_sa_id_encoding_t*)(this->buf.ptr + this->buf.len);
|
|
this->buf.len += sizeof(ike_sa_id_encoding_t);
|
|
enc->initiator = id->is_initiator(id);
|
|
enc->ike_version = id->get_ike_version(id);
|
|
enc->initiator_spi = id->get_initiator_spi(id);
|
|
enc->responder_spi = id->get_responder_spi(id);
|
|
break;
|
|
}
|
|
/* identification_t* */
|
|
case HA_LOCAL_ID:
|
|
case HA_REMOTE_ID:
|
|
case HA_REMOTE_EAP_ID:
|
|
{
|
|
identification_encoding_t *enc;
|
|
identification_t *id;
|
|
chunk_t data;
|
|
|
|
id = va_arg(args, identification_t*);
|
|
data = id->get_encoding(id);
|
|
check_buf(this, sizeof(identification_encoding_t) + data.len);
|
|
enc = (identification_encoding_t*)(this->buf.ptr + this->buf.len);
|
|
this->buf.len += sizeof(identification_encoding_t) + data.len;
|
|
enc->type = id->get_type(id);
|
|
enc->len = data.len;
|
|
memcpy(enc->encoding, data.ptr, data.len);
|
|
break;
|
|
}
|
|
/* host_t* */
|
|
case HA_LOCAL_ADDR:
|
|
case HA_REMOTE_ADDR:
|
|
case HA_LOCAL_VIP:
|
|
case HA_REMOTE_VIP:
|
|
case HA_PEER_ADDR:
|
|
{
|
|
host_encoding_t *enc;
|
|
host_t *host;
|
|
chunk_t data;
|
|
|
|
host = va_arg(args, host_t*);
|
|
data = host->get_address(host);
|
|
check_buf(this, sizeof(host_encoding_t) + data.len);
|
|
enc = (host_encoding_t*)(this->buf.ptr + this->buf.len);
|
|
this->buf.len += sizeof(host_encoding_t) + data.len;
|
|
enc->family = host->get_family(host);
|
|
enc->port = htons(host->get_port(host));
|
|
memcpy(enc->encoding, data.ptr, data.len);
|
|
break;
|
|
}
|
|
/* char* */
|
|
case HA_CONFIG_NAME:
|
|
{
|
|
char *str;
|
|
|
|
str = va_arg(args, char*);
|
|
len = strlen(str) + 1;
|
|
check_buf(this, len);
|
|
memcpy(this->buf.ptr + this->buf.len, str, len);
|
|
this->buf.len += len;
|
|
break;
|
|
}
|
|
/* uint8_t */
|
|
case HA_IKE_VERSION:
|
|
case HA_INITIATOR:
|
|
case HA_IPSEC_MODE:
|
|
case HA_IPCOMP:
|
|
{
|
|
uint8_t val;
|
|
|
|
val = va_arg(args, u_int);
|
|
check_buf(this, sizeof(val));
|
|
this->buf.ptr[this->buf.len] = val;
|
|
this->buf.len += sizeof(val);
|
|
break;
|
|
}
|
|
/* uint16_t */
|
|
case HA_ALG_DH:
|
|
case HA_ALG_PRF:
|
|
case HA_ALG_OLD_PRF:
|
|
case HA_ALG_ENCR:
|
|
case HA_ALG_ENCR_LEN:
|
|
case HA_ALG_INTEG:
|
|
case HA_INBOUND_CPI:
|
|
case HA_OUTBOUND_CPI:
|
|
case HA_SEGMENT:
|
|
case HA_ESN:
|
|
case HA_AUTH_METHOD:
|
|
{
|
|
uint16_t val;
|
|
|
|
val = va_arg(args, u_int);
|
|
check_buf(this, sizeof(val));
|
|
*(uint16_t*)(this->buf.ptr + this->buf.len) = htons(val);
|
|
this->buf.len += sizeof(val);
|
|
break;
|
|
}
|
|
/** uint32_t */
|
|
case HA_CONDITIONS:
|
|
case HA_EXTENSIONS:
|
|
case HA_INBOUND_SPI:
|
|
case HA_OUTBOUND_SPI:
|
|
case HA_MID:
|
|
{
|
|
uint32_t val;
|
|
|
|
val = va_arg(args, u_int);
|
|
check_buf(this, sizeof(val));
|
|
*(uint32_t*)(this->buf.ptr + this->buf.len) = htonl(val);
|
|
this->buf.len += sizeof(val);
|
|
break;
|
|
}
|
|
/** chunk_t */
|
|
case HA_NONCE_I:
|
|
case HA_NONCE_R:
|
|
case HA_SECRET:
|
|
case HA_LOCAL_DH:
|
|
case HA_REMOTE_DH:
|
|
case HA_PSK:
|
|
case HA_IV:
|
|
case HA_OLD_SKD:
|
|
{
|
|
chunk_t chunk;
|
|
|
|
chunk = va_arg(args, chunk_t);
|
|
check_buf(this, chunk.len + sizeof(uint16_t));
|
|
*(uint16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len);
|
|
memcpy(this->buf.ptr + this->buf.len + sizeof(uint16_t),
|
|
chunk.ptr, chunk.len);
|
|
this->buf.len += chunk.len + sizeof(uint16_t);;
|
|
break;
|
|
}
|
|
/** traffic_selector_t */
|
|
case HA_LOCAL_TS:
|
|
case HA_REMOTE_TS:
|
|
{
|
|
ts_encoding_t *enc;
|
|
traffic_selector_t *ts;
|
|
chunk_t data;
|
|
|
|
ts = va_arg(args, traffic_selector_t*);
|
|
data = chunk_cata("cc", ts->get_from_address(ts),
|
|
ts->get_to_address(ts));
|
|
check_buf(this, sizeof(ts_encoding_t) + data.len);
|
|
enc = (ts_encoding_t*)(this->buf.ptr + this->buf.len);
|
|
this->buf.len += sizeof(ts_encoding_t) + data.len;
|
|
enc->type = ts->get_type(ts);
|
|
enc->protocol = ts->get_protocol(ts);
|
|
enc->from_port = htons(ts->get_from_port(ts));
|
|
enc->to_port = htons(ts->get_to_port(ts));
|
|
enc->dynamic = ts->is_dynamic(ts);
|
|
memcpy(enc->encoding, data.ptr, data.len);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DBG1(DBG_CFG, "unable to encode, attribute %d unknown", attribute);
|
|
this->buf.len -= sizeof(uint8_t);
|
|
break;
|
|
}
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
/**
|
|
* Attribute enumerator implementation
|
|
*/
|
|
typedef struct {
|
|
/** implements enumerator_t */
|
|
enumerator_t public;
|
|
/** position in message */
|
|
chunk_t buf;
|
|
/** cleanup handler of current element, if any */
|
|
void (*cleanup)(void* data);
|
|
/** data to pass to cleanup handler */
|
|
void *cleanup_data;
|
|
} attribute_enumerator_t;
|
|
|
|
METHOD(enumerator_t, attribute_enumerate, bool,
|
|
attribute_enumerator_t *this, va_list args)
|
|
{
|
|
ha_message_attribute_t attr, *attr_out;
|
|
ha_message_value_t *value;
|
|
|
|
VA_ARGS_VGET(args, attr_out, value);
|
|
|
|
if (this->cleanup)
|
|
{
|
|
this->cleanup(this->cleanup_data);
|
|
this->cleanup = NULL;
|
|
}
|
|
if (this->buf.len < 1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
attr = this->buf.ptr[0];
|
|
this->buf = chunk_skip(this->buf, 1);
|
|
switch (attr)
|
|
{
|
|
/* ike_sa_id_t* */
|
|
case HA_IKE_ID:
|
|
case HA_IKE_REKEY_ID:
|
|
{
|
|
ike_sa_id_encoding_t *enc;
|
|
|
|
if (this->buf.len < sizeof(ike_sa_id_encoding_t))
|
|
{
|
|
return FALSE;
|
|
}
|
|
enc = (ike_sa_id_encoding_t*)(this->buf.ptr);
|
|
value->ike_sa_id = ike_sa_id_create(enc->ike_version,
|
|
enc->initiator_spi, enc->responder_spi,
|
|
enc->initiator);
|
|
*attr_out = attr;
|
|
this->cleanup = (void*)value->ike_sa_id->destroy;
|
|
this->cleanup_data = value->ike_sa_id;
|
|
this->buf = chunk_skip(this->buf, sizeof(ike_sa_id_encoding_t));
|
|
return TRUE;
|
|
}
|
|
/* identification_t* */
|
|
case HA_LOCAL_ID:
|
|
case HA_REMOTE_ID:
|
|
case HA_REMOTE_EAP_ID:
|
|
{
|
|
identification_encoding_t *enc;
|
|
|
|
enc = (identification_encoding_t*)(this->buf.ptr);
|
|
if (this->buf.len < sizeof(identification_encoding_t) ||
|
|
this->buf.len < sizeof(identification_encoding_t) + enc->len)
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->id = identification_create_from_encoding(enc->type,
|
|
chunk_create(enc->encoding, enc->len));
|
|
*attr_out = attr;
|
|
this->cleanup = (void*)value->id->destroy;
|
|
this->cleanup_data = value->id;
|
|
this->buf = chunk_skip(this->buf,
|
|
sizeof(identification_encoding_t) + enc->len);
|
|
return TRUE;
|
|
}
|
|
/* host_t* */
|
|
case HA_LOCAL_ADDR:
|
|
case HA_REMOTE_ADDR:
|
|
case HA_LOCAL_VIP:
|
|
case HA_REMOTE_VIP:
|
|
case HA_PEER_ADDR:
|
|
{
|
|
host_encoding_t *enc;
|
|
|
|
enc = (host_encoding_t*)(this->buf.ptr);
|
|
if (this->buf.len < sizeof(host_encoding_t))
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->host = host_create_from_chunk(enc->family,
|
|
chunk_create(enc->encoding,
|
|
this->buf.len - sizeof(host_encoding_t)),
|
|
ntohs(enc->port));
|
|
if (!value->host)
|
|
{
|
|
return FALSE;
|
|
}
|
|
*attr_out = attr;
|
|
this->cleanup = (void*)value->host->destroy;
|
|
this->cleanup_data = value->host;
|
|
this->buf = chunk_skip(this->buf, sizeof(host_encoding_t) +
|
|
value->host->get_address(value->host).len);
|
|
return TRUE;
|
|
}
|
|
/* char* */
|
|
case HA_CONFIG_NAME:
|
|
{
|
|
size_t len;
|
|
|
|
len = strnlen(this->buf.ptr, this->buf.len);
|
|
if (len >= this->buf.len)
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->str = this->buf.ptr;
|
|
*attr_out = attr;
|
|
this->buf = chunk_skip(this->buf, len + 1);
|
|
return TRUE;
|
|
}
|
|
/* uint8_t */
|
|
case HA_IKE_VERSION:
|
|
case HA_INITIATOR:
|
|
case HA_IPSEC_MODE:
|
|
case HA_IPCOMP:
|
|
{
|
|
if (this->buf.len < sizeof(uint8_t))
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->u8 = *(uint8_t*)this->buf.ptr;
|
|
*attr_out = attr;
|
|
this->buf = chunk_skip(this->buf, sizeof(uint8_t));
|
|
return TRUE;
|
|
}
|
|
/** uint16_t */
|
|
case HA_ALG_DH:
|
|
case HA_ALG_PRF:
|
|
case HA_ALG_OLD_PRF:
|
|
case HA_ALG_ENCR:
|
|
case HA_ALG_ENCR_LEN:
|
|
case HA_ALG_INTEG:
|
|
case HA_INBOUND_CPI:
|
|
case HA_OUTBOUND_CPI:
|
|
case HA_SEGMENT:
|
|
case HA_ESN:
|
|
case HA_AUTH_METHOD:
|
|
{
|
|
if (this->buf.len < sizeof(uint16_t))
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->u16 = ntohs(*(uint16_t*)this->buf.ptr);
|
|
*attr_out = attr;
|
|
this->buf = chunk_skip(this->buf, sizeof(uint16_t));
|
|
return TRUE;
|
|
}
|
|
/** uint32_t */
|
|
case HA_CONDITIONS:
|
|
case HA_EXTENSIONS:
|
|
case HA_INBOUND_SPI:
|
|
case HA_OUTBOUND_SPI:
|
|
case HA_MID:
|
|
{
|
|
if (this->buf.len < sizeof(uint32_t))
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->u32 = ntohl(*(uint32_t*)this->buf.ptr);
|
|
*attr_out = attr;
|
|
this->buf = chunk_skip(this->buf, sizeof(uint32_t));
|
|
return TRUE;
|
|
}
|
|
/** chunk_t */
|
|
case HA_NONCE_I:
|
|
case HA_NONCE_R:
|
|
case HA_SECRET:
|
|
case HA_LOCAL_DH:
|
|
case HA_REMOTE_DH:
|
|
case HA_PSK:
|
|
case HA_IV:
|
|
case HA_OLD_SKD:
|
|
{
|
|
size_t len;
|
|
|
|
if (this->buf.len < sizeof(uint16_t))
|
|
{
|
|
return FALSE;
|
|
}
|
|
len = ntohs(*(uint16_t*)this->buf.ptr);
|
|
this->buf = chunk_skip(this->buf, sizeof(uint16_t));
|
|
if (this->buf.len < len)
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->chunk.len = len;
|
|
value->chunk.ptr = this->buf.ptr;
|
|
*attr_out = attr;
|
|
this->buf = chunk_skip(this->buf, len);
|
|
return TRUE;
|
|
}
|
|
case HA_LOCAL_TS:
|
|
case HA_REMOTE_TS:
|
|
{
|
|
ts_encoding_t *enc;
|
|
host_t *host;
|
|
int addr_len;
|
|
|
|
enc = (ts_encoding_t*)(this->buf.ptr);
|
|
if (this->buf.len < sizeof(ts_encoding_t))
|
|
{
|
|
return FALSE;
|
|
}
|
|
switch (enc->type)
|
|
{
|
|
case TS_IPV4_ADDR_RANGE:
|
|
addr_len = 4;
|
|
if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case TS_IPV6_ADDR_RANGE:
|
|
addr_len = 16;
|
|
if (this->buf.len < sizeof(ts_encoding_t) + 2 * addr_len)
|
|
{
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
if (enc->dynamic)
|
|
{
|
|
host = host_create_from_chunk(0,
|
|
chunk_create(enc->encoding, addr_len), 0);
|
|
if (!host)
|
|
{
|
|
return FALSE;
|
|
}
|
|
value->ts = traffic_selector_create_dynamic(enc->protocol,
|
|
ntohs(enc->from_port), ntohs(enc->to_port));
|
|
value->ts->set_address(value->ts, host);
|
|
host->destroy(host);
|
|
}
|
|
else
|
|
{
|
|
value->ts = traffic_selector_create_from_bytes(enc->protocol,
|
|
enc->type, chunk_create(enc->encoding, addr_len),
|
|
ntohs(enc->from_port),
|
|
chunk_create(enc->encoding + addr_len, addr_len),
|
|
ntohs(enc->to_port));
|
|
if (!value->ts)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
*attr_out = attr;
|
|
this->cleanup = (void*)value->ts->destroy;
|
|
this->cleanup_data = value->ts;
|
|
this->buf = chunk_skip(this->buf, sizeof(ts_encoding_t)
|
|
+ addr_len * 2);
|
|
return TRUE;
|
|
}
|
|
default:
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
METHOD(enumerator_t, enum_destroy, void,
|
|
attribute_enumerator_t *this)
|
|
{
|
|
if (this->cleanup)
|
|
{
|
|
this->cleanup(this->cleanup_data);
|
|
}
|
|
free(this);
|
|
}
|
|
|
|
METHOD(ha_message_t, create_attribute_enumerator, enumerator_t*,
|
|
private_ha_message_t *this)
|
|
{
|
|
attribute_enumerator_t *e;
|
|
|
|
INIT(e,
|
|
.public = {
|
|
.enumerate = enumerator_enumerate_default,
|
|
.venumerate = _attribute_enumerate,
|
|
.destroy = _enum_destroy,
|
|
},
|
|
.buf = chunk_skip(this->buf, 2),
|
|
);
|
|
|
|
return &e->public;
|
|
}
|
|
|
|
METHOD(ha_message_t, get_encoding, chunk_t,
|
|
private_ha_message_t *this)
|
|
{
|
|
return this->buf;
|
|
}
|
|
|
|
METHOD(ha_message_t, destroy, void,
|
|
private_ha_message_t *this)
|
|
{
|
|
free(this->buf.ptr);
|
|
free(this);
|
|
}
|
|
|
|
|
|
static private_ha_message_t *ha_message_create_generic()
|
|
{
|
|
private_ha_message_t *this;
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.get_type = _get_type,
|
|
.add_attribute = _add_attribute,
|
|
.create_attribute_enumerator = _create_attribute_enumerator,
|
|
.get_encoding = _get_encoding,
|
|
.destroy = _destroy,
|
|
},
|
|
);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
ha_message_t *ha_message_create(ha_message_type_t type)
|
|
{
|
|
private_ha_message_t *this = ha_message_create_generic();
|
|
|
|
this->allocated = ALLOCATION_BLOCK;
|
|
this->buf.ptr = malloc(this->allocated);
|
|
this->buf.len = 2;
|
|
this->buf.ptr[0] = HA_MESSAGE_VERSION;
|
|
this->buf.ptr[1] = type;
|
|
|
|
return &this->public;
|
|
}
|
|
|
|
/**
|
|
* See header
|
|
*/
|
|
ha_message_t *ha_message_parse(chunk_t data)
|
|
{
|
|
private_ha_message_t *this;
|
|
|
|
if (data.len < 2)
|
|
{
|
|
DBG1(DBG_CFG, "HA message too short");
|
|
return NULL;
|
|
}
|
|
if (data.ptr[0] != HA_MESSAGE_VERSION)
|
|
{
|
|
DBG1(DBG_CFG, "HA message has version %d, expected %d",
|
|
data.ptr[0], HA_MESSAGE_VERSION);
|
|
return NULL;
|
|
}
|
|
|
|
this = ha_message_create_generic();
|
|
this->buf = chunk_clone(data);
|
|
this->allocated = this->buf.len;
|
|
|
|
return &this->public;
|
|
}
|
|
|