ikev1: Fix handling of overlapping Quick Mode exchanges
authorTobias Brunner <tobias@strongswan.org>
Wed, 19 Aug 2015 13:28:02 +0000 (15:28 +0200)
committerTobias Brunner <tobias@strongswan.org>
Thu, 20 Aug 2015 17:13:45 +0000 (19:13 +0200)
In some cases the third message of a Quick Mode exchange might arrive
after the first message of a subsequent Quick Mode exchange.  Previously
these messages were handled incorrectly and the second Quick Mode
exchange failed.

Some implementations might even try to establish multiple Quick Modes
simultaneously, which is explicitly allowed in RFC 2409.  We don't fully
support that, though, in particular in case of retransmits.

Fixes #1076.

src/libcharon/sa/ikev1/task_manager_v1.c
src/libcharon/sa/ikev1/tasks/quick_mode.c
src/libcharon/sa/ikev1/tasks/quick_mode.h

index ed547c4..678f99d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2014 Tobias Brunner
+ * Copyright (C) 2007-2015 Tobias Brunner
  * Copyright (C) 2007-2011 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -901,6 +901,34 @@ static bool process_dpd(private_task_manager_t *this, message_t *message)
 }
 
 /**
+ * Check if we already have a quick mode task queued for the exchange with the
+ * given message ID
+ */
+static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid)
+{
+       enumerator_t *enumerator;
+       quick_mode_t *qm;
+       task_t *task;
+       bool found = FALSE;
+
+       enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (task->get_type(task) == TASK_QUICK_MODE)
+               {
+                       qm = (quick_mode_t*)task;
+                       if (qm->get_mid(qm) == mid)
+                       {
+                               found = TRUE;
+                               break;
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       return found;
+}
+
+/**
  * handle an incoming request message
  */
 static status_t process_request(private_task_manager_t *this,
@@ -911,6 +939,7 @@ static status_t process_request(private_task_manager_t *this,
        bool send_response = FALSE, dpd = FALSE;
 
        if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
+               message->get_exchange_type(message) == QUICK_MODE ||
                this->passive_tasks->get_count(this->passive_tasks) == 0)
        {       /* create tasks depending on request type, if not already some queued */
                switch (message->get_exchange_type(message))
@@ -946,6 +975,10 @@ static status_t process_request(private_task_manager_t *this,
                                                 "unestablished IKE_SA, ignored");
                                        return FAILED;
                                }
+                               if (have_quick_mode_task(this, message->get_message_id(message)))
+                               {
+                                       break;
+                               }
                                task = (task_t *)quick_mode_create(this->ike_sa, NULL,
                                                                                                   NULL, NULL);
                                this->passive_tasks->insert_last(this->passive_tasks, task);
index 96edfd8..d6a3f2c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
  * Hochschule fuer Technik Rapperswil
  *
  * Copyright (C) 2011 Martin Willi
@@ -185,6 +185,11 @@ struct private_quick_mode_t {
         */
        bool udp;
 
+       /**
+        * Message ID of handled quick mode exchange
+        */
+       u_int32_t mid;
+
        /** states of quick mode */
        enum {
                QM_INIT,
@@ -1019,6 +1024,11 @@ static void check_for_rekeyed_child(private_quick_mode_t *this)
 METHOD(task_t, process_r, status_t,
        private_quick_mode_t *this, message_t *message)
 {
+       if (this->mid && this->mid != message->get_message_id(message))
+       {       /* not responsible for this quick mode exchange */
+               return NEED_MORE;
+       }
+
        switch (this->state)
        {
                case QM_INIT:
@@ -1188,6 +1198,11 @@ METHOD(task_t, process_r, status_t,
 METHOD(task_t, build_r, status_t,
        private_quick_mode_t *this, message_t *message)
 {
+       if (this->mid && this->mid != message->get_message_id(message))
+       {       /* not responsible for this quick mode exchange */
+               return NEED_MORE;
+       }
+
        switch (this->state)
        {
                case QM_INIT:
@@ -1242,6 +1257,7 @@ METHOD(task_t, build_r, status_t,
                        add_ts(this, message);
 
                        this->state = QM_NEGOTIATED;
+                       this->mid = message->get_message_id(message);
                        return NEED_MORE;
                }
                case QM_NEGOTIATED:
@@ -1335,6 +1351,12 @@ METHOD(task_t, get_type, task_type_t,
        return TASK_QUICK_MODE;
 }
 
+METHOD(quick_mode_t, get_mid, u_int32_t,
+       private_quick_mode_t *this)
+{
+       return this->mid;
+}
+
 METHOD(quick_mode_t, use_reqid, void,
        private_quick_mode_t *this, u_int32_t reqid)
 {
@@ -1368,6 +1390,7 @@ METHOD(task_t, migrate, void,
        this->ike_sa = ike_sa;
        this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
        this->state = QM_INIT;
+       this->mid = 0;
        this->tsi = NULL;
        this->tsr = NULL;
        this->proposal = NULL;
@@ -1414,6 +1437,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
                                .migrate = _migrate,
                                .destroy = _destroy,
                        },
+                       .get_mid = _get_mid,
                        .use_reqid = _use_reqid,
                        .use_marks = _use_marks,
                        .rekey = _rekey,
index ee9b64d..062d634 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
  * Copyright (C) 2011 Martin Willi
  * Copyright (C) 2011 revosec AG
  *
@@ -38,6 +41,14 @@ struct quick_mode_t {
        task_t task;
 
        /**
+        * Get the message ID of the quick mode exchange handled by this task as
+        * responder.
+        *
+        * @return                              message ID, or 0 (not defined yet or as initiator)
+        */
+       u_int32_t (*get_mid)(quick_mode_t *this);
+
+       /**
         * Use a specific reqid to install this CHILD_SA.
         *
         * @param reqid                 reqid to use