diff options
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 15 | ||||
-rw-r--r-- | src/libcharon/sa/eap/eap_method.c | 3 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa.c | 43 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa_id.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa_manager.c | 67 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa_manager.h | 9 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/phase1.c | 2 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/task_manager_v1.c | 35 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.c | 26 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.h | 11 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/authenticators/eap_authenticator.c | 9 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c | 2 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/keymat_v2.c | 2 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_create.c | 36 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_rekey.c | 18 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/ike_rekey.c | 34 | ||||
-rw-r--r-- | src/libcharon/sa/shunt_manager.c | 71 | ||||
-rw-r--r-- | src/libcharon/sa/shunt_manager.h | 6 | ||||
-rw-r--r-- | src/libcharon/sa/trap_manager.c | 276 |
19 files changed, 541 insertions, 128 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 94cf07c33..73f2ec9d3 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Tobias Brunner + * Copyright (C) 2006-2015 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter @@ -106,6 +106,11 @@ struct private_child_sa_t { */ bool reqid_allocated; + /** + * Is the reqid statically configured + */ + bool static_reqid; + /* * Unique CHILD_SA identifier */ @@ -698,7 +703,7 @@ METHOD(child_sa_t, install, status_t, this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS, &esn, NULL); - if (!this->reqid_allocated && !this->reqid) + 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, @@ -826,7 +831,7 @@ METHOD(child_sa_t, add_policies, status_t, traffic_selector_t *my_ts, *other_ts; status_t status = SUCCESS; - if (!this->reqid_allocated && !this->reqid) + if (!this->reqid_allocated && !this->static_reqid) { /* trap policy, get or confirm reqid */ status = hydra->kernel_interface->alloc_reqid( @@ -1305,6 +1310,10 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->reqid = charon->traps->find_reqid(charon->traps, config); } } + else + { + this->static_reqid = TRUE; + } /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ if (config->get_mode(config) == MODE_TRANSPORT && diff --git a/src/libcharon/sa/eap/eap_method.c b/src/libcharon/sa/eap/eap_method.c index a05e8c59a..9ce6ecf00 100644 --- a/src/libcharon/sa/eap/eap_method.c +++ b/src/libcharon/sa/eap/eap_method.c @@ -30,7 +30,8 @@ bool eap_method_register(plugin_t *plugin, plugin_feature_t *feature, { if (reg) { - charon->eap->add_method(charon->eap, feature->arg.eap, 0, + charon->eap->add_method(charon->eap, feature->arg.eap.type, + feature->arg.eap.vendor, feature->type == FEATURE_EAP_SERVER ? EAP_SERVER : EAP_PEER, (eap_constructor_t)data); } diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 3aafa4c13..dcf9d5f2c 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -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 @@ -487,8 +487,9 @@ METHOD(ike_sa_t, send_keepalive, void, send_keepalive_job_t *job; time_t last_out, now, diff; - if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0) - { /* disable keep alives if we are not NATed anymore */ + 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 */ return; } @@ -651,7 +652,7 @@ METHOD(ike_sa_t, get_state, ike_sa_state_t, METHOD(ike_sa_t, set_state, void, private_ike_sa_t *this, ike_sa_state_t state) { - bool trigger_dpd = FALSE; + bool trigger_dpd = FALSE, keepalives = FALSE; DBG2(DBG_IKE, "IKE_SA %s[%d] state change: %N => %N", get_name(this), this->unique_id, @@ -722,6 +723,10 @@ METHOD(ike_sa_t, set_state, void, * so yet, so prevent that. */ this->stats[STAT_INBOUND] = this->stats[STAT_ESTABLISHED]; } + if (this->state == IKE_PASSIVE) + { + keepalives = TRUE; + } } break; } @@ -742,6 +747,10 @@ METHOD(ike_sa_t, set_state, void, DBG1(DBG_IKE, "DPD not supported by peer, disabled"); } } + if (keepalives) + { + send_keepalive(this); + } } METHOD(ike_sa_t, reset, void, @@ -1200,6 +1209,19 @@ static void resolve_hosts(private_ike_sa_t *this) break; } + /* if an IP address is set locally, use the same family to resolve remote */ + if (family == AF_UNSPEC && !this->remote_host) + { + if (this->local_host) + { + family = this->local_host->get_family(this->local_host); + } + else + { + family = ike_cfg_get_family(this->ike_cfg, TRUE); + } + } + if (this->remote_host) { host = this->remote_host->clone(this->remote_host); @@ -1211,7 +1233,18 @@ static void resolve_hosts(private_ike_sa_t *this) } if (host) { - set_other_host(this, host); + if (!host->is_anyaddr(host) || + this->other_host->is_anyaddr(this->other_host)) + { /* don't set to %any if we currently have an address, but the + * address family might have changed */ + set_other_host(this, host); + } + else + { /* reuse the original port as some implementations might not like + * initial IKE messages on other ports */ + this->other_host->set_port(this->other_host, host->get_port(host)); + host->destroy(host); + } } if (this->local_host) diff --git a/src/libcharon/sa/ike_sa_id.c b/src/libcharon/sa/ike_sa_id.c index 0f0f1ab63..e52086483 100644 --- a/src/libcharon/sa/ike_sa_id.c +++ b/src/libcharon/sa/ike_sa_id.c @@ -18,7 +18,7 @@ #include "ike_sa_id.h" #include <stdio.h> - +#include <encoding/payloads/ike_header.h> typedef struct private_ike_sa_id_t private_ike_sa_id_t; @@ -90,6 +90,8 @@ METHOD(ike_sa_id_t, equals, bool, return FALSE; } return this->ike_version == other->ike_version && + (this->ike_version == IKEV1_MAJOR_VERSION || + this->is_initiator_flag == other->is_initiator_flag) && this->initiator_spi == other->initiator_spi && this->responder_spi == other->responder_spi; } diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 938f7848f..37d69874d 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-2012 Tobias Brunner + * Copyright (C) 2008-2015 Tobias Brunner * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -157,6 +157,8 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id) } if ((id->get_responder_spi(id) == 0 || entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) && + (id->get_ike_version(id) == IKEV1_MAJOR_VERSION || + id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id)) && id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id)) { /* this is TRUE for IKE_SAs that we initiated but have not yet received a response */ @@ -204,6 +206,9 @@ struct half_open_t { /** the number of half-open IKE_SAs with that host */ u_int count; + + /** the number of half-open IKE_SAs we responded to with that host */ + u_int count_responder; }; /** @@ -359,6 +364,11 @@ struct private_ike_sa_manager_t { refcount_t half_open_count; /** + * Total number of half-open IKE_SAs as responder. + */ + refcount_t half_open_count_responder; + + /** * Hash table with connected_peers_t objects. */ table_item_t **connected_peers_table; @@ -384,6 +394,11 @@ struct private_ike_sa_manager_t { rng_t *rng; /** + * Lock to access the RNG instance + */ + rwlock_t *rng_lock; + + /** * reuse existing IKE_SAs in checkout_by_config */ bool reuse_ikesa; @@ -730,9 +745,11 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) table_item_t *item; u_int row, segment; rwlock_t *lock; + ike_sa_id_t *ike_id; half_open_t *half_open; chunk_t addr; + ike_id = entry->ike_sa_id; addr = entry->other->get_address(entry->other); row = chunk_hash(addr) & this->table_mask; segment = row & this->segment_mask; @@ -745,7 +762,6 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) if (chunk_equals(addr, half_open->other)) { - half_open->count++; break; } item = item->next; @@ -755,7 +771,6 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) { INIT(half_open, .other = chunk_clone(addr), - .count = 1, ); INIT(item, .value = half_open, @@ -763,8 +778,14 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) ); this->half_open_table[row] = item; } - this->half_open_segments[segment].count++; + half_open->count++; ref_get(&this->half_open_count); + if (!ike_id->is_initiator(ike_id)) + { + half_open->count_responder++; + ref_get(&this->half_open_count_responder); + } + this->half_open_segments[segment].count++; lock->unlock(lock); } @@ -776,8 +797,10 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) table_item_t *item, *prev = NULL; u_int row, segment; rwlock_t *lock; + ike_sa_id_t *ike_id; chunk_t addr; + ike_id = entry->ike_sa_id; addr = entry->other->get_address(entry->other); row = chunk_hash(addr) & this->table_mask; segment = row & this->segment_mask; @@ -790,6 +813,12 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) if (chunk_equals(addr, half_open->other)) { + if (!ike_id->is_initiator(ike_id)) + { + half_open->count_responder--; + ignore_result(ref_put(&this->half_open_count_responder)); + } + ignore_result(ref_put(&this->half_open_count)); if (--half_open->count == 0) { if (prev) @@ -804,7 +833,6 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) free(item); } this->half_open_segments[segment].count--; - ignore_result(ref_put(&this->half_open_count)); break; } prev = item; @@ -943,12 +971,14 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this) { u_int64_t spi; - if (this->rng && - this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_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)) { - return spi; + spi = 0; } - return 0; + this->rng_lock->unlock(this->rng_lock); + return spi; } /** @@ -1563,7 +1593,6 @@ METHOD(ike_sa_manager_t, checkin, void, put_half_open(this, entry); } else if (!entry->half_open && - !entry->ike_sa_id->is_initiator(entry->ike_sa_id) && ike_sa->get_state(ike_sa) == IKE_CONNECTING) { /* this is a new half-open SA */ @@ -1579,6 +1608,12 @@ METHOD(ike_sa_manager_t, checkin, void, entry = entry_create(); entry->ike_sa_id = ike_sa_id->clone(ike_sa_id); entry->ike_sa = ike_sa; + if (ike_sa->get_state(ike_sa) == IKE_CONNECTING) + { + entry->half_open = TRUE; + entry->other = other->clone(other); + put_half_open(this, entry); + } segment = put_entry(this, entry); } @@ -1937,7 +1972,7 @@ METHOD(ike_sa_manager_t, get_count, u_int, } METHOD(ike_sa_manager_t, get_half_open_count, u_int, - private_ike_sa_manager_t *this, host_t *ip) + private_ike_sa_manager_t *this, host_t *ip, bool responder_only) { table_item_t *item; u_int row, segment; @@ -1959,7 +1994,8 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int, if (chunk_equals(addr, half_open->other)) { - count = half_open->count; + count = responder_only ? half_open->count_responder + : half_open->count; break; } item = item->next; @@ -1968,7 +2004,8 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int, } else { - count = (u_int)ref_cur(&this->half_open_count); + count = responder_only ? (u_int)ref_cur(&this->half_open_count_responder) + : (u_int)ref_cur(&this->half_open_count); } return count; } @@ -2055,8 +2092,10 @@ 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->rng->destroy(this->rng); this->rng = NULL; + this->rng_lock->unlock(this->rng_lock); } METHOD(ike_sa_manager_t, destroy, void, @@ -2081,6 +2120,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); free(this); } @@ -2138,6 +2178,7 @@ ike_sa_manager_t *ike_sa_manager_create() free(this); return NULL; } + this->rng_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 f259d8e56..3ea928ea5 100644 --- a/src/libcharon/sa/ike_sa_manager.h +++ b/src/libcharon/sa/ike_sa_manager.h @@ -216,14 +216,15 @@ struct ike_sa_manager_t { * To prevent the server from resource exhaustion, cookies and other * mechanisms are used. The number of half open IKE_SAs is a good * indicator to see if a peer is flooding the server. - * If a host is supplied, only the number of half open IKE_SAs initiated - * from this IP are counted. - * Only SAs for which we are the responder are counted. + * If a host is supplied, only the number of half open IKE_SAs with this IP + * are counted. * * @param ip NULL for all, IP for half open IKE_SAs with IP + * @param responder_only TRUE to return only the number of responding SAs * @return number of half open IKE_SAs */ - u_int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip); + u_int (*get_half_open_count)(ike_sa_manager_t *this, host_t *ip, + bool responder_only); /** * Delete all existing IKE_SAs and destroy them immediately. diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index c968b2a9c..b7047e8fc 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, NULL); + private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth); if (private) { switch (private->get_type(private)) diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index ed547c4c2..678f99df1 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2014 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -901,6 +901,34 @@ static bool process_dpd(private_task_manager_t *this, message_t *message) } /** + * Check if we already have a quick mode task queued for the exchange with the + * given message ID + */ +static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid) +{ + enumerator_t *enumerator; + quick_mode_t *qm; + 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_QUICK_MODE) + { + qm = (quick_mode_t*)task; + if (qm->get_mid(qm) == mid) + { + found = TRUE; + break; + } + } + } + enumerator->destroy(enumerator); + return found; +} + +/** * handle an incoming request message */ static status_t process_request(private_task_manager_t *this, @@ -911,6 +939,7 @@ static status_t process_request(private_task_manager_t *this, bool send_response = FALSE, dpd = FALSE; if (message->get_exchange_type(message) == INFORMATIONAL_V1 || + message->get_exchange_type(message) == QUICK_MODE || this->passive_tasks->get_count(this->passive_tasks) == 0) { /* create tasks depending on request type, if not already some queued */ switch (message->get_exchange_type(message)) @@ -946,6 +975,10 @@ static status_t process_request(private_task_manager_t *this, "unestablished IKE_SA, ignored"); return FAILED; } + if (have_quick_mode_task(this, message->get_message_id(message))) + { + break; + } task = (task_t *)quick_mode_create(this->ike_sa, NULL, NULL, NULL); this->passive_tasks->insert_last(this->passive_tasks, task); diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 96edfd8d8..d6a3f2cd1 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Hochschule fuer Technik Rapperswil * * Copyright (C) 2011 Martin Willi @@ -185,6 +185,11 @@ struct private_quick_mode_t { */ bool udp; + /** + * Message ID of handled quick mode exchange + */ + u_int32_t mid; + /** states of quick mode */ enum { QM_INIT, @@ -1019,6 +1024,11 @@ static void check_for_rekeyed_child(private_quick_mode_t *this) METHOD(task_t, process_r, status_t, private_quick_mode_t *this, message_t *message) { + if (this->mid && this->mid != message->get_message_id(message)) + { /* not responsible for this quick mode exchange */ + return NEED_MORE; + } + switch (this->state) { case QM_INIT: @@ -1188,6 +1198,11 @@ METHOD(task_t, process_r, status_t, METHOD(task_t, build_r, status_t, private_quick_mode_t *this, message_t *message) { + if (this->mid && this->mid != message->get_message_id(message)) + { /* not responsible for this quick mode exchange */ + return NEED_MORE; + } + switch (this->state) { case QM_INIT: @@ -1242,6 +1257,7 @@ METHOD(task_t, build_r, status_t, add_ts(this, message); this->state = QM_NEGOTIATED; + this->mid = message->get_message_id(message); return NEED_MORE; } case QM_NEGOTIATED: @@ -1335,6 +1351,12 @@ METHOD(task_t, get_type, task_type_t, return TASK_QUICK_MODE; } +METHOD(quick_mode_t, get_mid, u_int32_t, + private_quick_mode_t *this) +{ + return this->mid; +} + METHOD(quick_mode_t, use_reqid, void, private_quick_mode_t *this, u_int32_t reqid) { @@ -1368,6 +1390,7 @@ METHOD(task_t, migrate, void, this->ike_sa = ike_sa; this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa); this->state = QM_INIT; + this->mid = 0; this->tsi = NULL; this->tsr = NULL; this->proposal = NULL; @@ -1414,6 +1437,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, .migrate = _migrate, .destroy = _destroy, }, + .get_mid = _get_mid, .use_reqid = _use_reqid, .use_marks = _use_marks, .rekey = _rekey, diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.h b/src/libcharon/sa/ikev1/tasks/quick_mode.h index ee9b64d13..062d63465 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.h +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.h @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2011 Martin Willi * Copyright (C) 2011 revosec AG * @@ -38,6 +41,14 @@ struct quick_mode_t { task_t task; /** + * Get the message ID of the quick mode exchange handled by this task as + * responder. + * + * @return message ID, or 0 (not defined yet or as initiator) + */ + u_int32_t (*get_mid)(quick_mode_t *this); + + /** * Use a specific reqid to install this CHILD_SA. * * @param reqid reqid to use diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c index f1442096c..91f6187f9 100644 --- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c @@ -448,6 +448,8 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, identification_t *other_id; auth_cfg_t *auth; keymat_v2_t *keymat; + eap_type_t type; + u_int32_t vendor; auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH); @@ -478,6 +480,13 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, this->auth_complete = TRUE; auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); + + type = this->method->get_type(this->method, &vendor); + auth->add(auth, AUTH_RULE_EAP_TYPE, type); + if (vendor) + { + auth->add(auth, AUTH_RULE_EAP_VENDOR, vendor); + } return TRUE; } diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 151b49718..2284a484d 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -321,7 +321,7 @@ METHOD(authenticator_t, build, status_t, chunk_t auth_data; status_t status; auth_payload_t *auth_payload; - auth_method_t auth_method; + auth_method_t auth_method = AUTH_NONE; id = this->ike_sa->get_my_id(this->ike_sa); auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE); diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index 6fedc8eb5..fce0840e3 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -112,6 +112,7 @@ static bool derive_ike_aead(private_keymat_v2_t *this, u_int16_t alg, case ENCR_AES_GCM_ICV12: case ENCR_AES_GCM_ICV16: /* RFC 4106 */ + case ENCR_CHACHA20_POLY1305: salt_size = 4; break; case ENCR_AES_CCM_ICV8: @@ -527,6 +528,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool, case ENCR_AES_GCM_ICV16: case ENCR_AES_CTR: case ENCR_NULL_AUTH_AES_GMAC: + case ENCR_CHACHA20_POLY1305: enc_size += 4; break; default: diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index e0f930c3c..e08f3dab1 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -145,6 +145,11 @@ struct private_child_create_t { ipcomp_transform_t ipcomp_received; /** + * IPsec protocol + */ + protocol_id_t proto; + + /** * Own allocated SPI */ u_int32_t my_spi; @@ -260,23 +265,23 @@ static bool allocate_spi(private_child_create_t *this) { enumerator_t *enumerator; proposal_t *proposal; - protocol_id_t proto = PROTO_ESP; if (this->initiator) { + this->proto = PROTO_ESP; /* we just get a SPI for the first protocol. TODO: If we ever support * proposal lists with mixed protocols, we'd need multiple SPIs */ if (this->proposals->get_first(this->proposals, (void**)&proposal) == SUCCESS) { - proto = proposal->get_protocol(proposal); + this->proto = proposal->get_protocol(proposal); } } else { - proto = this->proposal->get_protocol(this->proposal); + this->proto = this->proposal->get_protocol(this->proposal); } - this->my_spi = this->child_sa->alloc_spi(this->child_sa, proto); + this->my_spi = this->child_sa->alloc_spi(this->child_sa, this->proto); if (this->my_spi) { if (this->initiator) @@ -1352,20 +1357,16 @@ METHOD(task_t, build_i_delete, status_t, private_child_create_t *this, message_t *message) { message->set_exchange_type(message, INFORMATIONAL); - if (this->child_sa && this->proposal) + if (this->my_spi && this->proto) { - protocol_id_t proto; delete_payload_t *del; - u_int32_t spi; - proto = this->proposal->get_protocol(this->proposal); - spi = this->child_sa->get_spi(this->child_sa, TRUE); - del = delete_payload_create(PLV2_DELETE, proto); - del->add_spi(del, spi); + del = delete_payload_create(PLV2_DELETE, this->proto); + del->add_spi(del, this->my_spi); message->add_payload(message, (payload_t*)del); DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", - protocol_id_names, proto, ntohl(spi)); + protocol_id_names, this->proto, ntohl(this->my_spi)); } return NEED_MORE; } @@ -1375,9 +1376,13 @@ METHOD(task_t, build_i_delete, status_t, */ static status_t delete_failed_sa(private_child_create_t *this) { - this->public.task.build = _build_i_delete; - this->public.task.process = (void*)return_success; - return NEED_MORE; + if (this->my_spi && this->proto) + { + this->public.task.build = _build_i_delete; + this->public.task.process = (void*)return_success; + return NEED_MORE; + } + return SUCCESS; } METHOD(task_t, process_i, status_t, @@ -1596,6 +1601,7 @@ METHOD(task_t, migrate, void, this->tsi = NULL; this->tsr = NULL; this->dh = NULL; + this->nonceg = NULL; this->child_sa = NULL; this->mode = MODE_TUNNEL; this->ipcomp = IPCOMP_NONE; diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index c806e19ca..c7a8a1342 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -170,13 +170,8 @@ METHOD(task_t, build_i, status_t, } config = this->child_sa->get_config(this->child_sa); - /* we just need the rekey notify ... */ - notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY, - this->protocol, REKEY_SA); - notify->set_spi(notify, this->spi); - message->add_payload(message, (payload_t*)notify); - /* ... our CHILD_CREATE task does the hard work for us. */ + /* our CHILD_CREATE task does the hard work for us */ if (!this->child_create) { this->child_create = child_create_create(this->ike_sa, @@ -194,6 +189,14 @@ METHOD(task_t, build_i, status_t, schedule_delayed_rekey(this); return FAILED; } + if (message->get_exchange_type(message) == CREATE_CHILD_SA) + { + /* don't add the notify if the CHILD_CREATE task changed the exchange */ + notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY, + this->protocol, REKEY_SA); + notify->set_spi(notify, this->spi); + message->add_payload(message, (payload_t*)notify); + } this->child_sa->set_state(this->child_sa, CHILD_REKEYING); return NEED_MORE; @@ -334,8 +337,7 @@ METHOD(task_t, process_i, status_t, if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE) { - /* bad DH group while rekeying, try again */ - this->child_create->task.migrate(&this->child_create->task, this->ike_sa); + /* bad DH group while rekeying, retry, or failure requiring deletion */ return NEED_MORE; } if (message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL) diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c index 1855517ce..eaba04e3a 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c @@ -116,7 +116,6 @@ static void establish_new(private_ike_rekey_t *this) lib->processor->queue_job(lib->processor, job); } this->new_sa = NULL; - /* set threads active IKE_SA after checkin */ charon->bus->set_sa(charon->bus, this->ike_sa); } } @@ -229,9 +228,10 @@ METHOD(task_t, build_r, status_t, if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED) { + charon->bus->set_sa(charon->bus, this->ike_sa); return SUCCESS; } - + charon->bus->set_sa(charon->bus, this->ike_sa); this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); /* rekeying successful, delete the IKE_SA using a subtask */ @@ -335,15 +335,13 @@ METHOD(task_t, process_i, status_t, { charon->ike_sa_manager->checkin( charon->ike_sa_manager, this->new_sa); - /* set threads active IKE_SA after checkin */ - charon->bus->set_sa(charon->bus, this->ike_sa); } + charon->bus->set_sa(charon->bus, this->ike_sa); this->new_sa = NULL; establish_new(other); return SUCCESS; } } - /* set threads active IKE_SA after checkin */ charon->bus->set_sa(charon->bus, this->ike_sa); } @@ -372,9 +370,13 @@ METHOD(ike_rekey_t, collide, void, this->collision = other; } -METHOD(task_t, migrate, void, - private_ike_rekey_t *this, ike_sa_t *ike_sa) +/** + * Cleanup the task + */ +static void cleanup(private_ike_rekey_t *this) { + ike_sa_t *cur_sa; + if (this->ike_init) { this->ike_init->task.destroy(&this->ike_init->task); @@ -383,9 +385,16 @@ METHOD(task_t, migrate, void, { this->ike_delete->task.destroy(&this->ike_delete->task); } + cur_sa = charon->bus->get_sa(charon->bus); DESTROY_IF(this->new_sa); + charon->bus->set_sa(charon->bus, cur_sa); DESTROY_IF(this->collision); +} +METHOD(task_t, migrate, void, + private_ike_rekey_t *this, ike_sa_t *ike_sa) +{ + cleanup(this); this->collision = NULL; this->ike_sa = ike_sa; this->new_sa = NULL; @@ -396,16 +405,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_ike_rekey_t *this) { - if (this->ike_init) - { - this->ike_init->task.destroy(&this->ike_init->task); - } - if (this->ike_delete) - { - this->ike_delete->task.destroy(&this->ike_delete->task); - } - DESTROY_IF(this->new_sa); - DESTROY_IF(this->collision); + cleanup(this); free(this); } diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index 73e1abbf3..1a984435c 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -18,8 +19,10 @@ #include <hydra.h> #include <daemon.h> #include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> #include <collections/linked_list.h> +#define INSTALL_DISABLED ((u_int)~0) typedef struct private_shunt_manager_t private_shunt_manager_t; @@ -37,6 +40,21 @@ struct private_shunt_manager_t { * Installed shunts, as child_cfg_t */ linked_list_t *shunts; + + /** + * Lock to safely access the list of shunts + */ + rwlock_t *lock; + + /** + * Number of threads currently installing shunts, or INSTALL_DISABLED + */ + u_int installing; + + /** + * Condvar to signal shunt installation + */ + rwlock_condvar_t *condvar; }; /** @@ -117,9 +135,15 @@ METHOD(shunt_manager_t, install, bool, { enumerator_t *enumerator; child_cfg_t *child_cfg; - bool found = FALSE; + bool found = FALSE, success; /* check if not already installed */ + this->lock->write_lock(this->lock); + if (this->installing == INSTALL_DISABLED) + { /* flush() has been called */ + this->lock->unlock(this->lock); + return FALSE; + } enumerator = this->shunts->create_enumerator(this->shunts); while (enumerator->enumerate(enumerator, &child_cfg)) { @@ -130,16 +154,29 @@ METHOD(shunt_manager_t, install, bool, } } enumerator->destroy(enumerator); - if (found) { DBG1(DBG_CFG, "shunt %N policy '%s' already installed", ipsec_mode_names, child->get_mode(child), child->get_name(child)); + this->lock->unlock(this->lock); return TRUE; } this->shunts->insert_last(this->shunts, child->get_ref(child)); + this->installing++; + this->lock->unlock(this->lock); - return install_shunt_policy(child); + success = install_shunt_policy(child); + + this->lock->write_lock(this->lock); + if (!success) + { + this->shunts->remove(this->shunts, child, NULL); + child->destroy(child); + } + this->installing--; + this->condvar->signal(this->condvar); + this->lock->unlock(this->lock); + return success; } /** @@ -215,6 +252,7 @@ METHOD(shunt_manager_t, uninstall, bool, enumerator_t *enumerator; child_cfg_t *child, *found = NULL; + this->lock->write_lock(this->lock); enumerator = this->shunts->create_enumerator(this->shunts); while (enumerator->enumerate(enumerator, &child)) { @@ -226,6 +264,7 @@ METHOD(shunt_manager_t, uninstall, bool, } } enumerator->destroy(enumerator); + this->lock->unlock(this->lock); if (!found) { @@ -239,20 +278,37 @@ METHOD(shunt_manager_t, uninstall, bool, METHOD(shunt_manager_t, create_enumerator, enumerator_t*, private_shunt_manager_t *this) { - return this->shunts->create_enumerator(this->shunts); + this->lock->read_lock(this->lock); + return enumerator_create_cleaner( + this->shunts->create_enumerator(this->shunts), + (void*)this->lock->unlock, this->lock); } -METHOD(shunt_manager_t, destroy, void, +METHOD(shunt_manager_t, flush, void, private_shunt_manager_t *this) { child_cfg_t *child; + this->lock->write_lock(this->lock); + while (this->installing) + { + this->condvar->wait(this->condvar, this->lock); + } while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS) { uninstall_shunt_policy(child); child->destroy(child); } - this->shunts->destroy(this->shunts); + this->installing = INSTALL_DISABLED; + this->lock->unlock(this->lock); +} + +METHOD(shunt_manager_t, destroy, void, + private_shunt_manager_t *this) +{ + this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy)); + this->lock->destroy(this->lock); + this->condvar->destroy(this->condvar); free(this); } @@ -268,9 +324,12 @@ shunt_manager_t *shunt_manager_create() .install = _install, .uninstall = _uninstall, .create_enumerator = _create_enumerator, + .flush = _flush, .destroy = _destroy, }, .shunts = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), ); return &this->public; diff --git a/src/libcharon/sa/shunt_manager.h b/src/libcharon/sa/shunt_manager.h index 28a795dc9..c43f5db3d 100644 --- a/src/libcharon/sa/shunt_manager.h +++ b/src/libcharon/sa/shunt_manager.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015 Tobias Brunner * Copyright (C) 2011 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * @@ -56,6 +57,11 @@ struct shunt_manager_t { enumerator_t* (*create_enumerator)(shunt_manager_t *this); /** + * Clear any installed shunt. + */ + void (*flush)(shunt_manager_t *this); + + /** * Destroy a shunt_manager_t. */ void (*destroy)(shunt_manager_t *this); diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index d6ff3c8c5..63505c960 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Tobias Brunner + * Copyright (C) 2011-2015 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -18,9 +18,12 @@ #include <hydra.h> #include <daemon.h> +#include <threading/mutex.h> #include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> #include <collections/linked_list.h> +#define INSTALL_DISABLED ((u_int)~0) typedef struct private_trap_manager_t private_trap_manager_t; typedef struct trap_listener_t trap_listener_t; @@ -67,6 +70,26 @@ struct private_trap_manager_t { trap_listener_t listener; /** + * list of acquires we currently handle + */ + linked_list_t *acquires; + + /** + * mutex for list of acquires + */ + mutex_t *mutex; + + /** + * number of threads currently installing trap policies, or INSTALL_DISABLED + */ + u_int installing; + + /** + * condvar to signal trap policy installation + */ + rwlock_condvar_t *condvar; + + /** * Whether to ignore traffic selectors from acquires */ bool ignore_acquire_ts; @@ -80,23 +103,58 @@ typedef struct { char *name; /** ref to peer_cfg to initiate */ peer_cfg_t *peer_cfg; - /** ref to instanciated CHILD_SA */ + /** ref to instantiated CHILD_SA (i.e the trap policy) */ child_sa_t *child_sa; - /** TRUE if an acquire is pending */ - bool pending; + /** TRUE in case of wildcard Transport Mode SA */ + bool wildcard; +} entry_t; + +/** + * A handled acquire + */ +typedef struct { /** pending IKE_SA connecting upon acquire */ ike_sa_t *ike_sa; -} entry_t; + /** reqid of pending trap policy */ + u_int32_t reqid; + /** destination address (wildcard case) */ + host_t *dst; +} acquire_t; /** * actually uninstall and destroy an installed entry */ -static void destroy_entry(entry_t *entry) +static void destroy_entry(entry_t *this) +{ + this->child_sa->destroy(this->child_sa); + this->peer_cfg->destroy(this->peer_cfg); + free(this->name); + free(this); +} + +/** + * destroy a cached acquire entry + */ +static void destroy_acquire(acquire_t *this) { - entry->child_sa->destroy(entry->child_sa); - entry->peer_cfg->destroy(entry->peer_cfg); - free(entry->name); - free(entry); + DESTROY_IF(this->dst); + free(this); +} + +/** + * match an acquire entry by reqid + */ +static bool acquire_by_reqid(acquire_t *this, u_int32_t *reqid) +{ + return this->reqid == *reqid; +} + +/** + * match an acquire entry by destination address + */ +static bool acquire_by_dst(acquire_t *this, host_t *dst) +{ + return this->dst && this->dst->ip_equals(this->dst, dst); } METHOD(trap_manager_t, install, u_int32_t, @@ -113,32 +171,49 @@ METHOD(trap_manager_t, install, u_int32_t, linked_list_t *proposals; proposal_t *proposal; protocol_id_t proto = PROTO_ESP; + bool wildcard = FALSE; /* try to resolve addresses */ ike_cfg = peer->get_ike_cfg(peer); other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC); - if (!other || other->is_anyaddr(other)) + if (other && other->is_anyaddr(other) && + child->get_mode(child) == MODE_TRANSPORT) + { + /* allow wildcard for Transport Mode SAs */ + me = host_create_any(other->get_family(other)); + wildcard = TRUE; + } + else if (!other || other->is_anyaddr(other)) { DESTROY_IF(other); DBG1(DBG_CFG, "installing trap failed, remote address unknown"); return 0; } - me = ike_cfg->resolve_me(ike_cfg, other->get_family(other)); - if (!me || me->is_anyaddr(me)) + else { - DESTROY_IF(me); - me = hydra->kernel_interface->get_source_addr( - hydra->kernel_interface, other, NULL); - if (!me) + me = ike_cfg->resolve_me(ike_cfg, other->get_family(other)); + if (!me || me->is_anyaddr(me)) { - DBG1(DBG_CFG, "installing trap failed, local address unknown"); - other->destroy(other); - return 0; + DESTROY_IF(me); + me = hydra->kernel_interface->get_source_addr( + hydra->kernel_interface, other, NULL); + if (!me) + { + DBG1(DBG_CFG, "installing trap failed, local address unknown"); + other->destroy(other); + return 0; + } + me->set_port(me, ike_cfg->get_my_port(ike_cfg)); } - me->set_port(me, ike_cfg->get_my_port(ike_cfg)); } this->lock->write_lock(this->lock); + if (this->installing == INSTALL_DISABLED) + { /* flush() has been called */ + this->lock->unlock(this->lock); + me->destroy(me); + return 0; + } enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { @@ -160,6 +235,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); + me->destroy(me); return 0; } /* config might have changed so update everything */ @@ -170,8 +246,10 @@ METHOD(trap_manager_t, install, u_int32_t, INIT(entry, .name = strdup(child->get_name(child)), .peer_cfg = peer->get_ref(peer), + .wildcard = wildcard, ); this->traps->insert_first(this->traps, entry); + this->installing++; /* don't hold lock while creating CHILD_SA and installing policies */ this->lock->unlock(this->lock); @@ -220,6 +298,11 @@ METHOD(trap_manager_t, install, u_int32_t, { destroy_entry(found); } + this->lock->write_lock(this->lock); + /* do this at the end, so entries created temporarily are also destroyed */ + this->installing--; + this->condvar->signal(this->condvar); + this->lock->unlock(this->lock); return reqid; } @@ -314,9 +397,12 @@ METHOD(trap_manager_t, acquire, void, { enumerator_t *enumerator; entry_t *entry, *found = NULL; + acquire_t *acquire; peer_cfg_t *peer; child_cfg_t *child; ike_sa_t *ike_sa; + host_t *host; + bool wildcard, ignore = FALSE; this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); @@ -333,11 +419,52 @@ METHOD(trap_manager_t, acquire, void, if (!found) { - DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid); + DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid); this->lock->unlock(this->lock); return; } - if (!cas_bool(&found->pending, FALSE, TRUE)) + reqid = found->child_sa->get_reqid(found->child_sa); + wildcard = found->wildcard; + + this->mutex->lock(this->mutex); + if (wildcard) + { /* for wildcard acquires we check that we don't have a pending acquire + * with the same peer */ + u_int8_t mask; + + dst->to_subnet(dst, &host, &mask); + if (this->acquires->find_first(this->acquires, (void*)acquire_by_dst, + (void**)&acquire, host) == SUCCESS) + { + host->destroy(host); + ignore = TRUE; + } + else + { + INIT(acquire, + .dst = host, + .reqid = reqid, + ); + this->acquires->insert_last(this->acquires, acquire); + } + } + else + { + if (this->acquires->find_first(this->acquires, (void*)acquire_by_reqid, + (void**)&acquire, &reqid) == SUCCESS) + { + ignore = TRUE; + } + else + { + INIT(acquire, + .reqid = reqid, + ); + this->acquires->insert_last(this->acquires, acquire); + } + } + this->mutex->unlock(this->mutex); + if (ignore) { DBG1(DBG_CFG, "ignoring acquire, connection attempt pending"); this->lock->unlock(this->lock); @@ -346,12 +473,40 @@ METHOD(trap_manager_t, acquire, void, peer = found->peer_cfg->get_ref(found->peer_cfg); child = found->child_sa->get_config(found->child_sa); child = child->get_ref(child); - reqid = found->child_sa->get_reqid(found->child_sa); /* don't hold the lock while checking out the IKE_SA */ this->lock->unlock(this->lock); - ike_sa = charon->ike_sa_manager->checkout_by_config( + if (wildcard) + { /* the peer config would match IKE_SAs with other peers */ + ike_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + peer->get_ike_version(peer), TRUE); + if (ike_sa) + { + ike_cfg_t *ike_cfg; + u_int16_t port; + u_int8_t mask; + + ike_sa->set_peer_cfg(ike_sa, peer); + ike_cfg = ike_sa->get_ike_cfg(ike_sa); + + port = ike_cfg->get_other_port(ike_cfg); + dst->to_subnet(dst, &host, &mask); + host->set_port(host, port); + ike_sa->set_other_host(ike_sa, host); + + port = ike_cfg->get_my_port(ike_cfg); + src->to_subnet(src, &host, &mask); + host->set_port(host, port); + ike_sa->set_my_host(ike_sa, host); + + charon->bus->set_sa(charon->bus, ike_sa); + } + } + else + { + ike_sa = charon->ike_sa_manager->checkout_by_config( charon->ike_sa_manager, peer); + } if (ike_sa) { if (ike_sa->get_peer_cfg(ike_sa) == NULL) @@ -363,24 +518,29 @@ METHOD(trap_manager_t, acquire, void, * have a single TS that we can establish in a Quick Mode. */ src = dst = NULL; } + + this->mutex->lock(this->mutex); + acquire->ike_sa = ike_sa; + this->mutex->unlock(this->mutex); + if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME) { - /* make sure the entry is still there */ - this->lock->read_lock(this->lock); - if (this->traps->find_first(this->traps, NULL, - (void**)&found) == SUCCESS) - { - found->ike_sa = ike_sa; - } - this->lock->unlock(this->lock); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } else { - ike_sa->destroy(ike_sa); - charon->bus->set_sa(charon->bus, NULL); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + ike_sa); } } + else + { + this->mutex->lock(this->mutex); + this->acquires->remove(this->acquires, acquire, NULL); + this->mutex->unlock(this->mutex); + destroy_acquire(acquire); + child->destroy(child); + } peer->destroy(peer); } @@ -391,26 +551,33 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa) { enumerator_t *enumerator; - entry_t *entry; + acquire_t *acquire; - this->lock->read_lock(this->lock); - enumerator = this->traps->create_enumerator(this->traps); - while (enumerator->enumerate(enumerator, &entry)) + this->mutex->lock(this->mutex); + enumerator = this->acquires->create_enumerator(this->acquires); + while (enumerator->enumerate(enumerator, &acquire)) { - if (entry->ike_sa != ike_sa) + if (!acquire->ike_sa || acquire->ike_sa != ike_sa) { continue; } - if (child_sa && child_sa->get_reqid(child_sa) != - entry->child_sa->get_reqid(entry->child_sa)) + if (child_sa) { - continue; + if (acquire->dst) + { + /* since every wildcard acquire results in a separate IKE_SA + * there is no need to compare the destination address */ + } + else if (child_sa->get_reqid(child_sa) != acquire->reqid) + { + continue; + } } - entry->ike_sa = NULL; - entry->pending = FALSE; + this->acquires->remove_at(this->acquires, enumerator); + destroy_acquire(acquire); } enumerator->destroy(enumerator); - this->lock->unlock(this->lock); + this->mutex->unlock(this->mutex); } METHOD(listener_t, ike_state_change, bool, @@ -444,14 +611,15 @@ METHOD(listener_t, child_state_change, bool, METHOD(trap_manager_t, flush, void, private_trap_manager_t *this) { - linked_list_t *traps; - /* since destroying the CHILD_SA results in events which require a read - * lock we cannot destroy the list while holding the write lock */ this->lock->write_lock(this->lock); - traps = this->traps; + while (this->installing) + { + this->condvar->wait(this->condvar, this->lock); + } + this->traps->destroy_function(this->traps, (void*)destroy_entry); this->traps = linked_list_create(); + this->installing = INSTALL_DISABLED; this->lock->unlock(this->lock); - traps->destroy_function(traps, (void*)destroy_entry); } METHOD(trap_manager_t, destroy, void, @@ -459,6 +627,9 @@ METHOD(trap_manager_t, destroy, void, { charon->bus->remove_listener(charon->bus, &this->listener.listener); this->traps->destroy_function(this->traps, (void*)destroy_entry); + this->acquires->destroy_function(this->acquires, (void*)destroy_acquire); + this->condvar->destroy(this->condvar); + this->mutex->destroy(this->mutex); this->lock->destroy(this->lock); free(this); } @@ -488,7 +659,10 @@ trap_manager_t *trap_manager_create(void) }, }, .traps = linked_list_create(), + .acquires = linked_list_create(), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), .ignore_acquire_ts = lib->settings->get_bool(lib->settings, "%s.ignore_acquire_ts", FALSE, lib->ns), ); |