summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2016-03-24 11:59:32 +0100
committerYves-Alexis Perez <corsac@debian.org>2016-03-24 11:59:32 +0100
commit518dd33c94e041db0444c7d1f33da363bb8e3faf (patch)
treee8d1665ffadff7ec40228dda47e81f8f4691cd07 /src/libcharon/sa
parentf42f239a632306ed082f6fde878977248eea85cf (diff)
downloadvyos-strongswan-518dd33c94e041db0444c7d1f33da363bb8e3faf.tar.gz
vyos-strongswan-518dd33c94e041db0444c7d1f33da363bb8e3faf.zip
Imported Upstream version 5.4.0
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/child_sa.c82
-rw-r--r--src/libcharon/sa/ike_sa.c557
-rw-r--r--src/libcharon/sa/ike_sa.h65
-rw-r--r--src/libcharon/sa/ike_sa_manager.c97
-rw-r--r--src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c4
-rw-r--r--src/libcharon/sa/ikev1/phase1.c2
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_natd.c3
-rw-r--r--src/libcharon/sa/ikev1/tasks/mode_config.c37
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c18
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.c1
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c23
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c102
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c7
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c11
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth.c144
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_config.c5
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.c146
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_me.c5
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.c13
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_natd.c9
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_redirect.c150
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_redirect.h54
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_vendor.c56
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c117
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h54
-rw-r--r--src/libcharon/sa/redirect_manager.c274
-rw-r--r--src/libcharon/sa/redirect_manager.h109
-rw-r--r--src/libcharon/sa/redirect_provider.h59
-rw-r--r--src/libcharon/sa/shunt_manager.c25
-rw-r--r--src/libcharon/sa/task.c2
-rw-r--r--src/libcharon/sa/task.h18
-rw-r--r--src/libcharon/sa/trap_manager.c4
32 files changed, 1933 insertions, 320 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index b0f163c83..56b7cb5a4 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -23,7 +23,6 @@
#include <string.h>
#include <time.h>
-#include <hydra.h>
#include <daemon.h>
#include <collections/array.h>
@@ -469,10 +468,10 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
{
if (this->my_spi)
{
- status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
- this->other_addr, this->my_addr, this->my_spi,
- proto_ike2ip(this->protocol), this->mark_in,
- &bytes, &packets, &time);
+ status = charon->kernel->query_sa(charon->kernel, this->other_addr,
+ this->my_addr, this->my_spi,
+ proto_ike2ip(this->protocol), this->mark_in,
+ &bytes, &packets, &time);
if (status == SUCCESS)
{
if (bytes > this->my_usebytes)
@@ -493,10 +492,10 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
{
if (this->other_spi)
{
- status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
- this->my_addr, this->other_addr, this->other_spi,
- proto_ike2ip(this->protocol), this->mark_out,
- &bytes, &packets, &time);
+ status = charon->kernel->query_sa(charon->kernel, this->my_addr,
+ this->other_addr, this->other_spi,
+ proto_ike2ip(this->protocol), this->mark_out,
+ &bytes, &packets, &time);
if (status == SUCCESS)
{
if (bytes > this->other_usebytes)
@@ -532,15 +531,15 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
if (inbound)
{
- if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
- other_ts, my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS)
+ if (charon->kernel->query_policy(charon->kernel, other_ts,
+ my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS)
{
last_use = max(last_use, in);
}
if (this->mode != MODE_TRANSPORT)
{
- if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
- other_ts, my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS)
+ if (charon->kernel->query_policy(charon->kernel, other_ts,
+ my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS)
{
last_use = max(last_use, fwd);
}
@@ -548,8 +547,8 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
}
else
{
- if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
- my_ts, other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS)
+ if (charon->kernel->query_policy(charon->kernel, my_ts,
+ other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS)
{
last_use = max(last_use, out);
}
@@ -629,10 +628,8 @@ METHOD(child_sa_t, get_installtime, time_t,
METHOD(child_sa_t, alloc_spi, u_int32_t,
private_child_sa_t *this, protocol_id_t protocol)
{
- if (hydra->kernel_interface->get_spi(hydra->kernel_interface,
- this->other_addr, this->my_addr,
- proto_ike2ip(protocol),
- &this->my_spi) == SUCCESS)
+ if (charon->kernel->get_spi(charon->kernel, this->other_addr, this->my_addr,
+ proto_ike2ip(protocol), &this->my_spi) == SUCCESS)
{
/* if we allocate a SPI, but then are unable to establish the SA, we
* need to know the protocol family to delete the partial SA */
@@ -645,9 +642,8 @@ METHOD(child_sa_t, alloc_spi, u_int32_t,
METHOD(child_sa_t, alloc_cpi, u_int16_t,
private_child_sa_t *this)
{
- if (hydra->kernel_interface->get_cpi(hydra->kernel_interface,
- this->other_addr, this->my_addr,
- &this->my_cpi) == SUCCESS)
+ if (charon->kernel->get_cpi(charon->kernel, this->other_addr, this->my_addr,
+ &this->my_cpi) == SUCCESS)
{
return this->my_cpi;
}
@@ -711,9 +707,8 @@ METHOD(child_sa_t, install, status_t,
if (!this->reqid_allocated && !this->static_reqid)
{
- status = hydra->kernel_interface->alloc_reqid(hydra->kernel_interface,
- my_ts, other_ts, this->mark_in, this->mark_out,
- &this->reqid);
+ status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts,
+ this->mark_in, this->mark_out, &this->reqid);
if (status != SUCCESS)
{
return status;
@@ -757,7 +752,7 @@ METHOD(child_sa_t, install, status_t,
dst_ts = other_ts;
}
- status = hydra->kernel_interface->add_sa(hydra->kernel_interface,
+ status = charon->kernel->add_sa(charon->kernel,
src, dst, spi, proto_ike2ip(this->protocol), this->reqid,
inbound ? this->mark_in : this->mark_out, tfc,
lifetime, enc_alg, encr, int_alg, integ, this->mode,
@@ -776,7 +771,7 @@ static bool require_policy_update()
{
kernel_feature_t f;
- f = hydra->kernel_interface->get_features(hydra->kernel_interface);
+ f = charon->kernel->get_features(charon->kernel);
return !(f & KERNEL_NO_POLICY_UPDATES);
}
@@ -833,18 +828,18 @@ static status_t install_policies_internal(private_child_sa_t *this,
ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority)
{
status_t status = SUCCESS;
- status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
+ status |= charon->kernel->add_policy(charon->kernel,
my_addr, other_addr, my_ts, other_ts,
POLICY_OUT, type, other_sa,
this->mark_out, priority);
- status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
+ status |= charon->kernel->add_policy(charon->kernel,
other_addr, my_addr, other_ts, my_ts,
POLICY_IN, type, my_sa,
this->mark_in, priority);
if (this->mode != MODE_TRANSPORT)
{
- status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
+ status |= charon->kernel->add_policy(charon->kernel,
other_addr, my_addr, other_ts, my_ts,
POLICY_FWD, type, my_sa,
this->mark_in, priority);
@@ -861,15 +856,15 @@ static void del_policies_internal(private_child_sa_t *this,
ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority)
{
- hydra->kernel_interface->del_policy(hydra->kernel_interface,
+ charon->kernel->del_policy(charon->kernel,
my_addr, other_addr, my_ts, other_ts, POLICY_OUT, type,
other_sa, this->mark_out, priority);
- hydra->kernel_interface->del_policy(hydra->kernel_interface,
+ charon->kernel->del_policy(charon->kernel,
other_addr, my_addr, other_ts, my_ts, POLICY_IN,
type, my_sa, this->mark_in, priority);
if (this->mode != MODE_TRANSPORT)
{
- hydra->kernel_interface->del_policy(hydra->kernel_interface,
+ charon->kernel->del_policy(charon->kernel,
other_addr, my_addr, other_ts, my_ts, POLICY_FWD,
type, my_sa, this->mark_in, priority);
}
@@ -886,8 +881,8 @@ METHOD(child_sa_t, add_policies, status_t,
if (!this->reqid_allocated && !this->static_reqid)
{
/* trap policy, get or confirm reqid */
- status = hydra->kernel_interface->alloc_reqid(
- hydra->kernel_interface, my_ts_list, other_ts_list,
+ status = charon->kernel->alloc_reqid(
+ charon->kernel, my_ts_list, other_ts_list,
this->mark_in, this->mark_out, &this->reqid);
if (status != SUCCESS)
{
@@ -967,11 +962,10 @@ static void reinstall_vip(host_t *vip, host_t *me)
{
char *iface;
- if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
- me, &iface))
+ if (charon->kernel->get_interface(charon->kernel, me, &iface))
{
- hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE);
- hydra->kernel_interface->add_ip(hydra->kernel_interface, vip, -1, iface);
+ charon->kernel->del_ip(charon->kernel, vip, -1, TRUE);
+ charon->kernel->add_ip(charon->kernel, vip, -1, iface);
free(iface);
}
}
@@ -1000,7 +994,7 @@ METHOD(child_sa_t, update, status_t,
/* update our (initiator) SA */
if (this->my_spi)
{
- if (hydra->kernel_interface->update_sa(hydra->kernel_interface,
+ if (charon->kernel->update_sa(charon->kernel,
this->my_spi, proto_ike2ip(this->protocol),
this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
this->other_addr, this->my_addr, other, me,
@@ -1014,7 +1008,7 @@ METHOD(child_sa_t, update, status_t,
/* update his (responder) SA */
if (this->other_spi)
{
- if (hydra->kernel_interface->update_sa(hydra->kernel_interface,
+ if (charon->kernel->update_sa(charon->kernel,
this->other_spi, proto_ike2ip(this->protocol),
this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0,
this->my_addr, this->other_addr, me, other,
@@ -1143,14 +1137,14 @@ METHOD(child_sa_t, destroy, void,
/* delete SAs in the kernel, if they are set up */
if (this->my_spi)
{
- hydra->kernel_interface->del_sa(hydra->kernel_interface,
+ charon->kernel->del_sa(charon->kernel,
this->other_addr, this->my_addr, this->my_spi,
proto_ike2ip(this->protocol), this->my_cpi,
this->mark_in);
}
if (this->other_spi)
{
- hydra->kernel_interface->del_sa(hydra->kernel_interface,
+ charon->kernel->del_sa(charon->kernel,
this->my_addr, this->other_addr, this->other_spi,
proto_ike2ip(this->protocol), this->other_cpi,
this->mark_out);
@@ -1158,7 +1152,7 @@ METHOD(child_sa_t, destroy, void,
if (this->reqid_allocated)
{
- if (hydra->kernel_interface->release_reqid(hydra->kernel_interface,
+ if (charon->kernel->release_reqid(charon->kernel,
this->reqid, this->mark_in, this->mark_out) != SUCCESS)
{
DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid);
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index dcf9d5f2c..bcbff3211 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -46,7 +46,6 @@
#include "ike_sa.h"
#include <library.h>
-#include <hydra.h>
#include <daemon.h>
#include <collections/array.h>
#include <utils/lexparser.h>
@@ -57,6 +56,9 @@
#include <processing/jobs/rekey_ike_sa_job.h>
#include <processing/jobs/retry_initiate_job.h>
#include <sa/ikev2/tasks/ike_auth_lifetime.h>
+#include <sa/ikev2/tasks/ike_reauth_complete.h>
+#include <sa/ikev2/tasks/ike_redirect.h>
+#include <credentials/sets/auth_cfg_wrapper.h>
#ifdef ME
#include <sa/ikev2/tasks/ike_me.h>
@@ -239,6 +241,11 @@ struct private_ike_sa_t {
u_int32_t keepalive_interval;
/**
+ * The schedueld keep alive job, if any
+ */
+ send_keepalive_job_t *keepalive_job;
+
+ /**
* interval for retries during initiation (e.g. if DNS resolution failed),
* 0 to disable (default)
*/
@@ -278,6 +285,21 @@ struct private_ike_sa_t {
* Maximum length of a single fragment, 0 for address-specific defaults
*/
size_t fragment_size;
+
+ /**
+ * Whether to follow IKEv2 redirects
+ */
+ bool follow_redirects;
+
+ /**
+ * Original gateway address from which we got redirected
+ */
+ host_t *redirected_from;
+
+ /**
+ * Timestamps of redirect attempts to handle loops
+ */
+ array_t *redirected_at;
};
/**
@@ -382,6 +404,12 @@ METHOD(ike_sa_t, set_other_host, void,
this->other_host = other;
}
+METHOD(ike_sa_t, get_redirected_from, host_t*,
+ private_ike_sa_t *this)
+{
+ return this->redirected_from;
+}
+
METHOD(ike_sa_t, get_peer_cfg, peer_cfg_t*,
private_ike_sa_t *this)
{
@@ -455,6 +483,113 @@ static void flush_auth_cfgs(private_ike_sa_t *this)
}
}
+METHOD(ike_sa_t, verify_peer_certificate, bool,
+ private_ike_sa_t *this)
+{
+ enumerator_t *e1, *e2, *certs;
+ auth_cfg_t *cfg, *cfg_done;
+ certificate_t *peer, *cert;
+ public_key_t *key;
+ auth_cfg_t *auth;
+ auth_cfg_wrapper_t *wrapper;
+ time_t not_before, not_after;
+ bool valid = TRUE, found;
+
+ if (this->state != IKE_ESTABLISHED)
+ {
+ DBG1(DBG_IKE, "unable to verify peer certificate in state %N",
+ ike_sa_state_names, this->state);
+ return FALSE;
+ }
+
+ if (!this->flush_auth_cfg &&
+ lib->settings->get_bool(lib->settings,
+ "%s.flush_auth_cfg", FALSE, lib->ns))
+ { /* we can do this check only once if auth configs are flushed */
+ DBG1(DBG_IKE, "unable to verify peer certificate as authentication "
+ "information has been flushed");
+ return FALSE;
+ }
+ this->public.set_condition(&this->public, COND_ONLINE_VALIDATION_SUSPENDED,
+ FALSE);
+
+ e1 = this->peer_cfg->create_auth_cfg_enumerator(this->peer_cfg, FALSE);
+ e2 = array_create_enumerator(this->other_auths);
+ while (e1->enumerate(e1, &cfg))
+ {
+ if (!e2->enumerate(e2, &cfg_done))
+ { /* this should not happen as the authentication should never have
+ * succeeded */
+ valid = FALSE;
+ break;
+ }
+ if ((uintptr_t)cfg_done->get(cfg_done,
+ AUTH_RULE_AUTH_CLASS) != AUTH_CLASS_PUBKEY)
+ {
+ continue;
+ }
+ peer = cfg_done->get(cfg_done, AUTH_RULE_SUBJECT_CERT);
+ if (!peer)
+ {
+ DBG1(DBG_IKE, "no subject certificate found, skipping certificate "
+ "verification");
+ continue;
+ }
+ if (!peer->get_validity(peer, NULL, &not_before, &not_after))
+ {
+ DBG1(DBG_IKE, "peer certificate invalid (valid from %T to %T)",
+ &not_before, FALSE, &not_after, FALSE);
+ valid = FALSE;
+ break;
+ }
+ key = peer->get_public_key(peer);
+ if (!key)
+ {
+ DBG1(DBG_IKE, "unable to retrieve public key, skipping certificate "
+ "verification");
+ continue;
+ }
+ DBG1(DBG_IKE, "verifying peer certificate");
+ /* serve received certificates */
+ wrapper = auth_cfg_wrapper_create(cfg_done);
+ lib->credmgr->add_local_set(lib->credmgr, &wrapper->set, FALSE);
+ certs = lib->credmgr->create_trusted_enumerator(lib->credmgr,
+ key->get_type(key), peer->get_subject(peer), TRUE);
+ key->destroy(key);
+
+ found = FALSE;
+ while (certs->enumerate(certs, &cert, &auth))
+ {
+ if (peer->equals(peer, cert))
+ {
+ cfg_done->add(cfg_done, AUTH_RULE_CERT_VALIDATION_SUSPENDED,
+ FALSE);
+ cfg_done->merge(cfg_done, auth, FALSE);
+ valid = cfg_done->complies(cfg_done, cfg, TRUE);
+ found = TRUE;
+ break;
+ }
+ }
+ certs->destroy(certs);
+ lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set);
+ wrapper->destroy(wrapper);
+ if (!found || !valid)
+ {
+ valid = FALSE;
+ break;
+ }
+ }
+ e1->destroy(e1);
+ e2->destroy(e2);
+
+ if (this->flush_auth_cfg)
+ {
+ this->flush_auth_cfg = FALSE;
+ flush_auth_cfgs(this);
+ }
+ return valid;
+}
+
METHOD(ike_sa_t, get_proposal, proposal_t*,
private_ike_sa_t *this)
{
@@ -482,14 +617,20 @@ METHOD(ike_sa_t, set_message_id, void,
}
METHOD(ike_sa_t, send_keepalive, void,
- private_ike_sa_t *this)
+ private_ike_sa_t *this, bool scheduled)
{
- send_keepalive_job_t *job;
time_t last_out, now, diff;
- if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0 ||
- this->state == IKE_PASSIVE)
- { /* disable keep alives if we are not NATed anymore, or we are passive */
+ if (scheduled)
+ {
+ this->keepalive_job = NULL;
+ }
+ if (!this->keepalive_interval || this->state == IKE_PASSIVE)
+ { /* keepalives disabled either by configuration or for passive IKE_SAs */
+ return;
+ }
+ if (!(this->conditions & COND_NAT_HERE) || (this->conditions & COND_STALE))
+ { /* disable keepalives if we are not NATed anymore, or the SA is stale */
return;
}
@@ -514,9 +655,12 @@ METHOD(ike_sa_t, send_keepalive, void,
charon->sender->send_no_marker(charon->sender, packet);
diff = 0;
}
- job = send_keepalive_job_create(this->ike_sa_id);
- lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
- this->keepalive_interval - diff);
+ if (!this->keepalive_job)
+ {
+ this->keepalive_job = send_keepalive_job_create(this->ike_sa_id);
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)this->keepalive_job,
+ this->keepalive_interval - diff);
+ }
}
METHOD(ike_sa_t, get_ike_cfg, ike_cfg_t*,
@@ -563,7 +707,7 @@ METHOD(ike_sa_t, set_condition, void,
case COND_NAT_HERE:
DBG1(DBG_IKE, "local host is behind NAT, sending keep alives");
this->conditions |= COND_NAT_ANY;
- send_keepalive(this);
+ send_keepalive(this, FALSE);
break;
case COND_NAT_THERE:
DBG1(DBG_IKE, "remote host is behind NAT");
@@ -590,6 +734,9 @@ METHOD(ike_sa_t, set_condition, void,
has_condition(this, COND_NAT_THERE) ||
has_condition(this, COND_NAT_FAKE));
break;
+ case COND_STALE:
+ send_keepalive(this, FALSE);
+ break;
default:
break;
}
@@ -727,6 +874,8 @@ METHOD(ike_sa_t, set_state, void,
{
keepalives = TRUE;
}
+ DESTROY_IF(this->redirected_from);
+ this->redirected_from = NULL;
}
break;
}
@@ -749,7 +898,7 @@ METHOD(ike_sa_t, set_state, void,
}
if (keepalives)
{
- send_keepalive(this);
+ send_keepalive(this, FALSE);
}
}
@@ -786,12 +935,12 @@ METHOD(ike_sa_t, add_virtual_ip, void,
{
char *iface;
- if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
- this->my_host, &iface))
+ if (charon->kernel->get_interface(charon->kernel, this->my_host,
+ &iface))
{
DBG1(DBG_IKE, "installing new virtual IP %H", ip);
- if (hydra->kernel_interface->add_ip(hydra->kernel_interface,
- ip, -1, iface) == SUCCESS)
+ if (charon->kernel->add_ip(charon->kernel, ip, -1,
+ iface) == SUCCESS)
{
array_insert_create(&this->my_vips, ARRAY_TAIL, ip->clone(ip));
}
@@ -828,8 +977,7 @@ METHOD(ike_sa_t, clear_virtual_ips, void,
{
if (local)
{
- hydra->kernel_interface->del_ip(hydra->kernel_interface,
- vip, -1, TRUE);
+ charon->kernel->del_ip(charon->kernel, vip, -1, TRUE);
}
vip->destroy(vip);
}
@@ -1265,8 +1413,8 @@ static void resolve_hosts(private_ike_sa_t *this)
!this->other_host->is_anyaddr(this->other_host))
{
host->destroy(host);
- host = hydra->kernel_interface->get_source_addr(
- hydra->kernel_interface, this->other_host, NULL);
+ host = charon->kernel->get_source_addr(charon->kernel,
+ this->other_host, NULL);
if (host)
{
host->set_port(host, this->ike_cfg->get_my_port(this->ike_cfg));
@@ -1401,9 +1549,14 @@ METHOD(ike_sa_t, process_message, status_t,
status = this->task_manager->process_message(this->task_manager, message);
if (this->flush_auth_cfg && this->state == IKE_ESTABLISHED)
{
- /* authentication completed */
- this->flush_auth_cfg = FALSE;
- flush_auth_cfgs(this);
+ /* authentication completed but if the online validation is suspended we
+ * need the auth cfgs until we did the delayed verification, we flush
+ * them afterwards */
+ if (!has_condition(this, COND_ONLINE_VALIDATION_SUSPENDED))
+ {
+ this->flush_auth_cfg = FALSE;
+ flush_auth_cfgs(this);
+ }
}
return status;
}
@@ -1735,6 +1888,86 @@ static bool is_child_queued(private_ike_sa_t *this, task_queue_t queue)
return found;
}
+/**
+ * Reestablish CHILD_SAs and migrate queued tasks.
+ *
+ * If force is true all SAs are restarted, otherwise their close/dpd_action
+ * is followed.
+ */
+static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
+ bool force)
+{
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+ child_cfg_t *child_cfg;
+ action_t action;
+ status_t status = FAILED;
+
+ /* handle existing CHILD_SAs */
+ enumerator = create_child_sa_enumerator(this);
+ while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ {
+ if (force)
+ {
+ switch (child_sa->get_state(child_sa))
+ {
+ case CHILD_ROUTED:
+ { /* move routed child directly */
+ remove_child_sa(this, enumerator);
+ new->add_child_sa(new, child_sa);
+ action = ACTION_NONE;
+ break;
+ }
+ default:
+ { /* initiate/queue all other CHILD_SAs */
+ action = ACTION_RESTART;
+ break;
+ }
+ }
+ }
+ else
+ { /* only restart CHILD_SAs that are configured accordingly */
+ if (this->state == IKE_DELETING)
+ {
+ action = child_sa->get_close_action(child_sa);
+ }
+ else
+ {
+ action = child_sa->get_dpd_action(child_sa);
+ }
+ }
+ switch (action)
+ {
+ case ACTION_RESTART:
+ child_cfg = child_sa->get_config(child_sa);
+ DBG1(DBG_IKE, "restarting CHILD_SA %s",
+ child_cfg->get_name(child_cfg));
+ child_cfg->get_ref(child_cfg);
+ status = new->initiate(new, child_cfg,
+ child_sa->get_reqid(child_sa), NULL, NULL);
+ break;
+ default:
+ continue;
+ }
+ if (status == DESTROY_ME)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ /* adopt any active or queued CHILD-creating tasks */
+ if (status != DESTROY_ME)
+ {
+ task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager;
+ other_tasks->adopt_child_tasks(other_tasks, this->task_manager);
+ if (new->get_state(new) == IKE_CREATED)
+ {
+ status = new->initiate(new, NULL, 0, NULL, NULL);
+ }
+ }
+ return status;
+}
+
METHOD(ike_sa_t, reestablish, status_t,
private_ike_sa_t *this)
{
@@ -1743,7 +1976,6 @@ METHOD(ike_sa_t, reestablish, status_t,
action_t action;
enumerator_t *enumerator;
child_sa_t *child_sa;
- child_cfg_t *child_cfg;
bool restart = FALSE;
status_t status = FAILED;
@@ -1836,8 +2068,11 @@ METHOD(ike_sa_t, reestablish, status_t,
host = this->my_host;
new->set_my_host(new, host->clone(host));
charon->bus->ike_reestablish_pre(charon->bus, &this->public, new);
- /* resolve hosts but use the old addresses above as fallback */
- resolve_hosts((private_ike_sa_t*)new);
+ if (!has_condition(this, COND_REAUTHENTICATING))
+ { /* reauthenticate to the same addresses, but resolve hosts if
+ * reestablishing (old addresses serve as fallback) */
+ resolve_hosts((private_ike_sa_t*)new);
+ }
/* if we already have a virtual IP, we reuse it */
enumerator = array_create_enumerator(this->my_vips);
while (enumerator->enumerate(enumerator, &host))
@@ -1854,68 +2089,8 @@ METHOD(ike_sa_t, reestablish, status_t,
else
#endif /* ME */
{
- /* handle existing CHILD_SAs */
- enumerator = create_child_sa_enumerator(this);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
- {
- if (has_condition(this, COND_REAUTHENTICATING))
- {
- switch (child_sa->get_state(child_sa))
- {
- case CHILD_ROUTED:
- { /* move routed child directly */
- remove_child_sa(this, enumerator);
- new->add_child_sa(new, child_sa);
- action = ACTION_NONE;
- break;
- }
- default:
- { /* initiate/queue all other CHILD_SAs */
- action = ACTION_RESTART;
- break;
- }
- }
- }
- else
- { /* only restart CHILD_SAs that are configured accordingly */
- if (this->state == IKE_DELETING)
- {
- action = child_sa->get_close_action(child_sa);
- }
- else
- {
- action = child_sa->get_dpd_action(child_sa);
- }
- }
- switch (action)
- {
- case ACTION_RESTART:
- child_cfg = child_sa->get_config(child_sa);
- DBG1(DBG_IKE, "restarting CHILD_SA %s",
- child_cfg->get_name(child_cfg));
- child_cfg->get_ref(child_cfg);
- status = new->initiate(new, child_cfg,
- child_sa->get_reqid(child_sa), NULL, NULL);
- break;
- default:
- continue;
- }
- if (status == DESTROY_ME)
- {
- break;
- }
- }
- enumerator->destroy(enumerator);
- /* adopt any active or queued CHILD-creating tasks */
- if (status != DESTROY_ME)
- {
- task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager;
- other_tasks->adopt_child_tasks(other_tasks, this->task_manager);
- if (new->get_state(new) == IKE_CREATED)
- {
- status = new->initiate(new, NULL, 0, NULL, NULL);
- }
- }
+ status = reestablish_children(this, new,
+ has_condition(this, COND_REAUTHENTICATING));
}
if (status == DESTROY_ME)
@@ -1936,6 +2111,195 @@ METHOD(ike_sa_t, reestablish, status_t,
return status;
}
+/**
+ * Resolve the given gateway ID
+ */
+static host_t *resolve_gateway_id(identification_t *gateway)
+{
+ char gw[BUF_LEN];
+ host_t *addr;
+
+ snprintf(gw, sizeof(gw), "%Y", gateway);
+ gw[sizeof(gw)-1] = '\0';
+ addr = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT);
+ if (!addr)
+ {
+ DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed",
+ gateway);
+ }
+ return addr;
+}
+
+/**
+ * Redirect the current SA to the given target host
+ */
+static bool redirect_established(private_ike_sa_t *this, identification_t *to)
+{
+ private_ike_sa_t *new_priv;
+ ike_sa_t *new;
+ host_t *other;
+ time_t redirect;
+
+ new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ this->version, TRUE);
+ if (!new)
+ {
+ return FALSE;
+ }
+ new_priv = (private_ike_sa_t*)new;
+ new->set_peer_cfg(new, this->peer_cfg);
+ new_priv->redirected_from = this->other_host->clone(this->other_host);
+ charon->bus->ike_reestablish_pre(charon->bus, &this->public, new);
+ other = resolve_gateway_id(to);
+ if (other)
+ {
+ set_my_host(new_priv, this->my_host->clone(this->my_host));
+ /* this allows us to force the remote address while we still properly
+ * resolve the local address */
+ new_priv->remote_host = other;
+ resolve_hosts(new_priv);
+ new_priv->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS);
+ while (array_remove(this->redirected_at, ARRAY_HEAD, &redirect))
+ {
+ array_insert(new_priv->redirected_at, ARRAY_TAIL, &redirect);
+ }
+ if (reestablish_children(this, new, TRUE) != DESTROY_ME)
+ {
+#ifdef USE_IKEV2
+ new->queue_task(new, (task_t*)ike_reauth_complete_create(new,
+ this->ike_sa_id));
+#endif
+ charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+ TRUE);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
+ charon->bus->set_sa(charon->bus, &this->public);
+ return TRUE;
+ }
+ }
+ charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+ FALSE);
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new);
+ charon->bus->set_sa(charon->bus, &this->public);
+ return FALSE;
+}
+
+/**
+ * Redirect the current connecting SA to the given target host
+ */
+static bool redirect_connecting(private_ike_sa_t *this, identification_t *to)
+{
+ host_t *other;
+
+ other = resolve_gateway_id(to);
+ if (!other)
+ {
+ return FALSE;
+ }
+ reset(this);
+ DESTROY_IF(this->redirected_from);
+ this->redirected_from = this->other_host->clone(this->other_host);
+ DESTROY_IF(this->remote_host);
+ /* this allows us to force the remote address while we still properly
+ * resolve the local address */
+ this->remote_host = other;
+ resolve_hosts(this);
+ return TRUE;
+}
+
+/**
+ * Check if the current redirect exceeds the limits for redirects
+ */
+static bool redirect_count_exceeded(private_ike_sa_t *this)
+{
+ time_t now, redirect;
+
+ now = time_monotonic(NULL);
+ /* remove entries outside the defined period */
+ while (array_get(this->redirected_at, ARRAY_HEAD, &redirect) &&
+ now - redirect >= REDIRECT_LOOP_DETECT_PERIOD)
+ {
+ array_remove(this->redirected_at, ARRAY_HEAD, NULL);
+ }
+ if (array_count(this->redirected_at) < MAX_REDIRECTS)
+ {
+ if (!this->redirected_at)
+ {
+ this->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS);
+ }
+ array_insert(this->redirected_at, ARRAY_TAIL, &now);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+METHOD(ike_sa_t, handle_redirect, bool,
+ private_ike_sa_t *this, identification_t *gateway)
+{
+ DBG1(DBG_IKE, "redirected to %Y", gateway);
+ if (!this->follow_redirects)
+ {
+ DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it");
+ return FALSE;
+ }
+ if (redirect_count_exceeded(this))
+ {
+ DBG1(DBG_IKE, "only %d redirects are allowed within %d seconds",
+ MAX_REDIRECTS, REDIRECT_LOOP_DETECT_PERIOD);
+ return FALSE;
+ }
+
+ switch (this->state)
+ {
+ case IKE_CONNECTING:
+ return redirect_connecting(this, gateway);
+ case IKE_ESTABLISHED:
+ return redirect_established(this, gateway);
+ default:
+ DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N",
+ ike_sa_state_names, this->state);
+ return FALSE;
+ }
+}
+
+METHOD(ike_sa_t, redirect, status_t,
+ private_ike_sa_t *this, identification_t *gateway)
+{
+ switch (this->state)
+ {
+ case IKE_CONNECTING:
+ case IKE_ESTABLISHED:
+ case IKE_REKEYING:
+ if (has_condition(this, COND_REDIRECTED))
+ { /* IKE_SA already got redirected */
+ return SUCCESS;
+ }
+ if (has_condition(this, COND_ORIGINAL_INITIATOR))
+ {
+ DBG1(DBG_IKE, "unable to redirect IKE_SA as initiator");
+ return FAILED;
+ }
+ if (this->version == IKEV1)
+ {
+ DBG1(DBG_IKE, "unable to redirect IKEv1 SA");
+ return FAILED;
+ }
+ if (!supports_extension(this, EXT_IKE_REDIRECTION))
+ {
+ DBG1(DBG_IKE, "client does not support IKE redirection");
+ return FAILED;
+ }
+#ifdef USE_IKEV2
+ this->task_manager->queue_task(this->task_manager,
+ (task_t*)ike_redirect_create(&this->public, gateway));
+#endif
+ return this->task_manager->initiate(this->task_manager);
+ default:
+ DBG1(DBG_IKE, "unable to redirect IKE_SA in state %N",
+ ike_sa_state_names, this->state);
+ return INVALID_STATE;
+ }
+}
+
METHOD(ike_sa_t, retransmit, status_t,
private_ike_sa_t *this, u_int32_t message_id)
{
@@ -2067,8 +2431,8 @@ static bool is_current_path_valid(private_ike_sa_t *this)
{
bool valid = FALSE;
host_t *src;
- src = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
- this->other_host, this->my_host);
+ src = charon->kernel->get_source_addr(charon->kernel, this->other_host,
+ this->my_host);
if (src)
{
if (src->ip_equals(src, this->my_host))
@@ -2112,8 +2476,7 @@ static bool is_any_path_valid(private_ike_sa_t *this)
continue;
}
DBG1(DBG_IKE, "looking for a route to %H ...", addr);
- src = hydra->kernel_interface->get_source_addr(
- hydra->kernel_interface, addr, NULL);
+ src = charon->kernel->get_source_addr(charon->kernel, addr, NULL);
if (src)
{
break;
@@ -2323,7 +2686,7 @@ METHOD(ike_sa_t, inherit_post, void,
this->conditions = other->conditions;
if (this->conditions & COND_NAT_HERE)
{
- send_keepalive(this);
+ send_keepalive(this, FALSE);
}
#ifdef ME
@@ -2401,7 +2764,7 @@ METHOD(ike_sa_t, destroy, void,
}
while (array_remove(this->my_vips, ARRAY_TAIL, &vip))
{
- hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE);
+ charon->kernel->del_ip(charon->kernel, vip, -1, TRUE);
vip->destroy(vip);
}
if (array_count(this->other_vips))
@@ -2450,6 +2813,8 @@ METHOD(ike_sa_t, destroy, void,
DESTROY_IF(this->other_id);
DESTROY_IF(this->local_host);
DESTROY_IF(this->remote_host);
+ DESTROY_IF(this->redirected_from);
+ array_destroy(this->redirected_at);
DESTROY_IF(this->ike_cfg);
DESTROY_IF(this->peer_cfg);
@@ -2498,6 +2863,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
.set_peer_cfg = _set_peer_cfg,
.get_auth_cfg = _get_auth_cfg,
.create_auth_cfg_enumerator = _create_auth_cfg_enumerator,
+ .verify_peer_certificate = _verify_peer_certificate,
.add_auth_cfg = _add_auth_cfg,
.get_proposal = _get_proposal,
.set_proposal = _set_proposal,
@@ -2529,6 +2895,9 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
.destroy = _destroy,
.send_dpd = _send_dpd,
.send_keepalive = _send_keepalive,
+ .redirect = _redirect,
+ .handle_redirect = _handle_redirect,
+ .get_redirected_from = _get_redirected_from,
.get_keymat = _get_keymat,
.add_child_sa = _add_child_sa,
.get_child_sa = _get_child_sa,
@@ -2594,6 +2963,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
"%s.flush_auth_cfg", FALSE, lib->ns),
.fragment_size = lib->settings->get_int(lib->settings,
"%s.fragment_size", 0, lib->ns),
+ .follow_redirects = lib->settings->get_bool(lib->settings,
+ "%s.follow_redirects", TRUE, lib->ns),
);
if (version == IKEV2)
diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h
index 9dbc805c9..836360e3c 100644
--- a/src/libcharon/sa/ike_sa.h
+++ b/src/libcharon/sa/ike_sa.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2014 Tobias Brunner
+ * Copyright (C) 2006-2015 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -66,6 +66,16 @@ typedef struct ike_sa_t ike_sa_t;
#define RETRY_JITTER 20
/**
+ * Number of redirects allowed within REDIRECT_LOOP_DETECT_PERIOD.
+ */
+#define MAX_REDIRECTS 5
+
+/**
+ * Time period in seconds in which at most MAX_REDIRECTS are allowed.
+ */
+#define REDIRECT_LOOP_DETECT_PERIOD 300
+
+/**
* Extensions (or optional features) the peer supports
*/
enum ike_extension_t {
@@ -136,6 +146,11 @@ enum ike_extension_t {
* Signature Authentication, RFC 7427
*/
EXT_SIGNATURE_AUTH = (1<<12),
+
+ /**
+ * IKEv2 Redirect Mechanism, RFC 5685
+ */
+ EXT_IKE_REDIRECTION = (1<<13),
};
/**
@@ -197,6 +212,16 @@ enum ike_condition_t {
* This IKE_SA is currently being reauthenticated
*/
COND_REAUTHENTICATING = (1<<10),
+
+ /**
+ * This IKE_SA has been redirected
+ */
+ COND_REDIRECTED = (1<<11),
+
+ /**
+ * Online certificate revocation checking is suspended for this IKE_SA
+ */
+ COND_ONLINE_VALIDATION_SUSPENDED = (1<<12),
};
/**
@@ -502,6 +527,14 @@ struct ike_sa_t {
enumerator_t* (*create_auth_cfg_enumerator)(ike_sa_t *this, bool local);
/**
+ * Verify the trustchains (validity, revocation) in completed public key
+ * auth rounds.
+ *
+ * @return TRUE if certificates were valid, FALSE otherwise
+ */
+ bool (*verify_peer_certificate)(ike_sa_t *this);
+
+ /**
* Get the selected proposal of this IKE_SA.
*
* @return selected proposal
@@ -837,8 +870,36 @@ struct ike_sa_t {
*
* To refresh NAT tables in a NAT router between the peers, periodic empty
* UDP packets are sent if no other traffic was sent.
+ *
+ * @param scheduled if this is a scheduled keepalive
+ */
+ void (*send_keepalive) (ike_sa_t *this, bool scheduled);
+
+ /**
+ * Redirect an active IKE_SA.
+ *
+ * @param gateway gateway ID (IP or FQDN) of the target
+ * @return state, including DESTROY_ME, if this IKE_SA MUST be
+ * destroyed
+ */
+ status_t (*redirect)(ike_sa_t *this, identification_t *gateway);
+
+ /**
+ * Handle a redirect request.
+ *
+ * The behavior is different depending on the state of the IKE_SA.
+ *
+ * @param gateway gateway ID (IP or FQDN) of the target
+ * @return FALSE if redirect not possible, TRUE otherwise
+ */
+ bool (*handle_redirect)(ike_sa_t *this, identification_t *gateway);
+
+ /**
+ * Get the address of the gateway that redirected us.
+ *
+ * @return original gateway address
*/
- void (*send_keepalive) (ike_sa_t *this);
+ host_t *(*get_redirected_from)(ike_sa_t *this);
/**
* Get the keying material of this IKE_SA.
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 4625df5b8..307ea3b4a 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-2015 Tobias Brunner
+ * Copyright (C) 2008-2016 Tobias Brunner
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -17,12 +17,14 @@
*/
#include <string.h>
+#include <inttypes.h>
#include "ike_sa_manager.h"
#include <daemon.h>
#include <sa/ike_sa_id.h>
#include <bus/bus.h>
+#include <threading/thread.h>
#include <threading/condvar.h>
#include <threading/mutex.h>
#include <threading/rwlock.h>
@@ -57,9 +59,9 @@ struct entry_t {
condvar_t *condvar;
/**
- * Is this ike_sa currently checked out?
+ * Thread by which this IKE_SA is currently checked out, if any
*/
- bool checked_out;
+ thread_t *checked_out;
/**
* Does this SA drives out new threads?
@@ -1142,13 +1144,16 @@ METHOD(ike_sa_manager_t, checkout, ike_sa_t*,
entry_t *entry;
u_int segment;
- DBG2(DBG_MGR, "checkout IKE_SA");
+ DBG2(DBG_MGR, "checkout %N SA with SPIs %.16"PRIx64"_i %.16"PRIx64"_r",
+ ike_version_names, ike_sa_id->get_ike_version(ike_sa_id),
+ be64toh(ike_sa_id->get_initiator_spi(ike_sa_id)),
+ be64toh(ike_sa_id->get_responder_spi(ike_sa_id)));
if (get_entry_by_id(this, ike_sa_id, &entry, &segment) == SUCCESS)
{
if (wait_for_entry(this, entry, segment))
{
- entry->checked_out = TRUE;
+ entry->checked_out = thread_current();
ike_sa = entry->ike_sa;
DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out",
ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
@@ -1156,6 +1161,11 @@ METHOD(ike_sa_manager_t, checkout, ike_sa_t*,
unlock_single_segment(this, segment);
}
charon->bus->set_sa(charon->bus, ike_sa);
+
+ if (!ike_sa)
+ {
+ DBG2(DBG_MGR, "IKE_SA checkout not successful");
+ }
return ike_sa;
}
@@ -1228,7 +1238,10 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
id = id->clone(id);
id->switch_initiator(id);
- DBG2(DBG_MGR, "checkout IKE_SA by message");
+ DBG2(DBG_MGR, "checkout %N SA by message with SPIs %.16"PRIx64"_i "
+ "%.16"PRIx64"_r", ike_version_names, id->get_ike_version(id),
+ be64toh(id->get_initiator_spi(id)),
+ be64toh(id->get_responder_spi(id)));
if (id->get_responder_spi(id) == 0 &&
message->get_message_id(message) == 0)
@@ -1269,7 +1282,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
DBG1(DBG_MGR, "ignoring message, failed to hash message");
DESTROY_IF(hasher);
id->destroy(id);
- return NULL;
+ goto out;
}
hasher->destroy(hasher);
@@ -1288,20 +1301,17 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
entry = entry_create();
entry->ike_sa = ike_sa;
entry->ike_sa_id = id;
+ entry->processing = get_message_id_or_hash(message);
+ entry->init_hash = hash;
segment = put_entry(this, entry);
- entry->checked_out = TRUE;
+ entry->checked_out = thread_current();
unlock_single_segment(this, segment);
- entry->processing = get_message_id_or_hash(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));
-
- charon->bus->set_sa(charon->bus, ike_sa);
- return ike_sa;
+ goto out;
}
else
{
@@ -1317,14 +1327,14 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
remove_init_hash(this, hash);
chunk_free(&hash);
id->destroy(id);
- return NULL;
+ goto out;
}
case FAILED:
{ /* we failed to allocate an SPI */
chunk_free(&hash);
id->destroy(id);
DBG1(DBG_MGR, "ignoring message, failed to allocate SPI");
- return NULL;
+ goto out;
}
case ALREADY_DONE:
default:
@@ -1348,7 +1358,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
ike_sa_id_t *ike_id;
ike_id = entry->ike_sa->get_id(entry->ike_sa);
- entry->checked_out = TRUE;
+ entry->checked_out = thread_current();
if (message->get_first_payload_type(message) != PLV1_FRAGMENT &&
message->get_first_payload_type(message) != PLV2_FRAGMENT)
{ /* TODO-FRAG: this fails if there are unencrypted payloads */
@@ -1369,7 +1379,13 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
charon->bus->alert(charon->bus, ALERT_INVALID_IKE_SPI, message);
}
id->destroy(id);
+
+out:
charon->bus->set_sa(charon->bus, ike_sa);
+ if (!ike_sa)
+ {
+ DBG2(DBG_MGR, "IKE_SA checkout not successful");
+ }
return ike_sa;
}
@@ -1385,11 +1401,11 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
DBG2(DBG_MGR, "checkout IKE_SA by config");
- if (!this->reuse_ikesa)
- { /* IKE_SA reuse disable by config */
+ if (!this->reuse_ikesa && peer_cfg->get_ike_version(peer_cfg) != IKEV1)
+ { /* IKE_SA reuse disabled by config (not possible for IKEv1) */
ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE);
charon->bus->set_sa(charon->bus, ike_sa);
- return ike_sa;
+ goto out;
}
enumerator = create_table_enumerator(this);
@@ -1411,7 +1427,7 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
current_ike = current_peer->get_ike_cfg(current_peer);
if (current_ike->equals(current_ike, peer_cfg->get_ike_cfg(peer_cfg)))
{
- entry->checked_out = TRUE;
+ entry->checked_out = thread_current();
ike_sa = entry->ike_sa;
DBG2(DBG_MGR, "found existing IKE_SA %u with a '%s' config",
ike_sa->get_unique_id(ike_sa),
@@ -1429,6 +1445,12 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
ike_sa = checkout_new(this, peer_cfg->get_ike_version(peer_cfg), TRUE);
}
charon->bus->set_sa(charon->bus, ike_sa);
+
+out:
+ if (!ike_sa)
+ {
+ DBG2(DBG_MGR, "IKE_SA checkout not successful");
+ }
return ike_sa;
}
@@ -1440,7 +1462,7 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*,
ike_sa_t *ike_sa = NULL;
u_int segment;
- DBG2(DBG_MGR, "checkout IKE_SA by ID %u", id);
+ DBG2(DBG_MGR, "checkout IKE_SA by unique ID %u", id);
enumerator = create_table_enumerator(this);
while (enumerator->enumerate(enumerator, &entry, &segment))
@@ -1450,7 +1472,7 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*,
if (entry->ike_sa->get_unique_id(entry->ike_sa) == id)
{
ike_sa = entry->ike_sa;
- entry->checked_out = TRUE;
+ entry->checked_out = thread_current();
break;
}
/* other threads might be waiting for this entry */
@@ -1464,6 +1486,10 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*,
DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out",
ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
}
+ else
+ {
+ DBG2(DBG_MGR, "IKE_SA checkout not successful");
+ }
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
}
@@ -1477,6 +1503,8 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*,
child_sa_t *child_sa;
u_int segment;
+ DBG2(DBG_MGR, "checkout IKE_SA by%s name '%s'", child ? " child" : "", name);
+
enumerator = create_table_enumerator(this);
while (enumerator->enumerate(enumerator, &entry, &segment))
{
@@ -1506,7 +1534,7 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*,
/* got one, return */
if (ike_sa)
{
- entry->checked_out = TRUE;
+ entry->checked_out = thread_current();
DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out",
ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
break;
@@ -1518,6 +1546,11 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*,
enumerator->destroy(enumerator);
charon->bus->set_sa(charon->bus, ike_sa);
+
+ if (!ike_sa)
+ {
+ DBG2(DBG_MGR, "IKE_SA checkout not successful");
+ }
return ike_sa;
}
@@ -1598,7 +1631,7 @@ METHOD(ike_sa_manager_t, checkin, void,
/* ike_sa_id must be updated */
entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa));
/* signal waiting threads */
- entry->checked_out = FALSE;
+ entry->checked_out = NULL;
entry->processing = -1;
/* check if this SA is half-open */
if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING)
@@ -1623,7 +1656,6 @@ METHOD(ike_sa_manager_t, checkin, void,
entry->other = other->clone(other);
put_half_open(this, entry);
}
- DBG2(DBG_MGR, "check-in of IKE_SA successful.");
entry->condvar->signal(entry->condvar);
}
else
@@ -1639,6 +1671,7 @@ METHOD(ike_sa_manager_t, checkin, void,
}
segment = put_entry(this, entry);
}
+ DBG2(DBG_MGR, "checkin of IKE_SA successful");
/* apply identities for duplicate test */
if ((ike_sa->get_state(ike_sa) == IKE_ESTABLISHED ||
@@ -1657,7 +1690,7 @@ METHOD(ike_sa_manager_t, checkin, void,
* thread can acquire it. Since it is not yet in the list of
* connected peers that will not cause a deadlock as no other
* caller of check_unqiueness() will try to check out this SA */
- entry->checked_out = TRUE;
+ entry->checked_out = thread_current();
unlock_single_segment(this, segment);
this->public.check_uniqueness(&this->public, ike_sa, TRUE);
@@ -1668,7 +1701,7 @@ METHOD(ike_sa_manager_t, checkin, void,
* thread is waiting, but it should still exist, so there is no
* need for a lookup via get_entry_by... */
lock_single_segment(this, segment);
- entry->checked_out = FALSE;
+ entry->checked_out = NULL;
/* We already signaled waiting threads above, we have to do that
* again after checking the SA out and back in again. */
entry->condvar->signal(entry->condvar);
@@ -1711,8 +1744,8 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
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;
+ DBG2(DBG_MGR, "ignored checkin and destroy of IKE_SA during shutdown");
+ entry->checked_out = NULL;
entry->condvar->broadcast(entry->condvar);
unlock_single_segment(this, segment);
return;
@@ -1748,11 +1781,11 @@ METHOD(ike_sa_manager_t, checkin_and_destroy, void,
entry_destroy(entry);
- DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful");
+ DBG2(DBG_MGR, "checkin and destroy of IKE_SA successful");
}
else
{
- DBG1(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA");
+ DBG1(DBG_MGR, "tried to checkin and delete nonexisting IKE_SA");
ike_sa->destroy(ike_sa);
}
charon->bus->set_sa(charon->bus, NULL);
diff --git a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
index 52228ef2e..eee7dd10b 100644
--- a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
+++ b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
@@ -173,13 +173,13 @@ METHOD(authenticator_t, process, status_t,
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);
+ id, auth, TRUE);
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);
+ id, signature_scheme_names, scheme);
status = SUCCESS;
auth->merge(auth, current_auth, FALSE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c
index b7047e8fc..c968b2a9c 100644
--- a/src/libcharon/sa/ikev1/phase1.c
+++ b/src/libcharon/sa/ikev1/phase1.c
@@ -404,7 +404,7 @@ static auth_method_t get_pubkey_method(private_phase1_t *this, auth_cfg_t *auth)
id = (identification_t*)auth->get(auth, AUTH_RULE_IDENTITY);
if (id)
{
- private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth);
+ private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, NULL);
if (private)
{
switch (private->get_type(private))
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
index b8af6f67b..cb1a31371 100644
--- a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
@@ -41,7 +41,6 @@
#include <string.h>
-#include <hydra.h>
#include <daemon.h>
#include <sa/ikev1/keymat_v1.h>
#include <config/peer_cfg.h>
@@ -104,7 +103,7 @@ static bool force_encap(ike_cfg_t *ike_cfg)
{
if (!ike_cfg->force_encap(ike_cfg))
{
- return hydra->kernel_interface->get_features(hydra->kernel_interface) &
+ return charon->kernel->get_features(charon->kernel) &
KERNEL_REQUIRE_UDP_ENCAPSULATION;
}
return TRUE;
diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c
index a03477e18..b9f924009 100644
--- a/src/libcharon/sa/ikev1/tasks/mode_config.c
+++ b/src/libcharon/sa/ikev1/tasks/mode_config.c
@@ -76,35 +76,20 @@ typedef struct {
*/
static configuration_attribute_t *build_vip(host_t *vip)
{
- configuration_attribute_type_t type;
- chunk_t chunk, prefix;
+ configuration_attribute_type_t type = INTERNAL_IP4_ADDRESS;
+ chunk_t chunk;
- if (vip->get_family(vip) == AF_INET)
+ if (vip->get_family(vip) == AF_INET6)
{
- type = INTERNAL_IP4_ADDRESS;
- if (vip->is_anyaddr(vip))
- {
- chunk = chunk_empty;
- }
- else
- {
- chunk = vip->get_address(vip);
- }
+ type = INTERNAL_IP6_ADDRESS;
+ }
+ if (vip->is_anyaddr(vip))
+ {
+ chunk = chunk_empty;
}
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);
- }
+ chunk = vip->get_address(vip);
}
return configuration_attribute_create_chunk(PLV1_CONFIGURATION_ATTRIBUTE,
type, chunk);
@@ -165,8 +150,8 @@ static void process_attribute(private_mode_config_t *this,
}
else
{
- /* skip prefix byte in IPv6 payload*/
- if (family == AF_INET6)
+ /* skip prefix byte in IPv6 payload sent by older releases */
+ if (family == AF_INET6 && addr.len == 17)
{
addr.len--;
}
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index e7d26443b..b4fe04663 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -171,6 +171,11 @@ struct private_quick_mode_t {
u_int32_t rekey;
/**
+ * Delete old child after successful rekey
+ */
+ bool delete;
+
+ /**
* Negotiated mode, tunnel or transport
*/
ipsec_mode_t mode;
@@ -406,8 +411,17 @@ static bool install(private_quick_mode_t *this)
if (old)
{
charon->bus->child_rekey(charon->bus, old, this->child_sa);
- /* rekeyed CHILD_SAs stay installed until they expire */
+ /* rekeyed CHILD_SAs stay installed until they expire or are deleted
+ * by the other peer */
old->set_state(old, CHILD_REKEYED);
+ /* as initiator we delete the CHILD_SA if configured to do so */
+ if (this->initiator && this->delete)
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)quick_delete_create(this->ike_sa,
+ this->proposal->get_protocol(this->proposal),
+ this->rekey, TRUE, FALSE));
+ }
}
else
{
@@ -1450,6 +1464,8 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
.tsi = tsi ? tsi->clone(tsi) : NULL,
.tsr = tsr ? tsr->clone(tsr) : NULL,
.proto = PROTO_ESP,
+ .delete = lib->settings->get_bool(lib->settings,
+ "%s.delete_rekeyed", FALSE, lib->ns),
);
if (config)
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c
index c0c91574c..ecdfc780d 100644
--- a/src/libcharon/sa/ikev1/tasks/xauth.c
+++ b/src/libcharon/sa/ikev1/tasks/xauth.c
@@ -16,7 +16,6 @@
#include "xauth.h"
#include <daemon.h>
-#include <hydra.h>
#include <encoding/payloads/cp_payload.h>
#include <processing/jobs/adopt_children_job.h>
#include <sa/ikev1/tasks/mode_config.h>
diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
index 2284a484d..04ccd4f4f 100644
--- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
@@ -55,11 +55,6 @@ struct private_pubkey_authenticator_t {
* Reserved bytes of ID payload
*/
char reserved[3];
-
- /**
- * Whether to store signature schemes on remote auth configs.
- */
- bool store_signature_scheme;
};
/**
@@ -130,7 +125,7 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat,
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &rule, &config))
{
- if (rule != AUTH_RULE_SIGNATURE_SCHEME)
+ if (rule != AUTH_RULE_IKE_SIGNATURE_SCHEME)
{
continue;
}
@@ -369,6 +364,8 @@ METHOD(authenticator_t, process, status_t,
signature_scheme_t scheme;
status_t status = NOT_FOUND;
keymat_v2_t *keymat;
+ const char *reason = "unsupported";
+ bool online;
auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH);
if (!auth_payload)
@@ -397,8 +394,11 @@ METHOD(authenticator_t, process, status_t,
{
break;
}
+ reason = "payload invalid";
/* fall-through */
default:
+ DBG1(DBG_IKE, "%N authentication %s", auth_method_names,
+ auth_method, reason);
return INVALID_ARG;
}
id = this->ike_sa->get_other_id(this->ike_sa);
@@ -409,8 +409,10 @@ METHOD(authenticator_t, process, status_t,
return FAILED;
}
auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+ online = !this->ike_sa->has_condition(this->ike_sa,
+ COND_ONLINE_VALIDATION_SUSPENDED);
enumerator = lib->credmgr->create_public_enumerator(lib->credmgr,
- key_type, id, auth);
+ key_type, id, auth, online);
while (enumerator->enumerate(enumerator, &public, &current_auth))
{
if (public->verify(public, scheme, octets, auth_data))
@@ -421,9 +423,10 @@ METHOD(authenticator_t, process, status_t,
status = SUCCESS;
auth->merge(auth, current_auth, FALSE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
- if (this->store_signature_scheme)
+ auth->add(auth, AUTH_RULE_IKE_SIGNATURE_SCHEME, (uintptr_t)scheme);
+ if (!online)
{
- auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, (uintptr_t)scheme);
+ auth->add(auth, AUTH_RULE_CERT_VALIDATION_SUSPENDED, TRUE);
}
break;
}
@@ -497,8 +500,6 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa,
.ike_sa = ike_sa,
.ike_sa_init = received_init,
.nonce = sent_nonce,
- .store_signature_scheme = lib->settings->get_bool(lib->settings,
- "%s.signature_authentication_constraints", TRUE, lib->ns),
);
memcpy(this->reserved, reserved, sizeof(this->reserved));
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index 4676867df..c2f972ab1 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2014 Tobias Brunner
+ * Copyright (C) 2007-2015 Tobias Brunner
* Copyright (C) 2007-2010 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -30,10 +30,12 @@
#include <sa/ikev2/tasks/ike_rekey.h>
#include <sa/ikev2/tasks/ike_reauth.h>
#include <sa/ikev2/tasks/ike_reauth_complete.h>
+#include <sa/ikev2/tasks/ike_redirect.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/ike_verify_peer_cert.h>
#include <sa/ikev2/tasks/child_create.h>
#include <sa/ikev2/tasks/child_rekey.h>
#include <sa/ikev2/tasks/child_delete.h>
@@ -474,6 +476,11 @@ METHOD(task_manager_t, initiate, status_t,
exchange = INFORMATIONAL;
break;
}
+ if (activate_task(this, TASK_IKE_REDIRECT))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
if (activate_task(this, TASK_CHILD_DELETE))
{
exchange = INFORMATIONAL;
@@ -521,6 +528,11 @@ METHOD(task_manager_t, initiate, status_t,
exchange = INFORMATIONAL;
break;
}
+ if (activate_task(this, TASK_IKE_VERIFY_PEER_CERT))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
case IKE_REKEYING:
if (activate_task(this, TASK_IKE_DELETE))
{
@@ -618,7 +630,7 @@ METHOD(task_manager_t, initiate, status_t,
if (this->initiating.type == EXCHANGE_TYPE_UNDEFINED)
{
message->destroy(message);
- return SUCCESS;
+ return initiate(this);
}
if (!generate_message(this, message, &this->initiating.packets))
@@ -656,6 +668,32 @@ static status_t process_response(private_task_manager_t *this,
return DESTROY_ME;
}
+ enumerator = array_create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (!task->pre_process)
+ {
+ continue;
+ }
+ switch (task->pre_process(task, message))
+ {
+ case SUCCESS:
+ break;
+ case FAILED:
+ default:
+ /* just ignore the message */
+ DBG1(DBG_IKE, "ignore invalid %N response",
+ exchange_type_names, message->get_exchange_type(message));
+ enumerator->destroy(enumerator);
+ return SUCCESS;
+ case DESTROY_ME:
+ /* critical failure, destroy IKE_SA */
+ enumerator->destroy(enumerator);
+ return DESTROY_ME;
+ }
+ }
+ enumerator->destroy(enumerator);
+
/* catch if we get resetted while processing */
this->reset = FALSE;
enumerator = array_create_enumerator(this->active_tasks);
@@ -992,6 +1030,11 @@ static status_t process_request(private_task_manager_t *this,
* invokes all the required hooks. */
task = (task_t*)ike_delete_create(
this->ike_sa, FALSE);
+ break;
+ case REDIRECT:
+ task = (task_t*)ike_redirect_create(
+ this->ike_sa, NULL);
+ break;
default:
break;
}
@@ -1041,6 +1084,44 @@ static status_t process_request(private_task_manager_t *this,
}
}
+ enumerator = array_create_enumerator(this->passive_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (!task->pre_process)
+ {
+ continue;
+ }
+ switch (task->pre_process(task, message))
+ {
+ case SUCCESS:
+ break;
+ case FAILED:
+ default:
+ /* just ignore the message */
+ DBG1(DBG_IKE, "ignore invalid %N request",
+ exchange_type_names, message->get_exchange_type(message));
+ enumerator->destroy(enumerator);
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ /* no point in keeping the SA when it was created with
+ * an invalid IKE_SA_INIT message */
+ return DESTROY_ME;
+ default:
+ /* remove tasks we queued for this request */
+ flush_queue(this, TASK_QUEUE_PASSIVE);
+ /* fall-through */
+ case IKE_AUTH:
+ return NEED_MORE;
+ }
+ case DESTROY_ME:
+ /* critical failure, destroy IKE_SA */
+ enumerator->destroy(enumerator);
+ return DESTROY_ME;
+ }
+ }
+ enumerator->destroy(enumerator);
+
/* let the tasks process the message */
enumerator = array_create_enumerator(this->passive_tasks);
while (enumerator->enumerate(enumerator, (void*)&task))
@@ -1331,12 +1412,17 @@ METHOD(task_manager_t, process_message, status_t,
{ /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
return SUCCESS;
}
- if (process_request(this, msg) != SUCCESS)
+ switch (process_request(this, msg))
{
- flush(this);
- return DESTROY_ME;
+ case SUCCESS:
+ this->responding.mid++;
+ break;
+ case NEED_MORE:
+ break;
+ default:
+ flush(this);
+ return DESTROY_ME;
}
- this->responding.mid++;
}
else if ((mid == this->responding.mid - 1) &&
array_count(this->responding.packets))
@@ -1570,8 +1656,12 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
}
enumerator->destroy(enumerator);
+ /* suspend online revocation checking until the SA is established */
+ new->set_condition(new, COND_ONLINE_VALIDATION_SUSPENDED, TRUE);
+
if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME)
{
+ new->queue_task(new, (task_t*)ike_verify_peer_cert_create(new));
new->queue_task(new, (task_t*)ike_reauth_complete_create(new,
this->ike_sa->get_id(this->ike_sa)));
charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index 97f73d851..3d4ded944 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -18,7 +18,6 @@
#include "child_create.h"
#include <daemon.h>
-#include <hydra.h>
#include <sa/ikev2/keymat_v2.h>
#include <crypto/diffie_hellman.h>
#include <credentials/certificates/x509.h>
@@ -786,7 +785,7 @@ static bool build_payloads(private_child_create_t *this, message_t *message)
break;
}
- features = hydra->kernel_interface->get_features(hydra->kernel_interface);
+ features = charon->kernel->get_features(charon->kernel);
if (!(features & KERNEL_ESP_V3_TFC))
{
message->add_notify(message, FALSE, ESP_TFC_PADDING_NOT_SUPPORTED,
@@ -1221,6 +1220,10 @@ METHOD(task_t, build_r, status_t,
{ /* wait until all authentication round completed */
return NEED_MORE;
}
+ if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED))
+ { /* no CHILD_SA is created for redirected SAs */
+ return SUCCESS;
+ }
ike_auth = TRUE;
default:
break;
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index c7a8a1342..6f0c2b2c7 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -279,11 +279,15 @@ static child_sa_t *handle_collision(private_child_rekey_t *this)
/* don't touch child other created, it has already been deleted */
if (!this->other_child_destroyed)
{
- /* disable close action for the redundand child */
+ /* disable close action and updown event for redundant child */
child_sa = other->child_create->get_child(other->child_create);
if (child_sa)
{
child_sa->set_close_action(child_sa, ACTION_NONE);
+ if (child_sa->get_state(child_sa) != CHILD_REKEYING)
+ {
+ child_sa->set_state(child_sa, CHILD_REKEYING);
+ }
}
}
}
@@ -372,6 +376,11 @@ METHOD(task_t, process_i, status_t,
{
return SUCCESS;
}
+ /* disable updown event for redundant CHILD_SA */
+ if (to_delete->get_state(to_delete) != CHILD_REKEYING)
+ {
+ to_delete->set_state(to_delete, CHILD_REKEYING);
+ }
spi = to_delete->get_spi(to_delete, TRUE);
protocol = to_delete->get_protocol(to_delete);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c
index 2554496c1..79a436fbf 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_auth.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -25,6 +25,7 @@
#include <encoding/payloads/eap_payload.h>
#include <encoding/payloads/nonce_payload.h>
#include <sa/ikev2/authenticators/eap_authenticator.h>
+#include <processing/jobs/delete_ike_sa_job.h>
typedef struct private_ike_auth_t private_ike_auth_t;
@@ -117,6 +118,11 @@ struct private_ike_auth_t {
* Is EAP acceptable, did we strictly authenticate peer?
*/
bool eap_acceptable;
+
+ /**
+ * Gateway ID if redirected
+ */
+ identification_t *redirect_to;
};
/**
@@ -685,6 +691,7 @@ METHOD(task_t, process_r, status_t,
METHOD(task_t, build_r, status_t,
private_ike_auth_t *this, message_t *message)
{
+ identification_t *gateway;
auth_cfg_t *cfg;
if (message->get_exchange_type(message) == IKE_SA_INIT)
@@ -817,34 +824,56 @@ METHOD(task_t, build_r, status_t,
{
this->do_another_auth = FALSE;
}
- if (!this->do_another_auth && !this->expect_another_auth)
+ if (this->do_another_auth || this->expect_another_auth)
{
- if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
- this->ike_sa, FALSE))
- {
- DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy");
- charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
- message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
- chunk_empty);
- return FAILED;
- }
- if (!charon->bus->authorize(charon->bus, TRUE))
- {
- DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
- goto peer_auth_failed;
- }
- DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
- this->ike_sa->get_name(this->ike_sa),
- this->ike_sa->get_unique_id(this->ike_sa),
- this->ike_sa->get_my_host(this->ike_sa),
- this->ike_sa->get_my_id(this->ike_sa),
- this->ike_sa->get_other_host(this->ike_sa),
- this->ike_sa->get_other_id(this->ike_sa));
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
- charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
- return SUCCESS;
+ return NEED_MORE;
}
- return NEED_MORE;
+
+ if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+ this->ike_sa, FALSE))
+ {
+ DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy");
+ charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
+ chunk_empty);
+ return FAILED;
+ }
+ if (!charon->bus->authorize(charon->bus, TRUE))
+ {
+ DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
+ goto peer_auth_failed;
+ }
+ if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
+ charon->redirect->redirect_on_auth(charon->redirect, this->ike_sa,
+ &gateway))
+ {
+ delete_ike_sa_job_t *job;
+ chunk_t data;
+
+ DBG1(DBG_IKE, "redirecting peer to %Y", gateway);
+ data = redirect_data_create(gateway, chunk_empty);
+ message->add_notify(message, FALSE, REDIRECT, data);
+ gateway->destroy(gateway);
+ chunk_free(&data);
+ /* we use this condition to prevent the CHILD_SA from getting created */
+ this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE);
+ /* if the peer does not delete the SA we do so after a while */
+ job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE);
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
+ lib->settings->get_int(lib->settings,
+ "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
+ lib->ns));
+ }
+ DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
+ this->ike_sa->get_name(this->ike_sa),
+ this->ike_sa->get_unique_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
+ return SUCCESS;
peer_auth_failed:
message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
@@ -964,6 +993,15 @@ METHOD(task_t, process_i, status_t,
case ME_ENDPOINT:
/* handled in ike_me task */
break;
+ case REDIRECT:
+ DESTROY_IF(this->redirect_to);
+ this->redirect_to = redirect_data_parse(
+ notify->get_notification_data(notify), NULL);
+ if (!this->redirect_to)
+ {
+ DBG1(DBG_IKE, "received invalid REDIRECT notify");
+ }
+ break;
default:
{
if (type <= 16383)
@@ -1094,30 +1132,35 @@ METHOD(task_t, process_i, status_t,
{
this->expect_another_auth = FALSE;
}
- if (!this->expect_another_auth && !this->do_another_auth && !this->my_auth)
+ if (this->expect_another_auth || this->do_another_auth || this->my_auth)
{
- if (!update_cfg_candidates(this, TRUE))
- {
- goto peer_auth_failed;
- }
- if (!charon->bus->authorize(charon->bus, TRUE))
- {
- DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, "
- "cancelling");
- goto peer_auth_failed;
- }
- DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
- this->ike_sa->get_name(this->ike_sa),
- this->ike_sa->get_unique_id(this->ike_sa),
- this->ike_sa->get_my_host(this->ike_sa),
- this->ike_sa->get_my_id(this->ike_sa),
- this->ike_sa->get_other_host(this->ike_sa),
- this->ike_sa->get_other_id(this->ike_sa));
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
- charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
- return SUCCESS;
+ return NEED_MORE;
}
- return NEED_MORE;
+ if (!update_cfg_candidates(this, TRUE))
+ {
+ goto peer_auth_failed;
+ }
+ if (!charon->bus->authorize(charon->bus, TRUE))
+ {
+ DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, "
+ "cancelling");
+ goto peer_auth_failed;
+ }
+ DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
+ this->ike_sa->get_name(this->ike_sa),
+ this->ike_sa->get_unique_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
+
+ if (this->redirect_to)
+ {
+ this->ike_sa->handle_redirect(this->ike_sa, this->redirect_to);
+ }
+ return SUCCESS;
peer_auth_failed:
charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED);
@@ -1141,6 +1184,7 @@ METHOD(task_t, migrate, void,
DESTROY_IF(this->peer_cfg);
DESTROY_IF(this->my_auth);
DESTROY_IF(this->other_auth);
+ DESTROY_IF(this->redirect_to);
this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
this->my_packet = NULL;
@@ -1149,6 +1193,7 @@ METHOD(task_t, migrate, void,
this->peer_cfg = NULL;
this->my_auth = NULL;
this->other_auth = NULL;
+ this->redirect_to = NULL;
this->do_another_auth = TRUE;
this->expect_another_auth = TRUE;
this->authentication_failed = FALSE;
@@ -1165,6 +1210,7 @@ METHOD(task_t, destroy, void,
DESTROY_IF(this->my_auth);
DESTROY_IF(this->other_auth);
DESTROY_IF(this->peer_cfg);
+ DESTROY_IF(this->redirect_to);
this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
free(this);
}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c
index 646f20c61..6c42b81a6 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_config.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_config.c
@@ -333,6 +333,11 @@ METHOD(task_t, build_r, status_t,
linked_list_t *vips, *pools;
host_t *requested;
+ if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED))
+ { /* don't assign attributes for redirected SAs */
+ return SUCCESS;
+ }
+
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();
diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c
index 1ff643d62..78579be95 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_init.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_init.c
@@ -118,6 +118,11 @@ struct private_ike_init_t {
* Whether to use Signature Authentication as per RFC 7427
*/
bool signature_authentication;
+
+ /**
+ * Whether to follow IKEv2 redirects as per RFC 5685
+ */
+ bool follow_redirects;
};
/**
@@ -166,7 +171,7 @@ static void send_supported_hash_algorithms(private_ike_init_t *this,
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &rule, &config))
{
- if (rule == AUTH_RULE_SIGNATURE_SCHEME)
+ if (rule == AUTH_RULE_IKE_SIGNATURE_SCHEME)
{
hash = hasher_from_signature_scheme(config);
if (hasher_algorithm_for_ikev2(hash))
@@ -324,6 +329,29 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
send_supported_hash_algorithms(this, message);
}
}
+ /* notify other peer if we support redirection */
+ if (!this->old_sa && this->initiator && this->follow_redirects)
+ {
+ identification_t *gateway;
+ host_t *from;
+ chunk_t data;
+
+ from = this->ike_sa->get_redirected_from(this->ike_sa);
+ if (from)
+ {
+ gateway = identification_create_from_sockaddr(
+ from->get_sockaddr(from));
+ data = redirect_data_create(gateway, chunk_empty);
+ message->add_notify(message, FALSE, REDIRECTED_FROM, data);
+ chunk_free(&data);
+ gateway->destroy(gateway);
+ }
+ else
+ {
+ message->add_notify(message, FALSE, REDIRECT_SUPPORTED,
+ chunk_empty);
+ }
+ }
return TRUE;
}
@@ -391,6 +419,30 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
handle_supported_hash_algorithms(this, notify);
}
break;
+ case REDIRECTED_FROM:
+ {
+ identification_t *gateway;
+ chunk_t data;
+
+ data = notify->get_notification_data(notify);
+ gateway = redirect_data_parse(data, NULL);
+ if (!gateway)
+ {
+ DBG1(DBG_IKE, "received invalid REDIRECTED_FROM "
+ "notify, ignored");
+ break;
+ }
+ DBG1(DBG_IKE, "client got redirected from %Y", gateway);
+ gateway->destroy(gateway);
+ /* fall-through */
+ }
+ case REDIRECT_SUPPORTED:
+ if (!this->old_sa)
+ {
+ this->ike_sa->enable_extension(this->ike_sa,
+ EXT_IKE_REDIRECTION);
+ }
+ break;
default:
/* other notifies are handled elsewhere */
break;
@@ -550,6 +602,8 @@ static bool derive_keys(private_ike_init_t *this,
METHOD(task_t, build_r, status_t,
private_ike_init_t *this, message_t *message)
{
+ identification_t *gateway;
+
/* check if we have everything we need */
if (this->proposal == NULL ||
this->other_nonce.len == 0 || this->my_nonce.len == 0)
@@ -560,6 +614,22 @@ METHOD(task_t, build_r, status_t,
}
this->ike_sa->set_proposal(this->ike_sa, this->proposal);
+ /* check if we'd have to redirect the client */
+ if (!this->old_sa &&
+ this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
+ charon->redirect->redirect_on_init(charon->redirect, this->ike_sa,
+ &gateway))
+ {
+ chunk_t data;
+
+ DBG1(DBG_IKE, "redirecting peer to %Y", gateway);
+ data = redirect_data_create(gateway, this->other_nonce);
+ message->add_notify(message, TRUE, REDIRECT, data);
+ gateway->destroy(gateway);
+ chunk_free(&data);
+ return FAILED;
+ }
+
if (this->dh == NULL ||
!this->proposal->has_dh_group(this->proposal, this->dh_group))
{
@@ -623,6 +693,54 @@ static void raise_alerts(private_ike_init_t *this, notify_type_t type)
}
}
+METHOD(task_t, pre_process_i, status_t,
+ private_ike_init_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+
+ /* check for erroneous notifies */
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == PLV2_NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ case REDIRECT:
+ {
+ identification_t *gateway;
+ chunk_t data, nonce = chunk_empty;
+ status_t status = SUCCESS;
+
+ if (this->old_sa)
+ {
+ break;
+ }
+ data = notify->get_notification_data(notify);
+ gateway = redirect_data_parse(data, &nonce);
+ if (!gateway || !chunk_equals(nonce, this->my_nonce))
+ {
+ DBG1(DBG_IKE, "received invalid REDIRECT notify");
+ status = FAILED;
+ }
+ DESTROY_IF(gateway);
+ chunk_free(&nonce);
+ enumerator->destroy(enumerator);
+ return status;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ return SUCCESS;
+}
+
METHOD(task_t, process_i, status_t,
private_ike_init_t *this, message_t *message)
{
@@ -678,6 +796,29 @@ METHOD(task_t, process_i, status_t,
this->retry++;
return NEED_MORE;
}
+ case REDIRECT:
+ {
+ identification_t *gateway;
+ chunk_t data, nonce = chunk_empty;
+ status_t status = FAILED;
+
+ if (this->old_sa)
+ {
+ DBG1(DBG_IKE, "received REDIRECT notify during rekeying"
+ ", ignored");
+ break;
+ }
+ data = notify->get_notification_data(notify);
+ gateway = redirect_data_parse(data, &nonce);
+ if (this->ike_sa->handle_redirect(this->ike_sa, gateway))
+ {
+ status = NEED_MORE;
+ }
+ DESTROY_IF(gateway);
+ chunk_free(&nonce);
+ enumerator->destroy(enumerator);
+ return status;
+ }
default:
{
if (type <= 16383)
@@ -802,6 +943,8 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
.old_sa = old_sa,
.signature_authentication = lib->settings->get_bool(lib->settings,
"%s.signature_authentication", TRUE, lib->ns),
+ .follow_redirects = lib->settings->get_bool(lib->settings,
+ "%s.follow_redirects", TRUE, lib->ns),
);
this->nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
@@ -809,6 +952,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
+ this->public.task.pre_process = _pre_process_i;
}
else
{
diff --git a/src/libcharon/sa/ikev2/tasks/ike_me.c b/src/libcharon/sa/ikev2/tasks/ike_me.c
index a7e7505a1..10d412ffd 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_me.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_me.c
@@ -17,7 +17,6 @@
#include <string.h>
-#include <hydra.h>
#include <daemon.h>
#include <config/peer_cfg.h>
#include <encoding/payloads/id_payload.h>
@@ -135,8 +134,8 @@ static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message)
host = this->ike_sa->get_my_host(this->ike_sa);
port = host->get_port(host);
- enumerator = hydra->kernel_interface->create_address_enumerator(
- hydra->kernel_interface, ADDR_TYPE_REGULAR);
+ enumerator = charon->kernel->create_address_enumerator(charon->kernel,
+ ADDR_TYPE_REGULAR);
while (enumerator->enumerate(enumerator, (void**)&addr))
{
host = addr->clone(addr);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
index cbdc5e797..3f7bb175f 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
@@ -18,7 +18,6 @@
#include <string.h>
-#include <hydra.h>
#include <daemon.h>
#include <sa/ikev2/tasks/ike_natd.h>
#include <encoding/payloads/notify_payload.h>
@@ -196,8 +195,8 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message)
int added = 0;
me = this->ike_sa->get_my_host(this->ike_sa);
- enumerator = hydra->kernel_interface->create_address_enumerator(
- hydra->kernel_interface, ADDR_TYPE_REGULAR);
+ enumerator = charon->kernel->create_address_enumerator(charon->kernel,
+ ADDR_TYPE_REGULAR);
while (enumerator->enumerate(enumerator, (void**)&host))
{
if (me->ip_equals(me, host))
@@ -333,8 +332,7 @@ METHOD(ike_mobike_t, transmit, bool,
if (!this->check)
{
- me = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
- other_old, me_old);
+ me = charon->kernel->get_source_addr(charon->kernel, other_old, me_old);
if (me)
{
if (me->ip_equals(me, me_old))
@@ -372,8 +370,7 @@ METHOD(ike_mobike_t, transmit, bool,
{
continue;
}
- me = hydra->kernel_interface->get_source_addr(
- hydra->kernel_interface, other, NULL);
+ me = charon->kernel->get_source_addr(charon->kernel, other, NULL);
if (me)
{
/* reuse port for an active address, 4500 otherwise */
@@ -407,7 +404,7 @@ METHOD(task_t, build_i, status_t,
/* we check if the existing address is still valid */
old = message->get_source(message);
- new = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
+ new = charon->kernel->get_source_addr(charon->kernel,
message->get_destination(message), old);
if (new)
{
diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c
index dd34c1234..4bf5264dd 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_natd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c
@@ -18,7 +18,6 @@
#include <string.h>
-#include <hydra.h>
#include <daemon.h>
#include <config/peer_cfg.h>
#include <crypto/hashers/hasher.h>
@@ -86,7 +85,7 @@ static bool force_encap(ike_cfg_t *ike_cfg)
{
if (!ike_cfg->force_encap(ike_cfg))
{
- return hydra->kernel_interface->get_features(hydra->kernel_interface) &
+ return charon->kernel->get_features(charon->kernel) &
KERNEL_REQUIRE_UDP_ENCAPSULATION;
}
return TRUE;
@@ -327,7 +326,7 @@ METHOD(task_t, build_i, status_t,
}
else
{
- host = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
+ host = charon->kernel->get_source_addr(charon->kernel,
this->ike_sa->get_other_host(this->ike_sa), NULL);
if (host)
{ /* 2. */
@@ -341,8 +340,8 @@ METHOD(task_t, build_i, status_t,
}
else
{ /* 3. */
- enumerator = hydra->kernel_interface->create_address_enumerator(
- hydra->kernel_interface, ADDR_TYPE_REGULAR);
+ enumerator = charon->kernel->create_address_enumerator(
+ charon->kernel, ADDR_TYPE_REGULAR);
while (enumerator->enumerate(enumerator, (void**)&host))
{
/* apply port 500 to host, but work on a copy */
diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.c b/src/libcharon/sa/ikev2/tasks/ike_redirect.c
new file mode 100644
index 000000000..f82c80f71
--- /dev/null
+++ b/src/libcharon/sa/ikev2/tasks/ike_redirect.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 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 "ike_redirect.h"
+
+#include <daemon.h>
+#include <processing/jobs/delete_ike_sa_job.h>
+
+typedef struct private_ike_redirect_t private_ike_redirect_t;
+
+/**
+ * Private members
+ */
+struct private_ike_redirect_t {
+
+ /**
+ * Public interface
+ */
+ ike_redirect_t public;
+
+ /**
+ * Assigned IKE_SA
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Gateway ID to redirect to
+ */
+ identification_t *gateway;
+};
+
+METHOD(task_t, build_i, status_t,
+ private_ike_redirect_t *this, message_t *message)
+{
+ chunk_t data;
+
+ DBG1(DBG_IKE, "redirecting peer to %Y", this->gateway);
+ data = redirect_data_create(this->gateway, chunk_empty);
+ message->add_notify(message, FALSE, REDIRECT, data);
+ chunk_free(&data);
+ this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE);
+ return NEED_MORE;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_ike_redirect_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ identification_t *to;
+
+ notify = message->get_notify(message, REDIRECT);
+ if (!notify)
+ {
+ return SUCCESS;
+ }
+
+ to = redirect_data_parse(notify->get_notification_data(notify), NULL);
+ if (!to)
+ {
+ DBG1(DBG_IKE, "received invalid REDIRECT notify");
+ }
+ else
+ {
+ this->ike_sa->handle_redirect(this->ike_sa, to);
+ to->destroy(to);
+ }
+ return SUCCESS;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_ike_redirect_t *this, message_t *message)
+{
+ /* not called because SUCCESS is returned above */
+ return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_ike_redirect_t *this, message_t *message)
+{
+ delete_ike_sa_job_t *job;
+
+ /* if the peer does not delete the SA we do so after a while */
+ job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE);
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
+ lib->settings->get_int(lib->settings,
+ "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
+ lib->ns));
+ return SUCCESS;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_ike_redirect_t *this)
+{
+ return TASK_IKE_REDIRECT;
+}
+
+METHOD(task_t, migrate, void,
+ private_ike_redirect_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+ private_ike_redirect_t *this)
+{
+ DESTROY_IF(this->gateway);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa, identification_t *to)
+{
+ private_ike_redirect_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .build = _build_r,
+ .process = _process_r,
+ .migrate = _migrate,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ );
+
+ if (to)
+ {
+ this->gateway = to->clone(to);
+ this->public.task.build = _build_i;
+ this->public.task.process = _process_i;
+ }
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.h b/src/libcharon/sa/ikev2/tasks/ike_redirect.h
new file mode 100644
index 000000000..afa00ce5d
--- /dev/null
+++ b/src/libcharon/sa/ikev2/tasks/ike_redirect.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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 ike_redirect ike_redirect
+ * @{ @ingroup tasks_v2
+ */
+
+#ifndef IKE_REDIRECT_H_
+#define IKE_REDIRECT_H_
+
+typedef struct ike_redirect_t ike_redirect_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task that handles redirection requests for established SAs.
+ */
+struct ike_redirect_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new ike_redirect_t task.
+ *
+ * As initiator (i.e. original responder) pass the ID of the target gateway,
+ * as responder (i.e. original initiator) this argument is NULL.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param to gateway ID (gets cloned), or NULL as responder
+ * @return task instance
+ */
+ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa,
+ identification_t *to);
+
+#endif /** IKE_REDIRECT_H_ @}*/
diff --git a/src/libcharon/sa/ikev2/tasks/ike_vendor.c b/src/libcharon/sa/ikev2/tasks/ike_vendor.c
index cb3c270dc..e85b276e8 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_vendor.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_vendor.c
@@ -13,6 +13,29 @@
* for more details.
*/
+/*
+ * Copyright (C) 2016 secunet Security Networks AG
+ * Copyright (C) 2016 Thomas Egerer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
#include "ike_vendor.h"
#include <daemon.h>
@@ -49,6 +72,8 @@ typedef struct {
char *desc;
/* extension flag negotiated with vendor ID, if any */
ike_extension_t extension;
+ /* Value from strongswan.conf, whether to send vendor ID */
+ char *setting;
/* length of vendor ID string, 0 for NULL terminated */
int len;
/* vendor ID string */
@@ -68,23 +93,23 @@ static chunk_t get_vid_data(vid_data_t *data)
*/
static vid_data_t vids[] = {
/* strongSwan MD5("strongSwan") */
- { "strongSwan", EXT_STRONGSWAN, 16,
+ { "strongSwan", EXT_STRONGSWAN, "send_vendor_id", 16,
"\x88\x2f\xe5\x6d\x6f\xd2\x0d\xbc\x22\x51\x61\x3b\x2e\xbe\x5b\xeb"},
- { "Cisco Delete Reason", 0, 0,
+ { "Cisco Delete Reason", 0, NULL, 0,
"CISCO-DELETE-REASON" },
- { "Cisco Copyright (c) 2009", 0, 0,
+ { "Cisco Copyright (c) 2009", 0, NULL, 0,
"CISCO(COPYRIGHT)&Copyright (c) 2009 Cisco Systems, Inc." },
- { "FRAGMENTATION", 0, 16,
+ { "FRAGMENTATION", 0, NULL, 16,
"\x40\x48\xb7\xd5\x6e\xbc\xe8\x85\x25\xe7\xde\x7f\x00\xd6\xc2\xd3"},
- { "MS NT5 ISAKMPOAKLEY v7", 0, 20,
+ { "MS NT5 ISAKMPOAKLEY v7", 0, NULL, 20,
"\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00\x00\x07"},
- { "MS NT5 ISAKMPOAKLEY v8", 0, 20,
+ { "MS NT5 ISAKMPOAKLEY v8", 0, NULL, 20,
"\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00\x00\x08"},
- { "MS NT5 ISAKMPOAKLEY v9", 0, 20,
+ { "MS NT5 ISAKMPOAKLEY v9", 0, NULL, 20,
"\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00\x00\x09"},
- { "MS-Negotiation Discovery Capable", 0, 16,
+ { "MS-Negotiation Discovery Capable", 0, NULL, 16,
"\xfb\x1d\xe3\xcd\xf3\x41\xb7\xea\x16\xb7\xe5\xbe\x08\x55\xf1\x20"},
- { "Vid-Initial-Contact", 0, 16,
+ { "Vid-Initial-Contact", 0, NULL, 16,
"\x26\x24\x4d\x38\xed\xdb\x61\xb3\x17\x2a\x36\xe3\xd0\xcf\xb8\x19"},
};
@@ -92,14 +117,19 @@ METHOD(task_t, build, status_t,
private_ike_vendor_t *this, message_t *message)
{
vendor_id_payload_t *vid;
- bool strongswan;
+ bool send_vid;
int i;
- strongswan = lib->settings->get_bool(lib->settings,
- "%s.send_vendor_id", FALSE, lib->ns);
for (i = 0; i < countof(vids); i++)
{
- if (vids[i].extension == EXT_STRONGSWAN && strongswan)
+ send_vid = FALSE;
+
+ if (vids[i].setting)
+ {
+ send_vid = lib->settings->get_bool(lib->settings, "%s.%s", send_vid,
+ lib->ns, vids[i].setting);
+ }
+ if (send_vid)
{
DBG2(DBG_IKE, "sending %s vendor ID", vids[i].desc);
vid = vendor_id_payload_create_data(PLV2_VENDOR_ID,
diff --git a/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c
new file mode 100644
index 000000000..069d51d00
--- /dev/null
+++ b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 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 "ike_verify_peer_cert.h"
+
+#include <daemon.h>
+#include <sa/ikev2/tasks/ike_delete.h>
+
+typedef struct private_ike_verify_peer_cert_t private_ike_verify_peer_cert_t;
+
+/**
+ * Private members
+ */
+struct private_ike_verify_peer_cert_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_verify_peer_cert_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Child ike_delete task, if necessary
+ */
+ ike_delete_t *ike_delete;
+};
+
+METHOD(task_t, build_i, status_t,
+ private_ike_verify_peer_cert_t *this, message_t *message)
+{
+ if (!this->ike_sa->verify_peer_certificate(this->ike_sa))
+ {
+ DBG1(DBG_IKE, "peer certificate verification failed, deleting SA");
+ this->ike_delete = ike_delete_create(this->ike_sa, TRUE);
+ return this->ike_delete->task.build(&this->ike_delete->task, message);
+ }
+ DBG1(DBG_IKE, "peer certificate successfully verified");
+ message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
+ return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_ike_verify_peer_cert_t *this, message_t *message)
+{
+ if (this->ike_delete)
+ {
+ this->ike_delete->task.process(&this->ike_delete->task, message);
+ /* try to reestablish the IKE_SA and all children */
+ this->ike_sa->reestablish(this->ike_sa);
+ }
+ return DESTROY_ME;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_ike_verify_peer_cert_t *this)
+{
+ return TASK_IKE_VERIFY_PEER_CERT;
+}
+
+METHOD(task_t, migrate, void,
+ private_ike_verify_peer_cert_t *this, ike_sa_t *ike_sa)
+{
+ if (this->ike_delete)
+ {
+ this->ike_delete->task.migrate(&this->ike_delete->task, ike_sa);
+ }
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+ private_ike_verify_peer_cert_t *this)
+{
+ if (this->ike_delete)
+ {
+ this->ike_delete->task.destroy(&this->ike_delete->task);
+ }
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_verify_peer_cert_t *ike_verify_peer_cert_create(ike_sa_t *ike_sa)
+{
+ private_ike_verify_peer_cert_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .build = _build_i,
+ .process = _process_i,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h
new file mode 100644
index 000000000..3d9aae0b3
--- /dev/null
+++ b/src/libcharon/sa/ikev2/tasks/ike_verify_peer_cert.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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 ike_verify_peer_cert ike_verify_peer_cert
+ * @{ @ingroup tasks_v2
+ */
+
+#ifndef IKE_VERIFY_PEER_CERT_H_
+#define IKE_VERIFY_PEER_CERT_H_
+
+typedef struct ike_verify_peer_cert_t ike_verify_peer_cert_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task of type ike_verify_peer_cert, verifies a peer's certificate.
+ *
+ * This task (re-)verifies the peer's certificate explicitly including online
+ * OCSP and CRL checks.
+ */
+struct ike_verify_peer_cert_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new ike_verify_peer_cert task.
+ *
+ * This task is initiator only.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @return ike_verify_peer_cert task to handle by the task_manager
+ */
+ike_verify_peer_cert_t *ike_verify_peer_cert_create(ike_sa_t *ike_sa);
+
+#endif /** IKE_VERIFY_PEER_CERT_H_ @}*/
diff --git a/src/libcharon/sa/redirect_manager.c b/src/libcharon/sa/redirect_manager.c
new file mode 100644
index 000000000..ff92ac29f
--- /dev/null
+++ b/src/libcharon/sa/redirect_manager.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2015 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 "redirect_manager.h"
+
+#include <collections/linked_list.h>
+#include <threading/rwlock.h>
+#include <bio/bio_reader.h>
+#include <bio/bio_writer.h>
+
+typedef struct private_redirect_manager_t private_redirect_manager_t;
+
+/**
+ * Private data
+ */
+struct private_redirect_manager_t {
+
+ /**
+ * Public interface
+ */
+ redirect_manager_t public;
+
+ /**
+ * Registered providers
+ */
+ linked_list_t *providers;
+
+ /**
+ * Lock to access list of providers
+ */
+ rwlock_t *lock;
+};
+
+
+/**
+ * Gateway identify types
+ *
+ * The encoding is the same as that for corresponding ID payloads.
+ */
+typedef enum {
+ /** IPv4 address of the VPN gateway */
+ GATEWAY_ID_TYPE_IPV4 = 1,
+ /** IPv6 address of the VPN gateway */
+ GATEWAY_ID_TYPE_IPV6 = 2,
+ /** FQDN of the VPN gateway */
+ GATEWAY_ID_TYPE_FQDN = 3,
+} gateway_id_type_t;
+
+/**
+ * Mapping of gateway identity types to identity types
+ */
+static id_type_t gateway_to_id_type(gateway_id_type_t type)
+{
+ switch (type)
+ {
+ case GATEWAY_ID_TYPE_IPV4:
+ return ID_IPV4_ADDR;
+ case GATEWAY_ID_TYPE_IPV6:
+ return ID_IPV6_ADDR;
+ case GATEWAY_ID_TYPE_FQDN:
+ return ID_FQDN;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Mapping of identity types to gateway identity types
+ */
+static gateway_id_type_t id_type_to_gateway(id_type_t type)
+{
+ switch (type)
+ {
+ case ID_IPV4_ADDR:
+ return GATEWAY_ID_TYPE_IPV4;
+ case ID_IPV6_ADDR:
+ return GATEWAY_ID_TYPE_IPV6;
+ case ID_FQDN:
+ return GATEWAY_ID_TYPE_FQDN;
+ default:
+ return 0;
+ }
+}
+
+METHOD(redirect_manager_t, add_provider, void,
+ private_redirect_manager_t *this, redirect_provider_t *provider)
+{
+ this->lock->write_lock(this->lock);
+ this->providers->insert_last(this->providers, provider);
+ this->lock->unlock(this->lock);
+}
+
+METHOD(redirect_manager_t, remove_provider, void,
+ private_redirect_manager_t *this, redirect_provider_t *provider)
+{
+ this->lock->write_lock(this->lock);
+ this->providers->remove(this->providers, provider, NULL);
+ this->lock->unlock(this->lock);
+}
+
+/**
+ * Determine whether a client should be redirected using the callback with the
+ * given offset into the redirect_provider_t interface.
+ */
+static bool should_redirect(private_redirect_manager_t *this, ike_sa_t *ike_sa,
+ identification_t **gateway, size_t offset)
+{
+ enumerator_t *enumerator;
+ void *provider;
+ bool redirect = FALSE;
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->providers->create_enumerator(this->providers);
+ while (enumerator->enumerate(enumerator, &provider))
+ {
+ bool (**method)(void*,ike_sa_t*,identification_t**) = provider + offset;
+ if (*method && (*method)(provider, ike_sa, gateway))
+ {
+ if (*gateway && id_type_to_gateway((*gateway)->get_type(*gateway)))
+ {
+ redirect = TRUE;
+ break;
+ }
+ else
+ {
+ DBG1(DBG_CFG, "redirect provider returned invalid gateway ID");
+ DESTROY_IF(*gateway);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+ return redirect;
+}
+
+METHOD(redirect_manager_t, redirect_on_init, bool,
+ private_redirect_manager_t *this, ike_sa_t *ike_sa,
+ identification_t **gateway)
+{
+ return should_redirect(this, ike_sa, gateway,
+ offsetof(redirect_provider_t, redirect_on_init));
+}
+
+METHOD(redirect_manager_t, redirect_on_auth, bool,
+ private_redirect_manager_t *this, ike_sa_t *ike_sa,
+ identification_t **gateway)
+{
+ return should_redirect(this, ike_sa, gateway,
+ offsetof(redirect_provider_t, redirect_on_auth));
+}
+
+METHOD(redirect_manager_t, destroy, void,
+ private_redirect_manager_t *this)
+{
+ this->providers->destroy(this->providers);
+ this->lock->destroy(this->lock);
+ free(this);
+}
+
+/*
+ * Described in header
+ */
+redirect_manager_t *redirect_manager_create()
+{
+ private_redirect_manager_t *this;
+
+ INIT(this,
+ .public = {
+ .add_provider = _add_provider,
+ .remove_provider = _remove_provider,
+ .redirect_on_init = _redirect_on_init,
+ .redirect_on_auth = _redirect_on_auth,
+ .destroy = _destroy,
+ },
+ .providers = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}
+
+/*
+ * Encoding of a REDIRECT or REDIRECTED_FROM notify
+ *
+ 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Next Payload |C| RESERVED | Payload Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |Protocol ID(=0)| SPI Size (=0) | Notify Message Type |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | GW Ident Type | GW Ident Len | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~
+ ~ New Responder GW Identity ~
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ ~ Nonce Data ~
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+/*
+ * Described in header
+ */
+chunk_t redirect_data_create(identification_t *gw, chunk_t nonce)
+{
+ gateway_id_type_t type;
+ bio_writer_t *writer;
+ chunk_t data;
+
+ type = id_type_to_gateway(gw->get_type(gw));
+ if (!type)
+ {
+ return chunk_empty;
+ }
+
+ writer = bio_writer_create(0);
+ writer->write_uint8(writer, type);
+ writer->write_data8(writer, gw->get_encoding(gw));
+ if (nonce.ptr)
+ {
+ writer->write_data(writer, nonce);
+ }
+
+ data = writer->extract_buf(writer);
+ writer->destroy(writer);
+ return data;
+}
+
+/*
+ * Described in header
+ */
+identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce)
+{
+ bio_reader_t *reader;
+ id_type_t id_type;
+ chunk_t gateway;
+ u_int8_t type;
+
+ reader = bio_reader_create(data);
+ if (!reader->read_uint8(reader, &type) ||
+ !reader->read_data8(reader, &gateway))
+ {
+ DBG1(DBG_ENC, "invalid REDIRECT notify data");
+ reader->destroy(reader);
+ return NULL;
+ }
+ id_type = gateway_to_id_type(type);
+ if (!id_type)
+ {
+ DBG1(DBG_ENC, "invalid gateway ID type (%d) in REDIRECT notify", type);
+ reader->destroy(reader);
+ return NULL;
+ }
+ if (nonce)
+ {
+ *nonce = chunk_clone(reader->peek(reader));
+ }
+ reader->destroy(reader);
+ return identification_create_from_encoding(id_type, gateway);
+}
diff --git a/src/libcharon/sa/redirect_manager.h b/src/libcharon/sa/redirect_manager.h
new file mode 100644
index 000000000..e8753265c
--- /dev/null
+++ b/src/libcharon/sa/redirect_manager.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2015 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 redirect_manager redirect_manager
+ * @{ @ingroup sa
+ */
+
+#ifndef REDIRECT_MANAGER_H_
+#define REDIRECT_MANAGER_H_
+
+typedef struct redirect_manager_t redirect_manager_t;
+
+#include <sa/redirect_provider.h>
+
+/**
+ * Manages redirect providers.
+ */
+struct redirect_manager_t {
+
+ /**
+ * Add a redirect provider.
+ *
+ * All registered providers are queried until one of them decides to
+ * redirect a client.
+ *
+ * A provider may be called concurrently for different IKE_SAs.
+ *
+ * @param provider provider to register
+ */
+ void (*add_provider)(redirect_manager_t *this,
+ redirect_provider_t *provider);
+
+ /**
+ * Remove a redirect provider.
+ *
+ * @param provider provider to unregister
+ */
+ void (*remove_provider)(redirect_manager_t *this,
+ redirect_provider_t *provider);
+
+ /**
+ * Determine whether a client should be redirected upon receipt of the
+ * IKE_SA_INIT message.
+ *
+ * @param ike_sa IKE_SA for which this is called
+ * @param gateway[out] new IKE gateway (IP or FQDN)
+ * @return TRUE if client should be redirected, FALSE otherwise
+ */
+ bool (*redirect_on_init)(redirect_manager_t *this, ike_sa_t *ike_sa,
+ identification_t **gateway);
+
+ /**
+ * Determine whether a client should be redirected after the IKE_AUTH has
+ * been handled. Should be called after the client is authenticated and
+ * when the server authenticates itself.
+ *
+ * @param ike_sa IKE_SA for which this is called
+ * @param gateway[out] new IKE gateway (IP or FQDN)
+ * @return TRUE if client should be redirected, FALSE otherwise
+ */
+ bool (*redirect_on_auth)(redirect_manager_t *this, ike_sa_t *ike_sa,
+ identification_t **gateway);
+
+ /**
+ * Destroy this instance.
+ */
+ void (*destroy)(redirect_manager_t *this);
+};
+
+/**
+ * Create a redirect manager instance.
+ *
+ * @return manager instance
+ */
+redirect_manager_t *redirect_manager_create();
+
+/**
+ * Create notification data of a REDIRECT or REDIRECT_FROM payload using the
+ * given gateway identity and optional nonce (only used during IKE_SA_INIT).
+ *
+ * @param gw gateway identity (IP or FQDN), gets cloned
+ * @param nonce nonce value, or chunk_empty, gets cloned
+ * @return notify data, chunk_empty if ID type is not supported
+ */
+chunk_t redirect_data_create(identification_t *gw, chunk_t nonce);
+
+/**
+ * Parse notification data of a REDIRECT or REDIRECTED_FROM notify payload.
+ *
+ * @param data notification data to parse
+ * @param[out] nonce nonce data (allocated), if any was provided
+ * @return gateway identity, NULL if data is invalid
+ */
+identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce);
+
+#endif /** REDIRECT_MANAGER_H_ @}*/
diff --git a/src/libcharon/sa/redirect_provider.h b/src/libcharon/sa/redirect_provider.h
new file mode 100644
index 000000000..ef2288ffc
--- /dev/null
+++ b/src/libcharon/sa/redirect_provider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 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 redirect_provider redirect_provider
+ * @{ @ingroup sa
+ */
+
+#ifndef REDIRECT_PROVIDER_H_
+#define REDIRECT_PROVIDER_H_
+
+typedef struct redirect_provider_t redirect_provider_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+
+/**
+ * Interface that allows implementations to decide whether a client is
+ * redirected during IKE_SA_INIT or IKE_AUTH using RFC 5685.
+ */
+struct redirect_provider_t {
+
+ /**
+ * Decide whether a client is redirect directly upon receipt of the
+ * IKE_SA_INIT message.
+ *
+ * @param ike_sa IKE_SA for which this is called
+ * @param gateway[out] new IKE gateway (IP or FQDN)
+ * @return TRUE if client should be redirected, FALSE otherwise
+ */
+ bool (*redirect_on_init)(redirect_provider_t *this, ike_sa_t *ike_sa,
+ identification_t **gateway);
+
+ /**
+ * Decide whether a client is redirect after the IKE_AUTH has been
+ * handled. This is called after the client is authenticated and when the
+ * server authenticates itself.
+ *
+ * @param ike_sa IKE_SA for which this is called
+ * @param gateway[out] new IKE gateway (IP or FQDN)
+ * @return TRUE if client should be redirected, FALSE otherwise
+ */
+ bool (*redirect_on_auth)(redirect_provider_t *this, ike_sa_t *ike_sa,
+ identification_t **gateway);
+};
+
+#endif /** REDIRECT_PROVIDER_H_ @}*/
diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c
index 5231994c8..0e9cf6e1f 100644
--- a/src/libcharon/sa/shunt_manager.c
+++ b/src/libcharon/sa/shunt_manager.c
@@ -16,7 +16,6 @@
#include "shunt_manager.h"
-#include <hydra.h>
#include <daemon.h>
#include <threading/rwlock.h>
#include <threading/rwlock_condvar.h>
@@ -111,22 +110,22 @@ static bool install_shunt_policy(child_cfg_t *child)
continue;
}
/* install out policy */
- status |= hydra->kernel_interface->add_policy(
- hydra->kernel_interface, host_any, host_any,
+ status |= charon->kernel->add_policy(charon->kernel,
+ host_any, host_any,
my_ts, other_ts, POLICY_OUT, policy_type,
&sa, child->get_mark(child, FALSE),
policy_prio);
/* install in policy */
- status |= hydra->kernel_interface->add_policy(
- hydra->kernel_interface, host_any, host_any,
+ status |= charon->kernel->add_policy(charon->kernel,
+ host_any, host_any,
other_ts, my_ts, POLICY_IN, policy_type,
&sa, child->get_mark(child, TRUE),
policy_prio);
/* install forward policy */
- status |= hydra->kernel_interface->add_policy(
- hydra->kernel_interface, host_any, host_any,
+ status |= charon->kernel->add_policy(charon->kernel,
+ host_any, host_any,
other_ts, my_ts, POLICY_FWD, policy_type,
&sa, child->get_mark(child, TRUE),
policy_prio);
@@ -248,22 +247,22 @@ static void uninstall_shunt_policy(child_cfg_t *child)
continue;
}
/* uninstall out policy */
- status |= hydra->kernel_interface->del_policy(
- hydra->kernel_interface, host_any, host_any,
+ status |= charon->kernel->del_policy(charon->kernel,
+ host_any, host_any,
my_ts, other_ts, POLICY_OUT, policy_type,
&sa, child->get_mark(child, FALSE),
policy_prio);
/* uninstall in policy */
- status |= hydra->kernel_interface->del_policy(
- hydra->kernel_interface, host_any, host_any,
+ status |= charon->kernel->del_policy(charon->kernel,
+ host_any, host_any,
other_ts, my_ts, POLICY_IN, policy_type,
&sa, child->get_mark(child, TRUE),
policy_prio);
/* uninstall forward policy */
- status |= hydra->kernel_interface->del_policy(
- hydra->kernel_interface, host_any, host_any,
+ status |= charon->kernel->del_policy(charon->kernel,
+ host_any, host_any,
other_ts, my_ts, POLICY_FWD, policy_type,
&sa, child->get_mark(child, TRUE),
policy_prio);
diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c
index b35b58185..405eda66b 100644
--- a/src/libcharon/sa/task.c
+++ b/src/libcharon/sa/task.c
@@ -28,6 +28,8 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST,
"IKE_REKEY",
"IKE_REAUTH",
"IKE_REAUTH_COMPLETE",
+ "IKE_REDIRECT",
+ "IKE_VERIFY_PEER_CERT",
"IKE_DELETE",
"IKE_DPD",
"IKE_VENDOR",
diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h
index 7bd3da1fe..31d70fb3b 100644
--- a/src/libcharon/sa/task.h
+++ b/src/libcharon/sa/task.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 Tobias Brunner
+ * Copyright (C) 2007-2015 Tobias Brunner
* Copyright (C) 2006 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -57,6 +57,10 @@ enum task_type_t {
TASK_IKE_REAUTH,
/** completion task for make-before-break IKE_SA re-authentication */
TASK_IKE_REAUTH_COMPLETE,
+ /** redirect an active IKE_SA */
+ TASK_IKE_REDIRECT,
+ /** verify a peer's certificate */
+ TASK_IKE_VERIFY_PEER_CERT,
/** delete an IKE_SA */
TASK_IKE_DELETE,
/** liveness check */
@@ -154,6 +158,18 @@ struct task_t {
status_t (*process) (task_t *this, message_t *message);
/**
+ * Verify a message before processing it (optional to implement by tasks).
+ *
+ * @param message message to verify
+ * @return
+ * - FAILED if verification is not successful, the
+ * message will be silently discarded
+ * - DESTROY_ME if IKE_SA has to be destroyed
+ * - SUCCESS if verification is successful
+ */
+ status_t (*pre_process) (task_t *this, message_t *message);
+
+ /**
* Get the type of the task implementation.
*/
task_type_t (*get_type) (task_t *this);
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index 90ad7e40e..85e220775 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -16,7 +16,6 @@
#include "trap_manager.h"
-#include <hydra.h>
#include <daemon.h>
#include <threading/mutex.h>
#include <threading/rwlock.h>
@@ -195,8 +194,7 @@ METHOD(trap_manager_t, install, u_int32_t,
if (!me || me->is_anyaddr(me))
{
DESTROY_IF(me);
- me = hydra->kernel_interface->get_source_addr(
- hydra->kernel_interface, other, NULL);
+ me = charon->kernel->get_source_addr(charon->kernel, other, NULL);
if (!me)
{
DBG1(DBG_CFG, "installing trap failed, local address unknown");