From 5313d2d78ca150515f7f5eb39801c100690b6b29 Mon Sep 17 00:00:00 2001 From: Yves-Alexis Perez Date: Fri, 1 Nov 2013 13:32:07 +0100 Subject: Imported Upstream version 5.1.1 --- src/libcharon/sa/child_sa.c | 19 +- src/libcharon/sa/ike_sa.c | 30 ++- src/libcharon/sa/ike_sa_manager.c | 58 ++++-- src/libcharon/sa/ikev1/keymat_v1.c | 8 + src/libcharon/sa/ikev1/task_manager_v1.c | 54 +++-- src/libcharon/sa/ikev1/tasks/aggressive_mode.c | 67 ++++-- src/libcharon/sa/ikev1/tasks/main_mode.c | 66 ++++-- src/libcharon/sa/ikev1/tasks/mode_config.c | 269 +++++++++++++++++++++---- src/libcharon/sa/ikev1/tasks/mode_config.h | 3 +- src/libcharon/sa/ikev1/tasks/quick_delete.c | 2 +- src/libcharon/sa/ikev1/tasks/quick_mode.c | 91 +++++++-- src/libcharon/sa/ikev1/tasks/xauth.c | 2 +- src/libcharon/sa/ikev2/task_manager_v2.c | 20 +- src/libcharon/sa/ikev2/tasks/child_create.c | 18 +- src/libcharon/sa/ikev2/tasks/child_delete.c | 7 +- src/libcharon/sa/keymat.c | 1 + src/libcharon/sa/trap_manager.c | 42 ++-- src/libcharon/sa/xauth/xauth_manager.c | 17 +- src/libcharon/sa/xauth/xauth_manager.h | 6 +- src/libcharon/sa/xauth/xauth_method.h | 4 +- 20 files changed, 606 insertions(+), 178 deletions(-) (limited to 'src/libcharon/sa') diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 46e4b6f7b..9c3876a94 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -167,12 +167,12 @@ struct private_child_sa_t { /** * time of last use in seconds (inbound) */ - u_int32_t my_usetime; + time_t my_usetime; /** * time of last use in seconds (outbound) */ - u_int32_t other_usetime; + time_t other_usetime; /** * last number of inbound bytes @@ -429,7 +429,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { status_t status = FAILED; u_int64_t bytes, packets; - u_int32_t time; + time_t time; if (inbound) { @@ -489,12 +489,12 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; - u_int32_t last_use = 0; + time_t last_use = 0; enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { - u_int32_t in, out, fwd; + time_t in, out, fwd; if (inbound) { @@ -594,6 +594,9 @@ METHOD(child_sa_t, alloc_spi, u_int32_t, proto_ike2ip(protocol), this->reqid, &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 */ + this->protocol = protocol; return this->my_spi; } return 0; @@ -1039,12 +1042,6 @@ METHOD(child_sa_t, destroy, void, /* delete SAs in the kernel, if they are set up */ if (this->my_spi) { - /* if CHILD was not established, use PROTO_ESP used during alloc_spi(). - * TODO: For AH support, we have to store protocol specific SPI.s */ - if (this->protocol == PROTO_NONE) - { - this->protocol = PROTO_ESP; - } hydra->kernel_interface->del_sa(hydra->kernel_interface, this->other_addr, this->my_addr, this->my_spi, proto_ike2ip(this->protocol), this->my_cpi, diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 2f4e1123c..028208782 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1077,7 +1077,7 @@ METHOD(ike_sa_t, initiate_mediated, status_t, static void resolve_hosts(private_ike_sa_t *this) { host_t *host; - int family = 0; + int family = AF_UNSPEC; switch (charon->socket->supported_families(charon->socket)) { @@ -1099,12 +1099,7 @@ static void resolve_hosts(private_ike_sa_t *this) } else { - char *other_addr; - u_int16_t other_port; - - other_addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL); - other_port = this->ike_cfg->get_other_port(this->ike_cfg); - host = host_create_from_dns(other_addr, family, other_port); + host = this->ike_cfg->resolve_other(this->ike_cfg, family); } if (host) { @@ -1118,17 +1113,12 @@ static void resolve_hosts(private_ike_sa_t *this) } else { - char *my_addr; - u_int16_t my_port; - /* use same address family as for other */ if (!this->other_host->is_anyaddr(this->other_host)) { family = this->other_host->get_family(this->other_host); } - my_addr = this->ike_cfg->get_my_addr(this->ike_cfg, NULL); - my_port = this->ike_cfg->get_my_port(this->ike_cfg); - host = host_create_from_dns(my_addr, family, my_port); + host = this->ike_cfg->resolve_me(this->ike_cfg, family); if (host && host->is_anyaddr(host) && !this->other_host->is_anyaddr(this->other_host)) @@ -1142,7 +1132,7 @@ static void resolve_hosts(private_ike_sa_t *this) } else { /* fallback to address family specific %any(6), if configured */ - host = host_create_from_dns(my_addr, family, my_port); + host = this->ike_cfg->resolve_me(this->ike_cfg, family); } } } @@ -1172,8 +1162,14 @@ METHOD(ike_sa_t, initiate, status_t, #endif /* ME */ ) { - char *addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL); - bool is_anyaddr = streq(addr, "%any") || streq(addr, "%any6"); + bool is_anyaddr; + host_t *host; + char *addr; + + addr = this->ike_cfg->get_my_addr(this->ike_cfg); + host = this->ike_cfg->resolve_other(this->ike_cfg, AF_UNSPEC); + is_anyaddr = host && host->is_anyaddr(host); + DESTROY_IF(host); if (is_anyaddr || !this->retry_initiate_interval) { @@ -1659,6 +1655,8 @@ METHOD(ike_sa_t, reestablish, status_t, new->set_other_host(new, host->clone(host)); host = this->my_host; new->set_my_host(new, host->clone(host)); + /* resolve hosts but use the old addresses above 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)) diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 4fbc4da8e..5768803aa 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -28,6 +28,7 @@ #include #include #include +#include /* the default size of the hash table (MUST be a power of 2) */ #define DEFAULT_HASHTABLE_SIZE 1 @@ -1764,6 +1765,40 @@ static void adopt_children(ike_sa_t *old, ike_sa_t *new) enumerator->destroy(enumerator); } +/** + * Check if the replaced IKE_SA might get reauthenticated from host + */ +static bool is_ikev1_reauth(ike_sa_t *duplicate, host_t *host) +{ + return duplicate->get_version(duplicate) == IKEV1 && + host->equals(host, duplicate->get_other_host(duplicate)); +} + +/** + * Delete an existing IKE_SA due to a unique replace policy + */ +static status_t enforce_replace(private_ike_sa_manager_t *this, + ike_sa_t *duplicate, ike_sa_t *new, + identification_t *other, host_t *host) +{ + charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE); + + if (is_ikev1_reauth(duplicate, host)) + { + /* looks like a reauthentication attempt */ + adopt_children(duplicate, new); + /* For IKEv1 we have to delay the delete for the old IKE_SA. Some + * peers need to complete the new SA first, otherwise the quick modes + * might get lost. */ + lib->scheduler->schedule_job(lib->scheduler, (job_t*) + delete_ike_sa_job_create(duplicate->get_id(duplicate), TRUE), 10); + return SUCCESS; + } + DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer '%Y' due to " + "uniqueness policy", other); + return duplicate->delete(duplicate); +} + METHOD(ike_sa_manager_t, check_uniqueness, bool, private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace) { @@ -1815,20 +1850,17 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool, switch (policy) { case UNIQUE_REPLACE: - charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE); - if (duplicate->get_version(duplicate) == IKEV1) - { - adopt_children(duplicate, ike_sa); - } - DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer " - "'%Y' due to uniqueness policy", other); - status = duplicate->delete(duplicate); + status = enforce_replace(this, duplicate, ike_sa, + other, other_host); break; case UNIQUE_KEEP: - cancel = TRUE; - /* we keep the first IKE_SA and delete all - * other duplicates that might exist */ - policy = UNIQUE_REPLACE; + if (!is_ikev1_reauth(duplicate, other_host)) + { + cancel = TRUE; + /* we keep the first IKE_SA and delete all + * other duplicates that might exist */ + policy = UNIQUE_REPLACE; + } break; default: break; @@ -2101,7 +2133,7 @@ ike_sa_manager_t *ike_sa_manager_create() }, ); - this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_PREFERRED); + this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); if (this->hasher == NULL) { DBG1(DBG_MGR, "manager initialization failed, no hasher supported"); diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c index 39e4cad20..bf1b0046c 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.c +++ b/src/libcharon/sa/ikev1/keymat_v1.c @@ -196,6 +196,13 @@ METHOD(aead_t, get_iv_size, size_t, return 0; } +METHOD(aead_t, get_iv_gen, iv_gen_t*, + private_aead_t *this) +{ + /* IVs are retrieved via keymat_v1.get_iv() */ + return NULL; +} + METHOD(aead_t, get_key_size, size_t, private_aead_t *this) { @@ -304,6 +311,7 @@ static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e) .get_block_size = _get_block_size, .get_icv_size = _get_icv_size, .get_iv_size = _get_iv_size, + .get_iv_gen = _get_iv_gen, .get_key_size = _get_key_size, .set_key = _set_key, .destroy = _aead_destroy, diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 857cb027e..597416e36 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -413,7 +413,6 @@ static bool send_packet(private_task_manager_t *this, bool request, { bool use_frags = FALSE; ike_cfg_t *ike_cfg; - host_t *src, *dst; chunk_t data; ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); @@ -438,15 +437,18 @@ static bool send_packet(private_task_manager_t *this, bool request, fragment_payload_t *fragment; u_int8_t num, count; size_t len, frag_size; - bool nat; - - /* reduce size due to non-ESP marker */ - nat = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); - frag_size = this->frag.size - (nat ? 4 : 0); + host_t *src, *dst; src = packet->get_source(packet); dst = packet->get_destination(packet); - count = (data.len / (frag_size + 1)) + 1; + + frag_size = this->frag.size; + if (dst->get_port(dst) != IKEV2_UDP_PORT && + src->get_port(src) != IKEV2_UDP_PORT) + { /* reduce size due to non-ESP marker */ + frag_size -= 4; + } + count = data.len / frag_size + (data.len % frag_size ? 1 : 0); DBG1(DBG_IKE, "sending IKE message with length of %zu bytes in " "%hhu fragments", data.len, count); @@ -537,23 +539,40 @@ static bool mode_config_expected(private_task_manager_t *this) enumerator_t *enumerator; peer_cfg_t *peer_cfg; char *pool; + bool local; host_t *host; peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (peer_cfg) { - enumerator = peer_cfg->create_pool_enumerator(peer_cfg); - if (!enumerator->enumerate(enumerator, &pool)) - { /* no pool configured */ + if (peer_cfg->use_pull_mode(peer_cfg)) + { + enumerator = peer_cfg->create_pool_enumerator(peer_cfg); + if (!enumerator->enumerate(enumerator, &pool)) + { /* no pool configured */ + enumerator->destroy(enumerator); + return FALSE; + } enumerator->destroy(enumerator); - return FALSE; + + local = FALSE; } - enumerator->destroy(enumerator); + else + { + enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); + if (!enumerator->enumerate(enumerator, &host)) + { /* not requesting a vip */ + enumerator->destroy(enumerator); + return FALSE; + } + enumerator->destroy(enumerator); + local = TRUE; + } enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, - FALSE); + local); if (!enumerator->enumerate(enumerator, &host)) - { /* have a pool, but no VIP assigned yet */ + { /* expecting a VIP exchange, but no VIP assigned yet */ enumerator->destroy(enumerator); return TRUE; } @@ -1085,7 +1104,8 @@ static status_t process_request(private_task_manager_t *this, case TRANSACTION: if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) { - task = (task_t *)mode_config_create(this->ike_sa, FALSE); + task = (task_t *)mode_config_create(this->ike_sa, + FALSE, TRUE); } else { @@ -1253,7 +1273,7 @@ static status_t handle_fragment(private_task_manager_t *this, message_t *msg) return FAILED; } - if (this->frag.id != payload->get_id(payload)) + if (!this->frag.list || this->frag.id != payload->get_id(payload)) { clear_fragments(this, payload->get_id(payload)); this->frag.list = linked_list_create(); @@ -1765,7 +1785,7 @@ static bool have_equal_ts(child_sa_t *child1, child_sa_t *child2, bool local) { equal = ts1->equals(ts1, ts2); } - e1->destroy(e1); + e2->destroy(e2); e1->destroy(e1); return equal; diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c index 6b00706bf..46cbb879b 100644 --- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c +++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c @@ -196,6 +196,17 @@ static status_t send_delete(private_aggressive_mode_t *this) return ALREADY_DONE; } +/** + * Schedule a timeout for the IKE_SA should it not establish + */ +static void schedule_timeout(ike_sa_t *ike_sa) +{ + job_t *job; + + job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE); + lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); +} + METHOD(task_t, build_i, status_t, private_aggressive_mode_t *this, message_t *message) { @@ -300,20 +311,15 @@ METHOD(task_t, build_i, status_t, case AUTH_XAUTH_INIT_PSK: case AUTH_XAUTH_INIT_RSA: case AUTH_HYBRID_INIT_RSA: - { /* wait for XAUTH request, since this may never come, - * we queue a timeout */ - job_t *job = (job_t*)delete_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), FALSE); - lib->scheduler->schedule_job(lib->scheduler, job, - HALF_OPEN_IKE_SA_TIMEOUT); + /* wait for XAUTH request */ + schedule_timeout(this->ike_sa); break; - } case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; default: if (charon->ike_sa_manager->check_uniqueness( charon->ike_sa_manager, this->ike_sa, FALSE)) @@ -328,10 +334,30 @@ METHOD(task_t, build_i, status_t, } break; } + /* check for and prepare mode config push/pull */ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + else + { + schedule_timeout(this->ike_sa); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) + { + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + schedule_timeout(this->ike_sa); + } + else + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } @@ -482,7 +508,7 @@ METHOD(task_t, process_r, status_t, case AUTH_HYBRID_INIT_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: @@ -505,11 +531,22 @@ METHOD(task_t, process_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && - this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) + /* check for and prepare mode config push/pull */ + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) + { + if (!this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c index 441bd7a78..81638169a 100644 --- a/src/libcharon/sa/ikev1/tasks/main_mode.c +++ b/src/libcharon/sa/ikev1/tasks/main_mode.c @@ -504,7 +504,7 @@ METHOD(task_t, build_r, status_t, case AUTH_HYBRID_INIT_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: @@ -527,11 +527,21 @@ METHOD(task_t, build_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && - this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) + { + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (!this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } @@ -540,6 +550,17 @@ METHOD(task_t, build_r, status_t, } } +/** + * Schedule a timeout for the IKE_SA should it not establish + */ +static void schedule_timeout(ike_sa_t *ike_sa) +{ + job_t *job; + + job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE); + lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); +} + METHOD(task_t, process_i, status_t, private_main_mode_t *this, message_t *message) { @@ -639,20 +660,15 @@ METHOD(task_t, process_i, status_t, case AUTH_XAUTH_INIT_PSK: case AUTH_XAUTH_INIT_RSA: case AUTH_HYBRID_INIT_RSA: - { /* wait for XAUTH request, since this may never come, - * we queue a timeout */ - job_t *job = (job_t*)delete_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), FALSE); - lib->scheduler->schedule_job(lib->scheduler, job, - HALF_OPEN_IKE_SA_TIMEOUT); + /* wait for XAUTH request */ + schedule_timeout(this->ike_sa); break; - } case AUTH_XAUTH_RESP_PSK: case AUTH_XAUTH_RESP_RSA: case AUTH_HYBRID_RESP_RSA: this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth_create(this->ike_sa, TRUE)); - return SUCCESS; + break; default: if (charon->ike_sa_manager->check_uniqueness( charon->ike_sa_manager, this->ike_sa, FALSE)) @@ -667,10 +683,30 @@ METHOD(task_t, process_i, status_t, } break; } + /* check for and prepare mode config push/pull */ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { - this->ike_sa->queue_task(this->ike_sa, - (task_t*)mode_config_create(this->ike_sa, TRUE)); + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE)); + } + else + { + schedule_timeout(this->ike_sa); + } + } + else if (this->ph1->has_pool(this->ph1, this->peer_cfg)) + { + if (this->peer_cfg->use_pull_mode(this->peer_cfg)) + { + schedule_timeout(this->ike_sa); + } + else + { + this->ike_sa->queue_task(this->ike_sa, + (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE)); + } } return SUCCESS; } diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index ce897727a..17fe02538 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -41,15 +41,20 @@ struct private_mode_config_t { */ bool initiator; + /** + * Use pull (CFG_REQUEST/RESPONSE) or push (CFG_SET/ACK)? + */ + bool pull; + /** * Received list of virtual IPs, host_t* */ linked_list_t *vips; /** - * list of attributes requested and its handler, entry_t + * Requested/received list of attributes, entry_t */ - linked_list_t *requested; + linked_list_t *attributes; /** * Identifier to include in response @@ -58,12 +63,12 @@ struct private_mode_config_t { }; /** - * Entry for a requested attribute and the requesting handler + * Entry for a attribute and associated handler */ typedef struct { - /** attribute requested */ + /** attribute type */ configuration_attribute_type_t type; - /** handler requesting this attribute */ + /** handler for this attribute */ attribute_handler_t *handler; } entry_t; @@ -117,13 +122,13 @@ static void handle_attribute(private_mode_config_t *this, entry_t *entry; /* find the handler which requested this attribute */ - enumerator = this->requested->create_enumerator(this->requested); + enumerator = this->attributes->create_enumerator(this->attributes); while (enumerator->enumerate(enumerator, &entry)) { if (entry->type == ca->get_type(ca)) { handler = entry->handler; - this->requested->remove_at(this->requested, enumerator); + this->attributes->remove_at(this->attributes, enumerator); free(entry); break; } @@ -180,7 +185,7 @@ static void process_attribute(private_mode_config_t *this, } default: { - if (this->initiator) + if (this->initiator == this->pull) { handle_attribute(this, ca); } @@ -188,6 +193,24 @@ static void process_attribute(private_mode_config_t *this, } } +/** + * Check if config allows push mode when acting as task responder + */ +static bool accept_push(private_mode_config_t *this) +{ + enumerator_t *enumerator; + peer_cfg_t *config; + bool vip; + host_t *host; + + config = this->ike_sa->get_peer_cfg(this->ike_sa); + enumerator = config->create_virtual_ip_enumerator(config); + vip = enumerator->enumerate(enumerator, &host); + enumerator->destroy(enumerator); + + return vip && !config->use_pull_mode(config); +} + /** * Scan for configuration payloads and attributes */ @@ -206,6 +229,15 @@ static void process_payloads(private_mode_config_t *this, message_t *message) switch (cp->get_type(cp)) { + case CFG_SET: + /* when acting as a responder, we detect the mode using + * the type of configuration payload. But we should double + * check the peer is allowed to use push mode on us. */ + if (!this->initiator && accept_push(this)) + { + this->pull = FALSE; + } + /* FALL */ case CFG_REQUEST: this->identifier = cp->get_identifier(cp); /* FALL */ @@ -219,6 +251,8 @@ static void process_payloads(private_mode_config_t *this, message_t *message) } attributes->destroy(attributes); break; + case CFG_ACK: + break; default: DBG1(DBG_IKE, "ignoring %N config payload", config_type_names, cp->get_type(cp)); @@ -229,8 +263,29 @@ static void process_payloads(private_mode_config_t *this, message_t *message) enumerator->destroy(enumerator); } -METHOD(task_t, build_i, status_t, - private_mode_config_t *this, message_t *message) +/** + * Add an attribute to a configuration payload, and store it in task + */ +static void add_attribute(private_mode_config_t *this, cp_payload_t *cp, + configuration_attribute_type_t type, chunk_t data, + attribute_handler_t *handler) +{ + entry_t *entry; + + cp->add_attribute(cp, + configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, + type, data)); + INIT(entry, + .type = type, + .handler = handler, + ); + this->attributes->insert_last(this->attributes, entry); +} + +/** + * Build a CFG_REQUEST as initiator + */ +static status_t build_request(private_mode_config_t *this, message_t *message) { cp_payload_t *cp; enumerator_t *enumerator; @@ -279,18 +334,7 @@ METHOD(task_t, build_i, status_t, this->ike_sa->get_other_id(this->ike_sa), vips); while (enumerator->enumerate(enumerator, &handler, &type, &data)) { - entry_t *entry; - - DBG2(DBG_IKE, "building %N attribute", - configuration_attribute_type_names, type); - cp->add_attribute(cp, - configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, - type, data)); - INIT(entry, - .type = type, - .handler = handler, - ); - this->requested->insert_last(this->requested, entry); + add_attribute(this, cp, type, data, handler); } enumerator->destroy(enumerator); @@ -301,15 +345,121 @@ METHOD(task_t, build_i, status_t, return NEED_MORE; } +/** + * Build a CFG_SET as initiator + */ +static status_t build_set(private_mode_config_t *this, message_t *message) +{ + enumerator_t *enumerator; + configuration_attribute_type_t type; + chunk_t value; + cp_payload_t *cp; + peer_cfg_t *config; + identification_t *id; + linked_list_t *pools; + host_t *any4, *any6, *found; + char *name; + + cp = cp_payload_create_type(CONFIGURATION_V1, CFG_SET); + + id = this->ike_sa->get_other_eap_id(this->ike_sa); + config = this->ike_sa->get_peer_cfg(this->ike_sa); + any4 = host_create_any(AF_INET); + any6 = host_create_any(AF_INET6); + + this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE); + + /* in push mode, we ask each configured pool for an address */ + enumerator = config->create_pool_enumerator(config); + while (enumerator->enumerate(enumerator, &name)) + { + pools = linked_list_create_with_items(name, NULL); + /* try IPv4, then IPv6 */ + found = hydra->attributes->acquire_address(hydra->attributes, + pools, id, any4); + if (!found) + { + found = hydra->attributes->acquire_address(hydra->attributes, + pools, id, any6); + } + pools->destroy(pools); + if (found) + { + DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id); + this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found); + cp->add_attribute(cp, build_vip(found)); + this->vips->insert_last(this->vips, found); + } + } + enumerator->destroy(enumerator); + + any4->destroy(any4); + any6->destroy(any6); + + /* query registered providers for additional attributes to include */ + pools = linked_list_create_from_enumerator( + config->create_pool_enumerator(config)); + enumerator = hydra->attributes->create_responder_enumerator( + hydra->attributes, pools, id, this->vips); + while (enumerator->enumerate(enumerator, &type, &value)) + { + add_attribute(this, cp, type, value, NULL); + } + enumerator->destroy(enumerator); + pools->destroy(pools); + + message->add_payload(message, (payload_t*)cp); + + return SUCCESS; +} + +METHOD(task_t, build_i, status_t, + private_mode_config_t *this, message_t *message) +{ + if (this->pull) + { + return build_request(this, message); + } + return build_set(this, message); +} + +/** + * Store received virtual IPs to the IKE_SA, install them + */ +static void install_vips(private_mode_config_t *this) +{ + enumerator_t *enumerator; + host_t *host; + + this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE); + + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &host)) + { + if (!host->is_anyaddr(host)) + { + this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + } + } + enumerator->destroy(enumerator); +} + METHOD(task_t, process_r, status_t, private_mode_config_t *this, message_t *message) { process_payloads(this, message); + + if (!this->pull) + { + install_vips(this); + } return NEED_MORE; } -METHOD(task_t, build_r, status_t, - private_mode_config_t *this, message_t *message) +/** + * Build CFG_REPLY message after receiving CFG_REQUEST + */ +static status_t build_reply(private_mode_config_t *this, message_t *message) { enumerator_t *enumerator; configuration_attribute_type_t type; @@ -360,8 +510,6 @@ METHOD(task_t, build_r, status_t, hydra->attributes, pools, id, vips); while (enumerator->enumerate(enumerator, &type, &value)) { - DBG2(DBG_IKE, "building %N attribute", - configuration_attribute_type_names, type); cp->add_attribute(cp, configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, type, value)); @@ -376,26 +524,72 @@ METHOD(task_t, build_r, status_t, return SUCCESS; } -METHOD(task_t, process_i, status_t, - private_mode_config_t *this, message_t *message) +/** + * Build CFG_ACK for a received CFG_SET + */ +static status_t build_ack(private_mode_config_t *this, message_t *message) { + cp_payload_t *cp; enumerator_t *enumerator; host_t *host; + configuration_attribute_type_t type; + entry_t *entry; - process_payloads(this, message); + cp = cp_payload_create_type(CONFIGURATION_V1, CFG_ACK); - this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE); + /* return empty attributes for installed IPs */ enumerator = this->vips->create_enumerator(this->vips); while (enumerator->enumerate(enumerator, &host)) { - if (!host->is_anyaddr(host)) + type = INTERNAL_IP6_ADDRESS; + if (host->get_family(host) == AF_INET6) { - this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + type = INTERNAL_IP6_ADDRESS; } + else + { + type = INTERNAL_IP4_ADDRESS; + } + cp->add_attribute(cp, configuration_attribute_create_chunk( + CONFIGURATION_ATTRIBUTE_V1, type, chunk_empty)); } enumerator->destroy(enumerator); + enumerator = this->attributes->create_enumerator(this->attributes); + while (enumerator->enumerate(enumerator, &entry)) + { + cp->add_attribute(cp, + configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1, + entry->type, chunk_empty)); + } + enumerator->destroy(enumerator); + + cp->set_identifier(cp, this->identifier); + message->add_payload(message, (payload_t*)cp); + + return SUCCESS; +} + +METHOD(task_t, build_r, status_t, + private_mode_config_t *this, message_t *message) +{ + if (this->pull) + { + return build_reply(this, message); + } + return build_ack(this, message); +} + +METHOD(task_t, process_i, status_t, + private_mode_config_t *this, message_t *message) +{ + process_payloads(this, message); + + if (this->pull) + { + install_vips(this); + } return SUCCESS; } @@ -411,22 +605,22 @@ METHOD(task_t, migrate, void, this->ike_sa = ike_sa; this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); this->vips = linked_list_create(); - this->requested->destroy_function(this->requested, free); - this->requested = linked_list_create(); + this->attributes->destroy_function(this->attributes, free); + this->attributes = linked_list_create(); } METHOD(task_t, destroy, void, private_mode_config_t *this) { this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); - this->requested->destroy_function(this->requested, free); + this->attributes->destroy_function(this->attributes, free); free(this); } /* * Described in header. */ -mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator) +mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull) { private_mode_config_t *this; @@ -439,8 +633,9 @@ mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator) }, }, .initiator = initiator, + .pull = initiator ? pull : TRUE, .ike_sa = ike_sa, - .requested = linked_list_create(), + .attributes = linked_list_create(), .vips = linked_list_create(), ); diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.h b/src/libcharon/sa/ikev1/tasks/mode_config.h index 462bee374..c2da7a086 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.h +++ b/src/libcharon/sa/ikev1/tasks/mode_config.h @@ -43,8 +43,9 @@ struct mode_config_t { * * @param ike_sa IKE_SA this task works for * @param initiator TRUE for initiator + * @param pull TRUE to pull, FALSE to push (applies if initiator only) * @return mode_config task to handle by the task_manager */ -mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator); +mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull); #endif /** MODE_CONFIG_H_ @}*/ diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c index 1a2cdb777..605c10cea 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_delete.c +++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c @@ -177,7 +177,7 @@ METHOD(task_t, build_i, status_t, DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", protocol_id_names, this->protocol, ntohl(this->spi)); - delete_payload = delete_payload_create(DELETE_V1, PROTO_ESP); + delete_payload = delete_payload_create(DELETE_V1, this->protocol); delete_payload->add_spi(delete_payload, this->spi); message->add_payload(message, &delete_payload->payload_interface); diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 6271e5b05..12ee594b9 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -165,6 +165,11 @@ struct private_quick_mode_t { */ ipsec_mode_t mode; + /* + * SA protocol (ESP|AH) negotiated + */ + protocol_id_t proto; + /** * Use UDP encapsulation */ @@ -722,7 +727,7 @@ static status_t send_notify(private_quick_mode_t *this, notify_type_t type) notify_payload_t *notify; notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1, - PROTO_ESP, type); + this->proto, type); notify->set_spi(notify, this->spi_i); this->ike_sa->queue_task(this->ike_sa, @@ -733,6 +738,38 @@ static status_t send_notify(private_quick_mode_t *this, notify_type_t type) return ALREADY_DONE; } +/** + * Prepare a list of proposals from child_config containing only the specified + * DH group, unless it is set to MODP_NONE. + */ +static linked_list_t *get_proposals(private_quick_mode_t *this, + diffie_hellman_group_t group) +{ + linked_list_t *list; + proposal_t *proposal; + enumerator_t *enumerator; + + list = this->config->get_proposals(this->config, FALSE); + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &proposal)) + { + if (group != MODP_NONE) + { + if (!proposal->has_dh_group(proposal, group)) + { + list->remove_at(list, enumerator); + proposal->destroy(proposal); + continue; + } + proposal->strip_dh(proposal, group); + } + proposal->set_spi(proposal, this->spi_i); + } + enumerator->destroy(enumerator); + + return list; +} + METHOD(task_t, build_i, status_t, private_quick_mode_t *this, message_t *message) { @@ -740,7 +777,6 @@ METHOD(task_t, build_i, status_t, { case QM_INIT: { - enumerator_t *enumerator; sa_payload_t *sa_payload; linked_list_t *list, *tsi, *tsr; proposal_t *proposal; @@ -770,42 +806,55 @@ METHOD(task_t, build_i, status_t, } } - this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); + list = this->config->get_proposals(this->config, MODP_NONE); + if (list->get_first(list, (void**)&proposal) == SUCCESS) + { + this->proto = proposal->get_protocol(proposal); + } + list->destroy_offset(list, offsetof(proposal_t, destroy)); + this->spi_i = this->child_sa->alloc_spi(this->child_sa, this->proto); if (!this->spi_i) { DBG1(DBG_IKE, "allocating SPI from kernel failed"); return FAILED; } + group = this->config->get_dh_group(this->config); if (group != MODP_NONE) { + proposal_t *proposal; + u_int16_t preferred_group; + + proposal = this->ike_sa->get_proposal(this->ike_sa); + proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, + &preferred_group, NULL); + /* try the negotiated DH group from IKE_SA */ + list = get_proposals(this, preferred_group); + if (list->get_count(list)) + { + group = preferred_group; + } + else + { + /* fall back to the first configured DH group */ + list->destroy(list); + list = get_proposals(this, group); + } + this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, group); if (!this->dh) { DBG1(DBG_IKE, "configured DH group %N not supported", diffie_hellman_group_names, group); + list->destroy_offset(list, offsetof(proposal_t, destroy)); return FAILED; } } - - list = this->config->get_proposals(this->config, FALSE); - enumerator = list->create_enumerator(list); - while (enumerator->enumerate(enumerator, &proposal)) + else { - if (group != MODP_NONE) - { - if (!proposal->has_dh_group(proposal, group)) - { - list->remove_at(list, enumerator); - proposal->destroy(proposal); - continue; - } - proposal->strip_dh(proposal, group); - } - proposal->set_spi(proposal, this->spi_i); + list = get_proposals(this, MODP_NONE); } - enumerator->destroy(enumerator); get_lifetimes(this); encap = get_encap(this->ike_sa, this->udp); @@ -1103,7 +1152,8 @@ METHOD(task_t, build_r, status_t, sa_payload_t *sa_payload; encap_t encap; - this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); + this->proto = this->proposal->get_protocol(this->proposal); + this->spi_r = this->child_sa->alloc_spi(this->child_sa, this->proto); if (!this->spi_r) { DBG1(DBG_IKE, "allocating SPI from kernel failed"); @@ -1311,6 +1361,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config, .state = QM_INIT, .tsi = tsi ? tsi->clone(tsi) : NULL, .tsr = tsr ? tsr->clone(tsr) : NULL, + .proto = PROTO_ESP, ); if (config) diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c index 31114e592..f5555ecd2 100644 --- a/src/libcharon/sa/ikev1/tasks/xauth.c +++ b/src/libcharon/sa/ikev1/tasks/xauth.c @@ -127,7 +127,7 @@ static xauth_method_t *load_method(private_xauth_t* this) { if (name) { - DBG1(DBG_CFG, "no XAuth method found named '%s'", name); + DBG1(DBG_CFG, "no XAuth method found for '%s'", name); } else { diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index a6af744fc..8e6da1609 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1145,14 +1145,9 @@ METHOD(task_manager_t, process_message, status_t, return FAILED; } } - if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED || - this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING || - msg->get_exchange_type(msg) != IKE_SA_INIT) - { /* only do host updates based on verified messages */ - if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) - { /* with MOBIKE, we do no implicit updates */ - this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1); - } + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) + { /* with MOBIKE, we do no implicit updates */ + this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1); } charon->bus->message(charon->bus, msg, TRUE, TRUE); if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED) @@ -1198,10 +1193,13 @@ METHOD(task_manager_t, process_message, status_t, if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED || this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING || msg->get_exchange_type(msg) != IKE_SA_INIT) - { /* only do host updates based on verified messages */ + { /* only do updates based on verified messages (or initial ones) */ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) - { /* with MOBIKE, we do no implicit updates */ - this->ike_sa->update_hosts(this->ike_sa, me, other, FALSE); + { /* with MOBIKE, we do no implicit updates. we force an + * update of the local address on IKE_SA_INIT, but never + * for the remote address */ + this->ike_sa->update_hosts(this->ike_sa, me, NULL, mid == 0); + this->ike_sa->update_hosts(this->ike_sa, NULL, other, FALSE); } } charon->bus->message(charon->bus, msg, TRUE, TRUE); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 8ae36af84..7cfa537a9 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -244,9 +244,23 @@ static bool allocate_spi(private_child_create_t *this) { enumerator_t *enumerator; proposal_t *proposal; + protocol_id_t proto = PROTO_ESP; - /* TODO: allocate additional SPI for AH if we have such proposals */ - this->my_spi = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); + if (this->initiator) + { + /* 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); + } + } + else + { + proto = this->proposal->get_protocol(this->proposal); + } + this->my_spi = this->child_sa->alloc_spi(this->child_sa, proto); if (this->my_spi) { if (this->initiator) diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index eaaca2039..e898efc88 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -198,7 +198,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) child_sa_t *child_sa; child_cfg_t *child_cfg; protocol_id_t protocol; - u_int32_t spi; + u_int32_t spi, reqid; action_t action; status_t status = SUCCESS; @@ -211,6 +211,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) charon->bus->child_updown(charon->bus, child_sa, FALSE); } spi = child_sa->get_spi(child_sa, TRUE); + reqid = child_sa->get_reqid(child_sa); protocol = child_sa->get_protocol(child_sa); child_cfg = child_sa->get_config(child_sa); child_cfg->get_ref(child_cfg); @@ -223,12 +224,12 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) case ACTION_RESTART: child_cfg->get_ref(child_cfg); status = this->ike_sa->initiate(this->ike_sa, child_cfg, - child_sa->get_reqid(child_sa), NULL, NULL); + reqid, NULL, NULL); break; case ACTION_ROUTE: charon->traps->install(charon->traps, this->ike_sa->get_peer_cfg(this->ike_sa), child_cfg, - child_sa->get_reqid(child_sa)); + reqid); break; default: break; diff --git a/src/libcharon/sa/keymat.c b/src/libcharon/sa/keymat.c index 26c305f77..d1f6a1bdc 100644 --- a/src/libcharon/sa/keymat.c +++ b/src/libcharon/sa/keymat.c @@ -93,6 +93,7 @@ int keymat_get_keylen_integ(integrity_algorithm_t alg) {AUTH_HMAC_SHA2_384_192, 384}, {AUTH_HMAC_SHA2_512_256, 512}, {AUTH_AES_XCBC_96, 128}, + {AUTH_AES_CMAC_96, 128}, }; int i; diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 37426fc47..1f66d6ceb 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Tobias Brunner + * Copyright (C) 2011-2013 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -61,6 +62,11 @@ struct private_trap_manager_t { */ rwlock_t *lock; + /** + * track if the current thread is installing a trap policy + */ + thread_value_t *installing; + /** * listener to track acquiring IKE_SAs */ @@ -102,19 +108,20 @@ METHOD(trap_manager_t, install, u_int32_t, linked_list_t *my_ts, *other_ts, *list; enumerator_t *enumerator; status_t status; + linked_list_t *proposals; + proposal_t *proposal; + protocol_id_t proto = PROTO_ESP; /* try to resolve addresses */ ike_cfg = peer->get_ike_cfg(peer); - other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg, NULL), - 0, ike_cfg->get_other_port(ike_cfg)); + other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC); if (!other || other->is_anyaddr(other)) { DESTROY_IF(other); DBG1(DBG_CFG, "installing trap failed, remote address unknown"); return 0; } - me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg, NULL), - other->get_family(other), ike_cfg->get_my_port(ike_cfg)); + me = ike_cfg->resolve_me(ike_cfg, other->get_family(other)); if (!me || me->is_anyaddr(me)) { DESTROY_IF(me); @@ -130,6 +137,7 @@ METHOD(trap_manager_t, install, u_int32_t, } this->lock->write_lock(this->lock); + this->installing->set(this->installing, this); enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { @@ -142,7 +150,6 @@ METHOD(trap_manager_t, install, u_int32_t, } } enumerator->destroy(enumerator); - this->lock->unlock(this->lock); if (found) { /* config might have changed so update everything */ @@ -162,10 +169,15 @@ METHOD(trap_manager_t, install, u_int32_t, other_ts = child->get_traffic_selectors(child, FALSE, NULL, list); list->destroy_offset(list, offsetof(host_t, destroy)); - /* while we don't know the finally negotiated protocol (ESP|AH), we - * could iterate all proposals for a best guess (TODO). But as we - * support ESP only for now, we set it here. */ - child_sa->set_protocol(child_sa, PROTO_ESP); + /* We don't know the finally negotiated protocol (ESP|AH), we install + * the SA with the protocol of the first proposal */ + proposals = child->get_proposals(child, TRUE); + if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS) + { + proto = proposal->get_protocol(proposal); + } + proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); + child_sa->set_protocol(child_sa, proto); child_sa->set_mode(child_sa, child->get_mode(child)); status = child_sa->add_policies(child_sa, my_ts, other_ts); my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); @@ -182,11 +194,11 @@ METHOD(trap_manager_t, install, u_int32_t, .child_sa = child_sa, .peer_cfg = peer->get_ref(peer), ); - this->lock->write_lock(this->lock); this->traps->insert_last(this->traps, entry); - this->lock->unlock(this->lock); reqid = child_sa->get_reqid(child_sa); } + this->installing->set(this->installing, NULL); + this->lock->unlock(this->lock); if (status != SUCCESS) { @@ -263,6 +275,10 @@ METHOD(trap_manager_t, find_reqid, u_int32_t, entry_t *entry; u_int32_t reqid = 0; + if (this->installing->get(this->installing)) + { /* current thread holds the lock */ + return reqid; + } this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) @@ -429,6 +445,7 @@ 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->installing->destroy(this->installing); this->lock->destroy(this->lock); free(this); } @@ -459,6 +476,7 @@ trap_manager_t *trap_manager_create(void) }, .traps = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .installing = thread_value_create(NULL), ); charon->bus->add_listener(charon->bus, &this->listener.listener); diff --git a/src/libcharon/sa/xauth/xauth_manager.c b/src/libcharon/sa/xauth/xauth_manager.c index 5709dc652..17eecc2c9 100644 --- a/src/libcharon/sa/xauth/xauth_manager.c +++ b/src/libcharon/sa/xauth/xauth_manager.c @@ -107,6 +107,17 @@ METHOD(xauth_manager_t, create_instance, xauth_method_t*, enumerator_t *enumerator; xauth_entry_t *entry; xauth_method_t *method = NULL; + char *profile = NULL; + + if (name) + { + profile = strchr(name, ':'); + if (profile) + { + name = strndup(name, profile - name); + profile++; + } + } this->lock->read_lock(this->lock); enumerator = this->methods->create_enumerator(this->methods); @@ -118,7 +129,7 @@ METHOD(xauth_manager_t, create_instance, xauth_method_t*, } if (role == entry->role && (!name || streq(name, entry->name))) { - method = entry->constructor(server, peer); + method = entry->constructor(server, peer, profile); if (method) { break; @@ -127,6 +138,10 @@ METHOD(xauth_manager_t, create_instance, xauth_method_t*, } enumerator->destroy(enumerator); this->lock->unlock(this->lock); + if (profile) + { + free(name); + } return method; } diff --git a/src/libcharon/sa/xauth/xauth_manager.h b/src/libcharon/sa/xauth/xauth_manager.h index 929d5de8f..65b3c58a3 100644 --- a/src/libcharon/sa/xauth/xauth_manager.h +++ b/src/libcharon/sa/xauth/xauth_manager.h @@ -55,7 +55,11 @@ struct xauth_manager_t { /** * Create a new XAuth method instance. * - * @param name backend name, as it was registered with + * The name may contain an option string, separated by a colon. This option + * string gets passed to the XAuth constructor to specify the behavior + * of the XAuth method. + * + * @param name backend name, with optional config string * @param role XAUTH_SERVER or XAUTH_PEER * @param server identity of the server * @param peer identity of the peer (client) diff --git a/src/libcharon/sa/xauth/xauth_method.h b/src/libcharon/sa/xauth/xauth_method.h index 9f6067dbf..701b4dc77 100644 --- a/src/libcharon/sa/xauth/xauth_method.h +++ b/src/libcharon/sa/xauth/xauth_method.h @@ -104,10 +104,12 @@ struct xauth_method_t { * * @param server ID of the server to use for credential lookup * @param peer ID of the peer to use for credential lookup + * @param profile configuration string to pass to XAuth method, or NULL * @return implementation of the eap_method_t interface */ typedef xauth_method_t *(*xauth_constructor_t)(identification_t *server, - identification_t *peer); + identification_t *peer, + char *profile); /** * Helper function to (un-)register XAuth methods from plugin features. -- cgit v1.2.3