]> git.ipfire.org Git - thirdparty/strongswan.git/blobdiff - src/libcharon/sa/ikev2/tasks/ike_init.c
ike: Optionally allow private algorithms for IKE/CHILD_SAs
[thirdparty/strongswan.git] / src / libcharon / sa / ikev2 / tasks / ike_init.c
index 42be11896d1ff78c78c919659977894d51bc5d96..477d2caae5ac9560fe4211aa67aeb85db28d124e 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (C) 2008-2015 Tobias Brunner
+ * Copyright (C) 2008-2019 Tobias Brunner
  * Copyright (C) 2005-2008 Martin Willi
  * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -54,11 +54,6 @@ struct private_ike_init_t {
         */
        bool initiator;
 
-       /**
-        * IKE config to establish
-        */
-       ike_cfg_t *config;
-
        /**
         * diffie hellman group to use
         */
@@ -158,7 +153,11 @@ static void send_supported_hash_algorithms(private_ike_init_t *this,
        peer_cfg_t *peer;
        auth_cfg_t *auth;
        auth_rule_t rule;
-       uintptr_t config;
+       signature_params_t *config;
+       int written;
+       size_t len = BUF_LEN;
+       char buf[len];
+       char *pos = buf;
        char *plugin_name;
 
        algos = hash_algorithm_set_create();
@@ -171,9 +170,10 @@ static void send_supported_hash_algorithms(private_ike_init_t *this,
                        enumerator = auth->create_enumerator(auth);
                        while (enumerator->enumerate(enumerator, &rule, &config))
                        {
-                               if (rule == AUTH_RULE_SIGNATURE_SCHEME)
+                               if (rule == AUTH_RULE_IKE_SIGNATURE_SCHEME)
                                {
-                                       hash = hasher_from_signature_scheme(config);
+                                       hash = hasher_from_signature_scheme(config->scheme,
+                                                                                                               config->params);
                                        if (hasher_algorithm_for_ikev2(hash))
                                        {
                                                algos->add(algos, hash);
@@ -205,11 +205,23 @@ static void send_supported_hash_algorithms(private_ike_init_t *this,
                while (enumerator->enumerate(enumerator, &hash))
                {
                        writer->write_uint16(writer, hash);
+
+                       /* generate debug output */
+                       written = snprintf(pos, len, " %N", hash_algorithm_short_names,
+                                                          hash);
+                       if (written > 0 && written < len)
+                       {
+                               pos += written;
+                               len -= written;
+                       }
                }
                enumerator->destroy(enumerator);
                message->add_notify(message, FALSE, SIGNATURE_HASH_ALGORITHMS,
                                                        writer->get_buf(writer));
                writer->destroy(writer);
+
+               *pos = '\0';
+               DBG2(DBG_CFG, "sending supported signature hash algorithms:%s", buf);
        }
        algos->destroy(algos);
 }
@@ -221,7 +233,11 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this,
                                                                                         notify_payload_t *notify)
 {
        bio_reader_t *reader;
-       u_int16_t algo;
+       uint16_t algo;
+       int written;
+       size_t len = BUF_LEN;
+       char buf[len];
+       char *pos = buf;
        bool added = FALSE;
 
        reader = bio_reader_create(notify->get_notification_data(notify));
@@ -231,16 +247,60 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this,
                {
                        this->keymat->add_hash_algorithm(this->keymat, algo);
                        added = TRUE;
+
+                       /* generate debug output */
+                       written = snprintf(pos, len, " %N", hash_algorithm_short_names,
+                                                          algo);
+                       if (written > 0 && written < len)
+                       {
+                               pos += written;
+                               len -= written;
+                       }
                }
        }
        reader->destroy(reader);
 
+       *pos = '\0';
+       DBG2(DBG_CFG, "received supported signature hash algorithms:%s", buf);
+
        if (added)
        {
                this->ike_sa->enable_extension(this->ike_sa, EXT_SIGNATURE_AUTH);
        }
 }
 
+/**
+ * Check whether to send a USE_PPK notify
+ */
+static bool send_use_ppk(private_ike_init_t *this)
+{
+       peer_cfg_t *peer;
+       enumerator_t *keys;
+       shared_key_t *key;
+       bool use_ppk = FALSE;
+
+       if (this->initiator)
+       {
+               peer = this->ike_sa->get_peer_cfg(this->ike_sa);
+               if (peer->get_ppk_id(peer))
+               {
+                       use_ppk = TRUE;
+               }
+       }
+       else if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK))
+       {
+               /* check if we have at least one PPK available */
+               keys = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PPK,
+                                                                                                         NULL, NULL);
+               if (keys->enumerate(keys, &key, NULL, NULL))
+               {
+                       use_ppk = TRUE;
+               }
+               keys->destroy(keys);
+       }
+       return use_ppk;
+}
+
 /**
  * build the payloads for the message
  */
@@ -249,28 +309,44 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
        sa_payload_t *sa_payload;
        ke_payload_t *ke_payload;
        nonce_payload_t *nonce_payload;
-       linked_list_t *proposal_list;
+       linked_list_t *proposal_list, *other_dh_groups;
        ike_sa_id_t *id;
        proposal_t *proposal;
        enumerator_t *enumerator;
+       ike_cfg_t *ike_cfg;
 
        id = this->ike_sa->get_id(this->ike_sa);
 
-       this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
 
        if (this->initiator)
        {
-               proposal_list = this->config->get_proposals(this->config);
-               if (this->old_sa)
+               proposal_list = ike_cfg->get_proposals(ike_cfg);
+               other_dh_groups = linked_list_create();
+               enumerator = proposal_list->create_enumerator(proposal_list);
+               while (enumerator->enumerate(enumerator, (void**)&proposal))
                {
                        /* include SPI of new IKE_SA when we are rekeying */
-                       enumerator = proposal_list->create_enumerator(proposal_list);
-                       while (enumerator->enumerate(enumerator, (void**)&proposal))
+                       if (this->old_sa)
                        {
                                proposal->set_spi(proposal, id->get_initiator_spi(id));
                        }
-                       enumerator->destroy(enumerator);
+                       /* move the selected DH group to the front of the proposal */
+                       if (!proposal->promote_dh_group(proposal, this->dh_group))
+                       {       /* the proposal does not include the group, move to the back */
+                               proposal_list->remove_at(proposal_list, enumerator);
+                               other_dh_groups->insert_last(other_dh_groups, proposal);
+                       }
+               }
+               enumerator->destroy(enumerator);
+               /* add proposals that don't contain the selected group */
+               enumerator = other_dh_groups->create_enumerator(other_dh_groups);
+               while (enumerator->enumerate(enumerator, (void**)&proposal))
+               {       /* no need to remove from the list as we destroy it anyway*/
+                       proposal_list->insert_last(proposal_list, proposal);
                }
+               enumerator->destroy(enumerator);
+               other_dh_groups->destroy(other_dh_groups);
 
                sa_payload = sa_payload_create_from_proposals_v2(proposal_list);
                proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
@@ -286,8 +362,6 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
        }
        message->add_payload(message, (payload_t*)sa_payload);
 
-       nonce_payload = nonce_payload_create(PLV2_NONCE);
-       nonce_payload->set_nonce(nonce_payload, this->my_nonce);
        ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE,
                                                                                                           this->dh);
        if (!ke_payload)
@@ -295,6 +369,8 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
                DBG1(DBG_IKE, "creating KE payload failed");
                return FALSE;
        }
+       nonce_payload = nonce_payload_create(PLV2_NONCE);
+       nonce_payload->set_nonce(nonce_payload, this->my_nonce);
 
        if (this->old_sa)
        {       /* payload order differs if we are rekeying */
@@ -309,7 +385,7 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
 
        /* negotiate fragmentation if we are not rekeying */
        if (!this->old_sa &&
-                this->config->fragmentation(this->config) != FRAGMENTATION_NO)
+                ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO)
        {
                if (this->initiator ||
                        this->ike_sa->supports_extension(this->ike_sa,
@@ -332,11 +408,109 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
        /* notify other peer if we support redirection */
        if (!this->old_sa && this->initiator && this->follow_redirects)
        {
-               message->add_notify(message, FALSE, REDIRECT_SUPPORTED, chunk_empty);
+               identification_t *gateway;
+               host_t *from;
+               chunk_t data;
+
+               from = this->ike_sa->get_redirected_from(this->ike_sa);
+               if (from)
+               {
+                       gateway = identification_create_from_sockaddr(
+                                                                                                       from->get_sockaddr(from));
+                       data = redirect_data_create(gateway, chunk_empty);
+                       message->add_notify(message, FALSE, REDIRECTED_FROM, data);
+                       chunk_free(&data);
+                       gateway->destroy(gateway);
+               }
+               else
+               {
+                       message->add_notify(message, FALSE, REDIRECT_SUPPORTED,
+                                                               chunk_empty);
+               }
+       }
+       /* notify the peer if we want to use/support PPK */
+       if (!this->old_sa && send_use_ppk(this))
+       {
+               message->add_notify(message, FALSE, USE_PPK, chunk_empty);
+       }
+       /* notify the peer if we accept childless IKE_SAs */
+       if (!this->old_sa && !this->initiator &&
+                ike_cfg->childless(ike_cfg) != CHILDLESS_NEVER)
+       {
+               message->add_notify(message, FALSE, CHILDLESS_IKEV2_SUPPORTED,
+                                                       chunk_empty);
        }
        return TRUE;
 }
 
+/**
+ * Process the SA payload and select a proposal
+ */
+static void process_sa_payload(private_ike_init_t *this, message_t *message,
+                                                          sa_payload_t *sa_payload)
+{
+       ike_cfg_t *ike_cfg, *cfg, *alt_cfg = NULL;
+       enumerator_t *enumerator;
+       linked_list_t *proposal_list;
+       host_t *me, *other;
+       proposal_selection_flag_t flags = 0;
+
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+
+       proposal_list = sa_payload->get_proposals(sa_payload);
+       if (!this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN) &&
+               !lib->settings->get_bool(lib->settings, "%s.accept_private_algs",
+                                                                FALSE, lib->ns))
+       {
+               flags |= PROPOSAL_SKIP_PRIVATE;
+       }
+       if (!lib->settings->get_bool(lib->settings,
+                                                       "%s.prefer_configured_proposals", TRUE, lib->ns))
+       {
+               flags |= PROPOSAL_PREFER_SUPPLIED;
+       }
+       this->proposal = ike_cfg->select_proposal(ike_cfg, proposal_list, flags);
+       if (!this->proposal)
+       {
+               if (!this->initiator && !this->old_sa)
+               {
+                       me = message->get_destination(message);
+                       other = message->get_source(message);
+                       enumerator = charon->backends->create_ike_cfg_enumerator(
+                                                                                       charon->backends, me, other, IKEV2);
+                       while (enumerator->enumerate(enumerator, &cfg))
+                       {
+                               if (ike_cfg == cfg)
+                               {       /* already tried and failed */
+                                       continue;
+                               }
+                               DBG1(DBG_IKE, "no matching proposal found, trying alternative "
+                                        "config");
+                               this->proposal = cfg->select_proposal(cfg, proposal_list,
+                                                                                                         flags);
+                               if (this->proposal)
+                               {
+                                       alt_cfg = cfg->get_ref(cfg);
+                                       break;
+                               }
+                       }
+                       enumerator->destroy(enumerator);
+               }
+               if (alt_cfg)
+               {
+                       this->ike_sa->set_ike_cfg(this->ike_sa, alt_cfg);
+                       alt_cfg->destroy(alt_cfg);
+               }
+               else
+               {
+                       charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE,
+                                                          proposal_list);
+               }
+       }
+       proposal_list->destroy_offset(proposal_list,
+                                                                 offsetof(proposal_t, destroy));
+}
+
 /**
  * Read payloads from message
  */
@@ -353,22 +527,7 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
                {
                        case PLV2_SECURITY_ASSOCIATION:
                        {
-                               sa_payload_t *sa_payload = (sa_payload_t*)payload;
-                               linked_list_t *proposal_list;
-                               bool private;
-
-                               proposal_list = sa_payload->get_proposals(sa_payload);
-                               private = this->ike_sa->supports_extension(this->ike_sa,
-                                                                                                                  EXT_STRONGSWAN);
-                               this->proposal = this->config->select_proposal(this->config,
-                                                                                                               proposal_list, private);
-                               if (!this->proposal)
-                               {
-                                       charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE,
-                                                                          proposal_list);
-                               }
-                               proposal_list->destroy_offset(proposal_list,
-                                                                                         offsetof(proposal_t, destroy));
+                               process_sa_payload(this, message, (sa_payload_t*)payload);
                                break;
                        }
                        case PLV2_KEY_EXCHANGE:
@@ -401,9 +560,43 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
                                                        handle_supported_hash_algorithms(this, notify);
                                                }
                                                break;
+                                       case USE_PPK:
+                                               if (!this->old_sa)
+                                               {
+                                                       this->ike_sa->enable_extension(this->ike_sa,
+                                                                                                                  EXT_PPK);
+                                               }
+                                               break;
+                                       case REDIRECTED_FROM:
+                                       {
+                                               identification_t *gateway;
+                                               chunk_t data;
+
+                                               data = notify->get_notification_data(notify);
+                                               gateway = redirect_data_parse(data, NULL);
+                                               if (!gateway)
+                                               {
+                                                       DBG1(DBG_IKE, "received invalid REDIRECTED_FROM "
+                                                                "notify, ignored");
+                                                       break;
+                                               }
+                                               DBG1(DBG_IKE, "client got redirected from %Y", gateway);
+                                               gateway->destroy(gateway);
+                                               /* fall-through */
+                                       }
                                        case REDIRECT_SUPPORTED:
-                                               this->ike_sa->enable_extension(this->ike_sa,
-                                                                                                          EXT_IKE_REDIRECTION);
+                                               if (!this->old_sa)
+                                               {
+                                                       this->ike_sa->enable_extension(this->ike_sa,
+                                                                                                                  EXT_IKE_REDIRECTION);
+                                               }
+                                               break;
+                                       case CHILDLESS_IKEV2_SUPPORTED:
+                                               if (this->initiator && !this->old_sa)
+                                               {
+                                                       this->ike_sa->enable_extension(this->ike_sa,
+                                                                                                                  EXT_IKE_CHILDLESS);
+                                               }
                                                break;
                                        default:
                                                /* other notifies are handled elsewhere */
@@ -417,6 +610,11 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
        }
        enumerator->destroy(enumerator);
 
+       if (this->proposal)
+       {
+               this->ike_sa->set_proposal(this->ike_sa, this->proposal);
+       }
+
        if (ke_payload && this->proposal &&
                this->proposal->has_dh_group(this->proposal, this->dh_group))
        {
@@ -425,7 +623,11 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
                        this->dh = this->keymat->keymat.create_dh(
                                                                &this->keymat->keymat, this->dh_group);
                }
-               if (this->dh)
+               else if (this->dh)
+               {
+                       this->dh_failed = this->dh->get_dh_group(this->dh) != this->dh_group;
+               }
+               if (this->dh && !this->dh_failed)
                {
                        this->dh_failed = !this->dh->set_other_public_value(this->dh,
                                                                ke_payload->get_key_exchange_data(ke_payload));
@@ -436,7 +638,10 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
 METHOD(task_t, build_i, status_t,
        private_ike_init_t *this, message_t *message)
 {
-       this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
+       ike_cfg_t *ike_cfg;
+
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+
        DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H",
                 this->ike_sa->get_name(this->ike_sa),
                 this->ike_sa->get_unique_id(this->ike_sa),
@@ -449,10 +654,30 @@ METHOD(task_t, build_i, status_t,
                return FAILED;
        }
 
-       /* if the DH group is set via use_dh_group(), we already have a DH object */
+       /* if we are retrying after an INVALID_KE_PAYLOAD we already have one */
        if (!this->dh)
        {
-               this->dh_group = this->config->get_dh_group(this->config);
+               if (this->old_sa && lib->settings->get_bool(lib->settings,
+                                                               "%s.prefer_previous_dh_group", TRUE, lib->ns))
+               {       /* reuse the DH group we used for the old IKE_SA when rekeying */
+                       proposal_t *proposal;
+                       uint16_t dh_group;
+
+                       proposal = this->old_sa->get_proposal(this->old_sa);
+                       if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
+                                                                               &dh_group, NULL))
+                       {
+                               this->dh_group = dh_group;
+                       }
+                       else
+                       {       /* this shouldn't happen, but let's be safe */
+                               this->dh_group = ike_cfg->get_dh_group(ike_cfg);
+                       }
+               }
+               else
+               {
+                       this->dh_group = ike_cfg->get_dh_group(ike_cfg);
+               }
                this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
                                                                                                  this->dh_group);
                if (!this->dh)
@@ -462,6 +687,18 @@ METHOD(task_t, build_i, status_t,
                        return FAILED;
                }
        }
+       else if (this->dh->get_dh_group(this->dh) != this->dh_group)
+       {       /* reset DH instance if group changed (INVALID_KE_PAYLOAD) */
+               this->dh->destroy(this->dh);
+               this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
+                                                                                                 this->dh_group);
+               if (!this->dh)
+               {
+                       DBG1(DBG_IKE, "requested DH group %N not supported",
+                                diffie_hellman_group_names, this->dh_group);
+                       return FAILED;
+               }
+       }
 
        /* generate nonce only when we are trying the first time */
        if (this->my_nonce.ptr == NULL)
@@ -498,7 +735,6 @@ METHOD(task_t, build_i, status_t,
 METHOD(task_t, process_r,  status_t,
        private_ike_init_t *this, message_t *message)
 {
-       this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
        DBG0(DBG_IKE, "%H is initiating an IKE_SA", message->get_source(message));
        this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
 
@@ -557,7 +793,7 @@ static bool derive_keys(private_ike_init_t *this,
                return FALSE;
        }
        charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, chunk_empty,
-                                                 nonce_i, nonce_r, this->old_sa, NULL);
+                                                 nonce_i, nonce_r, this->old_sa, NULL, AUTH_NONE);
        return TRUE;
 }
 
@@ -570,14 +806,14 @@ METHOD(task_t, build_r, status_t,
        if (this->proposal == NULL ||
                this->other_nonce.len == 0 || this->my_nonce.len == 0)
        {
-               DBG1(DBG_IKE, "received proposals inacceptable");
+               DBG1(DBG_IKE, "received proposals unacceptable");
                message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
                return FAILED;
        }
-       this->ike_sa->set_proposal(this->ike_sa, this->proposal);
 
        /* check if we'd have to redirect the client */
-       if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
+       if (!this->old_sa &&
+               this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
                charon->redirect->redirect_on_init(charon->redirect, this->ike_sa,
                                                                                   &gateway))
        {
@@ -594,12 +830,12 @@ METHOD(task_t, build_r, status_t,
        if (this->dh == NULL ||
                !this->proposal->has_dh_group(this->proposal, this->dh_group))
        {
-               u_int16_t group;
+               uint16_t group;
 
                if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
                                                                                  &group, NULL))
                {
-                       DBG1(DBG_IKE, "DH group %N inacceptable, requesting %N",
+                       DBG1(DBG_IKE, "DH group %N unacceptable, requesting %N",
                                 diffie_hellman_group_names, this->dh_group,
                                 diffie_hellman_group_names, group);
                        this->dh_group = group;
@@ -610,6 +846,7 @@ METHOD(task_t, build_r, status_t,
                else
                {
                        DBG1(DBG_IKE, "no acceptable proposal found");
+                       message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
                }
                return FAILED;
        }
@@ -640,12 +877,14 @@ METHOD(task_t, build_r, status_t,
  */
 static void raise_alerts(private_ike_init_t *this, notify_type_t type)
 {
+       ike_cfg_t *ike_cfg;
        linked_list_t *list;
 
        switch (type)
        {
                case NO_PROPOSAL_CHOSEN:
-                       list = this->config->get_proposals(this->config);
+                       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+                       list = ike_cfg->get_proposals(ike_cfg);
                        charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, list);
                        list->destroy_offset(list, offsetof(proposal_t, destroy));
                        break;
@@ -654,6 +893,68 @@ static void raise_alerts(private_ike_init_t *this, notify_type_t type)
        }
 }
 
+METHOD(task_t, pre_process_i, status_t,
+       private_ike_init_t *this, message_t *message)
+{
+       enumerator_t *enumerator;
+       payload_t *payload;
+
+       /* check for erroneous notifies */
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               if (payload->get_type(payload) == PLV2_NOTIFY)
+               {
+                       notify_payload_t *notify = (notify_payload_t*)payload;
+                       notify_type_t type = notify->get_notify_type(notify);
+
+                       switch (type)
+                       {
+                               case COOKIE:
+                               {
+                                       chunk_t cookie;
+
+                                       cookie = notify->get_notification_data(notify);
+                                       if (chunk_equals(cookie, this->cookie))
+                                       {
+                                               DBG1(DBG_IKE, "ignore response with duplicate COOKIE "
+                                                        "notify");
+                                               enumerator->destroy(enumerator);
+                                               return FAILED;
+                                       }
+                                       break;
+                               }
+                               case REDIRECT:
+                               {
+                                       identification_t *gateway;
+                                       chunk_t data, nonce = chunk_empty;
+                                       status_t status = SUCCESS;
+
+                                       if (this->old_sa)
+                                       {
+                                               break;
+                                       }
+                                       data = notify->get_notification_data(notify);
+                                       gateway = redirect_data_parse(data, &nonce);
+                                       if (!gateway || !chunk_equals(nonce, this->my_nonce))
+                                       {
+                                               DBG1(DBG_IKE, "received invalid REDIRECT notify");
+                                               status = FAILED;
+                                       }
+                                       DESTROY_IF(gateway);
+                                       chunk_free(&nonce);
+                                       enumerator->destroy(enumerator);
+                                       return status;
+                               }
+                               default:
+                                       break;
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       return SUCCESS;
+}
+
 METHOD(task_t, process_i, status_t,
        private_ike_init_t *this, message_t *message)
 {
@@ -678,14 +979,14 @@ METHOD(task_t, process_i, status_t,
 
                                        bad_group = this->dh_group;
                                        data = notify->get_notification_data(notify);
-                                       this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+                                       this->dh_group = ntohs(*((uint16_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);
 
                                        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, FALSE);
                                        }
 
                                        enumerator->destroy(enumerator);
@@ -703,7 +1004,7 @@ METHOD(task_t, process_i, status_t,
                                {
                                        chunk_free(&this->cookie);
                                        this->cookie = chunk_clone(notify->get_notification_data(notify));
-                                       this->ike_sa->reset(this->ike_sa);
+                                       this->ike_sa->reset(this->ike_sa, FALSE);
                                        enumerator->destroy(enumerator);
                                        DBG2(DBG_IKE, "received %N notify", notify_type_names, type);
                                        this->retry++;
@@ -715,20 +1016,21 @@ METHOD(task_t, process_i, status_t,
                                        chunk_t data, nonce = chunk_empty;
                                        status_t status = FAILED;
 
-                                       data = notify->get_notification_data(notify);
-                                       gateway = redirect_data_parse(data, &nonce);
-                                       enumerator->destroy(enumerator);
-                                       if (!gateway || !chunk_equals(nonce, this->my_nonce))
+                                       if (this->old_sa)
                                        {
-                                               DBG1(DBG_IKE, "received invalid REDIRECT notify");
+                                               DBG1(DBG_IKE, "received REDIRECT notify during rekeying"
+                                                    ", ignored");
+                                               break;
                                        }
-                                       else if (this->ike_sa->handle_redirect(this->ike_sa,
-                                                                                                                       gateway))
+                                       data = notify->get_notification_data(notify);
+                                       gateway = redirect_data_parse(data, &nonce);
+                                       if (this->ike_sa->handle_redirect(this->ike_sa, gateway))
                                        {
                                                status = NEED_MORE;
                                        }
                                        DESTROY_IF(gateway);
                                        chunk_free(&nonce);
+                                       enumerator->destroy(enumerator);
                                        return status;
                                }
                                default:
@@ -759,7 +1061,6 @@ METHOD(task_t, process_i, status_t,
                DBG1(DBG_IKE, "peers proposal selection invalid");
                return FAILED;
        }
-       this->ike_sa->set_proposal(this->ike_sa, this->proposal);
 
        if (this->dh == NULL ||
                !this->proposal->has_dh_group(this->proposal, this->dh_group))
@@ -798,12 +1099,6 @@ METHOD(task_t, migrate, void,
        this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
        this->proposal = NULL;
        this->dh_failed = FALSE;
-       if (this->dh && this->dh->get_dh_group(this->dh) != this->dh_group)
-       {       /* reset DH value only if group changed (INVALID_KE_PAYLOAD) */
-               this->dh->destroy(this->dh);
-               this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
-                                                                                                 this->dh_group);
-       }
 }
 
 METHOD(task_t, destroy, void,
@@ -864,6 +1159,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
        {
                this->public.task.build = _build_i;
                this->public.task.process = _process_i;
+               this->public.task.pre_process = _pre_process_i;
        }
        else
        {