diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2015-11-18 14:49:27 +0100 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2015-11-18 14:49:27 +0100 |
commit | 1e980d6be0ef0e243c6fe82b5e855454b97e24a4 (patch) | |
tree | 0d59eec2ce2ed332434ae80fc78a44db9ad293c5 /src/libcharon/sa | |
parent | 5dca9ea0e2931f0e2a056c7964d311bcc30a01b8 (diff) | |
download | vyos-strongswan-1e980d6be0ef0e243c6fe82b5e855454b97e24a4.tar.gz vyos-strongswan-1e980d6be0ef0e243c6fe82b5e855454b97e24a4.zip |
Imported Upstream version 5.3.4
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 178 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa_manager.c | 84 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa_manager.h | 21 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/keymat_v1.c | 20 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/task_manager_v1.c | 110 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/mode_config.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_delete.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.c | 6 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/xauth.c | 10 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/keymat_v2.c | 1 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_create.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_delete.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/ike_mobike.c | 6 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/ike_natd.c | 28 | ||||
-rw-r--r-- | src/libcharon/sa/shunt_manager.c | 66 | ||||
-rw-r--r-- | src/libcharon/sa/trap_manager.c | 2 |
16 files changed, 375 insertions, 173 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 73f2ec9d3..b0f163c83 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -413,8 +413,14 @@ METHOD(enumerator_t, policy_enumerate, bool, { /* protocol mismatch */ continue; } - *my_out = this->ts; - *other_out = other_ts; + if (my_out) + { + *my_out = this->ts; + } + if (other_out) + { + *other_out = other_ts; + } return TRUE; } return FALSE; @@ -775,6 +781,50 @@ static bool require_policy_update() } /** + * Prepare SA config to install/delete policies + */ +static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa, + ipsec_sa_cfg_t *other_sa) +{ + enumerator_t *enumerator; + + *my_sa = (ipsec_sa_cfg_t){ + .mode = this->mode, + .reqid = this->reqid, + .ipcomp = { + .transform = this->ipcomp, + }, + }; + *other_sa = *my_sa; + + my_sa->ipcomp.cpi = this->my_cpi; + other_sa->ipcomp.cpi = this->other_cpi; + + if (this->protocol == PROTO_ESP) + { + my_sa->esp.use = TRUE; + my_sa->esp.spi = this->my_spi; + other_sa->esp.use = TRUE; + other_sa->esp.spi = this->other_spi; + } + else + { + my_sa->ah.use = TRUE; + my_sa->ah.spi = this->my_spi; + other_sa->ah.use = TRUE; + other_sa->ah.spi = this->other_spi; + } + + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, NULL, NULL)) + { + my_sa->policy_count++; + other_sa->policy_count++; + } + enumerator->destroy(enumerator); +} + +/** * Install 3 policies: out, in and forward */ static status_t install_policies_internal(private_child_sa_t *this, @@ -806,20 +856,22 @@ static status_t install_policies_internal(private_child_sa_t *this, * Delete 3 policies: out, in and forward */ static void del_policies_internal(private_child_sa_t *this, - traffic_selector_t *my_ts, traffic_selector_t *other_ts, - policy_priority_t priority) + host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, + traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, + ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority) { + hydra->kernel_interface->del_policy(hydra->kernel_interface, - my_ts, other_ts, POLICY_OUT, this->reqid, - this->mark_out, priority); + 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, - other_ts, my_ts, POLICY_IN, this->reqid, - this->mark_in, priority); + 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, - other_ts, my_ts, POLICY_FWD, this->reqid, - this->mark_in, priority); + other_addr, my_addr, other_ts, my_ts, POLICY_FWD, + type, my_sa, this->mark_in, priority); } } @@ -864,31 +916,9 @@ METHOD(child_sa_t, add_policies, status_t, if (this->config->install_policy(this->config)) { policy_priority_t priority; - ipsec_sa_cfg_t my_sa = { - .mode = this->mode, - .reqid = this->reqid, - .ipcomp = { - .transform = this->ipcomp, - }, - }, other_sa = my_sa; - - my_sa.ipcomp.cpi = this->my_cpi; - other_sa.ipcomp.cpi = this->other_cpi; - - if (this->protocol == PROTO_ESP) - { - my_sa.esp.use = TRUE; - my_sa.esp.spi = this->my_spi; - other_sa.esp.use = TRUE; - other_sa.esp.spi = this->other_spi; - } - else - { - my_sa.ah.use = TRUE; - my_sa.ah.spi = this->my_spi; - other_sa.ah.use = TRUE; - other_sa.ah.spi = this->other_spi; - } + ipsec_sa_cfg_t my_sa, other_sa; + + prepare_sa_cfg(this, &my_sa, &other_sa); /* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD * entry) we install a trap policy */ @@ -896,14 +926,6 @@ METHOD(child_sa_t, add_policies, status_t, priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT; - enumerator = create_policy_enumerator(this); - while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) - { - my_sa.policy_count++; - other_sa.policy_count++; - } - enumerator->destroy(enumerator); - /* enumerate pairs of traffic selectors */ enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) @@ -1006,47 +1028,24 @@ METHOD(child_sa_t, update, status_t, if (this->config->install_policy(this->config) && require_policy_update()) { - ipsec_sa_cfg_t my_sa = { - .mode = this->mode, - .reqid = this->reqid, - .ipcomp = { - .transform = this->ipcomp, - }, - }, other_sa = my_sa; - - my_sa.ipcomp.cpi = this->my_cpi; - other_sa.ipcomp.cpi = this->other_cpi; - - if (this->protocol == PROTO_ESP) - { - my_sa.esp.use = TRUE; - my_sa.esp.spi = this->my_spi; - other_sa.esp.use = TRUE; - other_sa.esp.spi = this->other_spi; - } - else - { - my_sa.ah.use = TRUE; - my_sa.ah.spi = this->my_spi; - other_sa.ah.use = TRUE; - other_sa.ah.spi = this->other_spi; - } - - /* update policies */ if (!me->ip_equals(me, this->my_addr) || !other->ip_equals(other, this->other_addr)) { + ipsec_sa_cfg_t my_sa, other_sa; enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; + prepare_sa_cfg(this, &my_sa, &other_sa); + /* always use high priorities, as hosts getting updated are INSTALLED */ enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL; /* remove old policies first */ - del_policies_internal(this, my_ts, other_ts, - POLICY_PRIORITY_DEFAULT); + del_policies_internal(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, + POLICY_IPSEC, POLICY_PRIORITY_DEFAULT); /* check if we have to update a "dynamic" traffic selector */ if (!me->ip_equals(me, this->my_addr) && @@ -1068,21 +1067,20 @@ METHOD(child_sa_t, update, status_t, /* reinstall updated policies */ install_policies_internal(this, me, other, my_ts, other_ts, - &my_sa, &other_sa, POLICY_IPSEC, - POLICY_PRIORITY_DEFAULT); + &my_sa, &other_sa, POLICY_IPSEC, + POLICY_PRIORITY_DEFAULT); /* update fallback policies after the new policy is in place */ - if (old_my_ts || old_other_ts) - { - del_policies_internal(this, old_my_ts ?: my_ts, - old_other_ts ?: other_ts, + del_policies_internal(this, this->my_addr, this->other_addr, + old_my_ts ?: my_ts, + old_other_ts ?: other_ts, + &my_sa, &other_sa, POLICY_DROP, + POLICY_PRIORITY_FALLBACK); + install_policies_internal(this, me, other, my_ts, other_ts, + &my_sa, &other_sa, POLICY_DROP, POLICY_PRIORITY_FALLBACK); - install_policies_internal(this, me, other, my_ts, other_ts, - &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK); - DESTROY_IF(old_my_ts); - DESTROY_IF(old_other_ts); - } + DESTROY_IF(old_my_ts); + DESTROY_IF(old_other_ts); } enumerator->destroy(enumerator); } @@ -1122,15 +1120,21 @@ METHOD(child_sa_t, destroy, void, if (this->config->install_policy(this->config)) { + ipsec_sa_cfg_t my_sa, other_sa; + + prepare_sa_cfg(this, &my_sa, &other_sa); + /* delete all policies in the kernel */ enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { - del_policies_internal(this, my_ts, other_ts, priority); + del_policies_internal(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority); if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update()) { - del_policies_internal(this, my_ts, other_ts, - POLICY_PRIORITY_FALLBACK); + del_policies_internal(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, + POLICY_PRIORITY_FALLBACK); } } enumerator->destroy(enumerator); diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 37d69874d..4625df5b8 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -394,9 +394,17 @@ struct private_ike_sa_manager_t { rng_t *rng; /** - * Lock to access the RNG instance + * Registered callback for IKE SPIs */ - rwlock_t *rng_lock; + struct { + spi_cb_t cb; + void *data; + } spi_cb; + + /** + * Lock to access the RNG instance and the callback + */ + rwlock_t *spi_lock; /** * reuse existing IKE_SAs in checkout_by_config @@ -971,13 +979,17 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this) { u_int64_t spi; - this->rng_lock->read_lock(this->rng_lock); - if (!this->rng || - !this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi)) + this->spi_lock->read_lock(this->spi_lock); + if (this->spi_cb.cb) + { + spi = this->spi_cb.cb(this->spi_cb.data); + } + else if (!this->rng || + !this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi)) { spi = 0; } - this->rng_lock->unlock(this->rng_lock); + this->spi_lock->unlock(this->spi_lock); return spi; } @@ -1188,11 +1200,15 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*, */ static u_int32_t get_message_id_or_hash(message_t *message) { - /* Use the message ID, or the message hash in IKEv1 Main/Aggressive mode */ - if (message->get_major_version(message) == IKEV1_MAJOR_VERSION && - message->get_message_id(message) == 0) + if (message->get_major_version(message) == IKEV1_MAJOR_VERSION) { - return chunk_hash(message->get_packet_data(message)); + /* Use a hash for IKEv1 Phase 1, where we don't have a MID, and Quick + * Mode, where all three messages use the same message ID */ + if (message->get_message_id(message) == 0 || + message->get_exchange_type(message) == QUICK_MODE) + { + return chunk_hash(message->get_packet_data(message)); + } } return message->get_message_id(message); } @@ -1384,7 +1400,8 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*, continue; } if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) - { /* skip IKE_SAs which are not usable */ + { /* skip IKE_SAs which are not usable, wake other waiting threads */ + entry->condvar->signal(entry->condvar); continue; } @@ -1402,6 +1419,8 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*, break; } } + /* other threads might be waiting for this entry */ + entry->condvar->signal(entry->condvar); } enumerator->destroy(enumerator); @@ -1434,6 +1453,8 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*, entry->checked_out = TRUE; break; } + /* other threads might be waiting for this entry */ + entry->condvar->signal(entry->condvar); } } enumerator->destroy(enumerator); @@ -1490,6 +1511,8 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*, ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa)); break; } + /* other threads might be waiting for this entry */ + entry->condvar->signal(entry->condvar); } } enumerator->destroy(enumerator); @@ -1628,8 +1651,27 @@ METHOD(ike_sa_manager_t, checkin, void, * delete any existing IKE_SAs with that peer. */ if (ike_sa->has_condition(ike_sa, COND_INIT_CONTACT_SEEN)) { + /* We can't hold the segment locked while checking the + * uniqueness as this could lead to deadlocks. We mark the + * entry as checked out while we release the lock so no other + * 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; + unlock_single_segment(this, segment); + this->public.check_uniqueness(&this->public, ike_sa, TRUE); ike_sa->set_condition(ike_sa, COND_INIT_CONTACT_SEEN, FALSE); + + /* The entry could have been modified in the mean time, e.g. + * because another SA was added/removed next to it or another + * 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; + /* 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); } } @@ -2010,6 +2052,15 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int, return count; } +METHOD(ike_sa_manager_t, set_spi_cb, void, + private_ike_sa_manager_t *this, spi_cb_t callback, void *data) +{ + this->spi_lock->write_lock(this->spi_lock); + this->spi_cb.cb = callback; + this->spi_cb.data = data; + this->spi_lock->unlock(this->spi_lock); +} + METHOD(ike_sa_manager_t, flush, void, private_ike_sa_manager_t *this) { @@ -2092,10 +2143,12 @@ METHOD(ike_sa_manager_t, flush, void, charon->bus->set_sa(charon->bus, NULL); unlock_all_segments(this); - this->rng_lock->write_lock(this->rng_lock); + this->spi_lock->write_lock(this->spi_lock); this->rng->destroy(this->rng); this->rng = NULL; - this->rng_lock->unlock(this->rng_lock); + this->spi_cb.cb = NULL; + this->spi_cb.data = NULL; + this->spi_lock->unlock(this->spi_lock); } METHOD(ike_sa_manager_t, destroy, void, @@ -2120,7 +2173,7 @@ METHOD(ike_sa_manager_t, destroy, void, free(this->connected_peers_segments); free(this->init_hashes_segments); - this->rng_lock->destroy(this->rng_lock); + this->spi_lock->destroy(this->spi_lock); free(this); } @@ -2167,6 +2220,7 @@ ike_sa_manager_t *ike_sa_manager_create() .get_count = _get_count, .get_half_open_count = _get_half_open_count, .flush = _flush, + .set_spi_cb = _set_spi_cb, .destroy = _destroy, }, ); @@ -2178,7 +2232,7 @@ ike_sa_manager_t *ike_sa_manager_create() free(this); return NULL; } - this->rng_lock = rwlock_create(RWLOCK_TYPE_DEFAULT); + this->spi_lock = rwlock_create(RWLOCK_TYPE_DEFAULT); this->ikesa_limit = lib->settings->get_int(lib->settings, "%s.ikesa_limit", 0, lib->ns); diff --git a/src/libcharon/sa/ike_sa_manager.h b/src/libcharon/sa/ike_sa_manager.h index 3ea928ea5..f1b7c2579 100644 --- a/src/libcharon/sa/ike_sa_manager.h +++ b/src/libcharon/sa/ike_sa_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -31,6 +31,16 @@ typedef struct ike_sa_manager_t ike_sa_manager_t; #include <config/peer_cfg.h> /** + * Callback called to generate an IKE SPI. + * + * This may be called from multiple threads concurrently. + * + * @param data data supplied during registration of the callback + * @return allocated SPI, 0 on failure + */ +typedef u_int64_t (*spi_cb_t)(void *data); + +/** * Manages and synchronizes access to all IKE_SAs. * * To synchronize access to thread-unsave IKE_SAs, they are checked out for @@ -227,6 +237,15 @@ struct ike_sa_manager_t { bool responder_only); /** + * Set the callback to generate IKE SPIs + * + * @param callback callback to register + * @param data data provided to callback + */ + void (*set_spi_cb)(ike_sa_manager_t *this, spi_cb_t callback, + void *data); + + /** * Delete all existing IKE_SAs and destroy them immediately. * * Threads will be driven out, so all SAs can be deleted cleanly. diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c index f5a91dbeb..e428966ad 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.c +++ b/src/libcharon/sa/ikev1/keymat_v1.c @@ -23,14 +23,9 @@ typedef struct private_keymat_v1_t private_keymat_v1_t; /** - * Max. number of IVs to track. + * Max. number of IVs/QMs to track. */ -#define MAX_IV 3 - -/** - * Max. number of Quick Modes to track. - */ -#define MAX_QM 2 +#define MAX_EXCHANGES_DEFAULT 3 /** * Data stored for IVs @@ -110,6 +105,11 @@ struct private_keymat_v1_t { * of QMs are tracked at the same time. Stores qm_data_t objects. */ linked_list_t *qms; + + /** + * Max. number of IVs/Quick Modes to track. + */ + int max_exchanges; }; @@ -874,7 +874,7 @@ static qm_data_t *lookup_quick_mode(private_keymat_v1_t *this, u_int32_t mid) } this->qms->insert_first(this->qms, found); /* remove least recently used state if maximum reached */ - if (this->qms->get_count(this->qms) > MAX_QM && + if (this->qms->get_count(this->qms) > this->max_exchanges && this->qms->remove_last(this->qms, (void**)&qm) == SUCCESS) { qm_data_destroy(qm); @@ -1048,7 +1048,7 @@ static iv_data_t *lookup_iv(private_keymat_v1_t *this, u_int32_t mid) } this->ivs->insert_first(this->ivs, found); /* remove least recently used IV if maximum reached */ - if (this->ivs->get_count(this->ivs) > MAX_IV && + if (this->ivs->get_count(this->ivs) > this->max_exchanges && this->ivs->remove_last(this->ivs, (void**)&iv) == SUCCESS) { iv_data_destroy(iv); @@ -1163,6 +1163,8 @@ keymat_v1_t *keymat_v1_create(bool initiator) .ivs = linked_list_create(), .qms = linked_list_create(), .initiator = initiator, + .max_exchanges = lib->settings->get_int(lib->settings, + "%s.max_ikev1_exchanges", MAX_EXCHANGES_DEFAULT, lib->ns), ); return &this->public; diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 678f99df1..3c601a4fa 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -752,6 +752,12 @@ static status_t build_response(private_task_manager_t *this, message_t *request) case ALREADY_DONE: cancelled = TRUE; break; + case INVALID_ARG: + if (task->get_type(task) == TASK_QUICK_MODE) + { /* not responsible for this exchange */ + continue; + } + /* FALL */ case FAILED: default: charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); @@ -929,6 +935,28 @@ static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid) } /** + * Check if we still have an aggressive mode task queued + */ +static bool have_aggressive_mode_task(private_task_manager_t *this) +{ + enumerator_t *enumerator; + task_t *task; + bool found = FALSE; + + enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_AGGRESSIVE_MODE) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + +/** * handle an incoming request message */ static status_t process_request(private_task_manager_t *this, @@ -1034,6 +1062,12 @@ static status_t process_request(private_task_manager_t *this, case ALREADY_DONE: send_response = FALSE; break; + case INVALID_ARG: + if (task->get_type(task) == TASK_QUICK_MODE) + { /* not responsible for this exchange */ + continue; + } + /* FALL */ case FAILED: default: charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); @@ -1061,6 +1095,22 @@ static status_t process_request(private_task_manager_t *this, * the same message again. */ clear_packets(this->responding.packets); } + if (this->queued && + this->queued->get_exchange_type(this->queued) == INFORMATIONAL_V1) + { + message_t *queued; + status_t status; + + queued = this->queued; + this->queued = NULL; + status = this->public.task_manager.process_message( + &this->public.task_manager, queued); + queued->destroy(queued); + if (status == DESTROY_ME) + { + return status; + } + } if (this->passive_tasks->get_count(this->passive_tasks) == 0 && this->queued_tasks->get_count(this->queued_tasks) > 0) { @@ -1133,7 +1183,8 @@ static status_t process_response(private_task_manager_t *this, this->initiating.type = EXCHANGE_TYPE_UNDEFINED; clear_packets(this->initiating.packets); - if (this->queued && this->active_tasks->get_count(this->active_tasks) == 0) + if (this->queued && !this->active_tasks->get_count(this->active_tasks) && + this->queued->get_exchange_type(this->queued) == TRANSACTION) { queued = this->queued; this->queued = NULL; @@ -1228,6 +1279,29 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg) return status; } +/** + * Queue the given message if possible + */ +static status_t queue_message(private_task_manager_t *this, message_t *msg) +{ + if (this->queued) + { + DBG1(DBG_IKE, "ignoring %N request, queue full", + exchange_type_names, msg->get_exchange_type(msg)); + return FAILED; + } + this->queued = message_create_from_packet(msg->get_packet(msg)); + if (this->queued->parse_header(this->queued) != SUCCESS) + { + this->queued->destroy(this->queued); + this->queued = NULL; + return FAILED; + } + DBG1(DBG_IKE, "queueing %N request as tasks still active", + exchange_type_names, msg->get_exchange_type(msg)); + return SUCCESS; +} + METHOD(task_manager_t, process_message, status_t, private_task_manager_t *this, message_t *msg) { @@ -1328,25 +1402,29 @@ METHOD(task_manager_t, process_message, status_t, } } - if (msg->get_exchange_type(msg) == TRANSACTION && - this->active_tasks->get_count(this->active_tasks)) - { /* main mode not yet complete, queue XAuth/Mode config tasks */ - if (this->queued) + /* drop XAuth/Mode Config/Quick Mode messages until we received the last + * Aggressive Mode message. since Informational messages are not + * retransmitted we queue them. */ + if (have_aggressive_mode_task(this)) + { + if (msg->get_exchange_type(msg) == INFORMATIONAL_V1) { - DBG1(DBG_IKE, "ignoring additional %N request, queue full", - exchange_type_names, TRANSACTION); - return SUCCESS; + return queue_message(this, msg); } - this->queued = message_create_from_packet(msg->get_packet(msg)); - if (this->queued->parse_header(this->queued) != SUCCESS) + else if (msg->get_exchange_type(msg) != AGGRESSIVE) { - this->queued->destroy(this->queued); - this->queued = NULL; + DBG1(DBG_IKE, "ignoring %N request while phase 1 is incomplete", + exchange_type_names, msg->get_exchange_type(msg)); return FAILED; } - DBG1(DBG_IKE, "queueing %N request as tasks still active", - exchange_type_names, TRANSACTION); - return SUCCESS; + } + + /* queue XAuth/Mode Config messages unless the Main Mode exchange we + * initiated is complete */ + if (msg->get_exchange_type(msg) == TRANSACTION && + this->active_tasks->get_count(this->active_tasks)) + { + return queue_message(this, msg); } msg->set_request(msg, TRUE); @@ -1724,6 +1802,8 @@ METHOD(task_manager_t, queue_dpd, void, pow(this->retransmit_base, retransmit)); } } + /* compensate for the already elapsed dpd delay */ + t -= 1000 * peer_cfg->get_dpd(peer_cfg); /* schedule DPD timeout job */ lib->scheduler->schedule_job_ms(lib->scheduler, diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index d0994a961..a03477e18 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -482,7 +482,9 @@ static host_t *assign_migrated_vip(linked_list_t *migrated, host_t *requested) enumerator = migrated->create_enumerator(migrated); while (enumerator->enumerate(enumerator, &vip)) { - if (vip->ip_equals(vip, requested)) + if (vip->ip_equals(vip, requested) || + (requested->is_anyaddr(requested) && + requested->get_family(requested) == vip->get_family(vip))) { migrated->remove_at(migrated, enumerator); found = vip; diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c index 1b95a8b11..ade59a2dd 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_delete.c +++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c @@ -115,7 +115,7 @@ static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol, if (this->expired) { DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", + "with SPIs %.8x_i %.8x_o and TS %#R === %#R", child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), ntohl(child_sa->get_spi(child_sa, FALSE)), my_ts, other_ts); @@ -126,7 +126,7 @@ static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol, child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL); DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs " - "%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R", + "%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R === %#R", child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in, ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out, diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index d6a3f2cd1..e7d26443b 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -388,7 +388,7 @@ static bool install(private_quick_mode_t *this) this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); DBG0(DBG_IKE, "CHILD_SA %s{%d} established " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", + "with SPIs %.8x_i %.8x_o and TS %#R === %#R", this->child_sa->get_name(this->child_sa), this->child_sa->get_unique_id(this->child_sa), ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), @@ -1026,7 +1026,7 @@ METHOD(task_t, process_r, status_t, { if (this->mid && this->mid != message->get_message_id(message)) { /* not responsible for this quick mode exchange */ - return NEED_MORE; + return INVALID_ARG; } switch (this->state) @@ -1200,7 +1200,7 @@ METHOD(task_t, build_r, status_t, { if (this->mid && this->mid != message->get_message_id(message)) { /* not responsible for this quick mode exchange */ - return NEED_MORE; + return INVALID_ARG; } switch (this->state) diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c index a770e90ff..c0c91574c 100644 --- a/src/libcharon/sa/ikev1/tasks/xauth.c +++ b/src/libcharon/sa/ikev1/tasks/xauth.c @@ -271,7 +271,10 @@ static bool add_auth_cfg(private_xauth_t *this, identification_t *id, bool local auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_XAUTH); - auth->add(auth, AUTH_RULE_XAUTH_IDENTITY, id->clone(id)); + if (id) + { + auth->add(auth, AUTH_RULE_XAUTH_IDENTITY, id->clone(id)); + } auth->merge(auth, this->ike_sa->get_auth_cfg(this->ike_sa, local), FALSE); this->ike_sa->add_auth_cfg(this->ike_sa, local, auth); @@ -342,7 +345,10 @@ METHOD(task_t, build_i, status_t, break; case SUCCESS: DESTROY_IF(cp); - this->status = XAUTH_OK; + if (add_auth_cfg(this, NULL, FALSE) && allowed(this)) + { + this->status = XAUTH_OK; + } this->public.task.process = _process_i_status; return build_i_status(this, message); default: diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index fce0840e3..55cb5dd9c 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -527,6 +527,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool, case ENCR_AES_GCM_ICV12: case ENCR_AES_GCM_ICV16: case ENCR_AES_CTR: + case ENCR_CAMELLIA_CTR: case ENCR_NULL_AUTH_AES_GMAC: case ENCR_CHACHA20_POLY1305: enc_size += 4; diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index e08f3dab1..97f73d851 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -712,7 +712,7 @@ static status_t select_and_install(private_child_create_t *this, this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); DBG0(DBG_IKE, "CHILD_SA %s{%d} established " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", + "with SPIs %.8x_i %.8x_o and TS %#R === %#R", this->child_sa->get_name(this->child_sa), this->child_sa->get_unique_id(this->child_sa), ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), @@ -1245,7 +1245,7 @@ METHOD(task_t, build_r, status_t, } if (this->config == NULL) { - DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable", + DBG1(DBG_IKE, "traffic selectors %#R === %#R inacceptable", this->tsr, this->tsi); charon->bus->alert(charon->bus, ALERT_TS_MISMATCH, this->tsi, this->tsr); message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index f0b11e291..877ae0531 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -266,7 +266,7 @@ static void log_children(private_child_delete_t *this) if (this->expired) { DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", + "with SPIs %.8x_i %.8x_o and TS %#R === %#R", child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), ntohl(child_sa->get_spi(child_sa, FALSE)), my_ts, other_ts); @@ -277,7 +277,7 @@ static void log_children(private_child_delete_t *this) child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL); DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs %.8x_i " - "(%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R", + "(%llu bytes) %.8x_o (%llu bytes) and TS %#R === %#R", child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in, ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out, diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index 11b0bb281..cbdc5e797 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -339,7 +339,11 @@ METHOD(ike_mobike_t, transmit, bool, { if (me->ip_equals(me, me_old)) { - charon->sender->send(charon->sender, packet->clone(packet)); + copy = packet->clone(packet); + /* hosts might have been updated by a peer's MOBIKE exchange */ + copy->set_source(copy, me_old->clone(me_old)); + copy->set_destination(copy, other_old->clone(other_old)); + charon->sender->send(charon->sender, copy); me->destroy(me); return TRUE; } diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c index 9e0eb68ce..dd34c1234 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_natd.c +++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c @@ -129,25 +129,6 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this, } /** - * build a faked NATD payload to enforce UDP encap - */ -static chunk_t generate_natd_hash_faked(private_ike_natd_t *this) -{ - rng_t *rng; - chunk_t chunk; - - rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng || !rng->allocate_bytes(rng, HASH_SIZE_SHA1, &chunk)) - { - DBG1(DBG_IKE, "unable to get random bytes for NATD fake"); - DESTROY_IF(rng); - return chunk_empty; - } - rng->destroy(rng); - return chunk; -} - -/** * Build a NAT detection notify payload. */ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, @@ -162,7 +143,14 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, config = this->ike_sa->get_ike_cfg(this->ike_sa); if (force_encap(config) && type == NAT_DETECTION_SOURCE_IP) { - hash = generate_natd_hash_faked(this); + u_int32_t addr; + + /* chunk_hash() is randomly keyed so this produces a random IPv4 address + * that changes with every restart but otherwise stays the same */ + addr = chunk_hash(chunk_from_chars(0x00, 0x00, 0x00, 0x00)); + host = host_create_from_chunk(AF_INET, chunk_from_thing(addr), 0); + hash = generate_natd_hash(this, ike_sa_id, host); + host->destroy(host); } else { diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index 1a984435c..5231994c8 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -63,9 +63,9 @@ struct private_shunt_manager_t { static bool install_shunt_policy(child_cfg_t *child) { enumerator_t *e_my_ts, *e_other_ts; - linked_list_t *my_ts_list, *other_ts_list; + linked_list_t *my_ts_list, *other_ts_list, *hosts; traffic_selector_t *my_ts, *other_ts; - host_t *host_any; + host_t *host_any, *host_any6; policy_type_t policy_type; policy_priority_t policy_prio; status_t status = SUCCESS; @@ -85,9 +85,13 @@ static bool install_shunt_policy(child_cfg_t *child) return FALSE; } - my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL); - other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL); host_any = host_create_any(AF_INET); + host_any6 = host_create_any(AF_INET6); + + hosts = linked_list_create_with_items(host_any, host_any6, NULL); + my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts); + other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); + hosts->destroy(hosts); /* enumerate pairs of traffic selectors */ e_my_ts = my_ts_list->create_enumerator(my_ts_list); @@ -96,6 +100,16 @@ static bool install_shunt_policy(child_cfg_t *child) e_other_ts = other_ts_list->create_enumerator(other_ts_list); while (e_other_ts->enumerate(e_other_ts, &other_ts)) { + if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts)) + { + continue; + } + if (my_ts->get_protocol(my_ts) && + other_ts->get_protocol(other_ts) && + my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts)) + { + continue; + } /* install out policy */ status |= hydra->kernel_interface->add_policy( hydra->kernel_interface, host_any, host_any, @@ -125,6 +139,7 @@ static bool install_shunt_policy(child_cfg_t *child) offsetof(traffic_selector_t, destroy)); other_ts_list->destroy_offset(other_ts_list, offsetof(traffic_selector_t, destroy)); + host_any6->destroy(host_any6); host_any->destroy(host_any); return status == SUCCESS; @@ -185,25 +200,35 @@ METHOD(shunt_manager_t, install, bool, static void uninstall_shunt_policy(child_cfg_t *child) { enumerator_t *e_my_ts, *e_other_ts; - linked_list_t *my_ts_list, *other_ts_list; + linked_list_t *my_ts_list, *other_ts_list, *hosts; traffic_selector_t *my_ts, *other_ts; + host_t *host_any, *host_any6; + policy_type_t policy_type; policy_priority_t policy_prio; status_t status = SUCCESS; + ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT }; switch (child->get_mode(child)) { case MODE_PASS: + policy_type = POLICY_PASS; policy_prio = POLICY_PRIORITY_PASS; break; case MODE_DROP: + policy_type = POLICY_DROP; policy_prio = POLICY_PRIORITY_FALLBACK; break; default: return; } - my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL); - other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL); + host_any = host_create_any(AF_INET); + host_any6 = host_create_any(AF_INET6); + + hosts = linked_list_create_with_items(host_any, host_any6, NULL); + my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts); + other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); + hosts->destroy(hosts); /* enumerate pairs of traffic selectors */ e_my_ts = my_ts_list->create_enumerator(my_ts_list); @@ -212,22 +237,35 @@ static void uninstall_shunt_policy(child_cfg_t *child) e_other_ts = other_ts_list->create_enumerator(other_ts_list); while (e_other_ts->enumerate(e_other_ts, &other_ts)) { + if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts)) + { + continue; + } + if (my_ts->get_protocol(my_ts) && + other_ts->get_protocol(other_ts) && + my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts)) + { + continue; + } /* uninstall out policy */ status |= hydra->kernel_interface->del_policy( - hydra->kernel_interface, my_ts, other_ts, - POLICY_OUT, 0, child->get_mark(child, FALSE), + hydra->kernel_interface, 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, other_ts, my_ts, - POLICY_IN, 0, child->get_mark(child, TRUE), + hydra->kernel_interface, 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, other_ts, my_ts, - POLICY_FWD, 0, child->get_mark(child, TRUE), + hydra->kernel_interface, host_any, host_any, + other_ts, my_ts, POLICY_FWD, policy_type, + &sa, child->get_mark(child, TRUE), policy_prio); } e_other_ts->destroy(e_other_ts); @@ -238,6 +276,8 @@ static void uninstall_shunt_policy(child_cfg_t *child) offsetof(traffic_selector_t, destroy)); other_ts_list->destroy_offset(other_ts_list, offsetof(traffic_selector_t, destroy)); + host_any6->destroy(host_any6); + host_any->destroy(host_any); if (status != SUCCESS) { diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 63505c960..90ad7e40e 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -211,6 +211,7 @@ METHOD(trap_manager_t, install, u_int32_t, if (this->installing == INSTALL_DISABLED) { /* flush() has been called */ this->lock->unlock(this->lock); + other->destroy(other); me->destroy(me); return 0; } @@ -235,6 +236,7 @@ METHOD(trap_manager_t, install, u_int32_t, { DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name); this->lock->unlock(this->lock); + other->destroy(other); me->destroy(me); return 0; } |