diff options
Diffstat (limited to 'src/charon/config/backend_manager.c')
-rw-r--r-- | src/charon/config/backend_manager.c | 314 |
1 files changed, 206 insertions, 108 deletions
diff --git a/src/charon/config/backend_manager.c b/src/charon/config/backend_manager.c index a9fe974af..3a3a78466 100644 --- a/src/charon/config/backend_manager.c +++ b/src/charon/config/backend_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Martin Willi + * Copyright (C) 2007-2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -11,8 +11,6 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. - * - * $Id: backend_manager.c 4758 2008-12-04 23:16:10Z andreas $ */ #include "backend_manager.h" @@ -68,15 +66,6 @@ typedef struct { } ike_data_t; /** - * data to pass nested peer enumerator - */ -typedef struct { - private_backend_manager_t *this; - identification_t *me; - identification_t *other; -} peer_data_t; - -/** * inner enumerator constructor for IKE cfgs */ static enumerator_t *ike_enum_create(backend_t *backend, ike_data_t *data) @@ -85,59 +74,58 @@ static enumerator_t *ike_enum_create(backend_t *backend, ike_data_t *data) } /** - * inner enumerator constructor for Peer cfgs - */ -static enumerator_t *peer_enum_create(backend_t *backend, peer_data_t *data) -{ - return backend->create_peer_cfg_enumerator(backend, data->me, data->other); -} -/** - * inner enumerator constructor for all Peer cfgs - */ -static enumerator_t *peer_enum_create_all(backend_t *backend) -{ - return backend->create_peer_cfg_enumerator(backend, NULL, NULL); -} - -/** * get a match of a candidate ike_cfg for two hosts */ -static ike_cfg_match_t get_match(ike_cfg_t *cand, host_t *me, host_t *other) +static ike_cfg_match_t get_ike_match(ike_cfg_t *cand, host_t *me, host_t *other) { host_t *me_cand, *other_cand; ike_cfg_match_t match = MATCH_NONE; - me_cand = host_create_from_dns(cand->get_my_addr(cand), - me->get_family(me), 0); - if (!me_cand) + if (me) { - return MATCH_NONE; - } - if (me_cand->ip_equals(me_cand, me)) - { - match += MATCH_ME; + me_cand = host_create_from_dns(cand->get_my_addr(cand), + me->get_family(me), 0); + if (!me_cand) + { + return MATCH_NONE; + } + if (me_cand->ip_equals(me_cand, me)) + { + match += MATCH_ME; + } + else if (me_cand->is_anyaddr(me_cand)) + { + match += MATCH_ANY; + } + me_cand->destroy(me_cand); } - else if (me_cand->is_anyaddr(me_cand)) + else { match += MATCH_ANY; } - me_cand->destroy(me_cand); - other_cand = host_create_from_dns(cand->get_other_addr(cand), - other->get_family(other), 0); - if (!other_cand) + if (other) { - return MATCH_NONE; - } - if (other_cand->ip_equals(other_cand, other)) - { - match += MATCH_OTHER; + other_cand = host_create_from_dns(cand->get_other_addr(cand), + other->get_family(other), 0); + if (!other_cand) + { + return MATCH_NONE; + } + if (other_cand->ip_equals(other_cand, other)) + { + match += MATCH_OTHER; + } + else if (other_cand->is_anyaddr(other_cand)) + { + match += MATCH_ANY; + } + other_cand->destroy(other_cand); } - else if (other_cand->is_anyaddr(other_cand)) + else { match += MATCH_ANY; } - other_cand->destroy(other_cand); return match; } @@ -165,7 +153,7 @@ static ike_cfg_t *get_ike_cfg(private_backend_manager_t *this, (void*)ike_enum_create, data, (void*)free); while (enumerator->enumerate(enumerator, (void**)¤t)) { - match = get_match(current, me, other); + match = get_ike_match(current, me, other); if (match) { @@ -191,87 +179,198 @@ static ike_cfg_t *get_ike_cfg(private_backend_manager_t *this, return found; } +/** + * Get the best ID match in one of the configs auth_cfg + */ +static id_match_t get_peer_match(identification_t *id, + peer_cfg_t *cfg, bool local) +{ + enumerator_t *enumerator; + auth_cfg_t *auth; + identification_t *candidate; + id_match_t match = ID_MATCH_NONE; + + if (!id) + { + return ID_MATCH_ANY; + } + + /* compare first auth config only */ + enumerator = cfg->create_auth_cfg_enumerator(cfg, local); + if (enumerator->enumerate(enumerator, &auth)) + { + candidate = auth->get(auth, AUTH_RULE_IDENTITY); + if (candidate) + { + match = id->matches(id, candidate); + /* match vice-versa, as the proposed IDr might be ANY */ + if (!match) + { + match = candidate->matches(candidate, id); + } + } + else + { + match = ID_MATCH_ANY; + } + } + enumerator->destroy(enumerator); + return match; +} + +/** + * data to pass nested peer enumerator + */ +typedef struct { + rwlock_t *lock; + identification_t *me; + identification_t *other; +} peer_data_t; + +/** + * list element to help sorting + */ +typedef struct { + id_match_t match_peer; + ike_cfg_match_t match_ike; + peer_cfg_t *cfg; +} match_entry_t; + +/** + * inner enumerator constructor for peer cfgs + */ +static enumerator_t *peer_enum_create(backend_t *backend, peer_data_t *data) +{ + return backend->create_peer_cfg_enumerator(backend, data->me, data->other); +} + +/** + * unlock/cleanup peer enumerator + */ +static void peer_enum_destroy(peer_data_t *data) +{ + data->lock->unlock(data->lock); + free(data); +} -static enumerator_t *create_peer_cfg_enumerator(private_backend_manager_t *this) +/** + * convert enumerator value from match_entry to config + */ +static bool peer_enum_filter(linked_list_t *configs, + match_entry_t **in, peer_cfg_t **out) { - this->lock->read_lock(this->lock); - return enumerator_create_nested( - this->backends->create_enumerator(this->backends), - (void*)peer_enum_create_all, this->lock, - (void*)this->lock->unlock); + *out = (*in)->cfg; + return TRUE; +} + +/** + * Clean up temporary config list + */ +static void peer_enum_filter_destroy(linked_list_t *configs) +{ + match_entry_t *entry; + + while (configs->remove_last(configs, (void**)&entry) == SUCCESS) + { + entry->cfg->destroy(entry->cfg); + free(entry); + } + configs->destroy(configs); } /** - * implements backend_manager_t.get_peer_cfg. + * Insert entry into match-sorted list, using helper + */ +static void insert_sorted(match_entry_t *entry, linked_list_t *list, + linked_list_t *helper) +{ + match_entry_t *current; + + while (list->remove_first(list, (void**)¤t) == SUCCESS) + { + 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))) + { + list->insert_last(list, entry); + entry = NULL; + } + list->insert_last(list, current); + } + if (entry) + { + list->insert_last(list, entry); + } +} + +/** + * Implements backend_manager_t.create_peer_cfg_enumerator. */ -static peer_cfg_t *get_peer_cfg(private_backend_manager_t *this, host_t *me, - host_t *other, identification_t *my_id, - identification_t *other_id, auth_info_t *auth) +static enumerator_t *create_peer_cfg_enumerator(private_backend_manager_t *this, + host_t *me, host_t *other, identification_t *my_id, + identification_t *other_id) { - peer_cfg_t *current, *found = NULL; enumerator_t *enumerator; - id_match_t best_peer = ID_MATCH_NONE; - ike_cfg_match_t best_ike = MATCH_NONE; peer_data_t *data; - - DBG2(DBG_CFG, "looking for a peer config for %H[%D]...%H[%D]", - me, my_id, other, other_id); + peer_cfg_t *cfg; + linked_list_t *configs, *helper; data = malloc_thing(peer_data_t); - data->this = this; + data->lock = this->lock; data->me = my_id; data->other = other_id; + /* create a sorted list with all matches */ this->lock->read_lock(this->lock); enumerator = enumerator_create_nested( - this->backends->create_enumerator(this->backends), - (void*)peer_enum_create, data, (void*)free); - while (enumerator->enumerate(enumerator, ¤t)) + this->backends->create_enumerator(this->backends), + (void*)peer_enum_create, data, (void*)peer_enum_destroy); + + if (!me && !other && !my_id && !other_id) + { /* shortcut if we are doing a "listall" */ + return enumerator; + } + + DBG1(DBG_CFG, "looking for peer configs matching %H[%Y]...%H[%Y]", + me, my_id, other, other_id); + + configs = linked_list_create(); + /* only once allocated helper list for sorting */ + helper = linked_list_create(); + while (enumerator->enumerate(enumerator, &cfg)) { - identification_t *my_cand, *other_cand; - id_match_t m1, m2, match_peer; + id_match_t match_peer_me, match_peer_other; ike_cfg_match_t match_ike; + match_entry_t *entry; - my_cand = current->get_my_id(current); - other_cand = current->get_other_id(current); - - /* own ID may have wildcards in both, config and request (missing IDr) */ - m1 = my_cand->matches(my_cand, my_id); - if (!m1) - { - m1 = my_id->matches(my_id, my_cand); - } - m2 = other_id->matches(other_id, other_cand); - - match_peer = m1 + m2; - match_ike = get_match(current->get_ike_cfg(current), me, other); + match_peer_me = get_peer_match(my_id, cfg, TRUE); + match_peer_other = get_peer_match(other_id, cfg, FALSE); + match_ike = get_ike_match(cfg->get_ike_cfg(cfg), me, other); - if (m1 && m2 && match_ike && - auth->complies(auth, current->get_auth(current))) + if (match_peer_me && match_peer_other && match_ike) { - DBG2(DBG_CFG, " candidate \"%s\": %D...%D with prio %d.%d", - current->get_name(current), my_cand, other_cand, - match_peer, match_ike); - if ((match_peer > best_peer && match_ike >= best_ike) || - (match_peer >= best_peer && match_ike > best_ike)) - { - DESTROY_IF(found); - found = current; - found->get_ref(found); - best_peer = match_peer; - best_ike = match_ike; - } + DBG2(DBG_CFG, " candidate \"%s\", match: %d/%d/%d (me/other/ike)", + cfg->get_name(cfg), match_peer_me, match_peer_other, match_ike); + + entry = malloc_thing(match_entry_t); + entry->match_peer = match_peer_me + match_peer_other; + entry->match_ike = match_ike; + entry->cfg = cfg->get_ref(cfg); + insert_sorted(entry, configs, helper); } } - if (found) - { - DBG1(DBG_CFG, "found matching peer config \"%s\": %D...%D with prio %d.%d", - found->get_name(found), found->get_my_id(found), - found->get_other_id(found), best_peer, best_ike); - } enumerator->destroy(enumerator); - this->lock->unlock(this->lock); - return found; + helper->destroy(helper); + + return enumerator_create_filter(configs->create_enumerator(configs), + (void*)peer_enum_filter, configs, + (void*)peer_enum_filter_destroy); } /** @@ -332,9 +431,8 @@ backend_manager_t *backend_manager_create() private_backend_manager_t *this = malloc_thing(private_backend_manager_t); this->public.get_ike_cfg = (ike_cfg_t* (*)(backend_manager_t*, host_t*, host_t*))get_ike_cfg; - this->public.get_peer_cfg = (peer_cfg_t* (*)(backend_manager_t*,host_t*,host_t*,identification_t*,identification_t*,auth_info_t*))get_peer_cfg; this->public.get_peer_cfg_by_name = (peer_cfg_t* (*)(backend_manager_t*,char*))get_peer_cfg_by_name; - this->public.create_peer_cfg_enumerator = (enumerator_t* (*)(backend_manager_t*))create_peer_cfg_enumerator; + this->public.create_peer_cfg_enumerator = (enumerator_t* (*)(backend_manager_t*,host_t*,host_t*,identification_t*,identification_t*))create_peer_cfg_enumerator; this->public.add_backend = (void(*)(backend_manager_t*, backend_t *backend))add_backend; this->public.remove_backend = (void(*)(backend_manager_t*, backend_t *backend))remove_backend; this->public.destroy = (void (*)(backend_manager_t*))destroy; |