/*
- * Copyright (C) 2008-2015 Tobias Brunner
+ * Copyright (C) 2008-2019 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
*/
bool initiator;
- /**
- * IKE config to establish
- */
- ike_cfg_t *config;
-
/**
* diffie hellman group to use
*/
peer_cfg_t *peer;
auth_cfg_t *auth;
auth_rule_t rule;
- uintptr_t config;
+ signature_params_t *config;
+ int written;
+ size_t len = BUF_LEN;
+ char buf[len];
+ char *pos = buf;
char *plugin_name;
algos = hash_algorithm_set_create();
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &rule, &config))
{
- if (rule == AUTH_RULE_SIGNATURE_SCHEME)
+ if (rule == AUTH_RULE_IKE_SIGNATURE_SCHEME)
{
- hash = hasher_from_signature_scheme(config);
+ hash = hasher_from_signature_scheme(config->scheme,
+ config->params);
if (hasher_algorithm_for_ikev2(hash))
{
algos->add(algos, hash);
while (enumerator->enumerate(enumerator, &hash))
{
writer->write_uint16(writer, hash);
+
+ /* generate debug output */
+ written = snprintf(pos, len, " %N", hash_algorithm_short_names,
+ hash);
+ if (written > 0 && written < len)
+ {
+ pos += written;
+ len -= written;
+ }
}
enumerator->destroy(enumerator);
message->add_notify(message, FALSE, SIGNATURE_HASH_ALGORITHMS,
writer->get_buf(writer));
writer->destroy(writer);
+
+ *pos = '\0';
+ DBG2(DBG_CFG, "sending supported signature hash algorithms:%s", buf);
}
algos->destroy(algos);
}
notify_payload_t *notify)
{
bio_reader_t *reader;
- u_int16_t algo;
+ uint16_t algo;
+ int written;
+ size_t len = BUF_LEN;
+ char buf[len];
+ char *pos = buf;
bool added = FALSE;
reader = bio_reader_create(notify->get_notification_data(notify));
{
this->keymat->add_hash_algorithm(this->keymat, algo);
added = TRUE;
+
+ /* generate debug output */
+ written = snprintf(pos, len, " %N", hash_algorithm_short_names,
+ algo);
+ if (written > 0 && written < len)
+ {
+ pos += written;
+ len -= written;
+ }
}
}
reader->destroy(reader);
+ *pos = '\0';
+ DBG2(DBG_CFG, "received supported signature hash algorithms:%s", buf);
+
if (added)
{
this->ike_sa->enable_extension(this->ike_sa, EXT_SIGNATURE_AUTH);
}
}
+/**
+ * Check whether to send a USE_PPK notify
+ */
+static bool send_use_ppk(private_ike_init_t *this)
+{
+ peer_cfg_t *peer;
+ enumerator_t *keys;
+ shared_key_t *key;
+ bool use_ppk = FALSE;
+
+ if (this->initiator)
+ {
+ peer = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer->get_ppk_id(peer))
+ {
+ use_ppk = TRUE;
+ }
+ }
+ else if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK))
+ {
+ /* check if we have at least one PPK available */
+ keys = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PPK,
+ NULL, NULL);
+ if (keys->enumerate(keys, &key, NULL, NULL))
+ {
+ use_ppk = TRUE;
+ }
+ keys->destroy(keys);
+ }
+ return use_ppk;
+}
+
/**
* build the payloads for the message
*/
sa_payload_t *sa_payload;
ke_payload_t *ke_payload;
nonce_payload_t *nonce_payload;
- linked_list_t *proposal_list;
+ linked_list_t *proposal_list, *other_dh_groups;
ike_sa_id_t *id;
proposal_t *proposal;
enumerator_t *enumerator;
+ ike_cfg_t *ike_cfg;
id = this->ike_sa->get_id(this->ike_sa);
- this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
if (this->initiator)
{
- proposal_list = this->config->get_proposals(this->config);
- if (this->old_sa)
+ proposal_list = ike_cfg->get_proposals(ike_cfg);
+ other_dh_groups = linked_list_create();
+ enumerator = proposal_list->create_enumerator(proposal_list);
+ while (enumerator->enumerate(enumerator, (void**)&proposal))
{
/* include SPI of new IKE_SA when we are rekeying */
- enumerator = proposal_list->create_enumerator(proposal_list);
- while (enumerator->enumerate(enumerator, (void**)&proposal))
+ if (this->old_sa)
{
proposal->set_spi(proposal, id->get_initiator_spi(id));
}
- enumerator->destroy(enumerator);
+ /* move the selected DH group to the front of the proposal */
+ if (!proposal->promote_dh_group(proposal, this->dh_group))
+ { /* the proposal does not include the group, move to the back */
+ proposal_list->remove_at(proposal_list, enumerator);
+ other_dh_groups->insert_last(other_dh_groups, proposal);
+ }
+ }
+ enumerator->destroy(enumerator);
+ /* add proposals that don't contain the selected group */
+ enumerator = other_dh_groups->create_enumerator(other_dh_groups);
+ while (enumerator->enumerate(enumerator, (void**)&proposal))
+ { /* no need to remove from the list as we destroy it anyway*/
+ proposal_list->insert_last(proposal_list, proposal);
}
+ enumerator->destroy(enumerator);
+ other_dh_groups->destroy(other_dh_groups);
sa_payload = sa_payload_create_from_proposals_v2(proposal_list);
proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
}
message->add_payload(message, (payload_t*)sa_payload);
- nonce_payload = nonce_payload_create(PLV2_NONCE);
- nonce_payload->set_nonce(nonce_payload, this->my_nonce);
ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE,
this->dh);
if (!ke_payload)
DBG1(DBG_IKE, "creating KE payload failed");
return FALSE;
}
+ nonce_payload = nonce_payload_create(PLV2_NONCE);
+ nonce_payload->set_nonce(nonce_payload, this->my_nonce);
if (this->old_sa)
{ /* payload order differs if we are rekeying */
/* negotiate fragmentation if we are not rekeying */
if (!this->old_sa &&
- this->config->fragmentation(this->config) != FRAGMENTATION_NO)
+ ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO)
{
if (this->initiator ||
this->ike_sa->supports_extension(this->ike_sa,
/* notify other peer if we support redirection */
if (!this->old_sa && this->initiator && this->follow_redirects)
{
- message->add_notify(message, FALSE, REDIRECT_SUPPORTED, chunk_empty);
+ identification_t *gateway;
+ host_t *from;
+ chunk_t data;
+
+ from = this->ike_sa->get_redirected_from(this->ike_sa);
+ if (from)
+ {
+ gateway = identification_create_from_sockaddr(
+ from->get_sockaddr(from));
+ data = redirect_data_create(gateway, chunk_empty);
+ message->add_notify(message, FALSE, REDIRECTED_FROM, data);
+ chunk_free(&data);
+ gateway->destroy(gateway);
+ }
+ else
+ {
+ message->add_notify(message, FALSE, REDIRECT_SUPPORTED,
+ chunk_empty);
+ }
+ }
+ /* notify the peer if we want to use/support PPK */
+ if (!this->old_sa && send_use_ppk(this))
+ {
+ message->add_notify(message, FALSE, USE_PPK, chunk_empty);
+ }
+ /* notify the peer if we accept childless IKE_SAs */
+ if (!this->old_sa && !this->initiator &&
+ ike_cfg->childless(ike_cfg) != CHILDLESS_NEVER)
+ {
+ message->add_notify(message, FALSE, CHILDLESS_IKEV2_SUPPORTED,
+ chunk_empty);
}
return TRUE;
}
+/**
+ * Process the SA payload and select a proposal
+ */
+static void process_sa_payload(private_ike_init_t *this, message_t *message,
+ sa_payload_t *sa_payload)
+{
+ ike_cfg_t *ike_cfg, *cfg, *alt_cfg = NULL;
+ enumerator_t *enumerator;
+ linked_list_t *proposal_list;
+ host_t *me, *other;
+ proposal_selection_flag_t flags = 0;
+
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+
+ proposal_list = sa_payload->get_proposals(sa_payload);
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN) &&
+ !lib->settings->get_bool(lib->settings, "%s.accept_private_algs",
+ FALSE, lib->ns))
+ {
+ flags |= PROPOSAL_SKIP_PRIVATE;
+ }
+ if (!lib->settings->get_bool(lib->settings,
+ "%s.prefer_configured_proposals", TRUE, lib->ns))
+ {
+ flags |= PROPOSAL_PREFER_SUPPLIED;
+ }
+ this->proposal = ike_cfg->select_proposal(ike_cfg, proposal_list, flags);
+ if (!this->proposal)
+ {
+ if (!this->initiator && !this->old_sa)
+ {
+ me = message->get_destination(message);
+ other = message->get_source(message);
+ enumerator = charon->backends->create_ike_cfg_enumerator(
+ charon->backends, me, other, IKEV2);
+ while (enumerator->enumerate(enumerator, &cfg))
+ {
+ if (ike_cfg == cfg)
+ { /* already tried and failed */
+ continue;
+ }
+ DBG1(DBG_IKE, "no matching proposal found, trying alternative "
+ "config");
+ this->proposal = cfg->select_proposal(cfg, proposal_list,
+ flags);
+ if (this->proposal)
+ {
+ alt_cfg = cfg->get_ref(cfg);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ if (alt_cfg)
+ {
+ this->ike_sa->set_ike_cfg(this->ike_sa, alt_cfg);
+ alt_cfg->destroy(alt_cfg);
+ }
+ else
+ {
+ charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE,
+ proposal_list);
+ }
+ }
+ proposal_list->destroy_offset(proposal_list,
+ offsetof(proposal_t, destroy));
+}
+
/**
* Read payloads from message
*/
{
case PLV2_SECURITY_ASSOCIATION:
{
- sa_payload_t *sa_payload = (sa_payload_t*)payload;
- linked_list_t *proposal_list;
- bool private;
-
- proposal_list = sa_payload->get_proposals(sa_payload);
- private = this->ike_sa->supports_extension(this->ike_sa,
- EXT_STRONGSWAN);
- this->proposal = this->config->select_proposal(this->config,
- proposal_list, private);
- if (!this->proposal)
- {
- charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE,
- proposal_list);
- }
- proposal_list->destroy_offset(proposal_list,
- offsetof(proposal_t, destroy));
+ process_sa_payload(this, message, (sa_payload_t*)payload);
break;
}
case PLV2_KEY_EXCHANGE:
handle_supported_hash_algorithms(this, notify);
}
break;
+ case USE_PPK:
+ if (!this->old_sa)
+ {
+ this->ike_sa->enable_extension(this->ike_sa,
+ EXT_PPK);
+ }
+ break;
+ case REDIRECTED_FROM:
+ {
+ identification_t *gateway;
+ chunk_t data;
+
+ data = notify->get_notification_data(notify);
+ gateway = redirect_data_parse(data, NULL);
+ if (!gateway)
+ {
+ DBG1(DBG_IKE, "received invalid REDIRECTED_FROM "
+ "notify, ignored");
+ break;
+ }
+ DBG1(DBG_IKE, "client got redirected from %Y", gateway);
+ gateway->destroy(gateway);
+ /* fall-through */
+ }
case REDIRECT_SUPPORTED:
- this->ike_sa->enable_extension(this->ike_sa,
- EXT_IKE_REDIRECTION);
+ if (!this->old_sa)
+ {
+ this->ike_sa->enable_extension(this->ike_sa,
+ EXT_IKE_REDIRECTION);
+ }
+ break;
+ case CHILDLESS_IKEV2_SUPPORTED:
+ if (this->initiator && !this->old_sa)
+ {
+ this->ike_sa->enable_extension(this->ike_sa,
+ EXT_IKE_CHILDLESS);
+ }
break;
default:
/* other notifies are handled elsewhere */
}
enumerator->destroy(enumerator);
+ if (this->proposal)
+ {
+ this->ike_sa->set_proposal(this->ike_sa, this->proposal);
+ }
+
if (ke_payload && this->proposal &&
this->proposal->has_dh_group(this->proposal, this->dh_group))
{
this->dh = this->keymat->keymat.create_dh(
&this->keymat->keymat, this->dh_group);
}
- if (this->dh)
+ else if (this->dh)
+ {
+ this->dh_failed = this->dh->get_dh_group(this->dh) != this->dh_group;
+ }
+ if (this->dh && !this->dh_failed)
{
this->dh_failed = !this->dh->set_other_public_value(this->dh,
ke_payload->get_key_exchange_data(ke_payload));
METHOD(task_t, build_i, status_t,
private_ike_init_t *this, message_t *message)
{
- this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
+ ike_cfg_t *ike_cfg;
+
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+
DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H",
this->ike_sa->get_name(this->ike_sa),
this->ike_sa->get_unique_id(this->ike_sa),
return FAILED;
}
- /* if the DH group is set via use_dh_group(), we already have a DH object */
+ /* if we are retrying after an INVALID_KE_PAYLOAD we already have one */
if (!this->dh)
{
- this->dh_group = this->config->get_dh_group(this->config);
+ if (this->old_sa && lib->settings->get_bool(lib->settings,
+ "%s.prefer_previous_dh_group", TRUE, lib->ns))
+ { /* reuse the DH group we used for the old IKE_SA when rekeying */
+ proposal_t *proposal;
+ uint16_t dh_group;
+
+ proposal = this->old_sa->get_proposal(this->old_sa);
+ if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
+ &dh_group, NULL))
+ {
+ this->dh_group = dh_group;
+ }
+ else
+ { /* this shouldn't happen, but let's be safe */
+ this->dh_group = ike_cfg->get_dh_group(ike_cfg);
+ }
+ }
+ else
+ {
+ this->dh_group = ike_cfg->get_dh_group(ike_cfg);
+ }
this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
this->dh_group);
if (!this->dh)
return FAILED;
}
}
+ else if (this->dh->get_dh_group(this->dh) != this->dh_group)
+ { /* reset DH instance if group changed (INVALID_KE_PAYLOAD) */
+ this->dh->destroy(this->dh);
+ this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
+ this->dh_group);
+ if (!this->dh)
+ {
+ DBG1(DBG_IKE, "requested DH group %N not supported",
+ diffie_hellman_group_names, this->dh_group);
+ return FAILED;
+ }
+ }
/* generate nonce only when we are trying the first time */
if (this->my_nonce.ptr == NULL)
METHOD(task_t, process_r, status_t,
private_ike_init_t *this, message_t *message)
{
- this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
DBG0(DBG_IKE, "%H is initiating an IKE_SA", message->get_source(message));
this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
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;
}
if (this->proposal == NULL ||
this->other_nonce.len == 0 || this->my_nonce.len == 0)
{
- DBG1(DBG_IKE, "received proposals inacceptable");
+ DBG1(DBG_IKE, "received proposals unacceptable");
message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
return FAILED;
}
- this->ike_sa->set_proposal(this->ike_sa, this->proposal);
/* check if we'd have to redirect the client */
- if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
+ if (!this->old_sa &&
+ this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
charon->redirect->redirect_on_init(charon->redirect, this->ike_sa,
&gateway))
{
if (this->dh == NULL ||
!this->proposal->has_dh_group(this->proposal, this->dh_group))
{
- u_int16_t group;
+ uint16_t group;
if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
&group, NULL))
{
- DBG1(DBG_IKE, "DH group %N inacceptable, requesting %N",
+ DBG1(DBG_IKE, "DH group %N unacceptable, requesting %N",
diffie_hellman_group_names, this->dh_group,
diffie_hellman_group_names, group);
this->dh_group = group;
else
{
DBG1(DBG_IKE, "no acceptable proposal found");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
}
return FAILED;
}
*/
static void raise_alerts(private_ike_init_t *this, notify_type_t type)
{
+ ike_cfg_t *ike_cfg;
linked_list_t *list;
switch (type)
{
case NO_PROPOSAL_CHOSEN:
- list = this->config->get_proposals(this->config);
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ list = ike_cfg->get_proposals(ike_cfg);
charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, list);
list->destroy_offset(list, offsetof(proposal_t, destroy));
break;
}
}
+METHOD(task_t, pre_process_i, status_t,
+ private_ike_init_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ payload_t *payload;
+
+ /* check for erroneous notifies */
+ enumerator = message->create_payload_enumerator(message);
+ while (enumerator->enumerate(enumerator, &payload))
+ {
+ if (payload->get_type(payload) == PLV2_NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ 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;
+ chunk_t data, nonce = chunk_empty;
+ status_t status = SUCCESS;
+
+ if (this->old_sa)
+ {
+ break;
+ }
+ data = notify->get_notification_data(notify);
+ gateway = redirect_data_parse(data, &nonce);
+ if (!gateway || !chunk_equals(nonce, this->my_nonce))
+ {
+ DBG1(DBG_IKE, "received invalid REDIRECT notify");
+ status = FAILED;
+ }
+ DESTROY_IF(gateway);
+ chunk_free(&nonce);
+ enumerator->destroy(enumerator);
+ return status;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ return SUCCESS;
+}
+
METHOD(task_t, process_i, status_t,
private_ike_init_t *this, message_t *message)
{
bad_group = this->dh_group;
data = notify->get_notification_data(notify);
- this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+ this->dh_group = ntohs(*((uint16_t*)data.ptr));
DBG1(DBG_IKE, "peer didn't accept DH group %N, "
"it requested %N", diffie_hellman_group_names,
bad_group, diffie_hellman_group_names, this->dh_group);
if (this->old_sa == NULL)
{ /* reset the IKE_SA if we are not rekeying */
- this->ike_sa->reset(this->ike_sa);
+ this->ike_sa->reset(this->ike_sa, FALSE);
}
enumerator->destroy(enumerator);
{
chunk_free(&this->cookie);
this->cookie = chunk_clone(notify->get_notification_data(notify));
- this->ike_sa->reset(this->ike_sa);
+ this->ike_sa->reset(this->ike_sa, FALSE);
enumerator->destroy(enumerator);
DBG2(DBG_IKE, "received %N notify", notify_type_names, type);
this->retry++;
chunk_t data, nonce = chunk_empty;
status_t status = FAILED;
- data = notify->get_notification_data(notify);
- gateway = redirect_data_parse(data, &nonce);
- enumerator->destroy(enumerator);
- if (!gateway || !chunk_equals(nonce, this->my_nonce))
+ if (this->old_sa)
{
- DBG1(DBG_IKE, "received invalid REDIRECT notify");
+ DBG1(DBG_IKE, "received REDIRECT notify during rekeying"
+ ", ignored");
+ break;
}
- else if (this->ike_sa->handle_redirect(this->ike_sa,
- gateway))
+ data = notify->get_notification_data(notify);
+ gateway = redirect_data_parse(data, &nonce);
+ if (this->ike_sa->handle_redirect(this->ike_sa, gateway))
{
status = NEED_MORE;
}
DESTROY_IF(gateway);
chunk_free(&nonce);
+ enumerator->destroy(enumerator);
return status;
}
default:
DBG1(DBG_IKE, "peers proposal selection invalid");
return FAILED;
}
- this->ike_sa->set_proposal(this->ike_sa, this->proposal);
if (this->dh == NULL ||
!this->proposal->has_dh_group(this->proposal, this->dh_group))
this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
this->proposal = NULL;
this->dh_failed = FALSE;
- if (this->dh && this->dh->get_dh_group(this->dh) != this->dh_group)
- { /* reset DH value only if group changed (INVALID_KE_PAYLOAD) */
- this->dh->destroy(this->dh);
- this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
- this->dh_group);
- }
}
METHOD(task_t, destroy, void,
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
+ this->public.task.pre_process = _pre_process_i;
}
else
{