implemented IKE_SA uniqueness using ipsec.conf uniqueids paramater
authorMartin Willi <martin@strongswan.org>
Mon, 14 Apr 2008 13:23:24 +0000 (13:23 -0000)
committerMartin Willi <martin@strongswan.org>
Mon, 14 Apr 2008 13:23:24 +0000 (13:23 -0000)
additionally supports a "keep" value to keep the old IKE_SA

src/charon/config/peer_cfg.c
src/charon/config/peer_cfg.h
src/charon/plugins/sql/sql_config.c
src/charon/plugins/stroke/stroke_config.c
src/charon/sa/ike_sa_manager.c
src/charon/sa/ike_sa_manager.h
src/charon/sa/tasks/ike_auth.c
src/starter/args.c
src/starter/ipsec.conf.5
src/starter/starterstroke.c
src/stroke/stroke_msg.h

index 85fa22a..ca93947 100644 (file)
@@ -89,6 +89,11 @@ struct private_peer_cfg_t {
        cert_policy_t cert_policy;
        
        /**
+        * uniqueness of an IKE_SA
+        */
+       unique_policy_t unique;
+       
+       /**
         * Method to use for own authentication data
         */
        auth_method_t auth_method;
@@ -295,6 +300,14 @@ static cert_policy_t get_cert_policy(private_peer_cfg_t *this)
 }
 
 /**
+ * Implementation of peer_cfg_t.get_unique_policy.
+ */
+static unique_policy_t get_unique_policy(private_peer_cfg_t *this)
+{
+       return this->unique;
+}
+
+/**
  * Implementation of connection_t.auth_method_t.
  */
 static auth_method_t get_auth_method(private_peer_cfg_t *this)
@@ -444,6 +457,7 @@ static bool equals(private_peer_cfg_t *this, private_peer_cfg_t *other)
                this->my_id->equals(this->my_id, other->my_id) &&
                this->other_id->equals(this->other_id, other->other_id) &&
                this->cert_policy == other->cert_policy &&
+               this->unique == other->unique &&
                this->auth_method == other->auth_method &&
                this->eap_type == other->eap_type &&
                this->eap_vendor == other->eap_vendor &&
@@ -505,7 +519,7 @@ static void destroy(private_peer_cfg_t *this)
  */
 peer_cfg_t *peer_cfg_create(char *name, u_int ike_version, ike_cfg_t *ike_cfg,
                                                        identification_t *my_id, identification_t *other_id,
-                                                       cert_policy_t cert_policy,
+                                                       cert_policy_t cert_policy, unique_policy_t unique,
                                                        auth_method_t auth_method, eap_type_t eap_type,
                                                        u_int32_t eap_vendor,
                                                        u_int32_t keyingtries, u_int32_t rekey_time,
@@ -528,6 +542,7 @@ peer_cfg_t *peer_cfg_create(char *name, u_int ike_version, ike_cfg_t *ike_cfg,
        this->public.get_my_id = (identification_t* (*)(peer_cfg_t*))get_my_id;
        this->public.get_other_id = (identification_t* (*)(peer_cfg_t *))get_other_id;
        this->public.get_cert_policy = (cert_policy_t (*) (peer_cfg_t *))get_cert_policy;
+       this->public.get_unique_policy = (unique_policy_t (*) (peer_cfg_t *))get_unique_policy;
        this->public.get_auth_method = (auth_method_t (*) (peer_cfg_t *))get_auth_method;
        this->public.get_eap_type = (eap_type_t (*) (peer_cfg_t *,u_int32_t*))get_eap_type;
        this->public.get_keyingtries = (u_int32_t (*) (peer_cfg_t *))get_keyingtries;
@@ -557,6 +572,7 @@ peer_cfg_t *peer_cfg_create(char *name, u_int ike_version, ike_cfg_t *ike_cfg,
        this->my_id = my_id;
        this->other_id = other_id;
        this->cert_policy = cert_policy;
+       this->unique = unique;
        this->auth_method = auth_method;
        this->eap_type = eap_type;
        this->eap_vendor = eap_vendor;
index 29de4af..5e12fbf 100644 (file)
@@ -26,6 +26,7 @@
 #define PEER_CFG_H_
 
 typedef enum cert_policy_t cert_policy_t;
+typedef enum unique_policy_t unique_policy_t;
 typedef struct peer_cfg_t peer_cfg_t;
 
 #include <library.h>
@@ -63,6 +64,23 @@ enum cert_policy_t {
 extern enum_name_t *cert_policy_names;
 
 /**
+ * Uniqueness of an IKE_SA, used to drop multiple connections with one peer.
+ */
+enum unique_policy_t {
+       /** do not check for client uniqueness */
+       UNIQUE_NO,
+       /** replace unique IKE_SAs if new ones get established */
+       UNIQUE_REPLACE,
+       /** keep existing IKE_SAs, close the new ones on connection attept */
+       UNIQUE_KEEP,
+};
+
+/**
+ * enum strings for unique_policy_t
+ */
+extern enum_name_t *unique_policy_names;
+
+/**
  * Configuration of a peer, specified by IDs.
  *
  * The peer config defines a connection between two given IDs. It contains
@@ -179,6 +197,13 @@ struct peer_cfg_t {
        cert_policy_t (*get_cert_policy) (peer_cfg_t *this);
 
        /**
+        * How to handle uniqueness of IKE_SAs?
+        *
+        * @return                      unique policy
+        */
+       unique_policy_t (*get_unique_policy) (peer_cfg_t *this);
+
+       /**
         * Get the authentication method to use to authenticate us.
         * 
         * @return                      authentication method
@@ -332,6 +357,7 @@ struct peer_cfg_t {
  * @param my_id                        identification_t for ourselves
  * @param other_id                     identification_t for the remote guy
  * @param cert_policy          should we send a certificate payload?
+ * @param unique                       uniqueness of an IKE_SA
  * @param auth_method          auth method to use to authenticate us
  * @param eap_type                     EAP type to use for peer authentication
  * @param eap_vendor           EAP vendor identifier, if vendor specific type is used
@@ -352,7 +378,7 @@ struct peer_cfg_t {
  */
 peer_cfg_t *peer_cfg_create(char *name, u_int ikev_version, ike_cfg_t *ike_cfg,
                                                        identification_t *my_id, identification_t *other_id,
-                                                       cert_policy_t cert_policy,
+                                                       cert_policy_t cert_policy, unique_policy_t unique,
                                                        auth_method_t auth_method, eap_type_t eap_type,
                                                        u_int32_t eap_vendor,
                                                        u_int32_t keyingtries, u_int32_t rekey_time,
index 920268a..930fb24 100644 (file)
@@ -309,11 +309,11 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e,
                if (ike)
                {
                        peer_cfg = peer_cfg_create(
-                                                       name, 2, ike, local_id, remote_id, cert_policy,
-                                                       auth_method, eap_type, eap_vendor, keyingtries, 
-                                                       rekeytime, reauthtime, jitter, overtime, mobike,
-                                                       dpd_delay, NULL, NULL,
-                                                       mediation, mediated_cfg, peer_id);
+                                       name, 2, ike, local_id, remote_id, cert_policy, UNIQUE_NO,
+                                       auth_method, eap_type, eap_vendor, keyingtries, 
+                                       rekeytime, reauthtime, jitter, overtime, mobike,
+                                       dpd_delay, NULL, NULL,
+                                       mediation, mediated_cfg, peer_id);
                        add_child_cfgs(this, peer_cfg, id);
                        return peer_cfg;
                }
index 57349f7..5c2cb55 100644 (file)
@@ -356,6 +356,7 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
        peer_cfg_t *mediated_by = NULL;
        host_t *vip = NULL;
        certificate_t *cert;
+       unique_policy_t unique;
        u_int32_t rekey = 0, reauth = 0, over, jitter;
        
        me = identification_create_from_string(msg->add_conn.me.id ?
@@ -483,12 +484,25 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
                        }
                }
        }
+       switch (msg->add_conn.unique)
+       {
+               case 1: /* yes */
+               case 2: /* replace */
+                       unique = UNIQUE_REPLACE;
+                       break;
+               case 3: /* keep */
+                       unique = UNIQUE_KEEP;
+                       break;
+               default: /* no */
+                       unique = UNIQUE_NO;
+                       break;
+       }
        /* other.sourceip is managed in stroke_attributes. If it is set, we define
         * the pool name as the connection name, which the attribute provider
         * uses to serve pool addresses. */
        return peer_cfg_create(msg->add_conn.name,
                msg->add_conn.ikev2 ? 2 : 1, ike_cfg, me, other,
-               msg->add_conn.me.sendcert, UNIQUE_NO, msg->add_conn.auth_method,
+               msg->add_conn.me.sendcert, unique, msg->add_conn.auth_method,
                msg->add_conn.eap_type, msg->add_conn.eap_vendor,
                msg->add_conn.rekey.tries, rekey, reauth, jitter, over,
                msg->add_conn.mobike, msg->add_conn.dpd.delay,
index e3db6a6..b20cb28 100644 (file)
@@ -686,6 +686,44 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
        charon->bus->set_sa(charon->bus, ike_sa);
        return ike_sa;
 }
+       
+/**
+ * Implementation of ike_sa_manager_t.checkout_duplicate.
+ */
+static ike_sa_t* checkout_duplicate(private_ike_sa_manager_t *this,
+                                                                       ike_sa_t *ike_sa)
+{
+       enumerator_t *enumerator;
+       entry_t *entry;
+       ike_sa_t *duplicate = NULL;
+       identification_t *me, *other;
+       
+       me = ike_sa->get_my_id(ike_sa);
+       other = ike_sa->get_other_id(ike_sa);
+       
+       pthread_mutex_lock(&this->mutex);
+       enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list);
+       while (enumerator->enumerate(enumerator, &entry))
+       {
+               if (entry->ike_sa == ike_sa)
+               {       /* self is not a duplicate */
+                       continue;
+               }
+               if (wait_for_entry(this, entry))
+               {
+                       if (me->equals(me, entry->ike_sa->get_my_id(entry->ike_sa)) &&
+                               other->equals(other, entry->ike_sa->get_other_id(entry->ike_sa)))
+                       {
+                               duplicate = entry->ike_sa;
+                               entry->checked_out = TRUE;
+                               break;
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       pthread_mutex_unlock(&this->mutex);
+       return duplicate;
+}
 
 /**
  * enumerator cleanup function
@@ -916,6 +954,7 @@ ike_sa_manager_t *ike_sa_manager_create()
        this->public.checkout_by_config = (ike_sa_t*(*)(ike_sa_manager_t*,peer_cfg_t*))checkout_by_config;
        this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id;
        this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name;
+       this->public.checkout_duplicate = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))checkout_duplicate;
        this->public.create_enumerator = (enumerator_t*(*)(ike_sa_manager_t*))create_enumerator;
        this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin;
        this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy;
index f0c14d0..a91c943 100644 (file)
@@ -103,6 +103,14 @@ struct ike_sa_manager_t {
                                                                         peer_cfg_t *peer_cfg);
        
        /**
+        * Check out a duplicate if ike_sa to do uniqueness tests.
+        *
+        * @param ike_sa                        ike_sa to get a duplicate from
+        * @return                                      checked out duplicate
+        */
+       ike_sa_t* (*checkout_duplicate)(ike_sa_manager_t *this, ike_sa_t *ike_sa);
+       
+       /**
         * Check out an IKE_SA a unique ID.
         *
         * Every IKE_SA and every CHILD_SA is uniquely identified by an ID. 
index 3984936..c3a3f3b 100644 (file)
@@ -89,6 +89,68 @@ struct private_ike_auth_t {
 };
 
 /**
+ * check uniqueness and delete duplicates
+ */
+static bool check_uniqueness(private_ike_auth_t *this)
+{
+       ike_sa_t *duplicate;
+       unique_policy_t policy;
+       status_t status = SUCCESS;
+       peer_cfg_t *peer_cfg;
+       bool cancel = FALSE;
+       
+       peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+       policy = peer_cfg->get_unique_policy(peer_cfg);
+       if (policy == UNIQUE_NO)
+       {
+               return FALSE;
+       }
+       duplicate = charon->ike_sa_manager->checkout_duplicate(
+                                                                               charon->ike_sa_manager, this->ike_sa);
+       if (duplicate)
+       {
+               peer_cfg = duplicate->get_peer_cfg(duplicate);
+               if (peer_cfg &&
+                       peer_cfg->equals(peer_cfg, this->ike_sa->get_peer_cfg(this->ike_sa)))
+               {
+                       switch (duplicate->get_state(duplicate))
+                       {
+                               case IKE_ESTABLISHED:
+                               case IKE_REKEYING:
+                                       switch (policy)
+                                       {
+                                               case UNIQUE_REPLACE:
+                                                       DBG1(DBG_IKE, "deleting duplicate IKE_SA due "
+                                                                "uniqueness policy");
+                                                       status = duplicate->delete(duplicate);
+                                                       break;
+                                               case UNIQUE_KEEP:
+                                                       DBG1(DBG_IKE, "cancelling IKE_SA setup due "
+                                                                "uniqueness policy");
+                                                       cancel = TRUE;
+                                                       break;
+                                               default:
+                                                       break;
+                                       }
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               if (status == DESTROY_ME)
+               {
+                       charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+                                                                                                               duplicate);
+               }
+               else
+               {
+                       charon->ike_sa_manager->checkin(charon->ike_sa_manager, duplicate);
+               }
+       }
+       return cancel;
+}
+
+/**
  * build the AUTH payload
  */
 static status_t build_auth(private_ike_auth_t *this, message_t *message)
@@ -576,6 +638,12 @@ static status_t build_r(private_ike_auth_t *this, message_t *message)
                return FAILED;
        }
        
+       if (check_uniqueness(this))
+       {
+               message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+               return FAILED;
+       }
+       
        /* use "traditional" authentication if we could authenticate peer */
        if (this->peer_authenticated)
        {
index 075956d..f9e307e 100644 (file)
@@ -62,6 +62,14 @@ static const char *LST_sendcert[] = {
      NULL
 };
 
+static const char *LST_unique[] = {
+    "no",
+    "yes",
+    "replace",
+    "keep",
+     NULL
+};
+
 static const char *LST_strict[] = {
     "no",
     "yes",
@@ -163,7 +171,7 @@ static const token_info_t token_info[] =
     { ARG_STR,  offsetof(starter_config_t, setup.charondebug),  NULL               },
     { ARG_STR,  offsetof(starter_config_t, setup.prepluto), NULL                   },
     { ARG_STR,  offsetof(starter_config_t, setup.postpluto), NULL                  },
-    { ARG_ENUM, offsetof(starter_config_t, setup.uniqueids), LST_bool              },
+    { ARG_ENUM, offsetof(starter_config_t, setup.uniqueids), LST_unique            },
     { ARG_UINT, offsetof(starter_config_t, setup.overridemtu), NULL                },
     { ARG_TIME, offsetof(starter_config_t, setup.crlcheckinterval), NULL           },
     { ARG_ENUM, offsetof(starter_config_t, setup.cachecrls), LST_bool              },
index b5534ef..db0ab98 100644 (file)
@@ -1049,6 +1049,13 @@ and
 Participant IDs normally \fIare\fR unique,
 so a new (automatically-keyed) connection using the same ID is
 almost invariably intended to replace an old one.
+The IKEv2 daemon also accepts the value
+.B replace
+wich is identical to
+.B yes
+and the value
+.B keep
+to reject new IKE_SA setups and keep the duplicate established earlier.
 .PP
 The following
 .B config section
index 025cd66..ddac556 100644 (file)
@@ -257,6 +257,7 @@ int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn)
        msg.add_conn.mobike = conn->policy & POLICY_MOBIKE;
        msg.add_conn.force_encap = conn->policy & POLICY_FORCE_ENCAP;
        msg.add_conn.crl_policy = cfg->setup.strictcrlpolicy;
+       msg.add_conn.unique = cfg->setup.uniqueids;
        msg.add_conn.algorithms.ike = push_string(&msg, conn->ike);
        msg.add_conn.algorithms.esp = push_string(&msg, conn->esp);
        msg.add_conn.dpd.delay = conn->dpd_delay;
index 4ba4b5d..068f063 100644 (file)
@@ -198,6 +198,7 @@ struct stroke_msg_t {
                        int mobike;
                        int force_encap;
                        crl_policy_t crl_policy;
+                       int unique;
                        struct {
                                char *ike;
                                char *esp;