Merge branch 'mid-sync'
Adds support for handling IKEV2_MESSAGE_ID_SYNC notifies as responder (usually the original initiator) as defined in RFC 6311. Some HA solutions use these notifies to set the new IKEv2 message IDs after a failover event.
This commit is contained in:
@@ -106,6 +106,7 @@ sa/ikev2/tasks/ike_delete.c sa/ikev2/tasks/ike_delete.h \
|
|||||||
sa/ikev2/tasks/ike_dpd.c sa/ikev2/tasks/ike_dpd.h \
|
sa/ikev2/tasks/ike_dpd.c sa/ikev2/tasks/ike_dpd.h \
|
||||||
sa/ikev2/tasks/ike_init.c sa/ikev2/tasks/ike_init.h \
|
sa/ikev2/tasks/ike_init.c sa/ikev2/tasks/ike_init.h \
|
||||||
sa/ikev2/tasks/ike_natd.c sa/ikev2/tasks/ike_natd.h \
|
sa/ikev2/tasks/ike_natd.c sa/ikev2/tasks/ike_natd.h \
|
||||||
|
sa/ikev2/tasks/ike_mid_sync.c sa/ikev2/tasks/ike_mid_sync.h \
|
||||||
sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \
|
sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \
|
||||||
sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \
|
sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \
|
||||||
sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \
|
sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ sa/ikev2/tasks/ike_delete.c sa/ikev2/tasks/ike_delete.h \
|
|||||||
sa/ikev2/tasks/ike_dpd.c sa/ikev2/tasks/ike_dpd.h \
|
sa/ikev2/tasks/ike_dpd.c sa/ikev2/tasks/ike_dpd.h \
|
||||||
sa/ikev2/tasks/ike_init.c sa/ikev2/tasks/ike_init.h \
|
sa/ikev2/tasks/ike_init.c sa/ikev2/tasks/ike_init.h \
|
||||||
sa/ikev2/tasks/ike_natd.c sa/ikev2/tasks/ike_natd.h \
|
sa/ikev2/tasks/ike_natd.c sa/ikev2/tasks/ike_natd.h \
|
||||||
|
sa/ikev2/tasks/ike_mid_sync.c sa/ikev2/tasks/ike_mid_sync.h \
|
||||||
sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \
|
sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \
|
||||||
sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \
|
sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \
|
||||||
sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \
|
sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \
|
||||||
|
|||||||
@@ -617,6 +617,12 @@ METHOD(ike_sa_t, set_message_id, void,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
METHOD(ike_sa_t, get_message_id, uint32_t,
|
||||||
|
private_ike_sa_t *this, bool initiate)
|
||||||
|
{
|
||||||
|
return this->task_manager->get_mid(this->task_manager, initiate);
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(ike_sa_t, send_keepalive, void,
|
METHOD(ike_sa_t, send_keepalive, void,
|
||||||
private_ike_sa_t *this, bool scheduled)
|
private_ike_sa_t *this, bool scheduled)
|
||||||
{
|
{
|
||||||
@@ -2885,6 +2891,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
|
|||||||
.get_other_host = _get_other_host,
|
.get_other_host = _get_other_host,
|
||||||
.set_other_host = _set_other_host,
|
.set_other_host = _set_other_host,
|
||||||
.set_message_id = _set_message_id,
|
.set_message_id = _set_message_id,
|
||||||
|
.get_message_id = _get_message_id,
|
||||||
.float_ports = _float_ports,
|
.float_ports = _float_ports,
|
||||||
.update_hosts = _update_hosts,
|
.update_hosts = _update_hosts,
|
||||||
.get_my_id = _get_my_id,
|
.get_my_id = _get_my_id,
|
||||||
|
|||||||
@@ -151,6 +151,11 @@ enum ike_extension_t {
|
|||||||
* IKEv2 Redirect Mechanism, RFC 5685
|
* IKEv2 Redirect Mechanism, RFC 5685
|
||||||
*/
|
*/
|
||||||
EXT_IKE_REDIRECTION = (1<<13),
|
EXT_IKE_REDIRECTION = (1<<13),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IKEv2 Message ID sync, RFC 6311
|
||||||
|
*/
|
||||||
|
EXT_IKE_MESSAGE_ID_SYNC = (1<<14),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -554,7 +559,7 @@ struct ike_sa_t {
|
|||||||
void (*set_proposal)(ike_sa_t *this, proposal_t *proposal);
|
void (*set_proposal)(ike_sa_t *this, proposal_t *proposal);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the message id of the IKE_SA.
|
* Set the message ID of the IKE_SA.
|
||||||
*
|
*
|
||||||
* The IKE_SA stores two message IDs, one for initiating exchanges (send)
|
* The IKE_SA stores two message IDs, one for initiating exchanges (send)
|
||||||
* and one to respond to exchanges (expect).
|
* and one to respond to exchanges (expect).
|
||||||
@@ -564,6 +569,17 @@ struct ike_sa_t {
|
|||||||
*/
|
*/
|
||||||
void (*set_message_id)(ike_sa_t *this, bool initiate, uint32_t mid);
|
void (*set_message_id)(ike_sa_t *this, bool initiate, uint32_t mid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the message ID of the IKE_SA.
|
||||||
|
*
|
||||||
|
* The IKE_SA stores two message IDs, one for initiating exchanges (send)
|
||||||
|
* and one to respond to exchanges (expect).
|
||||||
|
*
|
||||||
|
* @param initiate TRUE to get message ID for initiating
|
||||||
|
* @return current message
|
||||||
|
*/
|
||||||
|
uint32_t (*get_message_id)(ike_sa_t *this, bool initiate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an additional address for the peer.
|
* Add an additional address for the peer.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1896,6 +1896,12 @@ METHOD(task_manager_t, incr_mid, void,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
METHOD(task_manager_t, get_mid, uint32_t,
|
||||||
|
private_task_manager_t *this, bool initiate)
|
||||||
|
{
|
||||||
|
return initiate ? this->initiating.mid : this->responding.mid;
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(task_manager_t, reset, void,
|
METHOD(task_manager_t, reset, void,
|
||||||
private_task_manager_t *this, uint32_t initiate, uint32_t respond)
|
private_task_manager_t *this, uint32_t initiate, uint32_t respond)
|
||||||
{
|
{
|
||||||
@@ -1999,6 +2005,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
|
|||||||
.initiate = _initiate,
|
.initiate = _initiate,
|
||||||
.retransmit = _retransmit,
|
.retransmit = _retransmit,
|
||||||
.incr_mid = _incr_mid,
|
.incr_mid = _incr_mid,
|
||||||
|
.get_mid = _get_mid,
|
||||||
.reset = _reset,
|
.reset = _reset,
|
||||||
.adopt_tasks = _adopt_tasks,
|
.adopt_tasks = _adopt_tasks,
|
||||||
.adopt_child_tasks = _adopt_child_tasks,
|
.adopt_child_tasks = _adopt_child_tasks,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include <sa/ikev2/tasks/ike_delete.h>
|
#include <sa/ikev2/tasks/ike_delete.h>
|
||||||
#include <sa/ikev2/tasks/ike_config.h>
|
#include <sa/ikev2/tasks/ike_config.h>
|
||||||
#include <sa/ikev2/tasks/ike_dpd.h>
|
#include <sa/ikev2/tasks/ike_dpd.h>
|
||||||
|
#include <sa/ikev2/tasks/ike_mid_sync.h>
|
||||||
#include <sa/ikev2/tasks/ike_vendor.h>
|
#include <sa/ikev2/tasks/ike_vendor.h>
|
||||||
#include <sa/ikev2/tasks/ike_verify_peer_cert.h>
|
#include <sa/ikev2/tasks/ike_verify_peer_cert.h>
|
||||||
#include <sa/ikev2/tasks/child_create.h>
|
#include <sa/ikev2/tasks/child_create.h>
|
||||||
@@ -817,7 +818,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
|
|||||||
task_t *task;
|
task_t *task;
|
||||||
message_t *message;
|
message_t *message;
|
||||||
host_t *me, *other;
|
host_t *me, *other;
|
||||||
bool delete = FALSE, hook = FALSE;
|
bool delete = FALSE, hook = FALSE, mid_sync = FALSE;
|
||||||
ike_sa_id_t *id = NULL;
|
ike_sa_id_t *id = NULL;
|
||||||
uint64_t responder_spi = 0;
|
uint64_t responder_spi = 0;
|
||||||
bool result;
|
bool result;
|
||||||
@@ -836,6 +837,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
|
|||||||
enumerator = array_create_enumerator(this->passive_tasks);
|
enumerator = array_create_enumerator(this->passive_tasks);
|
||||||
while (enumerator->enumerate(enumerator, (void*)&task))
|
while (enumerator->enumerate(enumerator, (void*)&task))
|
||||||
{
|
{
|
||||||
|
if (task->get_type(task) == TASK_IKE_MID_SYNC)
|
||||||
|
{
|
||||||
|
mid_sync = TRUE;
|
||||||
|
}
|
||||||
switch (task->build(task, message))
|
switch (task->build(task, message))
|
||||||
{
|
{
|
||||||
case SUCCESS:
|
case SUCCESS:
|
||||||
@@ -908,6 +913,15 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
|
|||||||
}
|
}
|
||||||
return DESTROY_ME;
|
return DESTROY_ME;
|
||||||
}
|
}
|
||||||
|
else if (mid_sync)
|
||||||
|
{
|
||||||
|
/* we don't want to resend messages to sync MIDs if requests with the
|
||||||
|
* previous MID arrive */
|
||||||
|
clear_packets(this->responding.packets);
|
||||||
|
/* avoid increasing the expected message ID after handling a message
|
||||||
|
* to sync MIDs with MID 0 */
|
||||||
|
return NEED_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
array_compress(this->passive_tasks);
|
array_compress(this->passive_tasks);
|
||||||
|
|
||||||
@@ -1069,6 +1083,10 @@ static status_t process_request(private_task_manager_t *this,
|
|||||||
task = (task_t*)ike_redirect_create(
|
task = (task_t*)ike_redirect_create(
|
||||||
this->ike_sa, NULL);
|
this->ike_sa, NULL);
|
||||||
break;
|
break;
|
||||||
|
case IKEV2_MESSAGE_ID_SYNC:
|
||||||
|
task = (task_t*)ike_mid_sync_create(
|
||||||
|
this->ike_sa);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1200,6 +1218,12 @@ METHOD(task_manager_t, incr_mid, void,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
METHOD(task_manager_t, get_mid, uint32_t,
|
||||||
|
private_task_manager_t *this, bool initiate)
|
||||||
|
{
|
||||||
|
return initiate ? this->initiating.mid : this->responding.mid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle the given IKE fragment, if it is one.
|
* Handle the given IKE fragment, if it is one.
|
||||||
*
|
*
|
||||||
@@ -1373,6 +1397,64 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a message with message ID 0 looks like it is used to synchronize
|
||||||
|
* the message IDs.
|
||||||
|
*/
|
||||||
|
static bool looks_like_mid_sync(private_task_manager_t *this, message_t *msg,
|
||||||
|
bool strict)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
notify_payload_t *notify;
|
||||||
|
payload_t *payload;
|
||||||
|
bool found = FALSE, other = FALSE;
|
||||||
|
|
||||||
|
if (msg->get_exchange_type(msg) == INFORMATIONAL)
|
||||||
|
{
|
||||||
|
enumerator = msg->create_payload_enumerator(msg);
|
||||||
|
while (enumerator->enumerate(enumerator, &payload))
|
||||||
|
{
|
||||||
|
if (payload->get_type(payload) == PLV2_NOTIFY)
|
||||||
|
{
|
||||||
|
notify = (notify_payload_t*)payload;
|
||||||
|
switch (notify->get_notify_type(notify))
|
||||||
|
{
|
||||||
|
case IKEV2_MESSAGE_ID_SYNC:
|
||||||
|
case IPSEC_REPLAY_COUNTER_SYNC:
|
||||||
|
found = TRUE;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strict)
|
||||||
|
{
|
||||||
|
other = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
}
|
||||||
|
return found && !other;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a message with message ID 0 looks like it is used to synchronize
|
||||||
|
* the message IDs and we are prepared to process it.
|
||||||
|
*
|
||||||
|
* Note: This is not called if the responder never sent a message before (i.e.
|
||||||
|
* we expect MID 0).
|
||||||
|
*/
|
||||||
|
static bool is_mid_sync(private_task_manager_t *this, message_t *msg)
|
||||||
|
{
|
||||||
|
if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED &&
|
||||||
|
this->ike_sa->supports_extension(this->ike_sa,
|
||||||
|
EXT_IKE_MESSAGE_ID_SYNC))
|
||||||
|
{
|
||||||
|
return looks_like_mid_sync(this, msg, TRUE);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(task_manager_t, process_message, status_t,
|
METHOD(task_manager_t, process_message, status_t,
|
||||||
private_task_manager_t *this, message_t *msg)
|
private_task_manager_t *this, message_t *msg)
|
||||||
@@ -1421,7 +1503,7 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
mid = msg->get_message_id(msg);
|
mid = msg->get_message_id(msg);
|
||||||
if (msg->get_request(msg))
|
if (msg->get_request(msg))
|
||||||
{
|
{
|
||||||
if (mid == this->responding.mid)
|
if (mid == this->responding.mid || (mid == 0 && is_mid_sync(this, msg)))
|
||||||
{
|
{
|
||||||
/* reject initial messages if not received in specific states,
|
/* reject initial messages if not received in specific states,
|
||||||
* after rekeying we only expect a DELETE in an INFORMATIONAL */
|
* after rekeying we only expect a DELETE in an INFORMATIONAL */
|
||||||
@@ -1462,7 +1544,8 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((mid == this->responding.mid - 1) &&
|
else if ((mid == this->responding.mid - 1) &&
|
||||||
array_count(this->responding.packets))
|
array_count(this->responding.packets) &&
|
||||||
|
!(mid == 0 && looks_like_mid_sync(this, msg, FALSE)))
|
||||||
{
|
{
|
||||||
status = handle_fragment(this, &this->responding.defrag, msg);
|
status = handle_fragment(this, &this->responding.defrag, msg);
|
||||||
if (status != SUCCESS)
|
if (status != SUCCESS)
|
||||||
@@ -1477,7 +1560,7 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
|
DBG1(DBG_IKE, "received message ID %d, expected %d, ignored",
|
||||||
mid, this->responding.mid);
|
mid, this->responding.mid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1515,7 +1598,7 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
|
DBG1(DBG_IKE, "received message ID %d, expected %d, ignored",
|
||||||
mid, this->initiating.mid);
|
mid, this->initiating.mid);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -2046,6 +2129,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
|
|||||||
.initiate = _initiate,
|
.initiate = _initiate,
|
||||||
.retransmit = _retransmit,
|
.retransmit = _retransmit,
|
||||||
.incr_mid = _incr_mid,
|
.incr_mid = _incr_mid,
|
||||||
|
.get_mid = _get_mid,
|
||||||
.reset = _reset,
|
.reset = _reset,
|
||||||
.adopt_tasks = _adopt_tasks,
|
.adopt_tasks = _adopt_tasks,
|
||||||
.adopt_child_tasks = _adopt_child_tasks,
|
.adopt_child_tasks = _adopt_child_tasks,
|
||||||
|
|||||||
@@ -417,6 +417,9 @@ METHOD(task_t, build_i, status_t,
|
|||||||
/* indicate support for EAP-only authentication */
|
/* indicate support for EAP-only authentication */
|
||||||
message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION,
|
message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION,
|
||||||
chunk_empty);
|
chunk_empty);
|
||||||
|
/* indicate support for RFC 6311 Message ID synchronization */
|
||||||
|
message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC_SUPPORTED,
|
||||||
|
chunk_empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->do_another_auth && !this->my_auth)
|
if (!this->do_another_auth && !this->my_auth)
|
||||||
@@ -992,6 +995,10 @@ METHOD(task_t, process_i, status_t,
|
|||||||
DBG1(DBG_IKE, "received invalid REDIRECT notify");
|
DBG1(DBG_IKE, "received invalid REDIRECT notify");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case IKEV2_MESSAGE_ID_SYNC_SUPPORTED:
|
||||||
|
this->ike_sa->enable_extension(this->ike_sa,
|
||||||
|
EXT_IKE_MESSAGE_ID_SYNC);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if (type <= 16383)
|
if (type <= 16383)
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Tobias Brunner
|
||||||
|
* HSR 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Stephen J. Bevan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ike_mid_sync.h"
|
||||||
|
|
||||||
|
#include <daemon.h>
|
||||||
|
#include <bio/bio_reader.h>
|
||||||
|
#include <bio/bio_writer.h>
|
||||||
|
#include <encoding/payloads/notify_payload.h>
|
||||||
|
|
||||||
|
typedef struct private_ike_mid_sync_t private_ike_mid_sync_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private members
|
||||||
|
*/
|
||||||
|
struct private_ike_mid_sync_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public methods and task_t interface.
|
||||||
|
*/
|
||||||
|
ike_mid_sync_t public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigned IKE_SA.
|
||||||
|
*/
|
||||||
|
ike_sa_t *ike_sa;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nonce sent by the peer and expected to be returned
|
||||||
|
*/
|
||||||
|
chunk_t nonce;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected next sender message ID
|
||||||
|
*/
|
||||||
|
uint32_t send;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected received message ID
|
||||||
|
*/
|
||||||
|
uint32_t recv;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encoding of IKEV2_MESSAGE_SYNC_ID notify, RFC 6311
|
||||||
|
*
|
||||||
|
* 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 |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* |Protocol ID(=0)| SPI Size (=0) | Notify Message Type |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Nonce Data |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | EXPECTED_SEND_REQ_MESSAGE_ID |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | EXPECTED_RECV_REQ_MESSAGE_ID |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC 6311 section 5.1
|
||||||
|
*
|
||||||
|
* o The peer MUST silently drop any received synchronization message
|
||||||
|
* if M1 is lower than or equal to the highest value it has seen from
|
||||||
|
* the cluster. This includes any previous received synchronization
|
||||||
|
* messages.
|
||||||
|
*/
|
||||||
|
METHOD(task_t, pre_process, status_t,
|
||||||
|
private_ike_mid_sync_t *this, message_t *message)
|
||||||
|
{
|
||||||
|
notify_payload_t *notify;
|
||||||
|
bio_reader_t *reader;
|
||||||
|
chunk_t nonce;
|
||||||
|
uint32_t resp;
|
||||||
|
|
||||||
|
if (message->get_message_id(message) != 0)
|
||||||
|
{ /* ignore the notify if it was contained in an INFORMATIONAL with
|
||||||
|
* unexpected message ID */
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
if (!this->ike_sa->supports_extension(this->ike_sa,
|
||||||
|
EXT_IKE_MESSAGE_ID_SYNC))
|
||||||
|
{
|
||||||
|
DBG1(DBG_ENC, "unexpected %N notify, ignored", notify_type_names,
|
||||||
|
IKEV2_MESSAGE_ID_SYNC);
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
notify = message->get_notify(message, IKEV2_MESSAGE_ID_SYNC);
|
||||||
|
|
||||||
|
reader = bio_reader_create(notify->get_notification_data(notify));
|
||||||
|
if (!reader->read_data(reader, 4, &nonce) ||
|
||||||
|
!reader->read_uint32(reader, &this->send) ||
|
||||||
|
!reader->read_uint32(reader, &this->recv))
|
||||||
|
{
|
||||||
|
reader->destroy(reader);
|
||||||
|
DBG1(DBG_ENC, "received invalid %N notify",
|
||||||
|
notify_type_names, IKEV2_MESSAGE_ID_SYNC);
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
reader->destroy(reader);
|
||||||
|
resp = this->ike_sa->get_message_id(this->ike_sa, FALSE);
|
||||||
|
if (this->send < resp)
|
||||||
|
{
|
||||||
|
DBG1(DBG_ENC, "ignore %N notify with lower (%d) than expected (%d) "
|
||||||
|
"sender MID", notify_type_names, IKEV2_MESSAGE_ID_SYNC, this->send,
|
||||||
|
resp);
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
this->nonce = chunk_clone(nonce);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there are any active tasks, indicating that we already
|
||||||
|
* used the currents message ID and are waiting for a response.
|
||||||
|
*/
|
||||||
|
static bool has_active_tasks(private_ike_mid_sync_t *this)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
task_t *task;
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
enumerator = this->ike_sa->create_task_enumerator(this->ike_sa,
|
||||||
|
TASK_QUEUE_ACTIVE);
|
||||||
|
active = enumerator->enumerate(enumerator, &task);
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC 6311 section 5.1
|
||||||
|
*
|
||||||
|
* o M2 MUST be at least the higher of the received M1, and one more
|
||||||
|
* than the highest sender value received from the cluster. This
|
||||||
|
* includes any previous received synchronization messages.
|
||||||
|
*
|
||||||
|
* o P2 MUST be the higher of the received P1 value, and one more than
|
||||||
|
* the highest sender value used by the peer.
|
||||||
|
*
|
||||||
|
* M1 is this->send, P1 is this->recv
|
||||||
|
*/
|
||||||
|
METHOD(task_t, process, status_t,
|
||||||
|
private_ike_mid_sync_t *this, message_t *message)
|
||||||
|
{
|
||||||
|
uint32_t resp, init, m2, p2;
|
||||||
|
|
||||||
|
if (message->get_message_id(message) != 0)
|
||||||
|
{ /* ignore the notify if it was contained in an INFORMATIONAL with
|
||||||
|
* unexpected message id */
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
resp = this->ike_sa->get_message_id(this->ike_sa, FALSE);
|
||||||
|
m2 = max(this->send, resp);
|
||||||
|
if (resp != m2)
|
||||||
|
{
|
||||||
|
this->ike_sa->set_message_id(this->ike_sa, FALSE, m2);
|
||||||
|
}
|
||||||
|
init = this->ike_sa->get_message_id(this->ike_sa, TRUE);
|
||||||
|
p2 = max(this->recv, has_active_tasks(this) ? init + 1 : init);
|
||||||
|
if (init != p2)
|
||||||
|
{
|
||||||
|
this->ike_sa->set_message_id(this->ike_sa, TRUE, p2);
|
||||||
|
}
|
||||||
|
DBG1(DBG_IKE, "responder requested MID sync: initiating %d[%d], "
|
||||||
|
"responding %d[%d]", p2, init, m2, resp);
|
||||||
|
this->send = p2;
|
||||||
|
this->recv = m2;
|
||||||
|
return NEED_MORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(task_t, build, status_t,
|
||||||
|
private_ike_mid_sync_t *this, message_t *message)
|
||||||
|
{
|
||||||
|
bio_writer_t *writer;
|
||||||
|
|
||||||
|
writer = bio_writer_create(12);
|
||||||
|
writer->write_data(writer, this->nonce);
|
||||||
|
writer->write_uint32(writer, this->send);
|
||||||
|
writer->write_uint32(writer, this->recv);
|
||||||
|
|
||||||
|
message->set_message_id(message, 0);
|
||||||
|
message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC,
|
||||||
|
writer->get_buf(writer));
|
||||||
|
|
||||||
|
writer->destroy(writer);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(task_t, get_type, task_type_t,
|
||||||
|
private_ike_mid_sync_t *this)
|
||||||
|
{
|
||||||
|
return TASK_IKE_MID_SYNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(task_t, migrate, void,
|
||||||
|
private_ike_mid_sync_t *this, ike_sa_t *ike_sa)
|
||||||
|
{
|
||||||
|
this->ike_sa = ike_sa;
|
||||||
|
chunk_free(&this->nonce);
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(task_t, destroy, void,
|
||||||
|
private_ike_mid_sync_t *this)
|
||||||
|
{
|
||||||
|
chunk_free(&this->nonce);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Described in header.
|
||||||
|
*/
|
||||||
|
ike_mid_sync_t *ike_mid_sync_create(ike_sa_t *ike_sa)
|
||||||
|
{
|
||||||
|
private_ike_mid_sync_t *this;
|
||||||
|
|
||||||
|
INIT(this,
|
||||||
|
.public = {
|
||||||
|
.task = {
|
||||||
|
.get_type = _get_type,
|
||||||
|
.build = _build,
|
||||||
|
.pre_process = _pre_process,
|
||||||
|
.process = _process,
|
||||||
|
.migrate = _migrate,
|
||||||
|
.destroy = _destroy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.ike_sa = ike_sa,
|
||||||
|
);
|
||||||
|
return &this->public;
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Tobias Brunner
|
||||||
|
* HSR 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Stephen J. Bevan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup ike_mid_sync ike_mid_sync
|
||||||
|
* @{ @ingroup tasks_v2
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IKE_MID_SYNC_H_
|
||||||
|
#define IKE_MID_SYNC_H_
|
||||||
|
|
||||||
|
typedef struct ike_mid_sync_t ike_mid_sync_t;
|
||||||
|
|
||||||
|
#include <library.h>
|
||||||
|
#include <sa/ike_sa.h>
|
||||||
|
#include <sa/task.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task of type TASK_IKE_MID_SYNC, implements RFC 6311 responder.
|
||||||
|
*
|
||||||
|
* This task handles an IKEV2_MESSAGE_ID_SYNC notify sent by a peer
|
||||||
|
* and if acceptable updates the SA MIDs and replies with the updated
|
||||||
|
* MID values.
|
||||||
|
*/
|
||||||
|
struct ike_mid_sync_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the task_t interface
|
||||||
|
*/
|
||||||
|
task_t task;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new TASK_IKE_MID_SYNC task.
|
||||||
|
*
|
||||||
|
* @param ike_sa IKE_SA this task works for
|
||||||
|
* @return task to handle by the task_manager
|
||||||
|
*/
|
||||||
|
ike_mid_sync_t *ike_mid_sync_create(ike_sa_t *ike_sa);
|
||||||
|
|
||||||
|
#endif /** IKE_MID_SYNC_H_ @}*/
|
||||||
@@ -30,6 +30,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST,
|
|||||||
"IKE_REAUTH_COMPLETE",
|
"IKE_REAUTH_COMPLETE",
|
||||||
"IKE_REDIRECT",
|
"IKE_REDIRECT",
|
||||||
"IKE_VERIFY_PEER_CERT",
|
"IKE_VERIFY_PEER_CERT",
|
||||||
|
"IKE_MID_SYNC",
|
||||||
"IKE_DELETE",
|
"IKE_DELETE",
|
||||||
"IKE_DPD",
|
"IKE_DPD",
|
||||||
"IKE_VENDOR",
|
"IKE_VENDOR",
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ enum task_type_t {
|
|||||||
TASK_IKE_REDIRECT,
|
TASK_IKE_REDIRECT,
|
||||||
/** verify a peer's certificate */
|
/** verify a peer's certificate */
|
||||||
TASK_IKE_VERIFY_PEER_CERT,
|
TASK_IKE_VERIFY_PEER_CERT,
|
||||||
|
/** synchronize message IDs, RFC6311 */
|
||||||
|
TASK_IKE_MID_SYNC,
|
||||||
/** delete an IKE_SA */
|
/** delete an IKE_SA */
|
||||||
TASK_IKE_DELETE,
|
TASK_IKE_DELETE,
|
||||||
/** liveness check */
|
/** liveness check */
|
||||||
|
|||||||
@@ -239,6 +239,14 @@ struct task_manager_t {
|
|||||||
*/
|
*/
|
||||||
void (*incr_mid)(task_manager_t *this, bool initiate);
|
void (*incr_mid)(task_manager_t *this, bool initiate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current message ID counter, in- or outbound.
|
||||||
|
*
|
||||||
|
* @param initiate TRUE to get the initiating ID
|
||||||
|
* @return current message ID
|
||||||
|
*/
|
||||||
|
uint32_t (*get_mid)(task_manager_t *this, bool initiate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset message ID counters of the task manager.
|
* Reset message ID counters of the task manager.
|
||||||
*
|
*
|
||||||
@@ -253,7 +261,7 @@ struct task_manager_t {
|
|||||||
* @param initiate message ID / DPD seq to initiate exchanges (send)
|
* @param initiate message ID / DPD seq to initiate exchanges (send)
|
||||||
* @param respond message ID / DPD seq to respond to exchanges (expect)
|
* @param respond message ID / DPD seq to respond to exchanges (expect)
|
||||||
*/
|
*/
|
||||||
void (*reset) (task_manager_t *this, uint32_t initiate, uint32_t respond);
|
void (*reset)(task_manager_t *this, uint32_t initiate, uint32_t respond);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we are currently waiting for a reply.
|
* Check if we are currently waiting for a reply.
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ exchange_tests_SOURCES = \
|
|||||||
suites/test_child_delete.c \
|
suites/test_child_delete.c \
|
||||||
suites/test_child_rekey.c \
|
suites/test_child_rekey.c \
|
||||||
suites/test_ike_delete.c \
|
suites/test_ike_delete.c \
|
||||||
|
suites/test_ike_mid_sync.c \
|
||||||
suites/test_ike_rekey.c \
|
suites/test_ike_rekey.c \
|
||||||
utils/exchange_test_asserts.h utils/exchange_test_asserts.c \
|
utils/exchange_test_asserts.h utils/exchange_test_asserts.c \
|
||||||
utils/exchange_test_helper.h utils/exchange_test_helper.c \
|
utils/exchange_test_helper.h utils/exchange_test_helper.c \
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
TEST_SUITE(ike_delete_suite_create)
|
TEST_SUITE(ike_delete_suite_create)
|
||||||
|
TEST_SUITE(ike_mid_sync_suite_create)
|
||||||
TEST_SUITE(ike_rekey_suite_create)
|
TEST_SUITE(ike_rekey_suite_create)
|
||||||
TEST_SUITE(child_create_suite_create)
|
TEST_SUITE(child_create_suite_create)
|
||||||
TEST_SUITE(child_delete_suite_create)
|
TEST_SUITE(child_delete_suite_create)
|
||||||
|
|||||||
@@ -0,0 +1,535 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Tobias Brunner
|
||||||
|
* HSR 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 "test_suite.h"
|
||||||
|
|
||||||
|
#include <tests/utils/exchange_test_helper.h>
|
||||||
|
#include <tests/utils/exchange_test_asserts.h>
|
||||||
|
#include <tests/utils/sa_asserts.h>
|
||||||
|
#include <bio/bio_reader.h>
|
||||||
|
#include <bio/bio_writer.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME: Since we don't have the server side yet, this is kind of a hack!!!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the IKEV2_MESSAGE_ID_SYNC_SUPPORTED notify to the IKE_AUTH response
|
||||||
|
*/
|
||||||
|
static bool add_notify(listener_t *listener, ike_sa_t *ike_sa,
|
||||||
|
message_t *message, bool incoming, bool plain)
|
||||||
|
{
|
||||||
|
if (plain && !incoming && message->get_exchange_type(message) == IKE_AUTH &&
|
||||||
|
!message->get_request(message))
|
||||||
|
{
|
||||||
|
message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC_SUPPORTED,
|
||||||
|
chunk_empty);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
#define add_notify_to_ike_auth() ({ \
|
||||||
|
listener_t _notify_listener = { \
|
||||||
|
.message = add_notify, \
|
||||||
|
}; \
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &_notify_listener); \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle IKEV2_MESSAGE_ID_SYNC notifies
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
listener_t listener;
|
||||||
|
struct {
|
||||||
|
chunk_t nonce;
|
||||||
|
uint32_t send;
|
||||||
|
uint32_t recv;
|
||||||
|
} init, resp;
|
||||||
|
} mid_sync_listener_t;
|
||||||
|
|
||||||
|
static bool handle_mid(listener_t *listener,
|
||||||
|
ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain)
|
||||||
|
{
|
||||||
|
mid_sync_listener_t *this = (mid_sync_listener_t*)listener;
|
||||||
|
|
||||||
|
if (!plain || incoming)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message->get_exchange_type(message) == INFORMATIONAL)
|
||||||
|
{
|
||||||
|
if (streq("resp", ike_sa->get_name(ike_sa)))
|
||||||
|
{
|
||||||
|
bio_writer_t *writer;
|
||||||
|
rng_t *rng;
|
||||||
|
|
||||||
|
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||||
|
ignore_result(rng->allocate_bytes(rng, 4, &this->init.nonce));
|
||||||
|
rng->destroy(rng);
|
||||||
|
writer = bio_writer_create(12);
|
||||||
|
writer->write_data(writer, this->init.nonce);
|
||||||
|
writer->write_uint32(writer, this->init.send);
|
||||||
|
writer->write_uint32(writer, this->init.recv);
|
||||||
|
message->set_message_id(message, 0);
|
||||||
|
message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC,
|
||||||
|
writer->get_buf(writer));
|
||||||
|
writer->destroy(writer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notify_payload_t *notify;
|
||||||
|
bio_reader_t *reader;
|
||||||
|
|
||||||
|
notify = message->get_notify(message, IKEV2_MESSAGE_ID_SYNC);
|
||||||
|
reader = bio_reader_create(notify->get_notification_data(notify));
|
||||||
|
chunk_clear(&this->resp.nonce);
|
||||||
|
reader->read_data(reader, 4, &this->resp.nonce);
|
||||||
|
this->resp.nonce = chunk_clone(this->resp.nonce);
|
||||||
|
reader->read_uint32(reader, &this->resp.send);
|
||||||
|
reader->read_uint32(reader, &this->resp.recv);
|
||||||
|
reader->destroy(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a MESSAGE_ID_SYNC notify in an INFORMATIONAL. We reset the state
|
||||||
|
* afterwards so this seems as if nothing happened.
|
||||||
|
*/
|
||||||
|
static void send_mid_sync(ike_sa_t *sa, uint32_t send, uint32_t recv)
|
||||||
|
{
|
||||||
|
call_ikesa(sa, send_dpd);
|
||||||
|
sa->set_message_id(sa, TRUE, send);
|
||||||
|
sa->set_message_id(sa, FALSE, recv);
|
||||||
|
sa->flush_queue(sa, TASK_QUEUE_QUEUED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a regular DPD from one IKE_SA to another
|
||||||
|
*/
|
||||||
|
static void send_dpd(ike_sa_t *from, ike_sa_t *to)
|
||||||
|
{
|
||||||
|
uint32_t send, recv;
|
||||||
|
|
||||||
|
send = from->get_message_id(from, TRUE);
|
||||||
|
recv = to->get_message_id(to, FALSE);
|
||||||
|
call_ikesa(from, send_dpd);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, to, NULL);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, from, NULL);
|
||||||
|
ck_assert_int_eq(send + 1, from->get_message_id(from, TRUE));
|
||||||
|
ck_assert_int_eq(recv + 1, to->get_message_id(to, FALSE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a number of DPDs from on IKE_SA to the other
|
||||||
|
*/
|
||||||
|
static void send_dpds(ike_sa_t *from, ike_sa_t *to, int count)
|
||||||
|
{
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
send_dpd(from, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int dpds_a, dpds_b;
|
||||||
|
uint32_t send, recv;
|
||||||
|
} data[] = {
|
||||||
|
{ 0, 0, 0, 2 },
|
||||||
|
{ 0, 0, 1, 3 },
|
||||||
|
{ 1, 0, 0, 3 },
|
||||||
|
{ 1, 0, 5, 8 },
|
||||||
|
{ 0, 1, 1, 2 },
|
||||||
|
{ 0, 1, 2, 2 },
|
||||||
|
{ 1, 1, 1, 3 },
|
||||||
|
{ 1, 1, 2, 4 },
|
||||||
|
{ 1, 2, 2, 4 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The responder syncs message IDs with the initiator
|
||||||
|
*/
|
||||||
|
START_TEST(test_responder)
|
||||||
|
{
|
||||||
|
ike_sa_t *a, *b;
|
||||||
|
mid_sync_listener_t mid = {
|
||||||
|
.listener = { .message = (void*)handle_mid, },
|
||||||
|
.init = {
|
||||||
|
.send = data[_i].send,
|
||||||
|
.recv = data[_i].recv,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
add_notify_to_ike_auth();
|
||||||
|
exchange_test_helper->establish_sa(exchange_test_helper,
|
||||||
|
&a, &b, NULL);
|
||||||
|
|
||||||
|
send_dpds(a, b, data[_i].dpds_a);
|
||||||
|
send_dpds(b, a, data[_i].dpds_b);
|
||||||
|
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &mid.listener);
|
||||||
|
send_mid_sync(b, data[_i].send, data[_i].recv);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
|
||||||
|
ck_assert_chunk_eq(mid.init.nonce, mid.resp.nonce);
|
||||||
|
ck_assert_int_eq(data[_i].recv, mid.resp.send);
|
||||||
|
ck_assert_int_eq(data[_i].send, mid.resp.recv);
|
||||||
|
ck_assert_int_eq(data[_i].recv, a->get_message_id(a, TRUE));
|
||||||
|
ck_assert_int_eq(data[_i].send, a->get_message_id(a, FALSE));
|
||||||
|
/* this currently won't be handled */
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
|
||||||
|
charon->bus->remove_listener(charon->bus, &mid.listener);
|
||||||
|
|
||||||
|
send_dpd(a, b);
|
||||||
|
send_dpd(b, a);
|
||||||
|
|
||||||
|
call_ikesa(a, destroy);
|
||||||
|
call_ikesa(b, destroy);
|
||||||
|
chunk_free(&mid.init.nonce);
|
||||||
|
chunk_free(&mid.resp.nonce);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure a retransmit is handled properly.
|
||||||
|
*/
|
||||||
|
START_TEST(test_retransmit)
|
||||||
|
{
|
||||||
|
ike_sa_t *a, *b;
|
||||||
|
mid_sync_listener_t mid = {
|
||||||
|
.listener = { .message = (void*)handle_mid, },
|
||||||
|
.init = {
|
||||||
|
.send = data[_i].send,
|
||||||
|
.recv = data[_i].recv,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
message_t *msg, *retransmit;
|
||||||
|
|
||||||
|
add_notify_to_ike_auth();
|
||||||
|
exchange_test_helper->establish_sa(exchange_test_helper,
|
||||||
|
&a, &b, NULL);
|
||||||
|
|
||||||
|
send_dpds(a, b, data[_i].dpds_a);
|
||||||
|
send_dpds(b, a, data[_i].dpds_b);
|
||||||
|
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &mid.listener);
|
||||||
|
send_mid_sync(b, data[_i].send, data[_i].recv);
|
||||||
|
msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender);
|
||||||
|
retransmit = message_create_from_packet(msg->get_packet(msg));
|
||||||
|
retransmit->parse_header(retransmit);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, msg);
|
||||||
|
msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender);
|
||||||
|
msg->destroy(msg);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, retransmit);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
|
||||||
|
charon->bus->remove_listener(charon->bus, &mid.listener);
|
||||||
|
|
||||||
|
send_dpd(a, b);
|
||||||
|
send_dpd(b, a);
|
||||||
|
|
||||||
|
call_ikesa(a, destroy);
|
||||||
|
call_ikesa(b, destroy);
|
||||||
|
chunk_free(&mid.init.nonce);
|
||||||
|
chunk_free(&mid.resp.nonce);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure a replayed or delayed notify is ignored.
|
||||||
|
*/
|
||||||
|
START_TEST(test_replay)
|
||||||
|
{
|
||||||
|
ike_sa_t *a, *b;
|
||||||
|
mid_sync_listener_t mid = {
|
||||||
|
.listener = { .message = (void*)handle_mid, },
|
||||||
|
.init = {
|
||||||
|
.send = data[_i].send,
|
||||||
|
.recv = data[_i].recv,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
message_t *msg, *replay;
|
||||||
|
|
||||||
|
add_notify_to_ike_auth();
|
||||||
|
exchange_test_helper->establish_sa(exchange_test_helper,
|
||||||
|
&a, &b, NULL);
|
||||||
|
|
||||||
|
send_dpds(a, b, data[_i].dpds_a);
|
||||||
|
send_dpds(b, a, data[_i].dpds_b);
|
||||||
|
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &mid.listener);
|
||||||
|
send_mid_sync(b, data[_i].send, data[_i].recv);
|
||||||
|
msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender);
|
||||||
|
replay = message_create_from_packet(msg->get_packet(msg));
|
||||||
|
replay->parse_header(replay);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, msg);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
|
||||||
|
charon->bus->remove_listener(charon->bus, &mid.listener);
|
||||||
|
|
||||||
|
send_dpd(a, b);
|
||||||
|
send_dpd(b, a);
|
||||||
|
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, replay);
|
||||||
|
ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender));
|
||||||
|
|
||||||
|
call_ikesa(a, destroy);
|
||||||
|
call_ikesa(b, destroy);
|
||||||
|
chunk_free(&mid.init.nonce);
|
||||||
|
chunk_free(&mid.resp.nonce);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the notify is ignored if the extension is not enabled.
|
||||||
|
*/
|
||||||
|
START_TEST(test_disabled)
|
||||||
|
{
|
||||||
|
ike_sa_t *a, *b;
|
||||||
|
mid_sync_listener_t mid = {
|
||||||
|
.listener = { .message = (void*)handle_mid, },
|
||||||
|
.init = {
|
||||||
|
.send = data[_i].send,
|
||||||
|
.recv = data[_i].recv,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
exchange_test_helper->establish_sa(exchange_test_helper,
|
||||||
|
&a, &b, NULL);
|
||||||
|
|
||||||
|
send_dpds(a, b, data[_i].dpds_a);
|
||||||
|
send_dpds(b, a, data[_i].dpds_b);
|
||||||
|
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &mid.listener);
|
||||||
|
send_mid_sync(b, data[_i].dpds_b, UINT_MAX);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
|
||||||
|
/* we don't expect a response and unchanged MIDs */
|
||||||
|
ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender));
|
||||||
|
ck_assert_int_eq(2 + data[_i].dpds_a, a->get_message_id(a, TRUE));
|
||||||
|
ck_assert_int_eq(data[_i].dpds_b, a->get_message_id(a, FALSE));
|
||||||
|
charon->bus->remove_listener(charon->bus, &mid.listener);
|
||||||
|
|
||||||
|
send_dpd(a, b);
|
||||||
|
send_dpd(b, a);
|
||||||
|
|
||||||
|
call_ikesa(a, destroy);
|
||||||
|
call_ikesa(b, destroy);
|
||||||
|
chunk_free(&mid.init.nonce);
|
||||||
|
chunk_free(&mid.resp.nonce);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int dpds_a, dpds_b;
|
||||||
|
uint32_t send, recv;
|
||||||
|
} data_too_low[] = {
|
||||||
|
{ 0, 1, 0, 2 },
|
||||||
|
{ 1, 2, 0, 0 },
|
||||||
|
{ 1, 2, 1, 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The responder syncs message IDs with the initiator but uses too low sender
|
||||||
|
* MIDs so the initiator ignores the notify.
|
||||||
|
*/
|
||||||
|
START_TEST(test_sender_too_low)
|
||||||
|
{
|
||||||
|
ike_sa_t *a, *b;
|
||||||
|
mid_sync_listener_t mid = {
|
||||||
|
.listener = { .message = (void*)handle_mid, },
|
||||||
|
.init = {
|
||||||
|
.send = data_too_low[_i].send,
|
||||||
|
.recv = data_too_low[_i].recv,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
add_notify_to_ike_auth();
|
||||||
|
exchange_test_helper->establish_sa(exchange_test_helper,
|
||||||
|
&a, &b, NULL);
|
||||||
|
|
||||||
|
send_dpds(a, b, data_too_low[_i].dpds_a);
|
||||||
|
send_dpds(b, a, data_too_low[_i].dpds_b);
|
||||||
|
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &mid.listener);
|
||||||
|
send_mid_sync(b, data_too_low[_i].dpds_b, UINT_MAX);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
|
||||||
|
/* we don't expect a response and unchanged MIDs */
|
||||||
|
ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender));
|
||||||
|
ck_assert_int_eq(2 + data_too_low[_i].dpds_a, a->get_message_id(a, TRUE));
|
||||||
|
ck_assert_int_eq(data_too_low[_i].dpds_b, a->get_message_id(a, FALSE));
|
||||||
|
charon->bus->remove_listener(charon->bus, &mid.listener);
|
||||||
|
|
||||||
|
send_dpd(a, b);
|
||||||
|
send_dpd(b, a);
|
||||||
|
|
||||||
|
call_ikesa(a, destroy);
|
||||||
|
call_ikesa(b, destroy);
|
||||||
|
chunk_free(&mid.init.nonce);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int dpds_a, dpds_b;
|
||||||
|
uint32_t send, recv;
|
||||||
|
/* reversed so the table below is clearer */
|
||||||
|
uint32_t recv_exp, send_exp;
|
||||||
|
} data_recv_update[] = {
|
||||||
|
{ 0, 0, 0, 0, 0, 2 },
|
||||||
|
{ 0, 0, 0, 1, 0, 2 },
|
||||||
|
{ 0, 0, 1, 1, 1, 2 },
|
||||||
|
{ 1, 0, 0, 1, 0, 3 },
|
||||||
|
{ 1, 0, 5, 2, 5, 3 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The responder syncs message IDs with the initiator but uses too low receiver
|
||||||
|
* MID, which is updated by the initiator in the response.
|
||||||
|
*/
|
||||||
|
START_TEST(test_recv_update)
|
||||||
|
{
|
||||||
|
ike_sa_t *a, *b;
|
||||||
|
mid_sync_listener_t mid = {
|
||||||
|
.listener = { .message = (void*)handle_mid, },
|
||||||
|
.init = {
|
||||||
|
.send = data_recv_update[_i].send,
|
||||||
|
.recv = data_recv_update[_i].recv,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
add_notify_to_ike_auth();
|
||||||
|
exchange_test_helper->establish_sa(exchange_test_helper,
|
||||||
|
&a, &b, NULL);
|
||||||
|
|
||||||
|
send_dpds(a, b, data_recv_update[_i].dpds_a);
|
||||||
|
send_dpds(b, a, data_recv_update[_i].dpds_b);
|
||||||
|
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &mid.listener);
|
||||||
|
send_mid_sync(b, data_recv_update[_i].send, data_recv_update[_i].recv);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
|
||||||
|
ck_assert_chunk_eq(mid.init.nonce, mid.resp.nonce);
|
||||||
|
ck_assert_int_eq(data_recv_update[_i].send_exp, mid.resp.send);
|
||||||
|
ck_assert_int_eq(data_recv_update[_i].recv_exp, mid.resp.recv);
|
||||||
|
ck_assert_int_eq(data_recv_update[_i].send_exp, a->get_message_id(a, TRUE));
|
||||||
|
ck_assert_int_eq(data_recv_update[_i].recv_exp, a->get_message_id(a, FALSE));
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
|
||||||
|
charon->bus->remove_listener(charon->bus, &mid.listener);
|
||||||
|
/* fake the receipt of the notify */
|
||||||
|
b->set_message_id(b, TRUE, data_recv_update[_i].recv_exp);
|
||||||
|
b->set_message_id(b, FALSE, data_recv_update[_i].send_exp);
|
||||||
|
|
||||||
|
send_dpd(a, b);
|
||||||
|
send_dpd(b, a);
|
||||||
|
|
||||||
|
call_ikesa(a, destroy);
|
||||||
|
call_ikesa(b, destroy);
|
||||||
|
chunk_free(&mid.init.nonce);
|
||||||
|
chunk_free(&mid.resp.nonce);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int dpds_a, dpds_b;
|
||||||
|
uint32_t send, recv;
|
||||||
|
/* reversed so the table below is clearer */
|
||||||
|
uint32_t recv_exp, send_exp;
|
||||||
|
} data_active[] = {
|
||||||
|
{ 0, 0, 0, 2, 0, 3 },
|
||||||
|
{ 0, 0, 1, 3, 1, 3 },
|
||||||
|
{ 1, 0, 0, 3, 0, 4 },
|
||||||
|
{ 1, 0, 5, 8, 5, 8 },
|
||||||
|
{ 0, 1, 1, 2, 1, 3 },
|
||||||
|
{ 0, 1, 2, 2, 2, 2 },
|
||||||
|
{ 1, 1, 1, 3, 1, 4 },
|
||||||
|
{ 1, 1, 2, 4, 2, 4 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The responder syncs message IDs with the initiator that waits for the
|
||||||
|
* response for an active task.
|
||||||
|
*/
|
||||||
|
START_TEST(test_active)
|
||||||
|
{
|
||||||
|
ike_sa_t *a, *b;
|
||||||
|
mid_sync_listener_t mid = {
|
||||||
|
.listener = { .message = (void*)handle_mid, },
|
||||||
|
.init = {
|
||||||
|
.send = data_active[_i].send,
|
||||||
|
.recv = data_active[_i].recv,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
message_t *msg;
|
||||||
|
|
||||||
|
add_notify_to_ike_auth();
|
||||||
|
exchange_test_helper->establish_sa(exchange_test_helper,
|
||||||
|
&a, &b, NULL);
|
||||||
|
|
||||||
|
send_dpds(a, b, data_active[_i].dpds_a);
|
||||||
|
send_dpds(b, a, data_active[_i].dpds_b);
|
||||||
|
|
||||||
|
call_ikesa(a, send_dpd);
|
||||||
|
msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender);
|
||||||
|
msg->destroy(msg);
|
||||||
|
|
||||||
|
exchange_test_helper->add_listener(exchange_test_helper, &mid.listener);
|
||||||
|
send_mid_sync(b, data_active[_i].recv_exp, data_active[_i].send_exp);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
|
||||||
|
ck_assert_chunk_eq(mid.init.nonce, mid.resp.nonce);
|
||||||
|
ck_assert_int_eq(data_active[_i].send_exp, mid.resp.send);
|
||||||
|
ck_assert_int_eq(data_active[_i].recv_exp, mid.resp.recv);
|
||||||
|
ck_assert_int_eq(data_active[_i].send_exp, a->get_message_id(a, TRUE));
|
||||||
|
ck_assert_int_eq(data_active[_i].recv_exp, a->get_message_id(a, FALSE));
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
|
||||||
|
charon->bus->remove_listener(charon->bus, &mid.listener);
|
||||||
|
|
||||||
|
/* the active task was queued again */
|
||||||
|
call_ikesa(a, initiate, NULL, 0, NULL, NULL);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
|
||||||
|
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
|
||||||
|
send_dpd(b, a);
|
||||||
|
|
||||||
|
call_ikesa(a, destroy);
|
||||||
|
call_ikesa(b, destroy);
|
||||||
|
chunk_free(&mid.init.nonce);
|
||||||
|
chunk_free(&mid.resp.nonce);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *ike_mid_sync_suite_create()
|
||||||
|
{
|
||||||
|
Suite *s;
|
||||||
|
TCase *tc;
|
||||||
|
|
||||||
|
s = suite_create("ike MID sync");
|
||||||
|
|
||||||
|
tc = tcase_create("responder");
|
||||||
|
tcase_add_loop_test(tc, test_responder, 0, countof(data));
|
||||||
|
tcase_add_loop_test(tc, test_retransmit, 0, countof(data));
|
||||||
|
tcase_add_loop_test(tc, test_replay, 0, countof(data));
|
||||||
|
tcase_add_loop_test(tc, test_disabled, 0, countof(data));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("sender MID too low");
|
||||||
|
tcase_add_loop_test(tc, test_sender_too_low, 0, countof(data_too_low));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("receiver MID updated");
|
||||||
|
tcase_add_loop_test(tc, test_recv_update, 0, countof(data_recv_update));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("active task");
|
||||||
|
tcase_add_loop_test(tc, test_active, 0, countof(data_active));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user