Destroy existing IKE_SAs with same identities when receiving INITIAL_CONTACT
authorMartin Willi <martin@revosec.ch>
Wed, 5 Jan 2011 15:44:01 +0000 (16:44 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 5 Jan 2011 15:46:08 +0000 (16:46 +0100)
src/libcharon/sa/ike_sa_manager.c
src/libcharon/sa/ike_sa_manager.h
src/libcharon/sa/tasks/ike_auth.c

index ec2d82d..fb9cdbb 100644 (file)
@@ -1394,7 +1394,7 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
 }
 
 METHOD(ike_sa_manager_t, check_uniqueness, bool,
-       private_ike_sa_manager_t *this, ike_sa_t *ike_sa)
+       private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
 {
        bool cancel = FALSE;
        peer_cfg_t *peer_cfg;
@@ -1408,7 +1408,7 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
 
        peer_cfg = ike_sa->get_peer_cfg(ike_sa);
        policy = peer_cfg->get_unique_policy(peer_cfg);
-       if (policy == UNIQUE_NO)
+       if (policy == UNIQUE_NO && !force_replace)
        {
                return FALSE;
        }
@@ -1456,6 +1456,13 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
                {
                        continue;
                }
+               if (force_replace)
+               {
+                       DBG1(DBG_IKE, "destroying duplicate IKE_SA for peer '%Y', "
+                                "received INITIAL_CONTACT", other);
+                       checkin_and_destroy(this, duplicate);
+                       continue;
+               }
                peer_cfg = duplicate->get_peer_cfg(duplicate);
                if (peer_cfg && peer_cfg->equals(peer_cfg, ike_sa->get_peer_cfg(ike_sa)))
                {
index a9dbca6..ec157ab 100644 (file)
@@ -106,10 +106,12 @@ struct ike_sa_manager_t {
         * deadlocks occur otherwise.
         *
         * @param ike_sa                        ike_sa to check
+        * @param force_replace         replace existing SAs, regardless of unique policy
         * @return                                      TRUE, if the given IKE_SA has duplicates and
         *                                                      should be deleted
         */
-       bool (*check_uniqueness)(ike_sa_manager_t *this, ike_sa_t *ike_sa);
+       bool (*check_uniqueness)(ike_sa_manager_t *this, ike_sa_t *ike_sa,
+                                                        bool force_replace);
 
        /**
         * Check if we already have a connected IKE_SA between two identities.
index 6b024cc..178989a 100644 (file)
@@ -106,6 +106,11 @@ struct private_ike_auth_t {
         * should we send a AUTHENTICATION_FAILED notify?
         */
        bool authentication_failed;
+
+       /**
+        * received an INITIAL_CONTACT?
+        */
+       bool initial_contact;
 };
 
 /**
@@ -612,6 +617,14 @@ static status_t process_r(private_ike_auth_t *this, message_t *message)
                        return NEED_MORE;
        }
 
+       /* If authenticated (with non-EAP) and received INITIAL_CONTACT,
+        * delete any existing IKE_SAs with that peer. */
+       if (message->get_message_id(message) == 1 &&
+               message->get_notify(message, INITIAL_CONTACT))
+       {
+               this->initial_contact = TRUE;
+       }
+
        /* store authentication information */
        cfg = auth_cfg_create();
        cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE);
@@ -705,6 +718,13 @@ static status_t build_r(private_ike_auth_t *this, message_t *message)
                get_reserved_id_bytes(this, id_payload);
                message->add_payload(message, (payload_t*)id_payload);
 
+               if (this->initial_contact)
+               {
+                       charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+                                                                                                        this->ike_sa, TRUE);
+                       this->initial_contact = FALSE;
+               }
+
                if ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS) == AUTH_CLASS_EAP)
                {       /* EAP-only authentication */
                        if (!this->ike_sa->supports_extension(this->ike_sa,
@@ -786,7 +806,7 @@ static status_t build_r(private_ike_auth_t *this, message_t *message)
        if (!this->do_another_auth && !this->expect_another_auth)
        {
                if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
-                                                                                                        this->ike_sa))
+                                                                                                        this->ike_sa, FALSE))
                {
                        DBG1(DBG_IKE, "cancelling IKE_SA setup due uniqueness policy");
                        message->add_notify(message, TRUE, AUTHENTICATION_FAILED,