/* * Copyright (C) 2014 Tobias Brunner * Hochschule fuer Technik Rapperswil * * Copyright (C) 2012 Martin Willi * Copyright (C) 2012 revosec AG * * 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 * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * 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. */ #include "unity_narrow.h" #include #include #include typedef struct private_unity_narrow_t private_unity_narrow_t; /** * Private data of an unity_narrow_t object. */ struct private_unity_narrow_t { /** * Public unity_narrow_t interface. */ unity_narrow_t public; /** * Unity attribute handler */ unity_handler_t *handler; /** * IKE_SAs for which we received 0.0.0.0/0 as remote traffic selector */ hashtable_t *wildcard_ts; }; /** * Narrow the given received traffic selector with the child configuration and * put them into the given list of TS */ static void narrow_ts(child_cfg_t *cfg, traffic_selector_t *ts, linked_list_t *list) { linked_list_t *received, *selected; received = linked_list_create(); received->insert_last(received, ts); selected = cfg->get_traffic_selectors(cfg, FALSE, received, NULL); while (selected->remove_first(selected, (void**)&ts) == SUCCESS) { list->insert_last(list, ts); } selected->destroy(selected); received->destroy(received); } /** * Narrow TS as initiator to Unity Split-Include/Local-LAN */ static void narrow_initiator(private_unity_narrow_t *this, ike_sa_t *ike_sa, child_cfg_t *cfg, linked_list_t *remote) { traffic_selector_t *current, *orig = NULL; enumerator_t *enumerator; enumerator = this->handler->create_include_enumerator(this->handler, ike_sa->get_id(ike_sa)); while (enumerator->enumerate(enumerator, ¤t)) { if (orig == NULL) { /* got one, replace original TS */ if (remote->remove_first(remote, (void**)&orig) != SUCCESS) { break; } } narrow_ts(cfg, current, remote); } enumerator->destroy(enumerator); if (orig) { DBG1(DBG_CFG, "narrowed CHILD_SA to %N %#R", configuration_attribute_type_names, UNITY_SPLIT_INCLUDE, remote); orig->destroy(orig); } else { /* since we originally changed the traffic selector to 0.0.0.0/0 local * narrowing is not applied if no Split-Include attrs are received */ if (remote->remove_first(remote, (void**)&orig) == SUCCESS) { narrow_ts(cfg, orig, remote); orig->destroy(orig); } } } /** * As initiator and responder, bump up TS to 0.0.0.0/0 for on-the-wire bits */ static void narrow_pre(linked_list_t *list, char *side) { traffic_selector_t *ts; while (list->remove_first(list, (void**)&ts) == SUCCESS) { ts->destroy(ts); } ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0", 0, "255.255.255.255", 65535); if (ts) { DBG2(DBG_CFG, "changing proposed traffic selectors for %s:", side); DBG2(DBG_CFG, " %R", ts); list->insert_last(list, ts); } } /** * As responder, narrow down TS to configuration for installation */ static void narrow_responder_post(child_cfg_t *child_cfg, linked_list_t *local) { traffic_selector_t *ts; linked_list_t *configured; while (local->remove_first(local, (void**)&ts) == SUCCESS) { ts->destroy(ts); } configured = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL); while (configured->remove_first(configured, (void**)&ts) == SUCCESS) { local->insert_last(local, ts); } configured->destroy(configured); } /** * Check if any Split-Include attributes are active on this IKE_SA */ static bool has_split_includes(private_unity_narrow_t *this, ike_sa_t *ike_sa) { enumerator_t *enumerator; traffic_selector_t *ts; bool has; enumerator = this->handler->create_include_enumerator(this->handler, ike_sa->get_id(ike_sa)); has = enumerator->enumerate(enumerator, &ts); enumerator->destroy(enumerator); return has; } METHOD(listener_t, narrow, bool, private_unity_narrow_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, narrow_hook_t type, linked_list_t *local, linked_list_t *remote) { if (ike_sa->get_version(ike_sa) == IKEV1 && ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY)) { /* depending on who initiates a rekeying the hooks will not match the * roles in the IKE_SA */ if (ike_sa->has_condition(ike_sa, COND_ORIGINAL_INITIATOR)) { switch (type) { case NARROW_INITIATOR_PRE_AUTH: case NARROW_RESPONDER: if (has_split_includes(this, ike_sa)) { narrow_pre(remote, "other"); } break; case NARROW_INITIATOR_POST_AUTH: case NARROW_RESPONDER_POST: narrow_initiator(this, ike_sa, child_sa->get_config(child_sa), remote); break; default: break; } } else { switch (type) { case NARROW_INITIATOR_PRE_AUTH: case NARROW_RESPONDER: if (this->wildcard_ts->get(this->wildcard_ts, ike_sa)) { narrow_pre(local, "us"); } break; case NARROW_INITIATOR_POST_AUTH: case NARROW_RESPONDER_POST: if (this->wildcard_ts->get(this->wildcard_ts, ike_sa)) { narrow_responder_post(child_sa->get_config(child_sa), local); } break; default: break; } } } return TRUE; } METHOD(listener_t, message, bool, private_unity_narrow_t *this, ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain) { traffic_selector_t *tsr = NULL, *wildcard; enumerator_t *enumerator; id_payload_t *id_payload; payload_t *payload; bool first = TRUE; if (!incoming || !plain || message->get_exchange_type(message) != QUICK_MODE || !ike_sa || !ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY)) { return TRUE; } enumerator = message->create_payload_enumerator(message); while (enumerator->enumerate(enumerator, &payload)) { if (payload->get_type(payload) == PLV1_ID) { if (!first) { id_payload = (id_payload_t*)payload; tsr = id_payload->get_ts(id_payload); break; } first = FALSE; } } enumerator->destroy(enumerator); if (!tsr) { return TRUE; } wildcard = traffic_selector_create_from_cidr("0.0.0.0/0", 0, 0, 65535); if (tsr->equals(tsr, wildcard)) { this->wildcard_ts->put(this->wildcard_ts, ike_sa, ike_sa); } else { this->wildcard_ts->remove(this->wildcard_ts, ike_sa); } wildcard->destroy(wildcard); tsr->destroy(tsr); return TRUE; } METHOD(listener_t, ike_updown, bool, private_unity_narrow_t *this, ike_sa_t *ike_sa, bool up) { if (!up) { this->wildcard_ts->remove(this->wildcard_ts, ike_sa); } return TRUE; } METHOD(unity_narrow_t, destroy, void, private_unity_narrow_t *this) { this->wildcard_ts->destroy(this->wildcard_ts); free(this); } /** * See header */ unity_narrow_t *unity_narrow_create(unity_handler_t *handler) { private_unity_narrow_t *this; INIT(this, .public = { .listener = { .narrow = _narrow, .message = _message, .ike_updown = _ike_updown, }, .destroy = _destroy, }, .handler = handler, .wildcard_ts = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4), ); return &this->public; }