mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-08 00:02:03 -04:00
message: Fragment and reassemble IKEv2 messages
This commit is contained in:
parent
e8ffb256b3
commit
c0f4936a23
@ -33,6 +33,7 @@
|
|||||||
#include <encoding/payloads/payload.h>
|
#include <encoding/payloads/payload.h>
|
||||||
#include <encoding/payloads/hash_payload.h>
|
#include <encoding/payloads/hash_payload.h>
|
||||||
#include <encoding/payloads/encrypted_payload.h>
|
#include <encoding/payloads/encrypted_payload.h>
|
||||||
|
#include <encoding/payloads/encrypted_fragment_payload.h>
|
||||||
#include <encoding/payloads/unknown_payload.h>
|
#include <encoding/payloads/unknown_payload.h>
|
||||||
#include <encoding/payloads/cp_payload.h>
|
#include <encoding/payloads/cp_payload.h>
|
||||||
#include <encoding/payloads/fragment_payload.h>
|
#include <encoding/payloads/fragment_payload.h>
|
||||||
@ -819,8 +820,9 @@ typedef struct {
|
|||||||
* For IKEv1 the number of the last fragment (in case we receive them out
|
* 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
|
* of order), since the first one starts with 1 this defines the number of
|
||||||
* fragments we expect.
|
* 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.
|
* Length of all currently received fragments.
|
||||||
@ -908,7 +910,7 @@ struct private_message_t {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of generated fragments (if any), as packet_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;
|
array_t *fragments;
|
||||||
|
|
||||||
@ -952,11 +954,10 @@ static void fragment_destroy(fragment_t *this)
|
|||||||
free(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);
|
array_destroy_function(this->fragments, (void*)fragment_destroy, NULL);
|
||||||
this->fragments = NULL;
|
this->fragments = NULL;
|
||||||
this->message_id = id;
|
|
||||||
this->frag->last = 0;
|
this->frag->last = 0;
|
||||||
this->frag->len = 0;
|
this->frag->len = 0;
|
||||||
}
|
}
|
||||||
@ -1822,22 +1823,26 @@ static message_t *clone_message(private_message_t *this)
|
|||||||
message->set_source(message, src->clone(src));
|
message->set_source(message, src->clone(src));
|
||||||
message->set_destination(message, dst->clone(dst));
|
message->set_destination(message, dst->clone(dst));
|
||||||
message->set_exchange_type(message, this->exchange_type);
|
message->set_exchange_type(message, this->exchange_type);
|
||||||
|
memcpy(((private_message_t*)message)->reserved, this->reserved,
|
||||||
|
sizeof(this->reserved));
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a single fragment with the given data
|
* Create a single fragment with the given data
|
||||||
*/
|
*/
|
||||||
static message_t *create_fragment(private_message_t *this, u_int8_t num,
|
static message_t *create_fragment(private_message_t *this, payload_type_t next,
|
||||||
bool last, chunk_t data)
|
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;
|
message_t *message;
|
||||||
peer_cfg_t *peer_cfg;
|
peer_cfg_t *peer_cfg;
|
||||||
ike_sa_t *ike_sa;
|
ike_sa_t *ike_sa;
|
||||||
|
|
||||||
fragment = fragment_payload_create_from_data(num, last, data);
|
|
||||||
message = clone_message(this);
|
message = clone_message(this);
|
||||||
|
if (this->major_version == IKEV1_MAJOR_VERSION)
|
||||||
|
{
|
||||||
/* other implementations seem to just use 0 as message ID, so here we go */
|
/* other implementations seem to just use 0 as message ID, so here we go */
|
||||||
message->set_message_id(message, 0);
|
message->set_message_id(message, 0);
|
||||||
/* always use the initial message type for fragments, even for quick mode
|
/* always use the initial message type for fragments, even for quick mode
|
||||||
@ -1852,6 +1857,31 @@ static message_t *create_fragment(private_message_t *this, u_int8_t num,
|
|||||||
{
|
{
|
||||||
message->set_exchange_type(message, ID_PROT);
|
message->set_exchange_type(message, ID_PROT);
|
||||||
}
|
}
|
||||||
|
fragment = (payload_t*)fragment_payload_create_from_data(
|
||||||
|
num, num == count, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fragment = (payload_t*)encrypted_fragment_payload_create_from_data(
|
||||||
|
num, count, data);
|
||||||
|
if (num == 1)
|
||||||
|
{
|
||||||
|
/* only in the first fragment is this set to the type of the first
|
||||||
|
* payload in the encrypted payload */
|
||||||
|
fragment->set_next_type(fragment, next);
|
||||||
|
/* move unencrypted payloads to the first fragment */
|
||||||
|
enumerator = this->payloads->create_enumerator(this->payloads);
|
||||||
|
while (enumerator->enumerate(enumerator, &payload))
|
||||||
|
{
|
||||||
|
if (payload->get_type(payload) != PLV2_ENCRYPTED)
|
||||||
|
{
|
||||||
|
this->payloads->remove_at(this->payloads, enumerator);
|
||||||
|
message->add_payload(message, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
}
|
||||||
|
}
|
||||||
message->add_payload(message, (payload_t*)fragment);
|
message->add_payload(message, (payload_t*)fragment);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@ -1869,29 +1899,18 @@ METHOD(message_t, fragment, status_t,
|
|||||||
private_message_t *this, keymat_t *keymat, size_t frag_len,
|
private_message_t *this, keymat_t *keymat, size_t frag_len,
|
||||||
enumerator_t **fragments)
|
enumerator_t **fragments)
|
||||||
{
|
{
|
||||||
|
encrypted_payload_t *encrypted = NULL;
|
||||||
|
generator_t *generator = NULL;
|
||||||
message_t *fragment;
|
message_t *fragment;
|
||||||
packet_t *packet;
|
packet_t *packet;
|
||||||
u_int8_t num, count;
|
payload_type_t next = PL_NONE;
|
||||||
|
u_int16_t num, count;
|
||||||
host_t *src, *dst;
|
host_t *src, *dst;
|
||||||
chunk_t data;
|
chunk_t data;
|
||||||
status_t status;
|
status_t status;
|
||||||
|
u_int32_t *lenpos;
|
||||||
size_t len;
|
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);
|
src = this->packet->get_source(this->packet);
|
||||||
dst = this->packet->get_destination(this->packet);
|
dst = this->packet->get_destination(this->packet);
|
||||||
if (!frag_len)
|
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
|
/* frag_len is the complete IP datagram length, account for overhead (we
|
||||||
* assume no IP options/extension headers are used) */
|
* assume no IP options/extension headers are used) */
|
||||||
frag_len -= (src->get_family(src) == AF_INET) ? 20 : 40;
|
frag_len -= (src->get_family(src) == AF_INET) ? 20 : 40;
|
||||||
/* 8 (UDP header) + 28 (IKE header) */
|
/* 8 (UDP header) */
|
||||||
frag_len -= 36;
|
frag_len -= 8;
|
||||||
if (dst->get_port(dst) != IKEV2_UDP_PORT &&
|
if (dst->get_port(dst) != IKEV2_UDP_PORT &&
|
||||||
src->get_port(src) != IKEV2_UDP_PORT)
|
src->get_port(src) != IKEV2_UDP_PORT)
|
||||||
{ /* reduce length due to non-ESP marker */
|
{ /* reduce length due to non-ESP marker */
|
||||||
frag_len -= 4;
|
frag_len -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = this->packet->get_data(this->packet);
|
if (is_encoded(this))
|
||||||
if (data.len <= frag_len)
|
|
||||||
{
|
{
|
||||||
|
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);
|
*fragments = enumerator_create_single(this->packet, NULL);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* frag_len denoted the maximum IKE message size so far, later on it will
|
||||||
|
* denote the maximum content size of a fragment payload, therefore,
|
||||||
|
* account for IKE header */
|
||||||
|
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 */
|
/* overhead for the fragmentation payload header */
|
||||||
frag_len -= 8;
|
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);
|
count = data.len / frag_len + (data.len % frag_len ? 1 : 0);
|
||||||
this->fragments = array_create(0, count);
|
this->fragments = array_create(0, count);
|
||||||
DBG2(DBG_ENC, "splitting IKE message with length of %zu bytes into "
|
DBG1(DBG_ENC, "splitting IKE message with length of %zu bytes into "
|
||||||
"%hhu fragments", data.len, count);
|
"%hu fragments", len, count);
|
||||||
for (num = 1; num <= count; num++)
|
for (num = 1; num <= count; num++)
|
||||||
{
|
{
|
||||||
len = min(data.len, frag_len);
|
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));
|
chunk_create(data.ptr, len));
|
||||||
status = fragment->generate(fragment, keymat, &packet);
|
status = fragment->generate(fragment, keymat, &packet);
|
||||||
fragment->destroy(fragment);
|
fragment->destroy(fragment);
|
||||||
@ -1933,12 +2033,14 @@ METHOD(message_t, fragment, status_t,
|
|||||||
{
|
{
|
||||||
DBG1(DBG_ENC, "failed to generate IKE fragment");
|
DBG1(DBG_ENC, "failed to generate IKE fragment");
|
||||||
clear_fragments(this);
|
clear_fragments(this);
|
||||||
|
DESTROY_IF(generator);
|
||||||
return FAILED;
|
return FAILED;
|
||||||
}
|
}
|
||||||
array_insert(this->fragments, ARRAY_TAIL, packet);
|
array_insert(this->fragments, ARRAY_TAIL, packet);
|
||||||
data = chunk_skip(data, len);
|
data = chunk_skip(data, len);
|
||||||
}
|
}
|
||||||
*fragments = array_create_enumerator(this->fragments);
|
*fragments = array_create_enumerator(this->fragments);
|
||||||
|
DESTROY_IF(generator);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1970,6 +2072,10 @@ METHOD(message_t, parse_header, status_t,
|
|||||||
|
|
||||||
DBG2(DBG_ENC, "parsing header of message");
|
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);
|
this->parser->reset_context(this->parser);
|
||||||
status = this->parser->parse_payload(this->parser, PL_HEADER,
|
status = this->parser->parse_payload(this->parser, PL_HEADER,
|
||||||
(payload_t**)&ike_header);
|
(payload_t**)&ike_header);
|
||||||
@ -2149,6 +2255,14 @@ static status_t decrypt_and_extract(private_message_t *this, keymat_t *keymat,
|
|||||||
DBG1(DBG_ENC, "found encrypted payload, but no transform set");
|
DBG1(DBG_ENC, "found encrypted payload, but no transform set");
|
||||||
return INVALID_ARG;
|
return INVALID_ARG;
|
||||||
}
|
}
|
||||||
|
if (!this->parser)
|
||||||
|
{
|
||||||
|
/* reassembled IKEv2 messages are already decrypted, we still call
|
||||||
|
* decrypt() to parse the contained payloads */
|
||||||
|
status = encryption->decrypt(encryption, chunk_empty);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
bs = aead->get_block_size(aead);
|
bs = aead->get_block_size(aead);
|
||||||
encryption->set_transform(encryption, aead);
|
encryption->set_transform(encryption, aead);
|
||||||
chunk = this->packet->get_data(this->packet);
|
chunk = this->packet->get_data(this->packet);
|
||||||
@ -2186,6 +2300,7 @@ static status_t decrypt_and_extract(private_message_t *this, keymat_t *keymat,
|
|||||||
chunk.len -= encryption->get_length(encryption);
|
chunk.len -= encryption->get_length(encryption);
|
||||||
status = encryption->decrypt(encryption, chunk);
|
status = encryption->decrypt(encryption, chunk);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (status != SUCCESS)
|
if (status != SUCCESS)
|
||||||
{
|
{
|
||||||
return status;
|
return status;
|
||||||
@ -2432,11 +2547,16 @@ METHOD(message_t, parse_body, status_t,
|
|||||||
return NOT_SUPPORTED;
|
return NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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);
|
status = parse_payloads(this);
|
||||||
if (status != SUCCESS)
|
if (status != SUCCESS)
|
||||||
{ /* error is already logged */
|
{ /* error is already logged */
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
status = decrypt_payloads(this, keymat);
|
status = decrypt_payloads(this, keymat);
|
||||||
if (status != SUCCESS)
|
if (status != SUCCESS)
|
||||||
@ -2500,16 +2620,85 @@ METHOD(message_t, parse_body, status_t,
|
|||||||
return SUCCESS;
|
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;
|
fragment_t *fragment;
|
||||||
bio_writer_t *writer;
|
bio_writer_t *writer;
|
||||||
host_t *src, *dst;
|
host_t *src, *dst;
|
||||||
chunk_t data;
|
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;
|
u_int8_t num;
|
||||||
int i, insert_at = -1;
|
status_t status;
|
||||||
|
|
||||||
if (!this->frag)
|
if (!this->frag)
|
||||||
{
|
{
|
||||||
@ -2522,47 +2711,25 @@ METHOD(message_t, add_fragment, status_t,
|
|||||||
}
|
}
|
||||||
if (!this->fragments || this->message_id != payload->get_id(payload))
|
if (!this->fragments || this->message_id != payload->get_id(payload))
|
||||||
{
|
{
|
||||||
reset_defrag(this, payload->get_id(payload));
|
reset_defrag(this);
|
||||||
/* we don't know the total number of fragments */
|
this->message_id = payload->get_id(payload);
|
||||||
this->fragments = array_create(0, 0);
|
/* we don't know the total number of fragments, assume something */
|
||||||
|
this->fragments = array_create(0, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
num = payload->get_number(payload);
|
num = payload->get_number(payload);
|
||||||
|
data = payload->get_data(payload);
|
||||||
if (!this->frag->last && payload->is_last(payload))
|
if (!this->frag->last && payload->is_last(payload))
|
||||||
{
|
{
|
||||||
this->frag->last = num;
|
this->frag->last = num;
|
||||||
}
|
}
|
||||||
|
status = add_fragment(this, num, data);
|
||||||
|
if (status != SUCCESS)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < array_count(this->fragments); i++)
|
if (array_count(this->fragments) != this->frag->last)
|
||||||
{
|
|
||||||
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 */
|
/* there are some fragments missing */
|
||||||
DBG1(DBG_ENC, "received fragment #%hhu, waiting for complete IKE "
|
DBG1(DBG_ENC, "received fragment #%hhu, waiting for complete IKE "
|
||||||
@ -2570,26 +2737,12 @@ METHOD(message_t, add_fragment, status_t,
|
|||||||
return NEED_MORE;
|
return NEED_MORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer = bio_writer_create(this->frag->len);
|
|
||||||
DBG1(DBG_ENC, "received fragment #%hhu, reassembling fragmented IKE "
|
DBG1(DBG_ENC, "received fragment #%hhu, reassembling fragmented IKE "
|
||||||
"message", num);
|
"message", num);
|
||||||
|
|
||||||
for (i = 0; i < array_count(this->fragments); i++)
|
data = merge_fragments(this, message);
|
||||||
{
|
this->packet->set_data(this->packet, data);
|
||||||
array_get(this->fragments, i, &fragment);
|
this->parser = parser_create(data);
|
||||||
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)
|
if (parse_header(this) != SUCCESS)
|
||||||
{
|
{
|
||||||
@ -2599,16 +2752,91 @@ METHOD(message_t, add_fragment, status_t,
|
|||||||
return SUCCESS;
|
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,
|
METHOD(message_t, destroy, void,
|
||||||
private_message_t *this)
|
private_message_t *this)
|
||||||
{
|
{
|
||||||
DESTROY_IF(this->ike_sa_id);
|
DESTROY_IF(this->ike_sa_id);
|
||||||
|
DESTROY_IF(this->parser);
|
||||||
this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy));
|
this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy));
|
||||||
this->packet->destroy(this->packet);
|
this->packet->destroy(this->packet);
|
||||||
this->parser->destroy(this->parser);
|
|
||||||
if (this->frag)
|
if (this->frag)
|
||||||
{
|
{
|
||||||
reset_defrag(this, 0);
|
reset_defrag(this);
|
||||||
free(this->frag);
|
free(this->frag);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2652,7 +2880,7 @@ message_t *message_create_from_packet(packet_t *packet)
|
|||||||
.is_encoded = _is_encoded,
|
.is_encoded = _is_encoded,
|
||||||
.is_fragmented = _is_fragmented,
|
.is_fragmented = _is_fragmented,
|
||||||
.fragment = _fragment,
|
.fragment = _fragment,
|
||||||
.add_fragment = _add_fragment,
|
.add_fragment = _add_fragment_v2,
|
||||||
.set_source = _set_source,
|
.set_source = _set_source,
|
||||||
.get_source = _get_source,
|
.get_source = _get_source,
|
||||||
.set_destination = _set_destination,
|
.set_destination = _set_destination,
|
||||||
@ -2699,13 +2927,23 @@ message_t *message_create_defrag(message_t *fragment)
|
|||||||
{
|
{
|
||||||
private_message_t *this;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
this = (private_message_t*)message_create(
|
this = (private_message_t*)clone_message((private_message_t*)fragment);
|
||||||
fragment->get_major_version(fragment),
|
/* we don't need a parser for IKEv2, the one for IKEv1 is created after
|
||||||
fragment->get_minor_version(fragment));
|
* 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,
|
INIT(this->frag,
|
||||||
.max_packet = lib->settings->get_int(lib->settings,
|
.max_packet = lib->settings->get_int(lib->settings,
|
||||||
"%s.max_packet", MAX_PACKET, lib->ns),
|
"%s.max_packet", MAX_PACKET, lib->ns),
|
||||||
|
@ -268,8 +268,6 @@ struct message_t {
|
|||||||
* Generates the message split into fragments of the given size (total IP
|
* Generates the message split into fragments of the given size (total IP
|
||||||
* datagram length).
|
* datagram length).
|
||||||
*
|
*
|
||||||
* @note Only supported for IKEv1 at the moment.
|
|
||||||
*
|
|
||||||
* @param keymat keymat to encrypt/sign message(s)
|
* @param keymat keymat to encrypt/sign message(s)
|
||||||
* @param frag_len fragment length (maximum total IP datagram length), 0
|
* @param frag_len fragment length (maximum total IP datagram length), 0
|
||||||
* for default value depending on address family
|
* for default value depending on address family
|
||||||
@ -277,7 +275,6 @@ struct message_t {
|
|||||||
* which are owned by the enumerator
|
* which are owned by the enumerator
|
||||||
* @return
|
* @return
|
||||||
* - SUCCESS if message could be fragmented
|
* - SUCCESS if message could be fragmented
|
||||||
* - INVALID_STATE if message is IKEv2
|
|
||||||
* - FAILED if fragmentation failed
|
* - FAILED if fragmentation failed
|
||||||
* - and the possible return values of generate()
|
* - 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
|
* Once the message is completed it should be processed like any other
|
||||||
* inbound message.
|
* inbound message.
|
||||||
*
|
*
|
||||||
* @note Only supported for IKEv1 at the moment.
|
|
||||||
*
|
|
||||||
* @param fragment fragment to add
|
* @param fragment fragment to add
|
||||||
* @return
|
* @return
|
||||||
* - SUCCESS if message was reassembled
|
* - SUCCESS if message was reassembled
|
||||||
|
Loading…
x
Reference in New Issue
Block a user