summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/authenticator.c (renamed from src/libcharon/sa/authenticators/authenticator.c)64
-rw-r--r--src/libcharon/sa/authenticator.h (renamed from src/libcharon/sa/authenticators/authenticator.h)63
-rw-r--r--src/libcharon/sa/child_sa.c79
-rw-r--r--src/libcharon/sa/child_sa.h12
-rw-r--r--src/libcharon/sa/eap/eap_manager.c (renamed from src/libcharon/sa/authenticators/eap/eap_manager.c)41
-rw-r--r--src/libcharon/sa/eap/eap_manager.h (renamed from src/libcharon/sa/authenticators/eap/eap_manager.h)14
-rw-r--r--src/libcharon/sa/eap/eap_method.c (renamed from src/libcharon/sa/authenticators/eap/eap_method.c)0
-rw-r--r--src/libcharon/sa/eap/eap_method.h (renamed from src/libcharon/sa/authenticators/eap/eap_method.h)0
-rw-r--r--src/libcharon/sa/ike_sa.c694
-rw-r--r--src/libcharon/sa/ike_sa.h102
-rw-r--r--src/libcharon/sa/ike_sa_id.c51
-rw-r--r--src/libcharon/sa/ike_sa_id.h31
-rw-r--r--src/libcharon/sa/ike_sa_manager.c914
-rw-r--r--src/libcharon/sa/ike_sa_manager.h18
-rw-r--r--src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.c114
-rw-r--r--src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.h56
-rw-r--r--src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c172
-rw-r--r--src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.h57
-rw-r--r--src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c233
-rw-r--r--src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.h57
-rw-r--r--src/libcharon/sa/ikev1/keymat_v1.c1157
-rw-r--r--src/libcharon/sa/ikev1/keymat_v1.h166
-rw-r--r--src/libcharon/sa/ikev1/phase1.c795
-rw-r--r--src/libcharon/sa/ikev1/phase1.h166
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c1714
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.h46
-rw-r--r--src/libcharon/sa/ikev1/tasks/aggressive_mode.c714
-rw-r--r--src/libcharon/sa/ikev1/tasks/aggressive_mode.h50
-rw-r--r--src/libcharon/sa/ikev1/tasks/informational.c253
-rw-r--r--src/libcharon/sa/ikev1/tasks/informational.h51
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c359
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_cert_post.h53
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c578
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.h53
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_delete.c147
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_delete.h50
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_dpd.c123
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_dpd.h52
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_natd.c456
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_natd.h50
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_vendor.c225
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_vendor.h49
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.c735
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.h50
-rw-r--r--src/libcharon/sa/ikev1/tasks/mode_config.c459
-rw-r--r--src/libcharon/sa/ikev1/tasks/mode_config.h50
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_delete.c247
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_delete.h55
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c1273
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.h67
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.c551
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.h50
-rw-r--r--src/libcharon/sa/ikev2/authenticators/eap_authenticator.c (renamed from src/libcharon/sa/authenticators/eap_authenticator.c)97
-rw-r--r--src/libcharon/sa/ikev2/authenticators/eap_authenticator.h (renamed from src/libcharon/sa/authenticators/eap_authenticator.h)4
-rw-r--r--src/libcharon/sa/ikev2/authenticators/psk_authenticator.c (renamed from src/libcharon/sa/authenticators/psk_authenticator.c)25
-rw-r--r--src/libcharon/sa/ikev2/authenticators/psk_authenticator.h (renamed from src/libcharon/sa/authenticators/psk_authenticator.h)4
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c (renamed from src/libcharon/sa/authenticators/pubkey_authenticator.c)24
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.h (renamed from src/libcharon/sa/authenticators/pubkey_authenticator.h)4
-rw-r--r--src/libcharon/sa/ikev2/connect_manager.c (renamed from src/libcharon/sa/connect_manager.c)37
-rw-r--r--src/libcharon/sa/ikev2/connect_manager.h (renamed from src/libcharon/sa/connect_manager.h)2
-rw-r--r--src/libcharon/sa/ikev2/keymat_v2.c687
-rw-r--r--src/libcharon/sa/ikev2/keymat_v2.h137
-rw-r--r--src/libcharon/sa/ikev2/mediation_manager.c (renamed from src/libcharon/sa/mediation_manager.c)0
-rw-r--r--src/libcharon/sa/ikev2/mediation_manager.h (renamed from src/libcharon/sa/mediation_manager.h)2
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c1502
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.h46
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c (renamed from src/libcharon/sa/tasks/child_create.c)258
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.h (renamed from src/libcharon/sa/tasks/child_create.h)13
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c (renamed from src/libcharon/sa/tasks/child_delete.c)47
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.h (renamed from src/libcharon/sa/tasks/child_delete.h)7
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c (renamed from src/libcharon/sa/tasks/child_rekey.c)33
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.h (renamed from src/libcharon/sa/tasks/child_rekey.h)8
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth.c (renamed from src/libcharon/sa/tasks/ike_auth.c)17
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth.h (renamed from src/libcharon/sa/tasks/ike_auth.h)6
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c (renamed from src/libcharon/sa/tasks/ike_auth_lifetime.c)3
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.h (renamed from src/libcharon/sa/tasks/ike_auth_lifetime.h)10
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_cert_post.c (renamed from src/libcharon/sa/tasks/ike_cert_post.c)18
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_cert_post.h (renamed from src/libcharon/sa/tasks/ike_cert_post.h)4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_cert_pre.c (renamed from src/libcharon/sa/tasks/ike_cert_pre.c)5
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_cert_pre.h (renamed from src/libcharon/sa/tasks/ike_cert_pre.h)4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_config.c (renamed from src/libcharon/sa/tasks/ike_config.c)163
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_config.h (renamed from src/libcharon/sa/tasks/ike_config.h)6
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_delete.c (renamed from src/libcharon/sa/tasks/ike_delete.c)4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_delete.h (renamed from src/libcharon/sa/tasks/ike_delete.h)4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_dpd.c (renamed from src/libcharon/sa/tasks/ike_dpd.c)2
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_dpd.h (renamed from src/libcharon/sa/tasks/ike_dpd.h)4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.c (renamed from src/libcharon/sa/tasks/ike_init.c)71
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.h (renamed from src/libcharon/sa/tasks/ike_init.h)8
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_me.c (renamed from src/libcharon/sa/tasks/ike_me.c)20
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_me.h (renamed from src/libcharon/sa/tasks/ike_me.h)6
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.c (renamed from src/libcharon/sa/tasks/ike_mobike.c)57
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.h (renamed from src/libcharon/sa/tasks/ike_mobike.h)6
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_natd.c (renamed from src/libcharon/sa/tasks/ike_natd.c)54
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_natd.h (renamed from src/libcharon/sa/tasks/ike_natd.h)6
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_reauth.c (renamed from src/libcharon/sa/tasks/ike_reauth.c)5
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_reauth.h (renamed from src/libcharon/sa/tasks/ike_reauth.h)4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_rekey.c (renamed from src/libcharon/sa/tasks/ike_rekey.c)35
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_rekey.h (renamed from src/libcharon/sa/tasks/ike_rekey.h)10
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_vendor.c (renamed from src/libcharon/sa/tasks/ike_vendor.c)12
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_vendor.h (renamed from src/libcharon/sa/tasks/ike_vendor.h)4
-rw-r--r--src/libcharon/sa/keymat.c653
-rw-r--r--src/libcharon/sa/keymat.h141
-rw-r--r--src/libcharon/sa/shunt_manager.c1
-rw-r--r--src/libcharon/sa/task.c (renamed from src/libcharon/sa/tasks/task.c)40
-rw-r--r--src/libcharon/sa/task.h (renamed from src/libcharon/sa/tasks/task.h)66
-rw-r--r--src/libcharon/sa/task_manager.c1139
-rw-r--r--src/libcharon/sa/task_manager.h83
-rw-r--r--src/libcharon/sa/trap_manager.c60
-rw-r--r--src/libcharon/sa/xauth/xauth_manager.c157
-rw-r--r--src/libcharon/sa/xauth/xauth_manager.h79
-rw-r--r--src/libcharon/sa/xauth/xauth_method.c42
-rw-r--r--src/libcharon/sa/xauth/xauth_method.h126
112 files changed, 16671 insertions, 3017 deletions
diff --git a/src/libcharon/sa/authenticators/authenticator.c b/src/libcharon/sa/authenticator.c
index 9ffe661cc..a32b6ab12 100644
--- a/src/libcharon/sa/authenticators/authenticator.c
+++ b/src/libcharon/sa/authenticator.c
@@ -18,9 +18,12 @@
#include "authenticator.h"
-#include <sa/authenticators/pubkey_authenticator.h>
-#include <sa/authenticators/psk_authenticator.h>
-#include <sa/authenticators/eap_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>
@@ -33,7 +36,17 @@ ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_GSPM, AUTH_DSS,
"ECDSA-384 signature",
"ECDSA-521 signature",
"secure password method");
-ENUM_END(auth_method_names, AUTH_GSPM);
+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.
@@ -96,3 +109,46 @@ authenticator_t *authenticator_create_verifier(
}
}
+#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 */
diff --git a/src/libcharon/sa/authenticators/authenticator.h b/src/libcharon/sa/authenticator.h
index 5042e4a73..914f42d9d 100644
--- a/src/libcharon/sa/authenticators/authenticator.h
+++ b/src/libcharon/sa/authenticator.h
@@ -17,7 +17,7 @@
/**
* @defgroup authenticator authenticator
- * @{ @ingroup authenticators
+ * @{ @ingroup sa
*/
#ifndef AUTHENTICATOR_H_
@@ -34,6 +34,12 @@ typedef struct authenticator_t authenticator_t;
* 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.
@@ -73,6 +79,35 @@ enum auth_method_t {
*/
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,
};
/**
@@ -128,7 +163,7 @@ struct authenticator_t {
};
/**
- * Create an authenticator to build signatures.
+ * Create an IKEv2 authenticator to build signatures.
*
* @param ike_sa associated ike_sa
* @param cfg authentication configuration
@@ -146,7 +181,7 @@ authenticator_t *authenticator_create_builder(
char reserved[3]);
/**
- * Create an authenticator to verify signatures.
+ * Create an IKEv2 authenticator to verify signatures.
*
* @param ike_sa associated ike_sa
* @param message message containing authentication data
@@ -163,4 +198,26 @@ authenticator_t *authenticator_create_verifier(
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_ @}*/
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 2130a5998..1245734c9 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -124,6 +124,11 @@ struct private_child_sa_t {
child_sa_state_t state;
/**
+ * TRUE if this CHILD_SA is used to install trap policies
+ */
+ bool trap;
+
+ /**
* Specifies if UDP encapsulation is enabled (NAT traversal)
*/
bool encap;
@@ -526,6 +531,16 @@ METHOD(child_sa_t, get_usestats, void,
}
}
+METHOD(child_sa_t, get_mark, mark_t,
+ private_child_sa_t *this, bool inbound)
+{
+ if (inbound)
+ {
+ return this->mark_in;
+ }
+ return this->mark_out;
+}
+
METHOD(child_sa_t, get_lifetime, time_t,
private_child_sa_t *this, bool hard)
{
@@ -617,7 +632,14 @@ METHOD(child_sa_t, install, status_t,
now = time_monotonic(NULL);
if (lifetime->time.rekey)
{
- this->rekey_time = now + lifetime->time.rekey;
+ if (this->rekey_time)
+ {
+ this->rekey_time = min(this->rekey_time, now + lifetime->time.rekey);
+ }
+ else
+ {
+ this->rekey_time = now + lifetime->time.rekey;
+ }
}
if (lifetime->time.life)
{
@@ -757,8 +779,11 @@ METHOD(child_sa_t, add_policies, status_t,
other_sa.ah.spi = this->other_spi;
}
- priority = this->state == CHILD_CREATED ? POLICY_PRIORITY_ROUTED
- : POLICY_PRIORITY_DEFAULT;
+ /* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD
+ * entry) we install a trap policy */
+ this->trap = this->state == CHILD_CREATED;
+ priority = this->trap ? POLICY_PRIORITY_ROUTED
+ : POLICY_PRIORITY_DEFAULT;
/* enumerate pairs of traffic selectors */
enumerator = create_policy_enumerator(this);
@@ -787,16 +812,25 @@ METHOD(child_sa_t, add_policies, status_t,
enumerator->destroy(enumerator);
}
- if (status == SUCCESS && this->state == CHILD_CREATED)
- { /* switch to routed state if no SAD entry set up */
+ if (status == SUCCESS && this->trap)
+ {
set_state(this, CHILD_ROUTED);
}
return status;
}
+/**
+ * Callback to reinstall a virtual IP
+ */
+static void reinstall_vip(host_t *vip, host_t *me)
+{
+ hydra->kernel_interface->del_ip(hydra->kernel_interface, vip);
+ hydra->kernel_interface->add_ip(hydra->kernel_interface, vip, me);
+}
+
METHOD(child_sa_t, update, status_t,
- private_child_sa_t *this, host_t *me, host_t *other, host_t *vip,
- bool encap)
+ private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips,
+ bool encap)
{
child_sa_state_t old;
bool transport_proxy_mode;
@@ -902,13 +936,7 @@ METHOD(child_sa_t, update, status_t,
/* we reinstall the virtual IP to handle interface roaming
* correctly */
- if (vip)
- {
- hydra->kernel_interface->del_ip(hydra->kernel_interface,
- vip);
- hydra->kernel_interface->add_ip(hydra->kernel_interface,
- vip, me);
- }
+ vips->invoke_function(vips, (void*)reinstall_vip, me);
/* reinstall updated policies */
install_policies_internal(this, me, other, my_ts, other_ts,
@@ -960,8 +988,7 @@ METHOD(child_sa_t, destroy, void,
traffic_selector_t *my_ts, *other_ts;
policy_priority_t priority;
- priority = this->state == CHILD_ROUTED ? POLICY_PRIORITY_ROUTED
- : POLICY_PRIORITY_DEFAULT;
+ priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT;
set_state(this, CHILD_DESTROYING);
@@ -1038,6 +1065,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
.set_proposal = _set_proposal,
.get_lifetime = _get_lifetime,
.get_usestats = _get_usestats,
+ .get_mark = _get_mark,
.has_encap = _has_encap,
.get_ipcomp = _get_ipcomp,
.set_ipcomp = _set_ipcomp,
@@ -1079,6 +1107,15 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
this->reqid = rekey ? rekey : ++reqid;
}
+ if (this->mark_in.value == MARK_REQID)
+ {
+ this->mark_in.value = this->reqid;
+ }
+ if (this->mark_out.value == MARK_REQID)
+ {
+ this->mark_out.value = this->reqid;
+ }
+
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
if (config->get_mode(config) == MODE_TRANSPORT &&
config->use_proxy_mode(config))
@@ -1088,12 +1125,14 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
chunk_t addr;
host_t *host;
enumerator_t *enumerator;
- linked_list_t *my_ts_list, *other_ts_list;
+ linked_list_t *my_ts_list, *other_ts_list, *list;
traffic_selector_t *my_ts, *other_ts;
this->mode = MODE_TRANSPORT;
- my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, me);
+ list = linked_list_create_with_items(me, NULL);
+ my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, list);
+ list->destroy(list);
enumerator = my_ts_list->create_enumerator(my_ts_list);
if (enumerator->enumerate(enumerator, &my_ts))
{
@@ -1114,7 +1153,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
enumerator->destroy(enumerator);
my_ts_list->destroy_offset(my_ts_list, offsetof(traffic_selector_t, destroy));
- other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, other);
+ list = linked_list_create_with_items(other, NULL);
+ other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, list);
+ list->destroy(list);
enumerator = other_ts_list->create_enumerator(other_ts_list);
if (enumerator->enumerate(enumerator, &other_ts))
{
diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h
index f17ef01ac..dae3f2c18 100644
--- a/src/libcharon/sa/child_sa.h
+++ b/src/libcharon/sa/child_sa.h
@@ -275,6 +275,14 @@ struct child_sa_t {
u_int64_t *bytes);
/**
+ * Get the mark used with this CHILD_SA.
+ *
+ * @param inbound TRUE to get inbound mark, FALSE for outbound
+ * @return mark used with this CHILD_SA
+ */
+ mark_t (*get_mark)(child_sa_t *this, bool inbound);
+
+ /**
* Get the traffic selectors list added for one side.
*
* @param local TRUE for own traffic selectors, FALSE for remote
@@ -338,12 +346,12 @@ struct child_sa_t {
*
* @param me the new local host
* @param other the new remote host
- * @param vip virtual IP, if any
+ * @param vips list of local virtual IPs
* @param TRUE to use UDP encapsulation for NAT traversal
* @return SUCCESS or FAILED
*/
status_t (*update)(child_sa_t *this, host_t *me, host_t *other,
- host_t *vip, bool encap);
+ linked_list_t *vips, bool encap);
/**
* Destroys a child_sa.
*/
diff --git a/src/libcharon/sa/authenticators/eap/eap_manager.c b/src/libcharon/sa/eap/eap_manager.c
index bc2c4a617..520c0ce56 100644
--- a/src/libcharon/sa/authenticators/eap/eap_manager.c
+++ b/src/libcharon/sa/eap/eap_manager.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -104,6 +105,44 @@ METHOD(eap_manager_t, remove_method, void,
this->lock->unlock(this->lock);
}
+/**
+ * filter the registered methods
+ */
+static bool filter_methods(uintptr_t role, eap_entry_t **entry,
+ eap_type_t *type, void *in, u_int32_t *vendor)
+{
+ if ((*entry)->role != (eap_role_t)role)
+ {
+ return FALSE;
+ }
+ if ((*entry)->vendor == 0 &&
+ ((*entry)->type < 4 || (*entry)->type == EAP_EXPANDED ||
+ (*entry)->type > EAP_EXPERIMENTAL))
+ { /* filter invalid types */
+ return FALSE;
+ }
+ if (type)
+ {
+ *type = (*entry)->type;
+ }
+ if (vendor)
+ {
+ *vendor = (*entry)->vendor;
+ }
+ return TRUE;
+}
+
+METHOD(eap_manager_t, create_enumerator, enumerator_t*,
+ private_eap_manager_t *this, eap_role_t role)
+{
+ this->lock->read_lock(this->lock);
+ return enumerator_create_cleaner(
+ enumerator_create_filter(
+ this->methods->create_enumerator(this->methods),
+ (void*)filter_methods, (void*)(uintptr_t)role, NULL),
+ (void*)this->lock->unlock, this->lock);
+}
+
METHOD(eap_manager_t, create_instance, eap_method_t*,
private_eap_manager_t *this, eap_type_t type, u_int32_t vendor,
eap_role_t role, identification_t *server, identification_t *peer)
@@ -150,6 +189,7 @@ eap_manager_t *eap_manager_create()
.public = {
.add_method = _add_method,
.remove_method = _remove_method,
+ .create_enumerator = _create_enumerator,
.create_instance = _create_instance,
.destroy = _destroy,
},
@@ -159,4 +199,3 @@ eap_manager_t *eap_manager_create()
return &this->public;
}
-
diff --git a/src/libcharon/sa/authenticators/eap/eap_manager.h b/src/libcharon/sa/eap/eap_manager.h
index 0333fb6da..e318ef57a 100644
--- a/src/libcharon/sa/authenticators/eap/eap_manager.h
+++ b/src/libcharon/sa/eap/eap_manager.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -21,7 +22,7 @@
#ifndef EAP_MANAGER_H_
#define EAP_MANAGER_H_
-#include <sa/authenticators/eap/eap_method.h>
+#include <sa/eap/eap_method.h>
typedef struct eap_manager_t eap_manager_t;
@@ -54,6 +55,17 @@ struct eap_manager_t {
void (*remove_method)(eap_manager_t *this, eap_constructor_t constructor);
/**
+ * Enumerate the registered EAP authentication methods for the given role.
+ *
+ * @note Only authentication types are enumerated (e.g. EAP-Identity is not
+ * even though it is registered as method with this manager).
+ *
+ * @param role EAP role of methods to enumerate
+ * @return enumerator over (eap_type_t type, u_int32_t vendor)
+ */
+ enumerator_t* (*create_enumerator)(eap_manager_t *this, eap_role_t role);
+
+ /**
* Create a new EAP method instance.
*
* @param type type of the EAP method
diff --git a/src/libcharon/sa/authenticators/eap/eap_method.c b/src/libcharon/sa/eap/eap_method.c
index a05e8c59a..a05e8c59a 100644
--- a/src/libcharon/sa/authenticators/eap/eap_method.c
+++ b/src/libcharon/sa/eap/eap_method.c
diff --git a/src/libcharon/sa/authenticators/eap/eap_method.h b/src/libcharon/sa/eap/eap_method.h
index 6242a5a6e..6242a5a6e 100644
--- a/src/libcharon/sa/authenticators/eap/eap_method.h
+++ b/src/libcharon/sa/eap/eap_method.h
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index 07d19381d..1d49acb52 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -28,32 +28,16 @@
#include <daemon.h>
#include <utils/linked_list.h>
#include <utils/lexparser.h>
-#include <sa/task_manager.h>
-#include <sa/tasks/ike_init.h>
-#include <sa/tasks/ike_natd.h>
-#include <sa/tasks/ike_mobike.h>
-#include <sa/tasks/ike_auth.h>
-#include <sa/tasks/ike_auth_lifetime.h>
-#include <sa/tasks/ike_config.h>
-#include <sa/tasks/ike_cert_pre.h>
-#include <sa/tasks/ike_cert_post.h>
-#include <sa/tasks/ike_rekey.h>
-#include <sa/tasks/ike_reauth.h>
-#include <sa/tasks/ike_delete.h>
-#include <sa/tasks/ike_dpd.h>
-#include <sa/tasks/ike_vendor.h>
-#include <sa/tasks/child_create.h>
-#include <sa/tasks/child_delete.h>
-#include <sa/tasks/child_rekey.h>
#include <processing/jobs/retransmit_job.h>
#include <processing/jobs/delete_ike_sa_job.h>
#include <processing/jobs/send_dpd_job.h>
#include <processing/jobs/send_keepalive_job.h>
#include <processing/jobs/rekey_ike_sa_job.h>
-#include <encoding/payloads/unknown_payload.h>
+#include <processing/jobs/retry_initiate_job.h>
+#include <sa/ikev2/tasks/ike_auth_lifetime.h>
#ifdef ME
-#include <sa/tasks/ike_me.h>
+#include <sa/ikev2/tasks/ike_me.h>
#include <processing/jobs/initiate_mediation_job.h>
#endif
@@ -86,6 +70,11 @@ struct private_ike_sa_t {
ike_sa_id_t *ike_sa_id;
/**
+ * IKE version of this SA.
+ */
+ ike_version_t version;
+
+ /**
* unique numerical ID for this IKE_SA.
*/
u_int32_t unique_id;
@@ -193,14 +182,14 @@ struct private_ike_sa_t {
keymat_t *keymat;
/**
- * Virtual IP on local host, if any
+ * Virtual IPs on local host
*/
- host_t *my_virtual_ip;
+ linked_list_t *my_vips;
/**
- * Virtual IP on remote host, if any
+ * Virtual IPs on remote host
*/
- host_t *other_virtual_ip;
+ linked_list_t *other_vips;
/**
* List of configuration attributes (attribute_entry_t)
@@ -228,6 +217,17 @@ struct private_ike_sa_t {
u_int32_t keepalive_interval;
/**
+ * interval for retries during initiation (e.g. if DNS resolution failed),
+ * 0 to disable (default)
+ */
+ u_int32_t retry_initiate_interval;
+
+ /**
+ * TRUE if a retry_initiate_job has been queued
+ */
+ bool retry_initiate_queued;
+
+ /**
* Timestamps for this IKE_SA
*/
u_int32_t stats[STAT_MAX];
@@ -248,9 +248,9 @@ struct private_ike_sa_t {
host_t *remote_host;
/**
- * TRUE if we are currently reauthenticating this IKE_SA
+ * Flush auth configs once established?
*/
- bool is_reauthenticating;
+ bool flush_auth_cfg;
};
/**
@@ -319,6 +319,15 @@ METHOD(ike_sa_t, get_statistic, u_int32_t,
return 0;
}
+METHOD(ike_sa_t, set_statistic, void,
+ private_ike_sa_t *this, statistic_t kind, u_int32_t value)
+{
+ if (kind < STAT_MAX)
+ {
+ this->stats[kind] = value;
+ }
+}
+
METHOD(ike_sa_t, get_my_host, host_t*,
private_ike_sa_t *this)
{
@@ -405,6 +414,9 @@ static void flush_auth_cfgs(private_ike_sa_t *this)
{
auth_cfg_t *cfg;
+ this->my_auth->purge(this->my_auth, FALSE);
+ this->other_auth->purge(this->other_auth, FALSE);
+
while (this->my_auths->remove_last(this->my_auths,
(void**)&cfg) == SUCCESS)
{
@@ -471,8 +483,8 @@ METHOD(ike_sa_t, send_keepalive, void,
data.ptr[0] = 0xFF;
data.len = 1;
packet->set_data(packet, data);
- DBG1(DBG_IKE, "sending keep alive");
- charon->sender->send(charon->sender, packet);
+ DBG1(DBG_IKE, "sending keep alive to %#H", this->other_host);
+ charon->sender->send_no_marker(charon->sender, packet);
diff = 0;
}
job = send_keepalive_job_create(this->ike_sa_id);
@@ -563,6 +575,7 @@ METHOD(ike_sa_t, send_dpd, status_t,
{
job_t *job;
time_t diff, delay;
+ bool task_queued = FALSE;
if (this->state == IKE_PASSIVE)
{
@@ -583,27 +596,11 @@ METHOD(ike_sa_t, send_dpd, status_t,
diff = now - last_in;
if (!delay || diff >= delay)
{
- /* to long ago, initiate dead peer detection */
- task_t *task;
- ike_mobike_t *mobike;
-
- if (supports_extension(this, EXT_MOBIKE) &&
- has_condition(this, COND_NAT_HERE))
- {
- /* use mobike enabled DPD to detect NAT mapping changes */
- mobike = ike_mobike_create(&this->public, TRUE);
- mobike->dpd(mobike);
- task = &mobike->task;
- }
- else
- {
- task = (task_t*)ike_dpd_create(TRUE);
- }
- diff = 0;
+ /* too long ago, initiate dead peer detection */
DBG1(DBG_IKE, "sending DPD request");
-
- this->task_manager->queue_task(this->task_manager, task);
- this->task_manager->initiate(this->task_manager);
+ this->task_manager->queue_dpd(this->task_manager);
+ task_queued = TRUE;
+ diff = 0;
}
}
/* recheck in "interval" seconds */
@@ -612,6 +609,10 @@ METHOD(ike_sa_t, send_dpd, status_t,
job = (job_t*)send_dpd_job_create(this->ike_sa_id);
lib->scheduler->schedule_job(lib->scheduler, job, delay - diff);
}
+ if (task_queued)
+ {
+ return this->task_manager->initiate(this->task_manager);
+ }
return SUCCESS;
}
@@ -646,7 +647,7 @@ METHOD(ike_sa_t, set_state, void,
/* schedule rekeying if we have a time which is smaller than
* an already scheduled rekeying */
- t = this->peer_cfg->get_rekey_time(this->peer_cfg);
+ t = this->peer_cfg->get_rekey_time(this->peer_cfg, TRUE);
if (t && (this->stats[STAT_REKEY] == 0 ||
(this->stats[STAT_REKEY] > t + this->stats[STAT_ESTABLISHED])))
{
@@ -655,7 +656,7 @@ METHOD(ike_sa_t, set_state, void,
lib->scheduler->schedule_job(lib->scheduler, job, t);
DBG1(DBG_IKE, "scheduling rekeying in %ds", t);
}
- t = this->peer_cfg->get_reauth_time(this->peer_cfg);
+ t = this->peer_cfg->get_reauth_time(this->peer_cfg, TRUE);
if (t && (this->stats[STAT_REAUTH] == 0 ||
(this->stats[STAT_REAUTH] > t + this->stats[STAT_ESTABLISHED])))
{
@@ -698,7 +699,14 @@ METHOD(ike_sa_t, set_state, void,
if (trigger_dpd)
{
- send_dpd(this);
+ if (supports_extension(this, EXT_DPD))
+ {
+ send_dpd(this);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "DPD not supported by peer, disabled");
+ }
}
}
@@ -716,7 +724,8 @@ METHOD(ike_sa_t, reset, void,
flush_auth_cfgs(this);
this->keymat->destroy(this->keymat);
- this->keymat = keymat_create(this->ike_sa_id->is_initiator(this->ike_sa_id));
+ this->keymat = keymat_create(this->version,
+ this->ike_sa_id->is_initiator(this->ike_sa_id));
this->task_manager->reset(this->task_manager, 0, 0);
}
@@ -727,7 +736,7 @@ METHOD(ike_sa_t, get_keymat, keymat_t*,
return this->keymat;
}
-METHOD(ike_sa_t, set_virtual_ip, void,
+METHOD(ike_sa_t, add_virtual_ip, void,
private_ike_sa_t *this, bool local, host_t *ip)
{
if (local)
@@ -736,39 +745,44 @@ METHOD(ike_sa_t, set_virtual_ip, void,
if (hydra->kernel_interface->add_ip(hydra->kernel_interface, ip,
this->my_host) == SUCCESS)
{
- if (this->my_virtual_ip)
- {
- DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip);
- hydra->kernel_interface->del_ip(hydra->kernel_interface,
- this->my_virtual_ip);
- }
- DESTROY_IF(this->my_virtual_ip);
- this->my_virtual_ip = ip->clone(ip);
+ this->my_vips->insert_last(this->my_vips, ip->clone(ip));
}
else
{
DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
- this->my_virtual_ip = NULL;
}
}
else
{
- DESTROY_IF(this->other_virtual_ip);
- this->other_virtual_ip = ip->clone(ip);
+ this->other_vips->insert_last(this->other_vips, ip->clone(ip));
}
}
-METHOD(ike_sa_t, get_virtual_ip, host_t*,
+
+METHOD(ike_sa_t, clear_virtual_ips, void,
private_ike_sa_t *this, bool local)
{
- if (local)
+ linked_list_t *vips = local ? this->my_vips : this->other_vips;
+ host_t *vip;
+
+ while (vips->remove_first(vips, (void**)&vip) == SUCCESS)
{
- return this->my_virtual_ip;
+ if (local)
+ {
+ hydra->kernel_interface->del_ip(hydra->kernel_interface, vip);
+ }
+ vip->destroy(vip);
}
- else
+}
+
+METHOD(ike_sa_t, create_virtual_ip_enumerator, enumerator_t*,
+ private_ike_sa_t *this, bool local)
+{
+ if (local)
{
- return this->other_virtual_ip;
+ return this->my_vips->create_enumerator(this->my_vips);
}
+ return this->other_vips->create_enumerator(this->other_vips);
}
METHOD(ike_sa_t, add_peer_address, void,
@@ -837,9 +851,11 @@ METHOD(ike_sa_t, float_ports, void,
private_ike_sa_t *this)
{
/* do not switch if we have a custom port from MOBIKE/NAT */
- if (this->my_host->get_port(this->my_host) == IKEV2_UDP_PORT)
+ if (this->my_host->get_port(this->my_host) ==
+ charon->socket->get_port(charon->socket, FALSE))
{
- this->my_host->set_port(this->my_host, IKEV2_NATT_PORT);
+ this->my_host->set_port(this->my_host,
+ charon->socket->get_port(charon->socket, TRUE));
}
if (this->other_host->get_port(this->other_host) == IKEV2_UDP_PORT)
{
@@ -899,7 +915,7 @@ METHOD(ike_sa_t, update_hosts, void,
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
if (child_sa->update(child_sa, this->my_host,
- this->other_host, this->my_virtual_ip,
+ this->other_host, this->my_vips,
has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED)
{
this->public.rekey_child_sa(&this->public,
@@ -914,6 +930,8 @@ METHOD(ike_sa_t, update_hosts, void,
METHOD(ike_sa_t, generate_message, status_t,
private_ike_sa_t *this, message_t *message, packet_t **packet)
{
+ status_t status;
+
if (message->is_encoded(message))
{ /* already done */
*packet = message->get_packet(message);
@@ -921,44 +939,13 @@ METHOD(ike_sa_t, generate_message, status_t,
}
this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
message->set_ike_sa_id(message, this->ike_sa_id);
- charon->bus->message(charon->bus, message, FALSE);
- return message->generate(message,
- this->keymat->get_aead(this->keymat, FALSE), packet);
-}
-
-/**
- * send a notify back to the sender
- */
-static void send_notify_response(private_ike_sa_t *this, message_t *request,
- notify_type_t type, chunk_t data)
-{
- message_t *response;
- packet_t *packet;
-
- response = message_create();
- 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);
- if (this->my_host->is_anyaddr(this->my_host))
- {
- this->my_host->destroy(this->my_host);
- this->my_host = request->get_destination(request);
- this->my_host = this->my_host->clone(this->my_host);
- }
- if (this->other_host->is_anyaddr(this->other_host))
- {
- this->other_host->destroy(this->other_host);
- this->other_host = request->get_source(request);
- this->other_host = this->other_host->clone(this->other_host);
- }
- response->set_source(response, this->my_host->clone(this->my_host));
- response->set_destination(response, this->other_host->clone(this->other_host));
- if (generate_message(this, response, &packet) == SUCCESS)
+ charon->bus->message(charon->bus, message, FALSE, TRUE);
+ status = message->generate(message, this->keymat, packet);
+ if (status == SUCCESS)
{
- charon->sender->send(charon->sender, packet);
+ charon->bus->message(charon->bus, message, FALSE, FALSE);
}
- response->destroy(response);
+ return status;
}
METHOD(ike_sa_t, set_kmaddress, void,
@@ -1060,8 +1047,12 @@ static void resolve_hosts(private_ike_sa_t *this)
}
else
{
- host = host_create_from_dns(this->ike_cfg->get_other_addr(this->ike_cfg),
- 0, this->ike_cfg->get_other_port(this->ike_cfg));
+ char *other_addr;
+ u_int16_t other_port;
+
+ other_addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL);
+ other_port = this->ike_cfg->get_other_port(this->ike_cfg);
+ host = host_create_from_dns(other_addr, 0, other_port);
}
if (host)
{
@@ -1071,10 +1062,12 @@ static void resolve_hosts(private_ike_sa_t *this)
if (this->local_host)
{
host = this->local_host->clone(this->local_host);
- host->set_port(host, IKEV2_UDP_PORT);
+ host->set_port(host, charon->socket->get_port(charon->socket, FALSE));
}
else
{
+ char *my_addr;
+ u_int16_t my_port;
int family = 0;
/* use same address family as for other */
@@ -1082,8 +1075,9 @@ static void resolve_hosts(private_ike_sa_t *this)
{
family = this->other_host->get_family(this->other_host);
}
- host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg),
- family, this->ike_cfg->get_my_port(this->ike_cfg));
+ my_addr = this->ike_cfg->get_my_addr(this->ike_cfg, NULL);
+ my_port = this->ike_cfg->get_my_port(this->ike_cfg);
+ host = host_create_from_dns(my_addr, family, my_port);
if (host && host->is_anyaddr(host) &&
!this->other_host->is_anyaddr(this->other_host))
@@ -1097,9 +1091,7 @@ static void resolve_hosts(private_ike_sa_t *this)
}
else
{ /* fallback to address family specific %any(6), if configured */
- host = host_create_from_dns(
- this->ike_cfg->get_my_addr(this->ike_cfg),
- 0, this->ike_cfg->get_my_port(this->ike_cfg));
+ host = host_create_from_dns(my_addr, 0, my_port);
}
}
}
@@ -1113,7 +1105,7 @@ METHOD(ike_sa_t, initiate, status_t,
private_ike_sa_t *this, child_cfg_t *child_cfg, u_int32_t reqid,
traffic_selector_t *tsi, traffic_selector_t *tsr)
{
- task_t *task;
+ bool defer_initiate = FALSE;
if (this->state == IKE_CREATED)
{
@@ -1129,39 +1121,31 @@ METHOD(ike_sa_t, initiate, status_t,
#endif /* ME */
)
{
- child_cfg->destroy(child_cfg);
- DBG1(DBG_IKE, "unable to initiate to %%any");
- charon->bus->alert(charon->bus, ALERT_PEER_ADDR_FAILED);
- return DESTROY_ME;
+ char *addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL);
+ bool is_anyaddr = streq(addr, "%any") || streq(addr, "%any6");
+
+ if (is_anyaddr || !this->retry_initiate_interval)
+ {
+ if (is_anyaddr)
+ {
+ DBG1(DBG_IKE, "unable to initiate to %s", addr);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "unable to resolve %s, initiate aborted",
+ addr);
+ }
+ DESTROY_IF(child_cfg);
+ charon->bus->alert(charon->bus, ALERT_PEER_ADDR_FAILED);
+ return DESTROY_ME;
+ }
+ DBG1(DBG_IKE, "unable to resolve %s, retrying in %ds",
+ addr, this->retry_initiate_interval);
+ defer_initiate = TRUE;
}
set_condition(this, COND_ORIGINAL_INITIATOR, TRUE);
-
- task = (task_t*)ike_vendor_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_natd_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_cert_pre_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_auth_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_cert_post_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_config_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_auth_lifetime_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- if (this->peer_cfg->use_mobike(this->peer_cfg))
- {
- task = (task_t*)ike_mobike_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- }
-#ifdef ME
- task = (task_t*)ike_me_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
-#endif /* ME */
+ this->task_manager->queue_ike(this->task_manager);
}
#ifdef ME
@@ -1178,18 +1162,11 @@ METHOD(ike_sa_t, initiate, status_t,
}
else
#endif /* ME */
+ if (child_cfg)
{
/* normal IKE_SA with CHILD_SA */
- task = (task_t*)child_create_create(&this->public, child_cfg, FALSE,
- tsi, tsr);
- child_cfg->destroy(child_cfg);
- if (reqid)
- {
- child_create_t *child_create = (child_create_t*)task;
- child_create->use_reqid(child_create, reqid);
- }
- this->task_manager->queue_task(this->task_manager, task);
-
+ this->task_manager->queue_child(this->task_manager, child_cfg, reqid,
+ tsi, tsr);
#ifdef ME
if (this->peer_cfg->get_mediated_by(this->peer_cfg))
{
@@ -1201,135 +1178,74 @@ METHOD(ike_sa_t, initiate, status_t,
#endif /* ME */
}
+ if (defer_initiate)
+ {
+ if (!this->retry_initiate_queued)
+ {
+ job_t *job = (job_t*)retry_initiate_job_create(this->ike_sa_id);
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
+ this->retry_initiate_interval);
+ this->retry_initiate_queued = TRUE;
+ }
+ return SUCCESS;
+ }
+ this->retry_initiate_queued = FALSE;
return this->task_manager->initiate(this->task_manager);
}
+METHOD(ike_sa_t, retry_initiate, status_t,
+ private_ike_sa_t *this)
+{
+ if (this->retry_initiate_queued)
+ {
+ this->retry_initiate_queued = FALSE;
+ return initiate(this, NULL, 0, NULL, NULL);
+ }
+ return SUCCESS;
+}
+
METHOD(ike_sa_t, process_message, status_t,
private_ike_sa_t *this, message_t *message)
{
status_t status;
- bool is_request;
- u_int8_t type = 0;
if (this->state == IKE_PASSIVE)
{ /* do not handle messages in passive state */
return FAILED;
}
-
- is_request = message->get_request(message);
-
- status = message->parse_body(message,
- this->keymat->get_aead(this->keymat, TRUE));
- if (status == SUCCESS)
- { /* check for unsupported critical payloads */
- enumerator_t *enumerator;
- unknown_payload_t *unknown;
- payload_t *payload;
-
- enumerator = message->create_payload_enumerator(message);
- while (enumerator->enumerate(enumerator, &payload))
- {
- unknown = (unknown_payload_t*)payload;
- type = payload->get_type(payload);
- if (!payload_is_known(type) &&
- unknown->is_critical(unknown))
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ case AGGRESSIVE:
+ case IKE_SA_INIT:
+ case IKE_AUTH:
+ if (this->state != IKE_CREATED &&
+ this->state != IKE_CONNECTING)
{
- DBG1(DBG_ENC, "payload type %N is not supported, "
- "but its critical!", payload_type_names, type);
- status = NOT_SUPPORTED;
+ DBG1(DBG_IKE, "ignoring %N in established IKE_SA state",
+ exchange_type_names, message->get_exchange_type(message));
+ return FAILED;
}
- }
- enumerator->destroy(enumerator);
+ break;
+ default:
+ break;
}
- if (status != SUCCESS)
+ if (message->get_major_version(message) != this->version)
{
- if (is_request)
- {
- switch (status)
- {
- case NOT_SUPPORTED:
- DBG1(DBG_IKE, "critical unknown payloads found");
- if (is_request)
- {
- send_notify_response(this, message,
- UNSUPPORTED_CRITICAL_PAYLOAD,
- chunk_from_thing(type));
- this->task_manager->incr_mid(this->task_manager, FALSE);
- }
- break;
- case PARSE_ERROR:
- DBG1(DBG_IKE, "message parsing failed");
- if (is_request)
- {
- send_notify_response(this, message,
- INVALID_SYNTAX, chunk_empty);
- this->task_manager->incr_mid(this->task_manager, FALSE);
- }
- break;
- case VERIFY_ERROR:
- DBG1(DBG_IKE, "message verification failed");
- if (is_request)
- {
- send_notify_response(this, message,
- INVALID_SYNTAX, chunk_empty);
- this->task_manager->incr_mid(this->task_manager, 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",
+ DBG1(DBG_IKE, "ignoring %N IKEv%u exchange on %N SA",
exchange_type_names, message->get_exchange_type(message),
- message->get_request(message) ? "request" : "response",
- message->get_message_id(message));
-
- if (this->state == IKE_CREATED)
- { /* invalid initiation attempt, close SA */
- return DESTROY_ME;
- }
+ message->get_major_version(message),
+ ike_version_names, this->version);
+ /* TODO-IKEv1: fall back to IKEv1 if we receive an IKEv1
+ * INVALID_MAJOR_VERSION on an IKEv2 SA. */
+ return FAILED;
}
- else
+ status = this->task_manager->process_message(this->task_manager, message);
+ if (this->flush_auth_cfg && this->state == IKE_ESTABLISHED)
{
- /* if this IKE_SA is virgin, we check for a config */
- if (this->ike_cfg == NULL)
- {
- job_t *job;
- host_t *me = message->get_destination(message),
- *other = message->get_source(message);
- this->ike_cfg = charon->backends->get_ike_cfg(charon->backends,
- me, other);
- if (this->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, message,
- NO_PROPOSAL_CHOSEN, chunk_empty);
- return DESTROY_ME;
- }
- /* add a timeout if peer does not establish it completely */
- job = (job_t*)delete_ike_sa_job_create(this->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->stats[STAT_INBOUND] = time_monotonic(NULL);
- status = this->task_manager->process_message(this->task_manager,
- message);
- if (message->get_exchange_type(message) == IKE_AUTH &&
- this->state == IKE_ESTABLISHED &&
- lib->settings->get_bool(lib->settings,
- "charon.flush_auth_cfg", FALSE))
- { /* authentication completed */
- flush_auth_cfgs(this);
- }
+ /* authentication completed */
+ this->flush_auth_cfg = FALSE;
+ flush_auth_cfgs(this);
}
return status;
}
@@ -1340,6 +1256,12 @@ METHOD(ike_sa_t, get_id, ike_sa_id_t*,
return this->ike_sa_id;
}
+METHOD(ike_sa_t, get_version, ike_version_t,
+ private_ike_sa_t *this)
+{
+ return this->version;
+}
+
METHOD(ike_sa_t, get_my_id, identification_t*,
private_ike_sa_t *this)
{
@@ -1373,6 +1295,10 @@ METHOD(ike_sa_t, get_other_eap_id, identification_t*,
current = cfg->get(cfg, AUTH_RULE_EAP_IDENTITY);
if (!current || current->get_type(current) == ID_ANY)
{
+ current = cfg->get(cfg, AUTH_RULE_XAUTH_IDENTITY);
+ }
+ if (!current || current->get_type(current) == ID_ANY)
+ {
current = cfg->get(cfg, AUTH_RULE_IDENTITY);
}
if (current && current->get_type(current) != ID_ANY)
@@ -1442,30 +1368,23 @@ METHOD(ike_sa_t, remove_child_sa, void,
METHOD(ike_sa_t, rekey_child_sa, status_t,
private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
{
- child_rekey_t *child_rekey;
-
if (this->state == IKE_PASSIVE)
{
return INVALID_STATE;
}
-
- child_rekey = child_rekey_create(&this->public, protocol, spi);
- this->task_manager->queue_task(this->task_manager, &child_rekey->task);
+ this->task_manager->queue_child_rekey(this->task_manager, protocol, spi);
return this->task_manager->initiate(this->task_manager);
}
METHOD(ike_sa_t, delete_child_sa, status_t,
- private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
+ private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi, bool expired)
{
- child_delete_t *child_delete;
-
if (this->state == IKE_PASSIVE)
{
return INVALID_STATE;
}
-
- child_delete = child_delete_create(&this->public, protocol, spi);
- this->task_manager->queue_task(this->task_manager, &child_delete->task);
+ this->task_manager->queue_child_delete(this->task_manager,
+ protocol, spi, expired);
return this->task_manager->initiate(this->task_manager);
}
@@ -1495,14 +1414,17 @@ METHOD(ike_sa_t, destroy_child_sa, status_t,
METHOD(ike_sa_t, delete_, status_t,
private_ike_sa_t *this)
{
- ike_delete_t *ike_delete;
-
switch (this->state)
{
- case IKE_ESTABLISHED:
case IKE_REKEYING:
- ike_delete = ike_delete_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, &ike_delete->task);
+ if (this->version == IKEV1)
+ { /* SA has been reauthenticated, delete */
+ charon->bus->ike_updown(charon->bus, &this->public, FALSE);
+ break;
+ }
+ /* FALL */
+ case IKE_ESTABLISHED:
+ this->task_manager->queue_ike_delete(this->task_manager);
return this->task_manager->initiate(this->task_manager);
case IKE_CREATED:
DBG1(DBG_IKE, "deleting unestablished IKE_SA");
@@ -1521,23 +1443,17 @@ METHOD(ike_sa_t, delete_, status_t,
METHOD(ike_sa_t, rekey, status_t,
private_ike_sa_t *this)
{
- ike_rekey_t *ike_rekey;
-
if (this->state == IKE_PASSIVE)
{
return INVALID_STATE;
}
- ike_rekey = ike_rekey_create(&this->public, TRUE);
-
- this->task_manager->queue_task(this->task_manager, &ike_rekey->task);
+ this->task_manager->queue_ike_rekey(this->task_manager);
return this->task_manager->initiate(this->task_manager);
}
METHOD(ike_sa_t, reauth, status_t,
private_ike_sa_t *this)
{
- task_t *task;
-
if (this->state == IKE_PASSIVE)
{
return INVALID_STATE;
@@ -1548,7 +1464,8 @@ METHOD(ike_sa_t, reauth, status_t,
if (!has_condition(this, COND_ORIGINAL_INITIATOR))
{
DBG1(DBG_IKE, "initiator did not reauthenticate as requested");
- if (this->other_virtual_ip != NULL ||
+ if (this->other_vips->get_count(this->other_vips) != 0 ||
+ has_condition(this, COND_XAUTH_AUTHENTICATED) ||
has_condition(this, COND_EAP_AUTHENTICATED)
#ifdef ME
/* as mediation server we too cannot reauth the IKE_SA */
@@ -1575,10 +1492,8 @@ METHOD(ike_sa_t, reauth, status_t,
DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d]",
get_name(this), this->unique_id);
}
- this->is_reauthenticating = TRUE;
- task = (task_t*)ike_reauth_create(&this->public);
- this->task_manager->queue_task(this->task_manager, task);
-
+ set_condition(this, COND_REAUTHENTICATING, TRUE);
+ this->task_manager->queue_ike_reauth(this->task_manager);
return this->task_manager->initiate(this->task_manager);
}
@@ -1594,7 +1509,7 @@ METHOD(ike_sa_t, reestablish, status_t,
bool restart = FALSE;
status_t status = FAILED;
- if (this->is_reauthenticating)
+ if (has_condition(this, COND_REAUTHENTICATING))
{ /* only reauthenticate if we have children */
if (this->child_sas->get_count(this->child_sas) == 0
#ifdef ME
@@ -1653,7 +1568,7 @@ METHOD(ike_sa_t, reestablish, status_t,
/* check if we are able to reestablish this IKE_SA */
if (!has_condition(this, COND_ORIGINAL_INITIATOR) &&
- (this->other_virtual_ip != NULL ||
+ (this->other_vips->get_count(this->other_vips) != 0 ||
has_condition(this, COND_EAP_AUTHENTICATED)
#ifdef ME
|| this->is_mediation_server
@@ -1664,18 +1579,24 @@ METHOD(ike_sa_t, reestablish, status_t,
return FAILED;
}
- new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, TRUE);
+ new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ this->version, TRUE);
+ if (!new)
+ {
+ return FAILED;
+ }
new->set_peer_cfg(new, this->peer_cfg);
host = this->other_host;
new->set_other_host(new, host->clone(host));
host = this->my_host;
new->set_my_host(new, host->clone(host));
/* if we already have a virtual IP, we reuse it */
- host = this->my_virtual_ip;
- if (host)
+ enumerator = this->my_vips->create_enumerator(this->my_vips);
+ while (enumerator->enumerate(enumerator, &host))
{
- new->set_virtual_ip(new, TRUE, host);
+ new->add_virtual_ip(new, TRUE, host);
}
+ enumerator->destroy(enumerator);
#ifdef ME
if (this->peer_cfg->is_mediation(this->peer_cfg))
@@ -1688,7 +1609,7 @@ METHOD(ike_sa_t, reestablish, status_t,
enumerator = this->child_sas->create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
- if (this->is_reauthenticating)
+ if (has_condition(this, COND_REAUTHENTICATING))
{
switch (child_sa->get_state(child_sa))
{
@@ -1744,6 +1665,7 @@ METHOD(ike_sa_t, reestablish, status_t,
}
else
{
+ charon->bus->ike_reestablish(charon->bus, &this->public, new);
charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
status = SUCCESS;
}
@@ -1751,40 +1673,6 @@ METHOD(ike_sa_t, reestablish, status_t,
return status;
}
-/**
- * Requeue the IKE_SA_INIT tasks for initiation, if required
- */
-static void requeue_init_tasks(private_ike_sa_t *this)
-{
- enumerator_t *enumerator;
- bool has_init = FALSE;
- task_t *task;
-
- /* if we have advanced to IKE_AUTH, the IKE_INIT and related tasks
- * have already completed. Recreate them if necessary. */
- enumerator = this->task_manager->create_task_enumerator(
- this->task_manager, TASK_QUEUE_QUEUED);
- while (enumerator->enumerate(enumerator, &task))
- {
- if (task->get_type(task) == IKE_INIT)
- {
- has_init = TRUE;
- break;
- }
- }
- enumerator->destroy(enumerator);
-
- if (!has_init)
- {
- task = (task_t*)ike_vendor_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_natd_create(&this->public, TRUE);
- this->task_manager->queue_task(this->task_manager, task);
- task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
- this->task_manager->queue_task(this->task_manager, task);
- }
-}
-
METHOD(ike_sa_t, retransmit, status_t,
private_ike_sa_t *this, u_int32_t message_id)
{
@@ -1800,7 +1688,7 @@ METHOD(ike_sa_t, retransmit, status_t,
{
case IKE_CONNECTING:
{
- /* retry IKE_SA_INIT if we have multiple keyingtries */
+ /* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */
u_int32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg);
this->keyingtry++;
if (tries == 0 || tries > this->keyingtry)
@@ -1809,7 +1697,7 @@ METHOD(ike_sa_t, retransmit, status_t,
this->keyingtry + 1, tries);
reset(this);
resolve_hosts(this);
- requeue_init_tasks(this);
+ this->task_manager->queue_ike(this->task_manager);
return this->task_manager->initiate(this->task_manager);
}
DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding");
@@ -1817,7 +1705,7 @@ METHOD(ike_sa_t, retransmit, status_t,
}
case IKE_DELETING:
DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding");
- if (this->is_reauthenticating)
+ if (has_condition(this, COND_REAUTHENTICATING))
{
DBG1(DBG_IKE, "delete during reauthentication failed, "
"trying to reestablish IKE_SA anyway");
@@ -1831,6 +1719,10 @@ METHOD(ike_sa_t, retransmit, status_t,
reestablish(this);
break;
}
+ if (this->state != IKE_CONNECTING)
+ {
+ charon->bus->ike_updown(charon->bus, &this->public, FALSE);
+ }
return DESTROY_ME;
}
return SUCCESS;
@@ -1840,7 +1732,6 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t,
private_ike_sa_t *this, u_int32_t lifetime)
{
u_int32_t diff, hard, soft, now;
- ike_auth_lifetime_t *task;
bool send_update;
diff = this->peer_cfg->get_over_time(this->peer_cfg);
@@ -1850,9 +1741,9 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t,
/* check if we have to send an AUTH_LIFETIME to enforce the new lifetime.
* We send the notify in IKE_AUTH if not yet ESTABLISHED. */
- send_update = this->state == IKE_ESTABLISHED &&
+ send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 &&
!has_condition(this, COND_ORIGINAL_INITIATOR) &&
- (this->other_virtual_ip != NULL ||
+ (this->other_vips->get_count(this->other_vips) != 0 ||
has_condition(this, COND_EAP_AUTHENTICATED));
if (lifetime < diff)
@@ -1890,12 +1781,16 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t,
/* give at least some seconds to reauthenticate */
this->stats[STAT_DELETE] = max(hard, now + 10);
+#ifdef USE_IKEV2
if (send_update)
{
+ ike_auth_lifetime_t *task;
+
task = ike_auth_lifetime_create(&this->public, TRUE);
this->task_manager->queue_task(this->task_manager, &task->task);
return this->task_manager->initiate(this->task_manager);
}
+#endif
return SUCCESS;
}
@@ -1953,8 +1848,6 @@ static bool is_any_path_valid(private_ike_sa_t *this)
METHOD(ike_sa_t, roam, status_t,
private_ike_sa_t *this, bool address)
{
- ike_mobike_t *mobike;
-
switch (this->state)
{
case IKE_CREATED:
@@ -1976,10 +1869,7 @@ METHOD(ike_sa_t, roam, status_t,
if (supports_extension(this, EXT_MOBIKE) && address)
{ /* if any addresses changed, send an updated list */
DBG1(DBG_IKE, "sending address list update using MOBIKE");
- mobike = ike_mobike_create(&this->public, TRUE);
- mobike->addresses(mobike);
- this->task_manager->queue_task(this->task_manager,
- (task_t*)mobike);
+ this->task_manager->queue_mobike(this->task_manager, FALSE, TRUE);
return this->task_manager->initiate(this->task_manager);
}
return SUCCESS;
@@ -2007,9 +1897,7 @@ METHOD(ike_sa_t, roam, status_t,
{
DBG1(DBG_IKE, "requesting address change using MOBIKE");
}
- mobike = ike_mobike_create(&this->public, TRUE);
- mobike->roam(mobike, address);
- this->task_manager->queue_task(this->task_manager, (task_t*)mobike);
+ this->task_manager->queue_mobike(this->task_manager, TRUE, address);
return this->task_manager->initiate(this->task_manager);
}
@@ -2044,6 +1932,18 @@ METHOD(ike_sa_t, create_task_enumerator, enumerator_t*,
return this->task_manager->create_task_enumerator(this->task_manager, queue);
}
+METHOD(ike_sa_t, flush_queue, void,
+ private_ike_sa_t *this, task_queue_t queue)
+{
+ this->task_manager->flush_queue(this->task_manager, queue);
+}
+
+METHOD(ike_sa_t, queue_task, void,
+ private_ike_sa_t *this, task_t *task)
+{
+ this->task_manager->queue_task(this->task_manager, task);
+}
+
METHOD(ike_sa_t, inherit, void,
private_ike_sa_t *this, ike_sa_t *other_public)
{
@@ -2052,6 +1952,7 @@ METHOD(ike_sa_t, inherit, void,
attribute_entry_t *entry;
enumerator_t *enumerator;
auth_cfg_t *cfg;
+ host_t *vip;
/* apply hosts and ids */
this->my_host->destroy(this->my_host);
@@ -2063,16 +1964,15 @@ METHOD(ike_sa_t, inherit, void,
this->my_id = other->my_id->clone(other->my_id);
this->other_id = other->other_id->clone(other->other_id);
- /* apply virtual assigned IPs... */
- if (other->my_virtual_ip)
+ /* apply assigned virtual IPs... */
+ while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS)
{
- this->my_virtual_ip = other->my_virtual_ip;
- other->my_virtual_ip = NULL;
+ other->my_vips->insert_first(other->my_vips, vip);
}
- if (other->other_virtual_ip)
+ while (this->other_vips->remove_last(this->other_vips,
+ (void**)&vip) == SUCCESS)
{
- this->other_virtual_ip = other->other_virtual_ip;
- other->other_virtual_ip = NULL;
+ other->other_vips->insert_first(other->other_vips, vip);
}
/* authentication information */
@@ -2147,11 +2047,12 @@ METHOD(ike_sa_t, destroy, void,
private_ike_sa_t *this)
{
attribute_entry_t *entry;
+ host_t *vip;
charon->bus->set_sa(charon->bus, &this->public);
set_state(this, IKE_DESTROYING);
- this->task_manager->destroy(this->task_manager);
+ DESTROY_IF(this->task_manager);
/* remove attributes first, as we pass the IKE_SA to the handler */
while (this->attributes->remove_last(this->attributes,
@@ -2169,24 +2070,31 @@ METHOD(ike_sa_t, destroy, void,
/* unset SA after here to avoid usage by the listeners */
charon->bus->set_sa(charon->bus, NULL);
- this->keymat->destroy(this->keymat);
+ DESTROY_IF(this->keymat);
- if (this->my_virtual_ip)
+ while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS)
{
- hydra->kernel_interface->del_ip(hydra->kernel_interface,
- this->my_virtual_ip);
- this->my_virtual_ip->destroy(this->my_virtual_ip);
+ hydra->kernel_interface->del_ip(hydra->kernel_interface, vip);
+ vip->destroy(vip);
}
- if (this->other_virtual_ip)
+ this->my_vips->destroy(this->my_vips);
+ while (this->other_vips->remove_last(this->other_vips,
+ (void**)&vip) == SUCCESS)
{
- if (this->peer_cfg && this->peer_cfg->get_pool(this->peer_cfg))
+ if (this->peer_cfg)
{
- hydra->attributes->release_address(hydra->attributes,
- this->peer_cfg->get_pool(this->peer_cfg),
- this->other_virtual_ip, get_other_eap_id(this));
+ linked_list_t *pools;
+ identification_t *id;
+
+ id = get_other_eap_id(this);
+ pools = linked_list_create_from_enumerator(
+ this->peer_cfg->create_pool_enumerator(this->peer_cfg));
+ hydra->attributes->release_address(hydra->attributes, pools, vip, id);
+ pools->destroy(pools);
}
- this->other_virtual_ip->destroy(this->other_virtual_ip);
+ vip->destroy(vip);
}
+ this->other_vips->destroy(this->other_vips);
this->peer_addresses->destroy_offset(this->peer_addresses,
offsetof(host_t, destroy));
#ifdef ME
@@ -2224,19 +2132,32 @@ METHOD(ike_sa_t, destroy, void,
/*
* Described in header.
*/
-ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
+ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
+ ike_version_t version)
{
private_ike_sa_t *this;
static u_int32_t unique_id = 0;
+ if (version == IKE_ANY)
+ { /* prefer IKEv2 if protocol not specified */
+#ifdef USE_IKEV2
+ version = IKEV2;
+#else
+ version = IKEV1;
+#endif
+ }
+
INIT(this,
.public = {
+ .get_version = _get_version,
.get_state = _get_state,
.set_state = _set_state,
.get_name = _get_name,
.get_statistic = _get_statistic,
+ .set_statistic = _set_statistic,
.process_message = _process_message,
.initiate = _initiate,
+ .retry_initiate = _retry_initiate,
.get_ike_cfg = _get_ike_cfg,
.set_ike_cfg = _set_ike_cfg,
.get_peer_cfg = _get_peer_cfg,
@@ -2292,11 +2213,14 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
.generate_message = _generate_message,
.reset = _reset,
.get_unique_id = _get_unique_id,
- .set_virtual_ip = _set_virtual_ip,
- .get_virtual_ip = _get_virtual_ip,
+ .add_virtual_ip = _add_virtual_ip,
+ .clear_virtual_ips = _clear_virtual_ips,
+ .create_virtual_ip_enumerator = _create_virtual_ip_enumerator,
.add_configuration_attribute = _add_configuration_attribute,
.set_kmaddress = _set_kmaddress,
.create_task_enumerator = _create_task_enumerator,
+ .flush_queue = _flush_queue,
+ .queue_task = _queue_task,
#ifdef ME
.act_as_mediation_server = _act_as_mediation_server,
.get_server_reflexive_host = _get_server_reflexive_host,
@@ -2310,12 +2234,13 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
#endif /* ME */
},
.ike_sa_id = ike_sa_id->clone(ike_sa_id),
+ .version = version,
.child_sas = linked_list_create(),
.my_host = host_create_any(AF_INET),
.other_host = host_create_any(AF_INET),
.my_id = identification_create_from_encoding(ID_ANY, chunk_empty),
.other_id = identification_create_from_encoding(ID_ANY, chunk_empty),
- .keymat = keymat_create(ike_sa_id->is_initiator(ike_sa_id)),
+ .keymat = keymat_create(version, initiator),
.state = IKE_CREATED,
.stats[STAT_INBOUND] = time_monotonic(NULL),
.stats[STAT_OUTBOUND] = time_monotonic(NULL),
@@ -2325,12 +2250,31 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
.other_auths = linked_list_create(),
.unique_id = ++unique_id,
.peer_addresses = linked_list_create(),
+ .my_vips = linked_list_create(),
+ .other_vips = linked_list_create(),
.attributes = linked_list_create(),
.keepalive_interval = lib->settings->get_time(lib->settings,
- "charon.keep_alive", KEEPALIVE_INTERVAL),
+ "%s.keep_alive", KEEPALIVE_INTERVAL, charon->name),
+ .retry_initiate_interval = lib->settings->get_time(lib->settings,
+ "%s.retry_initiate_interval", 0, charon->name),
+ .flush_auth_cfg = lib->settings->get_bool(lib->settings,
+ "%s.flush_auth_cfg", FALSE, charon->name),
);
+
+ if (version == IKEV2)
+ { /* always supported with IKEv2 */
+ enable_extension(this, EXT_DPD);
+ }
+
this->task_manager = task_manager_create(&this->public);
- this->my_host->set_port(this->my_host, IKEV2_UDP_PORT);
+ this->my_host->set_port(this->my_host,
+ charon->socket->get_port(charon->socket, FALSE));
+ if (!this->task_manager || !this->keymat)
+ {
+ DBG1(DBG_IKE, "IKE version %d not supported", this->version);
+ destroy(this);
+ return NULL;
+ }
return &this->public;
}
diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h
index 537565e89..af741c799 100644
--- a/src/libcharon/sa/ike_sa.h
+++ b/src/libcharon/sa/ike_sa.h
@@ -37,11 +37,13 @@ typedef struct ike_sa_t ike_sa_t;
#include <encoding/payloads/configuration_attribute.h>
#include <sa/ike_sa_id.h>
#include <sa/child_sa.h>
+#include <sa/task.h>
#include <sa/task_manager.h>
#include <sa/keymat.h>
#include <config/peer_cfg.h>
#include <config/ike_cfg.h>
#include <credentials/auth_cfg.h>
+#include <utils/packet.h>
/**
* Timeout in seconds after that a half open IKE_SA gets deleted.
@@ -69,7 +71,7 @@ typedef struct ike_sa_t ike_sa_t;
enum ike_extension_t {
/**
- * peer supports NAT traversal as specified in RFC4306
+ * peer supports NAT traversal as specified in RFC4306 or RFC3947
*/
EXT_NATT = (1<<0),
@@ -102,6 +104,21 @@ enum ike_extension_t {
* peer is probably a Windows 7 RAS client
*/
EXT_MS_WINDOWS = (1<<6),
+
+ /**
+ * peer supports XAuth authentication, draft-ietf-ipsec-isakmp-xauth-06
+ */
+ EXT_XAUTH = (1<<7),
+
+ /**
+ * peer supports DPD detection, RFC 3706 (or IKEv2)
+ */
+ EXT_DPD = (1<<8),
+
+ /**
+ * peer supports Cisco Unity configuration attributes
+ */
+ EXT_CISCO_UNITY = (1<<9),
};
/**
@@ -148,6 +165,21 @@ enum ike_condition_t {
* IKE_SA is stale, the peer is currently unreachable (MOBIKE)
*/
COND_STALE = (1<<7),
+
+ /**
+ * Initial contact received
+ */
+ COND_INIT_CONTACT_SEEN = (1<<8),
+
+ /**
+ * Peer has been authenticated using XAuth
+ */
+ COND_XAUTH_AUTHENTICATED = (1<<9),
+
+ /**
+ * This IKE_SA is currently being reauthenticated
+ */
+ COND_REAUTHENTICATING = (1<<10),
};
/**
@@ -270,6 +302,11 @@ struct ike_sa_t {
ike_sa_id_t* (*get_id) (ike_sa_t *this);
/**
+ * Gets the IKE version of the SA
+ */
+ ike_version_t (*get_version)(ike_sa_t *this);
+
+ /**
* Get the numerical ID uniquely defining this IKE_SA.
*
* @return unique ID
@@ -288,7 +325,7 @@ struct ike_sa_t {
*
* @param state state to set for the IKE_SA
*/
- void (*set_state) (ike_sa_t *this, ike_sa_state_t ike_sa);
+ void (*set_state) (ike_sa_t *this, ike_sa_state_t state);
/**
* Get the name of the connection this IKE_SA uses.
@@ -306,6 +343,14 @@ struct ike_sa_t {
u_int32_t (*get_statistic)(ike_sa_t *this, statistic_t kind);
/**
+ * Set statistic value of the IKE_SA.
+ *
+ * @param kind kind of value to update
+ * @param value value as integer
+ */
+ void (*set_statistic)(ike_sa_t *this, statistic_t kind, u_int32_t value);
+
+ /**
* Get the own host address.
*
* @return host address
@@ -661,6 +706,15 @@ struct ike_sa_t {
traffic_selector_t *tsr);
/**
+ * Retry initiation of this IKE_SA after it got deferred previously.
+ *
+ * @return
+ * - SUCCESS if initiation deferred or started
+ * - DESTROY_ME if initiation failed
+ */
+ status_t (*retry_initiate) (ike_sa_t *this);
+
+ /**
* Initiates the deletion of an IKE_SA.
*
* Sends a delete message to the remote peer and waits for
@@ -821,11 +875,13 @@ struct ike_sa_t {
*
* @param protocol protocol of the SA
* @param spi inbound SPI of the CHILD_SA
+ * @param expired TRUE if CHILD_SA is expired
* @return
* - NOT_FOUND, if IKE_SA has no such CHILD_SA
* - SUCCESS, if delete message sent
*/
- status_t (*delete_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi);
+ status_t (*delete_child_sa)(ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi, bool expired);
/**
* Destroy a CHILD SA with the specified protocol/SPI.
@@ -880,7 +936,7 @@ struct ike_sa_t {
status_t (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime);
/**
- * Set the virtual IP to use for this IKE_SA and its children.
+ * Add a virtual IP to use for this IKE_SA and its children.
*
* The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same
* lifetime as the IKE_SA.
@@ -888,15 +944,22 @@ struct ike_sa_t {
* @param local TRUE to set local address, FALSE for remote
* @param ip IP to set as virtual IP
*/
- void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip);
+ void (*add_virtual_ip) (ike_sa_t *this, bool local, host_t *ip);
/**
- * Get the virtual IP configured.
+ * Clear all virtual IPs stored on this IKE_SA.
+ *
+ * @param local TRUE to clear local addresses, FALSE for remote
+ */
+ void (*clear_virtual_ips) (ike_sa_t *this, bool local);
+
+ /**
+ * Create an enumerator over virtual IPs.
*
* @param local TRUE to get local virtual IP, FALSE for remote
- * @return host_t *virtual IP
+ * @return enumerator over host_t*
*/
- host_t* (*get_virtual_ip) (ike_sa_t *this, bool local);
+ enumerator_t* (*create_virtual_ip_enumerator) (ike_sa_t *this, bool local);
/**
* Register a configuration attribute to the IKE_SA.
@@ -933,6 +996,20 @@ struct ike_sa_t {
enumerator_t* (*create_task_enumerator)(ike_sa_t *this, task_queue_t queue);
/**
+ * Flush a task queue, cancelling all tasks in it.
+ *
+ * @param queue queue type to flush
+ */
+ void (*flush_queue)(ike_sa_t *this, task_queue_t queue);
+
+ /**
+ * Queue a task for initiaton to the task manager.
+ *
+ * @param task task to queue
+ */
+ void (*queue_task)(ike_sa_t *this, task_t *task);
+
+ /**
* Inherit all attributes of other to this after rekeying.
*
* When rekeying is completed, all CHILD_SAs, the virtual IP and all
@@ -955,11 +1032,14 @@ struct ike_sa_t {
};
/**
- * Creates an ike_sa_t object with a specific ID.
+ * Creates an ike_sa_t object with a specific ID and IKE version.
*
- * @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA
+ * @param ike_sa_id ike_sa_id_t to associate with new IKE_SA/ISAKMP_SA
+ * @param initiator TRUE to create this IKE_SA as initiator
+ * @param version IKE version of this SA
* @return ike_sa_t object
*/
-ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id);
+ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
+ ike_version_t version);
#endif /** IKE_SA_H_ @}*/
diff --git a/src/libcharon/sa/ike_sa_id.c b/src/libcharon/sa/ike_sa_id.c
index bea4c2124..0f0f1ab63 100644
--- a/src/libcharon/sa/ike_sa_id.c
+++ b/src/libcharon/sa/ike_sa_id.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -30,13 +31,18 @@ struct private_ike_sa_id_t {
*/
ike_sa_id_t public;
+ /**
+ * Major IKE version of IKE_SA.
+ */
+ u_int8_t ike_version;
+
/**
- * SPI of Initiator.
+ * SPI of initiator.
*/
u_int64_t initiator_spi;
/**
- * SPI of Responder.
+ * SPI of responder.
*/
u_int64_t responder_spi;
@@ -46,6 +52,12 @@ struct private_ike_sa_id_t {
bool is_initiator_flag;
};
+METHOD(ike_sa_id_t, get_ike_version, u_int8_t,
+ private_ike_sa_id_t *this)
+{
+ return this->ike_version;
+}
+
METHOD(ike_sa_id_t, set_responder_spi, void,
private_ike_sa_id_t *this, u_int64_t responder_spi)
{
@@ -77,23 +89,15 @@ METHOD(ike_sa_id_t, equals, bool,
{
return FALSE;
}
- if ((this->is_initiator_flag == other->is_initiator_flag) &&
- (this->initiator_spi == other->initiator_spi) &&
- (this->responder_spi == other->responder_spi))
- {
- /* private_ike_sa_id's are equal */
- return TRUE;
- }
- else
- {
- /* private_ike_sa_id's are not equal */
- return FALSE;
- }
+ return this->ike_version == other->ike_version &&
+ this->initiator_spi == other->initiator_spi &&
+ this->responder_spi == other->responder_spi;
}
METHOD(ike_sa_id_t, replace_values, void,
private_ike_sa_id_t *this, private_ike_sa_id_t *other)
{
+ this->ike_version = other->ike_version;
this->initiator_spi = other->initiator_spi;
this->responder_spi = other->responder_spi;
this->is_initiator_flag = other->is_initiator_flag;
@@ -108,22 +112,15 @@ METHOD(ike_sa_id_t, is_initiator, bool,
METHOD(ike_sa_id_t, switch_initiator, bool,
private_ike_sa_id_t *this)
{
- if (this->is_initiator_flag)
- {
- this->is_initiator_flag = FALSE;
- }
- else
- {
- this->is_initiator_flag = TRUE;
- }
+ this->is_initiator_flag = !this->is_initiator_flag;
return this->is_initiator_flag;
}
METHOD(ike_sa_id_t, clone_, ike_sa_id_t*,
private_ike_sa_id_t *this)
{
- return ike_sa_id_create(this->initiator_spi, this->responder_spi,
- this->is_initiator_flag);
+ return ike_sa_id_create(this->ike_version, this->initiator_spi,
+ this->responder_spi, this->is_initiator_flag);
}
METHOD(ike_sa_id_t, destroy, void,
@@ -135,13 +132,14 @@ METHOD(ike_sa_id_t, destroy, void,
/*
* Described in header.
*/
-ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi,
- bool is_initiator_flag)
+ike_sa_id_t * ike_sa_id_create(u_int8_t ike_version, u_int64_t initiator_spi,
+ u_int64_t responder_spi, bool is_initiator_flag)
{
private_ike_sa_id_t *this;
INIT(this,
.public = {
+ .get_ike_version = _get_ike_version,
.set_responder_spi = _set_responder_spi,
.set_initiator_spi = _set_initiator_spi,
.get_responder_spi = _get_responder_spi,
@@ -153,6 +151,7 @@ ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi,
.clone = _clone_,
.destroy = _destroy,
},
+ .ike_version = ike_version,
.initiator_spi = initiator_spi,
.responder_spi = responder_spi,
.is_initiator_flag = is_initiator_flag,
diff --git a/src/libcharon/sa/ike_sa_id.h b/src/libcharon/sa/ike_sa_id.h
index fb55359bc..227683d1c 100644
--- a/src/libcharon/sa/ike_sa_id.h
+++ b/src/libcharon/sa/ike_sa_id.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -29,13 +30,20 @@ typedef struct ike_sa_id_t ike_sa_id_t;
/**
* An object of type ike_sa_id_t is used to identify an IKE_SA.
*
- * An IKE_SA is identified by its initiator and responder spi's.
- * Additionally it contains the role of the actual running IKEv2 daemon
- * for the specific IKE_SA (original initiator or responder).
+ * An IKE_SA is identified by its initiator and responder SPIs.
+ * Additionally, it contains the major IKE version of the IKE_SA and, for IKEv2,
+ * the role of the daemon (original initiator or responder).
*/
struct ike_sa_id_t {
/**
+ * Get the major IKE version of this IKE_SA.
+ *
+ * @return IKE version
+ */
+ u_int8_t (*get_ike_version) (ike_sa_id_t *this);
+
+ /**
* Set the SPI of the responder.
*
* This function is called when a request or reply of a IKE_SA_INIT is received.
@@ -68,10 +76,12 @@ struct ike_sa_id_t {
/**
* Check if two ike_sa_id_t objects are equal.
*
- * Two ike_sa_id_t objects are equal if both SPI values and the role matches.
+ * Two ike_sa_id_t objects are equal if version and both SPI values match.
+ * The role is not compared.
*
* @param other ike_sa_id_t object to check if equal
- * @return TRUE if given ike_sa_id_t are equal, FALSE otherwise
+ * @return TRUE if given ike_sa_id_t are equal,
+ * FALSE otherwise
*/
bool (*equals) (ike_sa_id_t *this, ike_sa_id_t *other);
@@ -93,9 +103,9 @@ struct ike_sa_id_t {
bool (*is_initiator) (ike_sa_id_t *this);
/**
- * Switche the original initiator flag.
+ * Switch the original initiator flag.
*
- * @return TRUE if we are the original initiator after switch, FALSE otherwise
+ * @return new value if initiator flag.
*/
bool (*switch_initiator) (ike_sa_id_t *this);
@@ -113,14 +123,15 @@ struct ike_sa_id_t {
};
/**
- * Creates an ike_sa_id_t object with specific SPI's and defined role.
+ * Creates an ike_sa_id_t object.
*
+ * @param ike_version major IKE version
* @param initiator_spi initiators SPI
* @param responder_spi responders SPI
* @param is_initiaor TRUE if we are the original initiator
* @return ike_sa_id_t object
*/
-ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi,
- bool is_initiaor);
+ike_sa_id_t * ike_sa_id_create(u_int8_t ike_version, u_int64_t initiator_spi,
+ u_int64_t responder_spi, bool is_initiaor);
#endif /** IKE_SA_ID_H_ @}*/
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 731ae6007..a396235c2 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2005-2011 Martin Willi
* Copyright (C) 2011 revosec AG
- * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2008-2012 Tobias Brunner
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -157,18 +157,6 @@ static entry_t *entry_create()
}
/**
- * Function that matches entry_t objects by initiator SPI and the hash of the
- * IKE_SA_INIT message.
- */
-static bool entry_match_by_hash(entry_t *entry, ike_sa_id_t *id, chunk_t *hash)
-{
- return id->get_responder_spi(id) == 0 &&
- id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
- id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id) &&
- chunk_equals(*hash, entry->init_hash);
-}
-
-/**
* Function that matches entry_t objects by ike_sa_id_t.
*/
static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
@@ -179,7 +167,6 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
}
if ((id->get_responder_spi(id) == 0 ||
entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) &&
- id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id))
{
/* this is TRUE for IKE_SAs that we initiated but have not yet received a response */
@@ -201,8 +188,19 @@ static bool entry_match_by_sa(entry_t *entry, ike_sa_t *ike_sa)
*/
static u_int ike_sa_id_hash(ike_sa_id_t *ike_sa_id)
{
- /* we always use initiator spi as key */
- return ike_sa_id->get_initiator_spi(ike_sa_id);
+ /* IKEv2 does not mandate random SPIs (RFC 5996, 2.6), they just have to be
+ * locally unique, so we use our randomly allocated SPI whether we are
+ * initiator or responder to ensure a good distribution. The latter is not
+ * possible for IKEv1 as we don't know whether we are original initiator or
+ * not (based on the IKE header). But as RFC 2408, section 2.5.3 proposes
+ * SPIs (Cookies) to be allocated near random (we allocate them randomly
+ * anyway) it seems safe to always use the initiator SPI. */
+ if (ike_sa_id->get_ike_version(ike_sa_id) == IKEV1_MAJOR_VERSION ||
+ ike_sa_id->is_initiator(ike_sa_id))
+ {
+ return ike_sa_id->get_initiator_spi(ike_sa_id);
+ }
+ return ike_sa_id->get_responder_spi(ike_sa_id);
}
typedef struct half_open_t half_open_t;
@@ -227,14 +225,6 @@ static void half_open_destroy(half_open_t *this)
free(this);
}
-/**
- * Function that matches half_open_t objects by the given IP address chunk.
- */
-static bool half_open_match(half_open_t *half_open, chunk_t *addr)
-{
- return chunk_equals(*addr, half_open->other);
-}
-
typedef struct connected_peers_t connected_peers_t;
struct connected_peers_t {
@@ -262,15 +252,25 @@ static void connected_peers_destroy(connected_peers_t *this)
/**
* Function that matches connected_peers_t objects by the given ids.
*/
-static bool connected_peers_match(connected_peers_t *connected_peers,
+static inline bool connected_peers_match(connected_peers_t *connected_peers,
identification_t *my_id, identification_t *other_id,
- uintptr_t family)
+ int family)
{
return my_id->equals(my_id, connected_peers->my_id) &&
other_id->equals(other_id, connected_peers->other_id) &&
- family == connected_peers->family;
+ (!family || family == connected_peers->family);
}
+typedef struct init_hash_t init_hash_t;
+
+struct init_hash_t {
+ /** hash of IKE_SA_INIT or initial phase1 message (data is not cloned) */
+ chunk_t hash;
+
+ /** our SPI allocated for the IKE_SA based on this message */
+ u_int64_t our_spi;
+};
+
typedef struct segment_t segment_t;
/**
@@ -298,6 +298,20 @@ struct shareable_segment_t {
u_int count;
};
+typedef struct table_item_t table_item_t;
+
+/**
+ * Instead of using linked_list_t for each bucket we store the data in our own
+ * list to save memory.
+ */
+struct table_item_t {
+ /** data of this item */
+ void *value;
+
+ /** next item in the overflow list */
+ table_item_t *next;
+};
+
typedef struct private_ike_sa_manager_t private_ike_sa_manager_t;
/**
@@ -312,7 +326,7 @@ struct private_ike_sa_manager_t {
/**
* Hash table with entries for the ike_sa_t objects.
*/
- linked_list_t **ike_sa_table;
+ table_item_t **ike_sa_table;
/**
* The size of the hash table.
@@ -342,7 +356,7 @@ struct private_ike_sa_manager_t {
/**
* Hash table with half_open_t objects.
*/
- linked_list_t **half_open_table;
+ table_item_t **half_open_table;
/**
* Segments of the "half-open" hash table.
@@ -352,7 +366,7 @@ struct private_ike_sa_manager_t {
/**
* Hash table with connected_peers_t objects.
*/
- linked_list_t **connected_peers_table;
+ table_item_t **connected_peers_table;
/**
* Segments of the "connected peers" hash table.
@@ -360,6 +374,16 @@ struct private_ike_sa_manager_t {
shareable_segment_t *connected_peers_segments;
/**
+ * Hash table with init_hash_t objects.
+ */
+ table_item_t **init_hashes_table;
+
+ /**
+ * Segments of the "hashes" hash table.
+ */
+ segment_t *init_hashes_segments;
+
+ /**
* RNG to get random SPIs for our side
*/
rng_t *rng;
@@ -379,10 +403,10 @@ struct private_ike_sa_manager_t {
* Acquire a lock to access the segment of the table row with the given index.
* It also works with the segment index directly.
*/
-static void lock_single_segment(private_ike_sa_manager_t *this, u_int index)
+static inline void lock_single_segment(private_ike_sa_manager_t *this,
+ u_int index)
{
mutex_t *lock = this->segments[index & this->segment_mask].mutex;
-
lock->lock(lock);
}
@@ -390,10 +414,10 @@ static void lock_single_segment(private_ike_sa_manager_t *this, u_int index)
* Release the lock required to access the segment of the table row with the given index.
* It also works with the segment index directly.
*/
-static void unlock_single_segment(private_ike_sa_manager_t *this, u_int index)
+static inline void unlock_single_segment(private_ike_sa_manager_t *this,
+ u_int index)
{
mutex_t *lock = this->segments[index & this->segment_mask].mutex;
-
lock->unlock(lock);
}
@@ -456,9 +480,14 @@ struct private_enumerator_t {
u_int row;
/**
- * enumerator for the current table row
+ * current table item
+ */
+ table_item_t *current;
+
+ /**
+ * previous table item
*/
- enumerator_t *current;
+ table_item_t *prev;
};
METHOD(enumerator_t, enumerate, bool,
@@ -473,33 +502,23 @@ METHOD(enumerator_t, enumerate, bool,
{
while (this->row < this->manager->table_size)
{
+ this->prev = this->current;
if (this->current)
{
- entry_t *item;
-
- if (this->current->enumerate(this->current, &item))
- {
- *entry = this->entry = item;
- *segment = this->segment;
- return TRUE;
- }
- this->current->destroy(this->current);
- this->current = NULL;
- unlock_single_segment(this->manager, this->segment);
+ this->current = this->current->next;
}
else
{
- linked_list_t *list;
-
lock_single_segment(this->manager, this->segment);
- if ((list = this->manager->ike_sa_table[this->row]) != NULL &&
- list->get_count(list))
- {
- this->current = list->create_enumerator(list);
- continue;
- }
- unlock_single_segment(this->manager, this->segment);
+ this->current = this->manager->ike_sa_table[this->row];
}
+ if (this->current)
+ {
+ *entry = this->entry = this->current->value;
+ *segment = this->segment;
+ return TRUE;
+ }
+ unlock_single_segment(this->manager, this->segment);
this->row += this->manager->segment_count;
}
this->segment++;
@@ -517,7 +536,6 @@ METHOD(enumerator_t, enumerator_destroy, void,
}
if (this->current)
{
- this->current->destroy(this->current);
unlock_single_segment(this->manager, this->segment);
}
free(this);
@@ -546,19 +564,23 @@ static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this)
*/
static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry)
{
- linked_list_t *list;
+ table_item_t *current, *item;
u_int row, segment;
+ INIT(item,
+ .value = entry,
+ );
+
row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask;
segment = row & this->segment_mask;
lock_single_segment(this, segment);
- list = this->ike_sa_table[row];
- if (!list)
- {
- list = this->ike_sa_table[row] = linked_list_create();
+ current = this->ike_sa_table[row];
+ if (current)
+ { /* insert at the front of current bucket */
+ item->next = current;
}
- list->insert_last(list, entry);
+ this->ike_sa_table[row] = item;
this->segments[segment].count++;
return segment;
}
@@ -569,28 +591,30 @@ static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry)
{
- linked_list_t *list;
+ table_item_t *item, *prev = NULL;
u_int row, segment;
row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask;
segment = row & this->segment_mask;
- list = this->ike_sa_table[row];
- if (list)
+ item = this->ike_sa_table[row];
+ while (item)
{
- entry_t *current;
- enumerator_t *enumerator;
-
- enumerator = list->create_enumerator(list);
- while (enumerator->enumerate(enumerator, &current))
+ if (item->value == entry)
{
- if (current == entry)
+ if (prev)
{
- list->remove_at(list, enumerator);
- this->segments[segment].count--;
- break;
+ prev->next = item->next;
}
+ else
+ {
+ this->ike_sa_table[row] = item->next;
+ }
+ this->segments[segment].count--;
+ free(item);
+ break;
}
- enumerator->destroy(enumerator);
+ prev = item;
+ item = item->next;
}
}
@@ -602,9 +626,21 @@ static void remove_entry_at(private_enumerator_t *this)
this->entry = NULL;
if (this->current)
{
- linked_list_t *list = this->manager->ike_sa_table[this->row];
- list->remove_at(list, this->current);
+ table_item_t *current = this->current;
+
this->manager->segments[this->segment].count--;
+ this->current = this->prev;
+
+ if (this->prev)
+ {
+ this->prev->next = current->next;
+ }
+ else
+ {
+ this->manager->ike_sa_table[this->row] = current->next;
+ unlock_single_segment(this->manager, this->segment);
+ }
+ free(current);
}
}
@@ -614,26 +650,26 @@ static void remove_entry_at(private_enumerator_t *this)
*/
static status_t get_entry_by_match_function(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment,
- linked_list_match_t match, void *p1, void *p2)
+ linked_list_match_t match, void *param)
{
- entry_t *current;
- linked_list_t *list;
+ table_item_t *item;
u_int row, seg;
row = ike_sa_id_hash(ike_sa_id) & this->table_mask;
seg = row & this->segment_mask;
lock_single_segment(this, seg);
- list = this->ike_sa_table[row];
- if (list)
+ item = this->ike_sa_table[row];
+ while (item)
{
- if (list->find_first(list, match, (void**)&current, p1, p2) == SUCCESS)
+ if (match(item->value, param))
{
- *entry = current;
+ *entry = item->value;
*segment = seg;
/* the locked segment has to be unlocked by the caller */
return SUCCESS;
}
+ item = item->next;
}
unlock_single_segment(this, seg);
return NOT_FOUND;
@@ -647,18 +683,7 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment)
{
return get_entry_by_match_function(this, ike_sa_id, entry, segment,
- (linked_list_match_t)entry_match_by_id, ike_sa_id, NULL);
-}
-
-/**
- * Find an entry by initiator SPI and IKE_SA_INIT hash.
- * Note: On SUCCESS, the caller has to unlock the segment.
- */
-static status_t get_entry_by_hash(private_ike_sa_manager_t *this,
- ike_sa_id_t *ike_sa_id, chunk_t hash, entry_t **entry, u_int *segment)
-{
- return get_entry_by_match_function(this, ike_sa_id, entry, segment,
- (linked_list_match_t)entry_match_by_hash, ike_sa_id, &hash);
+ (linked_list_match_t)entry_match_by_id, ike_sa_id);
}
/**
@@ -669,7 +694,7 @@ static status_t get_entry_by_sa(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, ike_sa_t *ike_sa, entry_t **entry, u_int *segment)
{
return get_entry_by_match_function(this, ike_sa_id, entry, segment,
- (linked_list_match_t)entry_match_by_sa, ike_sa, NULL);
+ (linked_list_match_t)entry_match_by_sa, ike_sa);
}
/**
@@ -707,44 +732,43 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry,
*/
static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
{
- half_open_t *half_open = NULL;
- linked_list_t *list;
- chunk_t addr;
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
+ half_open_t *half_open;
+ chunk_t addr;
addr = entry->other->get_address(entry->other);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
lock = this->half_open_segments[segment].lock;
lock->write_lock(lock);
- list = this->half_open_table[row];
- if (list)
+ item = this->half_open_table[row];
+ while (item)
{
- half_open_t *current;
+ half_open = item->value;
- if (list->find_first(list, (linked_list_match_t)half_open_match,
- (void**)&current, &addr) == SUCCESS)
+ if (chunk_equals(addr, half_open->other))
{
- half_open = current;
half_open->count++;
- this->half_open_segments[segment].count++;
+ break;
}
- }
- else
- {
- list = this->half_open_table[row] = linked_list_create();
+ item = item->next;
}
- if (!half_open)
+ if (!item)
{
INIT(half_open,
.other = chunk_clone(addr),
.count = 1,
);
- list->insert_last(list, half_open);
- this->half_open_segments[segment].count++;
+ INIT(item,
+ .value = half_open,
+ .next = this->half_open_table[row],
+ );
+ this->half_open_table[row] = item;
}
+ this->half_open_segments[segment].count++;
lock->unlock(lock);
}
@@ -753,37 +777,41 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
{
- linked_list_t *list;
- chunk_t addr;
+ table_item_t *item, *prev = NULL;
u_int row, segment;
rwlock_t *lock;
+ chunk_t addr;
addr = entry->other->get_address(entry->other);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
lock = this->half_open_segments[segment].lock;
lock->write_lock(lock);
- list = this->half_open_table[row];
- if (list)
+ item = this->half_open_table[row];
+ while (item)
{
- half_open_t *current;
- enumerator_t *enumerator;
+ half_open_t *half_open = item->value;
- enumerator = list->create_enumerator(list);
- while (enumerator->enumerate(enumerator, &current))
+ if (chunk_equals(addr, half_open->other))
{
- if (half_open_match(current, &addr))
+ if (--half_open->count == 0)
{
- if (--current->count == 0)
+ if (prev)
{
- list->remove_at(list, enumerator);
- half_open_destroy(current);
+ prev->next = item->next;
}
- this->half_open_segments[segment].count--;
- break;
+ else
+ {
+ this->half_open_table[row] = item->next;
+ }
+ half_open_destroy(half_open);
+ free(item);
}
+ this->half_open_segments[segment].count--;
+ break;
}
- enumerator->destroy(enumerator);
+ prev = item;
+ item = item->next;
}
lock->unlock(lock);
}
@@ -793,28 +821,28 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
{
- connected_peers_t *connected_peers = NULL;
- chunk_t my_id, other_id;
- linked_list_t *list;
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
+ connected_peers_t *connected_peers;
+ chunk_t my_id, other_id;
+ int family;
my_id = entry->my_id->get_encoding(entry->my_id);
other_id = entry->other_id->get_encoding(entry->other_id);
+ family = entry->other->get_family(entry->other);
row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask;
segment = row & this->segment_mask;
lock = this->connected_peers_segments[segment].lock;
lock->write_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- connected_peers_t *current;
+ connected_peers = item->value;
- if (list->find_first(list, (linked_list_match_t)connected_peers_match,
- (void**)&current, entry->my_id, entry->other_id,
- (uintptr_t)entry->other->get_family(entry->other)) == SUCCESS)
+ if (connected_peers_match(connected_peers, entry->my_id,
+ entry->other_id, family))
{
- connected_peers = current;
if (connected_peers->sas->find_first(connected_peers->sas,
(linked_list_match_t)entry->ike_sa_id->equals,
NULL, entry->ike_sa_id) == SUCCESS)
@@ -822,22 +850,24 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
lock->unlock(lock);
return;
}
+ break;
}
- }
- else
- {
- list = this->connected_peers_table[row] = linked_list_create();
+ item = item->next;
}
- if (!connected_peers)
+ if (!item)
{
INIT(connected_peers,
.my_id = entry->my_id->clone(entry->my_id),
.other_id = entry->other_id->clone(entry->other_id),
- .family = entry->other->get_family(entry->other),
+ .family = family,
.sas = linked_list_create(),
);
- list->insert_last(list, connected_peers);
+ INIT(item,
+ .value = connected_peers,
+ .next = this->connected_peers_table[row],
+ );
+ this->connected_peers_table[row] = item;
}
connected_peers->sas->insert_last(connected_peers->sas,
entry->ike_sa_id->clone(entry->ike_sa_id));
@@ -850,54 +880,61 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
*/
static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
{
- chunk_t my_id, other_id;
- linked_list_t *list;
+ table_item_t *item, *prev = NULL;
u_int row, segment;
rwlock_t *lock;
+ chunk_t my_id, other_id;
+ int family;
my_id = entry->my_id->get_encoding(entry->my_id);
other_id = entry->other_id->get_encoding(entry->other_id);
+ family = entry->other->get_family(entry->other);
+
row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask;
segment = row & this->segment_mask;
lock = this->connected_peers_segments[segment].lock;
lock->write_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- connected_peers_t *current;
- enumerator_t *enumerator;
+ connected_peers_t *current = item->value;
- enumerator = list->create_enumerator(list);
- while (enumerator->enumerate(enumerator, &current))
+ if (connected_peers_match(current, entry->my_id, entry->other_id,
+ family))
{
- if (connected_peers_match(current, entry->my_id, entry->other_id,
- (uintptr_t)entry->other->get_family(entry->other)))
- {
- ike_sa_id_t *ike_sa_id;
- enumerator_t *inner;
+ enumerator_t *enumerator;
+ ike_sa_id_t *ike_sa_id;
- inner = current->sas->create_enumerator(current->sas);
- while (inner->enumerate(inner, &ike_sa_id))
+ enumerator = current->sas->create_enumerator(current->sas);
+ while (enumerator->enumerate(enumerator, &ike_sa_id))
+ {
+ if (ike_sa_id->equals(ike_sa_id, entry->ike_sa_id))
{
- if (ike_sa_id->equals(ike_sa_id, entry->ike_sa_id))
- {
- current->sas->remove_at(current->sas, inner);
- ike_sa_id->destroy(ike_sa_id);
- this->connected_peers_segments[segment].count--;
- break;
- }
+ current->sas->remove_at(current->sas, enumerator);
+ ike_sa_id->destroy(ike_sa_id);
+ this->connected_peers_segments[segment].count--;
+ break;
}
- inner->destroy(inner);
- if (current->sas->get_count(current->sas) == 0)
+ }
+ enumerator->destroy(enumerator);
+ if (current->sas->get_count(current->sas) == 0)
+ {
+ if (prev)
{
- list->remove_at(list, enumerator);
- connected_peers_destroy(current);
+ prev->next = item->next;
}
- break;
+ else
+ {
+ this->connected_peers_table[row] = item->next;
+ }
+ connected_peers_destroy(current);
+ free(item);
}
+ break;
}
- enumerator->destroy(enumerator);
+ prev = item;
+ item = item->next;
}
lock->unlock(lock);
}
@@ -907,13 +944,143 @@ static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entr
*/
static u_int64_t get_spi(private_ike_sa_manager_t *this)
{
- u_int64_t spi = 0;
+ u_int64_t spi;
+
+ if (this->rng &&
+ this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi))
+ {
+ return spi;
+ }
+ return 0;
+}
+
+/**
+ * Calculate the hash of the initial IKE message. Memory for the hash is
+ * allocated on success.
+ *
+ * @returns TRUE on success
+ */
+static bool get_init_hash(private_ike_sa_manager_t *this, message_t *message,
+ chunk_t *hash)
+{
+ if (!this->hasher)
+ { /* this might be the case when flush() has been called */
+ return FALSE;
+ }
+ if (message->get_exchange_type(message) == ID_PROT)
+ { /* include the source for Main Mode as the hash will be the same if
+ * SPIs are reused by two initiators that use the same proposal */
+ host_t *src = message->get_source(message);
+
+ if (!this->hasher->allocate_hash(this->hasher,
+ src->get_address(src), NULL))
+ {
+ return FALSE;
+ }
+ }
+ return this->hasher->allocate_hash(this->hasher,
+ message->get_packet_data(message), hash);
+}
+
+/**
+ * Check if we already have created an IKE_SA based on the initial IKE message
+ * with the given hash.
+ * If not the hash is stored, the hash data is not(!) cloned.
+ *
+ * Also, the local SPI is returned. In case of a retransmit this is already
+ * stored together with the hash, otherwise it is newly allocated and should
+ * be used to create the IKE_SA.
+ *
+ * @returns ALREADY_DONE if the message with the given hash has been seen before
+ * NOT_FOUND if the message hash was not found
+ * FAILED if the SPI allocation failed
+ */
+static status_t check_and_put_init_hash(private_ike_sa_manager_t *this,
+ chunk_t init_hash, u_int64_t *our_spi)
+{
+ table_item_t *item;
+ u_int row, segment;
+ mutex_t *mutex;
+ init_hash_t *init;
+ u_int64_t spi;
- if (this->rng)
+ row = chunk_hash(init_hash) & this->table_mask;
+ segment = row & this->segment_mask;
+ mutex = this->init_hashes_segments[segment].mutex;
+ mutex->lock(mutex);
+ item = this->init_hashes_table[row];
+ while (item)
{
- this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi);
+ init_hash_t *current = item->value;
+
+ if (chunk_equals(init_hash, current->hash))
+ {
+ *our_spi = current->our_spi;
+ mutex->unlock(mutex);
+ return ALREADY_DONE;
+ }
+ item = item->next;
+ }
+
+ spi = get_spi(this);
+ if (!spi)
+ {
+ return FAILED;
}
- return spi;
+
+ INIT(init,
+ .hash = {
+ .len = init_hash.len,
+ .ptr = init_hash.ptr,
+ },
+ .our_spi = spi,
+ );
+ INIT(item,
+ .value = init,
+ .next = this->init_hashes_table[row],
+ );
+ this->init_hashes_table[row] = item;
+ *our_spi = init->our_spi;
+ mutex->unlock(mutex);
+ return NOT_FOUND;
+}
+
+/**
+ * Remove the hash of an initial IKE message from the cache.
+ */
+static void remove_init_hash(private_ike_sa_manager_t *this, chunk_t init_hash)
+{
+ table_item_t *item, *prev = NULL;
+ u_int row, segment;
+ mutex_t *mutex;
+
+ row = chunk_hash(init_hash) & this->table_mask;
+ segment = row & this->segment_mask;
+ mutex = this->init_hashes_segments[segment].mutex;
+ mutex->lock(mutex);
+ item = this->init_hashes_table[row];
+ while (item)
+ {
+ init_hash_t *current = item->value;
+
+ if (chunk_equals(init_hash, current->hash))
+ {
+ if (prev)
+ {
+ prev->next = item->next;
+ }
+ else
+ {
+ this->init_hashes_table[row] = item->next;
+ }
+ free(current);
+ free(item);
+ break;
+ }
+ prev = item;
+ item = item->next;
+ }
+ mutex->unlock(mutex);
}
METHOD(ike_sa_manager_t, checkout, ike_sa_t*,
@@ -941,25 +1108,38 @@ METHOD(ike_sa_manager_t, checkout, ike_sa_t*,
}
METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*,
- private_ike_sa_manager_t* this, bool initiator)
+ private_ike_sa_manager_t* this, ike_version_t version, bool initiator)
{
ike_sa_id_t *ike_sa_id;
ike_sa_t *ike_sa;
+ u_int8_t ike_version;
+ u_int64_t spi;
+
+ ike_version = version == IKEV1 ? IKEV1_MAJOR_VERSION : IKEV2_MAJOR_VERSION;
+
+ spi = get_spi(this);
+ if (!spi)
+ {
+ DBG1(DBG_MGR, "failed to allocate SPI for new IKE_SA");
+ return NULL;
+ }
if (initiator)
{
- ike_sa_id = ike_sa_id_create(get_spi(this), 0, TRUE);
+ ike_sa_id = ike_sa_id_create(ike_version, spi, 0, TRUE);
}
else
{
- ike_sa_id = ike_sa_id_create(0, get_spi(this), FALSE);
+ ike_sa_id = ike_sa_id_create(ike_version, 0, spi, FALSE);
}
- ike_sa = ike_sa_create(ike_sa_id);
+ ike_sa = ike_sa_create(ike_sa_id, initiator, version);
ike_sa_id->destroy(ike_sa_id);
- DBG2(DBG_MGR, "created IKE_SA %s[%u]", ike_sa->get_name(ike_sa),
- ike_sa->get_unique_id(ike_sa));
-
+ if (ike_sa)
+ {
+ DBG2(DBG_MGR, "created IKE_SA %s[%u]", ike_sa->get_name(ike_sa),
+ ike_sa->get_unique_id(ike_sa));
+ }
return ike_sa;
}
@@ -970,94 +1150,118 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
entry_t *entry;
ike_sa_t *ike_sa = NULL;
ike_sa_id_t *id;
+ ike_version_t ike_version;
+ bool is_init = FALSE;
id = message->get_ike_sa_id(message);
+ /* clone the IKE_SA ID so we can modify the initiator flag */
id = id->clone(id);
id->switch_initiator(id);
DBG2(DBG_MGR, "checkout IKE_SA by message");
- if (message->get_request(message) &&
- message->get_exchange_type(message) == IKE_SA_INIT &&
- this->hasher)
+ if (id->get_responder_spi(id) == 0)
{
- /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */
- chunk_t data, hash;
-
- data = message->get_packet_data(message);
- this->hasher->allocate_hash(this->hasher, data, &hash);
- chunk_free(&data);
-
- if (get_entry_by_hash(this, id, hash, &entry, &segment) == SUCCESS)
+ if (message->get_major_version(message) == IKEV2_MAJOR_VERSION)
{
- if (entry->message_id == 0)
+ if (message->get_exchange_type(message) == IKE_SA_INIT &&
+ message->get_request(message))
{
- unlock_single_segment(this, segment);
- chunk_free(&hash);
- id->destroy(id);
- DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing");
- return NULL;
+ ike_version = IKEV2;
+ is_init = TRUE;
}
- else if (wait_for_entry(this, entry, segment))
+ }
+ else
+ {
+ if (message->get_exchange_type(message) == ID_PROT ||
+ message->get_exchange_type(message) == AGGRESSIVE)
{
- entry->checked_out = TRUE;
- entry->message_id = message->get_message_id(message);
- ike_sa = entry->ike_sa;
- DBG2(DBG_MGR, "IKE_SA %s[%u] checked out by hash",
- ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
+ ike_version = IKEV1;
+ is_init = TRUE;
+ if (id->is_initiator(id))
+ { /* not set in IKEv1, switch back before applying to new SA */
+ id->switch_initiator(id);
+ }
}
- unlock_single_segment(this, segment);
+ }
+ }
+
+ if (is_init)
+ {
+ u_int64_t our_spi;
+ chunk_t hash;
+
+ if (!get_init_hash(this, message, &hash))
+ {
+ DBG1(DBG_MGR, "ignoring message, failed to hash message");
+ id->destroy(id);
+ return NULL;
}
- if (ike_sa == NULL)
+ /* ensure this is not a retransmit of an already handled init message */
+ switch (check_and_put_init_hash(this, hash, &our_spi))
{
- if (id->get_responder_spi(id) == 0 &&
- message->get_exchange_type(message) == IKE_SA_INIT)
- {
- /* no IKE_SA found, create a new one */
- id->set_responder_spi(id, get_spi(this));
- entry = entry_create();
- entry->ike_sa = ike_sa_create(id);
- entry->ike_sa_id = id->clone(id);
+ case NOT_FOUND:
+ { /* we've not seen this packet yet, create a new IKE_SA */
+ id->set_responder_spi(id, our_spi);
+ ike_sa = ike_sa_create(id, FALSE, ike_version);
+ if (ike_sa)
+ {
+ entry = entry_create();
+ entry->ike_sa = ike_sa;
+ entry->ike_sa_id = id->clone(id);
- segment = put_entry(this, entry);
- entry->checked_out = TRUE;
- unlock_single_segment(this, segment);
+ segment = put_entry(this, entry);
+ entry->checked_out = TRUE;
+ unlock_single_segment(this, segment);
- entry->message_id = message->get_message_id(message);
- entry->init_hash = hash;
- ike_sa = entry->ike_sa;
+ entry->message_id = message->get_message_id(message);
+ entry->init_hash = hash;
- DBG2(DBG_MGR, "created IKE_SA %s[%u]",
- ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
+ DBG2(DBG_MGR, "created IKE_SA %s[%u]",
+ ike_sa->get_name(ike_sa),
+ ike_sa->get_unique_id(ike_sa));
+ }
+ else
+ {
+ remove_init_hash(this, hash);
+ chunk_free(&hash);
+ DBG1(DBG_MGR, "ignoring message, no such IKE_SA");
+ }
+ id->destroy(id);
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
}
- else
- {
+ case FAILED:
+ { /* we failed to allocate an SPI */
chunk_free(&hash);
- DBG1(DBG_MGR, "ignoring message, no such IKE_SA");
+ id->destroy(id);
+ DBG1(DBG_MGR, "ignoring message, failed to allocate SPI");
+ return NULL;
}
+ case ALREADY_DONE:
+ default:
+ break;
}
- else
- {
- chunk_free(&hash);
- }
- id->destroy(id);
- charon->bus->set_sa(charon->bus, ike_sa);
- return ike_sa;
+ /* it looks like we already handled this init message to some degree */
+ id->set_responder_spi(id, our_spi);
+ chunk_free(&hash);
}
if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS)
{
- /* only check out if we are not processing this request */
+ /* only check out in IKEv2 if we are not already processing it */
if (message->get_request(message) &&
message->get_message_id(message) == entry->message_id)
{
- DBG1(DBG_MGR, "ignoring request with ID %d, already processing",
+ DBG1(DBG_MGR, "ignoring request with ID %u, already processing",
entry->message_id);
}
else if (wait_for_entry(this, entry, segment))
{
- ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa);
+ ike_sa_id_t *ike_id;
+
+ ike_id = entry->ike_sa->get_id(entry->ike_sa);
entry->checked_out = TRUE;
entry->message_id = message->get_message_id(message);
if (ike_id->get_responder_spi(ike_id) == 0)
@@ -1089,7 +1293,7 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
if (!this->reuse_ikesa)
{ /* IKE_SA reuse disable by config */
- ike_sa = checkout_new(this, TRUE);
+ ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE);
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
}
@@ -1125,7 +1329,7 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
if (!ike_sa)
{ /* no IKE_SA using such a config, hand out a new */
- ike_sa = checkout_new(this, TRUE);
+ ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE);
}
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
@@ -1244,6 +1448,7 @@ static bool enumerator_filter_wait(private_ike_sa_manager_t *this,
if (wait_for_entry(this, *in, *segment))
{
*out = (*in)->ike_sa;
+ charon->bus->set_sa(charon->bus, *out);
return TRUE;
}
return FALSE;
@@ -1260,17 +1465,26 @@ static bool enumerator_filter_skip(private_ike_sa_manager_t *this,
!(*in)->checked_out)
{
*out = (*in)->ike_sa;
+ charon->bus->set_sa(charon->bus, *out);
return TRUE;
}
return FALSE;
}
+/**
+ * Reset threads SA after enumeration
+ */
+static void reset_sa(void *data)
+{
+ charon->bus->set_sa(charon->bus, NULL);
+}
+
METHOD(ike_sa_manager_t, create_enumerator, enumerator_t*,
private_ike_sa_manager_t* this, bool wait)
{
return enumerator_create_filter(create_table_enumerator(this),
wait ? (void*)enumerator_filter_wait : (void*)enumerator_filter_skip,
- this, NULL);
+ this, reset_sa);
}
METHOD(ike_sa_manager_t, checkin, void,
@@ -1290,7 +1504,7 @@ METHOD(ike_sa_manager_t, checkin, void,
ike_sa_id = ike_sa->get_id(ike_sa);
my_id = ike_sa->get_my_id(ike_sa);
- other_id = ike_sa->get_other_id(ike_sa);
+ other_id = ike_sa->get_other_eap_id(ike_sa);
other = ike_sa->get_other_host(ike_sa);
DBG2(DBG_MGR, "checkin IKE_SA %s[%u]", ike_sa->get_name(ike_sa),
@@ -1340,9 +1554,21 @@ METHOD(ike_sa_manager_t, checkin, void,
}
/* apply identities for duplicate test */
- if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
+ if ((ike_sa->get_state(ike_sa) == IKE_ESTABLISHED ||
+ ike_sa->get_state(ike_sa) == IKE_PASSIVE) &&
entry->my_id == NULL && entry->other_id == NULL)
{
+ if (ike_sa->get_version(ike_sa) == IKEV1)
+ {
+ /* If authenticated and received INITIAL_CONTACT,
+ * delete any existing IKE_SAs with that peer. */
+ if (ike_sa->has_condition(ike_sa, COND_INIT_CONTACT_SEEN))
+ {
+ this->public.check_uniqueness(&this->public, ike_sa, TRUE);
+ ike_sa->set_condition(ike_sa, COND_INIT_CONTACT_SEEN, FALSE);
+ }
+ }
+
entry->my_id = my_id->clone(my_id);
entry->other_id = other_id->clone(other_id);
if (!entry->other)
@@ -1376,6 +1602,16 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS)
{
+ if (entry->driveout_waiting_threads && entry->driveout_new_threads)
+ { /* it looks like flush() has been called and the SA is being deleted
+ * anyway, just check it in */
+ DBG2(DBG_MGR, "ignored check-in and destroy of IKE_SA during shutdown");
+ entry->checked_out = FALSE;
+ entry->condvar->broadcast(entry->condvar);
+ unlock_single_segment(this, segment);
+ return;
+ }
+
/* drive out waiting threads, as we are in hurry */
entry->driveout_waiting_threads = TRUE;
/* mark it, so no new threads can get this entry */
@@ -1399,6 +1635,10 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
{
remove_connected_peers(this, entry);
}
+ if (entry->init_hash.ptr)
+ {
+ remove_init_hash(this, entry->init_hash);
+ }
entry_destroy(entry);
@@ -1412,65 +1652,81 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
charon->bus->set_sa(charon->bus, NULL);
}
-METHOD(ike_sa_manager_t, check_uniqueness, bool,
- private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
+/**
+ * Cleanup function for create_id_enumerator
+ */
+static void id_enumerator_cleanup(linked_list_t *ids)
{
- bool cancel = FALSE;
- peer_cfg_t *peer_cfg;
- unique_policy_t policy;
- linked_list_t *list, *duplicate_ids = NULL;
- enumerator_t *enumerator;
- ike_sa_id_t *duplicate_id = NULL;
- identification_t *me, *other;
+ ids->destroy_offset(ids, offsetof(ike_sa_id_t, destroy));
+}
+
+METHOD(ike_sa_manager_t, create_id_enumerator, enumerator_t*,
+ private_ike_sa_manager_t *this, identification_t *me,
+ identification_t *other, int family)
+{
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
-
- peer_cfg = ike_sa->get_peer_cfg(ike_sa);
- policy = peer_cfg->get_unique_policy(peer_cfg);
- if (policy == UNIQUE_NO && !force_replace)
- {
- return FALSE;
- }
-
- me = ike_sa->get_my_id(ike_sa);
- other = ike_sa->get_other_id(ike_sa);
+ linked_list_t *ids = NULL;
row = chunk_hash_inc(other->get_encoding(other),
chunk_hash(me->get_encoding(me))) & this->table_mask;
segment = row & this->segment_mask;
- lock = this->connected_peers_segments[segment & this->segment_mask].lock;
+ lock = this->connected_peers_segments[segment].lock;
lock->read_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- connected_peers_t *current;
- host_t *other_host;
+ connected_peers_t *current = item->value;
- other_host = ike_sa->get_other_host(ike_sa);
- if (list->find_first(list, (linked_list_match_t)connected_peers_match,
- (void**)&current, me, other,
- (uintptr_t)other_host->get_family(other_host)) == SUCCESS)
+ if (connected_peers_match(current, me, other, family))
{
- /* clone the list, so we can release the lock */
- duplicate_ids = current->sas->clone_offset(current->sas,
- offsetof(ike_sa_id_t, clone));
+ ids = current->sas->clone_offset(current->sas,
+ offsetof(ike_sa_id_t, clone));
+ break;
}
+ item = item->next;
}
lock->unlock(lock);
- if (!duplicate_ids)
+ if (!ids)
+ {
+ return enumerator_create_empty();
+ }
+ return enumerator_create_cleaner(ids->create_enumerator(ids),
+ (void*)id_enumerator_cleanup, ids);
+}
+
+METHOD(ike_sa_manager_t, check_uniqueness, bool,
+ private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
+{
+ bool cancel = FALSE;
+ peer_cfg_t *peer_cfg;
+ unique_policy_t policy;
+ enumerator_t *enumerator;
+ ike_sa_id_t *id = NULL;
+ identification_t *me, *other;
+ host_t *other_host;
+
+ peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+ policy = peer_cfg->get_unique_policy(peer_cfg);
+ if (policy == UNIQUE_NEVER || (policy == UNIQUE_NO && !force_replace))
{
return FALSE;
}
+ me = ike_sa->get_my_id(ike_sa);
+ other = ike_sa->get_other_eap_id(ike_sa);
+ other_host = ike_sa->get_other_host(ike_sa);
- enumerator = duplicate_ids->create_enumerator(duplicate_ids);
- while (enumerator->enumerate(enumerator, &duplicate_id))
+ enumerator = create_id_enumerator(this, me, other,
+ other_host->get_family(other_host));
+ while (enumerator->enumerate(enumerator, &id))
{
status_t status = SUCCESS;
ike_sa_t *duplicate;
- duplicate = checkout(this, duplicate_id);
+ duplicate = checkout(this, id);
if (!duplicate)
{
continue;
@@ -1520,7 +1776,6 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
}
}
enumerator->destroy(enumerator);
- duplicate_ids->destroy_offset(duplicate_ids, offsetof(ike_sa_id_t, destroy));
/* reset thread's current IKE_SA after checkin */
charon->bus->set_sa(charon->bus, ike_sa);
return cancel;
@@ -1530,7 +1785,7 @@ METHOD(ike_sa_manager_t, has_contact, bool,
private_ike_sa_manager_t *this, identification_t *me,
identification_t *other, int family)
{
- linked_list_t *list;
+ table_item_t *item;
u_int row, segment;
rwlock_t *lock;
bool found = FALSE;
@@ -1538,16 +1793,17 @@ METHOD(ike_sa_manager_t, has_contact, bool,
row = chunk_hash_inc(other->get_encoding(other),
chunk_hash(me->get_encoding(me))) & this->table_mask;
segment = row & this->segment_mask;
- lock = this->connected_peers_segments[segment & this->segment_mask].lock;
+ lock = this->connected_peers_segments[segment].lock;
lock->read_lock(lock);
- list = this->connected_peers_table[row];
- if (list)
+ item = this->connected_peers_table[row];
+ while (item)
{
- if (list->find_first(list, (linked_list_match_t)connected_peers_match,
- NULL, me, other, family) == SUCCESS)
+ if (connected_peers_match(item->value, me, other, family))
{
found = TRUE;
+ break;
}
+ item = item->next;
}
lock->unlock(lock);
@@ -1573,8 +1829,8 @@ METHOD(ike_sa_manager_t, get_count, u_int,
METHOD(ike_sa_manager_t, get_half_open_count, u_int,
private_ike_sa_manager_t *this, host_t *ip)
{
- linked_list_t *list;
- u_int segment, row;
+ table_item_t *item;
+ u_int row, segment;
rwlock_t *lock;
chunk_t addr;
u_int count = 0;
@@ -1584,17 +1840,19 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
addr = ip->get_address(ip);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
- lock = this->half_open_segments[segment & this->segment_mask].lock;
+ lock = this->half_open_segments[segment].lock;
lock->read_lock(lock);
- if ((list = this->half_open_table[row]) != NULL)
+ item = this->half_open_table[row];
+ while (item)
{
- half_open_t *current;
+ half_open_t *half_open = item->value;
- if (list->find_first(list, (linked_list_match_t)half_open_match,
- (void**)&current, &addr) == SUCCESS)
+ if (chunk_equals(addr, half_open->other))
{
- count = current->count;
+ count = half_open->count;
+ break;
}
+ item = item->next;
}
lock->unlock(lock);
}
@@ -1602,7 +1860,7 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
{
for (segment = 0; segment < this->segment_count; segment++)
{
- lock = this->half_open_segments[segment & this->segment_mask].lock;
+ lock = this->half_open_segments[segment].lock;
lock->read_lock(lock);
count += this->half_open_segments[segment].count;
lock->unlock(lock);
@@ -1651,16 +1909,18 @@ METHOD(ike_sa_manager_t, flush, void,
while (enumerator->enumerate(enumerator, &entry, &segment))
{
charon->bus->set_sa(charon->bus, entry->ike_sa);
- /* as the delete never gets processed, fire down events */
- switch (entry->ike_sa->get_state(entry->ike_sa))
- {
- case IKE_ESTABLISHED:
- case IKE_REKEYING:
- case IKE_DELETING:
- charon->bus->ike_updown(charon->bus, entry->ike_sa, FALSE);
- break;
- default:
- break;
+ if (entry->ike_sa->get_version(entry->ike_sa) == IKEV2)
+ { /* as the delete never gets processed, fire down events */
+ switch (entry->ike_sa->get_state(entry->ike_sa))
+ {
+ case IKE_ESTABLISHED:
+ case IKE_REKEYING:
+ case IKE_DELETING:
+ charon->bus->ike_updown(charon->bus, entry->ike_sa, FALSE);
+ break;
+ default:
+ break;
+ }
}
entry->ike_sa->delete(entry->ike_sa);
}
@@ -1680,6 +1940,10 @@ METHOD(ike_sa_manager_t, flush, void,
{
remove_connected_peers(this, entry);
}
+ if (entry->init_hash.ptr)
+ {
+ remove_init_hash(this, entry->init_hash);
+ }
remove_entry_at((private_enumerator_t*)enumerator);
entry_destroy(entry);
}
@@ -1698,24 +1962,22 @@ METHOD(ike_sa_manager_t, destroy, void,
{
u_int i;
- for (i = 0; i < this->table_size; i++)
- {
- DESTROY_IF(this->ike_sa_table[i]);
- DESTROY_IF(this->half_open_table[i]);
- DESTROY_IF(this->connected_peers_table[i]);
- }
+ /* these are already cleared in flush() above */
free(this->ike_sa_table);
free(this->half_open_table);
free(this->connected_peers_table);
+ free(this->init_hashes_table);
for (i = 0; i < this->segment_count; i++)
{
this->segments[i].mutex->destroy(this->segments[i].mutex);
this->half_open_segments[i].lock->destroy(this->half_open_segments[i].lock);
this->connected_peers_segments[i].lock->destroy(this->connected_peers_segments[i].lock);
+ this->init_hashes_segments[i].mutex->destroy(this->init_hashes_segments[i].mutex);
}
free(this->segments);
free(this->half_open_segments);
free(this->connected_peers_segments);
+ free(this->init_hashes_segments);
free(this);
}
@@ -1757,6 +2019,7 @@ ike_sa_manager_t *ike_sa_manager_create()
.check_uniqueness = _check_uniqueness,
.has_contact = _has_contact,
.create_enumerator = _create_enumerator,
+ .create_id_enumerator = _create_id_enumerator,
.checkin = _checkin,
.checkin_and_destroy = _checkin_and_destroy,
.get_count = _get_count,
@@ -1782,17 +2045,19 @@ ike_sa_manager_t *ike_sa_manager_create()
return NULL;
}
- this->table_size = get_nearest_powerof2(lib->settings->get_int(lib->settings,
- "charon.ikesa_table_size", DEFAULT_HASHTABLE_SIZE));
+ this->table_size = get_nearest_powerof2(lib->settings->get_int(
+ lib->settings, "%s.ikesa_table_size",
+ DEFAULT_HASHTABLE_SIZE, charon->name));
this->table_size = max(1, min(this->table_size, MAX_HASHTABLE_SIZE));
this->table_mask = this->table_size - 1;
- this->segment_count = get_nearest_powerof2(lib->settings->get_int(lib->settings,
- "charon.ikesa_table_segments", DEFAULT_SEGMENT_COUNT));
+ this->segment_count = get_nearest_powerof2(lib->settings->get_int(
+ lib->settings, "%s.ikesa_table_segments",
+ DEFAULT_SEGMENT_COUNT, charon->name));
this->segment_count = max(1, min(this->segment_count, this->table_size));
this->segment_mask = this->segment_count - 1;
- this->ike_sa_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->ike_sa_table = calloc(this->table_size, sizeof(table_item_t*));
this->segments = (segment_t*)calloc(this->segment_count, sizeof(segment_t));
for (i = 0; i < this->segment_count; i++)
{
@@ -1801,7 +2066,7 @@ ike_sa_manager_t *ike_sa_manager_create()
}
/* we use the same table parameters for the table to track half-open SAs */
- this->half_open_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->half_open_table = calloc(this->table_size, sizeof(table_item_t*));
this->half_open_segments = calloc(this->segment_count, sizeof(shareable_segment_t));
for (i = 0; i < this->segment_count; i++)
{
@@ -1810,7 +2075,7 @@ ike_sa_manager_t *ike_sa_manager_create()
}
/* also for the hash table used for duplicate tests */
- this->connected_peers_table = calloc(this->table_size, sizeof(linked_list_t*));
+ this->connected_peers_table = calloc(this->table_size, sizeof(table_item_t*));
this->connected_peers_segments = calloc(this->segment_count, sizeof(shareable_segment_t));
for (i = 0; i < this->segment_count; i++)
{
@@ -1818,7 +2083,16 @@ ike_sa_manager_t *ike_sa_manager_create()
this->connected_peers_segments[i].count = 0;
}
+ /* and again for the table of hashes of seen initial IKE messages */
+ this->init_hashes_table = calloc(this->table_size, sizeof(table_item_t*));
+ this->init_hashes_segments = calloc(this->segment_count, sizeof(segment_t));
+ for (i = 0; i < this->segment_count; i++)
+ {
+ this->init_hashes_segments[i].mutex = mutex_create(MUTEX_TYPE_RECURSIVE);
+ this->init_hashes_segments[i].count = 0;
+ }
+
this->reuse_ikesa = lib->settings->get_bool(lib->settings,
- "charon.reuse_ikesa", TRUE);
+ "%s.reuse_ikesa", TRUE, charon->name);
return &this->public;
}
diff --git a/src/libcharon/sa/ike_sa_manager.h b/src/libcharon/sa/ike_sa_manager.h
index 5e542e7df..a68ae7763 100644
--- a/src/libcharon/sa/ike_sa_manager.h
+++ b/src/libcharon/sa/ike_sa_manager.h
@@ -52,10 +52,12 @@ struct ike_sa_manager_t {
/**
* Create and check out a new IKE_SA.
*
+ * @param version IKE version of this SA
* @param initiator TRUE for initiator, FALSE otherwise
* @returns created and checked out IKE_SA
*/
- ike_sa_t* (*checkout_new) (ike_sa_manager_t* this, bool initiator);
+ ike_sa_t* (*checkout_new) (ike_sa_manager_t* this, ike_version_t version,
+ bool initiator);
/**
* Checkout an IKE_SA by a message.
@@ -168,6 +170,20 @@ struct ike_sa_manager_t {
enumerator_t *(*create_enumerator) (ike_sa_manager_t* this, bool wait);
/**
+ * Create an enumerator over ike_sa_id_t*, matching peer identities.
+ *
+ * The remote peer is identified by its XAuth or EAP identity, if available.
+ *
+ * @param me local peer identity to match
+ * @param other remote peer identity to match
+ * @param family address family to match, 0 for any
+ * @return enumerator over ike_sa_id_t*
+ */
+ enumerator_t* (*create_id_enumerator)(ike_sa_manager_t *this,
+ identification_t *me, identification_t *other,
+ int family);
+
+ /**
* Checkin the SA after usage.
*
* If the IKE_SA is not registered in the manager, a new entry is created.
diff --git a/src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.c b/src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.c
new file mode 100644
index 000000000..689f5f376
--- /dev/null
+++ b/src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "hybrid_authenticator.h"
+
+#include <daemon.h>
+#include <sa/ikev1/authenticators/psk_v1_authenticator.h>
+
+typedef struct private_hybrid_authenticator_t private_hybrid_authenticator_t;
+
+/**
+ * Private data of an hybrid_authenticator_t object.
+ */
+struct private_hybrid_authenticator_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ hybrid_authenticator_t public;
+
+ /**
+ * Public key authenticator
+ */
+ authenticator_t *sig;
+
+ /**
+ * HASH payload authenticator without credentials
+ */
+ authenticator_t *hash;
+};
+
+METHOD(authenticator_t, build_i, status_t,
+ private_hybrid_authenticator_t *this, message_t *message)
+{
+ return this->hash->build(this->hash, message);
+}
+
+METHOD(authenticator_t, process_r, status_t,
+ private_hybrid_authenticator_t *this, message_t *message)
+{
+ return this->hash->process(this->hash, message);
+}
+
+METHOD(authenticator_t, build_r, status_t,
+ private_hybrid_authenticator_t *this, message_t *message)
+{
+ return this->sig->build(this->sig, message);
+}
+
+METHOD(authenticator_t, process_i, status_t,
+ private_hybrid_authenticator_t *this, message_t *message)
+{
+ return this->sig->process(this->sig, message);
+}
+
+METHOD(authenticator_t, destroy, void,
+ private_hybrid_authenticator_t *this)
+{
+ DESTROY_IF(this->hash);
+ DESTROY_IF(this->sig);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+hybrid_authenticator_t *hybrid_authenticator_create(ike_sa_t *ike_sa,
+ bool initiator, diffie_hellman_t *dh,
+ chunk_t dh_value, chunk_t sa_payload,
+ chunk_t id_payload)
+{
+ private_hybrid_authenticator_t *this;
+
+ INIT(this,
+ .public = {
+ .authenticator = {
+ .is_mutual = (void*)return_false,
+ .destroy = _destroy,
+ },
+ },
+ .hash = (authenticator_t*)psk_v1_authenticator_create(ike_sa, initiator,
+ dh, dh_value, sa_payload, id_payload, TRUE),
+ .sig = authenticator_create_v1(ike_sa, initiator, AUTH_RSA, dh,
+ dh_value, sa_payload, chunk_clone(id_payload)),
+ );
+ if (!this->sig || !this->hash)
+ {
+ destroy(this);
+ return NULL;
+ }
+ if (initiator)
+ {
+ this->public.authenticator.build = _build_i;
+ this->public.authenticator.process = _process_i;
+ }
+ else
+ {
+ this->public.authenticator.build = _build_r;
+ this->public.authenticator.process = _process_r;
+ }
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.h b/src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.h
new file mode 100644
index 000000000..69e596959
--- /dev/null
+++ b/src/libcharon/sa/ikev1/authenticators/hybrid_authenticator.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 hybrid_authenticator hybrid_authenticator
+ * @{ @ingroup authenticators_v1
+ */
+
+#ifndef HYBRID_AUTHENTICATOR_H_
+#define HYBRID_AUTHENTICATOR_H_
+
+typedef struct hybrid_authenticator_t hybrid_authenticator_t;
+
+#include <sa/authenticator.h>
+
+/**
+ * Implementation of authenticator_t using IKEv1 hybrid authentication.
+ */
+struct hybrid_authenticator_t {
+
+ /**
+ * Implemented authenticator_t interface.
+ */
+ authenticator_t authenticator;
+};
+
+/**
+ * Create an authenticator to build hybrid signatures.
+ *
+ * @param ike_sa associated IKE_SA
+ * @param initiator TRUE if we are the IKE_SA initiator
+ * @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 hybrid authenticator
+ */
+hybrid_authenticator_t *hybrid_authenticator_create(ike_sa_t *ike_sa,
+ bool initiator, diffie_hellman_t *dh,
+ chunk_t dh_value, chunk_t sa_payload,
+ chunk_t id_payload);
+
+#endif /** HYBRID_AUTHENTICATOR_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c
new file mode 100644
index 000000000..ee15408c7
--- /dev/null
+++ b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "psk_v1_authenticator.h"
+
+#include <daemon.h>
+#include <sa/ikev1/keymat_v1.h>
+#include <encoding/payloads/hash_payload.h>
+
+typedef struct private_psk_v1_authenticator_t private_psk_v1_authenticator_t;
+
+/**
+ * Private data of an psk_v1_authenticator_t object.
+ */
+struct private_psk_v1_authenticator_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ psk_v1_authenticator_t public;
+
+ /**
+ * Assigned IKE_SA
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * TRUE if we are initiator
+ */
+ bool initiator;
+
+ /**
+ * DH key exchange
+ */
+ diffie_hellman_t *dh;
+
+ /**
+ * Others DH public value
+ */
+ chunk_t dh_value;
+
+ /**
+ * Encoded SA payload, without fixed header
+ */
+ chunk_t sa_payload;
+
+ /**
+ * Encoded ID payload, without fixed header
+ */
+ chunk_t id_payload;
+
+ /**
+ * Used for Hybrid authentication to build hash without PSK?
+ */
+ bool hybrid;
+};
+
+METHOD(authenticator_t, build, status_t,
+ private_psk_v1_authenticator_t *this, message_t *message)
+{
+ hash_payload_t *hash_payload;
+ keymat_v1_t *keymat;
+ chunk_t hash, dh;
+
+ this->dh->get_my_public_value(this->dh, &dh);
+ keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
+ if (!keymat->get_hash(keymat, this->initiator, dh, this->dh_value,
+ this->ike_sa->get_id(this->ike_sa), this->sa_payload,
+ this->id_payload, &hash))
+ {
+ free(dh.ptr);
+ return FAILED;
+ }
+ free(dh.ptr);
+
+ hash_payload = hash_payload_create(HASH_V1);
+ hash_payload->set_hash(hash_payload, hash);
+ message->add_payload(message, &hash_payload->payload_interface);
+ free(hash.ptr);
+
+ return SUCCESS;
+}
+
+METHOD(authenticator_t, process, status_t,
+ private_psk_v1_authenticator_t *this, message_t *message)
+{
+ hash_payload_t *hash_payload;
+ keymat_v1_t *keymat;
+ chunk_t hash, dh;
+ auth_cfg_t *auth;
+
+ hash_payload = (hash_payload_t*)message->get_payload(message, HASH_V1);
+ if (!hash_payload)
+ {
+ DBG1(DBG_IKE, "HASH payload missing in message");
+ return FAILED;
+ }
+
+ this->dh->get_my_public_value(this->dh, &dh);
+ keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
+ if (!keymat->get_hash(keymat, !this->initiator, this->dh_value, dh,
+ this->ike_sa->get_id(this->ike_sa), this->sa_payload,
+ this->id_payload, &hash))
+ {
+ free(dh.ptr);
+ return FAILED;
+ }
+ free(dh.ptr);
+ if (chunk_equals(hash, hash_payload->get_hash(hash_payload)))
+ {
+ free(hash.ptr);
+ if (!this->hybrid)
+ {
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+ auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
+ }
+ return SUCCESS;
+ }
+ free(hash.ptr);
+ DBG1(DBG_IKE, "calculated HASH does not match HASH payload");
+ return FAILED;
+}
+
+METHOD(authenticator_t, destroy, void,
+ private_psk_v1_authenticator_t *this)
+{
+ chunk_free(&this->id_payload);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+psk_v1_authenticator_t *psk_v1_authenticator_create(ike_sa_t *ike_sa,
+ bool initiator, diffie_hellman_t *dh,
+ chunk_t dh_value, chunk_t sa_payload,
+ chunk_t id_payload, bool hybrid)
+{
+ private_psk_v1_authenticator_t *this;
+
+ INIT(this,
+ .public = {
+ .authenticator = {
+ .build = _build,
+ .process = _process,
+ .is_mutual = (void*)return_false,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .initiator = initiator,
+ .dh = dh,
+ .dh_value = dh_value,
+ .sa_payload = sa_payload,
+ .id_payload = id_payload,
+ .hybrid = hybrid,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.h b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.h
new file mode 100644
index 000000000..cc9e18ba1
--- /dev/null
+++ b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 psk_v1_authenticator psk_v1_authenticator
+ * @{ @ingroup authenticators_v1
+ */
+
+#ifndef PSK_V1_AUTHENTICATOR_H_
+#define PSK_V1_AUTHENTICATOR_H_
+
+typedef struct psk_v1_authenticator_t psk_v1_authenticator_t;
+
+#include <sa/authenticator.h>
+
+/**
+ * Implementation of authenticator_t using pre-shared keys for IKEv1.
+ */
+struct psk_v1_authenticator_t {
+
+ /**
+ * Implemented authenticator_t interface.
+ */
+ authenticator_t authenticator;
+};
+
+/**
+ * Create an authenticator to build PSK signatures.
+ *
+ * @param ike_sa associated IKE_SA
+ * @param initiator TRUE if we are the IKE_SA initiator
+ * @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)
+ * @param hybrid TRUE if used for hybrid authentication without PSK
+ * @return PSK authenticator
+ */
+psk_v1_authenticator_t *psk_v1_authenticator_create(ike_sa_t *ike_sa,
+ bool initiator, diffie_hellman_t *dh,
+ chunk_t dh_value, chunk_t sa_payload,
+ chunk_t id_payload, bool hybrid);
+
+#endif /** PSK_V1_AUTHENTICATOR_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
new file mode 100644
index 000000000..d81c77f0d
--- /dev/null
+++ b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "pubkey_v1_authenticator.h"
+
+#include <daemon.h>
+#include <sa/ikev1/keymat_v1.h>
+#include <encoding/payloads/hash_payload.h>
+
+typedef struct private_pubkey_v1_authenticator_t private_pubkey_v1_authenticator_t;
+
+/**
+ * Private data of an pubkey_v1_authenticator_t object.
+ */
+struct private_pubkey_v1_authenticator_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ pubkey_v1_authenticator_t public;
+
+ /**
+ * Assigned IKE_SA
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * TRUE if we are initiator
+ */
+ bool initiator;
+
+ /**
+ * DH key exchange
+ */
+ diffie_hellman_t *dh;
+
+ /**
+ * Others DH public value
+ */
+ chunk_t dh_value;
+
+ /**
+ * Encoded SA payload, without fixed header
+ */
+ chunk_t sa_payload;
+
+ /**
+ * Encoded ID payload, without fixed header
+ */
+ chunk_t id_payload;
+
+ /**
+ * Key type to use
+ */
+ key_type_t type;
+};
+
+METHOD(authenticator_t, build, status_t,
+ private_pubkey_v1_authenticator_t *this, message_t *message)
+{
+ hash_payload_t *sig_payload;
+ chunk_t hash, sig, dh;
+ keymat_v1_t *keymat;
+ status_t status;
+ private_key_t *private;
+ identification_t *id;
+ auth_cfg_t *auth;
+ signature_scheme_t scheme = SIGN_RSA_EMSA_PKCS1_NULL;
+
+ if (this->type == KEY_ECDSA)
+ {
+ scheme = SIGN_ECDSA_WITH_NULL;
+ }
+
+ id = this->ike_sa->get_my_id(this->ike_sa);
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+ private = lib->credmgr->get_private(lib->credmgr, this->type, id, auth);
+ if (!private)
+ {
+ DBG1(DBG_IKE, "no %N private key found for '%Y'",
+ key_type_names, this->type, id);
+ return NOT_FOUND;
+ }
+
+ this->dh->get_my_public_value(this->dh, &dh);
+ keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
+ if (!keymat->get_hash(keymat, this->initiator, dh, this->dh_value,
+ this->ike_sa->get_id(this->ike_sa), this->sa_payload,
+ this->id_payload, &hash))
+ {
+ private->destroy(private);
+ free(dh.ptr);
+ return FAILED;
+ }
+ free(dh.ptr);
+
+ if (private->sign(private, scheme, hash, &sig))
+ {
+ sig_payload = hash_payload_create(SIGNATURE_V1);
+ sig_payload->set_hash(sig_payload, sig);
+ free(sig.ptr);
+ message->add_payload(message, &sig_payload->payload_interface);
+ status = SUCCESS;
+ DBG1(DBG_IKE, "authentication of '%Y' (myself) successful", id);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "authentication of '%Y' (myself) failed", id);
+ status = FAILED;
+ }
+ private->destroy(private);
+ free(hash.ptr);
+
+ return status;
+}
+
+METHOD(authenticator_t, process, status_t,
+ private_pubkey_v1_authenticator_t *this, message_t *message)
+{
+ chunk_t hash, sig, dh;
+ keymat_v1_t *keymat;
+ public_key_t *public;
+ hash_payload_t *sig_payload;
+ auth_cfg_t *auth, *current_auth;
+ enumerator_t *enumerator;
+ status_t status = NOT_FOUND;
+ identification_t *id;
+ signature_scheme_t scheme = SIGN_RSA_EMSA_PKCS1_NULL;
+
+ if (this->type == KEY_ECDSA)
+ {
+ scheme = SIGN_ECDSA_WITH_NULL;
+ }
+
+ sig_payload = (hash_payload_t*)message->get_payload(message, SIGNATURE_V1);
+ if (!sig_payload)
+ {
+ DBG1(DBG_IKE, "SIG payload missing in message");
+ return FAILED;
+ }
+
+ id = this->ike_sa->get_other_id(this->ike_sa);
+ this->dh->get_my_public_value(this->dh, &dh);
+ keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
+ if (!keymat->get_hash(keymat, !this->initiator, this->dh_value, dh,
+ this->ike_sa->get_id(this->ike_sa), this->sa_payload,
+ this->id_payload, &hash))
+ {
+ free(dh.ptr);
+ return FAILED;
+ }
+ free(dh.ptr);
+
+ sig = sig_payload->get_hash(sig_payload);
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+ enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, this->type,
+ id, auth);
+ while (enumerator->enumerate(enumerator, &public, &current_auth))
+ {
+ if (public->verify(public, scheme, hash, sig))
+ {
+ DBG1(DBG_IKE, "authentication of '%Y' with %N successful",
+ id, key_type_names, this->type);
+ status = SUCCESS;
+ auth->merge(auth, current_auth, FALSE);
+ auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
+ break;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "signature validation failed, looking for another key");
+ status = FAILED;
+ }
+ }
+ enumerator->destroy(enumerator);
+ free(hash.ptr);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "no trusted %N public key found for '%Y'",
+ key_type_names, this->type, id);
+ }
+ return status;
+}
+
+METHOD(authenticator_t, destroy, void,
+ private_pubkey_v1_authenticator_t *this)
+{
+ chunk_free(&this->id_payload);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+pubkey_v1_authenticator_t *pubkey_v1_authenticator_create(ike_sa_t *ike_sa,
+ bool initiator, diffie_hellman_t *dh,
+ chunk_t dh_value, chunk_t sa_payload,
+ chunk_t id_payload, key_type_t type)
+{
+ private_pubkey_v1_authenticator_t *this;
+
+ INIT(this,
+ .public = {
+ .authenticator = {
+ .build = _build,
+ .process = _process,
+ .is_mutual = (void*)return_false,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .initiator = initiator,
+ .dh = dh,
+ .dh_value = dh_value,
+ .sa_payload = sa_payload,
+ .id_payload = id_payload,
+ .type = type,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.h b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.h
new file mode 100644
index 000000000..385664cf3
--- /dev/null
+++ b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 pubkey_v1_authenticator pubkey_v1_authenticator
+ * @{ @ingroup authenticators_v1
+ */
+
+#ifndef PUBKEY_V1_AUTHENTICATOR_H_
+#define PUBKEY_V1_AUTHENTICATOR_H_
+
+typedef struct pubkey_v1_authenticator_t pubkey_v1_authenticator_t;
+
+#include <sa/authenticator.h>
+
+/**
+ * Implementation of authenticator_t using public keys for IKEv1.
+ */
+struct pubkey_v1_authenticator_t {
+
+ /**
+ * Implemented authenticator_t interface.
+ */
+ authenticator_t authenticator;
+};
+
+/**
+ * Create an authenticator to build and verify public key signatures.
+ *
+ * @param ike_sa associated IKE_SA
+ * @param initiator TRUE if we are IKE_SA initiator
+ * @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)
+ * @param type key type to use, KEY_RSA or KEY_ECDSA
+ * @return pubkey authenticator
+ */
+pubkey_v1_authenticator_t *pubkey_v1_authenticator_create(ike_sa_t *ike_sa,
+ bool initiator, diffie_hellman_t *dh,
+ chunk_t dh_value, chunk_t sa_payload,
+ chunk_t id_payload, key_type_t type);
+
+#endif /** PUBKEY_V1_AUTHENTICATOR_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c
new file mode 100644
index 000000000..cff344a34
--- /dev/null
+++ b/src/libcharon/sa/ikev1/keymat_v1.c
@@ -0,0 +1,1157 @@
+/*
+ * Copyright (C) 2011 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 "keymat_v1.h"
+
+#include <daemon.h>
+#include <encoding/generator.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <utils/linked_list.h>
+
+typedef struct private_keymat_v1_t private_keymat_v1_t;
+
+/**
+ * Max. number of IVs to track.
+ */
+#define MAX_IV 3
+
+/**
+ * Max. number of Quick Modes to track.
+ */
+#define MAX_QM 2
+
+/**
+ * Data stored for IVs
+ */
+typedef struct {
+ /** message ID */
+ u_int32_t mid;
+ /** current IV */
+ chunk_t iv;
+ /** last block of encrypted message */
+ chunk_t last_block;
+} iv_data_t;
+
+/**
+ * Private data of an keymat_t object.
+ */
+struct private_keymat_v1_t {
+
+ /**
+ * Public keymat_v1_t interface.
+ */
+ keymat_v1_t public;
+
+ /**
+ * IKE_SA Role, initiator or responder
+ */
+ bool initiator;
+
+ /**
+ * General purpose PRF
+ */
+ prf_t *prf;
+
+ /**
+ * PRF to create Phase 1 HASH payloads
+ */
+ prf_t *prf_auth;
+
+ /**
+ * Crypter wrapped in an aead_t interface
+ */
+ aead_t *aead;
+
+ /**
+ * Hasher used for IV generation (and other things like e.g. NAT-T)
+ */
+ hasher_t *hasher;
+
+ /**
+ * Key used for authentication during main mode
+ */
+ chunk_t skeyid;
+
+ /**
+ * Key to derive key material from for non-ISAKMP SAs, rekeying
+ */
+ chunk_t skeyid_d;
+
+ /**
+ * Key used for authentication after main mode
+ */
+ chunk_t skeyid_a;
+
+ /**
+ * Phase 1 IV
+ */
+ iv_data_t phase1_iv;
+
+ /**
+ * Keep track of IVs for exchanges after phase 1. We store only a limited
+ * number of IVs in an MRU sort of way. Stores iv_data_t objects.
+ */
+ linked_list_t *ivs;
+
+ /**
+ * Keep track of Nonces during Quick Mode exchanges. Only a limited number
+ * of QMs are tracked at the same time. Stores qm_data_t objects.
+ */
+ linked_list_t *qms;
+};
+
+
+/**
+ * Destroy an iv_data_t object.
+ */
+static void iv_data_destroy(iv_data_t *this)
+{
+ chunk_free(&this->last_block);
+ chunk_free(&this->iv);
+ free(this);
+}
+
+/**
+ * Data stored for Quick Mode exchanges
+ */
+typedef struct {
+ /** message ID */
+ u_int32_t mid;
+ /** Ni_b (Nonce from first message) */
+ chunk_t n_i;
+ /** Nr_b (Nonce from second message) */
+ chunk_t n_r;
+} qm_data_t;
+
+/**
+ * Destroy a qm_data_t object.
+ */
+static void qm_data_destroy(qm_data_t *this)
+{
+ chunk_free(&this->n_i);
+ chunk_free(&this->n_r);
+ free(this);
+}
+
+/**
+ * Constants used in key derivation.
+ */
+static const chunk_t octet_0 = chunk_from_chars(0x00);
+static const chunk_t octet_1 = chunk_from_chars(0x01);
+static const chunk_t octet_2 = chunk_from_chars(0x02);
+
+/**
+ * Simple aead_t implementation without support for authentication.
+ */
+typedef struct {
+ /** implements aead_t interface */
+ aead_t aead;
+ /** crypter to be used */
+ crypter_t *crypter;
+} private_aead_t;
+
+
+METHOD(aead_t, encrypt, bool,
+ private_aead_t *this, chunk_t plain, chunk_t assoc, chunk_t iv,
+ chunk_t *encrypted)
+{
+ return this->crypter->encrypt(this->crypter, plain, iv, encrypted);
+}
+
+METHOD(aead_t, decrypt, bool,
+ private_aead_t *this, chunk_t encrypted, chunk_t assoc, chunk_t iv,
+ chunk_t *plain)
+{
+ return this->crypter->decrypt(this->crypter, encrypted, iv, plain);
+}
+
+METHOD(aead_t, get_block_size, size_t,
+ private_aead_t *this)
+{
+ return this->crypter->get_block_size(this->crypter);
+}
+
+METHOD(aead_t, get_icv_size, size_t,
+ private_aead_t *this)
+{
+ return 0;
+}
+
+METHOD(aead_t, get_iv_size, size_t,
+ private_aead_t *this)
+{
+ /* in order to create the messages properly we return 0 here */
+ return 0;
+}
+
+METHOD(aead_t, get_key_size, size_t,
+ private_aead_t *this)
+{
+ return this->crypter->get_key_size(this->crypter);
+}
+
+METHOD(aead_t, set_key, bool,
+ private_aead_t *this, chunk_t key)
+{
+ return this->crypter->set_key(this->crypter, key);
+}
+
+METHOD(aead_t, aead_destroy, void,
+ private_aead_t *this)
+{
+ this->crypter->destroy(this->crypter);
+ free(this);
+}
+
+/**
+ * Expand SKEYID_e according to Appendix B in RFC 2409.
+ * TODO-IKEv1: verify keys (e.g. for weak keys, see Appendix B)
+ */
+static bool expand_skeyid_e(chunk_t skeyid_e, size_t key_size, prf_t *prf,
+ chunk_t *ka)
+{
+ size_t block_size;
+ chunk_t seed;
+ int i;
+
+ if (skeyid_e.len >= key_size)
+ { /* no expansion required, reduce to key_size */
+ skeyid_e.len = key_size;
+ *ka = skeyid_e;
+ return TRUE;
+ }
+ block_size = prf->get_block_size(prf);
+ *ka = chunk_alloc((key_size / block_size + 1) * block_size);
+ ka->len = key_size;
+
+ /* Ka = K1 | K2 | ..., K1 = prf(SKEYID_e, 0), K2 = prf(SKEYID_e, K1) ... */
+ if (!prf->set_key(prf, skeyid_e))
+ {
+ chunk_clear(ka);
+ chunk_clear(&skeyid_e);
+ return FALSE;
+ }
+ seed = octet_0;
+ for (i = 0; i < key_size; i += block_size)
+ {
+ if (!prf->get_bytes(prf, seed, ka->ptr + i))
+ {
+ chunk_clear(ka);
+ chunk_clear(&skeyid_e);
+ return FALSE;
+ }
+ seed = chunk_create(ka->ptr + i, block_size);
+ }
+ chunk_clear(&skeyid_e);
+ return TRUE;
+}
+
+/**
+ * Create a simple implementation of the aead_t interface which only encrypts
+ * or decrypts data.
+ */
+static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e)
+{
+ private_aead_t *this;
+ u_int16_t alg, key_size;
+ crypter_t *crypter;
+ chunk_t ka;
+
+ if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg,
+ &key_size))
+ {
+ DBG1(DBG_IKE, "no %N selected",
+ transform_type_names, ENCRYPTION_ALGORITHM);
+ return NULL;
+ }
+ crypter = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8);
+ if (!crypter)
+ {
+ DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
+ transform_type_names, ENCRYPTION_ALGORITHM,
+ encryption_algorithm_names, alg, key_size);
+ return NULL;
+ }
+ key_size = crypter->get_key_size(crypter);
+ if (!expand_skeyid_e(skeyid_e, crypter->get_key_size(crypter), prf, &ka))
+ {
+ return NULL;
+ }
+ DBG4(DBG_IKE, "encryption key Ka %B", &ka);
+ if (!crypter->set_key(crypter, ka))
+ {
+ chunk_clear(&ka);
+ return NULL;
+ }
+ chunk_clear(&ka);
+
+ INIT(this,
+ .aead = {
+ .encrypt = _encrypt,
+ .decrypt = _decrypt,
+ .get_block_size = _get_block_size,
+ .get_icv_size = _get_icv_size,
+ .get_iv_size = _get_iv_size,
+ .get_key_size = _get_key_size,
+ .set_key = _set_key,
+ .destroy = _aead_destroy,
+ },
+ .crypter = crypter,
+ );
+ return &this->aead;
+}
+
+/**
+ * Converts integrity algorithm to PRF algorithm
+ */
+static u_int16_t auth_to_prf(u_int16_t alg)
+{
+ switch (alg)
+ {
+ case AUTH_HMAC_SHA1_96:
+ return PRF_HMAC_SHA1;
+ case AUTH_HMAC_SHA2_256_128:
+ return PRF_HMAC_SHA2_256;
+ case AUTH_HMAC_SHA2_384_192:
+ return PRF_HMAC_SHA2_384;
+ case AUTH_HMAC_SHA2_512_256:
+ return PRF_HMAC_SHA2_512;
+ case AUTH_HMAC_MD5_96:
+ return PRF_HMAC_MD5;
+ case AUTH_AES_XCBC_96:
+ return PRF_AES128_XCBC;
+ default:
+ return PRF_UNDEFINED;
+ }
+}
+
+/**
+ * Converts integrity algorithm to hash algorithm
+ */
+static u_int16_t auth_to_hash(u_int16_t alg)
+{
+ switch (alg)
+ {
+ case AUTH_HMAC_SHA1_96:
+ return HASH_SHA1;
+ case AUTH_HMAC_SHA2_256_128:
+ return HASH_SHA256;
+ case AUTH_HMAC_SHA2_384_192:
+ return HASH_SHA384;
+ case AUTH_HMAC_SHA2_512_256:
+ return HASH_SHA512;
+ case AUTH_HMAC_MD5_96:
+ return HASH_MD5;
+ default:
+ return HASH_UNKNOWN;
+ }
+}
+
+/**
+ * Adjust the key length for PRF algorithms that expect a fixed key length.
+ */
+static void adjust_keylen(u_int16_t alg, chunk_t *key)
+{
+ switch (alg)
+ {
+ case PRF_AES128_XCBC:
+ /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does
+ * not and therefore fixed key semantics apply to XCBC for key
+ * derivation. */
+ key->len = min(key->len, 16);
+ break;
+ default:
+ /* all other algorithms use variable key length */
+ break;
+ }
+}
+
+METHOD(keymat_v1_t, derive_ike_keys, bool,
+ private_keymat_v1_t *this, proposal_t *proposal, diffie_hellman_t *dh,
+ chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
+ auth_method_t auth, shared_key_t *shared_key)
+{
+ chunk_t g_xy, g_xi, g_xr, dh_me, spi_i, spi_r, nonces, data, skeyid_e;
+ chunk_t skeyid;
+ u_int16_t alg;
+
+ spi_i = chunk_alloca(sizeof(u_int64_t));
+ spi_r = chunk_alloca(sizeof(u_int64_t));
+
+ if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
+ { /* no PRF negotiated, use HMAC version of integrity algorithm instead */
+ if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)
+ || (alg = auth_to_prf(alg)) == PRF_UNDEFINED)
+ {
+ DBG1(DBG_IKE, "no %N selected",
+ transform_type_names, PSEUDO_RANDOM_FUNCTION);
+ return FALSE;
+ }
+ }
+ this->prf = lib->crypto->create_prf(lib->crypto, alg);
+ if (!this->prf)
+ {
+ DBG1(DBG_IKE, "%N %N not supported!",
+ transform_type_names, PSEUDO_RANDOM_FUNCTION,
+ pseudo_random_function_names, alg);
+ return FALSE;
+ }
+ if (this->prf->get_block_size(this->prf) <
+ this->prf->get_key_size(this->prf))
+ { /* TODO-IKEv1: support PRF output expansion (RFC 2409, Appendix B) */
+ DBG1(DBG_IKE, "expansion of %N %N output not supported!",
+ transform_type_names, PSEUDO_RANDOM_FUNCTION,
+ pseudo_random_function_names, alg);
+ return FALSE;
+ }
+
+ if (dh->get_shared_secret(dh, &g_xy) != SUCCESS)
+ {
+ return FALSE;
+ }
+ DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &g_xy);
+
+ *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id);
+ *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id);
+ nonces = chunk_cata("cc", nonce_i, nonce_r);
+
+ switch (auth)
+ {
+ case AUTH_PSK:
+ case AUTH_XAUTH_INIT_PSK:
+ { /* SKEYID = prf(pre-shared-key, Ni_b | Nr_b) */
+ chunk_t psk;
+ if (!shared_key)
+ {
+ chunk_clear(&g_xy);
+ return FALSE;
+ }
+ psk = shared_key->get_key(shared_key);
+ adjust_keylen(alg, &psk);
+ if (!this->prf->set_key(this->prf, psk) ||
+ !this->prf->allocate_bytes(this->prf, nonces, &skeyid))
+ {
+ chunk_clear(&g_xy);
+ return FALSE;
+ }
+ break;
+ }
+ case AUTH_RSA:
+ case AUTH_ECDSA_256:
+ case AUTH_ECDSA_384:
+ case AUTH_ECDSA_521:
+ case AUTH_XAUTH_INIT_RSA:
+ case AUTH_XAUTH_RESP_RSA:
+ case AUTH_HYBRID_INIT_RSA:
+ case AUTH_HYBRID_RESP_RSA:
+ {
+ if (!this->prf->set_key(this->prf, nonces) ||
+ !this->prf->allocate_bytes(this->prf, g_xy, &skeyid))
+ {
+ chunk_clear(&g_xy);
+ return FALSE;
+ }
+ break;
+ }
+ default:
+ /* TODO-IKEv1: implement key derivation for other schemes */
+ /* authentication class not supported */
+ chunk_clear(&g_xy);
+ return FALSE;
+ }
+ adjust_keylen(alg, &skeyid);
+ DBG4(DBG_IKE, "SKEYID %B", &skeyid);
+
+ /* SKEYID_d = prf(SKEYID, g^xy | CKY-I | CKY-R | 0) */
+ data = chunk_cat("cccc", g_xy, spi_i, spi_r, octet_0);
+ if (!this->prf->set_key(this->prf, skeyid) ||
+ !this->prf->allocate_bytes(this->prf, data, &this->skeyid_d))
+ {
+ chunk_clear(&g_xy);
+ chunk_clear(&data);
+ return FALSE;
+ }
+ chunk_clear(&data);
+ DBG4(DBG_IKE, "SKEYID_d %B", &this->skeyid_d);
+
+ /* SKEYID_a = prf(SKEYID, SKEYID_d | g^xy | CKY-I | CKY-R | 1) */
+ data = chunk_cat("ccccc", this->skeyid_d, g_xy, spi_i, spi_r, octet_1);
+ if (!this->prf->allocate_bytes(this->prf, data, &this->skeyid_a))
+ {
+ chunk_clear(&g_xy);
+ chunk_clear(&data);
+ return FALSE;
+ }
+ chunk_clear(&data);
+ DBG4(DBG_IKE, "SKEYID_a %B", &this->skeyid_a);
+
+ /* SKEYID_e = prf(SKEYID, SKEYID_a | g^xy | CKY-I | CKY-R | 2) */
+ data = chunk_cat("ccccc", this->skeyid_a, g_xy, spi_i, spi_r, octet_2);
+ if (!this->prf->allocate_bytes(this->prf, data, &skeyid_e))
+ {
+ chunk_clear(&g_xy);
+ chunk_clear(&data);
+ return FALSE;
+ }
+ chunk_clear(&data);
+ DBG4(DBG_IKE, "SKEYID_e %B", &skeyid_e);
+
+ chunk_clear(&g_xy);
+
+ switch (auth)
+ {
+ case AUTH_ECDSA_256:
+ alg = PRF_HMAC_SHA2_256;
+ break;
+ case AUTH_ECDSA_384:
+ alg = PRF_HMAC_SHA2_384;
+ break;
+ case AUTH_ECDSA_521:
+ alg = PRF_HMAC_SHA2_512;
+ break;
+ default:
+ /* use proposal algorithm */
+ break;
+ }
+ this->prf_auth = lib->crypto->create_prf(lib->crypto, alg);
+ if (!this->prf_auth)
+ {
+ DBG1(DBG_IKE, "%N %N not supported!",
+ transform_type_names, PSEUDO_RANDOM_FUNCTION,
+ pseudo_random_function_names, alg);
+ chunk_clear(&skeyid);
+ return FALSE;
+ }
+ if (!this->prf_auth->set_key(this->prf_auth, skeyid))
+ {
+ chunk_clear(&skeyid);
+ return FALSE;
+ }
+ chunk_clear(&skeyid);
+
+ this->aead = create_aead(proposal, this->prf, skeyid_e);
+ if (!this->aead)
+ {
+ return FALSE;
+ }
+ if (!this->hasher && !this->public.create_hasher(&this->public, proposal))
+ {
+ return FALSE;
+ }
+
+ dh->get_my_public_value(dh, &dh_me);
+ g_xi = this->initiator ? dh_me : dh_other;
+ g_xr = this->initiator ? dh_other : dh_me;
+
+ /* initial IV = hash(g^xi | g^xr) */
+ data = chunk_cata("cc", g_xi, g_xr);
+ chunk_free(&dh_me);
+ if (!this->hasher->allocate_hash(this->hasher, data, &this->phase1_iv.iv))
+ {
+ return FALSE;
+ }
+ if (this->phase1_iv.iv.len > this->aead->get_block_size(this->aead))
+ {
+ this->phase1_iv.iv.len = this->aead->get_block_size(this->aead);
+ }
+ DBG4(DBG_IKE, "initial IV %B", &this->phase1_iv.iv);
+
+ return TRUE;
+}
+
+METHOD(keymat_v1_t, derive_child_keys, bool,
+ private_keymat_v1_t *this, proposal_t *proposal, diffie_hellman_t *dh,
+ u_int32_t spi_i, u_int32_t spi_r, chunk_t nonce_i, chunk_t nonce_r,
+ chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r)
+{
+ u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
+ u_int8_t protocol;
+ prf_plus_t *prf_plus;
+ chunk_t seed, secret = chunk_empty;
+ bool success = FALSE;
+
+ if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
+ &enc_alg, &enc_size))
+ {
+ DBG2(DBG_CHD, " using %N for encryption",
+ encryption_algorithm_names, enc_alg);
+
+ if (!enc_size)
+ {
+ enc_size = keymat_get_keylen_encr(enc_alg);
+ }
+ if (enc_alg != ENCR_NULL && !enc_size)
+ {
+ DBG1(DBG_CHD, "no keylength defined for %N",
+ encryption_algorithm_names, enc_alg);
+ return FALSE;
+ }
+ /* to bytes */
+ enc_size /= 8;
+
+ /* CCM/GCM/CTR/GMAC needs additional bytes */
+ switch (enc_alg)
+ {
+ case ENCR_AES_CCM_ICV8:
+ case ENCR_AES_CCM_ICV12:
+ case ENCR_AES_CCM_ICV16:
+ case ENCR_CAMELLIA_CCM_ICV8:
+ case ENCR_CAMELLIA_CCM_ICV12:
+ case ENCR_CAMELLIA_CCM_ICV16:
+ enc_size += 3;
+ break;
+ case ENCR_AES_GCM_ICV8:
+ case ENCR_AES_GCM_ICV12:
+ case ENCR_AES_GCM_ICV16:
+ case ENCR_AES_CTR:
+ case ENCR_NULL_AUTH_AES_GMAC:
+ enc_size += 4;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
+ &int_alg, &int_size))
+ {
+ DBG2(DBG_CHD, " using %N for integrity",
+ integrity_algorithm_names, int_alg);
+
+ if (!int_size)
+ {
+ int_size = keymat_get_keylen_integ(int_alg);
+ }
+ if (!int_size)
+ {
+ DBG1(DBG_CHD, "no keylength defined for %N",
+ integrity_algorithm_names, int_alg);
+ return FALSE;
+ }
+ /* to bytes */
+ int_size /= 8;
+ }
+
+ /* KEYMAT = prf+(SKEYID_d, [ g(qm)^xy | ] protocol | SPI | Ni_b | Nr_b) */
+ if (!this->prf->set_key(this->prf, this->skeyid_d))
+ {
+ return FALSE;
+ }
+ protocol = proposal->get_protocol(proposal);
+ if (dh)
+ {
+ if (dh->get_shared_secret(dh, &secret) != SUCCESS)
+ {
+ return FALSE;
+ }
+ DBG4(DBG_CHD, "DH secret %B", &secret);
+ }
+
+ *encr_r = *integ_r = *encr_i = *integ_i = chunk_empty;
+ seed = chunk_cata("ccccc", secret, chunk_from_thing(protocol),
+ chunk_from_thing(spi_r), nonce_i, nonce_r);
+ DBG4(DBG_CHD, "initiator SA seed %B", &seed);
+
+ prf_plus = prf_plus_create(this->prf, FALSE, seed);
+ if (!prf_plus ||
+ !prf_plus->allocate_bytes(prf_plus, enc_size, encr_i) ||
+ !prf_plus->allocate_bytes(prf_plus, int_size, integ_i))
+ {
+ goto failure;
+ }
+
+ seed = chunk_cata("ccccc", secret, chunk_from_thing(protocol),
+ chunk_from_thing(spi_i), nonce_i, nonce_r);
+ DBG4(DBG_CHD, "responder SA seed %B", &seed);
+ prf_plus->destroy(prf_plus);
+ prf_plus = prf_plus_create(this->prf, FALSE, seed);
+ if (!prf_plus ||
+ !prf_plus->allocate_bytes(prf_plus, enc_size, encr_r) ||
+ !prf_plus->allocate_bytes(prf_plus, int_size, integ_r))
+ {
+ goto failure;
+ }
+
+ if (enc_size)
+ {
+ DBG4(DBG_CHD, "encryption initiator key %B", encr_i);
+ DBG4(DBG_CHD, "encryption responder key %B", encr_r);
+ }
+ if (int_size)
+ {
+ DBG4(DBG_CHD, "integrity initiator key %B", integ_i);
+ DBG4(DBG_CHD, "integrity responder key %B", integ_r);
+ }
+ success = TRUE;
+
+failure:
+ if (!success)
+ {
+ chunk_clear(encr_i);
+ chunk_clear(integ_i);
+ chunk_clear(encr_r);
+ chunk_clear(integ_r);
+ }
+ DESTROY_IF(prf_plus);
+ chunk_clear(&secret);
+
+ return success;
+}
+
+METHOD(keymat_v1_t, create_hasher, bool,
+ private_keymat_v1_t *this, proposal_t *proposal)
+{
+ u_int16_t alg;
+ if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL) ||
+ (alg = auth_to_hash(alg)) == HASH_UNKNOWN)
+ {
+ DBG1(DBG_IKE, "no %N selected", transform_type_names, HASH_ALGORITHM);
+ return FALSE;
+ }
+ this->hasher = lib->crypto->create_hasher(lib->crypto, alg);
+ if (!this->hasher)
+ {
+ DBG1(DBG_IKE, "%N %N not supported!",
+ transform_type_names, HASH_ALGORITHM,
+ hash_algorithm_names, alg);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+METHOD(keymat_v1_t, get_hasher, hasher_t*,
+ private_keymat_v1_t *this)
+{
+ return this->hasher;
+}
+
+METHOD(keymat_v1_t, get_hash, bool,
+ private_keymat_v1_t *this, bool initiator, chunk_t dh, chunk_t dh_other,
+ ike_sa_id_t *ike_sa_id, chunk_t sa_i, chunk_t id, chunk_t *hash)
+{
+ chunk_t data;
+ u_int64_t spi, spi_other;
+
+ /* HASH_I = prf(SKEYID, g^xi | g^xr | CKY-I | CKY-R | SAi_b | IDii_b )
+ * HASH_R = prf(SKEYID, g^xr | g^xi | CKY-R | CKY-I | SAi_b | IDir_b )
+ */
+ if (initiator)
+ {
+ spi = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_other = ike_sa_id->get_responder_spi(ike_sa_id);
+ }
+ else
+ {
+ spi_other = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi = ike_sa_id->get_responder_spi(ike_sa_id);
+ }
+ data = chunk_cat("cccccc", dh, dh_other,
+ chunk_from_thing(spi), chunk_from_thing(spi_other),
+ sa_i, id);
+
+ DBG3(DBG_IKE, "HASH_%c data %B", initiator ? 'I' : 'R', &data);
+
+ if (!this->prf_auth->allocate_bytes(this->prf_auth, data, hash))
+ {
+ free(data.ptr);
+ return FALSE;
+ }
+
+ DBG3(DBG_IKE, "HASH_%c %B", initiator ? 'I' : 'R', hash);
+
+ free(data.ptr);
+ return TRUE;
+}
+
+/**
+ * Get the nonce value found in the given message.
+ * Returns FALSE if none is found.
+ */
+static bool get_nonce(message_t *message, chunk_t *n)
+{
+ nonce_payload_t *nonce;
+ nonce = (nonce_payload_t*)message->get_payload(message, NONCE_V1);
+ if (nonce)
+ {
+ *n = nonce->get_nonce(nonce);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Generate the message data in order to generate the hashes.
+ */
+static chunk_t get_message_data(message_t *message, generator_t *generator)
+{
+ payload_t *payload, *next;
+ enumerator_t *enumerator;
+ u_int32_t *lenpos;
+
+ if (message->is_encoded(message))
+ { /* inbound, although the message is generated, we cannot access the
+ * cleartext message data, so generate it anyway */
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == HASH_V1)
+ {
+ continue;
+ }
+ generator->generate_payload(generator, payload);
+ }
+ enumerator->destroy(enumerator);
+ }
+ else
+ {
+ /* outbound, generate the payloads (there is no HASH payload yet) */
+ enumerator = message->create_payload_enumerator(message);
+ if (enumerator->enumerate(enumerator, &payload))
+ {
+ while (enumerator->enumerate(enumerator, &next))
+ {
+ payload->set_next_type(payload, next->get_type(next));
+ generator->generate_payload(generator, payload);
+ payload = next;
+ }
+ payload->set_next_type(payload, NO_PAYLOAD);
+ generator->generate_payload(generator, payload);
+ }
+ enumerator->destroy(enumerator);
+ }
+ return generator->get_chunk(generator, &lenpos);
+}
+
+/**
+ * Try to find data about a Quick Mode with the given message ID,
+ * if none is found, state is generated.
+ */
+static qm_data_t *lookup_quick_mode(private_keymat_v1_t *this, u_int32_t mid)
+{
+ enumerator_t *enumerator;
+ qm_data_t *qm, *found = NULL;
+
+ enumerator = this->qms->create_enumerator(this->qms);
+ while (enumerator->enumerate(enumerator, &qm))
+ {
+ if (qm->mid == mid)
+ { /* state gets moved to the front of the list */
+ this->qms->remove_at(this->qms, enumerator);
+ found = qm;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (!found)
+ {
+ INIT(found,
+ .mid = mid,
+ );
+ }
+ this->qms->insert_first(this->qms, found);
+ /* remove least recently used state if maximum reached */
+ if (this->qms->get_count(this->qms) > MAX_QM &&
+ this->qms->remove_last(this->qms, (void**)&qm) == SUCCESS)
+ {
+ qm_data_destroy(qm);
+ }
+ return found;
+}
+
+METHOD(keymat_v1_t, get_hash_phase2, bool,
+ private_keymat_v1_t *this, message_t *message, chunk_t *hash)
+{
+ u_int32_t mid, mid_n;
+ chunk_t data = chunk_empty;
+ bool add_message = TRUE;
+ char *name = "Hash";
+
+ if (!this->prf)
+ { /* no keys derived yet */
+ return FALSE;
+ }
+
+ mid = message->get_message_id(message);
+ mid_n = htonl(mid);
+
+ /* Hashes are simple for most exchanges in Phase 2:
+ * Hash = prf(SKEYID_a, M-ID | Complete message after HASH payload)
+ * For Quick Mode there are three hashes:
+ * Hash(1) = same as above
+ * Hash(2) = prf(SKEYID_a, M-ID | Ni_b | Message after HASH payload)
+ * Hash(3) = prf(SKEYID_a, 0 | M-ID | Ni_b | Nr_b)
+ * So, for Quick Mode we keep track of the nonce values.
+ */
+ switch (message->get_exchange_type(message))
+ {
+ case QUICK_MODE:
+ {
+ qm_data_t *qm = lookup_quick_mode(this, mid);
+ if (!qm->n_i.ptr)
+ { /* Hash(1) = prf(SKEYID_a, M-ID | Message after HASH payload) */
+ name = "Hash(1)";
+ if (!get_nonce(message, &qm->n_i))
+ {
+ return FALSE;
+ }
+ data = chunk_from_thing(mid_n);
+ }
+ else if (!qm->n_r.ptr)
+ { /* Hash(2) = prf(SKEYID_a, M-ID | Ni_b | Message after HASH) */
+ name = "Hash(2)";
+ if (!get_nonce(message, &qm->n_r))
+ {
+ return FALSE;
+ }
+ data = chunk_cata("cc", chunk_from_thing(mid_n), qm->n_i);
+ }
+ else
+ { /* Hash(3) = prf(SKEYID_a, 0 | M-ID | Ni_b | Nr_b) */
+ name = "Hash(3)";
+ data = chunk_cata("cccc", octet_0, chunk_from_thing(mid_n),
+ qm->n_i, qm->n_r);
+ add_message = FALSE;
+ /* we don't need the state anymore */
+ this->qms->remove(this->qms, qm, NULL);
+ qm_data_destroy(qm);
+ }
+ break;
+ }
+ case TRANSACTION:
+ case INFORMATIONAL_V1:
+ /* Hash = prf(SKEYID_a, M-ID | Message after HASH payload) */
+ data = chunk_from_thing(mid_n);
+ break;
+ default:
+ return FALSE;
+ }
+ if (!this->prf->set_key(this->prf, this->skeyid_a))
+ {
+ return FALSE;
+ }
+ if (add_message)
+ {
+ generator_t *generator;
+ chunk_t msg;
+
+ generator = generator_create_no_dbg();
+ msg = get_message_data(message, generator);
+ if (!this->prf->allocate_bytes(this->prf, data, NULL) ||
+ !this->prf->allocate_bytes(this->prf, msg, hash))
+ {
+ generator->destroy(generator);
+ return FALSE;
+ }
+ generator->destroy(generator);
+ }
+ else
+ {
+ if (!this->prf->allocate_bytes(this->prf, data, hash))
+ {
+ return FALSE;
+ }
+ }
+ DBG3(DBG_IKE, "%s %B", name, hash);
+ return TRUE;
+}
+
+/**
+ * Generate an IV
+ */
+static bool generate_iv(private_keymat_v1_t *this, iv_data_t *iv)
+{
+ if (iv->mid == 0 || iv->iv.ptr)
+ { /* use last block of previous encrypted message */
+ chunk_free(&iv->iv);
+ iv->iv = iv->last_block;
+ iv->last_block = chunk_empty;
+ }
+ else
+ {
+ /* initial phase 2 IV = hash(last_phase1_block | mid) */
+ u_int32_t net;;
+ chunk_t data;
+
+ net = htonl(iv->mid);
+ data = chunk_cata("cc", this->phase1_iv.iv, chunk_from_thing(net));
+ if (!this->hasher->allocate_hash(this->hasher, data, &iv->iv))
+ {
+ return FALSE;
+ }
+ if (iv->iv.len > this->aead->get_block_size(this->aead))
+ {
+ iv->iv.len = this->aead->get_block_size(this->aead);
+ }
+ }
+ DBG4(DBG_IKE, "next IV for MID %u %B", iv->mid, &iv->iv);
+ return TRUE;
+}
+
+/**
+ * Try to find an IV for the given message ID, if not found, generate it.
+ */
+static iv_data_t *lookup_iv(private_keymat_v1_t *this, u_int32_t mid)
+{
+ enumerator_t *enumerator;
+ iv_data_t *iv, *found = NULL;
+
+ if (mid == 0)
+ {
+ return &this->phase1_iv;
+ }
+
+ enumerator = this->ivs->create_enumerator(this->ivs);
+ while (enumerator->enumerate(enumerator, &iv))
+ {
+ if (iv->mid == mid)
+ { /* IV gets moved to the front of the list */
+ this->ivs->remove_at(this->ivs, enumerator);
+ found = iv;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (!found)
+ {
+ INIT(found,
+ .mid = mid,
+ );
+ if (!generate_iv(this, found))
+ {
+ iv_data_destroy(found);
+ return NULL;
+ }
+ }
+ this->ivs->insert_first(this->ivs, found);
+ /* remove least recently used IV if maximum reached */
+ if (this->ivs->get_count(this->ivs) > MAX_IV &&
+ this->ivs->remove_last(this->ivs, (void**)&iv) == SUCCESS)
+ {
+ iv_data_destroy(iv);
+ }
+ return found;
+}
+
+METHOD(keymat_v1_t, get_iv, bool,
+ private_keymat_v1_t *this, u_int32_t mid, chunk_t *out)
+{
+ iv_data_t *iv;
+
+ iv = lookup_iv(this, mid);
+ if (iv)
+ {
+ *out = iv->iv;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+METHOD(keymat_v1_t, update_iv, bool,
+ private_keymat_v1_t *this, u_int32_t mid, chunk_t last_block)
+{
+ iv_data_t *iv = lookup_iv(this, mid);
+ if (iv)
+ { /* update last block */
+ chunk_free(&iv->last_block);
+ iv->last_block = chunk_clone(last_block);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+METHOD(keymat_v1_t, confirm_iv, bool,
+ private_keymat_v1_t *this, u_int32_t mid)
+{
+ iv_data_t *iv = lookup_iv(this, mid);
+ if (iv)
+ {
+ return generate_iv(this, iv);
+ }
+ return FALSE;
+}
+
+METHOD(keymat_t, get_version, ike_version_t,
+ private_keymat_v1_t *this)
+{
+ return IKEV1;
+}
+
+METHOD(keymat_t, create_dh, diffie_hellman_t*,
+ private_keymat_v1_t *this, diffie_hellman_group_t group)
+{
+ return lib->crypto->create_dh(lib->crypto, group);
+}
+
+METHOD(keymat_t, create_nonce_gen, nonce_gen_t*,
+ private_keymat_v1_t *this)
+{
+ return lib->crypto->create_nonce_gen(lib->crypto);
+}
+
+METHOD(keymat_t, get_aead, aead_t*,
+ private_keymat_v1_t *this, bool in)
+{
+ return this->aead;
+}
+
+METHOD(keymat_t, destroy, void,
+ private_keymat_v1_t *this)
+{
+ DESTROY_IF(this->prf);
+ DESTROY_IF(this->prf_auth);
+ DESTROY_IF(this->aead);
+ DESTROY_IF(this->hasher);
+ chunk_clear(&this->skeyid_d);
+ chunk_clear(&this->skeyid_a);
+ chunk_free(&this->phase1_iv.iv);
+ chunk_free(&this->phase1_iv.last_block);
+ this->ivs->destroy_function(this->ivs, (void*)iv_data_destroy);
+ this->qms->destroy_function(this->qms, (void*)qm_data_destroy);
+ free(this);
+}
+
+/**
+ * See header
+ */
+keymat_v1_t *keymat_v1_create(bool initiator)
+{
+ private_keymat_v1_t *this;
+
+ INIT(this,
+ .public = {
+ .keymat = {
+ .get_version = _get_version,
+ .create_dh = _create_dh,
+ .create_nonce_gen = _create_nonce_gen,
+ .get_aead = _get_aead,
+ .destroy = _destroy,
+ },
+ .derive_ike_keys = _derive_ike_keys,
+ .derive_child_keys = _derive_child_keys,
+ .create_hasher = _create_hasher,
+ .get_hasher = _get_hasher,
+ .get_hash = _get_hash,
+ .get_hash_phase2 = _get_hash_phase2,
+ .get_iv = _get_iv,
+ .update_iv = _update_iv,
+ .confirm_iv = _confirm_iv,
+ },
+ .ivs = linked_list_create(),
+ .qms = linked_list_create(),
+ .initiator = initiator,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/keymat_v1.h b/src/libcharon/sa/ikev1/keymat_v1.h
new file mode 100644
index 000000000..cc9f3b339
--- /dev/null
+++ b/src/libcharon/sa/ikev1/keymat_v1.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/**
+ * @defgroup keymat_v1 keymat_v1
+ * @{ @ingroup ikev1
+ */
+
+#ifndef KEYMAT_V1_H_
+#define KEYMAT_V1_H_
+
+#include <sa/keymat.h>
+#include <sa/authenticator.h>
+
+typedef struct keymat_v1_t keymat_v1_t;
+
+/**
+ * Derivation and management of sensitive keying material, IKEv1 variant.
+ */
+struct keymat_v1_t {
+
+ /**
+ * Implements keymat_t.
+ */
+ keymat_t keymat;
+
+ /**
+ * Derive keys for the IKE_SA.
+ *
+ * These keys are not handed out, but are used by the associated signers,
+ * crypters and authentication functions.
+ *
+ * @param proposal selected algorithms
+ * @param dh diffie hellman key allocated by create_dh()
+ * @param dh_other public DH value from other peer
+ * @param nonce_i initiators nonce value
+ * @param nonce_r responders nonce value
+ * @param id IKE_SA identifier
+ * @param auth authentication method
+ * @param shared_key PSK in case of AUTH_CLASS_PSK, NULL otherwise
+ * @return TRUE on success
+ */
+ bool (*derive_ike_keys)(keymat_v1_t *this, proposal_t *proposal,
+ diffie_hellman_t *dh, chunk_t dh_other,
+ chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
+ auth_method_t auth, shared_key_t *shared_key);
+
+ /**
+ * Derive keys for the CHILD_SA.
+ *
+ * @param proposal selected algorithms
+ * @param dh diffie hellman key, NULL if none used
+ * @param spi_i SPI chosen by initiatior
+ * @param spi_r SPI chosen by responder
+ * @param nonce_i quick mode initiator nonce
+ * @param nonce_r quick mode responder nonce
+ * @param encr_i allocated initiators encryption key
+ * @param integ_i allocated initiators integrity key
+ * @param encr_r allocated responders encryption key
+ * @param integ_r allocated responders integrity key
+ */
+ bool (*derive_child_keys)(keymat_v1_t *this, proposal_t *proposal,
+ diffie_hellman_t *dh, u_int32_t spi_i, u_int32_t spi_r,
+ chunk_t nonce_i, chunk_t nonce_r,
+ chunk_t *encr_i, chunk_t *integ_i,
+ chunk_t *encr_r, chunk_t *integ_r);
+
+ /**
+ * Create the negotiated hasher.
+ *
+ * @param proposal selected algorithms
+ * @return TRUE, if creation was successful
+ */
+ bool (*create_hasher)(keymat_v1_t *this, proposal_t *proposal);
+
+ /**
+ * Get the negotiated hasher.
+ *
+ * @return allocated hasher or NULL
+ */
+ hasher_t *(*get_hasher)(keymat_v1_t *this);
+
+ /**
+ * Get HASH data for authentication.
+ *
+ * @param initiatior TRUE to create HASH_I, FALSE for HASH_R
+ * @param dh public DH value of peer to create HASH for
+ * @param dh_other others public DH value
+ * @param ike_sa_id IKE_SA identifier
+ * @param sa_i encoded SA payload of initiator
+ * @param id encoded IDii payload for HASH_I (IDir for HASH_R)
+ * @param hash chunk receiving allocated HASH data
+ * @return TRUE if hash allocated successfully
+ */
+ bool (*get_hash)(keymat_v1_t *this, bool initiator,
+ chunk_t dh, chunk_t dh_other, ike_sa_id_t *ike_sa_id,
+ chunk_t sa_i, chunk_t id, chunk_t *hash);
+
+ /**
+ * Get HASH data for integrity/authentication in Phase 2 exchanges.
+ *
+ * @param message message to generate the HASH data for
+ * @param hash chunk receiving allocated hash data
+ * @return TRUE if hash allocated successfully
+ */
+ bool (*get_hash_phase2)(keymat_v1_t *this, message_t *message, chunk_t *hash);
+
+ /**
+ * Returns the IV for a message with the given message ID.
+ *
+ * The return chunk contains internal data and is valid until the next
+ * get_iv/udpate_iv/confirm_iv call.
+ *
+ * @param mid message ID
+ * @param iv chunk receiving IV, internal data
+ * @return TRUE if IV allocated successfully
+ */
+ bool (*get_iv)(keymat_v1_t *this, u_int32_t mid, chunk_t *iv);
+
+ /**
+ * Updates the IV for the next message with the given message ID.
+ *
+ * A call of confirm_iv() is required in order to actually make the IV
+ * available. This is needed for the inbound case where we store the last
+ * block of the encrypted message but want to update the IV only after
+ * verification of the decrypted message.
+ *
+ * @param mid message ID
+ * @param last_block last block of encrypted message (gets cloned)
+ * @return TRUE if IV updated successfully
+ */
+ bool (*update_iv)(keymat_v1_t *this, u_int32_t mid, chunk_t last_block);
+
+ /**
+ * Confirms the updated IV for the given message ID.
+ *
+ * To actually make the new IV available via get_iv this method has to
+ * be called after update_iv.
+ *
+ * @param mid message ID
+ * @return TRUE if IV confirmed successfully
+ */
+ bool (*confirm_iv)(keymat_v1_t *this, u_int32_t mid);
+};
+
+/**
+ * Create a keymat instance.
+ *
+ * @param initiator TRUE if we are the initiator
+ * @return keymat instance
+ */
+keymat_v1_t *keymat_v1_create(bool initiator);
+
+#endif /** KEYMAT_V1_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c
new file mode 100644
index 000000000..4096141ec
--- /dev/null
+++ b/src/libcharon/sa/ikev1/phase1.c
@@ -0,0 +1,795 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * 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 "phase1.h"
+
+#include <daemon.h>
+#include <sa/ikev1/keymat_v1.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <utils/linked_list.h>
+
+typedef struct private_phase1_t private_phase1_t;
+
+/**
+ * Private data of an phase1_t object.
+ */
+struct private_phase1_t {
+
+ /**
+ * Public phase1_t interface.
+ */
+ phase1_t public;
+
+ /**
+ * IKE_SA we negotiate
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Currently selected peer config
+ */
+ peer_cfg_t *peer_cfg;
+
+ /**
+ * Other possible peer config candidates
+ */
+ linked_list_t *candidates;
+
+ /**
+ * Acting as initiator
+ */
+ bool initiator;
+
+ /**
+ * Extracted SA payload bytes
+ */
+ chunk_t sa_payload;
+
+ /**
+ * DH exchange
+ */
+ diffie_hellman_t *dh;
+
+ /**
+ * Keymat derivation (from SA)
+ */
+ keymat_v1_t *keymat;
+
+ /**
+ * Received public DH value from peer
+ */
+ chunk_t dh_value;
+
+ /**
+ * Initiators nonce
+ */
+ chunk_t nonce_i;
+
+ /**
+ * Responder nonce
+ */
+ chunk_t nonce_r;
+};
+
+/**
+ * Get the first authentcation config from peer config
+ */
+static auth_cfg_t *get_auth_cfg(peer_cfg_t *peer_cfg, bool local)
+{
+ enumerator_t *enumerator;
+ auth_cfg_t *cfg = NULL;
+
+ enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, local);
+ enumerator->enumerate(enumerator, &cfg);
+ enumerator->destroy(enumerator);
+ return cfg;
+}
+
+/**
+ * Lookup a shared secret for this IKE_SA
+ */
+static shared_key_t *lookup_shared_key(private_phase1_t *this,
+ peer_cfg_t *peer_cfg)
+{
+ host_t *me, *other;
+ identification_t *my_id, *other_id;
+ shared_key_t *shared_key = NULL;
+ auth_cfg_t *my_auth, *other_auth;
+ enumerator_t *enumerator;
+
+ /* try to get a PSK for IP addresses */
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ my_id = identification_create_from_sockaddr(me->get_sockaddr(me));
+ other_id = identification_create_from_sockaddr(other->get_sockaddr(other));
+ if (my_id && other_id)
+ {
+ shared_key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE,
+ my_id, other_id);
+ }
+ DESTROY_IF(my_id);
+ DESTROY_IF(other_id);
+ if (shared_key)
+ {
+ return shared_key;
+ }
+
+ if (peer_cfg)
+ { /* as initiator or aggressive responder, use identities */
+ my_auth = get_auth_cfg(peer_cfg, TRUE);
+ other_auth = get_auth_cfg(peer_cfg, FALSE);
+ if (my_auth && other_auth)
+ {
+ my_id = my_auth->get(my_auth, AUTH_RULE_IDENTITY);
+ if (peer_cfg->use_aggressive(peer_cfg))
+ {
+ other_id = this->ike_sa->get_other_id(this->ike_sa);
+ }
+ else
+ {
+ other_id = other_auth->get(other_auth, AUTH_RULE_IDENTITY);
+ }
+ if (my_id && other_id)
+ {
+ shared_key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE,
+ my_id, other_id);
+ if (!shared_key)
+ {
+ DBG1(DBG_IKE, "no shared key found for '%Y'[%H] - '%Y'[%H]",
+ my_id, me, other_id, other);
+ }
+ }
+ }
+ return shared_key;
+ }
+ /* as responder, we try to find a config by IP */
+ enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
+ me, other, NULL, NULL, IKEV1);
+ while (enumerator->enumerate(enumerator, &peer_cfg))
+ {
+ my_auth = get_auth_cfg(peer_cfg, TRUE);
+ other_auth = get_auth_cfg(peer_cfg, FALSE);
+ if (my_auth && other_auth)
+ {
+ my_id = my_auth->get(my_auth, AUTH_RULE_IDENTITY);
+ other_id = other_auth->get(other_auth, AUTH_RULE_IDENTITY);
+ if (my_id)
+ {
+ shared_key = lib->credmgr->get_shared(lib->credmgr, SHARED_IKE,
+ my_id, other_id);
+ if (shared_key)
+ {
+ break;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "no shared key found for '%Y'[%H] - '%Y'[%H]",
+ my_id, me, other_id, other);
+ }
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (!peer_cfg)
+ {
+ DBG1(DBG_IKE, "no shared key found for %H - %H", me, other);
+ }
+ return shared_key;
+}
+
+METHOD(phase1_t, create_hasher, bool,
+ private_phase1_t *this)
+{
+ return this->keymat->create_hasher(this->keymat,
+ this->ike_sa->get_proposal(this->ike_sa));
+}
+
+METHOD(phase1_t, create_dh, bool,
+ private_phase1_t *this, diffie_hellman_group_t group)
+{
+ this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, group);
+ return this->dh != NULL;
+}
+
+METHOD(phase1_t, derive_keys, bool,
+ private_phase1_t *this, peer_cfg_t *peer_cfg, auth_method_t method)
+{
+ shared_key_t *shared_key = NULL;
+
+ switch (method)
+ {
+ case AUTH_PSK:
+ case AUTH_XAUTH_INIT_PSK:
+ case AUTH_XAUTH_RESP_PSK:
+ shared_key = lookup_shared_key(this, peer_cfg);
+ if (!shared_key)
+ {
+ return FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!this->keymat->derive_ike_keys(this->keymat,
+ this->ike_sa->get_proposal(this->ike_sa),
+ this->dh, this->dh_value, this->nonce_i, this->nonce_r,
+ this->ike_sa->get_id(this->ike_sa), method, shared_key))
+ {
+ DESTROY_IF(shared_key);
+ DBG1(DBG_IKE, "key derivation for %N failed", auth_method_names, method);
+ return FALSE;
+ }
+ charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, this->dh_value,
+ this->nonce_i, this->nonce_r, NULL, shared_key);
+ DESTROY_IF(shared_key);
+ return TRUE;
+}
+
+/**
+ * Check if a peer skipped authentication by using Hybrid authentication
+ */
+static bool skipped_auth(private_phase1_t *this,
+ auth_method_t method, bool local)
+{
+ bool initiator;
+
+ initiator = local == this->initiator;
+ if (initiator && method == AUTH_HYBRID_INIT_RSA)
+ {
+ return TRUE;
+ }
+ if (!initiator && method == AUTH_HYBRID_RESP_RSA)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Check if remote authentication constraints fulfilled
+ */
+static bool check_constraints(private_phase1_t *this, auth_method_t method)
+{
+ identification_t *id;
+ auth_cfg_t *auth, *cfg;
+ peer_cfg_t *peer_cfg;
+
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+ /* auth identity to comply */
+ id = this->ike_sa->get_other_id(this->ike_sa);
+ auth->add(auth, AUTH_RULE_IDENTITY, id->clone(id));
+ if (skipped_auth(this, method, FALSE))
+ {
+ return TRUE;
+ }
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ cfg = get_auth_cfg(peer_cfg, FALSE);
+ return cfg && auth->complies(auth, cfg, TRUE);
+}
+
+/**
+ * Save authentication information after authentication succeeded
+ */
+static void save_auth_cfg(private_phase1_t *this,
+ auth_method_t method, bool local)
+{
+ auth_cfg_t *auth;
+
+ if (skipped_auth(this, method, local))
+ {
+ return;
+ }
+ auth = auth_cfg_create();
+ /* for local config, we _copy_ entires from the config, as it contains
+ * certificates we must send later. */
+ auth->merge(auth, this->ike_sa->get_auth_cfg(this->ike_sa, local), local);
+ this->ike_sa->add_auth_cfg(this->ike_sa, local, auth);
+}
+
+/**
+ * Create an authenticator instance
+ */
+static authenticator_t* create_authenticator(private_phase1_t *this,
+ auth_method_t method, chunk_t id)
+{
+ authenticator_t *authenticator;
+
+ authenticator = authenticator_create_v1(this->ike_sa, this->initiator,
+ method, this->dh, this->dh_value, this->sa_payload, id);
+ if (!authenticator)
+ {
+ DBG1(DBG_IKE, "negotiated authentication method %N not supported",
+ auth_method_names, method);
+ }
+ return authenticator;
+}
+
+METHOD(phase1_t, verify_auth, bool,
+ private_phase1_t *this, auth_method_t method, message_t *message,
+ chunk_t id_data)
+{
+ authenticator_t *authenticator;
+ status_t status;
+
+ authenticator = create_authenticator(this, method, id_data);
+ if (authenticator)
+ {
+ status = authenticator->process(authenticator, message);
+ authenticator->destroy(authenticator);
+ if (status == SUCCESS && check_constraints(this, method))
+ {
+ save_auth_cfg(this, method, FALSE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+METHOD(phase1_t, build_auth, bool,
+ private_phase1_t *this, auth_method_t method, message_t *message,
+ chunk_t id_data)
+{
+ authenticator_t *authenticator;
+ status_t status;
+
+ authenticator = create_authenticator(this, method, id_data);
+ if (authenticator)
+ {
+ status = authenticator->build(authenticator, message);
+ authenticator->destroy(authenticator);
+ if (status == SUCCESS)
+ {
+ save_auth_cfg(this, method, TRUE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Get the two auth classes from local or remote config
+ */
+static void get_auth_class(peer_cfg_t *peer_cfg, bool local,
+ auth_class_t *c1, auth_class_t *c2)
+{
+ enumerator_t *enumerator;
+ auth_cfg_t *auth;
+
+ *c1 = *c2 = AUTH_CLASS_ANY;
+
+ enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, local);
+ while (enumerator->enumerate(enumerator, &auth))
+ {
+ if (*c1 == AUTH_CLASS_ANY)
+ {
+ *c1 = (uintptr_t)auth->get(auth, AUTH_RULE_AUTH_CLASS);
+ }
+ else
+ {
+ *c2 = (uintptr_t)auth->get(auth, AUTH_RULE_AUTH_CLASS);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Select an auth method to use by checking what key we have
+ */
+static auth_method_t get_pubkey_method(private_phase1_t *this, auth_cfg_t *auth)
+{
+ auth_method_t method = AUTH_NONE;
+ identification_t *id;
+ private_key_t *private;
+
+ if (auth)
+ {
+ id = (identification_t*)auth->get(auth, AUTH_RULE_IDENTITY);
+ if (id)
+ {
+ private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, NULL);
+ if (private)
+ {
+ switch (private->get_type(private))
+ {
+ case KEY_RSA:
+ method = AUTH_RSA;
+ break;
+ case KEY_ECDSA:
+ switch (private->get_keysize(private))
+ {
+ case 256:
+ method = AUTH_ECDSA_256;
+ break;
+ case 384:
+ method = AUTH_ECDSA_384;
+ break;
+ case 521:
+ method = AUTH_ECDSA_521;
+ break;
+ default:
+ DBG1(DBG_IKE, "%d bit ECDSA private key size not "
+ "supported", private->get_keysize(private));
+ break;
+ }
+ break;
+ default:
+ DBG1(DBG_IKE, "private key of type %N not supported",
+ key_type_names, private->get_type(private));
+ break;
+ }
+ private->destroy(private);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "no private key found for '%Y'", id);
+ }
+ }
+ }
+ return method;
+}
+
+/**
+ * Calculate authentication method from a peer config
+ */
+static auth_method_t calc_auth_method(private_phase1_t *this,
+ peer_cfg_t *peer_cfg)
+{
+ auth_class_t i1, i2, r1, r2;
+
+ get_auth_class(peer_cfg, this->initiator, &i1, &i2);
+ get_auth_class(peer_cfg, !this->initiator, &r1, &r2);
+
+ if (i1 == AUTH_CLASS_PUBKEY && r1 == AUTH_CLASS_PUBKEY)
+ {
+ if (i2 == AUTH_CLASS_ANY && r2 == AUTH_CLASS_ANY)
+ {
+ /* for any pubkey method, return RSA */
+ return AUTH_RSA;
+ }
+ if (i2 == AUTH_CLASS_XAUTH)
+ {
+ return AUTH_XAUTH_INIT_RSA;
+ }
+ if (r2 == AUTH_CLASS_XAUTH)
+ {
+ return AUTH_XAUTH_RESP_RSA;
+ }
+ }
+ if (i1 == AUTH_CLASS_PSK && r1 == AUTH_CLASS_PSK)
+ {
+ if (i2 == AUTH_CLASS_ANY && r2 == AUTH_CLASS_ANY)
+ {
+ return AUTH_PSK;
+ }
+ if (i2 == AUTH_CLASS_XAUTH)
+ {
+ return AUTH_XAUTH_INIT_PSK;
+ }
+ if (r2 == AUTH_CLASS_XAUTH)
+ {
+ return AUTH_XAUTH_RESP_PSK;
+ }
+ }
+ if (i1 == AUTH_CLASS_XAUTH && r1 == AUTH_CLASS_PUBKEY &&
+ i2 == AUTH_CLASS_ANY && r2 == AUTH_CLASS_ANY)
+ {
+ return AUTH_HYBRID_INIT_RSA;
+ }
+ return AUTH_NONE;
+}
+
+METHOD(phase1_t, get_auth_method, auth_method_t,
+ private_phase1_t *this, peer_cfg_t *peer_cfg)
+{
+ auth_method_t method;
+
+ method = calc_auth_method(this, peer_cfg);
+ if (method == AUTH_RSA)
+ {
+ return get_pubkey_method(this, get_auth_cfg(peer_cfg, TRUE));
+ }
+ return method;
+}
+
+/**
+ * Check if a peer config can be used with a given auth method
+ */
+static bool check_auth_method(private_phase1_t *this, peer_cfg_t *peer_cfg,
+ auth_method_t given)
+{
+ auth_method_t method;
+
+ method = calc_auth_method(this, peer_cfg);
+ switch (given)
+ {
+ case AUTH_ECDSA_256:
+ case AUTH_ECDSA_384:
+ case AUTH_ECDSA_521:
+ return method == AUTH_RSA;
+ default:
+ return method == given;
+ }
+}
+
+METHOD(phase1_t, select_config, peer_cfg_t*,
+ private_phase1_t *this, auth_method_t method, bool aggressive,
+ identification_t *id)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *current;
+ host_t *me, *other;
+
+ if (this->peer_cfg)
+ { /* try to find an alternative config */
+ if (this->candidates->remove_first(this->candidates,
+ (void**)&current) != SUCCESS)
+ {
+ DBG1(DBG_CFG, "no alternative config found");
+ return NULL;
+ }
+ DBG1(DBG_CFG, "switching to peer config '%s'",
+ current->get_name(current));
+ return current;
+ }
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ DBG1(DBG_CFG, "looking for %N peer configs matching %H...%H[%Y]",
+ auth_method_names, method, me, other, id);
+ enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
+ me, other, NULL, id, IKEV1);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (check_auth_method(this, current, method) &&
+ current->use_aggressive(current) == aggressive)
+ {
+ current->get_ref(current);
+ if (!this->peer_cfg)
+ {
+ this->peer_cfg = current;
+ }
+ else
+ {
+ this->candidates->insert_last(this->candidates, current);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (this->peer_cfg)
+ {
+ DBG1(DBG_CFG, "selected peer config \"%s\"",
+ this->peer_cfg->get_name(this->peer_cfg));
+ return this->peer_cfg->get_ref(this->peer_cfg);
+ }
+ DBG1(DBG_IKE, "no peer config found");
+ return NULL;
+}
+
+METHOD(phase1_t, get_id, identification_t*,
+ private_phase1_t *this, peer_cfg_t *peer_cfg, bool local)
+{
+ identification_t *id = NULL;
+ auth_cfg_t *auth;
+
+ auth = get_auth_cfg(peer_cfg, local);
+ if (auth)
+ {
+ id = auth->get(auth, AUTH_RULE_IDENTITY);
+ if (local && (!id || id->get_type(id) == ID_ANY))
+ { /* no ID configured, use local IP address */
+ host_t *me;
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ if (!me->is_anyaddr(me))
+ {
+ id = identification_create_from_sockaddr(me->get_sockaddr(me));
+ auth->add(auth, AUTH_RULE_IDENTITY, id);
+ }
+ }
+ }
+ return id;
+}
+
+METHOD(phase1_t, has_virtual_ip, bool,
+ private_phase1_t *this, peer_cfg_t *peer_cfg)
+{
+ enumerator_t *enumerator;
+ bool found = FALSE;
+ host_t *host;
+
+ enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg);
+ found = enumerator->enumerate(enumerator, &host);
+ enumerator->destroy(enumerator);
+
+ return found;
+}
+
+METHOD(phase1_t, has_pool, bool,
+ private_phase1_t *this, peer_cfg_t *peer_cfg)
+{
+ enumerator_t *enumerator;
+ bool found = FALSE;
+ char *pool;
+
+ enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
+ found = enumerator->enumerate(enumerator, &pool);
+ enumerator->destroy(enumerator);
+
+ return found;
+}
+
+METHOD(phase1_t, save_sa_payload, bool,
+ private_phase1_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload, *sa = NULL;
+ chunk_t data;
+ size_t offset = IKE_HEADER_LENGTH;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == SECURITY_ASSOCIATION_V1)
+ {
+ sa = payload;
+ break;
+ }
+ else
+ {
+ offset += payload->get_length(payload);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ data = message->get_packet_data(message);
+ if (sa && data.len >= offset + sa->get_length(sa))
+ {
+ /* Get SA payload without 4 byte fixed header */
+ data = chunk_skip(data, offset);
+ data.len = sa->get_length(sa);
+ data = chunk_skip(data, 4);
+ this->sa_payload = chunk_clone(data);
+ return TRUE;
+ }
+ DBG1(DBG_IKE, "unable to extract SA payload encoding");
+ return FALSE;
+}
+
+METHOD(phase1_t, add_nonce_ke, bool,
+ private_phase1_t *this, message_t *message)
+{
+ nonce_payload_t *nonce_payload;
+ ke_payload_t *ke_payload;
+ nonce_gen_t *nonceg;
+ chunk_t nonce;
+
+ ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1, this->dh);
+ message->add_payload(message, &ke_payload->payload_interface);
+
+ nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
+ if (!nonceg)
+ {
+ DBG1(DBG_IKE, "no nonce generator found to create nonce");
+ return FALSE;
+ }
+ if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, &nonce))
+ {
+ DBG1(DBG_IKE, "nonce allocation failed");
+ nonceg->destroy(nonceg);
+ return FALSE;
+ }
+ nonceg->destroy(nonceg);
+
+ nonce_payload = nonce_payload_create(NONCE_V1);
+ nonce_payload->set_nonce(nonce_payload, nonce);
+ message->add_payload(message, &nonce_payload->payload_interface);
+
+ if (this->initiator)
+ {
+ this->nonce_i = nonce;
+ }
+ else
+ {
+ this->nonce_r = nonce;
+ }
+ return TRUE;
+}
+
+METHOD(phase1_t, get_nonce_ke, bool,
+ private_phase1_t *this, message_t *message)
+{
+ nonce_payload_t *nonce_payload;
+ ke_payload_t *ke_payload;
+
+ ke_payload = (ke_payload_t*)message->get_payload(message, KEY_EXCHANGE_V1);
+ if (!ke_payload)
+ {
+ DBG1(DBG_IKE, "KE payload missing in message");
+ return FALSE;
+ }
+ this->dh_value = chunk_clone(ke_payload->get_key_exchange_data(ke_payload));
+ this->dh->set_other_public_value(this->dh, this->dh_value);
+
+ nonce_payload = (nonce_payload_t*)message->get_payload(message, NONCE_V1);
+ if (!nonce_payload)
+ {
+ DBG1(DBG_IKE, "NONCE payload missing in message");
+ return FALSE;
+ }
+
+ if (this->initiator)
+ {
+ this->nonce_r = nonce_payload->get_nonce(nonce_payload);
+ }
+ else
+ {
+ this->nonce_i = nonce_payload->get_nonce(nonce_payload);
+ }
+ return TRUE;
+}
+
+METHOD(phase1_t, destroy, void,
+ private_phase1_t *this)
+{
+ DESTROY_IF(this->peer_cfg);
+ this->candidates->destroy_offset(this->candidates,
+ offsetof(peer_cfg_t, destroy));
+ chunk_free(&this->sa_payload);
+ DESTROY_IF(this->dh);
+ free(this->dh_value.ptr);
+ free(this->nonce_i.ptr);
+ free(this->nonce_r.ptr);
+ free(this);
+}
+
+/**
+ * See header
+ */
+phase1_t *phase1_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_phase1_t *this;
+
+ INIT(this,
+ .public = {
+ .create_hasher = _create_hasher,
+ .create_dh = _create_dh,
+ .derive_keys = _derive_keys,
+ .get_auth_method = _get_auth_method,
+ .get_id = _get_id,
+ .select_config = _select_config,
+ .has_virtual_ip = _has_virtual_ip,
+ .has_pool = _has_pool,
+ .verify_auth = _verify_auth,
+ .build_auth = _build_auth,
+ .save_sa_payload = _save_sa_payload,
+ .add_nonce_ke = _add_nonce_ke,
+ .get_nonce_ke = _get_nonce_ke,
+ .destroy = _destroy,
+ },
+ .candidates = linked_list_create(),
+ .ike_sa = ike_sa,
+ .initiator = initiator,
+ .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/phase1.h b/src/libcharon/sa/ikev1/phase1.h
new file mode 100644
index 000000000..eaf8908e7
--- /dev/null
+++ b/src/libcharon/sa/ikev1/phase1.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * 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 phase1 phase1
+ * @{ @ingroup ikev1
+ */
+
+#ifndef PHASE1_H_
+#define PHASE1_H_
+
+typedef struct phase1_t phase1_t;
+
+#include <sa/ike_sa.h>
+#include <crypto/diffie_hellman.h>
+
+/**
+ * Common phase 1 helper for main and aggressive mode.
+ */
+struct phase1_t {
+
+ /**
+ * Create keymat hasher.
+ *
+ * @return TRUE if hasher created
+ */
+ bool (*create_hasher)(phase1_t *this);
+
+ /**
+ * Create DH object using SA keymat.
+ *
+ * @param group negotiated DH group
+ * @return TRUE if group supported
+ */
+ bool (*create_dh)(phase1_t *this, diffie_hellman_group_t group);
+
+ /**
+ * Derive key material.
+ *
+ * @param peer_cfg peer config to look up shared key for, or NULL
+ * @param method negotiated authenticated method
+ * @return TRUE if successful
+ */
+ bool (*derive_keys)(phase1_t *this, peer_cfg_t *peer_cfg,
+ auth_method_t method);
+ /**
+ * Verify a HASH or SIG payload in message.
+ *
+ * @param method negotiated auth method
+ * @param message message containing HASH or SIG payload
+ * @param id_data encoded identity, including protocol/port fields
+ * @return TRUE if verified successfully
+ */
+ bool (*verify_auth)(phase1_t *this, auth_method_t method,
+ message_t *message, chunk_t id_data);
+
+ /**
+ * Build a HASH or SIG payload and add it to message.
+ *
+ * @param method negotiated auth method
+ * @param message message to add payload to
+ * @param id_data encoded identity, including protocol/port fields
+ * @return TRUE if built successfully
+ */
+ bool (*build_auth)(phase1_t *this, auth_method_t method,
+ message_t *message, chunk_t id_data);
+
+ /**
+ * Get the IKEv1 authentication method defined by peer config.
+ *
+ * @param peer_cfg peer config to get auth method from
+ * @return auth method, or AUTH_NONE
+ */
+ auth_method_t (*get_auth_method)(phase1_t *this, peer_cfg_t *peer_cfg);
+
+ /**
+ * Select a peer config as responder.
+ *
+ * If called after the first successful call the next alternative config
+ * is returned, if any.
+ *
+ * @param method used authentication method
+ * @param aggressive TRUE to get an aggressive mode config
+ * @param id initiator identity
+ * @return selected peer config, NULL if none found
+ */
+ peer_cfg_t* (*select_config)(phase1_t *this, auth_method_t method,
+ bool aggressive, identification_t *id);
+
+ /**
+ * Get configured identity from peer config.
+ *
+ * @param peer_cfg peer config to get identity from
+ * @param local TRUE to get own identity, FALSE for remote
+ * @return identity, pointing to internal config data
+ */
+ identification_t* (*get_id)(phase1_t *this, peer_cfg_t *peer_cfg, bool local);
+
+ /**
+ * Check if peer config has virtual IPs pool assigned.
+ *
+ * @param peer_cfg peer_config to check
+ * @return TRUE if peer config contains at least one pool
+ */
+ bool (*has_pool)(phase1_t *this, peer_cfg_t *peer_cfg);
+
+ /**
+ * Check if peer config has virtual IPs to request
+ *
+ * @param peer_cfg peer_config to check
+ * @return TRUE if peer config contains at least one virtual IP
+ */
+ bool (*has_virtual_ip)(phase1_t *this, peer_cfg_t *peer_cfg);
+
+ /**
+ * Extract and store SA payload bytes from encoded message.
+ *
+ * @param message message to extract SA payload bytes from
+ * @return TRUE if SA payload found
+ */
+ bool (*save_sa_payload)(phase1_t *this, message_t *message);
+
+ /**
+ * Add Nonce and KE payload to message.
+ *
+ * @param message message to add payloads
+ * @return TRUE if payloads added successfully
+ */
+ bool (*add_nonce_ke)(phase1_t *this, message_t *message);
+
+ /**
+ * Extract Nonce and KE payload from message.
+ *
+ * @param message message to get payloads from
+ * @return TRUE if payloads extracted successfully
+ */
+ bool (*get_nonce_ke)(phase1_t *this, message_t *message);
+
+ /**
+ * Destroy a phase1_t.
+ */
+ void (*destroy)(phase1_t *this);
+};
+
+/**
+ * Create a phase1 instance.
+ *
+ * @param ike_sa IKE_SA to set up
+ * @param initiator TRUE if initiating actively
+ * @return Phase 1 helper
+ */
+phase1_t *phase1_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** PHASE1_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
new file mode 100644
index 000000000..fd0ad235a
--- /dev/null
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -0,0 +1,1714 @@
+/*
+ * Copyright (C) 2007-2011 Tobias Brunner
+ * Copyright (C) 2007-2011 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_v1.h"
+
+#include <math.h>
+
+#include <daemon.h>
+#include <sa/ikev1/tasks/main_mode.h>
+#include <sa/ikev1/tasks/aggressive_mode.h>
+#include <sa/ikev1/tasks/quick_mode.h>
+#include <sa/ikev1/tasks/quick_delete.h>
+#include <sa/ikev1/tasks/xauth.h>
+#include <sa/ikev1/tasks/mode_config.h>
+#include <sa/ikev1/tasks/informational.h>
+#include <sa/ikev1/tasks/isakmp_natd.h>
+#include <sa/ikev1/tasks/isakmp_vendor.h>
+#include <sa/ikev1/tasks/isakmp_cert_pre.h>
+#include <sa/ikev1/tasks/isakmp_cert_post.h>
+#include <sa/ikev1/tasks/isakmp_delete.h>
+#include <sa/ikev1/tasks/isakmp_dpd.h>
+
+#include <processing/jobs/retransmit_job.h>
+#include <processing/jobs/delete_ike_sa_job.h>
+#include <processing/jobs/dpd_timeout_job.h>
+
+/**
+ * Number of old messages hashes we keep for retransmission.
+ *
+ * In Main Mode, we must ignore messages from a previous message pair if
+ * we already continued to the next. Otherwise a late retransmission
+ * could be considered as a reply to the newer request.
+ */
+#define MAX_OLD_HASHES 2
+
+/**
+ * First sequence number of responding packets.
+ *
+ * To distinguish retransmission jobs for initiating and responding packets,
+ * we split up the sequence counter and use the upper half for responding.
+ */
+#define RESPONDING_SEQ INT_MAX
+
+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_v1_t public;
+
+ /**
+ * associated IKE_SA we are serving
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * RNG to create message IDs
+ */
+ rng_t *rng;
+
+ /**
+ * Exchange we are currently handling as responder
+ */
+ struct {
+ /**
+ * Message ID of the last response
+ */
+ u_int32_t mid;
+
+ /**
+ * Hash of a previously received message
+ */
+ u_int32_t hash;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ /**
+ * Sequence number of the last sent message
+ */
+ u_int32_t seqnr;
+
+ /**
+ * how many times we have retransmitted so far
+ */
+ u_int retransmitted;
+
+ } responding;
+
+ /**
+ * Exchange we are currently handling as initiator
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * Hashes of old responses we can ignore
+ */
+ u_int32_t old_hashes[MAX_OLD_HASHES];
+
+ /**
+ * Position in old hash array
+ */
+ int old_hash_pos;
+
+ /**
+ * Sequence number of the last sent message
+ */
+ u_int32_t seqnr;
+
+ /**
+ * 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;
+
+ /**
+ * Queued messages not yet ready to process
+ */
+ message_t *queued;
+
+ /**
+ * 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;
+
+ /**
+ * Sequence number for sending DPD requests
+ */
+ u_int32_t dpd_send;
+
+ /**
+ * Sequence number for received DPD requests
+ */
+ u_int32_t dpd_recv;
+};
+
+METHOD(task_manager_t, flush_queue, void,
+ private_task_manager_t *this, task_queue_t queue)
+{
+ linked_list_t *list;
+ task_t *task;
+
+ if (this->queued)
+ {
+ this->queued->destroy(this->queued);
+ this->queued = NULL;
+ }
+ switch (queue)
+ {
+ case TASK_QUEUE_ACTIVE:
+ list = this->active_tasks;
+ /* cancel pending retransmits */
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ DESTROY_IF(this->initiating.packet);
+ this->initiating.packet = NULL;
+ break;
+ case TASK_QUEUE_PASSIVE:
+ list = this->passive_tasks;
+ break;
+ case TASK_QUEUE_QUEUED:
+ list = this->queued_tasks;
+ break;
+ default:
+ return;
+ }
+ while (list->remove_last(list, (void**)&task) == SUCCESS)
+ {
+ task->destroy(task);
+ }
+}
+
+/**
+ * flush all tasks in the task manager
+ */
+static void flush(private_task_manager_t *this)
+{
+ flush_queue(this, TASK_QUEUE_QUEUED);
+ flush_queue(this, TASK_QUEUE_PASSIVE);
+ flush_queue(this, TASK_QUEUE_ACTIVE);
+}
+
+/**
+ * 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;
+}
+
+/**
+ * Retransmit a packet, either as initiator or as responder
+ */
+static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr,
+ u_int mid, u_int retransmitted, packet_t *packet)
+{
+ u_int32_t t;
+
+ if (retransmitted > this->retransmit_tries)
+ {
+ DBG1(DBG_IKE, "giving up after %u retransmits", retransmitted - 1);
+ return DESTROY_ME;
+ }
+ t = (u_int32_t)(this->retransmit_timeout * 1000.0 *
+ pow(this->retransmit_base, retransmitted));
+ if (retransmitted)
+ {
+ DBG1(DBG_IKE, "sending retransmit %u of %s message ID %u, seq %u",
+ retransmitted, seqnr < RESPONDING_SEQ ? "request" : "response",
+ mid, seqnr < RESPONDING_SEQ ? seqnr : seqnr - RESPONDING_SEQ);
+ }
+ charon->sender->send(charon->sender, packet->clone(packet));
+ lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)
+ retransmit_job_create(seqnr, this->ike_sa->get_id(this->ike_sa)), t);
+ return NEED_MORE;
+}
+
+METHOD(task_manager_t, retransmit, status_t,
+ private_task_manager_t *this, u_int32_t seqnr)
+{
+ status_t status = SUCCESS;
+
+ if (seqnr == this->initiating.seqnr && this->initiating.packet)
+ {
+ status = retransmit_packet(this, seqnr, this->initiating.mid,
+ this->initiating.retransmitted, this->initiating.packet);
+ if (status == NEED_MORE)
+ {
+ this->initiating.retransmitted++;
+ status = SUCCESS;
+ }
+ }
+ if (seqnr == this->responding.seqnr && this->responding.packet)
+ {
+ status = retransmit_packet(this, seqnr, this->responding.mid,
+ this->responding.retransmitted, this->responding.packet);
+ if (status == NEED_MORE)
+ {
+ this->responding.retransmitted++;
+ status = SUCCESS;
+ }
+ }
+ return status;
+}
+
+/**
+ * Check if we have to wait for a mode config before starting a quick mode
+ */
+static bool mode_config_expected(private_task_manager_t *this)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *peer_cfg;
+ char *pool;
+ host_t *host;
+
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer_cfg)
+ {
+ enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
+ if (!enumerator->enumerate(enumerator, &pool))
+ { /* no pool configured */
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ enumerator->destroy(enumerator);
+
+ enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa,
+ FALSE);
+ if (!enumerator->enumerate(enumerator, &host))
+ { /* have a pool, but no VIP assigned yet */
+ enumerator->destroy(enumerator);
+ return TRUE;
+ }
+ enumerator->destroy(enumerator);
+ }
+ return FALSE;
+}
+
+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 = EXCHANGE_TYPE_UNDEFINED;
+ bool new_mid = FALSE, expect_response = FALSE, cancelled = FALSE, keep = FALSE;
+
+ if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED &&
+ this->initiating.type != INFORMATIONAL_V1)
+ {
+ 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_ISAKMP_VENDOR);
+ activate_task(this, TASK_ISAKMP_CERT_PRE);
+ if (activate_task(this, TASK_MAIN_MODE))
+ {
+ exchange = ID_PROT;
+ }
+ else if (activate_task(this, TASK_AGGRESSIVE_MODE))
+ {
+ exchange = AGGRESSIVE;
+ }
+ activate_task(this, TASK_ISAKMP_CERT_POST);
+ activate_task(this, TASK_ISAKMP_NATD);
+ break;
+ case IKE_CONNECTING:
+ if (activate_task(this, TASK_ISAKMP_DELETE))
+ {
+ exchange = INFORMATIONAL_V1;
+ new_mid = TRUE;
+ break;
+ }
+ if (activate_task(this, TASK_XAUTH))
+ {
+ exchange = TRANSACTION;
+ new_mid = TRUE;
+ break;
+ }
+ if (activate_task(this, TASK_INFORMATIONAL))
+ {
+ exchange = INFORMATIONAL_V1;
+ new_mid = TRUE;
+ break;
+ }
+ break;
+ case IKE_ESTABLISHED:
+ if (activate_task(this, TASK_MODE_CONFIG))
+ {
+ exchange = TRANSACTION;
+ new_mid = TRUE;
+ break;
+ }
+ if (!mode_config_expected(this) &&
+ activate_task(this, TASK_QUICK_MODE))
+ {
+ exchange = QUICK_MODE;
+ new_mid = TRUE;
+ break;
+ }
+ if (activate_task(this, TASK_INFORMATIONAL))
+ {
+ exchange = INFORMATIONAL_V1;
+ new_mid = TRUE;
+ break;
+ }
+ if (activate_task(this, TASK_QUICK_DELETE))
+ {
+ exchange = INFORMATIONAL_V1;
+ new_mid = TRUE;
+ break;
+ }
+ if (activate_task(this, TASK_ISAKMP_DELETE))
+ {
+ exchange = INFORMATIONAL_V1;
+ new_mid = TRUE;
+ break;
+ }
+ if (activate_task(this, TASK_ISAKMP_DPD))
+ {
+ exchange = INFORMATIONAL_V1;
+ new_mid = TRUE;
+ break;
+ }
+ break;
+ 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_MAIN_MODE:
+ exchange = ID_PROT;
+ break;
+ case TASK_AGGRESSIVE_MODE:
+ exchange = AGGRESSIVE;
+ break;
+ case TASK_QUICK_MODE:
+ exchange = QUICK_MODE;
+ break;
+ case TASK_XAUTH:
+ exchange = TRANSACTION;
+ new_mid = TRUE;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ if (exchange == EXCHANGE_TYPE_UNDEFINED)
+ {
+ 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);
+
+ if (new_mid)
+ {
+ if (!this->rng->get_bytes(this->rng, sizeof(this->initiating.mid),
+ (void*)&this->initiating.mid))
+ {
+ DBG1(DBG_IKE, "failed to allocate message ID, destroying IKE_SA");
+ flush(this);
+ return DESTROY_ME;
+ }
+ }
+ message = message_create(IKEV1_MAJOR_VERSION, IKEV1_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);
+ if (task->get_type(task) == TASK_AGGRESSIVE_MODE ||
+ task->get_type(task) == TASK_QUICK_MODE)
+ { /* last message of three message exchange */
+ keep = TRUE;
+ }
+ task->destroy(task);
+ continue;
+ case NEED_MORE:
+ expect_response = TRUE;
+ /* processed, but task needs another exchange */
+ continue;
+ case ALREADY_DONE:
+ cancelled = TRUE;
+ 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;
+ }
+ break;
+ }
+ enumerator->destroy(enumerator);
+
+ if (this->active_tasks->get_count(this->active_tasks) == 0 &&
+ (exchange == QUICK_MODE || exchange == AGGRESSIVE))
+ { /* tasks completed, no exchange active anymore */
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ }
+ if (cancelled)
+ {
+ message->destroy(message);
+ return initiate(this);
+ }
+
+ DESTROY_IF(this->initiating.packet);
+ 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;
+ }
+
+ this->initiating.seqnr++;
+ if (expect_response)
+ {
+ message->destroy(message);
+ return retransmit(this, this->initiating.seqnr);
+ }
+ if (keep)
+ { /* keep the packet for retransmission, the responder might request it */
+ charon->sender->send(charon->sender,
+ this->initiating.packet->clone(this->initiating.packet));
+ }
+ else
+ {
+ charon->sender->send(charon->sender, this->initiating.packet);
+ this->initiating.packet = NULL;
+ }
+ message->destroy(message);
+
+ if (exchange == INFORMATIONAL_V1)
+ {
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_CONNECTING:
+ /* close after sending an INFORMATIONAL when unestablished */
+ return FAILED;
+ case IKE_DELETING:
+ /* close after sending a DELETE */
+ return DESTROY_ME;
+ default:
+ break;
+ }
+ }
+ return initiate(this);
+}
+
+/**
+ * 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;
+ bool delete = FALSE, cancelled = FALSE, expect_request = FALSE;
+ status_t status;
+
+ me = request->get_destination(request);
+ other = request->get_source(request);
+
+ message = message_create(IKEV1_MAJOR_VERSION, IKEV1_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, request->get_message_id(request));
+ message->set_request(message, FALSE);
+
+ this->responding.mid = request->get_message_id(request);
+ this->responding.retransmitted = 0;
+ this->responding.seqnr++;
+
+ 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);
+ task->destroy(task);
+ continue;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ if (task->get_type(task) == TASK_QUICK_MODE ||
+ task->get_type(task) == TASK_AGGRESSIVE_MODE)
+ { /* we rely on initiator retransmission, except for
+ * three-message exchanges */
+ expect_request = TRUE;
+ }
+ continue;
+ case ALREADY_DONE:
+ cancelled = TRUE;
+ break;
+ case FAILED:
+ default:
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ /* FALL */
+ case DESTROY_ME:
+ /* destroy IKE_SA, but SEND response first */
+ delete = TRUE;
+ break;
+ }
+ break;
+ }
+ enumerator->destroy(enumerator);
+
+ DESTROY_IF(this->responding.packet);
+ this->responding.packet = NULL;
+ if (cancelled)
+ {
+ message->destroy(message);
+ return initiate(this);
+ }
+ 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;
+ }
+
+ if (expect_request && !delete)
+ {
+ return retransmit(this, this->responding.seqnr);
+ }
+ charon->sender->send(charon->sender,
+ this->responding.packet->clone(this->responding.packet));
+ if (delete)
+ {
+ return DESTROY_ME;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Send a notify in a separate INFORMATIONAL exchange back to the sender.
+ * The notify protocol_id is set to ISAKMP
+ */
+static void send_notify(private_task_manager_t *this, message_t *request,
+ notify_type_t type)
+{
+ message_t *response;
+ packet_t *packet;
+ host_t *me, *other;
+ u_int32_t mid;
+
+ if (request->get_exchange_type(request) == INFORMATIONAL_V1)
+ { /* don't respond to INFORMATIONAL requests to avoid a notify war */
+ DBG1(DBG_IKE, "ignore malformed INFORMATIONAL request");
+ return;
+ }
+ if (!this->rng->get_bytes(this->rng, sizeof(mid), (void*)&mid))
+ {
+ DBG1(DBG_IKE, "failed to allocate message ID");
+ return;
+ }
+ response = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION);
+ response->set_exchange_type(response, INFORMATIONAL_V1);
+ response->set_request(response, TRUE);
+ response->set_message_id(response, mid);
+ response->add_payload(response, (payload_t*)
+ notify_payload_create_from_protocol_and_type(NOTIFY_V1,
+ PROTO_IKE, type));
+
+ 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);
+}
+
+/**
+ * Process a DPD request/response
+ */
+static bool process_dpd(private_task_manager_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ notify_type_t type;
+ u_int32_t seq;
+ chunk_t data;
+
+ type = DPD_R_U_THERE;
+ notify = message->get_notify(message, type);
+ if (!notify)
+ {
+ type = DPD_R_U_THERE_ACK;
+ notify = message->get_notify(message, type);
+ }
+ if (!notify)
+ {
+ return FALSE;
+ }
+ data = notify->get_notification_data(notify);
+ if (data.len != 4)
+ {
+ return FALSE;
+ }
+ seq = untoh32(data.ptr);
+
+ if (type == DPD_R_U_THERE)
+ {
+ if (this->dpd_recv == 0 || seq == this->dpd_recv)
+ { /* check sequence validity */
+ this->dpd_recv = seq + 1;
+ this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND,
+ time_monotonic(NULL));
+ }
+ /* but respond anyway */
+ this->ike_sa->queue_task(this->ike_sa,
+ &isakmp_dpd_create(this->ike_sa, DPD_R_U_THERE_ACK, seq)->task);
+ }
+ else /* DPD_R_U_THERE_ACK */
+ {
+ if (seq == this->dpd_send - 1)
+ {
+ this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND,
+ time_monotonic(NULL));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received invalid DPD sequence number %u "
+ "(expected %u), ignored", seq, this->dpd_send - 1);
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * 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;
+ bool send_response = FALSE, dpd = FALSE;
+
+ if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
+ 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 ID_PROT:
+ task = (task_t *)isakmp_vendor_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)isakmp_cert_pre_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t *)main_mode_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)isakmp_cert_post_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t *)isakmp_natd_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ case AGGRESSIVE:
+ task = (task_t *)isakmp_vendor_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)isakmp_cert_pre_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t *)aggressive_mode_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)isakmp_cert_post_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t *)isakmp_natd_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ case QUICK_MODE:
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED)
+ {
+ DBG1(DBG_IKE, "received quick mode request for "
+ "unestablished IKE_SA, ignored");
+ return FAILED;
+ }
+ task = (task_t *)quick_mode_create(this->ike_sa, NULL,
+ NULL, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ case INFORMATIONAL_V1:
+ if (process_dpd(this, message))
+ {
+ dpd = TRUE;
+ }
+ else
+ {
+ task = (task_t *)informational_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_first(this->passive_tasks, task);
+ }
+ break;
+ case TRANSACTION:
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+ {
+ task = (task_t *)mode_config_create(this->ike_sa, FALSE);
+ }
+ else
+ {
+ task = (task_t *)xauth_create(this->ike_sa, FALSE);
+ }
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ default:
+ return FAILED;
+ }
+ }
+ if (dpd)
+ {
+ return initiate(this);
+ }
+ this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, time_monotonic(NULL));
+
+ /* 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);
+ continue;
+ case NEED_MORE:
+ /* processed, but task needs at least another call to build() */
+ send_response = TRUE;
+ continue;
+ case ALREADY_DONE:
+ send_response = FALSE;
+ 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;
+ }
+ break;
+ }
+ enumerator->destroy(enumerator);
+
+ if (send_response)
+ {
+ if (build_response(this, message) != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+ }
+ else
+ { /* We don't send a response, so don't retransmit one if we get
+ * the same message again. */
+ DESTROY_IF(this->responding.packet);
+ this->responding.packet = NULL;
+ }
+ if (this->passive_tasks->get_count(this->passive_tasks) == 0 &&
+ this->queued_tasks->get_count(this->queued_tasks) > 0)
+ {
+ /* passive tasks completed, check if an active task has been queued,
+ * such as XAUTH or modeconfig push */
+ return initiate(this);
+ }
+ return SUCCESS;
+}
+
+/**
+ * handle an incoming response message
+ */
+static status_t process_response(private_task_manager_t *this,
+ message_t *message)
+{
+ enumerator_t *enumerator;
+ message_t *queued;
+ status_t status;
+ 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;
+ }
+
+ 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);
+ continue;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ continue;
+ case ALREADY_DONE:
+ 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;
+ }
+ break;
+ }
+ enumerator->destroy(enumerator);
+
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ DESTROY_IF(this->initiating.packet);
+ this->initiating.packet = NULL;
+
+ if (this->queued && this->active_tasks->get_count(this->active_tasks) == 0)
+ {
+ queued = this->queued;
+ this->queued = NULL;
+ status = this->public.task_manager.process_message(
+ &this->public.task_manager, queued);
+ queued->destroy(queued);
+ if (status == DESTROY_ME)
+ {
+ return status;
+ }
+ }
+
+ return initiate(this);
+}
+
+/**
+ * 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;
+
+ status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa));
+
+ if (status != SUCCESS)
+ {
+ switch (status)
+ {
+ case NOT_SUPPORTED:
+ DBG1(DBG_IKE, "unsupported exchange type");
+ send_notify(this, msg, INVALID_EXCHANGE_TYPE);
+ break;
+ case PARSE_ERROR:
+ DBG1(DBG_IKE, "message parsing failed");
+ send_notify(this, msg, PAYLOAD_MALFORMED);
+ break;
+ case VERIFY_ERROR:
+ DBG1(DBG_IKE, "message verification failed");
+ send_notify(this, msg, PAYLOAD_MALFORMED);
+ break;
+ case FAILED:
+ DBG1(DBG_IKE, "integrity check failed");
+ send_notify(this, msg, INVALID_HASH_INFORMATION);
+ break;
+ case INVALID_STATE:
+ DBG1(DBG_IKE, "found encrypted message, but no keys available");
+ send_notify(this, msg, PAYLOAD_MALFORMED);
+ default:
+ break;
+ }
+ DBG1(DBG_IKE, "%N %s with message ID %u processing failed",
+ exchange_type_names, msg->get_exchange_type(msg),
+ msg->get_request(msg) ? "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)
+{
+ u_int32_t hash, mid, i;
+ host_t *me, *other;
+ status_t status;
+
+ /* TODO-IKEv1: update hosts more selectively */
+ me = msg->get_destination(msg);
+ other = msg->get_source(msg);
+ mid = msg->get_message_id(msg);
+ hash = chunk_hash(msg->get_packet_data(msg));
+ for (i = 0; i < MAX_OLD_HASHES; i++)
+ {
+ if (this->initiating.old_hashes[i] == hash)
+ {
+ if (this->initiating.packet &&
+ i == (this->initiating.old_hash_pos % MAX_OLD_HASHES) &&
+ (msg->get_exchange_type(msg) == QUICK_MODE ||
+ msg->get_exchange_type(msg) == AGGRESSIVE))
+ {
+ DBG1(DBG_IKE, "received retransmit of response with ID %u, "
+ "resending last request", mid);
+ charon->sender->send(charon->sender,
+ this->initiating.packet->clone(this->initiating.packet));
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "received retransmit of response with ID %u, "
+ "but next request already sent", mid);
+ return SUCCESS;
+ }
+ }
+
+ if ((mid && mid == this->initiating.mid) ||
+ (this->initiating.mid == 0 &&
+ msg->get_exchange_type(msg) == this->initiating.type &&
+ this->active_tasks->get_count(this->active_tasks)))
+ {
+ msg->set_request(msg, FALSE);
+ charon->bus->message(charon->bus, msg, TRUE, FALSE);
+ status = parse_message(this, msg);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND,
+ time_monotonic(NULL));
+ this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE);
+ charon->bus->message(charon->bus, msg, TRUE, TRUE);
+ if (process_response(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ this->initiating.old_hashes[(++this->initiating.old_hash_pos) %
+ MAX_OLD_HASHES] = hash;
+ }
+ else
+ {
+ if (hash == this->responding.hash)
+ {
+ if (this->responding.packet)
+ {
+ DBG1(DBG_IKE, "received retransmit of request with ID %u, "
+ "retransmitting response", mid);
+ charon->sender->send(charon->sender,
+ this->responding.packet->clone(this->responding.packet));
+ }
+ else if (this->initiating.packet &&
+ this->initiating.type == INFORMATIONAL_V1)
+ {
+ DBG1(DBG_IKE, "received retransmit of DPD request, "
+ "retransmitting response");
+ charon->sender->send(charon->sender,
+ this->initiating.packet->clone(this->initiating.packet));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received retransmit of request with ID %u, "
+ "but no response to retransmit", mid);
+ }
+ return SUCCESS;
+ }
+ if (msg->get_exchange_type(msg) == TRANSACTION &&
+ this->active_tasks->get_count(this->active_tasks))
+ { /* main mode not yet complete, queue XAuth/Mode config tasks */
+ if (this->queued)
+ {
+ DBG1(DBG_IKE, "ignoring additional %N request, queue full",
+ exchange_type_names, TRANSACTION);
+ return SUCCESS;
+ }
+ this->queued = message_create_from_packet(msg->get_packet(msg));
+ if (this->queued->parse_header(this->queued) != SUCCESS)
+ {
+ this->queued->destroy(this->queued);
+ this->queued = NULL;
+ return FAILED;
+ }
+ DBG1(DBG_IKE, "queueing %N request as tasks still active",
+ exchange_type_names, TRANSACTION);
+ return SUCCESS;
+ }
+
+ msg->set_request(msg, TRUE);
+ charon->bus->message(charon->bus, msg, TRUE, FALSE);
+ status = parse_message(this, msg);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ /* 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(this, msg, NO_PROPOSAL_CHOSEN);
+ 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,
+ "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
+ charon->name));
+ }
+ this->ike_sa->update_hosts(this->ike_sa, me, other, TRUE);
+ charon->bus->message(charon->bus, msg, TRUE, TRUE);
+ if (process_request(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ this->responding.hash = hash;
+ }
+ return SUCCESS;
+}
+
+METHOD(task_manager_t, queue_task, void,
+ private_task_manager_t *this, task_t *task)
+{
+ 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)
+{
+ peer_cfg_t *peer_cfg;
+
+ if (!has_queued(this, TASK_ISAKMP_VENDOR))
+ {
+ queue_task(this, (task_t*)isakmp_vendor_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_ISAKMP_CERT_PRE))
+ {
+ queue_task(this, (task_t*)isakmp_cert_pre_create(this->ike_sa, TRUE));
+ }
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer_cfg->use_aggressive(peer_cfg))
+ {
+ if (!has_queued(this, TASK_AGGRESSIVE_MODE))
+ {
+ queue_task(this, (task_t*)aggressive_mode_create(this->ike_sa, TRUE));
+ }
+ }
+ else
+ {
+ if (!has_queued(this, TASK_MAIN_MODE))
+ {
+ queue_task(this, (task_t*)main_mode_create(this->ike_sa, TRUE));
+ }
+ }
+ if (!has_queued(this, TASK_ISAKMP_CERT_POST))
+ {
+ queue_task(this, (task_t*)isakmp_cert_post_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_ISAKMP_NATD))
+ {
+ queue_task(this, (task_t*)isakmp_natd_create(this->ike_sa, TRUE));
+ }
+}
+
+METHOD(task_manager_t, queue_ike_reauth, void,
+ private_task_manager_t *this)
+{
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+ ike_sa_t *new;
+ host_t *host;
+
+ new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ this->ike_sa->get_version(this->ike_sa), TRUE);
+ if (!new)
+ { /* shouldn't happen */
+ return;
+ }
+
+ new->set_peer_cfg(new, this->ike_sa->get_peer_cfg(this->ike_sa));
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ new->set_other_host(new, host->clone(host));
+ host = this->ike_sa->get_my_host(this->ike_sa);
+ new->set_my_host(new, host->clone(host));
+ enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ new->add_virtual_ip(new, TRUE, host);
+ }
+ enumerator->destroy(enumerator);
+
+ enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
+ while (enumerator->enumerate(enumerator, &child_sa))
+ {
+ this->ike_sa->remove_child_sa(this->ike_sa, enumerator);
+ new->add_child_sa(new, child_sa);
+ }
+ enumerator->destroy(enumerator);
+
+ if (!new->get_child_count(new))
+ { /* check if a Quick Mode task is queued (UNITY_LOAD_BALANCE case) */
+ task_t *task;
+
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (task->get_type(task) == TASK_QUICK_MODE)
+ {
+ this->queued_tasks->remove_at(this->queued_tasks, enumerator);
+ task->migrate(task, new);
+ new->queue_task(new, task);
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME)
+ {
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+ }
+ else
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new);
+ DBG1(DBG_IKE, "reauthenticating IKE_SA failed");
+ }
+ charon->bus->set_sa(charon->bus, this->ike_sa);
+}
+
+METHOD(task_manager_t, queue_ike_rekey, void,
+ private_task_manager_t *this)
+{
+ queue_ike_reauth(this);
+}
+
+METHOD(task_manager_t, queue_ike_delete, void,
+ private_task_manager_t *this)
+{
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+
+ enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
+ while (enumerator->enumerate(enumerator, &child_sa))
+ {
+ queue_task(this, (task_t*)
+ quick_delete_create(this->ike_sa, child_sa->get_protocol(child_sa),
+ child_sa->get_spi(child_sa, TRUE), FALSE, FALSE));
+ }
+ enumerator->destroy(enumerator);
+
+ queue_task(this, (task_t*)isakmp_delete_create(this->ike_sa, TRUE));
+}
+
+METHOD(task_manager_t, queue_mobike, void,
+ private_task_manager_t *this, bool roam, bool address)
+{
+ /* Not supported in IKEv1 */
+}
+
+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)
+{
+ quick_mode_t *task;
+
+ task = quick_mode_create(this->ike_sa, cfg, tsi, tsr);
+ task->use_reqid(task, reqid);
+
+ queue_task(this, &task->task);
+}
+
+/**
+ * Check if two CHILD_SAs have the same traffic selector
+ */
+static bool have_equal_ts(child_sa_t *a, child_sa_t *b, bool local)
+{
+ linked_list_t *list;
+ traffic_selector_t *ts_a, *ts_b;
+
+ list = a->get_traffic_selectors(a, local);
+ if (list->get_first(list, (void**)&ts_a) == SUCCESS)
+ {
+ list = b->get_traffic_selectors(b, local);
+ if (list->get_first(list, (void**)&ts_b) == SUCCESS)
+ {
+ return ts_a->equals(ts_a, ts_b);
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Check if a CHILD_SA is redundant and we should delete instead of rekey
+ */
+static bool is_redundant(private_task_manager_t *this, child_sa_t *child_sa)
+{
+ enumerator_t *enumerator;
+ child_sa_t *current;
+ bool redundant = FALSE;
+
+ enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (current->get_state(current) == CHILD_INSTALLED &&
+ streq(current->get_name(current), child_sa->get_name(child_sa)) &&
+ have_equal_ts(current, child_sa, TRUE) &&
+ have_equal_ts(current, child_sa, FALSE) &&
+ current->get_lifetime(current, FALSE) >
+ child_sa->get_lifetime(child_sa, FALSE))
+ {
+ DBG1(DBG_IKE, "deleting redundant CHILD_SA %s{%d}",
+ child_sa->get_name(child_sa), child_sa->get_reqid(child_sa));
+ redundant = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return redundant;
+}
+
+/**
+ * Get the first traffic selector of a CHILD_SA, local or remote
+ */
+static traffic_selector_t* get_first_ts(child_sa_t *child_sa, bool local)
+{
+ traffic_selector_t *ts = NULL;
+ linked_list_t *list;
+
+ list = child_sa->get_traffic_selectors(child_sa, local);
+ if (list->get_first(list, (void**)&ts) == SUCCESS)
+ {
+ return ts;
+ }
+ return NULL;
+}
+
+METHOD(task_manager_t, queue_child_rekey, void,
+ private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi)
+{
+ child_sa_t *child_sa;
+ child_cfg_t *cfg;
+ quick_mode_t *task;
+
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, TRUE);
+ if (!child_sa)
+ {
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE);
+ }
+ if (child_sa && child_sa->get_state(child_sa) == CHILD_INSTALLED)
+ {
+ if (is_redundant(this, child_sa))
+ {
+ queue_task(this, (task_t*)quick_delete_create(this->ike_sa,
+ protocol, spi, FALSE, FALSE));
+ }
+ else
+ {
+ child_sa->set_state(child_sa, CHILD_REKEYING);
+ cfg = child_sa->get_config(child_sa);
+ task = quick_mode_create(this->ike_sa, cfg->get_ref(cfg),
+ get_first_ts(child_sa, TRUE), get_first_ts(child_sa, FALSE));
+ task->use_reqid(task, child_sa->get_reqid(child_sa));
+ task->rekey(task, child_sa->get_spi(child_sa, TRUE));
+
+ queue_task(this, &task->task);
+ }
+ }
+}
+
+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*)quick_delete_create(this->ike_sa, protocol,
+ spi, FALSE, expired));
+}
+
+METHOD(task_manager_t, queue_dpd, void,
+ private_task_manager_t *this)
+{
+ peer_cfg_t *peer_cfg;
+ u_int32_t t, retransmit;
+
+ queue_task(this, (task_t*)isakmp_dpd_create(this->ike_sa, DPD_R_U_THERE,
+ this->dpd_send++));
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+
+ /* compute timeout in milliseconds */
+ t = 1000 * peer_cfg->get_dpd_timeout(peer_cfg);
+ if (t == 0)
+ {
+ /* use the same timeout as a retransmitting IKE message would have */
+ for (retransmit = 0; retransmit <= this->retransmit_tries; retransmit++)
+ {
+ t += (u_int32_t)(this->retransmit_timeout * 1000.0 *
+ pow(this->retransmit_base, retransmit));
+ }
+ }
+
+ /* schedule DPD timeout job */
+ lib->scheduler->schedule_job_ms(lib->scheduler,
+ (job_t*)dpd_timeout_job_create(this->ike_sa->get_id(this->ike_sa)), t);
+}
+
+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, incr_mid, void,
+ private_task_manager_t *this, bool initiate)
+{
+}
+
+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->responding.seqnr = RESPONDING_SEQ;
+ this->responding.retransmitted = 0;
+ this->initiating.packet = NULL;
+ this->initiating.mid = 0;
+ this->initiating.seqnr = 0;
+ this->initiating.retransmitted = 0;
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ if (initiate != UINT_MAX)
+ {
+ this->dpd_send = initiate;
+ }
+ if (respond != UINT_MAX)
+ {
+ this->dpd_recv = respond;
+ }
+
+ /* 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);
+ }
+}
+
+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->queued);
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ DESTROY_IF(this->rng);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+task_manager_v1_t *task_manager_v1_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,
+ .flush_queue = _flush_queue,
+ .destroy = _destroy,
+ },
+ },
+ .initiating = {
+ .type = EXCHANGE_TYPE_UNDEFINED,
+ },
+ .responding = {
+ .seqnr = RESPONDING_SEQ,
+ },
+ .ike_sa = ike_sa,
+ .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
+ .queued_tasks = linked_list_create(),
+ .active_tasks = linked_list_create(),
+ .passive_tasks = linked_list_create(),
+ .retransmit_tries = lib->settings->get_int(lib->settings,
+ "%s.retransmit_tries", RETRANSMIT_TRIES, charon->name),
+ .retransmit_timeout = lib->settings->get_double(lib->settings,
+ "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, charon->name),
+ .retransmit_base = lib->settings->get_double(lib->settings,
+ "%s.retransmit_base", RETRANSMIT_BASE, charon->name),
+ );
+
+ if (!this->rng)
+ {
+ DBG1(DBG_IKE, "no RNG found, unable to create IKE_SA");
+ destroy(this);
+ return NULL;
+ }
+ if (!this->rng->get_bytes(this->rng, sizeof(this->dpd_send),
+ (void*)&this->dpd_send))
+ {
+ DBG1(DBG_IKE, "failed to allocate message ID, unable to create IKE_SA");
+ destroy(this);
+ return NULL;
+ }
+ this->dpd_send &= 0x7FFFFFFF;
+
+ return &this->public;
+}
+
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.h b/src/libcharon/sa/ikev1/task_manager_v1.h
new file mode 100644
index 000000000..61e409bbe
--- /dev/null
+++ b/src/libcharon/sa/ikev1/task_manager_v1.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 task_manager_v1 task_manager_v1
+ * @{ @ingroup ikev1
+ */
+
+#ifndef TASK_MANAGER_V1_H_
+#define TASK_MANAGER_V1_H_
+
+typedef struct task_manager_v1_t task_manager_v1_t;
+
+#include <sa/task_manager.h>
+
+/**
+ * Task manager, IKEv1 variant.
+ */
+struct task_manager_v1_t {
+
+ /**
+ * Implements task_manager_t.
+ */
+ task_manager_t task_manager;
+};
+
+/**
+ * Create an instance of the task manager.
+ *
+ * @param ike_sa IKE_SA to manage.
+ */
+task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa);
+
+#endif /** TASK_MANAGER_V1_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
new file mode 100644
index 000000000..954dea880
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * 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 "aggressive_mode.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <sa/ikev1/phase1.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/hash_payload.h>
+#include <sa/ikev1/tasks/xauth.h>
+#include <sa/ikev1/tasks/mode_config.h>
+#include <sa/ikev1/tasks/informational.h>
+#include <sa/ikev1/tasks/isakmp_delete.h>
+#include <processing/jobs/adopt_children_job.h>
+
+typedef struct private_aggressive_mode_t private_aggressive_mode_t;
+
+/**
+ * Private members of a aggressive_mode_t task.
+ */
+struct private_aggressive_mode_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ aggressive_mode_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Common phase 1 helper class
+ */
+ phase1_t *ph1;
+
+ /**
+ * IKE config to establish
+ */
+ ike_cfg_t *ike_cfg;
+
+ /**
+ * Peer config to use
+ */
+ peer_cfg_t *peer_cfg;
+
+ /**
+ * selected IKE proposal
+ */
+ proposal_t *proposal;
+
+ /**
+ * Negotiated SA lifetime
+ */
+ u_int32_t lifetime;
+
+ /**
+ * Negotiated authentication method
+ */
+ auth_method_t method;
+
+ /**
+ * Encoded ID payload, without fixed header
+ */
+ chunk_t id_data;
+
+ /** states of aggressive mode */
+ enum {
+ AM_INIT,
+ AM_AUTH,
+ } state;
+};
+
+/**
+ * Set IKE_SA to established state
+ */
+static bool establish(private_aggressive_mode_t *this)
+{
+ if (!charon->bus->authorize(charon->bus, TRUE))
+ {
+ DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
+ return FALSE;
+ }
+
+ 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 TRUE;
+}
+
+/**
+ * Check for notify errors, return TRUE if error found
+ */
+static bool has_notify_errors(private_aggressive_mode_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ bool err = FALSE;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == NOTIFY_V1)
+ {
+ notify_payload_t *notify;
+ notify_type_t type;
+
+ notify = (notify_payload_t*)payload;
+ type = notify->get_notify_type(notify);
+ if (type < 16384)
+ {
+ DBG1(DBG_IKE, "received %N error notify",
+ notify_type_names, type);
+ err = TRUE;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N notify", notify_type_names, type);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return err;
+}
+
+/**
+ * Queue a task sending a notify in an INFORMATIONAL exchange
+ */
+static status_t send_notify(private_aggressive_mode_t *this, notify_type_t type)
+{
+ notify_payload_t *notify;
+ ike_sa_id_t *ike_sa_id;
+ u_int64_t spi_i, spi_r;
+ chunk_t spi;
+
+ notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
+ PROTO_IKE, type);
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ spi = chunk_cata("cc", chunk_from_thing(spi_i), chunk_from_thing(spi_r));
+ notify->set_spi_data(notify, spi);
+
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)informational_create(this->ike_sa, notify));
+ /* cancel all active/passive tasks in favour of informational */
+ this->ike_sa->flush_queue(this->ike_sa,
+ this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE);
+ return ALREADY_DONE;
+}
+
+/**
+ * Queue a delete task if authentication failed as initiator
+ */
+static status_t send_delete(private_aggressive_mode_t *this)
+{
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)isakmp_delete_create(this->ike_sa, TRUE));
+ /* cancel all active tasks in favour of informational */
+ this->ike_sa->flush_queue(this->ike_sa,
+ this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE);
+ return ALREADY_DONE;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_aggressive_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case AM_INIT:
+ {
+ sa_payload_t *sa_payload;
+ id_payload_t *id_payload;
+ linked_list_t *proposals;
+ identification_t *id;
+ packet_t *packet;
+ u_int16_t group;
+
+ DBG0(DBG_IKE, "initiating Aggressive Mode 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);
+
+ this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ this->peer_cfg->get_ref(this->peer_cfg);
+
+ this->method = this->ph1->get_auth_method(this->ph1, this->peer_cfg);
+ if (this->method == AUTH_NONE)
+ {
+ DBG1(DBG_CFG, "configuration uses unsupported authentication");
+ return FAILED;
+ }
+ this->lifetime = this->peer_cfg->get_reauth_time(this->peer_cfg,
+ FALSE);
+ if (!this->lifetime)
+ { /* fall back to rekey time of no rekey time configured */
+ this->lifetime = this->peer_cfg->get_rekey_time(this->peer_cfg,
+ FALSE);
+ }
+ this->lifetime += this->peer_cfg->get_over_time(this->peer_cfg);
+ proposals = this->ike_cfg->get_proposals(this->ike_cfg);
+ sa_payload = sa_payload_create_from_proposals_v1(proposals,
+ this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
+
+ message->add_payload(message, &sa_payload->payload_interface);
+
+ group = this->ike_cfg->get_dh_group(this->ike_cfg);
+ if (group == MODP_NONE)
+ {
+ DBG1(DBG_IKE, "DH group selection failed");
+ return FAILED;
+ }
+ if (!this->ph1->create_dh(this->ph1, group))
+ {
+ DBG1(DBG_IKE, "DH group %N not supported",
+ diffie_hellman_group_names, group);
+ return FAILED;
+ }
+ if (!this->ph1->add_nonce_ke(this->ph1, message))
+ {
+ return FAILED;
+ }
+ id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
+ if (!id)
+ {
+ DBG1(DBG_CFG, "own identity not known");
+ return FAILED;
+ }
+ this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
+ id_payload = id_payload_create_from_identification(ID_V1, id);
+ this->id_data = id_payload->get_encoded(id_payload);
+ message->add_payload(message, &id_payload->payload_interface);
+
+ /* pregenerate message to store SA payload */
+ if (this->ike_sa->generate_message(this->ike_sa, message,
+ &packet) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "pregenerating SA payload failed");
+ return FAILED;
+ }
+ packet->destroy(packet);
+ if (!this->ph1->save_sa_payload(this->ph1, message))
+ {
+ DBG1(DBG_IKE, "SA payload invalid");
+ return FAILED;
+ }
+ this->state = AM_AUTH;
+ return NEED_MORE;
+ }
+ case AM_AUTH:
+ {
+ if (!this->ph1->build_auth(this->ph1, this->method, message,
+ this->id_data))
+ {
+ this->id_data = chunk_empty;
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ this->id_data = chunk_empty;
+
+ switch (this->method)
+ {
+ case AUTH_XAUTH_INIT_PSK:
+ case AUTH_XAUTH_INIT_RSA:
+ case AUTH_HYBRID_INIT_RSA:
+ /* wait for XAUTH request */
+ break;
+ case AUTH_XAUTH_RESP_PSK:
+ case AUTH_XAUTH_RESP_RSA:
+ case AUTH_HYBRID_RESP_RSA:
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)xauth_create(this->ike_sa, TRUE));
+ return SUCCESS;
+ default:
+ if (charon->ike_sa_manager->check_uniqueness(
+ charon->ike_sa_manager, this->ike_sa, FALSE))
+ {
+ DBG1(DBG_IKE, "cancelling Aggressive Mode due to "
+ "uniqueness policy");
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ if (!establish(this))
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ break;
+ }
+ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE));
+ }
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_r, status_t,
+ private_aggressive_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case AM_INIT:
+ {
+ sa_payload_t *sa_payload;
+ id_payload_t *id_payload;
+ identification_t *id;
+ linked_list_t *list;
+ u_int16_t group;
+
+ this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ DBG0(DBG_IKE, "%H is initiating a Aggressive Mode IKE_SA",
+ message->get_source(message));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ this->ike_sa->update_hosts(this->ike_sa,
+ message->get_destination(message),
+ message->get_source(message), TRUE);
+
+ sa_payload = (sa_payload_t*)message->get_payload(message,
+ SECURITY_ASSOCIATION_V1);
+ if (!sa_payload)
+ {
+ DBG1(DBG_IKE, "SA payload missing");
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ if (!this->ph1->save_sa_payload(this->ph1, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+
+ list = sa_payload->get_proposals(sa_payload);
+ this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
+ list, FALSE);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+ if (!this->proposal)
+ {
+ DBG1(DBG_IKE, "no proposal found");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->ike_sa->set_proposal(this->ike_sa, this->proposal);
+
+ this->method = sa_payload->get_auth_method(sa_payload);
+ this->lifetime = sa_payload->get_lifetime(sa_payload);
+
+ switch (this->method)
+ {
+ case AUTH_XAUTH_INIT_PSK:
+ case AUTH_XAUTH_RESP_PSK:
+ case AUTH_PSK:
+ if (!lib->settings->get_bool(lib->settings, "%s.i_dont_care"
+ "_about_security_and_use_aggressive_mode_psk",
+ FALSE, charon->name))
+ {
+ DBG1(DBG_IKE, "Aggressive Mode PSK disabled for "
+ "security reasons");
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!this->proposal->get_algorithm(this->proposal,
+ DIFFIE_HELLMAN_GROUP, &group, NULL))
+ {
+ DBG1(DBG_IKE, "DH group selection failed");
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->create_dh(this->ph1, group))
+ {
+ DBG1(DBG_IKE, "negotiated DH group not supported");
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->get_nonce_ke(this->ph1, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+
+ id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
+ if (!id_payload)
+ {
+ DBG1(DBG_IKE, "IDii payload missing");
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+
+ id = id_payload->get_identification(id_payload);
+ this->id_data = id_payload->get_encoded(id_payload);
+ this->ike_sa->set_other_id(this->ike_sa, id);
+ this->peer_cfg = this->ph1->select_config(this->ph1,
+ this->method, TRUE, id);
+ if (!this->peer_cfg)
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
+
+ this->state = AM_AUTH;
+ if (has_notify_errors(this, message))
+ {
+ return FAILED;
+ }
+ return NEED_MORE;
+ }
+ case AM_AUTH:
+ {
+ while (TRUE)
+ {
+ if (this->ph1->verify_auth(this->ph1, this->method, message,
+ this->id_data))
+ {
+ break;
+ }
+ this->peer_cfg->destroy(this->peer_cfg);
+ this->peer_cfg = this->ph1->select_config(this->ph1,
+ this->method, TRUE, NULL);
+ if (!this->peer_cfg)
+ {
+ this->id_data = chunk_empty;
+ return send_delete(this);
+ }
+ this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
+ }
+ this->id_data = chunk_empty;
+
+ if (!charon->bus->authorize(charon->bus, FALSE))
+ {
+ DBG1(DBG_IKE, "Aggressive Mode authorization hook forbids "
+ "IKE_SA, cancelling");
+ return send_delete(this);
+ }
+
+ switch (this->method)
+ {
+ case AUTH_XAUTH_INIT_PSK:
+ case AUTH_XAUTH_INIT_RSA:
+ case AUTH_HYBRID_INIT_RSA:
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)xauth_create(this->ike_sa, TRUE));
+ return SUCCESS;
+ case AUTH_XAUTH_RESP_PSK:
+ case AUTH_XAUTH_RESP_RSA:
+ case AUTH_HYBRID_RESP_RSA:
+ /* wait for XAUTH request */
+ break;
+ default:
+ if (charon->ike_sa_manager->check_uniqueness(
+ charon->ike_sa_manager, this->ike_sa, FALSE))
+ {
+ DBG1(DBG_IKE, "cancelling Aggressive Mode due to "
+ "uniqueness policy");
+ return send_delete(this);
+ }
+ if (!establish(this))
+ {
+ return send_delete(this);
+ }
+ lib->processor->queue_job(lib->processor, (job_t*)
+ adopt_children_job_create(
+ this->ike_sa->get_id(this->ike_sa)));
+ break;
+ }
+ if (!this->ph1->has_pool(this->ph1, this->peer_cfg) &&
+ this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE));
+ }
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, build_r, status_t,
+ private_aggressive_mode_t *this, message_t *message)
+{
+ if (this->state == AM_AUTH)
+ {
+ sa_payload_t *sa_payload;
+ id_payload_t *id_payload;
+ identification_t *id;
+
+ sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
+ this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ message->add_payload(message, &sa_payload->payload_interface);
+
+ if (!this->ph1->add_nonce_ke(this->ph1, message))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->create_hasher(this->ph1))
+ {
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ if (!this->ph1->derive_keys(this->ph1, this->peer_cfg, this->method))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+
+ id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
+ if (!id)
+ {
+ DBG1(DBG_CFG, "own identity not known");
+ return send_notify(this, INVALID_ID_INFORMATION);
+ }
+ this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
+
+ id_payload = id_payload_create_from_identification(ID_V1, id);
+ message->add_payload(message, &id_payload->payload_interface);
+
+ if (!this->ph1->build_auth(this->ph1, this->method, message,
+ id_payload->get_encoded(id_payload)))
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ return NEED_MORE;
+ }
+ return FAILED;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_aggressive_mode_t *this, message_t *message)
+{
+ if (this->state == AM_AUTH)
+ {
+ auth_method_t method;
+ sa_payload_t *sa_payload;
+ id_payload_t *id_payload;
+ identification_t *id, *cid;
+ linked_list_t *list;
+ u_int32_t lifetime;
+
+ sa_payload = (sa_payload_t*)message->get_payload(message,
+ SECURITY_ASSOCIATION_V1);
+ if (!sa_payload)
+ {
+ DBG1(DBG_IKE, "SA payload missing");
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ list = sa_payload->get_proposals(sa_payload);
+ this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
+ list, FALSE);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+ if (!this->proposal)
+ {
+ DBG1(DBG_IKE, "no proposal found");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->ike_sa->set_proposal(this->ike_sa, this->proposal);
+
+ lifetime = sa_payload->get_lifetime(sa_payload);
+ if (lifetime != this->lifetime)
+ {
+ DBG1(DBG_IKE, "received lifetime %us does not match configured "
+ "lifetime %us", lifetime, this->lifetime);
+ }
+ this->lifetime = lifetime;
+ method = sa_payload->get_auth_method(sa_payload);
+ if (method != this->method)
+ {
+ DBG1(DBG_IKE, "received %N authentication, but configured %N, "
+ "continue with configured", auth_method_names, method,
+ auth_method_names, this->method);
+ }
+ if (!this->ph1->get_nonce_ke(this->ph1, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ if (!this->ph1->create_hasher(this->ph1))
+ {
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+
+ id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
+ if (!id_payload)
+ {
+ DBG1(DBG_IKE, "IDir payload missing");
+ return send_delete(this);
+ }
+ id = id_payload->get_identification(id_payload);
+ cid = this->ph1->get_id(this->ph1, this->peer_cfg, FALSE);
+ if (cid && !id->matches(id, cid))
+ {
+ DBG1(DBG_IKE, "IDir '%Y' does not match to '%Y'", id, cid);
+ id->destroy(id);
+ return send_notify(this, INVALID_ID_INFORMATION);
+ }
+ this->ike_sa->set_other_id(this->ike_sa, id);
+
+ if (!this->ph1->derive_keys(this->ph1, this->peer_cfg, this->method))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->verify_auth(this->ph1, this->method, message,
+ id_payload->get_encoded(id_payload)))
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ if (!charon->bus->authorize(charon->bus, FALSE))
+ {
+ DBG1(DBG_IKE, "Aggressive Mode authorization hook forbids IKE_SA, "
+ "cancelling");
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+
+ return NEED_MORE;
+ }
+ return FAILED;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_aggressive_mode_t *this)
+{
+ return TASK_AGGRESSIVE_MODE;
+}
+
+METHOD(task_t, migrate, void,
+ private_aggressive_mode_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->peer_cfg);
+ DESTROY_IF(this->proposal);
+ this->ph1->destroy(this->ph1);
+ chunk_free(&this->id_data);
+
+ this->ike_sa = ike_sa;
+ this->state = AM_INIT;
+ this->peer_cfg = NULL;
+ this->proposal = NULL;
+ this->ph1 = phase1_create(ike_sa, this->initiator);
+}
+
+METHOD(task_t, destroy, void,
+ private_aggressive_mode_t *this)
+{
+ DESTROY_IF(this->peer_cfg);
+ DESTROY_IF(this->proposal);
+ this->ph1->destroy(this->ph1);
+ chunk_free(&this->id_data);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+aggressive_mode_t *aggressive_mode_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_aggressive_mode_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .ph1 = phase1_create(ike_sa, initiator),
+ .initiator = initiator,
+ .state = AM_INIT,
+ );
+
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.h b/src/libcharon/sa/ikev1/tasks/aggressive_mode.h
new file mode 100644
index 000000000..d0666f41c
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * 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 aggressive_mode aggressive_mode
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef AGGRESSIVE_MODE_H_
+#define AGGRESSIVE_MODE_H_
+
+typedef struct aggressive_mode_t aggressive_mode_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * IKEv1 aggressive mode, establishes an IKE_SA without identity protection.
+ */
+struct aggressive_mode_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new AGGRESSIVE_MODE task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if task initiated locally
+ * @return task to handle by the task_manager
+ */
+aggressive_mode_t *aggressive_mode_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** AGGRESSIVE_MODE_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/informational.c b/src/libcharon/sa/ikev1/tasks/informational.c
new file mode 100644
index 000000000..bda1d2afb
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/informational.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "informational.h"
+
+#include <daemon.h>
+#include <sa/ikev1/tasks/isakmp_delete.h>
+#include <sa/ikev1/tasks/quick_delete.h>
+
+#include <encoding/payloads/delete_payload.h>
+
+typedef struct private_informational_t private_informational_t;
+
+/**
+ * Private members of a informational_t task.
+ */
+struct private_informational_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ informational_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Notify payload to send
+ */
+ notify_payload_t *notify;
+
+ /**
+ * Delete subtask
+ */
+ task_t *del;
+};
+
+/**
+ * Cancel active quick mode after receiving an error
+ */
+static void cancel_quick_mode(private_informational_t *this)
+{
+ enumerator_t *enumerator;
+ task_t *task;
+
+ enumerator = this->ike_sa->create_task_enumerator(this->ike_sa,
+ TASK_QUEUE_ACTIVE);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (task->get_type(task) == TASK_QUICK_MODE)
+ {
+ this->ike_sa->flush_queue(this->ike_sa, TASK_QUEUE_ACTIVE);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+METHOD(task_t, build_i, status_t,
+ private_informational_t *this, message_t *message)
+{
+ message->add_payload(message, &this->notify->payload_interface);
+ this->notify = NULL;
+ return SUCCESS;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_informational_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ delete_payload_t *delete;
+ notify_payload_t *notify;
+ notify_type_t type;
+ payload_t *payload;
+ status_t status = SUCCESS;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case NOTIFY_V1:
+ notify = (notify_payload_t*)payload;
+ type = notify->get_notify_type(notify);
+
+ if (type == INITIAL_CONTACT_IKEV1)
+ {
+ this->ike_sa->set_condition(this->ike_sa,
+ COND_INIT_CONTACT_SEEN, TRUE);
+ }
+ else if (type == UNITY_LOAD_BALANCE)
+ {
+ host_t *redirect, *me;
+ chunk_t data;
+
+ data = notify->get_notification_data(notify);
+ redirect = host_create_from_chunk(AF_INET, data,
+ IKEV2_UDP_PORT);
+ if (redirect)
+ { /* treat the redirect as reauthentication */
+ DBG1(DBG_IKE, "received %N notify. redirected to %H",
+ notify_type_names, type, redirect);
+ /* Cisco boxes reject the first message from 4500 */
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ me->set_port(me, charon->socket->get_port(
+ charon->socket, FALSE));
+ this->ike_sa->set_other_host(this->ike_sa, redirect);
+ this->ike_sa->reauth(this->ike_sa);
+ enumerator->destroy(enumerator);
+ return DESTROY_ME;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N notify, invalid address");
+ }
+ }
+ else if (type < 16384)
+ {
+ DBG1(DBG_IKE, "received %N error notify",
+ notify_type_names, type);
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING)
+ { /* only critical during main mode */
+ status = FAILED;
+ }
+ switch (type)
+ {
+ case INVALID_ID_INFORMATION:
+ case NO_PROPOSAL_CHOSEN:
+ cancel_quick_mode(this);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N notify",
+ notify_type_names, type);
+ }
+ continue;
+ case DELETE_V1:
+ if (!this->del)
+ {
+ delete = (delete_payload_t*)payload;
+ if (delete->get_protocol_id(delete) == PROTO_IKE)
+ {
+ this->del = (task_t*)isakmp_delete_create(this->ike_sa,
+ FALSE);
+ }
+ else
+ {
+ this->del = (task_t*)quick_delete_create(this->ike_sa,
+ PROTO_NONE, 0, FALSE, FALSE);
+ }
+ }
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ enumerator->destroy(enumerator);
+
+ if (this->del && status == SUCCESS)
+ {
+ return this->del->process(this->del, message);
+ }
+ return status;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_informational_t *this, message_t *message)
+{
+ if (this->del)
+ {
+ return this->del->build(this->del, message);
+ }
+ return FAILED;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_informational_t *this, message_t *message)
+{
+ return FAILED;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_informational_t *this)
+{
+ return TASK_INFORMATIONAL;
+}
+
+METHOD(task_t, migrate, void,
+ private_informational_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+ private_informational_t *this)
+{
+ DESTROY_IF(this->notify);
+ DESTROY_IF(this->del);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify)
+{
+ private_informational_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .notify = notify,
+ );
+
+ if (notify)
+ {
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/informational.h b/src/libcharon/sa/ikev1/tasks/informational.h
new file mode 100644
index 000000000..52938ffbc
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/informational.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 informational informational
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef INFORMATIONAL_H_
+#define INFORMATIONAL_H_
+
+typedef struct informational_t informational_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+#include <encoding/payloads/notify_payload.h>
+
+/**
+ * IKEv1 informational exchange, negotiates errors.
+ */
+struct informational_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new informational task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param notify notify to send as initiator, NULL if responder
+ * @return task to handle by the task_manager
+ */
+informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify);
+
+#endif /** INFORMATIONAL_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c
new file mode 100644
index 000000000..edad3b2fa
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "isakmp_cert_post.h"
+
+#include <daemon.h>
+#include <sa/ike_sa.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <credentials/certificates/x509.h>
+
+
+typedef struct private_isakmp_cert_post_t private_isakmp_cert_post_t;
+
+/**
+ * Private members of a isakmp_cert_post_t task.
+ */
+struct private_isakmp_cert_post_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ isakmp_cert_post_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * States of ike cert pre
+ */
+ enum {
+ CR_SA,
+ CR_KE,
+ CR_AUTH,
+ } state;
+};
+
+/**
+ * Check if we actually use certificates for authentication
+ */
+static bool use_certs(private_isakmp_cert_post_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ bool use = FALSE;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == SECURITY_ASSOCIATION_V1)
+ {
+ sa_payload_t *sa_payload = (sa_payload_t*)payload;
+
+ switch (sa_payload->get_auth_method(sa_payload))
+ {
+ case AUTH_RSA:
+ case AUTH_ECDSA_256:
+ case AUTH_ECDSA_384:
+ case AUTH_ECDSA_521:
+ case AUTH_XAUTH_INIT_RSA:
+ case AUTH_XAUTH_RESP_RSA:
+ case AUTH_HYBRID_INIT_RSA:
+ case AUTH_HYBRID_RESP_RSA:
+ use = TRUE;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return use;
+}
+
+/**
+ * Add certificates to message
+ */
+static void build_certs(private_isakmp_cert_post_t *this, message_t *message)
+{
+ peer_cfg_t *peer_cfg;
+
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (!peer_cfg)
+ {
+ return;
+ }
+
+ switch (peer_cfg->get_cert_policy(peer_cfg))
+ {
+ case CERT_NEVER_SEND:
+ break;
+ case CERT_SEND_IF_ASKED:
+ if (!this->ike_sa->has_condition(this->ike_sa, COND_CERTREQ_SEEN))
+ {
+ break;
+ }
+ /* FALL */
+ case CERT_ALWAYS_SEND:
+ {
+ cert_payload_t *payload;
+ enumerator_t *enumerator;
+ certificate_t *cert;
+ auth_rule_t type;
+ auth_cfg_t *auth;
+
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+ cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
+ if (!cert)
+ {
+ break;
+ }
+ payload = cert_payload_create_from_cert(CERTIFICATE_V1, cert);
+ if (!payload)
+ {
+ break;
+ }
+ DBG1(DBG_IKE, "sending end entity cert \"%Y\"",
+ cert->get_subject(cert));
+ message->add_payload(message, (payload_t*)payload);
+
+ enumerator = auth->create_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &type, &cert))
+ {
+ if (type == AUTH_RULE_IM_CERT)
+ {
+ payload = cert_payload_create_from_cert(CERTIFICATE_V1, cert);
+ if (payload)
+ {
+ DBG1(DBG_IKE, "sending issuer cert \"%Y\"",
+ cert->get_subject(cert));
+ message->add_payload(message, (payload_t*)payload);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ }
+}
+
+METHOD(task_t, build_i, status_t,
+ private_isakmp_cert_post_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ if (this->state == CR_AUTH)
+ {
+ build_certs(this, message);
+ return SUCCESS;
+ }
+ return NEED_MORE;
+ case AGGRESSIVE:
+ if (this->state == CR_AUTH)
+ {
+ build_certs(this, message);
+ return SUCCESS;
+ }
+ return NEED_MORE;
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_r, status_t,
+ private_isakmp_cert_post_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ {
+ switch (this->state)
+ {
+ case CR_SA:
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ return NEED_MORE;
+ case CR_KE:
+ return NEED_MORE;
+ case CR_AUTH:
+ return NEED_MORE;
+ default:
+ return FAILED;
+ }
+ }
+ case AGGRESSIVE:
+ {
+ switch (this->state)
+ {
+ case CR_SA:
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ return NEED_MORE;
+ case CR_AUTH:
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, build_r, status_t,
+ private_isakmp_cert_post_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ switch (this->state)
+ {
+ case CR_SA:
+ this->state = CR_KE;
+ return NEED_MORE;
+ case CR_KE:
+ this->state = CR_AUTH;
+ return NEED_MORE;
+ case CR_AUTH:
+ build_certs(this, message);
+ return SUCCESS;
+ }
+ case AGGRESSIVE:
+ switch (this->state)
+ {
+ case CR_SA:
+ build_certs(this, message);
+ this->state = CR_AUTH;
+ return NEED_MORE;
+ case CR_AUTH:
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_i, status_t,
+ private_isakmp_cert_post_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ {
+ switch (this->state)
+ {
+ case CR_SA:
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ this->state = CR_KE;
+ return NEED_MORE;
+ case CR_KE:
+ this->state = CR_AUTH;
+ return NEED_MORE;
+ case CR_AUTH:
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ break;
+ }
+ case AGGRESSIVE:
+ {
+ if (this->state == CR_SA)
+ {
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ this->state = CR_AUTH;
+ return NEED_MORE;
+ }
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_isakmp_cert_post_t *this)
+{
+ return TASK_ISAKMP_CERT_POST;
+}
+
+METHOD(task_t, migrate, void,
+ private_isakmp_cert_post_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->state = CR_SA;
+}
+
+METHOD(task_t, destroy, void,
+ private_isakmp_cert_post_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+isakmp_cert_post_t *isakmp_cert_post_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_isakmp_cert_post_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .initiator = initiator,
+ .state = CR_SA,
+ );
+ if (initiator)
+ {
+ this->public.task.process = _process_i;
+ this->public.task.build = _build_i;
+ }
+ else
+ {
+ this->public.task.process = _process_r;
+ this->public.task.build = _build_r;
+ }
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.h b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.h
new file mode 100644
index 000000000..3a155cb68
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 isakmp_cert_post isakmp_cert_post
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef ISAKMP_CERT_POST_H_
+#define ISAKMP_CERT_POST_H_
+
+typedef struct isakmp_cert_post_t isakmp_cert_post_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * ISAKMP_CERT_POST, IKEv1 certificate processing after authentication.
+ */
+struct isakmp_cert_post_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new isakmp_cert_post task.
+ *
+ * The initiator parameter means the original initiator, not the initiator
+ * of the certificate request.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if task is the original initiator
+ * @return isakmp_cert_post task to handle by the task_manager
+ */
+isakmp_cert_post_t *isakmp_cert_post_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** ISAKMP_CERT_POST_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c
new file mode 100644
index 000000000..d48484f09
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "isakmp_cert_pre.h"
+
+#include <daemon.h>
+#include <sa/ike_sa.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+#include <credentials/certificates/x509.h>
+
+
+typedef struct private_isakmp_cert_pre_t private_isakmp_cert_pre_t;
+
+/**
+ * Private members of a isakmp_cert_pre_t task.
+ */
+struct private_isakmp_cert_pre_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ isakmp_cert_pre_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Send certificate requests?
+ */
+ bool send_req;
+
+ /** next message we expect */
+ enum {
+ CR_SA,
+ CR_KE,
+ CR_AUTH,
+ } state;
+};
+
+/**
+ * Find the CA certificate for a given certreq payload
+ */
+static certificate_t* find_certificate(private_isakmp_cert_pre_t *this,
+ certreq_payload_t *certreq)
+{
+ identification_t *id;
+ certificate_t *cert;
+
+ if (certreq->get_cert_type(certreq) != CERT_X509)
+ {
+ DBG1(DBG_IKE, "%N CERTREQ not supported - ignored",
+ certificate_type_names, certreq->get_cert_type(certreq));
+ return NULL;
+ }
+ id = certreq->get_dn(certreq);
+ if (!id)
+ {
+ DBG1(DBG_IKE, "ignoring certificate request without data",
+ certificate_type_names, certreq->get_cert_type(certreq));
+ return NULL;
+ }
+ cert = lib->credmgr->get_cert(lib->credmgr, CERT_X509, KEY_ANY, id, TRUE);
+ if (cert)
+ {
+ DBG1(DBG_IKE, "received cert request for '%Y'",
+ cert->get_subject(cert));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received cert request for unknown ca '%Y'", id);
+ }
+ id->destroy(id);
+
+ return cert;
+}
+
+/**
+ * read certificate requests
+ */
+static void process_certreqs(private_isakmp_cert_pre_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ auth_cfg_t *auth;
+
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case CERTIFICATE_REQUEST_V1:
+ {
+ certificate_t *cert;
+
+ this->ike_sa->set_condition(this->ike_sa,
+ COND_CERTREQ_SEEN, TRUE);
+ cert = find_certificate(this, (certreq_payload_t*)payload);
+ if (cert)
+ {
+ auth->add(auth, AUTH_RULE_CA_CERT, cert);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Import receuved certificates
+ */
+static void process_certs(private_isakmp_cert_pre_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ auth_cfg_t *auth;
+ bool first = TRUE;
+
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == CERTIFICATE_V1)
+ {
+ cert_payload_t *cert_payload;
+ cert_encoding_t encoding;
+ certificate_t *cert;
+
+ cert_payload = (cert_payload_t*)payload;
+ encoding = cert_payload->get_cert_encoding(cert_payload);
+
+ switch (encoding)
+ {
+ case ENC_X509_SIGNATURE:
+ {
+ cert = cert_payload->get_cert(cert_payload);
+ if (cert)
+ {
+ if (first)
+ { /* the first is an end entity certificate */
+ DBG1(DBG_IKE, "received end entity cert \"%Y\"",
+ cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert);
+ first = FALSE;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received issuer cert \"%Y\"",
+ cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_IM_CERT, cert);
+ }
+ }
+ break;
+ }
+ case ENC_CRL:
+ cert = cert_payload->get_cert(cert_payload);
+ if (cert)
+ {
+ DBG1(DBG_IKE, "received CRL \"%Y\"",
+ cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert);
+ }
+ break;
+ case ENC_PKCS7_WRAPPED_X509:
+ case ENC_PGP:
+ case ENC_DNS_SIGNED_KEY:
+ case ENC_KERBEROS_TOKEN:
+ case ENC_ARL:
+ case ENC_SPKI:
+ case ENC_X509_ATTRIBUTE:
+ case ENC_RAW_RSA_KEY:
+ case ENC_X509_HASH_AND_URL_BUNDLE:
+ case ENC_OCSP_CONTENT:
+ default:
+ DBG1(DBG_ENC, "certificate encoding %N not supported",
+ cert_encoding_names, encoding);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Add the subject of a CA certificate a message
+ */
+static void add_certreq(private_isakmp_cert_pre_t *this, message_t *message,
+ certificate_t *cert)
+{
+ if (cert->get_type(cert) == CERT_X509)
+ {
+ x509_t *x509 = (x509_t*)cert;
+
+ if (x509->get_flags(x509) & X509_CA)
+ {
+ DBG1(DBG_IKE, "sending cert request for \"%Y\"",
+ cert->get_subject(cert));
+ message->add_payload(message, (payload_t*)
+ certreq_payload_create_dn(cert->get_subject(cert)));
+ }
+ }
+}
+
+/**
+ * Add auth_cfg's CA certificates to the certificate request
+ */
+static void add_certreqs(private_isakmp_cert_pre_t *this,
+ auth_cfg_t *auth, message_t *message)
+{
+ enumerator_t *enumerator;
+ auth_rule_t type;
+ void *value;
+
+ enumerator = auth->create_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ switch (type)
+ {
+ case AUTH_RULE_CA_CERT:
+ add_certreq(this, message, (certificate_t*)value);
+ break;
+ default:
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Build certificate requests
+ */
+static void build_certreqs(private_isakmp_cert_pre_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ ike_cfg_t *ike_cfg;
+ peer_cfg_t *peer_cfg;
+ certificate_t *cert;
+ auth_cfg_t *auth;
+
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ if (!ike_cfg->send_certreq(ike_cfg))
+ {
+ return;
+ }
+ /* check if we require a specific CA for that peer */
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer_cfg)
+ {
+ enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
+ if (enumerator->enumerate(enumerator, &auth))
+ {
+ add_certreqs(this, auth, message);
+ }
+ enumerator->destroy(enumerator);
+ }
+ if (!message->get_payload(message, CERTIFICATE_REQUEST_V1))
+ {
+ /* otherwise add all trusted CA certificates */
+ enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
+ CERT_ANY, KEY_ANY, NULL, TRUE);
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ add_certreq(this, message, cert);
+ }
+ enumerator->destroy(enumerator);
+ }
+}
+
+/**
+ * Check if we actually use certificates for authentication
+ */
+static bool use_certs(private_isakmp_cert_pre_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ bool use = FALSE;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == SECURITY_ASSOCIATION_V1)
+ {
+ sa_payload_t *sa_payload = (sa_payload_t*)payload;
+
+ switch (sa_payload->get_auth_method(sa_payload))
+ {
+ case AUTH_HYBRID_INIT_RSA:
+ case AUTH_HYBRID_RESP_RSA:
+ if (!this->initiator)
+ {
+ this->send_req = FALSE;
+ }
+ /* FALL */
+ case AUTH_RSA:
+ case AUTH_ECDSA_256:
+ case AUTH_ECDSA_384:
+ case AUTH_ECDSA_521:
+ case AUTH_XAUTH_INIT_RSA:
+ case AUTH_XAUTH_RESP_RSA:
+ use = TRUE;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return use;
+}
+
+/**
+ * Check if we should send a certificate request
+ */
+static bool send_certreq(private_isakmp_cert_pre_t *this)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *peer_cfg;
+ auth_cfg_t *auth;
+ bool req = FALSE;
+ auth_class_t class;
+
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer_cfg)
+ {
+ enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
+ if (enumerator->enumerate(enumerator, &auth))
+ {
+ class = (intptr_t)auth->get(auth, AUTH_RULE_AUTH_CLASS);
+ if (class == AUTH_CLASS_PUBKEY)
+ {
+ req = TRUE;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ return req;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_isakmp_cert_pre_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ if (this->state == CR_AUTH)
+ {
+ build_certreqs(this, message);
+ }
+ return NEED_MORE;
+ case AGGRESSIVE:
+ if (this->state == CR_SA)
+ {
+ if (send_certreq(this))
+ {
+ build_certreqs(this, message);
+ }
+ }
+ return NEED_MORE;
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_r, status_t,
+ private_isakmp_cert_pre_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ {
+ switch (this->state)
+ {
+ case CR_SA:
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ return NEED_MORE;
+ case CR_KE:
+ process_certreqs(this, message);
+ return NEED_MORE;
+ case CR_AUTH:
+ process_certreqs(this, message);
+ process_certs(this, message);
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ }
+ case AGGRESSIVE:
+ {
+ switch (this->state)
+ {
+ case CR_SA:
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ process_certreqs(this, message);
+ return NEED_MORE;
+ case CR_AUTH:
+ process_certs(this, message);
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, build_r, status_t,
+ private_isakmp_cert_pre_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ switch (this->state)
+ {
+ case CR_SA:
+ this->state = CR_KE;
+ return NEED_MORE;
+ case CR_KE:
+ if (this->send_req)
+ {
+ build_certreqs(this, message);
+ }
+ this->state = CR_AUTH;
+ return NEED_MORE;
+ case CR_AUTH:
+ return NEED_MORE;
+ default:
+ return FAILED;
+ }
+ case AGGRESSIVE:
+ switch (this->state)
+ {
+ case CR_SA:
+ if (this->send_req)
+ {
+ build_certreqs(this, message);
+ }
+ this->state = CR_AUTH;
+ return NEED_MORE;
+ case CR_AUTH:
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_i, status_t,
+ private_isakmp_cert_pre_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ {
+ switch (this->state)
+ {
+ case CR_SA:
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ this->state = CR_KE;
+ return NEED_MORE;
+ case CR_KE:
+ process_certreqs(this, message);
+ this->state = CR_AUTH;
+ return NEED_MORE;
+ case CR_AUTH:
+ process_certs(this, message);
+ return SUCCESS;
+ default:
+ return FAILED;
+ }
+ break;
+ }
+ case AGGRESSIVE:
+ {
+ if (!use_certs(this, message))
+ {
+ return SUCCESS;
+ }
+ process_certreqs(this, message);
+ process_certs(this, message);
+ this->state = CR_AUTH;
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_isakmp_cert_pre_t *this)
+{
+ return TASK_ISAKMP_CERT_PRE;
+}
+
+METHOD(task_t, migrate, void,
+ private_isakmp_cert_pre_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->state = CR_SA;
+ this->send_req = TRUE;
+}
+
+METHOD(task_t, destroy, void,
+ private_isakmp_cert_pre_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+isakmp_cert_pre_t *isakmp_cert_pre_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_isakmp_cert_pre_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .initiator = initiator,
+ .state = CR_SA,
+ .send_req = TRUE,
+ );
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.h b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.h
new file mode 100644
index 000000000..8e1a94b97
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 isakmp_cert_pre isakmp_cert_pre
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef ISAKMP_CERT_PRE_H_
+#define ISAKMP_CERT_PRE_H_
+
+typedef struct isakmp_cert_pre_t isakmp_cert_pre_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * ISAKMP_CERT_PRE task, IKEv1 certificate processing before authentication.
+ */
+struct isakmp_cert_pre_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new ISAKMP_CERT_PRE task.
+ *
+ * The initiator parameter means the original initiator, not the initiator
+ * of the certificate request.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if task is the original initiator
+ * @return isakmp_cert_pre task to handle by the task_manager
+ */
+isakmp_cert_pre_t *isakmp_cert_pre_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** ISAKMP_CERT_PRE_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_delete.c b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c
new file mode 100644
index 000000000..0640d13b1
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "isakmp_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+typedef struct private_isakmp_delete_t private_isakmp_delete_t;
+
+/**
+ * Private members of a isakmp_delete_t task.
+ */
+struct private_isakmp_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ isakmp_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+};
+
+METHOD(task_t, build_i, status_t,
+ private_isakmp_delete_t *this, message_t *message)
+{
+ delete_payload_t *delete_payload;
+ ike_sa_id_t *id;
+
+ DBG0(DBG_IKE, "deleting IKE_SA %s[%d] 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));
+
+ delete_payload = delete_payload_create(DELETE_V1, PROTO_IKE);
+ id = this->ike_sa->get_id(this->ike_sa);
+ delete_payload->set_ike_spi(delete_payload, id->get_initiator_spi(id),
+ id->get_responder_spi(id));
+ message->add_payload(message, (payload_t*)delete_payload);
+
+ DBG1(DBG_IKE, "sending DELETE for IKE_SA %s[%d]",
+ this->ike_sa->get_name(this->ike_sa),
+ this->ike_sa->get_unique_id(this->ike_sa));
+
+ this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_isakmp_delete_t *this, message_t *message)
+{
+ return FAILED;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_isakmp_delete_t *this, message_t *message)
+{
+ DBG1(DBG_IKE, "received DELETE for IKE_SA %s[%d]",
+ this->ike_sa->get_name(this->ike_sa),
+ this->ike_sa->get_unique_id(this->ike_sa));
+ DBG0(DBG_IKE, "deleting IKE_SA %s[%d] 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_DELETING);
+ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+ return DESTROY_ME;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_isakmp_delete_t *this, message_t *message)
+{
+ return FAILED;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_isakmp_delete_t *this)
+{
+ return TASK_ISAKMP_DELETE;
+}
+
+METHOD(task_t, migrate, void,
+ private_isakmp_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+ private_isakmp_delete_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+isakmp_delete_t *isakmp_delete_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_isakmp_delete_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_delete.h b/src/libcharon/sa/ikev1/tasks/isakmp_delete.h
new file mode 100644
index 000000000..1a7a62207
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_delete.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 isakmp_delete isakmp_delete
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef ISAKMP_DELETE_H_
+#define ISAKMP_DELETE_H_
+
+typedef struct isakmp_delete_t isakmp_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task of type ISAKMP_DELETE, delete an IKEv1 IKE_SA.
+ */
+struct isakmp_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new isakmp_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if we initiate the delete
+ * @return isakmp_delete task to handle by the task_manager
+ */
+isakmp_delete_t *isakmp_delete_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** ISAKMP_DELETE_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c
new file mode 100644
index 000000000..a3395a043
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "isakmp_dpd.h"
+
+#include <daemon.h>
+#include <encoding/payloads/notify_payload.h>
+
+typedef struct private_isakmp_dpd_t private_isakmp_dpd_t;
+
+/**
+ * Private members of a isakmp_dpd_t task.
+ */
+struct private_isakmp_dpd_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ isakmp_dpd_t public;
+
+ /**
+ * Sequence number.
+ */
+ u_int32_t seqnr;
+
+ /**
+ * DPD notify type
+ */
+ notify_type_t type;
+
+ /**
+ * IKE SA we are serving.
+ */
+ ike_sa_t *ike_sa;
+};
+
+METHOD(task_t, build, status_t,
+ private_isakmp_dpd_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ ike_sa_id_t *ike_sa_id;
+ u_int64_t spi_i, spi_r;
+ u_int32_t seqnr;
+ chunk_t spi;
+
+ notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
+ PROTO_IKE, this->type);
+ seqnr = htonl(this->seqnr);
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ spi = chunk_cata("cc", chunk_from_thing(spi_i), chunk_from_thing(spi_r));
+
+ notify->set_spi_data(notify, spi);
+ notify->set_notification_data(notify, chunk_from_thing(seqnr));
+
+ message->add_payload(message, (payload_t*)notify);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, process, status_t,
+ private_isakmp_dpd_t *this, message_t *message)
+{
+ /* done in task manager */
+ return FAILED;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_isakmp_dpd_t *this)
+{
+ return TASK_ISAKMP_DPD;
+}
+
+METHOD(task_t, migrate, void,
+ private_isakmp_dpd_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+ private_isakmp_dpd_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+isakmp_dpd_t *isakmp_dpd_create(ike_sa_t *ike_sa, notify_type_t type,
+ u_int32_t seqnr)
+{
+ private_isakmp_dpd_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .build = _build,
+ .process = _process,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .seqnr = seqnr,
+ .type = type,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h
new file mode 100644
index 000000000..06a0175eb
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 revosec AG
+ *
+ * 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 isakmp_dpd isakmp_dpd
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef ISAKMP_DPD_H_
+#define ISAKMP_DPD_H_
+
+typedef struct isakmp_dpd_t isakmp_dpd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * IKEv1 dead peer detection task.
+ */
+struct isakmp_dpd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new ISAKMP_DPD task.
+ *
+ * @param ike_sa associated IKE_SA
+ * @param type DPD notify to use, DPD_R_U_THERE | DPD_R_U_THERE_ACK
+ * @param seqnr DPD sequence number to use/expect
+ * @return ISAKMP_DPD task to handle by the task_manager
+ */
+isakmp_dpd_t *isakmp_dpd_create(ike_sa_t *ike_sa, notify_type_t type,
+ u_int32_t seqnr);
+
+#endif /** ISAKMP_DPD_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
new file mode 100644
index 000000000..50bf1612d
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2006-2011 Tobias Brunner,
+ * Copyright (C) 2006-2007 Martin Willi
+ * Copyright (C) 2006 Daniel Roethlisberger
+ * 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 "isakmp_natd.h"
+
+#include <string.h>
+
+#include <hydra.h>
+#include <daemon.h>
+#include <sa/ikev1/keymat_v1.h>
+#include <config/peer_cfg.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/hash_payload.h>
+
+typedef struct private_isakmp_natd_t private_isakmp_natd_t;
+
+/**
+ * Private members of a ike_natt_t task.
+ */
+struct private_isakmp_natd_t {
+
+ /**
+ * Public interface.
+ */
+ isakmp_natd_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Keymat derivation (from SA)
+ */
+ keymat_v1_t *keymat;
+
+ /**
+ * Did we process any NAT detection payloads for a source address?
+ */
+ bool src_seen;
+
+ /**
+ * Did we process any NAT detection payloads for a destination address?
+ */
+ bool dst_seen;
+
+ /**
+ * Have we found a matching source address NAT hash?
+ */
+ bool src_matched;
+
+ /**
+ * Have we found a matching destination address NAT hash?
+ */
+ bool dst_matched;
+};
+
+/**
+ * Build NAT detection hash for a host.
+ */
+static chunk_t generate_natd_hash(private_isakmp_natd_t *this,
+ ike_sa_id_t *ike_sa_id, host_t *host)
+{
+ hasher_t *hasher;
+ chunk_t natd_chunk, natd_hash;
+ u_int64_t spi_i, spi_r;
+ u_int16_t port;
+
+ hasher = this->keymat->get_hasher(this->keymat);
+ if (!hasher)
+ {
+ DBG1(DBG_IKE, "no hasher available to build NAT-D payload");
+ return chunk_empty;
+ }
+
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ port = htons(host->get_port(host));
+
+ /* natd_hash = HASH(CKY-I | CKY-R | IP | Port) */
+ natd_chunk = chunk_cata("cccc", chunk_from_thing(spi_i),
+ chunk_from_thing(spi_r), host->get_address(host),
+ chunk_from_thing(port));
+ if (!hasher->allocate_hash(hasher, natd_chunk, &natd_hash))
+ {
+ DBG1(DBG_IKE, "creating NAT-D payload hash failed");
+ return chunk_empty;
+ }
+ DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
+ DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
+
+ return natd_hash;
+}
+
+/**
+ * Build a faked NAT-D payload to enforce UDP encapsulation.
+ */
+static chunk_t generate_natd_hash_faked(private_isakmp_natd_t *this)
+{
+ hasher_t *hasher;
+ chunk_t chunk;
+ rng_t *rng;
+
+ hasher = this->keymat->get_hasher(this->keymat);
+ if (!hasher)
+ {
+ DBG1(DBG_IKE, "no hasher available to build NAT-D payload");
+ return chunk_empty;
+ }
+ rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+ if (!rng ||
+ !rng->allocate_bytes(rng, hasher->get_hash_size(hasher), &chunk))
+ {
+ DBG1(DBG_IKE, "unable to get random bytes for NAT-D fake");
+ DESTROY_IF(rng);
+ return chunk_empty;
+ }
+ rng->destroy(rng);
+ return chunk;
+}
+
+/**
+ * Build a NAT-D payload.
+ */
+static hash_payload_t *build_natd_payload(private_isakmp_natd_t *this, bool src,
+ host_t *host)
+{
+ hash_payload_t *payload;
+ ike_cfg_t *config;
+ chunk_t hash;
+
+ config = this->ike_sa->get_ike_cfg(this->ike_sa);
+ if (src && config->force_encap(config))
+ {
+ hash = generate_natd_hash_faked(this);
+ }
+ else
+ {
+ ike_sa_id_t *ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ hash = generate_natd_hash(this, ike_sa_id, host);
+ }
+ if (!hash.len)
+ {
+ return NULL;
+ }
+ payload = hash_payload_create(NAT_D_V1);
+ payload->set_hash(payload, hash);
+ chunk_free(&hash);
+ return payload;
+}
+
+/**
+ * Add NAT-D payloads to the message.
+ */
+static void add_natd_payloads(private_isakmp_natd_t *this, message_t *message)
+{
+ hash_payload_t *payload;
+ host_t *host;
+
+ /* destination has to be added first */
+ host = message->get_destination(message);
+ payload = build_natd_payload(this, FALSE, host);
+ if (payload)
+ {
+ message->add_payload(message, (payload_t*)payload);
+ }
+
+ /* source is added second, compared with IKEv2 we always know the source,
+ * as these payloads are added in the second Phase 1 exchange or the
+ * response to the first */
+ host = message->get_source(message);
+ payload = build_natd_payload(this, TRUE, host);
+ if (payload)
+ {
+ message->add_payload(message, (payload_t*)payload);
+ }
+}
+
+/**
+ * Read NAT-D payloads from message and evaluate them.
+ */
+static void process_payloads(private_isakmp_natd_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ hash_payload_t *hash_payload;
+ chunk_t hash, src_hash, dst_hash;
+ ike_sa_id_t *ike_sa_id;
+ host_t *me, *other;
+ ike_cfg_t *config;
+
+ /* precompute hashes for incoming NAT-D comparison */
+ ike_sa_id = message->get_ike_sa_id(message);
+ me = message->get_destination(message);
+ other = message->get_source(message);
+ dst_hash = generate_natd_hash(this, ike_sa_id, me);
+ src_hash = generate_natd_hash(this, ike_sa_id, other);
+
+ DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash);
+ DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash);
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) != NAT_D_V1)
+ {
+ continue;
+ }
+ hash_payload = (hash_payload_t*)payload;
+ if (!this->dst_seen)
+ { /* the first NAT-D payload contains the destination hash */
+ this->dst_seen = TRUE;
+ hash = hash_payload->get_hash(hash_payload);
+ DBG3(DBG_IKE, "received dst_hash %B", &hash);
+ if (chunk_equals(hash, dst_hash))
+ {
+ this->dst_matched = TRUE;
+ }
+ continue;
+ }
+ /* the other NAT-D payloads contain source hashes */
+ this->src_seen = TRUE;
+ if (!this->src_matched)
+ {
+ hash = hash_payload->get_hash(hash_payload);
+ DBG3(DBG_IKE, "received src_hash %B", &hash);
+ if (chunk_equals(hash, src_hash))
+ {
+ this->src_matched = TRUE;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ chunk_free(&src_hash);
+ chunk_free(&dst_hash);
+
+ if (this->src_seen && this->dst_seen)
+ {
+ this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE,
+ !this->dst_matched);
+ this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE,
+ !this->src_matched);
+ config = this->ike_sa->get_ike_cfg(this->ike_sa);
+ if (this->dst_matched && this->src_matched &&
+ config->force_encap(config))
+ {
+ this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE);
+ }
+ }
+}
+
+METHOD(task_t, build_i, status_t,
+ private_isakmp_natd_t *this, message_t *message)
+{
+ status_t result = NEED_MORE;
+
+ switch (message->get_exchange_type(message))
+ {
+ case AGGRESSIVE:
+ { /* add NAT-D payloads to the second request, already processed
+ * those by the responder contained in the first response */
+ result = SUCCESS;
+ /* fall */
+ }
+ case ID_PROT:
+ { /* add NAT-D payloads to the second request, need to process
+ * those by the responder contained in the second response */
+ if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
+ { /* wait for the second exchange */
+ return NEED_MORE;
+ }
+ add_natd_payloads(this, message);
+ return result;
+ }
+ default:
+ break;
+ }
+ return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_isakmp_natd_t *this, message_t *message)
+{
+ status_t result = NEED_MORE;
+
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))
+ { /* we didn't receive VIDs inidcating support for NAT-T */
+ return SUCCESS;
+ }
+
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ { /* process NAT-D payloads in the second response, added them in the
+ * second request already, so we're done afterwards */
+ if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
+ { /* wait for the second exchange */
+ return NEED_MORE;
+ }
+ result = SUCCESS;
+ /* fall */
+ }
+ case AGGRESSIVE:
+ { /* process NAT-D payloads in the first response, add them in the
+ * following second request */
+ process_payloads(this, message);
+
+ if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
+ {
+ this->ike_sa->float_ports(this->ike_sa);
+ }
+ return result;
+ }
+ default:
+ break;
+ }
+ return SUCCESS;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_isakmp_natd_t *this, message_t *message)
+{
+ status_t result = NEED_MORE;
+
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT))
+ { /* we didn't receive VIDs indicating NAT-T support */
+ return SUCCESS;
+ }
+
+ switch (message->get_exchange_type(message))
+ {
+ case AGGRESSIVE:
+ { /* proccess NAT-D payloads in the second request, already added ours
+ * in the first response */
+ result = SUCCESS;
+ /* fall */
+ }
+ case ID_PROT:
+ { /* process NAT-D payloads in the second request, need to add ours
+ * to the second response */
+ if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
+ { /* wait for the second exchange */
+ return NEED_MORE;
+ }
+ process_payloads(this, message);
+ return result;
+ }
+ default:
+ break;
+ }
+ return SUCCESS;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_isakmp_natd_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case ID_PROT:
+ { /* add NAT-D payloads to second response, already processed those
+ * contained in the second request */
+ if (message->get_payload(message, SECURITY_ASSOCIATION_V1))
+ { /* wait for the second exchange */
+ return NEED_MORE;
+ }
+ add_natd_payloads(this, message);
+ return SUCCESS;
+ }
+ case AGGRESSIVE:
+ { /* add NAT-D payloads to the first response, process those contained
+ * in the following second request */
+ add_natd_payloads(this, message);
+ return NEED_MORE;
+ }
+ default:
+ break;
+ }
+ return SUCCESS;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_isakmp_natd_t *this)
+{
+ return TASK_ISAKMP_NATD;
+}
+
+METHOD(task_t, migrate, void,
+ private_isakmp_natd_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
+ this->src_seen = FALSE;
+ this->dst_seen = FALSE;
+ this->src_matched = FALSE;
+ this->dst_matched = FALSE;
+}
+
+METHOD(task_t, destroy, void,
+ private_isakmp_natd_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+isakmp_natd_t *isakmp_natd_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_isakmp_natd_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
+ .initiator = initiator,
+ );
+
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.h b/src/libcharon/sa/ikev1/tasks/isakmp_natd.h
new file mode 100644
index 000000000..63947fc73
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/**
+ * @defgroup isakmp_natd isakmp_natd
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef ISAKMP_NATD_H_
+#define ISAKMP_NATD_H_
+
+typedef struct isakmp_natd_t isakmp_natd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task of type ISAKMP_NATD, detects NAT situation in IKEv1 Phase 1.
+ */
+struct isakmp_natd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new ISAKMP_NATD task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if task is the original initiator
+ * @return isakmp_natd task to handle by the task_manager
+ */
+isakmp_natd_t *isakmp_natd_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** ISAKMP_NATD_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
new file mode 100644
index 000000000..4fd0ef39b
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 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 "isakmp_vendor.h"
+
+#include <daemon.h>
+#include <encoding/payloads/vendor_id_payload.h>
+
+typedef struct private_isakmp_vendor_t private_isakmp_vendor_t;
+
+/**
+ * Private data of an isakmp_vendor_t object.
+ */
+struct private_isakmp_vendor_t {
+
+ /**
+ * Public isakmp_vendor_t interface.
+ */
+ isakmp_vendor_t public;
+
+ /**
+ * Associated IKE_SA
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the inititator of this task
+ */
+ bool initiator;
+};
+
+/**
+ * IKEv1 Vendor ID database
+ */
+static struct {
+ /* Description */
+ char *desc;
+ /* extension flag negotiated with vendor ID, if any */
+ ike_extension_t extension;
+ /* send yourself? */
+ bool send;
+ /* length of vendor ID string */
+ int len;
+ /* vendor ID string */
+ char *id;
+} vendor_ids[] = {
+
+ /* strongSwan MD5("strongSwan") */
+ { "strongSwan", EXT_STRONGSWAN, FALSE, 16,
+ "\x88\x2f\xe5\x6d\x6f\xd2\x0d\xbc\x22\x51\x61\x3b\x2e\xbe\x5b\xeb"},
+
+ /* XAuth, MD5("draft-ietf-ipsra-isakmp-xauth-06.txt") */
+ { "XAuth", EXT_XAUTH, TRUE, 8,
+ "\x09\x00\x26\x89\xdf\xd6\xb7\x12"},
+
+ /* NAT-Traversal, MD5("RFC 3947") */
+ { "NAT-T (RFC 3947)", EXT_NATT, TRUE, 16,
+ "\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f"},
+
+ /* Dead peer detection, RFC 3706 */
+ { "DPD", EXT_DPD, TRUE, 16,
+ "\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00"},
+
+ { "draft-stenberg-ipsec-nat-traversal-01", 0, FALSE, 16,
+ "\x27\xba\xb5\xdc\x01\xea\x07\x60\xea\x4e\x31\x90\xac\x27\xc0\xd0"},
+
+ { "draft-stenberg-ipsec-nat-traversal-02", 0, FALSE, 16,
+ "\x61\x05\xc4\x22\xe7\x68\x47\xe4\x3f\x96\x84\x80\x12\x92\xae\xcd"},
+
+ { "draft-ietf-ipsec-nat-t-ike", 0, FALSE, 16,
+ "\x4d\xf3\x79\x28\xe9\xfc\x4f\xd1\xb3\x26\x21\x70\xd5\x15\xc6\x62"},
+
+ { "draft-ietf-ipsec-nat-t-ike-00", 0, FALSE, 16,
+ "\x44\x85\x15\x2d\x18\xb6\xbb\xcd\x0b\xe8\xa8\x46\x95\x79\xdd\xcc"},
+
+ { "draft-ietf-ipsec-nat-t-ike-02", 0, FALSE, 16,
+ "\xcd\x60\x46\x43\x35\xdf\x21\xf8\x7c\xfd\xb2\xfc\x68\xb6\xa4\x48"},
+
+ { "draft-ietf-ipsec-nat-t-ike-02\\n", 0, FALSE, 16,
+ "\x90\xcb\x80\x91\x3e\xbb\x69\x6e\x08\x63\x81\xb5\xec\x42\x7b\x1f"},
+
+ { "draft-ietf-ipsec-nat-t-ike-03", 0, FALSE, 16,
+ "\x7d\x94\x19\xa6\x53\x10\xca\x6f\x2c\x17\x9d\x92\x15\x52\x9d\x56"},
+
+ { "draft-ietf-ipsec-nat-t-ike-04", 0, FALSE, 16,
+ "\x99\x09\xb6\x4e\xed\x93\x7c\x65\x73\xde\x52\xac\xe9\x52\xfa\x6b"},
+
+ { "draft-ietf-ipsec-nat-t-ike-05", 0, FALSE, 16,
+ "\x80\xd0\xbb\x3d\xef\x54\x56\x5e\xe8\x46\x45\xd4\xc8\x5c\xe3\xee"},
+
+ { "draft-ietf-ipsec-nat-t-ike-06", 0, FALSE, 16,
+ "\x4d\x1e\x0e\x13\x6d\xea\xfa\x34\xc4\xf3\xea\x9f\x02\xec\x72\x85"},
+
+ { "draft-ietf-ipsec-nat-t-ike-07", 0, FALSE, 16,
+ "\x43\x9b\x59\xf8\xba\x67\x6c\x4c\x77\x37\xae\x22\xea\xb8\xf5\x82"},
+
+ { "draft-ietf-ipsec-nat-t-ike-08", 0, FALSE, 16,
+ "\x8f\x8d\x83\x82\x6d\x24\x6b\x6f\xc7\xa8\xa6\xa4\x28\xc1\x1d\xe8"},
+
+ { "Cisco Unity", EXT_CISCO_UNITY, FALSE, 16,
+ "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"},
+};
+
+METHOD(task_t, build, status_t,
+ private_isakmp_vendor_t *this, message_t *message)
+{
+ vendor_id_payload_t *vid_payload;
+ bool strongswan, cisco_unity;
+ int i;
+
+ strongswan = lib->settings->get_bool(lib->settings,
+ "%s.send_vendor_id", FALSE, charon->name);
+ cisco_unity = lib->settings->get_bool(lib->settings,
+ "%s.cisco_unity", FALSE, charon->name);
+ for (i = 0; i < countof(vendor_ids); i++)
+ {
+ if (vendor_ids[i].send ||
+ (vendor_ids[i].extension == EXT_STRONGSWAN && strongswan) ||
+ (vendor_ids[i].extension == EXT_CISCO_UNITY && cisco_unity))
+ {
+ vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1,
+ chunk_clone(chunk_create(vendor_ids[i].id, vendor_ids[i].len)));
+ message->add_payload(message, &vid_payload->payload_interface);
+ }
+ }
+ return this->initiator ? NEED_MORE : SUCCESS;
+}
+
+METHOD(task_t, process, status_t,
+ private_isakmp_vendor_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ int i;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == VENDOR_ID_V1)
+ {
+ vendor_id_payload_t *vid;
+ bool found = FALSE;
+ chunk_t data;
+
+ vid = (vendor_id_payload_t*)payload;
+ data = vid->get_data(vid);
+
+ for (i = 0; i < countof(vendor_ids); i++)
+ {
+ if (chunk_equals(data, chunk_create(vendor_ids[i].id,
+ vendor_ids[i].len)))
+ {
+ DBG1(DBG_IKE, "received %s vendor ID", vendor_ids[i].desc);
+ if (vendor_ids[i].extension)
+ {
+ this->ike_sa->enable_extension(this->ike_sa,
+ vendor_ids[i].extension);
+ }
+ found = TRUE;
+ }
+ }
+ if (!found)
+ {
+ DBG1(DBG_ENC, "received unknown vendor ID: %#B", &data);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return this->initiator ? SUCCESS : NEED_MORE;
+}
+
+METHOD(task_t, migrate, void,
+ private_isakmp_vendor_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_isakmp_vendor_t *this)
+{
+ return TASK_ISAKMP_VENDOR;
+}
+
+METHOD(task_t, destroy, void,
+ private_isakmp_vendor_t *this)
+{
+ free(this);
+}
+
+/**
+ * See header
+ */
+isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_isakmp_vendor_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .build = _build,
+ .process = _process,
+ .migrate = _migrate,
+ .get_type = _get_type,
+ .destroy = _destroy,
+ },
+ },
+ .initiator = initiator,
+ .ike_sa = ike_sa,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.h b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.h
new file mode 100644
index 000000000..91891085b
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 isakmp_vendor isakmp_vendor
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef ISAKMP_VENDOR_H_
+#define ISAKMP_VENDOR_H_
+
+typedef struct isakmp_vendor_t isakmp_vendor_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Vendor ID processing task for IKEv1.
+ */
+struct isakmp_vendor_t {
+
+ /**
+ * Implements task interface.
+ */
+ task_t task;
+};
+
+/**
+ * Create a isakmp_vendor instance.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if task is the original initiator
+ */
+isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** ISAKMP_VENDOR_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c
new file mode 100644
index 000000000..9ccf9abf5
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/main_mode.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright (C) 2011-2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "main_mode.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <sa/ikev1/phase1.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/hash_payload.h>
+#include <sa/ikev1/tasks/xauth.h>
+#include <sa/ikev1/tasks/mode_config.h>
+#include <sa/ikev1/tasks/informational.h>
+#include <sa/ikev1/tasks/isakmp_delete.h>
+#include <processing/jobs/adopt_children_job.h>
+
+typedef struct private_main_mode_t private_main_mode_t;
+
+/**
+ * Private members of a main_mode_t task.
+ */
+struct private_main_mode_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ main_mode_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Common phase 1 helper class
+ */
+ phase1_t *ph1;
+
+ /**
+ * IKE config to establish
+ */
+ ike_cfg_t *ike_cfg;
+
+ /**
+ * Peer config to use
+ */
+ peer_cfg_t *peer_cfg;
+
+ /**
+ * selected IKE proposal
+ */
+ proposal_t *proposal;
+
+ /**
+ * Negotiated SA lifetime
+ */
+ u_int32_t lifetime;
+
+ /**
+ * Negotiated authentication method
+ */
+ auth_method_t method;
+
+ /** states of main mode */
+ enum {
+ MM_INIT,
+ MM_SA,
+ MM_KE,
+ MM_AUTH,
+ } state;
+};
+
+/**
+ * Set IKE_SA to established state
+ */
+static bool establish(private_main_mode_t *this)
+{
+ if (!charon->bus->authorize(charon->bus, TRUE))
+ {
+ DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
+ return FALSE;
+ }
+
+ 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 TRUE;
+}
+
+/**
+ * Check for notify errors, return TRUE if error found
+ */
+static bool has_notify_errors(private_main_mode_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ bool err = FALSE;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == NOTIFY_V1)
+ {
+ notify_payload_t *notify;
+ notify_type_t type;
+
+ notify = (notify_payload_t*)payload;
+ type = notify->get_notify_type(notify);
+ if (type < 16384)
+ {
+ DBG1(DBG_IKE, "received %N error notify",
+ notify_type_names, type);
+ err = TRUE;
+ }
+ else if (type == INITIAL_CONTACT_IKEV1)
+ {
+ if (!this->initiator && this->state == MM_AUTH)
+ {
+ /* If authenticated and received INITIAL_CONTACT,
+ * delete any existing IKE_SAs with that peer.
+ * The delete takes place when the SA is checked in due
+ * to other id not known until the 3rd message.*/
+ this->ike_sa->set_condition(this->ike_sa,
+ COND_INIT_CONTACT_SEEN, TRUE);
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N notify", notify_type_names, type);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return err;
+}
+
+/**
+ * Queue a task sending a notify in an INFORMATIONAL exchange
+ */
+static status_t send_notify(private_main_mode_t *this, notify_type_t type)
+{
+ notify_payload_t *notify;
+ ike_sa_id_t *ike_sa_id;
+ u_int64_t spi_i, spi_r;
+ chunk_t spi;
+
+ notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
+ PROTO_IKE, type);
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ spi = chunk_cata("cc", chunk_from_thing(spi_i), chunk_from_thing(spi_r));
+ notify->set_spi_data(notify, spi);
+
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)informational_create(this->ike_sa, notify));
+ /* cancel all active/passive tasks in favour of informational */
+ this->ike_sa->flush_queue(this->ike_sa,
+ this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE);
+ return ALREADY_DONE;
+}
+
+/**
+ * Queue a delete task if authentication failed as initiator
+ */
+static status_t send_delete(private_main_mode_t *this)
+{
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)isakmp_delete_create(this->ike_sa, TRUE));
+ /* cancel all active tasks in favour of informational */
+ this->ike_sa->flush_queue(this->ike_sa,
+ this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE);
+ return ALREADY_DONE;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_main_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case MM_INIT:
+ {
+ sa_payload_t *sa_payload;
+ linked_list_t *proposals;
+ packet_t *packet;
+
+ DBG0(DBG_IKE, "initiating Main Mode 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);
+
+ this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ this->peer_cfg->get_ref(this->peer_cfg);
+
+ this->method = this->ph1->get_auth_method(this->ph1, this->peer_cfg);
+ if (this->method == AUTH_NONE)
+ {
+ DBG1(DBG_CFG, "configuration uses unsupported authentication");
+ return FAILED;
+ }
+ this->lifetime = this->peer_cfg->get_reauth_time(this->peer_cfg,
+ FALSE);
+ if (!this->lifetime)
+ { /* fall back to rekey time of no rekey time configured */
+ this->lifetime = this->peer_cfg->get_rekey_time(this->peer_cfg,
+ FALSE);
+ }
+ this->lifetime += this->peer_cfg->get_over_time(this->peer_cfg);
+ proposals = this->ike_cfg->get_proposals(this->ike_cfg);
+ sa_payload = sa_payload_create_from_proposals_v1(proposals,
+ this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
+
+ message->add_payload(message, &sa_payload->payload_interface);
+
+ /* pregenerate message to store SA payload */
+ if (this->ike_sa->generate_message(this->ike_sa, message,
+ &packet) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "pregenerating SA payload failed");
+ return FAILED;
+ }
+ packet->destroy(packet);
+ if (!this->ph1->save_sa_payload(this->ph1, message))
+ {
+ return FAILED;
+ }
+
+ this->state = MM_SA;
+ return NEED_MORE;
+ }
+ case MM_SA:
+ {
+ u_int16_t group;
+
+ if (!this->ph1->create_hasher(this->ph1))
+ {
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ if (!this->proposal->get_algorithm(this->proposal,
+ DIFFIE_HELLMAN_GROUP, &group, NULL))
+ {
+ DBG1(DBG_IKE, "DH group selection failed");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ if (!this->ph1->create_dh(this->ph1, group))
+ {
+ DBG1(DBG_IKE, "negotiated DH group not supported");
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->add_nonce_ke(this->ph1, message))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ this->state = MM_KE;
+ return NEED_MORE;
+ }
+ case MM_KE:
+ {
+ id_payload_t *id_payload;
+ identification_t *id;
+
+ id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
+ if (!id)
+ {
+ DBG1(DBG_CFG, "own identity not known");
+ return send_notify(this, INVALID_ID_INFORMATION);
+ }
+ this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
+ id_payload = id_payload_create_from_identification(ID_V1, id);
+ message->add_payload(message, &id_payload->payload_interface);
+
+ if (!this->ph1->build_auth(this->ph1, this->method, message,
+ id_payload->get_encoded(id_payload)))
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+
+ this->state = MM_AUTH;
+ return NEED_MORE;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_r, status_t,
+ private_main_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case MM_INIT:
+ {
+ linked_list_t *list;
+ sa_payload_t *sa_payload;
+ bool private;
+
+ this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ DBG0(DBG_IKE, "%H is initiating a Main Mode IKE_SA",
+ message->get_source(message));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ this->ike_sa->update_hosts(this->ike_sa,
+ message->get_destination(message),
+ message->get_source(message), TRUE);
+
+ sa_payload = (sa_payload_t*)message->get_payload(message,
+ SECURITY_ASSOCIATION_V1);
+ if (!sa_payload)
+ {
+ DBG1(DBG_IKE, "SA payload missing");
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ if (!this->ph1->save_sa_payload(this->ph1, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+
+ list = sa_payload->get_proposals(sa_payload);
+ private = this->ike_sa->supports_extension(this->ike_sa,
+ EXT_STRONGSWAN);
+ this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
+ list, private);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+ if (!this->proposal)
+ {
+ DBG1(DBG_IKE, "no proposal found");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->ike_sa->set_proposal(this->ike_sa, this->proposal);
+
+ this->method = sa_payload->get_auth_method(sa_payload);
+ this->lifetime = sa_payload->get_lifetime(sa_payload);
+
+ this->state = MM_SA;
+ return NEED_MORE;
+ }
+ case MM_SA:
+ {
+ u_int16_t group;
+
+ if (!this->ph1->create_hasher(this->ph1))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->proposal->get_algorithm(this->proposal,
+ DIFFIE_HELLMAN_GROUP, &group, NULL))
+ {
+ DBG1(DBG_IKE, "DH group selection failed");
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->create_dh(this->ph1, group))
+ {
+ DBG1(DBG_IKE, "negotiated DH group not supported");
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->get_nonce_ke(this->ph1, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ this->state = MM_KE;
+ return NEED_MORE;
+ }
+ case MM_KE:
+ {
+ id_payload_t *id_payload;
+ identification_t *id;
+
+ id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
+ if (!id_payload)
+ {
+ DBG1(DBG_IKE, "IDii payload missing");
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ id = id_payload->get_identification(id_payload);
+ this->ike_sa->set_other_id(this->ike_sa, id);
+
+ while (TRUE)
+ {
+ DESTROY_IF(this->peer_cfg);
+ this->peer_cfg = this->ph1->select_config(this->ph1,
+ this->method, FALSE, id);
+ if (!this->peer_cfg)
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
+
+ if (this->ph1->verify_auth(this->ph1, this->method, message,
+ id_payload->get_encoded(id_payload)))
+ {
+ break;
+ }
+ }
+
+ if (!charon->bus->authorize(charon->bus, FALSE))
+ {
+ DBG1(DBG_IKE, "Main Mode authorization hook forbids IKE_SA, "
+ "cancelling");
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+
+ this->state = MM_AUTH;
+ if (has_notify_errors(this, message))
+ {
+ return FAILED;
+ }
+ return NEED_MORE;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, build_r, status_t,
+ private_main_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case MM_SA:
+ {
+ sa_payload_t *sa_payload;
+
+ sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
+ this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ message->add_payload(message, &sa_payload->payload_interface);
+
+ return NEED_MORE;
+ }
+ case MM_KE:
+ {
+ if (!this->ph1->add_nonce_ke(this->ph1, message))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!this->ph1->derive_keys(this->ph1, this->peer_cfg, this->method))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ return NEED_MORE;
+ }
+ case MM_AUTH:
+ {
+ id_payload_t *id_payload;
+ identification_t *id;
+
+ id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
+ if (!id)
+ {
+ DBG1(DBG_CFG, "own identity not known");
+ return send_notify(this, INVALID_ID_INFORMATION);
+ }
+ this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
+
+ id_payload = id_payload_create_from_identification(ID_V1, id);
+ message->add_payload(message, &id_payload->payload_interface);
+
+ if (!this->ph1->build_auth(this->ph1, this->method, message,
+ id_payload->get_encoded(id_payload)))
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+
+ switch (this->method)
+ {
+ case AUTH_XAUTH_INIT_PSK:
+ case AUTH_XAUTH_INIT_RSA:
+ case AUTH_HYBRID_INIT_RSA:
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)xauth_create(this->ike_sa, TRUE));
+ return SUCCESS;
+ case AUTH_XAUTH_RESP_PSK:
+ case AUTH_XAUTH_RESP_RSA:
+ case AUTH_HYBRID_RESP_RSA:
+ /* wait for XAUTH request */
+ break;
+ default:
+ if (charon->ike_sa_manager->check_uniqueness(
+ charon->ike_sa_manager, this->ike_sa, FALSE))
+ {
+ DBG1(DBG_IKE, "cancelling Main Mode due to uniqueness "
+ "policy");
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ if (!establish(this))
+ {
+ return send_notify(this, AUTHENTICATION_FAILED);
+ }
+ lib->processor->queue_job(lib->processor, (job_t*)
+ adopt_children_job_create(
+ this->ike_sa->get_id(this->ike_sa)));
+ break;
+ }
+ if (!this->ph1->has_pool(this->ph1, this->peer_cfg) &&
+ this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE));
+ }
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_i, status_t,
+ private_main_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case MM_SA:
+ {
+ linked_list_t *list;
+ sa_payload_t *sa_payload;
+ auth_method_t method;
+ u_int32_t lifetime;
+ bool private;
+
+ sa_payload = (sa_payload_t*)message->get_payload(message,
+ SECURITY_ASSOCIATION_V1);
+ if (!sa_payload)
+ {
+ DBG1(DBG_IKE, "SA payload missing");
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ list = sa_payload->get_proposals(sa_payload);
+ private = this->ike_sa->supports_extension(this->ike_sa,
+ EXT_STRONGSWAN);
+ this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
+ list, private);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+ if (!this->proposal)
+ {
+ DBG1(DBG_IKE, "no proposal found");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->ike_sa->set_proposal(this->ike_sa, this->proposal);
+
+ lifetime = sa_payload->get_lifetime(sa_payload);
+ if (lifetime != this->lifetime)
+ {
+ DBG1(DBG_IKE, "received lifetime %us does not match configured "
+ "lifetime %us", lifetime, this->lifetime);
+ }
+ this->lifetime = lifetime;
+ method = sa_payload->get_auth_method(sa_payload);
+ if (method != this->method)
+ {
+ DBG1(DBG_IKE, "received %N authentication, but configured %N, "
+ "continue with configured", auth_method_names, method,
+ auth_method_names, this->method);
+ }
+ return NEED_MORE;
+ }
+ case MM_KE:
+ {
+ if (!this->ph1->get_nonce_ke(this->ph1, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ if (!this->ph1->derive_keys(this->ph1, this->peer_cfg, this->method))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ return NEED_MORE;
+ }
+ case MM_AUTH:
+ {
+ id_payload_t *id_payload;
+ identification_t *id, *cid;
+
+ id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
+ if (!id_payload)
+ {
+ DBG1(DBG_IKE, "IDir payload missing");
+ return send_delete(this);
+ }
+ id = id_payload->get_identification(id_payload);
+ cid = this->ph1->get_id(this->ph1, this->peer_cfg, FALSE);
+ if (cid && !id->matches(id, cid))
+ {
+ DBG1(DBG_IKE, "IDir '%Y' does not match to '%Y'", id, cid);
+ id->destroy(id);
+ return send_delete(this);
+ }
+ this->ike_sa->set_other_id(this->ike_sa, id);
+
+ if (!this->ph1->verify_auth(this->ph1, this->method, message,
+ id_payload->get_encoded(id_payload)))
+ {
+ return send_delete(this);
+ }
+ if (!charon->bus->authorize(charon->bus, FALSE))
+ {
+ DBG1(DBG_IKE, "Main Mode authorization hook forbids IKE_SA, "
+ "cancelling");
+ return send_delete(this);
+ }
+
+ switch (this->method)
+ {
+ case AUTH_XAUTH_INIT_PSK:
+ case AUTH_XAUTH_INIT_RSA:
+ case AUTH_HYBRID_INIT_RSA:
+ /* wait for XAUTH request */
+ break;
+ case AUTH_XAUTH_RESP_PSK:
+ case AUTH_XAUTH_RESP_RSA:
+ case AUTH_HYBRID_RESP_RSA:
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)xauth_create(this->ike_sa, TRUE));
+ return SUCCESS;
+ default:
+ if (charon->ike_sa_manager->check_uniqueness(
+ charon->ike_sa_manager, this->ike_sa, FALSE))
+ {
+ DBG1(DBG_IKE, "cancelling Main Mode due to uniqueness "
+ "policy");
+ return send_delete(this);
+ }
+ if (!establish(this))
+ {
+ return send_delete(this);
+ }
+ break;
+ }
+ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE));
+ }
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_main_mode_t *this)
+{
+ return TASK_MAIN_MODE;
+}
+
+METHOD(task_t, migrate, void,
+ private_main_mode_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->peer_cfg);
+ DESTROY_IF(this->proposal);
+ this->ph1->destroy(this->ph1);
+
+ this->ike_sa = ike_sa;
+ this->state = MM_INIT;
+ this->peer_cfg = NULL;
+ this->proposal = NULL;
+ this->ph1 = phase1_create(ike_sa, this->initiator);
+}
+
+METHOD(task_t, destroy, void,
+ private_main_mode_t *this)
+{
+ DESTROY_IF(this->peer_cfg);
+ DESTROY_IF(this->proposal);
+ this->ph1->destroy(this->ph1);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_main_mode_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .ph1 = phase1_create(ike_sa, initiator),
+ .initiator = initiator,
+ .state = MM_INIT,
+ );
+
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.h b/src/libcharon/sa/ikev1/tasks/main_mode.h
new file mode 100644
index 000000000..141701f75
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/main_mode.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 main_mode main_mode
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef MAIN_MODE_H_
+#define MAIN_MODE_H_
+
+typedef struct main_mode_t main_mode_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * IKEv1 main mode, establishes a mainmode including authentication.
+ */
+struct main_mode_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new main_mode task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if task initiated locally
+ * @return task to handle by the task_manager
+ */
+main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** MAIN_MODE_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c
new file mode 100644
index 000000000..ce897727a
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/mode_config.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "mode_config.h"
+
+#include <daemon.h>
+#include <hydra.h>
+#include <encoding/payloads/cp_payload.h>
+
+typedef struct private_mode_config_t private_mode_config_t;
+
+/**
+ * Private members of a mode_config_t task.
+ */
+struct private_mode_config_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ mode_config_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Received list of virtual IPs, host_t*
+ */
+ linked_list_t *vips;
+
+ /**
+ * list of attributes requested and its handler, entry_t
+ */
+ linked_list_t *requested;
+
+ /**
+ * Identifier to include in response
+ */
+ u_int16_t identifier;
+};
+
+/**
+ * Entry for a requested attribute and the requesting handler
+ */
+typedef struct {
+ /** attribute requested */
+ configuration_attribute_type_t type;
+ /** handler requesting this attribute */
+ attribute_handler_t *handler;
+} entry_t;
+
+/**
+ * build INTERNAL_IPV4/6_ADDRESS attribute from virtual ip
+ */
+static configuration_attribute_t *build_vip(host_t *vip)
+{
+ configuration_attribute_type_t type;
+ chunk_t chunk, prefix;
+
+ if (vip->get_family(vip) == AF_INET)
+ {
+ type = INTERNAL_IP4_ADDRESS;
+ if (vip->is_anyaddr(vip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ chunk = vip->get_address(vip);
+ }
+ }
+ else
+ {
+ type = INTERNAL_IP6_ADDRESS;
+ if (vip->is_anyaddr(vip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ prefix = chunk_alloca(1);
+ *prefix.ptr = 64;
+ chunk = vip->get_address(vip);
+ chunk = chunk_cata("cc", chunk, prefix);
+ }
+ }
+ return configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
+ type, chunk);
+}
+
+/**
+ * Handle a received attribute as initiator
+ */
+static void handle_attribute(private_mode_config_t *this,
+ configuration_attribute_t *ca)
+{
+ attribute_handler_t *handler = NULL;
+ enumerator_t *enumerator;
+ entry_t *entry;
+
+ /* find the handler which requested this attribute */
+ enumerator = this->requested->create_enumerator(this->requested);
+ while (enumerator->enumerate(enumerator, &entry))
+ {
+ if (entry->type == ca->get_type(ca))
+ {
+ handler = entry->handler;
+ this->requested->remove_at(this->requested, enumerator);
+ free(entry);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* and pass it to the handle function */
+ handler = hydra->attributes->handle(hydra->attributes,
+ this->ike_sa->get_other_id(this->ike_sa), handler,
+ ca->get_type(ca), ca->get_chunk(ca));
+ if (handler)
+ {
+ this->ike_sa->add_configuration_attribute(this->ike_sa,
+ handler, ca->get_type(ca), ca->get_chunk(ca));
+ }
+}
+
+/**
+ * process a single configuration attribute
+ */
+static void process_attribute(private_mode_config_t *this,
+ configuration_attribute_t *ca)
+{
+ host_t *ip;
+ chunk_t addr;
+ int family = AF_INET6;
+
+ switch (ca->get_type(ca))
+ {
+ case INTERNAL_IP4_ADDRESS:
+ family = AF_INET;
+ /* fall */
+ case INTERNAL_IP6_ADDRESS:
+ {
+ addr = ca->get_chunk(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ /* skip prefix byte in IPv6 payload*/
+ if (family == AF_INET6)
+ {
+ addr.len--;
+ }
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip)
+ {
+ this->vips->insert_last(this->vips, ip);
+ }
+ break;
+ }
+ default:
+ {
+ if (this->initiator)
+ {
+ handle_attribute(this, ca);
+ }
+ }
+ }
+}
+
+/**
+ * Scan for configuration payloads and attributes
+ */
+static void process_payloads(private_mode_config_t *this, message_t *message)
+{
+ enumerator_t *enumerator, *attributes;
+ payload_t *payload;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == CONFIGURATION_V1)
+ {
+ cp_payload_t *cp = (cp_payload_t*)payload;
+ configuration_attribute_t *ca;
+
+ switch (cp->get_type(cp))
+ {
+ case CFG_REQUEST:
+ this->identifier = cp->get_identifier(cp);
+ /* FALL */
+ case CFG_REPLY:
+ attributes = cp->create_attribute_enumerator(cp);
+ while (attributes->enumerate(attributes, &ca))
+ {
+ DBG2(DBG_IKE, "processing %N attribute",
+ configuration_attribute_type_names, ca->get_type(ca));
+ process_attribute(this, ca);
+ }
+ attributes->destroy(attributes);
+ break;
+ default:
+ DBG1(DBG_IKE, "ignoring %N config payload",
+ config_type_names, cp->get_type(cp));
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+METHOD(task_t, build_i, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ cp_payload_t *cp;
+ enumerator_t *enumerator;
+ attribute_handler_t *handler;
+ peer_cfg_t *config;
+ configuration_attribute_type_t type;
+ chunk_t data;
+ linked_list_t *vips;
+ host_t *host;
+
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
+
+ vips = linked_list_create();
+
+ /* reuse virtual IP if we already have one */
+ enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ vips->insert_last(vips, host);
+ }
+ enumerator->destroy(enumerator);
+
+ if (vips->get_count(vips) == 0)
+ {
+ config = this->ike_sa->get_peer_cfg(this->ike_sa);
+ enumerator = config->create_virtual_ip_enumerator(config);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ vips->insert_last(vips, host);
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ if (vips->get_count(vips))
+ {
+ enumerator = vips->create_enumerator(vips);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ cp->add_attribute(cp, build_vip(host));
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ enumerator = hydra->attributes->create_initiator_enumerator(
+ hydra->attributes,
+ this->ike_sa->get_other_id(this->ike_sa), vips);
+ while (enumerator->enumerate(enumerator, &handler, &type, &data))
+ {
+ entry_t *entry;
+
+ DBG2(DBG_IKE, "building %N attribute",
+ configuration_attribute_type_names, type);
+ cp->add_attribute(cp,
+ configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
+ type, data));
+ INIT(entry,
+ .type = type,
+ .handler = handler,
+ );
+ this->requested->insert_last(this->requested, entry);
+ }
+ enumerator->destroy(enumerator);
+
+ vips->destroy(vips);
+
+ message->add_payload(message, (payload_t*)cp);
+
+ return NEED_MORE;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ process_payloads(this, message);
+ return NEED_MORE;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ configuration_attribute_type_t type;
+ chunk_t value;
+ cp_payload_t *cp;
+ peer_cfg_t *config;
+ identification_t *id;
+ linked_list_t *vips, *pools;
+ host_t *requested;
+
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
+
+ id = this->ike_sa->get_other_eap_id(this->ike_sa);
+ config = this->ike_sa->get_peer_cfg(this->ike_sa);
+ vips = linked_list_create();
+ pools = linked_list_create_from_enumerator(
+ config->create_pool_enumerator(config));
+
+ this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
+
+ enumerator = this->vips->create_enumerator(this->vips);
+ while (enumerator->enumerate(enumerator, &requested))
+ {
+ host_t *found = NULL;
+
+ /* query all pools until we get an address */
+ DBG1(DBG_IKE, "peer requested virtual IP %H", requested);
+
+ found = hydra->attributes->acquire_address(hydra->attributes,
+ pools, id, requested);
+ if (found)
+ {
+ DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
+ this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
+ cp->add_attribute(cp, build_vip(found));
+ vips->insert_last(vips, found);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'",
+ requested, id);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* query registered providers for additional attributes to include */
+ enumerator = hydra->attributes->create_responder_enumerator(
+ hydra->attributes, pools, id, vips);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ DBG2(DBG_IKE, "building %N attribute",
+ configuration_attribute_type_names, type);
+ cp->add_attribute(cp,
+ configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
+ type, value));
+ }
+ enumerator->destroy(enumerator);
+ vips->destroy_offset(vips, offsetof(host_t, destroy));
+ pools->destroy(pools);
+
+ cp->set_identifier(cp, this->identifier);
+ message->add_payload(message, (payload_t*)cp);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ host_t *host;
+
+ process_payloads(this, message);
+
+ this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
+
+ enumerator = this->vips->create_enumerator(this->vips);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ if (!host->is_anyaddr(host))
+ {
+ this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_mode_config_t *this)
+{
+ return TASK_MODE_CONFIG;
+}
+
+METHOD(task_t, migrate, void,
+ private_mode_config_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
+ this->vips = linked_list_create();
+ this->requested->destroy_function(this->requested, free);
+ this->requested = linked_list_create();
+}
+
+METHOD(task_t, destroy, void,
+ private_mode_config_t *this)
+{
+ this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
+ this->requested->destroy_function(this->requested, free);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_mode_config_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .initiator = initiator,
+ .ike_sa = ike_sa,
+ .requested = linked_list_create(),
+ .vips = linked_list_create(),
+ );
+
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.h b/src/libcharon/sa/ikev1/tasks/mode_config.h
new file mode 100644
index 000000000..462bee374
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/mode_config.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 mode_config mode_config
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef MODE_CONFIG_H_
+#define MODE_CONFIG_H_
+
+typedef struct mode_config_t mode_config_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task of type TASK_MODE_COFNIG, IKEv1 configuration attribute exchange.
+ */
+struct mode_config_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new mode_config task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE for initiator
+ * @return mode_config task to handle by the task_manager
+ */
+mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** MODE_CONFIG_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c
new file mode 100644
index 000000000..db48bc58e
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "quick_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+typedef struct private_quick_delete_t private_quick_delete_t;
+
+/**
+ * Private members of a quick_delete_t task.
+ */
+struct private_quick_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ quick_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Protocol of CHILD_SA to delete
+ */
+ protocol_id_t protocol;
+
+ /**
+ * Inbound SPI of CHILD_SA to delete
+ */
+ u_int32_t spi;
+
+ /**
+ * Send delete even if SA does not exist
+ */
+ bool force;
+
+ /**
+ * SA already expired?
+ */
+ bool expired;
+};
+
+/**
+ * Delete the specified CHILD_SA, if found
+ */
+static bool delete_child(private_quick_delete_t *this,
+ protocol_id_t protocol, u_int32_t spi)
+{
+ u_int64_t bytes_in, bytes_out;
+ child_sa_t *child_sa;
+ bool rekeyed;
+
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, TRUE);
+ if (!child_sa)
+ { /* fallback and check for outbound SA */
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE);
+ if (!child_sa)
+ {
+ return FALSE;
+ }
+ this->spi = spi = child_sa->get_spi(child_sa, TRUE);
+ }
+
+ rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYING;
+ child_sa->set_state(child_sa, CHILD_DELETING);
+
+ if (this->expired)
+ {
+ DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} "
+ "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+ child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ ntohl(child_sa->get_spi(child_sa, TRUE)),
+ ntohl(child_sa->get_spi(child_sa, FALSE)),
+ child_sa->get_traffic_selectors(child_sa, TRUE),
+ child_sa->get_traffic_selectors(child_sa, FALSE));
+ }
+ else
+ {
+ child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in);
+ child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out);
+
+ DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs "
+ "%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
+ child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
+ ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
+ child_sa->get_traffic_selectors(child_sa, TRUE),
+ child_sa->get_traffic_selectors(child_sa, FALSE));
+ }
+
+ if (!rekeyed)
+ {
+ charon->bus->child_updown(charon->bus, child_sa, FALSE);
+ }
+
+ this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
+
+ /* TODO-IKEv1: handle close action? */
+
+ return TRUE;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_quick_delete_t *this, message_t *message)
+{
+ if (delete_child(this, this->protocol, this->spi) || this->force)
+ {
+ delete_payload_t *delete_payload;
+
+ DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
+ protocol_id_names, this->protocol, ntohl(this->spi));
+
+ delete_payload = delete_payload_create(DELETE_V1, PROTO_ESP);
+ delete_payload->add_spi(delete_payload, this->spi);
+ message->add_payload(message, &delete_payload->payload_interface);
+
+ return SUCCESS;
+ }
+ this->ike_sa->flush_queue(this->ike_sa, TASK_QUEUE_ACTIVE);
+ return ALREADY_DONE;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_quick_delete_t *this, message_t *message)
+{
+ return FAILED;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_quick_delete_t *this, message_t *message)
+{
+ enumerator_t *payloads, *spis;
+ payload_t *payload;
+ delete_payload_t *delete_payload;
+ protocol_id_t protocol;
+ u_int32_t spi;
+
+ payloads = message->create_payload_enumerator(message);
+ while (payloads->enumerate(payloads, &payload))
+ {
+ if (payload->get_type(payload) == DELETE_V1)
+ {
+ delete_payload = (delete_payload_t*)payload;
+ protocol = delete_payload->get_protocol_id(delete_payload);
+ if (protocol != PROTO_ESP && protocol != PROTO_AH)
+ {
+ continue;
+ }
+ spis = delete_payload->create_spi_enumerator(delete_payload);
+ while (spis->enumerate(spis, &spi))
+ {
+ DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x",
+ protocol_id_names, protocol, ntohl(spi));
+ if (!delete_child(this, protocol, spi))
+ {
+ DBG1(DBG_IKE, "CHILD_SA not found, ignored");
+ continue;
+ }
+ }
+ spis->destroy(spis);
+ }
+ }
+ payloads->destroy(payloads);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_quick_delete_t *this, message_t *message)
+{
+ return FAILED;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_quick_delete_t *this)
+{
+ return TASK_QUICK_DELETE;
+}
+
+METHOD(task_t, migrate, void,
+ private_quick_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+ private_quick_delete_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+quick_delete_t *quick_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
+ u_int32_t spi, bool force, bool expired)
+{
+ private_quick_delete_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .protocol = protocol,
+ .spi = spi,
+ .force = force,
+ .expired = expired,
+ );
+
+ if (protocol != PROTO_NONE)
+ {
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.h b/src/libcharon/sa/ikev1/tasks/quick_delete.h
new file mode 100644
index 000000000..4df30c8fe
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/quick_delete.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 quick_delete quick_delete
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef QUICK_DELETE_H_
+#define QUICK_DELETE_H_
+
+typedef struct quick_delete_t quick_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+#include <sa/child_sa.h>
+
+/**
+ * Task of type QUICK_DELETE, delete an IKEv1 quick mode SA.
+ */
+struct quick_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new quick_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param protocol protocol of CHILD_SA to delete, PROTO_NONE as responder
+ * @param spi inbound SPI of CHILD_SA to delete
+ * @param force send delete even if SA does not exist
+ * @param expired TRUE if SA already expired
+ * @return quick_delete task to handle by the task_manager
+ */
+quick_delete_t *quick_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
+ u_int32_t spi, bool force, bool expired);
+
+#endif /** QUICK_DELETE_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
new file mode 100644
index 000000000..82a7238c3
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -0,0 +1,1273 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "quick_mode.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <sa/ikev1/keymat_v1.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/payload.h>
+#include <sa/ikev1/tasks/informational.h>
+#include <sa/ikev1/tasks/quick_delete.h>
+#include <processing/jobs/inactivity_job.h>
+
+typedef struct private_quick_mode_t private_quick_mode_t;
+
+/**
+ * Private members of a quick_mode_t task.
+ */
+struct private_quick_mode_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ quick_mode_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * TRUE if we are initiating quick mode
+ */
+ bool initiator;
+
+ /**
+ * Traffic selector of initiator
+ */
+ traffic_selector_t *tsi;
+
+ /**
+ * Traffic selector of responder
+ */
+ traffic_selector_t *tsr;
+
+ /**
+ * Initiators nonce
+ */
+ chunk_t nonce_i;
+
+ /**
+ * Responder nonce
+ */
+ chunk_t nonce_r;
+
+ /**
+ * Initiators ESP SPI
+ */
+ u_int32_t spi_i;
+
+ /**
+ * Responder ESP SPI
+ */
+ u_int32_t spi_r;
+
+ /**
+ * Initiators IPComp CPI
+ */
+ u_int16_t cpi_i;
+
+ /**
+ * Responders IPComp CPI
+ */
+ u_int16_t cpi_r;
+
+ /**
+ * selected CHILD_SA proposal
+ */
+ proposal_t *proposal;
+
+ /**
+ * Config of CHILD_SA to establish
+ */
+ child_cfg_t *config;
+
+ /**
+ * CHILD_SA we are about to establish
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * IKEv1 keymat
+ */
+ keymat_v1_t *keymat;
+
+ /**
+ * DH exchange, when PFS is in use
+ */
+ diffie_hellman_t *dh;
+
+ /**
+ * Negotiated lifetime of new SA
+ */
+ u_int32_t lifetime;
+
+ /**
+ * Negotaited lifebytes of new SA
+ */
+ u_int64_t lifebytes;
+
+ /**
+ * Reqid to use, 0 for auto-allocate
+ */
+ u_int32_t reqid;
+
+ /**
+ * SPI of SA we rekey
+ */
+ u_int32_t rekey;
+
+ /**
+ * Negotiated mode, tunnel or transport
+ */
+ ipsec_mode_t mode;
+
+ /**
+ * Use UDP encapsulation
+ */
+ bool udp;
+
+ /** states of quick mode */
+ enum {
+ QM_INIT,
+ QM_NEGOTIATED,
+ } state;
+};
+
+/**
+ * Schedule inactivity timeout for CHILD_SA with reqid, if enabled
+ */
+static void schedule_inactivity_timeout(private_quick_mode_t *this)
+{
+ u_int32_t timeout;
+ bool close_ike;
+
+ timeout = this->config->get_inactivity(this->config);
+ if (timeout)
+ {
+ close_ike = lib->settings->get_bool(lib->settings,
+ "%s.inactivity_close_ike", FALSE, charon->name);
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)
+ inactivity_job_create(this->child_sa->get_reqid(this->child_sa),
+ timeout, close_ike), timeout);
+ }
+}
+
+/**
+ * Check if we have a an address pool configured
+ */
+static bool have_pool(ike_sa_t *ike_sa)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *peer_cfg;
+ char *pool;
+ bool found = FALSE;
+
+ peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+ if (peer_cfg)
+ {
+ enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
+ if (enumerator->enumerate(enumerator, &pool))
+ {
+ found = TRUE;
+ }
+ enumerator->destroy(enumerator);
+ }
+ return found;
+}
+
+/**
+ * Get hosts to use for dynamic traffic selectors
+ */
+static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
+{
+ enumerator_t *enumerator;
+ linked_list_t *list;
+ host_t *host;
+
+ list = linked_list_create();
+ enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ list->insert_last(list, host);
+ }
+ enumerator->destroy(enumerator);
+
+ if (list->get_count(list) == 0)
+ { /* no virtual IPs assigned */
+ if (local)
+ {
+ host = ike_sa->get_my_host(ike_sa);
+ list->insert_last(list, host);
+ }
+ else if (!have_pool(ike_sa))
+ { /* use host only if we don't have a pool configured */
+ host = ike_sa->get_other_host(ike_sa);
+ list->insert_last(list, host);
+ }
+ }
+ return list;
+}
+
+/**
+ * Install negotiated CHILD_SA
+ */
+static bool install(private_quick_mode_t *this)
+{
+ status_t status, status_i, status_o;
+ chunk_t encr_i, encr_r, integ_i, integ_r;
+ linked_list_t *tsi, *tsr;
+ child_sa_t *old = NULL;
+
+ this->child_sa->set_proposal(this->child_sa, this->proposal);
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
+ this->child_sa->set_mode(this->child_sa, this->mode);
+
+ if (this->cpi_i && this->cpi_r)
+ { /* DEFLATE is the only transform we currently support */
+ this->child_sa->set_ipcomp(this->child_sa, IPCOMP_DEFLATE);
+ }
+ else
+ {
+ this->cpi_i = this->cpi_r = 0;
+ }
+
+ this->child_sa->set_protocol(this->child_sa,
+ this->proposal->get_protocol(this->proposal));
+
+ status_i = status_o = FAILED;
+ encr_i = encr_r = integ_i = integ_r = chunk_empty;
+ tsi = linked_list_create_with_items(this->tsi->clone(this->tsi), NULL);
+ tsr = linked_list_create_with_items(this->tsr->clone(this->tsr), NULL);
+ if (this->initiator)
+ {
+ charon->bus->narrow(charon->bus, this->child_sa,
+ NARROW_INITIATOR_POST_AUTH, tsi, tsr);
+ }
+ else
+ {
+ charon->bus->narrow(charon->bus, this->child_sa,
+ NARROW_RESPONDER_POST, tsr, tsi);
+ }
+ if (tsi->get_count(tsi) == 0 || tsr->get_count(tsr) == 0)
+ {
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+ DBG1(DBG_IKE, "no acceptable traffic selectors found");
+ return FALSE;
+ }
+
+ if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh,
+ this->spi_i, this->spi_r, this->nonce_i, this->nonce_r,
+ &encr_i, &integ_i, &encr_r, &integ_r))
+ {
+ if (this->initiator)
+ {
+ status_i = this->child_sa->install(this->child_sa, encr_r, integ_r,
+ this->spi_i, this->cpi_i, TRUE, FALSE, tsi, tsr);
+ status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
+ this->spi_r, this->cpi_r, FALSE, FALSE, tsi, tsr);
+ }
+ else
+ {
+ status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
+ this->spi_r, this->cpi_r, TRUE, FALSE, tsr, tsi);
+ status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
+ this->spi_i, this->cpi_i, FALSE, FALSE, tsr, tsi);
+ }
+ }
+ chunk_clear(&integ_i);
+ chunk_clear(&integ_r);
+ chunk_clear(&encr_i);
+ chunk_clear(&encr_r);
+
+ if (status_i != SUCCESS || status_o != SUCCESS)
+ {
+ DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel",
+ (status_i != SUCCESS) ? "inbound " : "",
+ (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "",
+ (status_o != SUCCESS) ? "outbound " : "");
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+ return FALSE;
+ }
+
+ if (this->initiator)
+ {
+ status = this->child_sa->add_policies(this->child_sa, tsi, tsr);
+ }
+ else
+ {
+ status = this->child_sa->add_policies(this->child_sa, tsr, tsi);
+ }
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
+ return FALSE;
+ }
+
+ charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
+ this->dh, this->nonce_i, this->nonce_r);
+
+ /* add to IKE_SA, and remove from task */
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+
+ DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
+ "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+ this->child_sa->get_name(this->child_sa),
+ this->child_sa->get_reqid(this->child_sa),
+ ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
+ ntohl(this->child_sa->get_spi(this->child_sa, FALSE)),
+ this->child_sa->get_traffic_selectors(this->child_sa, TRUE),
+ this->child_sa->get_traffic_selectors(this->child_sa, FALSE));
+
+ if (this->rekey)
+ {
+ old = this->ike_sa->get_child_sa(this->ike_sa,
+ this->proposal->get_protocol(this->proposal),
+ this->rekey, TRUE);
+ }
+ if (old)
+ {
+ charon->bus->child_rekey(charon->bus, old, this->child_sa);
+ }
+ else
+ {
+ charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
+ }
+ if (!this->rekey)
+ {
+ schedule_inactivity_timeout(this);
+ }
+ this->child_sa = NULL;
+ return TRUE;
+}
+
+/**
+ * Generate and add NONCE
+ */
+static bool add_nonce(private_quick_mode_t *this, chunk_t *nonce,
+ message_t *message)
+{
+ nonce_payload_t *nonce_payload;
+ nonce_gen_t *nonceg;
+
+ nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
+ if (!nonceg)
+ {
+ DBG1(DBG_IKE, "no nonce generator found to create nonce");
+ return FALSE;
+ }
+ if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, nonce))
+ {
+ DBG1(DBG_IKE, "nonce allocation failed");
+ nonceg->destroy(nonceg);
+ return FALSE;
+ }
+ nonceg->destroy(nonceg);
+
+ nonce_payload = nonce_payload_create(NONCE_V1);
+ nonce_payload->set_nonce(nonce_payload, *nonce);
+ message->add_payload(message, &nonce_payload->payload_interface);
+
+ return TRUE;
+}
+
+/**
+ * Extract nonce from NONCE payload
+ */
+static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce,
+ message_t *message)
+{
+ nonce_payload_t *nonce_payload;
+
+ nonce_payload = (nonce_payload_t*)message->get_payload(message, NONCE_V1);
+ if (!nonce_payload)
+ {
+ DBG1(DBG_IKE, "NONCE payload missing in message");
+ return FALSE;
+ }
+ *nonce = nonce_payload->get_nonce(nonce_payload);
+
+ return TRUE;
+}
+
+/**
+ * Add KE payload to message
+ */
+static void add_ke(private_quick_mode_t *this, message_t *message)
+{
+ ke_payload_t *ke_payload;
+
+ ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1, this->dh);
+ message->add_payload(message, &ke_payload->payload_interface);
+}
+
+/**
+ * Get DH value from a KE payload
+ */
+static bool get_ke(private_quick_mode_t *this, message_t *message)
+{
+ ke_payload_t *ke_payload;
+
+ ke_payload = (ke_payload_t*)message->get_payload(message, KEY_EXCHANGE_V1);
+ if (!ke_payload)
+ {
+ DBG1(DBG_IKE, "KE payload missing");
+ return FALSE;
+ }
+ this->dh->set_other_public_value(this->dh,
+ ke_payload->get_key_exchange_data(ke_payload));
+ return TRUE;
+}
+
+/**
+ * Select a traffic selector from configuration
+ */
+static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
+ linked_list_t *supplied)
+{
+ traffic_selector_t *ts;
+ linked_list_t *list, *hosts;
+
+ hosts = get_dynamic_hosts(this->ike_sa, local);
+ list = this->config->get_traffic_selectors(this->config,
+ local, supplied, hosts);
+ hosts->destroy(hosts);
+ if (list->get_first(list, (void**)&ts) == SUCCESS)
+ {
+ ts = ts->clone(ts);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "%s traffic selector missing in configuration",
+ local ? "local" : "local");
+ ts = NULL;
+ }
+ list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
+ return ts;
+}
+
+/**
+ * Add selected traffic selectors to message
+ */
+static void add_ts(private_quick_mode_t *this, message_t *message)
+{
+ id_payload_t *id_payload;
+ host_t *hsi, *hsr;
+
+ if (this->initiator)
+ {
+ hsi = this->ike_sa->get_my_host(this->ike_sa);
+ hsr = this->ike_sa->get_other_host(this->ike_sa);
+ }
+ else
+ {
+ hsr = this->ike_sa->get_my_host(this->ike_sa);
+ hsi = this->ike_sa->get_other_host(this->ike_sa);
+ }
+ /* add ID payload only if negotiating non host2host tunnels */
+ if (!this->tsi->is_host(this->tsi, hsi) ||
+ !this->tsr->is_host(this->tsr, hsr) ||
+ this->tsi->get_protocol(this->tsi) ||
+ this->tsr->get_protocol(this->tsr) ||
+ this->tsi->get_from_port(this->tsi) ||
+ this->tsr->get_from_port(this->tsr) ||
+ this->tsi->get_to_port(this->tsi) != 65535 ||
+ this->tsr->get_to_port(this->tsr) != 65535)
+ {
+ id_payload = id_payload_create_from_ts(this->tsi);
+ message->add_payload(message, &id_payload->payload_interface);
+ id_payload = id_payload_create_from_ts(this->tsr);
+ message->add_payload(message, &id_payload->payload_interface);
+ }
+}
+
+/**
+ * Get traffic selectors from received message
+ */
+static bool get_ts(private_quick_mode_t *this, message_t *message)
+{
+ traffic_selector_t *tsi = NULL, *tsr = NULL;
+ enumerator_t *enumerator;
+ id_payload_t *id_payload;
+ payload_t *payload;
+ host_t *hsi, *hsr;
+ bool first = TRUE;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == ID_V1)
+ {
+ id_payload = (id_payload_t*)payload;
+
+ if (first)
+ {
+ tsi = id_payload->get_ts(id_payload);
+ first = FALSE;
+ }
+ else
+ {
+ tsr = id_payload->get_ts(id_payload);
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* create host2host selectors if ID payloads missing */
+ if (this->initiator)
+ {
+ hsi = this->ike_sa->get_my_host(this->ike_sa);
+ hsr = this->ike_sa->get_other_host(this->ike_sa);
+ }
+ else
+ {
+ hsr = this->ike_sa->get_my_host(this->ike_sa);
+ hsi = this->ike_sa->get_other_host(this->ike_sa);
+ }
+ if (!tsi)
+ {
+ tsi = traffic_selector_create_from_subnet(hsi->clone(hsi),
+ hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0);
+ }
+ if (!tsr)
+ {
+ tsr = traffic_selector_create_from_subnet(hsr->clone(hsr),
+ hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0);
+ }
+ if (!this->initiator && this->mode == MODE_TRANSPORT && this->udp &&
+ (!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr)))
+ { /* change TS in case of a NAT in transport mode */
+ DBG2(DBG_IKE, "changing received traffic selectors %R=== %R due to NAT",
+ tsi, tsr);
+ tsi->set_address(tsi, hsi);
+ tsr->set_address(tsr, hsr);
+ }
+
+ if (this->initiator)
+ {
+ /* check if peer selection valid */
+ if (!tsr->is_contained_in(tsr, this->tsr) ||
+ !tsi->is_contained_in(tsi, this->tsi))
+ {
+ DBG1(DBG_IKE, "peer selected invalid traffic selectors: ",
+ "%R for %R, %R for %R", tsi, this->tsi, tsr, this->tsr);
+ tsi->destroy(tsi);
+ tsr->destroy(tsr);
+ return FALSE;
+ }
+ this->tsi->destroy(this->tsi);
+ this->tsr->destroy(this->tsr);
+ this->tsi = tsi;
+ this->tsr = tsr;
+ }
+ else
+ {
+ this->tsi = tsi;
+ this->tsr = tsr;
+ }
+ return TRUE;
+}
+
+/**
+ * Add NAT-OA payloads
+ */
+static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
+{
+ identification_t *id;
+ id_payload_t *nat_oa;
+ host_t *src, *dst;
+
+ src = message->get_source(message);
+ dst = message->get_destination(message);
+
+ src = this->initiator ? src : dst;
+ dst = this->initiator ? dst : src;
+
+ /* first NAT-OA is the initiator's address */
+ id = identification_create_from_sockaddr(src->get_sockaddr(src));
+ nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
+ message->add_payload(message, (payload_t*)nat_oa);
+ id->destroy(id);
+
+ /* second NAT-OA is that of the responder */
+ id = identification_create_from_sockaddr(dst->get_sockaddr(dst));
+ nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
+ message->add_payload(message, (payload_t*)nat_oa);
+ id->destroy(id);
+}
+
+/**
+ * Look up lifetimes
+ */
+static void get_lifetimes(private_quick_mode_t *this)
+{
+ lifetime_cfg_t *lft;
+
+ lft = this->config->get_lifetime(this->config);
+ if (lft->time.life)
+ {
+ this->lifetime = lft->time.life;
+ }
+ else if (lft->bytes.life)
+ {
+ this->lifebytes = lft->bytes.life;
+ }
+ free(lft);
+}
+
+/**
+ * Check and apply lifetimes
+ */
+static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload)
+{
+ u_int32_t lifetime;
+ u_int64_t lifebytes;
+
+ lifetime = sa_payload->get_lifetime(sa_payload);
+ lifebytes = sa_payload->get_lifebytes(sa_payload);
+ if (this->lifetime != lifetime)
+ {
+ DBG1(DBG_IKE, "received %us lifetime, configured %us",
+ lifetime, this->lifetime);
+ this->lifetime = lifetime;
+ }
+ if (this->lifebytes != lifebytes)
+ {
+ DBG1(DBG_IKE, "received %llu lifebytes, configured %llu",
+ lifebytes, this->lifebytes);
+ this->lifebytes = lifebytes;
+ }
+}
+
+/**
+ * Set the task ready to build notify error message
+ */
+static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
+{
+ notify_payload_t *notify;
+
+ notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
+ PROTO_ESP, type);
+ notify->set_spi(notify, this->spi_i);
+
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)informational_create(this->ike_sa, notify));
+ /* cancel all active/passive tasks in favour of informational */
+ this->ike_sa->flush_queue(this->ike_sa,
+ this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE);
+ return ALREADY_DONE;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_quick_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case QM_INIT:
+ {
+ enumerator_t *enumerator;
+ sa_payload_t *sa_payload;
+ linked_list_t *list, *tsi, *tsr;
+ proposal_t *proposal;
+ diffie_hellman_group_t group;
+
+ this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
+ this->mode = this->config->get_mode(this->config);
+ this->child_sa = child_sa_create(
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->config, this->reqid, this->udp);
+
+ if (this->udp && this->mode == MODE_TRANSPORT)
+ {
+ /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
+ add_nat_oa_payloads(this, message);
+ }
+
+ if (this->config->use_ipcomp(this->config))
+ {
+ if (this->udp)
+ {
+ DBG1(DBG_IKE, "IPComp is not supported if either peer is "
+ "natted, IPComp disabled");
+ }
+ else
+ {
+ this->cpi_i = this->child_sa->alloc_cpi(this->child_sa);
+ if (!this->cpi_i)
+ {
+ DBG1(DBG_IKE, "unable to allocate a CPI from kernel, "
+ "IPComp disabled");
+ }
+ }
+ }
+
+ this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+ if (!this->spi_i)
+ {
+ DBG1(DBG_IKE, "allocating SPI from kernel failed");
+ return FAILED;
+ }
+
+ list = this->config->get_proposals(this->config, FALSE);
+ enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &proposal))
+ {
+ proposal->set_spi(proposal, this->spi_i);
+ }
+ enumerator->destroy(enumerator);
+
+ get_lifetimes(this);
+ sa_payload = sa_payload_create_from_proposals_v1(list,
+ this->lifetime, this->lifebytes, AUTH_NONE,
+ this->mode, this->udp, this->cpi_i);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+ message->add_payload(message, &sa_payload->payload_interface);
+
+ if (!add_nonce(this, &this->nonce_i, message))
+ {
+ return FAILED;
+ }
+
+ group = this->config->get_dh_group(this->config);
+ if (group != MODP_NONE)
+ {
+ this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
+ group);
+ if (!this->dh)
+ {
+ DBG1(DBG_IKE, "configured DH group %N not supported",
+ diffie_hellman_group_names, group);
+ return FAILED;
+ }
+ add_ke(this, message);
+ }
+ if (!this->tsi)
+ {
+ this->tsi = select_ts(this, TRUE, NULL);
+ }
+ if (!this->tsr)
+ {
+ this->tsr = select_ts(this, FALSE, NULL);
+ }
+ tsi = linked_list_create_with_items(this->tsi, NULL);
+ tsr = linked_list_create_with_items(this->tsr, NULL);
+ this->tsi = this->tsr = NULL;
+ charon->bus->narrow(charon->bus, this->child_sa,
+ NARROW_INITIATOR_PRE_AUTH, tsi, tsr);
+ tsi->remove_first(tsi, (void**)&this->tsi);
+ tsr->remove_first(tsr, (void**)&this->tsr);
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+ if (!this->tsi || !this->tsr)
+ {
+ return FAILED;
+ }
+ add_ts(this, message);
+ return NEED_MORE;
+ }
+ case QM_NEGOTIATED:
+ {
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+/**
+ * Check for notify errors, return TRUE if error found
+ */
+static bool has_notify_errors(private_quick_mode_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+ bool err = FALSE;
+
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == NOTIFY_V1)
+ {
+ notify_payload_t *notify;
+ notify_type_t type;
+
+ notify = (notify_payload_t*)payload;
+ type = notify->get_notify_type(notify);
+ if (type < 16384)
+ {
+
+ DBG1(DBG_IKE, "received %N error notify",
+ notify_type_names, type);
+ err = TRUE;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received %N notify", notify_type_names, type);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return err;
+}
+
+/**
+ * Check if this is a rekey for an existing CHILD_SA, reuse reqid if so
+ */
+static void check_for_rekeyed_child(private_quick_mode_t *this)
+{
+ enumerator_t *enumerator, *policies;
+ traffic_selector_t *local, *remote;
+ child_sa_t *child_sa;
+
+ enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
+ while (this->reqid == 0 && enumerator->enumerate(enumerator, &child_sa))
+ {
+ if (child_sa->get_state(child_sa) == CHILD_INSTALLED &&
+ streq(child_sa->get_name(child_sa),
+ this->config->get_name(this->config)))
+ {
+ policies = child_sa->create_policy_enumerator(child_sa);
+ if (policies->enumerate(policies, &local, &remote))
+ {
+ if (local->equals(local, this->tsr) &&
+ remote->equals(remote, this->tsi) &&
+ this->proposal->equals(this->proposal,
+ child_sa->get_proposal(child_sa)))
+ {
+ this->reqid = child_sa->get_reqid(child_sa);
+ this->rekey = child_sa->get_spi(child_sa, TRUE);
+ child_sa->set_state(child_sa, CHILD_REKEYING);
+ DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}",
+ child_sa->get_name(child_sa), this->reqid);
+ }
+ }
+ policies->destroy(policies);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+METHOD(task_t, process_r, status_t,
+ private_quick_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case QM_INIT:
+ {
+ sa_payload_t *sa_payload;
+ linked_list_t *tsi, *tsr, *hostsi, *hostsr, *list = NULL;
+ peer_cfg_t *peer_cfg;
+ u_int16_t group;
+ bool private;
+
+ sa_payload = (sa_payload_t*)message->get_payload(message,
+ SECURITY_ASSOCIATION_V1);
+ if (!sa_payload)
+ {
+ DBG1(DBG_IKE, "sa payload missing");
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+
+ this->mode = sa_payload->get_encap_mode(sa_payload, &this->udp);
+
+ if (!get_ts(this, message))
+ {
+ return FAILED;
+ }
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ tsi = linked_list_create_with_items(this->tsi, NULL);
+ tsr = linked_list_create_with_items(this->tsr, NULL);
+ this->tsi = this->tsr = NULL;
+ hostsi = get_dynamic_hosts(this->ike_sa, FALSE);
+ hostsr = get_dynamic_hosts(this->ike_sa, TRUE);
+ this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
+ hostsr, hostsi);
+ hostsi->destroy(hostsi);
+ hostsr->destroy(hostsr);
+ if (this->config)
+ {
+ this->tsi = select_ts(this, FALSE, tsi);
+ this->tsr = select_ts(this, TRUE, tsr);
+ }
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+ if (!this->config || !this->tsi || !this->tsr)
+ {
+ DBG1(DBG_IKE, "no matching CHILD_SA config found");
+ return send_notify(this, INVALID_ID_INFORMATION);
+ }
+
+ if (this->config->use_ipcomp(this->config))
+ {
+ if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
+ {
+ DBG1(DBG_IKE, "IPComp is not supported if either peer is "
+ "natted, IPComp disabled");
+ }
+ else
+ {
+ list = sa_payload->get_ipcomp_proposals(sa_payload,
+ &this->cpi_i);
+ if (!list->get_count(list))
+ {
+ DBG1(DBG_IKE, "expected IPComp proposal but peer did "
+ "not send one, IPComp disabled");
+ this->cpi_i = 0;
+ }
+ }
+ }
+ if (!list || !list->get_count(list))
+ {
+ DESTROY_IF(list);
+ 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,
+ list, FALSE, private);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+
+ get_lifetimes(this);
+ apply_lifetimes(this, sa_payload);
+
+ if (!this->proposal)
+ {
+ DBG1(DBG_IKE, "no matching proposal found, sending %N",
+ notify_type_names, NO_PROPOSAL_CHOSEN);
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->spi_i = this->proposal->get_spi(this->proposal);
+
+ if (!get_nonce(this, &this->nonce_i, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+
+ if (this->proposal->get_algorithm(this->proposal,
+ DIFFIE_HELLMAN_GROUP, &group, NULL))
+ {
+ this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
+ group);
+ if (!this->dh)
+ {
+ DBG1(DBG_IKE, "negotiated DH group %N not supported",
+ diffie_hellman_group_names, group);
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!get_ke(this, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ }
+
+ check_for_rekeyed_child(this);
+
+ this->child_sa = child_sa_create(
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->config, this->reqid, this->udp);
+
+ tsi = linked_list_create_with_items(this->tsi, NULL);
+ tsr = linked_list_create_with_items(this->tsr, NULL);
+ this->tsi = this->tsr = NULL;
+ charon->bus->narrow(charon->bus, this->child_sa,
+ NARROW_RESPONDER, tsr, tsi);
+ if (tsi->remove_first(tsi, (void**)&this->tsi) != SUCCESS ||
+ tsr->remove_first(tsr, (void**)&this->tsr) != SUCCESS)
+ {
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+ return send_notify(this, INVALID_ID_INFORMATION);
+ }
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+
+ return NEED_MORE;
+ }
+ case QM_NEGOTIATED:
+ {
+ if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
+ has_notify_errors(this, message))
+ {
+ return SUCCESS;
+ }
+ if (!install(this))
+ {
+ ike_sa_t *ike_sa = this->ike_sa;
+ task_t *task;
+
+ task = (task_t*)quick_delete_create(this->ike_sa,
+ this->proposal->get_protocol(this->proposal),
+ this->spi_i, TRUE, TRUE);
+ /* flush_queue() destroys the current task */
+ ike_sa->flush_queue(ike_sa, TASK_QUEUE_PASSIVE);
+ ike_sa->queue_task(ike_sa, task);
+ return ALREADY_DONE;
+ }
+ return SUCCESS;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, build_r, status_t,
+ private_quick_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case QM_INIT:
+ {
+ sa_payload_t *sa_payload;
+
+ this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+ if (!this->spi_r)
+ {
+ DBG1(DBG_IKE, "allocating SPI from kernel failed");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->proposal->set_spi(this->proposal, this->spi_r);
+
+ if (this->cpi_i)
+ {
+ this->cpi_r = this->child_sa->alloc_cpi(this->child_sa);
+ if (!this->cpi_r)
+ {
+ DBG1(DBG_IKE, "unable to allocate a CPI from "
+ "kernel, IPComp disabled");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ }
+
+ if (this->udp && this->mode == MODE_TRANSPORT)
+ {
+ /* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
+ add_nat_oa_payloads(this, message);
+ }
+
+ sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
+ this->lifetime, this->lifebytes, AUTH_NONE,
+ this->mode, this->udp, this->cpi_r);
+ message->add_payload(message, &sa_payload->payload_interface);
+
+ if (!add_nonce(this, &this->nonce_r, message))
+ {
+ return FAILED;
+ }
+ if (this->dh)
+ {
+ add_ke(this, message);
+ }
+
+ add_ts(this, message);
+
+ this->state = QM_NEGOTIATED;
+ return NEED_MORE;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, process_i, status_t,
+ private_quick_mode_t *this, message_t *message)
+{
+ switch (this->state)
+ {
+ case QM_INIT:
+ {
+ sa_payload_t *sa_payload;
+ linked_list_t *list = NULL;
+ bool private;
+
+ sa_payload = (sa_payload_t*)message->get_payload(message,
+ SECURITY_ASSOCIATION_V1);
+ if (!sa_payload)
+ {
+ DBG1(DBG_IKE, "sa payload missing");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ if (this->cpi_i)
+ {
+ list = sa_payload->get_ipcomp_proposals(sa_payload,
+ &this->cpi_r);
+ if (!list->get_count(list))
+ {
+ DBG1(DBG_IKE, "peer did not acccept our IPComp proposal, "
+ "IPComp disabled");
+ this->cpi_i = 0;
+ }
+ }
+ if (!list || !list->get_count(list))
+ {
+ DESTROY_IF(list);
+ 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,
+ list, FALSE, private);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+ if (!this->proposal)
+ {
+ DBG1(DBG_IKE, "no matching proposal found");
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->spi_r = this->proposal->get_spi(this->proposal);
+
+ apply_lifetimes(this, sa_payload);
+
+ if (!get_nonce(this, &this->nonce_r, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ if (this->dh && !get_ke(this, message))
+ {
+ return send_notify(this, INVALID_KEY_INFORMATION);
+ }
+ if (!get_ts(this, message))
+ {
+ return send_notify(this, INVALID_PAYLOAD_TYPE);
+ }
+ if (!install(this))
+ {
+ return send_notify(this, NO_PROPOSAL_CHOSEN);
+ }
+ this->state = QM_NEGOTIATED;
+ return NEED_MORE;
+ }
+ default:
+ return FAILED;
+ }
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_quick_mode_t *this)
+{
+ return TASK_QUICK_MODE;
+}
+
+METHOD(quick_mode_t, use_reqid, void,
+ private_quick_mode_t *this, u_int32_t reqid)
+{
+ this->reqid = reqid;
+}
+
+METHOD(quick_mode_t, rekey, void,
+ private_quick_mode_t *this, u_int32_t spi)
+{
+ this->rekey = spi;
+}
+
+METHOD(task_t, migrate, void,
+ private_quick_mode_t *this, ike_sa_t *ike_sa)
+{
+ chunk_free(&this->nonce_i);
+ chunk_free(&this->nonce_r);
+ DESTROY_IF(this->tsi);
+ DESTROY_IF(this->tsr);
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->child_sa);
+ DESTROY_IF(this->dh);
+
+ this->ike_sa = ike_sa;
+ this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
+ this->state = QM_INIT;
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->proposal = NULL;
+ this->child_sa = NULL;
+ this->dh = NULL;
+ this->spi_i = 0;
+ this->spi_r = 0;
+
+ if (!this->initiator)
+ {
+ DESTROY_IF(this->config);
+ this->config = NULL;
+ }
+}
+
+METHOD(task_t, destroy, void,
+ private_quick_mode_t *this)
+{
+ chunk_free(&this->nonce_i);
+ chunk_free(&this->nonce_r);
+ DESTROY_IF(this->tsi);
+ DESTROY_IF(this->tsr);
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->child_sa);
+ DESTROY_IF(this->config);
+ DESTROY_IF(this->dh);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
+ traffic_selector_t *tsi, traffic_selector_t *tsr)
+{
+ private_quick_mode_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ .use_reqid = _use_reqid,
+ .rekey = _rekey,
+ },
+ .ike_sa = ike_sa,
+ .initiator = config != NULL,
+ .config = config,
+ .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
+ .state = QM_INIT,
+ .tsi = tsi ? tsi->clone(tsi) : NULL,
+ .tsr = tsr ? tsr->clone(tsr) : NULL,
+ );
+
+ if (config)
+ {
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.h b/src/libcharon/sa/ikev1/tasks/quick_mode.h
new file mode 100644
index 000000000..0b80cb836
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 quick_mode quick_mode
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef QUICK_MODE_H_
+#define QUICK_MODE_H_
+
+typedef struct quick_mode_t quick_mode_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * IKEv1 quick mode, establishes a CHILD_SA in IKEv1.
+ */
+struct quick_mode_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * Use a specific reqid to install this CHILD_SA.
+ *
+ * @param reqid reqid to use
+ */
+ void (*use_reqid)(quick_mode_t *this, u_int32_t reqid);
+
+ /**
+ * Set the SPI of the old SA, if rekeying.
+ *
+ * @param spi spi of SA to rekey
+ */
+ void (*rekey)(quick_mode_t *this, u_int32_t spi);
+};
+
+/**
+ * Create a new quick_mode task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param config child_cfg if task initiator, NULL if responder
+ * @param tsi source of triggering packet, or NULL
+ * @param tsr destination of triggering packet, or NULL
+ * @return task to handle by the task_manager
+ */
+quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
+ traffic_selector_t *tsi, traffic_selector_t *tsr);
+
+#endif /** QUICK_MODE_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c
new file mode 100644
index 000000000..10bea5636
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/xauth.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "xauth.h"
+
+#include <daemon.h>
+#include <hydra.h>
+#include <encoding/payloads/cp_payload.h>
+#include <processing/jobs/adopt_children_job.h>
+
+typedef struct private_xauth_t private_xauth_t;
+
+/**
+ * Status types exchanged
+ */
+typedef enum {
+ XAUTH_FAILED = 0,
+ XAUTH_OK = 1,
+} xauth_status_t;
+
+/**
+ * Private members of a xauth_t task.
+ */
+struct private_xauth_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ xauth_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the XAUTH initiator?
+ */
+ bool initiator;
+
+ /**
+ * XAuth backend to use
+ */
+ xauth_method_t *xauth;
+
+ /**
+ * XAuth username
+ */
+ identification_t *user;
+
+ /**
+ * Generated configuration payload
+ */
+ cp_payload_t *cp;
+
+ /**
+ * received identifier
+ */
+ u_int16_t identifier;
+
+ /**
+ * status of Xauth exchange
+ */
+ xauth_status_t status;
+};
+
+/**
+ * Load XAuth backend
+ */
+static xauth_method_t *load_method(private_xauth_t* this)
+{
+ identification_t *server, *peer;
+ enumerator_t *enumerator;
+ xauth_method_t *xauth;
+ xauth_role_t role;
+ peer_cfg_t *peer_cfg;
+ auth_cfg_t *auth;
+ char *name;
+
+ if (this->initiator)
+ {
+ server = this->ike_sa->get_my_id(this->ike_sa);
+ peer = this->ike_sa->get_other_id(this->ike_sa);
+ role = XAUTH_SERVER;
+ }
+ else
+ {
+ peer = this->ike_sa->get_my_id(this->ike_sa);
+ server = this->ike_sa->get_other_id(this->ike_sa);
+ role = XAUTH_PEER;
+ }
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, !this->initiator);
+ if (!enumerator->enumerate(enumerator, &auth) ||
+ (uintptr_t)auth->get(auth, AUTH_RULE_AUTH_CLASS) != AUTH_CLASS_XAUTH)
+ {
+ if (!enumerator->enumerate(enumerator, &auth) ||
+ (uintptr_t)auth->get(auth, AUTH_RULE_AUTH_CLASS) != AUTH_CLASS_XAUTH)
+ {
+ DBG1(DBG_CFG, "no XAuth authentication round found");
+ enumerator->destroy(enumerator);
+ return NULL;
+ }
+ }
+ name = auth->get(auth, AUTH_RULE_XAUTH_BACKEND);
+ this->user = auth->get(auth, AUTH_RULE_XAUTH_IDENTITY);
+ enumerator->destroy(enumerator);
+ if (!this->initiator && this->user)
+ { /* use XAUTH username, if configured */
+ peer = this->user;
+ }
+ xauth = charon->xauth->create_instance(charon->xauth, name, role,
+ server, peer);
+ if (!xauth)
+ {
+ if (name)
+ {
+ DBG1(DBG_CFG, "no XAuth method found named '%s'", name);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "no XAuth method found");
+ }
+ }
+ return xauth;
+}
+
+/**
+ * Check if XAuth connection is allowed to succeed
+ */
+static bool allowed(private_xauth_t *this)
+{
+ if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+ this->ike_sa, FALSE))
+ {
+ DBG1(DBG_IKE, "cancelling XAuth due to uniqueness policy");
+ return FALSE;
+ }
+ if (!charon->bus->authorize(charon->bus, FALSE))
+ {
+ DBG1(DBG_IKE, "XAuth authorization hook forbids IKE_SA, cancelling");
+ return FALSE;
+ }
+ if (!charon->bus->authorize(charon->bus, TRUE))
+ {
+ DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * Set IKE_SA to established state
+ */
+static bool establish(private_xauth_t *this)
+{
+ 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 TRUE;
+}
+
+/**
+ * Check if we are compliant to a given peer config
+ */
+static bool is_compliant(private_xauth_t *this, peer_cfg_t *peer_cfg, bool log)
+{
+ bool complies = TRUE;
+ enumerator_t *e1, *e2;
+ auth_cfg_t *c1, *c2;
+
+ e1 = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
+ e2 = this->ike_sa->create_auth_cfg_enumerator(this->ike_sa, FALSE);
+ while (e1->enumerate(e1, &c1))
+ {
+ if (!e2->enumerate(e2, &c2) || !c2->complies(c2, c1, log))
+ {
+ complies = FALSE;
+ break;
+ }
+ }
+ e1->destroy(e1);
+ e2->destroy(e2);
+
+ return complies;
+}
+
+/**
+ * Check if we are compliant to current config, switch to another if not
+ */
+static bool select_compliant_config(private_xauth_t *this)
+{
+ peer_cfg_t *peer_cfg = NULL, *old, *current;
+ identification_t *my_id, *other_id;
+ host_t *my_host, *other_host;
+ enumerator_t *enumerator;
+ bool aggressive;
+
+ old = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (is_compliant(this, old, TRUE))
+ { /* current config is fine */
+ return TRUE;
+ }
+ DBG1(DBG_CFG, "selected peer config '%s' inacceptable",
+ old->get_name(old));
+ aggressive = old->use_aggressive(old);
+
+ my_host = this->ike_sa->get_my_host(this->ike_sa);
+ other_host = this->ike_sa->get_other_host(this->ike_sa);
+ my_id = this->ike_sa->get_my_id(this->ike_sa);
+ other_id = this->ike_sa->get_other_id(this->ike_sa);
+ enumerator = charon->backends->create_peer_cfg_enumerator(charon->backends,
+ my_host, other_host, my_id, other_id, IKEV1);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (!current->equals(current, old) &&
+ current->use_aggressive(current) == aggressive &&
+ is_compliant(this, current, FALSE))
+ {
+ peer_cfg = current;
+ break;
+ }
+ }
+ if (peer_cfg)
+ {
+ DBG1(DBG_CFG, "switching to peer config '%s'",
+ peer_cfg->get_name(peer_cfg));
+ this->ike_sa->set_peer_cfg(this->ike_sa, peer_cfg);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "no alternative config found");
+ }
+ enumerator->destroy(enumerator);
+
+ return peer_cfg != NULL;
+}
+
+/**
+ * Create auth config after successful authentication
+ */
+static bool add_auth_cfg(private_xauth_t *this, identification_t *id, bool local)
+{
+ auth_cfg_t *auth;
+
+ auth = auth_cfg_create();
+ auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_XAUTH);
+ auth->add(auth, AUTH_RULE_XAUTH_IDENTITY, id->clone(id));
+ auth->merge(auth, this->ike_sa->get_auth_cfg(this->ike_sa, local), FALSE);
+ this->ike_sa->add_auth_cfg(this->ike_sa, local, auth);
+
+ return select_compliant_config(this);
+}
+
+METHOD(task_t, build_i_status, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ cp_payload_t *cp;
+
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_SET);
+ cp->add_attribute(cp,
+ configuration_attribute_create_value(XAUTH_STATUS, this->status));
+
+ message->add_payload(message, (payload_t *)cp);
+
+ return NEED_MORE;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ if (!this->xauth)
+ {
+ cp_payload_t *cp;
+
+ this->xauth = load_method(this);
+ if (!this->xauth)
+ {
+ return FAILED;
+ }
+ if (this->xauth->initiate(this->xauth, &cp) != NEED_MORE)
+ {
+ return FAILED;
+ }
+ message->add_payload(message, (payload_t *)cp);
+ return NEED_MORE;
+ }
+
+ if (this->cp)
+ { /* send previously generated payload */
+ message->add_payload(message, (payload_t *)this->cp);
+ this->cp = NULL;
+ return NEED_MORE;
+ }
+ return FAILED;
+}
+
+METHOD(task_t, build_r_ack, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ cp_payload_t *cp;
+
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_ACK);
+ cp->set_identifier(cp, this->identifier);
+ cp->add_attribute(cp,
+ configuration_attribute_create_chunk(
+ CONFIGURATION_ATTRIBUTE_V1, XAUTH_STATUS, chunk_empty));
+
+ message->add_payload(message, (payload_t *)cp);
+
+ if (this->status == XAUTH_OK && allowed(this) && establish(this))
+ {
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ cp_payload_t *cp;
+
+ if (!this->xauth)
+ {
+ this->xauth = load_method(this);
+ if (!this->xauth)
+ { /* send empty reply */
+ return NEED_MORE;
+ }
+ }
+ cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
+ if (!cp)
+ {
+ DBG1(DBG_IKE, "configuration payload missing in XAuth request");
+ return FAILED;
+ }
+ if (cp->get_type(cp) == CFG_REQUEST)
+ {
+ switch (this->xauth->process(this->xauth, cp, &this->cp))
+ {
+ case NEED_MORE:
+ return NEED_MORE;
+ case SUCCESS:
+ case FAILED:
+ default:
+ break;
+ }
+ this->cp = NULL;
+ return NEED_MORE;
+ }
+ if (cp->get_type(cp) == CFG_SET)
+ {
+ configuration_attribute_t *attribute;
+ enumerator_t *enumerator;
+
+ enumerator = cp->create_attribute_enumerator(cp);
+ while (enumerator->enumerate(enumerator, &attribute))
+ {
+ if (attribute->get_type(attribute) == XAUTH_STATUS)
+ {
+ this->status = attribute->get_value(attribute);
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (this->status == XAUTH_OK &&
+ add_auth_cfg(this, this->xauth->get_identity(this->xauth), TRUE))
+ {
+ DBG1(DBG_IKE, "XAuth authentication of '%Y' (myself) successful",
+ this->xauth->get_identity(this->xauth));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "XAuth authentication of '%Y' (myself) failed",
+ this->xauth->get_identity(this->xauth));
+ }
+ }
+ this->identifier = cp->get_identifier(cp);
+ this->public.task.build = _build_r_ack;
+ return NEED_MORE;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ if (!this->cp)
+ { /* send empty reply if building data failed */
+ this->cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY);
+ }
+ message->add_payload(message, (payload_t *)this->cp);
+ this->cp = NULL;
+ return NEED_MORE;
+}
+
+METHOD(task_t, process_i_status, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ cp_payload_t *cp;
+
+ cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
+ if (!cp || cp->get_type(cp) != CFG_ACK)
+ {
+ DBG1(DBG_IKE, "received invalid XAUTH status response");
+ return FAILED;
+ }
+ if (this->status != XAUTH_OK)
+ {
+ DBG1(DBG_IKE, "destroying IKE_SA after failed XAuth authentication");
+ return FAILED;
+ }
+ if (!establish(this))
+ {
+ return FAILED;
+ }
+ this->ike_sa->set_condition(this->ike_sa, COND_XAUTH_AUTHENTICATED, TRUE);
+ lib->processor->queue_job(lib->processor, (job_t*)
+ adopt_children_job_create(this->ike_sa->get_id(this->ike_sa)));
+ return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ identification_t *id;
+ cp_payload_t *cp;
+
+ cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
+ if (!cp)
+ {
+ DBG1(DBG_IKE, "configuration payload missing in XAuth response");
+ return FAILED;
+ }
+ switch (this->xauth->process(this->xauth, cp, &this->cp))
+ {
+ case NEED_MORE:
+ return NEED_MORE;
+ case SUCCESS:
+ id = this->xauth->get_identity(this->xauth);
+ if (this->user && !id->matches(id, this->user))
+ {
+ DBG1(DBG_IKE, "XAuth username '%Y' does not match to "
+ "configured username '%Y'", id, this->user);
+ break;
+ }
+ DBG1(DBG_IKE, "XAuth authentication of '%Y' successful", id);
+ if (add_auth_cfg(this, id, FALSE) && allowed(this))
+ {
+ this->status = XAUTH_OK;
+ }
+ break;
+ case FAILED:
+ DBG1(DBG_IKE, "XAuth authentication of '%Y' failed",
+ this->xauth->get_identity(this->xauth));
+ break;
+ default:
+ return FAILED;
+ }
+ this->public.task.build = _build_i_status;
+ this->public.task.process = _process_i_status;
+ return NEED_MORE;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_xauth_t *this)
+{
+ return TASK_XAUTH;
+}
+
+METHOD(task_t, migrate, void,
+ private_xauth_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->xauth);
+ DESTROY_IF(this->cp);
+
+ this->ike_sa = ike_sa;
+ this->xauth = NULL;
+ this->cp = NULL;
+ this->user = NULL;
+ this->status = XAUTH_FAILED;
+
+ if (this->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;
+ }
+}
+
+METHOD(task_t, destroy, void,
+ private_xauth_t *this)
+{
+ DESTROY_IF(this->xauth);
+ DESTROY_IF(this->cp);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+xauth_t *xauth_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_xauth_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .initiator = initiator,
+ .ike_sa = ike_sa,
+ .status = XAUTH_FAILED,
+ );
+
+ 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;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.h b/src/libcharon/sa/ikev1/tasks/xauth.h
new file mode 100644
index 000000000..303eb31ce
--- /dev/null
+++ b/src/libcharon/sa/ikev1/tasks/xauth.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 xauth_t xauth
+ * @{ @ingroup tasks_v1
+ */
+
+#ifndef XAUTH_H_
+#define XAUTH_H_
+
+typedef struct xauth_t xauth_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task of type TASK_XAUTH, additional authentication after main/aggressive mode.
+ */
+struct xauth_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new xauth task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE for initiator
+ * @return xauth task to handle by the task_manager
+ */
+xauth_t *xauth_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /** XAUTH_H_ @}*/
diff --git a/src/libcharon/sa/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
index 5c8f0b6ce..aa0644033 100644
--- a/src/libcharon/sa/authenticators/eap_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2006-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -16,7 +17,8 @@
#include "eap_authenticator.h"
#include <daemon.h>
-#include <sa/authenticators/eap/eap_method.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>
@@ -185,9 +187,9 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,
if (this->method)
{
action = "initiating";
- type = this->method->get_type(this->method, &vendor);
if (this->method->initiate(this->method, &out) == NEED_MORE)
{
+ type = this->method->get_type(this->method, &vendor);
if (vendor)
{
DBG1(DBG_IKE, "initiating EAP vendor type %d-%d method (id 0x%02X)",
@@ -200,6 +202,8 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,
}
return out;
}
+ /* type might have changed for virtual methods */
+ type = this->method->get_type(this->method, &vendor);
}
if (vendor)
{
@@ -232,9 +236,10 @@ static void replace_eap_identity(private_eap_authenticator_t *this)
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_type_t type, received_type, conf_type;
+ u_int32_t vendor, received_vendor, conf_vendor;
eap_payload_t *out;
+ auth_cfg_t *auth;
if (in->get_code(in) != EAP_RESPONSE)
{
@@ -249,15 +254,25 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
{
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);
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+ conf_type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE);
+ conf_vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR);
+ if ((type == EAP_IDENTITY && !vendor) ||
+ (type == conf_type && vendor == conf_vendor))
+ {
+ DBG1(DBG_IKE, "received %N, sending %N",
+ eap_type_names, EAP_NAK, eap_code_names, EAP_FAILURE);
+ return eap_payload_create_code(EAP_FAILURE,
+ in->get_identifier(in));
+ }
+ /* virtual methods handle NAKs in process() */
}
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));
}
- return eap_payload_create_code(EAP_FAILURE, in->get_identifier(in));
}
switch (this->method->process(this->method, in, &out))
@@ -301,6 +316,8 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
return eap_payload_create_code(EAP_SUCCESS, in->get_identifier(in));
case FAILED:
default:
+ /* type might have changed for virtual methods */
+ type = this->method->get_type(this->method, &vendor);
if (vendor)
{
DBG1(DBG_IKE, "EAP vendor specific method %d-%d failed for "
@@ -323,8 +340,8 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
static eap_payload_t* client_process_eap(private_eap_authenticator_t *this,
eap_payload_t *in)
{
- eap_type_t type;
- u_int32_t vendor;
+ eap_type_t type, conf_type;
+ u_int32_t vendor, conf_vendor;
auth_cfg_t *auth;
eap_payload_t *out;
identification_t *id;
@@ -356,27 +373,49 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this,
this->method->destroy(this->method);
this->method = NULL;
}
+ /* FIXME: sending a Nak is not correct here as EAP_IDENTITY (1) is no
+ * EAP method (types 3-253, 255) */
DBG1(DBG_IKE, "%N not supported, sending EAP_NAK",
eap_type_names, type);
- return eap_payload_create_nak(in->get_identifier(in));
+ return eap_payload_create_nak(in->get_identifier(in), 0, 0, FALSE);
}
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));
+ "(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));
}
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+ conf_type = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_TYPE);
+ conf_vendor = (uintptr_t)auth->get(auth, AUTH_RULE_EAP_VENDOR);
+ if (conf_type != EAP_NAK &&
+ (conf_type != type || conf_vendor != vendor))
+ {
+ if (conf_vendor)
+ {
+ DBG1(DBG_IKE, "requesting EAP method %d-%d, sending EAP_NAK",
+ conf_type, conf_vendor);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "requesting %N authentication, sending EAP_NAK",
+ eap_type_names, conf_type);
+ }
+ return eap_payload_create_nak(in->get_identifier(in), conf_type,
+ conf_vendor, in->is_expanded(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));
+ return eap_payload_create_nak(in->get_identifier(in), 0, 0,
+ in->is_expanded(in));
}
}
@@ -408,7 +447,7 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message,
chunk_t auth_data, recv_auth_data;
identification_t *other_id;
auth_cfg_t *auth;
- keymat_t *keymat;
+ keymat_v2_t *keymat;
auth_payload = (auth_payload_t*)message->get_payload(message,
AUTHENTICATION);
@@ -418,9 +457,12 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message,
return FALSE;
}
other_id = this->ike_sa->get_other_id(this->ike_sa);
- keymat = this->ike_sa->get_keymat(this->ike_sa);
- auth_data = keymat->get_psk_sig(keymat, TRUE, init, nonce,
- this->msk, other_id, this->reserved);
+ keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
+ if (!keymat->get_psk_sig(keymat, TRUE, init, nonce,
+ this->msk, other_id, this->reserved, &auth_data))
+ {
+ return FALSE;
+ }
recv_auth_data = auth_payload->get_data(auth_payload);
if (!auth_data.len || !chunk_equals(auth_data, recv_auth_data))
{
@@ -442,27 +484,31 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message,
/**
* Build AUTH payload
*/
-static void build_auth(private_eap_authenticator_t *this, message_t *message,
+static bool 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_t *keymat;
+ keymat_v2_t *keymat;
my_id = this->ike_sa->get_my_id(this->ike_sa);
- keymat = this->ike_sa->get_keymat(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);
+ if (!keymat->get_psk_sig(keymat, FALSE, init, nonce,
+ this->msk, my_id, this->reserved, &auth_data))
+ {
+ return FALSE;
+ }
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);
+ return TRUE;
}
METHOD(authenticator_t, process_server, status_t,
@@ -512,9 +558,9 @@ METHOD(authenticator_t, build_server, status_t,
}
return NEED_MORE;
}
- if (this->eap_complete && this->auth_complete)
+ if (this->eap_complete && this->auth_complete &&
+ build_auth(this, message, this->received_nonce, this->sent_init))
{
- build_auth(this, message, this->received_nonce, this->sent_init);
return SUCCESS;
}
return FAILED;
@@ -610,9 +656,9 @@ METHOD(authenticator_t, build_client, status_t,
this->eap_payload = NULL;
return NEED_MORE;
}
- if (this->eap_complete)
+ if (this->eap_complete &&
+ build_auth(this, message, this->received_nonce, this->sent_init))
{
- build_auth(this, message, this->received_nonce, this->sent_init);
return NEED_MORE;
}
return NEED_MORE;
@@ -695,4 +741,3 @@ eap_authenticator_t *eap_authenticator_create_verifier(ike_sa_t *ike_sa,
return &this->public;
}
-
diff --git a/src/libcharon/sa/authenticators/eap_authenticator.h b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.h
index 726411a18..d81ebd562 100644
--- a/src/libcharon/sa/authenticators/eap_authenticator.h
+++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.h
@@ -15,7 +15,7 @@
/**
* @defgroup eap_authenticator eap_authenticator
- * @{ @ingroup authenticators
+ * @{ @ingroup authenticators_v2
*/
#ifndef EAP_AUTHENTICATOR_H_
@@ -23,7 +23,7 @@
typedef struct eap_authenticator_t eap_authenticator_t;
-#include <sa/authenticators/authenticator.h>
+#include <sa/authenticator.h>
/**
* Implementation of authenticator_t using EAP authentication.
diff --git a/src/libcharon/sa/authenticators/psk_authenticator.c b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c
index 21fc0f9b8..997efe359 100644
--- a/src/libcharon/sa/authenticators/psk_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.c
@@ -18,6 +18,7 @@
#include <daemon.h>
#include <encoding/payloads/auth_payload.h>
+#include <sa/ikev2/keymat_v2.h>
typedef struct private_psk_authenticator_t private_psk_authenticator_t;
@@ -59,9 +60,9 @@ METHOD(authenticator_t, build, status_t,
auth_payload_t *auth_payload;
shared_key_t *key;
chunk_t auth_data;
- keymat_t *keymat;
+ keymat_v2_t *keymat;
- keymat = this->ike_sa->get_keymat(this->ike_sa);
+ keymat = (keymat_v2_t*)this->ike_sa->get_keymat(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_IKE, "authentication of '%Y' (myself) with %N",
@@ -72,8 +73,12 @@ METHOD(authenticator_t, build, status_t,
DBG1(DBG_IKE, "no shared key found for '%Y' - '%Y'", my_id, other_id);
return NOT_FOUND;
}
- auth_data = keymat->get_psk_sig(keymat, FALSE, this->ike_sa_init,
- this->nonce, key->get_key(key), my_id, this->reserved);
+ if (!keymat->get_psk_sig(keymat, FALSE, this->ike_sa_init, this->nonce,
+ key->get_key(key), my_id, this->reserved, &auth_data))
+ {
+ key->destroy(key);
+ return FAILED;
+ }
key->destroy(key);
DBG2(DBG_IKE, "successfully created shared key MAC");
auth_payload = auth_payload_create();
@@ -96,14 +101,14 @@ METHOD(authenticator_t, process, status_t,
enumerator_t *enumerator;
bool authenticated = FALSE;
int keys_found = 0;
- keymat_t *keymat;
+ keymat_v2_t *keymat;
auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION);
if (!auth_payload)
{
return FAILED;
}
- keymat = this->ike_sa->get_keymat(this->ike_sa);
+ keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
recv_auth_data = auth_payload->get_data(auth_payload);
my_id = this->ike_sa->get_my_id(this->ike_sa);
other_id = this->ike_sa->get_other_id(this->ike_sa);
@@ -113,8 +118,11 @@ METHOD(authenticator_t, process, status_t,
{
keys_found++;
- auth_data = keymat->get_psk_sig(keymat, TRUE, this->ike_sa_init,
- this->nonce, key->get_key(key), other_id, this->reserved);
+ if (!keymat->get_psk_sig(keymat, TRUE, this->ike_sa_init, this->nonce,
+ key->get_key(key), other_id, this->reserved, &auth_data))
+ {
+ continue;
+ }
if (auth_data.len && chunk_equals(auth_data, recv_auth_data))
{
DBG1(DBG_IKE, "authentication of '%Y' with %N successful",
@@ -201,4 +209,3 @@ psk_authenticator_t *psk_authenticator_create_verifier(ike_sa_t *ike_sa,
return &this->public;
}
-
diff --git a/src/libcharon/sa/authenticators/psk_authenticator.h b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.h
index 8cf1a0f98..91c534145 100644
--- a/src/libcharon/sa/authenticators/psk_authenticator.h
+++ b/src/libcharon/sa/ikev2/authenticators/psk_authenticator.h
@@ -15,7 +15,7 @@
/**
* @defgroup psk_authenticator psk_authenticator
- * @{ @ingroup authenticators
+ * @{ @ingroup authenticators_v2
*/
#ifndef PSK_AUTHENTICATOR_H_
@@ -23,7 +23,7 @@
typedef struct psk_authenticator_t psk_authenticator_t;
-#include <sa/authenticators/authenticator.h>
+#include <sa/authenticator.h>
/**
* Implementation of authenticator_t using pre-shared keys.
diff --git a/src/libcharon/sa/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
index 247891670..5ceff40ba 100644
--- a/src/libcharon/sa/authenticators/pubkey_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
@@ -19,6 +19,7 @@
#include <daemon.h>
#include <encoding/payloads/auth_payload.h>
+#include <sa/ikev2/keymat_v2.h>
typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t;
@@ -56,7 +57,7 @@ struct private_pubkey_authenticator_t {
METHOD(authenticator_t, build, status_t,
private_pubkey_authenticator_t *this, message_t *message)
{
- chunk_t octets, auth_data;
+ chunk_t octets = chunk_empty, auth_data;
status_t status = FAILED;
private_key_t *private;
identification_t *id;
@@ -64,7 +65,7 @@ METHOD(authenticator_t, build, status_t,
auth_payload_t *auth_payload;
auth_method_t auth_method;
signature_scheme_t scheme;
- keymat_t *keymat;
+ keymat_v2_t *keymat;
id = this->ike_sa->get_my_id(this->ike_sa);
auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
@@ -110,10 +111,10 @@ METHOD(authenticator_t, build, status_t,
key_type_names, private->get_type(private));
return status;
}
- keymat = this->ike_sa->get_keymat(this->ike_sa);
- octets = keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init,
- this->nonce, id, this->reserved);
- if (private->sign(private, scheme, octets, &auth_data))
+ keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
+ if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init,
+ this->nonce, id, this->reserved, &octets) &&
+ private->sign(private, scheme, octets, &auth_data))
{
auth_payload = auth_payload_create();
auth_payload->set_auth_method(auth_payload, auth_method);
@@ -144,7 +145,7 @@ METHOD(authenticator_t, process, status_t,
key_type_t key_type = KEY_ECDSA;
signature_scheme_t scheme;
status_t status = NOT_FOUND;
- keymat_t *keymat;
+ keymat_v2_t *keymat;
auth_payload = (auth_payload_t*)message->get_payload(message, AUTHENTICATION);
if (!auth_payload)
@@ -174,9 +175,12 @@ METHOD(authenticator_t, process, status_t,
}
auth_data = auth_payload->get_data(auth_payload);
id = this->ike_sa->get_other_id(this->ike_sa);
- keymat = this->ike_sa->get_keymat(this->ike_sa);
- octets = keymat->get_auth_octets(keymat, TRUE, this->ike_sa_init,
- this->nonce, id, this->reserved);
+ keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
+ if (!keymat->get_auth_octets(keymat, TRUE, this->ike_sa_init,
+ this->nonce, id, this->reserved, &octets))
+ {
+ return FAILED;
+ }
auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr,
key_type, id, auth);
diff --git a/src/libcharon/sa/authenticators/pubkey_authenticator.h b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.h
index 4c3937ecc..82bfea23b 100644
--- a/src/libcharon/sa/authenticators/pubkey_authenticator.h
+++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.h
@@ -16,7 +16,7 @@
/**
* @defgroup pubkey_authenticator pubkey_authenticator
- * @{ @ingroup authenticators
+ * @{ @ingroup authenticators_v2
*/
#ifndef PUBKEY_AUTHENTICATOR_H_
@@ -24,7 +24,7 @@
typedef struct pubkey_authenticator_t pubkey_authenticator_t;
-#include <sa/authenticators/authenticator.h>
+#include <sa/authenticator.h>
/**
* Implementation of authenticator_t using public key authenitcation.
diff --git a/src/libcharon/sa/connect_manager.c b/src/libcharon/sa/ikev2/connect_manager.c
index 7b6ca430f..5fdcea1ab 100644
--- a/src/libcharon/sa/connect_manager.c
+++ b/src/libcharon/sa/ikev2/connect_manager.c
@@ -839,7 +839,10 @@ static chunk_t build_signature(private_connect_manager_t *this,
/* signature = SHA1( MID | ME_CONNECTID | ME_ENDPOINT | ME_CONNECTKEY ) */
sig_chunk = chunk_cat("cccc", mid_chunk, check->connect_id,
check->endpoint_raw, key_chunk);
- this->hasher->allocate_hash(this->hasher, sig_chunk, &sig_hash);
+ if (!this->hasher->allocate_hash(this->hasher, sig_chunk, &sig_hash))
+ {
+ sig_hash = chunk_empty;
+ }
DBG3(DBG_IKE, "sig_chunk %#B", &sig_chunk);
DBG3(DBG_IKE, "sig_hash %#B", &sig_hash);
@@ -919,8 +922,10 @@ static void update_checklist_state(private_connect_manager_t *this,
&checklist->connect_id);
callback_data_t *data = callback_data_create(this, checklist->connect_id);
- job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiator_finish, data, (callback_job_cleanup_t)callback_data_destroy, NULL);
- lib->scheduler->schedule_job_ms(lib->scheduler, job, ME_WAIT_TO_FINISH);
+ lib->scheduler->schedule_job_ms(lib->scheduler,
+ (job_t*)callback_job_create((callback_job_cb_t)initiator_finish,
+ data, (callback_job_cleanup_t)callback_data_destroy, NULL),
+ ME_WAIT_TO_FINISH);
checklist->is_finishing = TRUE;
}
@@ -1007,8 +1012,12 @@ retransmit_end:
*/
static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair)
{
- callback_data_t *data = retransmit_data_create(this, checklist->connect_id, pair->id);
- job_t *job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)callback_data_destroy, NULL);
+ callback_data_t *data;
+ job_t *job;
+
+ data = retransmit_data_create(this, checklist->connect_id, pair->id);
+ job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data,
+ (callback_job_cleanup_t)callback_data_destroy, NULL);
u_int32_t retransmission = pair->retransmitted + 1;
u_int32_t rto = ME_INTERVAL;
@@ -1028,14 +1037,15 @@ static void queue_retransmission(private_connect_manager_t *this, check_list_t *
static void send_check(private_connect_manager_t *this, check_list_t *checklist,
check_t *check, endpoint_pair_t *pair, bool request)
{
- message_t *message = message_create();
+ message_t *message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
message->set_message_id(message, check->mid);
message->set_exchange_type(message, INFORMATIONAL);
message->set_request(message, request);
message->set_destination(message, check->dst->clone(check->dst));
message->set_source(message, check->src->clone(check->src));
- ike_sa_id_t *ike_sa_id = ike_sa_id_create(0, 0, request);
+ ike_sa_id_t *ike_sa_id = ike_sa_id_create(IKEV2_MAJOR_VERSION, 0, 0,
+ request);
message->set_ike_sa_id(message, ike_sa_id);
ike_sa_id->destroy(ike_sa_id);
@@ -1154,10 +1164,12 @@ static job_requeue_t sender(callback_data_t *data)
/**
* Schedules checks for a checklist (time in ms)
*/
-static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time)
+static void schedule_checks(private_connect_manager_t *this,
+ check_list_t *checklist, u_int32_t time)
{
callback_data_t *data = callback_data_create(this, checklist->connect_id);
- checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender, data, (callback_job_cleanup_t)callback_data_destroy, NULL);
+ checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender,
+ data, (callback_job_cleanup_t)callback_data_destroy, NULL);
lib->scheduler->schedule_job_ms(lib->scheduler, checklist->sender, time);
}
@@ -1209,12 +1221,15 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli
if (get_initiated_by_ids(this, checklist->initiator.id,
checklist->responder.id, &initiated) == SUCCESS)
{
+ callback_job_t *job;
+
remove_checklist(this, checklist);
remove_initiated(this, initiated);
initiate_data_t *data = initiate_data_create(checklist, initiated);
- job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiate_mediated, data, (callback_job_cleanup_t)initiate_data_destroy, NULL);
- lib->processor->queue_job(lib->processor, job);
+ job = callback_job_create((callback_job_cb_t)initiate_mediated,
+ data, (callback_job_cleanup_t)initiate_data_destroy, NULL);
+ lib->processor->queue_job(lib->processor, (job_t*)job);
return;
}
else
diff --git a/src/libcharon/sa/connect_manager.h b/src/libcharon/sa/ikev2/connect_manager.h
index 8fa8ff697..e667e1f70 100644
--- a/src/libcharon/sa/connect_manager.h
+++ b/src/libcharon/sa/ikev2/connect_manager.h
@@ -15,7 +15,7 @@
/**
* @defgroup connect_manager connect_manager
- * @{ @ingroup sa
+ * @{ @ingroup ikev2
*/
#ifndef CONNECT_MANAGER_H_
diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c
new file mode 100644
index 000000000..4d0683f0a
--- /dev/null
+++ b/src/libcharon/sa/ikev2/keymat_v2.c
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2008 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 "keymat_v2.h"
+
+#include <daemon.h>
+#include <crypto/prf_plus.h>
+
+typedef struct private_keymat_v2_t private_keymat_v2_t;
+
+/**
+ * Private data of an keymat_t object.
+ */
+struct private_keymat_v2_t {
+
+ /**
+ * Public keymat_v2_t interface.
+ */
+ keymat_v2_t public;
+
+ /**
+ * IKE_SA Role, initiator or responder
+ */
+ bool initiator;
+
+ /**
+ * inbound AEAD
+ */
+ aead_t *aead_in;
+
+ /**
+ * outbound AEAD
+ */
+ aead_t *aead_out;
+
+ /**
+ * General purpose PRF
+ */
+ prf_t *prf;
+
+ /**
+ * Negotiated PRF algorithm
+ */
+ pseudo_random_function_t prf_alg;
+
+ /**
+ * Key to derive key material from for CHILD_SAs, rekeying
+ */
+ chunk_t skd;
+
+ /**
+ * Key to build outging authentication data (SKp)
+ */
+ chunk_t skp_build;
+
+ /**
+ * Key to verify incoming authentication data (SKp)
+ */
+ chunk_t skp_verify;
+};
+
+METHOD(keymat_t, get_version, ike_version_t,
+ private_keymat_v2_t *this)
+{
+ return IKEV2;
+}
+
+METHOD(keymat_t, create_dh, diffie_hellman_t*,
+ private_keymat_v2_t *this, diffie_hellman_group_t group)
+{
+ return lib->crypto->create_dh(lib->crypto, group);
+}
+
+METHOD(keymat_t, create_nonce_gen, nonce_gen_t*,
+ private_keymat_v2_t *this)
+{
+ return lib->crypto->create_nonce_gen(lib->crypto);
+}
+
+/**
+ * Derive IKE keys for a combined AEAD algorithm
+ */
+static bool derive_ike_aead(private_keymat_v2_t *this, u_int16_t alg,
+ u_int16_t key_size, prf_plus_t *prf_plus)
+{
+ aead_t *aead_i, *aead_r;
+ chunk_t key = chunk_empty;
+
+ /* SK_ei/SK_er used for encryption */
+ aead_i = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
+ aead_r = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
+ if (aead_i == NULL || aead_r == NULL)
+ {
+ DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
+ transform_type_names, ENCRYPTION_ALGORITHM,
+ encryption_algorithm_names, alg, key_size);
+ goto failure;
+ }
+ key_size = aead_i->get_key_size(aead_i);
+ if (key_size != aead_r->get_key_size(aead_r))
+ {
+ goto failure;
+ }
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_ei secret %B", &key);
+ if (!aead_i->set_key(aead_i, key))
+ {
+ goto failure;
+ }
+ chunk_clear(&key);
+
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_er secret %B", &key);
+ if (!aead_r->set_key(aead_r, key))
+ {
+ goto failure;
+ }
+
+ if (this->initiator)
+ {
+ this->aead_in = aead_r;
+ this->aead_out = aead_i;
+ }
+ else
+ {
+ this->aead_in = aead_i;
+ this->aead_out = aead_r;
+ }
+ aead_i = aead_r = NULL;
+
+failure:
+ DESTROY_IF(aead_i);
+ DESTROY_IF(aead_r);
+ chunk_clear(&key);
+ return this->aead_in && this->aead_out;
+}
+
+/**
+ * Derive IKE keys for traditional encryption and MAC algorithms
+ */
+static bool derive_ike_traditional(private_keymat_v2_t *this, u_int16_t enc_alg,
+ u_int16_t enc_size, u_int16_t int_alg, prf_plus_t *prf_plus)
+{
+ crypter_t *crypter_i = NULL, *crypter_r = NULL;
+ signer_t *signer_i, *signer_r;
+ size_t key_size;
+ chunk_t key = chunk_empty;
+
+ signer_i = lib->crypto->create_signer(lib->crypto, int_alg);
+ signer_r = lib->crypto->create_signer(lib->crypto, int_alg);
+ crypter_i = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
+ crypter_r = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
+ if (signer_i == NULL || signer_r == NULL)
+ {
+ DBG1(DBG_IKE, "%N %N not supported!",
+ transform_type_names, INTEGRITY_ALGORITHM,
+ integrity_algorithm_names, int_alg);
+ goto failure;
+ }
+ if (crypter_i == NULL || crypter_r == NULL)
+ {
+ DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
+ transform_type_names, ENCRYPTION_ALGORITHM,
+ encryption_algorithm_names, enc_alg, enc_size);
+ goto failure;
+ }
+
+ /* SK_ai/SK_ar used for integrity protection */
+ key_size = signer_i->get_key_size(signer_i);
+
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_ai secret %B", &key);
+ if (!signer_i->set_key(signer_i, key))
+ {
+ goto failure;
+ }
+ chunk_clear(&key);
+
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_ar secret %B", &key);
+ if (!signer_r->set_key(signer_r, key))
+ {
+ goto failure;
+ }
+ chunk_clear(&key);
+
+ /* SK_ei/SK_er used for encryption */
+ key_size = crypter_i->get_key_size(crypter_i);
+
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_ei secret %B", &key);
+ if (!crypter_i->set_key(crypter_i, key))
+ {
+ goto failure;
+ }
+ chunk_clear(&key);
+
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_er secret %B", &key);
+ if (!crypter_r->set_key(crypter_r, key))
+ {
+ goto failure;
+ }
+
+ if (this->initiator)
+ {
+ this->aead_in = aead_create(crypter_r, signer_r);
+ this->aead_out = aead_create(crypter_i, signer_i);
+ }
+ else
+ {
+ this->aead_in = aead_create(crypter_i, signer_i);
+ this->aead_out = aead_create(crypter_r, signer_r);
+ }
+ signer_i = signer_r = NULL;
+ crypter_i = crypter_r = NULL;
+
+failure:
+ chunk_clear(&key);
+ DESTROY_IF(signer_i);
+ DESTROY_IF(signer_r);
+ DESTROY_IF(crypter_i);
+ DESTROY_IF(crypter_r);
+ return this->aead_in && this->aead_out;
+}
+
+METHOD(keymat_v2_t, derive_ike_keys, bool,
+ private_keymat_v2_t *this, proposal_t *proposal, diffie_hellman_t *dh,
+ chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
+ pseudo_random_function_t rekey_function, chunk_t rekey_skd)
+{
+ chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed;
+ chunk_t spi_i, spi_r;
+ prf_plus_t *prf_plus = NULL;
+ u_int16_t alg, key_size, int_alg;
+ prf_t *rekey_prf = NULL;
+
+ spi_i = chunk_alloca(sizeof(u_int64_t));
+ spi_r = chunk_alloca(sizeof(u_int64_t));
+
+ if (dh->get_shared_secret(dh, &secret) != SUCCESS)
+ {
+ return FALSE;
+ }
+
+ /* Create SAs general purpose PRF first, we may use it here */
+ if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
+ {
+ DBG1(DBG_IKE, "no %N selected",
+ transform_type_names, PSEUDO_RANDOM_FUNCTION);
+ return FALSE;
+ }
+ this->prf_alg = alg;
+ this->prf = lib->crypto->create_prf(lib->crypto, alg);
+ if (this->prf == NULL)
+ {
+ DBG1(DBG_IKE, "%N %N not supported!",
+ transform_type_names, PSEUDO_RANDOM_FUNCTION,
+ pseudo_random_function_names, alg);
+ return FALSE;
+ }
+ DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
+ /* full nonce is used as seed for PRF+ ... */
+ full_nonce = chunk_cat("cc", nonce_i, nonce_r);
+ /* but the PRF may need a fixed key which only uses the first bytes of
+ * the nonces. */
+ switch (alg)
+ {
+ case PRF_AES128_XCBC:
+ /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does
+ * not and therefore fixed key semantics apply to XCBC for key
+ * derivation. */
+ case PRF_CAMELLIA128_XCBC:
+ /* draft-kanno-ipsecme-camellia-xcbc refers to rfc 4434, we
+ * assume fixed key length. */
+ key_size = this->prf->get_key_size(this->prf)/2;
+ nonce_i.len = min(nonce_i.len, key_size);
+ nonce_r.len = min(nonce_r.len, key_size);
+ break;
+ default:
+ /* all other algorithms use variable key length, full nonce */
+ break;
+ }
+ fixed_nonce = chunk_cat("cc", nonce_i, nonce_r);
+ *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id);
+ *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id);
+ prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r);
+
+ /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr)
+ *
+ * if we are rekeying, SKEYSEED is built on another way
+ */
+ if (rekey_function == PRF_UNDEFINED) /* not rekeying */
+ {
+ /* SKEYSEED = prf(Ni | Nr, g^ir) */
+ if (this->prf->set_key(this->prf, fixed_nonce) &&
+ this->prf->allocate_bytes(this->prf, secret, &skeyseed) &&
+ this->prf->set_key(this->prf, skeyseed))
+ {
+ prf_plus = prf_plus_create(this->prf, TRUE, prf_plus_seed);
+ }
+ }
+ else
+ {
+ /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
+ * use OLD SAs PRF functions for both prf_plus and prf */
+ rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function);
+ if (!rekey_prf)
+ {
+ DBG1(DBG_IKE, "PRF of old SA %N not supported!",
+ pseudo_random_function_names, rekey_function);
+ chunk_free(&full_nonce);
+ chunk_free(&fixed_nonce);
+ chunk_clear(&prf_plus_seed);
+ return FALSE;
+ }
+ secret = chunk_cat("mc", secret, full_nonce);
+ if (rekey_prf->set_key(rekey_prf, rekey_skd) &&
+ rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed) &&
+ rekey_prf->set_key(rekey_prf, skeyseed))
+ {
+ prf_plus = prf_plus_create(rekey_prf, TRUE, prf_plus_seed);
+ }
+ }
+ DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
+
+ chunk_clear(&skeyseed);
+ chunk_clear(&secret);
+ chunk_free(&full_nonce);
+ chunk_free(&fixed_nonce);
+ chunk_clear(&prf_plus_seed);
+
+ if (!prf_plus)
+ {
+ goto failure;
+ }
+
+ /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */
+
+ /* SK_d is used for generating CHILD_SA key mat => store for later use */
+ key_size = this->prf->get_key_size(this->prf);
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &this->skd))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_d secret %B", &this->skd);
+
+ if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size))
+ {
+ DBG1(DBG_IKE, "no %N selected",
+ transform_type_names, ENCRYPTION_ALGORITHM);
+ goto failure;
+ }
+
+ if (encryption_algorithm_is_aead(alg))
+ {
+ if (!derive_ike_aead(this, alg, key_size, prf_plus))
+ {
+ goto failure;
+ }
+ }
+ else
+ {
+ if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
+ &int_alg, NULL))
+ {
+ DBG1(DBG_IKE, "no %N selected",
+ transform_type_names, INTEGRITY_ALGORITHM);
+ goto failure;
+ }
+ if (!derive_ike_traditional(this, alg, key_size, int_alg, prf_plus))
+ {
+ goto failure;
+ }
+ }
+
+ /* SK_pi/SK_pr used for authentication => stored for later */
+ key_size = this->prf->get_key_size(this->prf);
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_pi secret %B", &key);
+ if (this->initiator)
+ {
+ this->skp_build = key;
+ }
+ else
+ {
+ this->skp_verify = key;
+ }
+ if (!prf_plus->allocate_bytes(prf_plus, key_size, &key))
+ {
+ goto failure;
+ }
+ DBG4(DBG_IKE, "Sk_pr secret %B", &key);
+ if (this->initiator)
+ {
+ this->skp_verify = key;
+ }
+ else
+ {
+ this->skp_build = key;
+ }
+
+ /* all done, prf_plus not needed anymore */
+failure:
+ DESTROY_IF(prf_plus);
+ DESTROY_IF(rekey_prf);
+
+ return this->skp_build.len && this->skp_verify.len;
+}
+
+METHOD(keymat_v2_t, derive_child_keys, bool,
+ private_keymat_v2_t *this, proposal_t *proposal, diffie_hellman_t *dh,
+ chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i,
+ chunk_t *encr_r, chunk_t *integ_r)
+{
+ u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
+ chunk_t seed, secret = chunk_empty;
+ prf_plus_t *prf_plus;
+
+ if (dh)
+ {
+ if (dh->get_shared_secret(dh, &secret) != SUCCESS)
+ {
+ return FALSE;
+ }
+ DBG4(DBG_CHD, "DH secret %B", &secret);
+ }
+ seed = chunk_cata("mcc", secret, nonce_i, nonce_r);
+ DBG4(DBG_CHD, "seed %B", &seed);
+
+ if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
+ &enc_alg, &enc_size))
+ {
+ DBG2(DBG_CHD, " using %N for encryption",
+ encryption_algorithm_names, enc_alg);
+
+ if (!enc_size)
+ {
+ enc_size = keymat_get_keylen_encr(enc_alg);
+ }
+ if (enc_alg != ENCR_NULL && !enc_size)
+ {
+ DBG1(DBG_CHD, "no keylength defined for %N",
+ encryption_algorithm_names, enc_alg);
+ return FALSE;
+ }
+ /* to bytes */
+ enc_size /= 8;
+
+ /* CCM/GCM/CTR/GMAC needs additional bytes */
+ switch (enc_alg)
+ {
+ case ENCR_AES_CCM_ICV8:
+ case ENCR_AES_CCM_ICV12:
+ case ENCR_AES_CCM_ICV16:
+ case ENCR_CAMELLIA_CCM_ICV8:
+ case ENCR_CAMELLIA_CCM_ICV12:
+ case ENCR_CAMELLIA_CCM_ICV16:
+ enc_size += 3;
+ break;
+ case ENCR_AES_GCM_ICV8:
+ case ENCR_AES_GCM_ICV12:
+ case ENCR_AES_GCM_ICV16:
+ case ENCR_AES_CTR:
+ case ENCR_NULL_AUTH_AES_GMAC:
+ enc_size += 4;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
+ &int_alg, &int_size))
+ {
+ DBG2(DBG_CHD, " using %N for integrity",
+ integrity_algorithm_names, int_alg);
+
+ if (!int_size)
+ {
+ int_size = keymat_get_keylen_integ(int_alg);
+ }
+ if (!int_size)
+ {
+ DBG1(DBG_CHD, "no keylength defined for %N",
+ integrity_algorithm_names, int_alg);
+ return FALSE;
+ }
+ /* to bytes */
+ int_size /= 8;
+ }
+
+ if (!this->prf->set_key(this->prf, this->skd))
+ {
+ return FALSE;
+ }
+ prf_plus = prf_plus_create(this->prf, TRUE, seed);
+ if (!prf_plus)
+ {
+ return FALSE;
+ }
+
+ *encr_i = *integ_i = *encr_r = *integ_r = chunk_empty;
+ if (!prf_plus->allocate_bytes(prf_plus, enc_size, encr_i) ||
+ !prf_plus->allocate_bytes(prf_plus, int_size, integ_i) ||
+ !prf_plus->allocate_bytes(prf_plus, enc_size, encr_r) ||
+ !prf_plus->allocate_bytes(prf_plus, int_size, integ_r))
+ {
+ chunk_free(encr_i);
+ chunk_free(integ_i);
+ chunk_free(encr_r);
+ chunk_free(integ_r);
+ prf_plus->destroy(prf_plus);
+ return FALSE;
+ }
+
+ prf_plus->destroy(prf_plus);
+
+ if (enc_size)
+ {
+ DBG4(DBG_CHD, "encryption initiator key %B", encr_i);
+ DBG4(DBG_CHD, "encryption responder key %B", encr_r);
+ }
+ if (int_size)
+ {
+ DBG4(DBG_CHD, "integrity initiator key %B", integ_i);
+ DBG4(DBG_CHD, "integrity responder key %B", integ_r);
+ }
+ return TRUE;
+}
+
+METHOD(keymat_v2_t, get_skd, pseudo_random_function_t,
+ private_keymat_v2_t *this, chunk_t *skd)
+{
+ *skd = this->skd;
+ return this->prf_alg;
+}
+
+METHOD(keymat_t, get_aead, aead_t*,
+ private_keymat_v2_t *this, bool in)
+{
+ return in ? this->aead_in : this->aead_out;
+}
+
+METHOD(keymat_v2_t, get_auth_octets, bool,
+ private_keymat_v2_t *this, bool verify, chunk_t ike_sa_init,
+ chunk_t nonce, identification_t *id, char reserved[3], chunk_t *octets)
+{
+ chunk_t chunk, idx;
+ chunk_t skp;
+
+ skp = verify ? this->skp_verify : this->skp_build;
+
+ chunk = chunk_alloca(4);
+ chunk.ptr[0] = id->get_type(id);
+ memcpy(chunk.ptr + 1, reserved, 3);
+ idx = chunk_cata("cc", chunk, id->get_encoding(id));
+
+ DBG3(DBG_IKE, "IDx' %B", &idx);
+ DBG3(DBG_IKE, "SK_p %B", &skp);
+ if (!this->prf->set_key(this->prf, skp) ||
+ !this->prf->allocate_bytes(this->prf, idx, &chunk))
+ {
+ return FALSE;
+ }
+ *octets = chunk_cat("ccm", ike_sa_init, nonce, chunk);
+ DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", octets);
+ return TRUE;
+}
+
+/**
+ * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
+ */
+#define IKEV2_KEY_PAD "Key Pad for IKEv2"
+#define IKEV2_KEY_PAD_LENGTH 17
+
+METHOD(keymat_v2_t, get_psk_sig, bool,
+ private_keymat_v2_t *this, bool verify, chunk_t ike_sa_init, chunk_t nonce,
+ chunk_t secret, identification_t *id, char reserved[3], chunk_t *sig)
+{
+ chunk_t key_pad, key, octets;
+
+ if (!secret.len)
+ { /* EAP uses SK_p if no MSK has been established */
+ secret = verify ? this->skp_verify : this->skp_build;
+ }
+ if (!get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved, &octets))
+ {
+ return FALSE;
+ }
+ /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
+ key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH);
+ if (!this->prf->set_key(this->prf, secret) ||
+ !this->prf->allocate_bytes(this->prf, key_pad, &key))
+ {
+ chunk_free(&octets);
+ return FALSE;
+ }
+ if (!this->prf->set_key(this->prf, key) ||
+ !this->prf->allocate_bytes(this->prf, octets, sig))
+ {
+ chunk_free(&key);
+ chunk_free(&octets);
+ return FALSE;
+ }
+ DBG4(DBG_IKE, "secret %B", &secret);
+ DBG4(DBG_IKE, "prf(secret, keypad) %B", &key);
+ DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", sig);
+ chunk_free(&octets);
+ chunk_free(&key);
+
+ return TRUE;
+}
+
+METHOD(keymat_t, destroy, void,
+ private_keymat_v2_t *this)
+{
+ DESTROY_IF(this->aead_in);
+ DESTROY_IF(this->aead_out);
+ DESTROY_IF(this->prf);
+ chunk_clear(&this->skd);
+ chunk_clear(&this->skp_verify);
+ chunk_clear(&this->skp_build);
+ free(this);
+}
+
+/**
+ * See header
+ */
+keymat_v2_t *keymat_v2_create(bool initiator)
+{
+ private_keymat_v2_t *this;
+
+ INIT(this,
+ .public = {
+ .keymat = {
+ .get_version = _get_version,
+ .create_dh = _create_dh,
+ .create_nonce_gen = _create_nonce_gen,
+ .get_aead = _get_aead,
+ .destroy = _destroy,
+ },
+ .derive_ike_keys = _derive_ike_keys,
+ .derive_child_keys = _derive_child_keys,
+ .get_skd = _get_skd,
+ .get_auth_octets = _get_auth_octets,
+ .get_psk_sig = _get_psk_sig,
+ },
+ .initiator = initiator,
+ .prf_alg = PRF_UNDEFINED,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev2/keymat_v2.h b/src/libcharon/sa/ikev2/keymat_v2.h
new file mode 100644
index 000000000..04432f05b
--- /dev/null
+++ b/src/libcharon/sa/ikev2/keymat_v2.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+/**
+ * @defgroup keymat_v2 keymat_v2
+ * @{ @ingroup ikev2
+ */
+
+#ifndef KEYMAT_V2_H_
+#define KEYMAT_V2_H_
+
+#include <sa/keymat.h>
+
+typedef struct keymat_v2_t keymat_v2_t;
+
+/**
+ * Derivation and management of sensitive keying material, IKEv2 variant.
+ */
+struct keymat_v2_t {
+
+ /**
+ * Implements keymat_t.
+ */
+ keymat_t keymat;
+
+ /**
+ * Derive keys for the IKE_SA.
+ *
+ * These keys are not handed out, but are used by the associated signers,
+ * crypters and authentication functions.
+ *
+ * @param proposal selected algorithms
+ * @param dh diffie hellman key allocated by create_dh()
+ * @param nonce_i initiators nonce value
+ * @param nonce_r responders nonce value
+ * @param id IKE_SA identifier
+ * @param rekey_prf PRF of old SA if rekeying, PRF_UNDEFINED otherwise
+ * @param rekey_sdk SKd of old SA if rekeying
+ * @return TRUE on success
+ */
+ bool (*derive_ike_keys)(keymat_v2_t *this, proposal_t *proposal,
+ diffie_hellman_t *dh, chunk_t nonce_i,
+ chunk_t nonce_r, ike_sa_id_t *id,
+ pseudo_random_function_t rekey_function,
+ chunk_t rekey_skd);
+
+ /**
+ * Derive keys for a CHILD_SA.
+ *
+ * The keys for the CHILD_SA are allocated in the integ and encr chunks.
+ * An implementation might hand out encrypted keys only, which are
+ * decrypted in the kernel before use.
+ * If no PFS is used for the CHILD_SA, dh can be NULL.
+ *
+ * @param proposal selected algorithms
+ * @param dh diffie hellman key allocated by create_dh(), or NULL
+ * @param nonce_i initiators nonce value
+ * @param nonce_r responders nonce value
+ * @param encr_i chunk to write initiators encryption key to
+ * @param integ_i chunk to write initiators integrity key to
+ * @param encr_r chunk to write responders encryption key to
+ * @param integ_r chunk to write responders integrity key to
+ * @return TRUE on success
+ */
+ bool (*derive_child_keys)(keymat_v2_t *this,
+ proposal_t *proposal, diffie_hellman_t *dh,
+ chunk_t nonce_i, chunk_t nonce_r,
+ chunk_t *encr_i, chunk_t *integ_i,
+ chunk_t *encr_r, chunk_t *integ_r);
+ /**
+ * Get SKd to pass to derive_ikey_keys() during rekeying.
+ *
+ * @param skd chunk to write SKd to (internal data)
+ * @return PRF function to derive keymat
+ */
+ pseudo_random_function_t (*get_skd)(keymat_v2_t *this, chunk_t *skd);
+
+ /**
+ * Generate octets to use for authentication procedure (RFC4306 2.15).
+ *
+ * This method creates the plain octets and is usually signed by a private
+ * key. PSK and EAP authentication include a secret into the data, use
+ * the get_psk_sig() method instead.
+ *
+ * @param verify TRUE to create for verfification, FALSE to sign
+ * @param ike_sa_init encoded ike_sa_init message
+ * @param nonce nonce value
+ * @param id identity
+ * @param reserved reserved bytes of id_payload
+ * @param octests chunk receiving allocated auth octets
+ * @return TRUE if octets created successfully
+ */
+ bool (*get_auth_octets)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init,
+ chunk_t nonce, identification_t *id,
+ char reserved[3], chunk_t *octets);
+ /**
+ * Build the shared secret signature used for PSK and EAP authentication.
+ *
+ * This method wraps the get_auth_octets() method and additionally
+ * includes the secret into the signature. If no secret is given, SK_p is
+ * used as secret (used for EAP methods without MSK).
+ *
+ * @param verify TRUE to create for verfification, FALSE to sign
+ * @param ike_sa_init encoded ike_sa_init message
+ * @param nonce nonce value
+ * @param secret optional secret to include into signature
+ * @param id identity
+ * @param reserved reserved bytes of id_payload
+ * @param sign chunk receiving allocated signature octets
+ * @return TRUE if signature created successfully
+ */
+ bool (*get_psk_sig)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init,
+ chunk_t nonce, chunk_t secret,
+ identification_t *id, char reserved[3], chunk_t *sig);
+};
+
+/**
+ * Create a keymat instance.
+ *
+ * @param initiator TRUE if we are the initiator
+ * @return keymat instance
+ */
+keymat_v2_t *keymat_v2_create(bool initiator);
+
+#endif /** KEYMAT_V2_H_ @}*/
diff --git a/src/libcharon/sa/mediation_manager.c b/src/libcharon/sa/ikev2/mediation_manager.c
index 60eeb5d4b..60eeb5d4b 100644
--- a/src/libcharon/sa/mediation_manager.c
+++ b/src/libcharon/sa/ikev2/mediation_manager.c
diff --git a/src/libcharon/sa/mediation_manager.h b/src/libcharon/sa/ikev2/mediation_manager.h
index 31a16f69c..5212bdb86 100644
--- a/src/libcharon/sa/mediation_manager.h
+++ b/src/libcharon/sa/ikev2/mediation_manager.h
@@ -15,7 +15,7 @@
/**
* @defgroup mediation_manager mediation_manager
- * @{ @ingroup sa
+ * @{ @ingroup ikev2
*/
#ifndef MEDIATION_MANAGER_H_
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
new file mode 100644
index 000000000..53051fab4
--- /dev/null
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -0,0 +1,1502 @@
+/*
+ * 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;
+};
+
+METHOD(task_manager_t, flush_queue, void,
+ private_task_manager_t *this, task_queue_t queue)
+{
+ linked_list_t *list;
+ task_t *task;
+
+ switch (queue)
+ {
+ case TASK_QUEUE_ACTIVE:
+ list = this->active_tasks;
+ break;
+ case TASK_QUEUE_PASSIVE:
+ list = this->passive_tasks;
+ break;
+ case TASK_QUEUE_QUEUED:
+ list = this->queued_tasks;
+ break;
+ default:
+ return;
+ }
+ while (list->remove_last(list, (void**)&task) == SUCCESS)
+ {
+ task->destroy(task);
+ }
+}
+
+/**
+ * flush all tasks in the task manager
+ */
+static void flush(private_task_manager_t *this)
+{
+ flush_queue(this, TASK_QUEUE_QUEUED);
+ flush_queue(this, TASK_QUEUE_PASSIVE);
+ flush_queue(this, TASK_QUEUE_ACTIVE);
+}
+
+/**
+ * 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);
+ 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);
+ 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;
+ 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;
+ 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;
+ break;
+ }
+ }
+ 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,
+ "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
+ charon->name));
+ }
+ this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND,
+ time_monotonic(NULL));
+
+ mid = msg->get_message_id(msg);
+ if (msg->get_request(msg))
+ {
+ if (mid == this->responding.mid)
+ {
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+ this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+ msg->get_exchange_type(msg) != IKE_SA_INIT)
+ { /* only do host updates based on verified messages */
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+ { /* with MOBIKE, we do no implicit updates */
+ this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
+ }
+ }
+ charon->bus->message(charon->bus, msg, TRUE, TRUE);
+ if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+ { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+ return SUCCESS;
+ }
+ if (process_request(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ this->responding.mid++;
+ }
+ else if ((mid == this->responding.mid - 1) && this->responding.packet)
+ {
+ packet_t *clone;
+ host_t *host;
+
+ DBG1(DBG_IKE, "received retransmit of request with ID %d, "
+ "retransmitting response", mid);
+ clone = this->responding.packet->clone(this->responding.packet);
+ host = msg->get_destination(msg);
+ clone->set_source(clone, host->clone(host));
+ host = msg->get_source(msg);
+ clone->set_destination(clone, host->clone(host));
+ charon->sender->send(charon->sender, clone);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+ mid, this->responding.mid);
+ }
+ }
+ else
+ {
+ if (mid == this->initiating.mid)
+ {
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+ this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+ msg->get_exchange_type(msg) != IKE_SA_INIT)
+ { /* only do host updates based on verified messages */
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+ { /* with MOBIKE, we do no implicit updates */
+ this->ike_sa->update_hosts(this->ike_sa, me, other, FALSE);
+ }
+ }
+ charon->bus->message(charon->bus, msg, TRUE, TRUE);
+ if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+ { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+ return SUCCESS;
+ }
+ if (process_response(this, msg) != SUCCESS)
+ {
+ flush(this);
+ return DESTROY_ME;
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+ mid, this->initiating.mid);
+ return SUCCESS;
+ }
+ }
+ return SUCCESS;
+}
+
+METHOD(task_manager_t, queue_task, void,
+ private_task_manager_t *this, task_t *task)
+{
+ if (task->get_type(task) == TASK_IKE_MOBIKE)
+ { /* there is no need to queue more than one mobike task */
+ enumerator_t *enumerator;
+ task_t *current;
+
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (current->get_type(current) == TASK_IKE_MOBIKE)
+ {
+ enumerator->destroy(enumerator);
+ task->destroy(task);
+ return;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+ this->queued_tasks->insert_last(this->queued_tasks, task);
+}
+
+/**
+ * Check if a given task has been queued already
+ */
+static bool has_queued(private_task_manager_t *this, task_type_t type)
+{
+ enumerator_t *enumerator;
+ bool found = FALSE;
+ task_t *task;
+
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (task->get_type(task) == type)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+METHOD(task_manager_t, queue_ike, void,
+ private_task_manager_t *this)
+{
+ if (!has_queued(this, TASK_IKE_VENDOR))
+ {
+ queue_task(this, (task_t*)ike_vendor_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_INIT))
+ {
+ queue_task(this, (task_t*)ike_init_create(this->ike_sa, TRUE, NULL));
+ }
+ if (!has_queued(this, TASK_IKE_NATD))
+ {
+ queue_task(this, (task_t*)ike_natd_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_CERT_PRE))
+ {
+ queue_task(this, (task_t*)ike_cert_pre_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_AUTH))
+ {
+ queue_task(this, (task_t*)ike_auth_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_CERT_POST))
+ {
+ queue_task(this, (task_t*)ike_cert_post_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_CONFIG))
+ {
+ queue_task(this, (task_t*)ike_config_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_AUTH_LIFETIME))
+ {
+ queue_task(this, (task_t*)ike_auth_lifetime_create(this->ike_sa, TRUE));
+ }
+ if (!has_queued(this, TASK_IKE_MOBIKE))
+ {
+ peer_cfg_t *peer_cfg;
+
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer_cfg->use_mobike(peer_cfg))
+ {
+ queue_task(this, (task_t*)ike_mobike_create(this->ike_sa, TRUE));
+ }
+ }
+#ifdef ME
+ if (!has_queued(this, TASK_IKE_ME))
+ {
+ queue_task(this, (task_t*)ike_me_create(this->ike_sa, TRUE));
+ }
+#endif /* ME */
+}
+
+METHOD(task_manager_t, queue_ike_rekey, void,
+ private_task_manager_t *this)
+{
+ queue_task(this, (task_t*)ike_rekey_create(this->ike_sa, TRUE));
+}
+
+METHOD(task_manager_t, queue_ike_reauth, void,
+ private_task_manager_t *this)
+{
+ queue_task(this, (task_t*)ike_reauth_create(this->ike_sa));
+}
+
+METHOD(task_manager_t, queue_ike_delete, void,
+ private_task_manager_t *this)
+{
+ queue_task(this, (task_t*)ike_delete_create(this->ike_sa, TRUE));
+}
+
+METHOD(task_manager_t, queue_mobike, void,
+ private_task_manager_t *this, bool roam, bool address)
+{
+ ike_mobike_t *mobike;
+
+ mobike = ike_mobike_create(this->ike_sa, TRUE);
+ if (roam)
+ {
+ mobike->roam(mobike, address);
+ }
+ else
+ {
+ mobike->addresses(mobike);
+ }
+ queue_task(this, &mobike->task);
+}
+
+METHOD(task_manager_t, queue_child, void,
+ private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid,
+ traffic_selector_t *tsi, traffic_selector_t *tsr)
+{
+ child_create_t *task;
+
+ task = child_create_create(this->ike_sa, cfg, FALSE, tsi, tsr);
+ if (reqid)
+ {
+ task->use_reqid(task, reqid);
+ }
+ queue_task(this, &task->task);
+}
+
+METHOD(task_manager_t, queue_child_rekey, void,
+ private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi)
+{
+ queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi));
+}
+
+METHOD(task_manager_t, queue_child_delete, void,
+ private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi,
+ bool expired)
+{
+ queue_task(this, (task_t*)child_delete_create(this->ike_sa,
+ protocol, spi, expired));
+}
+
+METHOD(task_manager_t, queue_dpd, void,
+ private_task_manager_t *this)
+{
+ ike_mobike_t *mobike;
+
+ if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE) &&
+ this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE))
+ {
+ /* use mobike enabled DPD to detect NAT mapping changes */
+ mobike = ike_mobike_create(this->ike_sa, TRUE);
+ mobike->dpd(mobike);
+ queue_task(this, &mobike->task);
+ }
+ else
+ {
+ queue_task(this, (task_t*)ike_dpd_create(TRUE));
+ }
+}
+
+METHOD(task_manager_t, adopt_tasks, void,
+ private_task_manager_t *this, task_manager_t *other_public)
+{
+ private_task_manager_t *other = (private_task_manager_t*)other_public;
+ task_t *task;
+
+ /* move queued tasks from other to this */
+ while (other->queued_tasks->remove_last(other->queued_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+}
+
+METHOD(task_manager_t, busy, bool,
+ private_task_manager_t *this)
+{
+ return (this->active_tasks->get_count(this->active_tasks) > 0);
+}
+
+METHOD(task_manager_t, reset, void,
+ private_task_manager_t *this, u_int32_t initiate, u_int32_t respond)
+{
+ enumerator_t *enumerator;
+ task_t *task;
+
+ /* reset message counters and retransmit packets */
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ if (initiate != UINT_MAX)
+ {
+ this->initiating.mid = initiate;
+ }
+ if (respond != UINT_MAX)
+ {
+ this->responding.mid = respond;
+ }
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+
+ /* reset queued tasks */
+ enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ task->migrate(task, this->ike_sa);
+ }
+ enumerator->destroy(enumerator);
+
+ /* reset active tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+
+ this->reset = TRUE;
+}
+
+METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
+ private_task_manager_t *this, task_queue_t queue)
+{
+ switch (queue)
+ {
+ case TASK_QUEUE_ACTIVE:
+ return this->active_tasks->create_enumerator(this->active_tasks);
+ case TASK_QUEUE_PASSIVE:
+ return this->passive_tasks->create_enumerator(this->passive_tasks);
+ case TASK_QUEUE_QUEUED:
+ return this->queued_tasks->create_enumerator(this->queued_tasks);
+ default:
+ return enumerator_create_empty();
+ }
+}
+
+METHOD(task_manager_t, destroy, void,
+ private_task_manager_t *this)
+{
+ flush(this);
+
+ this->active_tasks->destroy(this->active_tasks);
+ this->queued_tasks->destroy(this->queued_tasks);
+ this->passive_tasks->destroy(this->passive_tasks);
+
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
+{
+ private_task_manager_t *this;
+
+ INIT(this,
+ .public = {
+ .task_manager = {
+ .process_message = _process_message,
+ .queue_task = _queue_task,
+ .queue_ike = _queue_ike,
+ .queue_ike_rekey = _queue_ike_rekey,
+ .queue_ike_reauth = _queue_ike_reauth,
+ .queue_ike_delete = _queue_ike_delete,
+ .queue_mobike = _queue_mobike,
+ .queue_child = _queue_child,
+ .queue_child_rekey = _queue_child_rekey,
+ .queue_child_delete = _queue_child_delete,
+ .queue_dpd = _queue_dpd,
+ .initiate = _initiate,
+ .retransmit = _retransmit,
+ .incr_mid = _incr_mid,
+ .reset = _reset,
+ .adopt_tasks = _adopt_tasks,
+ .busy = _busy,
+ .create_task_enumerator = _create_task_enumerator,
+ .flush_queue = _flush_queue,
+ .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,
+ "%s.retransmit_tries", RETRANSMIT_TRIES, charon->name),
+ .retransmit_timeout = lib->settings->get_double(lib->settings,
+ "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, charon->name),
+ .retransmit_base = lib->settings->get_double(lib->settings,
+ "%s.retransmit_base", RETRANSMIT_BASE, charon->name),
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.h b/src/libcharon/sa/ikev2/task_manager_v2.h
new file mode 100644
index 000000000..70444ae27
--- /dev/null
+++ b/src/libcharon/sa/ikev2/task_manager_v2.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 task_manager_v2 task_manager_v2
+ * @{ @ingroup ikev2
+ */
+
+#ifndef TASK_MANAGER_V2_H_
+#define TASK_MANAGER_V2_H_
+
+typedef struct task_manager_v2_t task_manager_v2_t;
+
+#include <sa/task_manager.h>
+
+/**
+ * Task manager, IKEv2 variant.
+ */
+struct task_manager_v2_t {
+
+ /**
+ * Implements task_manager_t.
+ */
+ task_manager_t task_manager;
+};
+
+/**
+ * Create an instance of the task manager.
+ *
+ * @param ike_sa IKE_SA to manage.
+ */
+task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa);
+
+#endif /** TASK_MANAGER_V2_H_ @}*/
diff --git a/src/libcharon/sa/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index 67c29d31f..46a165546 100644
--- a/src/libcharon/sa/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -18,6 +18,7 @@
#include "child_create.h"
#include <daemon.h>
+#include <sa/ikev2/keymat_v2.h>
#include <crypto/diffie_hellman.h>
#include <credentials/certificates/x509.h>
#include <encoding/payloads/sa_payload.h>
@@ -109,7 +110,7 @@ struct private_child_create_t {
/**
* IKE_SAs keymat
*/
- keymat_t *keymat;
+ keymat_v2_t *keymat;
/**
* mode the new CHILD_SA uses (transport/tunnel/beet)
@@ -170,6 +171,11 @@ struct private_child_create_t {
* whether the CHILD_SA rekeys an existing one
*/
bool rekey;
+
+ /**
+ * whether we are retrying with another DH group
+ */
+ bool retry;
};
/**
@@ -191,18 +197,24 @@ static status_t get_nonce(message_t *message, chunk_t *nonce)
/**
* generate a new nonce to include in a CREATE_CHILD_SA message
*/
-static status_t generate_nonce(chunk_t *nonce)
+static status_t generate_nonce(private_child_create_t *this)
{
- rng_t *rng;
+ nonce_gen_t *nonceg;
- rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
- if (!rng)
+ nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
+ if (!nonceg)
{
- DBG1(DBG_IKE, "error generating nonce value, no RNG found");
+ DBG1(DBG_IKE, "no nonce generator found to create nonce");
return FAILED;
}
- rng->allocate_bytes(rng, NONCE_SIZE, nonce);
- rng->destroy(rng);
+ if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, &this->my_nonce))
+ {
+ DBG1(DBG_IKE, "nonce allocation failed");
+ nonceg->destroy(nonceg);
+ return FAILED;
+ }
+ nonceg->destroy(nonceg);
+
return SUCCESS;
}
@@ -265,7 +277,7 @@ static void schedule_inactivity_timeout(private_child_create_t *this)
if (timeout)
{
close_ike = lib->settings->get_bool(lib->settings,
- "charon.inactivity_close_ike", FALSE);
+ "%s.inactivity_close_ike", FALSE, charon->name);
lib->scheduler->schedule_job(lib->scheduler, (job_t*)
inactivity_job_create(this->child_sa->get_reqid(this->child_sa),
timeout, close_ike), timeout);
@@ -273,6 +285,62 @@ static void schedule_inactivity_timeout(private_child_create_t *this)
}
/**
+ * Check if we have a an address pool configured
+ */
+static bool have_pool(ike_sa_t *ike_sa)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *peer_cfg;
+ char *pool;
+ bool found = FALSE;
+
+ peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+ if (peer_cfg)
+ {
+ enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
+ if (enumerator->enumerate(enumerator, &pool))
+ {
+ found = TRUE;
+ }
+ enumerator->destroy(enumerator);
+ }
+ return found;
+}
+
+/**
+ * Get hosts to use for dynamic traffic selectors
+ */
+static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
+{
+ enumerator_t *enumerator;
+ linked_list_t *list;
+ host_t *host;
+
+ list = linked_list_create();
+ enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ list->insert_last(list, host);
+ }
+ enumerator->destroy(enumerator);
+
+ if (list->get_count(list) == 0)
+ { /* no virtual IPs assigned */
+ if (local)
+ {
+ host = ike_sa->get_my_host(ike_sa);
+ list->insert_last(list, host);
+ }
+ else if (!have_pool(ike_sa))
+ { /* use host only if we don't have a pool configured */
+ host = ike_sa->get_other_host(ike_sa);
+ list->insert_last(list, host);
+ }
+ }
+ return list;
+}
+
+/**
* Install a CHILD_SA for usage, return value:
* - FAILED: no acceptable proposal
* - INVALID_ARG: diffie hellman group inacceptable
@@ -285,8 +353,8 @@ static status_t select_and_install(private_child_create_t *this,
chunk_t nonce_i, nonce_r;
chunk_t encr_i = chunk_empty, encr_r = chunk_empty;
chunk_t integ_i = chunk_empty, integ_r = chunk_empty;
- linked_list_t *my_ts, *other_ts;
- host_t *me, *other, *other_vip, *my_vip;
+ linked_list_t *my_ts, *other_ts, *list;
+ host_t *me, *other;
bool private;
if (this->proposals == NULL)
@@ -302,8 +370,6 @@ static status_t select_and_install(private_child_create_t *this,
me = this->ike_sa->get_my_host(this->ike_sa);
other = this->ike_sa->get_other_host(this->ike_sa);
- my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
- other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN);
this->proposal = this->config->select_proposal(this->config,
@@ -342,15 +408,6 @@ static status_t select_and_install(private_child_create_t *this,
this->dh_group = MODP_NONE;
}
- if (my_vip == NULL)
- {
- my_vip = me;
- }
- if (other_vip == NULL)
- {
- other_vip = other;
- }
-
if (this->initiator)
{
nonce_i = this->my_nonce;
@@ -365,10 +422,14 @@ static status_t select_and_install(private_child_create_t *this,
my_ts = this->tsr;
other_ts = this->tsi;
}
- my_ts = this->config->get_traffic_selectors(this->config, TRUE, my_ts,
- my_vip);
- other_ts = this->config->get_traffic_selectors(this->config, FALSE, other_ts,
- other_vip);
+ list = get_dynamic_hosts(this->ike_sa, TRUE);
+ my_ts = this->config->get_traffic_selectors(this->config,
+ TRUE, my_ts, list);
+ list->destroy(list);
+ list = get_dynamic_hosts(this->ike_sa, FALSE);
+ other_ts = this->config->get_traffic_selectors(this->config,
+ FALSE, other_ts, list);
+ list->destroy(list);
if (this->initiator)
{
@@ -491,7 +552,32 @@ static status_t select_and_install(private_child_create_t *this,
return FAILED;
}
- status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts);
+ if (this->initiator)
+ {
+ status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts);
+ }
+ else
+ {
+ /* use a copy of the traffic selectors, as the POST hook should not
+ * change payloads */
+ my_ts = this->tsr->clone_offset(this->tsr,
+ offsetof(traffic_selector_t, clone));
+ other_ts = this->tsi->clone_offset(this->tsi,
+ offsetof(traffic_selector_t, clone));
+ charon->bus->narrow(charon->bus, this->child_sa,
+ NARROW_RESPONDER_POST, my_ts, other_ts);
+ if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0)
+ {
+ status = FAILED;
+ }
+ else
+ {
+ status = this->child_sa->add_policies(this->child_sa,
+ my_ts, other_ts);
+ }
+ my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
+ other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
+ }
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
@@ -526,18 +612,18 @@ static void build_payloads(private_child_create_t *this, message_t *message)
/* add SA payload */
if (this->initiator)
{
- sa_payload = sa_payload_create_from_proposal_list(this->proposals);
+ sa_payload = sa_payload_create_from_proposals_v2(this->proposals);
}
else
{
- sa_payload = sa_payload_create_from_proposal(this->proposal);
+ sa_payload = sa_payload_create_from_proposal_v2(this->proposal);
}
message->add_payload(message, (payload_t*)sa_payload);
/* add nonce payload if not in IKE_AUTH */
if (message->get_exchange_type(message) == CREATE_CHILD_SA)
{
- nonce_payload = nonce_payload_create();
+ nonce_payload = nonce_payload_create(NONCE);
nonce_payload->set_nonce(nonce_payload, this->my_nonce);
message->add_payload(message, (payload_t*)nonce_payload);
}
@@ -545,7 +631,8 @@ static void build_payloads(private_child_create_t *this, message_t *message)
/* diffie hellman exchange, if PFS enabled */
if (this->dh)
{
- ke_payload = ke_payload_create_from_diffie_hellman(this->dh);
+ ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE,
+ this->dh);
message->add_payload(message, (payload_t*)ke_payload);
}
@@ -680,7 +767,8 @@ static void process_payloads(private_child_create_t *this, message_t *message)
if (!this->initiator)
{
this->dh_group = ke_payload->get_dh_group_number(ke_payload);
- this->dh = this->keymat->create_dh(this->keymat, this->dh_group);
+ this->dh = this->keymat->keymat.create_dh(
+ &this->keymat->keymat, this->dh_group);
}
if (this->dh)
{
@@ -709,20 +797,22 @@ static void process_payloads(private_child_create_t *this, message_t *message)
METHOD(task_t, build_i, status_t,
private_child_create_t *this, message_t *message)
{
- host_t *me, *other, *vip;
+ enumerator_t *enumerator;
+ host_t *vip;
peer_cfg_t *peer_cfg;
+ linked_list_t *list;
switch (message->get_exchange_type(message))
{
case IKE_SA_INIT:
return get_nonce(message, &this->my_nonce);
case CREATE_CHILD_SA:
- if (generate_nonce(&this->my_nonce) != SUCCESS)
+ if (generate_nonce(this) != SUCCESS)
{
message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
return SUCCESS;
}
- if (this->dh_group == MODP_NONE)
+ if (!this->retry)
{
this->dh_group = this->config->get_dh_group(this->config);
}
@@ -749,36 +839,38 @@ METHOD(task_t, build_i, status_t,
this->config->get_name(this->config));
}
- /* reuse virtual IP if we already have one */
- me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
- if (me == NULL)
- {
- me = this->ike_sa->get_my_host(this->ike_sa);
- }
- other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
- if (other == NULL)
- {
- other = this->ike_sa->get_other_host(this->ike_sa);
- }
-
/* check if we want a virtual IP, but don't have one */
+ list = linked_list_create();
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
- vip = peer_cfg->get_virtual_ip(peer_cfg);
- if (!this->reqid && vip)
+ if (!this->reqid)
{
- /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */
- vip = host_create_any(vip->get_family(vip));
- this->tsi = this->config->get_traffic_selectors(this->config, TRUE,
- NULL, vip);
- vip->destroy(vip);
+ enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg);
+ while (enumerator->enumerate(enumerator, &vip))
+ {
+ /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */
+ vip = host_create_any(vip->get_family(vip));
+ list->insert_last(list, vip);
+ }
+ enumerator->destroy(enumerator);
}
- else
- { /* but narrow it for host2host / if we already have a vip */
- this->tsi = this->config->get_traffic_selectors(this->config, TRUE,
- NULL, me);
+ if (list->get_count(list))
+ {
+ this->tsi = this->config->get_traffic_selectors(this->config,
+ TRUE, NULL, list);
+ list->destroy_offset(list, offsetof(host_t, destroy));
}
- this->tsr = this->config->get_traffic_selectors(this->config, FALSE,
- NULL, other);
+ else
+ { /* no virtual IPs configured */
+ list->destroy(list);
+ list = get_dynamic_hosts(this->ike_sa, TRUE);
+ this->tsi = this->config->get_traffic_selectors(this->config,
+ TRUE, NULL, list);
+ list->destroy(list);
+ }
+ list = get_dynamic_hosts(this->ike_sa, FALSE);
+ this->tsr = this->config->get_traffic_selectors(this->config,
+ FALSE, NULL, list);
+ list->destroy(list);
if (this->packet_tsi)
{
@@ -812,7 +904,8 @@ METHOD(task_t, build_i, status_t,
if (this->dh_group != MODP_NONE)
{
- this->dh = this->keymat->create_dh(this->keymat, this->dh_group);
+ this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
+ this->dh_group);
}
if (this->config->use_ipcomp(this->config))
@@ -877,7 +970,7 @@ static void handle_child_sa_failure(private_child_create_t *this,
{
if (message->get_exchange_type(message) == IKE_AUTH &&
lib->settings->get_bool(lib->settings,
- "charon.close_ike_on_child_failure", FALSE))
+ "%s.close_ike_on_child_failure", FALSE, charon->name))
{
/* we delay the delete for 100ms, as the IKE_AUTH response must arrive
* first */
@@ -905,7 +998,7 @@ METHOD(task_t, build_r, status_t,
case IKE_SA_INIT:
return get_nonce(message, &this->my_nonce);
case CREATE_CHILD_SA:
- if (generate_nonce(&this->my_nonce) != SUCCESS)
+ if (generate_nonce(this) != SUCCESS)
{
message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN,
chunk_empty);
@@ -931,22 +1024,16 @@ METHOD(task_t, build_r, status_t,
}
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
- if (peer_cfg && this->tsi && this->tsr)
+ if (!this->config && peer_cfg && this->tsi && this->tsr)
{
- host_t *me, *other;
+ linked_list_t *listr, *listi;
- me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
- if (me == NULL)
- {
- me = this->ike_sa->get_my_host(this->ike_sa);
- }
- other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
- if (other == NULL)
- {
- other = this->ike_sa->get_other_host(this->ike_sa);
- }
- this->config = peer_cfg->select_child_cfg(peer_cfg, this->tsr,
- this->tsi, me, other);
+ listr = get_dynamic_hosts(this->ike_sa, TRUE);
+ listi = get_dynamic_hosts(this->ike_sa, FALSE);
+ this->config = peer_cfg->select_child_cfg(peer_cfg,
+ this->tsr, this->tsi, listr, listi);
+ listr->destroy(listr);
+ listi->destroy(listi);
}
if (this->config == NULL)
@@ -1108,6 +1195,7 @@ METHOD(task_t, process_i, status_t,
DBG1(DBG_IKE, "peer didn't accept DH group %N, "
"it requested %N", diffie_hellman_group_names,
this->dh_group, diffie_hellman_group_names, group);
+ this->retry = TRUE;
this->dh_group = group;
this->public.task.migrate(&this->public.task, this->ike_sa);
enumerator->destroy(enumerator);
@@ -1192,6 +1280,13 @@ METHOD(child_create_t, get_child, child_sa_t*,
return this->child_sa;
}
+METHOD(child_create_t, set_config, void,
+ private_child_create_t *this, child_cfg_t *cfg)
+{
+ DESTROY_IF(this->config);
+ this->config = cfg;
+}
+
METHOD(child_create_t, get_lower_nonce, chunk_t,
private_child_create_t *this)
{
@@ -1209,7 +1304,7 @@ METHOD(child_create_t, get_lower_nonce, chunk_t,
METHOD(task_t, get_type, task_type_t,
private_child_create_t *this)
{
- return CHILD_CREATE;
+ return TASK_CHILD_CREATE;
}
METHOD(task_t, migrate, void,
@@ -1234,7 +1329,7 @@ METHOD(task_t, migrate, void,
}
this->ike_sa = ike_sa;
- this->keymat = ike_sa->get_keymat(ike_sa);
+ this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
this->proposal = NULL;
this->proposals = NULL;
this->tsi = NULL;
@@ -1291,6 +1386,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
INIT(this,
.public = {
.get_child = _get_child,
+ .set_config = _set_config,
.get_lower_nonce = _get_lower_nonce,
.use_reqid = _use_reqid,
.task = {
@@ -1304,12 +1400,13 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
.packet_tsi = tsi ? tsi->clone(tsi) : NULL,
.packet_tsr = tsr ? tsr->clone(tsr) : NULL,
.dh_group = MODP_NONE,
- .keymat = ike_sa->get_keymat(ike_sa),
+ .keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa),
.mode = MODE_TUNNEL,
.tfcv3 = TRUE,
.ipcomp = IPCOMP_NONE,
.ipcomp_received = IPCOMP_NONE,
.rekey = rekey,
+ .retry = FALSE,
);
if (config)
@@ -1317,7 +1414,6 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
this->public.task.build = _build_i;
this->public.task.process = _process_i;
this->initiator = TRUE;
- config->get_ref(config);
}
else
{
diff --git a/src/libcharon/sa/tasks/child_create.h b/src/libcharon/sa/ikev2/tasks/child_create.h
index 5dedeb8b1..d29ba3d98 100644
--- a/src/libcharon/sa/tasks/child_create.h
+++ b/src/libcharon/sa/ikev2/tasks/child_create.h
@@ -15,7 +15,7 @@
/**
* @defgroup child_create child_create
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef CHILD_CREATE_H_
@@ -25,11 +25,11 @@ typedef struct child_create_t child_create_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
#include <config/child_cfg.h>
/**
- * Task of type CHILD_CREATE, established a new CHILD_SA.
+ * Task of type TASK_CHILD_CREATE, established a new CHILD_SA.
*
* This task may be included in the IKE_AUTH message or in a separate
* CREATE_CHILD_SA exchange.
@@ -64,6 +64,13 @@ struct child_create_t {
* @return child_sa
*/
child_sa_t* (*get_child) (child_create_t *this);
+
+ /**
+ * Enforce a specific CHILD_SA config as responder.
+ *
+ * @param cfg configuration to enforce, reference gets owned
+ */
+ void (*set_config)(child_create_t *this, child_cfg_t *cfg);
};
/**
diff --git a/src/libcharon/sa/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index dc4b30dd3..644af782c 100644
--- a/src/libcharon/sa/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -62,6 +62,11 @@ struct private_child_delete_t {
bool rekeyed;
/**
+ * CHILD_SA already expired?
+ */
+ bool expired;
+
+ /**
* CHILD_SAs which get deleted
*/
linked_list_t *child_sas;
@@ -87,7 +92,7 @@ static void build_payloads(private_child_delete_t *this, message_t *message)
case PROTO_ESP:
if (esp == NULL)
{
- esp = delete_payload_create(PROTO_ESP);
+ esp = delete_payload_create(DELETE, PROTO_ESP);
message->add_payload(message, (payload_t*)esp);
}
esp->add_spi(esp, spi);
@@ -97,7 +102,7 @@ static void build_payloads(private_child_delete_t *this, message_t *message)
case PROTO_AH:
if (ah == NULL)
{
- ah = delete_payload_create(PROTO_AH);
+ ah = delete_payload_create(DELETE, PROTO_AH);
message->add_payload(message, (payload_t*)ah);
}
ah->add_spi(ah, spi);
@@ -247,16 +252,29 @@ static void log_children(private_child_delete_t *this)
enumerator = this->child_sas->create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
- child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in);
- child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out);
-
- DBG0(DBG_IKE, "closing CHILD_SA %s{%d} "
- "with SPIs %.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
- child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
- ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
- ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
- child_sa->get_traffic_selectors(child_sa, TRUE),
- child_sa->get_traffic_selectors(child_sa, FALSE));
+ if (this->expired)
+ {
+ DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} "
+ "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+ child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ ntohl(child_sa->get_spi(child_sa, TRUE)),
+ ntohl(child_sa->get_spi(child_sa, FALSE)),
+ child_sa->get_traffic_selectors(child_sa, TRUE),
+ child_sa->get_traffic_selectors(child_sa, FALSE));
+ }
+ else
+ {
+ child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in);
+ child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out);
+
+ DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs %.8x_i "
+ "(%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
+ child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
+ ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
+ child_sa->get_traffic_selectors(child_sa, TRUE),
+ child_sa->get_traffic_selectors(child_sa, FALSE));
+ }
}
enumerator->destroy(enumerator);
}
@@ -324,7 +342,7 @@ METHOD(task_t, build_r, status_t,
METHOD(task_t, get_type, task_type_t,
private_child_delete_t *this)
{
- return CHILD_DELETE;
+ return TASK_CHILD_DELETE;
}
METHOD(child_delete_t , get_child, child_sa_t*,
@@ -356,7 +374,7 @@ METHOD(task_t, destroy, void,
* Described in header.
*/
child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
- u_int32_t spi)
+ u_int32_t spi, bool expired)
{
private_child_delete_t *this;
@@ -373,6 +391,7 @@ child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
.child_sas = linked_list_create(),
.protocol = protocol,
.spi = spi,
+ .expired = expired,
);
if (protocol != PROTO_NONE)
diff --git a/src/libcharon/sa/tasks/child_delete.h b/src/libcharon/sa/ikev2/tasks/child_delete.h
index 365807c68..1ada0699e 100644
--- a/src/libcharon/sa/tasks/child_delete.h
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.h
@@ -15,7 +15,7 @@
/**
* @defgroup child_delete child_delete
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef CHILD_DELETE_H_
@@ -25,7 +25,7 @@ typedef struct child_delete_t child_delete_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
#include <sa/child_sa.h>
/**
@@ -52,9 +52,10 @@ struct child_delete_t {
* @param ike_sa IKE_SA this task works for
* @param protocol protocol of CHILD_SA to delete, PROTO_NONE as responder
* @param spi inbound SPI of CHILD_SA to delete
+ * @param expired TRUE if CHILD_SA already expired
* @return child_delete task to handle by the task_manager
*/
child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
- u_int32_t spi);
+ u_int32_t spi, bool expired);
#endif /** CHILD_DELETE_H_ @}*/
diff --git a/src/libcharon/sa/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index 76d185590..f8c2ed141 100644
--- a/src/libcharon/sa/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -18,8 +18,8 @@
#include <daemon.h>
#include <encoding/payloads/notify_payload.h>
-#include <sa/tasks/child_create.h>
-#include <sa/tasks/child_delete.h>
+#include <sa/ikev2/tasks/child_create.h>
+#include <sa/ikev2/tasks/child_delete.h>
#include <processing/jobs/rekey_child_sa_job.h>
#include <processing/jobs/rekey_ike_sa_job.h>
@@ -153,16 +153,16 @@ METHOD(task_t, build_i, status_t,
config = this->child_sa->get_config(this->child_sa);
/* we just need the rekey notify ... */
- notify = notify_payload_create_from_protocol_and_type(this->protocol,
- REKEY_SA);
+ notify = notify_payload_create_from_protocol_and_type(NOTIFY,
+ this->protocol, REKEY_SA);
notify->set_spi(notify, this->spi);
message->add_payload(message, (payload_t*)notify);
/* ... our CHILD_CREATE task does the hard work for us. */
if (!this->child_create)
{
- this->child_create = child_create_create(this->ike_sa, config, TRUE,
- NULL, NULL);
+ this->child_create = child_create_create(this->ike_sa,
+ config->get_ref(config), TRUE, NULL, NULL);
}
reqid = this->child_sa->get_reqid(this->child_sa);
this->child_create->use_reqid(this->child_create, reqid);
@@ -187,6 +187,7 @@ METHOD(task_t, process_r, status_t,
METHOD(task_t, build_r, status_t,
private_child_rekey_t *this, message_t *message)
{
+ child_cfg_t *config;
u_int32_t reqid;
if (this->child_sa == NULL ||
@@ -200,6 +201,8 @@ METHOD(task_t, build_r, status_t,
/* let the CHILD_CREATE task build the response */
reqid = this->child_sa->get_reqid(this->child_sa);
this->child_create->use_reqid(this->child_create, reqid);
+ config = this->child_sa->get_config(this->child_sa);
+ this->child_create->set_config(this->child_create, config->get_ref(config));
this->child_create->task.build(&this->child_create->task, message);
if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
@@ -224,7 +227,7 @@ static child_sa_t *handle_collision(private_child_rekey_t *this)
{
child_sa_t *to_delete;
- if (this->collision->get_type(this->collision) == CHILD_REKEY)
+ if (this->collision->get_type(this->collision) == TASK_CHILD_REKEY)
{
chunk_t this_nonce, other_nonce;
private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
@@ -311,7 +314,7 @@ METHOD(task_t, process_i, status_t,
/* establishing new child failed, reuse old. but not when we
* received a delete in the meantime */
if (!(this->collision &&
- this->collision->get_type(this->collision) == CHILD_DELETE))
+ this->collision->get_type(this->collision) == TASK_CHILD_DELETE))
{
job_t *job;
u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
@@ -352,7 +355,7 @@ METHOD(task_t, process_i, status_t,
protocol = to_delete->get_protocol(to_delete);
/* rekeying done, delete the obsolete CHILD_SA using a subtask */
- this->child_delete = child_delete_create(this->ike_sa, protocol, spi);
+ this->child_delete = child_delete_create(this->ike_sa, protocol, spi, FALSE);
this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete;
this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete;
@@ -362,7 +365,7 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_child_rekey_t *this)
{
- return CHILD_REKEY;
+ return TASK_CHILD_REKEY;
}
METHOD(child_rekey_t, collide, void,
@@ -370,7 +373,7 @@ METHOD(child_rekey_t, collide, void,
{
/* the task manager only detects exchange collision, but not if
* the collision is for the same child. we check it here. */
- if (other->get_type(other) == CHILD_REKEY)
+ if (other->get_type(other) == TASK_CHILD_REKEY)
{
private_child_rekey_t *rekey = (private_child_rekey_t*)other;
if (rekey->child_sa != this->child_sa)
@@ -380,7 +383,7 @@ METHOD(child_rekey_t, collide, void,
return;
}
}
- else if (other->get_type(other) == CHILD_DELETE)
+ else if (other->get_type(other) == TASK_CHILD_DELETE)
{
child_delete_t *del = (child_delete_t*)other;
if (del->get_child(del) == this->child_create->get_child(this->child_create))
@@ -403,8 +406,8 @@ METHOD(child_rekey_t, collide, void,
other->destroy(other);
return;
}
- DBG1(DBG_IKE, "detected %N collision with %N", task_type_names, CHILD_REKEY,
- task_type_names, other->get_type(other));
+ DBG1(DBG_IKE, "detected %N collision with %N", task_type_names,
+ TASK_CHILD_REKEY, task_type_names, other->get_type(other));
DESTROY_IF(this->collision);
this->collision = other;
}
@@ -462,7 +465,7 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
.protocol = protocol,
.spi = spi,
);
-
+
if (protocol != PROTO_NONE)
{
this->public.task.build = _build_i;
diff --git a/src/libcharon/sa/tasks/child_rekey.h b/src/libcharon/sa/ikev2/tasks/child_rekey.h
index 9b1aea5fa..23384653d 100644
--- a/src/libcharon/sa/tasks/child_rekey.h
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.h
@@ -15,7 +15,7 @@
/**
* @defgroup child_rekey child_rekey
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef CHILD_REKEY_H_
@@ -26,10 +26,10 @@ typedef struct child_rekey_t child_rekey_t;
#include <library.h>
#include <sa/ike_sa.h>
#include <sa/child_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
- * Task of type CHILD_REKEY, rekey an established CHILD_SA.
+ * Task of type TASK_CHILD_REKEY, rekey an established CHILD_SA.
*/
struct child_rekey_t {
@@ -51,7 +51,7 @@ struct child_rekey_t {
};
/**
- * Create a new CHILD_REKEY task.
+ * Create a new TASK_CHILD_REKEY task.
*
* @param ike_sa IKE_SA this task works for
* @param protocol protocol of CHILD_SA to rekey, PROTO_NONE as responder
diff --git a/src/libcharon/sa/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c
index 665468fe8..cd94ccd9e 100644
--- a/src/libcharon/sa/tasks/ike_auth.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c
@@ -24,7 +24,7 @@
#include <encoding/payloads/auth_payload.h>
#include <encoding/payloads/eap_payload.h>
#include <encoding/payloads/nonce_payload.h>
-#include <sa/authenticators/eap_authenticator.h>
+#include <sa/ikev2/authenticators/eap_authenticator.h>
typedef struct private_ike_auth_t private_ike_auth_t;
@@ -120,7 +120,7 @@ struct private_ike_auth_t {
static bool multiple_auth_enabled()
{
return lib->settings->get_bool(lib->settings,
- "charon.multiple_authentication", TRUE);
+ "%s.multiple_authentication", TRUE, charon->name);
}
/**
@@ -270,8 +270,10 @@ static bool load_cfg_candidates(private_ike_auth_t *this)
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);
+ me, other, my_id, other_id, IKEV2);
while (enumerator->enumerate(enumerator, &peer_cfg))
{
peer_cfg->get_ref(peer_cfg);
@@ -406,7 +408,8 @@ METHOD(task_t, build_i, status_t,
if (cfg)
{
idr = cfg->get(cfg, AUTH_RULE_IDENTITY);
- if (idr && !idr->contains_wildcards(idr))
+ if (!cfg->get(cfg, AUTH_RULE_IDENTITY_LOOSE) && idr &&
+ !idr->contains_wildcards(idr))
{
this->ike_sa->set_other_id(this->ike_sa, idr->clone(idr));
id_payload = id_payload_create_from_identification(
@@ -433,7 +436,8 @@ METHOD(task_t, build_i, status_t,
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)
+ this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NO &&
+ this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NEVER)
{
host_t *host;
@@ -1033,7 +1037,7 @@ peer_auth_failed:
METHOD(task_t, get_type, task_type_t,
private_ike_auth_t *this)
{
- return IKE_AUTHENTICATE;
+ return TASK_IKE_AUTH;
}
METHOD(task_t, migrate, void,
@@ -1104,4 +1108,3 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator)
}
return &this->public;
}
-
diff --git a/src/libcharon/sa/tasks/ike_auth.h b/src/libcharon/sa/ikev2/tasks/ike_auth.h
index 132907941..ca864a710 100644
--- a/src/libcharon/sa/tasks/ike_auth.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_auth ike_auth
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_AUTH_H_
@@ -25,7 +25,7 @@ typedef struct ike_auth_t ike_auth_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Task of type ike_auth, authenticates an IKE_SA using authenticators.
@@ -46,7 +46,7 @@ struct ike_auth_t {
};
/**
- * Create a new task of type IKE_AUTHENTICATE.
+ * Create a new task of type TASK_IKE_AUTH.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if task is the initiator of an exchange
diff --git a/src/libcharon/sa/tasks/ike_auth_lifetime.c b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c
index a57cfd075..a7d162e68 100644
--- a/src/libcharon/sa/tasks/ike_auth_lifetime.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c
@@ -124,7 +124,7 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_auth_lifetime_t *this)
{
- return IKE_AUTH_LIFETIME;
+ return TASK_IKE_AUTH_LIFETIME;
}
METHOD(task_t, migrate, void,
@@ -170,4 +170,3 @@ ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator)
return &this->public;
}
-
diff --git a/src/libcharon/sa/tasks/ike_auth_lifetime.h b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.h
index 3b129b9e3..4d5087ff5 100644
--- a/src/libcharon/sa/tasks/ike_auth_lifetime.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_auth_lifetime ike_auth_lifetime
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_AUTH_LIFETIME_H_
@@ -25,10 +25,10 @@ typedef struct ike_auth_lifetime_t ike_auth_lifetime_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
- * Task of type IKE_AUTH_LIFETIME, implements RFC4478.
+ * Task of type TASK_IKE_AUTH_LIFETIME, implements RFC4478.
*
* This task exchanges lifetimes for IKE_AUTH to force a client to
* reauthenticate before the responders lifetime reaches the limit.
@@ -42,7 +42,7 @@ struct ike_auth_lifetime_t {
};
/**
- * Create a new IKE_AUTH_LIFETIME task.
+ * Create a new TASK_IKE_AUTH_LIFETIME task.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE if taks is initiated by us
@@ -50,4 +50,4 @@ struct ike_auth_lifetime_t {
*/
ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator);
-#endif /** IKE_MOBIKE_H_ @}*/
+#endif /** IKE_AUTH_LIFETIME_H_ @}*/
diff --git a/src/libcharon/sa/tasks/ike_cert_post.c b/src/libcharon/sa/ikev2/tasks/ike_cert_post.c
index 94af50eae..a93e5137e 100644
--- a/src/libcharon/sa/tasks/ike_cert_post.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_cert_post.c
@@ -62,14 +62,14 @@ static cert_payload_t *build_cert_payload(private_ike_cert_post_t *this,
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_HASH_AND_URL))
{
- return cert_payload_create_from_cert(cert);
+ return cert_payload_create_from_cert(CERTIFICATE, cert);
}
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (!hasher)
{
DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported");
- return cert_payload_create_from_cert(cert);
+ return cert_payload_create_from_cert(CERTIFICATE, cert);
}
if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoded))
@@ -78,7 +78,12 @@ static cert_payload_t *build_cert_payload(private_ike_cert_post_t *this,
hasher->destroy(hasher);
return NULL;
}
- hasher->allocate_hash(hasher, encoded, &hash);
+ if (!hasher->allocate_hash(hasher, encoded, &hash))
+ {
+ hasher->destroy(hasher);
+ chunk_free(&encoded);
+ return cert_payload_create_from_cert(CERTIFICATE, cert);
+ }
chunk_free(&encoded);
hasher->destroy(hasher);
id = identification_create_from_encoding(ID_KEY_ID, hash);
@@ -91,7 +96,7 @@ static cert_payload_t *build_cert_payload(private_ike_cert_post_t *this,
}
else
{
- payload = cert_payload_create_from_cert(cert);
+ payload = cert_payload_create_from_cert(CERTIFICATE, cert);
}
enumerator->destroy(enumerator);
chunk_free(&hash);
@@ -154,7 +159,7 @@ static void build_certs(private_ike_cert_post_t *this, message_t *message)
{
if (type == AUTH_RULE_IM_CERT)
{
- payload = cert_payload_create_from_cert(cert);
+ payload = cert_payload_create_from_cert(CERTIFICATE, cert);
if (payload)
{
DBG1(DBG_IKE, "sending issuer cert \"%Y\"",
@@ -207,7 +212,7 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_cert_post_t *this)
{
- return IKE_CERT_POST;
+ return TASK_IKE_CERT_POST;
}
METHOD(task_t, migrate, void,
@@ -254,4 +259,3 @@ ike_cert_post_t *ike_cert_post_create(ike_sa_t *ike_sa, bool initiator)
return &this->public;
}
-
diff --git a/src/libcharon/sa/tasks/ike_cert_post.h b/src/libcharon/sa/ikev2/tasks/ike_cert_post.h
index b3881a01a..34606b1e8 100644
--- a/src/libcharon/sa/tasks/ike_cert_post.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_cert_post.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_cert_post ike_cert_post
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_CERT_POST_H_
@@ -25,7 +25,7 @@ typedef struct ike_cert_post_t ike_cert_post_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Task of type ike_cert_post, certificate processing after authentication.
diff --git a/src/libcharon/sa/tasks/ike_cert_pre.c b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c
index b33aebe46..60e878777 100644
--- a/src/libcharon/sa/tasks/ike_cert_pre.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c
@@ -398,7 +398,8 @@ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message)
{
message->add_payload(message, (payload_t*)req);
- if (lib->settings->get_bool(lib->settings, "charon.hash_and_url", FALSE))
+ if (lib->settings->get_bool(lib->settings,
+ "%s.hash_and_url", FALSE, charon->name))
{
message->add_notify(message, FALSE, HTTP_CERT_LOOKUP_SUPPORTED,
chunk_empty);
@@ -479,7 +480,7 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_cert_pre_t *this)
{
- return IKE_CERT_PRE;
+ return TASK_IKE_CERT_PRE;
}
METHOD(task_t, migrate, void,
diff --git a/src/libcharon/sa/tasks/ike_cert_pre.h b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.h
index 4b2d0d470..ac1a85c29 100644
--- a/src/libcharon/sa/tasks/ike_cert_pre.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_cert_pre ike_cert_pre
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_CERT_PRE_H_
@@ -25,7 +25,7 @@ typedef struct ike_cert_pre_t ike_cert_pre_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Task of type ike_cert_post, certificate processing before authentication.
diff --git a/src/libcharon/sa/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c
index 4ef9c56a5..c44f0452c 100644
--- a/src/libcharon/sa/tasks/ike_config.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_config.c
@@ -43,9 +43,9 @@ struct private_ike_config_t {
bool initiator;
/**
- * virtual ip
+ * Received list of virtual IPs, host_t*
*/
- host_t *virtual_ip;
+ linked_list_t *vips;
/**
* list of attributes requested and its handler, entry_t
@@ -98,7 +98,8 @@ static configuration_attribute_t *build_vip(host_t *vip)
chunk = chunk_cata("cc", chunk, prefix);
}
}
- return configuration_attribute_create_value(type, chunk);
+ return configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE,
+ type, chunk);
}
/**
@@ -128,11 +129,11 @@ static void handle_attribute(private_ike_config_t *this,
/* and pass it to the handle function */
handler = hydra->attributes->handle(hydra->attributes,
this->ike_sa->get_other_id(this->ike_sa), handler,
- ca->get_type(ca), ca->get_value(ca));
+ ca->get_type(ca), ca->get_chunk(ca));
if (handler)
{
this->ike_sa->add_configuration_attribute(this->ike_sa,
- handler, ca->get_type(ca), ca->get_value(ca));
+ handler, ca->get_type(ca), ca->get_chunk(ca));
}
}
@@ -153,7 +154,7 @@ static void process_attribute(private_ike_config_t *this,
/* fall */
case INTERNAL_IP6_ADDRESS:
{
- addr = ca->get_value(ca);
+ addr = ca->get_chunk(ca);
if (addr.len == 0)
{
ip = host_create_any(family);
@@ -169,8 +170,7 @@ static void process_attribute(private_ike_config_t *this,
}
if (ip)
{
- DESTROY_IF(this->virtual_ip);
- this->virtual_ip = ip;
+ this->vips->insert_last(this->vips, ip);
}
break;
}
@@ -241,23 +241,45 @@ METHOD(task_t, build_i, status_t,
peer_cfg_t *config;
configuration_attribute_type_t type;
chunk_t data;
- host_t *vip;
+ linked_list_t *vips;
+ host_t *host;
+
+ vips = linked_list_create();
/* reuse virtual IP if we already have one */
- vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
- if (!vip)
+ enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa,
+ TRUE);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ vips->insert_last(vips, host);
+ }
+ enumerator->destroy(enumerator);
+
+ if (vips->get_count(vips) == 0)
{
config = this->ike_sa->get_peer_cfg(this->ike_sa);
- vip = config->get_virtual_ip(config);
+ enumerator = config->create_virtual_ip_enumerator(config);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ vips->insert_last(vips, host);
+ }
+ enumerator->destroy(enumerator);
}
- if (vip)
+
+ if (vips->get_count(vips))
{
- cp = cp_payload_create_type(CFG_REQUEST);
- cp->add_attribute(cp, build_vip(vip));
+ cp = cp_payload_create_type(CONFIGURATION, CFG_REQUEST);
+ enumerator = vips->create_enumerator(vips);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ cp->add_attribute(cp, build_vip(host));
+ }
+ enumerator->destroy(enumerator);
}
- enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes,
- this->ike_sa->get_other_id(this->ike_sa), vip);
+ enumerator = hydra->attributes->create_initiator_enumerator(
+ hydra->attributes,
+ this->ike_sa->get_other_id(this->ike_sa), vips);
while (enumerator->enumerate(enumerator, &handler, &type, &data))
{
configuration_attribute_t *ca;
@@ -266,10 +288,11 @@ METHOD(task_t, build_i, status_t,
/* create configuration attribute */
DBG2(DBG_IKE, "building %N attribute",
configuration_attribute_type_names, type);
- ca = configuration_attribute_create_value(type, data);
+ ca = configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE,
+ type, data);
if (!cp)
{
- cp = cp_payload_create_type(CFG_REQUEST);
+ cp = cp_payload_create_type(CONFIGURATION, CFG_REQUEST);
}
cp->add_attribute(cp, ca);
@@ -282,6 +305,8 @@ METHOD(task_t, build_i, status_t,
}
enumerator->destroy(enumerator);
+ vips->destroy(vips);
+
if (cp)
{
message->add_payload(message, (payload_t*)cp);
@@ -308,58 +333,92 @@ METHOD(task_t, build_r, status_t,
enumerator_t *enumerator;
configuration_attribute_type_t type;
chunk_t value;
- host_t *vip = NULL;
cp_payload_t *cp = NULL;
peer_cfg_t *config;
identification_t *id;
+ linked_list_t *vips, *pools;
+ host_t *requested;
id = this->ike_sa->get_other_eap_id(this->ike_sa);
-
config = this->ike_sa->get_peer_cfg(this->ike_sa);
- if (this->virtual_ip)
+ vips = linked_list_create();
+ pools = linked_list_create_from_enumerator(
+ config->create_pool_enumerator(config));
+
+ this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
+
+ enumerator = this->vips->create_enumerator(this->vips);
+ while (enumerator->enumerate(enumerator, &requested))
{
- DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip);
- if (config->get_pool(config))
+ host_t *found = NULL;
+
+ /* query all pools until we get an address */
+ DBG1(DBG_IKE, "peer requested virtual IP %H", requested);
+
+ found = hydra->attributes->acquire_address(hydra->attributes,
+ pools, id, requested);
+ if (found)
{
- vip = hydra->attributes->acquire_address(hydra->attributes,
- config->get_pool(config), id, this->virtual_ip);
+ DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
+ this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
+ if (!cp)
+ {
+ cp = cp_payload_create_type(CONFIGURATION, CFG_REPLY);
+ }
+ cp->add_attribute(cp, build_vip(found));
+ vips->insert_last(vips, found);
}
- if (vip == NULL)
+ else
{
- DBG1(DBG_IKE, "no virtual IP found, sending %N",
- notify_type_names, INTERNAL_ADDRESS_FAILURE);
- message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE,
- chunk_empty);
- return SUCCESS;
+ DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'",
+ requested, id);
}
- DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id);
- this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip);
+ }
+ enumerator->destroy(enumerator);
- cp = cp_payload_create_type(CFG_REPLY);
- cp->add_attribute(cp, build_vip(vip));
+ if (this->vips->get_count(this->vips) && !vips->get_count(vips))
+ {
+ DBG1(DBG_IKE, "no virtual IP found, sending %N",
+ notify_type_names, INTERNAL_ADDRESS_FAILURE);
+ message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE,
+ chunk_empty);
+ vips->destroy_offset(vips, offsetof(host_t, destroy));
+ pools->destroy(pools);
+ return SUCCESS;
+ }
+ if (pools->get_count(pools) && !this->vips->get_count(this->vips))
+ {
+ DBG1(DBG_IKE, "expected a virtual IP request, sending %N",
+ notify_type_names, FAILED_CP_REQUIRED);
+ message->add_notify(message, FALSE, FAILED_CP_REQUIRED, chunk_empty);
+ vips->destroy_offset(vips, offsetof(host_t, destroy));
+ pools->destroy(pools);
+ return SUCCESS;
}
/* query registered providers for additional attributes to include */
enumerator = hydra->attributes->create_responder_enumerator(
- hydra->attributes, config->get_pool(config), id, vip);
+ hydra->attributes, pools, id, vips);
while (enumerator->enumerate(enumerator, &type, &value))
{
if (!cp)
{
- cp = cp_payload_create_type(CFG_REPLY);
+ cp = cp_payload_create_type(CONFIGURATION, CFG_REPLY);
}
DBG2(DBG_IKE, "building %N attribute",
configuration_attribute_type_names, type);
cp->add_attribute(cp,
- configuration_attribute_create_value(type, value));
+ configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE,
+ type, value));
}
enumerator->destroy(enumerator);
+ vips->destroy_offset(vips, offsetof(host_t, destroy));
+ pools->destroy(pools);
if (cp)
{
message->add_payload(message, (payload_t*)cp);
}
- DESTROY_IF(vip);
return SUCCESS;
}
return NEED_MORE;
@@ -370,13 +429,22 @@ METHOD(task_t, process_i, status_t,
{
if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED)
{ /* in last IKE_AUTH exchange */
+ enumerator_t *enumerator;
+ host_t *host;
process_payloads(this, message);
- if (this->virtual_ip)
+ this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
+
+ enumerator = this->vips->create_enumerator(this->vips);
+ while (enumerator->enumerate(enumerator, &host))
{
- this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip);
+ if (!host->is_anyaddr(host))
+ {
+ this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
+ }
}
+ enumerator->destroy(enumerator);
return SUCCESS;
}
return NEED_MORE;
@@ -385,16 +453,15 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_config_t *this)
{
- return IKE_CONFIG;
+ return TASK_IKE_CONFIG;
}
METHOD(task_t, migrate, void,
private_ike_config_t *this, ike_sa_t *ike_sa)
{
- DESTROY_IF(this->virtual_ip);
-
this->ike_sa = ike_sa;
- this->virtual_ip = NULL;
+ this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
+ this->vips = linked_list_create();
this->requested->destroy_function(this->requested, free);
this->requested = linked_list_create();
}
@@ -402,7 +469,7 @@ METHOD(task_t, migrate, void,
METHOD(task_t, destroy, void,
private_ike_config_t *this)
{
- DESTROY_IF(this->virtual_ip);
+ this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
this->requested->destroy_function(this->requested, free);
free(this);
}
@@ -424,6 +491,7 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator)
},
.initiator = initiator,
.ike_sa = ike_sa,
+ .vips = linked_list_create(),
.requested = linked_list_create(),
);
@@ -440,4 +508,3 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator)
return &this->public;
}
-
diff --git a/src/libcharon/sa/tasks/ike_config.h b/src/libcharon/sa/ikev2/tasks/ike_config.h
index 8cef08697..e35457645 100644
--- a/src/libcharon/sa/tasks/ike_config.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_config.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_config ike_config
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_CONFIG_H_
@@ -25,10 +25,10 @@ typedef struct ike_config_t ike_config_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
- * Task of type IKE_CONFIG, sets up a virtual IP and other
+ * Task of type TASK_IKE_CONFIG, sets up a virtual IP and other
* configurations for an IKE_SA.
*/
struct ike_config_t {
diff --git a/src/libcharon/sa/tasks/ike_delete.c b/src/libcharon/sa/ikev2/tasks/ike_delete.c
index d79674fe4..f127b0c15 100644
--- a/src/libcharon/sa/tasks/ike_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_delete.c
@@ -65,7 +65,7 @@ METHOD(task_t, build_i, status_t,
this->ike_sa->get_other_host(this->ike_sa),
this->ike_sa->get_other_id(this->ike_sa));
- delete_payload = delete_payload_create(PROTO_IKE);
+ delete_payload = delete_payload_create(DELETE, PROTO_IKE);
message->add_payload(message, (payload_t*)delete_payload);
if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING)
@@ -149,7 +149,7 @@ METHOD(task_t, build_r, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_delete_t *this)
{
- return IKE_DELETE;
+ return TASK_IKE_DELETE;
}
METHOD(task_t, migrate, void,
diff --git a/src/libcharon/sa/tasks/ike_delete.h b/src/libcharon/sa/ikev2/tasks/ike_delete.h
index 82782f393..2d5d7cb3a 100644
--- a/src/libcharon/sa/tasks/ike_delete.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_delete.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_delete ike_delete
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_DELETE_H_
@@ -25,7 +25,7 @@ typedef struct ike_delete_t ike_delete_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Task of type ike_delete, delete an IKE_SA.
diff --git a/src/libcharon/sa/tasks/ike_dpd.c b/src/libcharon/sa/ikev2/tasks/ike_dpd.c
index 106eff87c..28ccc2efe 100644
--- a/src/libcharon/sa/tasks/ike_dpd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_dpd.c
@@ -46,7 +46,7 @@ METHOD(task_t, return_success, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_dpd_t *this)
{
- return IKE_DPD;
+ return TASK_IKE_DPD;
}
diff --git a/src/libcharon/sa/tasks/ike_dpd.h b/src/libcharon/sa/ikev2/tasks/ike_dpd.h
index a9f68c31c..026871610 100644
--- a/src/libcharon/sa/tasks/ike_dpd.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_dpd.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_dpd ike_dpd
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_DPD_H_
@@ -25,7 +25,7 @@ typedef struct ike_dpd_t ike_dpd_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Task of type ike_dpd, detects dead peers.
diff --git a/src/libcharon/sa/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c
index dd8a4b086..f2a06735e 100644
--- a/src/libcharon/sa/tasks/ike_init.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_init.c
@@ -20,6 +20,7 @@
#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>
@@ -68,7 +69,7 @@ struct private_ike_init_t {
/**
* Keymat derivation (from IKE_SA)
*/
- keymat_t *keymat;
+ keymat_v2_t *keymat;
/**
* nonce chosen by us
@@ -132,7 +133,7 @@ static void build_payloads(private_ike_init_t *this, message_t *message)
enumerator->destroy(enumerator);
}
- sa_payload = sa_payload_create_from_proposal_list(proposal_list);
+ sa_payload = sa_payload_create_from_proposals_v2(proposal_list);
proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
}
else
@@ -142,13 +143,13 @@ static void build_payloads(private_ike_init_t *this, message_t *message)
/* 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(this->proposal);
+ sa_payload = sa_payload_create_from_proposal_v2(this->proposal);
}
message->add_payload(message, (payload_t*)sa_payload);
- nonce_payload = nonce_payload_create();
+ nonce_payload = nonce_payload_create(NONCE);
nonce_payload->set_nonce(nonce_payload, this->my_nonce);
- ke_payload = ke_payload_create_from_diffie_hellman(this->dh);
+ ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE, this->dh);
if (this->old_sa)
{ /* payload order differs if we are rekeying */
@@ -197,8 +198,8 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
this->dh_group = ke_payload->get_dh_group_number(ke_payload);
if (!this->initiator)
{
- this->dh = this->keymat->create_dh(this->keymat,
- this->dh_group);
+ this->dh = this->keymat->keymat.create_dh(
+ &this->keymat->keymat, this->dh_group);
}
if (this->dh)
{
@@ -224,8 +225,6 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
METHOD(task_t, build_i, status_t,
private_ike_init_t *this, message_t *message)
{
- 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),
@@ -243,7 +242,8 @@ METHOD(task_t, build_i, status_t,
if (!this->dh)
{
this->dh_group = this->config->get_dh_group(this->config);
- this->dh = this->keymat->create_dh(this->keymat, this->dh_group);
+ 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",
@@ -255,14 +255,21 @@ METHOD(task_t, build_i, status_t,
/* 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)
+ nonce_gen_t *nonceg;
+
+ nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
+ if (!nonceg)
+ {
+ DBG1(DBG_IKE, "no nonce generator found to create nonce");
+ return FAILED;
+ }
+ if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, &this->my_nonce))
{
- DBG1(DBG_IKE, "error generating nonce");
+ DBG1(DBG_IKE, "nonce allocation failed");
+ nonceg->destroy(nonceg);
return FAILED;
}
- rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce);
- rng->destroy(rng);
+ nonceg->destroy(nonceg);
}
if (this->cookie.ptr)
@@ -288,20 +295,25 @@ METHOD(task_t, build_i, status_t,
METHOD(task_t, process_r, status_t,
private_ike_init_t *this, message_t *message)
{
- rng_t *rng;
+ nonce_gen_t *nonceg;
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)
+ nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
+ if (!nonceg)
+ {
+ DBG1(DBG_IKE, "no nonce generator found to create nonce");
+ return FAILED;
+ }
+ if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, &this->my_nonce))
{
- DBG1(DBG_IKE, "error generating nonce");
+ DBG1(DBG_IKE, "nonce allocation failed");
+ nonceg->destroy(nonceg);
return FAILED;
}
- rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce);
- rng->destroy(rng);
+ nonceg->destroy(nonceg);
#ifdef ME
{
@@ -327,7 +339,7 @@ METHOD(task_t, process_r, status_t,
static bool derive_keys(private_ike_init_t *this,
chunk_t nonce_i, chunk_t nonce_r)
{
- keymat_t *old_keymat;
+ keymat_v2_t *old_keymat;
pseudo_random_function_t prf_alg = PRF_UNDEFINED;
chunk_t skd = chunk_empty;
ike_sa_id_t *id;
@@ -336,7 +348,7 @@ static bool derive_keys(private_ike_init_t *this,
if (this->old_sa)
{
/* rekeying: Include old SKd, use old PRF, apply SPI */
- old_keymat = this->old_sa->get_keymat(this->old_sa);
+ 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)
{
@@ -352,8 +364,8 @@ static bool derive_keys(private_ike_init_t *this,
{
return FALSE;
}
- charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh,
- nonce_i, nonce_r, this->old_sa);
+ charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, chunk_empty,
+ nonce_i, nonce_r, this->old_sa, NULL);
return TRUE;
}
@@ -505,7 +517,7 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_init_t *this)
{
- return IKE_INIT;
+ return TASK_IKE_INIT;
}
METHOD(task_t, migrate, void,
@@ -515,12 +527,13 @@ METHOD(task_t, migrate, void,
chunk_free(&this->other_nonce);
this->ike_sa = ike_sa;
- this->keymat = ike_sa->get_keymat(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->create_dh(this->keymat, this->dh_group);
+ this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
+ this->dh_group);
}
}
@@ -568,7 +581,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
.ike_sa = ike_sa,
.initiator = initiator,
.dh_group = MODP_NONE,
- .keymat = ike_sa->get_keymat(ike_sa),
+ .keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa),
.old_sa = old_sa,
);
diff --git a/src/libcharon/sa/tasks/ike_init.h b/src/libcharon/sa/ikev2/tasks/ike_init.h
index 4b7f60416..ab169954d 100644
--- a/src/libcharon/sa/tasks/ike_init.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_init.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_init ike_init
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_INIT_H_
@@ -25,10 +25,10 @@ typedef struct ike_init_t ike_init_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
- * Task of type IKE_INIT, creates an IKE_SA without authentication.
+ * Task of type TASK_IKE_INIT, creates an IKE_SA without authentication.
*
* The authentication of is handle in the ike_auth task.
*/
@@ -48,7 +48,7 @@ struct ike_init_t {
};
/**
- * Create a new IKE_INIT task.
+ * Create a new TASK_IKE_INIT task.
*
* @param ike_sa IKE_SA this task works for (new one when rekeying)
* @param initiator TRUE if task is the original initiator
diff --git a/src/libcharon/sa/tasks/ike_me.c b/src/libcharon/sa/ikev2/tasks/ike_me.c
index 8f90efcc3..135c06d19 100644
--- a/src/libcharon/sa/tasks/ike_me.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_me.c
@@ -136,7 +136,7 @@ static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message)
port = host->get_port(host);
enumerator = hydra->kernel_interface->create_address_enumerator(
- hydra->kernel_interface, FALSE, FALSE);
+ hydra->kernel_interface, ADDR_TYPE_REGULAR);
while (enumerator->enumerate(enumerator, (void**)&addr))
{
host = addr->clone(addr);
@@ -291,9 +291,21 @@ METHOD(task_t, build_i, status_t,
{
/* only the initiator creates a connect ID. the responder
* returns the connect ID that it received from the initiator */
- rng->allocate_bytes(rng, ME_CONNECTID_LEN, &this->connect_id);
+ if (!rng->allocate_bytes(rng, ME_CONNECTID_LEN,
+ &this->connect_id))
+ {
+ DBG1(DBG_IKE, "unable to generate ID for ME_CONNECT");
+ rng->destroy(rng);
+ return FAILED;
+ }
+ }
+ if (!rng->allocate_bytes(rng, ME_CONNECTKEY_LEN,
+ &this->connect_key))
+ {
+ DBG1(DBG_IKE, "unable to generate connect key for ME_CONNECT");
+ rng->destroy(rng);
+ return FAILED;
}
- rng->allocate_bytes(rng, ME_CONNECTKEY_LEN, &this->connect_key);
rng->destroy(rng);
message->add_notify(message, FALSE, ME_CONNECTID, this->connect_id);
@@ -750,7 +762,7 @@ METHOD(ike_me_t, relay, void,
METHOD(task_t, get_type, task_type_t,
private_ike_me_t *this)
{
- return IKE_ME;
+ return TASK_IKE_ME;
}
METHOD(task_t, migrate, void,
diff --git a/src/libcharon/sa/tasks/ike_me.h b/src/libcharon/sa/ikev2/tasks/ike_me.h
index 31285a426..44a4ce69c 100644
--- a/src/libcharon/sa/tasks/ike_me.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_me.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_me ike_me
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_ME_H_
@@ -25,10 +25,10 @@ typedef struct ike_me_t ike_me_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
- * Task of type IKE_ME, detects and handles IKE-ME extensions.
+ * Task of type TASK_IKE_ME, detects and handles IKE-ME extensions.
*
* This tasks handles the ME_MEDIATION Notify exchange to setup a mediation
* connection, allows to initiate mediated connections using ME_CONNECT
diff --git a/src/libcharon/sa/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
index fb1100028..ae3526f42 100644
--- a/src/libcharon/sa/tasks/ike_mobike.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
@@ -20,7 +20,7 @@
#include <hydra.h>
#include <daemon.h>
-#include <sa/tasks/ike_natd.h>
+#include <sa/ikev2/tasks/ike_natd.h>
#include <encoding/payloads/notify_payload.h>
#define COOKIE2_SIZE 16
@@ -54,7 +54,7 @@ struct private_ike_mobike_t {
chunk_t cookie2;
/**
- * NAT discovery reusing the IKE_NATD task
+ * NAT discovery reusing the TASK_IKE_NATD task
*/
ike_natd_t *natd;
@@ -192,7 +192,7 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message)
me = this->ike_sa->get_my_host(this->ike_sa);
enumerator = hydra->kernel_interface->create_address_enumerator(
- hydra->kernel_interface, FALSE, FALSE);
+ hydra->kernel_interface, ADDR_TYPE_REGULAR);
while (enumerator->enumerate(enumerator, (void**)&host))
{
if (me->ip_equals(me, host))
@@ -227,18 +227,20 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message)
/**
* build a cookie and add it to the message
*/
-static void build_cookie(private_ike_mobike_t *this, message_t *message)
+static bool build_cookie(private_ike_mobike_t *this, message_t *message)
{
rng_t *rng;
chunk_free(&this->cookie2);
rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
- if (rng)
+ if (!rng || !rng->allocate_bytes(rng, COOKIE2_SIZE, &this->cookie2))
{
- rng->allocate_bytes(rng, COOKIE2_SIZE, &this->cookie2);
- rng->destroy(rng);
- message->add_notify(message, FALSE, COOKIE2, this->cookie2);
+ DESTROY_IF(rng);
+ return FALSE;
}
+ message->add_notify(message, FALSE, COOKIE2, this->cookie2);
+ rng->destroy(rng);
+ return TRUE;
}
/**
@@ -248,15 +250,26 @@ static void update_children(private_ike_mobike_t *this)
{
enumerator_t *enumerator;
child_sa_t *child_sa;
+ linked_list_t *vips;
+ host_t *host;
+
+ vips = linked_list_create();
+
+ enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ vips->insert_last(vips, host);
+ }
+ enumerator->destroy(enumerator);
enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
if (child_sa->update(child_sa,
this->ike_sa->get_my_host(this->ike_sa),
- this->ike_sa->get_other_host(this->ike_sa),
- this->ike_sa->get_virtual_ip(this->ike_sa, TRUE),
- this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED)
+ this->ike_sa->get_other_host(this->ike_sa), vips,
+ this->ike_sa->has_condition(this->ike_sa,
+ COND_NAT_ANY)) == NOT_SUPPORTED)
{
this->ike_sa->rekey_child_sa(this->ike_sa,
child_sa->get_protocol(child_sa),
@@ -264,18 +277,24 @@ static void update_children(private_ike_mobike_t *this)
}
}
enumerator->destroy(enumerator);
+
+ vips->destroy(vips);
}
/**
* Apply the port of the old host, if its ip equals the new, use port otherwise.
*/
-static void apply_port(host_t *host, host_t *old, u_int16_t port)
+static void apply_port(host_t *host, host_t *old, u_int16_t port, bool local)
{
if (host->ip_equals(host, old))
{
port = old->get_port(old);
}
- else if (port == IKEV2_UDP_PORT)
+ else if (local && port == charon->socket->get_port(charon->socket, FALSE))
+ {
+ port = charon->socket->get_port(charon->socket, TRUE);
+ }
+ else if (!local && port == IKEV2_UDP_PORT)
{
port = IKEV2_NATT_PORT;
}
@@ -312,9 +331,9 @@ METHOD(ike_mobike_t, transmit, void,
continue;
}
/* reuse port for an active address, 4500 otherwise */
- apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg));
+ apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg), TRUE);
other = other->clone(other);
- apply_port(other, other_old, ike_cfg->get_other_port(ike_cfg));
+ apply_port(other, other_old, ike_cfg->get_other_port(ike_cfg), FALSE);
DBG1(DBG_IKE, "checking path %#H - %#H", me, other);
copy = packet->clone(packet);
copy->set_source(copy, me);
@@ -358,7 +377,10 @@ METHOD(task_t, build_i, status_t,
{
message->add_notify(message, FALSE, UPDATE_SA_ADDRESSES,
chunk_empty);
- build_cookie(this, message);
+ if (!build_cookie(this, message))
+ {
+ return FAILED;
+ }
update_children(this);
}
if (this->address && !this->check)
@@ -584,7 +606,7 @@ METHOD(ike_mobike_t, is_probing, bool,
METHOD(task_t, get_type, task_type_t,
private_ike_mobike_t *this)
{
- return IKE_MOBIKE;
+ return TASK_IKE_MOBIKE;
}
METHOD(task_t, migrate, void,
@@ -646,4 +668,3 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator)
return &this->public;
}
-
diff --git a/src/libcharon/sa/tasks/ike_mobike.h b/src/libcharon/sa/ikev2/tasks/ike_mobike.h
index 16611939e..3b447af51 100644
--- a/src/libcharon/sa/tasks/ike_mobike.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_mobike ike_mobike
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_MOBIKE_H_
@@ -25,8 +25,8 @@ typedef struct ike_mobike_t ike_mobike_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
-#include <network/packet.h>
+#include <sa/task.h>
+#include <utils/packet.h>
/**
* Task of type ike_mobike, detects and handles MOBIKE extension.
diff --git a/src/libcharon/sa/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c
index f06a518fa..0a93db9ed 100644
--- a/src/libcharon/sa/tasks/ike_natd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c
@@ -104,7 +104,10 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this,
/* natd_hash = SHA1( spi_i | spi_r | address | port ) */
natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk);
- this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash);
+ if (!this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash))
+ {
+ natd_hash = chunk_empty;
+ }
DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
@@ -121,12 +124,12 @@ static chunk_t generate_natd_hash_faked(private_ike_natd_t *this)
chunk_t chunk;
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
- if (!rng)
+ if (!rng || !rng->allocate_bytes(rng, HASH_SIZE_SHA1, &chunk))
{
DBG1(DBG_IKE, "unable to get random bytes for NATD fake");
+ DESTROY_IF(rng);
return chunk_empty;
}
- rng->allocate_bytes(rng, HASH_SIZE_SHA1, &chunk);
rng->destroy(rng);
return chunk;
}
@@ -152,7 +155,11 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this,
{
hash = generate_natd_hash(this, ike_sa_id, host);
}
- notify = notify_payload_create();
+ if (!hash.len)
+ {
+ return NULL;
+ }
+ notify = notify_payload_create(NOTIFY);
notify->set_notify_type(notify, type);
notify->set_notification_data(notify, hash);
chunk_free(&hash);
@@ -298,7 +305,10 @@ METHOD(task_t, build_i, status_t,
/* destination is always set */
host = message->get_destination(message);
notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host);
- message->add_payload(message, (payload_t*)notify);
+ if (notify)
+ {
+ message->add_payload(message, (payload_t*)notify);
+ }
/* source may be any, we have 3 possibilities to get our source address:
* 1. It is defined in the config => use the one of the IKE_SA
@@ -306,10 +316,13 @@ METHOD(task_t, build_i, status_t,
* 3. Include all possbile addresses
*/
host = message->get_source(message);
- if (!host->is_anyaddr(host))
- { /* 1. */
+ if (!host->is_anyaddr(host) || ike_cfg->force_encap(ike_cfg))
+ { /* 1. or if we force UDP encap, as it doesn't matter if it's %any */
notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
- message->add_payload(message, (payload_t*)notify);
+ if (notify)
+ {
+ message->add_payload(message, (payload_t*)notify);
+ }
}
else
{
@@ -319,13 +332,16 @@ METHOD(task_t, build_i, status_t,
{ /* 2. */
host->set_port(host, ike_cfg->get_my_port(ike_cfg));
notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
- message->add_payload(message, (payload_t*)notify);
+ if (notify)
+ {
+ message->add_payload(message, (payload_t*)notify);
+ }
host->destroy(host);
}
else
{ /* 3. */
enumerator = hydra->kernel_interface->create_address_enumerator(
- hydra->kernel_interface, FALSE, FALSE);
+ hydra->kernel_interface, ADDR_TYPE_REGULAR);
while (enumerator->enumerate(enumerator, (void**)&host))
{
/* apply port 500 to host, but work on a copy */
@@ -333,7 +349,10 @@ METHOD(task_t, build_i, status_t,
host->set_port(host, ike_cfg->get_my_port(ike_cfg));
notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
host->destroy(host);
- message->add_payload(message, (payload_t*)notify);
+ if (notify)
+ {
+ message->add_payload(message, (payload_t*)notify);
+ }
}
enumerator->destroy(enumerator);
}
@@ -365,11 +384,16 @@ METHOD(task_t, build_r, status_t,
/* initiator seems to support NAT detection, add response */
me = message->get_source(message);
notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me);
- message->add_payload(message, (payload_t*)notify);
-
+ if (notify)
+ {
+ message->add_payload(message, (payload_t*)notify);
+ }
other = message->get_destination(message);
notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
- message->add_payload(message, (payload_t*)notify);
+ if (notify)
+ {
+ message->add_payload(message, (payload_t*)notify);
+ }
}
return SUCCESS;
}
@@ -385,7 +409,7 @@ METHOD(task_t, process_r, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_natd_t *this)
{
- return IKE_NATD;
+ return TASK_IKE_NATD;
}
METHOD(task_t, migrate, void,
diff --git a/src/libcharon/sa/tasks/ike_natd.h b/src/libcharon/sa/ikev2/tasks/ike_natd.h
index 68114af42..9c571b8e6 100644
--- a/src/libcharon/sa/tasks/ike_natd.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_natd.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_natd ike_natd
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_NATD_H_
@@ -25,7 +25,7 @@ typedef struct ike_natd_t ike_natd_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange.
@@ -42,7 +42,7 @@ struct ike_natd_t {
*
* MOBIKE uses NAT payloads in DPD to detect changes in the NAT mappings.
*
- * @return TRUE if mappings have changed
+ * @return TRUE if mappings have changed
*/
bool (*has_mapping_changed)(ike_natd_t *this);
};
diff --git a/src/libcharon/sa/tasks/ike_reauth.c b/src/libcharon/sa/ikev2/tasks/ike_reauth.c
index 48002d81c..6f90339ea 100644
--- a/src/libcharon/sa/tasks/ike_reauth.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_reauth.c
@@ -16,7 +16,7 @@
#include "ike_reauth.h"
#include <daemon.h>
-#include <sa/tasks/ike_delete.h>
+#include <sa/ikev2/tasks/ike_delete.h>
typedef struct private_ike_reauth_t private_ike_reauth_t;
@@ -68,7 +68,7 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_reauth_t *this)
{
- return IKE_REAUTH;
+ return TASK_IKE_REAUTH;
}
METHOD(task_t, migrate, void,
@@ -108,4 +108,3 @@ ike_reauth_t *ike_reauth_create(ike_sa_t *ike_sa)
return &this->public;
}
-
diff --git a/src/libcharon/sa/tasks/ike_reauth.h b/src/libcharon/sa/ikev2/tasks/ike_reauth.h
index 5e97b719c..781b463a7 100644
--- a/src/libcharon/sa/tasks/ike_reauth.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_reauth.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_reauth ike_reauth
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_REAUTH_H_
@@ -25,7 +25,7 @@ typedef struct ike_reauth_t ike_reauth_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Task of type ike_reauth, reestablishes an IKE_SA.
diff --git a/src/libcharon/sa/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
index 826d6e192..c3c6cf00e 100644
--- a/src/libcharon/sa/tasks/ike_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
@@ -18,8 +18,8 @@
#include <daemon.h>
#include <encoding/payloads/notify_payload.h>
-#include <sa/tasks/ike_init.h>
-#include <sa/tasks/ike_delete.h>
+#include <sa/ikev2/tasks/ike_init.h>
+#include <sa/ikev2/tasks/ike_delete.h>
#include <processing/jobs/delete_ike_sa_job.h>
#include <processing/jobs/rekey_ike_sa_job.h>
@@ -52,7 +52,7 @@ struct private_ike_rekey_t {
bool initiator;
/**
- * the IKE_INIT task which is reused to simplify rekeying
+ * the TASK_IKE_INIT task which is reused to simplify rekeying
*/
ike_init_t *ike_init;
@@ -123,15 +123,20 @@ METHOD(task_t, process_i_delete, status_t,
METHOD(task_t, build_i, status_t,
private_ike_rekey_t *this, message_t *message)
{
+ ike_version_t version;
peer_cfg_t *peer_cfg;
host_t *other_host;
/* create new SA only on first try */
if (this->new_sa == NULL)
{
- this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
- TRUE);
-
+ version = this->ike_sa->get_version(this->ike_sa);
+ this->new_sa = charon->ike_sa_manager->checkout_new(
+ charon->ike_sa_manager, version, TRUE);
+ if (!this->new_sa)
+ { /* shouldn't happen */
+ return FAILED;
+ }
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
other_host = this->ike_sa->get_other_host(this->ike_sa);
this->new_sa->set_peer_cfg(this->new_sa, peer_cfg);
@@ -176,7 +181,11 @@ METHOD(task_t, process_r, status_t,
enumerator->destroy(enumerator);
this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
- FALSE);
+ this->ike_sa->get_version(this->ike_sa), FALSE);
+ if (!this->new_sa)
+ { /* shouldn't happen */
+ return FAILED;
+ }
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
this->new_sa->set_peer_cfg(this->new_sa, peer_cfg);
@@ -230,8 +239,8 @@ METHOD(task_t, process_i, status_t,
case FAILED:
/* rekeying failed, fallback to old SA */
if (!(this->collision && (
- this->collision->get_type(this->collision) == IKE_DELETE ||
- this->collision->get_type(this->collision) == IKE_REAUTH)))
+ this->collision->get_type(this->collision) == TASK_IKE_DELETE ||
+ this->collision->get_type(this->collision) == TASK_IKE_REAUTH)))
{
job_t *job;
u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
@@ -253,7 +262,7 @@ METHOD(task_t, process_i, status_t,
/* check for collisions */
if (this->collision &&
- this->collision->get_type(this->collision) == IKE_REKEY)
+ this->collision->get_type(this->collision) == TASK_IKE_REKEY)
{
private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision;
@@ -323,14 +332,14 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, get_type, task_type_t,
private_ike_rekey_t *this)
{
- return IKE_REKEY;
+ return TASK_IKE_REKEY;
}
METHOD(ike_rekey_t, collide, void,
private_ike_rekey_t* this, task_t *other)
{
- DBG1(DBG_IKE, "detected %N collision with %N", task_type_names, IKE_REKEY,
- task_type_names, other->get_type(other));
+ DBG1(DBG_IKE, "detected %N collision with %N", task_type_names,
+ TASK_IKE_REKEY, task_type_names, other->get_type(other));
DESTROY_IF(this->collision);
this->collision = other;
}
diff --git a/src/libcharon/sa/tasks/ike_rekey.h b/src/libcharon/sa/ikev2/tasks/ike_rekey.h
index 1c9550768..6a12e9034 100644
--- a/src/libcharon/sa/tasks/ike_rekey.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_rekey ike_rekey
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_REKEY_H_
@@ -25,10 +25,10 @@ typedef struct ike_rekey_t ike_rekey_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
- * Task of type IKE_REKEY, rekey an established IKE_SA.
+ * Task of type TASK_IKE_REKEY, rekey an established IKE_SA.
*/
struct ike_rekey_t {
@@ -50,11 +50,11 @@ struct ike_rekey_t {
};
/**
- * Create a new IKE_REKEY task.
+ * Create a new TASK_IKE_REKEY task.
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE for initiator, FALSE for responder
- * @return IKE_REKEY task to handle by the task_manager
+ * @return TASK_IKE_REKEY task to handle by the task_manager
*/
ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator);
diff --git a/src/libcharon/sa/tasks/ike_vendor.c b/src/libcharon/sa/ikev2/tasks/ike_vendor.c
index 1c14ee06b..2730f5876 100644
--- a/src/libcharon/sa/tasks/ike_vendor.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_vendor.c
@@ -53,11 +53,12 @@ METHOD(task_t, build, status_t,
private_ike_vendor_t *this, message_t *message)
{
if (lib->settings->get_bool(lib->settings,
- "charon.send_vendor_id", FALSE))
+ "%s.send_vendor_id", FALSE, charon->name))
{
vendor_id_payload_t *vid;
- vid = vendor_id_payload_create_data(chunk_clone(strongswan_vid));
+ vid = vendor_id_payload_create_data(VENDOR_ID,
+ chunk_clone(strongswan_vid));
message->add_payload(message, &vid->payload_interface);
}
@@ -83,12 +84,12 @@ METHOD(task_t, process, status_t,
if (chunk_equals(data, strongswan_vid))
{
- DBG1(DBG_IKE, "received strongSwan vendor id");
+ DBG1(DBG_IKE, "received strongSwan vendor ID");
this->ike_sa->enable_extension(this->ike_sa, EXT_STRONGSWAN);
}
else
{
- DBG1(DBG_ENC, "received unknown vendor id: %#B", &data);
+ DBG1(DBG_ENC, "received unknown vendor ID: %#B", &data);
}
}
}
@@ -106,7 +107,7 @@ METHOD(task_t, migrate, void,
METHOD(task_t, get_type, task_type_t,
private_ike_vendor_t *this)
{
- return IKE_VENDOR;
+ return TASK_IKE_VENDOR;
}
METHOD(task_t, destroy, void,
@@ -138,4 +139,3 @@ ike_vendor_t *ike_vendor_create(ike_sa_t *ike_sa, bool initiator)
return &this->public;
}
-
diff --git a/src/libcharon/sa/tasks/ike_vendor.h b/src/libcharon/sa/ikev2/tasks/ike_vendor.h
index 6c353c447..86c711636 100644
--- a/src/libcharon/sa/tasks/ike_vendor.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_vendor.h
@@ -15,7 +15,7 @@
/**
* @defgroup ike_vendor ike_vendor
- * @{ @ingroup tasks
+ * @{ @ingroup tasks_v2
*/
#ifndef IKE_VENDOR_H_
@@ -25,7 +25,7 @@ typedef struct ike_vendor_t ike_vendor_t;
#include <library.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* Vendor ID processing task.
diff --git a/src/libcharon/sa/keymat.c b/src/libcharon/sa/keymat.c
index d762fa34e..26c305f77 100644
--- a/src/libcharon/sa/keymat.c
+++ b/src/libcharon/sa/keymat.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Martin Willi
+ * Copyright (C) 2011 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -15,621 +15,112 @@
#include "keymat.h"
-#include <daemon.h>
-#include <crypto/prf_plus.h>
+#include <sa/ikev1/keymat_v1.h>
+#include <sa/ikev2/keymat_v2.h>
-typedef struct private_keymat_t private_keymat_t;
+static keymat_constructor_t keymat_v1_ctor = NULL, keymat_v2_ctor = NULL;
/**
- * Private data of an keymat_t object.
+ * See header
*/
-struct private_keymat_t {
-
- /**
- * Public keymat_t interface.
- */
- keymat_t public;
-
- /**
- * IKE_SA Role, initiator or responder
- */
- bool initiator;
-
- /**
- * inbound AEAD
- */
- aead_t *aead_in;
-
- /**
- * outbound AEAD
- */
- aead_t *aead_out;
-
- /**
- * General purpose PRF
- */
- prf_t *prf;
-
- /**
- * Negotiated PRF algorithm
- */
- pseudo_random_function_t prf_alg;
-
- /**
- * Key to derive key material from for CHILD_SAs, rekeying
- */
- chunk_t skd;
-
- /**
- * Key to build outging authentication data (SKp)
- */
- chunk_t skp_build;
-
- /**
- * Key to verify incoming authentication data (SKp)
- */
- chunk_t skp_verify;
-};
+keymat_t *keymat_create(ike_version_t version, bool initiator)
+{
+ keymat_t *keymat = NULL;
-typedef struct keylen_entry_t keylen_entry_t;
+ switch (version)
+ {
+ case IKEV1:
+#ifdef USE_IKEV1
+ keymat = keymat_v1_ctor ? keymat_v1_ctor(initiator)
+ : &keymat_v1_create(initiator)->keymat;
+#endif
+ break;
+ case IKEV2:
+#ifdef USE_IKEV2
+ keymat = keymat_v2_ctor ? keymat_v2_ctor(initiator)
+ : &keymat_v2_create(initiator)->keymat;
+#endif
+ break;
+ default:
+ break;
+ }
+ return keymat;
+}
/**
* Implicit key length for an algorithm
*/
-struct keylen_entry_t {
+typedef struct {
/** IKEv2 algorithm identifier */
- int algo;
+ int alg;
/** key length in bits */
int len;
-};
-
-#define END_OF_LIST -1
-
-/**
- * Keylen for encryption algos
- */
-keylen_entry_t keylen_enc[] = {
- {ENCR_DES, 64},
- {ENCR_3DES, 192},
- {END_OF_LIST, 0}
-};
+} keylen_entry_t;
/**
- * Keylen for integrity algos
+ * See header.
*/
-keylen_entry_t keylen_int[] = {
- {AUTH_HMAC_MD5_96, 128},
- {AUTH_HMAC_MD5_128, 128},
- {AUTH_HMAC_SHA1_96, 160},
- {AUTH_HMAC_SHA1_160, 160},
- {AUTH_HMAC_SHA2_256_96, 256},
- {AUTH_HMAC_SHA2_256_128, 256},
- {AUTH_HMAC_SHA2_384_192, 384},
- {AUTH_HMAC_SHA2_512_256, 512},
- {AUTH_AES_XCBC_96, 128},
- {END_OF_LIST, 0}
-};
-
-/**
- * Lookup key length of an algorithm
- */
-static int lookup_keylen(keylen_entry_t *list, int algo)
+int keymat_get_keylen_encr(encryption_algorithm_t alg)
{
- while (list->algo != END_OF_LIST)
+ keylen_entry_t map[] = {
+ {ENCR_DES, 64},
+ {ENCR_3DES, 192},
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
{
- if (algo == list->algo)
+ if (map[i].alg == alg)
{
- return list->len;
+ return map[i].len;
}
- list++;
}
return 0;
}
-METHOD(keymat_t, create_dh, diffie_hellman_t*,
- private_keymat_t *this, diffie_hellman_group_t group)
-{
- return lib->crypto->create_dh(lib->crypto, group);;
-}
-
/**
- * Derive IKE keys for a combined AEAD algorithm
+ * See header.
*/
-static bool derive_ike_aead(private_keymat_t *this, u_int16_t alg,
- u_int16_t key_size, prf_plus_t *prf_plus)
+int keymat_get_keylen_integ(integrity_algorithm_t alg)
{
- aead_t *aead_i, *aead_r;
- chunk_t key;
-
- /* SK_ei/SK_er used for encryption */
- aead_i = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
- aead_r = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
- if (aead_i == NULL || aead_r == NULL)
- {
- DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
- transform_type_names, ENCRYPTION_ALGORITHM,
- encryption_algorithm_names, alg, key_size);
- return FALSE;
- }
- key_size = aead_i->get_key_size(aead_i);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ei secret %B", &key);
- aead_i->set_key(aead_i, key);
- chunk_clear(&key);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_er secret %B", &key);
- aead_r->set_key(aead_r, key);
- chunk_clear(&key);
-
- if (this->initiator)
- {
- this->aead_in = aead_r;
- this->aead_out = aead_i;
- }
- else
- {
- this->aead_in = aead_i;
- this->aead_out = aead_r;
+ keylen_entry_t map[] = {
+ {AUTH_HMAC_MD5_96, 128},
+ {AUTH_HMAC_MD5_128, 128},
+ {AUTH_HMAC_SHA1_96, 160},
+ {AUTH_HMAC_SHA1_160, 160},
+ {AUTH_HMAC_SHA2_256_96, 256},
+ {AUTH_HMAC_SHA2_256_128, 256},
+ {AUTH_HMAC_SHA2_384_192, 384},
+ {AUTH_HMAC_SHA2_512_256, 512},
+ {AUTH_AES_XCBC_96, 128},
+ };
+ int i;
+
+ for (i = 0; i < countof(map); i++)
+ {
+ if (map[i].alg == alg)
+ {
+ return map[i].len;
+ }
}
- return TRUE;
+ return 0;
}
/**
- * Derive IKE keys for traditional encryption and MAC algorithms
+ * See header.
*/
-static bool derive_ike_traditional(private_keymat_t *this, u_int16_t enc_alg,
- u_int16_t enc_size, u_int16_t int_alg, prf_plus_t *prf_plus)
+void keymat_register_constructor(ike_version_t version,
+ keymat_constructor_t create)
{
- crypter_t *crypter_i, *crypter_r;
- signer_t *signer_i, *signer_r;
- size_t key_size;
- chunk_t key;
-
- /* SK_ai/SK_ar used for integrity protection */
- signer_i = lib->crypto->create_signer(lib->crypto, int_alg);
- signer_r = lib->crypto->create_signer(lib->crypto, int_alg);
- if (signer_i == NULL || signer_r == NULL)
+ switch (version)
{
- DBG1(DBG_IKE, "%N %N not supported!",
- transform_type_names, INTEGRITY_ALGORITHM,
- integrity_algorithm_names, int_alg);
- return FALSE;
- }
- key_size = signer_i->get_key_size(signer_i);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ai secret %B", &key);
- signer_i->set_key(signer_i, key);
- chunk_clear(&key);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ar secret %B", &key);
- signer_r->set_key(signer_r, key);
- chunk_clear(&key);
-
- /* SK_ei/SK_er used for encryption */
- crypter_i = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
- crypter_r = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
- if (crypter_i == NULL || crypter_r == NULL)
- {
- DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
- transform_type_names, ENCRYPTION_ALGORITHM,
- encryption_algorithm_names, enc_alg, enc_size);
- signer_i->destroy(signer_i);
- signer_r->destroy(signer_r);
- return FALSE;
- }
- key_size = crypter_i->get_key_size(crypter_i);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_ei secret %B", &key);
- crypter_i->set_key(crypter_i, key);
- chunk_clear(&key);
-
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_er secret %B", &key);
- crypter_r->set_key(crypter_r, key);
- chunk_clear(&key);
-
- if (this->initiator)
- {
- this->aead_in = aead_create(crypter_r, signer_r);
- this->aead_out = aead_create(crypter_i, signer_i);
- }
- else
- {
- this->aead_in = aead_create(crypter_i, signer_i);
- this->aead_out = aead_create(crypter_r, signer_r);
- }
- return TRUE;
-}
-
-METHOD(keymat_t, derive_ike_keys, bool,
- private_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh,
- chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
- pseudo_random_function_t rekey_function, chunk_t rekey_skd)
-{
- chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed;
- chunk_t spi_i, spi_r;
- prf_plus_t *prf_plus;
- u_int16_t alg, key_size, int_alg;
- prf_t *rekey_prf = NULL;
-
- spi_i = chunk_alloca(sizeof(u_int64_t));
- spi_r = chunk_alloca(sizeof(u_int64_t));
-
- if (dh->get_shared_secret(dh, &secret) != SUCCESS)
- {
- return FALSE;
- }
-
- /* Create SAs general purpose PRF first, we may use it here */
- if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
- {
- DBG1(DBG_IKE, "no %N selected",
- transform_type_names, PSEUDO_RANDOM_FUNCTION);
- return FALSE;
- }
- this->prf_alg = alg;
- this->prf = lib->crypto->create_prf(lib->crypto, alg);
- if (this->prf == NULL)
- {
- DBG1(DBG_IKE, "%N %N not supported!",
- transform_type_names, PSEUDO_RANDOM_FUNCTION,
- pseudo_random_function_names, alg);
- return FALSE;
- }
- DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
- /* full nonce is used as seed for PRF+ ... */
- full_nonce = chunk_cat("cc", nonce_i, nonce_r);
- /* but the PRF may need a fixed key which only uses the first bytes of
- * the nonces. */
- switch (alg)
- {
- case PRF_AES128_XCBC:
- /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does
- * not and therefore fixed key semantics apply to XCBC for key
- * derivation. */
- case PRF_CAMELLIA128_XCBC:
- /* draft-kanno-ipsecme-camellia-xcbc refers to rfc 4434, we
- * assume fixed key length. */
- key_size = this->prf->get_key_size(this->prf)/2;
- nonce_i.len = min(nonce_i.len, key_size);
- nonce_r.len = min(nonce_r.len, key_size);
+ case IKEV1:
+ keymat_v1_ctor = create;
+ break;
+ case IKEV2:
+ keymat_v2_ctor = create;
break;
default:
- /* all other algorithms use variable key length, full nonce */
break;
}
- fixed_nonce = chunk_cat("cc", nonce_i, nonce_r);
- *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id);
- *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id);
- prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r);
-
- /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr)
- *
- * if we are rekeying, SKEYSEED is built on another way
- */
- if (rekey_function == PRF_UNDEFINED) /* not rekeying */
- {
- /* SKEYSEED = prf(Ni | Nr, g^ir) */
- this->prf->set_key(this->prf, fixed_nonce);
- this->prf->allocate_bytes(this->prf, secret, &skeyseed);
- this->prf->set_key(this->prf, skeyseed);
- prf_plus = prf_plus_create(this->prf, prf_plus_seed);
- }
- else
- {
- /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
- * use OLD SAs PRF functions for both prf_plus and prf */
- rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function);
- if (!rekey_prf)
- {
- DBG1(DBG_IKE, "PRF of old SA %N not supported!",
- pseudo_random_function_names, rekey_function);
- chunk_free(&full_nonce);
- chunk_free(&fixed_nonce);
- chunk_clear(&prf_plus_seed);
- return FALSE;
- }
- secret = chunk_cat("mc", secret, full_nonce);
- rekey_prf->set_key(rekey_prf, rekey_skd);
- rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed);
- rekey_prf->set_key(rekey_prf, skeyseed);
- prf_plus = prf_plus_create(rekey_prf, prf_plus_seed);
- }
- DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
-
- chunk_clear(&skeyseed);
- chunk_clear(&secret);
- chunk_free(&full_nonce);
- chunk_free(&fixed_nonce);
- chunk_clear(&prf_plus_seed);
-
- /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */
-
- /* SK_d is used for generating CHILD_SA key mat => store for later use */
- key_size = this->prf->get_key_size(this->prf);
- prf_plus->allocate_bytes(prf_plus, key_size, &this->skd);
- DBG4(DBG_IKE, "Sk_d secret %B", &this->skd);
-
- if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size))
- {
- DBG1(DBG_IKE, "no %N selected",
- transform_type_names, ENCRYPTION_ALGORITHM);
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
-
- if (encryption_algorithm_is_aead(alg))
- {
- if (!derive_ike_aead(this, alg, key_size, prf_plus))
- {
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
- }
- else
- {
- if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
- &int_alg, NULL))
- {
- DBG1(DBG_IKE, "no %N selected",
- transform_type_names, INTEGRITY_ALGORITHM);
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
- if (!derive_ike_traditional(this, alg, key_size, int_alg, prf_plus))
- {
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
- return FALSE;
- }
- }
-
- /* SK_pi/SK_pr used for authentication => stored for later */
- key_size = this->prf->get_key_size(this->prf);
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_pi secret %B", &key);
- if (this->initiator)
- {
- this->skp_build = key;
- }
- else
- {
- this->skp_verify = key;
- }
- prf_plus->allocate_bytes(prf_plus, key_size, &key);
- DBG4(DBG_IKE, "Sk_pr secret %B", &key);
- if (this->initiator)
- {
- this->skp_verify = key;
- }
- else
- {
- this->skp_build = key;
- }
-
- /* all done, prf_plus not needed anymore */
- prf_plus->destroy(prf_plus);
- DESTROY_IF(rekey_prf);
-
- return TRUE;
-}
-
-METHOD(keymat_t, derive_child_keys, bool,
- private_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh,
- chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i,
- chunk_t *encr_r, chunk_t *integ_r)
-{
- u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
- chunk_t seed, secret = chunk_empty;
- prf_plus_t *prf_plus;
-
- if (dh)
- {
- if (dh->get_shared_secret(dh, &secret) != SUCCESS)
- {
- return FALSE;
- }
- DBG4(DBG_CHD, "DH secret %B", &secret);
- }
- seed = chunk_cata("mcc", secret, nonce_i, nonce_r);
- DBG4(DBG_CHD, "seed %B", &seed);
-
- if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
- &enc_alg, &enc_size))
- {
- DBG2(DBG_CHD, " using %N for encryption",
- encryption_algorithm_names, enc_alg);
-
- if (!enc_size)
- {
- enc_size = lookup_keylen(keylen_enc, enc_alg);
- }
- if (enc_alg != ENCR_NULL && !enc_size)
- {
- DBG1(DBG_CHD, "no keylength defined for %N",
- encryption_algorithm_names, enc_alg);
- return FALSE;
- }
- /* to bytes */
- enc_size /= 8;
-
- /* CCM/GCM/CTR/GMAC needs additional bytes */
- switch (enc_alg)
- {
- case ENCR_AES_CCM_ICV8:
- case ENCR_AES_CCM_ICV12:
- case ENCR_AES_CCM_ICV16:
- case ENCR_CAMELLIA_CCM_ICV8:
- case ENCR_CAMELLIA_CCM_ICV12:
- case ENCR_CAMELLIA_CCM_ICV16:
- enc_size += 3;
- break;
- case ENCR_AES_GCM_ICV8:
- case ENCR_AES_GCM_ICV12:
- case ENCR_AES_GCM_ICV16:
- case ENCR_AES_CTR:
- case ENCR_NULL_AUTH_AES_GMAC:
- enc_size += 4;
- break;
- default:
- break;
- }
- }
-
- if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
- &int_alg, &int_size))
- {
- DBG2(DBG_CHD, " using %N for integrity",
- integrity_algorithm_names, int_alg);
-
- if (!int_size)
- {
- int_size = lookup_keylen(keylen_int, int_alg);
- }
- if (!int_size)
- {
- DBG1(DBG_CHD, "no keylength defined for %N",
- integrity_algorithm_names, int_alg);
- return FALSE;
- }
- /* to bytes */
- int_size /= 8;
- }
-
- this->prf->set_key(this->prf, this->skd);
- prf_plus = prf_plus_create(this->prf, seed);
-
- prf_plus->allocate_bytes(prf_plus, enc_size, encr_i);
- prf_plus->allocate_bytes(prf_plus, int_size, integ_i);
- prf_plus->allocate_bytes(prf_plus, enc_size, encr_r);
- prf_plus->allocate_bytes(prf_plus, int_size, integ_r);
-
- prf_plus->destroy(prf_plus);
-
- if (enc_size)
- {
- DBG4(DBG_CHD, "encryption initiator key %B", encr_i);
- DBG4(DBG_CHD, "encryption responder key %B", encr_r);
- }
- if (int_size)
- {
- DBG4(DBG_CHD, "integrity initiator key %B", integ_i);
- DBG4(DBG_CHD, "integrity responder key %B", integ_r);
- }
- return TRUE;
-}
-
-METHOD(keymat_t, get_skd, pseudo_random_function_t,
- private_keymat_t *this, chunk_t *skd)
-{
- *skd = this->skd;
- return this->prf_alg;
-}
-
-METHOD(keymat_t, get_aead, aead_t*,
- private_keymat_t *this, bool in)
-{
- return in ? this->aead_in : this->aead_out;
-}
-
-METHOD(keymat_t, get_auth_octets, chunk_t,
- private_keymat_t *this, bool verify, chunk_t ike_sa_init,
- chunk_t nonce, identification_t *id, char reserved[3])
-{
- chunk_t chunk, idx, octets;
- chunk_t skp;
-
- skp = verify ? this->skp_verify : this->skp_build;
-
- chunk = chunk_alloca(4);
- chunk.ptr[0] = id->get_type(id);
- memcpy(chunk.ptr + 1, reserved, 3);
- idx = chunk_cata("cc", chunk, id->get_encoding(id));
-
- DBG3(DBG_IKE, "IDx' %B", &idx);
- DBG3(DBG_IKE, "SK_p %B", &skp);
- this->prf->set_key(this->prf, skp);
- this->prf->allocate_bytes(this->prf, idx, &chunk);
-
- octets = chunk_cat("ccm", ike_sa_init, nonce, chunk);
- DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets);
- return octets;
}
-
-/**
- * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
- */
-#define IKEV2_KEY_PAD "Key Pad for IKEv2"
-#define IKEV2_KEY_PAD_LENGTH 17
-
-METHOD(keymat_t, get_psk_sig, chunk_t,
- private_keymat_t *this, bool verify, chunk_t ike_sa_init,
- chunk_t nonce, chunk_t secret, identification_t *id, char reserved[3])
-{
- chunk_t key_pad, key, sig, octets;
-
- if (!secret.len)
- { /* EAP uses SK_p if no MSK has been established */
- secret = verify ? this->skp_verify : this->skp_build;
- }
- octets = get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved);
- /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
- key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH);
- this->prf->set_key(this->prf, secret);
- this->prf->allocate_bytes(this->prf, key_pad, &key);
- this->prf->set_key(this->prf, key);
- this->prf->allocate_bytes(this->prf, octets, &sig);
- DBG4(DBG_IKE, "secret %B", &secret);
- DBG4(DBG_IKE, "prf(secret, keypad) %B", &key);
- DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &sig);
- chunk_free(&octets);
- chunk_free(&key);
-
- return sig;
-}
-
-METHOD(keymat_t, destroy, void,
- private_keymat_t *this)
-{
- DESTROY_IF(this->aead_in);
- DESTROY_IF(this->aead_out);
- DESTROY_IF(this->prf);
- chunk_clear(&this->skd);
- chunk_clear(&this->skp_verify);
- chunk_clear(&this->skp_build);
- free(this);
-}
-
-/**
- * See header
- */
-keymat_t *keymat_create(bool initiator)
-{
- private_keymat_t *this;
-
- INIT(this,
- .public = {
- .create_dh = _create_dh,
- .derive_ike_keys = _derive_ike_keys,
- .derive_child_keys = _derive_child_keys,
- .get_skd = _get_skd,
- .get_aead = _get_aead,
- .get_auth_octets = _get_auth_octets,
- .get_psk_sig = _get_psk_sig,
- .destroy = _destroy,
- },
- .initiator = initiator,
- .prf_alg = PRF_UNDEFINED,
- );
-
- return &this->public;
-}
-
diff --git a/src/libcharon/sa/keymat.h b/src/libcharon/sa/keymat.h
index 6c2b5d4b5..02db5ca58 100644
--- a/src/libcharon/sa/keymat.h
+++ b/src/libcharon/sa/keymat.h
@@ -21,14 +21,23 @@
#ifndef KEYMAT_H_
#define KEYMAT_H_
+typedef struct keymat_t keymat_t;
+
#include <library.h>
#include <utils/identification.h>
#include <crypto/prfs/prf.h>
#include <crypto/aead.h>
#include <config/proposal.h>
+#include <config/peer_cfg.h>
#include <sa/ike_sa_id.h>
-typedef struct keymat_t keymat_t;
+/**
+ * Constructor function for custom keymat implementations
+ *
+ * @param initiator TRUE if the keymat is used as initiator
+ * @return keymat_t implementation
+ */
+typedef keymat_t* (*keymat_constructor_t)(bool initiator);
/**
* Derivation an management of sensitive keying material.
@@ -36,6 +45,13 @@ typedef struct keymat_t keymat_t;
struct keymat_t {
/**
+ * Get IKE version of this keymat.
+ *
+ * @return IKEV1 for keymat_v1_t, IKEV2 for keymat_v2_t
+ */
+ ike_version_t (*get_version)(keymat_t *this);
+
+ /**
* Create a diffie hellman object for key agreement.
*
* The diffie hellman is either for IKE negotiation/rekeying or
@@ -50,58 +66,18 @@ struct keymat_t {
* @param group diffie hellman group
* @return DH object, NULL if group not supported
*/
- diffie_hellman_t* (*create_dh)(keymat_t *this, diffie_hellman_group_t group);
+ diffie_hellman_t* (*create_dh)(keymat_t *this,
+ diffie_hellman_group_t group);
/**
- * Derive keys for the IKE_SA.
+ * Create a nonce generator object.
*
- * These keys are not handed out, but are used by the associated signers,
- * crypters and authentication functions.
+ * The nonce generator can be used to create nonces needed during IKE/CHILD
+ * SA establishment or rekeying.
*
- * @param proposal selected algorithms
- * @param dh diffie hellman key allocated by create_dh()
- * @param nonce_i initiators nonce value
- * @param nonce_r responders nonce value
- * @param id IKE_SA identifier
- * @param rekey_prf PRF of old SA if rekeying, PRF_UNDEFINED otherwise
- * @param rekey_sdk SKd of old SA if rekeying
- * @return TRUE on success
+ * @return nonce generator object
*/
- bool (*derive_ike_keys)(keymat_t *this, proposal_t *proposal,
- diffie_hellman_t *dh, chunk_t nonce_i,
- chunk_t nonce_r, ike_sa_id_t *id,
- pseudo_random_function_t rekey_function,
- chunk_t rekey_skd);
- /**
- * Derive keys for a CHILD_SA.
- *
- * The keys for the CHILD_SA are allocated in the integ and encr chunks.
- * An implementation might hand out encrypted keys only, which are
- * decrypted in the kernel before use.
- * If no PFS is used for the CHILD_SA, dh can be NULL.
- *
- * @param proposal selected algorithms
- * @param dh diffie hellman key allocated by create_dh(), or NULL
- * @param nonce_i initiators nonce value
- * @param nonce_r responders nonce value
- * @param encr_i chunk to write initiators encryption key to
- * @param integ_i chunk to write initiators integrity key to
- * @param encr_r chunk to write responders encryption key to
- * @param integ_r chunk to write responders integrity key to
- * @return TRUE on success
- */
- bool (*derive_child_keys)(keymat_t *this,
- proposal_t *proposal, diffie_hellman_t *dh,
- chunk_t nonce_i, chunk_t nonce_r,
- chunk_t *encr_i, chunk_t *integ_i,
- chunk_t *encr_r, chunk_t *integ_r);
- /**
- * Get SKd to pass to derive_ikey_keys() during rekeying.
- *
- * @param skd chunk to write SKd to (internal data)
- * @return PRF function to derive keymat
- */
- pseudo_random_function_t (*get_skd)(keymat_t *this, chunk_t *skd);
+ nonce_gen_t* (*create_nonce_gen)(keymat_t *this);
/*
* Get a AEAD transform to en-/decrypt and sign/verify IKE messages.
@@ -112,52 +88,43 @@ struct keymat_t {
aead_t* (*get_aead)(keymat_t *this, bool in);
/**
- * Generate octets to use for authentication procedure (RFC4306 2.15).
- *
- * This method creates the plain octets and is usually signed by a private
- * key. PSK and EAP authentication include a secret into the data, use
- * the get_psk_sig() method instead.
- *
- * @param verify TRUE to create for verfification, FALSE to sign
- * @param ike_sa_init encoded ike_sa_init message
- * @param nonce nonce value
- * @param id identity
- * @param reserved reserved bytes of id_payload
- * @return authentication octets
- */
- chunk_t (*get_auth_octets)(keymat_t *this, bool verify, chunk_t ike_sa_init,
- chunk_t nonce, identification_t *id,
- char reserved[3]);
- /**
- * Build the shared secret signature used for PSK and EAP authentication.
- *
- * This method wraps the get_auth_octets() method and additionally
- * includes the secret into the signature. If no secret is given, SK_p is
- * used as secret (used for EAP methods without MSK).
- *
- * @param verify TRUE to create for verfification, FALSE to sign
- * @param ike_sa_init encoded ike_sa_init message
- * @param nonce nonce value
- * @param secret optional secret to include into signature
- * @param id identity
- * @param reserved reserved bytes of id_payload
- * @return signature octets
- */
- chunk_t (*get_psk_sig)(keymat_t *this, bool verify, chunk_t ike_sa_init,
- chunk_t nonce, chunk_t secret,
- identification_t *id, char reserved[3]);
- /**
* Destroy a keymat_t.
*/
void (*destroy)(keymat_t *this);
};
/**
- * Create a keymat instance.
+ * Create the appropriate keymat_t implementation based on the IKE version.
+ *
+ * @param version requested IKE version
+ * @param initiator TRUE if we are initiator
+ * @return keymat_t implmenetation
+ */
+keymat_t *keymat_create(ike_version_t version, bool initiator);
+
+/**
+ * Look up the key length of an encryption algorithm.
+ *
+ * @param alg algorithm to get key length for
+ * @return key length in bits
+ */
+int keymat_get_keylen_encr(encryption_algorithm_t alg);
+
+/**
+ * Look up the key length of an integrity algorithm.
+ *
+ * @param alg algorithm to get key length for
+ * @return key length in bits
+ */
+int keymat_get_keylen_integ(integrity_algorithm_t alg);
+
+/**
+ * Register keymat_t constructor for given IKE version.
*
- * @param initiator TRUE if we are the initiator
- * @return keymat instance
+ * @param version IKE version of given keymat constructor
+ * @param create keymat constructor function, NULL to unregister
*/
-keymat_t *keymat_create(bool initiator);
+void keymat_register_constructor(ike_version_t version,
+ keymat_constructor_t create);
#endif /** KEYMAT_H_ @}*/
diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c
index 52b2ecd62..5af43fb91 100644
--- a/src/libcharon/sa/shunt_manager.c
+++ b/src/libcharon/sa/shunt_manager.c
@@ -206,6 +206,7 @@ METHOD(shunt_manager_t, uninstall, bool,
return FALSE;
}
uninstall_shunt_policy(child);
+ child->destroy(child);
return TRUE;
}
diff --git a/src/libcharon/sa/tasks/task.c b/src/libcharon/sa/task.c
index 0d7383141..4336b23ff 100644
--- a/src/libcharon/sa/tasks/task.c
+++ b/src/libcharon/sa/task.c
@@ -16,12 +16,11 @@
#include "task.h"
-#ifdef ME
-ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
+ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST,
"IKE_INIT",
"IKE_NATD",
"IKE_MOBIKE",
- "IKE_AUTHENTICATE",
+ "IKE_AUTH",
"IKE_AUTH_LIFETIME",
"IKE_CERT_PRE",
"IKE_CERT_POST",
@@ -31,28 +30,23 @@ ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
"IKE_DELETE",
"IKE_DPD",
"IKE_VENDOR",
+#ifdef ME
"IKE_ME",
+#endif /* ME */
"CHILD_CREATE",
"CHILD_DELETE",
"CHILD_REKEY",
+ "MAIN_MODE",
+ "AGGRESSIVE_MODE",
+ "INFORMATIONAL",
+ "ISAKMP_DELETE",
+ "XAUTH",
+ "MODE_CONFIG",
+ "QUICK_MODE",
+ "QUICK_DELETE",
+ "ISAKMP_VENDOR",
+ "ISAKMP_NATD",
+ "ISAKMP_DPD",
+ "ISAKMP_CERT_PRE",
+ "ISAKMP_CERT_POST",
);
-#else
-ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
- "IKE_INIT",
- "IKE_NATD",
- "IKE_MOBIKE",
- "IKE_AUTHENTICATE",
- "IKE_AUTH_LIFETIME",
- "IKE_CERT_PRE",
- "IKE_CERT_POST",
- "IKE_CONFIG",
- "IKE_REKEY",
- "IKE_REAUTH",
- "IKE_DELETE",
- "IKE_DPD",
- "IKE_VENDOR",
- "CHILD_CREATE",
- "CHILD_DELETE",
- "CHILD_REKEY",
-);
-#endif /* ME */
diff --git a/src/libcharon/sa/tasks/task.h b/src/libcharon/sa/task.h
index d57085954..c37221a77 100644
--- a/src/libcharon/sa/tasks/task.h
+++ b/src/libcharon/sa/task.h
@@ -16,7 +16,7 @@
/**
* @defgroup task task
- * @{ @ingroup tasks
+ * @{ @ingroup sa
*/
#ifndef TASK_H_
@@ -34,41 +34,67 @@ typedef struct task_t task_t;
*/
enum task_type_t {
/** establish an unauthenticated IKE_SA */
- IKE_INIT,
+ TASK_IKE_INIT,
/** detect NAT situation */
- IKE_NATD,
+ TASK_IKE_NATD,
/** handle MOBIKE stuff */
- IKE_MOBIKE,
+ TASK_IKE_MOBIKE,
/** authenticate the initiated IKE_SA */
- IKE_AUTHENTICATE,
+ TASK_IKE_AUTH,
/** AUTH_LIFETIME negotiation, RFC4478 */
- IKE_AUTH_LIFETIME,
+ TASK_IKE_AUTH_LIFETIME,
/** certificate processing before authentication (certreqs, cert parsing) */
- IKE_CERT_PRE,
+ TASK_IKE_CERT_PRE,
/** certificate processing after authentication (certs payload generation) */
- IKE_CERT_POST,
+ TASK_IKE_CERT_POST,
/** Configuration payloads, virtual IP and such */
- IKE_CONFIG,
+ TASK_IKE_CONFIG,
/** rekey an IKE_SA */
- IKE_REKEY,
+ TASK_IKE_REKEY,
/** reestablish a complete IKE_SA */
- IKE_REAUTH,
+ TASK_IKE_REAUTH,
/** delete an IKE_SA */
- IKE_DELETE,
+ TASK_IKE_DELETE,
/** liveness check */
- IKE_DPD,
+ TASK_IKE_DPD,
/** Vendor ID processing */
- IKE_VENDOR,
+ TASK_IKE_VENDOR,
#ifdef ME
/** handle ME stuff */
- IKE_ME,
+ TASK_IKE_ME,
#endif /* ME */
/** establish a CHILD_SA within an IKE_SA */
- CHILD_CREATE,
+ TASK_CHILD_CREATE,
/** delete an established CHILD_SA */
- CHILD_DELETE,
+ TASK_CHILD_DELETE,
/** rekey an CHILD_SA */
- CHILD_REKEY,
+ TASK_CHILD_REKEY,
+ /** IKEv1 main mode */
+ TASK_MAIN_MODE,
+ /** IKEv1 aggressive mode */
+ TASK_AGGRESSIVE_MODE,
+ /** IKEv1 informational exchange */
+ TASK_INFORMATIONAL,
+ /** IKEv1 delete using an informational */
+ TASK_ISAKMP_DELETE,
+ /** IKEv1 XAUTH authentication */
+ TASK_XAUTH,
+ /** IKEv1 Mode Config */
+ TASK_MODE_CONFIG,
+ /** IKEv1 quick mode */
+ TASK_QUICK_MODE,
+ /** IKEv1 delete of a quick mode SA */
+ TASK_QUICK_DELETE,
+ /** IKEv1 vendor ID payload handling */
+ TASK_ISAKMP_VENDOR,
+ /** IKEv1 NAT detection */
+ TASK_ISAKMP_NATD,
+ /** IKEv1 DPD */
+ TASK_ISAKMP_DPD,
+ /** IKEv1 pre-authentication certificate handling */
+ TASK_ISAKMP_CERT_PRE,
+ /** IKEv1 post-authentication certificate handling */
+ TASK_ISAKMP_CERT_POST,
};
/**
@@ -105,6 +131,7 @@ struct task_t {
* - FAILED if a critical error occurred
* - DESTROY_ME if IKE_SA has been properly deleted
* - NEED_MORE if another call to build/process needed
+ * - ALREADY_DONE to cancel task processing
* - SUCCESS if task completed
*/
status_t (*build) (task_t *this, message_t *message);
@@ -114,9 +141,10 @@ struct task_t {
*
* @param message message to read payloads from
* @return
- * - FAILED if a critical error occurred
+ * - FAILED if a critical error occurred
* - DESTROY_ME if IKE_SA has been properly deleted
* - NEED_MORE if another call to build/process needed
+ * - ALREADY_DONE to cancel task processing
* - SUCCESS if task completed
*/
status_t (*process) (task_t *this, message_t *message);
diff --git a/src/libcharon/sa/task_manager.c b/src/libcharon/sa/task_manager.c
index 022a5e3d6..c42008ba9 100644
--- a/src/libcharon/sa/task_manager.c
+++ b/src/libcharon/sa/task_manager.c
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2007 Tobias Brunner
- * Copyright (C) 2007-2010 Martin Willi
+ * Copyright (C) 2011 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -16,1135 +15,29 @@
#include "task_manager.h"
-#include <math.h>
-
-#include <daemon.h>
-#include <sa/tasks/ike_init.h>
-#include <sa/tasks/ike_natd.h>
-#include <sa/tasks/ike_mobike.h>
-#include <sa/tasks/ike_auth.h>
-#include <sa/tasks/ike_auth_lifetime.h>
-#include <sa/tasks/ike_cert_pre.h>
-#include <sa/tasks/ike_cert_post.h>
-#include <sa/tasks/ike_rekey.h>
-#include <sa/tasks/ike_delete.h>
-#include <sa/tasks/ike_config.h>
-#include <sa/tasks/ike_dpd.h>
-#include <sa/tasks/ike_vendor.h>
-#include <sa/tasks/child_create.h>
-#include <sa/tasks/child_rekey.h>
-#include <sa/tasks/child_delete.h>
-#include <encoding/payloads/delete_payload.h>
-#include <processing/jobs/retransmit_job.h>
-
-#ifdef ME
-#include <sa/tasks/ike_me.h>
-#endif
-
-typedef struct exchange_t exchange_t;
+#include <sa/ikev1/task_manager_v1.h>
+#include <sa/ikev2/task_manager_v2.h>
/**
- * An exchange in the air, used do detect and handle retransmission
+ * See header
*/
-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_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 (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) == 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)
+task_manager_t *task_manager_create(ike_sa_t *ike_sa)
{
- 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, IKE_VENDOR);
- if (activate_task(this, IKE_INIT))
- {
- this->initiating.mid = 0;
- exchange = IKE_SA_INIT;
- activate_task(this, IKE_NATD);
- activate_task(this, IKE_CERT_PRE);
-#ifdef ME
- /* this task has to be activated before the IKE_AUTHENTICATE
- * task, because that task pregenerates the packet after
- * which no payloads can be added to the message anymore.
- */
- activate_task(this, IKE_ME);
-#endif /* ME */
- activate_task(this, IKE_AUTHENTICATE);
- activate_task(this, IKE_CERT_POST);
- activate_task(this, IKE_CONFIG);
- activate_task(this, CHILD_CREATE);
- activate_task(this, IKE_AUTH_LIFETIME);
- activate_task(this, IKE_MOBIKE);
- }
- break;
- case IKE_ESTABLISHED:
- if (activate_task(this, CHILD_CREATE))
- {
- exchange = CREATE_CHILD_SA;
- break;
- }
- if (activate_task(this, CHILD_DELETE))
- {
- exchange = INFORMATIONAL;
- break;
- }
- if (activate_task(this, CHILD_REKEY))
- {
- exchange = CREATE_CHILD_SA;
- break;
- }
- if (activate_task(this, IKE_DELETE))
- {
- exchange = INFORMATIONAL;
- break;
- }
- if (activate_task(this, IKE_REKEY))
- {
- exchange = CREATE_CHILD_SA;
- break;
- }
- if (activate_task(this, IKE_REAUTH))
- {
- exchange = INFORMATIONAL;
- break;
- }
- if (activate_task(this, IKE_MOBIKE))
- {
- exchange = INFORMATIONAL;
- break;
- }
- if (activate_task(this, IKE_DPD))
- {
- exchange = INFORMATIONAL;
- break;
- }
- if (activate_task(this, IKE_AUTH_LIFETIME))
- {
- exchange = INFORMATIONAL;
- break;
- }
-#ifdef ME
- if (activate_task(this, IKE_ME))
- {
- exchange = ME_CONNECT;
- break;
- }
-#endif /* ME */
- case IKE_REKEYING:
- if (activate_task(this, IKE_DELETE))
- {
- exchange = INFORMATIONAL;
- break;
- }
- case IKE_DELETING:
- default:
- break;
- }
- }
- else
+ switch (ike_sa->get_version(ike_sa))
{
- 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 IKE_INIT:
- exchange = IKE_SA_INIT;
- break;
- case IKE_AUTHENTICATE:
- exchange = IKE_AUTH;
- break;
- case CHILD_CREATE:
- case CHILD_REKEY:
- case IKE_REKEY:
- exchange = CREATE_CHILD_SA;
- break;
- case IKE_MOBIKE:
- exchange = INFORMATIONAL;
- break;
- default:
- continue;
- }
+ case IKEV1:
+#ifdef USE_IKEV1
+ return &task_manager_v1_create(ike_sa)->task_manager;
+#endif
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();
- 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 == IKE_REKEY || type == CHILD_REKEY ||
- type == CHILD_DELETE || type == IKE_DELETE || type == 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 IKE_REKEY:
- if (type == IKE_REKEY || type == IKE_DELETE ||
- type == IKE_REAUTH)
- {
- ike_rekey_t *rekey = (ike_rekey_t*)active;
- rekey->collide(rekey, task);
- break;
- }
- continue;
- case CHILD_REKEY:
- if (type == CHILD_REKEY || type == 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;
- bool delete = FALSE, hook = FALSE;
- status_t status;
-
- me = request->get_destination(request);
- other = request->get_source(request);
-
- message = message_create();
- 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;
- case FAILED:
- default:
- hook = TRUE;
- /* FALL */
- case DESTROY_ME:
- /* destroy IKE_SA, but SEND response first */
- delete = TRUE;
- break;
- }
- if (delete)
- {
+ case IKEV2:
+#ifdef USE_IKEV2
+ return &task_manager_v2_create(ike_sa)->task_manager;
+#endif
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);
- }
- 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, process_message, status_t,
- private_task_manager_t *this, message_t *msg)
-{
- host_t *me, *other;
- u_int32_t mid;
-
- mid = msg->get_message_id(msg);
- me = msg->get_destination(msg);
- other = msg->get_source(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);
- 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);
- 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) == IKE_MOBIKE)
- { /* there is no need to queue more than one mobike task */
- enumerator_t *enumerator;
- task_t *current;
-
- enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
- while (enumerator->enumerate(enumerator, (void**)&current))
- {
- if (current->get_type(current) == 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);
-}
-
-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, incr_mid, void,
- private_task_manager_t *this, bool initiate)
-{
- if (initiate)
- {
- this->initiating.mid++;
- }
- else
- {
- this->responding.mid++;
- }
-}
-
-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();
+ break;
}
-}
-
-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_t *task_manager_create(ike_sa_t *ike_sa)
-{
- private_task_manager_t *this;
-
- INIT(this,
- .public = {
- .process_message = _process_message,
- .queue_task = _queue_task,
- .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;
+ return NULL;
}
diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h
index 5bc6c80c4..c649cf78e 100644
--- a/src/libcharon/sa/task_manager.h
+++ b/src/libcharon/sa/task_manager.h
@@ -29,7 +29,7 @@ typedef enum task_queue_t task_queue_t;
#include <library.h>
#include <encoding/message.h>
#include <sa/ike_sa.h>
-#include <sa/tasks/task.h>
+#include <sa/task.h>
/**
* First retransmit timeout in seconds.
@@ -125,6 +125,69 @@ struct task_manager_t {
void (*queue_task) (task_manager_t *this, task_t *task);
/**
+ * Queue IKE_SA establishing tasks.
+ */
+ void (*queue_ike)(task_manager_t *this);
+
+ /**
+ * Queue IKE_SA rekey tasks.
+ */
+ void (*queue_ike_rekey)(task_manager_t *this);
+
+ /**
+ * Queue IKE_SA reauth tasks.
+ */
+ void (*queue_ike_reauth)(task_manager_t *this);
+
+ /**
+ * Queue MOBIKE task
+ *
+ * @param roam TRUE to switch to new address
+ * @param address TRUE to include address list update
+ */
+ void (*queue_mobike)(task_manager_t *this, bool roam, bool address);
+
+ /**
+ * Queue IKE_SA delete tasks.
+ */
+ void (*queue_ike_delete)(task_manager_t *this);
+
+ /**
+ * Queue CHILD_SA establishing tasks.
+ *
+ * @param cfg CHILD_SA config to establish
+ * @param reqid reqid to use for CHILD_SA
+ * @param tsi initiator traffic selector, if packet-triggered
+ * @param tsr responder traffic selector, if packet-triggered
+ */
+ void (*queue_child)(task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid,
+ traffic_selector_t *tsi, traffic_selector_t *tsr);
+
+ /**
+ * Queue CHILD_SA rekeying tasks.
+ *
+ * @param protocol CHILD_SA protocol, AH|ESP
+ * @param spi CHILD_SA SPI to rekey
+ */
+ void (*queue_child_rekey)(task_manager_t *this, protocol_id_t protocol,
+ u_int32_t spi);
+
+ /**
+ * Queue CHILD_SA delete tasks.
+ *
+ * @param protocol CHILD_SA protocol, AH|ESP
+ * @param spi CHILD_SA SPI to rekey
+ * @param expired TRUE if SA already expired
+ */
+ void (*queue_child_delete)(task_manager_t *this, protocol_id_t protocol,
+ u_int32_t spi, bool expired);
+
+ /**
+ * Queue liveness checking tasks.
+ */
+ void (*queue_dpd)(task_manager_t *this);
+
+ /**
* Retransmit a request if it hasn't been acknowledged yet.
*
* A return value of INVALID_STATE means that the message was already
@@ -166,9 +229,11 @@ struct task_manager_t {
* resets the message IDs and resets all active tasks using the migrate()
* method.
* Use a value of UINT_MAX to keep the current message ID.
+ * For IKEv1, the arguments do not set the message ID, but the DPD sequence
+ * number counters.
*
- * @param initiate message ID to initiate exchanges (send)
- * @param respond message ID to respond to exchanges (expect)
+ * @param initiate message ID / DPD seq to initiate exchanges (send)
+ * @param respond message ID / DPD seq to respond to exchanges (expect)
*/
void (*reset) (task_manager_t *this, u_int32_t initiate, u_int32_t respond);
@@ -189,15 +254,23 @@ struct task_manager_t {
task_queue_t queue);
/**
+ * Flush a queue, cancelling all tasks.
+ *
+ * @param queue queue to flush
+ */
+ void (*flush_queue)(task_manager_t *this, task_queue_t queue);
+
+ /**
* Destroy the task_manager_t.
*/
void (*destroy) (task_manager_t *this);
};
/**
- * Create an instance of the task manager.
+ * Create a task manager instance for the correct IKE version.
*
- * @param ike_sa IKE_SA to manage.
+ * @param ike_sa IKE_SA to create a task manager for
+ * @return task manager implementation for IKE version
*/
task_manager_t *task_manager_create(ike_sa_t *ike_sa);
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index 86d9f4c22..fdcfa0a20 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -98,7 +98,7 @@ METHOD(trap_manager_t, install, u_int32_t,
ike_cfg_t *ike_cfg;
child_sa_t *child_sa;
host_t *me, *other;
- linked_list_t *my_ts, *other_ts;
+ linked_list_t *my_ts, *other_ts, *list;
enumerator_t *enumerator;
bool found = FALSE;
status_t status;
@@ -127,14 +127,14 @@ METHOD(trap_manager_t, install, u_int32_t,
/* try to resolve addresses */
ike_cfg = peer->get_ike_cfg(peer);
- other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg),
+ other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg, NULL),
0, ike_cfg->get_other_port(ike_cfg));
if (!other || other->is_anyaddr(other))
{
DBG1(DBG_CFG, "installing trap failed, remote address unknown");
return 0;
}
- me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg),
+ me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg, NULL),
other->get_family(other), ike_cfg->get_my_port(ike_cfg));
if (!me || me->is_anyaddr(me))
{
@@ -152,10 +152,14 @@ METHOD(trap_manager_t, install, u_int32_t,
/* create and route CHILD_SA */
child_sa = child_sa_create(me, other, child, 0, FALSE);
- my_ts = child->get_traffic_selectors(child, TRUE, NULL, me);
- other_ts = child->get_traffic_selectors(child, FALSE, NULL, other);
- me->destroy(me);
- other->destroy(other);
+
+ list = linked_list_create_with_items(me, NULL);
+ my_ts = child->get_traffic_selectors(child, TRUE, NULL, list);
+ list->destroy_offset(list, offsetof(host_t, destroy));
+
+ list = linked_list_create_with_items(other, NULL);
+ other_ts = child->get_traffic_selectors(child, FALSE, NULL, list);
+ list->destroy_offset(list, offsetof(host_t, destroy));
/* while we don't know the finally negotiated protocol (ESP|AH), we
* could iterate all proposals for a best guess (TODO). But as we
@@ -284,26 +288,34 @@ METHOD(trap_manager_t, acquire, void,
ike_sa = charon->ike_sa_manager->checkout_by_config(
charon->ike_sa_manager, peer);
- if (ike_sa->get_peer_cfg(ike_sa) == NULL)
- {
- ike_sa->set_peer_cfg(ike_sa, peer);
- }
- if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
+ if (ike_sa)
{
- /* make sure the entry is still there */
- this->lock->read_lock(this->lock);
- if (this->traps->find_first(this->traps, NULL,
- (void**)&found) == SUCCESS)
+ if (ike_sa->get_peer_cfg(ike_sa) == NULL)
{
- found->ike_sa = ike_sa;
+ ike_sa->set_peer_cfg(ike_sa, peer);
+ }
+ if (ike_sa->get_version(ike_sa) == IKEV1)
+ { /* in IKEv1, don't prepend the acquiring packet TS, as we only
+ * have a single TS that we can establish in a Quick Mode. */
+ src = dst = NULL;
+ }
+ if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
+ {
+ /* make sure the entry is still there */
+ this->lock->read_lock(this->lock);
+ if (this->traps->find_first(this->traps, NULL,
+ (void**)&found) == SUCCESS)
+ {
+ found->ike_sa = ike_sa;
+ }
+ this->lock->unlock(this->lock);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+ }
+ else
+ {
+ charon->ike_sa_manager->checkin_and_destroy(
+ charon->ike_sa_manager, ike_sa);
}
- this->lock->unlock(this->lock);
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
- }
- else
- {
- charon->ike_sa_manager->checkin_and_destroy(
- charon->ike_sa_manager, ike_sa);
}
peer->destroy(peer);
}
diff --git a/src/libcharon/sa/xauth/xauth_manager.c b/src/libcharon/sa/xauth/xauth_manager.c
new file mode 100644
index 000000000..432c9c0ab
--- /dev/null
+++ b/src/libcharon/sa/xauth/xauth_manager.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 "xauth_manager.h"
+
+#include <utils/linked_list.h>
+#include <threading/rwlock.h>
+
+typedef struct private_xauth_manager_t private_xauth_manager_t;
+typedef struct xauth_entry_t xauth_entry_t;
+
+/**
+ * XAuth constructor entry
+ */
+struct xauth_entry_t {
+
+ /**
+ * Xauth backend name
+ */
+ char *name;
+
+ /**
+ * Role of the method, XAUTH_SERVER or XAUTH_PEER
+ */
+ xauth_role_t role;
+
+ /**
+ * constructor function to create instance
+ */
+ xauth_constructor_t constructor;
+};
+
+/**
+ * private data of xauth_manager
+ */
+struct private_xauth_manager_t {
+
+ /**
+ * public functions
+ */
+ xauth_manager_t public;
+
+ /**
+ * list of eap_entry_t's
+ */
+ linked_list_t *methods;
+
+ /**
+ * rwlock to lock methods
+ */
+ rwlock_t *lock;
+};
+
+METHOD(xauth_manager_t, add_method, void,
+ private_xauth_manager_t *this, char *name, xauth_role_t role,
+ xauth_constructor_t constructor)
+{
+ xauth_entry_t *entry;
+
+ INIT(entry,
+ .name = name,
+ .role = role,
+ .constructor = constructor,
+ );
+
+ this->lock->write_lock(this->lock);
+ this->methods->insert_last(this->methods, entry);
+ this->lock->unlock(this->lock);
+}
+
+METHOD(xauth_manager_t, remove_method, void,
+ private_xauth_manager_t *this, xauth_constructor_t constructor)
+{
+ enumerator_t *enumerator;
+ xauth_entry_t *entry;
+
+ this->lock->write_lock(this->lock);
+ enumerator = this->methods->create_enumerator(this->methods);
+ while (enumerator->enumerate(enumerator, &entry))
+ {
+ if (constructor == entry->constructor)
+ {
+ this->methods->remove_at(this->methods, enumerator);
+ free(entry);
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+}
+
+METHOD(xauth_manager_t, create_instance, xauth_method_t*,
+ private_xauth_manager_t *this, char *name, xauth_role_t role,
+ identification_t *server, identification_t *peer)
+{
+ enumerator_t *enumerator;
+ xauth_entry_t *entry;
+ xauth_method_t *method = NULL;
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->methods->create_enumerator(this->methods);
+ while (enumerator->enumerate(enumerator, &entry))
+ {
+ if (role == entry->role &&
+ (!name || streq(name, entry->name)))
+ {
+ method = entry->constructor(server, peer);
+ if (method)
+ {
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+ return method;
+}
+
+METHOD(xauth_manager_t, destroy, void,
+ private_xauth_manager_t *this)
+{
+ this->methods->destroy_function(this->methods, free);
+ this->lock->destroy(this->lock);
+ free(this);
+}
+
+/*
+ * See header
+ */
+xauth_manager_t *xauth_manager_create()
+{
+ private_xauth_manager_t *this;
+
+ INIT(this,
+ .public = {
+ .add_method = _add_method,
+ .remove_method = _remove_method,
+ .create_instance = _create_instance,
+ .destroy = _destroy,
+ },
+ .methods = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/xauth/xauth_manager.h b/src/libcharon/sa/xauth/xauth_manager.h
new file mode 100644
index 000000000..929d5de8f
--- /dev/null
+++ b/src/libcharon/sa/xauth/xauth_manager.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * 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 xauth_manager xauth_manager
+ * @{ @ingroup xauth
+ */
+
+#ifndef XAUTH_MANAGER_H_
+#define XAUTH_MANAGER_H_
+
+#include <sa/xauth/xauth_method.h>
+
+typedef struct xauth_manager_t xauth_manager_t;
+
+/**
+ * The XAuth manager manages all XAuth implementations and creates instances.
+ *
+ * A plugin registers it's implemented XAuth method at the manager by
+ * providing type and a contructor function. The manager then instanciates
+ * xauth_method_t instances through the provided constructor to handle
+ * XAuth authentication.
+ */
+struct xauth_manager_t {
+
+ /**
+ * Register a XAuth method implementation.
+ *
+ * @param name backend name to register
+ * @param role XAUTH_SERVER or XAUTH_PEER
+ * @param constructor constructor function, returns an xauth_method_t
+ */
+ void (*add_method)(xauth_manager_t *this, char *name,
+ xauth_role_t role, xauth_constructor_t constructor);
+
+ /**
+ * Unregister a XAuth method implementation using it's constructor.
+ *
+ * @param constructor constructor function, as added in add_method
+ */
+ void (*remove_method)(xauth_manager_t *this, xauth_constructor_t constructor);
+
+ /**
+ * Create a new XAuth method instance.
+ *
+ * @param name backend name, as it was registered with
+ * @param role XAUTH_SERVER or XAUTH_PEER
+ * @param server identity of the server
+ * @param peer identity of the peer (client)
+ * @return XAUTH method instance, NULL if no constructor found
+ */
+ xauth_method_t* (*create_instance)(xauth_manager_t *this,
+ char *name, xauth_role_t role,
+ identification_t *server, identification_t *peer);
+
+ /**
+ * Destroy a eap_manager instance.
+ */
+ void (*destroy)(xauth_manager_t *this);
+};
+
+/**
+ * Create a eap_manager instance.
+ */
+xauth_manager_t *xauth_manager_create();
+
+#endif /** XAUTH_MANAGER_H_ @}*/
diff --git a/src/libcharon/sa/xauth/xauth_method.c b/src/libcharon/sa/xauth/xauth_method.c
new file mode 100644
index 000000000..838822d1e
--- /dev/null
+++ b/src/libcharon/sa/xauth/xauth_method.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 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 "xauth_method.h"
+
+#include <daemon.h>
+
+ENUM(xauth_role_names, XAUTH_SERVER, XAUTH_PEER,
+ "XAUTH_SERVER",
+ "XAUTH_PEER",
+);
+
+/**
+ * See header
+ */
+bool xauth_method_register(plugin_t *plugin, plugin_feature_t *feature,
+ bool reg, void *data)
+{
+ if (reg)
+ {
+ charon->xauth->add_method(charon->xauth, feature->arg.xauth,
+ feature->type == FEATURE_XAUTH_SERVER ? XAUTH_SERVER : XAUTH_PEER,
+ (xauth_constructor_t)data);
+ }
+ else
+ {
+ charon->xauth->remove_method(charon->xauth, (xauth_constructor_t)data);
+ }
+ return TRUE;
+}
diff --git a/src/libcharon/sa/xauth/xauth_method.h b/src/libcharon/sa/xauth/xauth_method.h
new file mode 100644
index 000000000..9f6067dbf
--- /dev/null
+++ b/src/libcharon/sa/xauth/xauth_method.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+/**
+ * @defgroup xauth_method xauth_method
+ * @{ @ingroup xauth
+ */
+
+#ifndef XAUTH_METHOD_H_
+#define XAUTH_METHOD_H_
+
+typedef struct xauth_method_t xauth_method_t;
+typedef enum xauth_role_t xauth_role_t;
+
+#include <library.h>
+#include <plugins/plugin.h>
+#include <utils/identification.h>
+#include <encoding/payloads/cp_payload.h>
+
+/**
+ * Role of an xauth_method, SERVER or PEER (client)
+ */
+enum xauth_role_t {
+ XAUTH_SERVER,
+ XAUTH_PEER,
+};
+
+/**
+ * enum names for xauth_role_t.
+ */
+extern enum_name_t *xauth_role_names;
+
+/**
+ * Interface of an XAuth method for server and client side.
+ *
+ * An XAuth method initiates an XAuth exchange and processes requests and
+ * responses. An XAuth method may need multiple exchanges before succeeding.
+ * Sending of XAUTH(STATUS) message is done by the framework, not a method.
+ */
+struct xauth_method_t {
+
+ /**
+ * Initiate the XAuth exchange.
+ *
+ * initiate() is only useable for server implementations, as clients only
+ * reply to server requests.
+ * A cp_payload is created in "out" if result is NEED_MORE.
+ *
+ * @param out cp_payload to send to the client
+ * @return
+ * - NEED_MORE, if an other exchange is required
+ * - FAILED, if unable to create XAuth request payload
+ */
+ status_t (*initiate) (xauth_method_t *this, cp_payload_t **out);
+
+ /**
+ * Process a received XAuth message.
+ *
+ * A cp_payload is created in "out" if result is NEED_MORE.
+ *
+ * @param in cp_payload response received
+ * @param out created cp_payload to send
+ * @return
+ * - NEED_MORE, if an other exchange is required
+ * - FAILED, if XAuth method failed
+ * - SUCCESS, if XAuth method succeeded
+ */
+ status_t (*process) (xauth_method_t *this, cp_payload_t *in,
+ cp_payload_t **out);
+
+ /**
+ * Get the XAuth username received as XAuth initiator.
+ *
+ * @return used XAuth username, pointer to internal data
+ */
+ identification_t* (*get_identity)(xauth_method_t *this);
+
+ /**
+ * Destroys a eap_method_t object.
+ */
+ void (*destroy) (xauth_method_t *this);
+};
+
+/**
+ * Constructor definition for a pluggable XAuth method.
+ *
+ * Each XAuth module must define a constructor function which will return
+ * an initialized object with the methods defined in xauth_method_t.
+ * Constructors for server and peers are identical, to support both roles
+ * of a XAuth method, a plugin needs register two constructors in the
+ * xauth_manager_t.
+ *
+ * @param server ID of the server to use for credential lookup
+ * @param peer ID of the peer to use for credential lookup
+ * @return implementation of the eap_method_t interface
+ */
+typedef xauth_method_t *(*xauth_constructor_t)(identification_t *server,
+ identification_t *peer);
+
+/**
+ * Helper function to (un-)register XAuth methods from plugin features.
+ *
+ * This function is a plugin_feature_callback_t and can be used with the
+ * PLUGIN_CALLBACK macro to register a XAuth method constructor.
+ *
+ * @param plugin plugin registering the XAuth method constructor
+ * @param feature associated plugin feature
+ * @param reg TRUE to register, FALSE to unregister.
+ * @param data data passed to callback, an xauth_constructor_t
+ */
+bool xauth_method_register(plugin_t *plugin, plugin_feature_t *feature,
+ bool reg, void *data);
+
+#endif /** XAUTH_METHOD_H_ @}*/