From 05a2be82c2b41991f25cd4aafb281c27655f2373 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 16 Sep 2016 16:18:32 +0200 Subject: ike: Add getter for the current message ID to task manager --- src/libcharon/sa/ikev1/task_manager_v1.c | 7 +++++++ src/libcharon/sa/ikev2/task_manager_v2.c | 7 +++++++ src/libcharon/sa/task_manager.h | 10 +++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index ba393196d..7c8eebaba 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -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, 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, .retransmit = _retransmit, .incr_mid = _incr_mid, + .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, .adopt_child_tasks = _adopt_child_tasks, diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 60a262ffc..c101ebf2b 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1200,6 +1200,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. * @@ -2046,6 +2052,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) .initiate = _initiate, .retransmit = _retransmit, .incr_mid = _incr_mid, + .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, .adopt_child_tasks = _adopt_child_tasks, diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index 86077d373..7e9262291 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -239,6 +239,14 @@ struct task_manager_t { */ 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. * @@ -253,7 +261,7 @@ struct task_manager_t { * @param initiate message ID / DPD seq to initiate exchanges (send) * @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. -- cgit v1.2.3 From 347c403cf61a131067fa240af074fd1a80de2c7d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 16 Sep 2016 16:19:25 +0200 Subject: ike: Publish getter for the current message ID on IKE_SA --- src/libcharon/sa/ike_sa.c | 7 +++++++ src/libcharon/sa/ike_sa.h | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 7b87918d3..f180bfb01 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -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, 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, .set_other_host = _set_other_host, .set_message_id = _set_message_id, + .get_message_id = _get_message_id, .float_ports = _float_ports, .update_hosts = _update_hosts, .get_my_id = _get_my_id, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 6f5040d7c..e78f4deb9 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -554,7 +554,7 @@ struct ike_sa_t { 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) * and one to respond to exchanges (expect). @@ -564,6 +564,17 @@ struct ike_sa_t { */ 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. * -- cgit v1.2.3 From 5ab59221d2b365428e99e63c26a08114350ef2d1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 16 Sep 2016 17:26:41 +0200 Subject: ikev2: Add task to handle IKEV2_MESSAGE_ID_SYNC notifies as responder --- src/libcharon/Android.mk | 1 + src/libcharon/Makefile.am | 1 + src/libcharon/sa/ikev2/task_manager_v2.c | 5 + src/libcharon/sa/ikev2/tasks/ike_mid_sync.c | 259 ++++++++++++++++++++++++++++ src/libcharon/sa/ikev2/tasks/ike_mid_sync.h | 74 ++++++++ src/libcharon/sa/task.c | 1 + src/libcharon/sa/task.h | 2 + 7 files changed, 343 insertions(+) create mode 100644 src/libcharon/sa/ikev2/tasks/ike_mid_sync.c create mode 100644 src/libcharon/sa/ikev2/tasks/ike_mid_sync.h (limited to 'src') diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index cb7e4b103..1a8e068d1 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -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_init.c sa/ikev2/tasks/ike_init.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_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 18f2dee10..8461d6230 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -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_init.c sa/ikev2/tasks/ike_init.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_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index c101ebf2b..d3b371f10 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1069,6 +1070,10 @@ static status_t process_request(private_task_manager_t *this, task = (task_t*)ike_redirect_create( this->ike_sa, NULL); break; + case IKEV2_MESSAGE_ID_SYNC: + task = (task_t*)ike_mid_sync_create( + this->ike_sa); + break; default: break; } diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c new file mode 100644 index 000000000..9967697dd --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c @@ -0,0 +1,259 @@ +/* + * 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 . + * + * 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 +#include +#include +#include + +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; + } + 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) + { + /* after responding to this message the expected message ID is increased + * by one so we reduce it here */ + this->ike_sa->set_message_id(this->ike_sa, FALSE, m2 - 1); + } + 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; +} diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h new file mode 100644 index 000000000..9dd46f925 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h @@ -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 . + * + * 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 +#include +#include + +/** + * 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_ @}*/ diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c index 405eda66b..30de08c9b 100644 --- a/src/libcharon/sa/task.c +++ b/src/libcharon/sa/task.c @@ -30,6 +30,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST, "IKE_REAUTH_COMPLETE", "IKE_REDIRECT", "IKE_VERIFY_PEER_CERT", + "IKE_MID_SYNC", "IKE_DELETE", "IKE_DPD", "IKE_VENDOR", diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index 31d70fb3b..5f77149ba 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -61,6 +61,8 @@ enum task_type_t { TASK_IKE_REDIRECT, /** verify a peer's certificate */ TASK_IKE_VERIFY_PEER_CERT, + /** synchronize message IDs, RFC6311 */ + TASK_IKE_MID_SYNC, /** delete an IKE_SA */ TASK_IKE_DELETE, /** liveness check */ -- cgit v1.2.3 From d6ffa85feaf443afca66e77249159ef928e3d35e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 16 Sep 2016 17:37:59 +0200 Subject: ikev2: Negotiate support for IKE message ID synchronisation during IKE_AUTH --- src/libcharon/sa/ike_sa.h | 5 +++++ src/libcharon/sa/ikev2/tasks/ike_auth.c | 7 +++++++ 2 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index e78f4deb9..c8ba2fd2a 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -151,6 +151,11 @@ enum ike_extension_t { * IKEv2 Redirect Mechanism, RFC 5685 */ EXT_IKE_REDIRECTION = (1<<13), + + /** + * IKEv2 Message ID sync, RFC 6311 + */ + EXT_IKE_MESSAGE_ID_SYNC = (1<<14), }; /** diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 1e47144dc..53daaf2ad 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -417,6 +417,9 @@ METHOD(task_t, build_i, status_t, /* indicate support for EAP-only authentication */ message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION, 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) @@ -992,6 +995,10 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received invalid REDIRECT notify"); } break; + case IKEV2_MESSAGE_ID_SYNC_SUPPORTED: + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC); + break; default: { if (type <= 16383) -- cgit v1.2.3 From 3abc104f7e8cbe6b7d98359d05d3c6249c30a58c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 16 Sep 2016 17:44:39 +0200 Subject: ikev2: Accept INFORMATIONAL messages with MID 0 if used to sync MIDs We are very picky to only allow MID 0 for these messages (while we currently don't support IPSEC_REPLAY_COUNTER_SYNC notifies we accept them). --- src/libcharon/sa/ikev2/task_manager_v2.c | 45 +++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index d3b371f10..ede908c72 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1384,6 +1384,45 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg) return status; } +/** + * Check if a message with message ID 0 might be used to synchronize the + * message IDs. + */ +static bool is_mid_sync(private_task_manager_t *this, message_t *msg) +{ + enumerator_t *enumerator; + notify_payload_t *notify; + payload_t *payload; + bool found = FALSE, other = FALSE; + + if (msg->get_exchange_type(msg) == INFORMATIONAL && + this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED && + this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC)) + { + 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; + } + } + other = TRUE; + break; + } + enumerator->destroy(enumerator); + } + return found && !other; +} METHOD(task_manager_t, process_message, status_t, private_task_manager_t *this, message_t *msg) @@ -1432,7 +1471,7 @@ METHOD(task_manager_t, process_message, status_t, mid = msg->get_message_id(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, * after rekeying we only expect a DELETE in an INFORMATIONAL */ @@ -1488,7 +1527,7 @@ METHOD(task_manager_t, process_message, status_t, } 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); } } @@ -1526,7 +1565,7 @@ METHOD(task_manager_t, process_message, status_t, } 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); return SUCCESS; } -- cgit v1.2.3 From fb57904cbffefcfdacedac59676115062d77c37a Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 19 Sep 2016 11:16:06 +0200 Subject: ikev2: Don't cache response to MID sync request --- src/libcharon/sa/ikev2/task_manager_v2.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index ede908c72..debfe23fa 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -818,7 +818,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) task_t *task; message_t *message; host_t *me, *other; - bool delete = FALSE, hook = FALSE; + bool delete = FALSE, hook = FALSE, mid_sync = FALSE; ike_sa_id_t *id = NULL; uint64_t responder_spi = 0; bool result; @@ -837,6 +837,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request) enumerator = array_create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) { + if (task->get_type(task) == TASK_IKE_MID_SYNC) + { + mid_sync = TRUE; + } switch (task->build(task, message)) { case SUCCESS: @@ -909,6 +913,12 @@ static status_t build_response(private_task_manager_t *this, message_t *request) } 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); + } array_compress(this->passive_tasks); -- cgit v1.2.3 From c3d98d298ec2238a8a76694587fa1f36b5a1e14c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 4 Oct 2016 15:15:36 +0200 Subject: ikev2: Don't increase expected MID after handling MID sync message If the responder never sent a message the expected MID is 0. While the sent MID (M1) SHOULD be increased beyond the known value, it's not necessarily the case. Since M2 - 1 would then equal UINT_MAX setting that MID would get ignored and while we'd return 0 in the notify we'd actually expect 1 afterwards. --- src/libcharon/sa/ikev2/task_manager_v2.c | 3 +++ src/libcharon/sa/ikev2/tasks/ike_mid_sync.c | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index debfe23fa..778349c08 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -918,6 +918,9 @@ static status_t build_response(private_task_manager_t *this, message_t *request) /* 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); diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c index 9967697dd..d7de11303 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c @@ -180,9 +180,7 @@ METHOD(task_t, process, status_t, m2 = max(this->send, resp); if (resp != m2) { - /* after responding to this message the expected message ID is increased - * by one so we reduce it here */ - this->ike_sa->set_message_id(this->ike_sa, FALSE, m2 - 1); + 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); -- cgit v1.2.3 From cbb6885e9b86acb1c424da2b54eabc6eeaeffca5 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 4 Oct 2016 17:07:30 +0200 Subject: ikev2: Ignore IKEV2_MESSAGE_ID_SYNC notifies if extension is disabled If this is the first message by the peer, i.e. we expect MID 0, the message is not pre-processed in the task manager so we ignore it in the task. We also make sure to ignore such messages if the extension is disabled and the peer already sent us one INFORMATIONAL, e.g. a DPD (we'd otherwise consider the message with MID 0 as a retransmit). --- src/libcharon/sa/ikev2/task_manager_v2.c | 40 +++++++++++++++++++++-------- src/libcharon/sa/ikev2/tasks/ike_mid_sync.c | 7 +++++ 2 files changed, 37 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 778349c08..e4a16faf0 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1398,20 +1398,18 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg) } /** - * Check if a message with message ID 0 might be used to synchronize the - * message IDs. + * Check if a message with message ID 0 looks like it is used to synchronize + * the message IDs. */ -static bool is_mid_sync(private_task_manager_t *this, message_t *msg) +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 && - this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED && - this->ike_sa->supports_extension(this->ike_sa, - EXT_IKE_MESSAGE_ID_SYNC)) + if (msg->get_exchange_type(msg) == INFORMATIONAL) { enumerator = msg->create_payload_enumerator(msg); while (enumerator->enumerate(enumerator, &payload)) @@ -1429,14 +1427,35 @@ static bool is_mid_sync(private_task_manager_t *this, message_t *msg) break; } } - other = TRUE; - 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, private_task_manager_t *this, message_t *msg) { @@ -1525,7 +1544,8 @@ METHOD(task_manager_t, process_message, status_t, } } 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); if (status != SUCCESS) diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c index d7de11303..24cf276f4 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c @@ -112,6 +112,13 @@ METHOD(task_t, pre_process, status_t, * 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)); -- cgit v1.2.3 From 2f6ec15dff0495773c8dd616f6f7b594d09336ad Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 4 Oct 2016 19:35:51 +0200 Subject: unit-tests: Add test cases for MID sync exchanges --- src/libcharon/tests/Makefile.am | 1 + src/libcharon/tests/exchange_tests.h | 1 + src/libcharon/tests/suites/test_ike_mid_sync.c | 535 +++++++++++++++++++++++++ 3 files changed, 537 insertions(+) create mode 100644 src/libcharon/tests/suites/test_ike_mid_sync.c (limited to 'src') diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am index b8670246b..8f762a2e6 100644 --- a/src/libcharon/tests/Makefile.am +++ b/src/libcharon/tests/Makefile.am @@ -29,6 +29,7 @@ exchange_tests_SOURCES = \ suites/test_child_delete.c \ suites/test_child_rekey.c \ suites/test_ike_delete.c \ + suites/test_ike_mid_sync.c \ suites/test_ike_rekey.c \ utils/exchange_test_asserts.h utils/exchange_test_asserts.c \ utils/exchange_test_helper.h utils/exchange_test_helper.c \ diff --git a/src/libcharon/tests/exchange_tests.h b/src/libcharon/tests/exchange_tests.h index 30086721f..6b35ea5e5 100644 --- a/src/libcharon/tests/exchange_tests.h +++ b/src/libcharon/tests/exchange_tests.h @@ -14,6 +14,7 @@ */ TEST_SUITE(ike_delete_suite_create) +TEST_SUITE(ike_mid_sync_suite_create) TEST_SUITE(ike_rekey_suite_create) TEST_SUITE(child_create_suite_create) TEST_SUITE(child_delete_suite_create) diff --git a/src/libcharon/tests/suites/test_ike_mid_sync.c b/src/libcharon/tests/suites/test_ike_mid_sync.c new file mode 100644 index 000000000..3776f39e9 --- /dev/null +++ b/src/libcharon/tests/suites/test_ike_mid_sync.c @@ -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 . + * + * 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 +#include +#include +#include +#include + +/** + * 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; +} -- cgit v1.2.3