diff options
Diffstat (limited to 'src/libcharon/plugins/ha/ha_message.c')
-rw-r--r-- | src/libcharon/plugins/ha/ha_message.c | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/src/libcharon/plugins/ha/ha_message.c b/src/libcharon/plugins/ha/ha_message.c new file mode 100644 index 000000000..cd6c90af3 --- /dev/null +++ b/src/libcharon/plugins/ha/ha_message.c @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2008 Martin Willi + * 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; +}; + +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 { + u_int64_t initiator_spi; + u_int64_t responder_spi; + u_int8_t initiator; +} __attribute__((packed)); + +typedef struct identification_encoding_t identification_encoding_t; + +/** + * Encoding of a identification_t + */ +struct identification_encoding_t { + u_int8_t type; + u_int8_t len; + char encoding[]; +} __attribute__((packed)); + +typedef struct host_encoding_t host_encoding_t; + +/** + * encoding of a host_t + */ +struct host_encoding_t { + u_int16_t port; + u_int8_t family; + char encoding[]; +} __attribute__((packed)); + +typedef struct ts_encoding_t ts_encoding_t; + +/** + * encoding of a traffic_selector_t + */ +struct ts_encoding_t { + u_int8_t type; + u_int8_t protocol; + u_int16_t from_port; + u_int16_t to_port; + u_int8_t dynamic; + char encoding[]; +} __attribute__((packed)); + +/** + * Implementation of ha_message_t.get_type + */ +static ha_message_type_t get_type(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); + } +} + +/** + * Implementation of ha_message_t.add_attribute + */ +static void add_attribute(private_ha_message_t *this, + ha_message_attribute_t attribute, ...) +{ + size_t len; + va_list args; + + check_buf(this, sizeof(u_int8_t)); + this->buf.ptr[this->buf.len] = attribute; + this->buf.len += sizeof(u_int8_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->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_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_ADDITIONAL_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; + } + /* u_int8_t */ + case HA_IPSEC_MODE: + case HA_IPCOMP: + { + u_int8_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; + } + /* u_int16_t */ + 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: + { + u_int16_t val; + + val = va_arg(args, u_int); + check_buf(this, sizeof(val)); + *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(val); + this->buf.len += sizeof(val); + break; + } + /** u_int32_t */ + case HA_CONDITIONS: + case HA_EXTENSIONS: + case HA_INBOUND_SPI: + case HA_OUTBOUND_SPI: + case HA_INITIATE_MID: + case HA_RESPOND_MID: + { + u_int32_t val; + + val = va_arg(args, u_int); + check_buf(this, sizeof(val)); + *(u_int32_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_OLD_SKD: + { + chunk_t chunk; + + chunk = va_arg(args, chunk_t); + check_buf(this, chunk.len + sizeof(u_int16_t)); + *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len); + memcpy(this->buf.ptr + this->buf.len + sizeof(u_int16_t), + chunk.ptr, chunk.len); + this->buf.len += chunk.len + sizeof(u_int16_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(u_int8_t); + break; + } + } + va_end(args); +} + +/** + * Attribute enumerator implementation + */ +typedef struct { + /** implementes 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; + +/** + * Implementation of create_attribute_enumerator().enumerate + */ +static bool attribute_enumerate(attribute_enumerator_t *this, + ha_message_attribute_t *attr_out, + ha_message_value_t *value) +{ + ha_message_attribute_t attr; + + 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->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_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_ADDITIONAL_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; + } + /* u_int8_t */ + case HA_IPSEC_MODE: + case HA_IPCOMP: + { + if (this->buf.len < sizeof(u_int8_t)) + { + return FALSE; + } + value->u8 = *(u_int8_t*)this->buf.ptr; + *attr_out = attr; + this->buf = chunk_skip(this->buf, sizeof(u_int8_t)); + return TRUE; + } + /** u_int16_t */ + 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: + { + if (this->buf.len < sizeof(u_int16_t)) + { + return FALSE; + } + value->u16 = ntohs(*(u_int16_t*)this->buf.ptr); + *attr_out = attr; + this->buf = chunk_skip(this->buf, sizeof(u_int16_t)); + return TRUE; + } + /** u_int32_t */ + case HA_CONDITIONS: + case HA_EXTENSIONS: + case HA_INBOUND_SPI: + case HA_OUTBOUND_SPI: + case HA_INITIATE_MID: + case HA_RESPOND_MID: + { + if (this->buf.len < sizeof(u_int32_t)) + { + return FALSE; + } + value->u32 = ntohl(*(u_int32_t*)this->buf.ptr); + *attr_out = attr; + this->buf = chunk_skip(this->buf, sizeof(u_int32_t)); + return TRUE; + } + /** chunk_t */ + case HA_NONCE_I: + case HA_NONCE_R: + case HA_SECRET: + case HA_OLD_SKD: + { + size_t len; + + if (this->buf.len < sizeof(u_int16_t)) + { + return FALSE; + } + len = ntohs(*(u_int16_t*)this->buf.ptr); + this->buf = chunk_skip(this->buf, sizeof(u_int16_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; + } + } +} + +/** + * Implementation of create_attribute_enumerator().destroy + */ +static void enum_destroy(attribute_enumerator_t *this) +{ + if (this->cleanup) + { + this->cleanup(this->cleanup_data); + } + free(this); +} + +/** + * Implementation of ha_message_t.create_attribute_enumerator + */ +static enumerator_t* create_attribute_enumerator(private_ha_message_t *this) +{ + attribute_enumerator_t *e = malloc_thing(attribute_enumerator_t); + + e->public.enumerate = (void*)attribute_enumerate; + e->public.destroy = (void*)enum_destroy; + + e->buf = chunk_skip(this->buf, 2); + e->cleanup = NULL; + e->cleanup_data = NULL; + + return &e->public; +} + +/** + * Implementation of ha_message_t.get_encoding + */ +static chunk_t get_encoding(private_ha_message_t *this) +{ + return this->buf; +} + +/** + * Implementation of ha_message_t.destroy. + */ +static void destroy(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 = malloc_thing(private_ha_message_t); + + this->public.get_type = (ha_message_type_t(*)(ha_message_t*))get_type; + this->public.add_attribute = (void(*)(ha_message_t*, ha_message_attribute_t attribute, ...))add_attribute; + this->public.create_attribute_enumerator = (enumerator_t*(*)(ha_message_t*))create_attribute_enumerator; + this->public.get_encoding = (chunk_t(*)(ha_message_t*))get_encoding; + this->public.destroy = (void(*)(ha_message_t*))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; +} + |