diff options
author | Martin Willi <martin@strongswan.org> | 2009-06-03 17:06:33 +0200 |
---|---|---|
committer | Martin Willi <martin@revosec.ch> | 2010-04-07 13:55:13 +0200 |
commit | a33eb8631cb1d2cbce924c574b917f16baee15de (patch) | |
tree | 231ebf73e5304fbe4049b29035d3c3ce61edbe94 /src | |
parent | 80624c79d55bda332e9ff8296d60908e7f09b1a3 (diff) | |
download | strongswan-a33eb8631cb1d2cbce924c574b917f16baee15de.tar.bz2 strongswan-a33eb8631cb1d2cbce924c574b917f16baee15de.tar.xz |
automatically establish a PSK authenticated SA between cluster nodes
Diffstat (limited to 'src')
-rw-r--r-- | src/charon/plugins/ha_sync/ha_sync_child.c | 11 | ||||
-rw-r--r-- | src/charon/plugins/ha_sync/ha_sync_ike.c | 17 | ||||
-rw-r--r-- | src/charon/plugins/ha_sync/ha_sync_socket.c | 357 | ||||
-rw-r--r-- | src/charon/plugins/ha_sync/ha_sync_socket.h | 12 |
4 files changed, 366 insertions, 31 deletions
diff --git a/src/charon/plugins/ha_sync/ha_sync_child.c b/src/charon/plugins/ha_sync/ha_sync_child.c index 44d130b45..91b8573ca 100644 --- a/src/charon/plugins/ha_sync/ha_sync_child.c +++ b/src/charon/plugins/ha_sync/ha_sync_child.c @@ -48,6 +48,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->socket->is_sync_sa(this->socket, ike_sa)) + { /* do not sync SA between nodes */ + 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)); @@ -96,7 +101,6 @@ static bool child_keys(private_ha_sync_child_t *this, ike_sa_t *ike_sa, enumerator->destroy(enumerator); this->socket->push(this->socket, m); - m->destroy(m); return TRUE; } @@ -111,6 +115,10 @@ static bool child_state_change(private_ha_sync_child_t *this, ike_sa_t *ike_sa, { /* only sync active IKE_SAs */ return TRUE; } + if (this->socket->is_sync_sa(this->socket, ike_sa)) + { /* do not sync SA between nodes */ + return TRUE; + } if (state == CHILD_DESTROYING) { @@ -122,7 +130,6 @@ static bool child_state_change(private_ha_sync_child_t *this, ike_sa_t *ike_sa, m->add_attribute(m, HA_SYNC_INBOUND_SPI, child_sa->get_spi(child_sa, TRUE)); this->socket->push(this->socket, m); - m->destroy(m); } return TRUE; } diff --git a/src/charon/plugins/ha_sync/ha_sync_ike.c b/src/charon/plugins/ha_sync/ha_sync_ike.c index bee570671..18033c824 100644 --- a/src/charon/plugins/ha_sync/ha_sync_ike.c +++ b/src/charon/plugins/ha_sync/ha_sync_ike.c @@ -69,6 +69,10 @@ 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->socket->is_sync_sa(this->socket, ike_sa)) + { /* do not sync SA between nodes */ + return TRUE; + } if (dh->get_shared_secret(dh, &secret) != SUCCESS) { return TRUE; @@ -111,7 +115,6 @@ static bool ike_keys(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, chunk_clear(&secret); this->socket->push(this->socket, m); - m->destroy(m); return TRUE; } @@ -128,6 +131,10 @@ static bool ike_state_change(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, { /* only sync active IKE_SAs */ return TRUE; } + if (this->socket->is_sync_sa(this->socket, ike_sa)) + { /* do not sync SA between nodes */ + return TRUE; + } switch (state) { @@ -188,7 +195,6 @@ static bool ike_state_change(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, return TRUE; } this->socket->push(this->socket, m); - m->destroy(m); return TRUE; } @@ -198,6 +204,11 @@ static bool ike_state_change(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, static bool message_hook(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, message_t *message, bool incoming) { + if (this->socket->is_sync_sa(this->socket, ike_sa)) + { /* do not sync SA between nodes */ + return TRUE; + } + if (message->get_exchange_type(message) != IKE_SA_INIT && message->get_request(message)) { /* we sync on requests, but skip it on IKE_SA_INIT */ @@ -216,7 +227,6 @@ static bool message_hook(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, m->add_attribute(m, HA_SYNC_INITIATE_MID, mid); } this->socket->push(this->socket, m); - m->destroy(m); } if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && message->get_exchange_type(message) == IKE_AUTH && @@ -234,7 +244,6 @@ static bool message_hook(private_ha_sync_ike_t *this, ike_sa_t *ike_sa, m->add_attribute(m, HA_SYNC_IKE_ID, ike_sa->get_id(ike_sa)); m->add_attribute(m, HA_SYNC_REMOTE_VIP, vip); this->socket->push(this->socket, m); - m->destroy(m); } } return TRUE; diff --git a/src/charon/plugins/ha_sync/ha_sync_socket.c b/src/charon/plugins/ha_sync/ha_sync_socket.c index b88175cc7..5587e517e 100644 --- a/src/charon/plugins/ha_sync/ha_sync_socket.c +++ b/src/charon/plugins/ha_sync/ha_sync_socket.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Martin Willi + * Copyright (C) 2008-2009 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -23,8 +23,53 @@ #include <daemon.h> #include <utils/host.h> +#include <processing/jobs/callback_job.h> typedef struct private_ha_sync_socket_t private_ha_sync_socket_t; +typedef struct ha_backend_t ha_backend_t; +typedef struct ha_creds_t ha_creds_t; + +/** + * Serves credentials for the HA sync SA + */ +struct ha_creds_t { + + /** + * Implements credential_set_t + */ + credential_set_t public; + + /** + * own identity + */ + identification_t *local; + + /** + * peer identity + */ + identification_t *remote; + + /** + * Shared key to serve + */ + shared_key_t *key; +}; + +/** + * Serves configurations for the HA sync SA + */ +struct ha_backend_t { + + /** + * Implements backend_t + */ + backend_t public; + + /** + * peer config we serve + */ + peer_cfg_t *cfg; +}; /** * Private data of an ha_sync_socket_t object. @@ -40,19 +85,93 @@ struct private_ha_sync_socket_t { * UDP communication socket fd */ int fd; + + /** + * remote host to receive/send to + */ + host_t *remote; + + /** + * Reqid of installed trap + */ + u_int32_t trap; + + /** + * backend for sync SA + */ + ha_backend_t backend; + + /** + * credential set for sync SA + */ + ha_creds_t creds; }; /** + * Data to pass to the send_message() callback job + */ +typedef struct { + ha_sync_message_t *message; + private_ha_sync_socket_t *this; +} job_data_t; + +/** + * Cleanup job data + */ +static void job_data_destroy(job_data_t *this) +{ + this->message->destroy(this->message); + free(this); +} + +/** + * Callback to asynchronously send messages + */ +static job_requeue_t send_message(job_data_t *data) +{ + private_ha_sync_socket_t *this; + chunk_t chunk; + + this = data->this; + chunk = data->message->get_encoding(data->message); + if (sendto(this->fd, chunk.ptr, chunk.len, 0, + this->remote->get_sockaddr(this->remote), + *this->remote->get_sockaddr_len(this->remote)) < chunk.len) + { + DBG1(DBG_CFG, "pushing HA sync message failed: %s", strerror(errno)); + } + return JOB_REQUEUE_NONE; +} + +/** * Implementation of ha_sync_socket_t.push */ static void push(private_ha_sync_socket_t *this, ha_sync_message_t *message) { - chunk_t data; + if (this->trap) + { + callback_job_t *job; + job_data_t *data; - data = message->get_encoding(message); - if (send(this->fd, data.ptr, data.len, 0) < data.len) + data = malloc_thing(job_data_t); + data->message = message; + data->this = this; + + /* we send sync message asynchronously. This is required, as sendto() + * is a blocking call if it acquires a policy. Otherwise we could + * end up in a deadlock, as we own an IKE_SA. */ + job = callback_job_create((callback_job_cb_t)send_message, + data, (void*)job_data_destroy, NULL); + charon->processor->queue_job(charon->processor, (job_t*)job); + } + else { - DBG1(DBG_CFG, "pushing HA sync message failed: %s", strerror(errno)); + job_data_t data; + + data.message = message; + data.this = this; + send_message(&data); + message->destroy(message); } } @@ -69,7 +188,9 @@ static ha_sync_message_t *pull(private_ha_sync_socket_t *this) ssize_t len; pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); - len = recv(this->fd, buf, sizeof(buf), 0); + len = recvfrom(this->fd, buf, sizeof(buf), 0, + this->remote->get_sockaddr(this->remote), + this->remote->get_sockaddr_len(this->remote)); pthread_setcancelstate(oldstate, NULL); if (len <= 0) { @@ -90,6 +211,173 @@ static ha_sync_message_t *pull(private_ha_sync_socket_t *this) } /** + * Implementation of ha_sync_socket_t.is_sync_sa + */ +static bool is_sync_sa(private_ha_sync_socket_t *this, ike_sa_t *ike_sa) +{ + peer_cfg_t *cfg = this->backend.cfg; + + return cfg && ike_sa->get_ike_cfg(ike_sa) == cfg->get_ike_cfg(cfg); +} + +/** + * Enumerator over HA shared_key + */ +typedef struct { + /** Implements enumerator_t */ + enumerator_t public; + /** a single secret we serve */ + shared_key_t *key; +} shared_enum_t; + +/** + * Implementation of shared_enum_t.enumerate + */ +static bool shared_enumerate(shared_enum_t *this, shared_key_t **key, + id_match_t *me, id_match_t *other) +{ + if (this->key) + { + if (me) + { + *me = ID_MATCH_PERFECT; + } + if (other) + { + *other = ID_MATCH_PERFECT; + } + *key = this->key; + this->key = NULL; + return TRUE; + } + return FALSE; +} + +/** + * Implements ha_creds_t.create_shared_enumerator + */ +static enumerator_t* create_shared_enumerator(ha_creds_t *this, + shared_key_type_t type, identification_t *me, + identification_t *other) +{ + shared_enum_t *enumerator; + + if (type != SHARED_IKE && type != SHARED_ANY) + { + return NULL; + } + if (me && !me->equals(me, this->local)) + { + return NULL; + } + if (other && !other->equals(other, this->remote)) + { + return NULL; + } + + enumerator = malloc_thing(shared_enum_t); + enumerator->public.enumerate = (void*)shared_enumerate; + enumerator->public.destroy = (void*)free; + enumerator->key = this->key; + + return &enumerator->public; +} + +/** + * Implementation of backend_t.create_peer_cfg_enumerator. + */ +static enumerator_t* create_peer_cfg_enumerator(ha_backend_t *this, + identification_t *me, identification_t *other) +{ + return enumerator_create_single(this->cfg, NULL); +} + +/** + * Implementation of backend_t.create_ike_cfg_enumerator. + */ +static enumerator_t* create_ike_cfg_enumerator(ha_backend_t *this, + host_t *me, host_t *other) +{ + return enumerator_create_single(this->cfg->get_ike_cfg(this->cfg), NULL); +} + +/** + * Install configs and a a trap for secured sync + */ +static void setup_sync_tunnel(private_ha_sync_socket_t *this) +{ + char *local, *remote, *secret; + peer_cfg_t *peer_cfg; + ike_cfg_t *ike_cfg; + auth_cfg_t *auth_cfg; + child_cfg_t *child_cfg; + traffic_selector_t *ts; + + secret = lib->settings->get_str(lib->settings, + "charon.plugins.ha_sync.secret", NULL); + if (!secret) + { + DBG1(DBG_CFG, "no HA sync secret defined, using unencrypted sync"); + return; + } + local = lib->settings->get_str(lib->settings, + "charon.plugins.ha_sync.local", NULL); + remote = lib->settings->get_str(lib->settings, + "charon.plugins.ha_sync.remote", NULL); + + /* setup credentials */ + this->creds.key = shared_key_create(SHARED_IKE, + chunk_clone(chunk_create(secret, strlen(secret)))); + this->creds.local = identification_create_from_string(local); + this->creds.remote = identification_create_from_string(remote); + this->creds.public.create_private_enumerator = (void*)return_null; + this->creds.public.create_cert_enumerator = (void*)return_null; + this->creds.public.create_shared_enumerator = (void*)create_shared_enumerator; + this->creds.public.create_cdp_enumerator = (void*)return_null; + this->creds.public.cache_cert = (void*)nop; + + charon->credentials->add_set(charon->credentials, &this->creds.public); + + /* create config and backend */ + ike_cfg = ike_cfg_create(FALSE, FALSE, local, remote); + ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); + peer_cfg = peer_cfg_create("ha-sync", 2, ike_cfg, CERT_NEVER_SEND, + UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, 30, + NULL, NULL, FALSE, NULL, NULL); + + auth_cfg = auth_cfg_create(); + auth_cfg->add(auth_cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); + auth_cfg->add(auth_cfg, AUTH_RULE_IDENTITY, + identification_create_from_string(local)); + peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE); + + auth_cfg = auth_cfg_create(); + auth_cfg->add(auth_cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); + auth_cfg->add(auth_cfg, AUTH_RULE_IDENTITY, + identification_create_from_string(remote)); + peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE); + + child_cfg = child_cfg_create("ha-sync", 0, 21600, 1200, FALSE, TRUE, + MODE_TRANSPORT, ACTION_NONE, ACTION_NONE, FALSE); + ts = traffic_selector_create_dynamic(0, HA_SYNC_PORT, HA_SYNC_PORT); + child_cfg->add_traffic_selector(child_cfg, TRUE, ts); + ts = traffic_selector_create_dynamic(0, HA_SYNC_PORT, HA_SYNC_PORT); + child_cfg->add_traffic_selector(child_cfg, FALSE, ts); + child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); + peer_cfg->add_child_cfg(peer_cfg, child_cfg); + + this->backend.cfg = peer_cfg; + this->backend.public.create_peer_cfg_enumerator = (void*)create_peer_cfg_enumerator; + this->backend.public.create_ike_cfg_enumerator = (void*)create_ike_cfg_enumerator; + this->backend.public.get_peer_cfg_by_name = (void*)return_null; + + charon->backends->add_backend(charon->backends, &this->backend.public); + + /* install an acquiring trap */ + this->trap = charon->traps->install(charon->traps, peer_cfg, child_cfg); +} + +/** * read local/remote node address from config */ static host_t *get_host_config(char *key) @@ -117,23 +405,18 @@ static host_t *get_host_config(char *key) */ static bool open_socket(private_ha_sync_socket_t *this) { - host_t *local, *remote; - bool success = TRUE; + host_t *local; local = get_host_config("local"); - remote = get_host_config("remote"); - if (!local || !remote) + if (!local) { - DESTROY_IF(local); - DESTROY_IF(remote); return FALSE; } this->fd = socket(local->get_family(local), SOCK_DGRAM, 0); if (!this->fd) { - DESTROY_IF(local); - DESTROY_IF(remote); + local->destroy(local); DBG1(DBG_CFG, "opening HA sync socket failed: %s", strerror(errno)); return FALSE; } @@ -143,18 +426,11 @@ static bool open_socket(private_ha_sync_socket_t *this) { DBG1(DBG_CFG, "binding HA sync socket failed: %s", strerror(errno)); close(this->fd); - success = FALSE; - } - if (connect(this->fd, remote->get_sockaddr(remote), - *remote->get_sockaddr_len(remote)) == -1) - { - DBG1(DBG_CFG, "connecting HA sync socket failed: %s", strerror(errno)); - close(this->fd); - success = FALSE; + local->destroy(local); + return FALSE; } local->destroy(local); - remote->destroy(remote); - return success; + return TRUE; } /** @@ -163,6 +439,23 @@ static bool open_socket(private_ha_sync_socket_t *this) static void destroy(private_ha_sync_socket_t *this) { close(this->fd); + if (this->backend.cfg) + { + charon->backends->remove_backend(charon->backends, &this->backend.public); + this->backend.cfg->destroy(this->backend.cfg); + } + if (this->creds.key) + { + charon->credentials->remove_set(charon->credentials, &this->creds.public); + this->creds.key->destroy(this->creds.key); + } + DESTROY_IF(this->creds.local); + DESTROY_IF(this->creds.remote); + DESTROY_IF(this->remote); + if (this->trap) + { + charon->traps->uninstall(charon->traps, this->trap); + } free(this); } @@ -175,13 +468,29 @@ ha_sync_socket_t *ha_sync_socket_create() this->public.push = (void(*)(ha_sync_socket_t*, ha_sync_message_t*))push; this->public.pull = (ha_sync_message_t*(*)(ha_sync_socket_t*))pull; + this->public.is_sync_sa = (bool(*)(ha_sync_socket_t*, ike_sa_t *ike_sa))is_sync_sa; this->public.destroy = (void(*)(ha_sync_socket_t*))destroy; + this->remote = get_host_config("remote"); + if (!this->remote) + { + free(this); + return NULL; + } + this->trap = 0; + this->creds.key = NULL; + this->creds.local = NULL; + this->creds.remote = NULL; + this->backend.cfg = NULL; + + setup_sync_tunnel(this); + if (!open_socket(this)) { free(this); return NULL; } + return &this->public; } diff --git a/src/charon/plugins/ha_sync/ha_sync_socket.h b/src/charon/plugins/ha_sync/ha_sync_socket.h index aaa526963..2bd5fdc33 100644 --- a/src/charon/plugins/ha_sync/ha_sync_socket.h +++ b/src/charon/plugins/ha_sync/ha_sync_socket.h @@ -23,6 +23,8 @@ #include "ha_sync_message.h" +#include <sa/ike_sa.h> + /** * UDP port we use for communication */ @@ -38,7 +40,7 @@ struct ha_sync_socket_t { /** * Push synchronization information to the responsible node. * - * @param message message to send + * @param message message to send, gets destroyed by push() */ void (*push)(ha_sync_socket_t *this, ha_sync_message_t *message); @@ -50,6 +52,14 @@ struct ha_sync_socket_t { ha_sync_message_t *(*pull)(ha_sync_socket_t *this); /** + * Check if an IKE_SA is used for exchanging sync messages. + * + * @param ike_Sa ike_sa to check + * @return TRUE if IKE_SA is used to secure sync messages + */ + bool (*is_sync_sa)(ha_sync_socket_t *this, ike_sa_t *ike_sa); + + /** * Destroy a ha_sync_socket_t. */ void (*destroy)(ha_sync_socket_t *this); |