From c94fe198e9cad3d30c04d6f06475633a332341f4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 20 Nov 2008 08:51:54 +0000 Subject: [PATCH] syncing of complete IKE/CHILD_SAs works --- src/charon/plugins/ha_sync/ha_sync_cache.c | 24 ++ src/charon/plugins/ha_sync/ha_sync_cache.h | 8 + src/charon/plugins/ha_sync/ha_sync_child.c | 19 +- src/charon/plugins/ha_sync/ha_sync_child.h | 8 +- .../plugins/ha_sync/ha_sync_dispatcher.c | 270 +++++++++++++++++- src/charon/plugins/ha_sync/ha_sync_ike.c | 19 +- src/charon/plugins/ha_sync/ha_sync_ike.h | 8 +- src/charon/plugins/ha_sync/ha_sync_plugin.c | 4 +- 8 files changed, 346 insertions(+), 14 deletions(-) diff --git a/src/charon/plugins/ha_sync/ha_sync_cache.c b/src/charon/plugins/ha_sync/ha_sync_cache.c index 9924b084a..2626ceffa 100644 --- a/src/charon/plugins/ha_sync/ha_sync_cache.c +++ b/src/charon/plugins/ha_sync/ha_sync_cache.c @@ -64,6 +64,28 @@ static ike_sa_t* get_ike_sa(private_ha_sync_cache_t *this, ike_sa_id_t *id) return found; } +/** + * Implementation of ha_sync_cache_t.has_ike_sa + */ +static bool has_ike_sa(private_ha_sync_cache_t *this, ike_sa_id_t *id) +{ + enumerator_t *enumerator; + ike_sa_t *ike_sa; + bool found = FALSE; + + enumerator = this->list->create_enumerator(this->list); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + if (id->equals(id, ike_sa->get_id(ike_sa))) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + /** * Implementation of ha_sync_cache_t.delete_ike_sa */ @@ -96,6 +118,7 @@ static void activate_segment(private_ha_sync_cache_t *this, u_int segment) while (this->list->remove_last(this->list, (void**)&ike_sa) == SUCCESS) { /* TODO: fix checkin of inexisting IKE_SA in manager */ + /* TODO: do not activate SAs not in state CONNECTING */ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } } @@ -117,6 +140,7 @@ ha_sync_cache_t *ha_sync_cache_create() private_ha_sync_cache_t *this = malloc_thing(private_ha_sync_cache_t); this->public.get_ike_sa = (ike_sa_t*(*)(ha_sync_cache_t*, ike_sa_id_t *id))get_ike_sa; + this->public.has_ike_sa = (bool(*)(ha_sync_cache_t*, ike_sa_id_t *id))has_ike_sa; this->public.delete_ike_sa = (void(*)(ha_sync_cache_t*, ike_sa_id_t *id))delete_ike_sa; this->public.activate_segment = (void(*)(ha_sync_cache_t*, u_int segment))activate_segment; this->public.destroy = (void(*)(ha_sync_cache_t*))destroy; diff --git a/src/charon/plugins/ha_sync/ha_sync_cache.h b/src/charon/plugins/ha_sync/ha_sync_cache.h index 20880116e..8508c7f22 100644 --- a/src/charon/plugins/ha_sync/ha_sync_cache.h +++ b/src/charon/plugins/ha_sync/ha_sync_cache.h @@ -42,6 +42,14 @@ struct ha_sync_cache_t { */ ike_sa_t* (*get_ike_sa)(ha_sync_cache_t *this, ike_sa_id_t *id); + /** + * Check if an IKE_SA is in the cache. + * + * @param id IKE_SA identifier of cached SA. + * @return TRUE if IKE_SA found + */ + bool (*has_ike_sa)(ha_sync_cache_t *this, ike_sa_id_t *id); + /** * Delete a synced and cached IKE_SA entry. * diff --git a/src/charon/plugins/ha_sync/ha_sync_child.c b/src/charon/plugins/ha_sync/ha_sync_child.c index 260d33e27..b3ace7269 100644 --- a/src/charon/plugins/ha_sync/ha_sync_child.c +++ b/src/charon/plugins/ha_sync/ha_sync_child.c @@ -33,6 +33,11 @@ struct private_ha_sync_child_t { * socket we use for syncing */ ha_sync_socket_t *socket; + + /** + * synced and cached IKE_SA state + */ + ha_sync_cache_t *cache; }; /** @@ -50,6 +55,11 @@ static bool child_keys(private_ha_sync_child_t *this, ike_sa_t *ike_sa, enumerator_t *enumerator; traffic_selector_t *ts; + if (this->cache->has_ike_sa(this->cache, ike_sa->get_id(ike_sa))) + { /* IKE_SA is cached, do not sync */ + return TRUE; + } + m = ha_sync_message_create(HA_SYNC_CHILD_ADD); m->add_attribute(m, HA_SYNC_IKE_ID, ike_sa->get_id(ike_sa)); @@ -109,6 +119,11 @@ static bool child_keys(private_ha_sync_child_t *this, ike_sa_t *ike_sa, static bool child_state_change(private_ha_sync_child_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, child_sa_state_t state) { + if (this->cache->has_ike_sa(this->cache, ike_sa->get_id(ike_sa))) + { /* IKE_SA is cached, do not sync */ + return TRUE; + } + if (state == CHILD_DESTROYING) { ha_sync_message_t *m; @@ -135,7 +150,8 @@ static void destroy(private_ha_sync_child_t *this) /** * See header */ -ha_sync_child_t *ha_sync_child_create(ha_sync_socket_t *socket) +ha_sync_child_t *ha_sync_child_create(ha_sync_socket_t *socket, + ha_sync_cache_t *cache) { private_ha_sync_child_t *this = malloc_thing(private_ha_sync_child_t); @@ -145,6 +161,7 @@ ha_sync_child_t *ha_sync_child_create(ha_sync_socket_t *socket) this->public.destroy = (void(*)(ha_sync_child_t*))destroy; this->socket = socket; + this->cache = cache; return &this->public; } diff --git a/src/charon/plugins/ha_sync/ha_sync_child.h b/src/charon/plugins/ha_sync/ha_sync_child.h index 8a5edcb68..fe7e5ec65 100644 --- a/src/charon/plugins/ha_sync/ha_sync_child.h +++ b/src/charon/plugins/ha_sync/ha_sync_child.h @@ -24,13 +24,14 @@ #define HA_SYNC_CHILD_H_ #include "ha_sync_socket.h" +#include "ha_sync_cache.h" #include typedef struct ha_sync_child_t ha_sync_child_t; /** - * Synchronize CHILD_SAs. + * Listener to synchronize CHILD_SAs. */ struct ha_sync_child_t { @@ -49,7 +50,10 @@ struct ha_sync_child_t { * Create a ha_sync_child instance. * * @param socket socket to use for sending synchronization messages + * @param cache synced and cached SAs + * @return CHILD listener */ -ha_sync_child_t *ha_sync_child_create(ha_sync_socket_t *socket); +ha_sync_child_t *ha_sync_child_create(ha_sync_socket_t *socket, + ha_sync_cache_t *cache); #endif /* HA_SYNC_CHILD_ @}*/ diff --git a/src/charon/plugins/ha_sync/ha_sync_dispatcher.c b/src/charon/plugins/ha_sync/ha_sync_dispatcher.c index dc404a50e..fd3a6ddc6 100644 --- a/src/charon/plugins/ha_sync/ha_sync_dispatcher.c +++ b/src/charon/plugins/ha_sync/ha_sync_dispatcher.c @@ -77,6 +77,10 @@ static void process_ike_add(private_ha_sync_dispatcher_t *this, { case HA_SYNC_IKE_ID: ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id); + DBG2(DBG_IKE, "got HA_SYNC_IKE_ADD: %llx:%llx - %p", + value.ike_sa_id->get_initiator_spi(value.ike_sa_id), + value.ike_sa_id->get_responder_spi(value.ike_sa_id), + ike_sa); break; case HA_SYNC_IKE_REKEY_ID: DBG1(DBG_IKE, "TODO: rekey HA sync"); @@ -131,11 +135,13 @@ static void process_ike_add(private_ha_sync_dispatcher_t *this, { proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0); } + charon->bus->set_sa(charon->bus, ike_sa); if (!keymat->derive_ike_keys(keymat, proposal, &dh, nonce_i, nonce_r, - ike_sa->get_id(ike_sa), NULL)) + ike_sa->get_id(ike_sa), NULL)) { DBG1(DBG_IKE, "HA sync keymat derivation failed"); } + charon->bus->set_sa(charon->bus, NULL); proposal->destroy(proposal); } } @@ -185,6 +191,10 @@ static void process_ike_update(private_ha_sync_dispatcher_t *this, { case HA_SYNC_IKE_ID: ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id); + DBG2(DBG_IKE, "got HA_SYNC_IKE_UPDATE: %llx:%llx - %p", + value.ike_sa_id->get_initiator_spi(value.ike_sa_id), + value.ike_sa_id->get_responder_spi(value.ike_sa_id), + ike_sa); break; case HA_SYNC_LOCAL_ID: ike_sa->set_my_id(ike_sa, value.id->clone(value.id)); @@ -238,10 +248,16 @@ static void process_ike_update(private_ha_sync_dispatcher_t *this, { ike_sa->set_peer_cfg(ike_sa, peer_cfg); peer_cfg->destroy(peer_cfg); + + charon->bus->set_sa(charon->bus, ike_sa); + /* we use the CONNECTING state to indicate sync is complete. */ + /* TODO: add an additional HOT_COPY state? */ + ike_sa->set_state(ike_sa, IKE_CONNECTING); + charon->bus->set_sa(charon->bus, NULL); } else { - DBG1(DBG_IKE, "HA sync is missing nodes configuration"); + DBG1(DBG_IKE, "HA sync is missing nodes peer configuration"); } } @@ -261,6 +277,9 @@ static void process_ike_delete(private_ha_sync_dispatcher_t *this, switch (attribute) { case HA_SYNC_IKE_ID: + DBG2(DBG_IKE, "got HA_SYNC_IKE_DELETE: %llx:%llx", + value.ike_sa_id->get_initiator_spi(value.ike_sa_id), + value.ike_sa_id->get_responder_spi(value.ike_sa_id)); this->cache->delete_ike_sa(this->cache, value.ike_sa_id); break; default: @@ -270,15 +289,222 @@ static void process_ike_delete(private_ha_sync_dispatcher_t *this, enumerator->destroy(enumerator); } + +/** + * get the child_cfg with the same name as the peer cfg + */ +static child_cfg_t* find_child_cfg(char *name) +{ + peer_cfg_t *peer_cfg; + child_cfg_t *current, *found = NULL; + enumerator_t *enumerator; + + peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name); + if (peer_cfg) + { + enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (streq(current->get_name(current), name)) + { + found = current; + found->get_ref(found); + break; + } + } + enumerator->destroy(enumerator); + peer_cfg->destroy(peer_cfg); + } + return found; +} + /** * Process messages of type CHILD_ADD */ static void process_child_add(private_ha_sync_dispatcher_t *this, ha_sync_message_t *message) { - chunk_t chunk = message->get_encoding(message); + ha_sync_message_attribute_t attribute; + ha_sync_message_value_t value; + enumerator_t *enumerator; + ike_sa_t *ike_sa = NULL; + child_cfg_t *config = NULL; + child_sa_t *child_sa; + proposal_t *proposal; + keymat_t *keymat; + bool initiator, failed = FALSE; + u_int32_t inbound_spi = 0, outbound_spi = 0; + u_int16_t inbound_cpi = 0, outbound_cpi = 0; + u_int8_t mode = MODE_TUNNEL, ipcomp = 0; + u_int16_t encr = ENCR_UNDEFINED, integ = AUTH_UNDEFINED, len = 0; + chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty; + chunk_t encr_i, integ_i, encr_r, integ_r; + linked_list_t *local_ts, *remote_ts; + /* quick and dirty hack of a DH implementation */ + diffie_hellman_t dh = { .get_shared_secret = get_shared_secret, + .destroy = (void*)&secret }; - DBG1(DBG_CHD, "CHILD_ADD: %B", &chunk); + enumerator = message->create_attribute_enumerator(message); + while (enumerator->enumerate(enumerator, &attribute, &value)) + { + switch (attribute) + { + case HA_SYNC_IKE_ID: + ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id); + initiator = value.ike_sa_id->is_initiator(value.ike_sa_id); + DBG2(DBG_CHD, "got HA_SYNC_CHILD_ADD: %llx:%llx - %p", + value.ike_sa_id->get_initiator_spi(value.ike_sa_id), + value.ike_sa_id->get_responder_spi(value.ike_sa_id), + ike_sa); + break; + case HA_SYNC_CONFIG_NAME: + config = find_child_cfg(value.str); + break; + case HA_SYNC_INBOUND_SPI: + inbound_spi = value.u32; + break; + case HA_SYNC_OUTBOUND_SPI: + outbound_spi = value.u32; + break; + case HA_SYNC_INBOUND_CPI: + inbound_cpi = value.u32; + break; + case HA_SYNC_OUTBOUND_CPI: + outbound_cpi = value.u32; + break; + case HA_SYNC_IPSEC_MODE: + mode = value.u8; + break; + case HA_SYNC_IPCOMP: + ipcomp = value.u8; + break; + case HA_SYNC_ALG_ENCR: + encr = value.u16; + break; + case HA_SYNC_ALG_ENCR_LEN: + len = value.u16; + break; + case HA_SYNC_ALG_INTEG: + integ = value.u16; + break; + case HA_SYNC_NONCE_I: + nonce_i = value.chunk; + break; + case HA_SYNC_NONCE_R: + nonce_r = value.chunk; + break; + case HA_SYNC_SECRET: + secret = value.chunk; + break; + default: + break; + } + } + enumerator->destroy(enumerator); + + if (!config) + { + DBG1(DBG_CHD, "HA sync is missing nodes child configuration"); + return; + } + if (!ike_sa) + { + config->destroy(config); + DBG1(DBG_CHD, "IKE_SA for HA sync CHILD_SA not found"); + return; + } + charon->bus->set_sa(charon->bus, ike_sa); + child_sa = child_sa_create(ike_sa->get_my_host(ike_sa), + ike_sa->get_other_host(ike_sa), config, 0, + ike_sa->has_condition(ike_sa, COND_NAT_ANY)); + config->destroy(config); + child_sa->set_mode(child_sa, mode); + child_sa->set_protocol(child_sa, PROTO_ESP); + child_sa->set_ipcomp(child_sa, ipcomp); + + proposal = proposal_create(PROTO_ESP); + if (integ) + { + proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0); + } + if (encr) + { + proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len); + } + keymat = ike_sa->get_keymat(ike_sa); + + if (!keymat->derive_child_keys(keymat, proposal, secret.ptr ? &dh : NULL, + nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) + { + DBG1(DBG_CHD, "HA sync CHILD_SA key derivation failed"); + child_sa->destroy(child_sa); + proposal->destroy(proposal); + charon->bus->set_sa(charon->bus, NULL); + return; + } + child_sa->set_proposal(child_sa, proposal); + child_sa->set_state(child_sa, CHILD_INSTALLING); + proposal->destroy(proposal); + + if (initiator) + { + if (child_sa->install(child_sa, encr_r, integ_r, + inbound_spi, inbound_cpi, TRUE) != SUCCESS || + child_sa->install(child_sa, encr_i, integ_i, + outbound_spi, outbound_cpi, FALSE) != SUCCESS) + { + failed = TRUE; + } + } + else + { + if (child_sa->install(child_sa, encr_i, integ_i, + inbound_spi, inbound_cpi, TRUE) != SUCCESS || + child_sa->install(child_sa, encr_r, integ_r, + outbound_spi, outbound_cpi, FALSE) != SUCCESS) + { + failed = TRUE; + } + } + chunk_clear(&encr_i); + chunk_clear(&integ_i); + chunk_clear(&encr_r); + chunk_clear(&integ_r); + + if (failed) + { + DBG1(DBG_CHD, "HA sync CHILD_SA installation failed"); + child_sa->destroy(child_sa); + charon->bus->set_sa(charon->bus, NULL); + return; + } + + /* TODO: Change CHILD_SA API to avoid cloning twice */ + local_ts = linked_list_create(); + remote_ts = linked_list_create(); + enumerator = message->create_attribute_enumerator(message); + while (enumerator->enumerate(enumerator, &attribute, &value)) + { + switch (attribute) + { + case HA_SYNC_LOCAL_TS: + local_ts->insert_last(local_ts, value.ts->clone(value.ts)); + break; + case HA_SYNC_REMOTE_TS: + remote_ts->insert_last(remote_ts, value.ts->clone(value.ts)); + break; + default: + break; + } + } + enumerator->destroy(enumerator); + child_sa->add_policies(child_sa, local_ts, remote_ts); + local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy)); + remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy)); + + child_sa->set_state(child_sa, CHILD_INSTALLED); + ike_sa->add_child_sa(ike_sa, child_sa); + charon->bus->set_sa(charon->bus, NULL); } /** @@ -287,9 +513,41 @@ static void process_child_add(private_ha_sync_dispatcher_t *this, static void process_child_delete(private_ha_sync_dispatcher_t *this, ha_sync_message_t *message) { - chunk_t chunk = message->get_encoding(message); + ha_sync_message_attribute_t attribute; + ha_sync_message_value_t value; + enumerator_t *enumerator; + ike_sa_t *ike_sa = NULL; - DBG1(DBG_CHD, "CHILD_DELETE: %B", &chunk); + enumerator = message->create_attribute_enumerator(message); + while (enumerator->enumerate(enumerator, &attribute, &value)) + { + switch (attribute) + { + case HA_SYNC_IKE_ID: + if (this->cache->has_ike_sa(this->cache, value.ike_sa_id)) + { + ike_sa = this->cache->get_ike_sa(this->cache, value.ike_sa_id); + DBG2(DBG_CHD, "got HA_SYNC_CHILD_DELETE: %llx:%llx - %p", + value.ike_sa_id->get_initiator_spi(value.ike_sa_id), + value.ike_sa_id->get_responder_spi(value.ike_sa_id), + ike_sa); + continue; + } + break; + case HA_SYNC_INBOUND_SPI: + if (!ike_sa || ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, + value.u32) != SUCCESS) + { + DBG1(DBG_CHD, "HA sync CHILD_SA 0x%lx delete failed (%p)", + value.u32, ike_sa); + } + break; + default: + break; + } + break; + } + enumerator->destroy(enumerator); } /** diff --git a/src/charon/plugins/ha_sync/ha_sync_ike.c b/src/charon/plugins/ha_sync/ha_sync_ike.c index 2984f57c3..2381bc367 100644 --- a/src/charon/plugins/ha_sync/ha_sync_ike.c +++ b/src/charon/plugins/ha_sync/ha_sync_ike.c @@ -33,6 +33,11 @@ struct private_ha_sync_ike_t { * socket we use for syncing */ ha_sync_socket_t *socket; + + /** + * Synced and cached SAs + */ + ha_sync_cache_t *cache; }; /** @@ -71,6 +76,11 @@ static bool ike_keys(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, proposal_t *proposal; u_int16_t alg, len; + if (this->cache->has_ike_sa(this->cache, ike_sa->get_id(ike_sa))) + { /* IKE_SA is cached, do not sync */ + return TRUE; + } + if (dh->get_shared_secret(dh, &secret) != SUCCESS) { return TRUE; @@ -126,6 +136,11 @@ static bool ike_state_change(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, { ha_sync_message_t *m; + if (this->cache->has_ike_sa(this->cache, ike_sa->get_id(ike_sa))) + { /* IKE_SA is cached, do not sync */ + return TRUE; + } + switch (state) { case IKE_ESTABLISHED: @@ -208,7 +223,8 @@ static void destroy(private_ha_sync_ike_t *this) /** * See header */ -ha_sync_ike_t *ha_sync_ike_create(ha_sync_socket_t *socket) +ha_sync_ike_t *ha_sync_ike_create(ha_sync_socket_t *socket, + ha_sync_cache_t *cache) { private_ha_sync_ike_t *this = malloc_thing(private_ha_sync_ike_t); @@ -218,6 +234,7 @@ ha_sync_ike_t *ha_sync_ike_create(ha_sync_socket_t *socket) this->public.destroy = (void(*)(ha_sync_ike_t*))destroy; this->socket = socket; + this->cache = cache; return &this->public; } diff --git a/src/charon/plugins/ha_sync/ha_sync_ike.h b/src/charon/plugins/ha_sync/ha_sync_ike.h index f2d034343..b47534ae9 100644 --- a/src/charon/plugins/ha_sync/ha_sync_ike.h +++ b/src/charon/plugins/ha_sync/ha_sync_ike.h @@ -24,13 +24,14 @@ #define HA_SYNC_IKE_H_ #include "ha_sync_socket.h" +#include "ha_sync_cache.h" #include typedef struct ha_sync_ike_t ha_sync_ike_t; /** - * Synchronize IKE_SAs. + * Listener to synchronize IKE_SAs. */ struct ha_sync_ike_t { @@ -49,7 +50,10 @@ struct ha_sync_ike_t { * Create a ha_sync_ike instance. * * @param socket socket to use for sending synchronization messages + * @param cache synced and cached SAs + * @return IKE listener */ -ha_sync_ike_t *ha_sync_ike_create(ha_sync_socket_t *socket); +ha_sync_ike_t *ha_sync_ike_create(ha_sync_socket_t *socket, + ha_sync_cache_t *cache); #endif /* HA_SYNC_IKE_ @}*/ diff --git a/src/charon/plugins/ha_sync/ha_sync_plugin.c b/src/charon/plugins/ha_sync/ha_sync_plugin.c index aae51aa72..9bbc7f8e4 100644 --- a/src/charon/plugins/ha_sync/ha_sync_plugin.c +++ b/src/charon/plugins/ha_sync/ha_sync_plugin.c @@ -95,8 +95,8 @@ plugin_t *plugin_create() } this->cache = ha_sync_cache_create(); this->dispatcher = ha_sync_dispatcher_create(this->socket, this->cache); - this->ike = ha_sync_ike_create(this->socket); - this->child = ha_sync_child_create(this->socket); + this->ike = ha_sync_ike_create(this->socket, this->cache); + this->child = ha_sync_child_create(this->socket, this->cache); charon->bus->add_listener(charon->bus, &this->ike->listener); charon->bus->add_listener(charon->bus, &this->child->listener);