Merge branch 'ikev2-fragmentation'

This adds support for IKEv2 fragmentation as per RFC 7383.
This commit is contained in:
Tobias Brunner
2014-10-10 09:35:27 +02:00
36 changed files with 2717 additions and 1270 deletions
+4 -3
View File
@@ -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.
+1 -1
View File
@@ -19,7 +19,7 @@
#include <netinet/udp.h>
#include <encoding/payloads/cert_payload.h>
#include <encoding/payloads/encryption_payload.h>
#include <encoding/payloads/encrypted_payload.h>
typedef struct private_ike_auth_fill_t private_ike_auth_fill_t;
+1 -1
View File
@@ -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 \
+2 -1
View File
@@ -23,7 +23,8 @@ 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/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 \
+4 -2
View File
@@ -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,
File diff suppressed because it is too large Load Diff
+68 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2011 Tobias Brunner
* Copyright (C) 2006-2014 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005 Jan Hutter
@@ -39,7 +39,7 @@ typedef struct message_t message_t;
*
* The message handles parsing and generation of payloads
* via parser_t/generator_t. Encryption is done transparently
* via the encryption_payload_t. A set of rules for messages
* via the encrypted_payload_t. A set of rules for messages
* and payloads does check parsed messages.
*/
struct message_t {
@@ -264,6 +264,53 @@ struct message_t {
*/
bool (*is_encoded)(message_t *this);
/**
* Generates the message split into fragments of the given size (total IP
* datagram length).
*
* @param keymat keymat to encrypt/sign message(s)
* @param frag_len fragment length (maximum total IP datagram length), 0
* for default value depending on address family
* @param fragments receives an enumerator with generated packet_t*,
* which are owned by the enumerator
* @return
* - SUCCESS if message could be fragmented
* - FAILED if fragmentation failed
* - and the possible return values of generate()
*/
status_t (*fragment)(message_t *this, keymat_t *keymat, size_t frag_len,
enumerator_t **fragments);
/**
* Check if the message has been encoded and fragmented using fragment(),
* and whether there actually resulted fragments (if not is_encoded() will
* be TRUE).
*
* The packets of individual fragments can be retrieved with
* get_fragments().
*
* @return TRUE if message has been encoded and fragmented
*/
bool (*is_fragmented)(message_t *this);
/**
* Add a fragment to the message if it was created with
* message_create_defrag().
*
* Once the message is completed it should be processed like any other
* inbound message.
*
* @param fragment fragment to add
* @return
* - SUCCESS if message was reassembled
* - NEED_MORE if not all fragments have yet been received
* - FAILED if reassembling failed
* - INVALID_ARG if fragment is invalid for some reason
* - INVALID_STATE if message was not created using
* message_create_defrag()
*/
status_t (*add_fragment)(message_t *this, message_t *fragment);
/**
* Gets the source host informations.
*
@@ -337,11 +384,11 @@ struct message_t {
notify_payload_t* (*get_notify)(message_t *this, notify_type_t type);
/**
* Returns a clone of the internal stored packet_t object.
* Returns a clone of the internally stored packet_t object.
*
* @return packet_t object as clone of internal one
*/
packet_t * (*get_packet) (message_t *this);
packet_t *(*get_packet) (message_t *this);
/**
* Returns a chunk pointing to internal packet_t data.
@@ -350,6 +397,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.
*/
@@ -380,4 +434,14 @@ message_t *message_create_from_packet(packet_t *packet);
*/
message_t *message_create(int major, int minor);
/**
* Creates a message_t object that is used to reassemble fragmented messages.
*
* Use add_fragment() to add fragments.
*
* @param fragment initial fragment (is not added)
* @return message_t object, NULL if fragment is not actually one
*/
message_t *message_create_defrag(message_t *fragment);
#endif /** MESSAGE_H_ @}*/
+1 -1
View File
@@ -32,7 +32,7 @@
#include <encoding/payloads/nonce_payload.h>
#include <encoding/payloads/id_payload.h>
#include <encoding/payloads/notify_payload.h>
#include <encoding/payloads/encryption_payload.h>
#include <encoding/payloads/encrypted_payload.h>
#include <encoding/payloads/auth_payload.h>
#include <encoding/payloads/cert_payload.h>
#include <encoding/payloads/certreq_payload.h>
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2014 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup encrypted_fragment_payload encrypted_fragment_payload
* @{ @ingroup payloads
*/
#ifndef ENCRYPTED_FRAGMENT_PAYLOAD_H_
#define ENCRYPTED_FRAGMENT_PAYLOAD_H_
typedef struct encrypted_fragment_payload_t encrypted_fragment_payload_t;
#include <encoding/payloads/encrypted_payload.h>
/**
* The Encrypted Fragment Payload as described in RFC 7383
*
* The implementation is located in encrypted_payload.c as it is very similar.
*/
struct encrypted_fragment_payload_t {
/**
* Implements payload_t interface.
*/
encrypted_payload_t encrypted;
/**
* Get the fragment number.
*
* @return fragment number
*/
u_int16_t (*get_fragment_number)(encrypted_fragment_payload_t *this);
/**
* Get the total number of fragments.
*
* @return total number of fragments
*/
u_int16_t (*get_total_fragments)(encrypted_fragment_payload_t *this);
/**
* Get the (decrypted) content of this payload.
*
* @return internal payload data
*/
chunk_t (*get_content)(encrypted_fragment_payload_t *this);
/**
* Destroys an encrypted_fragment_payload_t object.
*/
void (*destroy)(encrypted_fragment_payload_t *this);
};
/**
* Creates an empty encrypted_fragment_payload_t object.
*
* @return encrypted_fragment_payload_t object
*/
encrypted_fragment_payload_t *encrypted_fragment_payload_create();
/**
* Creates an encrypted fragment payload from the given data.
*
* @param num fragment number (first one should be 1)
* @param total total number of fragments
* @param data fragment data (gets cloned)
* @return encrypted_fragment_payload_t object
*/
encrypted_fragment_payload_t *encrypted_fragment_payload_create_from_data(
u_int16_t num, u_int16_t total, chunk_t data);
#endif /** ENCRYPTED_FRAGMENT_PAYLOAD_H_ @}*/
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,5 @@
/*
* Copyright (C) 2014 Tobias Brunner
* Copyright (C) 2005-2010 Martin Willi
* Copyright (C) 2010 revosec AG
* Copyright (C) 2005 Jan Hutter
@@ -16,23 +17,24 @@
*/
/**
* @defgroup encryption_payload encryption_payload
* @defgroup encrypted_payload encrypted_payload
* @{ @ingroup payloads
*/
#ifndef ENCRYPTION_PAYLOAD_H_
#define ENCRYPTION_PAYLOAD_H_
#ifndef ENCRYPTED_PAYLOAD_H_
#define ENCRYPTED_PAYLOAD_H_
typedef struct encryption_payload_t encryption_payload_t;
typedef struct encrypted_payload_t encrypted_payload_t;
#include <library.h>
#include <crypto/aead.h>
#include <encoding/payloads/payload.h>
#include <encoding/generator.h>
/**
* The encryption payload as described in RFC section 3.14.
* The encrypted payload as described in RFC section 3.14.
*/
struct encryption_payload_t {
struct encrypted_payload_t {
/**
* Implements payload_t interface.
@@ -44,14 +46,14 @@ struct encryption_payload_t {
*
* @return (expected) payload length
*/
size_t (*get_length)(encryption_payload_t *this);
size_t (*get_length)(encrypted_payload_t *this);
/**
* Adds a payload to this encryption payload.
*
* @param payload payload_t object to add
*/
void (*add_payload) (encryption_payload_t *this, payload_t *payload);
void (*add_payload) (encrypted_payload_t *this, payload_t *payload);
/**
* Remove the first payload in the list
@@ -59,14 +61,22 @@ struct encryption_payload_t {
* @param payload removed payload
* @return payload, NULL if none left
*/
payload_t* (*remove_payload)(encryption_payload_t *this);
payload_t* (*remove_payload)(encrypted_payload_t *this);
/**
* Uses the given generator to generate the contained payloads.
*
* @param generator generator used to generate the contained payloads
*/
void (*generate_payloads)(encrypted_payload_t *this,
generator_t *generator);
/**
* Set the AEAD transform to use.
*
* @param aead aead transform to use
*/
void (*set_transform) (encryption_payload_t *this, aead_t *aead);
void (*set_transform) (encrypted_payload_t *this, aead_t *aead);
/**
* Generate, encrypt and sign contained payloads.
@@ -78,7 +88,7 @@ struct encryption_payload_t {
* - FAILED if encryption failed
* - INVALID_STATE if aead not supplied, but needed
*/
status_t (*encrypt) (encryption_payload_t *this, u_int64_t mid,
status_t (*encrypt) (encrypted_payload_t *this, u_int64_t mid,
chunk_t assoc);
/**
@@ -92,20 +102,31 @@ struct encryption_payload_t {
* - FAILED if integrity check failed
* - INVALID_STATE if aead not supplied, but needed
*/
status_t (*decrypt) (encryption_payload_t *this, chunk_t assoc);
status_t (*decrypt) (encrypted_payload_t *this, chunk_t assoc);
/**
* Destroys an encryption_payload_t object.
* Destroys an encrypted_payload_t object.
*/
void (*destroy) (encryption_payload_t *this);
void (*destroy) (encrypted_payload_t *this);
};
/**
* Creates an empty encryption_payload_t object.
* Creates an empty encrypted_payload_t object.
*
* @param type PLV2_ENCRYPTED or PLV1_ENCRYPTED
* @return encryption_payload_t object
* @return encrypted_payload_t object
*/
encryption_payload_t *encryption_payload_create(payload_type_t type);
encrypted_payload_t *encrypted_payload_create(payload_type_t type);
#endif /** ENCRYPTION_PAYLOAD_H_ @}*/
/**
* 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_ @}*/
@@ -1,635 +0,0 @@
/*
* Copyright (C) 2005-2010 Martin Willi
* Copyright (C) 2010 revosec AG
* Copyright (C) 2011 Tobias Brunner
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <stddef.h>
#include <string.h>
#include "encryption_payload.h"
#include <daemon.h>
#include <encoding/payloads/encodings.h>
#include <collections/linked_list.h>
#include <encoding/generator.h>
#include <encoding/parser.h>
typedef struct private_encryption_payload_t private_encryption_payload_t;
/**
* Private data of an encryption_payload_t' Object.
*
*/
struct private_encryption_payload_t {
/**
* Public encryption_payload_t interface.
*/
encryption_payload_t public;
/**
* There is no next payload for an encryption payload,
* since encryption payload MUST be the last one.
* next_payload means here the first payload of the
* contained, encrypted payload.
*/
u_int8_t next_payload;
/**
* Flags, including reserved bits
*/
u_int8_t flags;
/**
* Length of this payload
*/
u_int16_t payload_length;
/**
* Chunk containing the IV, plain, padding and ICV.
*/
chunk_t encrypted;
/**
* AEAD transform to use
*/
aead_t *aead;
/**
* Contained payloads
*/
linked_list_t *payloads;
/**
* Type of payload, PLV2_ENCRYPTED or PLV1_ENCRYPTED
*/
payload_type_t type;
};
/**
* Encoding rules to parse or generate a IKEv2-Encryption Payload.
*
* The defined offsets are the positions in a object of type
* private_encryption_payload_t.
*/
static encoding_rule_t encodings_v2[] = {
/* 1 Byte next payload type, stored in the field next_payload */
{ U_INT_8, offsetof(private_encryption_payload_t, next_payload) },
/* Critical and 7 reserved bits, all stored for reconstruction */
{ U_INT_8, offsetof(private_encryption_payload_t, flags) },
/* Length of the whole encryption payload*/
{ PAYLOAD_LENGTH, offsetof(private_encryption_payload_t, payload_length) },
/* encrypted data, stored in a chunk. contains iv, data, padding */
{ CHUNK_DATA, offsetof(private_encryption_payload_t, encrypted) },
};
/*
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Next Payload !C! RESERVED ! Payload Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Initialization Vector !
! (length is block size for encryption algorithm) !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Encrypted IKE Payloads !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! ! Padding (0-255 octets) !
+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
! ! Pad Length !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ Integrity Checksum Data ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/**
* Encoding rules to parse or generate a complete encrypted IKEv1 message.
*
* The defined offsets are the positions in a object of type
* private_encryption_payload_t.
*/
static encoding_rule_t encodings_v1[] = {
/* encrypted data, stored in a chunk */
{ ENCRYPTED_DATA, offsetof(private_encryption_payload_t, encrypted) },
};
/*
1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! Encrypted IKE Payloads !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
! ! Padding (0-255 octets) !
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
METHOD(payload_t, verify, status_t,
private_encryption_payload_t *this)
{
return SUCCESS;
}
METHOD(payload_t, get_encoding_rules, int,
private_encryption_payload_t *this, encoding_rule_t **rules)
{
if (this->type == PLV2_ENCRYPTED)
{
*rules = encodings_v2;
return countof(encodings_v2);
}
*rules = encodings_v1;
return countof(encodings_v1);
}
METHOD(payload_t, get_header_length, int,
private_encryption_payload_t *this)
{
if (this->type == PLV2_ENCRYPTED)
{
return 4;
}
return 0;
}
METHOD(payload_t, get_type, payload_type_t,
private_encryption_payload_t *this)
{
return this->type;
}
METHOD(payload_t, get_next_type, payload_type_t,
private_encryption_payload_t *this)
{
return this->next_payload;
}
METHOD(payload_t, set_next_type, void,
private_encryption_payload_t *this, payload_type_t type)
{
/* the next payload is set during add, still allow this for IKEv1 */
this->next_payload = type;
}
/**
* Compute the length of the whole payload
*/
static void compute_length(private_encryption_payload_t *this)
{
enumerator_t *enumerator;
payload_t *payload;
size_t bs, length = 0;
if (this->encrypted.len)
{
length = this->encrypted.len;
}
else
{
enumerator = this->payloads->create_enumerator(this->payloads);
while (enumerator->enumerate(enumerator, &payload))
{
length += payload->get_length(payload);
}
enumerator->destroy(enumerator);
if (this->aead)
{
/* append padding */
bs = this->aead->get_block_size(this->aead);
length += bs - (length % bs);
/* add iv */
length += this->aead->get_iv_size(this->aead);
/* add icv */
length += this->aead->get_icv_size(this->aead);
}
}
length += get_header_length(this);
this->payload_length = length;
}
METHOD2(payload_t, encryption_payload_t, get_length, size_t,
private_encryption_payload_t *this)
{
compute_length(this);
return this->payload_length;
}
METHOD(encryption_payload_t, add_payload, void,
private_encryption_payload_t *this, payload_t *payload)
{
payload_t *last_payload;
if (this->payloads->get_count(this->payloads) > 0)
{
this->payloads->get_last(this->payloads, (void **)&last_payload);
last_payload->set_next_type(last_payload, payload->get_type(payload));
}
else
{
this->next_payload = payload->get_type(payload);
}
payload->set_next_type(payload, PL_NONE);
this->payloads->insert_last(this->payloads, payload);
compute_length(this);
}
METHOD(encryption_payload_t, remove_payload, payload_t *,
private_encryption_payload_t *this)
{
payload_t *payload;
if (this->payloads->remove_first(this->payloads,
(void**)&payload) == SUCCESS)
{
return payload;
}
return NULL;
}
/**
* Generate payload before encryption
*/
static chunk_t generate(private_encryption_payload_t *this,
generator_t *generator)
{
payload_t *current, *next;
enumerator_t *enumerator;
u_int32_t *lenpos;
chunk_t chunk = chunk_empty;
enumerator = this->payloads->create_enumerator(this->payloads);
if (enumerator->enumerate(enumerator, &current))
{
this->next_payload = current->get_type(current);
while (enumerator->enumerate(enumerator, &next))
{
current->set_next_type(current, next->get_type(next));
generator->generate_payload(generator, current);
current = next;
}
current->set_next_type(current, PL_NONE);
generator->generate_payload(generator, current);
chunk = generator->get_chunk(generator, &lenpos);
DBG2(DBG_ENC, "generated content in encryption payload");
}
enumerator->destroy(enumerator);
return chunk;
}
/**
* Append the encryption payload header to the associated data
*/
static chunk_t append_header(private_encryption_payload_t *this, chunk_t assoc)
{
struct {
u_int8_t next_payload;
u_int8_t flags;
u_int16_t length;
} __attribute__((packed)) header = {
.next_payload = this->next_payload,
.flags = this->flags,
.length = htons(get_length(this)),
};
return chunk_cat("cc", assoc, chunk_from_thing(header));
}
METHOD(encryption_payload_t, encrypt, status_t,
private_encryption_payload_t *this, u_int64_t mid, chunk_t assoc)
{
chunk_t iv, plain, padding, icv, crypt;
generator_t *generator;
iv_gen_t *iv_gen;
rng_t *rng;
size_t bs;
if (this->aead == NULL)
{
DBG1(DBG_ENC, "encrypting encryption payload failed, transform missing");
return INVALID_STATE;
}
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!rng)
{
DBG1(DBG_ENC, "encrypting encryption payload failed, no RNG found");
return NOT_SUPPORTED;
}
iv_gen = this->aead->get_iv_gen(this->aead);
if (!iv_gen)
{
DBG1(DBG_ENC, "encrypting encryption payload failed, no IV generator");
return NOT_SUPPORTED;
}
assoc = append_header(this, assoc);
generator = generator_create();
plain = generate(this, generator);
bs = this->aead->get_block_size(this->aead);
/* we need at least one byte padding to store the padding length */
padding.len = bs - (plain.len % bs);
iv.len = this->aead->get_iv_size(this->aead);
icv.len = this->aead->get_icv_size(this->aead);
/* prepare data to authenticate-encrypt:
* | IV | plain | padding | ICV |
* \____crypt______/ ^
* | /
* v /
* assoc -> + ------->/
*/
free(this->encrypted.ptr);
this->encrypted = chunk_alloc(iv.len + plain.len + padding.len + icv.len);
iv.ptr = this->encrypted.ptr;
memcpy(iv.ptr + iv.len, plain.ptr, plain.len);
plain.ptr = iv.ptr + iv.len;
padding.ptr = plain.ptr + plain.len;
icv.ptr = padding.ptr + padding.len;
crypt = chunk_create(plain.ptr, plain.len + padding.len);
generator->destroy(generator);
if (!iv_gen->get_iv(iv_gen, mid, iv.len, iv.ptr) ||
!rng->get_bytes(rng, padding.len - 1, padding.ptr))
{
DBG1(DBG_ENC, "encrypting encryption payload failed, no IV or padding");
rng->destroy(rng);
free(assoc.ptr);
return FAILED;
}
padding.ptr[padding.len - 1] = padding.len - 1;
rng->destroy(rng);
DBG3(DBG_ENC, "encryption payload encryption:");
DBG3(DBG_ENC, "IV %B", &iv);
DBG3(DBG_ENC, "plain %B", &plain);
DBG3(DBG_ENC, "padding %B", &padding);
DBG3(DBG_ENC, "assoc %B", &assoc);
if (!this->aead->encrypt(this->aead, crypt, assoc, iv, NULL))
{
free(assoc.ptr);
return FAILED;
}
DBG3(DBG_ENC, "encrypted %B", &crypt);
DBG3(DBG_ENC, "ICV %B", &icv);
free(assoc.ptr);
return SUCCESS;
}
METHOD(encryption_payload_t, encrypt_v1, status_t,
private_encryption_payload_t *this, u_int64_t mid, chunk_t iv)
{
generator_t *generator;
chunk_t plain, padding;
size_t bs;
if (this->aead == NULL)
{
DBG1(DBG_ENC, "encryption failed, transform missing");
return INVALID_STATE;
}
generator = generator_create();
plain = generate(this, generator);
bs = this->aead->get_block_size(this->aead);
padding.len = bs - (plain.len % bs);
/* prepare data to encrypt:
* | plain | padding | */
free(this->encrypted.ptr);
this->encrypted = chunk_alloc(plain.len + padding.len);
memcpy(this->encrypted.ptr, plain.ptr, plain.len);
plain.ptr = this->encrypted.ptr;
padding.ptr = plain.ptr + plain.len;
memset(padding.ptr, 0, padding.len);
generator->destroy(generator);
DBG3(DBG_ENC, "encrypting payloads:");
DBG3(DBG_ENC, "IV %B", &iv);
DBG3(DBG_ENC, "plain %B", &plain);
DBG3(DBG_ENC, "padding %B", &padding);
if (!this->aead->encrypt(this->aead, this->encrypted, chunk_empty, iv, NULL))
{
return FAILED;
}
DBG3(DBG_ENC, "encrypted %B", &this->encrypted);
return SUCCESS;
}
/**
* Parse the payloads after decryption.
*/
static status_t parse(private_encryption_payload_t *this, chunk_t plain)
{
parser_t *parser;
payload_type_t type;
parser = parser_create(plain);
type = this->next_payload;
while (type != PL_NONE)
{
payload_t *payload;
if (plain.len < 4 || untoh16(plain.ptr + 2) > plain.len)
{
DBG1(DBG_ENC, "invalid %N payload length, decryption failed?",
payload_type_names, type);
parser->destroy(parser);
return PARSE_ERROR;
}
if (parser->parse_payload(parser, type, &payload) != SUCCESS)
{
parser->destroy(parser);
return PARSE_ERROR;
}
if (payload->verify(payload) != SUCCESS)
{
DBG1(DBG_ENC, "%N verification failed",
payload_type_names, payload->get_type(payload));
payload->destroy(payload);
parser->destroy(parser);
return VERIFY_ERROR;
}
type = payload->get_next_type(payload);
this->payloads->insert_last(this->payloads, payload);
}
parser->destroy(parser);
DBG2(DBG_ENC, "parsed content of encryption payload");
return SUCCESS;
}
METHOD(encryption_payload_t, decrypt, status_t,
private_encryption_payload_t *this, chunk_t assoc)
{
chunk_t iv, plain, padding, icv, crypt;
size_t bs;
if (this->aead == NULL)
{
DBG1(DBG_ENC, "decrypting encryption payload failed, transform missing");
return INVALID_STATE;
}
/* prepare data to authenticate-decrypt:
* | IV | plain | padding | ICV |
* \____crypt______/ ^
* | /
* v /
* assoc -> + ------->/
*/
bs = this->aead->get_block_size(this->aead);
iv.len = this->aead->get_iv_size(this->aead);
iv.ptr = this->encrypted.ptr;
icv.len = this->aead->get_icv_size(this->aead);
icv.ptr = this->encrypted.ptr + this->encrypted.len - icv.len;
crypt.ptr = iv.ptr + iv.len;
crypt.len = this->encrypted.len - iv.len;
if (iv.len + icv.len > this->encrypted.len ||
(crypt.len - icv.len) % bs)
{
DBG1(DBG_ENC, "decrypting encryption payload failed, invalid length");
return FAILED;
}
assoc = append_header(this, assoc);
DBG3(DBG_ENC, "encryption payload decryption:");
DBG3(DBG_ENC, "IV %B", &iv);
DBG3(DBG_ENC, "encrypted %B", &crypt);
DBG3(DBG_ENC, "ICV %B", &icv);
DBG3(DBG_ENC, "assoc %B", &assoc);
if (!this->aead->decrypt(this->aead, crypt, assoc, iv, NULL))
{
DBG1(DBG_ENC, "verifying encryption payload integrity failed");
free(assoc.ptr);
return FAILED;
}
free(assoc.ptr);
plain = chunk_create(crypt.ptr, crypt.len - icv.len);
padding.len = plain.ptr[plain.len - 1] + 1;
if (padding.len > plain.len)
{
DBG1(DBG_ENC, "decrypting encryption payload failed, "
"padding invalid %B", &crypt);
return PARSE_ERROR;
}
plain.len -= padding.len;
padding.ptr = plain.ptr + plain.len;
DBG3(DBG_ENC, "plain %B", &plain);
DBG3(DBG_ENC, "padding %B", &padding);
return parse(this, plain);
}
METHOD(encryption_payload_t, decrypt_v1, status_t,
private_encryption_payload_t *this, chunk_t iv)
{
if (this->aead == NULL)
{
DBG1(DBG_ENC, "decryption failed, transform missing");
return INVALID_STATE;
}
/* data must be a multiple of block size */
if (iv.len != this->aead->get_block_size(this->aead) ||
this->encrypted.len < iv.len || this->encrypted.len % iv.len)
{
DBG1(DBG_ENC, "decryption failed, invalid length");
return FAILED;
}
DBG3(DBG_ENC, "decrypting payloads:");
DBG3(DBG_ENC, "encrypted %B", &this->encrypted);
if (!this->aead->decrypt(this->aead, this->encrypted, chunk_empty, iv, NULL))
{
return FAILED;
}
DBG3(DBG_ENC, "plain %B", &this->encrypted);
return parse(this, this->encrypted);
}
METHOD(encryption_payload_t, set_transform, void,
private_encryption_payload_t *this, aead_t* aead)
{
this->aead = aead;
}
METHOD2(payload_t, encryption_payload_t, destroy, void,
private_encryption_payload_t *this)
{
this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy));
free(this->encrypted.ptr);
free(this);
}
/*
* Described in header
*/
encryption_payload_t *encryption_payload_create(payload_type_t type)
{
private_encryption_payload_t *this;
INIT(this,
.public = {
.payload_interface = {
.verify = _verify,
.get_encoding_rules = _get_encoding_rules,
.get_header_length = _get_header_length,
.get_length = _get_length,
.get_next_type = _get_next_type,
.set_next_type = _set_next_type,
.get_type = _get_type,
.destroy = _destroy,
},
.get_length = _get_length,
.add_payload = _add_payload,
.remove_payload = _remove_payload,
.set_transform = _set_transform,
.encrypt = _encrypt,
.decrypt = _decrypt,
.destroy = _destroy,
},
.next_payload = PL_NONE,
.payloads = linked_list_create(),
.type = type,
);
this->payload_length = get_header_length(this);
if (type == PLV1_ENCRYPTED)
{
this->public.encrypt = _encrypt_v1;
this->public.decrypt = _decrypt_v1;
}
return &this->public;
}
@@ -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",
@@ -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 */
+29 -14
View File
@@ -28,7 +28,8 @@
#include <encoding/payloads/auth_payload.h>
#include <encoding/payloads/cert_payload.h>
#include <encoding/payloads/certreq_payload.h>
#include <encoding/payloads/encryption_payload.h>
#include <encoding/payloads/encrypted_payload.h>
#include <encoding/payloads/encrypted_fragment_payload.h>
#include <encoding/payloads/ts_payload.h>
#include <encoding/payloads/delete_payload.h>
#include <encoding/payloads/vendor_id_payload.h>
@@ -59,7 +60,7 @@ ENUM_NEXT(payload_type_names, PLV1_SECURITY_ASSOCIATION, PLV1_CONFIGURATION, PL_
ENUM_NEXT(payload_type_names, PLV1_NAT_D, PLV1_NAT_OA, PLV1_CONFIGURATION,
"NAT_D_V1",
"NAT_OA_V1");
ENUM_NEXT(payload_type_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_NAT_OA,
ENUM_NEXT(payload_type_names, PLV2_SECURITY_ASSOCIATION, PLV2_FRAGMENT, PLV1_NAT_OA,
"SECURITY_ASSOCIATION",
"KEY_EXCHANGE",
"ID_INITIATOR",
@@ -76,16 +77,20 @@ ENUM_NEXT(payload_type_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_NAT_OA,
"ENCRYPTED",
"CONFIGURATION",
"EAP",
"GSPM");
"GSPM",
"GROUP_ID",
"GROUP_SECURITY_ASSOCIATION",
"KEY_DOWNLOAD",
"ENCRYPTED_FRAGMENT");
#ifdef ME
ENUM_NEXT(payload_type_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_GSPM,
ENUM_NEXT(payload_type_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_FRAGMENT,
"ID_PEER");
ENUM_NEXT(payload_type_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_ID_PEER,
"NAT_D_DRAFT_V1",
"NAT_OA_DRAFT_V1",
"FRAGMENT");
#else
ENUM_NEXT(payload_type_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_GSPM,
ENUM_NEXT(payload_type_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_FRAGMENT,
"NAT_D_DRAFT_V1",
"NAT_OA_DRAFT_V1",
"FRAGMENT");
@@ -125,7 +130,7 @@ ENUM_NEXT(payload_type_short_names, PLV1_SECURITY_ASSOCIATION, PLV1_CONFIGURATIO
ENUM_NEXT(payload_type_short_names, PLV1_NAT_D, PLV1_NAT_OA, PLV1_CONFIGURATION,
"NAT-D",
"NAT-OA");
ENUM_NEXT(payload_type_short_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_NAT_OA,
ENUM_NEXT(payload_type_short_names, PLV2_SECURITY_ASSOCIATION, PLV2_FRAGMENT, PLV1_NAT_OA,
"SA",
"KE",
"IDi",
@@ -142,16 +147,20 @@ ENUM_NEXT(payload_type_short_names, PLV2_SECURITY_ASSOCIATION, PLV2_GSPM, PLV1_N
"E",
"CP",
"EAP",
"GSPM");
"GSPM",
"IDg",
"GSA",
"KD",
"EF");
#ifdef ME
ENUM_NEXT(payload_type_short_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_GSPM,
ENUM_NEXT(payload_type_short_names, PLV2_ID_PEER, PLV2_ID_PEER, PLV2_FRAGMENT,
"IDp");
ENUM_NEXT(payload_type_short_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_ID_PEER,
"NAT-D",
"NAT-OA",
"FRAG");
#else
ENUM_NEXT(payload_type_short_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_GSPM,
ENUM_NEXT(payload_type_short_names, PLV1_NAT_D_DRAFT_00_03, PLV1_FRAGMENT, PLV2_FRAGMENT,
"NAT-D",
"NAT-OA",
"FRAG");
@@ -244,9 +253,11 @@ payload_t *payload_create(payload_type_t type)
return (payload_t*)eap_payload_create();
case PLV2_ENCRYPTED:
case PLV1_ENCRYPTED:
return (payload_t*)encryption_payload_create(type);
return (payload_t*)encrypted_payload_create(type);
case PLV1_FRAGMENT:
return (payload_t*)fragment_payload_create();
case PLV2_FRAGMENT:
return (payload_t*)encrypted_fragment_payload_create();
default:
return (payload_t*)unknown_payload_create(type);
}
@@ -261,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)
{
+22 -2
View File
@@ -193,7 +193,7 @@ enum payload_type_t {
PLV2_TS_RESPONDER = 45,
/**
* Encryption payload, contains other payloads (E).
* Encrypted payload, contains other payloads (E).
*/
PLV2_ENCRYPTED = 46,
@@ -212,6 +212,26 @@ enum payload_type_t {
*/
PLV2_GSPM = 49,
/**
* Group Identification (draft-yeung-g-ikev2)
*/
PLV2_IDG = 50,
/**
* Group Security Association (draft-yeung-g-ikev2)
*/
PLV2_GSA = 51,
/**
* Key Download (draft-yeung-g-ikev2)
*/
PLV2_KD = 52,
/**
* Encrypted fragment payload (SKF), RFC 7383
*/
PLV2_FRAGMENT = 53,
#ifdef ME
/**
* Identification payload for peers has a value from
@@ -231,7 +251,7 @@ enum payload_type_t {
PLV1_NAT_OA_DRAFT_00_03 = 131,
/**
* IKE fragment (proprietary IKEv1 extension)
* IKEv1 fragment (proprietary IKEv1 extension)
*/
PLV1_FRAGMENT = 132,
@@ -45,9 +45,6 @@
#include <daemon.h>
#include <threading/thread.h>
/* 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),
@@ -42,9 +42,6 @@
#include <threading/rwlock.h>
#include <collections/hashtable.h>
/* 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)
@@ -25,9 +25,6 @@
#include <mswsock.h>
/* 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++)
+64 -1
View File
@@ -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)
{
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)
+25 -7
View File
@@ -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
@@ -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),
};
@@ -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.
+3 -2
View File
@@ -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)
+99 -333
View File
@@ -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
*
@@ -38,8 +38,7 @@
#include <processing/jobs/dpd_timeout_job.h>
#include <processing/jobs/process_message_job.h>
#include <encoding/payloads/fragment_payload.h>
#include <bio/bio_writer.h>
#include <collections/array.h>
/**
* Number of old messages hashes we keep for retransmission.
@@ -50,20 +49,6 @@
*/
#define MAX_OLD_HASHES 2
/**
* Maximum packet size for fragmented packets (same as in sockets)
*/
#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 +112,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 +158,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
@@ -185,50 +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;
/**
* 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;
message_t *defrag;
/**
* List of queued tasks not yet in action
@@ -277,31 +221,16 @@ struct private_task_manager_t {
};
/**
* A single fragment within a fragmented message
* Reset retransmission packet list
*/
typedef struct {
/** fragment number */
u_int8_t num;
/** fragment data */
chunk_t data;
} fragment_t;
static void fragment_destroy(fragment_t *this)
static void clear_packets(array_t *array)
{
chunk_free(&this->data);
free(this);
}
packet_t *packet;
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;
while (array_remove(array, ARRAY_TAIL, &packet))
{
packet->destroy(packet);
}
}
METHOD(task_manager_t, flush_queue, void,
@@ -321,8 +250,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,110 +301,53 @@ 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);
enumerator = array_create_enumerator(packets);
while (enumerator->enumerate(enumerator, &packet))
{
charon->sender->send(charon->sender, packet->clone(packet));
}
enumerator->destroy(enumerator);
}
status = this->ike_sa->generate_message(this->ike_sa, message, &packet);
if (status != SUCCESS)
/**
* 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)
{
DBG1(DBG_IKE, "failed to generate IKE fragment");
message->destroy(message);
return FALSE;
}
charon->sender->send(charon->sender, packet);
message->destroy(message);
while (fragments->enumerate(fragments, &fragment))
{
array_insert_create(packets, ARRAY_TAIL, fragment);
}
fragments->destroy(fragments);
return TRUE;
}
/**
* Send a packet, if supported and required do so in fragments
* Retransmit a packet (or its fragments)
*/
static bool send_packet(private_task_manager_t *this, bool request,
packet_t *packet)
{
bool use_frags = FALSE;
ike_cfg_t *ike_cfg;
chunk_t data;
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;
}
}
data = packet->get_data(packet);
if (data.len > this->frag.size && 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);
return TRUE;
}
charon->sender->send(charon->sender, packet);
return TRUE;
}
/**
* Retransmit a packet, either as initiator or as responder
*/
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 +363,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 +374,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 +456,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 +659,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 +678,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 +714,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 +765,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 +799,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 +835,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);
}
@@ -1075,7 +938,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)
@@ -1164,8 +1026,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 +1098,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)
{
@@ -1258,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;
@@ -1435,15 +1211,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 +1259,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
{
@@ -1657,7 +1430,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
{
@@ -1984,17 +1756,16 @@ 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;
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;
@@ -2045,11 +1816,13 @@ 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);
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);
}
@@ -2094,13 +1867,6 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
.responding = {
.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", MAX_FRAGMENT_SIZE, lib->ns),
},
.ike_sa = ike_sa,
.rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
.queued_tasks = linked_list_create(),
+171 -44
View File
@@ -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,17 @@ 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,
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa));
}
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 +386,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 +413,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 +605,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 +677,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 +745,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 +808,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 +1069,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 +1304,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 +1321,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 +1361,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 +1660,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 +1719,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);
}
+23
View File
@@ -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;
}
+5
View File
@@ -28,6 +28,11 @@ typedef struct packet_t packet_t;
#include <library.h>
#include <networking/host.h>
/**
* Maximum packet size we handle by default
*/
#define PACKET_MAX_DEFAULT 10000
/**
* Abstraction of an IP/UDP-Packet, contains data, sender and receiver.
*/
@@ -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
@@ -0,0 +1,9 @@
A connection between the subnets behind the gateways <b>moon</b> and <b>sun</b> is set up.
The authentication is based on <b>X.509 certificates</b>. The IKEv2 fragmentation protocol
defined in <b>RFC 7383</b> prevents the IP fragmentation of the IKEv2 messages carrying the
large X.509 certificates.
<p/>
Upon the successful establishment of the IPsec tunnel, <b>leftfirewall=yes</b> automatically
inserts iptables-based firewall rules that let pass the tunneled traffic.
In order to test both tunnel and firewall, client <b>alice</b> behind gateway <b>moon</b>
pings client <b>bob</b> located behind gateway <b>sun</b>.
@@ -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
@@ -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
[email protected]
leftsubnet=10.1.0.0/16
leftfirewall=yes
right=PH_IP_SUN
[email protected]
rightsubnet=10.2.0.0/16
auto=add
@@ -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
}
@@ -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
[email protected]
leftsubnet=10.2.0.0/16
leftfirewall=yes
right=PH_IP_MOON
[email protected]
rightsubnet=10.1.0.0/16
auto=add
@@ -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
}
@@ -0,0 +1,5 @@
moon::ipsec stop
sun::ipsec stop
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush
@@ -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
@@ -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"