diff options
Diffstat (limited to 'src/libcharon/encoding')
-rw-r--r-- | src/libcharon/encoding/message.c | 997 | ||||
-rw-r--r-- | src/libcharon/encoding/message.h | 72 | ||||
-rw-r--r-- | src/libcharon/encoding/parser.c | 2 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/encrypted_fragment_payload.h | 85 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/encrypted_payload.c | 1022 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/encrypted_payload.h (renamed from src/libcharon/encoding/payloads/encryption_payload.h) | 57 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/encryption_payload.c | 634 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/ike_header.c | 16 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/notify_payload.c | 18 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/notify_payload.h | 4 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/payload.c | 41 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/payload.h | 24 | ||||
-rw-r--r-- | src/libcharon/encoding/payloads/sa_payload.c | 17 |
13 files changed, 2167 insertions, 822 deletions
diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 0f5f40ada..cb6c97f25 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2013 Tobias Brunner + * Copyright (C) 2006-2014 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2010 revosec AG * Copyright (C) 2006 Daniel Roethlisberger @@ -23,6 +23,8 @@ #include "message.h" #include <library.h> +#include <bio/bio_writer.h> +#include <collections/array.h> #include <daemon.h> #include <sa/ikev1/keymat_v1.h> #include <encoding/generator.h> @@ -30,9 +32,11 @@ #include <encoding/payloads/encodings.h> #include <encoding/payloads/payload.h> #include <encoding/payloads/hash_payload.h> -#include <encoding/payloads/encryption_payload.h> +#include <encoding/payloads/encrypted_payload.h> +#include <encoding/payloads/encrypted_fragment_payload.h> #include <encoding/payloads/unknown_payload.h> #include <encoding/payloads/cp_payload.h> +#include <encoding/payloads/fragment_payload.h> /** * Max number of notify payloads per IKEv2 message @@ -802,6 +806,30 @@ static message_rule_t message_rules[] = { #endif /* USE_IKEV1 */ }; +/** + * Data for fragment reassembly. + */ +typedef struct { + + /** + * For IKEv1 the number of the last fragment (in case we receive them out + * of order), since the first one starts with 1 this defines the number of + * fragments we expect. + * For IKEv2 we store the total number of fragment we received last. + */ + u_int16_t last; + + /** + * Length of all currently received fragments. + */ + size_t len; + + /** + * Maximum length of a fragmented packet. + */ + size_t max_packet; + +} fragment_data_t; typedef struct private_message_t private_message_t; @@ -876,6 +904,12 @@ struct private_message_t { packet_t *packet; /** + * Array of generated fragments (if any), as packet_t*. + * If defragmenting (i.e. frag != NULL) this contains fragment_t* + */ + array_t *fragments; + + /** * Linked List where payload data are stored in. */ linked_list_t *payloads; @@ -889,9 +923,46 @@ struct private_message_t { * The message rule for this message instance */ message_rule_t *rule; + + /** + * Data used to reassemble a fragmented message + */ + fragment_data_t *frag; }; /** + * Maximum number of fragments we will handle + */ +#define MAX_FRAGMENTS 255 + +/** + * A single fragment within a fragmented message + */ +typedef struct { + + /** fragment number */ + u_int8_t num; + + /** fragment data */ + chunk_t data; + +} fragment_t; + +static void fragment_destroy(fragment_t *this) +{ + chunk_free(&this->data); + free(this); +} + +static void reset_defrag(private_message_t *this) +{ + array_destroy_function(this->fragments, (void*)fragment_destroy, NULL); + this->fragments = NULL; + this->frag->last = 0; + this->frag->len = 0; +} + +/** * Get the message rule that applies to this message */ static message_rule_t* get_message_rule(private_message_t *this) @@ -1049,6 +1120,12 @@ METHOD(message_t, is_encoded, bool, return this->packet->get_data(this->packet).ptr != NULL; } +METHOD(message_t, is_fragmented, bool, + private_message_t *this) +{ + return array_count(this->fragments) > 0; +} + METHOD(message_t, add_payload, void, private_message_t *this, payload_t *payload) { @@ -1330,6 +1407,12 @@ static char* get_string(private_message_t *this, char *buf, int len) return buf; } +METHOD(message_t, disable_sort, void, + private_message_t *this) +{ + this->sort_disabled = TRUE; +} + /** * reorder payloads depending on reordering rules */ @@ -1339,6 +1422,8 @@ static void order_payloads(private_message_t *this) payload_t *payload; int i; + DBG2(DBG_ENC, "order payloads in message"); + /* move to temp list */ list = linked_list_create(); while (this->payloads->remove_last(this->payloads, @@ -1392,29 +1477,42 @@ static void order_payloads(private_message_t *this) } /** - * Wrap payloads in an encryption payload + * Wrap payloads in an encrypted payload */ -static encryption_payload_t* wrap_payloads(private_message_t *this) +static encrypted_payload_t* wrap_payloads(private_message_t *this) { - encryption_payload_t *encryption; + encrypted_payload_t *encrypted = NULL; linked_list_t *payloads; payload_t *current; - /* copy all payloads in a temporary list */ + /* move all payloads to a temporary list */ payloads = linked_list_create(); while (this->payloads->remove_first(this->payloads, (void**)¤t) == SUCCESS) { - payloads->insert_last(payloads, current); + if (current->get_type(current) == PLV2_FRAGMENT) + { /* treat encrypted fragment payload as encrypted payload */ + encrypted = (encrypted_payload_t*)current; + } + else + { + payloads->insert_last(payloads, current); + } + } + if (encrypted) + { /* simply adopt all the unencrypted payloads */ + this->payloads->destroy(this->payloads); + this->payloads = payloads; + return encrypted; } if (this->is_encrypted) { - encryption = encryption_payload_create(PLV1_ENCRYPTED); + encrypted = encrypted_payload_create(PLV1_ENCRYPTED); } else { - encryption = encryption_payload_create(PLV2_ENCRYPTED); + encrypted = encrypted_payload_create(PLV2_ENCRYPTED); } while (payloads->remove_first(payloads, (void**)¤t) == SUCCESS) { @@ -1432,7 +1530,7 @@ static encryption_payload_t* wrap_payloads(private_message_t *this) { /* encryption is forced for IKEv1 */ DBG2(DBG_ENC, "insert payload %N into encrypted payload", payload_type_names, type); - encryption->add_payload(encryption, current); + encrypted->add_payload(encrypted, current); } else { @@ -1443,31 +1541,71 @@ static encryption_payload_t* wrap_payloads(private_message_t *this) } payloads->destroy(payloads); - return encryption; + return encrypted; } -METHOD(message_t, disable_sort, void, - private_message_t *this) +/** + * Creates the IKE header for this message + */ +static ike_header_t *create_header(private_message_t *this) { - this->sort_disabled = TRUE; + ike_header_t *ike_header; + bool *reserved; + int i; + + ike_header = ike_header_create_version(this->major_version, + this->minor_version); + ike_header->set_exchange_type(ike_header, this->exchange_type); + ike_header->set_message_id(ike_header, this->message_id); + if (this->major_version == IKEV2_MAJOR_VERSION) + { + ike_header->set_response_flag(ike_header, !this->is_request); + ike_header->set_version_flag(ike_header, this->version_flag); + ike_header->set_initiator_flag(ike_header, + this->ike_sa_id->is_initiator(this->ike_sa_id)); + } + else + { + ike_header->set_encryption_flag(ike_header, this->is_encrypted); + } + ike_header->set_initiator_spi(ike_header, + this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); + ike_header->set_responder_spi(ike_header, + this->ike_sa_id->get_responder_spi(this->ike_sa_id)); + + for (i = 0; i < countof(this->reserved); i++) + { + reserved = payload_get_field(&ike_header->payload_interface, + RESERVED_BIT, i); + if (reserved) + { + *reserved = this->reserved[i]; + } + } + return ike_header; } -METHOD(message_t, generate, status_t, - private_message_t *this, keymat_t *keymat, packet_t **packet) +/** + * Generates the message, if needed, wraps the payloads in an encrypted payload. + * + * The generator and the possible enrypted payload are returned. The latter + * is not yet encrypted (but the transform is set). It is also not added to + * the payload list (so unless there are unencrypted payloads that list will + * be empty afterwards). + */ +static status_t generate_message(private_message_t *this, keymat_t *keymat, + generator_t **out_generator, encrypted_payload_t **encrypted) { keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; generator_t *generator; - ike_header_t *ike_header; - payload_t *payload, *next; - encryption_payload_t *encryption = NULL; payload_type_t next_type; enumerator_t *enumerator; aead_t *aead = NULL; - chunk_t chunk, hash = chunk_empty; + chunk_t hash = chunk_empty; char str[BUF_LEN]; - u_int32_t *lenpos; - bool encrypted = FALSE, *reserved; - int i; + ike_header_t *ike_header; + payload_t *payload, *next; + bool encrypting = FALSE; if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) { @@ -1493,6 +1631,7 @@ METHOD(message_t, generate, status_t, { order_payloads(this); } + if (keymat && keymat->get_version(keymat) == IKEV1) { /* get a hash for this message, if any is required */ @@ -1505,16 +1644,17 @@ METHOD(message_t, generate, status_t, this->payloads->insert_first(this->payloads, hash_payload); if (this->exchange_type == INFORMATIONAL_V1) { - this->is_encrypted = encrypted = TRUE; + this->is_encrypted = encrypting = TRUE; } chunk_free(&hash); } } + if (this->major_version == IKEV2_MAJOR_VERSION) { - encrypted = this->rule->encrypted; + encrypting = this->rule->encrypted; } - else if (!encrypted) + else if (!encrypting) { /* If at least one payload requires encryption, encrypt the message. * If no key material is available, the flag will be reset below. */ @@ -1526,7 +1666,7 @@ METHOD(message_t, generate, status_t, rule = get_payload_rule(this, payload->get_type(payload)); if (rule && rule->encrypted) { - this->is_encrypted = encrypted = TRUE; + this->is_encrypted = encrypting = TRUE; break; } } @@ -1539,9 +1679,10 @@ METHOD(message_t, generate, status_t, { aead = keymat->get_aead(keymat, FALSE); } - if (aead && encrypted) + if (aead && encrypting) { - encryption = wrap_payloads(this); + *encrypted = wrap_payloads(this); + (*encrypted)->set_transform(*encrypted, aead); } else { @@ -1549,39 +1690,9 @@ METHOD(message_t, generate, status_t, this->is_encrypted = FALSE; } - ike_header = ike_header_create_version(this->major_version, - this->minor_version); - ike_header->set_exchange_type(ike_header, this->exchange_type); - ike_header->set_message_id(ike_header, this->message_id); - if (this->major_version == IKEV2_MAJOR_VERSION) - { - ike_header->set_response_flag(ike_header, !this->is_request); - ike_header->set_version_flag(ike_header, this->version_flag); - ike_header->set_initiator_flag(ike_header, - this->ike_sa_id->is_initiator(this->ike_sa_id)); - } - else - { - ike_header->set_encryption_flag(ike_header, this->is_encrypted); - } - ike_header->set_initiator_spi(ike_header, - this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); - ike_header->set_responder_spi(ike_header, - this->ike_sa_id->get_responder_spi(this->ike_sa_id)); - - for (i = 0; i < countof(this->reserved); i++) - { - reserved = payload_get_field(&ike_header->payload_interface, - RESERVED_BIT, i); - if (reserved) - { - *reserved = this->reserved[i]; - } - } - - generator = generator_create(); - /* generate all payloads with proper next type */ + *out_generator = generator = generator_create(); + ike_header = create_header(this); payload = (payload_t*)ike_header; enumerator = create_payload_enumerator(this); while (enumerator->enumerate(enumerator, &next)) @@ -1591,53 +1702,71 @@ METHOD(message_t, generate, status_t, payload = next; } enumerator->destroy(enumerator); + + next_type = PL_NONE; if (this->is_encrypted) { /* for encrypted IKEv1 messages */ - next_type = encryption->payload_interface.get_next_type( - (payload_t*)encryption); + next_type = (*encrypted)->payload_interface.get_next_type( + (payload_t*)*encrypted); } - else - { - next_type = encryption ? PLV2_ENCRYPTED : PL_NONE; + else if (*encrypted) + { /* use proper IKEv2 encrypted (fragment) payload type */ + next_type = (*encrypted)->payload_interface.get_type( + (payload_t*)*encrypted); } payload->set_next_type(payload, next_type); generator->generate_payload(generator, payload); ike_header->destroy(ike_header); + return SUCCESS; +} - if (encryption) - { /* set_transform() has to be called before get_length() */ - encryption->set_transform(encryption, aead); +/** + * Encrypts and adds the encrypted payload (if any) to the payload list and + * finalizes the message generation. Destroys the given generator. + */ +static status_t finalize_message(private_message_t *this, keymat_t *keymat, + generator_t *generator, encrypted_payload_t *encrypted) +{ + keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; + chunk_t chunk; + u_int32_t *lenpos; + + if (encrypted) + { if (this->is_encrypted) { /* for IKEv1 instead of associated data we provide the IV */ if (!keymat_v1->get_iv(keymat_v1, this->message_id, &chunk)) { generator->destroy(generator); + encrypted->destroy(encrypted); return FAILED; } } else - { /* build associated data (without header of encryption payload) */ + { /* build associated data (without header of encrypted payload) */ chunk = generator->get_chunk(generator, &lenpos); - /* fill in length, including encryption payload */ - htoun32(lenpos, chunk.len + encryption->get_length(encryption)); + /* fill in length, including encrypted payload */ + htoun32(lenpos, chunk.len + encrypted->get_length(encrypted)); } - this->payloads->insert_last(this->payloads, encryption); - if (encryption->encrypt(encryption, this->message_id, chunk) != SUCCESS) + this->payloads->insert_last(this->payloads, encrypted); + if (encrypted->encrypt(encrypted, this->message_id, chunk) != SUCCESS) { generator->destroy(generator); return INVALID_STATE; } - generator->generate_payload(generator, &encryption->payload_interface); + generator->generate_payload(generator, &encrypted->payload_interface); } chunk = generator->get_chunk(generator, &lenpos); htoun32(lenpos, chunk.len); this->packet->set_data(this->packet, chunk_clone(chunk)); - if (this->is_encrypted) + if (this->is_encrypted && this->exchange_type != INFORMATIONAL_V1) { /* update the IV for the next IKEv1 message */ chunk_t last_block; + aead_t *aead; size_t bs; + aead = keymat->get_aead(keymat, FALSE); bs = aead->get_block_size(aead); last_block = chunk_create(chunk.ptr + chunk.len - bs, bs); if (!keymat_v1->update_iv(keymat_v1, this->message_id, last_block) || @@ -1648,30 +1777,301 @@ METHOD(message_t, generate, status_t, } } generator->destroy(generator); - *packet = this->packet->clone(this->packet); return SUCCESS; } -METHOD(message_t, get_packet, packet_t*, - private_message_t *this) +METHOD(message_t, generate, status_t, + private_message_t *this, keymat_t *keymat, packet_t **packet) { - if (this->packet == NULL) + generator_t *generator = NULL; + encrypted_payload_t *encrypted = NULL; + status_t status; + + status = generate_message(this, keymat, &generator, &encrypted); + if (status != SUCCESS) { - return NULL; + DESTROY_IF(generator); + return status; + } + status = finalize_message(this, keymat, generator, encrypted); + if (status != SUCCESS) + { + return status; + } + if (packet) + { + *packet = this->packet->clone(this->packet); + } + return SUCCESS; +} + +/** + * Creates a (basic) clone of the given message + */ +static message_t *clone_message(private_message_t *this) +{ + message_t *message; + host_t *src, *dst; + + src = this->packet->get_source(this->packet); + dst = this->packet->get_destination(this->packet); + + message = message_create(this->major_version, this->minor_version); + message->set_ike_sa_id(message, this->ike_sa_id); + message->set_message_id(message, this->message_id); + message->set_request(message, this->is_request); + message->set_source(message, src->clone(src)); + message->set_destination(message, dst->clone(dst)); + message->set_exchange_type(message, this->exchange_type); + memcpy(((private_message_t*)message)->reserved, this->reserved, + sizeof(this->reserved)); + return message; +} + +/** + * Create a single fragment with the given data + */ +static message_t *create_fragment(private_message_t *this, payload_type_t next, + u_int16_t num, u_int16_t count, chunk_t data) +{ + enumerator_t *enumerator; + payload_t *fragment, *payload; + message_t *message; + peer_cfg_t *peer_cfg; + ike_sa_t *ike_sa; + + message = clone_message(this); + if (this->major_version == IKEV1_MAJOR_VERSION) + { + /* other implementations seem to just use 0 as message ID, so here we go */ + message->set_message_id(message, 0); + /* always use the initial message type for fragments, even for quick mode + * or transaction messages. */ + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa && (peer_cfg = ike_sa->get_peer_cfg(ike_sa)) && + peer_cfg->use_aggressive(peer_cfg)) + { + message->set_exchange_type(message, AGGRESSIVE); + } + else + { + message->set_exchange_type(message, ID_PROT); + } + fragment = (payload_t*)fragment_payload_create_from_data( + num, num == count, data); + } + else + { + fragment = (payload_t*)encrypted_fragment_payload_create_from_data( + num, count, data); + if (num == 1) + { + /* only in the first fragment is this set to the type of the first + * payload in the encrypted payload */ + fragment->set_next_type(fragment, next); + /* move unencrypted payloads to the first fragment */ + enumerator = this->payloads->create_enumerator(this->payloads); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) != PLV2_ENCRYPTED) + { + this->payloads->remove_at(this->payloads, enumerator); + message->add_payload(message, payload); + } + } + enumerator->destroy(enumerator); + } + } + message->add_payload(message, (payload_t*)fragment); + return message; +} + +/** + * Destroy all fragments + */ +static void clear_fragments(private_message_t *this) +{ + array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); + this->fragments = NULL; +} + +/** + * Reduce the fragment length but ensure it stays > 0 + */ +#define REDUCE_FRAG_LEN(fl, amount) ({ \ + fl = max(1, (ssize_t)fl - (amount)); \ +}) + +METHOD(message_t, fragment, status_t, + private_message_t *this, keymat_t *keymat, size_t frag_len, + enumerator_t **fragments) +{ + encrypted_payload_t *encrypted = NULL; + generator_t *generator = NULL; + message_t *fragment; + packet_t *packet; + payload_type_t next = PL_NONE; + u_int16_t num, count; + host_t *src, *dst; + chunk_t data; + status_t status; + u_int32_t *lenpos; + size_t len; + + src = this->packet->get_source(this->packet); + dst = this->packet->get_destination(this->packet); + if (!frag_len) + { + frag_len = (src->get_family(src) == AF_INET) ? 576 : 1280; + } + /* frag_len is the complete IP datagram length, account for overhead (we + * assume no IP options/extension headers are used) */ + REDUCE_FRAG_LEN(frag_len, (src->get_family(src) == AF_INET) ? 20 : 40); + /* 8 (UDP header) */ + REDUCE_FRAG_LEN(frag_len, 8); + if (dst->get_port(dst) != IKEV2_UDP_PORT && + src->get_port(src) != IKEV2_UDP_PORT) + { /* reduce length due to non-ESP marker */ + REDUCE_FRAG_LEN(frag_len, 4); + } + + if (is_encoded(this)) + { + if (this->major_version == IKEV2_MAJOR_VERSION) + { + encrypted = (encrypted_payload_t*)get_payload(this, PLV2_ENCRYPTED); + } + data = this->packet->get_data(this->packet); + len = data.len; + } + else + { + status = generate_message(this, keymat, &generator, &encrypted); + if (status != SUCCESS) + { + DESTROY_IF(generator); + return status; + } + data = generator->get_chunk(generator, &lenpos); + len = data.len + (encrypted ? encrypted->get_length(encrypted) : 0); + } + + /* check if we actually need to fragment the message and if we have an + * encrypted payload for IKEv2 */ + if (len <= frag_len || + (this->major_version == IKEV2_MAJOR_VERSION && !encrypted)) + { + if (generator) + { + status = finalize_message(this, keymat, generator, encrypted); + if (status != SUCCESS) + { + return status; + } + } + *fragments = enumerator_create_single(this->packet, NULL); + return SUCCESS; + } + + /* frag_len denoted the maximum IKE message size so far, later on it will + * denote the maximum content size of a fragment payload, therefore, + * account for IKE header */ + REDUCE_FRAG_LEN(frag_len, 28); + + if (this->major_version == IKEV1_MAJOR_VERSION) + { + if (generator) + { + status = finalize_message(this, keymat, generator, encrypted); + if (status != SUCCESS) + { + return status; + } + data = this->packet->get_data(this->packet); + generator = NULL; + } + /* overhead for the fragmentation payload header */ + REDUCE_FRAG_LEN(frag_len, 8); } + else + { + aead_t *aead; + + if (generator) + { + generator->destroy(generator); + generator = generator_create(); + } + else + { /* do not log again if it was generated previously */ + generator = generator_create_no_dbg(); + } + next = encrypted->payload_interface.get_next_type((payload_t*)encrypted); + encrypted->generate_payloads(encrypted, generator); + data = generator->get_chunk(generator, &lenpos); + if (!is_encoded(this)) + { + encrypted->destroy(encrypted); + } + aead = keymat->get_aead(keymat, FALSE); + /* overhead for the encrypted fragment payload */ + REDUCE_FRAG_LEN(frag_len, aead->get_iv_size(aead)); + REDUCE_FRAG_LEN(frag_len, aead->get_icv_size(aead)); + /* header */ + REDUCE_FRAG_LEN(frag_len, 8); + /* padding and padding length */ + frag_len = round_down(frag_len, aead->get_block_size(aead)); + REDUCE_FRAG_LEN(frag_len, 1); + /* TODO-FRAG: if there are unencrypted payloads, should we account for + * their length in the first fragment? we still would have to add + * an encrypted fragment payload (albeit empty), even so we couldn't + * prevent IP fragmentation in every case */ + } + + count = data.len / frag_len + (data.len % frag_len ? 1 : 0); + this->fragments = array_create(0, count); + DBG1(DBG_ENC, "splitting IKE message with length of %zu bytes into " + "%hu fragments", len, count); + for (num = 1; num <= count; num++) + { + len = min(data.len, frag_len); + fragment = create_fragment(this, next, num, count, + chunk_create(data.ptr, len)); + status = fragment->generate(fragment, keymat, &packet); + fragment->destroy(fragment); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "failed to generate IKE fragment"); + clear_fragments(this); + DESTROY_IF(generator); + return FAILED; + } + array_insert(this->fragments, ARRAY_TAIL, packet); + data = chunk_skip(data, len); + } + *fragments = array_create_enumerator(this->fragments); + DESTROY_IF(generator); + return SUCCESS; +} + +METHOD(message_t, get_packet, packet_t*, + private_message_t *this) +{ return this->packet->clone(this->packet); } METHOD(message_t, get_packet_data, chunk_t, private_message_t *this) { - if (this->packet == NULL) - { - return chunk_empty; - } return this->packet->get_data(this->packet); } +METHOD(message_t, get_fragments, enumerator_t*, + private_message_t *this) +{ + return array_create_enumerator(this->fragments); +} + METHOD(message_t, parse_header, status_t, private_message_t *this) { @@ -1682,6 +2082,10 @@ METHOD(message_t, parse_header, status_t, DBG2(DBG_ENC, "parsing header of message"); + if (!this->parser) + { /* reassembled IKEv2 message, header is inherited from fragments */ + return SUCCESS; + } this->parser->reset_context(this->parser); status = this->parser->parse_payload(this->parser, PL_HEADER, (payload_t**)&ike_header); @@ -1723,7 +2127,7 @@ METHOD(message_t, parse_header, status_t, this->first_payload = ike_header->payload_interface.get_next_type( &ike_header->payload_interface); if (this->first_payload == PLV1_FRAGMENT && this->is_encrypted) - { /* racoon sets the encryted bit when sending a fragment, but these + { /* racoon sets the encrypted bit when sending a fragment, but these * messages are really not encrypted */ this->is_encrypted = FALSE; } @@ -1780,9 +2184,9 @@ static status_t parse_payloads(private_message_t *this) status_t status; if (this->is_encrypted) - { /* wrap the whole encrypted IKEv1 message in a special encryption + { /* wrap the whole encrypted IKEv1 message in a special encrypted * payload which is then handled just like a regular payload */ - encryption_payload_t *encryption; + encrypted_payload_t *encryption; status = this->parser->parse_payload(this->parser, PLV1_ENCRYPTED, (payload_t**)&encryption); @@ -1824,9 +2228,9 @@ static status_t parse_payloads(private_message_t *this) payload_type_names, type); this->payloads->insert_last(this->payloads, payload); - /* an encrypted payload is the last one, so STOP here. decryption is - * done later */ - if (type == PLV2_ENCRYPTED) + /* an encrypted (fragment) payload MUST be the last one, so STOP here. + * decryption is done later */ + if (type == PLV2_ENCRYPTED || type == PLV2_FRAGMENT) { DBG2(DBG_ENC, "%N payload found, stop parsing", payload_type_names, type); @@ -1841,7 +2245,7 @@ static status_t parse_payloads(private_message_t *this) * Decrypt an encrypted payload and extract all contained payloads. */ static status_t decrypt_and_extract(private_message_t *this, keymat_t *keymat, - payload_t *previous, encryption_payload_t *encryption) + payload_t *previous, encrypted_payload_t *encryption) { payload_t *encrypted; payload_type_t type; @@ -1861,43 +2265,52 @@ static status_t decrypt_and_extract(private_message_t *this, keymat_t *keymat, DBG1(DBG_ENC, "found encrypted payload, but no transform set"); return INVALID_ARG; } - bs = aead->get_block_size(aead); - encryption->set_transform(encryption, aead); - chunk = this->packet->get_data(this->packet); - if (chunk.len < encryption->get_length(encryption) || - chunk.len < bs) + if (!this->parser) { - DBG1(DBG_ENC, "invalid payload length"); - return VERIFY_ERROR; + /* reassembled IKEv2 messages are already decrypted, we still call + * decrypt() to parse the contained payloads */ + status = encryption->decrypt(encryption, chunk_empty); } - if (keymat->get_version(keymat) == IKEV1) - { /* instead of associated data we provide the IV, we also update - * the IV with the last encrypted block */ - keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; - chunk_t iv; - - if (keymat_v1->get_iv(keymat_v1, this->message_id, &iv)) + else + { + bs = aead->get_block_size(aead); + encryption->set_transform(encryption, aead); + chunk = this->packet->get_data(this->packet); + if (chunk.len < encryption->get_length(encryption) || + chunk.len < bs) { - status = encryption->decrypt(encryption, iv); - if (status == SUCCESS) + DBG1(DBG_ENC, "invalid payload length"); + return VERIFY_ERROR; + } + if (keymat->get_version(keymat) == IKEV1) + { /* instead of associated data we provide the IV, we also update + * the IV with the last encrypted block */ + keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; + chunk_t iv; + + if (keymat_v1->get_iv(keymat_v1, this->message_id, &iv)) { - if (!keymat_v1->update_iv(keymat_v1, this->message_id, - chunk_create(chunk.ptr + chunk.len - bs, bs))) + status = encryption->decrypt(encryption, iv); + if (status == SUCCESS) { - status = FAILED; + if (!keymat_v1->update_iv(keymat_v1, this->message_id, + chunk_create(chunk.ptr + chunk.len - bs, bs))) + { + status = FAILED; + } } } + else + { + status = FAILED; + } } else { - status = FAILED; + chunk.len -= encryption->get_length(encryption); + status = encryption->decrypt(encryption, chunk); } } - else - { - chunk.len -= encryption->get_length(encryption); - status = encryption->decrypt(encryption, chunk); - } if (status != SUCCESS) { return status; @@ -1923,6 +2336,41 @@ static status_t decrypt_and_extract(private_message_t *this, keymat_t *keymat, } /** + * Decrypt an encrypted fragment payload. + */ +static status_t decrypt_fragment(private_message_t *this, keymat_t *keymat, + encrypted_fragment_payload_t *fragment) +{ + encrypted_payload_t *encrypted = (encrypted_payload_t*)fragment; + chunk_t chunk; + aead_t *aead; + size_t bs; + + if (!keymat) + { + DBG1(DBG_ENC, "found encrypted fragment payload, but no keymat"); + return INVALID_ARG; + } + aead = keymat->get_aead(keymat, TRUE); + if (!aead) + { + DBG1(DBG_ENC, "found encrypted fragment payload, but no transform set"); + return INVALID_ARG; + } + bs = aead->get_block_size(aead); + encrypted->set_transform(encrypted, aead); + chunk = this->packet->get_data(this->packet); + if (chunk.len < encrypted->get_length(encrypted) || + chunk.len < bs) + { + DBG1(DBG_ENC, "invalid payload length"); + return VERIFY_ERROR; + } + chunk.len -= encrypted->get_length(encrypted); + return encrypted->decrypt(encrypted, chunk); +} + +/** * Do we accept unencrypted ID/HASH payloads in Main Mode, as seen from * some SonicWall boxes? */ @@ -1941,7 +2389,7 @@ static bool accept_unencrypted_mm(private_message_t *this, payload_type_t type) } /** - * Decrypt payload from the encryption payload + * Decrypt payload from the encrypted payload */ static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) { @@ -1950,7 +2398,7 @@ static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) payload_rule_t *rule; payload_type_t type; status_t status = SUCCESS; - bool was_encrypted = FALSE; + char *was_encrypted = NULL; enumerator = this->payloads->create_enumerator(this->payloads); while (enumerator->enumerate(enumerator, &payload)) @@ -1959,20 +2407,24 @@ static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) DBG2(DBG_ENC, "process payload of type %N", payload_type_names, type); - if (type == PLV2_ENCRYPTED || type == PLV1_ENCRYPTED) + if (type == PLV2_ENCRYPTED || type == PLV1_ENCRYPTED || + type == PLV2_FRAGMENT) { - encryption_payload_t *encryption; - if (was_encrypted) { - DBG1(DBG_ENC, "encrypted payload can't contain other payloads " - "of type %N", payload_type_names, type); + DBG1(DBG_ENC, "%s can't contain other payloads of type %N", + was_encrypted, payload_type_names, type); status = VERIFY_ERROR; break; } + } + + if (type == PLV2_ENCRYPTED || type == PLV1_ENCRYPTED) + { + encrypted_payload_t *encryption; DBG2(DBG_ENC, "found an encrypted payload"); - encryption = (encryption_payload_t*)payload; + encryption = (encrypted_payload_t*)payload; this->payloads->remove_at(this->payloads, enumerator); if (enumerator->enumerate(enumerator, NULL)) @@ -1988,7 +2440,27 @@ static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) { break; } - was_encrypted = TRUE; + was_encrypted = "encrypted payload"; + } + else if (type == PLV2_FRAGMENT) + { + encrypted_fragment_payload_t *fragment; + + DBG2(DBG_ENC, "found an encrypted fragment payload"); + fragment = (encrypted_fragment_payload_t*)payload; + + if (enumerator->enumerate(enumerator, NULL)) + { + DBG1(DBG_ENC, "encrypted fragment payload is not last payload"); + status = VERIFY_ERROR; + break; + } + status = decrypt_fragment(this, keymat, fragment); + if (status != SUCCESS) + { + break; + } + was_encrypted = "encrypted fragment payload"; } if (payload_is_known(type) && !was_encrypted && @@ -2085,10 +2557,15 @@ METHOD(message_t, parse_body, status_t, return NOT_SUPPORTED; } - status = parse_payloads(this); - if (status != SUCCESS) - { /* error is already logged */ - return status; + /* reassembled IKEv2 messages are already parsed (except for the payloads + * contained in the encrypted payload, which are handled below) */ + if (this->parser) + { + status = parse_payloads(this); + if (status != SUCCESS) + { /* error is already logged */ + return status; + } } status = decrypt_payloads(this, keymat); @@ -2142,7 +2619,7 @@ METHOD(message_t, parse_body, status_t, } chunk_free(&hash); } - if (this->is_encrypted) + if (this->is_encrypted && this->exchange_type != INFORMATIONAL_V1) { /* message verified, confirm IV */ if (!keymat_v1->confirm_iv(keymat_v1, this->message_id)) { @@ -2153,13 +2630,234 @@ METHOD(message_t, parse_body, status_t, return SUCCESS; } +/** + * Store the fragment data for the fragment with the given fragment number. + */ +static status_t add_fragment(private_message_t *this, u_int16_t num, + chunk_t data) +{ + fragment_t *fragment; + int i, insert_at = -1; + + for (i = 0; i < array_count(this->fragments); i++) + { + array_get(this->fragments, i, &fragment); + if (fragment->num == num) + { + /* ignore a duplicate fragment */ + DBG1(DBG_ENC, "received duplicate fragment #%hu", num); + return NEED_MORE; + } + if (fragment->num > num) + { + insert_at = i; + break; + } + } + this->frag->len += data.len; + if (this->frag->len > this->frag->max_packet) + { + DBG1(DBG_ENC, "fragmented IKE message is too large"); + reset_defrag(this); + return FAILED; + } + INIT(fragment, + .num = num, + .data = chunk_clone(data), + ); + array_insert(this->fragments, insert_at, fragment); + return SUCCESS; +} + +/** + * Merge the cached fragment data and resets the defragmentation state. + * Also updates the IP addresses to those of the last received fragment. + */ +static chunk_t merge_fragments(private_message_t *this, message_t *last) +{ + fragment_t *fragment; + bio_writer_t *writer; + host_t *src, *dst; + chunk_t data; + int i; + + writer = bio_writer_create(this->frag->len); + for (i = 0; i < array_count(this->fragments); i++) + { + array_get(this->fragments, i, &fragment); + writer->write_data(writer, fragment->data); + } + data = writer->extract_buf(writer); + writer->destroy(writer); + + /* set addresses to those of the last fragment we received */ + src = last->get_source(last); + dst = last->get_destination(last); + this->packet->set_source(this->packet, src->clone(src)); + this->packet->set_destination(this->packet, dst->clone(dst)); + + reset_defrag(this); + free(this->frag); + this->frag = NULL; + return data; +} + +METHOD(message_t, add_fragment_v1, status_t, + private_message_t *this, message_t *message) +{ + fragment_payload_t *payload; + chunk_t data; + u_int8_t num; + status_t status; + + if (!this->frag) + { + return INVALID_STATE; + } + payload = (fragment_payload_t*)message->get_payload(message, PLV1_FRAGMENT); + if (!payload) + { + return INVALID_ARG; + } + if (!this->fragments || this->message_id != payload->get_id(payload)) + { + reset_defrag(this); + this->message_id = payload->get_id(payload); + /* we don't know the total number of fragments, assume something */ + this->fragments = array_create(0, 4); + } + + num = payload->get_number(payload); + data = payload->get_data(payload); + if (!this->frag->last && payload->is_last(payload)) + { + this->frag->last = num; + } + status = add_fragment(this, num, data); + if (status != SUCCESS) + { + return status; + } + + if (array_count(this->fragments) != this->frag->last) + { + /* there are some fragments missing */ + DBG1(DBG_ENC, "received fragment #%hhu, waiting for complete IKE " + "message", num); + return NEED_MORE; + } + + DBG1(DBG_ENC, "received fragment #%hhu, reassembling fragmented IKE " + "message", num); + + data = merge_fragments(this, message); + this->packet->set_data(this->packet, data); + this->parser = parser_create(data); + + if (parse_header(this) != SUCCESS) + { + DBG1(DBG_IKE, "failed to parse header of reassembled IKE message"); + return FAILED; + } + return SUCCESS; +} + +METHOD(message_t, add_fragment_v2, status_t, + private_message_t *this, message_t *message) +{ + encrypted_fragment_payload_t *encrypted_fragment; + encrypted_payload_t *encrypted; + payload_t *payload; + enumerator_t *enumerator; + chunk_t data; + u_int16_t total, num; + status_t status; + + if (!this->frag) + { + return INVALID_STATE; + } + payload = message->get_payload(message, PLV2_FRAGMENT); + if (!payload || this->message_id != message->get_message_id(message)) + { + return INVALID_ARG; + } + encrypted_fragment = (encrypted_fragment_payload_t*)payload; + total = encrypted_fragment->get_total_fragments(encrypted_fragment); + if (total > MAX_FRAGMENTS) + { + DBG1(DBG_IKE, "maximum fragment count exceeded"); + reset_defrag(this); + return FAILED; + } + if (!this->fragments || total > this->frag->last) + { + reset_defrag(this); + this->frag->last = total; + this->fragments = array_create(0, total); + } + num = encrypted_fragment->get_fragment_number(encrypted_fragment); + data = encrypted_fragment->get_content(encrypted_fragment); + status = add_fragment(this, num, data); + if (status != SUCCESS) + { + return status; + } + + if (num == 1) + { + /* the first fragment denotes the payload type of the first payload in + * the original encrypted payload, cache that */ + this->first_payload = payload->get_next_type(payload); + /* move all unencrypted payloads contained in the first fragment */ + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) != PLV2_FRAGMENT) + { + message->remove_payload_at(message, enumerator); + this->payloads->insert_last(this->payloads, payload); + } + } + enumerator->destroy(enumerator); + } + + if (array_count(this->fragments) != total) + { + /* there are some fragments missing */ + DBG1(DBG_ENC, "received fragment #%hu of %hu, waiting for complete IKE " + "message", num, total); + return NEED_MORE; + } + + DBG1(DBG_ENC, "received fragment #%hu of %hu, reassembling fragmented IKE " + "message", num, total); + + data = merge_fragments(this, message); + encrypted = encrypted_payload_create_from_plain(this->first_payload, data); + this->payloads->insert_last(this->payloads, encrypted); + /* update next payload type (could be an unencrypted payload) */ + this->payloads->get_first(this->payloads, (void**)&payload); + this->first_payload = payload->get_type(payload); + return SUCCESS; +} + METHOD(message_t, destroy, void, private_message_t *this) { DESTROY_IF(this->ike_sa_id); + DESTROY_IF(this->parser); this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); this->packet->destroy(this->packet); - this->parser->destroy(this->parser); + if (this->frag) + { + reset_defrag(this); + free(this->frag); + } + else + { + array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); + } free(this); } @@ -2195,6 +2893,9 @@ message_t *message_create_from_packet(packet_t *packet) .disable_sort = _disable_sort, .generate = _generate, .is_encoded = _is_encoded, + .is_fragmented = _is_fragmented, + .fragment = _fragment, + .add_fragment = _add_fragment_v2, .set_source = _set_source, .get_source = _get_source, .set_destination = _set_destination, @@ -2207,6 +2908,7 @@ message_t *message_create_from_packet(packet_t *packet) .parse_body = _parse_body, .get_packet = _get_packet, .get_packet_data = _get_packet_data, + .get_fragments = _get_fragments, .destroy = _destroy, }, .exchange_type = EXCHANGE_TYPE_UNDEFINED, @@ -2232,3 +2934,34 @@ message_t *message_create(int major, int minor) return this; } + +/* + * Described in header. + */ +message_t *message_create_defrag(message_t *fragment) +{ + private_message_t *this; + + if (!fragment->get_payload(fragment, PLV1_FRAGMENT) && + !fragment->get_payload(fragment, PLV2_FRAGMENT)) + { + return NULL; + } + this = (private_message_t*)clone_message((private_message_t*)fragment); + /* we don't need a parser for IKEv2, the one for IKEv1 is created after + * reassembling the original message */ + this->parser->destroy(this->parser); + this->parser = NULL; + if (fragment->get_major_version(fragment) == IKEV1_MAJOR_VERSION) + { + /* we store the fragment ID in the message ID field, which should be + * zero for fragments, but make sure */ + this->message_id = 0; + this->public.add_fragment = _add_fragment_v1; + } + INIT(this->frag, + .max_packet = lib->settings->get_int(lib->settings, + "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), + ); + return &this->public; +} diff --git a/src/libcharon/encoding/message.h b/src/libcharon/encoding/message.h index 7631a7c3a..a03aa8e96 100644 --- a/src/libcharon/encoding/message.h +++ b/src/libcharon/encoding/message.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Tobias Brunner + * Copyright (C) 2006-2014 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter @@ -39,7 +39,7 @@ typedef struct message_t message_t; * * The message handles parsing and generation of payloads * via parser_t/generator_t. Encryption is done transparently - * via the encryption_payload_t. A set of rules for messages + * via the encrypted_payload_t. A set of rules for messages * and payloads does check parsed messages. */ struct message_t { @@ -265,6 +265,53 @@ struct message_t { bool (*is_encoded)(message_t *this); /** + * Generates the message split into fragments of the given size (total IP + * datagram length). + * + * @param keymat keymat to encrypt/sign message(s) + * @param frag_len fragment length (maximum total IP datagram length), 0 + * for default value depending on address family + * @param fragments receives an enumerator with generated packet_t*, + * which are owned by the enumerator + * @return + * - SUCCESS if message could be fragmented + * - FAILED if fragmentation failed + * - and the possible return values of generate() + */ + status_t (*fragment)(message_t *this, keymat_t *keymat, size_t frag_len, + enumerator_t **fragments); + + /** + * Check if the message has been encoded and fragmented using fragment(), + * and whether there actually resulted fragments (if not is_encoded() will + * be TRUE). + * + * The packets of individual fragments can be retrieved with + * get_fragments(). + * + * @return TRUE if message has been encoded and fragmented + */ + bool (*is_fragmented)(message_t *this); + + /** + * Add a fragment to the message if it was created with + * message_create_defrag(). + * + * Once the message is completed it should be processed like any other + * inbound message. + * + * @param fragment fragment to add + * @return + * - SUCCESS if message was reassembled + * - NEED_MORE if not all fragments have yet been received + * - FAILED if reassembling failed + * - INVALID_ARG if fragment is invalid for some reason + * - INVALID_STATE if message was not created using + * message_create_defrag() + */ + status_t (*add_fragment)(message_t *this, message_t *fragment); + + /** * Gets the source host informations. * * @warning Returned host_t object is not getting cloned, @@ -337,11 +384,11 @@ struct message_t { notify_payload_t* (*get_notify)(message_t *this, notify_type_t type); /** - * Returns a clone of the internal stored packet_t object. + * Returns a clone of the internally stored packet_t object. * * @return packet_t object as clone of internal one */ - packet_t * (*get_packet) (message_t *this); + packet_t *(*get_packet) (message_t *this); /** * Returns a chunk pointing to internal packet_t data. @@ -351,6 +398,13 @@ struct message_t { chunk_t (*get_packet_data) (message_t *this); /** + * Returns internally stored packet_t* objects for each fragment. + * + * @return enumerator internal packet_t* objects + */ + enumerator_t *(*get_fragments)(message_t *this); + + /** * Destroys a message and all including objects. */ void (*destroy) (message_t *this); @@ -380,4 +434,14 @@ message_t *message_create_from_packet(packet_t *packet); */ message_t *message_create(int major, int minor); +/** + * Creates a message_t object that is used to reassemble fragmented messages. + * + * Use add_fragment() to add fragments. + * + * @param fragment initial fragment (is not added) + * @return message_t object, NULL if fragment is not actually one + */ +message_t *message_create_defrag(message_t *fragment); + #endif /** MESSAGE_H_ @}*/ diff --git a/src/libcharon/encoding/parser.c b/src/libcharon/encoding/parser.c index c33e30dd3..d6240fde2 100644 --- a/src/libcharon/encoding/parser.c +++ b/src/libcharon/encoding/parser.c @@ -32,7 +32,7 @@ #include <encoding/payloads/nonce_payload.h> #include <encoding/payloads/id_payload.h> #include <encoding/payloads/notify_payload.h> -#include <encoding/payloads/encryption_payload.h> +#include <encoding/payloads/encrypted_payload.h> #include <encoding/payloads/auth_payload.h> #include <encoding/payloads/cert_payload.h> #include <encoding/payloads/certreq_payload.h> diff --git a/src/libcharon/encoding/payloads/encrypted_fragment_payload.h b/src/libcharon/encoding/payloads/encrypted_fragment_payload.h new file mode 100644 index 000000000..1c2cc379f --- /dev/null +++ b/src/libcharon/encoding/payloads/encrypted_fragment_payload.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 Tobias Brunner + * 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. + */ + +/** + * @defgroup encrypted_fragment_payload encrypted_fragment_payload + * @{ @ingroup payloads + */ + +#ifndef ENCRYPTED_FRAGMENT_PAYLOAD_H_ +#define ENCRYPTED_FRAGMENT_PAYLOAD_H_ + +typedef struct encrypted_fragment_payload_t encrypted_fragment_payload_t; + +#include <encoding/payloads/encrypted_payload.h> + +/** + * The Encrypted Fragment Payload as described in RFC 7383 + * + * The implementation is located in encrypted_payload.c as it is very similar. + */ +struct encrypted_fragment_payload_t { + + /** + * Implements payload_t interface. + */ + encrypted_payload_t encrypted; + + /** + * Get the fragment number. + * + * @return fragment number + */ + u_int16_t (*get_fragment_number)(encrypted_fragment_payload_t *this); + + /** + * Get the total number of fragments. + * + * @return total number of fragments + */ + u_int16_t (*get_total_fragments)(encrypted_fragment_payload_t *this); + + /** + * Get the (decrypted) content of this payload. + * + * @return internal payload data + */ + chunk_t (*get_content)(encrypted_fragment_payload_t *this); + + /** + * Destroys an encrypted_fragment_payload_t object. + */ + void (*destroy)(encrypted_fragment_payload_t *this); +}; + +/** + * Creates an empty encrypted_fragment_payload_t object. + * + * @return encrypted_fragment_payload_t object + */ +encrypted_fragment_payload_t *encrypted_fragment_payload_create(); + +/** + * Creates an encrypted fragment payload from the given data. + * + * @param num fragment number (first one should be 1) + * @param total total number of fragments + * @param data fragment data (gets cloned) + * @return encrypted_fragment_payload_t object + */ +encrypted_fragment_payload_t *encrypted_fragment_payload_create_from_data( + u_int16_t num, u_int16_t total, chunk_t data); + +#endif /** ENCRYPTED_FRAGMENT_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/encoding/payloads/encrypted_payload.c b/src/libcharon/encoding/payloads/encrypted_payload.c new file mode 100644 index 000000000..5c574c34d --- /dev/null +++ b/src/libcharon/encoding/payloads/encrypted_payload.c @@ -0,0 +1,1022 @@ +/* + * Copyright (C) 2011-2014 Tobias Brunner + * Copyright (C) 2005-2010 Martin Willi + * Copyright (C) 2010 revosec AG + * Copyright (C) 2005 Jan Hutter + * 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. + */ + +#include <stddef.h> +#include <string.h> + +#include "encrypted_payload.h" +#include "encrypted_fragment_payload.h" + +#include <daemon.h> +#include <encoding/payloads/encodings.h> +#include <collections/linked_list.h> +#include <encoding/parser.h> + +typedef struct private_encrypted_payload_t private_encrypted_payload_t; +typedef struct private_encrypted_fragment_payload_t private_encrypted_fragment_payload_t; + +struct private_encrypted_payload_t { + + /** + * Public encrypted_payload_t interface. + */ + encrypted_payload_t public; + + /** + * There is no next payload for an encrypted payload, + * since encrypted payload MUST be the last one. + * next_payload means here the first payload of the + * contained, encrypted payload. + */ + u_int8_t next_payload; + + /** + * Flags, including reserved bits + */ + u_int8_t flags; + + /** + * Length of this payload + */ + u_int16_t payload_length; + + /** + * Chunk containing the IV, plain, padding and ICV. + */ + chunk_t encrypted; + + /** + * AEAD transform to use + */ + aead_t *aead; + + /** + * Contained payloads + */ + linked_list_t *payloads; + + /** + * Type of payload, PLV2_ENCRYPTED or PLV1_ENCRYPTED + */ + payload_type_t type; +}; + +struct private_encrypted_fragment_payload_t { + + /** + * Public interface. + */ + encrypted_fragment_payload_t public; + + /** + * The first fragment contains the type of the first payload contained in + * the original encrypted payload, for all other fragments it MUST be set + * to zero. + */ + u_int8_t next_payload; + + /** + * Flags, including reserved bits + */ + u_int8_t flags; + + /** + * Length of this payload + */ + u_int16_t payload_length; + + /** + * Chunk containing the IV, plain, padding and ICV. + */ + chunk_t encrypted; + + /** + * Fragment number + */ + u_int16_t fragment_number; + + /** + * Total fragments + */ + u_int16_t total_fragments; + + /** + * AEAD transform to use + */ + aead_t *aead; + + /** + * Chunk containing the plain packet data. + */ + chunk_t plain; +}; + +/** + * Encoding rules to parse or generate a IKEv2-Encrypted Payload. + * + * The defined offsets are the positions in a object of type + * private_encrypted_payload_t. + */ +static encoding_rule_t encodings_v2[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_encrypted_payload_t, next_payload) }, + /* Critical and 7 reserved bits, all stored for reconstruction */ + { U_INT_8, offsetof(private_encrypted_payload_t, flags) }, + /* Length of the whole encrypted payload*/ + { PAYLOAD_LENGTH, offsetof(private_encrypted_payload_t, payload_length) }, + /* encrypted data, stored in a chunk. contains iv, data, padding */ + { CHUNK_DATA, offsetof(private_encrypted_payload_t, encrypted) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Initialization Vector ! + ! (length is block size for encryption algorithm) ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Encrypted IKE Payloads ! + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! Padding (0-255 octets) ! + +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + ! ! Pad Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ Integrity Checksum Data ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Encoding rules to parse or generate a complete encrypted IKEv1 message. + * + * The defined offsets are the positions in a object of type + * private_encrypted_payload_t. + */ +static encoding_rule_t encodings_v1[] = { + /* encrypted data, stored in a chunk */ + { ENCRYPTED_DATA, offsetof(private_encrypted_payload_t, encrypted) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Encrypted IKE Payloads ! + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! Padding (0-255 octets) ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/** + * Encoding rules to parse or generate an IKEv2-Encrypted Fragment Payload. + * + * The defined offsets are the positions in a object of type + * private_encrypted_payload_t. + */ +static encoding_rule_t encodings_fragment[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_encrypted_fragment_payload_t, next_payload) }, + /* Critical and 7 reserved bits, all stored for reconstruction */ + { U_INT_8, offsetof(private_encrypted_fragment_payload_t, flags) }, + /* Length of the whole encryption payload*/ + { PAYLOAD_LENGTH, offsetof(private_encrypted_fragment_payload_t, payload_length) }, + /* Fragment number */ + { U_INT_16, offsetof(private_encrypted_fragment_payload_t, fragment_number) }, + /* Total number of fragments */ + { U_INT_16, offsetof(private_encrypted_fragment_payload_t, total_fragments) }, + /* encrypted data, stored in a chunk. contains iv, data, padding */ + { CHUNK_DATA, offsetof(private_encrypted_fragment_payload_t, encrypted) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload !C! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Fragment Number | Total Fragments ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Initialization Vector ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Encrypted IKE Payloads ! + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! Padding (0-255 octets) ! + +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ + ! ! Pad Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ~ Integrity Checksum Data ~ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +METHOD(payload_t, verify, status_t, + private_encrypted_payload_t *this) +{ + return SUCCESS; +} + +METHOD(payload_t, get_encoding_rules, int, + private_encrypted_payload_t *this, encoding_rule_t **rules) +{ + if (this->type == PLV2_ENCRYPTED) + { + *rules = encodings_v2; + return countof(encodings_v2); + } + *rules = encodings_v1; + return countof(encodings_v1); +} + +METHOD(payload_t, get_header_length, int, + private_encrypted_payload_t *this) +{ + if (this->type == PLV2_ENCRYPTED) + { + return 4; + } + return 0; +} + +METHOD(payload_t, get_type, payload_type_t, + private_encrypted_payload_t *this) +{ + return this->type; +} + +METHOD(payload_t, get_next_type, payload_type_t, + private_encrypted_payload_t *this) +{ + return this->next_payload; +} + +METHOD(payload_t, set_next_type, void, + private_encrypted_payload_t *this, payload_type_t type) +{ + /* the next payload is set during add, still allow this for IKEv1 */ + this->next_payload = type; +} + +/** + * Get length of encryption/integrity overhead for the given plaintext length + */ +static size_t compute_overhead(aead_t *aead, size_t len) +{ + size_t bs, overhead; + + /* padding */ + bs = aead->get_block_size(aead); + overhead = bs - (len % bs); + /* add iv */ + overhead += aead->get_iv_size(aead); + /* add icv */ + overhead += aead->get_icv_size(aead); + return overhead; +} + +/** + * Compute the length of the whole payload + */ +static void compute_length(private_encrypted_payload_t *this) +{ + enumerator_t *enumerator; + payload_t *payload; + size_t length = 0; + + if (this->encrypted.len) + { + length = this->encrypted.len; + } + else + { + enumerator = this->payloads->create_enumerator(this->payloads); + while (enumerator->enumerate(enumerator, &payload)) + { + length += payload->get_length(payload); + } + enumerator->destroy(enumerator); + + if (this->aead) + { + length += compute_overhead(this->aead, length); + } + } + length += get_header_length(this); + this->payload_length = length; +} + +METHOD2(payload_t, encrypted_payload_t, get_length, size_t, + private_encrypted_payload_t *this) +{ + compute_length(this); + return this->payload_length; +} + +METHOD(encrypted_payload_t, add_payload, void, + private_encrypted_payload_t *this, payload_t *payload) +{ + payload_t *last_payload; + + if (this->payloads->get_count(this->payloads) > 0) + { + this->payloads->get_last(this->payloads, (void **)&last_payload); + last_payload->set_next_type(last_payload, payload->get_type(payload)); + } + else + { + this->next_payload = payload->get_type(payload); + } + payload->set_next_type(payload, PL_NONE); + this->payloads->insert_last(this->payloads, payload); + compute_length(this); +} + +METHOD(encrypted_payload_t, remove_payload, payload_t *, + private_encrypted_payload_t *this) +{ + payload_t *payload; + + if (this->payloads->remove_first(this->payloads, + (void**)&payload) == SUCCESS) + { + return payload; + } + return NULL; +} + +/** + * Generate payload before encryption + */ +static chunk_t generate(private_encrypted_payload_t *this, + generator_t *generator) +{ + payload_t *current, *next; + enumerator_t *enumerator; + u_int32_t *lenpos; + chunk_t chunk = chunk_empty; + + enumerator = this->payloads->create_enumerator(this->payloads); + if (enumerator->enumerate(enumerator, ¤t)) + { + this->next_payload = current->get_type(current); + + while (enumerator->enumerate(enumerator, &next)) + { + current->set_next_type(current, next->get_type(next)); + generator->generate_payload(generator, current); + current = next; + } + current->set_next_type(current, PL_NONE); + generator->generate_payload(generator, current); + + chunk = generator->get_chunk(generator, &lenpos); + DBG2(DBG_ENC, "generated content in encrypted payload"); + } + enumerator->destroy(enumerator); + return chunk; +} + +METHOD(encrypted_payload_t, generate_payloads, void, + private_encrypted_payload_t *this, generator_t *generator) +{ + generate(this, generator); +} + +/** + * Append the encrypted payload header to the associated data + */ +static chunk_t append_header(private_encrypted_payload_t *this, chunk_t assoc) +{ + struct { + u_int8_t next_payload; + u_int8_t flags; + u_int16_t length; + } __attribute__((packed)) header = { + .next_payload = this->next_payload, + .flags = this->flags, + .length = htons(get_length(this)), + }; + return chunk_cat("cc", assoc, chunk_from_thing(header)); +} + +/** + * Encrypts the data in plain and returns it in an allocated chunk. + */ +static status_t encrypt_content(char *label, aead_t *aead, u_int64_t mid, + chunk_t plain, chunk_t assoc, chunk_t *encrypted) +{ + chunk_t iv, padding, icv, crypt; + iv_gen_t *iv_gen; + rng_t *rng; + size_t bs; + + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_ENC, "encrypting %s failed, no RNG found", label); + return NOT_SUPPORTED; + } + + iv_gen = aead->get_iv_gen(aead); + if (!iv_gen) + { + DBG1(DBG_ENC, "encrypting %s failed, no IV generator", label); + return NOT_SUPPORTED; + } + + bs = aead->get_block_size(aead); + /* we need at least one byte padding to store the padding length */ + padding.len = bs - (plain.len % bs); + iv.len = aead->get_iv_size(aead); + icv.len = aead->get_icv_size(aead); + + /* prepare data to authenticate-encrypt: + * | IV | plain | padding | ICV | + * \____crypt______/ ^ + * | / + * v / + * assoc -> + ------->/ + */ + *encrypted = chunk_alloc(iv.len + plain.len + padding.len + icv.len); + iv.ptr = encrypted->ptr; + memcpy(iv.ptr + iv.len, plain.ptr, plain.len); + plain.ptr = iv.ptr + iv.len; + padding.ptr = plain.ptr + plain.len; + icv.ptr = padding.ptr + padding.len; + crypt = chunk_create(plain.ptr, plain.len + padding.len); + + if (!iv_gen->get_iv(iv_gen, mid, iv.len, iv.ptr) || + !rng->get_bytes(rng, padding.len - 1, padding.ptr)) + { + DBG1(DBG_ENC, "encrypting %s failed, no IV or padding", label); + rng->destroy(rng); + + return FAILED; + } + padding.ptr[padding.len - 1] = padding.len - 1; + rng->destroy(rng); + + DBG3(DBG_ENC, "%s encryption:", label); + DBG3(DBG_ENC, "IV %B", &iv); + DBG3(DBG_ENC, "plain %B", &plain); + DBG3(DBG_ENC, "padding %B", &padding); + DBG3(DBG_ENC, "assoc %B", &assoc); + + if (!aead->encrypt(aead, crypt, assoc, iv, NULL)) + { + return FAILED; + } + DBG3(DBG_ENC, "encrypted %B", &crypt); + DBG3(DBG_ENC, "ICV %B", &icv); + return SUCCESS; +} + +METHOD(encrypted_payload_t, encrypt, status_t, + private_encrypted_payload_t *this, u_int64_t mid, chunk_t assoc) +{ + generator_t *generator; + chunk_t plain; + status_t status; + + if (this->aead == NULL) + { + DBG1(DBG_ENC, "encrypting encrypted payload failed, transform missing"); + return INVALID_STATE; + } + + free(this->encrypted.ptr); + generator = generator_create(); + plain = generate(this, generator); + assoc = append_header(this, assoc); + status = encrypt_content("encrypted payload", this->aead, mid, plain, assoc, + &this->encrypted); + generator->destroy(generator); + free(assoc.ptr); + return status; +} + +METHOD(encrypted_payload_t, encrypt_v1, status_t, + private_encrypted_payload_t *this, u_int64_t mid, chunk_t iv) +{ + generator_t *generator; + chunk_t plain, padding; + size_t bs; + + if (this->aead == NULL) + { + DBG1(DBG_ENC, "encryption failed, transform missing"); + return INVALID_STATE; + } + + generator = generator_create(); + plain = generate(this, generator); + bs = this->aead->get_block_size(this->aead); + padding.len = bs - (plain.len % bs); + + /* prepare data to encrypt: + * | plain | padding | */ + free(this->encrypted.ptr); + this->encrypted = chunk_alloc(plain.len + padding.len); + memcpy(this->encrypted.ptr, plain.ptr, plain.len); + plain.ptr = this->encrypted.ptr; + padding.ptr = plain.ptr + plain.len; + memset(padding.ptr, 0, padding.len); + generator->destroy(generator); + + DBG3(DBG_ENC, "encrypting payloads:"); + DBG3(DBG_ENC, "IV %B", &iv); + DBG3(DBG_ENC, "plain %B", &plain); + DBG3(DBG_ENC, "padding %B", &padding); + + if (!this->aead->encrypt(this->aead, this->encrypted, chunk_empty, iv, NULL)) + { + return FAILED; + } + + DBG3(DBG_ENC, "encrypted %B", &this->encrypted); + + return SUCCESS; +} + +/** + * Parse the payloads after decryption. + */ +static status_t parse(private_encrypted_payload_t *this, chunk_t plain) +{ + parser_t *parser; + payload_type_t type; + + parser = parser_create(plain); + type = this->next_payload; + while (type != PL_NONE) + { + payload_t *payload; + + if (plain.len < 4 || untoh16(plain.ptr + 2) > plain.len) + { + DBG1(DBG_ENC, "invalid %N payload length, decryption failed?", + payload_type_names, type); + parser->destroy(parser); + return PARSE_ERROR; + } + if (parser->parse_payload(parser, type, &payload) != SUCCESS) + { + parser->destroy(parser); + return PARSE_ERROR; + } + if (payload->verify(payload) != SUCCESS) + { + DBG1(DBG_ENC, "%N verification failed", + payload_type_names, payload->get_type(payload)); + payload->destroy(payload); + parser->destroy(parser); + return VERIFY_ERROR; + } + type = payload->get_next_type(payload); + this->payloads->insert_last(this->payloads, payload); + } + parser->destroy(parser); + DBG2(DBG_ENC, "parsed content of encrypted payload"); + return SUCCESS; +} + +/** + * Decrypts the given data in-place and returns a chunk pointing to the + * resulting plaintext. + */ +static status_t decrypt_content(char *label, aead_t *aead, chunk_t encrypted, + chunk_t assoc, chunk_t *plain) +{ + chunk_t iv, padding, icv, crypt; + size_t bs; + + /* prepare data to authenticate-decrypt: + * | IV | plain | padding | ICV | + * \____crypt______/ ^ + * | / + * v / + * assoc -> + ------->/ + */ + bs = aead->get_block_size(aead); + iv.len = aead->get_iv_size(aead); + iv.ptr = encrypted.ptr; + icv.len = aead->get_icv_size(aead); + icv.ptr = encrypted.ptr + encrypted.len - icv.len; + crypt.ptr = iv.ptr + iv.len; + crypt.len = encrypted.len - iv.len; + + if (iv.len + icv.len > encrypted.len || + (crypt.len - icv.len) % bs) + { + DBG1(DBG_ENC, "decrypting %s payload failed, invalid length", label); + return FAILED; + } + + DBG3(DBG_ENC, "%s decryption:", label); + DBG3(DBG_ENC, "IV %B", &iv); + DBG3(DBG_ENC, "encrypted %B", &crypt); + DBG3(DBG_ENC, "ICV %B", &icv); + DBG3(DBG_ENC, "assoc %B", &assoc); + + if (!aead->decrypt(aead, crypt, assoc, iv, NULL)) + { + DBG1(DBG_ENC, "verifying %s integrity failed", label); + return FAILED; + } + + *plain = chunk_create(crypt.ptr, crypt.len - icv.len); + padding.len = plain->ptr[plain->len - 1] + 1; + if (padding.len > plain->len) + { + DBG1(DBG_ENC, "decrypting %s failed, padding invalid %B", label, + &crypt); + return PARSE_ERROR; + } + plain->len -= padding.len; + padding.ptr = plain->ptr + plain->len; + + DBG3(DBG_ENC, "plain %B", plain); + DBG3(DBG_ENC, "padding %B", &padding); + return SUCCESS; +} + +METHOD(encrypted_payload_t, decrypt, status_t, + private_encrypted_payload_t *this, chunk_t assoc) +{ + chunk_t plain; + status_t status; + + if (this->aead == NULL) + { + DBG1(DBG_ENC, "decrypting encrypted payload failed, transform missing"); + return INVALID_STATE; + } + + assoc = append_header(this, assoc); + status = decrypt_content("encrypted payload", this->aead, this->encrypted, + assoc, &plain); + free(assoc.ptr); + + if (status != SUCCESS) + { + return status; + } + return parse(this, plain); +} + +METHOD(encrypted_payload_t, decrypt_plain, status_t, + private_encrypted_payload_t *this, chunk_t assoc) +{ + if (!this->encrypted.ptr) + { + return FAILED; + } + return parse(this, this->encrypted); +} + +METHOD(encrypted_payload_t, decrypt_v1, status_t, + private_encrypted_payload_t *this, chunk_t iv) +{ + if (this->aead == NULL) + { + DBG1(DBG_ENC, "decryption failed, transform missing"); + return INVALID_STATE; + } + + /* data must be a multiple of block size */ + if (iv.len != this->aead->get_block_size(this->aead) || + this->encrypted.len < iv.len || this->encrypted.len % iv.len) + { + DBG1(DBG_ENC, "decryption failed, invalid length"); + return FAILED; + } + + DBG3(DBG_ENC, "decrypting payloads:"); + DBG3(DBG_ENC, "encrypted %B", &this->encrypted); + + if (!this->aead->decrypt(this->aead, this->encrypted, chunk_empty, iv, NULL)) + { + return FAILED; + } + + DBG3(DBG_ENC, "plain %B", &this->encrypted); + + return parse(this, this->encrypted); +} + +METHOD(encrypted_payload_t, set_transform, void, + private_encrypted_payload_t *this, aead_t* aead) +{ + this->aead = aead; +} + +METHOD2(payload_t, encrypted_payload_t, destroy, void, + private_encrypted_payload_t *this) +{ + this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); + free(this->encrypted.ptr); + free(this); +} + +/* + * Described in header + */ +encrypted_payload_t *encrypted_payload_create(payload_type_t type) +{ + private_encrypted_payload_t *this; + + INIT(this, + .public = { + .payload_interface = { + .verify = _verify, + .get_encoding_rules = _get_encoding_rules, + .get_header_length = _get_header_length, + .get_length = _get_length, + .get_next_type = _get_next_type, + .set_next_type = _set_next_type, + .get_type = _get_type, + .destroy = _destroy, + }, + .get_length = _get_length, + .add_payload = _add_payload, + .remove_payload = _remove_payload, + .generate_payloads = _generate_payloads, + .set_transform = _set_transform, + .encrypt = _encrypt, + .decrypt = _decrypt, + .destroy = _destroy, + }, + .next_payload = PL_NONE, + .payloads = linked_list_create(), + .type = type, + ); + this->payload_length = get_header_length(this); + + if (type == PLV1_ENCRYPTED) + { + this->public.encrypt = _encrypt_v1; + this->public.decrypt = _decrypt_v1; + } + + return &this->public; +} + +/* + * Described in header + */ +encrypted_payload_t *encrypted_payload_create_from_plain(payload_type_t next, + chunk_t plain) +{ + private_encrypted_payload_t *this; + + this = (private_encrypted_payload_t*)encrypted_payload_create(PLV2_ENCRYPTED); + this->public.decrypt = _decrypt_plain; + this->next_payload = next; + this->encrypted = plain; + compute_length(this); + + return &this->public; +} + +METHOD(payload_t, frag_verify, status_t, + private_encrypted_fragment_payload_t *this) +{ + if (!this->fragment_number || !this->total_fragments || + this->fragment_number > this->total_fragments) + { + DBG1(DBG_ENC, "invalid fragment number (%u) or total fragments (%u)", + this->fragment_number, this->total_fragments); + return FAILED; + } + if (this->fragment_number > 1 && this->next_payload != 0) + { + DBG1(DBG_ENC, "invalid next payload (%u) for fragment %u, ignored", + this->next_payload, this->fragment_number); + this->next_payload = 0; + } + return SUCCESS; +} + +METHOD(payload_t, frag_get_encoding_rules, int, + private_encrypted_fragment_payload_t *this, encoding_rule_t **rules) +{ + *rules = encodings_fragment; + return countof(encodings_fragment); +} + +METHOD(payload_t, frag_get_header_length, int, + private_encrypted_fragment_payload_t *this) +{ + return 8; +} + +METHOD(payload_t, frag_get_type, payload_type_t, + private_encrypted_fragment_payload_t *this) +{ + return PLV2_FRAGMENT; +} + +METHOD(payload_t, frag_get_next_type, payload_type_t, + private_encrypted_fragment_payload_t *this) +{ + return this->next_payload; +} + +METHOD(payload_t, frag_set_next_type, void, + private_encrypted_fragment_payload_t *this, payload_type_t type) +{ + if (this->fragment_number == 1 && this->next_payload == PL_NONE) + { + this->next_payload = type; + } +} + +METHOD2(payload_t, encrypted_payload_t, frag_get_length, size_t, + private_encrypted_fragment_payload_t *this) +{ + if (this->encrypted.len) + { + this->payload_length = this->encrypted.len; + } + else + { + this->payload_length = this->plain.len; + + if (this->aead) + { + this->payload_length += compute_overhead(this->aead, + this->payload_length); + } + } + this->payload_length += frag_get_header_length(this); + return this->payload_length; +} + +METHOD(encrypted_fragment_payload_t, get_fragment_number, u_int16_t, + private_encrypted_fragment_payload_t *this) +{ + return this->fragment_number; +} + +METHOD(encrypted_fragment_payload_t, get_total_fragments, u_int16_t, + private_encrypted_fragment_payload_t *this) +{ + return this->total_fragments; +} + +METHOD(encrypted_fragment_payload_t, frag_get_content, chunk_t, + private_encrypted_fragment_payload_t *this) +{ + return this->plain; +} + +METHOD(encrypted_payload_t, frag_add_payload, void, + private_encrypted_fragment_payload_t *this, payload_t* payload) +{ + payload->destroy(payload); +} + +METHOD(encrypted_payload_t, frag_set_transform, void, + private_encrypted_fragment_payload_t *this, aead_t* aead) +{ + this->aead = aead; +} + +/** + * Append the encrypted fragment payload header to the associated data + */ +static chunk_t append_header_frag(private_encrypted_fragment_payload_t *this, + chunk_t assoc) +{ + struct { + u_int8_t next_payload; + u_int8_t flags; + u_int16_t length; + u_int16_t fragment_number; + u_int16_t total_fragments; + } __attribute__((packed)) header = { + .next_payload = this->next_payload, + .flags = this->flags, + .length = htons(frag_get_length(this)), + .fragment_number = htons(this->fragment_number), + .total_fragments = htons(this->total_fragments), + }; + return chunk_cat("cc", assoc, chunk_from_thing(header)); +} + +METHOD(encrypted_payload_t, frag_encrypt, status_t, + private_encrypted_fragment_payload_t *this, u_int64_t mid, chunk_t assoc) +{ + status_t status; + + if (!this->aead) + { + DBG1(DBG_ENC, "encrypting encrypted fragment payload failed, " + "transform missing"); + return INVALID_STATE; + } + free(this->encrypted.ptr); + assoc = append_header_frag(this, assoc); + status = encrypt_content("encrypted fragment payload", this->aead, mid, + this->plain, assoc, &this->encrypted); + free(assoc.ptr); + return status; +} + +METHOD(encrypted_payload_t, frag_decrypt, status_t, + private_encrypted_fragment_payload_t *this, chunk_t assoc) +{ + status_t status; + + if (!this->aead) + { + DBG1(DBG_ENC, "decrypting encrypted fragment payload failed, " + "transform missing"); + return INVALID_STATE; + } + free(this->plain.ptr); + assoc = append_header_frag(this, assoc); + status = decrypt_content("encrypted fragment payload", this->aead, + this->encrypted, assoc, &this->plain); + this->plain = chunk_clone(this->plain); + free(assoc.ptr); + return status; +} + +METHOD2(payload_t, encrypted_payload_t, frag_destroy, void, + private_encrypted_fragment_payload_t *this) +{ + free(this->encrypted.ptr); + free(this->plain.ptr); + free(this); +} + +/* + * Described in header + */ +encrypted_fragment_payload_t *encrypted_fragment_payload_create() +{ + private_encrypted_fragment_payload_t *this; + + INIT(this, + .public = { + .encrypted = { + .payload_interface = { + .verify = _frag_verify, + .get_encoding_rules = _frag_get_encoding_rules, + .get_header_length = _frag_get_header_length, + .get_length = _frag_get_length, + .get_next_type = _frag_get_next_type, + .set_next_type = _frag_set_next_type, + .get_type = _frag_get_type, + .destroy = _frag_destroy, + }, + .get_length = _frag_get_length, + .add_payload = _frag_add_payload, + .remove_payload = (void*)return_null, + .generate_payloads = nop, + .set_transform = _frag_set_transform, + .encrypt = _frag_encrypt, + .decrypt = _frag_decrypt, + .destroy = _frag_destroy, + }, + .get_fragment_number = _get_fragment_number, + .get_total_fragments = _get_total_fragments, + .get_content = _frag_get_content, + }, + .next_payload = PL_NONE, + ); + this->payload_length = frag_get_header_length(this); + + return &this->public; +} + +/* + * Described in header + */ +encrypted_fragment_payload_t *encrypted_fragment_payload_create_from_data( + u_int16_t num, u_int16_t total, chunk_t plain) +{ + private_encrypted_fragment_payload_t *this; + + this = (private_encrypted_fragment_payload_t*)encrypted_fragment_payload_create(); + this->fragment_number = num; + this->total_fragments = total; + this->plain = chunk_clone(plain); + + return &this->public; +} diff --git a/src/libcharon/encoding/payloads/encryption_payload.h b/src/libcharon/encoding/payloads/encrypted_payload.h index ee44c2de1..be59e3c2d 100644 --- a/src/libcharon/encoding/payloads/encryption_payload.h +++ b/src/libcharon/encoding/payloads/encrypted_payload.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2014 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2010 revosec AG * Copyright (C) 2005 Jan Hutter @@ -16,23 +17,24 @@ */ /** - * @defgroup encryption_payload encryption_payload + * @defgroup encrypted_payload encrypted_payload * @{ @ingroup payloads */ -#ifndef ENCRYPTION_PAYLOAD_H_ -#define ENCRYPTION_PAYLOAD_H_ +#ifndef ENCRYPTED_PAYLOAD_H_ +#define ENCRYPTED_PAYLOAD_H_ -typedef struct encryption_payload_t encryption_payload_t; +typedef struct encrypted_payload_t encrypted_payload_t; #include <library.h> #include <crypto/aead.h> #include <encoding/payloads/payload.h> +#include <encoding/generator.h> /** - * The encryption payload as described in RFC section 3.14. + * The encrypted payload as described in RFC section 3.14. */ -struct encryption_payload_t { +struct encrypted_payload_t { /** * Implements payload_t interface. @@ -44,14 +46,14 @@ struct encryption_payload_t { * * @return (expected) payload length */ - size_t (*get_length)(encryption_payload_t *this); + size_t (*get_length)(encrypted_payload_t *this); /** * Adds a payload to this encryption payload. * * @param payload payload_t object to add */ - void (*add_payload) (encryption_payload_t *this, payload_t *payload); + void (*add_payload) (encrypted_payload_t *this, payload_t *payload); /** * Remove the first payload in the list @@ -59,14 +61,22 @@ struct encryption_payload_t { * @param payload removed payload * @return payload, NULL if none left */ - payload_t* (*remove_payload)(encryption_payload_t *this); + payload_t* (*remove_payload)(encrypted_payload_t *this); + + /** + * Uses the given generator to generate the contained payloads. + * + * @param generator generator used to generate the contained payloads + */ + void (*generate_payloads)(encrypted_payload_t *this, + generator_t *generator); /** * Set the AEAD transform to use. * * @param aead aead transform to use */ - void (*set_transform) (encryption_payload_t *this, aead_t *aead); + void (*set_transform) (encrypted_payload_t *this, aead_t *aead); /** * Generate, encrypt and sign contained payloads. @@ -78,7 +88,7 @@ struct encryption_payload_t { * - FAILED if encryption failed * - INVALID_STATE if aead not supplied, but needed */ - status_t (*encrypt) (encryption_payload_t *this, u_int64_t mid, + status_t (*encrypt) (encrypted_payload_t *this, u_int64_t mid, chunk_t assoc); /** @@ -92,20 +102,31 @@ struct encryption_payload_t { * - FAILED if integrity check failed * - INVALID_STATE if aead not supplied, but needed */ - status_t (*decrypt) (encryption_payload_t *this, chunk_t assoc); + status_t (*decrypt) (encrypted_payload_t *this, chunk_t assoc); /** - * Destroys an encryption_payload_t object. + * Destroys an encrypted_payload_t object. */ - void (*destroy) (encryption_payload_t *this); + void (*destroy) (encrypted_payload_t *this); }; /** - * Creates an empty encryption_payload_t object. + * Creates an empty encrypted_payload_t object. * * @param type PLV2_ENCRYPTED or PLV1_ENCRYPTED - * @return encryption_payload_t object + * @return encrypted_payload_t object + */ +encrypted_payload_t *encrypted_payload_create(payload_type_t type); + +/** + * Creates an encrypted payload with the given plain text data and next payload + * type. + * + * @param next next payload type + * @param plain plaintext data (gets adopted) + * @return encrypted_payload_t object */ -encryption_payload_t *encryption_payload_create(payload_type_t type); +encrypted_payload_t *encrypted_payload_create_from_plain(payload_type_t next, + chunk_t plain); -#endif /** ENCRYPTION_PAYLOAD_H_ @}*/ +#endif /** ENCRYPTED_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/encoding/payloads/encryption_payload.c b/src/libcharon/encoding/payloads/encryption_payload.c deleted file mode 100644 index 5784562f8..000000000 --- a/src/libcharon/encoding/payloads/encryption_payload.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright (C) 2005-2010 Martin Willi - * Copyright (C) 2010 revosec AG - * Copyright (C) 2011 Tobias Brunner - * Copyright (C) 2005 Jan Hutter - * 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. - */ - -#include <stddef.h> -#include <string.h> - -#include "encryption_payload.h" - -#include <daemon.h> -#include <encoding/payloads/encodings.h> -#include <collections/linked_list.h> -#include <encoding/generator.h> -#include <encoding/parser.h> - -typedef struct private_encryption_payload_t private_encryption_payload_t; - -/** - * Private data of an encryption_payload_t' Object. - * - */ -struct private_encryption_payload_t { - - /** - * Public encryption_payload_t interface. - */ - encryption_payload_t public; - - /** - * There is no next payload for an encryption payload, - * since encryption payload MUST be the last one. - * next_payload means here the first payload of the - * contained, encrypted payload. - */ - u_int8_t next_payload; - - /** - * Flags, including reserved bits - */ - u_int8_t flags; - - /** - * Length of this payload - */ - u_int16_t payload_length; - - /** - * Chunk containing the IV, plain, padding and ICV. - */ - chunk_t encrypted; - - /** - * AEAD transform to use - */ - aead_t *aead; - - /** - * Contained payloads - */ - linked_list_t *payloads; - - /** - * Type of payload, PLV2_ENCRYPTED or PLV1_ENCRYPTED - */ - payload_type_t type; -}; - -/** - * Encoding rules to parse or generate a IKEv2-Encryption Payload. - * - * The defined offsets are the positions in a object of type - * private_encryption_payload_t. - */ -static encoding_rule_t encodings_v2[] = { - /* 1 Byte next payload type, stored in the field next_payload */ - { U_INT_8, offsetof(private_encryption_payload_t, next_payload) }, - /* Critical and 7 reserved bits, all stored for reconstruction */ - { U_INT_8, offsetof(private_encryption_payload_t, flags) }, - /* Length of the whole encryption payload*/ - { PAYLOAD_LENGTH, offsetof(private_encryption_payload_t, payload_length) }, - /* encrypted data, stored in a chunk. contains iv, data, padding */ - { CHUNK_DATA, offsetof(private_encryption_payload_t, encrypted) }, -}; - -/* - 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ! Next Payload !C! RESERVED ! Payload Length ! - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ! Initialization Vector ! - ! (length is block size for encryption algorithm) ! - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ! Encrypted IKE Payloads ! - + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ! ! Padding (0-255 octets) ! - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - ! ! Pad Length ! - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ~ Integrity Checksum Data ~ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - -/** - * Encoding rules to parse or generate a complete encrypted IKEv1 message. - * - * The defined offsets are the positions in a object of type - * private_encryption_payload_t. - */ -static encoding_rule_t encodings_v1[] = { - /* encrypted data, stored in a chunk */ - { ENCRYPTED_DATA, offsetof(private_encryption_payload_t, encrypted) }, -}; - -/* - 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ! Encrypted IKE Payloads ! - + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - ! ! Padding (0-255 octets) ! - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -*/ - -METHOD(payload_t, verify, status_t, - private_encryption_payload_t *this) -{ - return SUCCESS; -} - -METHOD(payload_t, get_encoding_rules, int, - private_encryption_payload_t *this, encoding_rule_t **rules) -{ - if (this->type == PLV2_ENCRYPTED) - { - *rules = encodings_v2; - return countof(encodings_v2); - } - *rules = encodings_v1; - return countof(encodings_v1); -} - -METHOD(payload_t, get_header_length, int, - private_encryption_payload_t *this) -{ - if (this->type == PLV2_ENCRYPTED) - { - return 4; - } - return 0; -} - -METHOD(payload_t, get_type, payload_type_t, - private_encryption_payload_t *this) -{ - return this->type; -} - -METHOD(payload_t, get_next_type, payload_type_t, - private_encryption_payload_t *this) -{ - return this->next_payload; -} - -METHOD(payload_t, set_next_type, void, - private_encryption_payload_t *this, payload_type_t type) -{ - /* the next payload is set during add, still allow this for IKEv1 */ - this->next_payload = type; -} - -/** - * Compute the length of the whole payload - */ -static void compute_length(private_encryption_payload_t *this) -{ - enumerator_t *enumerator; - payload_t *payload; - size_t bs, length = 0; - - if (this->encrypted.len) - { - length = this->encrypted.len; - } - else - { - enumerator = this->payloads->create_enumerator(this->payloads); - while (enumerator->enumerate(enumerator, &payload)) - { - length += payload->get_length(payload); - } - enumerator->destroy(enumerator); - - if (this->aead) - { - /* append padding */ - bs = this->aead->get_block_size(this->aead); - length += bs - (length % bs); - /* add iv */ - length += this->aead->get_iv_size(this->aead); - /* add icv */ - length += this->aead->get_icv_size(this->aead); - } - } - length += get_header_length(this); - this->payload_length = length; -} - -METHOD2(payload_t, encryption_payload_t, get_length, size_t, - private_encryption_payload_t *this) -{ - compute_length(this); - return this->payload_length; -} - -METHOD(encryption_payload_t, add_payload, void, - private_encryption_payload_t *this, payload_t *payload) -{ - payload_t *last_payload; - - if (this->payloads->get_count(this->payloads) > 0) - { - this->payloads->get_last(this->payloads, (void **)&last_payload); - last_payload->set_next_type(last_payload, payload->get_type(payload)); - } - else - { - this->next_payload = payload->get_type(payload); - } - payload->set_next_type(payload, PL_NONE); - this->payloads->insert_last(this->payloads, payload); - compute_length(this); -} - -METHOD(encryption_payload_t, remove_payload, payload_t *, - private_encryption_payload_t *this) -{ - payload_t *payload; - - if (this->payloads->remove_first(this->payloads, - (void**)&payload) == SUCCESS) - { - return payload; - } - return NULL; -} - -/** - * Generate payload before encryption - */ -static chunk_t generate(private_encryption_payload_t *this, - generator_t *generator) -{ - payload_t *current, *next; - enumerator_t *enumerator; - u_int32_t *lenpos; - chunk_t chunk = chunk_empty; - - enumerator = this->payloads->create_enumerator(this->payloads); - if (enumerator->enumerate(enumerator, ¤t)) - { - this->next_payload = current->get_type(current); - - while (enumerator->enumerate(enumerator, &next)) - { - current->set_next_type(current, next->get_type(next)); - generator->generate_payload(generator, current); - current = next; - } - current->set_next_type(current, PL_NONE); - generator->generate_payload(generator, current); - - chunk = generator->get_chunk(generator, &lenpos); - DBG2(DBG_ENC, "generated content in encryption payload"); - } - enumerator->destroy(enumerator); - return chunk; -} - -/** - * Append the encryption payload header to the associated data - */ -static chunk_t append_header(private_encryption_payload_t *this, chunk_t assoc) -{ - struct { - u_int8_t next_payload; - u_int8_t flags; - u_int16_t length; - } __attribute__((packed)) header = { - .next_payload = this->next_payload, - .flags = this->flags, - .length = htons(get_length(this)), - }; - return chunk_cat("cc", assoc, chunk_from_thing(header)); -} - -METHOD(encryption_payload_t, encrypt, status_t, - private_encryption_payload_t *this, u_int64_t mid, chunk_t assoc) -{ - chunk_t iv, plain, padding, icv, crypt; - generator_t *generator; - iv_gen_t *iv_gen; - rng_t *rng; - size_t bs; - - if (this->aead == NULL) - { - DBG1(DBG_ENC, "encrypting encryption payload failed, transform missing"); - return INVALID_STATE; - } - - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng) - { - DBG1(DBG_ENC, "encrypting encryption payload failed, no RNG found"); - return NOT_SUPPORTED; - } - - iv_gen = this->aead->get_iv_gen(this->aead); - if (!iv_gen) - { - DBG1(DBG_ENC, "encrypting encryption payload failed, no IV generator"); - return NOT_SUPPORTED; - } - - assoc = append_header(this, assoc); - - generator = generator_create(); - plain = generate(this, generator); - bs = this->aead->get_block_size(this->aead); - /* we need at least one byte padding to store the padding length */ - padding.len = bs - (plain.len % bs); - iv.len = this->aead->get_iv_size(this->aead); - icv.len = this->aead->get_icv_size(this->aead); - - /* prepare data to authenticate-encrypt: - * | IV | plain | padding | ICV | - * \____crypt______/ ^ - * | / - * v / - * assoc -> + ------->/ - */ - free(this->encrypted.ptr); - this->encrypted = chunk_alloc(iv.len + plain.len + padding.len + icv.len); - iv.ptr = this->encrypted.ptr; - memcpy(iv.ptr + iv.len, plain.ptr, plain.len); - plain.ptr = iv.ptr + iv.len; - padding.ptr = plain.ptr + plain.len; - icv.ptr = padding.ptr + padding.len; - crypt = chunk_create(plain.ptr, plain.len + padding.len); - generator->destroy(generator); - - if (!iv_gen->get_iv(iv_gen, mid, iv.len, iv.ptr) || - !rng->get_bytes(rng, padding.len - 1, padding.ptr)) - { - DBG1(DBG_ENC, "encrypting encryption payload failed, no IV or padding"); - rng->destroy(rng); - free(assoc.ptr); - return FAILED; - } - padding.ptr[padding.len - 1] = padding.len - 1; - rng->destroy(rng); - - DBG3(DBG_ENC, "encryption payload encryption:"); - DBG3(DBG_ENC, "IV %B", &iv); - DBG3(DBG_ENC, "plain %B", &plain); - DBG3(DBG_ENC, "padding %B", &padding); - DBG3(DBG_ENC, "assoc %B", &assoc); - - if (!this->aead->encrypt(this->aead, crypt, assoc, iv, NULL)) - { - free(assoc.ptr); - return FAILED; - } - - DBG3(DBG_ENC, "encrypted %B", &crypt); - DBG3(DBG_ENC, "ICV %B", &icv); - - free(assoc.ptr); - - return SUCCESS; -} - -METHOD(encryption_payload_t, encrypt_v1, status_t, - private_encryption_payload_t *this, u_int64_t mid, chunk_t iv) -{ - generator_t *generator; - chunk_t plain, padding; - size_t bs; - - if (this->aead == NULL) - { - DBG1(DBG_ENC, "encryption failed, transform missing"); - return INVALID_STATE; - } - - generator = generator_create(); - plain = generate(this, generator); - bs = this->aead->get_block_size(this->aead); - padding.len = bs - (plain.len % bs); - - /* prepare data to encrypt: - * | plain | padding | */ - free(this->encrypted.ptr); - this->encrypted = chunk_alloc(plain.len + padding.len); - memcpy(this->encrypted.ptr, plain.ptr, plain.len); - plain.ptr = this->encrypted.ptr; - padding.ptr = plain.ptr + plain.len; - memset(padding.ptr, 0, padding.len); - generator->destroy(generator); - - DBG3(DBG_ENC, "encrypting payloads:"); - DBG3(DBG_ENC, "plain %B", &plain); - DBG3(DBG_ENC, "padding %B", &padding); - - if (!this->aead->encrypt(this->aead, this->encrypted, chunk_empty, iv, NULL)) - { - return FAILED; - } - - DBG3(DBG_ENC, "encrypted %B", &this->encrypted); - - return SUCCESS; -} - -/** - * Parse the payloads after decryption. - */ -static status_t parse(private_encryption_payload_t *this, chunk_t plain) -{ - parser_t *parser; - payload_type_t type; - - parser = parser_create(plain); - type = this->next_payload; - while (type != PL_NONE) - { - payload_t *payload; - - if (plain.len < 4 || untoh16(plain.ptr + 2) > plain.len) - { - DBG1(DBG_ENC, "invalid %N payload length, decryption failed?", - payload_type_names, type); - parser->destroy(parser); - return PARSE_ERROR; - } - if (parser->parse_payload(parser, type, &payload) != SUCCESS) - { - parser->destroy(parser); - return PARSE_ERROR; - } - if (payload->verify(payload) != SUCCESS) - { - DBG1(DBG_ENC, "%N verification failed", - payload_type_names, payload->get_type(payload)); - payload->destroy(payload); - parser->destroy(parser); - return VERIFY_ERROR; - } - type = payload->get_next_type(payload); - this->payloads->insert_last(this->payloads, payload); - } - parser->destroy(parser); - DBG2(DBG_ENC, "parsed content of encryption payload"); - return SUCCESS; -} - -METHOD(encryption_payload_t, decrypt, status_t, - private_encryption_payload_t *this, chunk_t assoc) -{ - chunk_t iv, plain, padding, icv, crypt; - size_t bs; - - if (this->aead == NULL) - { - DBG1(DBG_ENC, "decrypting encryption payload failed, transform missing"); - return INVALID_STATE; - } - - /* prepare data to authenticate-decrypt: - * | IV | plain | padding | ICV | - * \____crypt______/ ^ - * | / - * v / - * assoc -> + ------->/ - */ - - bs = this->aead->get_block_size(this->aead); - iv.len = this->aead->get_iv_size(this->aead); - iv.ptr = this->encrypted.ptr; - icv.len = this->aead->get_icv_size(this->aead); - icv.ptr = this->encrypted.ptr + this->encrypted.len - icv.len; - crypt.ptr = iv.ptr + iv.len; - crypt.len = this->encrypted.len - iv.len; - - if (iv.len + icv.len > this->encrypted.len || - (crypt.len - icv.len) % bs) - { - DBG1(DBG_ENC, "decrypting encryption payload failed, invalid length"); - return FAILED; - } - - assoc = append_header(this, assoc); - - DBG3(DBG_ENC, "encryption payload decryption:"); - DBG3(DBG_ENC, "IV %B", &iv); - DBG3(DBG_ENC, "encrypted %B", &crypt); - DBG3(DBG_ENC, "ICV %B", &icv); - DBG3(DBG_ENC, "assoc %B", &assoc); - - if (!this->aead->decrypt(this->aead, crypt, assoc, iv, NULL)) - { - DBG1(DBG_ENC, "verifying encryption payload integrity failed"); - free(assoc.ptr); - return FAILED; - } - free(assoc.ptr); - - plain = chunk_create(crypt.ptr, crypt.len - icv.len); - padding.len = plain.ptr[plain.len - 1] + 1; - if (padding.len > plain.len) - { - DBG1(DBG_ENC, "decrypting encryption payload failed, " - "padding invalid %B", &crypt); - return PARSE_ERROR; - } - plain.len -= padding.len; - padding.ptr = plain.ptr + plain.len; - - DBG3(DBG_ENC, "plain %B", &plain); - DBG3(DBG_ENC, "padding %B", &padding); - - return parse(this, plain); -} - -METHOD(encryption_payload_t, decrypt_v1, status_t, - private_encryption_payload_t *this, chunk_t iv) -{ - if (this->aead == NULL) - { - DBG1(DBG_ENC, "decryption failed, transform missing"); - return INVALID_STATE; - } - - /* data must be a multiple of block size */ - if (iv.len != this->aead->get_block_size(this->aead) || - this->encrypted.len < iv.len || this->encrypted.len % iv.len) - { - DBG1(DBG_ENC, "decryption failed, invalid length"); - return FAILED; - } - - DBG3(DBG_ENC, "decrypting payloads:"); - DBG3(DBG_ENC, "encrypted %B", &this->encrypted); - - if (!this->aead->decrypt(this->aead, this->encrypted, chunk_empty, iv, NULL)) - { - return FAILED; - } - - DBG3(DBG_ENC, "plain %B", &this->encrypted); - - return parse(this, this->encrypted); -} - -METHOD(encryption_payload_t, set_transform, void, - private_encryption_payload_t *this, aead_t* aead) -{ - this->aead = aead; -} - -METHOD2(payload_t, encryption_payload_t, destroy, void, - private_encryption_payload_t *this) -{ - this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); - free(this->encrypted.ptr); - free(this); -} - -/* - * Described in header - */ -encryption_payload_t *encryption_payload_create(payload_type_t type) -{ - private_encryption_payload_t *this; - - INIT(this, - .public = { - .payload_interface = { - .verify = _verify, - .get_encoding_rules = _get_encoding_rules, - .get_header_length = _get_header_length, - .get_length = _get_length, - .get_next_type = _get_next_type, - .set_next_type = _set_next_type, - .get_type = _get_type, - .destroy = _destroy, - }, - .get_length = _get_length, - .add_payload = _add_payload, - .remove_payload = _remove_payload, - .set_transform = _set_transform, - .encrypt = _encrypt, - .decrypt = _decrypt, - .destroy = _destroy, - }, - .next_payload = PL_NONE, - .payloads = linked_list_create(), - .type = type, - ); - this->payload_length = get_header_length(this); - - if (type == PLV1_ENCRYPTED) - { - this->public.encrypt = _encrypt_v1; - this->public.decrypt = _decrypt_v1; - } - - return &this->public; -} diff --git a/src/libcharon/encoding/payloads/ike_header.c b/src/libcharon/encoding/payloads/ike_header.c index 7015667ee..c96738a34 100644 --- a/src/libcharon/encoding/payloads/ike_header.c +++ b/src/libcharon/encoding/payloads/ike_header.c @@ -210,8 +210,9 @@ METHOD(payload_t, verify, status_t, case TRANSACTION: case QUICK_MODE: case NEW_GROUP_MODE: - if (this->maj_version != IKEV1_MAJOR_VERSION) + if (this->maj_version == IKEV2_MAJOR_VERSION) { + /* IKEv1 exchange type in IKEv2? */ return FAILED; } break; @@ -223,14 +224,20 @@ METHOD(payload_t, verify, status_t, #ifdef ME case ME_CONNECT: #endif /* ME */ - if (this->maj_version != IKEV2_MAJOR_VERSION) + if (this->maj_version == IKEV1_MAJOR_VERSION) { + /* IKEv2 exchange type in IKEv1? */ return FAILED; } break; default: - /* unsupported exchange type */ - return FAILED; + if (this->maj_version == IKEV1_MAJOR_VERSION || + this->maj_version == IKEV2_MAJOR_VERSION) + { + /* unsupported exchange type for known version */ + return FAILED; + } + break; } if (this->initiator_spi == 0) { @@ -501,4 +508,3 @@ ike_header_t *ike_header_create_version(int major, int minor) } return this; } - diff --git a/src/libcharon/encoding/payloads/notify_payload.c b/src/libcharon/encoding/payloads/notify_payload.c index dd92e429a..94723ddd7 100644 --- a/src/libcharon/encoding/payloads/notify_payload.c +++ b/src/libcharon/encoding/payloads/notify_payload.c @@ -65,7 +65,7 @@ ENUM_NEXT(notify_type_names, ME_CONNECT_FAILED, ME_CONNECT_FAILED, CHILD_SA_NOT_ "ME_CONNECT_FAILED"); ENUM_NEXT(notify_type_names, MS_NOTIFY_STATUS, MS_NOTIFY_STATUS, ME_CONNECT_FAILED, "MS_NOTIFY_STATUS"); -ENUM_NEXT(notify_type_names, INITIAL_CONTACT, IFOM_CAPABILITY, MS_NOTIFY_STATUS, +ENUM_NEXT(notify_type_names, INITIAL_CONTACT, FRAGMENTATION_SUPPORTED, MS_NOTIFY_STATUS, "INITIAL_CONTACT", "SET_WINDOW_SIZE", "ADDITIONAL_TS_POSSIBLE", @@ -110,8 +110,10 @@ ENUM_NEXT(notify_type_names, INITIAL_CONTACT, IFOM_CAPABILITY, MS_NOTIFY_STATUS, "PSK_PERSIST", "PSK_CONFIRM", "ERX_SUPPORTED", - "IFOM_CAPABILITY"); -ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IFOM_CAPABILITY, + "IFOM_CAPABILITY", + "SENDER_REQUEST_ID", + "FRAGMENTATION_SUPPORTED"); +ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, FRAGMENTATION_SUPPORTED, "INITIAL_CONTACT"); ENUM_NEXT(notify_type_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, "DPD_R_U_THERE", @@ -128,7 +130,7 @@ ENUM_NEXT(notify_type_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE, "ME_CONNECTKEY", "ME_CONNECTAUTH", "ME_RESPONSE", - "RADIUS_ATTRIBUTE",); + "RADIUS_ATTRIBUTE"); ENUM_END(notify_type_names, RADIUS_ATTRIBUTE); @@ -172,7 +174,7 @@ ENUM_NEXT(notify_type_short_names, ME_CONNECT_FAILED, ME_CONNECT_FAILED, CHILD_S "ME_CONN_FAIL"); ENUM_NEXT(notify_type_short_names, MS_NOTIFY_STATUS, MS_NOTIFY_STATUS, ME_CONNECT_FAILED, "MS_STATUS"); -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT, IFOM_CAPABILITY, MS_NOTIFY_STATUS, +ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT, FRAGMENTATION_SUPPORTED, MS_NOTIFY_STATUS, "INIT_CONTACT", "SET_WINSIZE", "ADD_TS_POSS", @@ -217,8 +219,10 @@ ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT, IFOM_CAPABILITY, MS_NOTIFY_S "PSK_PST", "PSK_CFM", "ERX_SUP", - "IFOM_CAP"); -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IFOM_CAPABILITY, + "IFOM_CAP", + "SENDER_REQ_ID", + "FRAG_SUP"); +ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, FRAGMENTATION_SUPPORTED, "INITIAL_CONTACT"); ENUM_NEXT(notify_type_short_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, "DPD", diff --git a/src/libcharon/encoding/payloads/notify_payload.h b/src/libcharon/encoding/payloads/notify_payload.h index 3c56f0673..25521c2bb 100644 --- a/src/libcharon/encoding/payloads/notify_payload.h +++ b/src/libcharon/encoding/payloads/notify_payload.h @@ -147,6 +147,10 @@ enum notify_type_t { ERX_SUPPORTED = 16427, /* IFOM capability, 3GPP TS 24.303, annex B.2 */ IFOM_CAPABILITY = 16428, + /* SENDER_REQUEST_ID (draft-yeung-g-ikev2) */ + SENDER_REQUEST_ID = 16429, + /* IKEv2 fragmentation supported, RFC 7383 */ + FRAGMENTATION_SUPPORTED = 16430, /* IKEv1 initial contact */ INITIAL_CONTACT_IKEV1 = 24578, /* IKEv1 DPD */ diff --git a/src/libcharon/encoding/payloads/payload.c b/src/libcharon/encoding/payloads/payload.c index fd616620d..600b6dd68 100644 --- a/src/libcharon/encoding/payloads/payload.c +++ b/src/libcharon/encoding/payloads/payload.c @@ -28,7 +28,8 @@ #include <encoding/payloads/auth_payload.h> #include <encoding/payloads/cert_payload.h> #include <encoding/payloads/certreq_payload.h> -#include <encoding/payloads/encryption_payload.h> +#include <encoding/payloads/encrypted_payload.h> +#include <encoding/payloads/encrypted_fragment_payload.h> #include <encoding/payloads/ts_payload.h> #include <encoding/payloads/delete_payload.h> #include <encoding/payloads/vendor_id_payload.h> @@ -59,7 +60,7 @@ ENUM_NEXT(payload_type_names, PLV1_SECURITY_ASSOCIATION, PLV1_CONFIGURATION, PL_ ENUM_NEXT(payload_type_names, PLV1_NAT_D, PLV1_NAT_OA, PLV1_CONFIGURATION, "NAT_D_V1", "NAT_OA_V1"); -ENUM_NEXT(payload_type_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_NAT_OA, +ENUM_NEXT(payload_type_names, PLV2_SECURITY_ASSOCIATION, PLV2_FRAGMENT, PLV1_NAT_OA, "SECURITY_ASSOCIATION", "KEY_EXCHANGE", "ID_INITIATOR", @@ -76,16 +77,20 @@ ENUM_NEXT(payload_type_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_NAT_OA, "ENCRYPTED", "CONFIGURATION", "EAP", - "GSPM"); + "GSPM", + "GROUP_ID", + "GROUP_SECURITY_ASSOCIATION", + "KEY_DOWNLOAD", + "ENCRYPTED_FRAGMENT"); #ifdef ME -ENUM_NEXT(payload_type_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_GSPM, +ENUM_NEXT(payload_type_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_FRAGMENT, "ID_PEER"); ENUM_NEXT(payload_type_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_ID_PEER, "NAT_D_DRAFT_V1", "NAT_OA_DRAFT_V1", "FRAGMENT"); #else -ENUM_NEXT(payload_type_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_GSPM, +ENUM_NEXT(payload_type_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_FRAGMENT, "NAT_D_DRAFT_V1", "NAT_OA_DRAFT_V1", "FRAGMENT"); @@ -125,7 +130,7 @@ ENUM_NEXT(payload_type_short_names, PLV1_SECURITY_ASSOCIATION, PLV1_CONFIGURATIO ENUM_NEXT(payload_type_short_names, PLV1_NAT_D, PLV1_NAT_OA, PLV1_CONFIGURATION, "NAT-D", "NAT-OA"); -ENUM_NEXT(payload_type_short_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_NAT_OA, +ENUM_NEXT(payload_type_short_names, PLV2_SECURITY_ASSOCIATION, PLV2_FRAGMENT, PLV1_NAT_OA, "SA", "KE", "IDi", @@ -142,16 +147,20 @@ ENUM_NEXT(payload_type_short_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_N "E", "CP", "EAP", - "GSPM"); + "GSPM", + "IDg", + "GSA", + "KD", + "EF"); #ifdef ME -ENUM_NEXT(payload_type_short_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_GSPM, +ENUM_NEXT(payload_type_short_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_FRAGMENT, "IDp"); ENUM_NEXT(payload_type_short_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_ID_PEER, "NAT-D", "NAT-OA", "FRAG"); #else -ENUM_NEXT(payload_type_short_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_GSPM, +ENUM_NEXT(payload_type_short_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_FRAGMENT, "NAT-D", "NAT-OA", "FRAG"); @@ -244,9 +253,11 @@ payload_t *payload_create(payload_type_t type) return (payload_t*)eap_payload_create(); case PLV2_ENCRYPTED: case PLV1_ENCRYPTED: - return (payload_t*)encryption_payload_create(type); + return (payload_t*)encrypted_payload_create(type); case PLV1_FRAGMENT: return (payload_t*)fragment_payload_create(); + case PLV2_FRAGMENT: + return (payload_t*)encrypted_fragment_payload_create(); default: return (payload_t*)unknown_payload_create(type); } @@ -261,15 +272,19 @@ bool payload_is_known(payload_type_t type) { return TRUE; } - if (type >= PLV2_SECURITY_ASSOCIATION && type <= PLV2_EAP) + if (type >= PLV1_SECURITY_ASSOCIATION && type <= PLV1_CONFIGURATION) { return TRUE; } - if (type >= PLV1_SECURITY_ASSOCIATION && type <= PLV1_CONFIGURATION) + if (type >= PLV1_NAT_D && type <= PLV1_NAT_OA) { return TRUE; } - if (type >= PLV1_NAT_D && type <= PLV1_NAT_OA) + if (type >= PLV2_SECURITY_ASSOCIATION && type <= PLV2_EAP) + { + return TRUE; + } + if (type == PLV2_FRAGMENT) { return TRUE; } diff --git a/src/libcharon/encoding/payloads/payload.h b/src/libcharon/encoding/payloads/payload.h index d9dd619f7..036cd422d 100644 --- a/src/libcharon/encoding/payloads/payload.h +++ b/src/libcharon/encoding/payloads/payload.h @@ -193,7 +193,7 @@ enum payload_type_t { PLV2_TS_RESPONDER = 45, /** - * Encryption payload, contains other payloads (E). + * Encrypted payload, contains other payloads (E). */ PLV2_ENCRYPTED = 46, @@ -212,6 +212,26 @@ enum payload_type_t { */ PLV2_GSPM = 49, + /** + * Group Identification (draft-yeung-g-ikev2) + */ + PLV2_IDG = 50, + + /** + * Group Security Association (draft-yeung-g-ikev2) + */ + PLV2_GSA = 51, + + /** + * Key Download (draft-yeung-g-ikev2) + */ + PLV2_KD = 52, + + /** + * Encrypted fragment payload (SKF), RFC 7383 + */ + PLV2_FRAGMENT = 53, + #ifdef ME /** * Identification payload for peers has a value from @@ -231,7 +251,7 @@ enum payload_type_t { PLV1_NAT_OA_DRAFT_00_03 = 131, /** - * IKE fragment (proprietary IKEv1 extension) + * IKEv1 fragment (proprietary IKEv1 extension) */ PLV1_FRAGMENT = 132, diff --git a/src/libcharon/encoding/payloads/sa_payload.c b/src/libcharon/encoding/payloads/sa_payload.c index 8e3a01285..407038a2d 100644 --- a/src/libcharon/encoding/payloads/sa_payload.c +++ b/src/libcharon/encoding/payloads/sa_payload.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2014 Tobias Brunner * Copyright (C) 2005-2010 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -296,7 +296,7 @@ METHOD(sa_payload_t, get_proposals, linked_list_t*, linked_list_t *substructs, *list; if (this->type == PLV1_SECURITY_ASSOCIATION) - { /* IKEv1 proposals start with 0 */ + { /* IKEv1 proposals may start with 0 or 1 (or any other number really) */ struct_number = ignore_struct_number = -1; } @@ -309,17 +309,22 @@ METHOD(sa_payload_t, get_proposals, linked_list_t*, enumerator = this->proposals->create_enumerator(this->proposals); while (enumerator->enumerate(enumerator, &substruct)) { + int current_number = substruct->get_proposal_number(substruct); + /* check if a proposal has a single protocol */ - if (substruct->get_proposal_number(substruct) == struct_number) + if (current_number == struct_number) { if (ignore_struct_number < struct_number) - { /* remove an already added, if first of series */ + { /* remove an already added substruct, if first of series */ substructs->remove_last(substructs, (void**)&substruct); ignore_struct_number = struct_number; } continue; } - struct_number++; + /* for IKEv1 the numbers don't have to be consecutive, for IKEv2 they do + * but since we don't really care for the actual number we accept them + * anyway. we already verified that they increase monotonically. */ + struct_number = current_number; substructs->insert_last(substructs, substruct); } enumerator->destroy(enumerator); @@ -364,7 +369,7 @@ METHOD(sa_payload_t, get_ipcomp_proposals, linked_list_t*, } if (proposal_number != current_proposal) { /* start of a new proposal */ - if (espah && ipcomp) + if (espah && ipcomp && ipcomp->get_cpi(ipcomp, NULL)) { /* previous proposal is valid */ break; } |