diff options
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 4 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa.c | 45 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa.h | 17 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa_manager.c | 2 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/phase1.c | 3 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/task_manager_v1.c | 134 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c | 1 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c | 1 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.c | 9 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c | 97 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/task_manager_v2.c | 227 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_delete.c | 24 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/ike_init.c | 16 | ||||
-rw-r--r-- | src/libcharon/sa/task_manager.h | 19 |
14 files changed, 428 insertions, 171 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index c33398bee..bdc96a4bc 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -978,7 +978,7 @@ static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa, } /** - * Install inbound policie(s): in, fwd + * Install inbound policies: in, fwd */ static status_t install_policies_inbound(private_child_sa_t *this, host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, @@ -1012,7 +1012,7 @@ static status_t install_policies_inbound(private_child_sa_t *this, } /** - * Install outbound policie(s): out, [fwd] + * Install outbound policies: out, [fwd] */ static status_t install_policies_outbound(private_child_sa_t *this, host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index a4ad866d3..3d576a0e8 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1996,8 +1996,7 @@ static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new, /* adopt any active or queued CHILD-creating tasks */ if (status != DESTROY_ME) { - task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; - other_tasks->adopt_child_tasks(other_tasks, this->task_manager); + new->adopt_child_tasks(new, &this->public); if (new->get_state(new) == IKE_CREATED) { status = new->initiate(new, NULL, 0, NULL, NULL); @@ -2404,7 +2403,9 @@ METHOD(ike_sa_t, retransmit, status_t, } case IKE_DELETING: DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding"); - if (has_condition(this, COND_REAUTHENTICATING)) + if (has_condition(this, COND_REAUTHENTICATING) && + !lib->settings->get_bool(lib->settings, + "%s.make_before_break", FALSE, lib->ns)) { DBG1(DBG_IKE, "delete during reauthentication failed, " "trying to reestablish IKE_SA anyway"); @@ -2719,6 +2720,12 @@ METHOD(ike_sa_t, create_task_enumerator, enumerator_t*, return this->task_manager->create_task_enumerator(this->task_manager, queue); } +METHOD(ike_sa_t, remove_task, void, + private_ike_sa_t *this, enumerator_t *enumerator) +{ + return this->task_manager->remove_task(this->task_manager, enumerator); +} + METHOD(ike_sa_t, flush_queue, void, private_ike_sa_t *this, task_queue_t queue) { @@ -2737,6 +2744,36 @@ METHOD(ike_sa_t, queue_task_delayed, void, this->task_manager->queue_task_delayed(this->task_manager, task, delay); } +/** + * Migrate and queue child-creating tasks from another IKE_SA + */ +static void migrate_child_tasks(private_ike_sa_t *this, ike_sa_t *other, + task_queue_t queue) +{ + enumerator_t *enumerator; + task_t *task; + + enumerator = other->create_task_enumerator(other, queue); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_CHILD_CREATE || + task->get_type(task) == TASK_QUICK_MODE) + { + other->remove_task(other, enumerator); + task->migrate(task, &this->public); + queue_task(this, task); + } + } + enumerator->destroy(enumerator); +} + +METHOD(ike_sa_t, adopt_child_tasks, void, + private_ike_sa_t *this, ike_sa_t *other) +{ + migrate_child_tasks(this, other, TASK_QUEUE_ACTIVE); + migrate_child_tasks(this, other, TASK_QUEUE_QUEUED); +} + METHOD(ike_sa_t, inherit_pre, void, private_ike_sa_t *this, ike_sa_t *other_public) { @@ -3052,9 +3089,11 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .create_attribute_enumerator = _create_attribute_enumerator, .set_kmaddress = _set_kmaddress, .create_task_enumerator = _create_task_enumerator, + .remove_task = _remove_task, .flush_queue = _flush_queue, .queue_task = _queue_task, .queue_task_delayed = _queue_task_delayed, + .adopt_child_tasks = _adopt_child_tasks, #ifdef ME .act_as_mediation_server = _act_as_mediation_server, .get_server_reflexive_host = _get_server_reflexive_host, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index c1d3e1d7a..be480eac8 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1125,6 +1125,16 @@ struct ike_sa_t { enumerator_t* (*create_task_enumerator)(ike_sa_t *this, task_queue_t queue); /** + * Remove the task the given enumerator points to. + * + * @note This should be used with caution, in partciular, for tasks in the + * active and passive queues. + * + * @param enumerator enumerator created with the method above + */ + void (*remove_task)(ike_sa_t *this, enumerator_t *enumerator); + + /** * Flush a task queue, cancelling all tasks in it. * * @param queue queue type to flush @@ -1148,6 +1158,13 @@ struct ike_sa_t { void (*queue_task_delayed)(ike_sa_t *this, task_t *task, uint32_t delay); /** + * Adopt child creating tasks from the given IKE_SA. + * + * @param other other IKE_SA to adopt tasks from + */ + void (*adopt_child_tasks)(ike_sa_t *this, ike_sa_t *other); + + /** * Inherit required attributes to new SA before rekeying. * * Some properties of the SA must be applied before starting IKE_SA diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index c50c70860..3bac4b109 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -1967,6 +1967,8 @@ static void adopt_children_and_vips(ike_sa_t *old, ike_sa_t *new) } enumerator->destroy(enumerator); + new->adopt_child_tasks(new, old); + enumerator = old->create_virtual_ip_enumerator(old, FALSE); while (enumerator->enumerate(enumerator, &vip)) { diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index b99d75142..ac2899f11 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -251,7 +251,8 @@ METHOD(phase1_t, derive_keys, bool, return FALSE; } charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, this->dh_value, - this->nonce_i, this->nonce_r, NULL, shared_key); + this->nonce_i, this->nonce_r, NULL, shared_key, + method); DESTROY_IF(shared_key); return TRUE; } diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 5f6c3bbe8..f76471e78 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-2016 Tobias Brunner + * Copyright (C) 2007-2018 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -544,20 +544,20 @@ METHOD(task_manager_t, initiate, status_t, new_mid = TRUE; break; } - if (!mode_config_expected(this) && - activate_task(this, TASK_QUICK_MODE)) + if (activate_task(this, TASK_ISAKMP_DPD)) { - exchange = QUICK_MODE; + exchange = INFORMATIONAL_V1; new_mid = TRUE; break; } - if (activate_task(this, TASK_INFORMATIONAL)) + if (!mode_config_expected(this) && + activate_task(this, TASK_QUICK_MODE)) { - exchange = INFORMATIONAL_V1; + exchange = QUICK_MODE; new_mid = TRUE; break; } - if (activate_task(this, TASK_ISAKMP_DPD)) + if (activate_task(this, TASK_INFORMATIONAL)) { exchange = INFORMATIONAL_V1; new_mid = TRUE; @@ -1121,7 +1121,15 @@ static status_t process_request(private_task_manager_t *this, } } else - { /* We don't send a response, so don't retransmit one if we get + { + if (this->responding.retransmitted > 1) + { + packet_t *packet = NULL; + array_get(this->responding.packets, 0, &packet); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_CLEARED, + packet); + } + /* We don't send a response, so don't retransmit one if we get * the same message again. */ clear_packets(this->responding.packets); } @@ -1883,39 +1891,6 @@ METHOD(task_manager_t, adopt_tasks, void, } } -/** - * Migrates child-creating tasks from src to dst - */ -static void migrate_child_tasks(private_task_manager_t *this, - linked_list_t *src, linked_list_t *dst) -{ - enumerator_t *enumerator; - task_t *task; - - enumerator = src->create_enumerator(src); - while (enumerator->enumerate(enumerator, &task)) - { - if (task->get_type(task) == TASK_QUICK_MODE) - { - src->remove_at(src, enumerator); - task->migrate(task, this->ike_sa); - dst->insert_last(dst, task); - } - } - enumerator->destroy(enumerator); -} - -METHOD(task_manager_t, adopt_child_tasks, void, - private_task_manager_t *this, task_manager_t *other_public) -{ - private_task_manager_t *other = (private_task_manager_t*)other_public; - - /* move active child tasks from other to this */ - migrate_child_tasks(this, other->active_tasks, this->queued_tasks); - /* do the same for queued tasks */ - migrate_child_tasks(this, other->queued_tasks, this->queued_tasks); -} - METHOD(task_manager_t, busy, bool, private_task_manager_t *this) { @@ -1976,19 +1951,86 @@ METHOD(task_manager_t, reset, void, } } +/** + * Data for a task queue enumerator + */ +typedef struct { + enumerator_t public; + task_queue_t queue; + enumerator_t *inner; +} task_enumerator_t; + +METHOD(enumerator_t, task_enumerator_destroy, void, + task_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +METHOD(enumerator_t, task_enumerator_enumerate, bool, + task_enumerator_t *this, va_list args) +{ + task_t **task; + + VA_ARGS_VGET(args, task); + return this->inner->enumerate(this->inner, task); +} + METHOD(task_manager_t, create_task_enumerator, enumerator_t*, private_task_manager_t *this, task_queue_t queue) { + task_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = enumerator_enumerate_default, + .venumerate = _task_enumerator_enumerate, + .destroy = _task_enumerator_destroy, + }, + .queue = queue, + ); switch (queue) { case TASK_QUEUE_ACTIVE: - return this->active_tasks->create_enumerator(this->active_tasks); + enumerator->inner = this->active_tasks->create_enumerator( + this->active_tasks); + break; + case TASK_QUEUE_PASSIVE: + enumerator->inner = this->passive_tasks->create_enumerator( + this->passive_tasks); + break; + case TASK_QUEUE_QUEUED: + enumerator->inner = this->queued_tasks->create_enumerator( + this->queued_tasks); + break; + default: + enumerator->inner = enumerator_create_empty(); + break; + } + return &enumerator->public; +} + +METHOD(task_manager_t, remove_task, void, + private_task_manager_t *this, enumerator_t *enumerator_public) +{ + task_enumerator_t *enumerator = (task_enumerator_t*)enumerator_public; + + switch (enumerator->queue) + { + case TASK_QUEUE_ACTIVE: + this->active_tasks->remove_at(this->active_tasks, + enumerator->inner); + break; case TASK_QUEUE_PASSIVE: - return this->passive_tasks->create_enumerator(this->passive_tasks); + this->passive_tasks->remove_at(this->passive_tasks, + enumerator->inner); + break; case TASK_QUEUE_QUEUED: - return this->queued_tasks->create_enumerator(this->queued_tasks); + this->queued_tasks->remove_at(this->queued_tasks, + enumerator->inner); + break; default: - return enumerator_create_empty(); + break; } } @@ -2039,9 +2081,9 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, - .adopt_child_tasks = _adopt_child_tasks, .busy = _busy, .create_task_enumerator = _create_task_enumerator, + .remove_task = _remove_task, .flush = _flush, .flush_queue = _flush_queue, .destroy = _destroy, diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c index 7dbbdc92f..b652d926f 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_post.c @@ -287,7 +287,6 @@ METHOD(task_t, process_i, status_t, default: return FAILED; } - break; } case AGGRESSIVE: { diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c index 58f856e3f..566bfe83a 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c @@ -605,7 +605,6 @@ METHOD(task_t, process_i, status_t, default: return FAILED; } - break; } case AGGRESSIVE: { diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 007e94d96..b0a42b8bd 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -1110,14 +1110,17 @@ METHOD(task_t, process_r, status_t, this->tsi = select_ts(this, FALSE, tsi); this->tsr = select_ts(this, TRUE, tsr); } - tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); - tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); if (!this->config || !this->tsi || !this->tsr || this->mode != this->config->get_mode(this->config)) { - DBG1(DBG_IKE, "no matching CHILD_SA config found"); + DBG1(DBG_IKE, "no matching CHILD_SA config found for " + "%#R === %#R", tsi, tsr); + tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); + tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); return send_notify(this, INVALID_ID_INFORMATION); } + tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); + tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); if (this->config->has_option(this->config, OPT_IPCOMP)) { diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 1fcef03cc..97d33a89e 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -111,6 +111,40 @@ static bool build_signature_auth_data(chunk_t *auth_data, } /** + * Check if the given scheme is supported by the key and, if so, add it to the + * first array (we add the scheme supported by the key in case the parameters + * are different) + */ +static void add_scheme_if_supported(array_t *selected, array_t *supported, + signature_params_t *config) +{ + signature_params_t *sup; + int i; + + if (!supported) + { + array_insert(selected, ARRAY_TAIL, signature_params_clone(config)); + return; + } + + for (i = 0; i < array_count(supported); i++) + { + array_get(supported, i, &sup); + if (signature_params_comply(sup, config)) + { + array_insert(selected, ARRAY_TAIL, signature_params_clone(sup)); + return; + } + } +} + +CALLBACK(destroy_scheme, void, + signature_params_t *params, int idx, void *user) +{ + signature_params_destroy(params); +} + +/** * Selects possible signature schemes based on our configuration, the other * peer's capabilities and the private key */ @@ -123,10 +157,32 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat, auth_rule_t rule; key_type_t key_type; bool have_config = FALSE; - array_t *selected; + array_t *supported = NULL, *selected; selected = array_create(0, 0); key_type = private->get_type(private); + + if (private->supported_signature_schemes) + { + enumerator = private->supported_signature_schemes(private); + while (enumerator->enumerate(enumerator, &config)) + { + if (keymat->hash_algorithm_supported(keymat, + hasher_from_signature_scheme(config->scheme, + config->params))) + { + array_insert_create(&supported, ARRAY_TAIL, + signature_params_clone(config)); + } + } + enumerator->destroy(enumerator); + + if (!supported) + { + return selected; + } + } + enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &rule, &config)) { @@ -134,21 +190,32 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat, { continue; } - have_config = TRUE; if (key_type == key_type_from_signature_scheme(config->scheme) && keymat->hash_algorithm_supported(keymat, hasher_from_signature_scheme(config->scheme, config->params))) { - array_insert(selected, ARRAY_TAIL, signature_params_clone(config)); + add_scheme_if_supported(selected, supported, config); } + have_config = TRUE; } enumerator->destroy(enumerator); - if (!have_config) + if (have_config) { - /* if no specific configuration, find schemes appropriate for the key - * and supported by the other peer */ + array_destroy_function(supported, destroy_scheme, NULL); + } + else + { + /* if we have no config, return either whatever schemes the key (and + * peer) support or.. */ + if (supported) + { + array_destroy(selected); + return supported; + } + + /* ...find schemes appropriate for the key and supported by the peer */ enumerator = signature_schemes_for_key(key_type, private->get_keysize(private)); while (enumerator->enumerate(enumerator, &config)) @@ -207,12 +274,6 @@ static array_t *select_signature_schemes(keymat_v2_t *keymat, return selected; } -CALLBACK(destroy_scheme, void, - signature_params_t *params, int idx, void *user) -{ - signature_params_destroy(params); -} - /** * Adds the given auth data to the message, either in an AUTH payload or * a NO_PPK_AUTH notify. @@ -310,9 +371,9 @@ static status_t sign_signature_auth(private_pubkey_authenticator_t *this, if (params->scheme == SIGN_RSA_EMSA_PSS) { rsa_pss_params_t *pss = params->params; - DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N_%N %s", id, - signature_scheme_names, params->scheme, - hash_algorithm_short_names_upper, pss->hash, + DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N_%N_SALT_%zd " + "%s", id, signature_scheme_names, params->scheme, + hash_algorithm_short_names_upper, pss->hash, pss->salt_len, status == SUCCESS ? "successful" : "failed"); } else @@ -586,9 +647,9 @@ METHOD(authenticator_t, process, status_t, else if (params->scheme == SIGN_RSA_EMSA_PSS) { rsa_pss_params_t *pss = params->params; - DBG1(DBG_IKE, "authentication of '%Y' with %N_%N successful", - id, signature_scheme_names, params->scheme, - hash_algorithm_short_names_upper, pss->hash); + DBG1(DBG_IKE, "authentication of '%Y' with %N_%N_SALT_%zd " + "successful", id, signature_scheme_names, params->scheme, + hash_algorithm_short_names_upper, pss->hash, pss->salt_len); } else { diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 910c77a2d..e9142d79b 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1459,6 +1459,59 @@ static bool looks_like_mid_sync(private_task_manager_t *this, message_t *msg, } /** + * Check whether we should reject the given request message + */ +static inline bool reject_request(private_task_manager_t *this, + message_t *msg) +{ + ike_sa_state_t state; + exchange_type_t type; + ike_sa_id_t *ike_sa_id; + bool reject = FALSE; + + state = this->ike_sa->get_state(this->ike_sa); + type = msg->get_exchange_type(msg); + + /* reject initial messages if not received in specific states */ + switch (type) + { + case IKE_SA_INIT: + reject = state != IKE_CREATED; + break; + case IKE_AUTH: + reject = state != IKE_CONNECTING; + break; + default: + break; + } + + if (!reject) + { + switch (state) + { + /* after rekeying we only expect a DELETE in an INFORMATIONAL */ + case IKE_REKEYED: + reject = type != INFORMATIONAL; + break; + /* also reject requests for half-open IKE_SAs as initiator */ + case IKE_CREATED: + case IKE_CONNECTING: + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + reject = ike_sa_id->is_initiator(ike_sa_id); + break; + default: + break; + } + } + + if (reject) + { + DBG1(DBG_IKE, "ignoring %N in IKE_SA state %N", exchange_type_names, + type, ike_sa_state_names, state); + } + return reject; +} +/** * Check if a message with message ID 0 looks like it is used to synchronize * the message IDs and we are prepared to process it. * @@ -1483,8 +1536,6 @@ METHOD(task_manager_t, process_message, status_t, status_t status; uint32_t mid; bool schedule_delete_job = FALSE; - ike_sa_state_t state; - exchange_type_t type; charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); @@ -1517,24 +1568,14 @@ METHOD(task_manager_t, process_message, status_t, /* add a timeout if peer does not establish it completely */ schedule_delete_job = TRUE; } - this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, - time_monotonic(NULL)); mid = msg->get_message_id(msg); if (msg->get_request(msg)) { if (mid == this->responding.mid || (mid == 0 && is_mid_sync(this, msg))) { - /* reject initial messages if not received in specific states, - * after rekeying we only expect a DELETE in an INFORMATIONAL */ - type = msg->get_exchange_type(msg); - state = this->ike_sa->get_state(this->ike_sa); - if ((type == IKE_SA_INIT && state != IKE_CREATED) || - (type == IKE_AUTH && state != IKE_CONNECTING) || - (state == IKE_REKEYED && type != INFORMATIONAL)) + if (reject_request(this, msg)) { - DBG1(DBG_IKE, "ignoring %N in IKE_SA state %N", - exchange_type_names, type, ike_sa_state_names, state); return FAILED; } if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) @@ -1544,6 +1585,11 @@ METHOD(task_manager_t, process_message, status_t, status = handle_fragment(this, &this->responding.defrag, msg); if (status != SUCCESS) { + if (status == NEED_MORE) + { + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); + } return status; } charon->bus->message(charon->bus, msg, TRUE, TRUE); @@ -1554,6 +1600,8 @@ METHOD(task_manager_t, process_message, status_t, switch (process_request(this, msg)) { case SUCCESS: + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); this->responding.mid++; break; case NEED_MORE: @@ -1570,10 +1618,17 @@ METHOD(task_manager_t, process_message, status_t, status = handle_fragment(this, &this->responding.defrag, msg); if (status != SUCCESS) { + if (status == NEED_MORE) + { + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); + } return status; } DBG1(DBG_IKE, "received retransmit of request with ID %d, " "retransmitting response", mid); + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg); send_packets(this, this->responding.packets, msg->get_destination(msg), msg->get_source(msg)); @@ -1603,6 +1658,11 @@ METHOD(task_manager_t, process_message, status_t, status = handle_fragment(this, &this->initiating.defrag, msg); if (status != SUCCESS) { + if (status == NEED_MORE) + { + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); + } return status; } charon->bus->message(charon->bus, msg, TRUE, TRUE); @@ -1615,6 +1675,8 @@ METHOD(task_manager_t, process_message, status_t, flush(this); return DESTROY_ME; } + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); } else { @@ -2014,61 +2076,6 @@ METHOD(task_manager_t, adopt_tasks, void, } } -/** - * Migrates child-creating tasks from other to this - */ -static void migrate_child_tasks(private_task_manager_t *this, - private_task_manager_t *other, - task_queue_t queue) -{ - enumerator_t *enumerator; - array_t *array; - task_t *task; - - switch (queue) - { - case TASK_QUEUE_ACTIVE: - array = other->active_tasks; - break; - case TASK_QUEUE_QUEUED: - array = other->queued_tasks; - break; - default: - return; - } - - enumerator = array_create_enumerator(array); - while (enumerator->enumerate(enumerator, &task)) - { - queued_task_t *queued = NULL; - - if (queue == TASK_QUEUE_QUEUED) - { - queued = (queued_task_t*)task; - task = queued->task; - } - if (task->get_type(task) == TASK_CHILD_CREATE) - { - array_remove_at(array, enumerator); - task->migrate(task, this->ike_sa); - queue_task(this, task); - free(queued); - } - } - enumerator->destroy(enumerator); -} - -METHOD(task_manager_t, adopt_child_tasks, void, - private_task_manager_t *this, task_manager_t *other_public) -{ - private_task_manager_t *other = (private_task_manager_t*)other_public; - - /* move active child tasks from other to this */ - migrate_child_tasks(this, other, TASK_QUEUE_ACTIVE); - /* do the same for queued tasks */ - migrate_child_tasks(this, other, TASK_QUEUE_QUEUED); -} - METHOD(task_manager_t, busy, bool, private_task_manager_t *this) { @@ -2124,17 +2131,39 @@ METHOD(task_manager_t, reset, void, this->reset = TRUE; } -CALLBACK(filter_queued, bool, - void *unused, enumerator_t *orig, va_list args) -{ +/** + * Data for a task queue enumerator + */ +typedef struct { + enumerator_t public; + task_queue_t queue; + enumerator_t *inner; queued_task_t *queued; +} task_enumerator_t; + +METHOD(enumerator_t, task_enumerator_destroy, void, + task_enumerator_t *this) +{ + this->inner->destroy(this->inner); + free(this); +} + +METHOD(enumerator_t, task_enumerator_enumerate, bool, + task_enumerator_t *this, va_list args) +{ task_t **task; VA_ARGS_VGET(args, task); - - if (orig->enumerate(orig, &queued)) + if (this->queue == TASK_QUEUE_QUEUED) + { + if (this->inner->enumerate(this->inner, &this->queued)) + { + *task = this->queued->task; + return TRUE; + } + } + else if (this->inner->enumerate(this->inner, task)) { - *task = queued->task; return TRUE; } return FALSE; @@ -2143,18 +2172,54 @@ CALLBACK(filter_queued, bool, METHOD(task_manager_t, create_task_enumerator, enumerator_t*, private_task_manager_t *this, task_queue_t queue) { + task_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = enumerator_enumerate_default, + .venumerate = _task_enumerator_enumerate, + .destroy = _task_enumerator_destroy, + }, + .queue = queue, + ); switch (queue) { case TASK_QUEUE_ACTIVE: - return array_create_enumerator(this->active_tasks); + enumerator->inner = array_create_enumerator(this->active_tasks); + break; case TASK_QUEUE_PASSIVE: - return array_create_enumerator(this->passive_tasks); + enumerator->inner = array_create_enumerator(this->passive_tasks); + break; case TASK_QUEUE_QUEUED: - return enumerator_create_filter( - array_create_enumerator(this->queued_tasks), - filter_queued, NULL, NULL); + enumerator->inner = array_create_enumerator(this->queued_tasks); + break; default: - return enumerator_create_empty(); + enumerator->inner = enumerator_create_empty(); + break; + } + return &enumerator->public; +} + +METHOD(task_manager_t, remove_task, void, + private_task_manager_t *this, enumerator_t *enumerator_public) +{ + task_enumerator_t *enumerator = (task_enumerator_t*)enumerator_public; + + switch (enumerator->queue) + { + case TASK_QUEUE_ACTIVE: + array_remove_at(this->active_tasks, enumerator->inner); + break; + case TASK_QUEUE_PASSIVE: + array_remove_at(this->passive_tasks, enumerator->inner); + break; + case TASK_QUEUE_QUEUED: + array_remove_at(this->queued_tasks, enumerator->inner); + free(enumerator->queued); + enumerator->queued = NULL; + break; + default: + break; } } @@ -2204,9 +2269,9 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, - .adopt_child_tasks = _adopt_child_tasks, .busy = _busy, .create_task_enumerator = _create_task_enumerator, + .remove_task = _remove_task, .flush = _flush, .flush_queue = _flush_queue, .destroy = _destroy, diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index 6c8b29018..0e3711898 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -174,6 +174,11 @@ static void install_outbound(private_child_delete_t *this, linked_list_t *my_ts, *other_ts; status_t status; + if (!spi) + { + return; + } + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE); if (!child_sa) @@ -312,7 +317,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; - uint32_t spi, reqid, rekey_spi; + uint32_t spi, reqid; action_t action; status_t status = SUCCESS; time_t now, expire; @@ -335,11 +340,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) } else { - rekey_spi = child_sa->get_rekey_spi(child_sa); - if (rekey_spi) - { - install_outbound(this, protocol, rekey_spi); - } + install_outbound(this, protocol, child_sa->get_rekey_spi(child_sa)); /* for rekeyed CHILD_SAs we uninstall the outbound SA but don't * immediately destroy it, by default, so we can process delayed * packets */ @@ -459,6 +460,17 @@ METHOD(task_t, build_i, status_t, this->spi = child_sa->get_spi(child_sa, TRUE); } + if (this->expired && child_sa->get_state(child_sa) == CHILD_REKEYED) + { /* the peer was expected to delete this SA, but if we send a DELETE + * we might cause a collision there if the CREATE_CHILD_SA response + * is delayed (the peer wouldn't know if we deleted this SA due to an + * expire or because of a forced delete by the user and might then + * ignore the CREATE_CHILD_SA response once it arrives) */ + child_sa->set_state(child_sa, CHILD_DELETED); + install_outbound(this, this->protocol, + child_sa->get_rekey_spi(child_sa)); + } + if (child_sa->get_state(child_sa) == CHILD_DELETED) { /* DELETEs for this CHILD_SA were already exchanged, but it was not yet * destroyed to allow delayed packets to get processed */ diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 307d99264..b570904e2 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -773,7 +773,7 @@ static bool derive_keys(private_ike_init_t *this, return FALSE; } charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, chunk_empty, - nonce_i, nonce_r, this->old_sa, NULL); + nonce_i, nonce_r, this->old_sa, NULL, AUTH_NONE); return TRUE; } @@ -890,6 +890,20 @@ METHOD(task_t, pre_process_i, status_t, switch (type) { + case COOKIE: + { + chunk_t cookie; + + cookie = notify->get_notification_data(notify); + if (chunk_equals(cookie, this->cookie)) + { + DBG1(DBG_IKE, "ignore response with duplicate COOKIE " + "notify"); + enumerator->destroy(enumerator); + return FAILED; + } + break; + } case REDIRECT: { identification_t *gateway; diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index 9545da4f3..c357d5035 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2016 Tobias Brunner + * Copyright (C) 2013-2018 Tobias Brunner * Copyright (C) 2006 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -228,13 +228,6 @@ struct task_manager_t { void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); /** - * Migrate all active or queued CHILD_SA-creating tasks from other to this. - * - * @param other manager which gives away its tasks - */ - void (*adopt_child_tasks) (task_manager_t *this, task_manager_t *other); - - /** * Increment a message ID counter, in- or outbound. * * If a message is processed outside of the manager, this call increments @@ -285,6 +278,16 @@ struct task_manager_t { task_queue_t queue); /** + * Remove the task the given enumerator points to. + * + * @note This should be used with caution, in partciular, for tasks in the + * active and passive queues. + * + * @param enumerator enumerator created with the method above + */ + void (*remove_task)(task_manager_t *this, enumerator_t *enumerator); + + /** * Flush all tasks, regardless of the queue. */ void (*flush)(task_manager_t *this); |