From 147fe503af6752972bb0934c57f0f387ac4759f1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 6 Jun 2014 16:35:49 +0200 Subject: [PATCH 01/24] ike: Rename encryption_payload to encrypted_payload --- src/conftest/hooks/ike_auth_fill.c | 2 +- src/libcharon/Android.mk | 2 +- src/libcharon/Makefile.am | 2 +- src/libcharon/encoding/message.c | 26 ++-- src/libcharon/encoding/message.h | 2 +- src/libcharon/encoding/parser.c | 2 +- ...cryption_payload.c => encrypted_payload.c} | 122 +++++++++--------- ...cryption_payload.h => encrypted_payload.h} | 36 +++--- src/libcharon/encoding/payloads/payload.c | 4 +- src/libcharon/encoding/payloads/payload.h | 2 +- 10 files changed, 98 insertions(+), 102 deletions(-) rename src/libcharon/encoding/payloads/{encryption_payload.c => encrypted_payload.c} (80%) rename src/libcharon/encoding/payloads/{encryption_payload.h => encrypted_payload.h} (69%) diff --git a/src/conftest/hooks/ike_auth_fill.c b/src/conftest/hooks/ike_auth_fill.c index 5cdd5be38..e3eabe2f2 100644 --- a/src/conftest/hooks/ike_auth_fill.c +++ b/src/conftest/hooks/ike_auth_fill.c @@ -19,7 +19,7 @@ #include #include -#include +#include typedef struct private_ike_auth_fill_t private_ike_auth_fill_t; diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index a28b459de..a2e064e83 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -25,7 +25,7 @@ encoding/payloads/cp_payload.c encoding/payloads/cp_payload.h \ encoding/payloads/delete_payload.c encoding/payloads/delete_payload.h \ encoding/payloads/eap_payload.c encoding/payloads/eap_payload.h \ encoding/payloads/encodings.c encoding/payloads/encodings.h \ -encoding/payloads/encryption_payload.c encoding/payloads/encryption_payload.h \ +encoding/payloads/encrypted_payload.c encoding/payloads/encrypted_payload.h \ encoding/payloads/id_payload.c encoding/payloads/id_payload.h \ encoding/payloads/ike_header.c encoding/payloads/ike_header.h \ encoding/payloads/ke_payload.c encoding/payloads/ke_payload.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 0eaabf57f..5fb1e3dce 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -23,7 +23,7 @@ encoding/payloads/cp_payload.c encoding/payloads/cp_payload.h \ encoding/payloads/delete_payload.c encoding/payloads/delete_payload.h \ encoding/payloads/eap_payload.c encoding/payloads/eap_payload.h \ encoding/payloads/encodings.c encoding/payloads/encodings.h \ -encoding/payloads/encryption_payload.c encoding/payloads/encryption_payload.h \ +encoding/payloads/encrypted_payload.c encoding/payloads/encrypted_payload.h \ encoding/payloads/id_payload.c encoding/payloads/id_payload.h \ encoding/payloads/ike_header.c encoding/payloads/ike_header.h \ encoding/payloads/ke_payload.c encoding/payloads/ke_payload.h \ diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index f6f13ae2f..cecb55470 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -1392,11 +1392,11 @@ 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 *encryption; linked_list_t *payloads; payload_t *current; @@ -1410,11 +1410,11 @@ static encryption_payload_t* wrap_payloads(private_message_t *this) if (this->is_encrypted) { - encryption = encryption_payload_create(PLV1_ENCRYPTED); + encryption = encrypted_payload_create(PLV1_ENCRYPTED); } else { - encryption = encryption_payload_create(PLV2_ENCRYPTED); + encryption = encrypted_payload_create(PLV2_ENCRYPTED); } while (payloads->remove_first(payloads, (void**)¤t) == SUCCESS) { @@ -1459,7 +1459,7 @@ METHOD(message_t, generate, status_t, generator_t *generator; ike_header_t *ike_header; payload_t *payload, *next; - encryption_payload_t *encryption = NULL; + encrypted_payload_t *encryption = NULL; payload_type_t next_type; enumerator_t *enumerator; aead_t *aead = NULL; @@ -1780,9 +1780,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); @@ -1841,7 +1841,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; @@ -1941,7 +1941,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) { @@ -1961,7 +1961,7 @@ static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) if (type == PLV2_ENCRYPTED || type == PLV1_ENCRYPTED) { - encryption_payload_t *encryption; + encrypted_payload_t *encryption; if (was_encrypted) { @@ -1972,7 +1972,7 @@ static status_t decrypt_payloads(private_message_t *this, keymat_t *keymat) } 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)) diff --git a/src/libcharon/encoding/message.h b/src/libcharon/encoding/message.h index 7631a7c3a..bc4aa08bf 100644 --- a/src/libcharon/encoding/message.h +++ b/src/libcharon/encoding/message.h @@ -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 { 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 #include #include -#include +#include #include #include #include diff --git a/src/libcharon/encoding/payloads/encryption_payload.c b/src/libcharon/encoding/payloads/encrypted_payload.c similarity index 80% rename from src/libcharon/encoding/payloads/encryption_payload.c rename to src/libcharon/encoding/payloads/encrypted_payload.c index 9fd6641da..d71f514c2 100644 --- a/src/libcharon/encoding/payloads/encryption_payload.c +++ b/src/libcharon/encoding/payloads/encrypted_payload.c @@ -19,7 +19,7 @@ #include #include -#include "encryption_payload.h" +#include "encrypted_payload.h" #include #include @@ -27,22 +27,18 @@ #include #include -typedef struct private_encryption_payload_t private_encryption_payload_t; +typedef struct private_encrypted_payload_t private_encrypted_payload_t; -/** - * Private data of an encryption_payload_t' Object. - * - */ -struct private_encryption_payload_t { +struct private_encrypted_payload_t { /** - * Public encryption_payload_t interface. + * Public encrypted_payload_t interface. */ - encryption_payload_t public; + encrypted_payload_t public; /** - * There is no next payload for an encryption payload, - * since encryption payload MUST be the last one. + * 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. */ @@ -80,20 +76,20 @@ struct private_encryption_payload_t { }; /** - * Encoding rules to parse or generate a IKEv2-Encryption Payload. + * Encoding rules to parse or generate a IKEv2-Encrypted Payload. * * The defined offsets are the positions in a object of type - * private_encryption_payload_t. + * 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_encryption_payload_t, 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_encryption_payload_t, flags) }, - /* Length of the whole encryption payload*/ - { PAYLOAD_LENGTH, offsetof(private_encryption_payload_t, payload_length) }, + { 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_encryption_payload_t, encrypted) }, + { CHUNK_DATA, offsetof(private_encrypted_payload_t, encrypted) }, }; /* @@ -119,11 +115,11 @@ static encoding_rule_t encodings_v2[] = { * 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. + * private_encrypted_payload_t. */ static encoding_rule_t encodings_v1[] = { /* encrypted data, stored in a chunk */ - { ENCRYPTED_DATA, offsetof(private_encryption_payload_t, encrypted) }, + { ENCRYPTED_DATA, offsetof(private_encrypted_payload_t, encrypted) }, }; /* @@ -137,13 +133,13 @@ static encoding_rule_t encodings_v1[] = { */ METHOD(payload_t, verify, status_t, - private_encryption_payload_t *this) + private_encrypted_payload_t *this) { return SUCCESS; } METHOD(payload_t, get_encoding_rules, int, - private_encryption_payload_t *this, encoding_rule_t **rules) + private_encrypted_payload_t *this, encoding_rule_t **rules) { if (this->type == PLV2_ENCRYPTED) { @@ -155,7 +151,7 @@ METHOD(payload_t, get_encoding_rules, int, } METHOD(payload_t, get_header_length, int, - private_encryption_payload_t *this) + private_encrypted_payload_t *this) { if (this->type == PLV2_ENCRYPTED) { @@ -165,19 +161,19 @@ METHOD(payload_t, get_header_length, int, } METHOD(payload_t, get_type, payload_type_t, - private_encryption_payload_t *this) + private_encrypted_payload_t *this) { return this->type; } METHOD(payload_t, get_next_type, payload_type_t, - private_encryption_payload_t *this) + private_encrypted_payload_t *this) { return this->next_payload; } METHOD(payload_t, set_next_type, void, - private_encryption_payload_t *this, payload_type_t type) + 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; @@ -186,7 +182,7 @@ METHOD(payload_t, set_next_type, void, /** * Compute the length of the whole payload */ -static void compute_length(private_encryption_payload_t *this) +static void compute_length(private_encrypted_payload_t *this) { enumerator_t *enumerator; payload_t *payload; @@ -220,15 +216,15 @@ static void compute_length(private_encryption_payload_t *this) this->payload_length = length; } -METHOD2(payload_t, encryption_payload_t, get_length, size_t, - private_encryption_payload_t *this) +METHOD2(payload_t, encrypted_payload_t, get_length, size_t, + private_encrypted_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) +METHOD(encrypted_payload_t, add_payload, void, + private_encrypted_payload_t *this, payload_t *payload) { payload_t *last_payload; @@ -246,8 +242,8 @@ METHOD(encryption_payload_t, add_payload, void, compute_length(this); } -METHOD(encryption_payload_t, remove_payload, payload_t *, - private_encryption_payload_t *this) +METHOD(encrypted_payload_t, remove_payload, payload_t *, + private_encrypted_payload_t *this) { payload_t *payload; @@ -262,7 +258,7 @@ METHOD(encryption_payload_t, remove_payload, payload_t *, /** * Generate payload before encryption */ -static chunk_t generate(private_encryption_payload_t *this, +static chunk_t generate(private_encrypted_payload_t *this, generator_t *generator) { payload_t *current, *next; @@ -285,16 +281,16 @@ static chunk_t generate(private_encryption_payload_t *this, generator->generate_payload(generator, current); chunk = generator->get_chunk(generator, &lenpos); - DBG2(DBG_ENC, "generated content in encryption payload"); + DBG2(DBG_ENC, "generated content in encrypted payload"); } enumerator->destroy(enumerator); return chunk; } /** - * Append the encryption payload header to the associated data + * Append the encrypted payload header to the associated data */ -static chunk_t append_header(private_encryption_payload_t *this, chunk_t assoc) +static chunk_t append_header(private_encrypted_payload_t *this, chunk_t assoc) { struct { u_int8_t next_payload; @@ -308,8 +304,8 @@ static chunk_t append_header(private_encryption_payload_t *this, chunk_t assoc) 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) +METHOD(encrypted_payload_t, encrypt, status_t, + private_encrypted_payload_t *this, u_int64_t mid, chunk_t assoc) { chunk_t iv, plain, padding, icv, crypt; generator_t *generator; @@ -319,21 +315,21 @@ METHOD(encryption_payload_t, encrypt, status_t, if (this->aead == NULL) { - DBG1(DBG_ENC, "encrypting encryption payload failed, transform missing"); + DBG1(DBG_ENC, "encrypting encrypted 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"); + DBG1(DBG_ENC, "encrypting encrypted 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"); + DBG1(DBG_ENC, "encrypting encrypted payload failed, no IV generator"); return NOT_SUPPORTED; } @@ -367,7 +363,7 @@ METHOD(encryption_payload_t, encrypt, status_t, 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"); + DBG1(DBG_ENC, "encrypting encrypted payload failed, no IV or padding"); rng->destroy(rng); free(assoc.ptr); return FAILED; @@ -375,7 +371,7 @@ METHOD(encryption_payload_t, encrypt, status_t, padding.ptr[padding.len - 1] = padding.len - 1; rng->destroy(rng); - DBG3(DBG_ENC, "encryption payload encryption:"); + DBG3(DBG_ENC, "encrypted payload encryption:"); DBG3(DBG_ENC, "IV %B", &iv); DBG3(DBG_ENC, "plain %B", &plain); DBG3(DBG_ENC, "padding %B", &padding); @@ -395,8 +391,8 @@ METHOD(encryption_payload_t, encrypt, status_t, return SUCCESS; } -METHOD(encryption_payload_t, encrypt_v1, status_t, - private_encryption_payload_t *this, u_int64_t mid, chunk_t iv) +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; @@ -441,7 +437,7 @@ METHOD(encryption_payload_t, encrypt_v1, status_t, /** * Parse the payloads after decryption. */ -static status_t parse(private_encryption_payload_t *this, chunk_t plain) +static status_t parse(private_encrypted_payload_t *this, chunk_t plain) { parser_t *parser; payload_type_t type; @@ -476,19 +472,19 @@ static status_t parse(private_encryption_payload_t *this, chunk_t plain) this->payloads->insert_last(this->payloads, payload); } parser->destroy(parser); - DBG2(DBG_ENC, "parsed content of encryption payload"); + DBG2(DBG_ENC, "parsed content of encrypted payload"); return SUCCESS; } -METHOD(encryption_payload_t, decrypt, status_t, - private_encryption_payload_t *this, chunk_t assoc) +METHOD(encrypted_payload_t, decrypt, status_t, + private_encrypted_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"); + DBG1(DBG_ENC, "decrypting encrypted payload failed, transform missing"); return INVALID_STATE; } @@ -511,13 +507,13 @@ METHOD(encryption_payload_t, decrypt, status_t, if (iv.len + icv.len > this->encrypted.len || (crypt.len - icv.len) % bs) { - DBG1(DBG_ENC, "decrypting encryption payload failed, invalid length"); + DBG1(DBG_ENC, "decrypting encrypted payload failed, invalid length"); return FAILED; } assoc = append_header(this, assoc); - DBG3(DBG_ENC, "encryption payload decryption:"); + DBG3(DBG_ENC, "encrypted payload decryption:"); DBG3(DBG_ENC, "IV %B", &iv); DBG3(DBG_ENC, "encrypted %B", &crypt); DBG3(DBG_ENC, "ICV %B", &icv); @@ -525,7 +521,7 @@ METHOD(encryption_payload_t, decrypt, status_t, if (!this->aead->decrypt(this->aead, crypt, assoc, iv, NULL)) { - DBG1(DBG_ENC, "verifying encryption payload integrity failed"); + DBG1(DBG_ENC, "verifying encrypted payload integrity failed"); free(assoc.ptr); return FAILED; } @@ -535,7 +531,7 @@ METHOD(encryption_payload_t, decrypt, status_t, padding.len = plain.ptr[plain.len - 1] + 1; if (padding.len > plain.len) { - DBG1(DBG_ENC, "decrypting encryption payload failed, " + DBG1(DBG_ENC, "decrypting encrypted payload failed, " "padding invalid %B", &crypt); return PARSE_ERROR; } @@ -548,8 +544,8 @@ METHOD(encryption_payload_t, decrypt, status_t, return parse(this, plain); } -METHOD(encryption_payload_t, decrypt_v1, status_t, - private_encryption_payload_t *this, chunk_t iv) +METHOD(encrypted_payload_t, decrypt_v1, status_t, + private_encrypted_payload_t *this, chunk_t iv) { if (this->aead == NULL) { @@ -578,14 +574,14 @@ METHOD(encryption_payload_t, decrypt_v1, status_t, return parse(this, this->encrypted); } -METHOD(encryption_payload_t, set_transform, void, - private_encryption_payload_t *this, aead_t* aead) +METHOD(encrypted_payload_t, set_transform, void, + private_encrypted_payload_t *this, aead_t* aead) { this->aead = aead; } -METHOD2(payload_t, encryption_payload_t, destroy, void, - private_encryption_payload_t *this) +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); @@ -595,9 +591,9 @@ METHOD2(payload_t, encryption_payload_t, destroy, void, /* * Described in header */ -encryption_payload_t *encryption_payload_create(payload_type_t type) +encrypted_payload_t *encrypted_payload_create(payload_type_t type) { - private_encryption_payload_t *this; + private_encrypted_payload_t *this; INIT(this, .public = { diff --git a/src/libcharon/encoding/payloads/encryption_payload.h b/src/libcharon/encoding/payloads/encrypted_payload.h similarity index 69% rename from src/libcharon/encoding/payloads/encryption_payload.h rename to src/libcharon/encoding/payloads/encrypted_payload.h index ee44c2de1..57bcc2158 100644 --- a/src/libcharon/encoding/payloads/encryption_payload.h +++ b/src/libcharon/encoding/payloads/encrypted_payload.h @@ -16,23 +16,23 @@ */ /** - * @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 #include #include /** - * 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 +44,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 +59,14 @@ 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); /** * 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 +78,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 +92,20 @@ 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 */ -encryption_payload_t *encryption_payload_create(payload_type_t type); +encrypted_payload_t *encrypted_payload_create(payload_type_t type); -#endif /** ENCRYPTION_PAYLOAD_H_ @}*/ +#endif /** ENCRYPTED_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/encoding/payloads/payload.c b/src/libcharon/encoding/payloads/payload.c index fd616620d..e687f2707 100644 --- a/src/libcharon/encoding/payloads/payload.c +++ b/src/libcharon/encoding/payloads/payload.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -244,7 +244,7 @@ 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(); default: diff --git a/src/libcharon/encoding/payloads/payload.h b/src/libcharon/encoding/payloads/payload.h index d9dd619f7..f22470e01 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, From 127a98dc906065b494acbb5c71c24a30a9c07885 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 10 Jun 2014 15:53:11 +0200 Subject: [PATCH 02/24] ikev1: Move fragment generation to message_t --- conf/options/charon.opt | 7 +- src/libcharon/bus/bus.h | 6 +- src/libcharon/encoding/message.c | 106 +++++++++- src/libcharon/encoding/message.h | 21 +- src/libcharon/sa/ikev1/task_manager_v1.c | 254 +++++++++++------------ 5 files changed, 253 insertions(+), 141 deletions(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index 1eb1b8877..b7fb7ef75 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -85,9 +85,10 @@ charon.flush_auth_cfg = no this might conflict with plugins that later need access to e.g. the used certificates. -charon.fragment_size = 512 - Maximum size (in bytes) of a sent fragment when using the proprietary IKEv1 - fragmentation extension. +charon.fragment_size = 0 + Maximum size (complete IP datagram size in bytes) of a sent fragment when + using the proprietary IKEv1 fragmentation extension (0 for address family + specific default values). If specified this limit is used for IPv4 and IPv6. charon.group Name of the group the daemon changes to after startup. diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h index 1a6711a41..e1d221ca5 100644 --- a/src/libcharon/bus/bus.h +++ b/src/libcharon/bus/bus.h @@ -101,9 +101,11 @@ enum alert_t { /** received IKE message with invalid body, argument is message_t*, * followed by a status_t result returned by message_t.parse_body(). */ ALERT_PARSE_ERROR_BODY, - /** sending a retransmit for a message, argument is packet_t */ + /** sending a retransmit for a message, argument is packet_t, if the message + * got fragmented only the first fragment is passed */ ALERT_RETRANSMIT_SEND, - /** sending retransmits timed out, argument is packet_t, if available */ + /** sending retransmits timed out, argument is packet_t, if available and if + * the message got fragmented only the first fragment is passed */ ALERT_RETRANSMIT_SEND_TIMEOUT, /** received a retransmit for a message, argument is message_t */ ALERT_RETRANSMIT_RECEIVE, diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index cecb55470..435b98205 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,7 @@ #include "message.h" #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include /** * Max number of notify payloads per IKEv2 message @@ -1652,6 +1654,107 @@ METHOD(message_t, generate, status_t, 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_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); + return message; +} + +/** + * Create a single fragment with the given data + */ +static message_t *create_fragment(private_message_t *this, u_int8_t num, + bool last, chunk_t data) +{ + fragment_payload_t *fragment; + message_t *message; + + fragment = fragment_payload_create_from_data(num, last, data); + message = clone_message(this); + message->add_payload(message, (payload_t*)fragment); + return message; +} + +/** + * Destroy all messages in the given array + */ +CALLBACK(destroy_fragments, void, + array_t *fragments) +{ + array_destroy_offset(fragments, offsetof(message_t, destroy)); +} + +METHOD(message_t, fragment, status_t, + private_message_t *this, size_t frag_len, enumerator_t **fragments) +{ + array_t *messages; + message_t *fragment; + u_int8_t num, count; + host_t *src, *dst; + chunk_t data; + size_t len; + + if (!is_encoded(this) || this->major_version == IKEV2_MAJOR_VERSION) + { + return INVALID_STATE; + } + + 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) */ + frag_len -= (src->get_family(src) == AF_INET) ? 20 : 40; + /* 8 (UDP header) + 28 (IKE header) */ + frag_len -= 36; + if (dst->get_port(dst) != IKEV2_UDP_PORT && + src->get_port(src) != IKEV2_UDP_PORT) + { /* reduce length due to non-ESP marker */ + frag_len -= 4; + } + + data = this->packet->get_data(this->packet); + if (data.len <= frag_len) + { + return ALREADY_DONE; + } + /* overhead for the fragmentation payload header */ + frag_len -= 8; + + count = data.len / frag_len + (data.len % frag_len ? 1 : 0); + messages = array_create(0, count); + DBG2(DBG_ENC, "splitting IKE message with length of %zu bytes into " + "%hhu fragments", data.len, count); + for (num = 1; num <= count; num++) + { + len = min(data.len, frag_len); + fragment = create_fragment(this, num, num == count, + chunk_create(data.ptr, len)); + array_insert(messages, ARRAY_TAIL, fragment); + data = chunk_skip(data, len); + } + *fragments = enumerator_create_cleaner(array_create_enumerator(messages), + destroy_fragments, messages); + return SUCCESS; +} + METHOD(message_t, get_packet, packet_t*, private_message_t *this) { @@ -2195,6 +2298,7 @@ message_t *message_create_from_packet(packet_t *packet) .disable_sort = _disable_sort, .generate = _generate, .is_encoded = _is_encoded, + .fragment = _fragment, .set_source = _set_source, .get_source = _get_source, .set_destination = _set_destination, diff --git a/src/libcharon/encoding/message.h b/src/libcharon/encoding/message.h index bc4aa08bf..bd329657a 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 @@ -264,6 +264,25 @@ struct message_t { */ bool (*is_encoded)(message_t *this); + /** + * Split the (generated) message into fragments of the given size (total IP + * datagram length). + * + * @note Only supported for IKEv1 at the moment. + * + * @param frag_len fragment length (maximum total IP datagram length), 0 + * for default value depending on address family + * @param fragments receives an enumerator with message_t* (not generated), + * which are owned by the enumerator + * @return + * - SUCCESS if message could be fragmented + * - ALREADY_DONE if message does not need to be fragmented + * - INVALID_STATE if message was not generated or is IKEv2 + * - FAILED if fragmentation failed + */ + status_t (*fragment)(message_t *this, size_t frag_len, + enumerator_t **fragments); + /** * Gets the source host informations. * diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 2dd393b8d..af9db65a6 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2013 Tobias Brunner + * Copyright (C) 2007-2014 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -40,6 +40,7 @@ #include #include +#include /** * Number of old messages hashes we keep for retransmission. @@ -55,15 +56,6 @@ */ #define MAX_PACKET 10000 -/** - * Maximum size of fragment data when sending packets (currently the same is - * used for IPv4 and IPv6, even though the latter has a higher minimum datagram - * size). 576 (= min. IPv4) - 20 (= IP header) - 8 (= UDP header) - - * - 28 (= IKE header) - 8 (= fragment header) = 512 - * This is reduced by 4 in case of NAT-T (due to the non-ESP marker). - */ -#define MAX_FRAGMENT_SIZE 512 - /** * First sequence number of responding packets. * @@ -127,9 +119,9 @@ struct private_task_manager_t { u_int32_t hash; /** - * packet for retransmission + * packet(s) for retransmission */ - packet_t *packet; + array_t *packets; /** * Sequence number of the last sent message @@ -173,9 +165,9 @@ struct private_task_manager_t { u_int retransmitted; /** - * packet for retransmission + * packet(s) for retransmission */ - packet_t *packet; + array_t *packets; /** * type of the initiated exchange @@ -304,6 +296,19 @@ static void clear_fragments(private_task_manager_t *this, u_int16_t id) this->frag.id = id; } +/** + * Reset retransmission packet list + */ +static void clear_packets(array_t *array) +{ + packet_t *packet; + + while (array_remove(array, ARRAY_TAIL, &packet)) + { + packet->destroy(packet); + } +} + METHOD(task_manager_t, flush_queue, void, private_task_manager_t *this, task_queue_t queue) { @@ -321,8 +326,7 @@ METHOD(task_manager_t, flush_queue, void, list = this->active_tasks; /* cancel pending retransmits */ this->initiating.type = EXCHANGE_TYPE_UNDEFINED; - DESTROY_IF(this->initiating.packet); - this->initiating.packet = NULL; + clear_packets(this->initiating.packets); break; case TASK_QUEUE_PASSIVE: list = this->passive_tasks; @@ -373,45 +377,39 @@ static bool activate_task(private_task_manager_t *this, task_type_t type) } /** - * Send a single fragment with the given data + * Send packets in the given array (they get cloned) */ -static bool send_fragment(private_task_manager_t *this, bool request, - host_t *src, host_t *dst, fragment_payload_t *fragment) +static void send_packets(private_task_manager_t *this, array_t *packets) { - message_t *message; + enumerator_t *enumerator; packet_t *packet; - status_t status; - message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION); - /* other implementations seem to just use 0 as message ID, so here we go */ - message->set_message_id(message, 0); - message->set_request(message, request); - message->set_source(message, src->clone(src)); - message->set_destination(message, dst->clone(dst)); - message->set_exchange_type(message, this->frag.exchange); - message->add_payload(message, (payload_t*)fragment); - - status = this->ike_sa->generate_message(this->ike_sa, message, &packet); - if (status != SUCCESS) + enumerator = array_create_enumerator(packets); + while (enumerator->enumerate(enumerator, &packet)) { - DBG1(DBG_IKE, "failed to generate IKE fragment"); - message->destroy(message); - return FALSE; + charon->sender->send(charon->sender, packet->clone(packet)); } - charon->sender->send(charon->sender, packet); - message->destroy(message); - return TRUE; + enumerator->destroy(enumerator); } /** - * Send a packet, if supported and required do so in fragments + * Generates the given message and stores packet(s) in the given array */ -static bool send_packet(private_task_manager_t *this, bool request, - packet_t *packet) +static bool generate_message(private_task_manager_t *this, message_t *message, + array_t **packets) { - bool use_frags = FALSE; + bool use_frags = FALSE, result = TRUE; ike_cfg_t *ike_cfg; - chunk_t data; + enumerator_t *fragments; + message_t *fragment; + packet_t *packet; + status_t status; + + if (this->ike_sa->generate_message(this->ike_sa, message, + &packet) != SUCCESS) + { + return FALSE; + } ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); if (ike_cfg) @@ -423,60 +421,61 @@ static bool send_packet(private_task_manager_t *this, bool request, break; case FRAGMENTATION_YES: use_frags = this->ike_sa->supports_extension(this->ike_sa, - EXT_IKE_FRAGMENTATION); + EXT_IKE_FRAGMENTATION); break; default: break; } } - data = packet->get_data(packet); - if (data.len > this->frag.size && use_frags) + + if (!use_frags) { - fragment_payload_t *fragment; - u_int8_t num, count; - size_t len, frag_size; - host_t *src, *dst; - - src = packet->get_source(packet); - dst = packet->get_destination(packet); - - frag_size = this->frag.size; - if (dst->get_port(dst) != IKEV2_UDP_PORT && - src->get_port(src) != IKEV2_UDP_PORT) - { /* reduce size due to non-ESP marker */ - frag_size -= 4; - } - count = data.len / frag_size + (data.len % frag_size ? 1 : 0); - - DBG1(DBG_IKE, "sending IKE message with length of %zu bytes in " - "%hhu fragments", data.len, count); - for (num = 1; num <= count; num++) - { - len = min(data.len, frag_size); - fragment = fragment_payload_create_from_data(num, num == count, - chunk_create(data.ptr, len)); - if (!send_fragment(this, request, src, dst, fragment)) - { - packet->destroy(packet); - return FALSE; - } - data = chunk_skip(data, len); - } - packet->destroy(packet); + array_insert_create(packets, ARRAY_TAIL, packet); return TRUE; } - charon->sender->send(charon->sender, packet); - return TRUE; + /* 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 */ + message->set_exchange_type(message, this->frag.exchange); + status = message->fragment(message, this->frag.size, &fragments); + if (status == ALREADY_DONE) + { + array_insert_create(packets, ARRAY_TAIL, packet); + return TRUE; + } + else if (status != SUCCESS) + { + packet->destroy(packet); + return FALSE; + } + packet->destroy(packet); + + while (fragments->enumerate(fragments, &fragment)) + { + status = this->ike_sa->generate_message(this->ike_sa, fragment, + &packet); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "failed to generate IKE fragment"); + result = FALSE; + break; + } + array_insert_create(packets, ARRAY_TAIL, packet); + } + fragments->destroy(fragments); + return result; } /** - * Retransmit a packet, either as initiator or as responder + * Retransmit a packet (or its fragments) */ -static status_t retransmit_packet(private_task_manager_t *this, bool request, - u_int32_t seqnr, u_int mid, u_int retransmitted, packet_t *packet) +static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, + u_int mid, u_int retransmitted, array_t *packets) { + packet_t *packet; u_int32_t t; + array_get(packets, 0, &packet); if (retransmitted > this->retransmit_tries) { DBG1(DBG_IKE, "giving up after %u retransmits", retransmitted - 1); @@ -492,10 +491,7 @@ static status_t retransmit_packet(private_task_manager_t *this, bool request, mid, seqnr < RESPONDING_SEQ ? seqnr : seqnr - RESPONDING_SEQ); charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet); } - if (!send_packet(this, request, packet->clone(packet))) - { - return DESTROY_ME; - } + send_packets(this, packets); lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*) retransmit_job_create(seqnr, this->ike_sa->get_id(this->ike_sa)), t); return NEED_MORE; @@ -506,20 +502,22 @@ METHOD(task_manager_t, retransmit, status_t, { status_t status = SUCCESS; - if (seqnr == this->initiating.seqnr && this->initiating.packet) + if (seqnr == this->initiating.seqnr && + array_count(this->initiating.packets)) { - status = retransmit_packet(this, TRUE, seqnr, this->initiating.mid, - this->initiating.retransmitted, this->initiating.packet); + status = retransmit_packet(this, seqnr, this->initiating.mid, + this->initiating.retransmitted, this->initiating.packets); if (status == NEED_MORE) { this->initiating.retransmitted++; status = SUCCESS; } } - if (seqnr == this->responding.seqnr && this->responding.packet) + if (seqnr == this->responding.seqnr && + array_count(this->responding.packets)) { - status = retransmit_packet(this, FALSE, seqnr, this->responding.mid, - this->responding.retransmitted, this->responding.packet); + status = retransmit_packet(this, seqnr, this->responding.mid, + this->responding.retransmitted, this->responding.packets); if (status == NEED_MORE) { this->responding.retransmitted++; @@ -586,7 +584,6 @@ METHOD(task_manager_t, initiate, status_t, task_t *task; message_t *message; host_t *me, *other; - status_t status; exchange_type_t exchange = EXCHANGE_TYPE_UNDEFINED; bool new_mid = FALSE, expect_response = FALSE, cancelled = FALSE, keep = FALSE; @@ -790,10 +787,8 @@ METHOD(task_manager_t, initiate, status_t, return initiate(this); } - DESTROY_IF(this->initiating.packet); - status = this->ike_sa->generate_message(this->ike_sa, message, - &this->initiating.packet); - if (status != SUCCESS) + clear_packets(this->initiating.packets); + if (!generate_message(this, message, &this->initiating.packets)) { /* message generation failed. There is nothing more to do than to * close the SA */ @@ -811,13 +806,12 @@ METHOD(task_manager_t, initiate, status_t, } if (keep) { /* keep the packet for retransmission, the responder might request it */ - send_packet(this, TRUE, - this->initiating.packet->clone(this->initiating.packet)); + send_packets(this, this->initiating.packets); } else { - send_packet(this, TRUE, this->initiating.packet); - this->initiating.packet = NULL; + send_packets(this, this->initiating.packets); + clear_packets(this->initiating.packets); } message->destroy(message); @@ -848,7 +842,6 @@ static status_t build_response(private_task_manager_t *this, message_t *request) message_t *message; host_t *me, *other; bool delete = FALSE, cancelled = FALSE, expect_request = FALSE; - status_t status; me = request->get_destination(request); other = request->get_source(request); @@ -900,28 +893,25 @@ static status_t build_response(private_task_manager_t *this, message_t *request) } enumerator->destroy(enumerator); - DESTROY_IF(this->responding.packet); - this->responding.packet = NULL; + clear_packets(this->responding.packets); if (cancelled) { message->destroy(message); return initiate(this); } - status = this->ike_sa->generate_message(this->ike_sa, message, - &this->responding.packet); - message->destroy(message); - if (status != SUCCESS) + if (!generate_message(this, message, &this->responding.packets)) { + message->destroy(message); charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; } + message->destroy(message); if (expect_request && !delete) { return retransmit(this, this->responding.seqnr); } - send_packet(this, FALSE, - this->responding.packet->clone(this->responding.packet)); + send_packets(this, this->responding.packets); if (delete) { return DESTROY_ME; @@ -937,7 +927,7 @@ static void send_notify(private_task_manager_t *this, message_t *request, notify_type_t type) { message_t *response; - packet_t *packet; + array_t *packets = NULL; host_t *me, *other; u_int32_t mid; @@ -973,11 +963,12 @@ static void send_notify(private_task_manager_t *this, message_t *request, } response->set_source(response, me->clone(me)); response->set_destination(response, other->clone(other)); - if (this->ike_sa->generate_message(this->ike_sa, response, - &packet) == SUCCESS) + if (generate_message(this, response, &packets)) { - send_packet(this, TRUE, packet); + send_packets(this, packets); } + clear_packets(packets); + array_destroy(packets); response->destroy(response); } @@ -1164,8 +1155,7 @@ static status_t process_request(private_task_manager_t *this, else { /* We don't send a response, so don't retransmit one if we get * the same message again. */ - DESTROY_IF(this->responding.packet); - this->responding.packet = NULL; + clear_packets(this->responding.packets); } if (this->passive_tasks->get_count(this->passive_tasks) == 0 && this->queued_tasks->get_count(this->queued_tasks) > 0) @@ -1237,8 +1227,7 @@ static status_t process_response(private_task_manager_t *this, enumerator->destroy(enumerator); this->initiating.type = EXCHANGE_TYPE_UNDEFINED; - DESTROY_IF(this->initiating.packet); - this->initiating.packet = NULL; + clear_packets(this->initiating.packets); if (this->queued && this->active_tasks->get_count(this->active_tasks) == 0) { @@ -1435,15 +1424,14 @@ METHOD(task_manager_t, process_message, status_t, { if (this->initiating.old_hashes[i] == hash) { - if (this->initiating.packet && + if (array_count(this->initiating.packets) && i == (this->initiating.old_hash_pos % MAX_OLD_HASHES) && (msg->get_exchange_type(msg) == QUICK_MODE || msg->get_exchange_type(msg) == AGGRESSIVE)) { DBG1(DBG_IKE, "received retransmit of response with ID %u, " "resending last request", mid); - send_packet(this, TRUE, - this->initiating.packet->clone(this->initiating.packet)); + send_packets(this, this->initiating.packets); return SUCCESS; } DBG1(DBG_IKE, "received retransmit of response with ID %u, " @@ -1484,20 +1472,18 @@ METHOD(task_manager_t, process_message, status_t, { if (hash == this->responding.hash) { - if (this->responding.packet) + if (array_count(this->responding.packets)) { DBG1(DBG_IKE, "received retransmit of request with ID %u, " "retransmitting response", mid); - send_packet(this, FALSE, - this->responding.packet->clone(this->responding.packet)); + send_packets(this, this->responding.packets); } - else if (this->initiating.packet && + else if (array_count(this->initiating.packets) && this->initiating.type == INFORMATIONAL_V1) { DBG1(DBG_IKE, "received retransmit of DPD request, " "retransmitting response"); - send_packet(this, TRUE, - this->initiating.packet->clone(this->initiating.packet)); + send_packets(this, this->initiating.packets); } else { @@ -1984,12 +1970,10 @@ METHOD(task_manager_t, reset, void, task_t *task; /* reset message counters and retransmit packets */ - DESTROY_IF(this->responding.packet); - DESTROY_IF(this->initiating.packet); - this->responding.packet = NULL; + clear_packets(this->responding.packets); + clear_packets(this->initiating.packets); this->responding.seqnr = RESPONDING_SEQ; this->responding.retransmitted = 0; - this->initiating.packet = NULL; this->initiating.mid = 0; this->initiating.seqnr = 0; this->initiating.retransmitted = 0; @@ -2048,8 +2032,10 @@ METHOD(task_manager_t, destroy, void, clear_fragments(this, 0); DESTROY_IF(this->queued); - DESTROY_IF(this->responding.packet); - DESTROY_IF(this->initiating.packet); + clear_packets(this->responding.packets); + array_destroy(this->responding.packets); + clear_packets(this->initiating.packets); + array_destroy(this->initiating.packets); DESTROY_IF(this->rng); free(this); } @@ -2099,7 +2085,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .max_packet = lib->settings->get_int(lib->settings, "%s.max_packet", MAX_PACKET, lib->ns), .size = lib->settings->get_int(lib->settings, - "%s.fragment_size", MAX_FRAGMENT_SIZE, lib->ns), + "%s.fragment_size", 0, lib->ns), }, .ike_sa = ike_sa, .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), From e0b35142c1c72144bf251d23aa5b494b525d3a92 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 12 Jun 2014 10:01:18 +0200 Subject: [PATCH 03/24] message: Make packet argument optional in generate() --- src/libcharon/encoding/message.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 435b98205..44221b6ae 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -1650,7 +1650,10 @@ METHOD(message_t, generate, status_t, } } generator->destroy(generator); - *packet = this->packet->clone(this->packet); + if (packet) + { + *packet = this->packet->clone(this->packet); + } return SUCCESS; } From 2e7a2c06a1121b2b1b6ce8f17860dbda081cf89d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 12 Jun 2014 10:14:00 +0200 Subject: [PATCH 04/24] message: fragment() generates message and fragments and caches them --- src/libcharon/encoding/message.c | 91 ++++++++++++++++++------ src/libcharon/encoding/message.h | 34 +++++++-- src/libcharon/sa/ikev1/task_manager_v1.c | 50 ++++--------- 3 files changed, 113 insertions(+), 62 deletions(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 44221b6ae..31f8c14aa 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -877,6 +877,11 @@ struct private_message_t { */ packet_t *packet; + /** + * Array of generated fragments (if any), as packet_t*. + */ + array_t *fragments; + /** * Linked List where payload data are stored in. */ @@ -1051,6 +1056,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) { @@ -1341,6 +1352,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, @@ -1669,6 +1682,7 @@ static message_t *clone_message(private_message_t *this) 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)); @@ -1685,36 +1699,64 @@ static message_t *create_fragment(private_message_t *this, u_int8_t num, { fragment_payload_t *fragment; message_t *message; + peer_cfg_t *peer_cfg; + ike_sa_t *ike_sa; fragment = fragment_payload_create_from_data(num, last, data); message = clone_message(this); + /* 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); + } message->add_payload(message, (payload_t*)fragment); return message; } /** - * Destroy all messages in the given array + * Destroy all fragments */ -CALLBACK(destroy_fragments, void, - array_t *fragments) +static void clear_fragments(private_message_t *this) { - array_destroy_offset(fragments, offsetof(message_t, destroy)); + array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); + this->fragments = NULL; } METHOD(message_t, fragment, status_t, - private_message_t *this, size_t frag_len, enumerator_t **fragments) + private_message_t *this, keymat_t *keymat, size_t frag_len, + enumerator_t **fragments) { - array_t *messages; message_t *fragment; + packet_t *packet; u_int8_t num, count; host_t *src, *dst; chunk_t data; + status_t status; size_t len; - if (!is_encoded(this) || this->major_version == IKEV2_MAJOR_VERSION) + if (this->major_version == IKEV2_MAJOR_VERSION) { return INVALID_STATE; } + clear_fragments(this); + + if (!is_encoded(this)) + { + status = generate(this, keymat, NULL); + if (status != SUCCESS) + { + return status; + } + } src = this->packet->get_source(this->packet); dst = this->packet->get_destination(this->packet); @@ -1736,13 +1778,14 @@ METHOD(message_t, fragment, status_t, data = this->packet->get_data(this->packet); if (data.len <= frag_len) { - return ALREADY_DONE; + *fragments = enumerator_create_single(this->packet, NULL); + return SUCCESS; } /* overhead for the fragmentation payload header */ frag_len -= 8; count = data.len / frag_len + (data.len % frag_len ? 1 : 0); - messages = array_create(0, count); + this->fragments = array_create(0, count); DBG2(DBG_ENC, "splitting IKE message with length of %zu bytes into " "%hhu fragments", data.len, count); for (num = 1; num <= count; num++) @@ -1750,34 +1793,39 @@ METHOD(message_t, fragment, status_t, len = min(data.len, frag_len); fragment = create_fragment(this, num, num == count, chunk_create(data.ptr, len)); - array_insert(messages, ARRAY_TAIL, fragment); + status = fragment->generate(fragment, keymat, &packet); + fragment->destroy(fragment); + if (status != SUCCESS) + { + DBG1(DBG_ENC, "failed to generate IKE fragment"); + clear_fragments(this); + return FAILED; + } + array_insert(this->fragments, ARRAY_TAIL, packet); data = chunk_skip(data, len); } - *fragments = enumerator_create_cleaner(array_create_enumerator(messages), - destroy_fragments, messages); + *fragments = array_create_enumerator(this->fragments); return SUCCESS; } METHOD(message_t, get_packet, packet_t*, private_message_t *this) { - if (this->packet == NULL) - { - return NULL; - } 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) { @@ -2264,6 +2312,7 @@ METHOD(message_t, destroy, void, { DESTROY_IF(this->ike_sa_id); this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); + array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); this->packet->destroy(this->packet); this->parser->destroy(this->parser); free(this); @@ -2301,6 +2350,7 @@ 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, .set_source = _set_source, .get_source = _get_source, @@ -2314,6 +2364,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, diff --git a/src/libcharon/encoding/message.h b/src/libcharon/encoding/message.h index bd329657a..e8db51ce5 100644 --- a/src/libcharon/encoding/message.h +++ b/src/libcharon/encoding/message.h @@ -265,24 +265,37 @@ struct message_t { bool (*is_encoded)(message_t *this); /** - * Split the (generated) message into fragments of the given size (total IP + * Generates the message split into fragments of the given size (total IP * datagram length). * * @note Only supported for IKEv1 at the moment. * + * @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 message_t* (not generated), + * @param fragments receives an enumerator with generated packet_t*, * which are owned by the enumerator * @return * - SUCCESS if message could be fragmented - * - ALREADY_DONE if message does not need to be fragmented - * - INVALID_STATE if message was not generated or is IKEv2 + * - INVALID_STATE if message is IKEv2 * - FAILED if fragmentation failed + * - and the possible return values of generate() */ - status_t (*fragment)(message_t *this, size_t frag_len, + 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); + /** * Gets the source host informations. * @@ -356,11 +369,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. @@ -369,6 +382,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. */ diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index af9db65a6..4e3511858 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -401,16 +401,9 @@ static bool generate_message(private_task_manager_t *this, message_t *message, bool use_frags = FALSE, result = TRUE; ike_cfg_t *ike_cfg; enumerator_t *fragments; - message_t *fragment; packet_t *packet; status_t status; - if (this->ike_sa->generate_message(this->ike_sa, message, - &packet) != SUCCESS) - { - return FALSE; - } - ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); if (ike_cfg) { @@ -430,37 +423,24 @@ static bool generate_message(private_task_manager_t *this, message_t *message, if (!use_frags) { - array_insert_create(packets, ARRAY_TAIL, packet); - return TRUE; - } - /* 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 */ - message->set_exchange_type(message, this->frag.exchange); - status = message->fragment(message, this->frag.size, &fragments); - if (status == ALREADY_DONE) - { - array_insert_create(packets, ARRAY_TAIL, packet); - return TRUE; - } - else if (status != SUCCESS) - { - packet->destroy(packet); - return FALSE; - } - packet->destroy(packet); - - while (fragments->enumerate(fragments, &fragment)) - { - status = this->ike_sa->generate_message(this->ike_sa, fragment, - &packet); - if (status != SUCCESS) + if (this->ike_sa->generate_message(this->ike_sa, message, + &packet) != SUCCESS) { - DBG1(DBG_IKE, "failed to generate IKE fragment"); - result = FALSE; - break; + return FALSE; } array_insert_create(packets, ARRAY_TAIL, packet); + return TRUE; + } + message->set_ike_sa_id(message, this->ike_sa->get_id(this->ike_sa)); + status = message->fragment(message, this->ike_sa->get_keymat(this->ike_sa), + this->frag.size, &fragments); + if (status != SUCCESS) + { + return FALSE; + } + while (fragments->enumerate(fragments, &packet)) + { + array_insert_create(packets, ARRAY_TAIL, packet->clone(packet)); } fragments->destroy(fragments); return result; From 40bab9a176617501dc6b98685c3e9c9d6fdd59d9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 12 Jun 2014 16:28:27 +0200 Subject: [PATCH 05/24] ike: Move fragmentation to ike_sa_t The message() hook on bus_t is now called exactly once before (plain) and once after fragmenting (!plain), not twice for the complete message and again for each individual fragment, as was the case in earlier iterations. For inbound messages the hook is called once for each fragment (!plain) and twice for the reassembled message. --- src/libcharon/sa/ike_sa.c | 65 +++++++++++++++++++++++- src/libcharon/sa/ike_sa.h | 30 ++++++++--- src/libcharon/sa/ikev1/task_manager_v1.c | 61 +++------------------- 3 files changed, 94 insertions(+), 62 deletions(-) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 516b2435b..6f4ffb845 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2013 Tobias Brunner + * Copyright (C) 2006-2014 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -251,6 +251,11 @@ struct private_ike_sa_t { * Flush auth configs once established? */ bool flush_auth_cfg; + + /** + * Maximum length of a single fragment, 0 for address-specific defaults + */ + size_t fragment_size; }; /** @@ -994,6 +999,61 @@ METHOD(ike_sa_t, generate_message, status_t, return status; } +static bool filter_fragments(private_ike_sa_t *this, packet_t **fragment, + packet_t **packet) +{ + *packet = (*fragment)->clone(*fragment); + set_dscp(this, *packet); + return TRUE; +} + +METHOD(ike_sa_t, generate_message_fragmented, status_t, + private_ike_sa_t *this, message_t *message, enumerator_t **packets) +{ + enumerator_t *fragments; + packet_t *packet; + status_t status; + bool use_frags = FALSE; + + if (this->ike_cfg && this->version == IKEV1) + { + switch (this->ike_cfg->fragmentation(this->ike_cfg)) + { + case FRAGMENTATION_FORCE: + use_frags = TRUE; + break; + case FRAGMENTATION_YES: + use_frags = supports_extension(this, EXT_IKE_FRAGMENTATION); + break; + default: + break; + } + } + if (!use_frags) + { + status = generate_message(this, message, &packet); + if (status != SUCCESS) + { + return status; + } + *packets = enumerator_create_single(packet, NULL); + return SUCCESS; + } + + this->stats[STAT_OUTBOUND] = time_monotonic(NULL); + message->set_ike_sa_id(message, this->ike_sa_id); + charon->bus->message(charon->bus, message, FALSE, TRUE); + status = message->fragment(message, this->keymat, this->fragment_size, + &fragments); + if (status == SUCCESS) + { + charon->bus->message(charon->bus, message, FALSE, FALSE); + *packets = enumerator_create_filter(fragments, (void*)filter_fragments, + this, NULL); + } + return status; +} + METHOD(ike_sa_t, set_kmaddress, void, private_ike_sa_t *this, host_t *local, host_t *remote) { @@ -2362,6 +2422,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .inherit_pre = _inherit_pre, .inherit_post = _inherit_post, .generate_message = _generate_message, + .generate_message_fragmented = _generate_message_fragmented, .reset = _reset, .get_unique_id = _get_unique_id, .add_virtual_ip = _add_virtual_ip, @@ -2407,6 +2468,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, "%s.retry_initiate_interval", 0, lib->ns), .flush_auth_cfg = lib->settings->get_bool(lib->settings, "%s.flush_auth_cfg", FALSE, lib->ns), + .fragment_size = lib->settings->get_int(lib->settings, + "%s.fragment_size", 0, lib->ns), ); if (version == IKEV2) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 15fb47484..7926301a7 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2012 Tobias Brunner + * Copyright (C) 2006-2014 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -756,7 +756,7 @@ struct ike_sa_t { status_t (*roam)(ike_sa_t *this, bool address); /** - * Processes a incoming IKEv2-Message. + * Processes an incoming IKE message. * * Message processing may fail. If a critical failure occurs, * process_message() return DESTROY_ME. Then the caller must @@ -768,10 +768,10 @@ struct ike_sa_t { * - FAILED * - DESTROY_ME if this IKE_SA MUST be deleted */ - status_t (*process_message) (ike_sa_t *this, message_t *message); + status_t (*process_message)(ike_sa_t *this, message_t *message); /** - * Generate a IKE message to send it to the peer. + * Generate an IKE message to send it to the peer. * * This method generates all payloads in the message and encrypts/signs * the packet. @@ -783,8 +783,26 @@ struct ike_sa_t { * - FAILED * - DESTROY_ME if this IKE_SA MUST be deleted */ - status_t (*generate_message) (ike_sa_t *this, message_t *message, - packet_t **packet); + status_t (*generate_message)(ike_sa_t *this, message_t *message, + packet_t **packet); + + /** + * Generate an IKE message to send it to the peer. If enabled and supported + * it will be fragmented. + * + * This method generates all payloads in the message and encrypts/signs + * the packet/fragments. + * + * @param message message to generate + * @param packets enumerator of generated packet_t* (are not destroyed + * with the enumerator) + * @return + * - SUCCESS + * - FAILED + * - DESTROY_ME if this IKE_SA MUST be deleted + */ + status_t (*generate_message_fragmented)(ike_sa_t *this, message_t *message, + enumerator_t **packets); /** * Retransmits a request. diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 4e3511858..498d6c47a 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -208,18 +208,6 @@ struct private_task_manager_t { */ size_t max_packet; - /** - * Maximum length of a single fragment (when sending) - */ - size_t size; - - /** - * The exchange type we use for fragments. Always the initial type even - * for fragmented quick mode or transaction messages (i.e. either - * ID_PROT or AGGRESSIVE) - */ - exchange_type_t exchange; - } frag; /** @@ -398,52 +386,20 @@ static void send_packets(private_task_manager_t *this, array_t *packets) static bool generate_message(private_task_manager_t *this, message_t *message, array_t **packets) { - bool use_frags = FALSE, result = TRUE; - ike_cfg_t *ike_cfg; enumerator_t *fragments; - packet_t *packet; - status_t status; + packet_t *fragment; - ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); - if (ike_cfg) - { - switch (ike_cfg->fragmentation(ike_cfg)) - { - case FRAGMENTATION_FORCE: - use_frags = TRUE; - break; - case FRAGMENTATION_YES: - use_frags = this->ike_sa->supports_extension(this->ike_sa, - EXT_IKE_FRAGMENTATION); - break; - default: - break; - } - } - - if (!use_frags) - { - if (this->ike_sa->generate_message(this->ike_sa, message, - &packet) != SUCCESS) - { - return FALSE; - } - array_insert_create(packets, ARRAY_TAIL, packet); - return TRUE; - } - message->set_ike_sa_id(message, this->ike_sa->get_id(this->ike_sa)); - status = message->fragment(message, this->ike_sa->get_keymat(this->ike_sa), - this->frag.size, &fragments); - if (status != SUCCESS) + if (this->ike_sa->generate_message_fragmented(this->ike_sa, message, + &fragments) != SUCCESS) { return FALSE; } - while (fragments->enumerate(fragments, &packet)) + while (fragments->enumerate(fragments, &fragment)) { - array_insert_create(packets, ARRAY_TAIL, packet->clone(packet)); + array_insert_create(packets, ARRAY_TAIL, fragment); } fragments->destroy(fragments); - return result; + return TRUE; } /** @@ -1046,7 +1002,6 @@ static status_t process_request(private_task_manager_t *this, this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t *)isakmp_natd_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); - this->frag.exchange = AGGRESSIVE; break; case QUICK_MODE: if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED) @@ -1623,7 +1578,6 @@ METHOD(task_manager_t, queue_ike, void, { queue_task(this, (task_t*)aggressive_mode_create(this->ike_sa, TRUE)); } - this->frag.exchange = AGGRESSIVE; } else { @@ -2061,11 +2015,8 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .seqnr = RESPONDING_SEQ, }, .frag = { - .exchange = ID_PROT, .max_packet = lib->settings->get_int(lib->settings, "%s.max_packet", MAX_PACKET, lib->ns), - .size = lib->settings->get_int(lib->settings, - "%s.fragment_size", 0, lib->ns), }, .ike_sa = ike_sa, .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), From 70e0be072bca5a1af768da244e3cbf140ea0e172 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 13 Jun 2014 16:00:59 +0200 Subject: [PATCH 06/24] ikev1: Move defragmentation to message_t --- src/libcharon/encoding/message.c | 196 ++++++++++++++++++++++- src/libcharon/encoding/message.h | 30 ++++ src/libcharon/sa/ikev1/task_manager_v1.c | 183 ++------------------- 3 files changed, 240 insertions(+), 169 deletions(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 31f8c14aa..2fdbeb607 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -23,6 +23,7 @@ #include "message.h" #include +#include #include #include #include @@ -66,6 +67,11 @@ */ #define MAX_NAT_D_PAYLOADS 10 +/** + * Maximum packet size for fragmented packets (same as in sockets) + */ +#define MAX_PACKET 10000 + /** * A payload rule defines the rules for a payload * in a specific message rule. It defines if and how @@ -804,6 +810,29 @@ 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. + */ + u_int8_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; @@ -879,6 +908,7 @@ struct private_message_t { /** * Array of generated fragments (if any), as packet_t*. + * If defragmenting (frag != NULL) this contains fragment_t* */ array_t *fragments; @@ -896,8 +926,41 @@ 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; }; +/** + * 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, u_int16_t id) +{ + array_destroy_function(this->fragments, (void*)fragment_destroy, NULL); + this->fragments = NULL; + this->message_id = id; + this->frag->last = 0; + this->frag->len = 0; +} + /** * Get the message rule that applies to this message */ @@ -1877,7 +1940,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; } @@ -2307,14 +2370,121 @@ METHOD(message_t, parse_body, status_t, return SUCCESS; } +METHOD(message_t, add_fragment, status_t, + private_message_t *this, message_t *message) +{ + fragment_payload_t *payload; + fragment_t *fragment; + bio_writer_t *writer; + host_t *src, *dst; + chunk_t data; + u_int8_t num; + int i, insert_at = -1; + + 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, payload->get_id(payload)); + /* we don't know the total number of fragments */ + this->fragments = array_create(0, 0); + } + + num = payload->get_number(payload); + if (!this->frag->last && payload->is_last(payload)) + { + this->frag->last = num; + } + + 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 #%hhu", num); + return NEED_MORE; + } + if (fragment->num > num) + { + insert_at = i; + break; + } + } + data = payload->get_data(payload); + 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, 0); + return FAILED; + } + INIT(fragment, + .num = num, + .data = chunk_clone(data), + ); + array_insert(this->fragments, insert_at, fragment); + + if (this->frag->last < array_count(this->fragments)) + { + /* there are some fragments missing */ + DBG1(DBG_ENC, "received fragment #%hhu, waiting for complete IKE " + "message", num); + return NEED_MORE; + } + + writer = bio_writer_create(this->frag->len); + DBG1(DBG_ENC, "received fragment #%hhu, reassembling fragmented IKE " + "message", num); + + for (i = 0; i < array_count(this->fragments); i++) + { + array_get(this->fragments, i, &fragment); + writer->write_data(writer, fragment->data); + } + src = message->get_source(message); + dst = message->get_destination(message); + this->packet->set_source(this->packet, src->clone(src)); + this->packet->set_destination(this->packet, dst->clone(dst)); + this->packet->set_data(this->packet, writer->extract_buf(writer)); + writer->destroy(writer); + this->parser->destroy(this->parser); + this->parser = parser_create(this->packet->get_data(this->packet)); + reset_defrag(this, 0); + free(this->frag); + this->frag = NULL; + + if (parse_header(this) != SUCCESS) + { + DBG1(DBG_IKE, "failed to parse header of reassembled IKE message"); + return FAILED; + } + return SUCCESS; +} + METHOD(message_t, destroy, void, private_message_t *this) { DESTROY_IF(this->ike_sa_id); this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy)); - array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); this->packet->destroy(this->packet); this->parser->destroy(this->parser); + if (this->frag) + { + reset_defrag(this, 0); + free(this->frag); + } + else + { + array_destroy_offset(this->fragments, offsetof(packet_t, destroy)); + } free(this); } @@ -2352,6 +2522,7 @@ message_t *message_create_from_packet(packet_t *packet) .is_encoded = _is_encoded, .is_fragmented = _is_fragmented, .fragment = _fragment, + .add_fragment = _add_fragment, .set_source = _set_source, .get_source = _get_source, .set_destination = _set_destination, @@ -2390,3 +2561,24 @@ 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)) + { + return NULL; + } + this = (private_message_t*)message_create( + fragment->get_major_version(fragment), + fragment->get_minor_version(fragment)); + INIT(this->frag, + .max_packet = lib->settings->get_int(lib->settings, + "%s.max_packet", MAX_PACKET, lib->ns), + ); + return &this->public; +} diff --git a/src/libcharon/encoding/message.h b/src/libcharon/encoding/message.h index e8db51ce5..69a8e93b1 100644 --- a/src/libcharon/encoding/message.h +++ b/src/libcharon/encoding/message.h @@ -296,6 +296,26 @@ struct message_t { */ 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. + * + * @note Only supported for IKEv1 at the moment. + * + * @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. * @@ -419,4 +439,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/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 498d6c47a..0f8e8bc6d 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -38,8 +38,6 @@ #include #include -#include -#include #include /** @@ -51,11 +49,6 @@ */ #define MAX_OLD_HASHES 2 -/** - * Maximum packet size for fragmented packets (same as in sockets) - */ -#define MAX_PACKET 10000 - /** * First sequence number of responding packets. * @@ -177,38 +170,9 @@ struct private_task_manager_t { } initiating; /** - * Data used to reassemble a fragmented message + * Message we are currently defragmenting, if any (only one at a time) */ - struct { - - /** - * Fragment ID (currently only one is supported at a time) - */ - u_int16_t id; - - /** - * The number of the last fragment (in case we receive the fragments out - * of order), since the first starts with 1 this defines the number of - * fragments we expect - */ - u_int8_t last; - - /** - * List of fragments (fragment_t*) - */ - linked_list_t *list; - - /** - * Length of all currently received fragments - */ - size_t len; - - /** - * Maximum length of a fragmented packet - */ - size_t max_packet; - - } frag; + message_t *defrag; /** * List of queued tasks not yet in action @@ -256,34 +220,6 @@ struct private_task_manager_t { u_int32_t dpd_recv; }; -/** - * 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 clear_fragments(private_task_manager_t *this, u_int16_t id) -{ - DESTROY_FUNCTION_IF(this->frag.list, (void*)fragment_destroy); - this->frag.list = NULL; - this->frag.last = 0; - this->frag.len = 0; - this->frag.id = id; -} - /** * Reset retransmission packet list */ @@ -1182,107 +1118,23 @@ static status_t process_response(private_task_manager_t *this, static status_t handle_fragment(private_task_manager_t *this, message_t *msg) { - fragment_payload_t *payload; - enumerator_t *enumerator; - fragment_t *fragment; - status_t status = SUCCESS; - chunk_t data; - u_int8_t num; + status_t status; - payload = (fragment_payload_t*)msg->get_payload(msg, PLV1_FRAGMENT); - if (!payload) + if (!this->defrag) { - return FAILED; - } - - if (!this->frag.list || this->frag.id != payload->get_id(payload)) - { - clear_fragments(this, payload->get_id(payload)); - this->frag.list = linked_list_create(); - } - - num = payload->get_number(payload); - if (!this->frag.last && payload->is_last(payload)) - { - this->frag.last = num; - } - - enumerator = this->frag.list->create_enumerator(this->frag.list); - while (enumerator->enumerate(enumerator, &fragment)) - { - if (fragment->num == num) - { /* ignore a duplicate fragment */ - DBG1(DBG_IKE, "received duplicate fragment #%hhu", num); - enumerator->destroy(enumerator); - return NEED_MORE; - } - if (fragment->num > num) + this->defrag = message_create_defrag(msg); + if (!this->defrag) { - break; + return FAILED; } } - - data = payload->get_data(payload); - this->frag.len += data.len; - if (this->frag.len > this->frag.max_packet) + status = this->defrag->add_fragment(this->defrag, msg); + if (status == SUCCESS) { - DBG1(DBG_IKE, "fragmented IKE message is too large"); - enumerator->destroy(enumerator); - clear_fragments(this, 0); - return FAILED; - } - - INIT(fragment, - .num = num, - .data = chunk_clone(data), - ); - - this->frag.list->insert_before(this->frag.list, enumerator, fragment); - enumerator->destroy(enumerator); - - if (this->frag.list->get_count(this->frag.list) == this->frag.last) - { - message_t *message; - packet_t *pkt; - host_t *src, *dst; - bio_writer_t *writer; - - writer = bio_writer_create(this->frag.len); - DBG1(DBG_IKE, "received fragment #%hhu, reassembling fragmented IKE " - "message", num); - enumerator = this->frag.list->create_enumerator(this->frag.list); - while (enumerator->enumerate(enumerator, &fragment)) - { - writer->write_data(writer, fragment->data); - } - enumerator->destroy(enumerator); - - src = msg->get_source(msg); - dst = msg->get_destination(msg); - pkt = packet_create_from_data(src->clone(src), dst->clone(dst), - writer->extract_buf(writer)); - writer->destroy(writer); - - message = message_create_from_packet(pkt); - if (message->parse_header(message) != SUCCESS) - { - DBG1(DBG_IKE, "failed to parse header of reassembled IKE message"); - message->destroy(message); - status = FAILED; - } - else - { - lib->processor->queue_job(lib->processor, - (job_t*)process_message_job_create(message)); - status = NEED_MORE; - - } - clear_fragments(this, 0); - } - else - { /* there are some fragments missing */ - DBG1(DBG_IKE, "received fragment #%hhu, waiting for complete IKE " - "message", num); + lib->processor->queue_job(lib->processor, + (job_t*)process_message_job_create(this->defrag)); + this->defrag = NULL; + /* do not process the last fragment */ status = NEED_MORE; } return status; @@ -1912,7 +1764,8 @@ METHOD(task_manager_t, reset, void, this->initiating.seqnr = 0; this->initiating.retransmitted = 0; this->initiating.type = EXCHANGE_TYPE_UNDEFINED; - clear_fragments(this, 0); + DESTROY_IF(this->defrag); + this->defrag = NULL; if (initiate != UINT_MAX) { this->dpd_send = initiate; @@ -1963,7 +1816,7 @@ METHOD(task_manager_t, destroy, void, this->active_tasks->destroy(this->active_tasks); this->queued_tasks->destroy(this->queued_tasks); this->passive_tasks->destroy(this->passive_tasks); - clear_fragments(this, 0); + DESTROY_IF(this->defrag); DESTROY_IF(this->queued); clear_packets(this->responding.packets); @@ -2014,10 +1867,6 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .responding = { .seqnr = RESPONDING_SEQ, }, - .frag = { - .max_packet = lib->settings->get_int(lib->settings, - "%s.max_packet", MAX_PACKET, lib->ns), - }, .ike_sa = ike_sa, .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), .queued_tasks = linked_list_create(), From 7223b642a7f03fa41a10a8334ba7482efe01cdb9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 6 Jun 2014 15:12:16 +0200 Subject: [PATCH 07/24] ikev2: Add notify for IKEv2 fragmentation --- .../encoding/payloads/notify_payload.c | 18 +++++++++++------- .../encoding/payloads/notify_payload.h | 4 ++++ 2 files changed, 15 insertions(+), 7 deletions(-) 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 */ From 34dc37f3cb031d93a3f9e182052eec61a5a48236 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 6 Jun 2014 16:19:55 +0200 Subject: [PATCH 08/24] ikev2: Negotiate support for IKEv2 fragmentation --- src/libcharon/sa/ike_sa.h | 2 +- src/libcharon/sa/ikev2/tasks/ike_init.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 7926301a7..f04fab009 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -128,7 +128,7 @@ enum ike_extension_t { EXT_NATT_DRAFT_02_03 = (1<<10), /** - * peer support proprietary IKE fragmentation + * peer supports proprietary IKEv1 or standardized IKEv2 fragmentation */ EXT_IKE_FRAGMENTATION = (1<<11), }; diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index e3c18ea0f..71c5f22fa 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -161,6 +161,19 @@ static void build_payloads(private_ike_init_t *this, message_t *message) message->add_payload(message, (payload_t*)ke_payload); message->add_payload(message, (payload_t*)nonce_payload); } + + /* negotiate fragmentation if we are not rekeying */ + if (!this->old_sa && + this->config->fragmentation(this->config) != FRAGMENTATION_NO) + { + if (this->initiator || + this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_FRAGMENTATION)) + { + message->add_notify(message, FALSE, FRAGMENTATION_SUPPORTED, + chunk_empty); + } + } } /** @@ -220,6 +233,16 @@ static void process_payloads(private_ike_init_t *this, message_t *message) this->other_nonce = nonce_payload->get_nonce(nonce_payload); break; } + case PLV2_NOTIFY: + { + notify_payload_t *notify = (notify_payload_t*)payload; + + if (notify->get_notify_type(notify) == FRAGMENTATION_SUPPORTED) + { + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_FRAGMENTATION); + } + } default: break; } From 41751a70d90494c5931e9678279b1006da34c1a2 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 12 Jun 2014 18:39:30 +0200 Subject: [PATCH 09/24] message: Split generate() in multiple functions --- src/libcharon/encoding/message.c | 289 ++++++++++++++++++------------- 1 file changed, 172 insertions(+), 117 deletions(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 2fdbeb607..789390002 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -1406,6 +1406,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 */ @@ -1474,7 +1480,7 @@ static void order_payloads(private_message_t *this) */ static encrypted_payload_t* wrap_payloads(private_message_t *this) { - encrypted_payload_t *encryption; + encrypted_payload_t *encrypted; linked_list_t *payloads; payload_t *current; @@ -1488,11 +1494,11 @@ static encrypted_payload_t* wrap_payloads(private_message_t *this) if (this->is_encrypted) { - encryption = encrypted_payload_create(PLV1_ENCRYPTED); + encrypted = encrypted_payload_create(PLV1_ENCRYPTED); } else { - encryption = encrypted_payload_create(PLV2_ENCRYPTED); + encrypted = encrypted_payload_create(PLV2_ENCRYPTED); } while (payloads->remove_first(payloads, (void**)¤t) == SUCCESS) { @@ -1510,7 +1516,7 @@ static encrypted_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 { @@ -1521,112 +1527,18 @@ static encrypted_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; -} - -METHOD(message_t, generate, status_t, - private_message_t *this, keymat_t *keymat, packet_t **packet) -{ - keymat_v1_t *keymat_v1 = (keymat_v1_t*)keymat; - generator_t *generator; ike_header_t *ike_header; - payload_t *payload, *next; - encrypted_payload_t *encryption = NULL; - payload_type_t next_type; - enumerator_t *enumerator; - aead_t *aead = NULL; - chunk_t chunk, hash = chunk_empty; - char str[BUF_LEN]; - u_int32_t *lenpos; - bool encrypted = FALSE, *reserved; + bool *reserved; int i; - if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) - { - DBG1(DBG_ENC, "exchange type is not defined"); - return INVALID_STATE; - } - - if (this->packet->get_source(this->packet) == NULL || - this->packet->get_destination(this->packet) == NULL) - { - DBG1(DBG_ENC, "source/destination not defined"); - return INVALID_STATE; - } - - this->rule = get_message_rule(this); - if (!this->rule) - { - DBG1(DBG_ENC, "no message rules specified for this message type"); - return NOT_SUPPORTED; - } - - if (!this->sort_disabled) - { - order_payloads(this); - } - if (keymat && keymat->get_version(keymat) == IKEV1) - { - /* get a hash for this message, if any is required */ - if (keymat_v1->get_hash_phase2(keymat_v1, &this->public, &hash)) - { /* insert a HASH payload as first payload */ - hash_payload_t *hash_payload; - - hash_payload = hash_payload_create(PLV1_HASH); - hash_payload->set_hash(hash_payload, hash); - this->payloads->insert_first(this->payloads, hash_payload); - if (this->exchange_type == INFORMATIONAL_V1) - { - this->is_encrypted = encrypted = TRUE; - } - chunk_free(&hash); - } - } - if (this->major_version == IKEV2_MAJOR_VERSION) - { - encrypted = this->rule->encrypted; - } - else if (!encrypted) - { - /* If at least one payload requires encryption, encrypt the message. - * If no key material is available, the flag will be reset below. */ - enumerator = this->payloads->create_enumerator(this->payloads); - while (enumerator->enumerate(enumerator, (void**)&payload)) - { - payload_rule_t *rule; - - rule = get_payload_rule(this, payload->get_type(payload)); - if (rule && rule->encrypted) - { - this->is_encrypted = encrypted = TRUE; - break; - } - } - enumerator->destroy(enumerator); - } - - DBG1(DBG_ENC, "generating %s", get_string(this, str, sizeof(str))); - - if (keymat) - { - aead = keymat->get_aead(keymat, FALSE); - } - if (aead && encrypted) - { - encryption = wrap_payloads(this); - } - else - { - DBG2(DBG_ENC, "not encrypting payloads"); - 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); @@ -1656,10 +1568,117 @@ METHOD(message_t, generate, status_t, *reserved = this->reserved[i]; } } + return ike_header; +} - generator = generator_create(); +/** + * 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; + payload_type_t next_type; + enumerator_t *enumerator; + aead_t *aead = NULL; + chunk_t hash = chunk_empty; + char str[BUF_LEN]; + ike_header_t *ike_header; + payload_t *payload, *next; + bool encrypting = FALSE; + + if (this->exchange_type == EXCHANGE_TYPE_UNDEFINED) + { + DBG1(DBG_ENC, "exchange type is not defined"); + return INVALID_STATE; + } + + if (this->packet->get_source(this->packet) == NULL || + this->packet->get_destination(this->packet) == NULL) + { + DBG1(DBG_ENC, "source/destination not defined"); + return INVALID_STATE; + } + + this->rule = get_message_rule(this); + if (!this->rule) + { + DBG1(DBG_ENC, "no message rules specified for this message type"); + return NOT_SUPPORTED; + } + + if (!this->sort_disabled) + { + order_payloads(this); + } + + if (keymat && keymat->get_version(keymat) == IKEV1) + { + /* get a hash for this message, if any is required */ + if (keymat_v1->get_hash_phase2(keymat_v1, &this->public, &hash)) + { /* insert a HASH payload as first payload */ + hash_payload_t *hash_payload; + + hash_payload = hash_payload_create(PLV1_HASH); + hash_payload->set_hash(hash_payload, hash); + this->payloads->insert_first(this->payloads, hash_payload); + if (this->exchange_type == INFORMATIONAL_V1) + { + this->is_encrypted = encrypting = TRUE; + } + chunk_free(&hash); + } + } + + if (this->major_version == IKEV2_MAJOR_VERSION) + { + encrypting = this->rule->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. */ + enumerator = this->payloads->create_enumerator(this->payloads); + while (enumerator->enumerate(enumerator, (void**)&payload)) + { + payload_rule_t *rule; + + rule = get_payload_rule(this, payload->get_type(payload)); + if (rule && rule->encrypted) + { + this->is_encrypted = encrypting = TRUE; + break; + } + } + enumerator->destroy(enumerator); + } + + DBG1(DBG_ENC, "generating %s", get_string(this, str, sizeof(str))); + + if (keymat) + { + aead = keymat->get_aead(keymat, FALSE); + } + if (aead && encrypting) + { + *encrypted = wrap_payloads(this); + (*encrypted)->set_transform(*encrypted, aead); + } + else + { + DBG2(DBG_ENC, "not encrypting payloads"); + this->is_encrypted = FALSE; + } /* 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)) @@ -1671,41 +1690,54 @@ METHOD(message_t, generate, status_t, enumerator->destroy(enumerator); 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; + next_type = (*encrypted) ? PLV2_ENCRYPTED : PL_NONE; } 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); @@ -1714,8 +1746,10 @@ METHOD(message_t, generate, status_t, { /* 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) || @@ -1726,6 +1760,27 @@ METHOD(message_t, generate, status_t, } } generator->destroy(generator); + return SUCCESS; +} + +METHOD(message_t, generate, status_t, + private_message_t *this, keymat_t *keymat, packet_t **packet) +{ + generator_t *generator = NULL; + encrypted_payload_t *encrypted = NULL; + status_t status; + + status = generate_message(this, keymat, &generator, &encrypted); + if (status != SUCCESS) + { + 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); From 44996b5866928e3cc05e22e7f08209bd74aecda3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 12 Jun 2014 19:04:24 +0200 Subject: [PATCH 10/24] encrypted_payload: Extract some utility functions --- .../encoding/payloads/encrypted_payload.c | 186 +++++++++++------- 1 file changed, 111 insertions(+), 75 deletions(-) diff --git a/src/libcharon/encoding/payloads/encrypted_payload.c b/src/libcharon/encoding/payloads/encrypted_payload.c index d71f514c2..bd309a974 100644 --- a/src/libcharon/encoding/payloads/encrypted_payload.c +++ b/src/libcharon/encoding/payloads/encrypted_payload.c @@ -1,7 +1,7 @@ /* + * Copyright (C) 2011-2014 Tobias Brunner * 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 * @@ -179,6 +179,23 @@ METHOD(payload_t, set_next_type, void, 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 */ @@ -186,7 +203,7 @@ static void compute_length(private_encrypted_payload_t *this) { enumerator_t *enumerator; payload_t *payload; - size_t bs, length = 0; + size_t length = 0; if (this->encrypted.len) { @@ -203,13 +220,7 @@ static void compute_length(private_encrypted_payload_t *this) 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 += compute_overhead(this->aead, length); } } length += get_header_length(this); @@ -304,44 +315,36 @@ static chunk_t append_header(private_encrypted_payload_t *this, chunk_t assoc) return chunk_cat("cc", assoc, chunk_from_thing(header)); } -METHOD(encrypted_payload_t, encrypt, status_t, - private_encrypted_payload_t *this, u_int64_t mid, chunk_t assoc) +/** + * 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, plain, padding, icv, crypt; - generator_t *generator; + chunk_t iv, padding, icv, crypt; iv_gen_t *iv_gen; rng_t *rng; size_t bs; - if (this->aead == NULL) - { - DBG1(DBG_ENC, "encrypting encrypted payload failed, transform missing"); - return INVALID_STATE; - } - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) { - DBG1(DBG_ENC, "encrypting encrypted payload failed, no RNG found"); + DBG1(DBG_ENC, "encrypting %s failed, no RNG found", label); return NOT_SUPPORTED; } - iv_gen = this->aead->get_iv_gen(this->aead); + iv_gen = aead->get_iv_gen(aead); if (!iv_gen) { - DBG1(DBG_ENC, "encrypting encrypted payload failed, no IV generator"); + DBG1(DBG_ENC, "encrypting %s failed, no IV generator", label); return NOT_SUPPORTED; } - assoc = append_header(this, assoc); - - generator = generator_create(); - plain = generate(this, generator); - bs = this->aead->get_block_size(this->aead); + 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 = this->aead->get_iv_size(this->aead); - icv.len = this->aead->get_icv_size(this->aead); + iv.len = aead->get_iv_size(aead); + icv.len = aead->get_icv_size(aead); /* prepare data to authenticate-encrypt: * | IV | plain | padding | ICV | @@ -350,47 +353,64 @@ METHOD(encrypted_payload_t, encrypt, status_t, * v / * assoc -> + ------->/ */ - free(this->encrypted.ptr); - this->encrypted = chunk_alloc(iv.len + plain.len + padding.len + icv.len); - iv.ptr = this->encrypted.ptr; + *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); - 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 encrypted payload failed, no IV or padding"); + DBG1(DBG_ENC, "encrypting %s failed, no IV or padding", label); rng->destroy(rng); - free(assoc.ptr); + return FAILED; } padding.ptr[padding.len - 1] = padding.len - 1; rng->destroy(rng); - DBG3(DBG_ENC, "encrypted payload encryption:"); + 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 (!this->aead->encrypt(this->aead, crypt, assoc, iv, NULL)) + if (!aead->encrypt(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(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) { @@ -476,18 +496,16 @@ static status_t parse(private_encrypted_payload_t *this, chunk_t plain) return SUCCESS; } -METHOD(encrypted_payload_t, decrypt, status_t, - private_encrypted_payload_t *this, chunk_t assoc) +/** + * 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, plain, padding, icv, crypt; + chunk_t iv, padding, icv, crypt; size_t bs; - if (this->aead == NULL) - { - DBG1(DBG_ENC, "decrypting encrypted payload failed, transform missing"); - return INVALID_STATE; - } - /* prepare data to authenticate-decrypt: * | IV | plain | padding | ICV | * \____crypt______/ ^ @@ -495,52 +513,70 @@ METHOD(encrypted_payload_t, decrypt, status_t, * 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; + 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 = this->encrypted.len - iv.len; + crypt.len = encrypted.len - iv.len; - if (iv.len + icv.len > this->encrypted.len || + if (iv.len + icv.len > encrypted.len || (crypt.len - icv.len) % bs) { - DBG1(DBG_ENC, "decrypting encrypted payload failed, invalid length"); + DBG1(DBG_ENC, "decrypting %s payload failed, invalid length", label); return FAILED; } - assoc = append_header(this, assoc); - - DBG3(DBG_ENC, "encrypted payload decryption:"); + 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 (!this->aead->decrypt(this->aead, crypt, assoc, iv, NULL)) + if (!aead->decrypt(aead, crypt, assoc, iv, NULL)) { - DBG1(DBG_ENC, "verifying encrypted payload integrity failed"); - free(assoc.ptr); + DBG1(DBG_ENC, "verifying %s integrity failed", label); 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) + *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 encrypted payload failed, " - "padding invalid %B", &crypt); + DBG1(DBG_ENC, "decrypting %s failed, padding invalid %B", label, + &crypt); return PARSE_ERROR; } - plain.len -= padding.len; - padding.ptr = plain.ptr + plain.len; + plain->len -= padding.len; + padding.ptr = plain->ptr + plain->len; - DBG3(DBG_ENC, "plain %B", &plain); + 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); } From 1e0d8f3ec087cb108c61857f98ad7443728e17aa Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 12 Jun 2014 21:42:07 +0200 Subject: [PATCH 11/24] encrypted_payload: Expose generate() to generate the plaintext --- src/libcharon/encoding/payloads/encrypted_payload.c | 8 +++++++- src/libcharon/encoding/payloads/encrypted_payload.h | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/libcharon/encoding/payloads/encrypted_payload.c b/src/libcharon/encoding/payloads/encrypted_payload.c index bd309a974..431f7bb28 100644 --- a/src/libcharon/encoding/payloads/encrypted_payload.c +++ b/src/libcharon/encoding/payloads/encrypted_payload.c @@ -24,7 +24,6 @@ #include #include #include -#include #include typedef struct private_encrypted_payload_t private_encrypted_payload_t; @@ -298,6 +297,12 @@ static chunk_t generate(private_encrypted_payload_t *this, 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 */ @@ -646,6 +651,7 @@ encrypted_payload_t *encrypted_payload_create(payload_type_t type) .get_length = _get_length, .add_payload = _add_payload, .remove_payload = _remove_payload, + .generate_payloads = _generate_payloads, .set_transform = _set_transform, .encrypt = _encrypt, .decrypt = _decrypt, diff --git a/src/libcharon/encoding/payloads/encrypted_payload.h b/src/libcharon/encoding/payloads/encrypted_payload.h index 57bcc2158..663360f80 100644 --- a/src/libcharon/encoding/payloads/encrypted_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 @@ -28,6 +29,7 @@ typedef struct encrypted_payload_t encrypted_payload_t; #include #include #include +#include /** * The encrypted payload as described in RFC section 3.14. @@ -61,6 +63,14 @@ struct encrypted_payload_t { */ 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. * From edfd33455cb772f43f0d1690266074e722a9f4fe Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 16 Jun 2014 15:01:28 +0200 Subject: [PATCH 12/24] encrypted_payload: Encrypted payload can be constructed from plaintext --- .../encoding/payloads/encrypted_payload.c | 27 +++++++++++++++++++ .../encoding/payloads/encrypted_payload.h | 11 ++++++++ 2 files changed, 38 insertions(+) diff --git a/src/libcharon/encoding/payloads/encrypted_payload.c b/src/libcharon/encoding/payloads/encrypted_payload.c index 431f7bb28..c61fb8683 100644 --- a/src/libcharon/encoding/payloads/encrypted_payload.c +++ b/src/libcharon/encoding/payloads/encrypted_payload.c @@ -585,6 +585,16 @@ METHOD(encrypted_payload_t, decrypt, status_t, 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) { @@ -671,3 +681,20 @@ encrypted_payload_t *encrypted_payload_create(payload_type_t type) 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; +} diff --git a/src/libcharon/encoding/payloads/encrypted_payload.h b/src/libcharon/encoding/payloads/encrypted_payload.h index 663360f80..be59e3c2d 100644 --- a/src/libcharon/encoding/payloads/encrypted_payload.h +++ b/src/libcharon/encoding/payloads/encrypted_payload.h @@ -118,4 +118,15 @@ struct encrypted_payload_t { */ 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 + */ +encrypted_payload_t *encrypted_payload_create_from_plain(payload_type_t next, + chunk_t plain); + #endif /** ENCRYPTED_PAYLOAD_H_ @}*/ From 4c345b15c76222b0679ba0beb00c34a485d19b72 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 16 Jun 2014 15:29:45 +0200 Subject: [PATCH 13/24] ikev2: Add encrypted fragment payload --- src/libcharon/Makefile.am | 1 + .../payloads/encrypted_fragment_payload.h | 85 +++++ .../encoding/payloads/encrypted_payload.c | 322 ++++++++++++++++++ src/libcharon/encoding/payloads/payload.c | 39 ++- src/libcharon/encoding/payloads/payload.h | 22 +- 5 files changed, 456 insertions(+), 13 deletions(-) create mode 100644 src/libcharon/encoding/payloads/encrypted_fragment_payload.h diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 5fb1e3dce..e98f5e137 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -24,6 +24,7 @@ encoding/payloads/delete_payload.c encoding/payloads/delete_payload.h \ encoding/payloads/eap_payload.c encoding/payloads/eap_payload.h \ encoding/payloads/encodings.c encoding/payloads/encodings.h \ encoding/payloads/encrypted_payload.c encoding/payloads/encrypted_payload.h \ +encoding/payloads/encrypted_fragment_payload.h \ encoding/payloads/id_payload.c encoding/payloads/id_payload.h \ encoding/payloads/ike_header.c encoding/payloads/ike_header.h \ encoding/payloads/ke_payload.c encoding/payloads/ke_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 . + * + * 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 + +/** + * 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 index c61fb8683..5c574c34d 100644 --- a/src/libcharon/encoding/payloads/encrypted_payload.c +++ b/src/libcharon/encoding/payloads/encrypted_payload.c @@ -20,6 +20,7 @@ #include #include "encrypted_payload.h" +#include "encrypted_fragment_payload.h" #include #include @@ -27,6 +28,7 @@ #include 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 { @@ -74,6 +76,56 @@ struct private_encrypted_payload_t { 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. * @@ -131,6 +183,47 @@ static encoding_rule_t encodings_v1[] = { +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ +/** + * 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) { @@ -698,3 +791,232 @@ encrypted_payload_t *encrypted_payload_create_from_plain(payload_type_t next, 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/payload.c b/src/libcharon/encoding/payloads/payload.c index e687f2707..600b6dd68 100644 --- a/src/libcharon/encoding/payloads/payload.c +++ b/src/libcharon/encoding/payloads/payload.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -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"); @@ -247,6 +256,8 @@ payload_t *payload_create(payload_type_t 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,10 +272,6 @@ bool payload_is_known(payload_type_t type) { return TRUE; } - if (type >= PLV2_SECURITY_ASSOCIATION && type <= PLV2_EAP) - { - return TRUE; - } if (type >= PLV1_SECURITY_ASSOCIATION && type <= PLV1_CONFIGURATION) { return TRUE; @@ -273,6 +280,14 @@ bool payload_is_known(payload_type_t type) { return TRUE; } + if (type >= PLV2_SECURITY_ASSOCIATION && type <= PLV2_EAP) + { + return TRUE; + } + if (type == PLV2_FRAGMENT) + { + return TRUE; + } #ifdef ME if (type == PLV2_ID_PEER) { diff --git a/src/libcharon/encoding/payloads/payload.h b/src/libcharon/encoding/payloads/payload.h index f22470e01..036cd422d 100644 --- a/src/libcharon/encoding/payloads/payload.h +++ b/src/libcharon/encoding/payloads/payload.h @@ -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, From e8ffb256b373de0094903fd227ccc8c5520caf9c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 16 Jun 2014 15:38:45 +0200 Subject: [PATCH 14/24] message: Handle encrypted fragment payload similar to the encrypted payload --- src/libcharon/encoding/message.c | 107 ++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 16 deletions(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 789390002..262f1050b 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -1480,16 +1480,29 @@ static void order_payloads(private_message_t *this) */ static encrypted_payload_t* wrap_payloads(private_message_t *this) { - encrypted_payload_t *encrypted; + 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) @@ -1688,14 +1701,17 @@ static status_t generate_message(private_message_t *this, keymat_t *keymat, payload = next; } enumerator->destroy(enumerator); + + next_type = PL_NONE; if (this->is_encrypted) { /* for encrypted IKEv1 messages */ next_type = (*encrypted)->payload_interface.get_next_type( (payload_t*)*encrypted); } - else - { - next_type = (*encrypted) ? 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); @@ -2096,9 +2112,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); @@ -2194,6 +2210,41 @@ static status_t decrypt_and_extract(private_message_t *this, keymat_t *keymat, return SUCCESS; } +/** + * 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? @@ -2222,7 +2273,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)) @@ -2231,17 +2282,21 @@ 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) { - encrypted_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 = (encrypted_payload_t*)payload; @@ -2260,7 +2315,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 && From c0f4936a23578625e7be7032d2a66db5d6a6c1a3 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 16 Jun 2014 15:46:33 +0200 Subject: [PATCH 15/24] message: Fragment and reassemble IKEv2 messages --- src/libcharon/encoding/message.c | 498 +++++++++++++++++++++++-------- src/libcharon/encoding/message.h | 5 - 2 files changed, 368 insertions(+), 135 deletions(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 262f1050b..43f74238f 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -819,8 +820,9 @@ 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_int8_t last; + u_int16_t last; /** * Length of all currently received fragments. @@ -908,7 +910,7 @@ struct private_message_t { /** * Array of generated fragments (if any), as packet_t*. - * If defragmenting (frag != NULL) this contains fragment_t* + * If defragmenting (i.e. frag != NULL) this contains fragment_t* */ array_t *fragments; @@ -952,11 +954,10 @@ static void fragment_destroy(fragment_t *this) free(this); } -static void reset_defrag(private_message_t *this, u_int16_t id) +static void reset_defrag(private_message_t *this) { array_destroy_function(this->fragments, (void*)fragment_destroy, NULL); this->fragments = NULL; - this->message_id = id; this->frag->last = 0; this->frag->len = 0; } @@ -1822,35 +1823,64 @@ static message_t *clone_message(private_message_t *this) 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, u_int8_t num, - bool last, chunk_t 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) { - fragment_payload_t *fragment; + enumerator_t *enumerator; + payload_t *fragment, *payload; message_t *message; peer_cfg_t *peer_cfg; ike_sa_t *ike_sa; - fragment = fragment_payload_create_from_data(num, last, data); message = clone_message(this); - /* 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)) + if (this->major_version == IKEV1_MAJOR_VERSION) { - message->set_exchange_type(message, AGGRESSIVE); + /* 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 { - message->set_exchange_type(message, ID_PROT); + 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; @@ -1869,29 +1899,18 @@ 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; - u_int8_t num, count; + 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; - if (this->major_version == IKEV2_MAJOR_VERSION) - { - return INVALID_STATE; - } - clear_fragments(this); - - if (!is_encoded(this)) - { - status = generate(this, keymat, NULL); - if (status != SUCCESS) - { - return status; - } - } - src = this->packet->get_source(this->packet); dst = this->packet->get_destination(this->packet); if (!frag_len) @@ -1901,31 +1920,112 @@ METHOD(message_t, fragment, status_t, /* frag_len is the complete IP datagram length, account for overhead (we * assume no IP options/extension headers are used) */ frag_len -= (src->get_family(src) == AF_INET) ? 20 : 40; - /* 8 (UDP header) + 28 (IKE header) */ - frag_len -= 36; + /* 8 (UDP header) */ + 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 */ frag_len -= 4; } - data = this->packet->get_data(this->packet); - if (data.len <= frag_len) + 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; } - /* overhead for the fragmentation payload header */ - frag_len -= 8; + + /* 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 */ + 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 */ + 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 */ + frag_len -= aead->get_iv_size(aead) + aead->get_icv_size(aead); + frag_len -= 8 /* header */; + /* padding and padding length */ + frag_len = round_down(frag_len, aead->get_block_size(aead)) - 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); - DBG2(DBG_ENC, "splitting IKE message with length of %zu bytes into " - "%hhu fragments", data.len, 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, num, num == count, + fragment = create_fragment(this, next, num, count, chunk_create(data.ptr, len)); status = fragment->generate(fragment, keymat, &packet); fragment->destroy(fragment); @@ -1933,12 +2033,14 @@ METHOD(message_t, fragment, status_t, { 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; } @@ -1970,6 +2072,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); @@ -2149,43 +2255,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; @@ -2432,10 +2547,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); @@ -2500,16 +2620,85 @@ METHOD(message_t, parse_body, status_t, return SUCCESS; } -METHOD(message_t, add_fragment, status_t, - private_message_t *this, message_t *message) +/** + * 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_payload_t *payload; 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; - int i, insert_at = -1; + status_t status; if (!this->frag) { @@ -2522,47 +2711,25 @@ METHOD(message_t, add_fragment, status_t, } if (!this->fragments || this->message_id != payload->get_id(payload)) { - reset_defrag(this, payload->get_id(payload)); - /* we don't know the total number of fragments */ - this->fragments = array_create(0, 0); + 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; } - - for (i = 0; i < array_count(this->fragments); i++) + status = add_fragment(this, num, data); + if (status != SUCCESS) { - array_get(this->fragments, i, &fragment); - if (fragment->num == num) - { - /* ignore a duplicate fragment */ - DBG1(DBG_ENC, "received duplicate fragment #%hhu", num); - return NEED_MORE; - } - if (fragment->num > num) - { - insert_at = i; - break; - } + return status; } - data = payload->get_data(payload); - 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, 0); - return FAILED; - } - INIT(fragment, - .num = num, - .data = chunk_clone(data), - ); - array_insert(this->fragments, insert_at, fragment); - if (this->frag->last < array_count(this->fragments)) + if (array_count(this->fragments) != this->frag->last) { /* there are some fragments missing */ DBG1(DBG_ENC, "received fragment #%hhu, waiting for complete IKE " @@ -2570,26 +2737,12 @@ METHOD(message_t, add_fragment, status_t, return NEED_MORE; } - writer = bio_writer_create(this->frag->len); DBG1(DBG_ENC, "received fragment #%hhu, reassembling fragmented IKE " "message", num); - for (i = 0; i < array_count(this->fragments); i++) - { - array_get(this->fragments, i, &fragment); - writer->write_data(writer, fragment->data); - } - src = message->get_source(message); - dst = message->get_destination(message); - this->packet->set_source(this->packet, src->clone(src)); - this->packet->set_destination(this->packet, dst->clone(dst)); - this->packet->set_data(this->packet, writer->extract_buf(writer)); - writer->destroy(writer); - this->parser->destroy(this->parser); - this->parser = parser_create(this->packet->get_data(this->packet)); - reset_defrag(this, 0); - free(this->frag); - this->frag = NULL; + data = merge_fragments(this, message); + this->packet->set_data(this->packet, data); + this->parser = parser_create(data); if (parse_header(this) != SUCCESS) { @@ -2599,16 +2752,91 @@ METHOD(message_t, add_fragment, status_t, 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 (!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, 0); + reset_defrag(this); free(this->frag); } else @@ -2652,7 +2880,7 @@ message_t *message_create_from_packet(packet_t *packet) .is_encoded = _is_encoded, .is_fragmented = _is_fragmented, .fragment = _fragment, - .add_fragment = _add_fragment, + .add_fragment = _add_fragment_v2, .set_source = _set_source, .get_source = _get_source, .set_destination = _set_destination, @@ -2699,13 +2927,23 @@ message_t *message_create_defrag(message_t *fragment) { private_message_t *this; - if (!fragment->get_payload(fragment, PLV1_FRAGMENT)) + if (!fragment->get_payload(fragment, PLV1_FRAGMENT) && + !fragment->get_payload(fragment, PLV2_FRAGMENT)) { return NULL; } - this = (private_message_t*)message_create( - fragment->get_major_version(fragment), - fragment->get_minor_version(fragment)); + 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", MAX_PACKET, lib->ns), diff --git a/src/libcharon/encoding/message.h b/src/libcharon/encoding/message.h index 69a8e93b1..a03aa8e96 100644 --- a/src/libcharon/encoding/message.h +++ b/src/libcharon/encoding/message.h @@ -268,8 +268,6 @@ struct message_t { * Generates the message split into fragments of the given size (total IP * datagram length). * - * @note Only supported for IKEv1 at the moment. - * * @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 @@ -277,7 +275,6 @@ struct message_t { * which are owned by the enumerator * @return * - SUCCESS if message could be fragmented - * - INVALID_STATE if message is IKEv2 * - FAILED if fragmentation failed * - and the possible return values of generate() */ @@ -303,8 +300,6 @@ struct message_t { * Once the message is completed it should be processed like any other * inbound message. * - * @note Only supported for IKEv1 at the moment. - * * @param fragment fragment to add * @return * - SUCCESS if message was reassembled From 7a53fe55118ab1d3471b430a8f384f3d2525dbbb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 16 Jun 2014 15:47:03 +0200 Subject: [PATCH 16/24] ike: Do not cache MID of IKEv2 fragments This fails if there are unencrypted payloads before an encrypted fragment payload in the first fragment. --- src/libcharon/sa/ike_sa_manager.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 8ffa93fda..bdabc59b5 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -1302,8 +1302,9 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, ike_id = entry->ike_sa->get_id(entry->ike_sa); entry->checked_out = TRUE; - if (message->get_first_payload_type(message) != PLV1_FRAGMENT) - { + if (message->get_first_payload_type(message) != PLV1_FRAGMENT && + message->get_first_payload_type(message) != PLV2_FRAGMENT) + { /* TODO-FRAG: this fails if there are unencrypted payloads */ entry->processing = get_message_id_or_hash(message); } if (ike_id->get_responder_spi(ike_id) == 0) From 1446fd8ac91241d2a0078598c83db67feb5abdb1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 16 Jun 2014 15:48:47 +0200 Subject: [PATCH 17/24] ike: IKE_SA may fragment IKEv2 messages --- src/libcharon/sa/ike_sa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 6f4ffb845..8b659c005 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1015,7 +1015,7 @@ METHOD(ike_sa_t, generate_message_fragmented, status_t, status_t status; bool use_frags = FALSE; - if (this->ike_cfg && this->version == IKEV1) + if (this->ike_cfg) { switch (this->ike_cfg->fragmentation(this->ike_cfg)) { From b678d9e14fd63e3012eaf40f45d3a67ee3d4830f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 16 Jun 2014 15:50:08 +0200 Subject: [PATCH 18/24] ikev2: Send and receive fragmented IKE messages If a fragmented message is retransmitted only the first packet is passed to the alert() hook. --- src/libcharon/sa/ikev2/task_manager_v2.c | 213 ++++++++++++++++++----- 1 file changed, 169 insertions(+), 44 deletions(-) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 89bde41fb..9b57d2ced 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2011 Tobias Brunner + * Copyright (C) 2007-2014 Tobias Brunner * Copyright (C) 2007-2010 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -90,9 +90,14 @@ struct private_task_manager_t { u_int32_t mid; /** - * packet for retransmission + * packet(s) for retransmission */ - packet_t *packet; + array_t *packets; + + /** + * Helper to defragment the request + */ + message_t *defrag; } responding; @@ -111,9 +116,9 @@ struct private_task_manager_t { u_int retransmitted; /** - * packet for retransmission + * packet(s) for retransmission */ - packet_t *packet; + array_t *packets; /** * type of the initated exchange @@ -125,6 +130,11 @@ struct private_task_manager_t { */ bool deferred; + /** + * Helper to defragment the response + */ + message_t *defrag; + } initiating; /** @@ -163,6 +173,19 @@ struct private_task_manager_t { double retransmit_base; }; +/** + * Reset retransmission packet list + */ +static void clear_packets(array_t *array) +{ + packet_t *packet; + + while (array_remove(array, ARRAY_TAIL, &packet)) + { + packet->destroy(packet); + } +} + METHOD(task_manager_t, flush_queue, void, private_task_manager_t *this, task_queue_t queue) { @@ -222,10 +245,60 @@ static bool activate_task(private_task_manager_t *this, task_type_t type) return found; } +/** + * Send packets in the given array (they get cloned). Optionally, the + * source and destination addresses are changed before sending it. + */ +static void send_packets(private_task_manager_t *this, array_t *packets, + host_t *src, host_t *dst) +{ + packet_t *packet, *clone; + int i; + + for (i = 0; i < array_count(packets); i++) + { + array_get(packets, i, &packet); + clone = packet->clone(packet); + if (src) + { + clone->set_source(clone, src->clone(src)); + } + if (dst) + { + clone->set_destination(clone, dst->clone(dst)); + } + charon->sender->send(charon->sender, clone); + } +} + +/** + * Generates the given message and stores packet(s) in the given array + */ +static bool generate_message(private_task_manager_t *this, message_t *message, + array_t **packets) +{ + enumerator_t *fragments; + packet_t *fragment; + + if (this->ike_sa->generate_message_fragmented(this->ike_sa, message, + &fragments) != SUCCESS) + { + return FALSE; + } + while (fragments->enumerate(fragments, &fragment)) + { + array_insert_create(packets, ARRAY_TAIL, fragment); + } + fragments->destroy(fragments); + array_compress(*packets); + return TRUE; +} + METHOD(task_manager_t, retransmit, status_t, private_task_manager_t *this, u_int32_t message_id) { - if (this->initiating.packet && message_id == this->initiating.mid) + if (message_id == this->initiating.mid && + array_count(this->initiating.packets)) { u_int32_t timeout; job_t *job; @@ -234,6 +307,8 @@ METHOD(task_manager_t, retransmit, status_t, task_t *task; ike_mobike_t *mobike = NULL; + array_get(this->initiating.packets, 0, &packet); + /* check if we are retransmitting a MOBIKE routability check */ if (this->initiating.type == INFORMATIONAL) { @@ -261,7 +336,7 @@ METHOD(task_manager_t, retransmit, status_t, DBG1(DBG_IKE, "giving up after %d retransmits", this->initiating.retransmitted - 1); charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT, - this->initiating.packet); + packet); return DESTROY_ME; } @@ -269,17 +344,15 @@ METHOD(task_manager_t, retransmit, status_t, { DBG1(DBG_IKE, "retransmit %d of request with message ID %d", this->initiating.retransmitted, message_id); - charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, - this->initiating.packet); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet); } if (!mobike) { - packet = this->initiating.packet->clone(this->initiating.packet); - charon->sender->send(charon->sender, packet); + send_packets(this, this->initiating.packets, NULL, NULL); } else { - if (!mobike->transmit(mobike, this->initiating.packet)) + if (!mobike->transmit(mobike, packet)) { DBG1(DBG_IKE, "no route found to reach peer, MOBIKE update " "deferred"); @@ -311,7 +384,9 @@ METHOD(task_manager_t, retransmit, status_t, DBG1(DBG_IKE, "path probing attempt %d", this->initiating.retransmitted); } - if (!mobike->transmit(mobike, this->initiating.packet)) + /* TODO-FRAG: presumably these small packets are not fragmented, + * we should maybe ensure this is the case when generating them */ + if (!mobike->transmit(mobike, packet)) { DBG1(DBG_IKE, "no route found to reach peer, path probing " "deferred"); @@ -336,7 +411,6 @@ METHOD(task_manager_t, initiate, status_t, task_t *task; message_t *message; host_t *me, *other; - status_t status; exchange_type_t exchange = 0; if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED) @@ -529,9 +603,7 @@ METHOD(task_manager_t, initiate, status_t, /* update exchange type if a task changed it */ this->initiating.type = message->get_exchange_type(message); - status = this->ike_sa->generate_message(this->ike_sa, message, - &this->initiating.packet); - if (status != SUCCESS) + if (!generate_message(this, message, &this->initiating.packets)) { /* message generation failed. There is nothing more to do than to * close the SA */ @@ -603,8 +675,7 @@ static status_t process_response(private_task_manager_t *this, this->initiating.mid++; this->initiating.type = EXCHANGE_TYPE_UNDEFINED; - this->initiating.packet->destroy(this->initiating.packet); - this->initiating.packet = NULL; + clear_packets(this->initiating.packets); array_compress(this->active_tasks); @@ -672,8 +743,8 @@ static status_t build_response(private_task_manager_t *this, message_t *request) host_t *me, *other; bool delete = FALSE, hook = FALSE; ike_sa_id_t *id = NULL; - u_int64_t responder_spi; - status_t status; + u_int64_t responder_spi = 0; + bool result; me = request->get_destination(request); other = request->get_source(request); @@ -735,23 +806,20 @@ static status_t build_response(private_task_manager_t *this, message_t *request) } /* message complete, send it */ - DESTROY_IF(this->responding.packet); - this->responding.packet = NULL; - status = this->ike_sa->generate_message(this->ike_sa, message, - &this->responding.packet); + clear_packets(this->responding.packets); + result = generate_message(this, message, &this->responding.packets); message->destroy(message); if (id) { id->set_responder_spi(id, responder_spi); } - if (status != SUCCESS) + if (!result) { charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; } - charon->sender->send(charon->sender, - this->responding.packet->clone(this->responding.packet)); + send_packets(this, this->responding.packets, NULL, NULL); if (delete) { if (hook) @@ -999,6 +1067,48 @@ METHOD(task_manager_t, incr_mid, void, } } +/** + * Handle the given IKE fragment, if it is one. + * + * Returns SUCCESS if the message is not a fragment, and NEED_MORE if it was + * handled properly. Error states are returned if the fragment was invalid or + * the reassembled message could not have been processed properly. + */ +static status_t handle_fragment(private_task_manager_t *this, + message_t **defrag, message_t *msg) +{ + message_t *reassembled; + status_t status; + + if (!msg->get_payload(msg, PLV2_FRAGMENT)) + { + return SUCCESS; + } + if (!*defrag) + { + *defrag = message_create_defrag(msg); + if (!*defrag) + { + return FAILED; + } + } + status = (*defrag)->add_fragment(*defrag, msg); + if (status == SUCCESS) + { + /* reinject the reassembled message */ + reassembled = *defrag; + *defrag = NULL; + status = this->ike_sa->process_message(this->ike_sa, reassembled); + if (status == SUCCESS) + { + /* avoid processing the last fragment */ + status = NEED_MORE; + } + reassembled->destroy(reassembled); + } + return status; +} + /** * Send a notify back to the sender */ @@ -1192,6 +1302,11 @@ METHOD(task_manager_t, process_message, status_t, { /* with MOBIKE, we do no implicit updates */ this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1); } + status = handle_fragment(this, &this->responding.defrag, msg); + if (status != SUCCESS) + { + return status; + } charon->bus->message(charon->bus, msg, TRUE, TRUE); if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED) { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */ @@ -1204,20 +1319,19 @@ METHOD(task_manager_t, process_message, status_t, } this->responding.mid++; } - else if ((mid == this->responding.mid - 1) && this->responding.packet) + else if ((mid == this->responding.mid - 1) && + array_count(this->responding.packets)) { - packet_t *clone; - host_t *host; - + status = handle_fragment(this, &this->responding.defrag, msg); + if (status != SUCCESS) + { + return status; + } DBG1(DBG_IKE, "received retransmit of request with ID %d, " "retransmitting response", mid); charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg); - clone = this->responding.packet->clone(this->responding.packet); - host = msg->get_destination(msg); - clone->set_source(clone, host->clone(host)); - host = msg->get_source(msg); - clone->set_destination(clone, host->clone(host)); - charon->sender->send(charon->sender, clone); + send_packets(this, this->responding.packets, + msg->get_destination(msg), msg->get_source(msg)); } else { @@ -1245,6 +1359,11 @@ METHOD(task_manager_t, process_message, status_t, this->ike_sa->update_hosts(this->ike_sa, NULL, other, FALSE); } } + status = handle_fragment(this, &this->initiating.defrag, msg); + if (status != SUCCESS) + { + return status; + } charon->bus->message(charon->bus, msg, TRUE, TRUE); if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED) { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */ @@ -1539,10 +1658,12 @@ METHOD(task_manager_t, reset, void, task_t *task; /* reset message counters and retransmit packets */ - DESTROY_IF(this->responding.packet); - DESTROY_IF(this->initiating.packet); - this->responding.packet = NULL; - this->initiating.packet = NULL; + clear_packets(this->responding.packets); + clear_packets(this->initiating.packets); + DESTROY_IF(this->responding.defrag); + DESTROY_IF(this->initiating.defrag); + this->responding.defrag = NULL; + this->initiating.defrag = NULL; if (initiate != UINT_MAX) { this->initiating.mid = initiate; @@ -1596,8 +1717,12 @@ METHOD(task_manager_t, destroy, void, array_destroy(this->queued_tasks); array_destroy(this->passive_tasks); - DESTROY_IF(this->responding.packet); - DESTROY_IF(this->initiating.packet); + clear_packets(this->responding.packets); + array_destroy(this->responding.packets); + clear_packets(this->initiating.packets); + array_destroy(this->initiating.packets); + DESTROY_IF(this->responding.defrag); + DESTROY_IF(this->initiating.defrag); free(this); } From f1aa18b2e841e065eacb0d2c4f17c3546912c358 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 23 Jun 2014 10:26:04 +0200 Subject: [PATCH 19/24] ikev2: Send retransmits using the latest known addresses For instance, if a DPD exchange is initiated by the gateway when a mobile client is roaming and it then gets a new IP address and sends an address update via MOBIKE, the DPD retransmits would still be sent to the old address and the SA would eventually get closed. --- src/libcharon/sa/ikev2/task_manager_v2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 9b57d2ced..eb7df3516 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -348,7 +348,9 @@ METHOD(task_manager_t, retransmit, status_t, } if (!mobike) { - send_packets(this, this->initiating.packets, NULL, NULL); + send_packets(this, this->initiating.packets, + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); } else { From 03512c47bf9557ab4785d689370d7f686e7d7402 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 15 Sep 2014 17:51:22 +0200 Subject: [PATCH 20/24] message: Ensure a minimum fragment length --- src/libcharon/encoding/message.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 43f74238f..4504c5b48 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -1895,6 +1895,13 @@ static void clear_fragments(private_message_t *this) 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) @@ -1919,13 +1926,13 @@ METHOD(message_t, fragment, status_t, } /* frag_len is the complete IP datagram length, account for overhead (we * assume no IP options/extension headers are used) */ - frag_len -= (src->get_family(src) == AF_INET) ? 20 : 40; + REDUCE_FRAG_LEN(frag_len, (src->get_family(src) == AF_INET) ? 20 : 40); /* 8 (UDP header) */ - frag_len -= 8; + 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 */ - frag_len -= 4; + REDUCE_FRAG_LEN(frag_len, 4); } if (is_encoded(this)) @@ -1969,7 +1976,7 @@ METHOD(message_t, fragment, status_t, /* 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 */ - frag_len -= 28; + REDUCE_FRAG_LEN(frag_len, 28); if (this->major_version == IKEV1_MAJOR_VERSION) { @@ -1984,7 +1991,7 @@ METHOD(message_t, fragment, status_t, generator = NULL; } /* overhead for the fragmentation payload header */ - frag_len -= 8; + REDUCE_FRAG_LEN(frag_len, 8); } else { @@ -2008,10 +2015,13 @@ METHOD(message_t, fragment, status_t, } aead = keymat->get_aead(keymat, FALSE); /* overhead for the encrypted fragment payload */ - frag_len -= aead->get_iv_size(aead) + aead->get_icv_size(aead); - frag_len -= 8 /* header */; + 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)) - 1; + 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 From f00a9c171592c05cc6e77030b791e80525dd839b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Sep 2014 15:38:38 +0200 Subject: [PATCH 21/24] packet: Define a global default maximum size for IKE packets --- src/libcharon/encoding/message.c | 7 +------ .../plugins/socket_default/socket_default_socket.c | 5 +---- .../plugins/socket_dynamic/socket_dynamic_socket.c | 5 +---- src/libcharon/plugins/socket_win/socket_win_socket.c | 5 +---- src/libstrongswan/networking/packet.h | 5 +++++ 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 4504c5b48..5e5647dd6 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -68,11 +68,6 @@ */ #define MAX_NAT_D_PAYLOADS 10 -/** - * Maximum packet size for fragmented packets (same as in sockets) - */ -#define MAX_PACKET 10000 - /** * A payload rule defines the rules for a payload * in a specific message rule. It defines if and how @@ -2956,7 +2951,7 @@ message_t *message_create_defrag(message_t *fragment) } INIT(this->frag, .max_packet = lib->settings->get_int(lib->settings, - "%s.max_packet", MAX_PACKET, lib->ns), + "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), ); return &this->public; } diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c index 081d3efc7..9cc39955b 100644 --- a/src/libcharon/plugins/socket_default/socket_default_socket.c +++ b/src/libcharon/plugins/socket_default/socket_default_socket.c @@ -45,9 +45,6 @@ #include #include -/* Maximum size of a packet */ -#define MAX_PACKET 10000 - /* these are not defined on some platforms */ #ifndef SOL_IP #define SOL_IP IPPROTO_IP @@ -739,7 +736,7 @@ socket_default_socket_t *socket_default_socket_create() .natt = lib->settings->get_int(lib->settings, "%s.port_nat_t", CHARON_NATT_PORT, lib->ns), .max_packet = lib->settings->get_int(lib->settings, - "%s.max_packet", MAX_PACKET, lib->ns), + "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), .set_source = lib->settings->get_bool(lib->settings, "%s.plugins.socket-default.set_source", TRUE, lib->ns), diff --git a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c index 3161a709f..b82a69e1b 100644 --- a/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c +++ b/src/libcharon/plugins/socket_dynamic/socket_dynamic_socket.c @@ -42,9 +42,6 @@ #include #include -/* Maximum size of a packet */ -#define MAX_PACKET 10000 - /* these are not defined on some platforms */ #ifndef SOL_IP #define SOL_IP IPPROTO_IP @@ -668,7 +665,7 @@ socket_dynamic_socket_t *socket_dynamic_socket_create() }, .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .max_packet = lib->settings->get_int(lib->settings, - "%s.max_packet", MAX_PACKET, lib->ns), + "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), ); if (pipe(this->notify) != 0) diff --git a/src/libcharon/plugins/socket_win/socket_win_socket.c b/src/libcharon/plugins/socket_win/socket_win_socket.c index 5ebe04aac..fbfbedae1 100644 --- a/src/libcharon/plugins/socket_win/socket_win_socket.c +++ b/src/libcharon/plugins/socket_win/socket_win_socket.c @@ -25,9 +25,6 @@ #include -/* Maximum size of a packet */ -#define MAX_PACKET 10000 - /* number of sockets in use */ #define SOCKET_COUNT 2 @@ -458,7 +455,7 @@ socket_win_socket_t *socket_win_socket_create() "%s.port_nat_t", CHARON_NATT_PORT, lib->ns), }, .max_packet = lib->settings->get_int(lib->settings, - "%s.max_packet", MAX_PACKET, lib->ns), + "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), ); for (i = 0; i < SOCKET_COUNT; i++) diff --git a/src/libstrongswan/networking/packet.h b/src/libstrongswan/networking/packet.h index a96a4b84f..1492dd0b9 100644 --- a/src/libstrongswan/networking/packet.h +++ b/src/libstrongswan/networking/packet.h @@ -28,6 +28,11 @@ typedef struct packet_t packet_t; #include #include +/** + * Maximum packet size we handle by default + */ +#define PACKET_MAX_DEFAULT 10000 + /** * Abstraction of an IP/UDP-Packet, contains data, sender and receiver. */ From 8dbe128c8c896763e13e1719bfc705aae783e4b1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Sep 2014 15:51:21 +0200 Subject: [PATCH 22/24] message: Limit maximum number of IKEv2 fragments The maximum for IKEv1 is already 255 due to the 8-bit fragment number. With an overhead of 17 bytes (x64) per fragment and a default maximum of 10000 bytes per packet the maximum memory required is 14 kB for a fragmented message. --- src/libcharon/encoding/message.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 5e5647dd6..cb6c97f25 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -930,6 +930,11 @@ struct private_message_t { fragment_data_t *frag; }; +/** + * Maximum number of fragments we will handle + */ +#define MAX_FRAGMENTS 255 + /** * A single fragment within a fragmented message */ @@ -2779,7 +2784,12 @@ METHOD(message_t, add_fragment_v2, status_t, } 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); From 144b40e07c994933652d43e60850269ce51b13e9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Sep 2014 16:51:58 +0200 Subject: [PATCH 23/24] testing: Update ikev1/net2net-fragmentation scenario --- testing/tests/ikev1/net2net-fragmentation/evaltest.dat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/tests/ikev1/net2net-fragmentation/evaltest.dat b/testing/tests/ikev1/net2net-fragmentation/evaltest.dat index 876787495..842eda00b 100644 --- a/testing/tests/ikev1/net2net-fragmentation/evaltest.dat +++ b/testing/tests/ikev1/net2net-fragmentation/evaltest.dat @@ -1,7 +1,7 @@ moon::cat /var/log/daemon.log::received FRAGMENTATION vendor ID::YES sun::cat /var/log/daemon.log::received FRAGMENTATION vendor ID::YES -moon::cat /var/log/daemon.log::sending IKE message with length of 1468 bytes in 2 fragments::YES -sun::cat /var/log/daemon.log::sending IKE message with length of 1388 bytes in 2 fragments::YES +moon::cat /var/log/daemon.log::splitting IKE message with length of 1468 bytes into 2 fragments::YES +sun::cat /var/log/daemon.log::splitting IKE message with length of 1388 bytes into 2 fragments::YES moon::cat /var/log/daemon.log::received fragment #1, waiting for complete IKE message::YES moon::cat /var/log/daemon.log::received fragment #2, reassembling fragmented IKE message::YES sun::cat /var/log/daemon.log::received fragment #1, waiting for complete IKE message::YES From 1836c1845b3ca7bff79cd73b2a113c3d0f21fc36 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 16 Sep 2014 16:52:23 +0200 Subject: [PATCH 24/24] testing: Add ikev2/net2net-fragmentation scenario --- .../net2net-fragmentation/description.txt | 9 ++++++++ .../ikev2/net2net-fragmentation/evaltest.dat | 15 +++++++++++++ .../hosts/moon/etc/ipsec.conf | 22 +++++++++++++++++++ .../hosts/moon/etc/strongswan.conf | 8 +++++++ .../hosts/sun/etc/ipsec.conf | 22 +++++++++++++++++++ .../hosts/sun/etc/strongswan.conf | 8 +++++++ .../ikev2/net2net-fragmentation/posttest.dat | 5 +++++ .../ikev2/net2net-fragmentation/pretest.dat | 6 +++++ .../ikev2/net2net-fragmentation/test.conf | 21 ++++++++++++++++++ 9 files changed, 116 insertions(+) create mode 100644 testing/tests/ikev2/net2net-fragmentation/description.txt create mode 100644 testing/tests/ikev2/net2net-fragmentation/evaltest.dat create mode 100644 testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/ipsec.conf create mode 100644 testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/strongswan.conf create mode 100644 testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/ipsec.conf create mode 100644 testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/strongswan.conf create mode 100644 testing/tests/ikev2/net2net-fragmentation/posttest.dat create mode 100644 testing/tests/ikev2/net2net-fragmentation/pretest.dat create mode 100644 testing/tests/ikev2/net2net-fragmentation/test.conf diff --git a/testing/tests/ikev2/net2net-fragmentation/description.txt b/testing/tests/ikev2/net2net-fragmentation/description.txt new file mode 100644 index 000000000..1f63c9638 --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/description.txt @@ -0,0 +1,9 @@ +A connection between the subnets behind the gateways moon and sun is set up. +The authentication is based on X.509 certificates. The IKEv2 fragmentation protocol +defined in RFC 7383 prevents the IP fragmentation of the IKEv2 messages carrying the +large X.509 certificates. +

+Upon the successful establishment of the IPsec tunnel, leftfirewall=yes automatically +inserts iptables-based firewall rules that let pass the tunneled traffic. +In order to test both tunnel and firewall, client alice behind gateway moon +pings client bob located behind gateway sun. diff --git a/testing/tests/ikev2/net2net-fragmentation/evaltest.dat b/testing/tests/ikev2/net2net-fragmentation/evaltest.dat new file mode 100644 index 000000000..7f227fd62 --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/evaltest.dat @@ -0,0 +1,15 @@ +moon::cat /var/log/daemon.log::IKE_SA_INIT request 0.*FRAG_SUP::YES +sun::cat /var/log/daemon.log::IKE_SA_INIT response 0.*FRAG_SUP::YES +moon::cat /var/log/daemon.log::splitting IKE message with length of 1804 bytes into 2 fragments::YES +sun::cat /var/log/daemon.log::splitting IKE message with length of 1596 bytes into 2 fragments::YES +moon::cat /var/log/daemon.log::received fragment #1 of 2, waiting for complete IKE message::YES +moon::cat /var/log/daemon.log::received fragment #2 of 2, reassembling fragmented IKE message::YES +sun::cat /var/log/daemon.log::received fragment #1 of 2, waiting for complete IKE message::YES +sun::cat /var/log/daemon.log::received fragment #2 of 2, reassembling fragmented IKE message::YES +moon::ipsec status 2> /dev/null::net-net.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES +sun:: ipsec status 2> /dev/null::net-net.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES +moon::ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES +sun:: ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_req=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/ipsec.conf b/testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/ipsec.conf new file mode 100644 index 000000000..bb0b2b069 --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/ipsec.conf @@ -0,0 +1,22 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + fragmentation=yes + +conn net-net + left=PH_IP_MOON + leftcert=moonCert.pem + leftid=@moon.strongswan.org + leftsubnet=10.1.0.0/16 + leftfirewall=yes + right=PH_IP_SUN + rightid=@sun.strongswan.org + rightsubnet=10.2.0.0/16 + auto=add diff --git a/testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..8cc4192c6 --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/hosts/moon/etc/strongswan.conf @@ -0,0 +1,8 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default updown + + fragment_size = 1024 + dh_exponent_ansi_x9_42 = no +} diff --git a/testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/ipsec.conf b/testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..855bb4409 --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/ipsec.conf @@ -0,0 +1,22 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + fragmentation=yes + +conn net-net + left=PH_IP_SUN + leftcert=sunCert.pem + leftid=@sun.strongswan.org + leftsubnet=10.2.0.0/16 + leftfirewall=yes + right=PH_IP_MOON + rightid=@moon.strongswan.org + rightsubnet=10.1.0.0/16 + auto=add diff --git a/testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/strongswan.conf b/testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..8cc4192c6 --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/hosts/sun/etc/strongswan.conf @@ -0,0 +1,8 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default updown + + fragment_size = 1024 + dh_exponent_ansi_x9_42 = no +} diff --git a/testing/tests/ikev2/net2net-fragmentation/posttest.dat b/testing/tests/ikev2/net2net-fragmentation/posttest.dat new file mode 100644 index 000000000..837738fc6 --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/posttest.dat @@ -0,0 +1,5 @@ +moon::ipsec stop +sun::ipsec stop +moon::iptables-restore < /etc/iptables.flush +sun::iptables-restore < /etc/iptables.flush + diff --git a/testing/tests/ikev2/net2net-fragmentation/pretest.dat b/testing/tests/ikev2/net2net-fragmentation/pretest.dat new file mode 100644 index 000000000..1732d6efa --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/pretest.dat @@ -0,0 +1,6 @@ +moon::iptables-restore < /etc/iptables.rules +sun::iptables-restore < /etc/iptables.rules +moon::ipsec start +sun::ipsec start +moon::expect-connection net-net +moon::ipsec up net-net diff --git a/testing/tests/ikev2/net2net-fragmentation/test.conf b/testing/tests/ikev2/net2net-fragmentation/test.conf new file mode 100644 index 000000000..afa2accbe --- /dev/null +++ b/testing/tests/ikev2/net2net-fragmentation/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun"