Merge branch 'ikev1'
authorMartin Willi <martin@revosec.ch>
Wed, 2 May 2012 09:12:31 +0000 (11:12 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 2 May 2012 09:12:31 +0000 (11:12 +0200)
Conflicts:
configure.in
man/ipsec.conf.5.in
src/libcharon/encoding/generator.c
src/libcharon/encoding/payloads/notify_payload.c
src/libcharon/encoding/payloads/notify_payload.h
src/libcharon/encoding/payloads/payload.c
src/libcharon/network/receiver.c
src/libcharon/sa/authenticator.c
src/libcharon/sa/authenticator.h
src/libcharon/sa/ikev2/tasks/ike_init.c
src/libcharon/sa/task_manager.c
src/libstrongswan/credentials/auth_cfg.c

34 files changed:
1  2 
configure.in
man/ipsec.conf.5.in
src/libcharon/daemon.c
src/libcharon/encoding/generator.c
src/libcharon/encoding/parser.c
src/libcharon/encoding/payloads/certreq_payload.c
src/libcharon/encoding/payloads/notify_payload.c
src/libcharon/encoding/payloads/notify_payload.h
src/libcharon/encoding/payloads/payload.c
src/libcharon/encoding/payloads/payload.h
src/libcharon/network/receiver.c
src/libcharon/plugins/smp/smp.c
src/libcharon/plugins/stroke/stroke_config.c
src/libcharon/plugins/stroke/stroke_cred.c
src/libcharon/plugins/stroke/stroke_list.c
src/libcharon/plugins/stroke/stroke_socket.c
src/libcharon/sa/authenticator.c
src/libcharon/sa/authenticator.h
src/libcharon/sa/ike_sa.c
src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/ikev2/tasks/ike_auth.c
src/libcharon/sa/ikev2/tasks/ike_init.c
src/libcharon/sa/keymat.h
src/libstrongswan/asn1/oid.txt
src/libstrongswan/chunk.h
src/libstrongswan/credentials/auth_cfg.c
src/libstrongswan/credentials/auth_cfg.h
src/libstrongswan/utils.h
src/pki/commands/print.c
src/pluto/keys.c
src/starter/starterstroke.c
src/stroke/stroke.c
src/stroke/stroke_msg.h

diff --cc configure.in
index 7f28762,b99487a..6c6a57f
mode 100644,100755..100755
@@@ -605,14 -509,13 +509,16 @@@ Fo
  .B eap,
  an optional EAP method can be appended. Currently defined methods are
  .BR eap-aka ,
+ .BR eap-sim ,
  .BR eap-gtc ,
  .BR eap-md5 ,
 +.BR eap-mschapv2 ,
 +.BR eap-peap ,
 +.BR eap-sim ,
  .BR eap-tls ,
 -.B eap-mschapv2
++.BR eap-ttls ,
  and
- .BR eap-ttls .
+ .BR eap-radius .
  Alternatively, IANA assigned EAP method numbers are accepted. Vendor specific
  EAP methods are defined in the form
  .B eap-type-vendor
@@@ -131,8 -128,8 +131,9 @@@ static void destroy(private_daemon_t *t
        DESTROY_IF(this->public.ike_sa_manager);
        DESTROY_IF(this->public.controller);
        DESTROY_IF(this->public.eap);
+       DESTROY_IF(this->public.xauth);
        DESTROY_IF(this->public.backends);
 +      DESTROY_IF(this->public.sender);
        DESTROY_IF(this->public.socket);
  
        /* rehook library logging, shutdown logging */
@@@ -519,11 -560,14 +560,14 @@@ METHOD(generator_t, generate_payload, v
                                return;
                }
        }
-       DBG2(DBG_ENC, "generating %N payload finished",
-                payload_type_names, payload_type);
-       DBG3(DBG_ENC, "generated data for this payload %b",
-                this->buffer + offset_start,
-                (u_int)(this->out_position - this->buffer - offset_start));
+       if (this->debug)
+       {
+               DBG2(DBG_ENC, "generating %N payload finished",
+                        payload_type_names, payload_type);
+               DBG3(DBG_ENC, "generated data for this payload %b",
+                        this->buffer + offset_start,
 -                       this->out_position - this->buffer - offset_start);
++                       (u_int)(this->out_position - this->buffer - offset_start));
+       }
  }
  
  METHOD(generator_t, destroy, void,
Simple merge
@@@ -109,9 -111,11 +111,10 @@@ static encoding_rule_t encodings[] = 
  METHOD(payload_t, verify, status_t,
        private_certreq_payload_t *this)
  {
-       if (this->encoding == ENC_X509_SIGNATURE)
+       if (this->type == CERTIFICATE_REQUEST &&
+               this->encoding == ENC_X509_SIGNATURE)
        {
 -              if (this->data.len < HASH_SIZE_SHA1 ||
 -                      this->data.len % HASH_SIZE_SHA1)
 +              if (this->data.len % HASH_SIZE_SHA1)
                {
                        DBG1(DBG_ENC, "invalid X509 hash length (%d) in certreq",
                                 this->data.len);
index e03d1af,4115344..d168e1c
mode 100644,100755..100755
@@@ -98,11 -105,15 +105,18 @@@ ENUM_NEXT(notify_type_names, INITIAL_CO
        "IKEV2_MESSAGE_ID_SYNC_SUPPORTED",
        "IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED",
        "IKEV2_MESSAGE_ID_SYNC",
 -      "IPSEC_REPLAY_COUNTER_SYNC");
 -ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
 +      "IPSEC_REPLAY_COUNTER_SYNC",
 +      "SECURE PASSWORD_METHOD",
 +      "PSK_PERSIST",
 +      "PSK_CONFIRM");
- ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, PSK_CONFIRM,
++ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, PSK_CONFIRM,
+       "INITIAL_CONTACT");
+ ENUM_NEXT(notify_type_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1,
+       "DPD_R_U_THERE",
+       "DPD_R_U_THERE_ACK");
+ ENUM_NEXT(notify_type_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK,
+       "UNITY_LOAD_BALANCE");
+ ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE,
        "USE_BEET_MODE");
  ENUM_NEXT(notify_type_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE,
        "ME_MEDIATION",
@@@ -189,11 -207,15 +210,18 @@@ ENUM_NEXT(notify_type_short_names, INIT
        "MSG_ID_SYN_SUP",
        "RPL_CTR_SYN_SUP",
        "MSG_ID_SYN",
 -      "RPL_CTR_SYN");
 -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
 +      "RPL_CTR_SYN",
 +      "SEC_PASSWD",
 +      "PSK_PST",
 +      "PSK_CFM");
- ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, PSK_CONFIRM,
++ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, PSK_CONFIRM,
+       "INITIAL_CONTACT");
+ ENUM_NEXT(notify_type_short_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1,
+       "DPD",
+       "DPD_ACK");
+ ENUM_NEXT(notify_type_short_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK,
+       "UNITY_LB");
+ ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE,
        "BEET_MODE");
  ENUM_NEXT(notify_type_short_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE,
        "ME_MED",
index ced2827,07fbcb4..beec1e2
mode 100644,100755..100755
@@@ -127,11 -138,13 +138,18 @@@ enum notify_type_t 
        IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED = 16421,
        IKEV2_MESSAGE_ID_SYNC = 16422,
        IPSEC_REPLAY_COUNTER_SYNC = 16423,
 +      /* Secure password methods, RFC 6467 */
 +      SECURE_PASSWORD_METHOD = 16424,
 +      /* PACE - draft-kuegler-ipsecme-pace-ikev2 */
 +      PSK_PERSIST = 16425,
 +      PSK_CONFIRM = 16426,
+       /* IKEv1 initial contact */
+       INITIAL_CONTACT_IKEV1 = 24578,
+       /* IKEv1 DPD */
+       DPD_R_U_THERE = 36136,
+       DPD_R_U_THERE_ACK = 36137,
+       /* IKEv1 Cisco High Availability */
+       UNITY_LOAD_BALANCE = 40501,
        /* BEET mode, not even a draft yet. private use */
        USE_BEET_MODE = 40961,
        /* IKE-ME, private use */
  #include <encoding/payloads/cp_payload.h>
  #include <encoding/payloads/configuration_attribute.h>
  #include <encoding/payloads/eap_payload.h>
+ #include <encoding/payloads/hash_payload.h>
  #include <encoding/payloads/unknown_payload.h>
  
  ENUM_BEGIN(payload_type_names, NO_PAYLOAD, NO_PAYLOAD,
        "NO_PAYLOAD");
- ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION,
-                                                         GENERIC_SECURE_PASSWORD_METHOD, NO_PAYLOAD,
+ ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION_V1, CONFIGURATION_V1, NO_PAYLOAD,
+       "SECURITY_ASSOCIATION_V1",
+       "PROPOSAL_V1",
+       "TRANSFORM_V1",
+       "KEY_EXCHANGE_V1",
+       "ID_V1",
+       "CERTIFICATE_V1",
+       "CERTIFICATE_REQUEST_V1",
+       "HASH_V1",
+       "SIGNATURE_V1",
+       "NONCE_V1",
+       "NOTIFY_V1",
+       "DELETE_V1",
+       "VENDOR_ID_V1",
+       "CONFIGURATION_V1");
+ ENUM_NEXT(payload_type_names, NAT_D_V1, NAT_OA_V1, CONFIGURATION_V1,
+       "NAT_D_V1",
+       "NAT_OA_V1");
 -ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NAT_OA_V1,
++ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METHOD, NAT_OA_V1,
        "SECURITY_ASSOCIATION",
        "KEY_EXCHANGE",
        "ID_INITIATOR",
        "TRAFFIC_SELECTOR_RESPONDER",
        "ENCRYPTED",
        "CONFIGURATION",
 -      "EXTENSIBLE_AUTHENTICATION");
 +      "EXTENSIBLE_AUTHENTICATION",
 +      "GENERIC_SECURE_PASSWORD_METHOD");
  #ifdef ME
 -ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION,
 +ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, GENERIC_SECURE_PASSWORD_METHOD,
        "ID_PEER");
- ENUM_NEXT(payload_type_names, HEADER, CONFIGURATION_ATTRIBUTE, ID_PEER,
+ ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, ID_PEER,
        "HEADER",
        "PROPOSAL_SUBSTRUCTURE",
+       "PROPOSAL_SUBSTRUCTURE_V1",
        "TRANSFORM_SUBSTRUCTURE",
+       "TRANSFORM_SUBSTRUCTURE_V1",
        "TRANSFORM_ATTRIBUTE",
+       "TRANSFORM_ATTRIBUTE_V1",
        "TRAFFIC_SELECTOR_SUBSTRUCTURE",
-       "CONFIGURATION_ATTRIBUTE");
+       "CONFIGURATION_ATTRIBUTE",
+       "CONFIGURATION_ATTRIBUTE_V1",
+       "ENCRYPTED_V1");
  #else
- ENUM_NEXT(payload_type_names, HEADER, CONFIGURATION_ATTRIBUTE,
-                                                         GENERIC_SECURE_PASSWORD_METHOD,
 -ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, EXTENSIBLE_AUTHENTICATION,
++ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD,
        "HEADER",
        "PROPOSAL_SUBSTRUCTURE",
+       "PROPOSAL_SUBSTRUCTURE_V1",
        "TRANSFORM_SUBSTRUCTURE",
+       "TRANSFORM_SUBSTRUCTURE_V1",
        "TRANSFORM_ATTRIBUTE",
+       "TRANSFORM_ATTRIBUTE_V1",
        "TRAFFIC_SELECTOR_SUBSTRUCTURE",
-       "CONFIGURATION_ATTRIBUTE");
+       "CONFIGURATION_ATTRIBUTE",
+       "CONFIGURATION_ATTRIBUTE_V1",
+       "ENCRYPTED_V1");
  #endif /* ME */
- ENUM_END(payload_type_names, CONFIGURATION_ATTRIBUTE);
+ ENUM_END(payload_type_names, ENCRYPTED_V1);
  
  /* short forms of payload names */
  ENUM_BEGIN(payload_type_short_names, NO_PAYLOAD, NO_PAYLOAD,
        "--");
- ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION,
-                                                                       GENERIC_SECURE_PASSWORD_METHOD, NO_PAYLOAD,
+ ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION_V1, CONFIGURATION_V1, NO_PAYLOAD,
+       "SA",
+       "PROP",
+       "TRANS",
+       "KE",
+       "ID",
+       "CERT",
+       "CERTREQ",
+       "HASH",
+       "SIG",
+       "No",
+       "N",
+       "D",
+       "V",
+       "CP");
+ ENUM_NEXT(payload_type_short_names, NAT_D_V1, NAT_OA_V1, CONFIGURATION_V1,
+       "NAT-D",
+       "NAT-OA");
 -ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NAT_OA_V1,
++ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METHOD, NAT_OA_V1,
        "SA",
        "KE",
        "IDi",
        "TSr",
        "E",
        "CP",
 -      "EAP");
 +      "EAP",
 +      "GSPM");
  #ifdef ME
 -ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION,
 +ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER,
 +                                                                      GENERIC_SECURE_PASSWORD_METHOD,
        "IDp");
- ENUM_NEXT(payload_type_short_names, HEADER, CONFIGURATION_ATTRIBUTE, ID_PEER,
+ ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, ID_PEER,
        "HDR",
        "PROP",
+       "PROP",
+       "TRANS",
        "TRANS",
        "TRANSATTR",
+       "TRANSATTR",
        "TSSUB",
-       "CPATTR");
+       "CATTR",
+       "CATTR",
+       "E");
  #else
- ENUM_NEXT(payload_type_short_names, HEADER, CONFIGURATION_ATTRIBUTE,
-                                                                       GENERIC_SECURE_PASSWORD_METHOD,
 -ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, EXTENSIBLE_AUTHENTICATION,
++ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD,
        "HDR",
        "PROP",
+       "PROP",
+       "TRANS",
        "TRANS",
        "TRANSATTR",
+       "TRANSATTR",
        "TSSUB",
-       "CPATTR");
+       "CATTR",
+       "CATTR",
+       "E");
  #endif /* ME */
- ENUM_END(payload_type_short_names, CONFIGURATION_ATTRIBUTE);
+ ENUM_END(payload_type_short_names, ENCRYPTED_V1);
  
  /*
   * see header
@@@ -308,13 -271,15 +310,14 @@@ static bool drop_ike_sa_init(private_re
        half_open = charon->ike_sa_manager->get_half_open_count(
                                                                                charon->ike_sa_manager, NULL);
  
-       /* check for cookies */
-       if (cookie_required(this, half_open, now) && !check_cookie(this, message))
+       /* check for cookies in IKEv2 */
+       if (message->get_major_version(message) == IKEV2_MAJOR_VERSION &&
 -              this->cookie_threshold && half_open >= this->cookie_threshold &&
 -              !check_cookie(this, message))
++              cookie_required(this, half_open, now) && !check_cookie(this, message))
        {
 -              u_int32_t now = time_monotonic(NULL);
 -              chunk_t cookie = cookie_build(this, message, now - this->secret_offset,
 -                                                                        chunk_from_thing(this->secret));
 +              chunk_t cookie;
  
 +              cookie = cookie_build(this, message, now - this->secret_offset,
 +                                                        chunk_from_thing(this->secret));
                DBG2(DBG_NET, "received packet from: %#H to %#H",
                         message->get_source(message),
                         message->get_destination(message));
Simple merge
@@@ -264,10 -263,9 +264,9 @@@ static auth_cfg_t *build_auth_cfg(priva
  {
        identification_t *identity;
        certificate_t *certificate;
 -      char *auth, *id, *cert, *ca;
 +      char *auth, *id, *pubkey, *cert, *ca;
        stroke_end_t *end, *other_end;
        auth_cfg_t *cfg;
-       char eap_buf[32];
  
        /* select strings */
        if (local)
index 0000000,91bb771..a32b6ab
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,153 +1,154 @@@
 -ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_ECDSA_521, AUTH_DSS,
+ /*
+  * Copyright (C) 2006-2009 Martin Willi
+  * Copyright (C) 2008 Tobias Brunner
+  * 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
+  * Free Software Foundation; either version 2 of the License, or (at your
+  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+  *
+  * 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.
+  */
+ #include <string.h>
+ #include "authenticator.h"
+ #include <sa/ikev2/authenticators/pubkey_authenticator.h>
+ #include <sa/ikev2/authenticators/psk_authenticator.h>
+ #include <sa/ikev2/authenticators/eap_authenticator.h>
+ #include <sa/ikev1/authenticators/psk_v1_authenticator.h>
+ #include <sa/ikev1/authenticators/pubkey_v1_authenticator.h>
+ #include <sa/ikev1/authenticators/hybrid_authenticator.h>
+ #include <encoding/payloads/auth_payload.h>
+ ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS,
+       "RSA signature",
+       "pre-shared key",
+       "DSS signature");
 -      "ECDSA-521 signature");
 -ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_ECDSA_521,
++ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_GSPM, AUTH_DSS,
+       "ECDSA-256 signature",
+       "ECDSA-384 signature",
++      "ECDSA-521 signature",
++      "secure password method");
++ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_GSPM,
+       "XAuthInitPSK",
+       "XAuthRespPSK",
+       "XAuthInitRSA",
+       "XauthRespRSA",
+       "HybridInitRSA",
+       "HybridRespRSA",
+ );
+ ENUM_END(auth_method_names, AUTH_HYBRID_RESP_RSA);
+ #ifdef USE_IKEV2
+ /**
+  * Described in header.
+  */
+ authenticator_t *authenticator_create_builder(ike_sa_t *ike_sa, auth_cfg_t *cfg,
+                                                                       chunk_t received_nonce, chunk_t sent_nonce,
+                                                                       chunk_t received_init, chunk_t sent_init,
+                                                                       char reserved[3])
+ {
+       switch ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS))
+       {
+               case AUTH_CLASS_ANY:
+                       /* defaults to PUBKEY */
+               case AUTH_CLASS_PUBKEY:
+                       return (authenticator_t*)pubkey_authenticator_create_builder(ike_sa,
+                                                                               received_nonce, sent_init, reserved);
+               case AUTH_CLASS_PSK:
+                       return (authenticator_t*)psk_authenticator_create_builder(ike_sa,
+                                                                               received_nonce, sent_init, reserved);
+               case AUTH_CLASS_EAP:
+                       return (authenticator_t*)eap_authenticator_create_builder(ike_sa,
+                                                                               received_nonce, sent_nonce,
+                                                                               received_init, sent_init, reserved);
+               default:
+                       return NULL;
+       }
+ }
+ /**
+  * Described in header.
+  */
+ authenticator_t *authenticator_create_verifier(
+                                                                       ike_sa_t *ike_sa, message_t *message,
+                                                                       chunk_t received_nonce, chunk_t sent_nonce,
+                                                                       chunk_t received_init, chunk_t sent_init,
+                                                                       char reserved[3])
+ {
+       auth_payload_t *auth_payload;
+       auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION);
+       if (auth_payload == NULL)
+       {
+               return (authenticator_t*)eap_authenticator_create_verifier(ike_sa,
+                                                                               received_nonce, sent_nonce,
+                                                                               received_init, sent_init, reserved);
+       }
+       switch (auth_payload->get_auth_method(auth_payload))
+       {
+               case AUTH_RSA:
+               case AUTH_ECDSA_256:
+               case AUTH_ECDSA_384:
+               case AUTH_ECDSA_521:
+                       return (authenticator_t*)pubkey_authenticator_create_verifier(ike_sa,
+                                                                               sent_nonce, received_init, reserved);
+               case AUTH_PSK:
+                       return (authenticator_t*)psk_authenticator_create_verifier(ike_sa,
+                                                                               sent_nonce, received_init, reserved);
+               default:
+                       return NULL;
+       }
+ }
+ #endif /* USE_IKEV2 */
+ #ifdef USE_IKEV1
+ /**
+  * Described in header.
+  */
+ authenticator_t *authenticator_create_v1(ike_sa_t *ike_sa, bool initiator,
+                                                               auth_method_t auth_method, diffie_hellman_t *dh,
+                                                               chunk_t dh_value, chunk_t sa_payload,
+                                                               chunk_t id_payload)
+ {
+       switch (auth_method)
+       {
+               case AUTH_PSK:
+               case AUTH_XAUTH_INIT_PSK:
+               case AUTH_XAUTH_RESP_PSK:
+                       return (authenticator_t*)psk_v1_authenticator_create(ike_sa,
+                                                                               initiator, dh, dh_value, sa_payload,
+                                                                               id_payload, FALSE);
+               case AUTH_RSA:
+               case AUTH_XAUTH_INIT_RSA:
+               case AUTH_XAUTH_RESP_RSA:
+                       return (authenticator_t*)pubkey_v1_authenticator_create(ike_sa,
+                                                                               initiator, dh, dh_value, sa_payload,
+                                                                               id_payload, KEY_RSA);
+               case AUTH_ECDSA_256:
+               case AUTH_ECDSA_384:
+               case AUTH_ECDSA_521:
+                       return (authenticator_t*)pubkey_v1_authenticator_create(ike_sa,
+                                                                               initiator, dh, dh_value, sa_payload,
+                                                                               id_payload, KEY_ECDSA);
+               case AUTH_HYBRID_INIT_RSA:
+               case AUTH_HYBRID_RESP_RSA:
+                       return (authenticator_t*)hybrid_authenticator_create(ike_sa,
+                                                                               initiator, dh, dh_value, sa_payload,
+                                                                               id_payload);
+               default:
+                       return NULL;
+       }
+ }
+ #endif /* USE_IKEV1 */
index 0000000,3af9391..86b42da
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,218 +1,223 @@@
+ /*
+  * Copyright (C) 2005-2009 Martin Willi
+  * Copyright (C) 2008 Tobias Brunner
+  * Copyright (C) 2005 Jan Hutter
+  * 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
+  * Free Software Foundation; either version 2 of the License, or (at your
+  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+  *
+  * 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.
+  */
+ /**
+  * @defgroup authenticator authenticator
+  * @{ @ingroup authenticators
+  */
+ #ifndef AUTHENTICATOR_H_
+ #define AUTHENTICATOR_H_
+ typedef enum auth_method_t auth_method_t;
+ typedef struct authenticator_t authenticator_t;
+ #include <library.h>
+ #include <credentials/auth_cfg.h>
+ #include <sa/ike_sa.h>
+ /**
+  * Method to use for authentication, as defined in IKEv2.
+  */
+ enum auth_method_t {
+       /**
+        * No authentication used.
+        */
+       AUTH_NONE = 0,
+       /**
+        * Computed as specified in section 2.15 of RFC using
+        * an RSA private key over a PKCS#1 padded hash.
+        */
+       AUTH_RSA = 1,
+       /**
+        * Computed as specified in section 2.15 of RFC using the
+        * shared key associated with the identity in the ID payload
+        * and the negotiated prf function
+        */
+       AUTH_PSK = 2,
+       /**
+        * Computed as specified in section 2.15 of RFC using a
+        * DSS private key over a SHA-1 hash.
+        */
+       AUTH_DSS = 3,
+       /**
+        * ECDSA with SHA-256 on the P-256 curve as specified in RFC 4754
+        */
+       AUTH_ECDSA_256 = 9,
+       /**
+        * ECDSA with SHA-384 on the P-384 curve as specified in RFC 4754
+        */
+       AUTH_ECDSA_384 = 10,
+       /**
+        * ECDSA with SHA-512 on the P-521 curve as specified in RFC 4754
+        */
+       AUTH_ECDSA_521 = 11,
+       /**
++       * Generic Secure Password Authentication Method as specified in RFC 6467
++       */
++      AUTH_GSPM = 12,
++
++      /**
+        * IKEv1 initiator XAUTH with PSK, outside of IANA range
+        */
+       AUTH_XAUTH_INIT_PSK = 256,
+       /**
+        * IKEv1 responder XAUTH with PSK, outside of IANA range
+        */
+       AUTH_XAUTH_RESP_PSK,
+       /**
+        * IKEv1 initiator XAUTH with RSA, outside of IANA range
+        */
+       AUTH_XAUTH_INIT_RSA,
+       /**
+        * IKEv1 responder XAUTH with RSA, outside of IANA range
+        */
+       AUTH_XAUTH_RESP_RSA,
+       /**
+        * IKEv1 initiator XAUTH, responder RSA, outside of IANA range
+        */
+       AUTH_HYBRID_INIT_RSA,
+       /**
+        * IKEv1 responder XAUTH, initiator RSA, outside of IANA range
+        */
+       AUTH_HYBRID_RESP_RSA,
+ };
+ /**
+  * enum names for auth_method_t.
+  */
+ extern enum_name_t *auth_method_names;
+ /**
+  * Authenticator interface implemented by the various authenticators.
+  *
+  * An authenticator implementation handles AUTH and EAP payloads. Received
+  * messages are passed to the process() method, to send authentication data
+  * the message is passed to the build() method.
+  */
+ struct authenticator_t {
+       /**
+        * Process an incoming message using the authenticator.
+        *
+        * @param message               message containing authentication payloads
+        * @return
+        *                                              - SUCCESS if authentication successful
+        *                                              - FAILED if authentication failed
+        *                                              - NEED_MORE if another exchange required
+        */
+       status_t (*process)(authenticator_t *this, message_t *message);
+       /**
+        * Attach authentication data to an outgoing message.
+        *
+        * @param message               message to add authentication data to
+        * @return
+        *                                              - SUCCESS if authentication successful
+        *                                              - FAILED if authentication failed
+        *                                              - NEED_MORE if another exchange required
+        */
+       status_t (*build)(authenticator_t *this, message_t *message);
+       /**
+        * Check if the authenticator is capable of mutual authentication.
+        *
+        * Some authenticator authenticate both peers, e.g. EAP. To support
+        * mutual authentication with only a single authenticator (EAP-only
+        * authentication), it must be mutual. This method is invoked in ike_auth
+        * to check if the given authenticator is capable of doing so.
+        */
+       bool (*is_mutual)(authenticator_t *this);
+       /**
+        * Destroy authenticator instance.
+        */
+       void (*destroy) (authenticator_t *this);
+ };
+ /**
+  * Create an IKEv2 authenticator to build signatures.
+  *
+  * @param ike_sa                      associated ike_sa
+  * @param cfg                         authentication configuration
+  * @param received_nonce      nonce received in IKE_SA_INIT
+  * @param sent_nonce          nonce sent in IKE_SA_INIT
+  * @param received_init               received IKE_SA_INIT message data
+  * @param sent_init                   sent IKE_SA_INIT message data
+  * @param reserved                    reserved bytes of the ID payload
+  * @return                                    authenticator, NULL if not supported
+  */
+ authenticator_t *authenticator_create_builder(
+                                                                       ike_sa_t *ike_sa, auth_cfg_t *cfg,
+                                                                       chunk_t received_nonce, chunk_t sent_nonce,
+                                                                       chunk_t received_init, chunk_t sent_init,
+                                                                       char reserved[3]);
+ /**
+  * Create an IKEv2 authenticator to verify signatures.
+  *
+  * @param ike_sa                      associated ike_sa
+  * @param message                     message containing authentication data
+  * @param received_nonce      nonce received in IKE_SA_INIT
+  * @param sent_nonce          nonce sent in IKE_SA_INIT
+  * @param received_init               received IKE_SA_INIT message data
+  * @param sent_init                   sent IKE_SA_INIT message data
+  * @param reserved                    reserved bytes of the ID payload
+  * @return                                    authenticator, NULL if not supported
+  */
+ authenticator_t *authenticator_create_verifier(
+                                                                       ike_sa_t *ike_sa, message_t *message,
+                                                                       chunk_t received_nonce, chunk_t sent_nonce,
+                                                                       chunk_t received_init, chunk_t sent_init,
+                                                                       char reserved[3]);
+ /**
+  * Create an IKEv1 authenticator to build and verify signatures or hash
+  * payloads.
+  *
+  * @note Due to the fixed ID, these authenticators can only be used in one
+  * direction at a time.
+  *
+  * @param ike_sa                      associated IKE_SA
+  * @param initiator                   TRUE if we are the IKE_SA initiator
+  * @param auth_method         negotiated authentication method to use
+  * @param dh                          diffie hellman key exchange
+  * @param dh_value                    others public diffie hellman value
+  * @param sa_payload          generated SA payload data, without payload header
+  * @param id_payload          encoded ID payload of peer to authenticate or verify
+  *                                                    without payload header (gets owned)
+  * @return                                    authenticator, NULL if not supported
+  */
+ authenticator_t *authenticator_create_v1(ike_sa_t *ike_sa, bool initiator,
+                                                               auth_method_t auth_method, diffie_hellman_t *dh,
+                                                               chunk_t dh_value, chunk_t sa_payload,
+                                                               chunk_t id_payload);
+ #endif /** AUTHENTICATOR_H_ @}*/
Simple merge
index 0000000,b81c5c8..2e661dc
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,709 +1,698 @@@
 -      enumerator_t *enumerator;
 -      auth_rule_t rule;
+ /*
+  * Copyright (C) 2006-2009 Martin Willi
+  * 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
+  * Free Software Foundation; either version 2 of the License, or (at your
+  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+  *
+  * 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.
+  */
+ #include "eap_authenticator.h"
+ #include <daemon.h>
+ #include <sa/ikev2/keymat_v2.h>
+ #include <sa/eap/eap_method.h>
+ #include <encoding/payloads/auth_payload.h>
+ #include <encoding/payloads/eap_payload.h>
+ typedef struct private_eap_authenticator_t private_eap_authenticator_t;
+ /**
+  * Private data of an eap_authenticator_t object.
+  */
+ struct private_eap_authenticator_t {
+       /**
+        * Public authenticator_t interface.
+        */
+       eap_authenticator_t public;
+       /**
+        * Assigned IKE_SA
+        */
+       ike_sa_t *ike_sa;
+       /**
+        * others nonce to include in AUTH calculation
+        */
+       chunk_t received_nonce;
+       /**
+        * our nonce to include in AUTH calculation
+        */
+       chunk_t sent_nonce;
+       /**
+        * others IKE_SA_INIT message data to include in AUTH calculation
+        */
+       chunk_t received_init;
+       /**
+        * our IKE_SA_INIT message data to include in AUTH calculation
+        */
+       chunk_t sent_init;
+       /**
+        * Reserved bytes of ID payload
+        */
+       char reserved[3];
+       /**
+        * Current EAP method processing
+        */
+       eap_method_t *method;
+       /**
+        * MSK used to build and verify auth payload
+        */
+       chunk_t msk;
+       /**
+        * EAP authentication method completed successfully
+        */
+       bool eap_complete;
+       /**
+        * Set if we require mutual EAP due EAP-only authentication
+        */
+       bool require_mutual;
+       /**
+        * authentication payload verified successfully
+        */
+       bool auth_complete;
+       /**
+        * generated EAP payload
+        */
+       eap_payload_t *eap_payload;
+       /**
+        * EAP identity of peer
+        */
+       identification_t *eap_identity;
+ };
+ /**
+  * load an EAP method
+  */
+ static eap_method_t *load_method(private_eap_authenticator_t *this,
+                                                       eap_type_t type, u_int32_t vendor, eap_role_t role)
+ {
+       identification_t *server, *peer, *aaa;
+       auth_cfg_t *auth;
+       if (role == EAP_SERVER)
+       {
+               server = this->ike_sa->get_my_id(this->ike_sa);
+               peer = this->ike_sa->get_other_id(this->ike_sa);
+               auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+       }
+       else
+       {
+               server = this->ike_sa->get_other_id(this->ike_sa);
+               peer = this->ike_sa->get_my_id(this->ike_sa);
+               auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+       }
+       if (this->eap_identity)
+       {
+               peer = this->eap_identity;
+       }
+       aaa = auth->get(auth, AUTH_RULE_AAA_IDENTITY);
+       if (aaa)
+       {
+               server = aaa;
+       }
+       return charon->eap->create_instance(charon->eap, type, vendor,
+                                                                               role, server, peer);
+ }
+ /**
+  * Initiate EAP conversation as server
+  */
+ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,
+                                                                                 bool do_identity)
+ {
+       auth_cfg_t *auth;
+       eap_type_t type;
+       identification_t *id;
+       u_int32_t vendor;
+       eap_payload_t *out;
+       char *action;
+       auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+       /* initiate EAP-Identity exchange if required */
+       if (!this->eap_identity && do_identity)
+       {
+               id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
+               if (id)
+               {
+                       if (id->get_type(id) == ID_ANY)
+                       {
+                               this->method = load_method(this, EAP_IDENTITY, 0, EAP_SERVER);
+                               if (this->method)
+                               {
+                                       if (this->method->initiate(this->method, &out) == NEED_MORE)
+                                       {
+                                               DBG1(DBG_IKE, "initiating %N method (id 0x%02X)",
+                                                        eap_type_names, EAP_IDENTITY,
+                                                        this->method->get_identifier(this->method));
+                                               return out;
+                                       }
+                                       this->method->destroy(this->method);
+                               }
+                               DBG1(DBG_IKE, "EAP-Identity request configured, "
+                                        "but not supported");
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "using configured EAP-Identity %Y", id);
+                               this->eap_identity = id->clone(id);
+                       }
+               }
+       }
+       /* invoke real EAP method */
+       type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE);
+       vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR);
+       action = "loading";
+       this->method = load_method(this, type, vendor, EAP_SERVER);
+       if (this->method)
+       {
+               action = "initiating";
+               type = this->method->get_type(this->method, &vendor);
+               if (this->method->initiate(this->method, &out) == NEED_MORE)
+               {
+                       if (vendor)
+                       {
+                               DBG1(DBG_IKE, "initiating EAP vendor type %d-%d method (id 0x%02X)",
+                                        type, vendor, out->get_identifier(out));
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "initiating %N method (id 0x%02X)", eap_type_names,
+                                        type, out->get_identifier(out));
+                       }
+                       return out;
+               }
+       }
+       if (vendor)
+       {
+               DBG1(DBG_IKE, "%s EAP vendor type %d-%d method failed",
+                                         action, type, vendor);
+       }
+       else
+       {
+               DBG1(DBG_IKE, "%s %N method failed", action, eap_type_names, type);
+       }
+       return eap_payload_create_code(EAP_FAILURE, 0);
+ }
+ /**
+  * Replace the existing EAP-Identity in other auth config
+  */
+ static void replace_eap_identity(private_eap_authenticator_t *this)
+ {
 -      void *ptr;
++      identification_t *eap_identity;
+       auth_cfg_t *cfg;
 -      enumerator = cfg->create_enumerator(cfg);
 -      while (enumerator->enumerate(enumerator, &rule, &ptr))
 -      {
 -              if (rule == AUTH_RULE_EAP_IDENTITY)
 -              {
 -                      cfg->replace(cfg, enumerator, AUTH_RULE_EAP_IDENTITY,
 -                                               this->eap_identity->clone(this->eap_identity));
 -                      break;
 -              }
 -      }
 -      enumerator->destroy(enumerator);
++      eap_identity = this->eap_identity->clone(this->eap_identity);
+       cfg = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
++      cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, eap_identity);
+ }
+ /**
+  * Handle EAP exchange as server
+  */
+ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
+                                                                                eap_payload_t *in)
+ {
+       eap_type_t type, received_type;
+       u_int32_t vendor, received_vendor;
+       eap_payload_t *out;
+       if (in->get_code(in) != EAP_RESPONSE)
+       {
+               DBG1(DBG_IKE, "received %N, sending %N",
+                        eap_code_names, in->get_code(in), eap_code_names, EAP_FAILURE);
+               return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
+       }
+       type = this->method->get_type(this->method, &vendor);
+       received_type = in->get_type(in, &received_vendor);
+       if (type != received_type || vendor != received_vendor)
+       {
+               if (received_vendor == 0 && received_type == EAP_NAK)
+               {
+                       DBG1(DBG_IKE, "received %N, sending %N",
+                                eap_type_names, EAP_NAK, eap_code_names, EAP_FAILURE);
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "received invalid EAP response, sending %N",
+                                eap_code_names, EAP_FAILURE);
+               }
+               return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
+       }
+       switch (this->method->process(this->method, in, &out))
+       {
+               case NEED_MORE:
+                       return out;
+               case SUCCESS:
+                       if (!vendor && type == EAP_IDENTITY)
+                       {
+                               chunk_t data;
+                               if (this->method->get_msk(this->method, &data) == SUCCESS)
+                               {
+                                       this->eap_identity = identification_create_from_data(data);
+                                       DBG1(DBG_IKE, "received EAP identity '%Y'",
+                                                this->eap_identity);
+                                       replace_eap_identity(this);
+                               }
+                               /* restart EAP exchange, but with real method */
+                               this->method->destroy(this->method);
+                               return server_initiate_eap(this, FALSE);
+                       }
+                       if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
+                       {
+                               this->msk = chunk_clone(this->msk);
+                       }
+                       if (vendor)
+                       {
+                               DBG1(DBG_IKE, "EAP vendor specific method %d-%d succeeded, "
+                                        "%sMSK established", type, vendor,
+                                        this->msk.ptr ? "" : "no ");
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "EAP method %N succeeded, %sMSK established",
+                                        eap_type_names, type, this->msk.ptr ? "" : "no ");
+                       }
+                       this->ike_sa->set_condition(this->ike_sa, COND_EAP_AUTHENTICATED,
+                                                                               TRUE);
+                       this->eap_complete = TRUE;
+                       return eap_payload_create_code(EAP_SUCCESS, in->get_identifier(in));
+               case FAILED:
+               default:
+                       if (vendor)
+                       {
+                               DBG1(DBG_IKE, "EAP vendor specific method %d-%d failed for "
+                                        "peer %Y", type, vendor,
+                                        this->ike_sa->get_other_id(this->ike_sa));
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "EAP method %N failed for peer %Y",
+                                        eap_type_names, type,
+                                        this->ike_sa->get_other_id(this->ike_sa));
+                       }
+                       return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
+       }
+ }
+ /**
+  * Processing method for a peer
+  */
+ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this,
+                                                                                eap_payload_t *in)
+ {
+       eap_type_t type;
+       u_int32_t vendor;
+       auth_cfg_t *auth;
+       eap_payload_t *out;
+       identification_t *id;
+       type = in->get_type(in, &vendor);
+       if (!vendor && type == EAP_IDENTITY)
+       {
+               DESTROY_IF(this->eap_identity);
+               auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+               id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
+               if (!id || id->get_type(id) == ID_ANY)
+               {
+                       id = this->ike_sa->get_my_id(this->ike_sa);
+               }
+               DBG1(DBG_IKE, "server requested %N (id 0x%02X), sending '%Y'",
+                        eap_type_names, type, in->get_identifier(in), id);
+               this->eap_identity = id->clone(id);
+               this->method = load_method(this, type, vendor, EAP_PEER);
+               if (this->method)
+               {
+                       if (this->method->process(this->method, in, &out) == SUCCESS)
+                       {
+                               this->method->destroy(this->method);
+                               this->method = NULL;
+                               return out;
+                       }
+                       this->method->destroy(this->method);
+                       this->method = NULL;
+               }
+               DBG1(DBG_IKE, "%N not supported, sending EAP_NAK",
+                        eap_type_names, type);
+               return eap_payload_create_nak(in->get_identifier(in));
+       }
+       if (this->method == NULL)
+       {
+               if (vendor)
+               {
+                       DBG1(DBG_IKE, "server requested vendor specific EAP method %d-%d ",
+                                                 "(id 0x%02X)", type, vendor, in->get_identifier(in));
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "server requested %N authentication (id 0x%02X)",
+                                eap_type_names, type, in->get_identifier(in));
+               }
+               this->method = load_method(this, type, vendor, EAP_PEER);
+               if (!this->method)
+               {
+                       DBG1(DBG_IKE, "EAP method not supported, sending EAP_NAK");
+                       return eap_payload_create_nak(in->get_identifier(in));
+               }
+       }
+       type = this->method->get_type(this->method, &vendor);
+       if (this->method->process(this->method, in, &out) == NEED_MORE)
+       {       /* client methods should never return SUCCESS */
+               return out;
+       }
+       if (vendor)
+       {
+               DBG1(DBG_IKE, "vendor specific EAP method %d-%d failed", type, vendor);
+       }
+       else
+       {
+               DBG1(DBG_IKE, "%N method failed", eap_type_names, type);
+       }
+       return NULL;
+ }
+ /**
+  * Verify AUTH payload
+  */
+ static bool verify_auth(private_eap_authenticator_t *this, message_t *message,
+                                               chunk_t nonce, chunk_t init)
+ {
+       auth_payload_t *auth_payload;
+       chunk_t auth_data, recv_auth_data;
+       identification_t *other_id;
+       auth_cfg_t *auth;
+       keymat_v2_t *keymat;
+       auth_payload = (auth_payload_t*)message->get_payload(message,
+                                                                                                                AUTHENTICATION);
+       if (!auth_payload)
+       {
+               DBG1(DBG_IKE, "AUTH payload missing");
+               return FALSE;
+       }
+       other_id = this->ike_sa->get_other_id(this->ike_sa);
+       keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
+       auth_data = keymat->get_psk_sig(keymat, TRUE, init, nonce,
+                                                                       this->msk, other_id, this->reserved);
+       recv_auth_data = auth_payload->get_data(auth_payload);
+       if (!auth_data.len || !chunk_equals(auth_data, recv_auth_data))
+       {
+               DBG1(DBG_IKE, "verification of AUTH payload with%s EAP MSK failed",
+                        this->msk.ptr ? "" : "out");
+               chunk_free(&auth_data);
+               return FALSE;
+       }
+       chunk_free(&auth_data);
+       DBG1(DBG_IKE, "authentication of '%Y' with %N successful",
+                other_id, auth_class_names, AUTH_CLASS_EAP);
+       this->auth_complete = TRUE;
+       auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+       auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
+       return TRUE;
+ }
+ /**
+  * Build AUTH payload
+  */
+ static void build_auth(private_eap_authenticator_t *this, message_t *message,
+                                          chunk_t nonce, chunk_t init)
+ {
+       auth_payload_t *auth_payload;
+       identification_t *my_id;
+       chunk_t auth_data;
+       keymat_v2_t *keymat;
+       my_id = this->ike_sa->get_my_id(this->ike_sa);
+       keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
+       DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N",
+                my_id, auth_class_names, AUTH_CLASS_EAP);
+       auth_data = keymat->get_psk_sig(keymat, FALSE, init, nonce,
+                                                                       this->msk, my_id, this->reserved);
+       auth_payload = auth_payload_create();
+       auth_payload->set_auth_method(auth_payload, AUTH_PSK);
+       auth_payload->set_data(auth_payload, auth_data);
+       message->add_payload(message, (payload_t*)auth_payload);
+       chunk_free(&auth_data);
+ }
+ METHOD(authenticator_t, process_server, status_t,
+       private_eap_authenticator_t *this, message_t *message)
+ {
+       eap_payload_t *eap_payload;
+       if (this->eap_complete)
+       {
+               if (!verify_auth(this, message, this->sent_nonce, this->received_init))
+               {
+                       return FAILED;
+               }
+               return NEED_MORE;
+       }
+       if (!this->method)
+       {
+               this->eap_payload = server_initiate_eap(this, TRUE);
+       }
+       else
+       {
+               eap_payload = (eap_payload_t*)message->get_payload(message,
+                                                                                                       EXTENSIBLE_AUTHENTICATION);
+               if (!eap_payload)
+               {
+                       return FAILED;
+               }
+               this->eap_payload = server_process_eap(this, eap_payload);
+       }
+       return NEED_MORE;
+ }
+ METHOD(authenticator_t, build_server, status_t,
+       private_eap_authenticator_t *this, message_t *message)
+ {
+       if (this->eap_payload)
+       {
+               eap_code_t code;
+               code = this->eap_payload->get_code(this->eap_payload);
+               message->add_payload(message, (payload_t*)this->eap_payload);
+               this->eap_payload = NULL;
+               if (code == EAP_FAILURE)
+               {
+                       return FAILED;
+               }
+               return NEED_MORE;
+       }
+       if (this->eap_complete && this->auth_complete)
+       {
+               build_auth(this, message, this->received_nonce, this->sent_init);
+               return SUCCESS;
+       }
+       return FAILED;
+ }
+ METHOD(authenticator_t, process_client, status_t,
+       private_eap_authenticator_t *this, message_t *message)
+ {
+       eap_payload_t *eap_payload;
+       if (this->eap_complete)
+       {
+               if (!verify_auth(this, message, this->sent_nonce, this->received_init))
+               {
+                       return FAILED;
+               }
+               if (this->require_mutual && !this->method->is_mutual(this->method))
+               {       /* we require mutual authentication due to EAP-only */
+                       u_int32_t vendor;
+                       DBG1(DBG_IKE, "EAP-only authentication requires a mutual and "
+                                "MSK deriving EAP method, but %N is not",
+                                eap_type_names, this->method->get_type(this->method, &vendor));
+                       return FAILED;
+               }
+               return SUCCESS;
+       }
+       eap_payload = (eap_payload_t*)message->get_payload(message,
+                                                                                                       EXTENSIBLE_AUTHENTICATION);
+       if (eap_payload)
+       {
+               switch (eap_payload->get_code(eap_payload))
+               {
+                       case EAP_REQUEST:
+                       {
+                               this->eap_payload = client_process_eap(this, eap_payload);
+                               if (this->eap_payload)
+                               {
+                                       return NEED_MORE;
+                               }
+                               return FAILED;
+                       }
+                       case EAP_SUCCESS:
+                       {
+                               eap_type_t type;
+                               u_int32_t vendor;
+                               auth_cfg_t *cfg;
+                               if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
+                               {
+                                       this->msk = chunk_clone(this->msk);
+                               }
+                               type = this->method->get_type(this->method, &vendor);
+                               if (vendor)
+                               {
+                                       DBG1(DBG_IKE, "EAP vendor specific method %d-%d succeeded, "
+                                                "%sMSK established", type, vendor,
+                                                this->msk.ptr ? "" : "no ");
+                               }
+                               else
+                               {
+                                       DBG1(DBG_IKE, "EAP method %N succeeded, %sMSK established",
+                                                eap_type_names, type, this->msk.ptr ? "" : "no ");
+                               }
+                               cfg = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+                               cfg->add(cfg, AUTH_RULE_EAP_TYPE, type);
+                               if (vendor)
+                               {
+                                       cfg->add(cfg, AUTH_RULE_EAP_VENDOR, vendor);
+                               }
+                               this->eap_complete = TRUE;
+                               return NEED_MORE;
+                       }
+                       case EAP_FAILURE:
+                       default:
+                       {
+                               DBG1(DBG_IKE, "received %N, EAP authentication failed",
+                                        eap_code_names, eap_payload->get_code(eap_payload));
+                               return FAILED;
+                       }
+               }
+       }
+       return FAILED;
+ }
+ METHOD(authenticator_t, build_client, status_t,
+       private_eap_authenticator_t *this, message_t *message)
+ {
+       if (this->eap_payload)
+       {
+               message->add_payload(message, (payload_t*)this->eap_payload);
+               this->eap_payload = NULL;
+               return NEED_MORE;
+       }
+       if (this->eap_complete)
+       {
+               build_auth(this, message, this->received_nonce, this->sent_init);
+               return NEED_MORE;
+       }
+       return NEED_MORE;
+ }
+ METHOD(authenticator_t, is_mutual, bool,
+       private_eap_authenticator_t *this)
+ {
+       /* we don't know yet, but insist on it after EAP is complete */
+       this->require_mutual = TRUE;
+       return TRUE;
+ }
+ METHOD(authenticator_t, destroy, void,
+       private_eap_authenticator_t *this)
+ {
+       DESTROY_IF(this->method);
+       DESTROY_IF(this->eap_payload);
+       DESTROY_IF(this->eap_identity);
+       chunk_free(&this->msk);
+       free(this);
+ }
+ /*
+  * Described in header.
+  */
+ eap_authenticator_t *eap_authenticator_create_builder(ike_sa_t *ike_sa,
+                                                                       chunk_t received_nonce, chunk_t sent_nonce,
+                                                                       chunk_t received_init, chunk_t sent_init,
+                                                                       char reserved[3])
+ {
+       private_eap_authenticator_t *this;
+       INIT(this,
+               .public = {
+                       .authenticator = {
+                               .build = _build_client,
+                               .process = _process_client,
+                               .is_mutual = _is_mutual,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+               .received_init = received_init,
+               .received_nonce = received_nonce,
+               .sent_init = sent_init,
+               .sent_nonce = sent_nonce,
+       );
+       memcpy(this->reserved, reserved, sizeof(this->reserved));
+       return &this->public;
+ }
+ /*
+  * Described in header.
+  */
+ eap_authenticator_t *eap_authenticator_create_verifier(ike_sa_t *ike_sa,
+                                                                       chunk_t received_nonce, chunk_t sent_nonce,
+                                                                       chunk_t received_init, chunk_t sent_init,
+                                                                       char reserved[3])
+ {
+       private_eap_authenticator_t *this;
+       INIT(this,
+               .public = {
+                       .authenticator = {
+                               .build = _build_server,
+                               .process = _process_server,
+                               .is_mutual = _is_mutual,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+               .received_init = received_init,
+               .received_nonce = received_nonce,
+               .sent_init = sent_init,
+               .sent_nonce = sent_nonce,
+       );
+       memcpy(this->reserved, reserved, sizeof(this->reserved));
+       return &this->public;
+ }
index 0000000,ba7fdd2..cc6d378
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1479 +1,1484 @@@
 -      bool delete = FALSE;
+ /*
+  * Copyright (C) 2007-2011 Tobias Brunner
+  * Copyright (C) 2007-2010 Martin Willi
+  * 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
+  * Free Software Foundation; either version 2 of the License, or (at your
+  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+  *
+  * 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.
+  */
+ #include "task_manager_v2.h"
+ #include <math.h>
+ #include <daemon.h>
+ #include <sa/ikev2/tasks/ike_init.h>
+ #include <sa/ikev2/tasks/ike_natd.h>
+ #include <sa/ikev2/tasks/ike_mobike.h>
+ #include <sa/ikev2/tasks/ike_auth.h>
+ #include <sa/ikev2/tasks/ike_auth_lifetime.h>
+ #include <sa/ikev2/tasks/ike_cert_pre.h>
+ #include <sa/ikev2/tasks/ike_cert_post.h>
+ #include <sa/ikev2/tasks/ike_rekey.h>
+ #include <sa/ikev2/tasks/ike_reauth.h>
+ #include <sa/ikev2/tasks/ike_delete.h>
+ #include <sa/ikev2/tasks/ike_config.h>
+ #include <sa/ikev2/tasks/ike_dpd.h>
+ #include <sa/ikev2/tasks/ike_vendor.h>
+ #include <sa/ikev2/tasks/child_create.h>
+ #include <sa/ikev2/tasks/child_rekey.h>
+ #include <sa/ikev2/tasks/child_delete.h>
+ #include <encoding/payloads/delete_payload.h>
+ #include <encoding/payloads/unknown_payload.h>
+ #include <processing/jobs/retransmit_job.h>
+ #include <processing/jobs/delete_ike_sa_job.h>
+ #ifdef ME
+ #include <sa/ikev2/tasks/ike_me.h>
+ #endif
+ typedef struct exchange_t exchange_t;
+ /**
+  * An exchange in the air, used do detect and handle retransmission
+  */
+ struct exchange_t {
+       /**
+        * Message ID used for this transaction
+        */
+       u_int32_t mid;
+       /**
+        * generated packet for retransmission
+        */
+       packet_t *packet;
+ };
+ typedef struct private_task_manager_t private_task_manager_t;
+ /**
+  * private data of the task manager
+  */
+ struct private_task_manager_t {
+       /**
+        * public functions
+        */
+       task_manager_v2_t public;
+       /**
+        * associated IKE_SA we are serving
+        */
+       ike_sa_t *ike_sa;
+       /**
+        * Exchange we are currently handling as responder
+        */
+       struct {
+               /**
+                * Message ID of the exchange
+                */
+               u_int32_t mid;
+               /**
+                * packet for retransmission
+                */
+               packet_t *packet;
+       } responding;
+       /**
+        * Exchange we are currently handling as initiator
+        */
+       struct {
+               /**
+                * Message ID of the exchange
+                */
+               u_int32_t mid;
+               /**
+                * how many times we have retransmitted so far
+                */
+               u_int retransmitted;
+               /**
+                * packet for retransmission
+                */
+               packet_t *packet;
+               /**
+                * type of the initated exchange
+                */
+               exchange_type_t type;
+       } initiating;
+       /**
+        * List of queued tasks not yet in action
+        */
+       linked_list_t *queued_tasks;
+       /**
+        * List of active tasks, initiated by ourselve
+        */
+       linked_list_t *active_tasks;
+       /**
+        * List of tasks initiated by peer
+        */
+       linked_list_t *passive_tasks;
+       /**
+        * the task manager has been reset
+        */
+       bool reset;
+       /**
+        * Number of times we retransmit messages before giving up
+        */
+       u_int retransmit_tries;
+       /**
+        * Retransmission timeout
+        */
+       double retransmit_timeout;
+       /**
+        * Base to calculate retransmission timeout
+        */
+       double retransmit_base;
+ };
+ /**
+  * flush all tasks in the task manager
+  */
+ static void flush(private_task_manager_t *this)
+ {
+       this->passive_tasks->destroy_offset(this->passive_tasks,
+                                                                               offsetof(task_t, destroy));
+       this->passive_tasks = linked_list_create();
+       this->active_tasks->destroy_offset(this->active_tasks,
+                                                                               offsetof(task_t, destroy));
+       this->active_tasks = linked_list_create();
+       this->queued_tasks->destroy_offset(this->queued_tasks,
+                                                                               offsetof(task_t, destroy));
+       this->queued_tasks = linked_list_create();
+ }
+ /**
+  * move a task of a specific type from the queue to the active list
+  */
+ static bool activate_task(private_task_manager_t *this, task_type_t type)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       bool found = FALSE;
+       enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+       while (enumerator->enumerate(enumerator, (void**)&task))
+       {
+               if (task->get_type(task) == type)
+               {
+                       DBG2(DBG_IKE, "  activating %N task", task_type_names, type);
+                       this->queued_tasks->remove_at(this->queued_tasks, enumerator);
+                       this->active_tasks->insert_last(this->active_tasks, task);
+                       found = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return found;
+ }
+ METHOD(task_manager_t, retransmit, status_t,
+       private_task_manager_t *this, u_int32_t message_id)
+ {
+       if (this->initiating.packet && message_id == this->initiating.mid)
+       {
+               u_int32_t timeout;
+               job_t *job;
+               enumerator_t *enumerator;
+               packet_t *packet;
+               task_t *task;
+               ike_mobike_t *mobike = NULL;
+               /* check if we are retransmitting a MOBIKE routability check */
+               enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, (void*)&task))
+               {
+                       if (task->get_type(task) == TASK_IKE_MOBIKE)
+                       {
+                               mobike = (ike_mobike_t*)task;
+                               if (!mobike->is_probing(mobike))
+                               {
+                                       mobike = NULL;
+                               }
+                               break;
+                       }
+               }
+               enumerator->destroy(enumerator);
+               if (mobike == NULL)
+               {
+                       if (this->initiating.retransmitted <= this->retransmit_tries)
+                       {
+                               timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 *
+                                       pow(this->retransmit_base, this->initiating.retransmitted));
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "giving up after %d retransmits",
+                                        this->initiating.retransmitted - 1);
+                               if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+                               {
+                                       charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               }
+                               return DESTROY_ME;
+                       }
+                       if (this->initiating.retransmitted)
+                       {
+                               DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
+                                        this->initiating.retransmitted, message_id);
+                       }
+                       packet = this->initiating.packet->clone(this->initiating.packet);
+                       charon->sender->send(charon->sender, packet);
+               }
+               else
+               {       /* for routeability checks, we use a more aggressive behavior */
+                       if (this->initiating.retransmitted <= ROUTEABILITY_CHECK_TRIES)
+                       {
+                               timeout = ROUTEABILITY_CHECK_INTERVAL;
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "giving up after %d path probings",
+                                        this->initiating.retransmitted - 1);
+                               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               return DESTROY_ME;
+                       }
+                       if (this->initiating.retransmitted)
+                       {
+                               DBG1(DBG_IKE, "path probing attempt %d",
+                                        this->initiating.retransmitted);
+                       }
+                       mobike->transmit(mobike, this->initiating.packet);
+               }
+               this->initiating.retransmitted++;
+               job = (job_t*)retransmit_job_create(this->initiating.mid,
+                                                                                       this->ike_sa->get_id(this->ike_sa));
+               lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout);
+       }
+       return SUCCESS;
+ }
+ METHOD(task_manager_t, initiate, status_t,
+       private_task_manager_t *this)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       message_t *message;
+       host_t *me, *other;
+       status_t status;
+       exchange_type_t exchange = 0;
+       if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
+       {
+               DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress",
+                               exchange_type_names, this->initiating.type);
+               /* do not initiate if we already have a message in the air */
+               return SUCCESS;
+       }
+       if (this->active_tasks->get_count(this->active_tasks) == 0)
+       {
+               DBG2(DBG_IKE, "activating new tasks");
+               switch (this->ike_sa->get_state(this->ike_sa))
+               {
+                       case IKE_CREATED:
+                               activate_task(this, TASK_IKE_VENDOR);
+                               if (activate_task(this, TASK_IKE_INIT))
+                               {
+                                       this->initiating.mid = 0;
+                                       exchange = IKE_SA_INIT;
+                                       activate_task(this, TASK_IKE_NATD);
+                                       activate_task(this, TASK_IKE_CERT_PRE);
+ #ifdef ME
+                                       /* this task has to be activated before the TASK_IKE_AUTH
+                                        * task, because that task pregenerates the packet after
+                                        * which no payloads can be added to the message anymore.
+                                        */
+                                       activate_task(this, TASK_IKE_ME);
+ #endif /* ME */
+                                       activate_task(this, TASK_IKE_AUTH);
+                                       activate_task(this, TASK_IKE_CERT_POST);
+                                       activate_task(this, TASK_IKE_CONFIG);
+                                       activate_task(this, TASK_CHILD_CREATE);
+                                       activate_task(this, TASK_IKE_AUTH_LIFETIME);
+                                       activate_task(this, TASK_IKE_MOBIKE);
+                               }
+                               break;
+                       case IKE_ESTABLISHED:
+                               if (activate_task(this, TASK_CHILD_CREATE))
+                               {
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_CHILD_DELETE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_CHILD_REKEY))
+                               {
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_DELETE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_REKEY))
+                               {
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_REAUTH))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_MOBIKE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_DPD))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_AUTH_LIFETIME))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+ #ifdef ME
+                               if (activate_task(this, TASK_IKE_ME))
+                               {
+                                       exchange = ME_CONNECT;
+                                       break;
+                               }
+ #endif /* ME */
+                       case IKE_REKEYING:
+                               if (activate_task(this, TASK_IKE_DELETE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                       case IKE_DELETING:
+                       default:
+                               break;
+               }
+       }
+       else
+       {
+               DBG2(DBG_IKE, "reinitiating already active tasks");
+               enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, (void**)&task))
+               {
+                       DBG2(DBG_IKE, "  %N task", task_type_names, task->get_type(task));
+                       switch (task->get_type(task))
+                       {
+                               case TASK_IKE_INIT:
+                                       exchange = IKE_SA_INIT;
+                                       break;
+                               case TASK_IKE_AUTH:
+                                       exchange = IKE_AUTH;
+                                       break;
+                               case TASK_CHILD_CREATE:
+                               case TASK_CHILD_REKEY:
+                               case TASK_IKE_REKEY:
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               case TASK_IKE_MOBIKE:
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               default:
+                                       continue;
+                       }
+                       break;
+               }
+               enumerator->destroy(enumerator);
+       }
+       if (exchange == 0)
+       {
+               DBG2(DBG_IKE, "nothing to initiate");
+               /* nothing to do yet... */
+               return SUCCESS;
+       }
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       other = this->ike_sa->get_other_host(this->ike_sa);
+       message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+       message->set_message_id(message, this->initiating.mid);
+       message->set_source(message, me->clone(me));
+       message->set_destination(message, other->clone(other));
+       message->set_exchange_type(message, exchange);
+       this->initiating.type = exchange;
+       this->initiating.retransmitted = 0;
+       enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->build(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->active_tasks->remove_at(this->active_tasks, enumerator);
+                               task->destroy(task);
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs another exchange */
+                               break;
+                       case FAILED:
+                       default:
+                               if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+                               {
+                                       charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               }
+                               /* FALL */
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               enumerator->destroy(enumerator);
+                               message->destroy(message);
+                               flush(this);
+                               return DESTROY_ME;
+               }
+       }
+       enumerator->destroy(enumerator);
+       /* update exchange type if a task changed it */
+       this->initiating.type = message->get_exchange_type(message);
+       status = this->ike_sa->generate_message(this->ike_sa, message,
+                                                                                       &this->initiating.packet);
+       if (status != SUCCESS)
+       {
+               /* message generation failed. There is nothing more to do than to
+                * close the SA */
+               message->destroy(message);
+               flush(this);
+               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+               return DESTROY_ME;
+       }
+       message->destroy(message);
+       return retransmit(this, this->initiating.mid);
+ }
+ /**
+  * handle an incoming response message
+  */
+ static status_t process_response(private_task_manager_t *this,
+                                                                message_t *message)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       if (message->get_exchange_type(message) != this->initiating.type)
+       {
+               DBG1(DBG_IKE, "received %N response, but expected %N",
+                        exchange_type_names, message->get_exchange_type(message),
+                        exchange_type_names, this->initiating.type);
+               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+               return DESTROY_ME;
+       }
+       /* catch if we get resetted while processing */
+       this->reset = FALSE;
+       enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->process(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->active_tasks->remove_at(this->active_tasks, enumerator);
+                               task->destroy(task);
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs another exchange */
+                               break;
+                       case FAILED:
+                       default:
+                               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               /* FALL */
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               this->active_tasks->remove_at(this->active_tasks, enumerator);
+                               enumerator->destroy(enumerator);
+                               task->destroy(task);
+                               return DESTROY_ME;
+               }
+               if (this->reset)
+               {       /* start all over again if we were reset */
+                       this->reset = FALSE;
+                       enumerator->destroy(enumerator);
+                       return initiate(this);
+               }
+       }
+       enumerator->destroy(enumerator);
+       this->initiating.mid++;
+       this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+       this->initiating.packet->destroy(this->initiating.packet);
+       this->initiating.packet = NULL;
+       return initiate(this);
+ }
+ /**
+  * handle exchange collisions
+  */
+ static bool handle_collisions(private_task_manager_t *this, task_t *task)
+ {
+       enumerator_t *enumerator;
+       task_t *active;
+       task_type_t type;
+       type = task->get_type(task);
+       /* do we have to check  */
+       if (type == TASK_IKE_REKEY || type == TASK_CHILD_REKEY ||
+               type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE ||
+               type == TASK_IKE_REAUTH)
+       {
+               /* find an exchange collision, and notify these tasks */
+               enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, (void**)&active))
+               {
+                       switch (active->get_type(active))
+                       {
+                               case TASK_IKE_REKEY:
+                                       if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE ||
+                                               type == TASK_IKE_REAUTH)
+                                       {
+                                               ike_rekey_t *rekey = (ike_rekey_t*)active;
+                                               rekey->collide(rekey, task);
+                                               break;
+                                       }
+                                       continue;
+                               case TASK_CHILD_REKEY:
+                                       if (type == TASK_CHILD_REKEY || type == TASK_CHILD_DELETE)
+                                       {
+                                               child_rekey_t *rekey = (child_rekey_t*)active;
+                                               rekey->collide(rekey, task);
+                                               break;
+                                       }
+                                       continue;
+                               default:
+                                       continue;
+                       }
+                       enumerator->destroy(enumerator);
+                       return TRUE;
+               }
+               enumerator->destroy(enumerator);
+       }
+       return FALSE;
+ }
+ /**
+  * build a response depending on the "passive" task list
+  */
+ static status_t build_response(private_task_manager_t *this, message_t *request)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       message_t *message;
+       host_t *me, *other;
 -                      case DESTROY_ME:
++      bool delete = FALSE, hook = FALSE;
+       status_t status;
+       me = request->get_destination(request);
+       other = request->get_source(request);
+       message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+       message->set_exchange_type(message, request->get_exchange_type(request));
+       /* send response along the path the request came in */
+       message->set_source(message, me->clone(me));
+       message->set_destination(message, other->clone(other));
+       message->set_message_id(message, this->responding.mid);
+       message->set_request(message, FALSE);
+       enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->build(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+                               if (!handle_collisions(this, task))
+                               {
+                                       task->destroy(task);
+                               }
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs another exchange */
+                               if (handle_collisions(this, task))
+                               {
+                                       this->passive_tasks->remove_at(this->passive_tasks,
+                                                                                                  enumerator);
+                               }
+                               break;
 -              charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                       case FAILED:
+                       default:
++                              hook = TRUE;
++                              /* FALL */
++                      case DESTROY_ME:
+                               /* destroy IKE_SA, but SEND response first */
+                               delete = TRUE;
+                               break;
+               }
+               if (delete)
+               {
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       /* remove resonder SPI if IKE_SA_INIT failed */
+       if (delete && request->get_exchange_type(request) == IKE_SA_INIT)
+       {
+               ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa);
+               id->set_responder_spi(id, 0);
+       }
+       /* message complete, send it */
+       DESTROY_IF(this->responding.packet);
+       this->responding.packet = NULL;
+       status = this->ike_sa->generate_message(this->ike_sa, message,
+                                                                                       &this->responding.packet);
+       message->destroy(message);
+       if (status != SUCCESS)
+       {
+               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+               return DESTROY_ME;
+       }
+       charon->sender->send(charon->sender,
+                                                this->responding.packet->clone(this->responding.packet));
+       if (delete)
+       {
++              if (hook)
++              {
++                      charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
++              }
+               return DESTROY_ME;
+       }
+       return SUCCESS;
+ }
+ /**
+  * handle an incoming request message
+  */
+ static status_t process_request(private_task_manager_t *this,
+                                                               message_t *message)
+ {
+       enumerator_t *enumerator;
+       task_t *task = NULL;
+       payload_t *payload;
+       notify_payload_t *notify;
+       delete_payload_t *delete;
+       if (this->passive_tasks->get_count(this->passive_tasks) == 0)
+       {       /* create tasks depending on request type, if not already some queued */
+               switch (message->get_exchange_type(message))
+               {
+                       case IKE_SA_INIT:
+                       {
+                               task = (task_t*)ike_vendor_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_natd_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+ #ifdef ME
+                               task = (task_t*)ike_me_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+ #endif /* ME */
+                               task = (task_t*)ike_auth_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_cert_post_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_config_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)child_create_create(this->ike_sa, NULL, FALSE,
+                                                                                                       NULL, NULL);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_mobike_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               break;
+                       }
+                       case CREATE_CHILD_SA:
+                       {       /* FIXME: we should prevent this on mediation connections */
+                               bool notify_found = FALSE, ts_found = FALSE;
+                               enumerator = message->create_payload_enumerator(message);
+                               while (enumerator->enumerate(enumerator, &payload))
+                               {
+                                       switch (payload->get_type(payload))
+                                       {
+                                               case NOTIFY:
+                                               {       /* if we find a rekey notify, its CHILD_SA rekeying */
+                                                       notify = (notify_payload_t*)payload;
+                                                       if (notify->get_notify_type(notify) == REKEY_SA &&
+                                                               (notify->get_protocol_id(notify) == PROTO_AH ||
+                                                                notify->get_protocol_id(notify) == PROTO_ESP))
+                                                       {
+                                                               notify_found = TRUE;
+                                                       }
+                                                       break;
+                                               }
+                                               case TRAFFIC_SELECTOR_INITIATOR:
+                                               case TRAFFIC_SELECTOR_RESPONDER:
+                                               {       /* if we don't find a TS, its IKE rekeying */
+                                                       ts_found = TRUE;
+                                                       break;
+                                               }
+                                               default:
+                                                       break;
+                                       }
+                               }
+                               enumerator->destroy(enumerator);
+                               if (ts_found)
+                               {
+                                       if (notify_found)
+                                       {
+                                               task = (task_t*)child_rekey_create(this->ike_sa,
+                                                                                                                  PROTO_NONE, 0);
+                                       }
+                                       else
+                                       {
+                                               task = (task_t*)child_create_create(this->ike_sa, NULL,
+                                                                                                                       FALSE, NULL, NULL);
+                                       }
+                               }
+                               else
+                               {
+                                       task = (task_t*)ike_rekey_create(this->ike_sa, FALSE);
+                               }
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               break;
+                       }
+                       case INFORMATIONAL:
+                       {
+                               enumerator = message->create_payload_enumerator(message);
+                               while (enumerator->enumerate(enumerator, &payload))
+                               {
+                                       switch (payload->get_type(payload))
+                                       {
+                                               case NOTIFY:
+                                               {
+                                                       notify = (notify_payload_t*)payload;
+                                                       switch (notify->get_notify_type(notify))
+                                                       {
+                                                               case ADDITIONAL_IP4_ADDRESS:
+                                                               case ADDITIONAL_IP6_ADDRESS:
+                                                               case NO_ADDITIONAL_ADDRESSES:
+                                                               case UPDATE_SA_ADDRESSES:
+                                                               case NO_NATS_ALLOWED:
+                                                               case UNACCEPTABLE_ADDRESSES:
+                                                               case UNEXPECTED_NAT_DETECTED:
+                                                               case COOKIE2:
+                                                               case NAT_DETECTION_SOURCE_IP:
+                                                               case NAT_DETECTION_DESTINATION_IP:
+                                                                       task = (task_t*)ike_mobike_create(
+                                                                                                                       this->ike_sa, FALSE);
+                                                                       break;
+                                                               case AUTH_LIFETIME:
+                                                                       task = (task_t*)ike_auth_lifetime_create(
+                                                                                                                       this->ike_sa, FALSE);
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                       }
+                                                       break;
+                                               }
+                                               case DELETE:
+                                               {
+                                                       delete = (delete_payload_t*)payload;
+                                                       if (delete->get_protocol_id(delete) == PROTO_IKE)
+                                                       {
+                                                               task = (task_t*)ike_delete_create(this->ike_sa,
+                                                                                                                               FALSE);
+                                                       }
+                                                       else
+                                                       {
+                                                               task = (task_t*)child_delete_create(this->ike_sa,
+                                                                                                               PROTO_NONE, 0, FALSE);
+                                                       }
+                                                       break;
+                                               }
+                                               default:
+                                                       break;
+                                       }
+                                       if (task)
+                                       {
+                                               break;
+                                       }
+                               }
+                               enumerator->destroy(enumerator);
+                               if (task == NULL)
+                               {
+                                       task = (task_t*)ike_dpd_create(FALSE);
+                               }
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               break;
+                       }
+ #ifdef ME
+                       case ME_CONNECT:
+                       {
+                               task = (task_t*)ike_me_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                       }
+ #endif /* ME */
+                       default:
+                               break;
+               }
+       }
+       /* let the tasks process the message */
+       enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->process(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+                               task->destroy(task);
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs at least another call to build() */
+                               break;
+                       case FAILED:
+                       default:
+                               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               /* FALL */
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+                               enumerator->destroy(enumerator);
+                               task->destroy(task);
+                               return DESTROY_ME;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return build_response(this, message);
+ }
+ METHOD(task_manager_t, incr_mid, void,
+       private_task_manager_t *this, bool initiate)
+ {
+       if (initiate)
+       {
+               this->initiating.mid++;
+       }
+       else
+       {
+               this->responding.mid++;
+       }
+ }
+ /**
+  * Send a notify back to the sender
+  */
+ static void send_notify_response(private_task_manager_t *this,
+                                                                message_t *request, notify_type_t type,
+                                                                chunk_t data)
+ {
+       message_t *response;
+       packet_t *packet;
+       host_t *me, *other;
+       response = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+       response->set_exchange_type(response, request->get_exchange_type(request));
+       response->set_request(response, FALSE);
+       response->set_message_id(response, request->get_message_id(request));
+       response->add_notify(response, FALSE, type, data);
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       if (me->is_anyaddr(me))
+       {
+               me = request->get_destination(request);
+               this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
+       }
+       other = this->ike_sa->get_other_host(this->ike_sa);
+       if (other->is_anyaddr(other))
+       {
+               other = request->get_source(request);
+               this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
+       }
+       response->set_source(response, me->clone(me));
+       response->set_destination(response, other->clone(other));
+       if (this->ike_sa->generate_message(this->ike_sa, response,
+                                                                          &packet) == SUCCESS)
+       {
+               charon->sender->send(charon->sender, packet);
+       }
+       response->destroy(response);
+ }
+ /**
+  * Parse the given message and verify that it is valid.
+  */
+ static status_t parse_message(private_task_manager_t *this, message_t *msg)
+ {
+       status_t status;
+       u_int8_t type = 0;
+       status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa));
+       if (status == SUCCESS)
+       {       /* check for unsupported critical payloads */
+               enumerator_t *enumerator;
+               unknown_payload_t *unknown;
+               payload_t *payload;
+               enumerator = msg->create_payload_enumerator(msg);
+               while (enumerator->enumerate(enumerator, &payload))
+               {
+                       unknown = (unknown_payload_t*)payload;
+                       type = payload->get_type(payload);
+                       if (!payload_is_known(type) &&
+                               unknown->is_critical(unknown))
+                       {
+                               DBG1(DBG_ENC, "payload type %N is not supported, "
+                                        "but its critical!", payload_type_names, type);
+                               status = NOT_SUPPORTED;
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+       if (status != SUCCESS)
+       {
+               bool is_request = msg->get_request(msg);
+               switch (status)
+               {
+                       case NOT_SUPPORTED:
+                               DBG1(DBG_IKE, "critical unknown payloads found");
+                               if (is_request)
+                               {
+                                       send_notify_response(this, msg,
+                                                                                UNSUPPORTED_CRITICAL_PAYLOAD,
+                                                                                chunk_from_thing(type));
+                                       incr_mid(this, FALSE);
+                               }
+                               break;
+                       case PARSE_ERROR:
+                               DBG1(DBG_IKE, "message parsing failed");
+                               if (is_request)
+                               {
+                                       send_notify_response(this, msg,
+                                                                                INVALID_SYNTAX, chunk_empty);
+                                       incr_mid(this, FALSE);
+                               }
+                               break;
+                       case VERIFY_ERROR:
+                               DBG1(DBG_IKE, "message verification failed");
+                               if (is_request)
+                               {
+                                       send_notify_response(this, msg,
+                                                                                INVALID_SYNTAX, chunk_empty);
+                                       incr_mid(this, FALSE);
+                               }
+                               break;
+                       case FAILED:
+                               DBG1(DBG_IKE, "integrity check failed");
+                               /* ignored */
+                               break;
+                       case INVALID_STATE:
+                               DBG1(DBG_IKE, "found encrypted message, but no keys available");
+                       default:
+                               break;
+               }
+               DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
+                        exchange_type_names, msg->get_exchange_type(msg),
+                        is_request ? "request" : "response",
+                        msg->get_message_id(msg));
+               if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED)
+               {       /* invalid initiation attempt, close SA */
+                       return DESTROY_ME;
+               }
+       }
+       return status;
+ }
+ METHOD(task_manager_t, process_message, status_t,
+       private_task_manager_t *this, message_t *msg)
+ {
+       host_t *me, *other;
+       status_t status;
+       u_int32_t mid;
+       charon->bus->message(charon->bus, msg, TRUE, FALSE);
+       status = parse_message(this, msg);
+       if (status != SUCCESS)
+       {
+               return status;
+       }
+       me = msg->get_destination(msg);
+       other = msg->get_source(msg);
+       /* if this IKE_SA is virgin, we check for a config */
+       if (this->ike_sa->get_ike_cfg(this->ike_sa) == NULL)
+       {
+               ike_sa_id_t *ike_sa_id;
+               ike_cfg_t *ike_cfg;
+               job_t *job;
+               ike_cfg = charon->backends->get_ike_cfg(charon->backends, me, other);
+               if (ike_cfg == NULL)
+               {
+                       /* no config found for these hosts, destroy */
+                       DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N",
+                                me, other, notify_type_names, NO_PROPOSAL_CHOSEN);
+                       send_notify_response(this, msg,
+                                                                NO_PROPOSAL_CHOSEN, chunk_empty);
+                       return DESTROY_ME;
+               }
+               this->ike_sa->set_ike_cfg(this->ike_sa, ike_cfg);
+               ike_cfg->destroy(ike_cfg);
+               /* add a timeout if peer does not establish it completely */
+               ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+               job = (job_t*)delete_ike_sa_job_create(ike_sa_id, FALSE);
+               lib->scheduler->schedule_job(lib->scheduler, job,
+                               lib->settings->get_int(lib->settings,
+                                       "charon.half_open_timeout",  HALF_OPEN_IKE_SA_TIMEOUT));
+       }
+       this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND,
+                                                               time_monotonic(NULL));
+       mid = msg->get_message_id(msg);
+       if (msg->get_request(msg))
+       {
+               if (mid == this->responding.mid)
+               {
+                       if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+                               this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+                               msg->get_exchange_type(msg) != IKE_SA_INIT)
+                       {       /* only do host updates based on verified messages */
+                               if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+                               {       /* with MOBIKE, we do no implicit updates */
+                                       this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
+                               }
+                       }
+                       charon->bus->message(charon->bus, msg, TRUE, TRUE);
+                       if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+                       {       /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+                               return SUCCESS;
+                       }
+                       if (process_request(this, msg) != SUCCESS)
+                       {
+                               flush(this);
+                               return DESTROY_ME;
+                       }
+                       this->responding.mid++;
+               }
+               else if ((mid == this->responding.mid - 1) && this->responding.packet)
+               {
+                       packet_t *clone;
+                       host_t *host;
+                       DBG1(DBG_IKE, "received retransmit of request with ID %d, "
+                                "retransmitting response", mid);
+                       clone = this->responding.packet->clone(this->responding.packet);
+                       host = msg->get_destination(msg);
+                       clone->set_source(clone, host->clone(host));
+                       host = msg->get_source(msg);
+                       clone->set_destination(clone, host->clone(host));
+                       charon->sender->send(charon->sender, clone);
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+                                mid, this->responding.mid);
+               }
+       }
+       else
+       {
+               if (mid == this->initiating.mid)
+               {
+                       if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+                               this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+                               msg->get_exchange_type(msg) != IKE_SA_INIT)
+                       {       /* only do host updates based on verified messages */
+                               if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+                               {       /* with MOBIKE, we do no implicit updates */
+                                       this->ike_sa->update_hosts(this->ike_sa, me, other, FALSE);
+                               }
+                       }
+                       charon->bus->message(charon->bus, msg, TRUE, TRUE);
+                       if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+                       {       /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+                               return SUCCESS;
+                       }
+                       if (process_response(this, msg) != SUCCESS)
+                       {
+                               flush(this);
+                               return DESTROY_ME;
+                       }
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+                                mid, this->initiating.mid);
+                       return SUCCESS;
+               }
+       }
+       return SUCCESS;
+ }
+ METHOD(task_manager_t, queue_task, void,
+       private_task_manager_t *this, task_t *task)
+ {
+       if (task->get_type(task) == TASK_IKE_MOBIKE)
+       {       /*  there is no need to queue more than one mobike task */
+               enumerator_t *enumerator;
+               task_t *current;
+               enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+               while (enumerator->enumerate(enumerator, (void**)&current))
+               {
+                       if (current->get_type(current) == TASK_IKE_MOBIKE)
+                       {
+                               enumerator->destroy(enumerator);
+                               task->destroy(task);
+                               return;
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+       DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+       this->queued_tasks->insert_last(this->queued_tasks, task);
+ }
+ /**
+  * Check if a given task has been queued already
+  */
+ static bool has_queued(private_task_manager_t *this, task_type_t type)
+ {
+       enumerator_t *enumerator;
+       bool found = FALSE;
+       task_t *task;
+       enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (task->get_type(task) == type)
+               {
+                       found = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return found;
+ }
+ METHOD(task_manager_t, queue_ike, void,
+       private_task_manager_t *this)
+ {
+       if (!has_queued(this, TASK_IKE_VENDOR))
+       {
+               queue_task(this, (task_t*)ike_vendor_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_INIT))
+       {
+               queue_task(this, (task_t*)ike_init_create(this->ike_sa, TRUE, NULL));
+       }
+       if (!has_queued(this, TASK_IKE_NATD))
+       {
+               queue_task(this, (task_t*)ike_natd_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_CERT_PRE))
+       {
+               queue_task(this, (task_t*)ike_cert_pre_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_AUTH))
+       {
+               queue_task(this, (task_t*)ike_auth_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_CERT_POST))
+       {
+               queue_task(this, (task_t*)ike_cert_post_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_CONFIG))
+       {
+               queue_task(this, (task_t*)ike_config_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_AUTH_LIFETIME))
+       {
+               queue_task(this, (task_t*)ike_auth_lifetime_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_MOBIKE))
+       {
+               peer_cfg_t *peer_cfg;
+               peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+               if (peer_cfg->use_mobike(peer_cfg))
+               {
+                       queue_task(this, (task_t*)ike_mobike_create(this->ike_sa, TRUE));
+               }
+       }
+ #ifdef ME
+       if (!has_queued(this, TASK_IKE_ME))
+       {
+               queue_task(this, (task_t*)ike_me_create(this->ike_sa, TRUE));
+       }
+ #endif /* ME */
+ }
+ METHOD(task_manager_t, queue_ike_rekey, void,
+       private_task_manager_t *this)
+ {
+       queue_task(this, (task_t*)ike_rekey_create(this->ike_sa, TRUE));
+ }
+ METHOD(task_manager_t, queue_ike_reauth, void,
+       private_task_manager_t *this)
+ {
+       queue_task(this, (task_t*)ike_reauth_create(this->ike_sa));
+ }
+ METHOD(task_manager_t, queue_ike_delete, void,
+       private_task_manager_t *this)
+ {
+       queue_task(this, (task_t*)ike_delete_create(this->ike_sa, TRUE));
+ }
+ METHOD(task_manager_t, queue_mobike, void,
+       private_task_manager_t *this, bool roam, bool address)
+ {
+       ike_mobike_t *mobike;
+       mobike = ike_mobike_create(this->ike_sa, TRUE);
+       if (roam)
+       {
+               mobike->roam(mobike, address);
+       }
+       else
+       {
+               mobike->addresses(mobike);
+       }
+       queue_task(this, &mobike->task);
+ }
+ METHOD(task_manager_t, queue_child, void,
+       private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid,
+       traffic_selector_t *tsi, traffic_selector_t *tsr)
+ {
+       child_create_t *task;
+       task = child_create_create(this->ike_sa, cfg, FALSE, tsi, tsr);
+       if (reqid)
+       {
+               task->use_reqid(task, reqid);
+       }
+       queue_task(this, &task->task);
+ }
+ METHOD(task_manager_t, queue_child_rekey, void,
+       private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi)
+ {
+       queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi));
+ }
+ METHOD(task_manager_t, queue_child_delete, void,
+       private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi,
+       bool expired)
+ {
+       queue_task(this, (task_t*)child_delete_create(this->ike_sa,
+                                                                                                 protocol, spi, expired));
+ }
+ METHOD(task_manager_t, queue_dpd, void,
+       private_task_manager_t *this)
+ {
+       ike_mobike_t *mobike;
+       if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE) &&
+               this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE))
+       {
+               /* use mobike enabled DPD to detect NAT mapping changes */
+               mobike = ike_mobike_create(this->ike_sa, TRUE);
+               mobike->dpd(mobike);
+               queue_task(this, &mobike->task);
+       }
+       else
+       {
+               queue_task(this, (task_t*)ike_dpd_create(TRUE));
+       }
+ }
+ METHOD(task_manager_t, adopt_tasks, void,
+       private_task_manager_t *this, task_manager_t *other_public)
+ {
+       private_task_manager_t *other = (private_task_manager_t*)other_public;
+       task_t *task;
+       /* move queued tasks from other to this */
+       while (other->queued_tasks->remove_last(other->queued_tasks,
+                                                                                               (void**)&task) == SUCCESS)
+       {
+               DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
+               task->migrate(task, this->ike_sa);
+               this->queued_tasks->insert_first(this->queued_tasks, task);
+       }
+ }
+ METHOD(task_manager_t, busy, bool,
+       private_task_manager_t *this)
+ {
+       return (this->active_tasks->get_count(this->active_tasks) > 0);
+ }
+ METHOD(task_manager_t, reset, void,
+       private_task_manager_t *this, u_int32_t initiate, u_int32_t respond)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       /* reset message counters and retransmit packets */
+       DESTROY_IF(this->responding.packet);
+       DESTROY_IF(this->initiating.packet);
+       this->responding.packet = NULL;
+       this->initiating.packet = NULL;
+       if (initiate != UINT_MAX)
+       {
+               this->initiating.mid = initiate;
+       }
+       if (respond != UINT_MAX)
+       {
+               this->responding.mid = respond;
+       }
+       this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+       /* reset queued tasks */
+       enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               task->migrate(task, this->ike_sa);
+       }
+       enumerator->destroy(enumerator);
+       /* reset active tasks */
+       while (this->active_tasks->remove_last(this->active_tasks,
+                                                                                  (void**)&task) == SUCCESS)
+       {
+               task->migrate(task, this->ike_sa);
+               this->queued_tasks->insert_first(this->queued_tasks, task);
+       }
+       this->reset = TRUE;
+ }
+ METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
+       private_task_manager_t *this, task_queue_t queue)
+ {
+       switch (queue)
+       {
+               case TASK_QUEUE_ACTIVE:
+                       return this->active_tasks->create_enumerator(this->active_tasks);
+               case TASK_QUEUE_PASSIVE:
+                       return this->passive_tasks->create_enumerator(this->passive_tasks);
+               case TASK_QUEUE_QUEUED:
+                       return this->queued_tasks->create_enumerator(this->queued_tasks);
+               default:
+                       return enumerator_create_empty();
+       }
+ }
+ METHOD(task_manager_t, destroy, void,
+       private_task_manager_t *this)
+ {
+       flush(this);
+       this->active_tasks->destroy(this->active_tasks);
+       this->queued_tasks->destroy(this->queued_tasks);
+       this->passive_tasks->destroy(this->passive_tasks);
+       DESTROY_IF(this->responding.packet);
+       DESTROY_IF(this->initiating.packet);
+       free(this);
+ }
+ /*
+  * see header file
+  */
+ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
+ {
+       private_task_manager_t *this;
+       INIT(this,
+               .public = {
+                       .task_manager = {
+                               .process_message = _process_message,
+                               .queue_task = _queue_task,
+                               .queue_ike = _queue_ike,
+                               .queue_ike_rekey = _queue_ike_rekey,
+                               .queue_ike_reauth = _queue_ike_reauth,
+                               .queue_ike_delete = _queue_ike_delete,
+                               .queue_mobike = _queue_mobike,
+                               .queue_child = _queue_child,
+                               .queue_child_rekey = _queue_child_rekey,
+                               .queue_child_delete = _queue_child_delete,
+                               .queue_dpd = _queue_dpd,
+                               .initiate = _initiate,
+                               .retransmit = _retransmit,
+                               .incr_mid = _incr_mid,
+                               .reset = _reset,
+                               .adopt_tasks = _adopt_tasks,
+                               .busy = _busy,
+                               .create_task_enumerator = _create_task_enumerator,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+               .initiating.type = EXCHANGE_TYPE_UNDEFINED,
+               .queued_tasks = linked_list_create(),
+               .active_tasks = linked_list_create(),
+               .passive_tasks = linked_list_create(),
+               .retransmit_tries = lib->settings->get_int(lib->settings,
+                                                               "charon.retransmit_tries", RETRANSMIT_TRIES),
+               .retransmit_timeout = lib->settings->get_double(lib->settings,
+                                                               "charon.retransmit_timeout", RETRANSMIT_TIMEOUT),
+               .retransmit_base = lib->settings->get_double(lib->settings,
+                                                               "charon.retransmit_base", RETRANSMIT_BASE),
+       );
+       return &this->public;
+ }
index 0000000,183ca34..6af0b37
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1098 +1,1108 @@@
 -              if (!idi)
 -              {
 -                      DBG1(DBG_CFG, "configuration misses IDi");
 -                      return FAILED;
+ /*
++ * Copyright (C) 2012 Tobias Brunner
+  * Copyright (C) 2005-2009 Martin Willi
+  * Copyright (C) 2005 Jan Hutter
+  * 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
+  * Free Software Foundation; either version 2 of the License, or (at your
+  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+  *
+  * 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
+  */
+ #include "ike_auth.h"
+ #include <string.h>
+ #include <daemon.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/ikev2/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;
+       /**
+        * Reserved bytes of ID payload
+        */
+       char reserved[3];
+       /**
+        * currently active authenticator, to authenticate us
+        */
+       authenticator_t *my_auth;
+       /**
+        * currently active authenticator, to authenticate peer
+        */
+       authenticator_t *other_auth;
+       /**
+        * peer_cfg candidates, ordered by priority
+        */
+       linked_list_t *candidates;
+       /**
+        * selected peer config (might change when using multiple authentications)
+        */
+       peer_cfg_t *peer_cfg;
+       /**
+        * have we planned an(other) authentication exchange?
+        */
+       bool do_another_auth;
+       /**
+        * has the peer announced another authentication exchange?
+        */
+       bool expect_another_auth;
+       /**
+        * should we send a AUTHENTICATION_FAILED notify?
+        */
+       bool authentication_failed;
+       /**
+        * received an INITIAL_CONTACT?
+        */
+       bool initial_contact;
+ };
+ /**
+  * check if multiple authentication extension is enabled, configuration-wise
+  */
+ static bool multiple_auth_enabled()
+ {
+       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)
+ {
+       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)
+       {
+               return FAILED;
+       }
+       this->my_nonce = nonce->get_nonce(nonce);
+       /* 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;
+ }
+ /**
+  * 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)
+ {
+       /* 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)
+       {
+               return FAILED;
+       }
+       this->other_nonce = nonce->get_nonce(nonce);
+       /* keep a copy of the received packet */
+       this->other_packet = message->get_packet(message);
+       return NEED_MORE;
+ }
+ /**
+  * Get and store reserved bytes of id_payload, required for AUTH payload
+  */
+ static void get_reserved_id_bytes(private_ike_auth_t *this, id_payload_t *id)
+ {
+       u_int8_t *byte;
+       int i;
+       for (i = 0; i < countof(this->reserved); i++)
+       {
+               byte = payload_get_field(&id->payload_interface, RESERVED_BYTE, i);
+               if (byte)
+               {
+                       this->reserved[i] = *byte;
+               }
+       }
+ }
+ /**
+  * Get the next authentication configuration
+  */
+ static auth_cfg_t *get_auth_cfg(private_ike_auth_t *this, bool local)
+ {
+       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))
+       {
+               bool found = FALSE;
+               e2 = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, local);
+               while (e2->enumerate(e2, &c2))
+               {
+                       if (c2->complies(c2, c1, FALSE))
+                       {
+                               found = TRUE;
+                               break;
+                       }
+               }
+               e2->destroy(e2);
+               if (!found)
+               {
+                       next = c1;
+                       break;
+               }
+       }
+       e1->destroy(e1);
+       return next;
+ }
+ /**
+  * Check if we have should initiate another authentication round
+  */
+ static bool do_another_auth(private_ike_auth_t *this)
+ {
+       bool do_another = FALSE;
+       enumerator_t *done, *todo;
+       auth_cfg_t *done_cfg, *todo_cfg;
+       if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH))
+       {
+               return FALSE;
+       }
+       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))
+       {
+               if (!done->enumerate(done, &done_cfg))
+               {
+                       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;
+               }
+       }
+       done->destroy(done);
+       todo->destroy(todo);
+       return do_another;
+ }
+ /**
+  * Get peer configuration candidates from backends
+  */
+ static bool load_cfg_candidates(private_ike_auth_t *this)
+ {
+       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);
+       DBG1(DBG_CFG, "looking for peer configs matching %H[%Y]...%H[%Y]",
+                me, my_id, other, other_id);
+       enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
+                                                                                       me, other, my_id, other_id, IKEV2);
+       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);
+               }
+       }
+       enumerator->destroy(enumerator);
+       if (this->peer_cfg)
+       {
+               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;
+ }
+ /**
+  * update the current peer candidate if necessary, using candidates
+  */
+ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict)
+ {
+       do
+       {
+               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);
+               }
+       }
+       while (this->peer_cfg);
+       return this->peer_cfg != NULL;
+ }
+ METHOD(task_t, build_i, status_t,
+       private_ike_auth_t *this, message_t *message)
+ {
+       auth_cfg_t *cfg;
+       if (message->get_exchange_type(message) == IKE_SA_INIT)
+       {
+               return collect_my_init_data(this, message);
+       }
+       if (this->peer_cfg == NULL)
+       {
+               this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+               this->peer_cfg->get_ref(this->peer_cfg);
+       }
+       if (message->get_message_id(message) == 1)
+       {       /* in the first IKE_AUTH ... */
+               if (this->ike_sa->supports_extension(this->ike_sa, EXT_MULTIPLE_AUTH))
+               {       /* indicate support for multiple authentication */
+                       message->add_notify(message, FALSE, MULTIPLE_AUTH_SUPPORTED,
+                                                               chunk_empty);
+               }
+               /* indicate support for EAP-only authentication */
+               message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION,
+                                                       chunk_empty);
+       }
+       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)
+       {
+               identification_t *idi, *idr = NULL;
+               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)
+               {
+                       idr = cfg->get(cfg, AUTH_RULE_IDENTITY);
+                       if (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);
+                               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);
+               idi = cfg->get(cfg, AUTH_RULE_IDENTITY);
 -                      {
 -                              DBG1(DBG_CFG, "IDr not configured and negotiation failed");
 -                              goto peer_auth_failed;
++              if (!idi || idi->get_type(idi) == ID_ANY)
++              {       /* ID_ANY is invalid as IDi, use local IP address instead */
++                      host_t *me;
++
++                      DBG1(DBG_CFG, "no IDi configured, fall back on IP address");
++                      me = this->ike_sa->get_my_host(this->ike_sa);
++                      idi = identification_create_from_sockaddr(me->get_sockaddr(me));
++                      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);
+               get_reserved_id_bytes(this, id_payload);
+               message->add_payload(message, (payload_t*)id_payload);
+               if (idr && message->get_message_id(message) == 1 &&
+                       this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NO)
+               {
+                       host_t *host;
+                       host = this->ike_sa->get_other_host(this->ike_sa);
+                       if (!charon->ike_sa_manager->has_contact(charon->ike_sa_manager,
+                                                                                       idi, idr, host->get_family(host)))
+                       {
+                               message->add_notify(message, FALSE, INITIAL_CONTACT, chunk_empty);
+                       }
+               }
+               /* 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),
+                                                       this->reserved);
+               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;
+ }
+ METHOD(task_t, process_r, status_t,
+       private_ike_auth_t *this, message_t *message)
+ {
+       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 (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;
+       }
+       if (message->get_message_id(message) == 1)
+       {       /* check for extensions in the first IKE_AUTH */
+               if (message->get_notify(message, MULTIPLE_AUTH_SUPPORTED))
+               {
+                       this->ike_sa->enable_extension(this->ike_sa, EXT_MULTIPLE_AUTH);
+               }
+               if (message->get_notify(message, EAP_ONLY_AUTHENTICATION))
+               {
+                       this->ike_sa->enable_extension(this->ike_sa,
+                                                                                  EXT_EAP_ONLY_AUTHENTICATION);
+               }
+       }
+       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);
+               get_reserved_id_bytes(this, 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 */
+                               DBG1(DBG_IKE, "peer requested EAP, config inacceptable");
+                               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);
+                       }
+                       /* copy over the EAP specific rules for authentication */
+                       cfg->add(cfg, AUTH_RULE_EAP_TYPE,
+                                        cand->get(cand, AUTH_RULE_EAP_TYPE));
+                       cfg->add(cfg, AUTH_RULE_EAP_VENDOR,
+                                        cand->get(cand, AUTH_RULE_EAP_VENDOR));
+                       id = (identification_t*)cand->get(cand, AUTH_RULE_EAP_IDENTITY);
+                       if (id)
+                       {
+                               cfg->add(cfg, AUTH_RULE_EAP_IDENTITY, id->clone(id));
+                       }
+                       id = (identification_t*)cand->get(cand, AUTH_RULE_AAA_IDENTITY);
+                       if (id)
+                       {
+                               cfg->add(cfg, AUTH_RULE_AAA_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),
+                                                       this->reserved);
+               if (!this->other_auth)
+               {
+                       this->authentication_failed = TRUE;
+                       return NEED_MORE;
+               }
+       }
+       switch (this->other_auth->process(this->other_auth, message))
+       {
+               case SUCCESS:
+                       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:
+                       this->authentication_failed = TRUE;
+                       return NEED_MORE;
+       }
+       /* If authenticated (with non-EAP) and received INITIAL_CONTACT,
+        * delete any existing IKE_SAs with that peer. */
+       if (message->get_message_id(message) == 1 &&
+               message->get_notify(message, INITIAL_CONTACT))
+       {
+               this->initial_contact = TRUE;
+       }
+       /* 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;
+       }
+       /* 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);
+       if (!update_cfg_candidates(this, FALSE))
+       {
+               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;
+ }
+ METHOD(task_t, build_r, status_t,
+       private_ike_auth_t *this, message_t *message)
+ {
+       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);
+       }
+       if (this->authentication_failed || this->peer_cfg == NULL)
+       {
+               goto peer_auth_failed;
+       }
+       if (this->my_auth == NULL && this->do_another_auth)
+       {
+               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))
++                      {       /* no ID configured, use local IP address */
++                              host_t *me;
++
++                              DBG1(DBG_CFG, "no IDr configured, fall back on IP address");
++                              me = this->ike_sa->get_my_host(this->ike_sa);
++                              id_cfg = identification_create_from_sockaddr(
++                                                                                                              me->get_sockaddr(me));
++                              cfg->add(cfg, AUTH_RULE_IDENTITY, id_cfg);
+                       }
+                       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);
+                               goto peer_auth_failed;
+                       }
+               }
+               id_payload = id_payload_create_from_identification(ID_RESPONDER, id);
+               get_reserved_id_bytes(this, id_payload);
+               message->add_payload(message, (payload_t*)id_payload);
+               if (this->initial_contact)
+               {
+                       charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+                                                                                                        this->ike_sa, TRUE);
+                       this->initial_contact = FALSE;
+               }
+               if ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS) == AUTH_CLASS_EAP)
+               {       /* EAP-only authentication */
+                       if (!this->ike_sa->supports_extension(this->ike_sa,
+                                                                                                 EXT_EAP_ONLY_AUTHENTICATION))
+                       {
+                               DBG1(DBG_IKE, "configured EAP-only authentication, but peer "
+                                        "does not support it");
+                               goto peer_auth_failed;
+                       }
+               }
+               else
+               {
+                       /* 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),
+                                                               this->reserved);
+                       if (!this->my_auth)
+                       {
+                               goto peer_auth_failed;
+                       }
+               }
+       }
+       if (this->other_auth)
+       {
+               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 */
+                                       goto peer_auth_failed_no_notify;
+                               }
+                               goto peer_auth_failed;
+               }
+       }
+       if (this->my_auth)
+       {
+               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);
+       }
+       else
+       {
+               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, 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;
+ peer_auth_failed:
+       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;
+ }
+ METHOD(task_t, process_i, status_t,
+       private_ike_auth_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       payload_t *payload;
+       auth_cfg_t *cfg;
+       bool mutual_eap = FALSE;
+       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);
+       }
+       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:
+                               case SINGLE_PAIR_REQUIRED:
+                               case NO_ADDITIONAL_SAS:
+                               case INTERNAL_ADDRESS_FAILURE:
+                               case FAILED_CP_REQUIRED:
+                               case TS_UNACCEPTABLE:
+                               case INVALID_SELECTORS:
+                                       /* these are errors, but are not critical as only the
+                                        * CHILD_SA won't get build, but IKE_SA establishes anyway */
+                                       break;
+                               case MOBIKE_SUPPORTED:
+                               case ADDITIONAL_IP4_ADDRESS:
+                               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)
+                                       {
+                                               DBG1(DBG_IKE, "received %N notify error",
+                                                        notify_type_names, type);
+                                               enumerator->destroy(enumerator);
+                                               return FAILED;
+                                       }
+                                       DBG2(DBG_IKE, "received %N notify",
+                                               notify_type_names, type);
+                                       break;
+                               }
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       if (this->expect_another_auth)
+       {
+               if (this->other_auth == NULL)
+               {
+                       id_payload_t *id_payload;
+                       identification_t *id;
+                       /* handle IDr payload */
+                       id_payload = (id_payload_t*)message->get_payload(message,
+                                                                                                                        ID_RESPONDER);
+                       if (!id_payload)
+                       {
+                               DBG1(DBG_IKE, "IDr payload missing");
+                               goto peer_auth_failed;
+                       }
+                       id = id_payload->get_identification(id_payload);
+                       get_reserved_id_bytes(this, 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 (message->get_payload(message, AUTHENTICATION))
+                       {
+                               /* 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),
+                                                               this->reserved);
+                               if (!this->other_auth)
+                               {
+                                       goto peer_auth_failed;
+                               }
+                       }
+                       else
+                       {
+                               /* responder omitted AUTH payload, indicating EAP-only */
+                               mutual_eap = TRUE;
+                       }
+               }
+               if (this->other_auth)
+               {
+                       switch (this->other_auth->process(this->other_auth, message))
+                       {
+                               case SUCCESS:
+                                       break;
+                               case NEED_MORE:
+                                       return NEED_MORE;
+                               default:
+                                       goto peer_auth_failed;
+                       }
+                       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");
+                       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 (this->my_auth)
+       {
+               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 (mutual_eap)
+       {
+               if (!this->my_auth || !this->my_auth->is_mutual(this->my_auth))
+               {
+                       DBG1(DBG_IKE, "do not allow non-mutual EAP-only authentication");
+                       goto peer_auth_failed;
+               }
+               DBG1(DBG_IKE, "allow mutual EAP-only authentication");
+       }
+       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))
+               {
+                       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;
+ peer_auth_failed:
+       charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED);
+       return FAILED;
+ }
+ METHOD(task_t, get_type, task_type_t,
+       private_ike_auth_t *this)
+ {
+       return TASK_IKE_AUTH;
+ }
+ METHOD(task_t, migrate, void,
+       private_ike_auth_t *this, ike_sa_t *ike_sa)
+ {
+       chunk_free(&this->my_nonce);
+       chunk_free(&this->other_nonce);
+       DESTROY_IF(this->my_packet);
+       DESTROY_IF(this->other_packet);
+       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->ike_sa = ike_sa;
+       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();
+ }
+ METHOD(task_t, destroy, void,
+       private_ike_auth_t *this)
+ {
+       chunk_free(&this->my_nonce);
+       chunk_free(&this->other_nonce);
+       DESTROY_IF(this->my_packet);
+       DESTROY_IF(this->other_packet);
+       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);
+ }
+ /*
+  * Described in header.
+  */
+ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator)
+ {
+       private_ike_auth_t *this;
+       INIT(this,
+               .public = {
+                       .task = {
+                               .get_type = _get_type,
+                               .migrate = _migrate,
+                               .build = _build_r,
+                               .process = _process_r,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+               .initiator = initiator,
+               .candidates = linked_list_create(),
+               .do_another_auth = TRUE,
+               .expect_another_auth = TRUE,
+       );
+       if (initiator)
+       {
+               this->public.task.build = _build_i;
+               this->public.task.process = _process_i;
+       }
+       return &this->public;
+ }
index 0000000,de68e86..3fbbcfd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,587 +1,590 @@@
 -      DESTROY_IF(this->dh);
 -      this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
 -                                                                                        this->dh_group);
+ /*
+  * Copyright (C) 2008-2009 Tobias Brunner
+  * Copyright (C) 2005-2008 Martin Willi
+  * Copyright (C) 2005 Jan Hutter
+  * 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
+  * Free Software Foundation; either version 2 of the License, or (at your
+  * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+  *
+  * 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.
+  */
+ #include "ike_init.h"
+ #include <string.h>
+ #include <daemon.h>
+ #include <sa/ikev2/keymat_v2.h>
+ #include <crypto/diffie_hellman.h>
+ #include <encoding/payloads/sa_payload.h>
+ #include <encoding/payloads/ke_payload.h>
+ #include <encoding/payloads/nonce_payload.h>
+ /** maximum retries to do with cookies/other dh groups */
+ #define MAX_RETRIES 5
+ typedef struct private_ike_init_t private_ike_init_t;
+ /**
+  * Private members of a ike_init_t task.
+  */
+ struct private_ike_init_t {
+       /**
+        * Public methods and task_t interface.
+        */
+       ike_init_t public;
+       /**
+        * Assigned IKE_SA.
+        */
+       ike_sa_t *ike_sa;
+       /**
+        * Are we the initiator?
+        */
+       bool initiator;
+       /**
+        * IKE config to establish
+        */
+       ike_cfg_t *config;
+       /**
+        * diffie hellman group to use
+        */
+       diffie_hellman_group_t dh_group;
+       /**
+        * diffie hellman key exchange
+        */
+       diffie_hellman_t *dh;
+       /**
+        * Keymat derivation (from IKE_SA)
+        */
+       keymat_v2_t *keymat;
+       /**
+        * nonce chosen by us
+        */
+       chunk_t my_nonce;
+       /**
+        * nonce chosen by peer
+        */
+       chunk_t other_nonce;
+       /**
+        * Negotiated proposal used for IKE_SA
+        */
+       proposal_t *proposal;
+       /**
+        * Old IKE_SA which gets rekeyed
+        */
+       ike_sa_t *old_sa;
+       /**
+        * cookie received from responder
+        */
+       chunk_t cookie;
+       /**
+        * retries done so far after failure (cookie or bad dh group)
+        */
+       u_int retry;
+ };
+ /**
+  * build the payloads for the message
+  */
+ static void 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;
+       ike_sa_id_t *id;
+       proposal_t *proposal;
+       enumerator_t *enumerator;
+       id = this->ike_sa->get_id(this->ike_sa);
+       this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
+       if (this->initiator)
+       {
+               proposal_list = this->config->get_proposals(this->config);
+               if (this->old_sa)
+               {
+                       /* include SPI of new IKE_SA when we are rekeying */
+                       enumerator = proposal_list->create_enumerator(proposal_list);
+                       while (enumerator->enumerate(enumerator, (void**)&proposal))
+                       {
+                               proposal->set_spi(proposal, id->get_initiator_spi(id));
+                       }
+                       enumerator->destroy(enumerator);
+               }
+               sa_payload = sa_payload_create_from_proposals_v2(proposal_list);
+               proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
+       }
+       else
+       {
+               if (this->old_sa)
+               {
+                       /* include SPI of new IKE_SA when we are rekeying */
+                       this->proposal->set_spi(this->proposal, id->get_responder_spi(id));
+               }
+               sa_payload = sa_payload_create_from_proposal_v2(this->proposal);
+       }
+       message->add_payload(message, (payload_t*)sa_payload);
+       nonce_payload = nonce_payload_create(NONCE);
+       nonce_payload->set_nonce(nonce_payload, this->my_nonce);
+       ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE, this->dh);
+       if (this->old_sa)
+       {       /* payload order differs if we are rekeying */
+               message->add_payload(message, (payload_t*)nonce_payload);
+               message->add_payload(message, (payload_t*)ke_payload);
+       }
+       else
+       {
+               message->add_payload(message, (payload_t*)ke_payload);
+               message->add_payload(message, (payload_t*)nonce_payload);
+       }
+ }
+ /**
+  * Read payloads from message
+  */
+ static void process_payloads(private_ike_init_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       payload_t *payload;
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               switch (payload->get_type(payload))
+               {
+                       case 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);
+                               proposal_list->destroy_offset(proposal_list,
+                                                                                         offsetof(proposal_t, destroy));
+                               break;
+                       }
+                       case KEY_EXCHANGE:
+                       {
+                               ke_payload_t *ke_payload = (ke_payload_t*)payload;
+                               this->dh_group = ke_payload->get_dh_group_number(ke_payload);
+                               if (!this->initiator)
+                               {
+                                       this->dh = this->keymat->keymat.create_dh(
+                                                                               &this->keymat->keymat, this->dh_group);
+                               }
+                               if (this->dh)
+                               {
+                                       this->dh->set_other_public_value(this->dh,
+                                                               ke_payload->get_key_exchange_data(ke_payload));
+                               }
+                               break;
+                       }
+                       case NONCE:
+                       {
+                               nonce_payload_t *nonce_payload = (nonce_payload_t*)payload;
+                               this->other_nonce = nonce_payload->get_nonce(nonce_payload);
+                               break;
+                       }
+                       default:
+                               break;
+               }
+       }
+       enumerator->destroy(enumerator);
+ }
+ METHOD(task_t, build_i, status_t,
+       private_ike_init_t *this, message_t *message)
+ {
+       rng_t *rng;
+       this->config = 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),
+                this->ike_sa->get_other_host(this->ike_sa));
+       this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+       if (this->retry >= MAX_RETRIES)
+       {
+               DBG1(DBG_IKE, "giving up after %d retries", MAX_RETRIES);
+               return FAILED;
+       }
+       /* if the DH group is set via use_dh_group(), we already have a DH object */
+       if (!this->dh)
+       {
+               this->dh_group = this->config->get_dh_group(this->config);
+               this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
+                                                                                                 this->dh_group);
+               if (!this->dh)
+               {
+                       DBG1(DBG_IKE, "configured 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)
+       {
+               rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+               if (!rng)
+               {
+                       DBG1(DBG_IKE, "error generating nonce");
+                       return FAILED;
+               }
+               rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce);
+               rng->destroy(rng);
+       }
+       if (this->cookie.ptr)
+       {
+               message->add_notify(message, FALSE, COOKIE, this->cookie);
+       }
+       build_payloads(this, message);
+ #ifdef ME
+       {
+               chunk_t connect_id = this->ike_sa->get_connect_id(this->ike_sa);
+               if (connect_id.ptr)
+               {
+                       message->add_notify(message, FALSE, ME_CONNECTID, connect_id);
+               }
+       }
+ #endif /* ME */
+       return NEED_MORE;
+ }
+ METHOD(task_t, process_r,  status_t,
+       private_ike_init_t *this, message_t *message)
+ {
+       rng_t *rng;
+       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);
+       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!rng)
+       {
+               DBG1(DBG_IKE, "error generating nonce");
+               return FAILED;
+       }
+       rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce);
+       rng->destroy(rng);
+ #ifdef ME
+       {
+               notify_payload_t *notify = message->get_notify(message, ME_CONNECTID);
+               if (notify)
+               {
+                       chunk_t connect_id = notify->get_notification_data(notify);
+                       DBG2(DBG_IKE, "received ME_CONNECTID %#B", &connect_id);
+                       charon->connect_manager->stop_checks(charon->connect_manager,
+                                                                                                connect_id);
+               }
+       }
+ #endif /* ME */
+       process_payloads(this, message);
+       return NEED_MORE;
+ }
+ /**
+  * Derive the keymat for the IKE_SA
+  */
+ static bool derive_keys(private_ike_init_t *this,
+                                               chunk_t nonce_i, chunk_t nonce_r)
+ {
+       keymat_v2_t *old_keymat;
+       pseudo_random_function_t prf_alg = PRF_UNDEFINED;
+       chunk_t skd = chunk_empty;
+       ike_sa_id_t *id;
+       id = this->ike_sa->get_id(this->ike_sa);
+       if (this->old_sa)
+       {
+               /* rekeying: Include old SKd, use old PRF, apply SPI */
+               old_keymat = (keymat_v2_t*)this->old_sa->get_keymat(this->old_sa);
+               prf_alg = old_keymat->get_skd(old_keymat, &skd);
+               if (this->initiator)
+               {
+                       id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
+               }
+               else
+               {
+                       id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
+               }
+       }
+       if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh,
+                                                                          nonce_i, nonce_r, id, prf_alg, skd))
+       {
+               return FALSE;
+       }
+       charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, chunk_empty,
+                                                 nonce_i, nonce_r, this->old_sa, NULL);
+       return TRUE;
+ }
+ METHOD(task_t, build_r, status_t,
+       private_ike_init_t *this, message_t *message)
+ {
+       /* check if we have everything we need */
+       if (this->proposal == NULL ||
+               this->other_nonce.len == 0 || this->my_nonce.len == 0)
+       {
+               DBG1(DBG_IKE, "received proposals inacceptable");
+               message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+               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))
+       {
+               u_int16_t group;
+               if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
+                                                                                 &group, NULL))
+               {
+                       DBG1(DBG_IKE, "DH group %N inacceptable, requesting %N",
+                                diffie_hellman_group_names, this->dh_group,
+                                diffie_hellman_group_names, group);
+                       this->dh_group = group;
+                       group = htons(group);
+                       message->add_notify(message, FALSE, INVALID_KE_PAYLOAD,
+                                                               chunk_from_thing(group));
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "no acceptable proposal found");
+               }
+               return FAILED;
+       }
+       if (!derive_keys(this, this->other_nonce, this->my_nonce))
+       {
+               DBG1(DBG_IKE, "key derivation failed");
+               message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+               return FAILED;
+       }
+       build_payloads(this, message);
+       return SUCCESS;
+ }
+ METHOD(task_t, process_i, status_t,
+       private_ike_init_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       payload_t *payload;
+       /* check for erronous notifies */
+       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 INVALID_KE_PAYLOAD:
+                               {
+                                       chunk_t data;
+                                       diffie_hellman_group_t bad_group;
+                                       bad_group = this->dh_group;
+                                       data = notify->get_notification_data(notify);
+                                       this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+                                       DBG1(DBG_IKE, "peer didn't accept DH group %N, "
+                                                "it requested %N", diffie_hellman_group_names,
+                                                bad_group, diffie_hellman_group_names, this->dh_group);
+                                       if (this->old_sa == NULL)
+                                       {       /* reset the IKE_SA if we are not rekeying */
+                                               this->ike_sa->reset(this->ike_sa);
+                                       }
+                                       enumerator->destroy(enumerator);
+                                       this->retry++;
+                                       return NEED_MORE;
+                               }
+                               case NAT_DETECTION_SOURCE_IP:
+                               case NAT_DETECTION_DESTINATION_IP:
+                                       /* skip, handled in ike_natd_t */
+                                       break;
+                               case MULTIPLE_AUTH_SUPPORTED:
+                                       /* handled in ike_auth_t */
+                                       break;
+                               case COOKIE:
+                               {
+                                       chunk_free(&this->cookie);
+                                       this->cookie = chunk_clone(notify->get_notification_data(notify));
+                                       this->ike_sa->reset(this->ike_sa);
+                                       enumerator->destroy(enumerator);
+                                       DBG2(DBG_IKE, "received %N notify", notify_type_names, type);
+                                       this->retry++;
+                                       return NEED_MORE;
+                               }
+                               default:
+                               {
+                                       if (type <= 16383)
+                                       {
+                                               DBG1(DBG_IKE, "received %N notify error",
+                                                        notify_type_names, type);
+                                               enumerator->destroy(enumerator);
+                                               return FAILED;
+                                       }
+                                       DBG2(DBG_IKE, "received %N notify",
+                                               notify_type_names, type);
+                                       break;
+                               }
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       process_payloads(this, message);
+       /* check if we have everything */
+       if (this->proposal == NULL ||
+               this->other_nonce.len == 0 || this->my_nonce.len == 0)
+       {
+               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))
+       {
+               DBG1(DBG_IKE, "peer DH group selection invalid");
+               return FAILED;
+       }
+       if (!derive_keys(this, this->my_nonce, this->other_nonce))
+       {
+               DBG1(DBG_IKE, "key derivation failed");
+               return FAILED;
+       }
+       return SUCCESS;
+ }
+ METHOD(task_t, get_type, task_type_t,
+       private_ike_init_t *this)
+ {
+       return TASK_IKE_INIT;
+ }
+ METHOD(task_t, migrate, void,
+       private_ike_init_t *this, ike_sa_t *ike_sa)
+ {
+       DESTROY_IF(this->proposal);
+       chunk_free(&this->other_nonce);
+       this->ike_sa = ike_sa;
+       this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
+       this->proposal = NULL;
++      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,
+       private_ike_init_t *this)
+ {
+       DESTROY_IF(this->dh);
+       DESTROY_IF(this->proposal);
+       chunk_free(&this->my_nonce);
+       chunk_free(&this->other_nonce);
+       chunk_free(&this->cookie);
+       free(this);
+ }
+ METHOD(ike_init_t, get_lower_nonce, chunk_t,
+       private_ike_init_t *this)
+ {
+       if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr,
+                          min(this->my_nonce.len, this->other_nonce.len)) < 0)
+       {
+               return this->my_nonce;
+       }
+       else
+       {
+               return this->other_nonce;
+       }
+ }
+ /*
+  * Described in header.
+  */
+ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
+ {
+       private_ike_init_t *this;
+       INIT(this,
+               .public = {
+                       .task = {
+                               .get_type = _get_type,
+                               .migrate = _migrate,
+                               .destroy = _destroy,
+                       },
+                       .get_lower_nonce = _get_lower_nonce,
+               },
+               .ike_sa = ike_sa,
+               .initiator = initiator,
+               .dh_group = MODP_NONE,
+               .keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa),
+               .old_sa = old_sa,
+       );
+       if (initiator)
+       {
+               this->public.task.build = _build_i;
+               this->public.task.process = _process_i;
+       }
+       else
+       {
+               this->public.task.build = _build_r;
+               this->public.task.process = _process_r;
+       }
+       return &this->public;
+ }
Simple merge
Simple merge
Simple merge
@@@ -53,40 -56,6 +56,42 @@@ ENUM(auth_rule_names, AUTH_RULE_IDENTIT
        "HELPER_REVOCATION_CERT",
  );
  
 +/**
 + * Check if the given rule is a rule for which there may be multiple values.
 + */
 +static inline bool is_multi_value_rule(auth_rule_t type)
 +{
 +      switch (type)
 +      {
 +              case AUTH_RULE_AUTH_CLASS:
 +              case AUTH_RULE_EAP_TYPE:
 +              case AUTH_RULE_EAP_VENDOR:
 +              case AUTH_RULE_RSA_STRENGTH:
 +              case AUTH_RULE_ECDSA_STRENGTH:
 +              case AUTH_RULE_IDENTITY:
 +              case AUTH_RULE_EAP_IDENTITY:
 +              case AUTH_RULE_AAA_IDENTITY:
++              case AUTH_RULE_XAUTH_IDENTITY:
++              case AUTH_RULE_XAUTH_BACKEND:
 +              case AUTH_RULE_SUBJECT_CERT:
 +              case AUTH_HELPER_SUBJECT_CERT:
 +              case AUTH_HELPER_SUBJECT_HASH_URL:
 +              case AUTH_RULE_MAX:
 +                      return FALSE;
 +              case AUTH_RULE_OCSP_VALIDATION:
 +              case AUTH_RULE_CRL_VALIDATION:
 +              case AUTH_RULE_GROUP:
 +              case AUTH_RULE_CA_CERT:
 +              case AUTH_RULE_IM_CERT:
 +              case AUTH_RULE_CERT_POLICY:
 +              case AUTH_HELPER_IM_CERT:
 +              case AUTH_HELPER_IM_HASH_URL:
 +              case AUTH_HELPER_REVOCATION_CERT:
 +                      return TRUE;
 +      }
 +      return FALSE;
 +}
 +
  typedef struct private_auth_cfg_t private_auth_cfg_t;
  
  /**
@@@ -181,108 -135,6 +186,112 @@@ METHOD(auth_cfg_t, create_enumerator, e
  }
  
  /**
 + * Create an entry from the given arguments.
 + */
 +static entry_t *entry_create(auth_rule_t type, va_list args)
 +{
 +      entry_t *this = malloc_thing(entry_t);
 +
 +      this->type = type;
 +      switch (type)
 +      {
 +              case AUTH_RULE_AUTH_CLASS:
 +              case AUTH_RULE_EAP_TYPE:
 +              case AUTH_RULE_EAP_VENDOR:
 +              case AUTH_RULE_CRL_VALIDATION:
 +              case AUTH_RULE_OCSP_VALIDATION:
 +              case AUTH_RULE_RSA_STRENGTH:
 +              case AUTH_RULE_ECDSA_STRENGTH:
 +                      /* integer type */
 +                      this->value = (void*)(uintptr_t)va_arg(args, u_int);
 +                      break;
 +              case AUTH_RULE_IDENTITY:
 +              case AUTH_RULE_EAP_IDENTITY:
 +              case AUTH_RULE_AAA_IDENTITY:
++              case AUTH_RULE_XAUTH_BACKEND:
++              case AUTH_RULE_XAUTH_IDENTITY:
 +              case AUTH_RULE_GROUP:
 +              case AUTH_RULE_CA_CERT:
 +              case AUTH_RULE_IM_CERT:
 +              case AUTH_RULE_SUBJECT_CERT:
 +              case AUTH_RULE_CERT_POLICY:
 +              case AUTH_HELPER_IM_CERT:
 +              case AUTH_HELPER_SUBJECT_CERT:
 +              case AUTH_HELPER_IM_HASH_URL:
 +              case AUTH_HELPER_SUBJECT_HASH_URL:
 +              case AUTH_HELPER_REVOCATION_CERT:
 +                      /* pointer type */
 +                      this->value = va_arg(args, void*);
 +                      break;
 +              case AUTH_RULE_MAX:
 +                      this->value = NULL;
 +                      break;
 +      }
 +      return this;
 +}
 +
 +/**
 + * Compare two entries for equality.
 + */
 +static bool entry_equals(entry_t *e1, entry_t *e2)
 +{
 +      if (e1->type != e2->type)
 +      {
 +              return FALSE;
 +      }
 +      switch (e1->type)
 +      {
 +              case AUTH_RULE_AUTH_CLASS:
 +              case AUTH_RULE_EAP_TYPE:
 +              case AUTH_RULE_EAP_VENDOR:
 +              case AUTH_RULE_CRL_VALIDATION:
 +              case AUTH_RULE_OCSP_VALIDATION:
 +              case AUTH_RULE_RSA_STRENGTH:
 +              case AUTH_RULE_ECDSA_STRENGTH:
 +              {
 +                      return e1->value == e2->value;
 +              }
 +              case AUTH_RULE_CA_CERT:
 +              case AUTH_RULE_IM_CERT:
 +              case AUTH_RULE_SUBJECT_CERT:
 +              case AUTH_HELPER_IM_CERT:
 +              case AUTH_HELPER_SUBJECT_CERT:
 +              case AUTH_HELPER_REVOCATION_CERT:
 +              {
 +                      certificate_t *c1, *c2;
 +
 +                      c1 = (certificate_t*)e1->value;
 +                      c2 = (certificate_t*)e2->value;
 +
 +                      return c1->equals(c1, c2);
 +              }
 +              case AUTH_RULE_IDENTITY:
 +              case AUTH_RULE_EAP_IDENTITY:
 +              case AUTH_RULE_AAA_IDENTITY:
++              case AUTH_RULE_XAUTH_IDENTITY:
 +              case AUTH_RULE_GROUP:
 +              {
 +                      identification_t *id1, *id2;
 +
 +                      id1 = (identification_t*)e1->value;
 +                      id2 = (identification_t*)e2->value;
 +
 +                      return id1->equals(id1, id2);
 +              }
 +              case AUTH_RULE_CERT_POLICY:
++              case AUTH_RULE_XAUTH_BACKEND:
 +              case AUTH_HELPER_IM_HASH_URL:
 +              case AUTH_HELPER_SUBJECT_HASH_URL:
 +              {
 +                      return streq(e1->value, e2->value);
 +              }
 +              case AUTH_RULE_MAX:
 +                      break;
 +      }
 +      return FALSE;
 +}
 +
 +/**
   * Destroy the value associated with an entry
   */
  static void destroy_entry_value(entry_t *entry)
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge