.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
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 */
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,
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);
"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",
"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",
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
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));
{
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)
--- /dev/null
-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 */
--- /dev/null
+ /*
+ * 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_ @}*/
--- /dev/null
- 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;
+ }
--- /dev/null
- 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**)¤t))
+ {
+ 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;
+ }
--- /dev/null
- 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;
+ }
--- /dev/null
- 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;
+ }
"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;
/**
}
/**
+ * 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)