ike-auth: Handle REDIRECT notifies during IKE_AUTH
[strongswan.git] / src / libcharon / sa / ikev2 / tasks / ike_auth.c
index 7d462f1..79a436f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -12,7 +12,7 @@
  * This program is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details
+ * for more details.
  */
 
 #include "ike_auth.h"
@@ -25,6 +25,7 @@
 #include <encoding/payloads/eap_payload.h>
 #include <encoding/payloads/nonce_payload.h>
 #include <sa/ikev2/authenticators/eap_authenticator.h>
+#include <processing/jobs/delete_ike_sa_job.h>
 
 typedef struct private_ike_auth_t private_ike_auth_t;
 
@@ -112,6 +113,16 @@ struct private_ike_auth_t {
         * received an INITIAL_CONTACT?
         */
        bool initial_contact;
+
+       /**
+        * Is EAP acceptable, did we strictly authenticate peer?
+        */
+       bool eap_acceptable;
+
+       /**
+        * Gateway ID if redirected
+        */
+       identification_t *redirect_to;
 };
 
 /**
@@ -120,7 +131,7 @@ struct private_ike_auth_t {
 static bool multiple_auth_enabled()
 {
        return lib->settings->get_bool(lib->settings,
-                                                       "%s.multiple_authentication", TRUE, charon->name);
+                                                                  "%s.multiple_authentication", TRUE, lib->ns);
 }
 
 /**
@@ -132,7 +143,7 @@ static status_t collect_my_init_data(private_ike_auth_t *this,
        nonce_payload_t *nonce;
 
        /* get the nonce that was generated in ike_init */
-       nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+       nonce = (nonce_payload_t*)message->get_payload(message, PLV2_NONCE);
        if (nonce == NULL)
        {
                return FAILED;
@@ -158,7 +169,7 @@ static status_t collect_other_init_data(private_ike_auth_t *this,
        nonce_payload_t *nonce;
 
        /* get the nonce that was generated in ike_init */
-       nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+       nonce = (nonce_payload_t*)message->get_payload(message, PLV2_NONCE);
        if (nonce == NULL)
        {
                return FAILED;
@@ -223,6 +234,18 @@ static auth_cfg_t *get_auth_cfg(private_ike_auth_t *this, bool local)
 }
 
 /**
+ * Move the currently active auth config to the auth configs completed
+ */
+static void apply_auth_cfg(private_ike_auth_t *this, bool local)
+{
+       auth_cfg_t *cfg;
+
+       cfg = auth_cfg_create();
+       cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, local), local);
+       this->ike_sa->add_auth_cfg(this->ike_sa, local, cfg);
+}
+
+/**
  * Check if we have should initiate another authentication round
  */
 static bool do_another_auth(private_ike_auth_t *this)
@@ -307,7 +330,7 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict)
        {
                if (this->peer_cfg)
                {
-                       bool complies = TRUE;
+                       char *comply_error = NULL;
                        enumerator_t *e1, *e2, *tmp;
                        auth_cfg_t *c1, *c2;
 
@@ -324,22 +347,30 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict)
                        while (e1->enumerate(e1, &c1))
                        {
                                /* check if done authentications comply to configured ones */
-                               if ((!e2->enumerate(e2, &c2)) ||
-                                       (!strict && !c1->complies(c1, c2, TRUE)) ||
-                                       (strict && !c2->complies(c2, c1, TRUE)))
+                               if (!e2->enumerate(e2, &c2))
+                               {
+                                       comply_error = "insufficient authentication rounds";
+                                       break;
+                               }
+                               if (!strict && !c1->complies(c1, c2, TRUE))
+                               {
+                                       comply_error = "non-matching authentication done";
+                                       break;
+                               }
+                               if (strict && !c2->complies(c2, c1, TRUE))
                                {
-                                       complies = FALSE;
+                                       comply_error = "constraint checking failed";
                                        break;
                                }
                        }
                        e1->destroy(e1);
                        e2->destroy(e2);
-                       if (complies)
+                       if (!comply_error)
                        {
                                break;
                        }
-                       DBG1(DBG_CFG, "selected peer config '%s' inacceptable",
-                                this->peer_cfg->get_name(this->peer_cfg));
+                       DBG1(DBG_CFG, "selected peer config '%s' inacceptable: %s",
+                                this->peer_cfg->get_name(this->peer_cfg), comply_error);
                        this->peer_cfg->destroy(this->peer_cfg);
                }
                if (this->candidates->remove_first(this->candidates,
@@ -408,11 +439,12 @@ METHOD(task_t, build_i, status_t,
                if (cfg)
                {
                        idr = cfg->get(cfg, AUTH_RULE_IDENTITY);
-                       if (idr && !idr->contains_wildcards(idr))
+                       if (!cfg->get(cfg, AUTH_RULE_IDENTITY_LOOSE) && idr &&
+                               !idr->contains_wildcards(idr))
                        {
                                this->ike_sa->set_other_id(this->ike_sa, idr->clone(idr));
                                id_payload = id_payload_create_from_identification(
-                                                                                                                       ID_RESPONDER, idr);
+                                                                                                                       PLV2_ID_RESPONDER, idr);
                                message->add_payload(message, (payload_t*)id_payload);
                        }
                }
@@ -430,7 +462,7 @@ METHOD(task_t, build_i, status_t,
                        cfg->add(cfg, AUTH_RULE_IDENTITY, idi);
                }
                this->ike_sa->set_my_id(this->ike_sa, idi->clone(idi));
-               id_payload = id_payload_create_from_identification(ID_INITIATOR, idi);
+               id_payload = id_payload_create_from_identification(PLV2_ID_INITIATOR, idi);
                get_reserved_id_bytes(this, id_payload);
                message->add_payload(message, (payload_t*)id_payload);
 
@@ -456,29 +488,28 @@ METHOD(task_t, build_i, status_t,
                                                        this->reserved);
                if (!this->my_auth)
                {
+                       charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED);
                        return FAILED;
                }
        }
        switch (this->my_auth->build(this->my_auth, message))
        {
                case SUCCESS:
-                       /* authentication step complete, reset authenticator */
-                       cfg = auth_cfg_create();
-                       cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE), TRUE);
-                       this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg);
+                       apply_auth_cfg(this, TRUE);
                        this->my_auth->destroy(this->my_auth);
                        this->my_auth = NULL;
                        break;
                case NEED_MORE:
                        break;
                default:
+                       charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED);
                        return FAILED;
        }
 
        /* check for additional authentication rounds */
        if (do_another_auth(this))
        {
-               if (message->get_payload(message, AUTHENTICATION))
+               if (message->get_payload(message, PLV2_AUTH))
                {
                        message->add_notify(message, FALSE, ANOTHER_AUTH_FOLLOWS, chunk_empty);
                }
@@ -505,7 +536,7 @@ METHOD(task_t, process_r, status_t,
        if (this->my_auth == NULL && this->do_another_auth)
        {
                /* handle (optional) IDr payload, apply proposed identity */
-               id_payload = (id_payload_t*)message->get_payload(message, ID_RESPONDER);
+               id_payload = (id_payload_t*)message->get_payload(message, PLV2_ID_RESPONDER);
                if (id_payload)
                {
                        id = id_payload->get_identification(id_payload);
@@ -538,7 +569,7 @@ METHOD(task_t, process_r, status_t,
        if (this->other_auth == NULL)
        {
                /* handle IDi payload */
-               id_payload = (id_payload_t*)message->get_payload(message, ID_INITIATOR);
+               id_payload = (id_payload_t*)message->get_payload(message, PLV2_ID_INITIATOR);
                if (!id_payload)
                {
                        DBG1(DBG_IKE, "IDi payload missing");
@@ -558,7 +589,7 @@ METHOD(task_t, process_r, status_t,
                                return NEED_MORE;
                        }
                }
-               if (message->get_payload(message, AUTHENTICATION) == NULL)
+               if (message->get_payload(message, PLV2_AUTH) == NULL)
                {       /* before authenticating with EAP, we need a EAP config */
                        cand = get_auth_cfg(this, FALSE);
                        while (!cand || (
@@ -611,7 +642,7 @@ METHOD(task_t, process_r, status_t,
                        this->other_auth = NULL;
                        break;
                case NEED_MORE:
-                       if (message->get_payload(message, AUTHENTICATION))
+                       if (message->get_payload(message, PLV2_AUTH))
                        {       /* AUTH verification successful, but another build() needed */
                                break;
                        }
@@ -637,10 +668,7 @@ METHOD(task_t, process_r, status_t,
                return NEED_MORE;
        }
 
-       /* store authentication information */
-       cfg = auth_cfg_create();
-       cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE);
-       this->ike_sa->add_auth_cfg(this->ike_sa, FALSE, cfg);
+       apply_auth_cfg(this, FALSE);
 
        if (!update_cfg_candidates(this, FALSE))
        {
@@ -663,6 +691,7 @@ METHOD(task_t, process_r, status_t,
 METHOD(task_t, build_r, status_t,
        private_ike_auth_t *this, message_t *message)
 {
+       identification_t *gateway;
        auth_cfg_t *cfg;
 
        if (message->get_exchange_type(message) == IKE_SA_INIT)
@@ -716,7 +745,7 @@ METHOD(task_t, build_r, status_t,
                        }
                }
 
-               id_payload = id_payload_create_from_identification(ID_RESPONDER, id);
+               id_payload = id_payload_create_from_identification(PLV2_ID_RESPONDER, id);
                get_reserved_id_bytes(this, id_payload);
                message->add_payload(message, (payload_t*)id_payload);
 
@@ -747,7 +776,7 @@ METHOD(task_t, build_r, status_t,
                                                                this->reserved);
                        if (!this->my_auth)
                        {
-                               goto peer_auth_failed;
+                               goto local_auth_failed;
                        }
                }
        }
@@ -763,7 +792,7 @@ METHOD(task_t, build_r, status_t,
                        case NEED_MORE:
                                break;
                        default:
-                               if (message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
+                               if (message->get_payload(message, PLV2_EAP))
                                {       /* skip AUTHENTICATION_FAILED if we have EAP_FAILURE */
                                        goto peer_auth_failed_no_notify;
                                }
@@ -775,19 +804,14 @@ METHOD(task_t, build_r, status_t,
                switch (this->my_auth->build(this->my_auth, message))
                {
                        case SUCCESS:
-                               cfg = auth_cfg_create();
-                               cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE),
-                                                  TRUE);
-                               this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg);
+                               apply_auth_cfg(this, TRUE);
                                this->my_auth->destroy(this->my_auth);
                                this->my_auth = NULL;
                                break;
                        case NEED_MORE:
                                break;
                        default:
-                               message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
-                                                                       chunk_empty);
-                               return FAILED;
+                               goto local_auth_failed;
                }
        }
 
@@ -800,40 +824,124 @@ METHOD(task_t, build_r, status_t,
        {
                this->do_another_auth = FALSE;
        }
-       if (!this->do_another_auth && !this->expect_another_auth)
+       if (this->do_another_auth || this->expect_another_auth)
        {
-               if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
-                                                                                                        this->ike_sa, FALSE))
-               {
-                       DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy");
-                       message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
-                                                               chunk_empty);
-                       return FAILED;
-               }
-               if (!charon->bus->authorize(charon->bus, TRUE))
-               {
-                       DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
-                       goto peer_auth_failed;
-               }
-               DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
-                        this->ike_sa->get_name(this->ike_sa),
-                        this->ike_sa->get_unique_id(this->ike_sa),
-                        this->ike_sa->get_my_host(this->ike_sa),
-                        this->ike_sa->get_my_id(this->ike_sa),
-                        this->ike_sa->get_other_host(this->ike_sa),
-                        this->ike_sa->get_other_id(this->ike_sa));
-               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-               charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
-               return SUCCESS;
+               return NEED_MORE;
        }
-       return NEED_MORE;
+
+       if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+                                                                                                this->ike_sa, FALSE))
+       {
+               DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy");
+               charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
+               message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
+                                                       chunk_empty);
+               return FAILED;
+       }
+       if (!charon->bus->authorize(charon->bus, TRUE))
+       {
+               DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
+               goto peer_auth_failed;
+       }
+       if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
+               charon->redirect->redirect_on_auth(charon->redirect, this->ike_sa,
+                                                                                  &gateway))
+       {
+               delete_ike_sa_job_t *job;
+               chunk_t data;
+
+               DBG1(DBG_IKE, "redirecting peer to %Y", gateway);
+               data = redirect_data_create(gateway, chunk_empty);
+               message->add_notify(message, FALSE, REDIRECT, data);
+               gateway->destroy(gateway);
+               chunk_free(&data);
+               /* we use this condition to prevent the CHILD_SA from getting created */
+               this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE);
+               /* if the peer does not delete the SA we do so after a while */
+               job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE);
+               lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
+                                               lib->settings->get_int(lib->settings,
+                                                       "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
+                                                       lib->ns));
+       }
+       DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
+                this->ike_sa->get_name(this->ike_sa),
+                this->ike_sa->get_unique_id(this->ike_sa),
+                this->ike_sa->get_my_host(this->ike_sa),
+                this->ike_sa->get_my_id(this->ike_sa),
+                this->ike_sa->get_other_host(this->ike_sa),
+                this->ike_sa->get_other_id(this->ike_sa));
+       this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+       charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
+       return SUCCESS;
 
 peer_auth_failed:
-       message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
-                                               chunk_empty);
+       message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
 peer_auth_failed_no_notify:
        charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED);
        return FAILED;
+local_auth_failed:
+       message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+       charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED);
+       return FAILED;
+}
+
+/**
+ * Send an INFORMATIONAL message with an AUTH_FAILED before closing IKE_SA
+ */
+static void send_auth_failed_informational(private_ike_auth_t *this,
+                                                                                  message_t *reply)
+{
+       message_t *message;
+       packet_t *packet;
+       host_t *host;
+
+       message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+       message->set_message_id(message, reply->get_message_id(reply) + 1);
+       host = this->ike_sa->get_my_host(this->ike_sa);
+       message->set_source(message, host->clone(host));
+       host = this->ike_sa->get_other_host(this->ike_sa);
+       message->set_destination(message, host->clone(host));
+       message->set_exchange_type(message, INFORMATIONAL);
+       message->add_notify(message, FALSE, AUTHENTICATION_FAILED, chunk_empty);
+
+       if (this->ike_sa->generate_message(this->ike_sa, message,
+                                                                          &packet) == SUCCESS)
+       {
+               charon->sender->send(charon->sender, packet);
+       }
+       message->destroy(message);
+}
+
+/**
+ * Check if strict constraint fullfillment required to continue current auth
+ */
+static bool require_strict(private_ike_auth_t *this, bool mutual_eap)
+{
+       auth_cfg_t *cfg;
+
+       if (this->eap_acceptable)
+       {
+               return FALSE;
+       }
+
+       cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+       switch ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS))
+       {
+               case AUTH_CLASS_EAP:
+                       if (mutual_eap && this->my_auth)
+                       {
+                               this->eap_acceptable = TRUE;
+                               return !this->my_auth->is_mutual(this->my_auth);
+                       }
+                       return TRUE;
+               case AUTH_CLASS_PSK:
+                       return TRUE;
+               case AUTH_CLASS_PUBKEY:
+               case AUTH_CLASS_ANY:
+               default:
+                       return FALSE;
+       }
 }
 
 METHOD(task_t, process_i, status_t,
@@ -857,7 +965,7 @@ METHOD(task_t, process_i, status_t,
        enumerator = message->create_payload_enumerator(message);
        while (enumerator->enumerate(enumerator, &payload))
        {
-               if (payload->get_type(payload) == NOTIFY)
+               if (payload->get_type(payload) == PLV2_NOTIFY)
                {
                        notify_payload_t *notify = (notify_payload_t*)payload;
                        notify_type_t type = notify->get_notify_type(notify);
@@ -885,6 +993,15 @@ METHOD(task_t, process_i, status_t,
                                case ME_ENDPOINT:
                                        /* handled in ike_me task */
                                        break;
+                               case REDIRECT:
+                                       DESTROY_IF(this->redirect_to);
+                                       this->redirect_to = redirect_data_parse(
+                                                               notify->get_notification_data(notify), NULL);
+                                       if (!this->redirect_to)
+                                       {
+                                               DBG1(DBG_IKE, "received invalid REDIRECT notify");
+                                       }
+                                       break;
                                default:
                                {
                                        if (type <= 16383)
@@ -892,6 +1009,7 @@ METHOD(task_t, process_i, status_t,
                                                DBG1(DBG_IKE, "received %N notify error",
                                                         notify_type_names, type);
                                                enumerator->destroy(enumerator);
+                                               charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED);
                                                return FAILED;
                                        }
                                        DBG2(DBG_IKE, "received %N notify",
@@ -912,7 +1030,7 @@ METHOD(task_t, process_i, status_t,
 
                        /* handle IDr payload */
                        id_payload = (id_payload_t*)message->get_payload(message,
-                                                                                                                        ID_RESPONDER);
+                                                                                                                        PLV2_ID_RESPONDER);
                        if (!id_payload)
                        {
                                DBG1(DBG_IKE, "IDr payload missing");
@@ -924,7 +1042,7 @@ METHOD(task_t, process_i, status_t,
                        cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
                        cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id));
 
-                       if (message->get_payload(message, AUTHENTICATION))
+                       if (message->get_payload(message, PLV2_AUTH))
                        {
                                /* verify authentication data */
                                this->other_auth = authenticator_create_verifier(this->ike_sa,
@@ -964,10 +1082,18 @@ METHOD(task_t, process_i, status_t,
                        goto peer_auth_failed;
                }
 
-               /* store authentication information, reset authenticator */
-               cfg = auth_cfg_create();
-               cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE);
-               this->ike_sa->add_auth_cfg(this->ike_sa, FALSE, cfg);
+               if (!mutual_eap)
+               {
+                       apply_auth_cfg(this, FALSE);
+               }
+       }
+
+       if (require_strict(this, mutual_eap))
+       {
+               if (!update_cfg_candidates(this, TRUE))
+               {
+                       goto peer_auth_failed;
+               }
        }
 
        if (this->my_auth)
@@ -975,10 +1101,11 @@ METHOD(task_t, process_i, status_t,
                switch (this->my_auth->process(this->my_auth, message))
                {
                        case SUCCESS:
-                               cfg = auth_cfg_create();
-                               cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE),
-                                                  TRUE);
-                               this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg);
+                               apply_auth_cfg(this, TRUE);
+                               if (this->my_auth->is_mutual(this->my_auth))
+                               {
+                                       apply_auth_cfg(this, FALSE);
+                               }
                                this->my_auth->destroy(this->my_auth);
                                this->my_auth = NULL;
                                this->do_another_auth = do_another_auth(this);
@@ -986,6 +1113,8 @@ METHOD(task_t, process_i, status_t,
                        case NEED_MORE:
                                break;
                        default:
+                               charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED);
+                               send_auth_failed_informational(this, message);
                                return FAILED;
                }
        }
@@ -1003,33 +1132,39 @@ METHOD(task_t, process_i, status_t,
        {
                this->expect_another_auth = FALSE;
        }
-       if (!this->expect_another_auth && !this->do_another_auth && !this->my_auth)
+       if (this->expect_another_auth || this->do_another_auth || this->my_auth)
        {
-               if (!update_cfg_candidates(this, TRUE))
-               {
-                       goto peer_auth_failed;
-               }
-               if (!charon->bus->authorize(charon->bus, TRUE))
-               {
-                       DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, "
-                                             "cancelling");
-                       goto peer_auth_failed;
-               }
-               DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
-                        this->ike_sa->get_name(this->ike_sa),
-                        this->ike_sa->get_unique_id(this->ike_sa),
-                        this->ike_sa->get_my_host(this->ike_sa),
-                        this->ike_sa->get_my_id(this->ike_sa),
-                        this->ike_sa->get_other_host(this->ike_sa),
-                        this->ike_sa->get_other_id(this->ike_sa));
-               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-               charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
-               return SUCCESS;
+               return NEED_MORE;
        }
-       return NEED_MORE;
+       if (!update_cfg_candidates(this, TRUE))
+       {
+               goto peer_auth_failed;
+       }
+       if (!charon->bus->authorize(charon->bus, TRUE))
+       {
+               DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, "
+                                     "cancelling");
+               goto peer_auth_failed;
+       }
+       DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
+                this->ike_sa->get_name(this->ike_sa),
+                this->ike_sa->get_unique_id(this->ike_sa),
+                this->ike_sa->get_my_host(this->ike_sa),
+                this->ike_sa->get_my_id(this->ike_sa),
+                this->ike_sa->get_other_host(this->ike_sa),
+                this->ike_sa->get_other_id(this->ike_sa));
+       this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+       charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
+
+       if (this->redirect_to)
+       {
+               this->ike_sa->handle_redirect(this->ike_sa, this->redirect_to);
+       }
+       return SUCCESS;
 
 peer_auth_failed:
        charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED);
+       send_auth_failed_informational(this, message);
        return FAILED;
 }
 
@@ -1049,6 +1184,7 @@ METHOD(task_t, migrate, void,
        DESTROY_IF(this->peer_cfg);
        DESTROY_IF(this->my_auth);
        DESTROY_IF(this->other_auth);
+       DESTROY_IF(this->redirect_to);
        this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
 
        this->my_packet = NULL;
@@ -1057,6 +1193,7 @@ METHOD(task_t, migrate, void,
        this->peer_cfg = NULL;
        this->my_auth = NULL;
        this->other_auth = NULL;
+       this->redirect_to = NULL;
        this->do_another_auth = TRUE;
        this->expect_another_auth = TRUE;
        this->authentication_failed = FALSE;
@@ -1073,6 +1210,7 @@ METHOD(task_t, destroy, void,
        DESTROY_IF(this->my_auth);
        DESTROY_IF(this->other_auth);
        DESTROY_IF(this->peer_cfg);
+       DESTROY_IF(this->redirect_to);
        this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
        free(this);
 }