]> git.ipfire.org Git - people/ms/strongswan.git/blobdiff - src/charon/sa/tasks/ike_auth.c
Store completed authentication rounds permanently on IKE_SA, with flush option
[people/ms/strongswan.git] / src / charon / sa / tasks / ike_auth.c
index e81877f046eb501680233b712fb7f0b40606eb73..7714b28ce91e8ab071c0ba097e6c1b4cca942d87 100644 (file)
@@ -1,12 +1,5 @@
-/**
- * @file ike_auth.c
- *
- * @brief Implementation of the ike_auth task.
- *
- */
-
 /*
- * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
  *
@@ -18,7 +11,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"
 #include <string.h>
 
 #include <daemon.h>
-#include <crypto/diffie_hellman.h>
 #include <encoding/payloads/id_payload.h>
 #include <encoding/payloads/auth_payload.h>
 #include <encoding/payloads/eap_payload.h>
 #include <encoding/payloads/nonce_payload.h>
 #include <sa/authenticators/eap_authenticator.h>
 
-
-
 typedef struct private_ike_auth_t private_ike_auth_t;
 
 /**
  * Private members of a ike_auth_t task.
  */
 struct private_ike_auth_t {
-       
+
        /**
         * Public methods and task_t interface.
         */
        ike_auth_t public;
-       
+
        /**
         * Assigned IKE_SA.
         */
        ike_sa_t *ike_sa;
-       
+
        /**
         * Are we the initiator?
         */
        bool initiator;
-       
+
        /**
         * Nonce chosen by us in ike_init
         */
        chunk_t my_nonce;
-       
+
        /**
         * Nonce chosen by peer in ike_init
         */
        chunk_t other_nonce;
-       
+
        /**
         * IKE_SA_INIT message sent by us
         */
        packet_t *my_packet;
-       
+
        /**
         * IKE_SA_INIT message sent by peer
         */
        packet_t *other_packet;
-       
+
        /**
-        * EAP authenticator when using EAP
+        * currently active authenticator, to authenticate us
         */
-       eap_authenticator_t *eap_auth;
-       
+       authenticator_t *my_auth;
+
        /**
-        * EAP payload received and ready to process
+        * currently active authenticator, to authenticate peer
         */
-       eap_payload_t *eap_payload;
-       
+       authenticator_t *other_auth;
+
        /**
-        * has the peer been authenticated successfully?
+        * peer_cfg candidates, ordered by priority
         */
-       bool peer_authenticated;
-};
+       linked_list_t *candidates;
 
-/**
- * build the AUTH payload
- */
-static status_t build_auth(private_ike_auth_t *this, message_t *message)
-{
-       authenticator_t *auth;
-       auth_payload_t *auth_payload;
-       peer_cfg_t *config;
-       auth_method_t method;
-       status_t status;
-       
-       /* create own authenticator and add auth payload */
-       config = this->ike_sa->get_peer_cfg(this->ike_sa);
-       if (!config)
-       {
-               SIG(IKE_UP_FAILED, "unable to authenticate, no peer config found");
-               return FAILED;
-       }
-       method = config->get_auth_method(config);
-       
-       auth = authenticator_create(this->ike_sa, method);
-       if (auth == NULL)
-       {
-               SIG(IKE_UP_FAILED, "configured authentication method %N not supported",
-                       auth_method_names, method);
-               return FAILED;
-       }
-       
-       status = auth->build(auth, this->my_packet->get_data(this->my_packet),
-                                                this->other_nonce, &auth_payload);
-       auth->destroy(auth);
-       if (status != SUCCESS)
-       {
-               SIG(IKE_UP_FAILED, "generating authentication data failed");
-               return FAILED;
-       }
-       message->add_payload(message, (payload_t*)auth_payload);
-       return SUCCESS;
-}
-
-/**
- * build ID payload(s)
- */
-static status_t build_id(private_ike_auth_t *this, message_t *message)
-{
-       identification_t *me, *other;
-       id_payload_t *id;
-       peer_cfg_t *config;
-       
-       me = this->ike_sa->get_my_id(this->ike_sa);
-       other = this->ike_sa->get_other_id(this->ike_sa);
-       config = this->ike_sa->get_peer_cfg(this->ike_sa);
-       
-       if (me->contains_wildcards(me))
-       {
-               me = config->get_my_id(config);
-               if (me->contains_wildcards(me))
-               {
-                       SIG(IKE_UP_FAILED, "negotiation of own ID failed");
-                       return FAILED;
-               }
-               this->ike_sa->set_my_id(this->ike_sa, me->clone(me));
-       }
-       
-       id = id_payload_create_from_identification(this->initiator, me);
-       message->add_payload(message, (payload_t*)id);
-       
-       /* as initiator, include other ID if it does not contain wildcards */
-       if (this->initiator && !other->contains_wildcards(other))
-       {
-               id = id_payload_create_from_identification(FALSE, other);
-               message->add_payload(message, (payload_t*)id);
-       }
-       return SUCCESS;
-}
+       /**
+        * selected peer config (might change when using multiple authentications)
+        */
+       peer_cfg_t *peer_cfg;
 
-/**
- * process AUTH payload
- */
-static status_t process_auth(private_ike_auth_t *this, message_t *message)
-{
-       auth_payload_t *auth_payload;
-       authenticator_t *auth;
-       auth_method_t auth_method;
-       status_t status;
-       
-       auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION);
-       
-       if (auth_payload == NULL)
-       {
-               /* AUTH payload is missing, client wants to use EAP authentication */
-               return NOT_FOUND;
-       }
+       /**
+        * have we planned an(other) authentication exchange?
+        */
+       bool do_another_auth;
 
-       auth_method = auth_payload->get_auth_method(auth_payload);
-       auth = authenticator_create(this->ike_sa, auth_method);
+       /**
+        * has the peer announced another authentication exchange?
+        */
+       bool expect_another_auth;
 
-       if (auth == NULL)
-       {
-               SIG(IKE_UP_FAILED, "authentication method %N used by %D not "
-                       "supported", auth_method_names, auth_method,
-                       this->ike_sa->get_other_id(this->ike_sa));
-               return NOT_SUPPORTED;
-       }
-       status = auth->verify(auth, this->other_packet->get_data(this->other_packet), 
-                                                 this->my_nonce, auth_payload);
-       auth->destroy(auth);
-       if (status != SUCCESS)
-       {
-               SIG(IKE_UP_FAILED, "authentication of '%D' with %N failed",
-                        this->ike_sa->get_other_id(this->ike_sa), 
-                        auth_method_names, auth_method);       
-               return FAILED;
-       }
-       return SUCCESS;
-}
+       /**
+        * should we send a AUTHENTICATION_FAILED notify?
+        */
+       bool authentication_failed;
+};
 
 /**
- * process ID payload(s)
+ * check if multiple authentication extension is enabled, configuration-wise
  */
-static status_t process_id(private_ike_auth_t *this, message_t *message)
+static bool multiple_auth_enabled()
 {
-       identification_t *id, *req;
-       id_payload_t *idr, *idi;
-
-       idi = (id_payload_t*)message->get_payload(message, ID_INITIATOR);
-       idr = (id_payload_t*)message->get_payload(message, ID_RESPONDER);
-
-       if ((this->initiator && idr == NULL) || (!this->initiator && idi == NULL))
-       {
-               SIG(IKE_UP_FAILED, "ID payload missing in message");
-               return FAILED;
-       }
-       
-       if (this->initiator)
-       {
-               id = idr->get_identification(idr);
-               req = this->ike_sa->get_other_id(this->ike_sa);
-               if (!id->matches(id, req, NULL))
-               {
-                       SIG(IKE_UP_FAILED, "peer ID %D unacceptable, %D required", id, req);
-                       id->destroy(id);
-                       return FAILED;
-               }
-               this->ike_sa->set_other_id(this->ike_sa, id);
-       }
-       else
-       {
-               id = idi->get_identification(idi);
-               this->ike_sa->set_other_id(this->ike_sa, id);
-               if (idr)
-               {
-                       id = idr->get_identification(idr);
-                       this->ike_sa->set_my_id(this->ike_sa, id);
-               }
-       }
-       return SUCCESS;
+       return lib->settings->get_bool(lib->settings,
+                                                                  "charon.multiple_authentication", TRUE);
 }
 
 /**
  * collect the needed information in the IKE_SA_INIT exchange from our message
  */
-static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message)
+static status_t collect_my_init_data(private_ike_auth_t *this,
+                                                                        message_t *message)
 {
        nonce_payload_t *nonce;
-       
+
        /* get the nonce that was generated in ike_init */
        nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
        if (nonce == NULL)
@@ -266,24 +127,25 @@ static status_t collect_my_init_data(private_ike_auth_t *this, message_t *messag
                return FAILED;
        }
        this->my_nonce = nonce->get_nonce(nonce);
-       
-       /* pre-generate the message, so we can store it for us */
+
+       /* pre-generate the message, keep a copy */
        if (this->ike_sa->generate_message(this->ike_sa, message,
                                                                           &this->my_packet) != SUCCESS)
        {
                return FAILED;
        }
-       return NEED_MORE; 
+       return NEED_MORE;
 }
 
 /**
  * collect the needed information in the IKE_SA_INIT exchange from others message
  */
-static status_t collect_other_init_data(private_ike_auth_t *this, message_t *message)
+static status_t collect_other_init_data(private_ike_auth_t *this,
+                                                                               message_t *message)
 {
        /* we collect the needed information in the IKE_SA_INIT exchange */
        nonce_payload_t *nonce;
-       
+
        /* get the nonce that was generated in ike_init */
        nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
        if (nonce == NULL)
@@ -291,176 +153,180 @@ static status_t collect_other_init_data(private_ike_auth_t *this, message_t *mes
                return FAILED;
        }
        this->other_nonce = nonce->get_nonce(nonce);
-       
-       /* pre-generate the message, so we can store it for us */
+
+       /* keep a copy of the received packet */
        this->other_packet = message->get_packet(message);
-       return NEED_MORE; 
+       return NEED_MORE;
 }
 
 /**
- * Implementation of task_t.build to create AUTH payload from EAP data
+ * Get the next authentication configuration
  */
-static status_t build_auth_eap(private_ike_auth_t *this, message_t *message)
+static auth_cfg_t *get_auth_cfg(private_ike_auth_t *this, bool local)
 {
-       authenticator_t *auth;
-       auth_payload_t *auth_payload;
-       
-       auth = (authenticator_t*)this->eap_auth;
-       if (auth->build(auth, this->my_packet->get_data(this->my_packet),
-               this->other_nonce, &auth_payload) != SUCCESS)
+       enumerator_t *e1, *e2;
+       auth_cfg_t *c1, *c2, *next = NULL;
+
+       /* find an available config not already done */
+       e1 = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, local);
+       while (e1->enumerate(e1, &c1))
        {
-               SIG(IKE_UP_FAILED, "generating authentication data failed");
-               if (!this->initiator)
+               bool found = FALSE;
+
+               e2 = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, local);
+               while (e2->enumerate(e2, &c2))
                {
-                       message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+                       if (c2->complies(c2, c1, FALSE))
+                       {
+                               found = TRUE;
+                               break;
+                       }
+               }
+               e2->destroy(e2);
+               if (!found)
+               {
+                       next = c1;
+                       break;
                }
-               return FAILED;
-       }
-       message->add_payload(message, (payload_t*)auth_payload);
-       if (!this->initiator)
-       {
-               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-               SIG(IKE_UP_SUCCESS, "IKE_SA %s established between %D[%H]...[%H]%D",
-                       this->ike_sa->get_name(this->ike_sa),
-                       this->ike_sa->get_my_id(this->ike_sa), 
-                       this->ike_sa->get_my_host(this->ike_sa),
-                       this->ike_sa->get_other_host(this->ike_sa),
-                       this->ike_sa->get_other_id(this->ike_sa));
-               return SUCCESS;
        }
-       return NEED_MORE;
+       e1->destroy(e1);
+       return next;
 }
 
 /**
- * Implementation of task_t.process to verify AUTH payload after EAP
+ * Check if we have should initiate another authentication round
  */
-static status_t process_auth_eap(private_ike_auth_t *this, message_t *message)
+static bool do_another_auth(private_ike_auth_t *this)
 {
-       auth_payload_t *auth_payload;
-       authenticator_t *auth;
+       bool do_another = FALSE;
+       enumerator_t *done, *todo;
+       auth_cfg_t *done_cfg, *todo_cfg;
 
-       auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION);
-       this->peer_authenticated = FALSE;
-       
-       if (auth_payload)
+       if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH))
        {
-               auth = (authenticator_t*)this->eap_auth;
-               if (auth->verify(auth, this->other_packet->get_data(this->other_packet), 
-                                               this->my_nonce, auth_payload) == SUCCESS)
-               {
-                       this->peer_authenticated = TRUE;
-               }
+               return FALSE;
        }
 
-       if (!this->peer_authenticated)
+       done = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, TRUE);
+       todo = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, TRUE);
+       while (todo->enumerate(todo, &todo_cfg))
        {
-               SIG(IKE_UP_FAILED, "authentication of '%D' with %N failed",
-                        this->ike_sa->get_other_id(this->ike_sa), 
-                        auth_method_names, AUTH_EAP);
-               if (this->initiator)
+               if (!done->enumerate(done, &done_cfg))
                {
-                       return FAILED;
+                       done_cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+               }
+               if (!done_cfg->complies(done_cfg, todo_cfg, FALSE))
+               {
+                       do_another = TRUE;
+                       break;
                }
-               return NEED_MORE;
-       }
-       if (this->initiator)
-       {
-               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-               SIG(IKE_UP_SUCCESS, "IKE_SA %s established between %D[%H]...[%H]%D",
-                       this->ike_sa->get_name(this->ike_sa),
-                       this->ike_sa->get_my_id(this->ike_sa), 
-                       this->ike_sa->get_my_host(this->ike_sa),
-                       this->ike_sa->get_other_host(this->ike_sa),
-                       this->ike_sa->get_other_id(this->ike_sa));
-               return SUCCESS;
        }
-       return NEED_MORE;
+       done->destroy(done);
+       todo->destroy(todo);
+       return do_another;
 }
 
 /**
- * Implementation of task_t.process for EAP exchanges
+ * Get peer configuration candidates from backends
  */
-static status_t process_eap_i(private_ike_auth_t *this, message_t *message)
+static bool load_cfg_candidates(private_ike_auth_t *this)
 {
-       eap_payload_t *eap;
-
-       eap = (eap_payload_t*)message->get_payload(message, EXTENSIBLE_AUTHENTICATION);
-       if (eap == NULL)
-       {       
-               SIG(IKE_UP_FAILED, "EAP payload missing");
-               return FAILED;
+       enumerator_t *enumerator;
+       peer_cfg_t *peer_cfg;
+       host_t *me, *other;
+       identification_t *my_id, *other_id;
+
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       other = this->ike_sa->get_other_host(this->ike_sa);
+       my_id = this->ike_sa->get_my_id(this->ike_sa);
+       other_id = this->ike_sa->get_other_id(this->ike_sa);
+
+       enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
+                                                                                                       me, other, my_id, other_id);
+       while (enumerator->enumerate(enumerator, &peer_cfg))
+       {
+               peer_cfg->get_ref(peer_cfg);
+               if (this->peer_cfg == NULL)
+               {       /* best match */
+                       this->peer_cfg = peer_cfg;
+                       this->ike_sa->set_peer_cfg(this->ike_sa, peer_cfg);
+               }
+               else
+               {
+                       this->candidates->insert_last(this->candidates, peer_cfg);
+               }
        }
-       switch (this->eap_auth->process(this->eap_auth, eap, &eap))
+       enumerator->destroy(enumerator);
+       if (this->peer_cfg)
        {
-               case NEED_MORE:
-                       this->eap_payload = eap;
-                       return NEED_MORE;
-               case SUCCESS:
-                       /* EAP exchange completed, now create and process AUTH */
-                       this->eap_payload = NULL;
-                       this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap;
-                       this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap;
-                       return NEED_MORE;
-               default:
-                       this->eap_payload = NULL;
-                       SIG(IKE_UP_FAILED, "failed to authenticate against %D using EAP",
-                               this->ike_sa->get_other_id(this->ike_sa));
-                       return FAILED;
+               DBG1(DBG_CFG, "selected peer config '%s'",
+                        this->peer_cfg->get_name(this->peer_cfg));
+               return TRUE;
        }
+       DBG1(DBG_CFG, "no matching peer config found");
+       return FALSE;
 }
 
 /**
- * Implementation of task_t.process for EAP exchanges
- */
-static status_t process_eap_r(private_ike_auth_t *this, message_t *message)
-{
-       this->eap_payload = (eap_payload_t*)message->get_payload(message, 
-                                                                                                       EXTENSIBLE_AUTHENTICATION);
-       return NEED_MORE;
-}
-
-/**
- * Implementation of task_t.build for EAP exchanges
- */
-static status_t build_eap_i(private_ike_auth_t *this, message_t *message)
-{
-       message->add_payload(message, (payload_t*)this->eap_payload);
-       return NEED_MORE;
-}
-
-/**
- * Implementation of task_t.build for EAP exchanges
+ * update the current peer candidate if necessary, using candidates
  */
-static status_t build_eap_r(private_ike_auth_t *this, message_t *message)
+static bool update_cfg_candidates(private_ike_auth_t *this, bool strict)
 {
-       status_t status = NEED_MORE;
-       eap_payload_t *eap;
-               
-       if (this->eap_payload == NULL)
-       {
-               SIG(IKE_UP_FAILED, "EAP payload missing");
-               return FAILED;
-       }
-       
-       switch (this->eap_auth->process(this->eap_auth, this->eap_payload, &eap))
+       do
        {
-               case NEED_MORE:
-                       
-                       break;
-               case SUCCESS:
-                       /* EAP exchange completed, now create and process AUTH */
-                       this->public.task.build = (status_t(*)(task_t*,message_t*))build_auth_eap;
-                       this->public.task.process = (status_t(*)(task_t*,message_t*))process_auth_eap;
-                       break;
-               default:
-                       SIG(IKE_UP_FAILED, "authentication of '%D' with %N failed",
-                               this->ike_sa->get_other_id(this->ike_sa),
-                               auth_method_names, AUTH_EAP);
-                       status = FAILED;
-                       break;
+               if (this->peer_cfg)
+               {
+                       bool complies = TRUE;
+                       enumerator_t *e1, *e2, *tmp;
+                       auth_cfg_t *c1, *c2;
+
+                       e1 = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE);
+                       e2 = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, FALSE);
+
+                       if (strict)
+                       {       /* swap lists in strict mode: all configured rounds must be
+                                * fulfilled. If !strict, we check only the rounds done so far. */
+                               tmp = e1;
+                               e1 = e2;
+                               e2 = tmp;
+                       }
+                       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)))
+                               {
+                                       complies = FALSE;
+                                       break;
+                               }
+                       }
+                       e1->destroy(e1);
+                       e2->destroy(e2);
+                       if (complies)
+                       {
+                               break;
+                       }
+                       DBG1(DBG_CFG, "selected peer config '%s' inacceptable",
+                                this->peer_cfg->get_name(this->peer_cfg));
+                       this->peer_cfg->destroy(this->peer_cfg);
+               }
+               if (this->candidates->remove_first(this->candidates,
+                                                                               (void**)&this->peer_cfg) != SUCCESS)
+               {
+                       DBG1(DBG_CFG, "no alternative config found");
+                       this->peer_cfg = NULL;
+               }
+               else
+               {
+                       DBG1(DBG_CFG, "switching to peer config '%s'",
+                                this->peer_cfg->get_name(this->peer_cfg));
+                       this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
+               }
        }
-       message->add_payload(message, (payload_t*)eap);
-       return status;
+       while (this->peer_cfg);
+
+       return this->peer_cfg != NULL;
 }
 
 /**
@@ -468,31 +334,104 @@ static status_t build_eap_r(private_ike_auth_t *this, message_t *message)
  */
 static status_t build_i(private_ike_auth_t *this, message_t *message)
 {
-       peer_cfg_t *config;
+       auth_cfg_t *cfg;
 
        if (message->get_exchange_type(message) == IKE_SA_INIT)
        {
                return collect_my_init_data(this, message);
        }
-               
-       if (build_id(this, message) != SUCCESS)
+
+       if (this->peer_cfg == NULL)
        {
-               return FAILED;
+               this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+               this->peer_cfg->get_ref(this->peer_cfg);
        }
-       
-       config = this->ike_sa->get_peer_cfg(this->ike_sa);
-       if (config->get_auth_method(config) == AUTH_EAP)
-       {
-               this->eap_auth = eap_authenticator_create(this->ike_sa);
+
+       if (message->get_message_id(message) == 1 &&
+               this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH))
+       {       /* in the first IKE_AUTH, indicate support for multiple authentication */
+               message->add_notify(message, FALSE, MULTIPLE_AUTH_SUPPORTED, chunk_empty);
        }
-       else
+
+       if (!this->do_another_auth && !this->my_auth)
+       {       /* we have done our rounds */
+               return NEED_MORE;
+       }
+
+       /* check if an authenticator is in progress */
+       if (this->my_auth == NULL)
        {
-               if (build_auth(this, message) != SUCCESS)
+               identification_t *id;
+               id_payload_t *id_payload;
+
+               /* clean up authentication config from a previous round */
+               cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+               cfg->purge(cfg, TRUE);
+
+               /* add (optional) IDr */
+               cfg = get_auth_cfg(this, FALSE);
+               if (cfg)
+               {
+                       id = cfg->get(cfg, AUTH_RULE_IDENTITY);
+                       if (id && !id->contains_wildcards(id))
+                       {
+                               this->ike_sa->set_other_id(this->ike_sa, id->clone(id));
+                               id_payload = id_payload_create_from_identification(
+                                                                                                                       ID_RESPONDER, id);
+                               message->add_payload(message, (payload_t*)id_payload);
+                       }
+               }
+               /* add IDi */
+               cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+               cfg->merge(cfg, get_auth_cfg(this, TRUE), TRUE);
+               id = cfg->get(cfg, AUTH_RULE_IDENTITY);
+               if (!id)
+               {
+                       DBG1(DBG_CFG, "configuration misses IDi");
+                       return FAILED;
+               }
+               this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
+               id_payload = id_payload_create_from_identification(ID_INITIATOR, id);
+               message->add_payload(message, (payload_t*)id_payload);
+
+               /* build authentication data */
+               this->my_auth = authenticator_create_builder(this->ike_sa, cfg,
+                                                       this->other_nonce, this->my_nonce,
+                                                       this->other_packet->get_data(this->other_packet),
+                                                       this->my_packet->get_data(this->my_packet));
+               if (!this->my_auth)
                {
                        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);
+                       this->my_auth->destroy(this->my_auth);
+                       this->my_auth = NULL;
+                       break;
+               case NEED_MORE:
+                       break;
+               default:
+                       return FAILED;
+       }
 
+       /* check for additional authentication rounds */
+       if (do_another_auth(this))
+       {
+               if (message->get_payload(message, AUTHENTICATION))
+               {
+                       message->add_notify(message, FALSE, ANOTHER_AUTH_FOLLOWS, chunk_empty);
+               }
+       }
+       else
+       {
+               this->do_another_auth = FALSE;
+       }
        return NEED_MORE;
 }
 
@@ -501,41 +440,136 @@ static status_t build_i(private_ike_auth_t *this, message_t *message)
  */
 static status_t process_r(private_ike_auth_t *this, message_t *message)
 {
-       peer_cfg_t *config;
-       
+       auth_cfg_t *cfg, *cand;
+       id_payload_t *id_payload;
+       identification_t *id;
+
        if (message->get_exchange_type(message) == IKE_SA_INIT)
        {
                return collect_other_init_data(this, message);
        }
-       
-       if (process_id(this, message) != SUCCESS)
+
+       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);
+               if (id_payload)
+               {
+                       id = id_payload->get_identification(id_payload);
+               }
+               else
+               {
+                       id = identification_create_from_encoding(ID_ANY, chunk_empty);
+               }
+               this->ike_sa->set_my_id(this->ike_sa, id);
+       }
+
+       if (!this->expect_another_auth)
        {
                return NEED_MORE;
        }
-       
-       switch (process_auth(this, message))
+       if (message->get_notify(message, MULTIPLE_AUTH_SUPPORTED))
+       {
+               this->ike_sa->enable_extension(this->ike_sa, EXT_MULTIPLE_AUTH);
+       }
+
+       if (this->other_auth == NULL)
+       {
+               /* handle IDi payload */
+               id_payload = (id_payload_t*)message->get_payload(message, ID_INITIATOR);
+               if (!id_payload)
+               {
+                       DBG1(DBG_IKE, "IDi payload missing");
+                       return FAILED;
+               }
+               id = id_payload->get_identification(id_payload);
+               this->ike_sa->set_other_id(this->ike_sa, id);
+               cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+               cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id));
+
+               if (this->peer_cfg == NULL)
+               {
+                       if (!load_cfg_candidates(this))
+                       {
+                               this->authentication_failed = TRUE;
+                               return NEED_MORE;
+                       }
+               }
+               if (message->get_payload(message, AUTHENTICATION) == NULL)
+               {       /* before authenticating with EAP, we need a EAP config */
+                       cand = get_auth_cfg(this, FALSE);
+                       while (!cand || (
+                                       (uintptr_t)cand->get(cand, AUTH_RULE_EAP_TYPE) == EAP_NAK &&
+                                       (uintptr_t)cand->get(cand, AUTH_RULE_EAP_VENDOR) == 0))
+                       {       /* peer requested EAP, but current config does not match */
+                               this->peer_cfg->destroy(this->peer_cfg);
+                               this->peer_cfg = NULL;
+                               if (!update_cfg_candidates(this, FALSE))
+                               {
+                                       this->authentication_failed = TRUE;
+                                       return NEED_MORE;
+                               }
+                               cand = get_auth_cfg(this, FALSE);
+                       }
+                       cfg->merge(cfg, cand, TRUE);
+               }
+
+               /* verify authentication data */
+               this->other_auth = authenticator_create_verifier(this->ike_sa,
+                                                       message, this->other_nonce, this->my_nonce,
+                                                       this->other_packet->get_data(this->other_packet),
+                                                       this->my_packet->get_data(this->my_packet));
+               if (!this->other_auth)
+               {
+                       this->authentication_failed = TRUE;
+                       return NEED_MORE;
+               }
+       }
+       switch (this->other_auth->process(this->other_auth, message))
        {
                case SUCCESS:
-                       this->peer_authenticated = TRUE;
-                       break;
-               case NOT_FOUND:
-                       /* use EAP if no AUTH payload found */
-                       this->eap_auth = eap_authenticator_create(this->ike_sa);
+                       this->other_auth->destroy(this->other_auth);
+                       this->other_auth = NULL;
                        break;
+               case NEED_MORE:
+                       if (message->get_payload(message, AUTHENTICATION))
+                       {       /* AUTH verification successful, but another build() needed */
+                               break;
+                       }
+                       return NEED_MORE;
                default:
-                       break;
+                       this->authentication_failed = TRUE;
+                       return NEED_MORE;
        }
 
-       config = charon->backends->get_peer_cfg(charon->backends,
-                                                                       this->ike_sa->get_my_id(this->ike_sa),
-                                                                       this->ike_sa->get_other_id(this->ike_sa),
-                                                                       this->ike_sa->get_other_ca(this->ike_sa));
-       if (config)
+       /* 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);
+
+       /* another auth round done, invoke authorize hook */
+       if (!charon->bus->authorize(charon->bus, FALSE))
+       {
+               DBG1(DBG_IKE, "authorization hook forbids IKE_SA, cancelling");
+               this->authentication_failed = TRUE;
+               return NEED_MORE;
+       }
+
+       if (!update_cfg_candidates(this, FALSE))
        {
-               this->ike_sa->set_peer_cfg(this->ike_sa, config);
-               config->destroy(config);
+               this->authentication_failed = TRUE;
+               return NEED_MORE;
+       }
+
+       if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL)
+       {
+               this->expect_another_auth = FALSE;
+               if (!update_cfg_candidates(this, TRUE))
+               {
+                       this->authentication_failed = TRUE;
+                       return NEED_MORE;
+               }
        }
-       
        return NEED_MORE;
 }
 
@@ -544,66 +578,151 @@ static status_t process_r(private_ike_auth_t *this, message_t *message)
  */
 static status_t build_r(private_ike_auth_t *this, message_t *message)
 {
-       peer_cfg_t *config;
-       eap_type_t eap_type;
-       eap_payload_t *eap_payload;
-       status_t status;
+       auth_cfg_t *cfg;
 
        if (message->get_exchange_type(message) == IKE_SA_INIT)
        {
+               if (multiple_auth_enabled())
+               {
+                       message->add_notify(message, FALSE, MULTIPLE_AUTH_SUPPORTED,
+                                                               chunk_empty);
+               }
                return collect_my_init_data(this, message);
        }
-       
-       config = this->ike_sa->get_peer_cfg(this->ike_sa);
-       if (config == NULL)
+
+       if (this->authentication_failed || this->peer_cfg == NULL)
        {
-               SIG(IKE_UP_FAILED, "no matching config found for %D...%D",
-                       this->ike_sa->get_my_id(this->ike_sa),
-                       this->ike_sa->get_other_id(this->ike_sa));
                message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
                return FAILED;
        }
-       
-       if (build_id(this, message) != SUCCESS ||
-               build_auth(this, message) != SUCCESS)
+
+       if (this->my_auth == NULL && this->do_another_auth)
        {
-               message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
-               return FAILED;
+               identification_t *id, *id_cfg;
+               id_payload_t *id_payload;
+
+               /* add IDr */
+               cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+               cfg->purge(cfg, TRUE);
+               cfg->merge(cfg, get_auth_cfg(this, TRUE), TRUE);
+
+               id_cfg = cfg->get(cfg, AUTH_RULE_IDENTITY);
+               id = this->ike_sa->get_my_id(this->ike_sa);
+               if (id->get_type(id) == ID_ANY)
+               {       /* no IDr received, apply configured ID */
+                       if (!id_cfg || id_cfg->contains_wildcards(id_cfg))
+                       {
+                               DBG1(DBG_CFG, "IDr not configured and negotiation failed");
+                               message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
+                                                                       chunk_empty);
+                               return FAILED;
+                       }
+                       this->ike_sa->set_my_id(this->ike_sa, id_cfg->clone(id_cfg));
+                       id = id_cfg;
+               }
+               else
+               {       /* IDr received, check if it matches configuration */
+                       if (id_cfg && !id->matches(id, id_cfg))
+                       {
+                               DBG1(DBG_CFG, "received IDr %Y, but require %Y", id, id_cfg);
+                               message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
+                                                                       chunk_empty);
+                               return FAILED;
+                       }
+               }
+
+               id_payload = id_payload_create_from_identification(ID_RESPONDER, id);
+               message->add_payload(message, (payload_t*)id_payload);
+
+               /* build authentication data */
+               this->my_auth = authenticator_create_builder(this->ike_sa, cfg,
+                                                       this->other_nonce, this->my_nonce,
+                                                       this->other_packet->get_data(this->other_packet),
+                                                       this->my_packet->get_data(this->my_packet));
+               if (!this->my_auth)
+               {
+                       message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+                       return FAILED;
+               }
        }
-       
-       /* use "traditional" authentication if we could authenticate peer */
-       if (this->peer_authenticated)
+
+       if (this->other_auth)
        {
-               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-               SIG(IKE_UP_SUCCESS, "IKE_SA %s established between %D[%H]...[%H]%D",
-                       this->ike_sa->get_name(this->ike_sa),
-                       this->ike_sa->get_my_id(this->ike_sa), 
-                       this->ike_sa->get_my_host(this->ike_sa),
-                       this->ike_sa->get_other_host(this->ike_sa),
-                       this->ike_sa->get_other_id(this->ike_sa));
-               return SUCCESS;
+               switch (this->other_auth->build(this->other_auth, message))
+               {
+                       case SUCCESS:
+                               this->other_auth->destroy(this->other_auth);
+                               this->other_auth = NULL;
+                               break;
+                       case NEED_MORE:
+                               break;
+                       default:
+                               if (!message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
+                               {       /* skip AUTHENTICATION_FAILED if we have EAP_FAILURE */
+                                       message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
+                                                                               chunk_empty);
+                               }
+                               return FAILED;
+               }
        }
-       
-       if (this->eap_auth == NULL)
+       if (this->my_auth)
        {
-               /* peer not authenticated, nor does it want to use EAP */
-               message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
-               return FAILED;
+               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);
+                               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;
+               }
+       }
+
+       /* check for additional authentication rounds */
+       if (do_another_auth(this))
+       {
+               message->add_notify(message, FALSE, ANOTHER_AUTH_FOLLOWS, chunk_empty);
        }
-               
-       /* initiate EAP authenitcation */
-       eap_type = config->get_eap_type(config);
-       status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_payload);
-       message->add_payload(message, (payload_t*)eap_payload);
-       if (status != NEED_MORE)
+       else
        {
-               SIG(IKE_UP_FAILED, "unable to initiate EAP authentication");
-               return FAILED;
+               this->do_another_auth = FALSE;
+       }
+       if (!this->do_another_auth && !this->expect_another_auth)
+       {
+               if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+                                                                                                        this->ike_sa))
+               {
+                       DBG1(DBG_IKE, "cancelling IKE_SA setup due 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");
+                       message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
+                                                               chunk_empty);
+                       return 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;
        }
-       
-       /* switch to EAP methods */
-       this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_r;
-       this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_r;
        return NEED_MORE;
 }
 
@@ -612,22 +731,28 @@ static status_t build_r(private_ike_auth_t *this, message_t *message)
  */
 static status_t process_i(private_ike_auth_t *this, message_t *message)
 {
-       iterator_t *iterator;
+       enumerator_t *enumerator;
        payload_t *payload;
-       
+       auth_cfg_t *cfg;
+
        if (message->get_exchange_type(message) == IKE_SA_INIT)
        {
+               if (message->get_notify(message, MULTIPLE_AUTH_SUPPORTED) &&
+                       multiple_auth_enabled())
+               {
+                       this->ike_sa->enable_extension(this->ike_sa, EXT_MULTIPLE_AUTH);
+               }
                return collect_other_init_data(this, message);
        }
-       
-       iterator = message->get_payload_iterator(message);
-       while (iterator->iterate(iterator, (void**)&payload))
+
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
        {
                if (payload->get_type(payload) == NOTIFY)
                {
                        notify_payload_t *notify = (notify_payload_t*)payload;
                        notify_type_t type = notify->get_notify_type(notify);
-                       
+
                        switch (type)
                        {
                                case NO_PROPOSAL_CHOSEN:
@@ -645,46 +770,138 @@ static status_t process_i(private_ike_auth_t *this, message_t *message)
                                case ADDITIONAL_IP6_ADDRESS:
                                        /* handled in ike_mobike task */
                                        break;
+                               case AUTH_LIFETIME:
+                                       /* handled in ike_auth_lifetime task */
+                                       break;
+                               case ME_ENDPOINT:
+                                       /* handled in ike_me task */
+                                       break;
                                default:
                                {
                                        if (type < 16383)
                                        {
-                                               SIG(IKE_UP_FAILED, "received %N notify error",
+                                               DBG1(DBG_IKE, "received %N notify error",
                                                         notify_type_names, type);
-                                               iterator->destroy(iterator);
-                                               return FAILED;  
+                                               enumerator->destroy(enumerator);
+                                               return FAILED;
                                        }
-                                       DBG1(DBG_IKE, "received %N notify",
+                                       DBG2(DBG_IKE, "received %N notify",
                                                notify_type_names, type);
                                        break;
                                }
                        }
                }
        }
-       iterator->destroy(iterator);
-       
-       if (process_id(this, message) != SUCCESS ||
-               process_auth(this, message) != SUCCESS)
+       enumerator->destroy(enumerator);
+
+       if (this->my_auth)
        {
-               return FAILED;
+               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);
+                               this->my_auth->destroy(this->my_auth);
+                               this->my_auth = NULL;
+                               this->do_another_auth = do_another_auth(this);
+                               break;
+                       case NEED_MORE:
+                               break;
+                       default:
+                               return FAILED;
+               }
+       }
+
+       if (this->expect_another_auth)
+       {
+               if (this->other_auth == NULL)
+               {
+                       id_payload_t *id_payload;
+                       identification_t *id;
+
+                       /* responder is not allowed to do EAP */
+                       if (!message->get_payload(message, AUTHENTICATION))
+                       {
+                               DBG1(DBG_IKE, "AUTH payload missing");
+                               return FAILED;
+                       }
+
+                       /* handle IDr payload */
+                       id_payload = (id_payload_t*)message->get_payload(message,
+                                                                                                                        ID_RESPONDER);
+                       if (!id_payload)
+                       {
+                               DBG1(DBG_IKE, "IDr payload missing");
+                               return FAILED;
+                       }
+                       id = id_payload->get_identification(id_payload);
+                       this->ike_sa->set_other_id(this->ike_sa, id);
+                       cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+                       cfg->add(cfg, AUTH_RULE_IDENTITY, id->clone(id));
+
+                       /* verify authentication data */
+                       this->other_auth = authenticator_create_verifier(this->ike_sa,
+                                                       message, this->other_nonce, this->my_nonce,
+                                                       this->other_packet->get_data(this->other_packet),
+                                                       this->my_packet->get_data(this->my_packet));
+                       if (!this->other_auth)
+                       {
+                               return FAILED;
+                       }
+               }
+               switch (this->other_auth->process(this->other_auth, message))
+               {
+                       case SUCCESS:
+                               break;
+                       case NEED_MORE:
+                               return NEED_MORE;
+                       default:
+                               return 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);
+               this->other_auth->destroy(this->other_auth);
+               this->other_auth = NULL;
+
+               /* another auth round done, invoke authorize hook */
+               if (!charon->bus->authorize(charon->bus, FALSE))
+               {
+                       DBG1(DBG_IKE, "authorization forbids IKE_SA, cancelling");
+                       return FAILED;
+               }
        }
-       
-       if (this->eap_auth)
-       {
-               /* switch to EAP authentication methods */
-               this->public.task.build = (status_t(*)(task_t*,message_t*))build_eap_i;
-               this->public.task.process = (status_t(*)(task_t*,message_t*))process_eap_i;
-               return process_eap_i(this, message);
-       }
-       
-       this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-       SIG(IKE_UP_SUCCESS, "IKE_SA %s established between %D[%H]...[%H]%D",
-               this->ike_sa->get_name(this->ike_sa),
-               this->ike_sa->get_my_id(this->ike_sa), 
-               this->ike_sa->get_my_host(this->ike_sa),
-               this->ike_sa->get_other_host(this->ike_sa),
-               this->ike_sa->get_other_id(this->ike_sa));
-       return SUCCESS;
+
+       if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS) == NULL)
+       {
+               this->expect_another_auth = FALSE;
+       }
+       if (!this->expect_another_auth && !this->do_another_auth && !this->my_auth)
+       {
+               if (!update_cfg_candidates(this, TRUE))
+               {
+                       return FAILED;
+               }
+               if (!charon->bus->authorize(charon->bus, TRUE))
+               {
+                       DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
+                       return 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;
 }
 
 /**
@@ -704,28 +921,21 @@ static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa)
        chunk_free(&this->other_nonce);
        DESTROY_IF(this->my_packet);
        DESTROY_IF(this->other_packet);
-       if (this->eap_auth)
-       {
-               this->eap_auth->authenticator_interface.destroy(
-                                                                       &this->eap_auth->authenticator_interface);
-       }
-       
+       DESTROY_IF(this->peer_cfg);
+       DESTROY_IF(this->my_auth);
+       DESTROY_IF(this->other_auth);
+       this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
+
        this->my_packet = NULL;
        this->other_packet = NULL;
-       this->peer_authenticated = FALSE;
-       this->eap_auth = NULL;
-       this->eap_payload = NULL;
        this->ike_sa = ike_sa;
-       if (this->initiator)
-       {
-               this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
-               this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
-       }
-       else
-       {
-               this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
-               this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
-       }
+       this->peer_cfg = NULL;
+       this->my_auth = NULL;
+       this->other_auth = NULL;
+       this->do_another_auth = TRUE;
+       this->expect_another_auth = TRUE;
+       this->authentication_failed = FALSE;
+       this->candidates = linked_list_create();
 }
 
 /**
@@ -737,11 +947,10 @@ static void destroy(private_ike_auth_t *this)
        chunk_free(&this->other_nonce);
        DESTROY_IF(this->my_packet);
        DESTROY_IF(this->other_packet);
-       if (this->eap_auth)
-       {
-               this->eap_auth->authenticator_interface.destroy(
-                                                                       &this->eap_auth->authenticator_interface);
-       }
+       DESTROY_IF(this->my_auth);
+       DESTROY_IF(this->other_auth);
+       DESTROY_IF(this->peer_cfg);
+       this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
        free(this);
 }
 
@@ -755,7 +964,7 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator)
        this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
        this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
        this->public.task.destroy = (void(*)(task_t*))destroy;
-       
+
        if (initiator)
        {
                this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
@@ -766,16 +975,21 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator)
                this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
                this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
        }
-       
+
        this->ike_sa = ike_sa;
        this->initiator = initiator;
        this->my_nonce = chunk_empty;
        this->other_nonce = chunk_empty;
        this->my_packet = NULL;
        this->other_packet = NULL;
-       this->peer_authenticated = FALSE;
-       this->eap_auth = NULL;
-       this->eap_payload = NULL;
-       
+       this->peer_cfg = NULL;
+       this->candidates = linked_list_create();
+       this->my_auth = NULL;
+       this->other_auth = NULL;
+       this->do_another_auth = TRUE;
+       this->expect_another_auth = TRUE;
+       this->authentication_failed = FALSE;
+
        return &this->public;
 }
+