added PDF support for CHILD_SAs
authorMartin Willi <martin@strongswan.org>
Thu, 19 Apr 2007 08:02:19 +0000 (08:02 -0000)
committerMartin Willi <martin@strongswan.org>
Thu, 19 Apr 2007 08:02:19 +0000 (08:02 -0000)
support for INVALID_KE_PAYLOAD negotiation for rekeying

src/charon/config/child_cfg.c
src/charon/config/child_cfg.h
src/charon/config/ike_cfg.c
src/charon/config/ike_cfg.h
src/charon/config/proposal.c
src/charon/config/proposal.h
src/charon/sa/task_manager.c
src/charon/sa/tasks/child_create.c
src/charon/sa/tasks/child_rekey.c
src/charon/sa/tasks/ike_init.c
src/charon/sa/tasks/ike_rekey.c

index 2f53767..440f771 100644 (file)
@@ -120,9 +120,26 @@ static void add_proposal(private_child_cfg_t *this, proposal_t *proposal)
 }
 
 /**
+ * strip out DH groups from a proposal
+ */
+static void strip_dh_from_proposal(proposal_t *proposal)
+{
+       iterator_t *iterator;
+       algorithm_t *algo;
+       
+       iterator = proposal->create_algorithm_iterator(proposal, DIFFIE_HELLMAN_GROUP);
+       while (iterator->iterate(iterator, (void**)&algo))
+       {
+               iterator->remove(iterator);
+               free(algo);
+       }
+       iterator->destroy(iterator);
+}
+
+/**
  * Implementation of child_cfg_t.get_proposals
  */
-static linked_list_t* get_proposals(private_child_cfg_t *this)
+static linked_list_t* get_proposals(private_child_cfg_t *this, bool strip_dh)
 {
        iterator_t *iterator;
        proposal_t *current;
@@ -132,6 +149,10 @@ static linked_list_t* get_proposals(private_child_cfg_t *this)
        while (iterator->iterate(iterator, (void**)&current))
        {
                current = current->clone(current);
+               if (strip_dh)
+               {
+                       strip_dh_from_proposal(current);
+               }
                proposals->insert_last(proposals, current);
        }
        iterator->destroy(iterator);
@@ -142,7 +163,8 @@ static linked_list_t* get_proposals(private_child_cfg_t *this)
 /**
  * Implementation of child_cfg_t.get_name
  */
-static proposal_t* select_proposal(private_child_cfg_t*this, linked_list_t *proposals)
+static proposal_t* select_proposal(private_child_cfg_t*this,
+                                                                  linked_list_t *proposals, bool strip_dh)
 {
        iterator_t *stored_iter, *supplied_iter;
        proposal_t *stored, *supplied, *selected = NULL;
@@ -156,7 +178,18 @@ static proposal_t* select_proposal(private_child_cfg_t*this, linked_list_t *prop
                supplied_iter->reset(supplied_iter);
                while (supplied_iter->iterate(supplied_iter, (void**)&supplied))
                {
-                       selected = stored->select(stored, supplied);
+                       if (strip_dh)
+                       {
+                               /* remove DH groups on a copy */
+                               stored = stored->clone(stored);
+                               strip_dh_from_proposal(stored);
+                               selected = stored->select(stored, supplied);
+                               stored->destroy(stored);
+                       }
+                       else
+                       {
+                               selected = stored->select(stored, supplied);
+                       }
                        if (selected)
                        {
                                break;
@@ -329,6 +362,29 @@ static mode_t get_mode(private_child_cfg_t *this)
 }
 
 /**
+ * Implementation of child_cfg_t.get_dh_group.
+ */
+static diffie_hellman_group_t get_dh_group(private_child_cfg_t *this)
+{
+       iterator_t *iterator;
+       proposal_t *proposal;
+       algorithm_t *algo;
+       diffie_hellman_group_t dh_group = MODP_NONE;
+       
+       iterator = this->proposals->create_iterator(this->proposals, TRUE);
+       while (iterator->iterate(iterator, (void**)&proposal))
+       {
+               if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &algo))
+               {
+                       dh_group = algo->algorithm;
+                       break;
+               }
+       }
+       iterator->destroy(iterator);
+       return dh_group;
+}
+
+/**
  * Implementation of child_cfg_t.get_name
  */
 static void get_ref(private_child_cfg_t *this)
@@ -369,12 +425,13 @@ child_cfg_t *child_cfg_create(char *name, u_int32_t lifetime,
        this->public.add_traffic_selector = (void (*)(child_cfg_t*,bool,traffic_selector_t*))add_traffic_selector;
        this->public.get_traffic_selectors = (linked_list_t*(*)(child_cfg_t*,bool,linked_list_t*,host_t*))get_traffic_selectors;
        this->public.add_proposal = (void (*) (child_cfg_t*,proposal_t*))add_proposal;
-       this->public.get_proposals = (linked_list_t* (*) (child_cfg_t*))get_proposals;
-       this->public.select_proposal = (proposal_t* (*) (child_cfg_t*,linked_list_t*))select_proposal;
+       this->public.get_proposals = (linked_list_t* (*) (child_cfg_t*,bool))get_proposals;
+       this->public.select_proposal = (proposal_t* (*) (child_cfg_t*,linked_list_t*,bool))select_proposal;
        this->public.get_updown = (char* (*) (child_cfg_t*))get_updown;
        this->public.get_hostaccess = (bool (*) (child_cfg_t*))get_hostaccess;
        this->public.get_mode = (mode_t (*) (child_cfg_t *))get_mode;
        this->public.get_lifetime = (u_int32_t (*) (child_cfg_t *,bool))get_lifetime;
+       this->public.get_dh_group = (diffie_hellman_group_t(*)(child_cfg_t*)) get_dh_group;
        this->public.get_ref = (void (*) (child_cfg_t*))get_ref;
        this->public.destroy = (void (*) (child_cfg_t*))destroy;
        
index 6b22990..e1a6553 100644 (file)
@@ -96,9 +96,10 @@ struct child_cfg_t {
         * Resulting list and all of its proposals must be freed after use.
         * 
         * @param this                  calling object
+        * @param strip_dh              TRUE strip out diffie hellman groups
         * @return                              list of proposals
         */
-       linked_list_t* (*get_proposals)(child_cfg_t *this);
+       linked_list_t* (*get_proposals)(child_cfg_t *this, bool strip_dh);
        
        /**
         * @brief Select a proposal from a supplied list.
@@ -107,9 +108,11 @@ struct child_cfg_t {
         * 
         * @param this                  calling object
         * @param proposals             list from from wich proposals are selected
+        * @param strip_dh              TRUE strip out diffie hellman groups
         * @return                              selected proposal, or NULL if nothing matches
         */
-       proposal_t* (*select_proposal)(child_cfg_t*this, linked_list_t *proposals);
+       proposal_t* (*select_proposal)(child_cfg_t*this, linked_list_t *proposals,
+                                                                  bool strip_dh);
        
        /**
         * @brief Add a traffic selector to the config.
@@ -189,6 +192,14 @@ struct child_cfg_t {
        mode_t (*get_mode) (child_cfg_t *this);
        
        /**
+        * @brief Get the DH group to use for CHILD_SA setup.
+        * 
+        * @param this          calling object
+        * @return                      dh group to use
+        */
+       diffie_hellman_group_t (*get_dh_group)(child_cfg_t *this);
+       
+       /**
         * @brief Get a new reference.
         *
         * Get a new reference to this child_cfg by increasing
index 61e6211..35f46a6 100644 (file)
@@ -176,36 +176,6 @@ static diffie_hellman_group_t get_dh_group(private_ike_cfg_t *this)
 }
 
 /**
- * Implementation of ike_cfg_t.check_dh_group.
- */
-static bool check_dh_group(private_ike_cfg_t *this,
-                                                  diffie_hellman_group_t dh_group)
-{
-       iterator_t *prop_iter, *alg_iter;
-       proposal_t *proposal;
-       algorithm_t *algo;
-       
-       prop_iter = this->proposals->create_iterator(this->proposals, TRUE);
-       while (prop_iter->iterate(prop_iter, (void**)&proposal))
-       {
-               alg_iter = proposal->create_algorithm_iterator(proposal,
-                                                                                                          DIFFIE_HELLMAN_GROUP);
-               while (alg_iter->iterate(alg_iter, (void**)&algo))
-               {
-                       if (algo->algorithm == dh_group)
-                       {
-                               prop_iter->destroy(prop_iter);
-                               alg_iter->destroy(alg_iter);
-                               return TRUE;
-                       }
-               }
-               alg_iter->destroy(alg_iter);
-       }
-       prop_iter->destroy(prop_iter);
-       return FALSE;
-}
-
-/**
  * Implementation of ike_cfg_t.get_ref.
  */
 static void get_ref(private_ike_cfg_t *this)
@@ -243,7 +213,6 @@ ike_cfg_t *ike_cfg_create(bool certreq, host_t *my_host, host_t *other_host)
        this->public.get_proposals = (linked_list_t*(*)(ike_cfg_t*))get_proposals;
        this->public.select_proposal = (proposal_t*(*)(ike_cfg_t*,linked_list_t*))select_proposal;
        this->public.get_dh_group = (diffie_hellman_group_t(*)(ike_cfg_t*)) get_dh_group;
-       this->public.check_dh_group = (bool(*)(ike_cfg_t*,diffie_hellman_group_t)) check_dh_group;
        this->public.get_ref = (void(*)(ike_cfg_t*))get_ref;
        this->public.destroy = (void(*)(ike_cfg_t*))destroy;
        
index ccea0a5..bcdc90d 100644 (file)
@@ -110,17 +110,6 @@ struct ike_cfg_t {
        diffie_hellman_group_t (*get_dh_group)(ike_cfg_t *this);
        
        /**
-        * @brief Check if a suggested DH group is acceptable.
-        * 
-        * If we guess a wrong DH group for IKE_SA_INIT, the other
-        * peer will send us a offer. But is this acceptable for us?
-        * 
-        * @param this          calling object
-        * @return                      TRUE if group acceptable
-        */
-       bool (*check_dh_group) (ike_cfg_t *this, diffie_hellman_group_t dh_group);
-       
-       /**
         * @brief Get a new reference to this ike_cfg.
         *
         * Get a new reference to this ike_cfg by increasing
index dcab8cb..fe113b1 100644 (file)
@@ -144,39 +144,6 @@ static void add_algorithm(private_proposal_t *this, transform_type_t type, u_int
 }
 
 /**
- * Implements proposal_t.get_algorithm.
- */
-static bool get_algorithm(private_proposal_t *this, transform_type_t type, algorithm_t** algo)
-{
-       linked_list_t *list;
-       switch (type)
-       {
-               case ENCRYPTION_ALGORITHM:
-                       list = this->encryption_algos;
-                       break;
-               case INTEGRITY_ALGORITHM:
-                       list = this->integrity_algos;
-                       break;
-               case PSEUDO_RANDOM_FUNCTION:
-                       list = this->prf_algos;
-                       break;
-               case DIFFIE_HELLMAN_GROUP:
-                       list = this->dh_groups;
-                       break;
-               case EXTENDED_SEQUENCE_NUMBERS:
-                       list = this->esns;
-                       break;
-               default:
-                       return FALSE;
-       }
-       if (list->get_first(list, (void**)algo) != SUCCESS)
-       {
-               return FALSE;
-       }
-       return TRUE;
-}
-
-/**
  * Implements proposal_t.create_algorithm_iterator.
  */
 static iterator_t *create_algorithm_iterator(private_proposal_t *this, transform_type_t type)
@@ -200,6 +167,50 @@ static iterator_t *create_algorithm_iterator(private_proposal_t *this, transform
 }
 
 /**
+ * Implements proposal_t.get_algorithm.
+ */
+static bool get_algorithm(private_proposal_t *this, transform_type_t type, algorithm_t** algo)
+{
+       iterator_t *iterator = create_algorithm_iterator(this, type);
+       if (iterator->iterate(iterator, (void**)algo))
+       {
+               iterator->destroy(iterator);
+               return TRUE;
+       }
+       iterator->destroy(iterator);
+       return FALSE;
+}
+
+/**
+ * Implements proposal_t.has_dh_group
+ */
+static bool has_dh_group(private_proposal_t *this, diffie_hellman_group_t group)
+{
+       algorithm_t *current;
+       iterator_t *iterator;
+       bool result = FALSE;
+       
+       iterator = this->dh_groups->create_iterator(this->dh_groups, TRUE);
+       if (iterator->get_count(iterator))
+       {
+               while (iterator->iterate(iterator, (void**)&current))
+               {
+                       if (current->algorithm == group)
+                       {
+                               result = TRUE;
+                               break;
+                       }
+               }
+       }
+       else if (group == MODP_NONE)
+       {
+               result = TRUE;
+       }
+       iterator->destroy(iterator);
+       return result;
+}
+
+/**
  * Find a matching alg/keysize in two linked lists
  */
 static bool select_algo(linked_list_t *first, linked_list_t *second, bool *add, u_int16_t *alg, size_t *key_size)
@@ -530,6 +541,7 @@ proposal_t *proposal_create(protocol_id_t protocol)
        this->public.add_algorithm = (void (*)(proposal_t*,transform_type_t,u_int16_t,size_t))add_algorithm;
        this->public.create_algorithm_iterator = (iterator_t* (*)(proposal_t*,transform_type_t))create_algorithm_iterator;
        this->public.get_algorithm = (bool (*)(proposal_t*,transform_type_t,algorithm_t**))get_algorithm;
+       this->public.has_dh_group = (bool (*)(proposal_t*,diffie_hellman_group_t))has_dh_group;
        this->public.select = (proposal_t* (*)(proposal_t*,proposal_t*))select_proposal;
        this->public.get_protocol = (protocol_id_t(*)(proposal_t*))get_protocol;
        this->public.set_spi = (void(*)(proposal_t*,u_int64_t))set_spi;
index 4bee08e..379550f 100644 (file)
@@ -164,7 +164,6 @@ struct proposal_t {
         * @brief Get the algorithm for a type to use.
         * 
         * If there are multiple algorithms, only the first is returned.
-        * Result is still owned by proposal, do not modify!
         * 
         * @param this                                  calling object
         * @param type                                  kind of algorithm
@@ -172,6 +171,15 @@ struct proposal_t {
         * @return                                              TRUE if algorithm of this kind available
         */
        bool (*get_algorithm) (proposal_t *this, transform_type_t type, algorithm_t** algo);
+       
+       /**
+        * @brief Check if the proposal has a specific DH group.
+        * 
+        * @param this                                  calling object
+        * @param group                                 group to check for
+        * @return                                              TRUE if algorithm included
+        */
+       bool (*has_dh_group) (proposal_t *this, diffie_hellman_group_t group);
 
        /**
         * @brief Compare two proposal, and select a matching subset.
index 9633ba7..eb707c1 100644 (file)
@@ -279,7 +279,6 @@ static status_t build_request(private_task_manager_t *this)
                                if (activate_task(this, CHILD_CREATE))
                                {
                                        exchange = CREATE_CHILD_SA;
-                                       activate_task(this, IKE_CONFIG);
                                        break;
                                }
                                if (activate_task(this, CHILD_DELETE))
@@ -333,6 +332,11 @@ static status_t build_request(private_task_manager_t *this)
                                case IKE_AUTHENTICATE:
                                        exchange = IKE_AUTH;
                                        break;
+                               case CHILD_CREATE:
+                               case CHILD_REKEY:
+                               case IKE_REKEY:
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
                                default:
                                        continue;
                        }
index 5ed9791..ac03a33 100644 (file)
@@ -26,6 +26,7 @@
 #include <daemon.h>
 #include <crypto/diffie_hellman.h>
 #include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
 #include <encoding/payloads/ts_payload.h>
 #include <encoding/payloads/nonce_payload.h>
 #include <encoding/payloads/notify_payload.h>
@@ -89,6 +90,16 @@ struct private_child_create_t {
        linked_list_t *tsr;
        
        /**
+        * optional diffie hellman exchange
+        */
+       diffie_hellman_t *dh;
+       
+       /**
+        * group used for DH exchange
+        */
+       diffie_hellman_group_t dh_group;
+       
+       /**
         * mode the new CHILD_SA uses (transport/tunnel/beet)
         */
        mode_t mode;
@@ -162,21 +173,29 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host)
 }
 
 /**
- * Install a CHILD_SA for usage
+ * Install a CHILD_SA for usage, return value:
+ * - FAILED: no acceptable proposal
+ * - INVALID_ARG: diffie hellman group inacceptable
+ * - NOT_FOUND: TS inacceptable
  */
-static status_t select_and_install(private_child_create_t *this)
+static status_t select_and_install(private_child_create_t *this, bool no_dh)
 {
        prf_plus_t *prf_plus;
        status_t status;
-       chunk_t nonce_i, nonce_r, seed;
+       chunk_t nonce_i, nonce_r, secret, seed;
        linked_list_t *my_ts, *other_ts;
        host_t *me, *other, *other_vip, *my_vip;
        
-       if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL)
+       if (this->proposals == NULL)
        {
-               SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message");
+               SIG(CHILD_UP_FAILED, "SA payload missing in message");
                return FAILED;
        }
+       if (this->tsi == NULL || this->tsr == NULL)
+       {
+               SIG(CHILD_UP_FAILED, "TS payloads missing in message");
+               return NOT_FOUND;
+       }
        
        if (this->initiator)
        {
@@ -198,14 +217,34 @@ static status_t select_and_install(private_child_create_t *this)
        my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
        other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
 
-       this->proposal = this->config->select_proposal(this->config, this->proposals);
-       
+       this->proposal = this->config->select_proposal(this->config, this->proposals,
+                                                                                                  no_dh);
        if (this->proposal == NULL)
        {
                SIG(CHILD_UP_FAILED, "no acceptable proposal found");
                return FAILED;
        }
        
+       if (!this->proposal->has_dh_group(this->proposal, this->dh_group))
+       {
+               algorithm_t *algo;
+               if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
+                                                                                 &algo))
+               {
+                       u_int16_t group = algo->algorithm;
+                       SIG(CHILD_UP_FAILED, "DH group %N inacceptable, requesting %N",
+                               diffie_hellman_group_names, this->dh_group,
+                               diffie_hellman_group_names, group);
+                       this->dh_group = group;
+                       return INVALID_ARG;
+               }
+               else
+               {
+                       SIG(CHILD_UP_FAILED, "no acceptable proposal found");
+                       return FAILED;
+               }
+       }
+       
        if (my_vip == NULL)
        {
                my_vip = me;
@@ -228,7 +267,7 @@ static status_t select_and_install(private_child_create_t *this)
        if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0)
        {
                SIG(CHILD_UP_FAILED, "no acceptable traffic selectors found");
-               return FAILED;
+               return NOT_FOUND;
        }
        
        this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
@@ -275,7 +314,20 @@ static status_t select_and_install(private_child_create_t *this)
                }
        }
        
-       seed = chunk_cata("cc", nonce_i, nonce_r);
+       if (this->dh)
+       {
+               if (this->dh->get_shared_secret(this->dh, &secret) != SUCCESS)
+               {
+                       SIG(CHILD_UP_FAILED, "DH exchange incomplete");
+                       return FAILED;
+               }
+               DBG3(DBG_IKE, "DH secret %B", &secret);
+               seed = chunk_cata("mcc", secret, nonce_i, nonce_r);
+       }
+       else
+       {
+               seed = chunk_cata("cc", nonce_i, nonce_r);
+       }
        prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
        
        if (this->initiator)
@@ -293,7 +345,7 @@ static status_t select_and_install(private_child_create_t *this)
        if (status != SUCCESS)
        {
                SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel");
-               return status;
+               return FAILED;
        }
        
        status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
@@ -302,7 +354,7 @@ static status_t select_and_install(private_child_create_t *this)
        if (status != SUCCESS)
        {       
                SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel");
-               return status;
+               return NOT_FOUND;
        }
        /* add to IKE_SA, and remove from task */
        this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
@@ -317,8 +369,9 @@ static status_t select_and_install(private_child_create_t *this)
 static void build_payloads(private_child_create_t *this, message_t *message)
 {
        sa_payload_t *sa_payload;
-       ts_payload_t *ts_payload;
        nonce_payload_t *nonce_payload;
+       ke_payload_t *ke_payload;
+       ts_payload_t *ts_payload;
 
        /* add SA payload */
        if (this->initiator)
@@ -339,6 +392,13 @@ static void build_payloads(private_child_create_t *this, message_t *message)
                message->add_payload(message, (payload_t*)nonce_payload);
        }
        
+       /* diffie hellman exchange, if PFS enabled */
+       if (this->dh)
+       {
+               ke_payload = ke_payload_create_from_diffie_hellman(this->dh);
+               message->add_payload(message, (payload_t*)ke_payload);
+       }
+       
        /* add TSi/TSr payloads */
        ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
        message->add_payload(message, (payload_t*)ts_payload);
@@ -367,6 +427,7 @@ static void process_payloads(private_child_create_t *this, message_t *message)
        iterator_t *iterator;
        payload_t *payload;
        sa_payload_t *sa_payload;
+       ke_payload_t *ke_payload;
        ts_payload_t *ts_payload;
        notify_payload_t *notify_payload;
        
@@ -382,6 +443,19 @@ static void process_payloads(private_child_create_t *this, message_t *message)
                                sa_payload = (sa_payload_t*)payload;
                                this->proposals = sa_payload->get_proposals(sa_payload);
                                break;
+                       case KEY_EXCHANGE:
+                               ke_payload = (ke_payload_t*)payload;
+                               if (!this->initiator)
+                               {
+                                       this->dh_group = ke_payload->get_dh_group_number(ke_payload);
+                                       this->dh = diffie_hellman_create(this->dh_group);
+                               }
+                               if (this->dh)
+                               {
+                                       this->dh->set_other_public_value(this->dh,
+                                                               ke_payload->get_key_exchange_data(ke_payload));
+                               }
+                               break;
                        case TRAFFIC_SELECTOR_INITIATOR:
                                ts_payload = (ts_payload_t*)payload;
                                this->tsi = ts_payload->get_traffic_selectors(ts_payload);
@@ -429,6 +503,10 @@ static status_t build_i(private_child_create_t *this, message_t *message)
                                message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
                                return SUCCESS;
                        }
+                       if (this->dh_group == MODP_NONE)
+                       {
+                               this->dh_group = this->config->get_dh_group(this->config);
+                       }
                        break;
                case IKE_AUTH:
                        if (!message->get_payload(message, ID_INITIATOR))
@@ -461,7 +539,8 @@ static status_t build_i(private_child_create_t *this, message_t *message)
        }
        this->tsr = this->config->get_traffic_selectors(this->config, FALSE, 
                                                                                                        NULL, other);
-       this->proposals = this->config->get_proposals(this->config);
+       this->proposals = this->config->get_proposals(this->config,
+                                                                                                 this->dh_group == MODP_NONE);
        this->mode = this->config->get_mode(this->config);
        
        this->child_sa = child_sa_create(me, other,
@@ -476,6 +555,11 @@ static status_t build_i(private_child_create_t *this, message_t *message)
                return FAILED;
        }
        
+       if (this->dh_group != MODP_NONE)
+       {
+               this->dh = diffie_hellman_create(this->dh_group);
+       }
+       
        build_payloads(this, message);
        
        this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
@@ -535,6 +619,8 @@ static status_t process_r(private_child_create_t *this, message_t *message)
  */
 static status_t build_r(private_child_create_t *this, message_t *message)
 {
+       bool no_dh = TRUE;
+
        switch (message->get_exchange_type(message))
        {
                case IKE_SA_INIT:
@@ -545,6 +631,7 @@ static status_t build_r(private_child_create_t *this, message_t *message)
                                message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
                                return SUCCESS;
                        }
+                       no_dh = FALSE;
                        break;
                case IKE_AUTH:
                        if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
@@ -578,10 +665,24 @@ static status_t build_r(private_child_create_t *this, message_t *message)
                                                                         this->config, this->reqid,
                                                                         this->ike_sa->is_natt_enabled(this->ike_sa));
        
-       if (select_and_install(this) != SUCCESS)
+       switch (select_and_install(this, no_dh))
        {
-               message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
-               return SUCCESS;
+               case SUCCESS:
+                       break;
+               case NOT_FOUND:
+                       message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty);
+                       return SUCCESS;
+               case INVALID_ARG:
+               {
+                       u_int16_t group = htons(this->dh_group);
+                       message->add_notify(message, FALSE, INVALID_KE_PAYLOAD,
+                                                               chunk_from_thing(group));
+                       return SUCCESS;
+               }
+               case FAILED:
+               default:
+                       message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+                       return SUCCESS;
        }
        
        build_payloads(this, message);
@@ -598,6 +699,7 @@ static status_t process_i(private_child_create_t *this, message_t *message)
 {
        iterator_t *iterator;
        payload_t *payload;
+       bool no_dh = TRUE;
 
        switch (message->get_exchange_type(message))
        {
@@ -605,6 +707,7 @@ static status_t process_i(private_child_create_t *this, message_t *message)
                        return get_nonce(message, &this->other_nonce);
                case CREATE_CHILD_SA:
                        get_nonce(message, &this->other_nonce);
+                       no_dh = FALSE;
                        break;
                case IKE_AUTH:
                        if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
@@ -642,6 +745,22 @@ static status_t process_i(private_child_create_t *this, message_t *message)
                                        /* an error in CHILD_SA creation is not critical */
                                        return SUCCESS;
                                }
+                               case INVALID_KE_PAYLOAD:
+                               {
+                                       chunk_t data;
+                                       diffie_hellman_group_t bad_group;
+                                       
+                                       bad_group = this->dh_group;
+                                       data = notify->get_notification_data(notify);
+                                       this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+                                       DBG1(DBG_IKE, "peer didn't accept DH group %N, "
+                                                "it requested %N", diffie_hellman_group_names,
+                                                bad_group, diffie_hellman_group_names, this->dh_group);
+                                       
+                                       this->public.task.migrate(&this->public.task, this->ike_sa);
+                                       iterator->destroy(iterator);
+                                       return NEED_MORE;
+                               }
                                default:
                                        break;
                        }
@@ -651,7 +770,7 @@ static status_t process_i(private_child_create_t *this, message_t *message)
        
        process_payloads(this, message);
        
-       if (select_and_install(this) == SUCCESS)
+       if (select_and_install(this, no_dh) == SUCCESS)
        {
                SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
        }
@@ -715,6 +834,7 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
        }
        DESTROY_IF(this->child_sa);
        DESTROY_IF(this->proposal);
+       DESTROY_IF(this->dh);
        if (this->proposals)
        {
                this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
@@ -724,6 +844,7 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
        this->proposals = NULL;
        this->tsi = NULL;
        this->tsr = NULL;
+       this->dh = NULL;
        this->child_sa = NULL;
        this->mode = MODE_TUNNEL;
        this->reqid = 0;
@@ -750,6 +871,7 @@ static void destroy(private_child_create_t *this)
                DESTROY_IF(this->child_sa);
        }
        DESTROY_IF(this->proposal);
+       DESTROY_IF(this->dh);
        if (this->proposals)
        {
                this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
@@ -794,6 +916,8 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, child_cfg_t *config)
        this->proposal = NULL;
        this->tsi = NULL;
        this->tsr = NULL;
+       this->dh = NULL;
+       this->dh_group = MODP_NONE;
        this->child_sa = NULL;
        this->mode = MODE_TUNNEL;
        this->reqid = 0;
index 1621357..4f3c690 100644 (file)
@@ -183,7 +183,12 @@ static status_t process_i(private_child_rekey_t *this, message_t *message)
        u_int32_t spi;
        child_sa_t *to_delete;
        
-       this->child_create->task.process(&this->child_create->task, message);
+       if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE)
+       {
+               /* bad DH group while rekeying, try again */
+               this->child_create->task.migrate(&this->child_create->task, this->ike_sa);
+               return NEED_MORE;
+       }
        if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
        {
                /* establishing new child failed, reuse old. but not when we
index 8165a01..f78b5dd 100644 (file)
@@ -69,7 +69,7 @@ struct private_ike_init_t {
        /**
         * Diffie hellman object used to generate public DH value.
         */
-       diffie_hellman_t *diffie_hellman;
+       diffie_hellman_t *dh;
        
        /**
         * nonce chosen by us
@@ -151,7 +151,7 @@ static void build_payloads(private_ike_init_t *this, message_t *message)
        nonce_payload->set_nonce(nonce_payload, this->my_nonce);
        message->add_payload(message, (payload_t*)nonce_payload);
        
-       ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
+       ke_payload = ke_payload_create_from_diffie_hellman(this->dh);
        message->add_payload(message, (payload_t*)ke_payload);
 }
 
@@ -183,33 +183,16 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
                        case KEY_EXCHANGE:
                        {
                                ke_payload_t *ke_payload = (ke_payload_t*)payload;
-                               diffie_hellman_group_t dh_group;
-                               chunk_t key_data;
                                
-                               dh_group = ke_payload->get_dh_group_number(ke_payload);
-                               
-                               if (this->initiator)
-                               {
-                                       if (dh_group != this->dh_group)
-                                       {
-                                               DBG1(DBG_IKE, "received a DH group not requested (%N)",
-                                                        diffie_hellman_group_names, dh_group);
-                                               break;
-                                       }
-                               }
-                               else
+                               this->dh_group = ke_payload->get_dh_group_number(ke_payload);
+                               if (!this->initiator)
                                {
-                                       this->dh_group = dh_group;
-                                       if (!this->config->check_dh_group(this->config, dh_group))
-                                       {
-                                               break;
-                                       }
-                                       this->diffie_hellman = diffie_hellman_create(dh_group);
+                                       this->dh = diffie_hellman_create(this->dh_group);
                                }
-                               if (this->diffie_hellman)
+                               if (this->dh)
                                {
-                                       key_data = ke_payload->get_key_exchange_data(ke_payload);
-                                       this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data);
+                                       this->dh->set_other_public_value(this->dh,
+                                                               ke_payload->get_key_exchange_data(ke_payload));
                                }
                                break;
                        }
@@ -246,11 +229,11 @@ static status_t build_i(private_ike_init_t *this, message_t *message)
        }
 
        /* if the DH group is set via use_dh_group(), we already have a DH object */
-       if (!this->diffie_hellman)
+       if (!this->dh)
        {
                this->dh_group = this->config->get_dh_group(this->config);
-               this->diffie_hellman = diffie_hellman_create(this->dh_group);
-               if (this->diffie_hellman == NULL)
+               this->dh = diffie_hellman_create(this->dh_group);
+               if (this->dh == NULL)
                {
                        SIG(IKE_UP_FAILED, "configured DH group %N not supported",
                                diffie_hellman_group_names, this->dh_group);
@@ -325,25 +308,29 @@ static status_t build_r(private_ike_init_t *this, message_t *message)
                return FAILED;
        }
        
-       if (this->diffie_hellman == NULL ||
-               this->diffie_hellman->get_shared_secret(this->diffie_hellman,
-                                                                                               &secret) != SUCCESS)
+       if (this->dh == NULL ||
+               !this->proposal->has_dh_group(this->proposal, this->dh_group) ||
+               this->dh->get_shared_secret(this->dh, &secret) != SUCCESS)
        {
-               chunk_t chunk;
-               u_int16_t dh_enc;
-               
-               SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)",
-                        diffie_hellman_group_names, this->dh_group);
-               this->dh_group = this->config->get_dh_group(this->config);
-               dh_enc = htons(this->dh_group);
-               chunk.ptr = (u_int8_t*)&dh_enc;
-               chunk.len = sizeof(dh_enc);
-               message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk);
-               DBG1(DBG_IKE, "requesting DH group %N",
-                        diffie_hellman_group_names, this->dh_group);
+               algorithm_t *algo;
+               if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
+                                                                                 &algo))
+               {
+                       u_int16_t group = algo->algorithm;
+                       SIG(CHILD_UP_FAILED, "DH group %N inacceptable, requesting %N",
+                               diffie_hellman_group_names, this->dh_group,
+                               diffie_hellman_group_names, group);
+                       this->dh_group = group;
+                       group = htons(group);
+                       message->add_notify(message, FALSE, INVALID_KE_PAYLOAD,
+                                                               chunk_from_thing(group));
+               }
+               else
+               {
+                       SIG(IKE_UP_FAILED, "no acceptable proposal found");
+               }
                return FAILED;
        }
-
        
        if (this->old_sa)
        {
@@ -404,26 +391,20 @@ static status_t process_i(private_ike_init_t *this, message_t *message)
                                case INVALID_KE_PAYLOAD:
                                {
                                        chunk_t data;
-                                       diffie_hellman_group_t old_dh_group;
+                                       diffie_hellman_group_t bad_group;
                                        
-                                       old_dh_group = this->dh_group;
+                                       bad_group = this->dh_group;
                                        data = notify->get_notification_data(notify);
                                        this->dh_group = ntohs(*((u_int16_t*)data.ptr));
-                                       
-                                       DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested"
-                                                " %N", diffie_hellman_group_names, old_dh_group,
-                                                diffie_hellman_group_names, this->dh_group);
-                                       if (!this->config->check_dh_group(this->config, this->dh_group))
-                                       {
-                                               DBG1(DBG_IKE, "requested DH group %N not acceptable, "
-                                                       "giving up", diffie_hellman_group_names,
-                                                       this->dh_group);
-                                               iterator->destroy(iterator);
-                                               return FAILED;
+                                       DBG1(DBG_IKE, "peer didn't accept DH group %N, "
+                                                "it requested %N", diffie_hellman_group_names,
+                                                bad_group, diffie_hellman_group_names, this->dh_group);
+                                                
+                                       if (this->old_sa == NULL)
+                                       {       /* reset the IKE_SA if we are not rekeying */
+                                               this->ike_sa->reset(this->ike_sa);
                                        }
                                        
-                                       this->ike_sa->reset(this->ike_sa);
-                                       
                                        iterator->destroy(iterator);
                                        return NEED_MORE;
                                }
@@ -468,9 +449,9 @@ static status_t process_i(private_ike_init_t *this, message_t *message)
                return FAILED;
        }
        
-       if (this->diffie_hellman == NULL ||
-               this->diffie_hellman->get_shared_secret(this->diffie_hellman,
-                                                                                               &secret) != SUCCESS)
+       if (this->dh == NULL ||
+               !this->proposal->has_dh_group(this->proposal, this->dh_group) ||
+               this->dh->get_shared_secret(this->dh, &secret) != SUCCESS)
        {
                SIG(IKE_UP_FAILED, "peers DH group selection invalid");
                return FAILED;
@@ -537,12 +518,12 @@ static chunk_t get_lower_nonce(private_ike_init_t *this)
 static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa)
 {
        DESTROY_IF(this->proposal);
-       DESTROY_IF(this->diffie_hellman);
+       DESTROY_IF(this->dh);
        chunk_free(&this->other_nonce);
        
        this->ike_sa = ike_sa;
        this->proposal = NULL;
-       this->diffie_hellman = diffie_hellman_create(this->dh_group);
+       this->dh = diffie_hellman_create(this->dh_group);
 }
 
 /**
@@ -551,7 +532,7 @@ static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa)
 static void destroy(private_ike_init_t *this)
 {
        DESTROY_IF(this->proposal);
-       DESTROY_IF(this->diffie_hellman);
+       DESTROY_IF(this->dh);
        chunk_free(&this->my_nonce);
        chunk_free(&this->other_nonce);
        chunk_free(&this->cookie);
@@ -583,7 +564,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
        this->ike_sa = ike_sa;
        this->initiator = initiator;
        this->dh_group = MODP_NONE;
-       this->diffie_hellman = NULL;
+       this->dh = NULL;
        this->my_nonce = chunk_empty;
        this->other_nonce = chunk_empty;
        this->cookie = chunk_empty;
index 3c3eae5..d54fc35 100644 (file)
@@ -75,14 +75,18 @@ static status_t build_i(private_ike_rekey_t *this, message_t *message)
 {
        peer_cfg_t *peer_cfg;
        
-       this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
-                                                                                                               TRUE);
-       
-       peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
-       this->new_sa->set_peer_cfg(this->new_sa, peer_cfg);
-       this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa);
+       /* create new SA only on first try */
+       if (this->new_sa == NULL)
+       {
+               this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+                                                                                                                       TRUE);
+               
+               peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+               this->new_sa->set_peer_cfg(this->new_sa, peer_cfg);
+               this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa);
+               this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+       }
        this->ike_init->task.build(&this->ike_init->task, message);
-       this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
 
        return NEED_MORE;
 }
@@ -162,22 +166,29 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message)
        job_t *job;
        ike_sa_id_t *to_delete;
 
-       if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED)
+       switch (this->ike_init->task.process(&this->ike_init->task, message))
        {
-               /* rekeying failed, fallback to old SA */
-               if (!(this->collision &&
-                       this->collision->get_type(this->collision) == IKE_DELETE))
-               {
-                       job_t *job;
-                       u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
-                       job = (job_t*)rekey_ike_sa_job_create(
-                                                                       this->ike_sa->get_id(this->ike_sa), FALSE);
-                       DBG1(DBG_IKE, "IKE_SA rekeying failed, "
-                                                                       "trying again in %d seconds", retry);
-                       this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-                       charon->event_queue->add_relative(charon->event_queue, job, retry * 1000);
-               }
-               return SUCCESS;
+               case FAILED:
+                       /* rekeying failed, fallback to old SA */
+                       if (!(this->collision &&
+                               this->collision->get_type(this->collision) == IKE_DELETE))
+                       {
+                               job_t *job;
+                               u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
+                               job = (job_t*)rekey_ike_sa_job_create(
+                                                                               this->ike_sa->get_id(this->ike_sa), FALSE);
+                               DBG1(DBG_IKE, "IKE_SA rekeying failed, "
+                                                                               "trying again in %d seconds", retry);
+                               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+                               charon->event_queue->add_relative(charon->event_queue, job, retry * 1000);
+                       }
+                       return SUCCESS;
+               case NEED_MORE:
+                       /* bad dh group, try again */
+                       this->ike_init->task.migrate(&this->ike_init->task, this->new_sa);
+                       return NEED_MORE;
+               default:
+                       break;
        }
 
        this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);