diff options
Diffstat (limited to 'src/charon/sa/ike_sa.c')
-rw-r--r-- | src/charon/sa/ike_sa.c | 694 |
1 files changed, 206 insertions, 488 deletions
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 7b2608e07..d9bb01c60 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -15,7 +15,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_sa.c 4394 2008-10-09 08:25:11Z martin $ + * $Id: ike_sa.c 4652 2008-11-14 08:38:53Z martin $ */ #include <sys/time.h> @@ -23,6 +23,7 @@ #include <printf.h> #include <sys/stat.h> #include <errno.h> +#include <time.h> #include "ike_sa.h" @@ -30,17 +31,6 @@ #include <daemon.h> #include <utils/linked_list.h> #include <utils/lexparser.h> -#include <crypto/diffie_hellman.h> -#include <crypto/prf_plus.h> -#include <crypto/crypters/crypter.h> -#include <crypto/hashers/hasher.h> -#include <encoding/payloads/sa_payload.h> -#include <encoding/payloads/nonce_payload.h> -#include <encoding/payloads/ke_payload.h> -#include <encoding/payloads/delete_payload.h> -#include <encoding/payloads/transform_substructure.h> -#include <encoding/payloads/transform_attribute.h> -#include <encoding/payloads/ts_payload.h> #include <sa/task_manager.h> #include <sa/tasks/ike_init.h> #include <sa/tasks/ike_natd.h> @@ -72,12 +62,13 @@ #define RESOLV_CONF "/etc/resolv.conf" #endif -ENUM(ike_sa_state_names, IKE_CREATED, IKE_DELETING, +ENUM(ike_sa_state_names, IKE_CREATED, IKE_DESTROYING, "CREATED", "CONNECTING", "ESTABLISHED", "REKEYING", "DELETING", + "DESTROYING", ); typedef struct private_ike_sa_t private_ike_sa_t; @@ -128,6 +119,11 @@ struct private_ike_sa_t { auth_info_t *other_auth; /** + * Selected IKE proposal + */ + proposal_t *proposal; + + /** * Juggles tasks to process messages */ task_manager_t *task_manager; @@ -190,49 +186,9 @@ struct private_ike_sa_t { linked_list_t *child_sas; /** - * String describing the selected IKE proposal - */ - char *selected_proposal; - - /** - * crypter for inbound traffic - */ - crypter_t *crypter_in; - - /** - * crypter for outbound traffic - */ - crypter_t *crypter_out; - - /** - * Signer for inbound traffic - */ - signer_t *signer_in; - - /** - * Signer for outbound traffic + * keymat of this IKE_SA */ - signer_t *signer_out; - - /** - * Multi purpose prf, set key, use it, forget it - */ - prf_t *prf; - - /** - * Prf function for derivating keymat child SAs - */ - prf_t *child_prf; - - /** - * Key to build outging authentication data (SKp) - */ - chunk_t skp_build; - - /** - * Key to verify incoming authentication data (SKp) - */ - chunk_t skp_verify; + keymat_t *keymat; /** * Virtual IP on local host, if any @@ -268,34 +224,26 @@ struct private_ike_sa_t { * NAT keep alive interval */ u_int32_t keepalive_interval; - + /** * Timestamps for this IKE_SA */ - struct { - /** last IKE message received */ - u_int32_t inbound; - /** last IKE message sent */ - u_int32_t outbound; - /** when IKE_SA became established */ - u_int32_t established; - /** when IKE_SA gets rekeyed */ - u_int32_t rekey; - /** when IKE_SA gets reauthenticated */ - u_int32_t reauth; - /** when IKE_SA gets deleted */ - u_int32_t delete; - } time; + u_int32_t stats[STAT_MAX]; /** * how many times we have retried so far (keyingtries) */ u_int32_t keyingtry; - + /** - * are we the initiator of this IKE_SA (rekeying does not affect this flag) + * local host address to be used for IKE, set via MIGRATE kernel message */ - bool ike_initiator; + host_t *local_host; + + /** + * remote host address to be used for IKE, set via MIGRATE kernel message + */ + host_t *remote_host; }; /** @@ -303,28 +251,26 @@ struct private_ike_sa_t { */ static time_t get_use_time(private_ike_sa_t* this, bool inbound) { - iterator_t *iterator; + enumerator_t *enumerator; child_sa_t *child_sa; - time_t latest = 0, use_time; - - iterator = this->child_sas->create_iterator(this->child_sas, TRUE); - while (iterator->iterate(iterator, (void**)&child_sa)) - { - if (child_sa->get_use_time(child_sa, inbound, &use_time) == SUCCESS) - { - latest = max(latest, use_time); - } - } - iterator->destroy(iterator); + time_t use_time; if (inbound) { - return max(this->time.inbound, latest); + use_time = this->stats[STAT_INBOUND]; } else { - return max(this->time.outbound, latest); + use_time = this->stats[STAT_OUTBOUND]; + } + enumerator = this->child_sas->create_enumerator(this->child_sas); + while (enumerator->enumerate(enumerator, &child_sa)) + { + use_time = max(use_time, child_sa->get_usetime(child_sa, inbound)); } + enumerator->destroy(enumerator); + + return use_time; } /** @@ -352,24 +298,9 @@ static char *get_name(private_ike_sa_t *this) */ static u_int32_t get_statistic(private_ike_sa_t *this, statistic_t kind) { - time_t now = time(NULL); - - switch (kind) + if (kind < STAT_MAX) { - case STAT_REKEY_TIME: - if (this->time.rekey > now) - { - return this->time.rekey - now; - } - break; - case STAT_REAUTH_TIME: - if (this->time.reauth > now) - { - return this->time.reauth - now; - } - break; - default: - break; + return this->stats[kind]; } return 0; } @@ -462,6 +393,23 @@ static auth_info_t* get_other_auth(private_ike_sa_t *this) } /** + * Implementation of ike_sa_t.get_proposal + */ +static proposal_t* get_proposal(private_ike_sa_t *this) +{ + return this->proposal; +} + +/** + * Implementation of ike_sa_t.set_proposal + */ +static void set_proposal(private_ike_sa_t *this, proposal_t *proposal) +{ + DESTROY_IF(this->proposal); + this->proposal = proposal->clone(proposal); +} + +/** * Implementation of ike_sa_t.send_keepalive */ static void send_keepalive(private_ike_sa_t *this) @@ -518,14 +466,6 @@ static void set_ike_cfg(private_ike_sa_t *this, ike_cfg_t *ike_cfg) } /** - * Implementation of ike_sa_t.is_ike_initiator - */ -static bool is_ike_initiator(private_ike_sa_t *this) -{ - return this->ike_initiator; -} - -/** * Implementation of ike_sa_t.enable_extension. */ static void enable_extension(private_ike_sa_t *this, ike_extension_t extension) @@ -687,47 +627,48 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) u_int32_t t; /* calculate rekey, reauth and lifetime */ - this->time.established = time(NULL); + this->stats[STAT_ESTABLISHED] = time(NULL); /* schedule rekeying if we have a time which is smaller than * an already scheduled rekeying */ t = this->peer_cfg->get_rekey_time(this->peer_cfg); - if (t && (this->time.rekey == 0 || - (this->time.rekey > t + this->time.established))) + if (t && (this->stats[STAT_REKEY] == 0 || + (this->stats[STAT_REKEY] > t + this->stats[STAT_ESTABLISHED]))) { - this->time.rekey = t + this->time.established; + this->stats[STAT_REKEY] = t + this->stats[STAT_ESTABLISHED]; job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, FALSE); charon->scheduler->schedule_job(charon->scheduler, job, t * 1000); DBG1(DBG_IKE, "scheduling rekeying in %ds", t); } t = this->peer_cfg->get_reauth_time(this->peer_cfg); - if (t && (this->time.reauth == 0 || - (this->time.reauth > t + this->time.established))) + if (t && (this->stats[STAT_REAUTH] == 0 || + (this->stats[STAT_REAUTH] > t + this->stats[STAT_ESTABLISHED]))) { - this->time.reauth = t + this->time.established; + this->stats[STAT_REAUTH] = t + this->stats[STAT_ESTABLISHED]; job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE); charon->scheduler->schedule_job(charon->scheduler, job, t * 1000); DBG1(DBG_IKE, "scheduling reauthentication in %ds", t); } t = this->peer_cfg->get_over_time(this->peer_cfg); - if (this->time.rekey || this->time.reauth) + if (this->stats[STAT_REKEY] || this->stats[STAT_REAUTH]) { - if (this->time.reauth == 0) + if (this->stats[STAT_REAUTH] == 0) { - this->time.delete = this->time.rekey; + this->stats[STAT_DELETE] = this->stats[STAT_REKEY]; } - else if (this->time.rekey == 0) + else if (this->stats[STAT_REKEY] == 0) { - this->time.delete = this->time.reauth; + this->stats[STAT_DELETE] = this->stats[STAT_REAUTH]; } else { - this->time.delete = min(this->time.rekey, this->time.reauth); + this->stats[STAT_DELETE] = min(this->stats[STAT_REKEY], + this->stats[STAT_REAUTH]); } - this->time.delete += t; - t = this->time.delete - this->time.established; + this->stats[STAT_DELETE] += t; + t = this->stats[STAT_DELETE] - this->stats[STAT_ESTABLISHED]; job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); charon->scheduler->schedule_job(charon->scheduler, job, t * 1000); @@ -750,7 +691,7 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) default: break; } - + charon->bus->ike_state_change(charon->bus, &this->public, state); this->state = state; } @@ -771,6 +712,14 @@ static void reset(private_ike_sa_t *this) } /** + * Implementation of ike_sa_t.get_keymat + */ +static keymat_t* get_keymat(private_ike_sa_t *this) +{ + return this->keymat; +} + +/** * Implementation of ike_sa_t.set_virtual_ip */ static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip) @@ -925,8 +874,14 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) { - child_sa->update_hosts(child_sa, this->my_host, this->other_host, - has_condition(this, COND_NAT_ANY)); + if (child_sa->update_hosts(child_sa, this->my_host, + this->other_host, this->my_virtual_ip, + has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED) + { + this->public.rekey_child_sa(&this->public, + child_sa->get_protocol(child_sa), + child_sa->get_spi(child_sa, TRUE)); + } } iterator->destroy(iterator); } @@ -938,9 +893,11 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) static status_t generate_message(private_ike_sa_t *this, message_t *message, packet_t **packet) { - this->time.outbound = time(NULL); + this->stats[STAT_OUTBOUND] = time(NULL); message->set_ike_sa_id(message, this->ike_sa_id); - return message->generate(message, this->crypter_out, this->signer_out, packet); + return message->generate(message, + this->keymat->get_crypter(this->keymat, FALSE), + this->keymat->get_signer(this->keymat, FALSE), packet); } /** @@ -978,6 +935,17 @@ static void send_notify_response(private_ike_sa_t *this, message_t *request, response->destroy(response); } +/** + * Implementation of ike_sa_t.set_kmaddress. + */ +static void set_kmaddress(private_ike_sa_t *this, host_t *local, host_t *remote) +{ + DESTROY_IF(this->local_host); + DESTROY_IF(this->remote_host); + this->local_host = local->clone(local); + this->remote_host = remote->clone(remote); +} + #ifdef ME /** * Implementation of ike_sa_t.act_as_mediation_server. @@ -1082,26 +1050,42 @@ static void resolve_hosts(private_ike_sa_t *this) { host_t *host; - host = host_create_from_dns(this->ike_cfg->get_other_addr(this->ike_cfg), - 0, IKEV2_UDP_PORT); + if (this->remote_host) + { + host = this->remote_host->clone(this->remote_host); + host->set_port(host, IKEV2_UDP_PORT); + } + else + { + host = host_create_from_dns(this->ike_cfg->get_other_addr(this->ike_cfg), + 0, IKEV2_UDP_PORT); + } if (host) { set_other_host(this, host); } - host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg), - this->my_host->get_family(this->my_host), - IKEV2_UDP_PORT); - - if (host && host->is_anyaddr(host) && - !this->other_host->is_anyaddr(this->other_host)) + if (this->local_host) { - host->destroy(host); - host = charon->kernel_interface->get_source_addr( - charon->kernel_interface, this->other_host, NULL); - if (host) + host = this->local_host->clone(this->local_host); + host->set_port(host, IKEV2_UDP_PORT); + } + else + { + host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg), + this->my_host->get_family(this->my_host), + IKEV2_UDP_PORT); + + if (host && host->is_anyaddr(host) && + !this->other_host->is_anyaddr(this->other_host)) { - host->set_port(host, IKEV2_UDP_PORT); + host->destroy(host); + host = charon->kernel_interface->get_source_addr( + charon->kernel_interface, this->other_host, NULL); + if (host) + { + host->set_port(host, IKEV2_UDP_PORT); + } } } if (host) @@ -1128,12 +1112,11 @@ static status_t initiate_with_reqid(private_ike_sa_t *this, child_cfg_t *child_c ) { child_cfg->destroy(child_cfg); - SIG_IKE(UP_START, "initiating IKE_SA"); - SIG_IKE(UP_FAILED, "unable to initiate to %%any"); + DBG1(DBG_IKE, "unable to initiate to %%any"); return DESTROY_ME; } - this->ike_initiator = TRUE; + set_condition(this, COND_ORIGINAL_INITIATOR, TRUE); task = (task_t*)ike_init_create(&this->public, TRUE, NULL); this->task_manager->queue_task(this->task_manager, task); @@ -1163,10 +1146,12 @@ static status_t initiate_with_reqid(private_ike_sa_t *this, child_cfg_t *child_c #ifdef ME if (this->peer_cfg->is_mediation(this->peer_cfg)) { - /* mediation connection */ if (this->state == IKE_ESTABLISHED) - { /* FIXME: we should try to find a better solution to this */ - SIG_CHD(UP_SUCCESS, NULL, "mediation connection is already up and running"); + { + /* mediation connection is already established, retrigger state change + * to notify bus listeners */ + DBG1(DBG_IKE, "mediation connection is already up"); + set_state(this, IKE_ESTABLISHED); } DESTROY_IF(child_cfg); } @@ -1216,9 +1201,8 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) if (this->state == IKE_DELETING) { - SIG_CHD(UP_START, NULL, "acquiring CHILD_SA on kernel request"); - SIG_CHD(UP_FAILED, NULL, "acquiring CHILD_SA {reqid %d} failed: " - "IKE_SA is deleting", reqid); + DBG1(DBG_IKE, "acquiring CHILD_SA {reqid %d} failed: " + "IKE_SA is deleting", reqid); return FAILED; } @@ -1235,9 +1219,8 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) iterator->destroy(iterator); if (!child_sa) { - SIG_CHD(UP_START, NULL, "acquiring CHILD_SA on kernel request"); - SIG_CHD(UP_FAILED, NULL, "acquiring CHILD_SA {reqid %d} failed: " - "CHILD_SA not found", reqid); + DBG1(DBG_IKE, "acquiring CHILD_SA {reqid %d} failed: " + "CHILD_SA not found", reqid); return FAILED; } @@ -1258,8 +1241,6 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) host_t *me, *other; status_t status; - SIG_CHD(ROUTE_START, NULL, "routing CHILD_SA"); - /* check if not already routed*/ iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) @@ -1268,7 +1249,7 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) streq(child_sa->get_name(child_sa), child_cfg->get_name(child_cfg))) { iterator->destroy(iterator); - SIG_CHD(ROUTE_FAILED, child_sa, "CHILD_SA with such a config already routed"); + DBG1(DBG_IKE, "routing CHILD_SA failed: already routed"); return FAILED; } } @@ -1278,8 +1259,8 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) { case IKE_DELETING: case IKE_REKEYING: - SIG_CHD(ROUTE_FAILED, NULL, - "unable to route CHILD_SA, as its IKE_SA gets deleted"); + DBG1(DBG_IKE, "routing CHILD_SA failed: IKE_SA is %N", + ike_sa_state_names, this->state); return FAILED; case IKE_CREATED: case IKE_CONNECTING: @@ -1291,8 +1272,8 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) resolve_hosts(this); /* install kernel policies */ - child_sa = child_sa_create(this->my_host, this->other_host, this->my_id, - this->other_id, child_cfg, 0, FALSE); + child_sa = child_sa_create(this->my_host, this->other_host, + child_cfg, 0, FALSE); me = this->my_host; if (this->my_virtual_ip) { @@ -1306,18 +1287,21 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, me); other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, other); + status = child_sa->add_policies(child_sa, my_ts, other_ts, - child_cfg->get_mode(child_cfg), PROTO_NONE); + child_cfg->get_mode(child_cfg), PROTO_NONE); + my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); if (status == SUCCESS) { this->child_sas->insert_last(this->child_sas, child_sa); - SIG_CHD(ROUTE_SUCCESS, child_sa, "CHILD_SA routed"); + DBG1(DBG_IKE, "CHILD_SA routed"); } else { - SIG_CHD(ROUTE_FAILED, child_sa, "routing CHILD_SA failed"); + child_sa->destroy(child_sa); + DBG1(DBG_IKE, "routing CHILD_SA failed"); } return status; } @@ -1331,8 +1315,6 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid) child_sa_t *child_sa; bool found = FALSE; - SIG_CHD(UNROUTE_START, NULL, "unrouting CHILD_SA"); - /* find CHILD_SA in ROUTED state */ iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) @@ -1341,7 +1323,7 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid) child_sa->get_reqid(child_sa) == reqid) { iterator->remove(iterator); - SIG_CHD(UNROUTE_SUCCESS, child_sa, "CHILD_SA unrouted"); + DBG1(DBG_IKE, "CHILD_SA unrouted"); child_sa->destroy(child_sa); found = TRUE; break; @@ -1351,7 +1333,7 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid) if (!found) { - SIG_CHD(UNROUTE_FAILED, NULL, "CHILD_SA to unroute not found"); + DBG1(DBG_IKE, "unrouting CHILD_SA failed: reqid %d not found", reqid); return FAILED; } /* if we are not established, and we have no more routed childs, remove whole SA */ @@ -1362,6 +1344,7 @@ static status_t unroute(private_ike_sa_t *this, u_int32_t reqid) } return SUCCESS; } + /** * Implementation of ike_sa_t.process_message. */ @@ -1372,7 +1355,9 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) is_request = message->get_request(message); - status = message->parse_body(message, this->crypter_in, this->signer_in); + status = message->parse_body(message, + this->keymat->get_crypter(this->keymat, TRUE), + this->keymat->get_signer(this->keymat, TRUE)); if (status != SUCCESS) { @@ -1431,7 +1416,7 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) me = message->get_destination(message); other = message->get_source(message); - + /* if this IKE_SA is virgin, we check for a config */ if (this->ike_cfg == NULL) { @@ -1451,7 +1436,7 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) charon->scheduler->schedule_job(charon->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT); } - this->time.inbound = time(NULL); + this->stats[STAT_INBOUND] = time(NULL); /* check if message is trustworthy, and update host information */ if (this->state == IKE_CREATED || this->state == IKE_CONNECTING || message->get_exchange_type(message) != IKE_SA_INIT) @@ -1510,38 +1495,6 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) } /** - * Implementation of ike_sa_t.get_prf. - */ -static prf_t *get_prf(private_ike_sa_t *this) -{ - return this->prf; -} - -/** - * Implementation of ike_sa_t.get_prf. - */ -static prf_t *get_child_prf(private_ike_sa_t *this) -{ - return this->child_prf; -} - -/** - * Implementation of ike_sa_t.get_skp_bild - */ -static chunk_t get_skp_build(private_ike_sa_t *this) -{ - return this->skp_build; -} - -/** - * Implementation of ike_sa_t.get_skp_verify - */ -static chunk_t get_skp_verify(private_ike_sa_t *this) -{ - return this->skp_verify; -} - -/** * Implementation of ike_sa_t.get_id. */ static ike_sa_id_t* get_id(private_ike_sa_t *this) @@ -1601,230 +1554,6 @@ static void set_eap_identity(private_ike_sa_t *this, identification_t *id) } /** - * Implementation of ike_sa_t.derive_keys. - */ -static status_t derive_keys(private_ike_sa_t *this, - proposal_t *proposal, chunk_t secret, - chunk_t nonce_i, chunk_t nonce_r, - bool initiator, prf_t *child_prf, prf_t *old_prf) -{ - prf_plus_t *prf_plus; - chunk_t skeyseed, key, full_nonce, fixed_nonce, prf_plus_seed; - u_int16_t alg, key_size; - crypter_t *crypter_i, *crypter_r; - signer_t *signer_i, *signer_r; - u_int8_t spi_i_buf[sizeof(u_int64_t)], spi_r_buf[sizeof(u_int64_t)]; - chunk_t spi_i = chunk_from_buf(spi_i_buf); - chunk_t spi_r = chunk_from_buf(spi_r_buf); - - /* Create SAs general purpose PRF first, we may use it here */ - if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) - { - DBG1(DBG_IKE, "no %N selected", - transform_type_names, PSEUDO_RANDOM_FUNCTION); - return FAILED; - } - this->prf = lib->crypto->create_prf(lib->crypto, alg); - if (this->prf == NULL) - { - DBG1(DBG_IKE, "%N %N not supported!", - transform_type_names, PSEUDO_RANDOM_FUNCTION, - pseudo_random_function_names, alg); - return FAILED; - } - DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret); - /* full nonce is used as seed for PRF+ ... */ - full_nonce = chunk_cat("cc", nonce_i, nonce_r); - /* but the PRF may need a fixed key which only uses the first bytes of - * the nonces. */ - switch (alg) - { - case PRF_AES128_XCBC: - /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does - * not and therefore fixed key semantics apply to XCBC for key - * derivation. */ - nonce_i.len = min(nonce_i.len, this->prf->get_key_size(this->prf)/2); - nonce_r.len = min(nonce_r.len, this->prf->get_key_size(this->prf)/2); - break; - default: - /* all other algorithms use variable key length, full nonce */ - break; - } - fixed_nonce = chunk_cat("cc", nonce_i, nonce_r); - *((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id); - *((u_int64_t*)spi_r.ptr) = this->ike_sa_id->get_responder_spi(this->ike_sa_id); - prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r); - - /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) - * - * if we are rekeying, SKEYSEED is built on another way - */ - if (child_prf == NULL) /* not rekeying */ - { - /* SKEYSEED = prf(Ni | Nr, g^ir) */ - this->prf->set_key(this->prf, fixed_nonce); - this->prf->allocate_bytes(this->prf, secret, &skeyseed); - DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); - this->prf->set_key(this->prf, skeyseed); - chunk_free(&skeyseed); - chunk_free(&secret); - prf_plus = prf_plus_create(this->prf, prf_plus_seed); - } - else - { - /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) - * use OLD SAs PRF functions for both prf_plus and prf */ - secret = chunk_cat("mc", secret, full_nonce); - child_prf->allocate_bytes(child_prf, secret, &skeyseed); - DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); - old_prf->set_key(old_prf, skeyseed); - chunk_free(&skeyseed); - chunk_free(&secret); - prf_plus = prf_plus_create(old_prf, prf_plus_seed); - } - chunk_free(&full_nonce); - chunk_free(&fixed_nonce); - chunk_free(&prf_plus_seed); - - /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */ - - /* SK_d is used for generating CHILD_SA key mat => child_prf */ - proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL); - this->child_prf = lib->crypto->create_prf(lib->crypto, alg); - key_size = this->child_prf->get_key_size(this->child_prf); - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_d secret %B", &key); - this->child_prf->set_key(this->child_prf, key); - chunk_free(&key); - - /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */ - if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) - { - DBG1(DBG_IKE, "no %N selected", - transform_type_names, INTEGRITY_ALGORITHM); - return FAILED; - } - signer_i = lib->crypto->create_signer(lib->crypto, alg); - signer_r = lib->crypto->create_signer(lib->crypto, alg); - if (signer_i == NULL || signer_r == NULL) - { - DBG1(DBG_IKE, "%N %N not supported!", - transform_type_names, INTEGRITY_ALGORITHM, - integrity_algorithm_names ,alg); - prf_plus->destroy(prf_plus); - return FAILED; - } - key_size = signer_i->get_key_size(signer_i); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_ai secret %B", &key); - signer_i->set_key(signer_i, key); - chunk_free(&key); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_ar secret %B", &key); - signer_r->set_key(signer_r, key); - chunk_free(&key); - - if (initiator) - { - this->signer_in = signer_r; - this->signer_out = signer_i; - } - else - { - this->signer_in = signer_i; - this->signer_out = signer_r; - } - - /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */ - if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size)) - { - DBG1(DBG_IKE, "no %N selected", - transform_type_names, ENCRYPTION_ALGORITHM); - prf_plus->destroy(prf_plus); - return FAILED; - } - crypter_i = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); - crypter_r = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); - if (crypter_i == NULL || crypter_r == NULL) - { - DBG1(DBG_IKE, "%N %N (key size %d) not supported!", - transform_type_names, ENCRYPTION_ALGORITHM, - encryption_algorithm_names, alg, key_size); - prf_plus->destroy(prf_plus); - return FAILED; - } - key_size = crypter_i->get_key_size(crypter_i); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_ei secret %B", &key); - crypter_i->set_key(crypter_i, key); - chunk_free(&key); - - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_er secret %B", &key); - crypter_r->set_key(crypter_r, key); - chunk_free(&key); - - if (initiator) - { - this->crypter_in = crypter_r; - this->crypter_out = crypter_i; - } - else - { - this->crypter_in = crypter_i; - this->crypter_out = crypter_r; - } - - /* SK_pi/SK_pr used for authentication => stored for later */ - key_size = this->prf->get_key_size(this->prf); - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_pi secret %B", &key); - if (initiator) - { - this->skp_build = key; - } - else - { - this->skp_verify = key; - } - prf_plus->allocate_bytes(prf_plus, key_size, &key); - DBG4(DBG_IKE, "Sk_pr secret %B", &key); - if (initiator) - { - this->skp_verify = key; - } - else - { - this->skp_build = key; - } - - /* all done, prf_plus not needed anymore */ - prf_plus->destroy(prf_plus); - - return SUCCESS; -} - -/** - * Implementation of ike_sa_t.get_proposal. - */ -static char* get_proposal(private_ike_sa_t *this) -{ - return this->selected_proposal; -} - -/** - * Implementation of ike_sa_t.set_proposal. - */ -static void set_proposal(private_ike_sa_t *this, char *proposal) -{ - free(this->selected_proposal); - this->selected_proposal = strdup(proposal); -} - -/** * Implementation of ike_sa_t.add_child_sa. */ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) @@ -1939,10 +1668,10 @@ static status_t delete_(private_ike_sa_t *this) this->task_manager->queue_task(this->task_manager, &ike_delete->task); return this->task_manager->initiate(this->task_manager); case IKE_CREATED: - SIG_IKE(DOWN_SUCCESS, "deleting unestablished IKE_SA"); + DBG1(DBG_IKE, "deleting unestablished IKE_SA"); break; default: - SIG_IKE(DOWN_SUCCESS, "destroying IKE_SA in state %N " + DBG1(DBG_IKE, "destroying IKE_SA in state %N " "without notification", ike_sa_state_names, this->state); break; } @@ -1972,7 +1701,7 @@ static status_t reauth(private_ike_sa_t *this) /* we can't reauthenticate as responder when we use EAP or virtual IPs. * If the peer does not support RFC4478, there is no way to keep the * IKE_SA up. */ - if (!this->ike_initiator) + if (!has_condition(this, COND_ORIGINAL_INITIATOR)) { DBG1(DBG_IKE, "initiator did not reauthenticate as requested"); if (this->other_virtual_ip != NULL || @@ -1985,7 +1714,8 @@ static status_t reauth(private_ike_sa_t *this) { time_t now = time(NULL); - DBG1(DBG_IKE, "IKE_SA will timeout in %#V", &now, &this->time.delete); + DBG1(DBG_IKE, "IKE_SA will timeout in %#V", + &now, &this->stats[STAT_DELETE]); return FAILED; } else @@ -2049,7 +1779,7 @@ static status_t reestablish(private_ike_sa_t *this) } /* check if we are able to reestablish this IKE_SA */ - if (!this->ike_initiator && + if (!has_condition(this, COND_ORIGINAL_INITIATOR) && (this->other_virtual_ip != NULL || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME @@ -2133,7 +1863,7 @@ static status_t reestablish(private_ike_sa_t *this) */ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) { - this->time.outbound = time(NULL); + this->stats[STAT_OUTBOUND] = time(NULL); if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) { /* send a proper signal to brief interested bus listeners */ @@ -2146,19 +1876,19 @@ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) this->keyingtry++; if (tries == 0 || tries > this->keyingtry) { - SIG_IKE(UP_FAILED, "peer not responding, trying again " - "(%d/%d) in background ", this->keyingtry + 1, tries); + DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)", + this->keyingtry + 1, tries); reset(this); return this->task_manager->initiate(this->task_manager); } - SIG_IKE(UP_FAILED, "establishing IKE_SA failed, peer not responding"); + DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding"); break; } case IKE_DELETING: - SIG_IKE(DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); + DBG1(DBG_IKE, "proper IKE_SA delete failed, peer not responding"); break; case IKE_REKEYING: - SIG_IKE(REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); + DBG1(DBG_IKE, "rekeying IKE_SA failed, peer not responding"); /* FALL */ default: reestablish(this); @@ -2184,9 +1914,10 @@ static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) charon->processor->queue_job(charon->processor, (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE)); } - else if (this->time.reauth == 0 || this->time.reauth > reauth_time) + else if (this->stats[STAT_REAUTH] == 0 || + this->stats[STAT_REAUTH] > reauth_time) { - this->time.reauth = reauth_time; + this->stats[STAT_REAUTH] = reauth_time; DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling reauthentication" " in %ds", lifetime, lifetime - reduction); charon->scheduler->schedule_job(charon->scheduler, @@ -2196,7 +1927,7 @@ static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) else { DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, reauthentication already " - "scheduled in %ds", lifetime, this->time.reauth - time(NULL)); + "scheduled in %ds", lifetime, this->stats[STAT_REAUTH] - time(NULL)); } } @@ -2275,7 +2006,6 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) this->other_host = other->other_host->clone(other->other_host); this->my_id = other->my_id->clone(other->my_id); this->other_id = other->other_id->clone(other->other_id); - this->ike_initiator = other->ike_initiator; /* apply virtual assigned IPs... */ if (other->my_virtual_ip) @@ -2296,7 +2026,7 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) this->dns_servers->insert_first(this->dns_servers, ip); } - /* inherit NAT-T conditions */ + /* inherit all conditions */ this->conditions = other->conditions; if (this->conditions & COND_NAT_HERE) { @@ -2326,14 +2056,14 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) this->task_manager->adopt_tasks(this->task_manager, other->task_manager); /* reauthentication timeout survives a rekeying */ - if (other->time.reauth) + if (other->stats[STAT_REAUTH]) { time_t reauth, delete, now = time(NULL); - this->time.reauth = other->time.reauth; - reauth = this->time.reauth - now; + this->stats[STAT_REAUTH] = other->stats[STAT_REAUTH]; + reauth = this->stats[STAT_REAUTH] - now; delete = reauth + this->peer_cfg->get_over_time(this->peer_cfg); - this->time.delete = this->time.reauth + delete; + this->stats[STAT_DELETE] = this->stats[STAT_REAUTH] + delete; DBG1(DBG_IKE, "rescheduling reauthentication in %ds after rekeying, " "lifetime reduced to %ds", reauth, delete); charon->scheduler->schedule_job(charon->scheduler, @@ -2421,7 +2151,7 @@ static void remove_dns_servers(private_ike_sa_t *this) if (!found) { /* write line untouched back to file */ - fwrite(orig_line.ptr, orig_line.len, 1, file); + ignore_result(fwrite(orig_line.ptr, orig_line.len, 1, file)); fprintf(file, "\n"); } } @@ -2475,7 +2205,7 @@ static void add_dns_server(private_ike_sa_t *this, host_t *dns) { this->dns_servers->insert_last(this->dns_servers, dns->clone(dns)); } - fwrite(contents.ptr, contents.len, 1, file); + ignore_result(fwrite(contents.ptr, contents.len, 1, file)); fclose(file); } @@ -2485,19 +2215,17 @@ static void add_dns_server(private_ike_sa_t *this, host_t *dns) */ static void destroy(private_ike_sa_t *this) { + charon->bus->set_sa(charon->bus, &this->public); + + set_state(this, IKE_DESTROYING); + this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy)); - this->task_manager->destroy(this->task_manager); + /* unset SA after here to avoid usage by the listeners */ + charon->bus->set_sa(charon->bus, NULL); - DESTROY_IF(this->crypter_in); - DESTROY_IF(this->crypter_out); - DESTROY_IF(this->signer_in); - DESTROY_IF(this->signer_out); - DESTROY_IF(this->prf); - DESTROY_IF(this->child_prf); - chunk_free(&this->skp_verify); - chunk_free(&this->skp_build); - free(this->selected_proposal); + this->task_manager->destroy(this->task_manager); + this->keymat->destroy(this->keymat); if (this->my_virtual_ip) { @@ -2535,12 +2263,15 @@ static void destroy(private_ike_sa_t *this) DESTROY_IF(this->other_host); DESTROY_IF(this->my_id); DESTROY_IF(this->other_id); + DESTROY_IF(this->local_host); + DESTROY_IF(this->remote_host); DESTROY_IF(this->eap_identity); DESTROY_IF(this->ike_cfg); DESTROY_IF(this->peer_cfg); DESTROY_IF(this->my_auth); DESTROY_IF(this->other_auth); + DESTROY_IF(this->proposal); this->ike_sa_id->destroy(this->ike_sa_id); free(this); @@ -2570,6 +2301,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_peer_cfg = (void (*)(ike_sa_t*,peer_cfg_t*))set_peer_cfg; this->public.get_my_auth = (auth_info_t*(*)(ike_sa_t*))get_my_auth; this->public.get_other_auth = (auth_info_t*(*)(ike_sa_t*))get_other_auth; + this->public.get_proposal = (proposal_t*(*)(ike_sa_t*))get_proposal; + this->public.set_proposal = (void(*)(ike_sa_t*, proposal_t *proposal))set_proposal; this->public.get_id = (ike_sa_id_t* (*)(ike_sa_t*)) get_id; this->public.get_my_host = (host_t* (*)(ike_sa_t*)) get_my_host; this->public.set_my_host = (void (*)(ike_sa_t*,host_t*)) set_my_host; @@ -2588,7 +2321,6 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.has_condition = (bool (*)(ike_sa_t*,ike_condition_t)) has_condition; this->public.set_pending_updates = (void(*)(ike_sa_t*, u_int32_t updates))set_pending_updates; this->public.get_pending_updates = (u_int32_t(*)(ike_sa_t*))get_pending_updates; - this->public.is_ike_initiator = (bool (*)(ike_sa_t*))is_ike_initiator; this->public.create_additional_address_iterator = (iterator_t*(*)(ike_sa_t*))create_additional_address_iterator; this->public.add_additional_address = (void(*)(ike_sa_t*, host_t *host))add_additional_address; this->public.has_mapping_changed = (bool(*)(ike_sa_t*, chunk_t hash))has_mapping_changed; @@ -2597,13 +2329,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.destroy = (void (*)(ike_sa_t*))destroy; this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd; this->public.send_keepalive = (void (*)(ike_sa_t*)) send_keepalive; - this->public.get_prf = (prf_t* (*)(ike_sa_t*)) get_prf; - this->public.get_child_prf = (prf_t* (*)(ike_sa_t *)) get_child_prf; - this->public.get_skp_verify = (chunk_t (*)(ike_sa_t *)) get_skp_verify; - this->public.get_skp_build = (chunk_t (*)(ike_sa_t *)) get_skp_build; - this->public.derive_keys = (status_t (*)(ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys; - this->public.get_proposal = (char* (*)(ike_sa_t*)) get_proposal; - this->public.set_proposal = (void (*)(ike_sa_t*,char*)) set_proposal; + this->public.get_keymat = (keymat_t*(*)(ike_sa_t*))get_keymat; this->public.add_child_sa = (void (*)(ike_sa_t*,child_sa_t*)) add_child_sa; this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa; this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator; @@ -2622,6 +2348,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_virtual_ip = (void (*)(ike_sa_t*,bool,host_t*))set_virtual_ip; this->public.get_virtual_ip = (host_t* (*)(ike_sa_t*,bool))get_virtual_ip; this->public.add_dns_server = (void (*)(ike_sa_t*,host_t*))add_dns_server; + this->public.set_kmaddress = (void (*)(ike_sa_t*,host_t*,host_t*))set_kmaddress; #ifdef ME this->public.act_as_mediation_server = (void (*)(ike_sa_t*)) act_as_mediation_server; this->public.get_server_reflexive_host = (host_t* (*)(ike_sa_t*)) get_server_reflexive_host; @@ -2637,34 +2364,24 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) /* initialize private fields */ this->ike_sa_id = ike_sa_id->clone(ike_sa_id); this->child_sas = linked_list_create(); - this->my_host = host_create_from_string("0.0.0.0", IKEV2_UDP_PORT); - this->other_host = host_create_from_string("0.0.0.0", IKEV2_UDP_PORT); + this->my_host = host_create_any(AF_INET); + this->other_host = host_create_any(AF_INET); this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->eap_identity = NULL; this->extensions = 0; this->conditions = 0; - this->selected_proposal = NULL; - this->crypter_in = NULL; - this->crypter_out = NULL; - this->signer_in = NULL; - this->signer_out = NULL; - this->prf = NULL; - this->skp_verify = chunk_empty; - this->skp_build = chunk_empty; - this->child_prf = NULL; + this->keymat = keymat_create(ike_sa_id->is_initiator(ike_sa_id)); this->state = IKE_CREATED; this->keepalive_interval = lib->settings->get_time(lib->settings, "charon.keep_alive", KEEPALIVE_INTERVAL); - this->time.inbound = this->time.outbound = time(NULL); - this->time.established = 0; - this->time.rekey = 0; - this->time.reauth = 0; - this->time.delete = 0; + memset(this->stats, 0, sizeof(this->stats)); + this->stats[STAT_INBOUND] = this->stats[STAT_OUTBOUND] = time(NULL); this->ike_cfg = NULL; this->peer_cfg = NULL; this->my_auth = auth_info_create(); this->other_auth = auth_info_create(); + this->proposal = NULL; this->task_manager = task_manager_create(&this->public); this->unique_id = ++unique_id; this->my_virtual_ip = NULL; @@ -2674,7 +2391,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->nat_detection_dest = chunk_empty; this->pending_updates = 0; this->keyingtry = 0; - this->ike_initiator = FALSE; + this->local_host = NULL; + this->remote_host = NULL; #ifdef ME this->is_mediation_server = FALSE; this->server_reflexive_host = NULL; |