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/hash_payload.h>
|
||||
#include <encoding/payloads/encrypted_payload.h>
|
||||
#include <encoding/payloads/encrypted_fragment_payload.h>
|
||||
#include <encoding/payloads/unknown_payload.h>
|
||||
#include <encoding/payloads/cp_payload.h>
|
||||
#include <encoding/payloads/fragment_payload.h>
|
||||
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue