summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/ikev2
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/ikev2')
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c97
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c227
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c24
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.c16
4 files changed, 258 insertions, 106 deletions
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;