From: Martin Willi Date: Wed, 2 May 2012 09:12:31 +0000 (+0200) Subject: Merge branch 'ikev1' X-Git-Tag: 5.0.0~338 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b24be29646442210f43b100d2282b6c0a0e52e09;p=thirdparty%2Fstrongswan.git Merge branch 'ikev1' Conflicts: configure.in man/ipsec.conf.5.in src/libcharon/encoding/generator.c src/libcharon/encoding/payloads/notify_payload.c src/libcharon/encoding/payloads/notify_payload.h src/libcharon/encoding/payloads/payload.c src/libcharon/network/receiver.c src/libcharon/sa/authenticator.c src/libcharon/sa/authenticator.h src/libcharon/sa/ikev2/tasks/ike_init.c src/libcharon/sa/task_manager.c src/libstrongswan/credentials/auth_cfg.c --- b24be29646442210f43b100d2282b6c0a0e52e09 diff --cc configure.in index 7f28762fba,b99487aea4..6c6a57f648 mode 100644,100755..100755 --- a/configure.in +++ b/configure.in diff --cc man/ipsec.conf.5.in index ab255304da,642499fc12..92cf0ca8b4 --- a/man/ipsec.conf.5.in +++ b/man/ipsec.conf.5.in @@@ -605,14 -509,13 +509,16 @@@ Fo .B eap, an optional EAP method can be appended. Currently defined methods are .BR eap-aka , + .BR eap-sim , .BR eap-gtc , .BR eap-md5 , +.BR eap-mschapv2 , +.BR eap-peap , +.BR eap-sim , .BR eap-tls , -.B eap-mschapv2 ++.BR eap-ttls , and - .BR eap-ttls . + .BR eap-radius . Alternatively, IANA assigned EAP method numbers are accepted. Vendor specific EAP methods are defined in the form .B eap-type-vendor diff --cc src/libcharon/daemon.c index f2c054a125,575627206e..ee2db33186 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@@ -131,8 -128,8 +131,9 @@@ static void destroy(private_daemon_t *t DESTROY_IF(this->public.ike_sa_manager); DESTROY_IF(this->public.controller); DESTROY_IF(this->public.eap); + DESTROY_IF(this->public.xauth); DESTROY_IF(this->public.backends); + DESTROY_IF(this->public.sender); DESTROY_IF(this->public.socket); /* rehook library logging, shutdown logging */ diff --cc src/libcharon/encoding/generator.c index 60fa7e0c4d,bf44801932..4d65bce84c --- a/src/libcharon/encoding/generator.c +++ b/src/libcharon/encoding/generator.c @@@ -519,11 -560,14 +560,14 @@@ METHOD(generator_t, generate_payload, v return; } } - DBG2(DBG_ENC, "generating %N payload finished", - payload_type_names, payload_type); - DBG3(DBG_ENC, "generated data for this payload %b", - this->buffer + offset_start, - (u_int)(this->out_position - this->buffer - offset_start)); + if (this->debug) + { + DBG2(DBG_ENC, "generating %N payload finished", + payload_type_names, payload_type); + DBG3(DBG_ENC, "generated data for this payload %b", + this->buffer + offset_start, - this->out_position - this->buffer - offset_start); ++ (u_int)(this->out_position - this->buffer - offset_start)); + } } METHOD(generator_t, destroy, void, diff --cc src/libcharon/encoding/payloads/certreq_payload.c index 02015f2731,26d1f0a436..df5e73b5b5 --- a/src/libcharon/encoding/payloads/certreq_payload.c +++ b/src/libcharon/encoding/payloads/certreq_payload.c @@@ -109,9 -111,11 +111,10 @@@ static encoding_rule_t encodings[] = METHOD(payload_t, verify, status_t, private_certreq_payload_t *this) { - if (this->encoding == ENC_X509_SIGNATURE) + if (this->type == CERTIFICATE_REQUEST && + this->encoding == ENC_X509_SIGNATURE) { - if (this->data.len < HASH_SIZE_SHA1 || - this->data.len % HASH_SIZE_SHA1) + if (this->data.len % HASH_SIZE_SHA1) { DBG1(DBG_ENC, "invalid X509 hash length (%d) in certreq", this->data.len); diff --cc src/libcharon/encoding/payloads/notify_payload.c index e03d1af674,4115344917..d168e1c12f mode 100644,100755..100755 --- a/src/libcharon/encoding/payloads/notify_payload.c +++ b/src/libcharon/encoding/payloads/notify_payload.c @@@ -98,11 -105,15 +105,18 @@@ ENUM_NEXT(notify_type_names, INITIAL_CO "IKEV2_MESSAGE_ID_SYNC_SUPPORTED", "IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED", "IKEV2_MESSAGE_ID_SYNC", - "IPSEC_REPLAY_COUNTER_SYNC"); -ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC, + "IPSEC_REPLAY_COUNTER_SYNC", + "SECURE PASSWORD_METHOD", + "PSK_PERSIST", + "PSK_CONFIRM"); - ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, PSK_CONFIRM, ++ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, PSK_CONFIRM, + "INITIAL_CONTACT"); + ENUM_NEXT(notify_type_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, + "DPD_R_U_THERE", + "DPD_R_U_THERE_ACK"); + ENUM_NEXT(notify_type_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK, + "UNITY_LOAD_BALANCE"); + ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE, "USE_BEET_MODE"); ENUM_NEXT(notify_type_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE, "ME_MEDIATION", @@@ -189,11 -207,15 +210,18 @@@ ENUM_NEXT(notify_type_short_names, INIT "MSG_ID_SYN_SUP", "RPL_CTR_SYN_SUP", "MSG_ID_SYN", - "RPL_CTR_SYN"); -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC, + "RPL_CTR_SYN", + "SEC_PASSWD", + "PSK_PST", + "PSK_CFM"); - ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, PSK_CONFIRM, ++ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, PSK_CONFIRM, + "INITIAL_CONTACT"); + ENUM_NEXT(notify_type_short_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1, + "DPD", + "DPD_ACK"); + ENUM_NEXT(notify_type_short_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK, + "UNITY_LB"); + ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE, "BEET_MODE"); ENUM_NEXT(notify_type_short_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE, "ME_MED", diff --cc src/libcharon/encoding/payloads/notify_payload.h index ced2827007,07fbcb49ba..beec1e2332 mode 100644,100755..100755 --- a/src/libcharon/encoding/payloads/notify_payload.h +++ b/src/libcharon/encoding/payloads/notify_payload.h @@@ -127,11 -138,13 +138,18 @@@ enum notify_type_t IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED = 16421, IKEV2_MESSAGE_ID_SYNC = 16422, IPSEC_REPLAY_COUNTER_SYNC = 16423, + /* Secure password methods, RFC 6467 */ + SECURE_PASSWORD_METHOD = 16424, + /* PACE - draft-kuegler-ipsecme-pace-ikev2 */ + PSK_PERSIST = 16425, + PSK_CONFIRM = 16426, + /* IKEv1 initial contact */ + INITIAL_CONTACT_IKEV1 = 24578, + /* IKEv1 DPD */ + DPD_R_U_THERE = 36136, + DPD_R_U_THERE_ACK = 36137, + /* IKEv1 Cisco High Availability */ + UNITY_LOAD_BALANCE = 40501, /* BEET mode, not even a draft yet. private use */ USE_BEET_MODE = 40961, /* IKE-ME, private use */ diff --cc src/libcharon/encoding/payloads/payload.c index a2c0a43851,257d538588..dc158476b7 --- a/src/libcharon/encoding/payloads/payload.c +++ b/src/libcharon/encoding/payloads/payload.c @@@ -34,13 -35,30 +35,30 @@@ #include #include #include + #include #include - ENUM_BEGIN(payload_type_names, NO_PAYLOAD, NO_PAYLOAD, "NO_PAYLOAD"); - ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, - GENERIC_SECURE_PASSWORD_METHOD, NO_PAYLOAD, + ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION_V1, CONFIGURATION_V1, NO_PAYLOAD, + "SECURITY_ASSOCIATION_V1", + "PROPOSAL_V1", + "TRANSFORM_V1", + "KEY_EXCHANGE_V1", + "ID_V1", + "CERTIFICATE_V1", + "CERTIFICATE_REQUEST_V1", + "HASH_V1", + "SIGNATURE_V1", + "NONCE_V1", + "NOTIFY_V1", + "DELETE_V1", + "VENDOR_ID_V1", + "CONFIGURATION_V1"); + ENUM_NEXT(payload_type_names, NAT_D_V1, NAT_OA_V1, CONFIGURATION_V1, + "NAT_D_V1", + "NAT_OA_V1"); -ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NAT_OA_V1, ++ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METHOD, NAT_OA_V1, "SECURITY_ASSOCIATION", "KEY_EXCHANGE", "ID_INITIATOR", @@@ -56,35 -74,60 +74,61 @@@ "TRAFFIC_SELECTOR_RESPONDER", "ENCRYPTED", "CONFIGURATION", - "EXTENSIBLE_AUTHENTICATION"); + "EXTENSIBLE_AUTHENTICATION", + "GENERIC_SECURE_PASSWORD_METHOD"); #ifdef ME -ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION, +ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, GENERIC_SECURE_PASSWORD_METHOD, "ID_PEER"); - ENUM_NEXT(payload_type_names, HEADER, CONFIGURATION_ATTRIBUTE, ID_PEER, + ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, ID_PEER, "HEADER", "PROPOSAL_SUBSTRUCTURE", + "PROPOSAL_SUBSTRUCTURE_V1", "TRANSFORM_SUBSTRUCTURE", + "TRANSFORM_SUBSTRUCTURE_V1", "TRANSFORM_ATTRIBUTE", + "TRANSFORM_ATTRIBUTE_V1", "TRAFFIC_SELECTOR_SUBSTRUCTURE", - "CONFIGURATION_ATTRIBUTE"); + "CONFIGURATION_ATTRIBUTE", + "CONFIGURATION_ATTRIBUTE_V1", + "ENCRYPTED_V1"); #else - ENUM_NEXT(payload_type_names, HEADER, CONFIGURATION_ATTRIBUTE, - GENERIC_SECURE_PASSWORD_METHOD, -ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, EXTENSIBLE_AUTHENTICATION, ++ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD, "HEADER", "PROPOSAL_SUBSTRUCTURE", + "PROPOSAL_SUBSTRUCTURE_V1", "TRANSFORM_SUBSTRUCTURE", + "TRANSFORM_SUBSTRUCTURE_V1", "TRANSFORM_ATTRIBUTE", + "TRANSFORM_ATTRIBUTE_V1", "TRAFFIC_SELECTOR_SUBSTRUCTURE", - "CONFIGURATION_ATTRIBUTE"); + "CONFIGURATION_ATTRIBUTE", + "CONFIGURATION_ATTRIBUTE_V1", + "ENCRYPTED_V1"); #endif /* ME */ - ENUM_END(payload_type_names, CONFIGURATION_ATTRIBUTE); + ENUM_END(payload_type_names, ENCRYPTED_V1); /* short forms of payload names */ ENUM_BEGIN(payload_type_short_names, NO_PAYLOAD, NO_PAYLOAD, "--"); - ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, - GENERIC_SECURE_PASSWORD_METHOD, NO_PAYLOAD, + ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION_V1, CONFIGURATION_V1, NO_PAYLOAD, + "SA", + "PROP", + "TRANS", + "KE", + "ID", + "CERT", + "CERTREQ", + "HASH", + "SIG", + "No", + "N", + "D", + "V", + "CP"); + ENUM_NEXT(payload_type_short_names, NAT_D_V1, NAT_OA_V1, CONFIGURATION_V1, + "NAT-D", + "NAT-OA"); -ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NAT_OA_V1, ++ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METHOD, NAT_OA_V1, "SA", "KE", "IDi", @@@ -100,30 -143,37 +144,39 @@@ "TSr", "E", "CP", - "EAP"); + "EAP", + "GSPM"); #ifdef ME -ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION, +ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, + GENERIC_SECURE_PASSWORD_METHOD, "IDp"); - ENUM_NEXT(payload_type_short_names, HEADER, CONFIGURATION_ATTRIBUTE, ID_PEER, + ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, ID_PEER, "HDR", "PROP", + "PROP", + "TRANS", "TRANS", "TRANSATTR", + "TRANSATTR", "TSSUB", - "CPATTR"); + "CATTR", + "CATTR", + "E"); #else - ENUM_NEXT(payload_type_short_names, HEADER, CONFIGURATION_ATTRIBUTE, - GENERIC_SECURE_PASSWORD_METHOD, -ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, EXTENSIBLE_AUTHENTICATION, ++ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD, "HDR", "PROP", + "PROP", + "TRANS", "TRANS", "TRANSATTR", + "TRANSATTR", "TSSUB", - "CPATTR"); + "CATTR", + "CATTR", + "E"); #endif /* ME */ - ENUM_END(payload_type_short_names, CONFIGURATION_ATTRIBUTE); + ENUM_END(payload_type_short_names, ENCRYPTED_V1); /* * see header diff --cc src/libcharon/network/receiver.c index cfb1408ef4,599249fcb6..fcc7304395 --- a/src/libcharon/network/receiver.c +++ b/src/libcharon/network/receiver.c @@@ -308,13 -271,15 +310,14 @@@ static bool drop_ike_sa_init(private_re half_open = charon->ike_sa_manager->get_half_open_count( charon->ike_sa_manager, NULL); - /* check for cookies */ - if (cookie_required(this, half_open, now) && !check_cookie(this, message)) + /* check for cookies in IKEv2 */ + if (message->get_major_version(message) == IKEV2_MAJOR_VERSION && - this->cookie_threshold && half_open >= this->cookie_threshold && - !check_cookie(this, message)) ++ cookie_required(this, half_open, now) && !check_cookie(this, message)) { - u_int32_t now = time_monotonic(NULL); - chunk_t cookie = cookie_build(this, message, now - this->secret_offset, - chunk_from_thing(this->secret)); + chunk_t cookie; + cookie = cookie_build(this, message, now - this->secret_offset, + chunk_from_thing(this->secret)); DBG2(DBG_NET, "received packet from: %#H to %#H", message->get_source(message), message->get_destination(message)); diff --cc src/libcharon/plugins/stroke/stroke_config.c index 483e3d2534,8ca1464fb5..89e1e0a911 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@@ -264,10 -263,9 +264,9 @@@ static auth_cfg_t *build_auth_cfg(priva { identification_t *identity; certificate_t *certificate; - char *auth, *id, *cert, *ca; + char *auth, *id, *pubkey, *cert, *ca; stroke_end_t *end, *other_end; auth_cfg_t *cfg; - char eap_buf[32]; /* select strings */ if (local) diff --cc src/libcharon/sa/authenticator.c index 9ffe661cca,91bb7715fb..a32b6ab122 --- a/src/libcharon/sa/authenticator.c +++ b/src/libcharon/sa/authenticator.c @@@ -28,12 -31,21 +31,22 @@@ ENUM_BEGIN(auth_method_names, AUTH_RSA "RSA signature", "pre-shared key", "DSS signature"); -ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_ECDSA_521, AUTH_DSS, +ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_GSPM, AUTH_DSS, "ECDSA-256 signature", "ECDSA-384 signature", - "ECDSA-521 signature"); -ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_ECDSA_521, + "ECDSA-521 signature", + "secure password method"); - ENUM_END(auth_method_names, AUTH_GSPM); ++ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_GSPM, + "XAuthInitPSK", + "XAuthRespPSK", + "XAuthInitRSA", + "XauthRespRSA", + "HybridInitRSA", + "HybridRespRSA", + ); + ENUM_END(auth_method_names, AUTH_HYBRID_RESP_RSA); + + #ifdef USE_IKEV2 /** * Described in header. diff --cc src/libcharon/sa/authenticator.h index 5042e4a732,3af939160f..86b42da7ac --- a/src/libcharon/sa/authenticator.h +++ b/src/libcharon/sa/authenticator.h @@@ -68,11 -74,35 +74,40 @@@ enum auth_method_t */ AUTH_ECDSA_521 = 11, + /** + * Generic Secure Password Authentication Method as specified in RFC 6467 + */ + AUTH_GSPM = 12, + + /** + * IKEv1 initiator XAUTH with PSK, outside of IANA range + */ + AUTH_XAUTH_INIT_PSK = 256, + + /** + * IKEv1 responder XAUTH with PSK, outside of IANA range + */ + AUTH_XAUTH_RESP_PSK, + + /** + * IKEv1 initiator XAUTH with RSA, outside of IANA range + */ + AUTH_XAUTH_INIT_RSA, + + /** + * IKEv1 responder XAUTH with RSA, outside of IANA range + */ + AUTH_XAUTH_RESP_RSA, + + /** + * IKEv1 initiator XAUTH, responder RSA, outside of IANA range + */ + AUTH_HYBRID_INIT_RSA, + + /** + * IKEv1 responder XAUTH, initiator RSA, outside of IANA range + */ + AUTH_HYBRID_RESP_RSA, }; /** diff --cc src/libcharon/sa/ikev2/task_manager_v2.c index 0000000000,ba7fdd2da6..cc6d378497 mode 000000,100644..100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@@ -1,0 -1,1479 +1,1484 @@@ + /* + * Copyright (C) 2007-2011 Tobias Brunner + * Copyright (C) 2007-2010 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 "task_manager_v2.h" + + #include + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifdef ME + #include + #endif + + typedef struct exchange_t exchange_t; + + /** + * An exchange in the air, used do detect and handle retransmission + */ + struct exchange_t { + + /** + * Message ID used for this transaction + */ + u_int32_t mid; + + /** + * generated packet for retransmission + */ + packet_t *packet; + }; + + typedef struct private_task_manager_t private_task_manager_t; + + /** + * private data of the task manager + */ + struct private_task_manager_t { + + /** + * public functions + */ + task_manager_v2_t public; + + /** + * associated IKE_SA we are serving + */ + ike_sa_t *ike_sa; + + /** + * Exchange we are currently handling as responder + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * packet for retransmission + */ + packet_t *packet; + + } responding; + + /** + * Exchange we are currently handling as initiator + */ + struct { + /** + * Message ID of the exchange + */ + u_int32_t mid; + + /** + * how many times we have retransmitted so far + */ + u_int retransmitted; + + /** + * packet for retransmission + */ + packet_t *packet; + + /** + * type of the initated exchange + */ + exchange_type_t type; + + } initiating; + + /** + * List of queued tasks not yet in action + */ + linked_list_t *queued_tasks; + + /** + * List of active tasks, initiated by ourselve + */ + linked_list_t *active_tasks; + + /** + * List of tasks initiated by peer + */ + linked_list_t *passive_tasks; + + /** + * the task manager has been reset + */ + bool reset; + + /** + * Number of times we retransmit messages before giving up + */ + u_int retransmit_tries; + + /** + * Retransmission timeout + */ + double retransmit_timeout; + + /** + * Base to calculate retransmission timeout + */ + double retransmit_base; + }; + + /** + * flush all tasks in the task manager + */ + static void flush(private_task_manager_t *this) + { + this->passive_tasks->destroy_offset(this->passive_tasks, + offsetof(task_t, destroy)); + this->passive_tasks = linked_list_create(); + this->active_tasks->destroy_offset(this->active_tasks, + offsetof(task_t, destroy)); + this->active_tasks = linked_list_create(); + this->queued_tasks->destroy_offset(this->queued_tasks, + offsetof(task_t, destroy)); + this->queued_tasks = linked_list_create(); + } + + /** + * move a task of a specific type from the queue to the active list + */ + static bool activate_task(private_task_manager_t *this, task_type_t type) + { + enumerator_t *enumerator; + task_t *task; + bool found = FALSE; + + enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + while (enumerator->enumerate(enumerator, (void**)&task)) + { + if (task->get_type(task) == type) + { + DBG2(DBG_IKE, " activating %N task", task_type_names, type); + this->queued_tasks->remove_at(this->queued_tasks, enumerator); + this->active_tasks->insert_last(this->active_tasks, task); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; + } + + METHOD(task_manager_t, retransmit, status_t, + private_task_manager_t *this, u_int32_t message_id) + { + if (this->initiating.packet && message_id == this->initiating.mid) + { + u_int32_t timeout; + job_t *job; + enumerator_t *enumerator; + packet_t *packet; + task_t *task; + ike_mobike_t *mobike = NULL; + + /* check if we are retransmitting a MOBIKE routability check */ + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void*)&task)) + { + if (task->get_type(task) == TASK_IKE_MOBIKE) + { + mobike = (ike_mobike_t*)task; + if (!mobike->is_probing(mobike)) + { + mobike = NULL; + } + break; + } + } + enumerator->destroy(enumerator); + + if (mobike == NULL) + { + if (this->initiating.retransmitted <= this->retransmit_tries) + { + timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 * + pow(this->retransmit_base, this->initiating.retransmitted)); + } + else + { + DBG1(DBG_IKE, "giving up after %d retransmits", + this->initiating.retransmitted - 1); + if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) + { + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + } + return DESTROY_ME; + } + + if (this->initiating.retransmitted) + { + DBG1(DBG_IKE, "retransmit %d of request with message ID %d", + this->initiating.retransmitted, message_id); + } + packet = this->initiating.packet->clone(this->initiating.packet); + charon->sender->send(charon->sender, packet); + } + else + { /* for routeability checks, we use a more aggressive behavior */ + if (this->initiating.retransmitted <= ROUTEABILITY_CHECK_TRIES) + { + timeout = ROUTEABILITY_CHECK_INTERVAL; + } + else + { + DBG1(DBG_IKE, "giving up after %d path probings", + this->initiating.retransmitted - 1); + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + return DESTROY_ME; + } + + if (this->initiating.retransmitted) + { + DBG1(DBG_IKE, "path probing attempt %d", + this->initiating.retransmitted); + } + mobike->transmit(mobike, this->initiating.packet); + } + + this->initiating.retransmitted++; + job = (job_t*)retransmit_job_create(this->initiating.mid, + this->ike_sa->get_id(this->ike_sa)); + lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout); + } + return SUCCESS; + } + + METHOD(task_manager_t, initiate, status_t, + private_task_manager_t *this) + { + enumerator_t *enumerator; + task_t *task; + message_t *message; + host_t *me, *other; + status_t status; + exchange_type_t exchange = 0; + + if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED) + { + DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress", + exchange_type_names, this->initiating.type); + /* do not initiate if we already have a message in the air */ + return SUCCESS; + } + + if (this->active_tasks->get_count(this->active_tasks) == 0) + { + DBG2(DBG_IKE, "activating new tasks"); + switch (this->ike_sa->get_state(this->ike_sa)) + { + case IKE_CREATED: + activate_task(this, TASK_IKE_VENDOR); + if (activate_task(this, TASK_IKE_INIT)) + { + this->initiating.mid = 0; + exchange = IKE_SA_INIT; + activate_task(this, TASK_IKE_NATD); + activate_task(this, TASK_IKE_CERT_PRE); + #ifdef ME + /* this task has to be activated before the TASK_IKE_AUTH + * task, because that task pregenerates the packet after + * which no payloads can be added to the message anymore. + */ + activate_task(this, TASK_IKE_ME); + #endif /* ME */ + activate_task(this, TASK_IKE_AUTH); + activate_task(this, TASK_IKE_CERT_POST); + activate_task(this, TASK_IKE_CONFIG); + activate_task(this, TASK_CHILD_CREATE); + activate_task(this, TASK_IKE_AUTH_LIFETIME); + activate_task(this, TASK_IKE_MOBIKE); + } + break; + case IKE_ESTABLISHED: + if (activate_task(this, TASK_CHILD_CREATE)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, TASK_CHILD_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, TASK_CHILD_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, TASK_IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, TASK_IKE_REKEY)) + { + exchange = CREATE_CHILD_SA; + break; + } + if (activate_task(this, TASK_IKE_REAUTH)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, TASK_IKE_MOBIKE)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, TASK_IKE_DPD)) + { + exchange = INFORMATIONAL; + break; + } + if (activate_task(this, TASK_IKE_AUTH_LIFETIME)) + { + exchange = INFORMATIONAL; + break; + } + #ifdef ME + if (activate_task(this, TASK_IKE_ME)) + { + exchange = ME_CONNECT; + break; + } + #endif /* ME */ + case IKE_REKEYING: + if (activate_task(this, TASK_IKE_DELETE)) + { + exchange = INFORMATIONAL; + break; + } + case IKE_DELETING: + default: + break; + } + } + else + { + DBG2(DBG_IKE, "reinitiating already active tasks"); + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void**)&task)) + { + DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task)); + switch (task->get_type(task)) + { + case TASK_IKE_INIT: + exchange = IKE_SA_INIT; + break; + case TASK_IKE_AUTH: + exchange = IKE_AUTH; + break; + case TASK_CHILD_CREATE: + case TASK_CHILD_REKEY: + case TASK_IKE_REKEY: + exchange = CREATE_CHILD_SA; + break; + case TASK_IKE_MOBIKE: + exchange = INFORMATIONAL; + break; + default: + continue; + } + break; + } + enumerator->destroy(enumerator); + } + + if (exchange == 0) + { + DBG2(DBG_IKE, "nothing to initiate"); + /* nothing to do yet... */ + return SUCCESS; + } + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + + message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION); + message->set_message_id(message, this->initiating.mid); + message->set_source(message, me->clone(me)); + message->set_destination(message, other->clone(other)); + message->set_exchange_type(message, exchange); + this->initiating.type = exchange; + this->initiating.retransmitted = 0; + + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + this->active_tasks->remove_at(this->active_tasks, enumerator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) + { + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + } + /* FALL */ + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + enumerator->destroy(enumerator); + message->destroy(message); + flush(this); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + + /* update exchange type if a task changed it */ + this->initiating.type = message->get_exchange_type(message); + + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->initiating.packet); + if (status != SUCCESS) + { + /* message generation failed. There is nothing more to do than to + * close the SA */ + message->destroy(message); + flush(this); + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + return DESTROY_ME; + } + message->destroy(message); + + return retransmit(this, this->initiating.mid); + } + + /** + * handle an incoming response message + */ + static status_t process_response(private_task_manager_t *this, + message_t *message) + { + enumerator_t *enumerator; + task_t *task; + + if (message->get_exchange_type(message) != this->initiating.type) + { + DBG1(DBG_IKE, "received %N response, but expected %N", + exchange_type_names, message->get_exchange_type(message), + exchange_type_names, this->initiating.type); + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + return DESTROY_ME; + } + + /* catch if we get resetted while processing */ + this->reset = FALSE; + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + this->active_tasks->remove_at(this->active_tasks, enumerator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + /* FALL */ + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + this->active_tasks->remove_at(this->active_tasks, enumerator); + enumerator->destroy(enumerator); + task->destroy(task); + return DESTROY_ME; + } + if (this->reset) + { /* start all over again if we were reset */ + this->reset = FALSE; + enumerator->destroy(enumerator); + return initiate(this); + } + } + enumerator->destroy(enumerator); + + this->initiating.mid++; + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + this->initiating.packet->destroy(this->initiating.packet); + this->initiating.packet = NULL; + + return initiate(this); + } + + /** + * handle exchange collisions + */ + static bool handle_collisions(private_task_manager_t *this, task_t *task) + { + enumerator_t *enumerator; + task_t *active; + task_type_t type; + + type = task->get_type(task); + + /* do we have to check */ + if (type == TASK_IKE_REKEY || type == TASK_CHILD_REKEY || + type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE || + type == TASK_IKE_REAUTH) + { + /* find an exchange collision, and notify these tasks */ + enumerator = this->active_tasks->create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, (void**)&active)) + { + switch (active->get_type(active)) + { + case TASK_IKE_REKEY: + if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE || + type == TASK_IKE_REAUTH) + { + ike_rekey_t *rekey = (ike_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + case TASK_CHILD_REKEY: + if (type == TASK_CHILD_REKEY || type == TASK_CHILD_DELETE) + { + child_rekey_t *rekey = (child_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + default: + continue; + } + enumerator->destroy(enumerator); + return TRUE; + } + enumerator->destroy(enumerator); + } + return FALSE; + } + + /** + * build a response depending on the "passive" task list + */ + static status_t build_response(private_task_manager_t *this, message_t *request) + { + enumerator_t *enumerator; + task_t *task; + message_t *message; + host_t *me, *other; - bool delete = FALSE; ++ bool delete = FALSE, hook = FALSE; + status_t status; + + me = request->get_destination(request); + other = request->get_source(request); + + message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION); + message->set_exchange_type(message, request->get_exchange_type(request)); + /* send response along the path the request came in */ + message->set_source(message, me->clone(me)); + message->set_destination(message, other->clone(other)); + message->set_message_id(message, this->responding.mid); + message->set_request(message, FALSE); + + enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); + while (enumerator->enumerate(enumerator, (void*)&task)) + { + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + this->passive_tasks->remove_at(this->passive_tasks, enumerator); + if (!handle_collisions(this, task)) + { + task->destroy(task); + } + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + if (handle_collisions(this, task)) + { + this->passive_tasks->remove_at(this->passive_tasks, + enumerator); + } + break; - case DESTROY_ME: + case FAILED: + default: ++ hook = TRUE; ++ /* FALL */ ++ case DESTROY_ME: + /* destroy IKE_SA, but SEND response first */ + delete = TRUE; + break; + } + if (delete) + { + break; + } + } + enumerator->destroy(enumerator); + + /* remove resonder SPI if IKE_SA_INIT failed */ + if (delete && request->get_exchange_type(request) == IKE_SA_INIT) + { + ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa); + id->set_responder_spi(id, 0); + } + + /* message complete, send it */ + DESTROY_IF(this->responding.packet); + this->responding.packet = NULL; + status = this->ike_sa->generate_message(this->ike_sa, message, + &this->responding.packet); + message->destroy(message); + if (status != SUCCESS) + { + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + return DESTROY_ME; + } + + charon->sender->send(charon->sender, + this->responding.packet->clone(this->responding.packet)); + if (delete) + { - charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); ++ if (hook) ++ { ++ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); ++ } + return DESTROY_ME; + } + return SUCCESS; + } + + /** + * handle an incoming request message + */ + static status_t process_request(private_task_manager_t *this, + message_t *message) + { + enumerator_t *enumerator; + task_t *task = NULL; + payload_t *payload; + notify_payload_t *notify; + delete_payload_t *delete; + + if (this->passive_tasks->get_count(this->passive_tasks) == 0) + { /* create tasks depending on request type, if not already some queued */ + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + { + task = (task_t*)ike_vendor_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_natd_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + #ifdef ME + task = (task_t*)ike_me_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + #endif /* ME */ + task = (task_t*)ike_auth_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_cert_post_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_config_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)child_create_create(this->ike_sa, NULL, FALSE, + NULL, NULL); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_mobike_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + case CREATE_CHILD_SA: + { /* FIXME: we should prevent this on mediation connections */ + bool notify_found = FALSE, ts_found = FALSE; + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + switch (payload->get_type(payload)) + { + case NOTIFY: + { /* if we find a rekey notify, its CHILD_SA rekeying */ + notify = (notify_payload_t*)payload; + if (notify->get_notify_type(notify) == REKEY_SA && + (notify->get_protocol_id(notify) == PROTO_AH || + notify->get_protocol_id(notify) == PROTO_ESP)) + { + notify_found = TRUE; + } + break; + } + case TRAFFIC_SELECTOR_INITIATOR: + case TRAFFIC_SELECTOR_RESPONDER: + { /* if we don't find a TS, its IKE rekeying */ + ts_found = TRUE; + break; + } + default: + break; + } + } + enumerator->destroy(enumerator); + + if (ts_found) + { + if (notify_found) + { + task = (task_t*)child_rekey_create(this->ike_sa, + PROTO_NONE, 0); + } + else + { + task = (task_t*)child_create_create(this->ike_sa, NULL, + FALSE, NULL, NULL); + } + } + else + { + task = (task_t*)ike_rekey_create(this->ike_sa, FALSE); + } + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + case INFORMATIONAL: + { + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + switch (payload->get_type(payload)) + { + case NOTIFY: + { + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + case ADDITIONAL_IP4_ADDRESS: + case ADDITIONAL_IP6_ADDRESS: + case NO_ADDITIONAL_ADDRESSES: + case UPDATE_SA_ADDRESSES: + case NO_NATS_ALLOWED: + case UNACCEPTABLE_ADDRESSES: + case UNEXPECTED_NAT_DETECTED: + case COOKIE2: + case NAT_DETECTION_SOURCE_IP: + case NAT_DETECTION_DESTINATION_IP: + task = (task_t*)ike_mobike_create( + this->ike_sa, FALSE); + break; + case AUTH_LIFETIME: + task = (task_t*)ike_auth_lifetime_create( + this->ike_sa, FALSE); + break; + default: + break; + } + break; + } + case DELETE: + { + delete = (delete_payload_t*)payload; + if (delete->get_protocol_id(delete) == PROTO_IKE) + { + task = (task_t*)ike_delete_create(this->ike_sa, + FALSE); + } + else + { + task = (task_t*)child_delete_create(this->ike_sa, + PROTO_NONE, 0, FALSE); + } + break; + } + default: + break; + } + if (task) + { + break; + } + } + enumerator->destroy(enumerator); + + if (task == NULL) + { + task = (task_t*)ike_dpd_create(FALSE); + } + this->passive_tasks->insert_last(this->passive_tasks, task); + break; + } + #ifdef ME + case ME_CONNECT: + { + task = (task_t*)ike_me_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); + } + #endif /* ME */ + default: + break; + } + } + + /* let the tasks process the message */ + enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); + while (enumerator->enumerate(enumerator, (void*)&task)) + { + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + this->passive_tasks->remove_at(this->passive_tasks, enumerator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs at least another call to build() */ + break; + case FAILED: + default: + charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); + /* FALL */ + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + this->passive_tasks->remove_at(this->passive_tasks, enumerator); + enumerator->destroy(enumerator); + task->destroy(task); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + + return build_response(this, message); + } + + METHOD(task_manager_t, incr_mid, void, + private_task_manager_t *this, bool initiate) + { + if (initiate) + { + this->initiating.mid++; + } + else + { + this->responding.mid++; + } + } + + /** + * Send a notify back to the sender + */ + static void send_notify_response(private_task_manager_t *this, + message_t *request, notify_type_t type, + chunk_t data) + { + message_t *response; + packet_t *packet; + host_t *me, *other; + + response = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION); + response->set_exchange_type(response, request->get_exchange_type(request)); + response->set_request(response, FALSE); + response->set_message_id(response, request->get_message_id(request)); + response->add_notify(response, FALSE, type, data); + me = this->ike_sa->get_my_host(this->ike_sa); + if (me->is_anyaddr(me)) + { + me = request->get_destination(request); + this->ike_sa->set_my_host(this->ike_sa, me->clone(me)); + } + other = this->ike_sa->get_other_host(this->ike_sa); + if (other->is_anyaddr(other)) + { + other = request->get_source(request); + this->ike_sa->set_other_host(this->ike_sa, other->clone(other)); + } + response->set_source(response, me->clone(me)); + response->set_destination(response, other->clone(other)); + if (this->ike_sa->generate_message(this->ike_sa, response, + &packet) == SUCCESS) + { + charon->sender->send(charon->sender, packet); + } + response->destroy(response); + } + + /** + * Parse the given message and verify that it is valid. + */ + static status_t parse_message(private_task_manager_t *this, message_t *msg) + { + status_t status; + u_int8_t type = 0; + + status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa)); + + if (status == SUCCESS) + { /* check for unsupported critical payloads */ + enumerator_t *enumerator; + unknown_payload_t *unknown; + payload_t *payload; + + enumerator = msg->create_payload_enumerator(msg); + while (enumerator->enumerate(enumerator, &payload)) + { + unknown = (unknown_payload_t*)payload; + type = payload->get_type(payload); + if (!payload_is_known(type) && + unknown->is_critical(unknown)) + { + DBG1(DBG_ENC, "payload type %N is not supported, " + "but its critical!", payload_type_names, type); + status = NOT_SUPPORTED; + } + } + enumerator->destroy(enumerator); + } + + if (status != SUCCESS) + { + bool is_request = msg->get_request(msg); + + switch (status) + { + case NOT_SUPPORTED: + DBG1(DBG_IKE, "critical unknown payloads found"); + if (is_request) + { + send_notify_response(this, msg, + UNSUPPORTED_CRITICAL_PAYLOAD, + chunk_from_thing(type)); + incr_mid(this, FALSE); + } + break; + case PARSE_ERROR: + DBG1(DBG_IKE, "message parsing failed"); + if (is_request) + { + send_notify_response(this, msg, + INVALID_SYNTAX, chunk_empty); + incr_mid(this, FALSE); + } + break; + case VERIFY_ERROR: + DBG1(DBG_IKE, "message verification failed"); + if (is_request) + { + send_notify_response(this, msg, + INVALID_SYNTAX, chunk_empty); + incr_mid(this, FALSE); + } + break; + case FAILED: + DBG1(DBG_IKE, "integrity check failed"); + /* ignored */ + break; + case INVALID_STATE: + DBG1(DBG_IKE, "found encrypted message, but no keys available"); + default: + break; + } + DBG1(DBG_IKE, "%N %s with message ID %d processing failed", + exchange_type_names, msg->get_exchange_type(msg), + is_request ? "request" : "response", + msg->get_message_id(msg)); + + if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED) + { /* invalid initiation attempt, close SA */ + return DESTROY_ME; + } + } + return status; + } + + + METHOD(task_manager_t, process_message, status_t, + private_task_manager_t *this, message_t *msg) + { + host_t *me, *other; + status_t status; + u_int32_t mid; + + charon->bus->message(charon->bus, msg, TRUE, FALSE); + status = parse_message(this, msg); + if (status != SUCCESS) + { + return status; + } + + me = msg->get_destination(msg); + other = msg->get_source(msg); + + /* if this IKE_SA is virgin, we check for a config */ + if (this->ike_sa->get_ike_cfg(this->ike_sa) == NULL) + { + ike_sa_id_t *ike_sa_id; + ike_cfg_t *ike_cfg; + job_t *job; + ike_cfg = charon->backends->get_ike_cfg(charon->backends, me, other); + if (ike_cfg == NULL) + { + /* no config found for these hosts, destroy */ + DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N", + me, other, notify_type_names, NO_PROPOSAL_CHOSEN); + send_notify_response(this, msg, + NO_PROPOSAL_CHOSEN, chunk_empty); + return DESTROY_ME; + } + this->ike_sa->set_ike_cfg(this->ike_sa, ike_cfg); + ike_cfg->destroy(ike_cfg); + /* add a timeout if peer does not establish it completely */ + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + job = (job_t*)delete_ike_sa_job_create(ike_sa_id, FALSE); + lib->scheduler->schedule_job(lib->scheduler, job, + lib->settings->get_int(lib->settings, + "charon.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT)); + } + this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, + time_monotonic(NULL)); + + mid = msg->get_message_id(msg); + if (msg->get_request(msg)) + { + if (mid == this->responding.mid) + { + if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED || + this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING || + msg->get_exchange_type(msg) != IKE_SA_INIT) + { /* only do host updates based on verified messages */ + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) + { /* with MOBIKE, we do no implicit updates */ + this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1); + } + } + charon->bus->message(charon->bus, msg, TRUE, TRUE); + if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED) + { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */ + return SUCCESS; + } + if (process_request(this, msg) != SUCCESS) + { + flush(this); + return DESTROY_ME; + } + this->responding.mid++; + } + else if ((mid == this->responding.mid - 1) && this->responding.packet) + { + packet_t *clone; + host_t *host; + + DBG1(DBG_IKE, "received retransmit of request with ID %d, " + "retransmitting response", mid); + clone = this->responding.packet->clone(this->responding.packet); + host = msg->get_destination(msg); + clone->set_source(clone, host->clone(host)); + host = msg->get_source(msg); + clone->set_destination(clone, host->clone(host)); + charon->sender->send(charon->sender, clone); + } + else + { + DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored", + mid, this->responding.mid); + } + } + else + { + if (mid == this->initiating.mid) + { + if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED || + this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING || + msg->get_exchange_type(msg) != IKE_SA_INIT) + { /* only do host updates based on verified messages */ + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) + { /* with MOBIKE, we do no implicit updates */ + this->ike_sa->update_hosts(this->ike_sa, me, other, FALSE); + } + } + charon->bus->message(charon->bus, msg, TRUE, TRUE); + if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED) + { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */ + return SUCCESS; + } + if (process_response(this, msg) != SUCCESS) + { + flush(this); + return DESTROY_ME; + } + } + else + { + DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored", + mid, this->initiating.mid); + return SUCCESS; + } + } + return SUCCESS; + } + + METHOD(task_manager_t, queue_task, void, + private_task_manager_t *this, task_t *task) + { + if (task->get_type(task) == TASK_IKE_MOBIKE) + { /* there is no need to queue more than one mobike task */ + enumerator_t *enumerator; + task_t *current; + + enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + while (enumerator->enumerate(enumerator, (void**)¤t)) + { + if (current->get_type(current) == TASK_IKE_MOBIKE) + { + enumerator->destroy(enumerator); + task->destroy(task); + return; + } + } + enumerator->destroy(enumerator); + } + DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task)); + this->queued_tasks->insert_last(this->queued_tasks, task); + } + + /** + * Check if a given task has been queued already + */ + static bool has_queued(private_task_manager_t *this, task_type_t type) + { + enumerator_t *enumerator; + bool found = FALSE; + task_t *task; + + enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == type) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; + } + + METHOD(task_manager_t, queue_ike, void, + private_task_manager_t *this) + { + if (!has_queued(this, TASK_IKE_VENDOR)) + { + queue_task(this, (task_t*)ike_vendor_create(this->ike_sa, TRUE)); + } + if (!has_queued(this, TASK_IKE_INIT)) + { + queue_task(this, (task_t*)ike_init_create(this->ike_sa, TRUE, NULL)); + } + if (!has_queued(this, TASK_IKE_NATD)) + { + queue_task(this, (task_t*)ike_natd_create(this->ike_sa, TRUE)); + } + if (!has_queued(this, TASK_IKE_CERT_PRE)) + { + queue_task(this, (task_t*)ike_cert_pre_create(this->ike_sa, TRUE)); + } + if (!has_queued(this, TASK_IKE_AUTH)) + { + queue_task(this, (task_t*)ike_auth_create(this->ike_sa, TRUE)); + } + if (!has_queued(this, TASK_IKE_CERT_POST)) + { + queue_task(this, (task_t*)ike_cert_post_create(this->ike_sa, TRUE)); + } + if (!has_queued(this, TASK_IKE_CONFIG)) + { + queue_task(this, (task_t*)ike_config_create(this->ike_sa, TRUE)); + } + if (!has_queued(this, TASK_IKE_AUTH_LIFETIME)) + { + queue_task(this, (task_t*)ike_auth_lifetime_create(this->ike_sa, TRUE)); + } + if (!has_queued(this, TASK_IKE_MOBIKE)) + { + peer_cfg_t *peer_cfg; + + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + if (peer_cfg->use_mobike(peer_cfg)) + { + queue_task(this, (task_t*)ike_mobike_create(this->ike_sa, TRUE)); + } + } + #ifdef ME + if (!has_queued(this, TASK_IKE_ME)) + { + queue_task(this, (task_t*)ike_me_create(this->ike_sa, TRUE)); + } + #endif /* ME */ + } + + METHOD(task_manager_t, queue_ike_rekey, void, + private_task_manager_t *this) + { + queue_task(this, (task_t*)ike_rekey_create(this->ike_sa, TRUE)); + } + + METHOD(task_manager_t, queue_ike_reauth, void, + private_task_manager_t *this) + { + queue_task(this, (task_t*)ike_reauth_create(this->ike_sa)); + } + + METHOD(task_manager_t, queue_ike_delete, void, + private_task_manager_t *this) + { + queue_task(this, (task_t*)ike_delete_create(this->ike_sa, TRUE)); + } + + METHOD(task_manager_t, queue_mobike, void, + private_task_manager_t *this, bool roam, bool address) + { + ike_mobike_t *mobike; + + mobike = ike_mobike_create(this->ike_sa, TRUE); + if (roam) + { + mobike->roam(mobike, address); + } + else + { + mobike->addresses(mobike); + } + queue_task(this, &mobike->task); + } + + METHOD(task_manager_t, queue_child, void, + private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid, + traffic_selector_t *tsi, traffic_selector_t *tsr) + { + child_create_t *task; + + task = child_create_create(this->ike_sa, cfg, FALSE, tsi, tsr); + if (reqid) + { + task->use_reqid(task, reqid); + } + queue_task(this, &task->task); + } + + METHOD(task_manager_t, queue_child_rekey, void, + private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi) + { + queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi)); + } + + METHOD(task_manager_t, queue_child_delete, void, + private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi, + bool expired) + { + queue_task(this, (task_t*)child_delete_create(this->ike_sa, + protocol, spi, expired)); + } + + METHOD(task_manager_t, queue_dpd, void, + private_task_manager_t *this) + { + ike_mobike_t *mobike; + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE) && + this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE)) + { + /* use mobike enabled DPD to detect NAT mapping changes */ + mobike = ike_mobike_create(this->ike_sa, TRUE); + mobike->dpd(mobike); + queue_task(this, &mobike->task); + } + else + { + queue_task(this, (task_t*)ike_dpd_create(TRUE)); + } + } + + METHOD(task_manager_t, adopt_tasks, void, + private_task_manager_t *this, task_manager_t *other_public) + { + private_task_manager_t *other = (private_task_manager_t*)other_public; + task_t *task; + + /* move queued tasks from other to this */ + while (other->queued_tasks->remove_last(other->queued_tasks, + (void**)&task) == SUCCESS) + { + DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } + } + + METHOD(task_manager_t, busy, bool, + private_task_manager_t *this) + { + return (this->active_tasks->get_count(this->active_tasks) > 0); + } + + METHOD(task_manager_t, reset, void, + private_task_manager_t *this, u_int32_t initiate, u_int32_t respond) + { + enumerator_t *enumerator; + task_t *task; + + /* reset message counters and retransmit packets */ + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + this->responding.packet = NULL; + this->initiating.packet = NULL; + if (initiate != UINT_MAX) + { + this->initiating.mid = initiate; + } + if (respond != UINT_MAX) + { + this->responding.mid = respond; + } + this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + + /* reset queued tasks */ + enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + task->migrate(task, this->ike_sa); + } + enumerator->destroy(enumerator); + + /* reset active tasks */ + while (this->active_tasks->remove_last(this->active_tasks, + (void**)&task) == SUCCESS) + { + task->migrate(task, this->ike_sa); + this->queued_tasks->insert_first(this->queued_tasks, task); + } + + this->reset = TRUE; + } + + METHOD(task_manager_t, create_task_enumerator, enumerator_t*, + private_task_manager_t *this, task_queue_t queue) + { + switch (queue) + { + case TASK_QUEUE_ACTIVE: + return this->active_tasks->create_enumerator(this->active_tasks); + case TASK_QUEUE_PASSIVE: + return this->passive_tasks->create_enumerator(this->passive_tasks); + case TASK_QUEUE_QUEUED: + return this->queued_tasks->create_enumerator(this->queued_tasks); + default: + return enumerator_create_empty(); + } + } + + METHOD(task_manager_t, destroy, void, + private_task_manager_t *this) + { + flush(this); + + this->active_tasks->destroy(this->active_tasks); + this->queued_tasks->destroy(this->queued_tasks); + this->passive_tasks->destroy(this->passive_tasks); + + DESTROY_IF(this->responding.packet); + DESTROY_IF(this->initiating.packet); + free(this); + } + + /* + * see header file + */ + task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) + { + private_task_manager_t *this; + + INIT(this, + .public = { + .task_manager = { + .process_message = _process_message, + .queue_task = _queue_task, + .queue_ike = _queue_ike, + .queue_ike_rekey = _queue_ike_rekey, + .queue_ike_reauth = _queue_ike_reauth, + .queue_ike_delete = _queue_ike_delete, + .queue_mobike = _queue_mobike, + .queue_child = _queue_child, + .queue_child_rekey = _queue_child_rekey, + .queue_child_delete = _queue_child_delete, + .queue_dpd = _queue_dpd, + .initiate = _initiate, + .retransmit = _retransmit, + .incr_mid = _incr_mid, + .reset = _reset, + .adopt_tasks = _adopt_tasks, + .busy = _busy, + .create_task_enumerator = _create_task_enumerator, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + .initiating.type = EXCHANGE_TYPE_UNDEFINED, + .queued_tasks = linked_list_create(), + .active_tasks = linked_list_create(), + .passive_tasks = linked_list_create(), + .retransmit_tries = lib->settings->get_int(lib->settings, + "charon.retransmit_tries", RETRANSMIT_TRIES), + .retransmit_timeout = lib->settings->get_double(lib->settings, + "charon.retransmit_timeout", RETRANSMIT_TIMEOUT), + .retransmit_base = lib->settings->get_double(lib->settings, + "charon.retransmit_base", RETRANSMIT_BASE), + ); + + return &this->public; + } diff --cc src/libcharon/sa/ikev2/tasks/ike_init.c index dd8a4b086c,de68e86622..3fbbcfd2ae --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@@ -515,13 -517,11 +517,14 @@@ METHOD(task_t, migrate, void chunk_free(&this->other_nonce); this->ike_sa = ike_sa; - this->keymat = ike_sa->get_keymat(ike_sa); + this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa); this->proposal = NULL; - DESTROY_IF(this->dh); - this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, - this->dh_group); + if (this->dh && this->dh->get_dh_group(this->dh) != this->dh_group) + { /* reset DH value only if group changed (INVALID_KE_PAYLOAD) */ + this->dh->destroy(this->dh); - this->dh = this->keymat->create_dh(this->keymat, this->dh_group); ++ this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat, ++ this->dh_group); + } } METHOD(task_t, destroy, void, diff --cc src/libstrongswan/credentials/auth_cfg.c index 12f75b2403,0646b0e2c9..fd2cee607a --- a/src/libstrongswan/credentials/auth_cfg.c +++ b/src/libstrongswan/credentials/auth_cfg.c @@@ -53,40 -56,6 +56,42 @@@ ENUM(auth_rule_names, AUTH_RULE_IDENTIT "HELPER_REVOCATION_CERT", ); +/** + * Check if the given rule is a rule for which there may be multiple values. + */ +static inline bool is_multi_value_rule(auth_rule_t type) +{ + switch (type) + { + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_RSA_STRENGTH: + case AUTH_RULE_ECDSA_STRENGTH: + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_AAA_IDENTITY: ++ case AUTH_RULE_XAUTH_IDENTITY: ++ case AUTH_RULE_XAUTH_BACKEND: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_SUBJECT_CERT: + case AUTH_HELPER_SUBJECT_HASH_URL: + case AUTH_RULE_MAX: + return FALSE; + case AUTH_RULE_OCSP_VALIDATION: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_GROUP: + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_CERT_POLICY: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_REVOCATION_CERT: + return TRUE; + } + return FALSE; +} + typedef struct private_auth_cfg_t private_auth_cfg_t; /** @@@ -180,108 -134,6 +185,112 @@@ METHOD(auth_cfg_t, create_enumerator, e return &enumerator->public; } +/** + * Create an entry from the given arguments. + */ +static entry_t *entry_create(auth_rule_t type, va_list args) +{ + entry_t *this = malloc_thing(entry_t); + + this->type = type; + switch (type) + { + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + case AUTH_RULE_RSA_STRENGTH: + case AUTH_RULE_ECDSA_STRENGTH: + /* integer type */ + this->value = (void*)(uintptr_t)va_arg(args, u_int); + break; + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_AAA_IDENTITY: ++ case AUTH_RULE_XAUTH_BACKEND: ++ case AUTH_RULE_XAUTH_IDENTITY: + case AUTH_RULE_GROUP: + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_RULE_CERT_POLICY: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + case AUTH_HELPER_REVOCATION_CERT: + /* pointer type */ + this->value = va_arg(args, void*); + break; + case AUTH_RULE_MAX: + this->value = NULL; + break; + } + return this; +} + +/** + * Compare two entries for equality. + */ +static bool entry_equals(entry_t *e1, entry_t *e2) +{ + if (e1->type != e2->type) + { + return FALSE; + } + switch (e1->type) + { + case AUTH_RULE_AUTH_CLASS: + case AUTH_RULE_EAP_TYPE: + case AUTH_RULE_EAP_VENDOR: + case AUTH_RULE_CRL_VALIDATION: + case AUTH_RULE_OCSP_VALIDATION: + case AUTH_RULE_RSA_STRENGTH: + case AUTH_RULE_ECDSA_STRENGTH: + { + return e1->value == e2->value; + } + case AUTH_RULE_CA_CERT: + case AUTH_RULE_IM_CERT: + case AUTH_RULE_SUBJECT_CERT: + case AUTH_HELPER_IM_CERT: + case AUTH_HELPER_SUBJECT_CERT: + case AUTH_HELPER_REVOCATION_CERT: + { + certificate_t *c1, *c2; + + c1 = (certificate_t*)e1->value; + c2 = (certificate_t*)e2->value; + + return c1->equals(c1, c2); + } + case AUTH_RULE_IDENTITY: + case AUTH_RULE_EAP_IDENTITY: + case AUTH_RULE_AAA_IDENTITY: ++ case AUTH_RULE_XAUTH_IDENTITY: + case AUTH_RULE_GROUP: + { + identification_t *id1, *id2; + + id1 = (identification_t*)e1->value; + id2 = (identification_t*)e2->value; + + return id1->equals(id1, id2); + } + case AUTH_RULE_CERT_POLICY: ++ case AUTH_RULE_XAUTH_BACKEND: + case AUTH_HELPER_IM_HASH_URL: + case AUTH_HELPER_SUBJECT_HASH_URL: + { + return streq(e1->value, e2->value); + } + case AUTH_RULE_MAX: + break; + } + return FALSE; +} + /** * Destroy the value associated with an entry */