From: Tobias Brunner Date: Tue, 1 Feb 2022 19:17:50 +0000 (+0100) Subject: trap-manager: Add facility to install externally managed trap policies X-Git-Tag: 5.9.6rc1~3^2~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3c65cf64563c6ff67f632ff4a8310ce02498c459;p=thirdparty%2Fstrongswan.git trap-manager: Add facility to install externally managed trap policies This allows managing trap policies outside of the trap manager. We'll use this to create trap policies with generic labels if trap policies can't (or won't) be used (e.g. as responder for roadwarriors). --- diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index b00f7edc97..7b6e2abdf6 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -106,6 +106,8 @@ typedef struct { child_sa_t *child_sa; /** TRUE in case of wildcard Transport Mode SA */ bool wildcard; + /** TRUE for CHILD_SAs that are externally managed */ + bool external; } entry_t; /** @@ -125,7 +127,10 @@ typedef struct { */ static void destroy_entry(entry_t *this) { - this->child_sa->destroy(this->child_sa); + if (!this->external) + { + this->child_sa->destroy(this->child_sa); + } this->peer_cfg->destroy(this->peer_cfg); free(this->name); free(this); @@ -183,6 +188,41 @@ static bool dynamic_remote_ts(child_cfg_t *child) return found; } +/** + * Install the given trap + */ +static status_t install_trap(child_sa_t *child_sa, linked_list_t *local, + linked_list_t *remote) +{ + linked_list_t *my_ts, *other_ts, *proposals; + proposal_t *proposal; + child_cfg_t *child; + protocol_id_t proto = PROTO_ESP; + + child = child_sa->get_config(child_sa); + + my_ts = child->get_traffic_selectors(child, TRUE, NULL, local, FALSE); + other_ts = child->get_traffic_selectors(child, FALSE, NULL, remote, FALSE); + + /* we don't know the finally negotiated protocol (ESP|AH), we install + * the SA with the protocol of the first proposal */ + proposals = child->get_proposals(child, TRUE); + if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS) + { + proto = proposal->get_protocol(proposal); + } + proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); + + child_sa->set_protocol(child_sa, proto); + child_sa->set_mode(child_sa, child->get_mode(child)); + + child_sa->set_policies(child_sa, my_ts, other_ts); + my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + + return child_sa->install_policies(child_sa); +} + METHOD(trap_manager_t, install, bool, private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child) { @@ -190,12 +230,9 @@ METHOD(trap_manager_t, install, bool, ike_cfg_t *ike_cfg; child_sa_t *child_sa; host_t *me, *other; - linked_list_t *my_ts, *other_ts, *list; + linked_list_t *local, *remote; enumerator_t *enumerator; status_t status; - linked_list_t *proposals; - proposal_t *proposal; - protocol_id_t proto = PROTO_ESP; bool result = FALSE, wildcard = FALSE; /* try to resolve addresses */ @@ -254,13 +291,14 @@ METHOD(trap_manager_t, install, bool, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (streq(entry->name, child->get_name(child)) && + if (!entry->external && + streq(entry->name, child->get_name(child)) && streq(entry->peer_cfg->get_name(entry->peer_cfg), peer->get_name(peer))) { found = entry; if (entry->child_sa) - { /* replace it with an updated version, if already installed */ + { /* replace it with an updated version if already installed */ this->traps->remove_at(this->traps, enumerator); } break; @@ -302,28 +340,13 @@ METHOD(trap_manager_t, install, bool, }; child_sa = child_sa_create(me, other, child, &child_data); - list = linked_list_create_with_items(me, NULL); - my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE); - list->destroy_offset(list, offsetof(host_t, destroy)); + local = linked_list_create_with_items(me, NULL); + remote = linked_list_create_with_items(other, NULL); - list = linked_list_create_with_items(other, NULL); - other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE); - list->destroy_offset(list, offsetof(host_t, destroy)); + status = install_trap(child_sa, local, remote); - /* We don't know the finally negotiated protocol (ESP|AH), we install - * the SA with the protocol of the first proposal */ - proposals = child->get_proposals(child, TRUE); - if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS) - { - proto = proposal->get_protocol(proposal); - } - proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); - child_sa->set_protocol(child_sa, proto); - child_sa->set_mode(child_sa, child->get_mode(child)); - child_sa->set_policies(child_sa, my_ts, other_ts); - status = child_sa->install_policies(child_sa); - my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); - other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + local->destroy_offset(local, offsetof(host_t, destroy)); + remote->destroy_offset(remote, offsetof(host_t, destroy)); if (status != SUCCESS) { DBG1(DBG_CFG, "installing trap failed"); @@ -366,8 +389,71 @@ METHOD(trap_manager_t, uninstall, bool, enumerator = this->traps->create_enumerator(this->traps); while (enumerator->enumerate(enumerator, &entry)) { - if (streq(entry->name, child) && - (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg)))) + if (!entry->external && + streq(entry->name, child) && + (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg)))) + { + this->traps->remove_at(this->traps, enumerator); + found = entry; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + if (!found) + { + return FALSE; + } + destroy_entry(found); + return TRUE; +} + +METHOD(trap_manager_t, install_external, bool, + private_trap_manager_t *this, peer_cfg_t *peer, child_sa_t *child, + linked_list_t *local, linked_list_t *remote) +{ + entry_t *entry; + + this->lock->write_lock(this->lock); + if (this->installing == INSTALL_DISABLED) + { /* flush() has been called */ + this->lock->unlock(this->lock); + return FALSE; + } + + INIT(entry, + .name = strdup(child->get_name(child)), + .peer_cfg = peer->get_ref(peer), + .child_sa = child, + .external = TRUE, + ); + this->traps->insert_first(this->traps, entry); + this->lock->unlock(this->lock); + + if (install_trap(child, local, remote) != SUCCESS) + { + DBG1(DBG_CFG, "installing trap failed"); + this->lock->write_lock(this->lock); + this->traps->remove(this->traps, entry, NULL); + this->lock->unlock(this->lock); + destroy_entry(entry); + return FALSE; + } + return TRUE; +} + +METHOD(trap_manager_t, remove_external, bool, + private_trap_manager_t *this, child_sa_t *child) +{ + enumerator_t *enumerator; + entry_t *entry, *found = NULL; + + this->lock->write_lock(this->lock); + enumerator = this->traps->create_enumerator(this->traps); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->external && entry->child_sa == child) { this->traps->remove_at(this->traps, enumerator); found = entry; @@ -396,8 +482,9 @@ CALLBACK(trap_filter, bool, while (orig->enumerate(orig, &entry)) { - if (!entry->child_sa) - { /* skip entries that are currently being installed */ + if (!entry->child_sa || entry->external) + { /* skip entries that are currently being installed or are managed + * externally */ continue; } if (peer_cfg) @@ -678,6 +765,8 @@ trap_manager_t *trap_manager_create(void) .public = { .install = _install, .uninstall = _uninstall, + .install_external = _install_external, + .remove_external = _remove_external, .create_enumerator = _create_enumerator, .acquire = _acquire, .flush = _flush, diff --git a/src/libcharon/sa/trap_manager.h b/src/libcharon/sa/trap_manager.h index d9b4906f74..60dbc2f125 100644 --- a/src/libcharon/sa/trap_manager.h +++ b/src/libcharon/sa/trap_manager.h @@ -25,6 +25,7 @@ #include #include #include +#include typedef struct trap_manager_t trap_manager_t; @@ -55,7 +56,31 @@ struct trap_manager_t { bool (*uninstall)(trap_manager_t *this, char *peer, char *child); /** - * Create an enumerator over all installed traps. + * Install and register an externally managed trap policy using the two + * lists of local and remote addresses when deriving traffic selectors. + * + * @param peer peer configuration to register + * @param child CHILD_SA to install and register + * @param local list of local addresses (virtual or physical) + * @param remote list of remote addresses (virtual or physical) + * @return TRUE if successfully installed and registered + */ + bool (*install_external)(trap_manager_t *this, peer_cfg_t *peer, + child_sa_t *child, linked_list_t *local, + linked_list_t *remote); + + /** + * Remove and uninstall a previously registered externally managed trap + * policy. + * + * @param child CHILD_SA to remove + * @return TRUE if successfully removed + */ + bool (*remove_external)(trap_manager_t *this, child_sa_t *child); + + /** + * Create an enumerator over all installed traps (does not include + * externally managed trap policies). * * @return enumerator over (peer_cfg_t, child_sa_t) */