diff --git a/src/libcharon/config/backend_manager.c b/src/libcharon/config/backend_manager.c index 02a41a5b3..47f62d59a 100644 --- a/src/libcharon/config/backend_manager.c +++ b/src/libcharon/config/backend_manager.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2018 Tobias Brunner * Copyright (C) 2007-2009 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -129,15 +130,77 @@ static ike_cfg_match_t get_ike_match(ike_cfg_t *cand, host_t *me, host_t *other, return match; } -METHOD(backend_manager_t, get_ike_cfg, ike_cfg_t*, - private_backend_manager_t *this, host_t *me, host_t *other, - ike_version_t version) +/** + * list element to help sorting + */ +typedef struct { + ike_cfg_match_t match; + ike_cfg_t *cfg; +} ike_match_entry_t; + +CALLBACK(ike_enum_filter, bool, + linked_list_t *configs, enumerator_t *orig, va_list args) { - ike_cfg_t *current, *found = NULL; + ike_match_entry_t *entry; + ike_cfg_t **out; + + VA_ARGS_VGET(args, out); + + if (orig->enumerate(orig, &entry)) + { + *out = entry->cfg; + return TRUE; + } + return FALSE; +} + +CALLBACK(ike_match_entry_list_destroy, void, + linked_list_t *configs) +{ + ike_match_entry_t *entry; + + while (configs->remove_last(configs, (void**)&entry) == SUCCESS) + { + entry->cfg->destroy(entry->cfg); + free(entry); + } + configs->destroy(configs); +} + +/** + * Insert entry into match-sorted list + */ +static void insert_sorted_ike(ike_match_entry_t *entry, linked_list_t *list) +{ + enumerator_t *enumerator; + ike_match_entry_t *current; + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (entry->match > current->match) + { + break; + } + } + list->insert_before(list, enumerator, entry); + enumerator->destroy(enumerator); +} + +/** + * Create a sorted list of all matching IKE configs + */ +static linked_list_t *get_matching_ike_cfgs(private_backend_manager_t *this, + host_t *me, host_t *other, + ike_version_t version) +{ + ike_cfg_t *current; char *my_addr, *other_addr; enumerator_t *enumerator; - ike_cfg_match_t match, best = MATCH_ANY; ike_data_t *data; + linked_list_t *configs; + ike_cfg_match_t match; + ike_match_entry_t *entry; INIT(data, .this = this, @@ -145,44 +208,82 @@ METHOD(backend_manager_t, get_ike_cfg, ike_cfg_t*, .other = other, ); - DBG2(DBG_CFG, "looking for an ike config for %H...%H", me, other); + configs = linked_list_create(); this->lock->read_lock(this->lock); enumerator = enumerator_create_nested( this->backends->create_enumerator(this->backends), (void*)ike_enum_create, data, (void*)free); - while (enumerator->enumerate(enumerator, (void**)¤t)) + + while (enumerator->enumerate(enumerator, ¤t)) { + my_addr = current->get_my_addr(current); + other_addr = current->get_other_addr(current); match = get_ike_match(current, me, other, version); - DBG3(DBG_CFG, "ike config match: %d (%H %H %N)", - match, me, other, ike_version_names, version); + DBG3(DBG_CFG, "ike config match: %d (%s...%s %N)", match, my_addr, + other_addr, ike_version_names, current->get_version(current)); + if (match) { - my_addr = current->get_my_addr(current); - other_addr = current->get_other_addr(current); DBG2(DBG_CFG, " candidate: %s...%s, prio %d", my_addr, other_addr, match); - if (match > best) - { - DESTROY_IF(found); - found = current; - found->get_ref(found); - best = match; - } + + INIT(entry, + .match = match, + .cfg = current->get_ref(current), + ); + insert_sorted_ike(entry, configs); } } enumerator->destroy(enumerator); this->lock->unlock(this->lock); - if (found) + + return configs; +} + +METHOD(backend_manager_t, get_ike_cfg, ike_cfg_t*, + private_backend_manager_t *this, host_t *me, host_t *other, + ike_version_t version) +{ + linked_list_t *configs; + ike_match_entry_t *entry; + ike_cfg_t *found = NULL; + char *my_addr, *other_addr; + + DBG2(DBG_CFG, "looking for an %N config for %H...%H", ike_version_names, + version, me, other); + + configs = get_matching_ike_cfgs(this, me, other, version); + if (configs->get_first(configs, (void**)&entry) == SUCCESS) { + found = entry->cfg->get_ref(entry->cfg); + my_addr = found->get_my_addr(found); other_addr = found->get_other_addr(found); DBG2(DBG_CFG, "found matching ike config: %s...%s with prio %d", - my_addr, other_addr, best); + my_addr, other_addr, entry->match); } + ike_match_entry_list_destroy(configs); + return found; } +METHOD(backend_manager_t, create_ike_cfg_enumerator, enumerator_t*, + private_backend_manager_t *this, host_t *me, host_t *other, + ike_version_t version) +{ + linked_list_t *configs; + + DBG2(DBG_CFG, "looking for %N configs for %H...%H", ike_version_names, + version, me, other); + + configs = get_matching_ike_cfgs(this, me, other, version); + + return enumerator_create_filter(configs->create_enumerator(configs), + ike_enum_filter, configs, + ike_match_entry_list_destroy); +} + /** * Get the best ID match in one of the configs auth_cfg */ @@ -198,7 +299,7 @@ static id_match_t get_peer_match(identification_t *id, if (!id) { - DBG3(DBG_CFG, "peer config match %s: %d (%N)", + DBG3(DBG_CFG, " %s id match: %d (%N)", where, ID_MATCH_ANY, id_type_names, ID_ANY); return ID_MATCH_ANY; } @@ -225,7 +326,7 @@ static id_match_t get_peer_match(identification_t *id, enumerator->destroy(enumerator); data = id->get_encoding(id); - DBG3(DBG_CFG, "peer config match %s: %d (%N -> %#B)", + DBG3(DBG_CFG, " %s id match: %d (%N: %#B)", where, match, id_type_names, id->get_type(id), &data); return match; } @@ -295,34 +396,26 @@ CALLBACK(peer_enum_filter_destroy, void, } /** - * Insert entry into match-sorted list, using helper + * Insert entry into match-sorted list */ -static void insert_sorted(match_entry_t *entry, linked_list_t *list, - linked_list_t *helper) +static void insert_sorted(match_entry_t *entry, linked_list_t *list) { + enumerator_t *enumerator; match_entry_t *current; - while (list->remove_first(list, (void**)¤t) == SUCCESS) + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, ¤t)) { - helper->insert_last(helper, current); - } - while (helper->remove_first(helper, (void**)¤t) == SUCCESS) - { - if (entry && ( - (entry->match_ike > current->match_ike && - entry->match_peer >= current->match_peer) || - (entry->match_ike >= current->match_ike && - entry->match_peer > current->match_peer))) + if ((entry->match_ike > current->match_ike && + entry->match_peer >= current->match_peer) || + (entry->match_ike >= current->match_ike && + entry->match_peer > current->match_peer)) { - list->insert_last(list, entry); - entry = NULL; + break; } - list->insert_last(list, current); - } - if (entry) - { - list->insert_last(list, entry); } + list->insert_before(list, enumerator, entry); + enumerator->destroy(enumerator); } METHOD(backend_manager_t, create_peer_cfg_enumerator, enumerator_t*, @@ -332,7 +425,7 @@ METHOD(backend_manager_t, create_peer_cfg_enumerator, enumerator_t*, enumerator_t *enumerator; peer_data_t *data; peer_cfg_t *cfg; - linked_list_t *configs, *helper; + linked_list_t *configs; INIT(data, .lock = this->lock, @@ -352,35 +445,46 @@ METHOD(backend_manager_t, create_peer_cfg_enumerator, enumerator_t*, } configs = linked_list_create(); - /* only once allocated helper list for sorting */ - helper = linked_list_create(); while (enumerator->enumerate(enumerator, &cfg)) { - id_match_t match_peer_me, match_peer_other; + ike_cfg_t *ike_cfg = cfg->get_ike_cfg(cfg); ike_cfg_match_t match_ike; + id_match_t match_peer_me, match_peer_other; match_entry_t *entry; + char *my_addr, *other_addr; + + match_ike = get_ike_match(ike_cfg, me, other, version); + my_addr = ike_cfg->get_my_addr(ike_cfg); + other_addr = ike_cfg->get_other_addr(ike_cfg); + DBG3(DBG_CFG, "peer config \"%s\", ike match: %d (%s...%s %N)", + cfg->get_name(cfg), match_ike, my_addr, other_addr, + ike_version_names, ike_cfg->get_version(ike_cfg)); + + if (!match_ike) + { + continue; + } match_peer_me = get_peer_match(my_id, cfg, TRUE); + if (!match_peer_me) + { + continue; + } match_peer_other = get_peer_match(other_id, cfg, FALSE); - match_ike = get_ike_match(cfg->get_ike_cfg(cfg), me, other, version); - DBG3(DBG_CFG, "ike config match: %d (%H %H %N)", - match_ike, me, other, ike_version_names, version); - if (match_peer_me && match_peer_other && match_ike) + if (match_peer_other) { DBG2(DBG_CFG, " candidate \"%s\", match: %d/%d/%d (me/other/ike)", cfg->get_name(cfg), match_peer_me, match_peer_other, match_ike); - INIT(entry, .match_peer = match_peer_me + match_peer_other, .match_ike = match_ike, .cfg = cfg->get_ref(cfg), ); - insert_sorted(entry, configs, helper); + insert_sorted(entry, configs); } } enumerator->destroy(enumerator); - helper->destroy(helper); return enumerator_create_filter(configs->create_enumerator(configs), peer_enum_filter, configs, @@ -430,8 +534,7 @@ METHOD(backend_manager_t, destroy, void, } /* - * Described in header-file - + * Described in header */ backend_manager_t *backend_manager_create() { @@ -440,6 +543,7 @@ backend_manager_t *backend_manager_create() INIT(this, .public = { .get_ike_cfg = _get_ike_cfg, + .create_ike_cfg_enumerator = _create_ike_cfg_enumerator, .get_peer_cfg_by_name = _get_peer_cfg_by_name, .create_peer_cfg_enumerator = _create_peer_cfg_enumerator, .add_backend = _add_backend, diff --git a/src/libcharon/config/backend_manager.h b/src/libcharon/config/backend_manager.h index 8ec79ce28..ada295f0d 100644 --- a/src/libcharon/config/backend_manager.h +++ b/src/libcharon/config/backend_manager.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2018 Tobias Brunner * Copyright (C) 2007 Martin Willi * HSR Hochschule fuer Technik Rapperswil * @@ -63,6 +64,20 @@ struct backend_manager_t { host_t *my_host, host_t *other_host, ike_version_t version); + /** + * Create an enumerator over all matching IKE configs. + * + * Pass NULL as parameters to match any. The enumerator enumerates over + * ike_cfgs, ordered by priority (best match first). + * + * @param me local address + * @param other remote address + * @param version IKE version to get a config for + * @return enumerator over ike_cfg + */ + enumerator_t* (*create_ike_cfg_enumerator)(backend_manager_t *this, + host_t *me, host_t *other, ike_version_t version); + /** * Get a peer_config identified by it's name. * diff --git a/src/libcharon/config/child_cfg.c b/src/libcharon/config/child_cfg.c index bc417f936..d8083d433 100644 --- a/src/libcharon/config/child_cfg.c +++ b/src/libcharon/config/child_cfg.c @@ -254,7 +254,7 @@ METHOD(child_cfg_t, select_proposal, proposal_t*, { DBG2(DBG_CFG, "received proposals: %#P", proposals); DBG2(DBG_CFG, "configured proposals: %#P", this->proposals); - DBG2(DBG_CFG, "selected proposal: %P", selected); + DBG1(DBG_CFG, "selected proposal: %P", selected); break; } } @@ -289,7 +289,7 @@ METHOD(child_cfg_t, add_traffic_selector, void, METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*, private_child_cfg_t *this, bool local, linked_list_t *supplied, - linked_list_t *hosts) + linked_list_t *hosts, bool log) { enumerator_t *e1, *e2; traffic_selector_t *ts1, *ts2, *selected; @@ -334,13 +334,19 @@ METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*, } e1->destroy(e1); - DBG2(DBG_CFG, "%s traffic selectors for %s:", - supplied ? "selecting" : "proposing", local ? "us" : "other"); - if (supplied == NULL) + if (log) + { + DBG2(DBG_CFG, "%s traffic selectors for %s:", + supplied ? "selecting" : "proposing", local ? "us" : "other"); + } + if (!supplied) { while (derived->remove_first(derived, (void**)&ts1) == SUCCESS) { - DBG2(DBG_CFG, " %R", ts1); + if (log) + { + DBG2(DBG_CFG, " %R", ts1); + } result->insert_last(result, ts1); } derived->destroy(derived); @@ -358,11 +364,14 @@ METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*, selected = ts1->get_subset(ts1, ts2); if (selected) { - DBG2(DBG_CFG, " config: %R, received: %R => match: %R", - ts1, ts2, selected); + if (log) + { + DBG2(DBG_CFG, " config: %R, received: %R => match: %R", + ts1, ts2, selected); + } result->insert_last(result, selected); } - else + else if (log) { DBG2(DBG_CFG, " config: %R, received: %R => no match", ts1, ts2); diff --git a/src/libcharon/config/child_cfg.h b/src/libcharon/config/child_cfg.h index d566da3ec..2defd0330 100644 --- a/src/libcharon/config/child_cfg.h +++ b/src/libcharon/config/child_cfg.h @@ -135,11 +135,13 @@ struct child_cfg_t { * @param local TRUE for TS on local side, FALSE for remote * @param supplied list with TS to select from, or NULL * @param hosts addresses to use for narrowing "dynamic" TS', host_t + * @param log FALSE to avoid logging details about the selection * @return list containing the traffic selectors */ linked_list_t *(*get_traffic_selectors)(child_cfg_t *this, bool local, linked_list_t *supplied, - linked_list_t *hosts); + linked_list_t *hosts, bool log); + /** * Get the updown script to run for the CHILD_SA. * diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index a73a5b5e2..357c4a73b 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 Tobias Brunner + * Copyright (C) 2012-2018 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -309,6 +309,25 @@ METHOD(ike_cfg_t, get_proposals, linked_list_t*, return proposals; } +METHOD(ike_cfg_t, has_proposal, bool, + private_ike_cfg_t *this, proposal_t *match, bool private) +{ + enumerator_t *enumerator; + proposal_t *proposal; + + enumerator = this->proposals->create_enumerator(this->proposals); + while (enumerator->enumerate(enumerator, &proposal)) + { + if (proposal->matches(proposal, match, private)) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + return FALSE; +} + METHOD(ike_cfg_t, select_proposal, proposal_t*, private_ike_cfg_t *this, linked_list_t *proposals, bool private, bool prefer_self) @@ -344,7 +363,7 @@ METHOD(ike_cfg_t, select_proposal, proposal_t*, { DBG2(DBG_CFG, "received proposals: %#P", proposals); DBG2(DBG_CFG, "configured proposals: %#P", this->proposals); - DBG2(DBG_CFG, "selected proposal: %P", selected); + DBG1(DBG_CFG, "selected proposal: %P", selected); break; } } @@ -618,6 +637,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, .add_proposal = _add_proposal, .get_proposals = _get_proposals, .select_proposal = _select_proposal, + .has_proposal = _has_proposal, .get_dh_group = _get_dh_group, .equals = _equals, .get_ref = _get_ref, diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index ac2deef70..49690c892 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2017 Tobias Brunner + * Copyright (C) 2012-2018 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -179,6 +179,15 @@ struct ike_cfg_t { proposal_t *(*select_proposal) (ike_cfg_t *this, linked_list_t *proposals, bool private, bool prefer_self); + /** + * Check if the config has a matching proposal. + * + * @param match proposal to check + * @param private accept algorithms from a private range + * @return TRUE if a matching proposal is contained + */ + bool(*has_proposal)(ike_cfg_t *this, proposal_t *match, bool private); + /** * Should we send a certificate request in IKE_SA_INIT? * diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index 29f067858..47a994f60 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -379,7 +379,7 @@ static int get_ts_match(child_cfg_t *cfg, bool local, int match = 0, round; /* fetch configured TS list, narrowing dynamic TS */ - cfg_list = cfg->get_traffic_selectors(cfg, local, NULL, hosts); + cfg_list = cfg->get_traffic_selectors(cfg, local, NULL, hosts, TRUE); /* use a round counter to rate leading TS with higher priority */ round = sup_list->get_count(sup_list); diff --git a/src/libcharon/plugins/smp/smp.c b/src/libcharon/plugins/smp/smp.c index 86296443d..e7f618584 100644 --- a/src/libcharon/plugins/smp/smp.c +++ b/src/libcharon/plugins/smp/smp.c @@ -324,10 +324,12 @@ static void request_query_config(xmlTextReaderPtr reader, xmlTextWriterPtr write xmlTextWriterStartElement(writer, "childconfig"); xmlTextWriterWriteElement(writer, "name", child_cfg->get_name(child_cfg)); - list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); + list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, + NULL, FALSE); write_networks(writer, "local", list); list->destroy_offset(list, offsetof(traffic_selector_t, destroy)); - list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL); + list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, + NULL, FALSE); write_networks(writer, "remote", list); list->destroy_offset(list, offsetof(traffic_selector_t, destroy)); xmlTextWriterEndElement(writer); diff --git a/src/libcharon/plugins/stroke/stroke_list.c b/src/libcharon/plugins/stroke/stroke_list.c index d1bf139c2..392eac86d 100644 --- a/src/libcharon/plugins/stroke/stroke_list.c +++ b/src/libcharon/plugins/stroke/stroke_list.c @@ -580,8 +580,10 @@ METHOD(stroke_list_t, status, void, children = peer_cfg->create_child_cfg_enumerator(peer_cfg); while (children->enumerate(children, &child_cfg)) { - my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); - other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL); + my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, + NULL, NULL, FALSE); + other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, + NULL, NULL, FALSE); fprintf(out, "%12s: child: %#R === %#R %N", child_cfg->get_name(child_cfg), my_ts, other_ts, ipsec_mode_names, child_cfg->get_mode(child_cfg)); @@ -614,8 +616,10 @@ METHOD(stroke_list_t, status, void, fprintf(out, "Shunted Connections:\n"); first = FALSE; } - my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); - other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL); + my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, + NULL, FALSE); + other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, + NULL, FALSE); fprintf(out, "%12s: %#R === %#R %N\n", child_cfg->get_name(child_cfg), my_ts, other_ts, ipsec_mode_names, child_cfg->get_mode(child_cfg)); diff --git a/src/libcharon/plugins/unity/unity_narrow.c b/src/libcharon/plugins/unity/unity_narrow.c index 05ae8d504..afbd6cc7e 100644 --- a/src/libcharon/plugins/unity/unity_narrow.c +++ b/src/libcharon/plugins/unity/unity_narrow.c @@ -56,7 +56,7 @@ static void narrow_ts(child_cfg_t *cfg, traffic_selector_t *ts, received = linked_list_create(); received->insert_last(received, ts); - selected = cfg->get_traffic_selectors(cfg, FALSE, received, NULL); + selected = cfg->get_traffic_selectors(cfg, FALSE, received, NULL, FALSE); while (selected->remove_first(selected, (void**)&ts) == SUCCESS) { list->insert_last(list, ts); @@ -140,7 +140,8 @@ static void narrow_responder_post(child_cfg_t *child_cfg, linked_list_t *local) { ts->destroy(ts); } - configured = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); + configured = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL, + FALSE); while (configured->remove_first(configured, (void**)&ts) == SUCCESS) { diff --git a/src/libcharon/plugins/unity/unity_provider.c b/src/libcharon/plugins/unity/unity_provider.c index b52ffeeb1..76aad47e6 100644 --- a/src/libcharon/plugins/unity/unity_provider.c +++ b/src/libcharon/plugins/unity/unity_provider.c @@ -160,7 +160,8 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); while (enumerator->enumerate(enumerator, &child_cfg)) { - current = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); + current = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL, + FALSE); while (current->remove_first(current, (void**)&ts) == SUCCESS) { if (use_ts(ts)) diff --git a/src/libcharon/plugins/vici/vici_query.c b/src/libcharon/plugins/vici/vici_query.c index 4385cf606..f529902db 100644 --- a/src/libcharon/plugins/vici/vici_query.c +++ b/src/libcharon/plugins/vici/vici_query.c @@ -570,7 +570,7 @@ static void raise_policy_cfg(private_vici_query_t *this, u_int id, char *ike, list_mode(b, NULL, cfg); b->begin_list(b, "local-ts"); - list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL); + list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL, FALSE); enumerator = list->create_enumerator(list); while (enumerator->enumerate(enumerator, &ts)) { @@ -581,7 +581,7 @@ static void raise_policy_cfg(private_vici_query_t *this, u_int id, char *ike, b->end_list(b /* local-ts */); b->begin_list(b, "remote-ts"); - list = cfg->get_traffic_selectors(cfg, FALSE, NULL, NULL); + list = cfg->get_traffic_selectors(cfg, FALSE, NULL, NULL, FALSE); enumerator = list->create_enumerator(list); while (enumerator->enumerate(enumerator, &ts)) { @@ -873,7 +873,8 @@ CALLBACK(list_conns, vici_message_t*, child_cfg->get_close_action(child_cfg)); b->begin_list(b, "local-ts"); - list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); + list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, + NULL, FALSE); selectors = list->create_enumerator(list); while (selectors->enumerate(selectors, &ts)) { @@ -884,7 +885,8 @@ CALLBACK(list_conns, vici_message_t*, b->end_list(b /* local-ts */); b->begin_list(b, "remote-ts"); - list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, NULL); + list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, + NULL, FALSE); selectors = list->create_enumerator(list); while (selectors->enumerate(selectors, &ts)) { diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 7eeb578f3..49717703c 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1723,7 +1723,7 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) traffic_selector_t *ts; list = linked_list_create_with_items(ike, NULL); - ts_list = config->get_traffic_selectors(config, local, NULL, list); + ts_list = config->get_traffic_selectors(config, local, NULL, list, FALSE); list->destroy(list); enumerator = ts_list->create_enumerator(ts_list); diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index f39fed6f0..a4ad866d3 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -674,6 +674,7 @@ METHOD(ike_sa_t, get_ike_cfg, ike_cfg_t*, METHOD(ike_sa_t, set_ike_cfg, void, private_ike_sa_t *this, ike_cfg_t *ike_cfg) { + DESTROY_IF(this->ike_cfg); ike_cfg->get_ref(ike_cfg); this->ike_cfg = ike_cfg; } diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 5e5b61e7f..007e94d96 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -544,7 +544,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, hosts = get_dynamic_hosts(this->ike_sa, local); list = this->config->get_traffic_selectors(this->config, - local, supplied, hosts); + local, supplied, hosts, TRUE); hosts->destroy(hosts); if (list->get_first(list, (void**)&ts) == SUCCESS) { diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c index 968b4386c..bec2cfe7d 100644 --- a/src/libcharon/sa/ikev1/tasks/xauth.c +++ b/src/libcharon/sa/ikev1/tasks/xauth.c @@ -226,7 +226,7 @@ static bool select_compliant_config(private_xauth_t *this) { /* current config is fine */ return TRUE; } - DBG1(DBG_CFG, "selected peer config '%s' inacceptable", + DBG1(DBG_CFG, "selected peer config '%s' unacceptable", old->get_name(old)); aggressive = old->use_aggressive(old); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index c90af23b9..c7eb0c854 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -481,12 +481,14 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local, this->ike_sa->has_condition(this->ike_sa, cond)) { nat = get_transport_nat_ts(this, local, in); - ts = this->config->get_traffic_selectors(this->config, local, nat, hosts); + ts = this->config->get_traffic_selectors(this->config, local, nat, + hosts, TRUE); nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy)); } else { - ts = this->config->get_traffic_selectors(this->config, local, in, hosts); + ts = this->config->get_traffic_selectors(this->config, local, in, + hosts, TRUE); } hosts->destroy(hosts); @@ -497,8 +499,8 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local, /** * Install a CHILD_SA for usage, return value: * - FAILED: no acceptable proposal - * - INVALID_ARG: diffie hellman group inacceptable - * - NOT_FOUND: TS inacceptable + * - INVALID_ARG: diffie hellman group unacceptable + * - NOT_FOUND: TS unacceptable */ static status_t select_and_install(private_child_create_t *this, bool no_dh, bool ike_auth) @@ -559,7 +561,7 @@ static status_t select_and_install(private_child_create_t *this, 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; @@ -1075,7 +1077,7 @@ METHOD(task_t, build_i, status_t, if (list->get_count(list)) { this->tsi = this->config->get_traffic_selectors(this->config, - TRUE, NULL, list); + TRUE, NULL, list, TRUE); list->destroy_offset(list, offsetof(host_t, destroy)); } else @@ -1083,12 +1085,12 @@ METHOD(task_t, build_i, status_t, list->destroy(list); list = get_dynamic_hosts(this->ike_sa, TRUE); this->tsi = this->config->get_traffic_selectors(this->config, - TRUE, NULL, list); + TRUE, NULL, list, TRUE); list->destroy(list); } list = get_dynamic_hosts(this->ike_sa, FALSE); this->tsr = this->config->get_traffic_selectors(this->config, - FALSE, NULL, list); + FALSE, NULL, list, TRUE); list->destroy(list); if (this->packet_tsi) @@ -1356,7 +1358,7 @@ METHOD(task_t, build_r, status_t, } if (this->config == NULL) { - DBG1(DBG_IKE, "traffic selectors %#R === %#R inacceptable", + DBG1(DBG_IKE, "traffic selectors %#R === %#R unacceptable", this->tsr, this->tsi); charon->bus->alert(charon->bus, ALERT_TS_MISMATCH, this->tsi, this->tsr); message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty); diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 6b63197d5..5e284bc97 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -285,13 +285,18 @@ static bool load_cfg_candidates(private_ike_auth_t *this) { enumerator_t *enumerator; peer_cfg_t *peer_cfg; + ike_cfg_t *ike_cfg; host_t *me, *other; identification_t *my_id, *other_id; + proposal_t *ike_proposal; + bool private; me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); + ike_proposal = this->ike_sa->get_proposal(this->ike_sa); + private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); DBG1(DBG_CFG, "looking for peer configs matching %H[%Y]...%H[%Y]", me, my_id, other, other_id); @@ -299,11 +304,18 @@ static bool load_cfg_candidates(private_ike_auth_t *this) me, other, my_id, other_id, IKEV2); while (enumerator->enumerate(enumerator, &peer_cfg)) { + /* ignore all configs that have no matching IKE proposal */ + ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); + if (!ike_cfg->has_proposal(ike_cfg, ike_proposal, private)) + { + DBG2(DBG_CFG, "ignore candidate '%s' without matching IKE proposal", + peer_cfg->get_name(peer_cfg)); + continue; + } peer_cfg->get_ref(peer_cfg); if (this->peer_cfg == NULL) { /* best match */ this->peer_cfg = peer_cfg; - this->ike_sa->set_peer_cfg(this->ike_sa, peer_cfg); } else { @@ -313,6 +325,7 @@ static bool load_cfg_candidates(private_ike_auth_t *this) enumerator->destroy(enumerator); if (this->peer_cfg) { + this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg); DBG1(DBG_CFG, "selected peer config '%s'", this->peer_cfg->get_name(this->peer_cfg)); return TRUE; @@ -369,7 +382,7 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict) { break; } - DBG1(DBG_CFG, "selected peer config '%s' inacceptable: %s", + DBG1(DBG_CFG, "selected peer config '%s' unacceptable: %s", this->peer_cfg->get_name(this->peer_cfg), comply_error); this->peer_cfg->destroy(this->peer_cfg); } @@ -603,7 +616,7 @@ METHOD(task_t, process_r, status_t, (uintptr_t)cand->get(cand, AUTH_RULE_EAP_TYPE) == EAP_NAK && (uintptr_t)cand->get(cand, AUTH_RULE_EAP_VENDOR) == 0)) { /* peer requested EAP, but current config does not match */ - DBG1(DBG_IKE, "peer requested EAP, config inacceptable"); + DBG1(DBG_IKE, "peer requested EAP, config unacceptable"); this->peer_cfg->destroy(this->peer_cfg); this->peer_cfg = NULL; if (!update_cfg_candidates(this, FALSE)) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 3d73d728b..295d4e9d9 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -54,11 +54,6 @@ struct private_ike_init_t { */ bool initiator; - /** - * IKE config to establish - */ - ike_cfg_t *config; - /** * diffie hellman group to use */ @@ -286,14 +281,15 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) 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); + 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)) @@ -357,7 +353,7 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) /* 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, @@ -403,6 +399,68 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) 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; + bool private, prefer_configured; + + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + + proposal_list = sa_payload->get_proposals(sa_payload); + private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); + prefer_configured = lib->settings->get_bool(lib->settings, + "%s.prefer_configured_proposals", TRUE, lib->ns); + + this->proposal = ike_cfg->select_proposal(ike_cfg, proposal_list, private, + prefer_configured); + 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, + private, prefer_configured); + 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 */ @@ -419,24 +477,7 @@ static void process_payloads(private_ike_init_t *this, message_t *message) { case PLV2_SECURITY_ASSOCIATION: { - sa_payload_t *sa_payload = (sa_payload_t*)payload; - linked_list_t *proposal_list; - bool private, prefer_configured; - - proposal_list = sa_payload->get_proposals(sa_payload); - private = this->ike_sa->supports_extension(this->ike_sa, - EXT_STRONGSWAN); - prefer_configured = lib->settings->get_bool(lib->settings, - "%s.prefer_configured_proposals", TRUE, lib->ns); - this->proposal = this->config->select_proposal(this->config, - proposal_list, private, prefer_configured); - 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: @@ -533,7 +574,10 @@ static void process_payloads(private_ike_init_t *this, message_t *message) 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), @@ -563,12 +607,12 @@ METHOD(task_t, build_i, status_t, } else { /* this shouldn't happen, but let's be safe */ - this->dh_group = this->config->get_dh_group(this->config); + this->dh_group = ike_cfg->get_dh_group(ike_cfg); } } else { - this->dh_group = this->config->get_dh_group(this->config); + this->dh_group = ike_cfg->get_dh_group(ike_cfg); } this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, this->dh_group); @@ -627,7 +671,6 @@ METHOD(task_t, build_i, status_t, 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); @@ -699,7 +742,7 @@ METHOD(task_t, build_r, status_t, 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; } @@ -728,7 +771,7 @@ METHOD(task_t, build_r, status_t, 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; @@ -770,12 +813,14 @@ METHOD(task_t, build_r, status_t, */ 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; diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c index 11123b415..57f9a797e 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c @@ -259,7 +259,7 @@ METHOD(task_t, build_r, status_t, } if (this->new_sa == NULL) { - /* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */ + /* IKE_SA/a CHILD_SA is in an unacceptable state, deny rekeying */ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return SUCCESS; } diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index a83da0480..d66e70937 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -117,8 +117,10 @@ static bool install_shunt_policy(child_cfg_t *child) host_any6 = host_create_any(AF_INET6); hosts = linked_list_create_with_items(host_any, host_any6, NULL); - my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts); - other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); + my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts, + FALSE); + other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts, + FALSE); hosts->destroy(hosts); manual_prio = child->get_manual_prio(child); @@ -287,8 +289,10 @@ static void uninstall_shunt_policy(child_cfg_t *child) host_any6 = host_create_any(AF_INET6); hosts = linked_list_create_with_items(host_any, host_any6, NULL); - my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts); - other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); + my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts, + FALSE); + other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts, + FALSE); hosts->destroy(hosts); manual_prio = child->get_manual_prio(child); diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 979f9290a..148df3923 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -168,7 +168,7 @@ static bool dynamic_remote_ts(child_cfg_t *child) traffic_selector_t *ts; bool found = FALSE; - other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL); + other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL, FALSE); enumerator = other_ts->create_enumerator(other_ts); while (enumerator->enumerate(enumerator, &ts)) { @@ -296,11 +296,11 @@ METHOD(trap_manager_t, install, bool, child_sa = child_sa_create(me, other, child, 0, FALSE, 0, 0); list = linked_list_create_with_items(me, NULL); - my_ts = child->get_traffic_selectors(child, TRUE, NULL, list); + my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE); list->destroy_offset(list, offsetof(host_t, destroy)); list = linked_list_create_with_items(other, NULL); - other_ts = child->get_traffic_selectors(child, FALSE, NULL, list); + other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE); list->destroy_offset(list, offsetof(host_t, destroy)); /* We don't know the finally negotiated protocol (ESP|AH), we install diff --git a/src/libpttls/pt_tls_client.c b/src/libpttls/pt_tls_client.c index 167918811..265a4a09a 100644 --- a/src/libpttls/pt_tls_client.c +++ b/src/libpttls/pt_tls_client.c @@ -225,7 +225,7 @@ static status_t do_sasl(private_pt_tls_client_t *this, sasl_mechanism_t *sasl) reader->destroy(reader); return SUCCESS; case NEED_MORE: - /* inacceptable, it won't get more. FALL */ + /* unacceptable, it won't get more. FALL */ case FAILED: default: reader->destroy(reader); diff --git a/src/libstrongswan/crypto/proposal/proposal.c b/src/libstrongswan/crypto/proposal/proposal.c index d671879c0..952608997 100644 --- a/src/libstrongswan/crypto/proposal/proposal.c +++ b/src/libstrongswan/crypto/proposal/proposal.c @@ -335,22 +335,16 @@ METHOD(proposal_t, strip_dh, void, } /** - * Select a matching proposal from this and other, insert into selected. + * Select a matching proposal from this and other. */ static bool select_algo(private_proposal_t *this, proposal_t *other, - proposal_t *selected, transform_type_t type, bool priv) + transform_type_t type, bool priv, bool log, + uint16_t *alg, uint16_t *ks) { enumerator_t *e1, *e2; uint16_t alg1, alg2, ks1, ks2; bool found = FALSE, optional = FALSE; - if (type == INTEGRITY_ALGORITHM && - selected->get_algorithm(selected, ENCRYPTION_ALGORITHM, &alg1, NULL) && - encryption_algorithm_is_aead(alg1)) - { - /* no integrity algorithm required, we have an AEAD */ - return TRUE; - } if (type == DIFFIE_HELLMAN_GROUP) { optional = this->protocol == PROTO_ESP || this->protocol == PROTO_AH; @@ -398,36 +392,86 @@ static bool select_algo(private_proposal_t *this, proposal_t *other, { if (!priv && alg1 >= 1024) { - /* accept private use algorithms only if requested */ - DBG1(DBG_CFG, "an algorithm from private space would match, " - "but peer implementation is unknown, skipped"); + if (log) + { + DBG1(DBG_CFG, "an algorithm from private space would " + "match, but peer implementation is unknown, " + "skipped"); + } continue; } - selected->add_algorithm(selected, type, alg1, ks1); + *alg = alg1; + *ks = ks1; found = TRUE; break; } } } - /* no match in all comparisons */ e1->destroy(e1); e2->destroy(e2); - - if (!found) - { - DBG2(DBG_CFG, " no acceptable %N found", transform_type_names, type); - } return found; } +/** + * Select algorithms from the given proposals, if selected is given, the result + * is stored there and errors are logged. + */ +static bool select_algos(private_proposal_t *this, proposal_t *other, + proposal_t *selected, bool private) +{ + transform_type_t type; + array_t *types; + bool skip_integrity = FALSE; + int i; + + types = merge_types(this, (private_proposal_t*)other); + for (i = 0; i < array_count(types); i++) + { + uint16_t alg = 0, ks = 0; + + array_get(types, i, &type); + if (type == INTEGRITY_ALGORITHM && skip_integrity) + { + continue; + } + if (select_algo(this, other, type, private, selected != NULL, &alg, &ks)) + { + if (alg == 0 && type != EXTENDED_SEQUENCE_NUMBERS) + { /* 0 is "valid" for extended sequence numbers, for other + * transforms it either means NONE or is reserved */ + continue; + } + if (selected) + { + selected->add_algorithm(selected, type, alg, ks); + } + if (type == ENCRYPTION_ALGORITHM && + encryption_algorithm_is_aead(alg)) + { + /* no integrity algorithm required, we have an AEAD */ + skip_integrity = TRUE; + } + } + else + { + if (selected) + { + DBG2(DBG_CFG, " no acceptable %N found", transform_type_names, + type); + } + array_destroy(types); + return FALSE; + } + } + array_destroy(types); + return TRUE; +} + METHOD(proposal_t, select_proposal, proposal_t*, private_proposal_t *this, proposal_t *other, bool other_remote, bool private) { proposal_t *selected; - transform_type_t type; - array_t *types; - int i; DBG2(DBG_CFG, "selecting proposal:"); @@ -448,23 +492,25 @@ METHOD(proposal_t, select_proposal, proposal_t*, selected->set_spi(selected, this->spi); } - types = merge_types(this, (private_proposal_t*)other); - for (i = 0; i < array_count(types); i++) + if (!select_algos(this, other, selected, private)) { - array_get(types, i, &type); - if (!select_algo(this, other, selected, type, private)) - { - selected->destroy(selected); - array_destroy(types); - return NULL; - } + selected->destroy(selected); + return NULL; } - array_destroy(types); - DBG2(DBG_CFG, " proposal matches"); return selected; } +METHOD(proposal_t, matches, bool, + private_proposal_t *this, proposal_t *other, bool private) +{ + if (this->protocol != other->get_protocol(other)) + { + return FALSE; + } + return select_algos(this, other, NULL, private); +} + METHOD(proposal_t, get_protocol, protocol_id_t, private_proposal_t *this) { @@ -910,6 +956,7 @@ proposal_t *proposal_create(protocol_id_t protocol, u_int number) .promote_dh_group = _promote_dh_group, .strip_dh = _strip_dh, .select = _select_proposal, + .matches = _matches, .get_protocol = _get_protocol, .set_spi = _set_spi, .get_spi = _get_spi, diff --git a/src/libstrongswan/crypto/proposal/proposal.h b/src/libstrongswan/crypto/proposal/proposal.h index 0052674b9..338324326 100644 --- a/src/libstrongswan/crypto/proposal/proposal.h +++ b/src/libstrongswan/crypto/proposal/proposal.h @@ -34,7 +34,6 @@ typedef struct proposal_t proposal_t; #include #include #include -#include /** * Protocol ID of a proposal. @@ -143,6 +142,17 @@ struct proposal_t { proposal_t *(*select)(proposal_t *this, proposal_t *other, bool other_remote, bool private); + /** + * Check if the given proposal matches this proposal. + * + * This is similar to select, but no resulting proposal is selected. + * + * @param other proposal to compare against + * @param private accepts algorithms allocated in a private range + * @return TRUE if the proposals match + */ + bool (*matches)(proposal_t *this, proposal_t *other, bool private); + /** * Get the protocol ID of the proposal. * diff --git a/src/libstrongswan/tests/suites/test_proposal.c b/src/libstrongswan/tests/suites/test_proposal.c index 938fa38aa..099cd19c7 100644 --- a/src/libstrongswan/tests/suites/test_proposal.c +++ b/src/libstrongswan/tests/suites/test_proposal.c @@ -102,7 +102,12 @@ static struct { { PROTO_ESP, "aes128-sha256-modp3072-modpnone", "aes128-sha256", "aes128-sha256" }, { PROTO_ESP, "aes128-sha256", "aes128-sha256-modp3072-modpnone", "aes128-sha256" }, { PROTO_ESP, "aes128-sha256-modp3072-modpnone", "aes128-sha256-modpnone-modp3072", "aes128-sha256-modp3072" }, - { PROTO_ESP, "aes128-sha256-modpnone-modp3072", "aes128-sha256-modp3072-modpnone", "aes128-sha256-modpnone" }, + { PROTO_ESP, "aes128-sha256-modpnone-modp3072", "aes128-sha256-modp3072-modpnone", "aes128-sha256" }, + { PROTO_ESP, "aes128-sha256-esn", "aes128-sha256-esn", "aes128-sha256-esn" }, + { PROTO_ESP, "aes128-sha256-noesn", "aes128-sha256-esn", NULL }, + { PROTO_ESP, "aes128-sha256-noesn-esn", "aes128-sha256-esn", "aes128-sha256-esn" }, + { PROTO_ESP, "aes128-sha256-noesn-esn", "aes128-sha256", "aes128-sha256" }, + { PROTO_ESP, "aes128-sha256-esn-noesn", "aes128-sha256-noesn-esn", "aes128-sha256-esn" }, { PROTO_IKE, "aes128-sha256-modp3072", "aes128-sha256-modp3072", "aes128-sha256-modp3072" }, { PROTO_IKE, "aes128-sha256-modp3072", "aes128-sha256-modp3072-modpnone", "aes128-sha256-modp3072" }, { PROTO_IKE, "aes128-sha256-modp3072-modpnone", "aes128-sha256-modp3072", "aes128-sha256-modp3072" }, @@ -159,6 +164,29 @@ START_TEST(test_select_spi) } END_TEST +START_TEST(test_matches) +{ + proposal_t *self, *other; + + self = proposal_create_from_string(select_data[_i].proto, + select_data[_i].self); + other = proposal_create_from_string(select_data[_i].proto, + select_data[_i].other); + if (select_data[_i].expected) + { + ck_assert(self->matches(self, other, FALSE)); + ck_assert(other->matches(other, self, FALSE)); + } + else + { + ck_assert(!self->matches(self, other, FALSE)); + ck_assert(!other->matches(other, self, FALSE)); + } + other->destroy(other); + self->destroy(self); +} +END_TEST + START_TEST(test_promote_dh_group) { proposal_t *proposal; @@ -312,6 +340,10 @@ Suite *proposal_suite_create() tcase_add_test(tc, test_select_spi); suite_add_tcase(s, tc); + tc = tcase_create("matches"); + tcase_add_loop_test(tc, test_matches, 0, countof(select_data)); + suite_add_tcase(s, tc); + tc = tcase_create("promote_dh_group"); tcase_add_test(tc, test_promote_dh_group); tcase_add_test(tc, test_promote_dh_group_already_front); diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 2ba6dd2a6..1f2439ca1 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -188,7 +188,7 @@ static status_t process_server_hello(private_tls_peer_t *this, suite = cipher; if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) { - DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", + DBG1(DBG_TLS, "received TLS cipher suite %N unacceptable", tls_cipher_suite_names, suite); this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); return NEED_MORE; diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c index 422211afa..70d17f22c 100644 --- a/src/libtls/tls_server.c +++ b/src/libtls/tls_server.c @@ -190,7 +190,7 @@ static bool select_suite_and_key(private_tls_server_t *this, suites, count, type); if (!this->suite) { - DBG1(DBG_TLS, "received cipher suites inacceptable"); + DBG1(DBG_TLS, "received cipher suites unacceptable"); return FALSE; } this->server_auth->destroy(this->server_auth); @@ -199,7 +199,7 @@ static bool select_suite_and_key(private_tls_server_t *this, this->server_auth); if (!key) { - DBG1(DBG_TLS, "received cipher suites inacceptable"); + DBG1(DBG_TLS, "received cipher suites unacceptable"); return FALSE; } } diff --git a/testing/tests/ikev2/multi-level-ca-ldap/evaltest.dat b/testing/tests/ikev2/multi-level-ca-ldap/evaltest.dat index 4abcde1e8..49271bd8c 100644 --- a/testing/tests/ikev2/multi-level-ca-ldap/evaltest.dat +++ b/testing/tests/ikev2/multi-level-ca-ldap/evaltest.dat @@ -10,7 +10,7 @@ carol::cat /var/log/daemon.log::received TS_UNACCEPTABLE notify, no CHILD_SA bui carol::ipsec status 2> /dev/null::venus.*INSTALLED::NO moon:: ipsec status 2> /dev/null::venus.*ESTABLISHED.*moon.strongswan.org.*carol@strongswan.org::NO moon:: cat /var/log/daemon.log::constraint check failed: peer not authenticated by.*Research CA::YES -moon:: cat /var/log/daemon.log::selected peer config.*alice.*inacceptable::YES +moon:: cat /var/log/daemon.log::selected peer config.*alice.*unacceptable::YES moon:: cat /var/log/daemon.log::switching to peer config.*venus::YES dave:: ipsec status 2> /dev/null::venus.*INSTALLED, TUNNEL::YES moon:: ipsec status 2> /dev/null::venus.*ESTABLISHED.*moon.strongswan.org.*dave@strongswan.org::YES diff --git a/testing/tests/ikev2/multi-level-ca/evaltest.dat b/testing/tests/ikev2/multi-level-ca/evaltest.dat index e1c5be4ed..10da97f98 100644 --- a/testing/tests/ikev2/multi-level-ca/evaltest.dat +++ b/testing/tests/ikev2/multi-level-ca/evaltest.dat @@ -10,7 +10,7 @@ carol::cat /var/log/daemon.log::received TS_UNACCEPTABLE notify, no CHILD_SA bui carol::ipsec status 2> /dev/null::venus.*INSTALLED::NO moon:: ipsec status 2> /dev/null::venus.*ESTABLISHED.*carol@strongswan.org::NO moon:: cat /var/log/daemon.log::constraint check failed: peer not authenticated by.*Research CA::YES -moon:: cat /var/log/daemon.log::selected peer config.*alice.*inacceptable::YES +moon:: cat /var/log/daemon.log::selected peer config.*alice.*unacceptable::YES moon:: cat /var/log/daemon.log::switching to peer config.*venus::YES dave:: ipsec status 2> /dev/null::venus.*INSTALLED, TUNNEL::YES moon:: ipsec status 2> /dev/null::venus.*ESTABLISHED.*dave@strongswan.org::YES diff --git a/testing/tests/swanctl/multi-level-ca/evaltest.dat b/testing/tests/swanctl/multi-level-ca/evaltest.dat index 050a35db3..e6ec6b2ff 100644 --- a/testing/tests/swanctl/multi-level-ca/evaltest.dat +++ b/testing/tests/swanctl/multi-level-ca/evaltest.dat @@ -11,7 +11,7 @@ carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED. moon:: swanctl --list-sas --raw 2> /dev/null::sales.*version=2 state=ESTABLISHED.*remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*child-sas.*venus.*state=INSTALLED::NO dave:: cat /var/log/daemon.log::received TS_UNACCEPTABLE notify, no CHILD_SA built::YES moon:: cat /var/log/daemon.log::constraint check failed: peer not authenticated by.*Research CA::YES -moon:: cat /var/log/daemon.log::selected peer config.*research.*inacceptable::YES +moon:: cat /var/log/daemon.log::selected peer config.*research.*unacceptable::YES moon:: cat /var/log/daemon.log::switching to peer config.*sales::YES dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED.*child-sas.*alice.*state=INSTALLED::NO moon:: swanctl --list-sas --raw 2> /dev/null::research.*version=2 state=ESTABLISHED.*remote-host=192.168.0.100 remote-port=4500 remote-id=dave@strongswan.org.*child-sas.*alice.*state=INSTALLED::NO diff --git a/testing/tests/swanctl/net2net-gw/hosts/carol/etc/swanctl/swanctl.conf b/testing/tests/swanctl/net2net-gw/hosts/carol/etc/swanctl/swanctl.conf index cdf6bcaf5..aeea94e1f 100755 --- a/testing/tests/swanctl/net2net-gw/hosts/carol/etc/swanctl/swanctl.conf +++ b/testing/tests/swanctl/net2net-gw/hosts/carol/etc/swanctl/swanctl.conf @@ -11,7 +11,7 @@ connections { id = moon.strongswan.org } children { - net-moon { + net { local_ts = 10.2.0.0/16 remote_ts = 10.1.0.0/16 @@ -23,27 +23,16 @@ connections { mobike = no proposals = aes128-sha256-x25519 } - gw-sun { - local { - auth = pubkey - certs = carolCert.pem - id = carol@strongswan.org - } + + gw-sun : connections.gw-moon { remote { - auth = pubkey id = sun.strongswan.org } children { - net-sun { + net { local_ts = 10.1.0.0/16 remote_ts = 10.2.0.0/16 - - updown = /usr/local/libexec/ipsec/_updown iptables - esp_proposals = aes128gcm128-modp3072 } } - version = 2 - mobike = no - proposals = aes128-sha256-modp3072 } } diff --git a/testing/tests/swanctl/ocsp-multi-level/evaltest.dat b/testing/tests/swanctl/ocsp-multi-level/evaltest.dat index 2f3c34622..87ef4319b 100644 --- a/testing/tests/swanctl/ocsp-multi-level/evaltest.dat +++ b/testing/tests/swanctl/ocsp-multi-level/evaltest.dat @@ -18,7 +18,7 @@ carol::swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED. moon:: swanctl --list-sas --raw 2> /dev/null::sales.*version=2 state=ESTABLISHED.*remote-host=192.168.0.100 remote-port=4500 remote-id=carol@strongswan.org.*child-sas.*venus.*state=INSTALLED::NO dave:: cat /var/log/daemon.log::received TS_UNACCEPTABLE notify, no CHILD_SA built::YES moon:: cat /var/log/daemon.log::constraint check failed: peer not authenticated by.*Research CA::YES -moon:: cat /var/log/daemon.log::selected peer config.*research.*inacceptable::YES +moon:: cat /var/log/daemon.log::selected peer config.*research.*unacceptable::YES moon:: cat /var/log/daemon.log::switching to peer config.*sales::YES dave:: swanctl --list-sas --raw 2> /dev/null::home.*version=2 state=ESTABLISHED.*child-sas.*alice.*state=INSTALLED::NO moon:: swanctl --list-sas --raw 2> /dev/null::research.*version=2 state=ESTABLISHED.*remote-host=192.168.0.100 remote-port=4500 remote-id=dave@strongswan.org.*child-sas.*alice.*state=INSTALLED::NO