diff options
Diffstat (limited to 'src/charon/sa')
71 files changed, 3881 insertions, 6582 deletions
diff --git a/src/charon/sa/authenticators/authenticator.c b/src/charon/sa/authenticators/authenticator.c index 707aae9ad..c301e4933 100644 --- a/src/charon/sa/authenticators/authenticator.c +++ b/src/charon/sa/authenticators/authenticator.c @@ -1,11 +1,5 @@ -/** - * @file authenticator.c - * - * @brief Generic constructor for authenticators. - * - */ - /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -18,13 +12,15 @@ * 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. + * + * $Id: authenticator.c 4051 2008-06-10 09:08:27Z tobias $ */ #include <string.h> #include "authenticator.h" -#include <sa/authenticators/rsa_authenticator.h> +#include <sa/authenticators/pubkey_authenticator.h> #include <sa/authenticators/psk_authenticator.h> #include <sa/authenticators/eap_authenticator.h> @@ -33,23 +29,46 @@ ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS, "RSA signature", "pre-shared key", "DSS signature"); -ENUM_NEXT(auth_method_names, AUTH_EAP, AUTH_EAP, AUTH_DSS, +ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_ECDSA_521, AUTH_DSS, + "ECDSA-256 signature", + "ECDSA-384 signature", + "ECDSA-521 signature"); +ENUM_NEXT(auth_method_names, AUTH_EAP, AUTH_EAP, AUTH_ECDSA_521, "EAP"); ENUM_END(auth_method_names, AUTH_EAP); -/* +/** * Described in header. */ -authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method) +authenticator_t *authenticator_create(ike_sa_t *ike_sa, config_auth_method_t auth_method) { switch (auth_method) { + case CONF_AUTH_PUBKEY: + return (authenticator_t*)pubkey_authenticator_create(ike_sa); + case CONF_AUTH_PSK: + return (authenticator_t*)psk_authenticator_create(ike_sa); + case CONF_AUTH_EAP: + return (authenticator_t*)eap_authenticator_create(ike_sa); + default: + return NULL; + } +} + +/** + * Described in header. + */ +authenticator_t *authenticator_create_from_auth_payload(ike_sa_t *ike_sa, auth_payload_t *auth_payload) +{ + switch (auth_payload->get_auth_method(auth_payload)) + { case AUTH_RSA: - return (authenticator_t*)rsa_authenticator_create(ike_sa); + case AUTH_ECDSA_256: + case AUTH_ECDSA_384: + case AUTH_ECDSA_521: + return (authenticator_t*)pubkey_authenticator_create(ike_sa); case AUTH_PSK: return (authenticator_t*)psk_authenticator_create(ike_sa); - case AUTH_EAP: - return (authenticator_t*)eap_authenticator_create(ike_sa); default: return NULL; } diff --git a/src/charon/sa/authenticators/authenticator.h b/src/charon/sa/authenticators/authenticator.h index c7b0fc81a..3c961d23e 100644 --- a/src/charon/sa/authenticators/authenticator.h +++ b/src/charon/sa/authenticators/authenticator.h @@ -1,11 +1,5 @@ -/** - * @file authenticator.h - * - * @brief Interface of authenticator_t. - * - */ - /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -19,6 +13,13 @@ * 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. + * + * $Id: authenticator.h 4051 2008-06-10 09:08:27Z tobias $ + */ + +/** + * @defgroup authenticator authenticator + * @{ @ingroup authenticators */ #ifndef AUTHENTICATOR_H_ @@ -29,12 +30,11 @@ typedef struct authenticator_t authenticator_t; #include <library.h> #include <sa/ike_sa.h> +#include <config/peer_cfg.h> #include <encoding/payloads/auth_payload.h> /** * Method to use for authentication. - * - * @ingroup authenticators */ enum auth_method_t { /** @@ -57,6 +57,21 @@ enum auth_method_t { AUTH_DSS = 3, /** + * ECDSA with SHA-256 on the P-256 curve as specified in RFC 4754 + */ + AUTH_ECDSA_256 = 9, + + /** + * ECDSA with SHA-384 on the P-384 curve as specified in RFC 4754 + */ + AUTH_ECDSA_384 = 10, + + /** + * ECDSA with SHA-512 on the P-521 curve as specified in RFC 4754 + */ + AUTH_ECDSA_521 = 11, + + /** * EAP authentication. This value is never negotiated and therefore * a value from private use. */ @@ -65,29 +80,22 @@ enum auth_method_t { /** * enum names for auth_method_t. - * - * @ingroup authenticators */ extern enum_name_t *auth_method_names; /** - * @brief Authenticator interface implemented by the various authenticators. + * Authenticator interface implemented by the various authenticators. * * Currently the following two AUTH methods are supported: - * - shared key message integrity code (AUTH_PSK) - * - RSA digital signature (AUTH_RSA) - * - * @b Constructors: - * - authenticator_create() - * - * @ingroup authenticators + * - shared key message integrity code + * - RSA digital signature + * - ECDSA is supported using OpenSSL */ struct authenticator_t { /** - * @brief Verify a received authentication payload. + * Verify a received authentication payload. * - * @param this calling object * @param ike_sa_init binary representation of received ike_sa_init * @param my_nonce the sent nonce * @param auth_payload authentication payload to verify @@ -102,9 +110,8 @@ struct authenticator_t { chunk_t my_nonce, auth_payload_t *auth_payload); /** - * @brief Build an authentication payload to send to the other peer. + * Build an authentication payload to send to the other peer. * - * @param this calling object * @param ike_sa_init binary representation of sent ike_sa_init * @param other_nonce the received nonce * @param[out] auth_payload the resulting authentication payload @@ -117,23 +124,29 @@ struct authenticator_t { chunk_t other_nonce, auth_payload_t **auth_payload); /** - * @brief Destroys a authenticator_t object. - * - * @param this calling object + * Destroys a authenticator_t object. */ void (*destroy) (authenticator_t *this); }; /** - * @brief Creates an authenticator for the specified auth method. + * Creates an authenticator for the specified auth method (as configured). * * @param ike_sa associated ike_sa * @param auth_method authentication method to use for build()/verify() * * @return authenticator_t object - * - * @ingroup authenticators */ -authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_method); +authenticator_t *authenticator_create(ike_sa_t *ike_sa, config_auth_method_t auth_method); + +/** + * Creates an authenticator from the given auth payload. + * + * @param ike_sa associated ike_sa + * @param auth_payload auth payload + * + * @return authenticator_t object + */ +authenticator_t *authenticator_create_from_auth_payload(ike_sa_t *ike_sa, auth_payload_t *auth_payload); -#endif /* AUTHENTICATOR_H_ */ +#endif /* AUTHENTICATOR_H_ @} */ diff --git a/src/charon/sa/authenticators/eap/eap_aka.c b/src/charon/sa/authenticators/eap/eap_aka.c deleted file mode 100644 index 8fb1f85cd..000000000 --- a/src/charon/sa/authenticators/eap/eap_aka.c +++ /dev/null @@ -1,1440 +0,0 @@ -/** - * @file eap_aka.c - * - * @brief Implementation of eap_aka_t. - * - */ - -/* - * Copyright (C) 2006 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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. - */ - - -/* The EAP-AKA method uses it's own simple parser for processing EAP-AKA - * payloads, as the IKEv2 parser is not suitable for that job. There are - * two simple methods for parsing payloads, read_header() and read_attribute(). - * Every EAP-AKA payload consists of a header and a list of attributes. Those - * functions mentioned read the data and return the type of the found - * attribute/EAP-AKA-type. For generating a EAP-AKA message, we have a - * build_aka_payload(), which builds the whole message from a variable - * argument list containing its attributes. - * The processing of messages is split up in various functions: - * - peer_process() - General processing multiplexer for the peer - * - peer_process_challenge() - Specific AKA-Challenge processor - * - peer_process_notification() - Processing of AKA-Notification - * - server_process() - General processing multiplexer for the server - * - peer_process_challenge() - Processing of a received Challenge response - * - peer_process_synchronize() - Process a sequence number synchronization - * - server_initiate() - Initiation method for the server, calls - * - server_initiate_challenge() - Initiation of AKA-Challenge - */ - -#include <string.h> -#include <unistd.h> -#include <sys/time.h> -#include <time.h> - -#include "eap_aka.h" - -#include <daemon.h> -#include <library.h> -#include <utils/randomizer.h> -#include <crypto/hashers/hasher.h> -#include <crypto/prfs/fips_prf.h> - -/* Use test vectors specified in S.S0055 -#define TEST_VECTORS */ - -#define RAND_LENGTH 16 -#define RES_LENGTH 16 -#define SQN_LENGTH 6 -#define K_LENGTH 16 -#define MAC_LENGTH 8 -#define CK_LENGTH 16 -#define IK_LENGTH 16 -#define AK_LENGTH 6 -#define AMF_LENGTH 2 -#define FMK_LENGTH 4 -#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH) -#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH) -#define PAYLOAD_LENGTH 64 -#define MK_LENGTH 20 -#define MSK_LENGTH 64 -#define EMSK_LENGTH 64 -#define KAUTH_LENGTH 16 -#define KENCR_LENGTH 16 -#define AT_MAC_LENGTH 16 - -#define F1 0x42 -#define F1STAR 0x43 -#define F2 0x44 -#define F3 0x45 -#define F4 0x46 -#define F5 0x47 -#define F5STAR 0x48 - -ENUM_BEGIN(aka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY, - "AKA_CHALLENGE", - "AKA_AUTHENTICATION_REJECT", - "AKA_3", - "AKA_SYNCHRONIZATION_FAILURE", - "AKA_IDENTITY"); -ENUM_NEXT(aka_subtype_names, AKA_NOTIFICATION, AKA_CLIENT_ERROR, AKA_IDENTITY, - "AKA_NOTIFICATION", - "AKA_REAUTHENTICATION", - "AKA_CLIENT_ERROR"); -ENUM_END(aka_subtype_names, AKA_CLIENT_ERROR); - - -ENUM_BEGIN(aka_attribute_names, AT_END, AT_CLIENT_ERROR_CODE, - "AT_END", - "AT_0", - "AT_RAND", - "AT_AUTN", - "AT_RES", - "AT_AUTS", - "AT_5", - "AT_PADDING", - "AT_NONCE_MT", - "AT_8", - "AT_9", - "AT_PERMANENT_ID_REQ", - "AT_MAC", - "AT_NOTIFICATION", - "AT_ANY_ID_REQ", - "AT_IDENTITY", - "AT_VERSION_LIST", - "AT_SELECTED_VERSION", - "AT_FULLAUTH_ID_REQ", - "AT_18", - "AT_COUNTER", - "AT_COUNTER_TOO_SMALL", - "AT_NONCE_S", - "AT_CLIENT_ERROR_CODE"); -ENUM_NEXT(aka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE, - "AT_IV", - "AT_ENCR_DATA", - "AT_131", - "AT_NEXT_PSEUDONYM", - "AT_NEXT_REAUTH_ID", - "AT_CHECKCODE", - "AT_RESULT_IND"); -ENUM_END(aka_attribute_names, AT_RESULT_IND); - - -typedef struct private_eap_aka_t private_eap_aka_t; - -/** - * Private data of an eap_aka_t object. - */ -struct private_eap_aka_t { - - /** - * Public authenticator_t interface. - */ - eap_aka_t public; - - /** - * ID of the server - */ - identification_t *server; - - /** - * ID of the peer - */ - identification_t *peer; - - /** - * Key for EAP MAC - */ - chunk_t k_auth; - - /** - * Key for EAP encryption - */ - chunk_t k_encr; - - /** - * MSK - */ - chunk_t msk; - - /** - * Extendend MSK - */ - chunk_t emsk; - - /** - * Expected result from client XRES - */ - chunk_t xres; - - /** - * Shared secret K from ipsec.conf (padded) - */ - chunk_t k; - - /** - * random value RAND generated by server - */ - chunk_t rand; -}; - -/** Family key, as proposed in S.S0055 */ -static u_int8_t fmk_buf[] = {0x41, 0x48, 0x41, 0x47}; -static chunk_t fmk = chunk_from_buf(fmk_buf); - -/** Authentication management field */ -static u_int8_t amf_buf[] = {0x00, 0x01}; -static chunk_t amf = chunk_from_buf(amf_buf); - -/** AT_CLIENT_ERROR_CODE AKA attribute */ -static u_int8_t client_error_code_buf[] = {0, 0}; -static chunk_t client_error_code = chunk_from_buf(client_error_code_buf); - -/** previously used sqn by peer, next one must be greater */ -static u_int8_t peer_sqn_buf[6]; -static chunk_t peer_sqn = chunk_from_buf(peer_sqn_buf); - -/** set SQN to the current time */ -static void update_sqn(u_int8_t *sqn, time_t offset) -{ - timeval_t time; - gettimeofday(&time, NULL); - /* set sqb_sqn to an integer containing seconds followed by most - * significant useconds */ - time.tv_sec = htonl(time.tv_sec + offset); - /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */ - time.tv_usec <<= 12; - time.tv_usec = htonl(time.tv_usec); - memcpy(sqn, &time.tv_sec, 4); - memcpy(sqn + 4, &time.tv_usec, 2); -} - -/** initialize peers SQN to the current system time at startup */ -static void __attribute__ ((constructor))init_sqn(void) -{ - update_sqn(peer_sqn_buf, 0); -} - -/** - * Binary represnation of the polynom T^160 + T^5 + T^3 + T^2 + 1 - */ -static u_int8_t g[] = { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2d -}; - -/** - * Predefined random bits from the RAND Corporation book - */ -static u_int8_t a[] = { - 0x9d, 0xe9, 0xc9, 0xc8, 0xef, 0xd5, 0x78, 0x11, - 0x48, 0x23, 0x14, 0x01, 0x90, 0x1f, 0x2d, 0x49, - 0x3f, 0x4c, 0x63, 0x65 -}; - -/** - * Predefined random bits from the RAND Corporation book - */ -static u_int8_t b[] = { - 0x75, 0xef, 0xd1, 0x5c, 0x4b, 0x8f, 0x8f, 0x51, - 0x4e, 0xf3, 0xbc, 0xc3, 0x79, 0x4a, 0x76, 0x5e, - 0x7e, 0xec, 0x45, 0xe0 -}; - -/** - * Multiplicate two mpz_t with bits interpreted as polynoms. - */ -static void mpz_mul_poly(mpz_t r, mpz_t a, mpz_t b) -{ - mpz_t bm, rm; - int current = 0, shifted = 0, shift; - - mpz_init_set(bm, b); - mpz_init_set_ui(rm, 0); - /* scan through a, for each found bit: */ - while ((current = mpz_scan1(a, current)) != ULONG_MAX) - { - /* XOR shifted b into r */ - shift = current - shifted; - mpz_mul_2exp(bm, bm, shift); - shifted += shift; - mpz_xor(rm, rm, bm); - current++; - } - - mpz_swap(r, rm); - mpz_clear(rm); - mpz_clear(bm); -} - -/** - * Calculate the sum of a + b interpreted as polynoms. - */ -static void mpz_add_poly(mpz_t res, mpz_t a, mpz_t b) -{ - /* addition of polynominals is just the XOR */ - mpz_xor(res, a, b); -} - -/** - * Calculate the remainder of a/b interpreted as polynoms. - */ -static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b) -{ - /* Example: - * a = 10001010 - * b = 00000101 - */ - int a_bit, b_bit, diff; - mpz_t bm, am; - - mpz_init_set(am, a); - mpz_init(bm); - - a_bit = mpz_sizeinbase(a, 2); - b_bit = mpz_sizeinbase(b, 2); - - /* don't do anything if b > a */ - if (a_bit >= b_bit) - { - /* shift b left to align up most signaficant "1" to a: - * a = 10001010 - * b = 10100000 - */ - mpz_mul_2exp(bm, b, a_bit - b_bit); - do - { - /* XOR b into a, this kills the most significant "1": - * a = 00101010 - */ - mpz_xor(am, am, bm); - /* find the next most significant "1" in a, and align up b: - * a = 00101010 - * b = 00101000 - */ - diff = a_bit - mpz_sizeinbase(am, 2); - mpz_div_2exp(bm, bm, diff); - a_bit -= diff; - } - while (b_bit <= mpz_sizeinbase(bm, 2)); - /* While b is not shifted to its original value */ - } - /* after another iteration: - * a = 00000010 - * which is the polynomial modulo - */ - - mpz_swap(r, am); - mpz_clear(am); - mpz_clear(bm); -} - -/** - * Step 4 of the various fx() functions: - * Polynomial whiten calculations - */ -static void step4(u_int8_t x[]) -{ - mpz_t xm, am, bm, gm; - - mpz_init(xm); - mpz_init(am); - mpz_init(bm); - mpz_init(gm); - - mpz_import(xm, HASH_SIZE_SHA1, 1, 1, 1, 0, x); - mpz_import(am, sizeof(a), 1, 1, 1, 0, a); - mpz_import(bm, sizeof(b), 1, 1, 1, 0, b); - mpz_import(gm, sizeof(g), 1, 1, 1, 0, g); - - mpz_mul_poly(xm, am, xm); - mpz_add_poly(xm, bm, xm); - mpz_mod_poly(xm, xm, gm); - - mpz_export(x, NULL, 1, HASH_SIZE_SHA1, 1, 0, xm); - - mpz_clear(xm); - mpz_clear(am); - mpz_clear(bm); - mpz_clear(gm); -} - -/** - * Step 3 of the various fx() functions: - * XOR the key into the SHA1 IV - */ -static void step3(chunk_t k, chunk_t payload, u_int8_t h[]) -{ - u_int8_t iv[] = { - 0x67,0x45,0x23,0x01,0xEF,0xCD,0xAB,0x89,0x98,0xBA, - 0xDC,0xFE,0x10,0x32,0x54,0x76,0xC3,0xD2,0xE1,0xF0, - }; - - /* XOR key into IV */ - memxor(iv, k.ptr, k.len); - - /* hash it with the G() function defined in FIPS 186-2 from fips_prf.h */ - g_sha1(iv, payload, h); -} - -/** - * Calculation function for f2(), f3(), f4() - */ -static void fx(u_int8_t f, chunk_t k, chunk_t rand, u_int8_t out[]) -{ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - u_int8_t i; - - for (i = 0; i < 2; i++) - { - memset(payload.ptr, 0x5c, payload.len); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 24, rand.ptr, rand.len); - - payload.ptr[3] ^= i; - payload.ptr[19] ^= i; - payload.ptr[35] ^= i; - payload.ptr[51] ^= i; - - step3(k, payload, h); - step4(h); - memcpy(out + i * 8, h, 8); - } -} - -/** - * Calculation function of f1() and f1star() - */ -static void f1x(u_int8_t f, chunk_t k, chunk_t rand, chunk_t sqn, - chunk_t amf, u_int8_t mac[]) -{ - /* generate MAC = f1(FMK, SQN, RAND, AMF) - * K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit - * payload which gets hashed - */ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - - memset(payload.ptr, 0x5c, PAYLOAD_LENGTH); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 16, rand.ptr, rand.len); - memxor(payload.ptr + 34, sqn.ptr, sqn.len); - memxor(payload.ptr + 42, amf.ptr, amf.len); - - step3(k, payload, h); - step4(h); - memcpy(mac, h, MAC_LENGTH); -} - -/** - * Calculation function of f5() and f5star() - */ -static void f5x(u_int8_t f, chunk_t k, chunk_t rand, u_int8_t ak[]) -{ - chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); - u_int8_t h[HASH_SIZE_SHA1]; - - memset(payload.ptr, 0x5c, payload.len); - payload.ptr[11] ^= f; - memxor(payload.ptr + 12, fmk.ptr, fmk.len); - memxor(payload.ptr + 16, rand.ptr, rand.len); - - step3(k, payload, h); - step4(h); - memcpy(ak, h, AK_LENGTH); -} - -/** - * Calculate the MAC from a RAND, SQN, AMF value using K - */ -static void f1(chunk_t k, chunk_t rand, chunk_t sqn, chunk_t amf, u_int8_t mac[]) -{ - f1x(F1, k, rand, sqn, amf, mac); - DBG3(DBG_IKE, "MAC %b", mac, MAC_LENGTH); -} - -/** - * Calculate the MACS from a RAND, SQN, AMF value using K - */ -static void f1star(chunk_t k, chunk_t rand, chunk_t sqn, chunk_t amf, u_int8_t macs[]) -{ - f1x(F1STAR, k, rand, sqn, amf, macs); - DBG3(DBG_IKE, "MACS %b", macs, MAC_LENGTH); -} - -/** - * Calculate RES from RAND using K - */ -static void f2(chunk_t k, chunk_t rand, u_int8_t res[]) -{ - fx(F2, k, rand, res); - DBG3(DBG_IKE, "RES %b", res, RES_LENGTH); -} - -/** - * Calculate CK from RAND using K - */ -static void f3(chunk_t k, chunk_t rand, u_int8_t ck[]) -{ - fx(F3, k, rand, ck); - DBG3(DBG_IKE, "CK %b", ck, CK_LENGTH); -} - -/** - * Calculate IK from RAND using K - */ -static void f4(chunk_t k, chunk_t rand, u_int8_t ik[]) -{ - fx(F4, k, rand, ik); - DBG3(DBG_IKE, "IK %b", ik, IK_LENGTH); -} - -/** - * Calculate AK from a RAND using K - */ -static void f5(chunk_t k, chunk_t rand, u_int8_t ak[]) -{ - f5x(F5, k, rand, ak); - DBG3(DBG_IKE, "AK %b", ak, AK_LENGTH); -} - -/** - * Calculate AKS from a RAND using K - */ -static void f5star(chunk_t k, chunk_t rand, u_int8_t aks[]) -{ - f5x(F5STAR, k, rand, aks); - DBG3(DBG_IKE, "AKS %b", aks, AK_LENGTH); -} - -/** - * derive the keys needed for EAP_AKA - */ -static void derive_keys(private_eap_aka_t *this, identification_t *id) -{ - hasher_t *hasher; - prf_t *prf; - chunk_t ck, ik, mk, identity, tmp; - - ck = chunk_alloca(CK_LENGTH); - ik = chunk_alloca(IK_LENGTH); - mk = chunk_alloca(MK_LENGTH); - identity = id->get_encoding(id); - - /* MK = SHA1( Identity | IK | CK ) */ - f3(this->k, this->rand, ck.ptr); - f4(this->k, this->rand, ik.ptr); - DBG3(DBG_IKE, "Identity %B", &identity); - tmp = chunk_cata("ccc", identity, ik, ck); - DBG3(DBG_IKE, "Identity|IK|CK %B", &tmp); - hasher = hasher_create(HASH_SHA1); - hasher->get_hash(hasher, tmp, mk.ptr); - hasher->destroy(hasher); - - /* K_encr | K_auth | MSK | EMSK = prf(0) | prf(0) - * FIPS PRF has 320 bit block size, we need 160 byte for keys - * => run prf four times */ - prf = prf_create(PRF_FIPS_SHA1_160); - prf->set_key(prf, mk); - tmp = chunk_alloca(prf->get_block_size(prf) * 4); - prf->get_bytes(prf, chunk_empty, tmp.ptr); - prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1); - prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2); - prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3); - prf->destroy(prf); - chunk_free(&this->k_encr); - chunk_free(&this->k_auth); - chunk_free(&this->msk); - chunk_free(&this->emsk); - chunk_split(tmp, "aaaa", 16, &this->k_encr, 16, &this->k_auth, - 64, &this->msk, 64, &this->emsk); - DBG3(DBG_IKE, "MK %B", &mk); - DBG3(DBG_IKE, "PRF res %B", &tmp); - DBG3(DBG_IKE, "K_encr %B", &this->k_encr); - DBG3(DBG_IKE, "K_auth %B", &this->k_auth); - DBG3(DBG_IKE, "MSK %B", &this->msk); - DBG3(DBG_IKE, "EMSK %B", &this->emsk); -} - -/* - * Get a shared key from ipsec.secrets. - * We use the standard keys as used in preshared key authentication. As - * these keys have an undefined length, we: - * - strip them if they are longer - * - fill them up with '\0' if they are shorter - */ -static status_t load_key(identification_t *me, identification_t *other, chunk_t *k) -{ - chunk_t shared_key; - - if (charon->credentials->get_eap_key(charon->credentials, me, - other, &shared_key) != SUCCESS) - { - return NOT_FOUND; - } - chunk_free(k); - *k = chunk_alloc(K_LENGTH); - memset(k->ptr, '\0', k->len); - memcpy(k->ptr, shared_key.ptr, min(shared_key.len, k->len)); - chunk_free(&shared_key); - return SUCCESS; -} - -/** - * skip EAP_AKA header in message and returns its AKA subtype - */ -static aka_subtype_t read_header(chunk_t *message) -{ - aka_subtype_t type; - - if (message->len < 8) - { - *message = chunk_empty; - return 0; - } - type = *(message->ptr + 5); - *message = chunk_skip(*message, 8); - return type; -} - -/** - * read the next attribute from the chunk data - */ -static aka_attribute_t read_attribute(chunk_t *data, chunk_t *attr_data) -{ - aka_attribute_t attribute; - size_t length; - - DBG3(DBG_IKE, "reading attribute from %B", data); - - if (data->len < 2) - { - return AT_END; - } - /* read attribute and length */ - attribute = *data->ptr++; - length = *data->ptr++ * 4 - 2; - data->len -= 2; - DBG3(DBG_IKE, "found attribute %N with length %d", - aka_attribute_names, attribute, length); - if (length > data->len) - { - return AT_END; - } - /* apply attribute value to attr_data */ - attr_data->len = length; - attr_data->ptr = data->ptr; - /* update data to point to next attribute */ - *data = chunk_skip(*data, length); - return attribute; -} - -/** - * Build an AKA payload from different attributes. - * The variable argument takes an aka_attribute_t - * followed by its data in a chunk. - */ -static eap_payload_t *build_aka_payload(private_eap_aka_t *this, eap_code_t code, - u_int8_t identifier, aka_subtype_t type, ...) -{ - chunk_t message = chunk_alloca(512); /* is enought for all current messages */ - chunk_t pos = message; - eap_payload_t *payload; - va_list args; - aka_attribute_t attr; - u_int8_t *mac_pos = NULL; - - /* write EAP header, skip length bytes */ - *pos.ptr++ = code; - *pos.ptr++ = identifier; - pos.ptr += 2; - pos.len -= 4; - /* write AKA header with type and subtype, null reserved bytes */ - *pos.ptr++ = EAP_AKA; - *pos.ptr++ = type; - *pos.ptr++ = 0; - *pos.ptr++ = 0; - pos.len -= 4; - - va_start(args, type); - while ((attr = va_arg(args, aka_attribute_t)) != AT_END) - { - chunk_t data = va_arg(args, chunk_t); - - DBG3(DBG_IKE, "building %N %B", aka_attribute_names, attr, &data); - - /* write attribute header */ - *pos.ptr++ = attr; - pos.len--; - - switch (attr) - { - case AT_RES: - { - /* attribute length in 4byte words */ - *pos.ptr = data.len/4 + 1; - pos = chunk_skip(pos, 1); - /* RES length in bits */ - *(u_int16_t*)pos.ptr = htons(data.len * 8); - pos = chunk_skip(pos, sizeof(u_int16_t)); - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - case AT_AUTN: - case AT_RAND: - { - *pos.ptr++ = data.len/4 + 1; pos.len--; - *pos.ptr++ = 0; pos.len--; - *pos.ptr++ = 0; pos.len--; - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - case AT_MAC: - { - *pos.ptr++ = 5; pos.len--; - *pos.ptr++ = 0; pos.len--; - *pos.ptr++ = 0; pos.len--; - mac_pos = pos.ptr; - /* MAC is calculated over message including zeroed AT_MAC attribute */ - memset(mac_pos, 0, AT_MAC_LENGTH); - pos.ptr += AT_MAC_LENGTH; - pos.len -= AT_MAC_LENGTH; - break; - } - default: - { - /* length is data length in 4-bytes + 1 for header */ - *pos.ptr = data.len/4 + 1; - pos = chunk_skip(pos, 1); - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - } - } - } - va_end(args); - - /* calculate message length, write into header */ - message.len = pos.ptr - message.ptr; - *(u_int16_t*)(message.ptr + 2) = htons(message.len); - - /* create MAC if AT_MAC attribte was included */ - if (mac_pos) - { - signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); - signer->set_key(signer, this->k_auth); - DBG3(DBG_IKE, "AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - signer->get_signature(signer, message, mac_pos); - DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LENGTH); - signer->destroy(signer); - } - - /* payload constructor takes data with some bytes skipped */ - payload = eap_payload_create_data(message); - - DBG3(DBG_IKE, "created EAP message %B", &message); - return payload; -} - -/** - * Initiate a AKA-Challenge using SQN - */ -static status_t server_initiate_challenge(private_eap_aka_t *this, chunk_t sqn, eap_payload_t **out) -{ - randomizer_t *randomizer; - status_t status; - chunk_t mac, ak, autn; - - mac = chunk_alloca(MAC_LENGTH); - ak = chunk_alloca(AK_LENGTH); - chunk_free(&this->rand); - chunk_free(&this->xres); - - /* generate RAND: - * we use our standard randomizer, not f0() proposed in S.S0055 - */ - randomizer = randomizer_create(); - status = randomizer->allocate_pseudo_random_bytes(randomizer, RAND_LENGTH, &this->rand); - randomizer->destroy(randomizer); - if (status != SUCCESS) - { - DBG1(DBG_IKE, "generating RAND for EAP-AKA authentication failed"); - return FAILED; - } - -# ifdef TEST_VECTORS - /* Test vector for RAND */ - u_int8_t test_rand[] = { - 0x4b,0x05,0x2b,0x20,0xe2,0xa0,0x6c,0x8f, - 0xf7,0x00,0xda,0x51,0x2b,0x4e,0x11,0x1e, - }; - memcpy(this->rand.ptr, test_rand, this->rand.len); -# endif /* TEST_VECTORS */ - - /* Get the shared key K: */ - if (load_key(this->server, this->peer, &this->k) != SUCCESS) - { - DBG1(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate " - "with EAP-AKA", this->server, this->peer); - return FAILED; - } - -# ifdef TEST_VECTORS - /* Test vector for K */ - u_int8_t test_k[] = { - 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, - 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, - }; - memcpy(this->k.ptr, test_k, this->k.len); -# endif /* TEST_VECTORS */ - - /* generate MAC */ - f1(this->k, this->rand, sqn, amf, mac.ptr); - - /* generate AK */ - f5(this->k, this->rand, ak.ptr); - - /* precalculate XRES as expected from client */ - this->xres = chunk_alloc(RES_LENGTH); - f2(this->k, this->rand, this->xres.ptr); - - /* calculate AUTN = (SQN xor AK) || AMF || MAC */ - autn = chunk_cata("ccc", sqn, amf, mac); - memxor(autn.ptr, ak.ptr, ak.len); - DBG3(DBG_IKE, "AUTN %B", &autn); - - - /* derive K_encr, K_auth, MSK, EMSK */ - derive_keys(this, this->peer); - - /* build payload */ - *out = build_aka_payload(this, EAP_REQUEST, 0, AKA_CHALLENGE, - AT_RAND, this->rand, AT_AUTN, autn, AT_MAC, - chunk_empty, AT_END); - return NEED_MORE; -} - -/** - * Implementation of eap_method_t.initiate for an EAP_AKA server - */ -static status_t server_initiate(private_eap_aka_t *this, eap_payload_t **out) -{ - chunk_t sqn = chunk_alloca(SQN_LENGTH); - - /* we use an offset of 3 minutes to tolerate clock inaccuracy - * without the need to synchronize sequence numbers */ - update_sqn(sqn.ptr, 180); - -# ifdef TEST_VECTORS - /* Test vector for SQN */ - u_int8_t test_sqn[] = {0x00,0x00,0x00,0x00,0x00,0x01}; - memcpy(sqn.ptr, test_sqn, sqn.len); -# endif /* TEST_VECTORS */ - - return server_initiate_challenge(this, sqn, out); -} - -static status_t server_process_synchronize(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t attr, auts = chunk_empty, pos, message, macs, xmacs, sqn, aks, amf; - u_int i; - - message = in->get_data(in); - pos = message; - read_header(&pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_AUTS: - auts = attr; - continue; - default: - if (attribute >= 0 && attribute <= 127) - { - DBG1(DBG_IKE, "found non skippable attribute %N", - aka_attribute_names, attribute); - return FAILED; - } - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - continue; - } - break; - } - - if (auts.len != AUTS_LENGTH) - { - DBG1(DBG_IKE, "synchronization request didn't contain useable AUTS"); - return FAILED; - } - - chunk_split(auts, "mm", SQN_LENGTH, &sqn, MAC_LENGTH, &macs); - aks = chunk_alloca(AK_LENGTH); - f5star(this->k, this->rand, aks.ptr); - /* decrypt serial number by XORing AKS */ - memxor(sqn.ptr, aks.ptr, aks.len); - - /* verify MACS */ - xmacs = chunk_alloca(MAC_LENGTH); - amf = chunk_alloca(AMF_LENGTH); - /* an AMF of zero is used for MACS calculation */ - memset(amf.ptr, 0, amf.len); - f1star(this->k, this->rand, sqn, amf, xmacs.ptr); - if (!chunk_equals(macs, xmacs)) - { - DBG1(DBG_IKE, "received MACS does not match XMACS"); - DBG3(DBG_IKE, "MACS %B XMACS %B", &macs, &xmacs); - return FAILED; - } - - /* retry the challenge with the received SQN + 1*/ - for (i = SQN_LENGTH - 1; i >= 0; i--) - { - if (++sqn.ptr[i] != 0) - { - break; - } - } - return server_initiate_challenge(this, sqn, out); -} - -/** - * process an AKA_Challenge response - */ -static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t *in) -{ - chunk_t attr, res = chunk_empty, at_mac = chunk_empty, pos, message; - - message = in->get_data(in); - pos = message; - read_header(&pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_RES: - res = attr; - if (attr.len == 2 + RES_LENGTH && - *(u_int16_t*)attr.ptr == htons(RES_LENGTH * 8)) - { - res = chunk_skip(attr, 2); - } - continue; - - case AT_MAC: - attr = chunk_skip(attr, 2); - at_mac = chunk_clonea(attr); - /* zero MAC in message for MAC verification */ - memset(attr.ptr, 0, attr.len); - continue; - default: - if (attribute >= 0 && attribute <= 127) - { - DBG1(DBG_IKE, "found non skippable attribute %N", - aka_attribute_names, attribute); - return FAILED; - } - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - continue; - } - break; - } - - /* verify EAP message MAC AT_MAC */ - { - bool valid; - signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); - signer->set_key(signer, this->k_auth); - DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - valid = signer->verify_signature(signer, message, at_mac); - signer->destroy(signer); - if (!valid) - { - DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed"); - return FAILED; - } - } - - /* compare received RES against stored precalculated XRES */ - if (!chunk_equals(res, this->xres)) - { - DBG1(DBG_IKE, "received RES does not match XRES"); - DBG3(DBG_IKE, "RES %Bb XRES %B", &res, &this->xres); - return FAILED; - } - return SUCCESS; -} - -/** - * Implementation of eap_method_t.process for EAP_AKA servers - */ -static status_t server_process(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message; - aka_subtype_t type; - - message = in->get_data(in); - type = read_header(&message); - - DBG3(DBG_IKE, "received EAP message %B", &message); - - switch (type) - { - case AKA_CHALLENGE: - { - return server_process_challenge(this, in); - } - case AKA_AUTHENTICATION_REJECT: - case AKA_CLIENT_ERROR: - { - DBG1(DBG_IKE, "received %N, authentication failed", - aka_subtype_names, type); - return FAILED; - } - case AKA_SYNCHRONIZATION_FAILURE: - { - DBG1(DBG_IKE, "received %N, retrying with received SQN", - aka_subtype_names, type); - return server_process_synchronize(this, in, out); - } - default: - DBG1(DBG_IKE, "received unknown AKA subtype %N, authentication failed", - aka_subtype_names, type); - return FAILED; - } -} - -/** - * Process an incoming AKA-Challenge client side - */ -static status_t peer_process_challenge(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t attr = chunk_empty; - chunk_t autn = chunk_empty, at_mac = chunk_empty; - chunk_t ak, sqn, sqn_ak, mac, xmac, res, amf, message, pos; - u_int8_t identifier; - - ak = chunk_alloca(AK_LENGTH); - xmac = chunk_alloca(MAC_LENGTH); - res = chunk_alloca(RES_LENGTH); - chunk_free(&this->rand); - - message = in->get_data(in); - pos = message; - read_header(&pos); - identifier = in->get_identifier(in); - - DBG3(DBG_IKE, "reading attributes from %B", &pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_RAND: - this->rand = chunk_clone(chunk_skip(attr, 2)); - continue; - case AT_AUTN: - autn = chunk_skip(attr, 2); - continue; - case AT_MAC: - attr = chunk_skip(attr, 2); - at_mac = chunk_clonea(attr); - /* set MAC in message to zero for own MAC verification */ - memset(attr.ptr, 0, attr.len); - continue; - default: - if (attribute >= 0 && attribute <= 127) - { - /* non skippable attribute, abort */ - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "found non skippable attribute %N, sending %N %d", - aka_attribute_names, attribute, - aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - continue; - } - break; - } - - if (this->rand.len != RAND_LENGTH || autn.len != AUTN_LENGTH) - { - /* required attributes wrong/not found, abort */ - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "could not find valid RAND/AUTN attribute, sending %N %d", - aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - - DBG3(DBG_IKE, "using autn %B", &autn); - /* split up AUTN = SQN xor AK | AMF | MAC */ - chunk_split(autn, "mmm", SQN_LENGTH, &sqn_ak, AMF_LENGTH, &amf, MAC_LENGTH, &mac); - - /* Get the shared key K: */ - chunk_free(&this->k); - if (load_key(this->peer, this->server, &this->k) != SUCCESS) - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_AUTHENTICATION_REJECT, AT_END); - DBG3(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate " - "with EAP-AKA, sending %N", this->peer, this->server, - aka_subtype_names, AKA_AUTHENTICATION_REJECT); - return NEED_MORE; - } - DBG3(DBG_IKE, "using K %B", &this->k); -# ifdef TEST_VECTORS - /* Test vector for K */ - u_int8_t test_k[] = { - 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, - 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, - }; - memcpy(this->k.ptr, test_k, this->k.len); -# endif /* TEST_VECTORS */ - - /* calculate anonymity key AK */ - f5(this->k, this->rand, ak.ptr); - DBG3(DBG_IKE, "using rand %B", &this->rand); - DBG3(DBG_IKE, "using ak %B", &ak); - /* XOR AK into SQN to decrypt it */ - - sqn = chunk_clonea(sqn_ak); - - DBG3(DBG_IKE, "using ak xor sqn %B", &sqn_ak); - memxor(sqn.ptr, ak.ptr, sqn.len); - DBG3(DBG_IKE, "using sqn %B", &sqn); - - /* calculate expected MAC and compare against received one */ - f1(this->k, this->rand, sqn, amf, xmac.ptr); - if (!chunk_equals(mac, xmac)) - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_AUTHENTICATION_REJECT, AT_END); - DBG1(DBG_IKE, "received MAC does not match XMAC, sending %N", - aka_subtype_names, AKA_AUTHENTICATION_REJECT); - DBG3(DBG_IKE, "MAC %B\nXMAC %B", &mac, &xmac); - return NEED_MORE; - } - -#if SEQ_CHECK - if (memcmp(peer_sqn.ptr, sqn.ptr, sqn.len) >= 0) - { - /* sequence number invalid. send AUTS */ - chunk_t auts, macs, aks, amf; - - macs = chunk_alloca(MAC_LENGTH); - aks = chunk_alloca(AK_LENGTH); - amf = chunk_alloca(AMF_LENGTH); - - /* AMF is set to zero in AKA_SYNCHRONIZATION_FAILURE */ - memset(amf.ptr, 0, amf.len); - /* AKS = f5*(RAND) */ - f5star(this->k, this->rand, aks.ptr); - /* MACS = f1*(RAND) */ - f1star(this->k, this->rand, peer_sqn, amf, macs.ptr); - /* AUTS = SQN xor AKS | MACS */ - memxor(aks.ptr, peer_sqn.ptr, aks.len); - auts = chunk_cata("cc", aks, macs); - - *out = build_aka_payload(this, EAP_RESPONSE, identifier, - AKA_SYNCHRONIZATION_FAILURE, - AT_AUTS, auts, AT_END); - DBG1(DBG_IKE, "received SQN invalid, sending %N", - aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE); - DBG3(DBG_IKE, "received SQN %B\ncurrent SQN %B", &sqn, &peer_sqn); - return NEED_MORE; - } -#endif /* SEQ_CHECK */ - - /* derive K_encr, K_auth, MSK, EMSK */ - derive_keys(this, this->peer); - - /* verify EAP message MAC AT_MAC */ - { - bool valid; - signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); - signer->set_key(signer, this->k_auth); - - DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); - DBG3(DBG_IKE, "using key %B", &this->k_auth); - valid = signer->verify_signature(signer, message, at_mac); - signer->destroy(signer); - if (!valid) - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "MAC in AT_MAC attribute verification " - "failed, sending %N %d", aka_attribute_names, - AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - } - - /* update stored SQN to the received one */ - memcpy(peer_sqn.ptr, sqn.ptr, sqn.len); - - /* calculate RES */ - f2(this->k, this->rand, res.ptr); - - /* build response */ - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CHALLENGE, - AT_RES, res, AT_MAC, chunk_empty, AT_END); - return NEED_MORE; -} - -/** - * Process an incoming AKA-Notification as client - */ -static status_t peer_process_notification(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message, pos, attr; - u_int8_t identifier; - - message = in->get_data(in); - pos = message; - read_header(&pos); - identifier = in->get_identifier(in); - - DBG3(DBG_IKE, "reading attributes from %B", &pos); - - /* iterate over attributes */ - while (TRUE) - { - aka_attribute_t attribute = read_attribute(&pos, &attr); - switch (attribute) - { - case AT_END: - break; - case AT_NOTIFICATION: - { - u_int16_t code; - - if (attr.len != 2) - { - DBG1(DBG_IKE, "received invalid AKA notification, ignored"); - continue; - } - code = ntohs(*(u_int16_t*)attr.ptr); - switch (code) - { - case 0: - DBG1(DBG_IKE, "received AKA notification 'general " - "failure after authentication' (%d)", code); - return FAILED; - case 16384: - DBG1(DBG_IKE, "received AKA notification 'general " - "failure' (%d)", code); - return FAILED; - case 32768: - DBG1(DBG_IKE, "received AKA notification 'successfully " - "authenticated' (%d)", code); - continue; - case 1026: - DBG1(DBG_IKE, "received AKA notification 'access " - "temporarily denied' (%d)", code); - return FAILED; - case 1031: - DBG1(DBG_IKE, "received AKA notification 'not " - "subscribed to service' (%d)", code); - return FAILED; - default: - DBG1(DBG_IKE, "received AKA notification code %d, " - "ignored", code); - continue; - } - } - default: - if (attribute >= 0 && attribute <= 127) - { - DBG1(DBG_IKE, "ignoring non-skippable attribute %N in %N", - aka_attribute_names, attribute, aka_subtype_names, - AKA_NOTIFICATION); - } - else - { - DBG1(DBG_IKE, "ignoring skippable attribute %N", - aka_attribute_names, attribute); - } - continue; - } - break; - } - return NEED_MORE; -} - -/** - * Implementation of eap_method_t.process for an EAP_AKA peer - */ -static status_t peer_process(private_eap_aka_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - aka_subtype_t type; - chunk_t message; - u_int8_t identifier; - - message = in->get_data(in); - type = read_header(&message); - identifier = in->get_identifier(in); - - DBG3(DBG_IKE, "received EAP message %B", &message); - - switch (type) - { - case AKA_CHALLENGE: - { - return peer_process_challenge(this, in, out); - } - case AKA_NOTIFICATION: - { - return peer_process_notification(this, in, out); - } - default: - { - *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_code, AT_END); - DBG1(DBG_IKE, "received unsupported %N request, sending %N %d", - aka_subtype_names, type, - aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); - return NEED_MORE; - } - } -} - -/** - * Implementation of eap_method_t.initiate for an EAP AKA peer - */ -static status_t peer_initiate(private_eap_aka_t *this, eap_payload_t **out) -{ - /* peer never initiates */ - return FAILED; -} - -/** - * Implementation of eap_method_t.get_type. - */ -static eap_type_t get_type(private_eap_aka_t *this, u_int32_t *vendor) -{ - *vendor = 0; - return EAP_AKA; -} - -/** - * Implementation of eap_method_t.get_msk. - */ -static status_t get_msk(private_eap_aka_t *this, chunk_t *msk) -{ - if (this->msk.ptr) - { - *msk = this->msk; - return SUCCESS; - } - return FAILED; -} - -/** - * Implementation of eap_method_t.is_mutual. - */ -static bool is_mutual(private_eap_aka_t *this) -{ - return TRUE; -} - -/** - * Implementation of eap_method_t.destroy. - */ -static void destroy(private_eap_aka_t *this) -{ - chunk_free(&this->k_encr); - chunk_free(&this->k_auth); - chunk_free(&this->msk); - chunk_free(&this->emsk); - chunk_free(&this->xres); - chunk_free(&this->k); - chunk_free(&this->rand); - free(this); -} - -/* - * Described in header. - */ -eap_aka_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer) -{ - private_eap_aka_t *this = malloc_thing(private_eap_aka_t); - - /* public functions */ - switch (role) - { - case EAP_SERVER: - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process; - break; - case EAP_PEER: - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process; - break; - default: - free(this); - return NULL; - } - this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; - this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; - this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; - this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; - - /* private data */ - this->server = server; - this->peer = peer; - this->k_encr = chunk_empty; - this->k_auth = chunk_empty; - this->msk = chunk_empty; - this->emsk = chunk_empty; - this->xres = chunk_empty; - this->k = chunk_empty; - this->rand = chunk_empty; - - return &this->public; -} diff --git a/src/charon/sa/authenticators/eap/eap_aka.h b/src/charon/sa/authenticators/eap/eap_aka.h deleted file mode 100644 index a886863be..000000000 --- a/src/charon/sa/authenticators/eap/eap_aka.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @file eap_aka.h - * - * @brief Interface of eap_aka_t. - * - */ - -/* - * Copyright (C) 2006 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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. - */ - -#ifndef EAP_AKA_H_ -#define EAP_AKA_H_ - -typedef struct eap_aka_t eap_aka_t; -typedef enum aka_subtype_t aka_subtype_t; -typedef enum aka_attribute_t aka_attribute_t; - -#include <sa/authenticators/eap/eap_method.h> - - -/** - * Subtypes of AKA messages - */ -enum aka_subtype_t { - AKA_CHALLENGE = 1, - AKA_AUTHENTICATION_REJECT = 2, - AKA_SYNCHRONIZATION_FAILURE = 4, - AKA_IDENTITY = 5, - AKA_NOTIFICATION = 12, - AKA_REAUTHENTICATION = 13, - AKA_CLIENT_ERROR = 14, -}; - -/** - * enum names for aka_subtype_t - */ -extern enum_name_t *aka_subtype_names; - -/** - * Attribute types in AKA messages - */ -enum aka_attribute_t { - /** defines the end of attribute list */ - AT_END = -1, - AT_RAND = 1, - AT_AUTN = 2, - AT_RES = 3, - AT_AUTS = 4, - AT_PADDING = 6, - AT_NONCE_MT = 7, - AT_PERMANENT_ID_REQ = 10, - AT_MAC = 11, - AT_NOTIFICATION = 12, - AT_ANY_ID_REQ = 13, - AT_IDENTITY = 14, - AT_VERSION_LIST = 15, - AT_SELECTED_VERSION = 16, - AT_FULLAUTH_ID_REQ = 17, - AT_COUNTER = 19, - AT_COUNTER_TOO_SMALL = 20, - AT_NONCE_S = 21, - AT_CLIENT_ERROR_CODE = 22, - AT_IV = 129, - AT_ENCR_DATA = 130, - AT_NEXT_PSEUDONYM = 132, - AT_NEXT_REAUTH_ID = 133, - AT_CHECKCODE = 134, - AT_RESULT_IND = 135, -}; - -/** - * enum names for aka_attribute_t - */ -extern enum_name_t *aka_attribute_names; - -/** check SEQ values as client for validity, disabled by default */ -#ifndef SEQ_CHECK -# define SEQ_CHECK 0 -#endif - -/** - * @brief Implementation of the eap_method_t interface using EAP-AKA. - * - * EAP-AKA uses 3rd generation mobile phone standard authentication - * mechanism for authentication. It is a mutual authentication - * mechanism which establishs a shared key and therefore supports EAP_ONLY - * authentication. This implementation follows the standard of the - * 3GPP2 (S.S0055) and not the one of 3GGP. - * The shared key used for authentication is from ipsec.secrets. The - * peers ID is used to query it. - * The AKA mechanism uses sequence numbers to detect replay attacks. The - * peer stores the sequence number normally in a USIM and accepts - * incremental sequence numbers (incremental for lifetime of the USIM). To - * prevent a complex sequence number management, this implementation uses - * a sequence number derived from time. It is initialized to the startup - * time of the daemon. As long as the (UTC) time of the system is not - * turned back while the daemon is not running, this method is secure. - * To enable time based SEQs, #define SEQ_CHECK as 1. Default is to accept - * any SEQ numbers. This allows an attacker to do replay attacks. But since - * the server has proven his identity via IKE, such an attack is only - * possible between server and AAA (if any). - * - * @b Constructors: - * - eap_aka_create() - * - eap_client_create() using eap_method EAP_AKA - * - * @ingroup eap - */ -struct eap_aka_t { - - /** - * Implemented eap_method_t interface. - */ - eap_method_t eap_method_interface; -}; - -/** - * @brief Creates the EAP method EAP-AKA. - * - * @param server ID of the EAP server - * @param peer ID of the EAP client - * @return eap_aka_t object - * - * @ingroup eap - */ -eap_aka_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer); - -#endif /* EAP_AKA_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_identity.c b/src/charon/sa/authenticators/eap/eap_identity.c deleted file mode 100644 index 12a8bf7cc..000000000 --- a/src/charon/sa/authenticators/eap/eap_identity.c +++ /dev/null @@ -1,135 +0,0 @@ -/** - * @file eap_identity.c - * - * @brief Implementation of eap_identity_t. - * - */ - -/* - * Copyright (C) 2007 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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 "eap_identity.h" - -#include <daemon.h> -#include <library.h> - -typedef struct private_eap_identity_t private_eap_identity_t; - -/** - * Private data of an eap_identity_t object. - */ -struct private_eap_identity_t { - - /** - * Public authenticator_t interface. - */ - eap_identity_t public; - - /** - * ID of the peer - */ - identification_t *peer; -}; - -/** - * Implementation of eap_method_t.process for the peer - */ -static status_t process(private_eap_identity_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t id, hdr; - - hdr = chunk_alloca(5); - id = this->peer->get_encoding(this->peer); - - *(hdr.ptr + 0) = EAP_RESPONSE; - *(hdr.ptr + 1) = in->get_identifier(in); - *(u_int16_t*)(hdr.ptr + 2) = htons(hdr.len + id.len); - *(hdr.ptr + 4) = EAP_IDENTITY; - - *out = eap_payload_create_data(chunk_cata("cc", hdr, id)); - return SUCCESS; - -} - -/** - * Implementation of eap_method_t.initiate for the peer - */ -static status_t initiate(private_eap_identity_t *this, eap_payload_t **out) -{ - /* peer never initiates */ - return FAILED; -} - -/** - * Implementation of eap_method_t.get_type. - */ -static eap_type_t get_type(private_eap_identity_t *this) -{ - return EAP_IDENTITY; -} - -/** - * Implementation of eap_method_t.get_msk. - */ -static status_t get_msk(private_eap_identity_t *this, chunk_t *msk) -{ - return FAILED; -} - -/** - * Implementation of eap_method_t.is_mutual. - */ -static bool is_mutual(private_eap_identity_t *this) -{ - return FALSE; -} - -/** - * Implementation of eap_method_t.destroy. - */ -static void destroy(private_eap_identity_t *this) -{ - free(this); -} - -/* - * Described in header. - */ -eap_identity_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer) -{ - private_eap_identity_t *this; - - if (role != EAP_PEER) - { - return NULL; - } - - this = malloc_thing(private_eap_identity_t); - - /* public functions */ - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; - this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type; - this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; - this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; - this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; - - /* private data */ - this->peer = peer; - - return &this->public; -} diff --git a/src/charon/sa/authenticators/eap/eap_identity.h b/src/charon/sa/authenticators/eap/eap_identity.h deleted file mode 100644 index 20f0f0b67..000000000 --- a/src/charon/sa/authenticators/eap/eap_identity.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file eap_identity.h - * - * @brief Interface of eap_identity_t. - * - */ - -/* - * Copyright (C) 2007 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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. - */ - -#ifndef EAP_IDENTITY_H_ -#define EAP_IDENTITY_H_ - -typedef struct eap_identity_t eap_identity_t; - -#include <sa/authenticators/eap/eap_method.h> - -/** - * @brief Implementation of the eap_method_t interface using EAP Identity. - * - * @b Constructors: - * - eap_identity_create() - * - eap_client_create() using eap_method EAP_IDENTITY - * - * @ingroup eap - */ -struct eap_identity_t { - - /** - * Implemented eap_method_t interface. - */ - eap_method_t eap_method_interface; -}; - -/** - * @brief Creates the EAP method EAP Identity. - * - * @param server ID of the EAP server - * @param peer ID of the EAP client - * @return eap_identity_t object - * - * @ingroup eap - */ -eap_identity_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer); - -#endif /* EAP_IDENTITY_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_manager.c b/src/charon/sa/authenticators/eap/eap_manager.c new file mode 100644 index 000000000..44d84156c --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_manager.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2008 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * $Id: eap_manager.c 3589 2008-03-13 14:14:44Z martin $ + */ + +#include "eap_manager.h" + +#include <pthread.h> + +#include <utils/linked_list.h> + +typedef struct private_eap_manager_t private_eap_manager_t; +typedef struct eap_entry_t eap_entry_t; + +/** + * EAP constructor entry + */ +struct eap_entry_t { + + /** + * EAP method type, vendor specific if vendor is set + */ + eap_type_t type; + + /** + * vendor ID, 0 for default EAP methods + */ + u_int32_t vendor; + + /** + * Role of the method returned by the constructor, EAP_SERVER or EAP_PEER + */ + eap_role_t role; + + /** + * constructor function to create instance + */ + eap_constructor_t constructor; +}; + +/** + * private data of eap_manager + */ +struct private_eap_manager_t { + + /** + * public functions + */ + eap_manager_t public; + + /** + * list of eap_entry_t's + */ + linked_list_t *methods; + + /** + * mutex to lock methods + */ + pthread_mutex_t mutex; +}; + +/** + * Implementation of eap_manager_t.add_method. + */ +static void add_method(private_eap_manager_t *this, eap_type_t type, + u_int32_t vendor, eap_role_t role, + eap_constructor_t constructor) +{ + eap_entry_t *entry = malloc_thing(eap_entry_t); + + entry->type = type; + entry->vendor = vendor; + entry->role = role; + entry->constructor = constructor; + + pthread_mutex_lock(&this->mutex); + this->methods->insert_last(this->methods, entry); + pthread_mutex_unlock(&this->mutex); +} + +/** + * Implementation of eap_manager_t.remove_method. + */ +static void remove_method(private_eap_manager_t *this, eap_constructor_t constructor) +{ + enumerator_t *enumerator; + eap_entry_t *entry; + + pthread_mutex_lock(&this->mutex); + enumerator = this->methods->create_enumerator(this->methods); + while (enumerator->enumerate(enumerator, &entry)) + { + if (constructor == entry->constructor) + { + this->methods->remove_at(this->methods, enumerator); + free(entry); + } + } + enumerator->destroy(enumerator); + pthread_mutex_unlock(&this->mutex); +} + +/** + * Implementation of eap_manager_t.create_instance. + */ +static eap_method_t* create_instance(private_eap_manager_t *this, + eap_type_t type, u_int32_t vendor, + eap_role_t role, identification_t *server, + identification_t *peer) +{ + enumerator_t *enumerator; + eap_entry_t *entry; + eap_method_t *method = NULL; + + pthread_mutex_lock(&this->mutex); + enumerator = this->methods->create_enumerator(this->methods); + while (enumerator->enumerate(enumerator, &entry)) + { + if (type == entry->type && vendor == entry->vendor && + role == entry->role) + { + method = entry->constructor(server, peer); + if (method) + { + break; + } + } + } + enumerator->destroy(enumerator); + pthread_mutex_unlock(&this->mutex); + return method; +} + +/** + * Implementation of 2008_t.destroy + */ +static void destroy(private_eap_manager_t *this) +{ + this->methods->destroy_function(this->methods, free); + free(this); +} + +/* + * see header file + */ +eap_manager_t *eap_manager_create() +{ + private_eap_manager_t *this = malloc_thing(private_eap_manager_t); + + this->public.add_method = (void(*)(eap_manager_t*, eap_type_t type, u_int32_t vendor, eap_role_t role, eap_constructor_t constructor))add_method; + this->public.remove_method = (void(*)(eap_manager_t*, eap_constructor_t constructor))remove_method; + this->public.create_instance = (eap_method_t*(*)(eap_manager_t*, eap_type_t type, u_int32_t vendor, eap_role_t role, identification_t*,identification_t*))create_instance; + this->public.destroy = (void(*)(eap_manager_t*))destroy; + + this->methods = linked_list_create(); + pthread_mutex_init(&this->mutex, NULL); + + return &this->public; +} + diff --git a/src/charon/sa/authenticators/eap/eap_manager.h b/src/charon/sa/authenticators/eap/eap_manager.h new file mode 100644 index 000000000..74bfa1f51 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_manager.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * $Id: eap_manager.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup eap_manager eap_manager + * @{ @ingroup eap + */ + +#ifndef EAP_MANAGER_H_ +#define EAP_MANAGER_H_ + +#include <sa/authenticators/eap/eap_method.h> + +typedef struct eap_manager_t eap_manager_t; + +/** + * The EAP manager manages all EAP implementations and creates instances. + * + * A plugin registers it's implemented EAP method at the manager by + * providing type and a contructor function. The manager then instanciates + * eap_method_t instances through the provided constructor to handle + * EAP authentication. + */ +struct eap_manager_t { + + /** + * Register a EAP method implementation. + * + * @param method vendor specific method, if vendor != 0 + * @param vendor vendor ID, 0 for non-vendor (default) EAP methods + * @param role EAP role of the registered method + * @param constructor constructor function, returns an eap_method_t + */ + void (*add_method)(eap_manager_t *this, eap_type_t type, u_int32_t vendor, + eap_role_t role, eap_constructor_t constructor); + + /** + * Unregister a EAP method implementation using it's constructor. + * + * @param constructor constructor function to remove, as added in add_method + */ + void (*remove_method)(eap_manager_t *this, eap_constructor_t constructor); + + /** + * Create a new EAP method instance. + * + * @param type type of the EAP method + * @param vendor vendor ID, 0 for non-vendor (default) EAP methods + * @param role role of EAP method, either EAP_SERVER or EAP_PEER + * @param server identity of the server + * @param peer identity of the peer (client) + * @return EAP method instance, NULL if no constructor found + */ + eap_method_t* (*create_instance)(eap_manager_t *this, eap_type_t type, + u_int32_t vendor, eap_role_t role, + identification_t *server, + identification_t *peer); + + /** + * Destroy a eap_manager instance. + */ + void (*destroy)(eap_manager_t *this); +}; + +/** + * Create a eap_manager instance. + */ +eap_manager_t *eap_manager_create(); + +#endif /* EAP_MANAGER_H_ @}*/ diff --git a/src/charon/sa/authenticators/eap/eap_md5.c b/src/charon/sa/authenticators/eap/eap_md5.c deleted file mode 100644 index 0ca9fc566..000000000 --- a/src/charon/sa/authenticators/eap/eap_md5.c +++ /dev/null @@ -1,282 +0,0 @@ -/** - * @file eap_md5.c - * - * @brief Implementation of eap_md5_t. - * - */ - -/* - * Copyright (C) 2007 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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 "eap_md5.h" - -#include <daemon.h> -#include <library.h> - -typedef struct private_eap_md5_t private_eap_md5_t; - -/** - * Private data of an eap_md5_t object. - */ -struct private_eap_md5_t { - - /** - * Public authenticator_t interface. - */ - eap_md5_t public; - - /** - * ID of the server - */ - identification_t *server; - - /** - * ID of the peer - */ - identification_t *peer; - - /** - * challenge sent by the server - */ - chunk_t challenge; - - /** - * EAP message identififier - */ - u_int8_t identifier; -}; - -typedef struct eap_md5_header_t eap_md5_header_t; - -/** - * packed eap MD5 header struct - */ -struct eap_md5_header_t { - /** EAP code (REQUEST/RESPONSE) */ - u_int8_t code; - /** unique message identifier */ - u_int8_t identifier; - /** length of whole message */ - u_int16_t length; - /** EAP type */ - u_int8_t type; - /** length of value (challenge) */ - u_int8_t value_size; - /** actual value */ - u_int8_t value[]; -} __attribute__((__packed__)); - -#define CHALLENGE_LEN 16 -#define PAYLOAD_LEN (CHALLENGE_LEN + sizeof(eap_md5_header_t)) - -/** - * Hash the challenge string, create response - */ -static status_t hash_challenge(private_eap_md5_t *this, chunk_t *response) -{ - chunk_t concat, secret; - hasher_t *hasher; - - if (charon->credentials->get_eap_key(charon->credentials, this->server, - this->peer, &secret) != SUCCESS) - { - DBG1(DBG_IKE, "no EAP key found for hosts '%D' - '%D'", - this->server, this->peer); - return NOT_FOUND; - } - concat = chunk_cata("cmc", chunk_from_thing(this->identifier), - secret, this->challenge); - hasher = hasher_create(HASH_MD5); - hasher->allocate_hash(hasher, concat, response); - hasher->destroy(hasher); - return SUCCESS; -} - -/** - * Implementation of eap_method_t.initiate for the peer - */ -static status_t initiate_peer(private_eap_md5_t *this, eap_payload_t **out) -{ - /* peer never initiates */ - return FAILED; -} - -/** - * Implementation of eap_method_t.initiate for the server - */ -static status_t initiate_server(private_eap_md5_t *this, eap_payload_t **out) -{ - randomizer_t *randomizer; - status_t status; - eap_md5_header_t *req; - - randomizer = randomizer_create(); - status = randomizer->allocate_pseudo_random_bytes(randomizer, CHALLENGE_LEN, - &this->challenge); - randomizer->destroy(randomizer); - if (status != SUCCESS) - { - return FAILED; - } - - req = alloca(PAYLOAD_LEN); - req->length = htons(PAYLOAD_LEN); - req->code = EAP_REQUEST; - req->identifier = this->identifier; - req->type = EAP_MD5; - req->value_size = this->challenge.len; - memcpy(req->value, this->challenge.ptr, this->challenge.len); - - *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN)); - return NEED_MORE; -} - -/** - * Implementation of eap_method_t.process for the peer - */ -static status_t process_peer(private_eap_md5_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t response; - chunk_t data; - eap_md5_header_t *req; - - this->identifier = in->get_identifier(in); - data = in->get_data(in); - this->challenge = chunk_clone(chunk_skip(data, 6)); - if (data.len < 6 || this->challenge.len < *(data.ptr + 5)) - { - DBG1(DBG_IKE, "received invalid EAP-MD5 message"); - return FAILED; - } - if (hash_challenge(this, &response) != SUCCESS) - { - return FAILED; - } - req = alloca(PAYLOAD_LEN); - req->length = htons(PAYLOAD_LEN); - req->code = EAP_RESPONSE; - req->identifier = this->identifier; - req->type = EAP_MD5; - req->value_size = response.len; - memcpy(req->value, response.ptr, response.len); - chunk_free(&response); - - *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN)); - return NEED_MORE; -} - -/** - * Implementation of eap_method_t.process for the server - */ -static status_t process_server(private_eap_md5_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t response, expected; - chunk_t data; - - if (this->identifier != in->get_identifier(in)) - { - DBG1(DBG_IKE, "received invalid EAP-MD5 message"); - return FAILED; - } - if (hash_challenge(this, &expected) != SUCCESS) - { - return FAILED; - } - data = in->get_data(in); - response = chunk_skip(data, 6); - - if (response.len < expected.len || - !memeq(response.ptr, expected.ptr, expected.len)) - { - chunk_free(&expected); - DBG1(DBG_IKE, "EAP-MD5 verification failed"); - return FAILED; - } - chunk_free(&expected); - return SUCCESS; -} - -/** - * Implementation of eap_method_t.get_type. - */ -static eap_type_t get_type(private_eap_md5_t *this, u_int32_t *vendor) -{ - *vendor = 0; - return EAP_MD5; -} - -/** - * Implementation of eap_method_t.get_msk. - */ -static status_t get_msk(private_eap_md5_t *this, chunk_t *msk) -{ - return FAILED; -} - -/** - * Implementation of eap_method_t.is_mutual. - */ -static bool is_mutual(private_eap_md5_t *this) -{ - return FALSE; -} - -/** - * Implementation of eap_method_t.destroy. - */ -static void destroy(private_eap_md5_t *this) -{ - chunk_free(&this->challenge); - free(this); -} - -/* - * Described in header. - */ -eap_md5_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer) -{ - private_eap_md5_t *this = malloc_thing(private_eap_md5_t); - - /* public functions */ - switch (role) - { - case EAP_SERVER: - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server; - break; - case EAP_PEER: - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer; - break; - default: - free(this); - return NULL; - } - this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; - this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; - this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; - this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; - - /* private data */ - this->peer = peer; - this->server = server; - this->challenge = chunk_empty; - this->identifier = random(); - - return &this->public; -} diff --git a/src/charon/sa/authenticators/eap/eap_md5.h b/src/charon/sa/authenticators/eap/eap_md5.h deleted file mode 100644 index 260210b59..000000000 --- a/src/charon/sa/authenticators/eap/eap_md5.h +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file eap_md5.h - * - * @brief Interface of eap_md5_t. - * - */ - -/* - * Copyright (C) 2007 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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. - */ - -#ifndef EAP_MD5_H_ -#define EAP_MD5_H_ - -typedef struct eap_md5_t eap_md5_t; - -#include <sa/authenticators/eap/eap_method.h> - -/** - * @brief Implementation of the eap_method_t interface using EAP-MD5 (CHAP). - * - * @b Constructors: - * - eap_md5_create() - * - eap_client_create() using eap_method EAP_MD5 - * - * @ingroup eap - */ -struct eap_md5_t { - - /** - * Implemented eap_method_t interface. - */ - eap_method_t eap_method_interface; -}; - -/** - * @brief Creates the EAP method EAP-MD5. - * - * @param server ID of the EAP server - * @param peer ID of the EAP client - * @return eap_md5_t object - * - * @ingroup eap - */ -eap_md5_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer); - -#endif /* EAP_MD5_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c index 7434ca2a1..5e2db5489 100644 --- a/src/charon/sa/authenticators/eap/eap_method.c +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -1,10 +1,3 @@ -/** - * @file eap_method.c - * - * @brief Generic constructor for eap_methods. - * - */ - /* * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,22 +11,12 @@ * 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. + * + * $Id: eap_method.c 3589 2008-03-13 14:14:44Z martin $ */ -#include <string.h> -#include <sys/stat.h> -#include <dirent.h> -#include <error.h> -#include <dlfcn.h> - #include "eap_method.h" -#include <daemon.h> -#include <library.h> -#include <utils/linked_list.h> -#include <utils/identification.h> - - ENUM_BEGIN(eap_type_names, EAP_IDENTITY, EAP_TOKEN_CARD, "EAP_IDENTITY", "EAP_NOTIFICATION", @@ -62,171 +45,3 @@ ENUM(eap_role_names, EAP_SERVER, EAP_PEER, "EAP_PEER", ); - -typedef struct module_entry_t module_entry_t; - -/** - * Representation of a loaded module: EAP type, library handle, constructor - */ -struct module_entry_t { - eap_type_t type; - u_int32_t vendor; - void *handle; - eap_constructor_t constructor; -}; - -/** List of module_entry_t's */ -static linked_list_t *modules = NULL; - -/** - * unload modules at daemon shutdown - */ -void eap_method_unload() -{ - if (modules) - { - module_entry_t *entry; - - while (modules->remove_last(modules, (void**)&entry) == SUCCESS) - { - DBG2(DBG_CFG, "unloaded module EAP module %d-%d", - entry->type, entry->vendor); - dlclose(entry->handle); - free(entry); - } - modules->destroy(modules); - modules = NULL; - } -} - -/** - * Load EAP modules at daemon startup - */ -void eap_method_load(char *directory) -{ - struct dirent* entry; - DIR* dir; - - eap_method_unload(); - modules = linked_list_create(); - - dir = opendir(directory); - if (dir == NULL) - { - DBG1(DBG_CFG, "error opening EAP modules directory %s", directory); - return; - } - - DBG1(DBG_CFG, "loading EAP modules from '%s'", directory); - - while ((entry = readdir(dir)) != NULL) - { - char file[256]; - module_entry_t module, *loaded_module; - eap_method_t *method; - identification_t *id; - char *ending; - - snprintf(file, sizeof(file), "%s/%s", directory, entry->d_name); - - ending = entry->d_name + strlen(entry->d_name) - 3; - if (ending <= entry->d_name || !streq(ending, ".so")) - { - /* skip anything which does not look like a library */ - DBG2(DBG_CFG, " skipping %s, doesn't look like a library", - entry->d_name); - continue; - } - - /* try to load the library */ - module.handle = dlopen(file, RTLD_LAZY); - if (module.handle == NULL) - { - DBG1(DBG_CFG, " opening EAP module %s failed: %s", entry->d_name, - dlerror()); - continue; - } - module.constructor = dlsym(module.handle, "eap_create"); - if (module.constructor == NULL) - { - DBG1(DBG_CFG, " EAP module %s has no eap_create() function, skipped", - entry->d_name); - dlclose(module.handle); - continue; - } - - /* get the type implemented in the method, create an instance for it */ - id = identification_create_from_string("john@doe.xyz"); - method = module.constructor(EAP_SERVER, id, id); - if (method == NULL) - { - method = module.constructor(EAP_PEER, id, id); - } - id->destroy(id); - if (method == NULL) - { - DBG1(DBG_CFG, " unable to create instance of EAP method %s, skipped", - entry->d_name); - dlclose(module.handle); - continue; - } - module.type = method->get_type(method, &module.vendor); - method->destroy(method); - - if (module.vendor) - { - DBG1(DBG_CFG, " loaded EAP method %d, vendor %d successfully from %s", - module.type, module.vendor, entry->d_name); - } - else - { - DBG1(DBG_CFG, " loaded EAP method %N successfully from %s", - eap_type_names, module.type, entry->d_name); - } - - loaded_module = malloc_thing(module_entry_t); - memcpy(loaded_module, &module, sizeof(module)); - modules->insert_last(modules, loaded_module); - } - closedir(dir); -} - -/* - * Described in header. - */ -eap_method_t *eap_method_create(eap_type_t type, u_int32_t vendor, eap_role_t role, - identification_t *server, identification_t *peer) -{ - eap_method_t *method = NULL; - iterator_t *iterator; - module_entry_t *entry; - - iterator = modules->create_iterator(modules, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) - { - if (entry->type == type && entry->vendor == vendor) - { - method = entry->constructor(role, server, peer); - if (method) - { - break; - } - } - } - iterator->destroy(iterator); - - if (method == NULL) - { - if (vendor) - { - DBG1(DBG_CFG, "no vendor %d specific EAP module found for method " - "%d %N", vendor, type, eap_role_names, role); - } - else - { - DBG1(DBG_CFG, "no EAP module found for %N %N", - eap_type_names, type, eap_role_names, role); - } - } - return method; -} diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h index 8675fd8ec..eda6f545e 100644 --- a/src/charon/sa/authenticators/eap/eap_method.h +++ b/src/charon/sa/authenticators/eap/eap_method.h @@ -1,10 +1,3 @@ -/** - * @file eap_method.h - * - * @brief Interface eap_method_t. - * - */ - /* * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: eap_method.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup eap_method eap_method + * @{ @ingroup eap */ #ifndef EAP_METHOD_H_ @@ -34,8 +34,6 @@ typedef enum eap_code_t eap_code_t; /** * Role of an eap_method, SERVER or PEER (client) - * - * @ingroup eap */ enum eap_role_t { EAP_SERVER, @@ -43,15 +41,11 @@ enum eap_role_t { }; /** * enum names for eap_role_t. - * - * @ingroup eap */ extern enum_name_t *eap_role_names; /** * EAP types, defines the EAP method implementation - * - * @ingroup eap */ enum eap_type_t { EAP_IDENTITY = 1, @@ -68,15 +62,11 @@ enum eap_type_t { /** * enum names for eap_type_t. - * - * @ingroup eap */ extern enum_name_t *eap_type_names; /** * EAP code, type of an EAP message - * - * @ingroup eap */ enum eap_code_t { EAP_REQUEST = 1, @@ -87,14 +77,12 @@ enum eap_code_t { /** * enum names for eap_code_t. - * - * @ingroup eap */ extern enum_name_t *eap_code_names; /** - * @brief Interface of an EAP method for server and client side. + * Interface of an EAP method for server and client side. * * An EAP method initiates an EAP exchange and processes requests and * responses. An EAP method may need multiple exchanges before succeeding, and @@ -107,22 +95,16 @@ extern enum_name_t *eap_code_names; * authentication. Even if a mutual EAP method is used, the traditional * AUTH payloads are required. Only these include the nonces and messages from * ike_sa_init and therefore prevent man in the middle attacks. - * - * @b Constructors: - * - eap_method_create() - * - * @ingroup eap */ struct eap_method_t { /** - * @brief Initiate the EAP exchange. + * Initiate the EAP exchange. * * initiate() is only useable for server implementations, as clients only * reply to server requests. * A eap_payload is created in "out" if result is NEED_MORE. * - * @param this calling object * @param out eap_payload to send to the client * @return * - NEED_MORE, if an other exchange is required @@ -131,11 +113,10 @@ struct eap_method_t { status_t (*initiate) (eap_method_t *this, eap_payload_t **out); /** - * @brief Process a received EAP message. + * Process a received EAP message. * * A eap_payload is created in "out" if result is NEED_MORE. * - * @param this calling object * @param in eap_payload response received * @param out created eap_payload to send * @return @@ -147,31 +128,28 @@ struct eap_method_t { eap_payload_t **out); /** - * @brief Get the EAP type implemented in this method. + * Get the EAP type implemented in this method. * - * @param this calling object * @param vendor pointer receiving vendor identifier for type, 0 for none * @return type of the EAP method */ eap_type_t (*get_type) (eap_method_t *this, u_int32_t *vendor); /** - * @brief Check if this EAP method authenticates the server. + * Check if this EAP method authenticates the server. * * Some EAP methods provide mutual authentication and * allow authentication using only EAP, if the peer supports it. * - * @param this calling object * @return TRUE if methods provides mutual authentication */ bool (*is_mutual) (eap_method_t *this); /** - * @brief Get the MSK established by this EAP method. + * Get the MSK established by this EAP method. * * Not all EAP methods establish a shared secret. * - * @param this calling object * @param msk chunk receiving internal stored MSK * @return * - SUCCESS, or @@ -180,68 +158,25 @@ struct eap_method_t { status_t (*get_msk) (eap_method_t *this, chunk_t *msk); /** - * @brief Destroys a eap_method_t object. - * - * @param this calling object + * Destroys a eap_method_t object. */ void (*destroy) (eap_method_t *this); }; /** - * @brief Creates an EAP method for a specific type and role. - * - * @param eap_type EAP type to use - * @param eap_vendor vendor identifier if a vendor specifc EAP type is used - * @param role role of the eap_method, server or peer - * @param server ID of acting server - * @param peer ID of involved peer (client) - * @return eap_method_t object - * - * @ingroup eap - */ -eap_method_t *eap_method_create(eap_type_t eap_type, u_int32_t eap_vendor, - eap_role_t role, identification_t *server, - identification_t *peer); - -/** - * @brief (Re-)Load all EAP modules in the EAP modules directory. - * - * For security reasons, the directory and all it's modules must be owned - * by root and must not be writeable by someone else. - * - * @param dir directory of the EAP modules - * - * @ingroup eap - */ -void eap_method_load(char *directory); - -/** - * @brief Unload all loaded EAP modules - * - * @ingroup eap - */ -void eap_method_unload(); - -/** - * @brief Constructor definition for a pluggable EAP module. + * Constructor definition for a pluggable EAP method. * * Each EAP module must define a constructor function which will return - * an initialized object with the methods defined in eap_method_t. The - * constructor must be named eap_create() and it's signature must be equal - * to that of eap_constructor_t. - * A module may implement only a single role. If it does not support the role - * requested, NULL should be returned. Multiple modules are allowed of the - * same EAP type to support seperate implementations of peer/server. + * an initialized object with the methods defined in eap_method_t. + * Constructors for server and peers are identical, to support both roles + * of a EAP method, a plugin needs register two constructors in the + * eap_manager_t. * - * @param role role the module will play, peer or server * @param server ID of the server to use for credential lookup * @param peer ID of the peer to use for credential lookup * @return implementation of the eap_method_t interface - * - * @ingroup eap */ -typedef eap_method_t *(*eap_constructor_t)(eap_role_t role, - identification_t *server, +typedef eap_method_t *(*eap_constructor_t)(identification_t *server, identification_t *peer); -#endif /* EAP_METHOD_H_ */ +#endif /* EAP_METHOD_H_ @} */ diff --git a/src/charon/sa/authenticators/eap/eap_sim.c b/src/charon/sa/authenticators/eap/eap_sim.c deleted file mode 100644 index 90898fb46..000000000 --- a/src/charon/sa/authenticators/eap/eap_sim.c +++ /dev/null @@ -1,1125 +0,0 @@ -/** - * @file eap_sim.c - * - * @brief Implementation of eap_sim_t. - * - */ - -/* - * Copyright (C) 2007 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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 "eap_sim.h" - -#include <dlfcn.h> - -#include <daemon.h> -#include <library.h> - -#define MAX_TRIES 3 - -/* number of triplets for one authentication */ -#define TRIPLET_COUNT 3 - -typedef enum sim_subtype_t sim_subtype_t; - -/** - * Subtypes of SIM messages - */ -enum sim_subtype_t { - SIM_START = 10, - SIM_CHALLENGE = 11, - SIM_NOTIFICATION = 12, - SIM_CLIENT_ERROR = 14, -}; - -ENUM(sim_subtype_names, SIM_START, SIM_CLIENT_ERROR, - "SIM_START", - "SIM_CHALLENGE", - "SIM_NOTIFICATION", - "SIM_13", - "SIM_CLIENT_ERROR", -); - -typedef enum sim_attribute_t sim_attribute_t; - -/** - * Attributes in SIM messages - */ -enum sim_attribute_t { - /** defines the end of attribute list */ - AT_END = -1, - AT_RAND = 1, - AT_AUTN = 2, - AT_RES = 3, - AT_AUTS = 4, - AT_PADDING = 6, - AT_NONCE_MT = 7, - AT_PERMANENT_ID_REQ = 10, - AT_MAC = 11, - AT_NOTIFICATION = 12, - AT_ANY_ID_REQ = 13, - AT_IDENTITY = 14, - AT_VERSION_LIST = 15, - AT_SELECTED_VERSION = 16, - AT_FULLAUTH_ID_REQ = 17, - AT_COUNTER = 19, - AT_COUNTER_TOO_SMALL = 20, - AT_NONCE_S = 21, - AT_CLIENT_ERROR_CODE = 22, - AT_IV = 129, - AT_ENCR_DATA = 130, - AT_NEXT_PSEUDONYM = 132, - AT_NEXT_REAUTH_ID = 133, - AT_CHECKCODE = 134, - AT_RESULT_IND = 135, -}; - -ENUM_BEGIN(sim_attribute_names, AT_END, AT_CLIENT_ERROR_CODE, - "AT_END", - "AT_0", - "AT_RAND", - "AT_AUTN", - "AT_RES", - "AT_AUTS", - "AT_5", - "AT_PADDING", - "AT_NONCE_MT", - "AT_8", - "AT_9", - "AT_PERMANENT_ID_REQ", - "AT_MAC", - "AT_NOTIFICATION", - "AT_ANY_ID_REQ", - "AT_IDENTITY", - "AT_VERSION_LIST", - "AT_SELECTED_VERSION", - "AT_FULLAUTH_ID_REQ", - "AT_18", - "AT_COUNTER", - "AT_COUNTER_TOO_SMALL", - "AT_NONCE_S", - "AT_CLIENT_ERROR_CODE"); -ENUM_NEXT(sim_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE, - "AT_IV", - "AT_ENCR_DATA", - "AT_131", - "AT_NEXT_PSEUDONYM", - "AT_NEXT_REAUTH_ID", - "AT_CHECKCODE", - "AT_RESULT_IND"); -ENUM_END(sim_attribute_names, AT_RESULT_IND); - - -typedef struct private_eap_sim_t private_eap_sim_t; - -/** - * Private data of an eap_sim_t object. - */ -struct private_eap_sim_t { - - /** - * Public authenticator_t interface. - */ - eap_sim_t public; - - /** - * ID of ourself - */ - identification_t *peer; - - /** - * SIM cardreader function loaded from library - */ - sim_algo_t alg; - - /** - * libraries get_triplet() function returning a triplet - */ - sim_get_triplet_t get_triplet; - - /** - * handle of the loaded library - */ - void *handle; - - /** - * how many times we try to authenticate - */ - int tries; - - /** - * unique EAP identifier - */ - u_int8_t identifier; - - /** - * EAP message type this role sends - */ - u_int8_t type; - - /** - * version this implementation uses - */ - chunk_t version; - - /** - * version list received from server - */ - chunk_t version_list; - - /** - * Nonce value used in AT_NONCE_MT - */ - chunk_t nonce; - - /** - * concatenated SRES values - */ - chunk_t sreses; - - /** - * k_encr key derived from MK - */ - chunk_t k_encr; - - /** - * k_auth key derived from MK, used for AT_MAC verification - */ - chunk_t k_auth; - - /** - * MSK, used for EAP-SIM based IKEv2 authentication - */ - chunk_t msk; - - /** - * EMSK, extendes MSK for further uses - */ - chunk_t emsk; -}; - -/** length of the AT_NONCE_MT nonce value */ -#define NONCE_LEN 16 -/** length of the AT_MAC value */ -#define MAC_LEN 16 -/** length of the AT_RAND value */ -#define RAND_LEN 16 -/** length of Kc */ -#define KC_LEN 8 -/** length of SRES */ -#define SRES_LEN 4 -/** length of the k_encr key */ -#define KENCR_LEN 16 -/** length of the k_auth key */ -#define KAUTH_LEN 16 -/** length of the MSK */ -#define MSK_LEN 64 -/** length of the EMSK */ -#define EMSK_LEN 64 - -static char version[] = {0x00,0x01}; -/* client error codes used in AT_CLIENT_ERROR_CODE */ -char client_error_general_buf[] = {0x00, 0x01}; -char client_error_unsupported_buf[] = {0x00, 0x02}; -char client_error_insufficient_buf[] = {0x00, 0x03}; -char client_error_notfresh_buf[] = {0x00, 0x04}; -chunk_t client_error_general = chunk_from_buf(client_error_general_buf); -chunk_t client_error_unsupported = chunk_from_buf(client_error_unsupported_buf); -chunk_t client_error_insufficient = chunk_from_buf(client_error_insufficient_buf); -chunk_t client_error_notfresh = chunk_from_buf(client_error_notfresh_buf); - -/** - * Read EAP and EAP-SIM header, return SIM type - */ -static sim_subtype_t read_header(chunk_t *message) -{ - sim_subtype_t type; - - if (message->len < 8) - { - *message = chunk_empty; - return 0; - } - type = *(message->ptr + 5); - *message = chunk_skip(*message, 8); - return type; -} - -/** - * read the next attribute from the chunk data - */ -static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data) -{ - sim_attribute_t attribute; - size_t length; - - DBG3(DBG_IKE, "reading attribute from %B", message); - - if (message->len < 2) - { - return AT_END; - } - attribute = *message->ptr++; - length = *message->ptr++ * 4 - 2; - message->len -= 2; - DBG3(DBG_IKE, "found attribute %N with length %d", - sim_attribute_names, attribute, length); - - if (length > message->len) - { - return AT_END; - } - data->len = length; - data->ptr = message->ptr; - *message = chunk_skip(*message, length); - return attribute; -} - -/** - * Build an EAP-SIM payload using a variable length attribute list. - * The variable argument takes a sim_attribute_t followed by its data in a chunk. - */ -static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier, - sim_subtype_t type, ...) -{ - chunk_t message = chunk_alloca(512); - chunk_t pos = message; - eap_payload_t *payload; - va_list args; - sim_attribute_t attr; - u_int8_t *mac_pos = NULL; - chunk_t mac_data = chunk_empty; - - /* write EAP header, skip length bytes */ - *pos.ptr++ = this->type; - *pos.ptr++ = identifier; - pos.ptr += 2; - pos.len -= 4; - /* write SIM header with type and subtype, zero reserved bytes */ - *pos.ptr++ = EAP_SIM; - *pos.ptr++ = type; - *pos.ptr++ = 0; - *pos.ptr++ = 0; - pos.len -= 4; - - va_start(args, type); - while ((attr = va_arg(args, sim_attribute_t)) != AT_END) - { - chunk_t data = va_arg(args, chunk_t); - - DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data); - - /* write attribute header */ - *pos.ptr++ = attr; - pos.len--; - - switch (attr) - { - case AT_CLIENT_ERROR_CODE: - case AT_SELECTED_VERSION: - { - *pos.ptr = data.len/4 + 1; - pos = chunk_skip(pos, 1); - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - case AT_IDENTITY: - case AT_VERSION_LIST: - { - u_int16_t act_len = data.len; - /* align up to four byte */ - if (data.len % 4) - { - chunk_t tmp = chunk_alloca((data.len/4)*4 + 4); - memset(tmp.ptr, 0, tmp.len); - memcpy(tmp.ptr, data.ptr, data.len); - data = tmp; - } - *pos.ptr = data.len/4 + 1; - pos = chunk_skip(pos, 1); - /* actual length in bytes */ - *(u_int16_t*)pos.ptr = htons(act_len); - pos = chunk_skip(pos, sizeof(u_int16_t)); - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - case AT_NONCE_MT: - { - *pos.ptr = data.len/4 + 1; - pos = chunk_skip(pos, 1); - memset(pos.ptr, 0, 2); - pos = chunk_skip(pos, 2); - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - case AT_MAC: - { - *pos.ptr++ = 5; pos.len--; - *pos.ptr++ = 0; pos.len--; - *pos.ptr++ = 0; pos.len--; - mac_pos = pos.ptr; - memset(mac_pos, 0, MAC_LEN); - pos = chunk_skip(pos, MAC_LEN); - mac_data = data; - break; - } - case AT_RAND: - { - *pos.ptr++ = data.len/4 + 1; pos.len--; - *pos.ptr++ = 0; pos.len--; - *pos.ptr++ = 0; pos.len--; - memcpy(pos.ptr, data.ptr, data.len); - pos = chunk_skip(pos, data.len); - break; - } - default: - DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped", - sim_attribute_names, attr); - break; - } - } - va_end(args); - - /* calculate message length, write into header */ - message.len = pos.ptr - message.ptr; - *(u_int16_t*)(message.ptr + 2) = htons(message.len); - - /* create MAC if AT_MAC attribte was included. Append supplied va_arg - * chunk mac_data to "to-sign" chunk */ - if (mac_pos) - { - signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); - signer->set_key(signer, this->k_auth); - mac_data = chunk_cata("cc", message, mac_data); - signer->get_signature(signer, mac_data, mac_pos); - DBG3(DBG_IKE, "AT_MAC signature of %B\n is %b", - &mac_data, mac_pos, MAC_LEN); - signer->destroy(signer); - } - - payload = eap_payload_create_data(message); - - DBG3(DBG_IKE, "created EAP message %B", &message); - return payload; -} - -/** - * process an EAP-SIM/Request/Start message - */ -static status_t peer_process_start(private_eap_sim_t *this, eap_payload_t *in, - eap_payload_t **out) -{ - chunk_t message, data; - sim_attribute_t attribute, include_id = AT_END; - u_int8_t identifier; - - identifier = in->get_identifier(in); - message = in->get_data(in); - read_header(&message); - - while ((attribute = read_attribute(&message, &data)) != AT_END) - { - switch (attribute) - { - case AT_VERSION_LIST: - { - /* check if server supports our implementation */ - bool found = FALSE; - if (data.len > 2) - { - /* read actual length first */ - data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2); - data = chunk_skip(data, 2); - chunk_free(&this->version_list); - this->version_list = chunk_clone(data); - while (data.len >= this->version.len) - { - if (memeq(data.ptr, this->version.ptr, this->version.len)) - { - found = TRUE; - break; - } - data = chunk_skip(data, this->version.len); - } - } - if (!found) - { - DBG1(DBG_IKE, "server does not support EAP_SIM " - "version number %#B", &this->version); - *out = build_payload(this, identifier, SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_unsupported, - AT_END); - return NEED_MORE; - } - break; - } - case AT_PERMANENT_ID_REQ: - case AT_FULLAUTH_ID_REQ: - case AT_ANY_ID_REQ: - /* only include AT_IDENTITY if requested */ - include_id = AT_IDENTITY; - break; - case AT_NOTIFICATION: - { - u_int16_t code = 0; - if (data.len == 2) - { - code = ntohs(*(u_int16_t*)data.ptr); - } - if (code <= 32767) /* no success bit */ - { - DBG1(DBG_IKE, "received %N error %d", - sim_attribute_names, attribute, code); - *out = build_payload(this, - in->get_identifier(in), SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_general, - AT_END); - return NEED_MORE; - } - else - { - DBG1(DBG_IKE, "received %N code %d", - sim_attribute_names, attribute, code); - } - break; - } - default: - DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", - sim_attribute_names, attribute); - break; - } - } - - /* build payload. If "include_id" is AT_END, AT_IDENTITY is ommited */ - *out = build_payload(this, identifier, SIM_START, - AT_SELECTED_VERSION, this->version, - AT_NONCE_MT, this->nonce, - include_id, this->peer->get_encoding(this->peer), - AT_END); - return NEED_MORE; -} - -/** - * derive EAP keys from kc - */ -static void derive_keys(private_eap_sim_t *this, chunk_t kcs) -{ - chunk_t tmp, mk; - hasher_t *hasher; - prf_t *prf; - int i; - - /* build MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ - tmp = chunk_cata("ccccc", this->peer->get_encoding(this->peer), kcs, - this->nonce, this->version_list, this->version); - hasher = hasher_create(HASH_SHA1); - mk = chunk_alloca(hasher->get_hash_size(hasher)); - hasher->get_hash(hasher, tmp, mk.ptr); - hasher->destroy(hasher); - DBG3(DBG_IKE, "MK = SHA1(%B\n) = %B", &tmp, &mk); - - /* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf() - * FIPS PRF has 320 bit block size, we need 160 byte for keys - * => run prf four times */ - prf = prf_create(PRF_FIPS_SHA1_160); - prf->set_key(prf, mk); - tmp = chunk_alloca(prf->get_block_size(prf) * 4); - for (i = 0; i < 4; i++) - { - prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * i); - } - prf->destroy(prf); - chunk_free(&this->k_encr); - chunk_free(&this->k_auth); - chunk_free(&this->msk); - chunk_free(&this->emsk); - chunk_split(tmp, "aaaa", KENCR_LEN, &this->k_encr, KAUTH_LEN, &this->k_auth, - MSK_LEN, &this->msk, EMSK_LEN, &this->emsk); - DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B\nEMSK %B", - &this->k_encr, &this->k_auth, &this->msk, &this->emsk); -} - -/** - * process an EAP-SIM/Request/Challenge message - */ -static status_t peer_process_challenge(private_eap_sim_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message, data, tmp, kcs, kc, sreses, sres; - sim_attribute_t attribute; - u_int8_t identifier; - chunk_t mac = chunk_empty, rands = chunk_empty; - signer_t *signer; - - if (this->tries-- <= 0) - { - /* give up without notification. This hack is required as some buggy - * server implementations won't respect our client-error. */ - return FAILED; - } - - identifier = in->get_identifier(in); - message = in->get_data(in); - read_header(&message); - - while ((attribute = read_attribute(&message, &data)) != AT_END) - { - switch (attribute) - { - case AT_RAND: - { - rands = chunk_skip(data, 2); - break; - } - case AT_MAC: - { - /* backup MAC, zero it inline for later verification */ - data = chunk_skip(data, 2); - mac = chunk_clonea(data); - memset(data.ptr, 0, data.len); - break; - } - case AT_NOTIFICATION: - { - u_int16_t code = 0; - if (data.len == 2) - { - code = ntohs(*(u_int16_t*)data.ptr); - } - if (code <= 32767) /* no success bit */ - { - DBG1(DBG_IKE, "received %N error %d", - sim_attribute_names, attribute, code); - *out = build_payload(this, - in->get_identifier(in), SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_general, - AT_END); - return NEED_MORE; - } - else - { - DBG1(DBG_IKE, "received %N code %d", - sim_attribute_names, attribute, code); - } - break; - } - default: - DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", - sim_attribute_names, attribute); - break; - } - } - - /* excepting two or three RAND, each 16 bytes. We require two valid - * and different RANDs */ - if ((rands.len != 2 * RAND_LEN && rands.len != 3 * RAND_LEN) || - memeq(rands.ptr, rands.ptr + RAND_LEN, RAND_LEN)) - { - DBG1(DBG_IKE, "no valid AT_RAND received"); - *out = build_payload(this, identifier, SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_insufficient, - AT_END); - return NEED_MORE; - } - if (mac.len != MAC_LEN) - { - DBG1(DBG_IKE, "no valid AT_MAC received"); - *out = build_payload(this, identifier, SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_general, - AT_END); - return NEED_MORE; - } - - /* get two or three KCs/SRESes from SIM using RANDs */ - kcs = kc = chunk_alloca(rands.len / 2); - sreses = sres = chunk_alloca(rands.len / 4); - while (rands.len > 0) - { - int kc_len = kc.len, sres_len = sres.len; - - if (this->alg(rands.ptr, RAND_LEN, sres.ptr, &sres_len, kc.ptr, &kc_len)) - { - DBG1(DBG_IKE, "unable to get EAP-SIM triplet"); - *out = build_payload(this, identifier, SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_general, - AT_END); - return NEED_MORE; - } - DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b", - rands.ptr, RAND_LEN, sres.ptr, sres_len, kc.ptr, kc_len); - kc = chunk_skip(kc, kc_len); - sres = chunk_skip(sres, sres_len); - rands = chunk_skip(rands, RAND_LEN); - } - - derive_keys(this, kcs); - - /* verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT" */ - signer = signer_create(AUTH_HMAC_SHA1_128); - signer->set_key(signer, this->k_auth); - tmp = chunk_cata("cc", in->get_data(in), this->nonce); - if (!signer->verify_signature(signer, tmp, mac)) - { - DBG1(DBG_IKE, "AT_MAC verification failed"); - signer->destroy(signer); - *out = build_payload(this, identifier, SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_general, - AT_END); - return NEED_MORE; - } - signer->destroy(signer); - - /* build response, AT_MAC is built over "EAP packet | n*SRES" */ - *out = build_payload(this, identifier, SIM_CHALLENGE, - AT_MAC, sreses, - AT_END); - return NEED_MORE; -} - -/** - * process an EAP-SIM/Response/Challenge message - */ -static status_t server_process_challenge(private_eap_sim_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message, data; - sim_attribute_t attribute; - chunk_t mac = chunk_empty, tmp; - signer_t *signer; - - message = in->get_data(in); - read_header(&message); - - while ((attribute = read_attribute(&message, &data)) != AT_END) - { - switch (attribute) - { - case AT_MAC: - /* MAC has two reserved bytes */ - if (data.len == MAC_LEN + 2) - { /* clone and zero MAC for verification */ - mac = chunk_clonea(chunk_skip(data, 2)); - memset(data.ptr, 0, data.len); - } - break; - default: - DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", - sim_attribute_names, attribute); - break; - } - } - if (!mac.ptr) - { - DBG1(DBG_IKE, "no valid AT_MAC attribute received"); - return FAILED; - } - /* verify AT_MAC attribute, signature is over "EAP packet | n*SRES" */ - signer = signer_create(AUTH_HMAC_SHA1_128); - signer->set_key(signer, this->k_auth); - tmp = chunk_cata("cc", in->get_data(in), this->sreses); - if (!signer->verify_signature(signer, tmp, mac)) - { - DBG1(DBG_IKE, "AT_MAC verification failed"); - signer->destroy(signer); - return FAILED; - } - signer->destroy(signer); - return SUCCESS; -} - -/** - * process an EAP-SIM/Response/Start message - */ -static status_t server_process_start(private_eap_sim_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message, data; - sim_attribute_t attribute; - bool supported = FALSE; - chunk_t rands, rand, kcs, kc, sreses, sres; - char id[64]; - int len, i, rand_len, kc_len, sres_len; - - message = in->get_data(in); - read_header(&message); - - while ((attribute = read_attribute(&message, &data)) != AT_END) - { - switch (attribute) - { - case AT_NONCE_MT: - if (data.len == NONCE_LEN + 2) - { - this->nonce = chunk_clone(chunk_skip(data, 2)); - } - break; - case AT_SELECTED_VERSION: - if (chunk_equals(data, this->version)) - { - supported = TRUE; - } - break; - default: - DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", - sim_attribute_names, attribute); - break; - } - } - if (!supported || !this->nonce.ptr) - { - DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start"); - return FAILED; - } - len = snprintf(id, sizeof(id), "%D", this->peer); - if (len > sizeof(id) || len < 0) - { - return FAILED; - } - - /* read triplets from provider */ - rand = rands = chunk_alloca(RAND_LEN * TRIPLET_COUNT); - kc = kcs = chunk_alloca(KC_LEN * TRIPLET_COUNT); - sres = sreses = chunk_alloca(SRES_LEN * TRIPLET_COUNT); - rands.len = 0; - kcs.len = 0; - sreses.len = 0; - for (i = 0; i < TRIPLET_COUNT; i++) - { - rand_len = RAND_LEN; - kc_len = KC_LEN; - sres_len = SRES_LEN; - if (this->get_triplet(id, rand.ptr, &rand_len, sres.ptr, &sres_len, - kc.ptr, &kc_len)) - { - DBG1(DBG_IKE, "getting EAP-SIM triplet %d failed", i); - return FAILED; - } - rands.len += rand_len; - kcs.len += kc_len; - sreses.len += sres_len; - rand = chunk_skip(rand, rand_len); - kc = chunk_skip(kc, kc_len); - sres = chunk_skip(sres, sres_len); - } - derive_keys(this, kcs); - - /* build MAC over "EAP packet | NONCE_MT" */ - *out = build_payload(this, this->identifier++, SIM_CHALLENGE, AT_RAND, - rands, AT_MAC, this->nonce, AT_END); - this->sreses = chunk_clone(sreses); - return NEED_MORE; -} - -/** - * process an EAP-SIM/Request/Notification message - */ -static status_t peer_process_notification(private_eap_sim_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message, data; - sim_attribute_t attribute; - - message = in->get_data(in); - read_header(&message); - - while ((attribute = read_attribute(&message, &data)) != AT_END) - { - switch (attribute) - { - case AT_NOTIFICATION: - { - u_int16_t code = 0; - if (data.len == 2) - { - code = ntohs(*(u_int16_t*)data.ptr); - } - if (code <= 32767) /* no success bit */ - { - DBG1(DBG_IKE, "received %N error %d", - sim_attribute_names, attribute, code); - *out = build_payload(this, - in->get_identifier(in), SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_general, - AT_END); - return NEED_MORE; - } - else - { - DBG1(DBG_IKE, "received %N code %d", - sim_attribute_names, attribute, code); - } - break; - } - default: - DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", - sim_attribute_names, attribute); - break; - } - } - /* reply with empty notification */ - *out = build_payload(this, in->get_identifier(in), SIM_NOTIFICATION, AT_END); - return NEED_MORE; -} - -/** - * Process a client error - */ -static status_t server_process_client_error(private_eap_sim_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - chunk_t message, data; - sim_attribute_t attribute; - - message = in->get_data(in); - read_header(&message); - - while ((attribute = read_attribute(&message, &data)) != AT_END) - { - if (attribute == AT_CLIENT_ERROR_CODE) - { - u_int16_t code = 0; - if (data.len == 2) - { - code = ntohs(*(u_int16_t*)data.ptr); - } - DBG1(DBG_IKE, "received %N error %d", - sim_attribute_names, attribute, code); - } - else - { - DBG1(DBG_IKE, "ignoring EAP_SIM attribute %N", - sim_attribute_names, attribute); - } - } - return FAILED; -} - -/** - * Implementation of eap_method_t.process for the peer - */ -static status_t peer_process(private_eap_sim_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - sim_subtype_t type; - chunk_t message; - - message = in->get_data(in); - type = read_header(&message); - - switch (type) - { - case SIM_START: - return peer_process_start(this, in, out); - case SIM_CHALLENGE: - return peer_process_challenge(this, in, out); - case SIM_NOTIFICATION: - return peer_process_notification(this, in, out); - default: - DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N", - sim_subtype_names, type); - *out = build_payload(this, in->get_identifier(in), SIM_CLIENT_ERROR, - AT_CLIENT_ERROR_CODE, client_error_general, AT_END); - return NEED_MORE; - } -} - -/** - * Implementation of eap_method_t.process for the server - */ -static status_t server_process(private_eap_sim_t *this, - eap_payload_t *in, eap_payload_t **out) -{ - sim_subtype_t type; - chunk_t message; - - message = in->get_data(in); - type = read_header(&message); - - switch (type) - { - case SIM_START: - return server_process_start(this, in, out); - case SIM_CHALLENGE: - return server_process_challenge(this, in, out); - case SIM_CLIENT_ERROR: - return server_process_client_error(this, in, out); - default: - DBG1(DBG_IKE, "unable to process EAP_SIM subtype %N", - sim_subtype_names, type); - return FAILED; - } -} - -/** - * Implementation of eap_method_t.initiate for the peer - */ -static status_t peer_initiate(private_eap_sim_t *this, eap_payload_t **out) -{ - /* peer never initiates */ - return FAILED; -} - -/** - * Implementation of eap_method_t.initiate for the server - */ -static status_t server_initiate(private_eap_sim_t *this, eap_payload_t **out) -{ - /* version_list to derive MK, no padding */ - this->version_list = chunk_clone(this->version); - /* build_payloads adds padding itself */ - *out = build_payload(this, this->identifier++, SIM_START, - AT_VERSION_LIST, this->version, AT_END); - return NEED_MORE; -} - -/** - * Implementation of eap_method_t.get_type. - */ -static eap_type_t get_type(private_eap_sim_t *this, u_int32_t *vendor) -{ - *vendor = 0; - return EAP_SIM; -} - -/** - * Implementation of eap_method_t.get_msk. - */ -static status_t get_msk(private_eap_sim_t *this, chunk_t *msk) -{ - if (this->msk.ptr) - { - *msk = this->msk; - return SUCCESS; - } - return FAILED; -} - -/** - * Implementation of eap_method_t.is_mutual. - */ -static bool is_mutual(private_eap_sim_t *this) -{ - return TRUE; -} - -/** - * Implementation of eap_method_t.destroy. - */ -static void destroy(private_eap_sim_t *this) -{ - dlclose(this->handle); - chunk_free(&this->nonce); - chunk_free(&this->sreses); - chunk_free(&this->version_list); - chunk_free(&this->k_auth); - chunk_free(&this->k_encr); - chunk_free(&this->msk); - chunk_free(&this->emsk); - free(this); -} - -/* - * Described in header. - */ -eap_sim_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer) -{ - private_eap_sim_t *this; - randomizer_t *randomizer; - void *symbol; - char *name; - - this = malloc_thing(private_eap_sim_t); - this->alg = NULL; - this->get_triplet = NULL; - this->nonce = chunk_empty; - this->sreses = chunk_empty; - this->peer = peer; - this->tries = MAX_TRIES; - this->version.ptr = version; - this->version.len = sizeof(version); - this->version_list = chunk_empty; - this->k_auth = chunk_empty; - this->k_encr = chunk_empty; - this->msk = chunk_empty; - this->emsk = chunk_empty; - this->identifier = random(); - - this->handle = dlopen(SIM_READER_LIB, RTLD_LAZY); - if (this->handle == NULL) - { - DBG1(DBG_IKE, "unable to open SIM reader '%s'", SIM_READER_LIB); - free(this); - return NULL; - } - switch (role) - { - case EAP_PEER: - name = SIM_READER_ALG; - break; - case EAP_SERVER: - name = SIM_READER_GET_TRIPLET; - break; - default: - free(this); - return NULL; - } - symbol = dlsym(this->handle, name); - if (symbol == NULL) - { - DBG1(DBG_IKE, "unable to open SIM function '%s' in '%s'", - name, SIM_READER_LIB); - dlclose(this->handle); - free(this); - return NULL; - } - switch (role) - { - case EAP_SERVER: - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process; - this->get_triplet = symbol; - this->type = EAP_REQUEST; - break; - case EAP_PEER: - this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate; - this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process; - this->alg = symbol; - this->type = EAP_RESPONSE; - randomizer = randomizer_create(); - if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_LEN, - &this->nonce)) - { - DBG1(DBG_IKE, "unable to generate NONCE for EAP_SIM"); - randomizer->destroy(randomizer); - free(this); - return NULL; - } - randomizer->destroy(randomizer); - break; - default: - free(this); - return NULL; - } - this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; - this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; - this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; - this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; - - return &this->public; -} diff --git a/src/charon/sa/authenticators/eap/eap_sim.h b/src/charon/sa/authenticators/eap/eap_sim.h deleted file mode 100644 index d50cf7397..000000000 --- a/src/charon/sa/authenticators/eap/eap_sim.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @file eap_sim.h - * - * @brief Interface of eap_sim_t. - * - */ - -/* - * Copyright (C) 2007 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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. - */ - -#ifndef EAP_SIM_H_ -#define EAP_SIM_H_ - -typedef struct eap_sim_t eap_sim_t; - -#include <sa/authenticators/eap/eap_method.h> - -/** the library containing with the triplet functions */ -#ifndef SIM_READER_LIB -#error SIM_READER_LIB not specified, use --with-sim-reader option -#endif /* SIM_READER_LIB */ - -/** - * @brief Cardreaders SIM function. - * - * @param rand RAND to run algo with - * @param rand_length length of value in rand - * @param sres buffer to get SRES - * @param sres_length size of buffer in sres, returns bytes written to SRES - * @param kc buffer to get Kc - * @param kc_length size of buffer in Kc, returns bytes written to Kc - * @return zero on success - */ -typedef int (*sim_algo_t)(const unsigned char *rand, int rand_length, - unsigned char *sres, int *sres_length, - unsigned char *kc, int *kc_length); - -#ifndef SIM_READER_ALG -/** the SIM_READER_LIB's algorithm, uses sim_algo_t signature */ -#define SIM_READER_ALG "sim_run_alg" -#endif /* SIM_READER_ALG */ - -/** - * @brief Function to get a SIM triplet. - * - * @param identity identity (imsi) to get a triplet for - * @param rand buffer to get RAND - * @param rand_length size of buffer in rand, returns bytes written to RAND - * @param sres buffer to get SRES - * @param sres_length size of buffer in sres, returns bytes written to SRES - * @param kc buffer to get Kc - * @param kc_length size of buffer in Kc, returns bytes written to Kc - * @return zero on success - */ -typedef int (*sim_get_triplet_t)(char *identity, - unsigned char *rand, int *rand_length, - unsigned char *sres, int *sres_length, - unsigned char *kc, int *kc_length); - -#ifndef SIM_READER_GET_TRIPLET -/** the SIM_READER_LIB's get-triplet function, uses sim_get_triplet_t signature */ -#define SIM_READER_GET_TRIPLET "sim_get_triplet" -#endif /* SIM_READER_GET_TRIPLET */ - -/** - * @brief Implementation of the eap_method_t interface using EAP-SIM. - * - * This EAP-SIM client implementation uses another pluggable library to - * access the SIM card/triplet provider. This module is specified using the - * SIM_READER_LIB definition. It has to privde a sim_run_alg() function to - * calculate a triplet (client), and/or a sim_get_triplet() function to get - * a triplet (server). These functions are named to the SIM_READER_ALG and - * the SIM_READER_GET_TRIPLET definitions. - * - * @b Constructors: - * - eap_create() of this module - * - eap_client_create() using eap_method EAP_SIM - * - * @ingroup eap - */ -struct eap_sim_t { - - /** - * Implemented eap_method_t interface. - */ - eap_method_t eap_method_interface; -}; - -/** - * @brief Creates the EAP method EAP-SIM. - * - * @param role role of the module, client/server - * @param server ID of the EAP server - * @param peer ID of the EAP client - * @return eap_sim_t object - * - * @ingroup eap - */ -eap_sim_t *eap_create(eap_role_t role, - identification_t *server, identification_t *peer); - -#endif /* EAP_SIM_H_ */ diff --git a/src/charon/sa/authenticators/eap/sim/eap_sim_file.c b/src/charon/sa/authenticators/eap/sim/eap_sim_file.c deleted file mode 100644 index 2ab45a578..000000000 --- a/src/charon/sa/authenticators/eap/sim/eap_sim_file.c +++ /dev/null @@ -1,288 +0,0 @@ -/**
- * @file eap_sim.h
- *
- * @brief Interface of eap_sim_t.
- *
- */
-
-/*
- * Copyright (C) 2007 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 <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * 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 <string.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <daemon.h>
-
-#define IMSI_LEN 64
-#define RAND_LEN 16
-#define SRES_LEN 4
-#define KC_LEN 8
-
-typedef struct triplet_t triplet_t;
-
-struct triplet_t {
- unsigned char imsi[IMSI_LEN];
- unsigned char rand[RAND_LEN];
- unsigned char sres[SRES_LEN];
- unsigned char kc[KC_LEN];
-};
-
-static triplet_t *triplets = NULL;
-static int triplet_count = 0;
-
-#define TRIPLET_FILE IPSEC_CONFDIR "/ipsec.d/triplets.dat"
-
-/**
- * convert a single HEX char to its integer value
- */
-static int hexchr(char chr)
-{
- switch (chr)
- {
- case '0'...'9':
- return chr - '0';
- case 'A'...'F':
- return 10 + chr - 'A';
- case 'a'...'f':
- return 10 + chr - 'a';
- }
- return 0;
-}
-
-/**
- * convert a HEX string into a char array bin, limited by array length len
- */
-static void hex2bin(char *hex, unsigned char *bin, size_t len)
-{
- char *pos;
- int i, even = 1;
-
- pos = hex - 1;
- /* find the end, as we convert bottom up */
- while (TRUE)
- {
- switch (*(pos+1))
- {
- case '0'...'9':
- case 'A'...'F':
- case 'a'...'f':
- pos++;
- continue;
- }
- break;
- }
- /* convert two hex chars into a single bin byte */
- for (i = 0; pos >= hex && i < len; pos--)
- {
- if (even)
- {
- bin[len - 1 - i] = hexchr(*pos);
- }
- else
- {
- bin[len - 1 - i] |= 16 * hexchr(*pos);
- i++;
- }
- even = !even;
- }
-}
-
-/**
- * free up allocated triplets
- */
-static void __attribute__ ((destructor)) free_triplets()
-{
- free(triplets);
-}
-
-/**
- * read the triplets from the file, using freeradius triplet file syntax:
- * http://www.freeradius.org/radiusd/doc/rlm_sim_triplets
- */
-static void __attribute__ ((constructor)) read_triplets()
-{
- char line[512], *data[4], *pos;
- FILE *file;
- int i, nr = 0;
- triplet_t *triplet;
-
- file = fopen(TRIPLET_FILE, "r");
- if (file == NULL)
- {
- DBG1(DBG_CFG, "opening triplet file %s failed: %s",
- TRIPLET_FILE, strerror(errno));
- return;
- }
-
- if (triplets)
- {
- free(triplets);
- triplets = NULL;
- triplet_count = 0;
- }
-
- /* read line by line */
- while (fgets(line, sizeof(line), file))
- {
- nr++;
- /* skip comments, empty lines */
- switch (line[0])
- {
- case '\n':
- case '\r':
- case '#':
- case '\0':
- continue;
- default:
- break;
- }
- /* read comma separated values */
- pos = line;
- for (i = 0; i < 4; i++)
- {
- data[i] = pos;
- pos = strchr(pos, ',');
- if (pos)
- {
- *pos = '\0';
- pos++;
- }
- else if (i != 3)
- {
- DBG1(DBG_CFG, "error in triplet file, line %d", nr);
- fclose(file);
- return;
- }
- }
- /* allocate new triplet */
- triplet_count++;
- triplets = realloc(triplets, triplet_count * sizeof(triplet_t));
- triplet = &triplets[triplet_count - 1];
- memset(triplet, 0, sizeof(triplet_t));
-
- /* convert/copy triplet data */
- for (i = 0; i < IMSI_LEN - 1; i++)
- {
- switch (data[0][i])
- {
- case '\n':
- case '\r':
- case '\0':
- break;
- default:
- triplet->imsi[i] = data[0][i];
- continue;
- }
- break;
- }
- hex2bin(data[1], triplet->rand, RAND_LEN);
- hex2bin(data[2], triplet->sres, SRES_LEN);
- hex2bin(data[3], triplet->kc, KC_LEN);
-
- DBG4(DBG_CFG, "triplet: imsi %b\nrand %b\nsres %b\nkc %b",
- triplet->imsi, IMSI_LEN, triplet->rand, RAND_LEN,
- triplet->sres, SRES_LEN, triplet->kc, KC_LEN);
- }
- fclose(file);
- DBG2(DBG_CFG, "read %d triplets from %s", triplet_count, TRIPLET_FILE);
-}
-
-/**
- * Run the sim algorithm, see eap_sim.h
- */
-int sim_run_alg(const unsigned char *rand, int rand_length,
- unsigned char *sres, int *sres_length,
- unsigned char *kc, int *kc_length)
-{
- int current;
-
- if (rand_length != RAND_LEN ||
- *sres_length < SRES_LEN ||
- *kc_length < KC_LEN)
- {
- return 1;
- }
-
- for (current = 0; current < triplet_count; current++)
- {
- if (memcmp(triplets[current].rand, rand, RAND_LEN) == 0)
- {
- memcpy(sres, triplets[current].sres, SRES_LEN);
- memcpy(kc, triplets[current].kc, KC_LEN);
- *sres_length = SRES_LEN;
- *kc_length = KC_LEN;
- return 0;
- }
- }
- return 2;
-}
-
-/**
- * Get a single triplet, see_eap_sim.h
- */
-int sim_get_triplet(char *imsi,
- unsigned char *rand, int *rand_length,
- unsigned char *sres, int *sres_length,
- unsigned char *kc, int *kc_length)
-{
- int current;
- triplet_t *triplet;
- static int skip = -1;
-
- DBG2(DBG_CFG, "getting triplet for %s", imsi);
-
- if (*rand_length < RAND_LEN ||
- *sres_length < SRES_LEN ||
- *kc_length < KC_LEN)
- {
- return 1;
- }
- if (triplet_count == 0)
- {
- return 2;
- }
- for (current = 0; current < triplet_count; current++)
- {
- triplet = &triplets[current];
-
- if (streq(imsi, triplet->imsi))
- {
- /* skip triplet if already used */
- if (skip >= current)
- {
- continue;
- }
- *rand_length = RAND_LEN;
- *sres_length = SRES_LEN;
- *kc_length = KC_LEN;
- memcpy(rand, triplet->rand, RAND_LEN);
- memcpy(sres, triplet->sres, SRES_LEN);
- memcpy(kc, triplet->kc, KC_LEN);
- /* remember used triplet */
- skip = current;
- return 0;
- }
- }
- if (skip > -1)
- {
- /* no triplet left, reuse triplets */
- skip = -1;
- return sim_get_triplet(imsi, rand, rand_length,
- sres, sres_length, kc, kc_length);
- }
- return 2;
-}
-
diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c index edd75da43..95bb5e57f 100644 --- a/src/charon/sa/authenticators/eap_authenticator.c +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -1,10 +1,3 @@ -/** - * @file eap_authenticator.c - * - * @brief Implementation of eap_authenticator_t. - * - */ - /* * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: eap_authenticator.c 3589 2008-03-13 14:14:44Z martin $ */ #include <string.h> @@ -160,9 +155,9 @@ static status_t initiate(private_eap_authenticator_t *this, eap_type_t type, { DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type); } - this->method = eap_method_create(type, vendor, this->role, - this->ike_sa->get_my_id(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); + this->method = charon->eap->create_instance(charon->eap, type, vendor, + this->role, this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); if (this->method == NULL) { @@ -195,9 +190,11 @@ static status_t process_peer(private_eap_authenticator_t *this, if (!vendor && type == EAP_IDENTITY) { - eap_method_t *method = eap_method_create(type, 0, EAP_PEER, - this->ike_sa->get_other_id(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa)); + eap_method_t *method; + + method = charon->eap->create_instance(charon->eap, type, 0, EAP_PEER, + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa)); if (method == NULL || method->process(method, in, out) != SUCCESS) { @@ -227,9 +224,10 @@ static status_t process_peer(private_eap_authenticator_t *this, DBG1(DBG_IKE, "EAP server requested %N authentication", eap_type_names, type); } - this->method = eap_method_create(type, vendor, EAP_PEER, - this->ike_sa->get_other_id(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa)); + this->method = charon->eap->create_instance(charon->eap, + type, vendor, EAP_PEER, + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa)); if (this->method == NULL) { DBG1(DBG_IKE, "EAP server requested unsupported " diff --git a/src/charon/sa/authenticators/eap_authenticator.h b/src/charon/sa/authenticators/eap_authenticator.h index cf2180ee3..2dad59fbb 100644 --- a/src/charon/sa/authenticators/eap_authenticator.h +++ b/src/charon/sa/authenticators/eap_authenticator.h @@ -1,10 +1,3 @@ -/** - * @file eap_authenticator.h - * - * @brief Interface of eap_authenticator_t. - * - */ - /* * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: eap_authenticator.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup eap_authenticator eap_authenticator + * @{ @ingroup authenticators */ #ifndef EAP_AUTHENTICATOR_H_ @@ -29,7 +29,7 @@ typedef struct eap_authenticator_t eap_authenticator_t; #include <encoding/payloads/eap_payload.h> /** - * @brief Implementation of the authenticator_t interface using AUTH_EAP. + * Implementation of the authenticator_t interface using AUTH_EAP. * * Authentication using EAP involves the most complex authenticator. It stays * alive over multiple ike_auth transactions and handles multiple EAP @@ -68,11 +68,6 @@ typedef struct eap_authenticator_t eap_authenticator_t; +--------+ +--------+ @endverbatim - * @b Constructors: - * - eap_authenticator_create() - * - authenticator_create() using auth_method AUTH_EAP - * - * @ingroup authenticators */ struct eap_authenticator_t { @@ -82,7 +77,7 @@ struct eap_authenticator_t { authenticator_t authenticator_interface; /** - * @brief Check if the EAP method was/is mutual and secure. + * Check if the EAP method was/is mutual and secure. * * RFC4306 proposes to authenticate the EAP responder (server) by standard * IKEv2 methods (RSA, psk). Not all, but some EAP methods @@ -93,19 +88,17 @@ struct eap_authenticator_t { * AUTH payload, the client must verify that the server initiated mutual * EAP authentication before it can trust the server. * - * @param this calling object * @return TRUE, if no AUTH payload required, FALSE otherwise */ bool (*is_mutual) (eap_authenticator_t* this); /** - * @brief Initiate the EAP exchange. + * Initiate the EAP exchange. * * The server initiates EAP exchanges, so the client never calls * this method. If initiate() returns NEED_MORE, the EAP authentication * process started. In any case, a payload is created in "out". * - * @param this calling object * @param type EAP method to use to authenticate client * @param vendor EAP vendor identifier, if type is vendor specific, or 0 * @param out created initiaal EAP message to send @@ -117,7 +110,7 @@ struct eap_authenticator_t { u_int32_t vendor, eap_payload_t **out); /** - * @brief Process an EAP message. + * Process an EAP message. * * After receiving an EAP message "in", the peer/server processes * the payload and creates a reply/subsequent request. @@ -132,7 +125,6 @@ struct eap_authenticator_t { * If a SUCCESS is returned (on any side), the EAP authentication was * successful and the AUTH payload can be exchanged. * - * @param this calling object * @param in received EAP message * @param out created EAP message to send * @return @@ -145,13 +137,11 @@ struct eap_authenticator_t { }; /** - * @brief Creates an authenticator for AUTH_EAP. + * Creates an authenticator for AUTH_EAP. * * @param ike_sa associated ike_sa * @return eap_authenticator_t object - * - * @ingroup authenticators */ eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa); -#endif /* EAP_AUTHENTICATOR_H_ */ +#endif /* EAP_AUTHENTICATOR_H_ @} */ diff --git a/src/charon/sa/authenticators/psk_authenticator.c b/src/charon/sa/authenticators/psk_authenticator.c index 6b76088bb..d003dc2c9 100644 --- a/src/charon/sa/authenticators/psk_authenticator.c +++ b/src/charon/sa/authenticators/psk_authenticator.c @@ -1,10 +1,3 @@ -/** - * @file psk_authenticator.c - * - * @brief Implementation of psk_authenticator_t. - * - */ - /* * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -19,6 +12,8 @@ * 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. + * + * $Id: psk_authenticator.c 3589 2008-03-13 14:14:44Z martin $ */ #include <string.h> @@ -26,6 +21,7 @@ #include "psk_authenticator.h" #include <daemon.h> +#include <credentials/auth_info.h> /** * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE. @@ -105,39 +101,49 @@ chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, * Implementation of authenticator_t.verify. */ static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init, - chunk_t my_nonce, auth_payload_t *auth_payload) + chunk_t my_nonce, auth_payload_t *auth_payload) { - status_t status; - chunk_t auth_data, recv_auth_data, shared_key; + chunk_t auth_data, recv_auth_data; identification_t *my_id, *other_id; + shared_key_t *shared_key; + enumerator_t *enumerator; + bool authenticated = FALSE; + int keys_found = 0; my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); - status = charon->credentials->get_shared_key(charon->credentials, my_id, - other_id, &shared_key); - if (status != SUCCESS) + enumerator = charon->credentials->create_shared_enumerator( + charon->credentials, SHARED_IKE, my_id, other_id); + while (!authenticated && enumerator->enumerate(enumerator, &shared_key, NULL, NULL)) { - DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); - return status; + keys_found++; + auth_data = build_shared_key_signature(ike_sa_init, my_nonce, + shared_key->get_key(shared_key), other_id, + this->ike_sa->get_skp_verify(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + recv_auth_data = auth_payload->get_data(auth_payload); + if (auth_data.len == recv_auth_data.len && + memeq(auth_data.ptr, recv_auth_data.ptr, auth_data.len)) + { + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_PSK); + authenticated = TRUE; + } + chunk_free(&auth_data); } + enumerator->destroy(enumerator); - auth_data = build_shared_key_signature(ike_sa_init, my_nonce, shared_key, - other_id, this->ike_sa->get_skp_verify(this->ike_sa), - this->ike_sa->get_prf(this->ike_sa)); - chunk_free_randomized(&shared_key); - - recv_auth_data = auth_payload->get_data(auth_payload); - if (auth_data.len != recv_auth_data.len || - !memeq(auth_data.ptr, recv_auth_data.ptr, auth_data.len)) + if (!authenticated) { - DBG1(DBG_IKE, "PSK MAC verification failed"); - chunk_free(&auth_data); + if (keys_found == 0) + { + DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); + return NOT_FOUND; + } + DBG1(DBG_IKE, "tried %d shared key%s for '%D' - '%D', but MAC mismatched", + keys_found, keys_found == 1 ? "" : "s", my_id, other_id); return FAILED; } - chunk_free(&auth_data); - - DBG1(DBG_IKE, "authentication of '%D' with %N successful", - other_id, auth_method_names, AUTH_PSK); return SUCCESS; } @@ -147,28 +153,27 @@ static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init, static status_t build(private_psk_authenticator_t *this, chunk_t ike_sa_init, chunk_t other_nonce, auth_payload_t **auth_payload) { - chunk_t shared_key; + shared_key_t *shared_key; chunk_t auth_data; - status_t status; identification_t *my_id, *other_id; my_id = this->ike_sa->get_my_id(this->ike_sa); other_id = this->ike_sa->get_other_id(this->ike_sa); DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", my_id, auth_method_names, AUTH_PSK); - status = charon->credentials->get_shared_key(charon->credentials, my_id, - other_id, &shared_key); - if (status != SUCCESS) + shared_key = charon->credentials->get_shared(charon->credentials, SHARED_IKE, + my_id, other_id); + if (shared_key == NULL) { DBG1(DBG_IKE, "no shared key found for '%D' - '%D'", my_id, other_id); - return status; + return NOT_FOUND; } - - auth_data = build_shared_key_signature(ike_sa_init, other_nonce, shared_key, - my_id, this->ike_sa->get_skp_build(this->ike_sa), - this->ike_sa->get_prf(this->ike_sa)); + auth_data = build_shared_key_signature(ike_sa_init, other_nonce, + shared_key->get_key(shared_key), my_id, + this->ike_sa->get_skp_build(this->ike_sa), + this->ike_sa->get_prf(this->ike_sa)); + shared_key->destroy(shared_key); DBG2(DBG_IKE, "successfully created shared key MAC"); - chunk_free_randomized(&shared_key); *auth_payload = auth_payload_create(); (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); (*auth_payload)->set_data(*auth_payload, auth_data); diff --git a/src/charon/sa/authenticators/psk_authenticator.h b/src/charon/sa/authenticators/psk_authenticator.h index c1c5bcaac..c7cb5a23c 100644 --- a/src/charon/sa/authenticators/psk_authenticator.h +++ b/src/charon/sa/authenticators/psk_authenticator.h @@ -1,10 +1,3 @@ -/** - * @file psk_authenticator.h - * - * @brief Interface of psk_authenticator_t. - * - */ - /* * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: psk_authenticator.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup psk_authenticator psk_authenticator + * @{ @ingroup authenticators */ #ifndef PSK_AUTHENTICATOR_H_ @@ -28,13 +28,7 @@ typedef struct psk_authenticator_t psk_authenticator_t; #include <sa/authenticators/authenticator.h> /** - * @brief Implementation of the authenticator_t interface using AUTH_PSK. - * - * @b Constructors: - * - psk_authenticator_create() - * - authenticator_create() using auth_method AUTH_PSK - * - * @ingroup authenticators + * Implementation of the authenticator_t interface using AUTH_PSK. */ struct psk_authenticator_t { @@ -45,13 +39,11 @@ struct psk_authenticator_t { }; /** - * @brief Creates an authenticator for AUTH_PSK. + * Creates an authenticator for AUTH_PSK. * * @param ike_sa associated ike_sa * @return psk_authenticator_t object - * - * @ingroup authenticators */ psk_authenticator_t *psk_authenticator_create(ike_sa_t *ike_sa); -#endif /* PSK_AUTHENTICATOR_H_ */ +#endif /* PSK_AUTHENTICATOR_H_ @} */ diff --git a/src/charon/sa/authenticators/pubkey_authenticator.c b/src/charon/sa/authenticators/pubkey_authenticator.c new file mode 100644 index 000000000..2c02ca84c --- /dev/null +++ b/src/charon/sa/authenticators/pubkey_authenticator.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * $Id: pubkey_authenticator.c 4054 2008-06-10 20:31:53Z andreas $ + */ + +#include <string.h> + +#include "pubkey_authenticator.h" + +#include <daemon.h> +#include <credentials/auth_info.h> + + +typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t; + +/** + * Private data of an pubkey_authenticator_t object. + */ +struct private_pubkey_authenticator_t { + + /** + * Public authenticator_t interface. + */ + pubkey_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; +}; + +/** + * Function implemented in psk_authenticator.c + */ +extern chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, + identification_t *id, prf_t *prf); + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_pubkey_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + public_key_t *public; + auth_method_t auth_method; + chunk_t auth_data, octets; + identification_t *other_id; + prf_t *prf; + auth_info_t *auth, *current_auth; + enumerator_t *enumerator; + key_type_t key_type = KEY_ECDSA; + signature_scheme_t scheme; + status_t status = FAILED; + + other_id = this->ike_sa->get_other_id(this->ike_sa); + auth_method = auth_payload->get_auth_method(auth_payload); + switch (auth_method) + { + case AUTH_RSA: + /* We are currently fixed to SHA1 hashes. + * TODO: allow other hash algorithms and note it in "auth" */ + key_type = KEY_RSA; + scheme = SIGN_RSA_EMSA_PKCS1_SHA1; + break; + case AUTH_ECDSA_256: + scheme = SIGN_ECDSA_256; + break; + case AUTH_ECDSA_384: + scheme = SIGN_ECDSA_384; + break; + case AUTH_ECDSA_521: + scheme = SIGN_ECDSA_521; + break; + default: + return INVALID_ARG; + } + auth_data = auth_payload->get_data(auth_payload); + prf = this->ike_sa->get_prf(this->ike_sa); + prf->set_key(prf, this->ike_sa->get_skp_verify(this->ike_sa)); + octets = build_tbs_octets(ike_sa_init, my_nonce, other_id, prf); + + auth = this->ike_sa->get_other_auth(this->ike_sa); + enumerator = charon->credentials->create_public_enumerator( + charon->credentials, key_type, other_id, auth); + while (enumerator->enumerate(enumerator, &public, ¤t_auth)) + { + if (public->verify(public, scheme, octets, auth_data)) + { + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, auth_method); + status = SUCCESS; + auth->merge(auth, current_auth); + break; + } + else + { + DBG1(DBG_IKE, "signature validation failed, looking for another key"); + } + } + enumerator->destroy(enumerator); + chunk_free(&octets); + return status; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_pubkey_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t octets, auth_data; + status_t status = FAILED; + private_key_t *private; + identification_t *my_id; + prf_t *prf; + auth_info_t *auth; + auth_method_t auth_method; + signature_scheme_t scheme; + + my_id = this->ike_sa->get_my_id(this->ike_sa); + auth = this->ike_sa->get_my_auth(this->ike_sa); + private = charon->credentials->get_private(charon->credentials, KEY_ANY, + my_id, auth); + if (private == NULL) + { + DBG1(DBG_IKE, "no private key found for '%D'", my_id); + return NOT_FOUND; + } + + switch (private->get_type(private)) + { + case KEY_RSA: + /* we currently use always SHA1 for signatures, + * TODO: support other hashes depending on configuration/auth */ + scheme = SIGN_RSA_EMSA_PKCS1_SHA1; + auth_method = AUTH_RSA; + break; + case KEY_ECDSA: + /* we try to deduct the signature scheme from the keysize */ + switch (private->get_keysize(private)) + { + case 32: + scheme = SIGN_ECDSA_256; + auth_method = AUTH_ECDSA_256; + break; + case 48: + scheme = SIGN_ECDSA_384; + auth_method = AUTH_ECDSA_384; + break; + case 66: + scheme = SIGN_ECDSA_521; + auth_method = AUTH_ECDSA_521; + break; + default: + DBG1(DBG_IKE, "%d bit ECDSA private key size not supported", + private->get_keysize(private)); + return status; + } + break; + default: + DBG1(DBG_IKE, "private key of type %N not supported", + key_type_names, private->get_type(private)); + return status; + } + prf = this->ike_sa->get_prf(this->ike_sa); + prf->set_key(prf, this->ike_sa->get_skp_build(this->ike_sa)); + octets = build_tbs_octets(ike_sa_init, other_nonce, my_id, prf); + + if (private->sign(private, scheme, octets, &auth_data)) + { + auth_payload_t *payload = auth_payload_create(); + payload->set_auth_method(payload, auth_method); + payload->set_data(payload, auth_data); + *auth_payload = payload; + chunk_free(&auth_data); + status = SUCCESS; + } + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N %s", my_id, + auth_method_names, auth_method, + (status == SUCCESS)? "successful":"failed"); + chunk_free(&octets); + private->destroy(private); + + return status; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_pubkey_authenticator_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +pubkey_authenticator_t *pubkey_authenticator_create(ike_sa_t *ike_sa) +{ + private_pubkey_authenticator_t *this = malloc_thing(private_pubkey_authenticator_t); + + /* public functions */ + this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; + this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; + this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; + + /* private data */ + this->ike_sa = ike_sa; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/rsa_authenticator.h b/src/charon/sa/authenticators/pubkey_authenticator.h index cc5cc0150..038d8b1d2 100644 --- a/src/charon/sa/authenticators/rsa_authenticator.h +++ b/src/charon/sa/authenticators/pubkey_authenticator.h @@ -1,11 +1,5 @@ -/** - * @file rsa_authenticator.h - * - * @brief Interface of rsa_authenticator_t. - * - */ - /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -18,25 +12,26 @@ * 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. + * + * $Id: pubkey_authenticator.h 4051 2008-06-10 09:08:27Z tobias $ */ -#ifndef RSA_AUTHENTICATOR_H_ -#define RSA_AUTHENTICATOR_H_ +/** + * @defgroup pubkey_authenticator pubkey_authenticator + * @{ @ingroup authenticators + */ + +#ifndef PUBKEY_AUTHENTICATOR_H_ +#define PUBKEY_AUTHENTICATOR_H_ -typedef struct rsa_authenticator_t rsa_authenticator_t; +typedef struct pubkey_authenticator_t pubkey_authenticator_t; #include <sa/authenticators/authenticator.h> /** - * @brief Implementation of the authenticator_t interface using AUTH_RSA. - * - * @b Constructors: - * - rsa_authenticator_create() - * - authenticator_create() using auth_method AUTH_RSA - * - * @ingroup authenticators + * Implementation of the authenticator_t interface using AUTH_PUBKEY. */ -struct rsa_authenticator_t { +struct pubkey_authenticator_t { /** * Implemented authenticator_t interface. @@ -45,13 +40,11 @@ struct rsa_authenticator_t { }; /** - * @brief Creates an authenticator for AUTH_RSA. + * Creates an authenticator for AUTH_PUBKEY. * * @param ike_sa associated ike_sa - * @return rsa_authenticator_t object - * - * @ingroup authenticators + * @return pubkey_authenticator_t object */ -rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa); +pubkey_authenticator_t *pubkey_authenticator_create(ike_sa_t *ike_sa); -#endif /* RSA_AUTHENTICATOR_H_ */ +#endif /* PUBKEY_AUTHENTICATOR_H_ @} */ diff --git a/src/charon/sa/authenticators/rsa_authenticator.c b/src/charon/sa/authenticators/rsa_authenticator.c deleted file mode 100644 index ba0fad1e3..000000000 --- a/src/charon/sa/authenticators/rsa_authenticator.c +++ /dev/null @@ -1,160 +0,0 @@ -/** - * @file rsa_authenticator.c - * - * @brief Implementation of rsa_authenticator_t. - * - */ - -/* - * Copyright (C) 2005-2006 Martin Willi - * Copyright (C) 2005 Jan Hutter - * 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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 <string.h> - -#include "rsa_authenticator.h" - -#include <daemon.h> - - -typedef struct private_rsa_authenticator_t private_rsa_authenticator_t; - -/** - * Private data of an rsa_authenticator_t object. - */ -struct private_rsa_authenticator_t { - - /** - * Public authenticator_t interface. - */ - rsa_authenticator_t public; - - /** - * Assigned IKE_SA - */ - ike_sa_t *ike_sa; -}; - -/** - * Function implemented in psk_authenticator.c - */ -extern chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, - identification_t *id, prf_t *prf); - -/** - * Implementation of authenticator_t.verify. - */ -static status_t verify(private_rsa_authenticator_t *this, chunk_t ike_sa_init, - chunk_t my_nonce, auth_payload_t *auth_payload) -{ - status_t status; - chunk_t auth_data, octets; - identification_t *other_id; - ca_info_t *issuer; - prf_t *prf; - - other_id = this->ike_sa->get_other_id(this->ike_sa); - - if (auth_payload->get_auth_method(auth_payload) != AUTH_RSA) - { - return INVALID_ARG; - } - auth_data = auth_payload->get_data(auth_payload); - prf = this->ike_sa->get_prf(this->ike_sa); - prf->set_key(prf, this->ike_sa->get_skp_verify(this->ike_sa)); - octets = build_tbs_octets(ike_sa_init, my_nonce, other_id, prf); - status = charon->credentials->verify_signature(charon->credentials, - octets, auth_data, other_id, &issuer); - chunk_free(&octets); - - if (status == SUCCESS) - { - this->ike_sa->set_other_ca(this->ike_sa, issuer); - DBG1(DBG_IKE, "authentication of '%D' with %N successful", - other_id, auth_method_names, AUTH_RSA); - } - return status; -} - -/** - * Implementation of authenticator_t.build. - */ -static status_t build(private_rsa_authenticator_t *this, chunk_t ike_sa_init, - chunk_t other_nonce, auth_payload_t **auth_payload) -{ - chunk_t octets, auth_data; - status_t status; - rsa_public_key_t *my_pubkey; - identification_t *my_id; - prf_t *prf; - - my_id = this->ike_sa->get_my_id(this->ike_sa); - DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", - my_id, auth_method_names, AUTH_RSA); - DBG2(DBG_IKE, "looking for RSA public key belonging to '%D'...", my_id); - - my_pubkey = charon->credentials->get_rsa_public_key(charon->credentials, my_id); - if (my_pubkey == NULL) - { - DBG1(DBG_IKE, "no RSA public key found for '%D'", my_id); - return NOT_FOUND; - } - DBG2(DBG_IKE, " matching RSA public key found"); - - prf = this->ike_sa->get_prf(this->ike_sa); - prf->set_key(prf, this->ike_sa->get_skp_build(this->ike_sa)); - octets = build_tbs_octets(ike_sa_init, other_nonce, my_id, prf); - status = charon->credentials->rsa_signature(charon->credentials, - my_pubkey, HASH_SHA1, octets, &auth_data); - chunk_free(&octets); - - if (status != SUCCESS) - { - DBG1(DBG_IKE, "building RSA signature with SHA-1 hash failed"); - return status; - } - DBG2(DBG_IKE, "successfully signed with RSA private key"); - - *auth_payload = auth_payload_create(); - (*auth_payload)->set_auth_method(*auth_payload, AUTH_RSA); - (*auth_payload)->set_data(*auth_payload, auth_data); - chunk_free(&auth_data); - return SUCCESS; -} - -/** - * Implementation of authenticator_t.destroy. - */ -static void destroy(private_rsa_authenticator_t *this) -{ - free(this); -} - -/* - * Described in header. - */ -rsa_authenticator_t *rsa_authenticator_create(ike_sa_t *ike_sa) -{ - private_rsa_authenticator_t *this = malloc_thing(private_rsa_authenticator_t); - - /* public functions */ - this->public.authenticator_interface.verify = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t*))verify; - this->public.authenticator_interface.build = (status_t(*)(authenticator_t*,chunk_t,chunk_t,auth_payload_t**))build; - this->public.authenticator_interface.destroy = (void(*)(authenticator_t*))destroy; - - /* private data */ - this->ike_sa = ike_sa; - - return &this->public; -} diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index b6c71a8b5..2a6b6f67c 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -1,13 +1,7 @@ -/** - * @file child_sa.c - * - * @brief Implementation of child_sa_t. - * - */ - /* + * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -20,6 +14,8 @@ * 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. + * + * $Id: child_sa.c 3920 2008-05-08 16:19:11Z tobias $ */ #define _GNU_SOURCE @@ -75,6 +71,8 @@ struct private_child_sa_t { identification_t *id; /** actual used SPI, 0 if unused */ u_int32_t spi; + /** Compression Parameter Index (CPI) used, 0 if unused */ + u_int16_t cpi; } me, other; /** @@ -115,12 +113,22 @@ struct private_child_sa_t { /** * encryption algorithm used for this SA */ - algorithm_t encryption; + u_int16_t enc_alg; + + /** + * key size of enc_alg + */ + u_int16_t enc_size; /** * integrity protection algorithm used for this SA */ - algorithm_t integrity; + u_int16_t int_alg; + + /** + * key size of int_alg + */ + u_int16_t int_size; /** * time, on which SA was installed @@ -143,6 +151,16 @@ struct private_child_sa_t { bool encap; /** + * Specifies the IPComp transform used (IPCOMP_NONE if disabled) + */ + ipcomp_transform_t ipcomp; + + /** + * TRUE if we allocated (or tried to allocate) a CPI + */ + bool cpi_allocated; + + /** * mode this SA uses, tunnel/transport */ mode_t mode; @@ -251,10 +269,10 @@ static void get_stats(private_child_sa_t *this, mode_t *mode, iterator->destroy(iterator); *mode = this->mode; - *encr_algo = this->encryption.algorithm; - *encr_len = this->encryption.key_size; - *int_algo = this->integrity.algorithm; - *int_len = this->integrity.key_size; + *encr_algo = this->enc_alg; + *encr_len = this->enc_size; + *int_algo = this->int_alg; + *int_len = this->int_size; *rekey = this->rekey_time; *use_in = in; *use_out = out; @@ -498,10 +516,7 @@ static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) static status_t install(private_child_sa_t *this, proposal_t *proposal, mode_t mode, prf_plus_t *prf_plus, bool mine) { - u_int32_t spi, soft, hard;; - algorithm_t *enc_algo, *int_algo; - algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0}; - algorithm_t int_algo_none = {AUTH_UNDEFINED, 0}; + u_int32_t spi, soft, hard; host_t *src; host_t *dst; status_t status; @@ -549,43 +564,43 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, protocol_id_names, this->protocol); /* select encryption algo */ - if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_algo)) + if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, + &this->enc_alg, &this->enc_size)) { - DBG2(DBG_CHD, " using %N for encryption", - encryption_algorithm_names, enc_algo->algorithm); - } - else - { - enc_algo = &enc_algo_none; + DBG2(DBG_CHD, " using %N for encryption", + encryption_algorithm_names, this->enc_alg); } /* select integrity algo */ - if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_algo)) + if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, + &this->int_alg, &this->int_size)) { DBG2(DBG_CHD, " using %N for integrity", - integrity_algorithm_names, int_algo->algorithm); - } - else - { - int_algo = &int_algo_none; + integrity_algorithm_names, this->int_alg); } - soft = this->config->get_lifetime(this->config, TRUE); hard = this->config->get_lifetime(this->config, FALSE); /* send SA down to the kernel */ DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); - status = charon->kernel_interface->add_sa(charon->kernel_interface, - src, dst, spi, this->protocol, - this->reqid, mine ? soft : 0, - hard, enc_algo, int_algo, - prf_plus, mode, this->encap, mine); - this->encryption = *enc_algo; - this->integrity = *int_algo; + if (this->ipcomp != IPCOMP_NONE) + { + /* we install an additional IPComp SA */ + u_int32_t cpi = htonl(ntohs(mine ? this->me.cpi : this->other.cpi)); + status = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, cpi, IPPROTO_COMP, this->reqid, 0, 0, + ENCR_UNDEFINED, 0, AUTH_UNDEFINED, 0, NULL, mode, + this->ipcomp, FALSE, mine); + } + + status = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, spi, this->protocol, this->reqid, mine ? soft : 0, hard, + this->enc_alg, this->enc_size, this->int_alg, this->int_size, + prf_plus, mode, IPCOMP_NONE, this->encap, mine); + this->install_time = time(NULL); this->rekey_time = this->install_time + soft; - return status; } @@ -686,15 +701,15 @@ static status_t add_policies(private_child_sa_t *this, /* install 3 policies: out, in and forward */ status = charon->kernel_interface->add_policy(charon->kernel_interface, this->me.addr, this->other.addr, my_ts, other_ts, POLICY_OUT, - this->protocol, this->reqid, high_prio, mode); + this->protocol, this->reqid, high_prio, mode, this->ipcomp); status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other.addr, this->me.addr, other_ts, my_ts, POLICY_IN, - this->protocol, this->reqid, high_prio, mode); + this->protocol, this->reqid, high_prio, mode, this->ipcomp); status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other.addr, this->me.addr, other_ts, my_ts, POLICY_FWD, - this->protocol, this->reqid, high_prio, mode); + this->protocol, this->reqid, high_prio, mode, this->ipcomp); if (status != SUCCESS) { @@ -795,10 +810,20 @@ static status_t update_hosts(private_child_sa_t *this, this->encap = encap; - /* update our (initator) SAs */ + if (this->ipcomp != IPCOMP_NONE) + { + /* update our (initator) IPComp SA */ + charon->kernel_interface->update_sa(charon->kernel_interface, htonl(ntohs(this->me.cpi)), + IPPROTO_COMP, this->other.addr, this->me.addr, other, me, FALSE); + /* update his (responder) IPComp SA */ + charon->kernel_interface->update_sa(charon->kernel_interface, htonl(ntohs(this->other.cpi)), + IPPROTO_COMP, this->me.addr, this->other.addr, me, other, FALSE); + } + + /* update our (initator) SA */ charon->kernel_interface->update_sa(charon->kernel_interface, this->me.spi, this->protocol, this->other.addr, this->me.addr, other, me, encap); - /* update his (responder) SAs */ + /* update his (responder) SA */ charon->kernel_interface->update_sa(charon->kernel_interface, this->other.spi, this->protocol, this->me.addr, this->other.addr, me, other, encap); @@ -846,13 +871,13 @@ static status_t update_hosts(private_child_sa_t *this, /* reinstall updated policies */ charon->kernel_interface->add_policy(charon->kernel_interface, me, other, policy->my_ts, policy->other_ts, POLICY_OUT, - this->protocol, this->reqid, TRUE, this->mode); + this->protocol, this->reqid, TRUE, this->mode, this->ipcomp); charon->kernel_interface->add_policy(charon->kernel_interface, other, me, policy->other_ts, policy->my_ts, POLICY_IN, - this->protocol, this->reqid, TRUE, this->mode); + this->protocol, this->reqid, TRUE, this->mode, this->ipcomp); charon->kernel_interface->add_policy(charon->kernel_interface, other, me, policy->other_ts, policy->my_ts, POLICY_FWD, - this->protocol, this->reqid, TRUE, this->mode); + this->protocol, this->reqid, TRUE, this->mode, this->ipcomp); } iterator->destroy(iterator); } @@ -884,6 +909,30 @@ static void set_virtual_ip(private_child_sa_t *this, host_t *ip) } /** + * Implementation of child_sa_t.activate_ipcomp. + */ +static void activate_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp, + u_int16_t other_cpi) +{ + this->ipcomp = ipcomp; + this->other.cpi = other_cpi; +} + +/** + * Implementation of child_sa_t.get_my_cpi. + */ +static u_int16_t get_my_cpi(private_child_sa_t *this) +{ + if (!this->cpi_allocated) + { + charon->kernel_interface->get_cpi(charon->kernel_interface, + this->other.addr, this->me.addr, this->reqid, &this->me.cpi); + this->cpi_allocated = TRUE; + } + return this->me.cpi; +} + +/** * Implementation of child_sa_t.destroy. */ static void destroy(private_child_sa_t *this) @@ -916,6 +965,16 @@ static void destroy(private_child_sa_t *this) charon->kernel_interface->del_sa(charon->kernel_interface, this->other.addr, this->other.spi, this->protocol); } + if (this->me.cpi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other.addr, htonl(ntohs(this->me.cpi)), IPPROTO_COMP); + } + if (this->other.cpi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other.addr, htonl(ntohs(this->other.cpi)), IPPROTO_COMP); + } /* delete all policies in the kernel */ while (this->policies->remove_last(this->policies, (void**)&policy) == SUCCESS) @@ -976,6 +1035,8 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config; + this->public.activate_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t,u_int16_t))activate_ipcomp; + this->public.get_my_cpi = (u_int16_t(*)(child_sa_t*))get_my_cpi; this->public.set_virtual_ip = (void(*)(child_sa_t*,host_t*))set_virtual_ip; this->public.destroy = (void(*)(child_sa_t*))destroy; @@ -985,17 +1046,21 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->me.id = my_id->clone(my_id); this->other.id = other_id->clone(other_id); this->me.spi = 0; + this->me.cpi = 0; this->other.spi = 0; + this->other.cpi = 0; this->alloc_ah_spi = 0; this->alloc_esp_spi = 0; this->encap = encap; + this->cpi_allocated = FALSE; + this->ipcomp = IPCOMP_NONE; this->state = CHILD_CREATED; /* reuse old reqid if we are rekeying an existing CHILD_SA */ this->reqid = rekey ? rekey : ++reqid; - this->encryption.algorithm = ENCR_UNDEFINED; - this->encryption.key_size = 0; - this->integrity.algorithm = AUTH_UNDEFINED; - this->encryption.key_size = 0; + this->enc_alg = ENCR_UNDEFINED; + this->enc_size = 0; + this->int_alg = AUTH_UNDEFINED; + this->int_size = 0; this->policies = linked_list_create(); this->my_ts = linked_list_create(); this->other_ts = linked_list_create(); diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h index b801dd012..5bd66acad 100644 --- a/src/charon/sa/child_sa.h +++ b/src/charon/sa/child_sa.h @@ -1,13 +1,7 @@ -/** - * @file child_sa.h - * - * @brief Interface of child_sa_t. - * - */ - /* + * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2006-2007 Martin Willi - * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006 Daniel Roethlisberger * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -19,8 +13,14 @@ * 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. + * + * $Id: child_sa.h 3920 2008-05-08 16:19:11Z tobias $ */ +/** + * @defgroup child_sa child_sa + * @{ @ingroup sa + */ #ifndef CHILD_SA_H_ #define CHILD_SA_H_ @@ -35,7 +35,7 @@ typedef struct child_sa_t child_sa_t; #include <config/child_cfg.h> /** - * @brief States of a CHILD_SA + * States of a CHILD_SA */ enum child_sa_state_t { @@ -71,7 +71,7 @@ enum child_sa_state_t { extern enum_name_t *child_sa_state_names; /** - * @brief Represents an IPsec SAs between two hosts. + * Represents an IPsec SAs between two hosts. * * A child_sa_t contains two SAs. SAs for both * directions are managed in one child_sa_t object. Both @@ -86,57 +86,47 @@ extern enum_name_t *child_sa_state_names; * - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal * * Once SAs are set up, policies can be added using add_policies. - * - * - * @b Constructors: - * - child_sa_create() - * - * @ingroup sa */ struct child_sa_t { /** - * @brief Get the name of the config this CHILD_SA uses. + * Get the name of the config this CHILD_SA uses. * - * @param this calling object - * @return name + * @return name */ char* (*get_name) (child_sa_t *this); /** - * @brief Get the reqid of the CHILD SA. + * Get the reqid of the CHILD SA. * * Every CHILD_SA has a reqid. The kernel uses this ID to * identify it. * - * @param this calling object * @return reqid of the CHILD SA */ u_int32_t (*get_reqid)(child_sa_t *this); /** - * @brief Get the SPI of this CHILD_SA. + * Get the SPI of this CHILD_SA. * * Set the boolean parameter inbound to TRUE to * get the SPI for which we receive packets, use * FALSE to get those we use for sending packets. * - * @param this calling object * @param inbound TRUE to get inbound SPI, FALSE for outbound. * @return spi of the CHILD SA */ u_int32_t (*get_spi) (child_sa_t *this, bool inbound); /** - * @brief Get the protocol which this CHILD_SA uses to protect traffic. + * Get the protocol which this CHILD_SA uses to protect traffic. * - * @param this calling object * @return AH | ESP */ protocol_id_t (*get_protocol) (child_sa_t *this); /** - * @brief Get info and statistics about this CHILD_SA. + * Get info and statistics about this CHILD_SA. * * @param mode mode this IKE_SA uses * @param encr_algo encryption algorithm used by this CHILD_SA. @@ -155,7 +145,7 @@ struct child_sa_t { u_int32_t *use_fwd); /** - * @brief Allocate SPIs for given proposals. + * Allocate SPIs for given proposals. * * Since the kernel manages SPIs for us, we need * to allocate them. If a proposal contains more @@ -163,15 +153,13 @@ struct child_sa_t { * allocated. SPIs are stored internally and written * back to the proposal. * - * @param this calling object * @param proposals list of proposals for which SPIs are allocated */ status_t (*alloc)(child_sa_t *this, linked_list_t* proposals); /** - * @brief Install the kernel SAs for a proposal, without previous SPI allocation. + * Install the kernel SAs for a proposal, without previous SPI allocation. * - * @param this calling object * @param proposal proposal for which SPIs are allocated * @param mode mode for the CHILD_SA * @param prf_plus key material to use for key derivation @@ -181,11 +169,10 @@ struct child_sa_t { prf_plus_t *prf_plus); /** - * @brief Install the kernel SAs for a proposal, after SPIs have been allocated. + * Install the kernel SAs for a proposal, after SPIs have been allocated. * * Updates an SA, for which SPIs are already allocated via alloc(). * - * @param this calling object * @param proposal proposal for which SPIs are allocated * @param mode mode for the CHILD_SA * @param prf_plus key material to use for key derivation @@ -195,11 +182,10 @@ struct child_sa_t { prf_plus_t *prf_plus); /** - * @brief Update the hosts in the kernel SAs and policies. + * Update the hosts in the kernel SAs and policies. * * The CHILD must be INSTALLED to do this update. * - * @param this calling object * @param me the new local host * @param other the new remote host * @param TRUE to use UDP encapsulation for NAT traversal @@ -209,12 +195,11 @@ struct child_sa_t { bool encap); /** - * @brief Install the policies using some traffic selectors. + * Install the policies using some traffic selectors. * * Supplied lists of traffic_selector_t's specify the policies * to use for this child sa. * - * @param this calling object * @param my_ts traffic selectors for local site * @param other_ts traffic selectors for remote site * @param mode mode for the SA: tunnel/transport @@ -224,18 +209,16 @@ struct child_sa_t { linked_list_t *other_ts_list, mode_t mode); /** - * @brief Get the traffic selectors of added policies of local host. + * Get the traffic selectors of added policies of local host. * - * @param this calling object * @param local TRUE for own traffic selectors, FALSE for remote * @return list of traffic selectors */ linked_list_t* (*get_traffic_selectors) (child_sa_t *this, bool local); /** - * @brief Get the time of this child_sa_t's last use (i.e. last use of any of its policies) + * Get the time of this child_sa_t's last use (i.e. last use of any of its policies) * - * @param this calling object * @param inbound query for in- or outbound usage * @param use_time the time * @return SUCCESS or FAILED @@ -243,48 +226,58 @@ struct child_sa_t { status_t (*get_use_time) (child_sa_t *this, bool inbound, time_t *use_time); /** - * @brief Get the state of the CHILD_SA. - * - * @param this calling object + * Get the state of the CHILD_SA. */ child_sa_state_t (*get_state) (child_sa_t *this); /** - * @brief Set the state of the CHILD_SA. + * Set the state of the CHILD_SA. * - * @param this calling object + * @param state state to set on CHILD_SA */ void (*set_state) (child_sa_t *this, child_sa_state_t state); /** - * @brief Get the config used to set up this child sa. + * Get the config used to set up this child sa. * - * @param this calling object * @return child_cfg */ child_cfg_t* (*get_config) (child_sa_t *this); /** - * @brief Set the virtual IP used received from IRAS. + * Set the virtual IP used received from IRAS. * * To allow proper setup of firewall rules, the virtual IP is required * for filtering. * - * @param this calling object * @param ip own virtual IP */ void (*set_virtual_ip) (child_sa_t *this, host_t *ip); /** - * @brief Destroys a child_sa. - * - * @param this calling object + * Activate IPComp by setting the transform ID and CPI values. + * + * @param ipcomp the IPComp transform to use + * @param other_cpi other Compression Parameter Index + */ + void (*activate_ipcomp) (child_sa_t *this, ipcomp_transform_t ipcomp, + u_int16_t other_cpi); + + /** + * Returns the Compression Parameter Index (CPI) allocated from the kernel. + * + * @return allocated CPI + */ + u_int16_t (*get_my_cpi) (child_sa_t *this); + + /** + * Destroys a child_sa. */ void (*destroy) (child_sa_t *this); }; /** - * @brief Constructor to create a new child_sa_t. + * Constructor to create a new child_sa_t. * * @param me own address * @param other remote address @@ -294,11 +287,9 @@ struct child_sa_t { * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise * @param encap TRUE to enable UDP encapsulation (NAT traversal) * @return child_sa_t object - * - * @ingroup sa */ child_sa_t * child_sa_create(host_t *me, host_t *other, identification_t *my_id, identification_t* other_id, child_cfg_t *config, u_int32_t reqid, bool encap); -#endif /*CHILD_SA_H_*/ +#endif /*CHILD_SA_H_ @} */ diff --git a/src/charon/sa/connect_manager.c b/src/charon/sa/connect_manager.c index 06755fa9c..19ceea666 100644 --- a/src/charon/sa/connect_manager.c +++ b/src/charon/sa/connect_manager.c @@ -1,12 +1,5 @@ -/** - * @file connect_manager.c - * - * @brief Implementation of connect_manager_t. - * - */ - /* - * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2007-2008 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +11,8 @@ * 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. + * + * $Id: connect_manager.c 3792 2008-04-10 12:51:04Z tobias $ */ #include "connect_manager.h" @@ -34,13 +29,20 @@ #include <encoding/payloads/endpoint_notify.h> /* base timeout - * the sending interval is P2P_INTERVAL * active checklists (N) - * retransmission timeout is P2P_INTERVAL * N * checks in waiting state (NW) */ -#define P2P_INTERVAL 20 /* ms */ -/* min retransmission timeout (RTO is P2P_INTERVAL * N * checks in waiting state) */ -#define P2P_RTO_MIN 100 /* ms */ -/* max number of retransmissions (+ the initial check) */ -#define P2P_MAX_RETRANS 2 + * the check interval is ME_INTERVAL */ +#define ME_INTERVAL 25 /* ms */ +/* retransmission timeout is first ME_INTERVAL for ME_BOOST retransmissions + * then gets reduced to ME_INTERVAL * ME_RETRANS_BASE ^ (sent retransmissions - ME_BOOST). */ +/* number of initial retransmissions sent in short interval */ +#define ME_BOOST 2 +/* base for retransmissions */ +#define ME_RETRANS_BASE 1.8 +/* max number of retransmissions */ +#define ME_MAX_RETRANS 13 + +/* time to wait before the initiator finishes the connectivity checks after + * the first check has succeeded */ +#define ME_WAIT_TO_FINISH 1000 /* ms */ typedef struct private_connect_manager_t private_connect_manager_t; @@ -70,7 +72,7 @@ struct private_connect_manager_t { linked_list_t *initiated; /** - * Linked list with checklists (hash table with session ID as key would be better). + * Linked list with checklists (hash table with connect ID as key would be better). */ linked_list_t *checklists; }; @@ -180,8 +182,8 @@ struct check_list_t { linked_list_t *endpoints; } responder; - /** session id */ - chunk_t session_id; + /** connect id */ + chunk_t connect_id; /** list of endpoint pairs */ linked_list_t *pairs; @@ -195,6 +197,12 @@ struct check_list_t { /** TRUE if this is the initiator */ bool is_initiator; + /** TRUE if the initiator is finishing the checks */ + bool is_finishing; + + /** the current sender job */ + job_t *sender; + }; /** @@ -205,7 +213,7 @@ static void check_list_destroy(check_list_t *this) DESTROY_IF(this->initiator.id); DESTROY_IF(this->responder.id); - chunk_free(&this->session_id); + chunk_free(&this->connect_id); chunk_free(&this->initiator.key); chunk_free(&this->responder.key); @@ -223,12 +231,12 @@ static void check_list_destroy(check_list_t *this) * Creates a new checklist */ static check_list_t *check_list_create(identification_t *initiator, identification_t *responder, - chunk_t session_id, chunk_t initiator_key, linked_list_t *initiator_endpoints, + chunk_t connect_id, chunk_t initiator_key, linked_list_t *initiator_endpoints, bool is_initiator) { check_list_t *this = malloc_thing(check_list_t); - this->session_id = chunk_clone(session_id); + this->connect_id = chunk_clone(connect_id); this->initiator.id = initiator->clone(initiator); this->initiator.key = chunk_clone(initiator_key); @@ -242,43 +250,7 @@ static check_list_t *check_list_create(identification_t *initiator, identificati this->triggered = linked_list_create(); this->state = CHECK_NONE; this->is_initiator = is_initiator; - - return this; -} - - -typedef struct waiting_sa_t waiting_sa_t; - -/** - * For an initiator, the data stored about a waiting mediated sa - */ -struct waiting_sa_t { - /** ike sa id */ - ike_sa_id_t *ike_sa_id; - - /** list of child_cfg_t */ - linked_list_t *childs; -}; - -/** - * Destroys a queued mediated sa - */ -static void waiting_sa_destroy(waiting_sa_t *this) -{ - DESTROY_IF(this->ike_sa_id); - this->childs->destroy_offset(this->childs, offsetof(child_cfg_t, destroy)); - free(this); -} - -/** - * Creates a new mediated sa object - */ -static waiting_sa_t *waiting_sa_create(ike_sa_id_t *ike_sa_id) -{ - waiting_sa_t *this = malloc_thing(waiting_sa_t); - - this->ike_sa_id = ike_sa_id->clone(ike_sa_id); - this->childs = linked_list_create(); + this->is_finishing = FALSE; return this; } @@ -306,7 +278,7 @@ static void initiated_destroy(initiated_t *this) { DESTROY_IF(this->id); DESTROY_IF(this->peer_id); - this->mediated->destroy_function(this->mediated, (void*)waiting_sa_destroy); + this->mediated->destroy_offset(this->mediated, offsetof(ike_sa_id_t, destroy)); free(this); } @@ -340,8 +312,8 @@ struct check_t { /** destination of the connectivity check */ host_t *dst; - /** session id */ - chunk_t session_id; + /** connect id */ + chunk_t connect_id; /** endpoint */ endpoint_notify_t *endpoint; @@ -349,8 +321,8 @@ struct check_t { /** raw endpoint payload (to verify the signature) */ chunk_t endpoint_raw; - /** cookie */ - chunk_t cookie; + /** connect auth */ + chunk_t auth; }; /** @@ -358,9 +330,11 @@ struct check_t { */ static void check_destroy(check_t *this) { - chunk_free(&this->session_id); + chunk_free(&this->connect_id); chunk_free(&this->endpoint_raw); - chunk_free(&this->cookie); + chunk_free(&this->auth); + DESTROY_IF(this->src); + DESTROY_IF(this->dst); DESTROY_IF(this->endpoint); free(this); } @@ -372,9 +346,11 @@ static check_t *check_create() { check_t *this = malloc_thing(check_t); - this->session_id = chunk_empty; - this->cookie = chunk_empty; + this->connect_id = chunk_empty; + this->auth = chunk_empty; this->endpoint_raw = chunk_empty; + this->src = NULL; + this->dst = NULL; this->endpoint = NULL; this->mid = 0; @@ -382,76 +358,52 @@ static check_t *check_create() return this; } -typedef struct sender_data_t sender_data_t; +typedef struct callback_data_t callback_data_t; /** - * Data required by the sender + * Data required by several callback jobs used in this file */ -struct sender_data_t { +struct callback_data_t { /** connect manager */ private_connect_manager_t *connect_manager; - /** session id */ - chunk_t session_id; + /** connect id */ + chunk_t connect_id; + + /** message (pair) id */ + u_int32_t mid; }; /** - * Destroys a sender data object + * Destroys a callback data object */ -static void sender_data_destroy(sender_data_t *this) +static void callback_data_destroy(callback_data_t *this) { - chunk_free(&this->session_id); + chunk_free(&this->connect_id); free(this); } /** - * Creates a new sender data object + * Creates a new callback data object */ -static sender_data_t *sender_data_create(private_connect_manager_t *connect_manager, chunk_t session_id) +static callback_data_t *callback_data_create(private_connect_manager_t *connect_manager, + chunk_t connect_id) { - sender_data_t *this = malloc_thing(sender_data_t); + callback_data_t *this = malloc_thing(callback_data_t); this->connect_manager = connect_manager; - this->session_id = session_id; + this->connect_id = chunk_clone(connect_id); + this->mid = 0; return this; } -typedef struct retransmit_data_t retransmit_data_t; - -/** - * Data required by the retransmission job - */ -struct retransmit_data_t { - /** connect manager */ - private_connect_manager_t *connect_manager; - - /** session id */ - chunk_t session_id; - - /** message (pair) id */ - u_int32_t mid; -}; - -/** - * Destroys a retransmission data object - */ -static void retransmit_data_destroy(retransmit_data_t *this) -{ - chunk_free(&this->session_id); - free(this); -} - /** * Creates a new retransmission data object */ -static retransmit_data_t *retransmit_data_create(private_connect_manager_t *connect_manager, - chunk_t session_id, u_int32_t mid) +static callback_data_t *retransmit_data_create(private_connect_manager_t *connect_manager, + chunk_t connect_id, u_int32_t mid) { - retransmit_data_t *this = malloc_thing(retransmit_data_t); - - this->connect_manager = connect_manager; - this->session_id = session_id; + callback_data_t *this = callback_data_create(connect_manager, connect_id); this->mid = mid; - return this; } @@ -529,34 +481,19 @@ static void remove_initiated(private_connect_manager_t *this, initiated_t *initi } /** - * Finds a waiting sa + * Find the checklist with a specific connect ID */ -static bool match_waiting_sa(waiting_sa_t *current, ike_sa_id_t *ike_sa_id) +static bool match_checklist_by_id(check_list_t *current, chunk_t *connect_id) { - return ike_sa_id->equals(ike_sa_id, current->ike_sa_id); -} - -static status_t get_waiting_sa(initiated_t *initiated, ike_sa_id_t *ike_sa_id, waiting_sa_t **waiting_sa) -{ - return initiated->mediated->find_first(initiated->mediated, - (linked_list_match_t)match_waiting_sa, - (void**)waiting_sa, ike_sa_id); -} - -/** - * Find the checklist with a specific session ID - */ -static bool match_checklist_by_id(check_list_t *current, chunk_t *session_id) -{ - return chunk_equals(*session_id, current->session_id); + return chunk_equals(*connect_id, current->connect_id); } static status_t get_checklist_by_id(private_connect_manager_t *this, - chunk_t session_id, check_list_t **check_list) + chunk_t connect_id, check_list_t **check_list) { return this->checklists->find_first(this->checklists, (linked_list_match_t)match_checklist_by_id, - (void**)check_list, &session_id); + (void**)check_list, &connect_id); } /** @@ -595,51 +532,6 @@ static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, endpoi } /** - * Updates the state of the whole checklist - */ -static void update_checklist_state(check_list_t *checklist) -{ - iterator_t *iterator; - endpoint_pair_t *current; - bool in_progress = FALSE, succeeded = FALSE; - - iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE); - while (iterator->iterate(iterator, (void**)¤t)) - { - switch(current->state) - { - case CHECK_WAITING: - /* at least one is still waiting -> checklist remains - * in waiting state */ - iterator->destroy(iterator); - return; - case CHECK_IN_PROGRESS: - in_progress = TRUE; - break; - case CHECK_SUCCEEDED: - succeeded = TRUE; - break; - default: - break; - } - } - iterator->destroy(iterator); - - if (in_progress) - { - checklist->state = CHECK_IN_PROGRESS; - } - else if (succeeded) - { - checklist->state = CHECK_SUCCEEDED; - } - else - { - checklist->state = CHECK_FAILED; - } -} - -/** * Inserts an endpoint pair into the list of pairs ordered by priority (high to low) */ static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair) @@ -681,14 +573,14 @@ static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local, host_t *r (void**)pair, local, remote); } -/** - * Searches for a pair with a specific id - */ static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id) { return current->id == *id; } +/** + * Searches for a pair with a specific id + */ static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, endpoint_pair_t **pair) { return checklist->pairs->find_first(checklist->pairs, @@ -696,14 +588,14 @@ static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, endpoint_p (void**)pair, &id); } -/** - * Returns the best pair of state CHECK_SUCCEEDED from a checklist. - */ static bool match_succeeded_pair(endpoint_pair_t *current) { return current->state == CHECK_SUCCEEDED; } +/** + * Returns the best pair of state CHECK_SUCCEEDED from a checklist. + */ static status_t get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **pair) { return checklist->pairs->find_first(checklist->pairs, @@ -711,14 +603,14 @@ static status_t get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **p (void**)pair); } -/** - * Returns and *removes* the first triggered pair in state CHECK_WAITING. - */ static bool match_waiting_pair(endpoint_pair_t *current) { return current->state == CHECK_WAITING; } +/** + * Returns and *removes* the first triggered pair in state CHECK_WAITING. + */ static status_t get_triggered_pair(check_list_t *checklist, endpoint_pair_t **pair) { iterator_t *iterator; @@ -746,6 +638,24 @@ static status_t get_triggered_pair(check_list_t *checklist, endpoint_pair_t **pa } /** + * Prints all the pairs on a checklist + */ +static void print_checklist(check_list_t *checklist) +{ + iterator_t *iterator; + endpoint_pair_t *current; + + DBG1(DBG_IKE, "pairs on checklist %#B:", &checklist->connect_id); + iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + DBG1(DBG_IKE, " * %#H - %#H (%d)", current->local, current->remote, + current->priority); + } + iterator->destroy(iterator); +} + +/** * Prunes identical pairs with lower priority from the list * Note: this function also numbers the remaining pairs serially */ @@ -775,7 +685,7 @@ static void prune_pairs(linked_list_t *pairs) * order, and we iterate the list from the beginning, we are * sure that the priority of 'other' is lower than that of * 'current', remove it */ - DBG1(DBG_IKE, "pruning endpoint pair %H - %H with priority %d", + DBG1(DBG_IKE, "pruning endpoint pair %#H - %#H with priority %d", other->local, other->remote, other->priority); search->remove(search); endpoint_pair_destroy(other); @@ -792,6 +702,7 @@ static void prune_pairs(linked_list_t *pairs) */ static void build_pairs(check_list_t *checklist) { + /* FIXME: limit endpoints and pairs */ iterator_t *iterator_i, *iterator_r; endpoint_notify_t *initiator, *responder; @@ -812,6 +723,8 @@ static void build_pairs(check_list_t *checklist) iterator_r->destroy(iterator_r); } iterator_i->destroy(iterator_i); + + print_checklist(checklist); prune_pairs(checklist->pairs); } @@ -838,45 +751,45 @@ static status_t process_payloads(message_t *message, check_t *check) switch (notify->get_notify_type(notify)) { - case P2P_ENDPOINT: + case ME_ENDPOINT: { if (check->endpoint) { - DBG1(DBG_IKE, "connectivity check contains multiple P2P_ENDPOINT notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple ME_ENDPOINT notifies"); break; } endpoint_notify_t *endpoint = endpoint_notify_create_from_payload(notify); if (!endpoint) { - DBG1(DBG_IKE, "received invalid P2P_ENDPOINT notify"); + DBG1(DBG_IKE, "received invalid ME_ENDPOINT notify"); break; } check->endpoint = endpoint; check->endpoint_raw = chunk_clone(notify->get_notification_data(notify)); - DBG2(DBG_IKE, "received P2P_ENDPOINT notify"); + DBG2(DBG_IKE, "received ME_ENDPOINT notify"); break; } - case P2P_SESSIONID: + case ME_CONNECTID: { - if (check->session_id.ptr) + if (check->connect_id.ptr) { - DBG1(DBG_IKE, "connectivity check contains multiple P2P_SESSIONID notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTID notifies"); break; } - check->session_id = chunk_clone(notify->get_notification_data(notify)); - DBG3(DBG_IKE, "received p2p_sessionid %B", &check->session_id); + check->connect_id = chunk_clone(notify->get_notification_data(notify)); + DBG2(DBG_IKE, "received ME_CONNECTID %#B", &check->connect_id); break; } - case COOKIE: + case ME_CONNECTAUTH: { - if (check->cookie.ptr) + if (check->auth.ptr) { - DBG1(DBG_IKE, "connectivity check contains multiple COOKIE notifies"); + DBG1(DBG_IKE, "connectivity check contains multiple ME_CONNECTAUTH notifies"); break; } - check->cookie = chunk_clone(notify->get_notification_data(notify)); - DBG3(DBG_IKE, "received cookie %B", &check->cookie); + check->auth = chunk_clone(notify->get_notification_data(notify)); + DBG2(DBG_IKE, "received ME_CONNECTAUTH %#B", &check->auth); break; } default: @@ -885,7 +798,7 @@ static status_t process_payloads(message_t *message, check_t *check) } iterator->destroy(iterator); - if (!check->session_id.ptr || !check->endpoint || !check->cookie.ptr) + if (!check->connect_id.ptr || !check->endpoint || !check->auth.ptr) { DBG1(DBG_IKE, "at least one payload was missing from the connectivity check"); return FAILED; @@ -900,42 +813,129 @@ static status_t process_payloads(message_t *message, check_t *check) static chunk_t build_signature(private_connect_manager_t *this, check_list_t *checklist, check_t *check, bool outbound) { + u_int32_t mid; chunk_t mid_chunk, key_chunk, sig_chunk; chunk_t sig_hash; - mid_chunk = chunk_from_thing(check->mid); + mid = htonl(check->mid); + mid_chunk = chunk_from_thing(mid); key_chunk = (checklist->is_initiator && outbound) || (!checklist->is_initiator && !outbound) ? checklist->initiator.key : checklist->responder.key; - /* signature = SHA1( MID | P2P_SESSIONID | P2P_ENDPOINT | P2P_SESSIONKEY ) */ - sig_chunk = chunk_cat("cccc", mid_chunk, check->session_id, check->endpoint_raw, key_chunk); + /* signature = SHA1( MID | ME_CONNECTID | ME_ENDPOINT | ME_CONNECTKEY ) */ + sig_chunk = chunk_cat("cccc", mid_chunk, check->connect_id, check->endpoint_raw, key_chunk); this->hasher->allocate_hash(this->hasher, sig_chunk, &sig_hash); - DBG3(DBG_IKE, "sig_chunk %B", &sig_chunk); - DBG3(DBG_IKE, "sig_hash %B", &sig_hash); + DBG3(DBG_IKE, "sig_chunk %#B", &sig_chunk); + DBG3(DBG_IKE, "sig_hash %#B", &sig_hash); chunk_free(&sig_chunk); return sig_hash; } -static void queue_retransmission(private_connect_manager_t *this, chunk_t session_id, u_int32_t mid); +static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair); static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time); static void finish_checks(private_connect_manager_t *this, check_list_t *checklist); /** + * After one of the initiator's pairs has succeeded we finish the checks without + * waiting for all the timeouts + */ +static job_requeue_t initiator_finish(callback_data_t *data) +{ + private_connect_manager_t *this = data->connect_manager; + + pthread_mutex_lock(&(this->mutex)); + + check_list_t *checklist; + if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) + { + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish connectivity checks", + &data->connect_id); + pthread_mutex_unlock(&(this->mutex)); + return JOB_REQUEUE_NONE; + } + + finish_checks(this, checklist); + + pthread_mutex_unlock(&(this->mutex)); + + return JOB_REQUEUE_NONE; +} + +/** + * Updates the state of the whole checklist + */ +static void update_checklist_state(private_connect_manager_t *this, check_list_t *checklist) +{ + iterator_t *iterator; + endpoint_pair_t *current; + bool in_progress = FALSE, succeeded = FALSE; + + iterator = checklist->pairs->create_iterator(checklist->pairs, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + switch(current->state) + { + case CHECK_WAITING: + /* at least one is still waiting -> checklist remains + * in waiting state */ + iterator->destroy(iterator); + return; + case CHECK_IN_PROGRESS: + in_progress = TRUE; + break; + case CHECK_SUCCEEDED: + succeeded = TRUE; + break; + default: + break; + } + } + iterator->destroy(iterator); + + if (checklist->is_initiator && succeeded && !checklist->is_finishing) + { + /* instead of waiting until all checks have finished (i.e. all + * retransmissions have failed) the initiator finishes the checks + * right after the first check has succeeded. to allow a probably + * better pair to succeed, we still wait a certain time */ + DBG2(DBG_IKE, "fast finishing checks for checklist '%#B'", &checklist->connect_id); + + callback_data_t *data = callback_data_create(this, checklist->connect_id); + job_t *job = (job_t*)callback_job_create((callback_job_cb_t)initiator_finish, data, (callback_job_cleanup_t)callback_data_destroy, NULL); + charon->scheduler->schedule_job(charon->scheduler, job, ME_WAIT_TO_FINISH); + checklist->is_finishing = TRUE; + } + + if (in_progress) + { + checklist->state = CHECK_IN_PROGRESS; + } + else if (succeeded) + { + checklist->state = CHECK_SUCCEEDED; + } + else + { + checklist->state = CHECK_FAILED; + } +} + +/** * This function is triggered for each sent check after a specific timeout */ -static job_requeue_t retransmit(retransmit_data_t *data) +static job_requeue_t retransmit(callback_data_t *data) { private_connect_manager_t *this = data->connect_manager; pthread_mutex_lock(&(this->mutex)); check_list_t *checklist; - if (get_checklist_by_id(this, data->session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found, can't retransmit connectivity check", - &data->session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit connectivity check", + &data->connect_id); pthread_mutex_unlock(&(this->mutex)); return JOB_REQUEUE_NONE; } @@ -955,20 +955,20 @@ static job_requeue_t retransmit(retransmit_data_t *data) goto retransmit_end; } - if (++pair->retransmitted >= P2P_MAX_RETRANS) + if (++pair->retransmitted > ME_MAX_RETRANS) { - DBG2(DBG_IKE, "pair with id '%d' failed after %d tries", - data->mid, pair->retransmitted); + DBG2(DBG_IKE, "pair with id '%d' failed after %d retransmissions", + data->mid, ME_MAX_RETRANS); pair->state = CHECK_FAILED; goto retransmit_end; } charon->sender->send(charon->sender, pair->packet->clone(pair->packet)); - queue_retransmission(this, checklist->session_id, pair->id); + queue_retransmission(this, checklist, pair); retransmit_end: - update_checklist_state(checklist); + update_checklist_state(this, checklist); switch(checklist->state) { @@ -989,11 +989,20 @@ retransmit_end: /** * Queues a retransmission job */ -static void queue_retransmission(private_connect_manager_t *this, chunk_t session_id, u_int32_t mid) +static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair) { - retransmit_data_t *data = retransmit_data_create(this, chunk_clone(session_id), mid); - job_t *job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)retransmit_data_destroy, NULL); - charon->scheduler->schedule_job(charon->scheduler, (job_t*)job, P2P_RTO_MIN); + callback_data_t *data = retransmit_data_create(this, checklist->connect_id, pair->id); + job_t *job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)callback_data_destroy, NULL); + + u_int32_t retransmission = pair->retransmitted + 1; + u_int32_t rto = ME_INTERVAL; + if (retransmission > ME_BOOST) + { + rto = (u_int32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST)); + } + DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms", retransmission, pair->id, rto); + + charon->scheduler->schedule_job(charon->scheduler, (job_t*)job, rto); } /** @@ -1009,16 +1018,21 @@ static void send_check(private_connect_manager_t *this, check_list_t *checklist, message->set_destination(message, check->dst->clone(check->dst)); message->set_source(message, check->src->clone(check->src)); - message->set_ike_sa_id(message, ike_sa_id_create(0, 0, request)); + ike_sa_id_t *ike_sa_id = ike_sa_id_create(0, 0, request); + message->set_ike_sa_id(message, ike_sa_id); + ike_sa_id->destroy(ike_sa_id); - message->add_notify(message, FALSE, P2P_SESSIONID, check->session_id); + message->add_notify(message, FALSE, ME_CONNECTID, check->connect_id); + DBG2(DBG_IKE, "send ME_CONNECTID %#B", &check->connect_id); notify_payload_t *endpoint = check->endpoint->build_notify(check->endpoint); check->endpoint_raw = chunk_clone(endpoint->get_notification_data(endpoint)); message->add_payload(message, (payload_t*)endpoint); + DBG2(DBG_IKE, "send ME_ENDPOINT notify"); - check->cookie = build_signature(this, checklist, check, TRUE); - message->add_notify(message, FALSE, COOKIE, check->cookie); + check->auth = build_signature(this, checklist, check, TRUE); + message->add_notify(message, FALSE, ME_CONNECTAUTH, check->auth); + DBG2(DBG_IKE, "send ME_CONNECTAUTH %#B", &check->auth); packet_t *packet; if (message->generate(message, NULL, NULL, &packet) == SUCCESS) @@ -1029,42 +1043,55 @@ static void send_check(private_connect_manager_t *this, check_list_t *checklist, { DESTROY_IF(pair->packet); pair->packet = packet; - queue_retransmission(this, checklist->session_id, pair->id); + pair->retransmitted = 0; + queue_retransmission(this, checklist, pair); } else { packet->destroy(packet); } } + message->destroy(message); } /** * Queues a triggered check */ -static void queue_triggered_check(check_list_t *checklist, endpoint_pair_t *pair) +static void queue_triggered_check(private_connect_manager_t *this, + check_list_t *checklist, endpoint_pair_t *pair) { + DBG2(DBG_IKE, "queueing triggered check for pair '%d'", pair->id); pair->state = CHECK_WAITING; checklist->triggered->insert_last(checklist->triggered, pair); + + if (!checklist->sender) + { + /* if the sender is not running we restart it */ + schedule_checks(this, checklist, ME_INTERVAL); + } } /** * This function is triggered for each checklist at a specific interval */ -static job_requeue_t sender(sender_data_t *data) +static job_requeue_t sender(callback_data_t *data) { private_connect_manager_t *this = data->connect_manager; pthread_mutex_lock(&(this->mutex)); - + check_list_t *checklist; - if (get_checklist_by_id(this, data->session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found, can't send connectivity check", - &data->session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send connectivity check", + &data->connect_id); pthread_mutex_unlock(&(this->mutex)); return JOB_REQUEUE_NONE; } + /* reset the sender */ + checklist->sender = NULL; + endpoint_pair_t *pair; if (get_triggered_pair(checklist, &pair) != SUCCESS) { @@ -1087,7 +1114,7 @@ static job_requeue_t sender(sender_data_t *data) check->mid = pair->id; check->src = pair->local->clone(pair->local); check->dst = pair->remote->clone(pair->remote); - check->session_id = chunk_clone(checklist->session_id); + check->connect_id = chunk_clone(checklist->connect_id); check->endpoint = endpoint_notify_create(); pair->state = CHECK_IN_PROGRESS; @@ -1097,8 +1124,7 @@ static job_requeue_t sender(sender_data_t *data) check_destroy(check); /* schedule this job again */ - u_int32_t N = this->checklists->get_count(this->checklists); - schedule_checks(this, checklist, P2P_INTERVAL * N); + schedule_checks(this, checklist, ME_INTERVAL); pthread_mutex_unlock(&(this->mutex)); @@ -1111,10 +1137,9 @@ static job_requeue_t sender(sender_data_t *data) */ static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time) { - chunk_t session_id = chunk_clone(checklist->session_id); - sender_data_t *data = sender_data_create(this, session_id); - job_t *job = (job_t*)callback_job_create((callback_job_cb_t)sender, data, (callback_job_cleanup_t)sender_data_destroy, NULL); - charon->scheduler->schedule_job(charon->scheduler, job, time); + callback_data_t *data = callback_data_create(this, checklist->connect_id); + checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender, data, (callback_job_cleanup_t)callback_data_destroy, NULL); + charon->scheduler->schedule_job(charon->scheduler, checklist->sender, time); } /** @@ -1128,12 +1153,12 @@ static job_requeue_t initiate_mediated(initiate_data_t *data) endpoint_pair_t *pair; if (get_best_valid_pair(checklist, &pair) == SUCCESS) { - waiting_sa_t *waiting_sa; + ike_sa_id_t *waiting_sa; iterator_t *iterator = initiated->mediated->create_iterator(initiated->mediated, TRUE); while (iterator->iterate(iterator, (void**)&waiting_sa)) { - ike_sa_t *sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, waiting_sa->ike_sa_id); - if (sa->initiate_mediated(sa, pair->local, pair->remote, waiting_sa->childs) != SUCCESS) + ike_sa_t *sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, waiting_sa); + if (sa->initiate_mediated(sa, pair->local, pair->remote, checklist->connect_id) != SUCCESS) { SIG(IKE_UP_FAILED, "establishing the mediated connection failed"); charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, sa); @@ -1175,11 +1200,6 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli "and '%D'", checklist->initiator.id, checklist->responder.id); } } - - /* remove_checklist(this, checklist); - * check_list_destroy(checklist); - * FIXME: we should do this ^^^ after a specific timeout on the - * responder side */ } /** @@ -1212,7 +1232,7 @@ static void process_response(private_connect_manager_t *this, check_t *check, local_endpoints->insert_last(local_endpoints, local_endpoint); } - update_checklist_state(checklist); + update_checklist_state(this, checklist); switch(checklist->state) { @@ -1253,12 +1273,12 @@ static void process_request(private_connect_manager_t *this, check_t *check, { case CHECK_IN_PROGRESS: /* prevent retransmissions */ - pair->retransmitted = P2P_MAX_RETRANS; + pair->retransmitted = ME_MAX_RETRANS; /* FIXME: we should wait to the next rto to send the triggered check * fall-through */ case CHECK_WAITING: case CHECK_FAILED: - queue_triggered_check(checklist, pair); + queue_triggered_check(this, checklist, pair); break; case CHECK_SUCCEEDED: default: @@ -1277,7 +1297,7 @@ static void process_request(private_connect_manager_t *this, check_t *check, insert_pair_by_priority(checklist->pairs, pair); - queue_triggered_check(checklist, pair); + queue_triggered_check(this, checklist, pair); local_endpoint->destroy(local_endpoint); } @@ -1288,7 +1308,7 @@ static void process_request(private_connect_manager_t *this, check_t *check, response->mid = check->mid; response->src = check->dst->clone(check->dst); response->dst = check->src->clone(check->src); - response->session_id = chunk_clone(check->session_id); + response->connect_id = chunk_clone(check->connect_id); response->endpoint = peer_reflexive; send_check(this, checklist, response, pair, FALSE); @@ -1313,7 +1333,9 @@ static void process_check(private_connect_manager_t *this, message_t *message) check_t *check = check_create(); check->mid = message->get_message_id(message); check->src = message->get_source(message); + check->src = check->src->clone(check->src); check->dst = message->get_destination(message); + check->dst = check->dst->clone(check->dst); if (process_payloads(message, check) != SUCCESS) { @@ -1326,17 +1348,17 @@ static void process_check(private_connect_manager_t *this, message_t *message) pthread_mutex_lock(&(this->mutex)); check_list_t *checklist; - if (get_checklist_by_id(this, check->session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, check->connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found", - &check->session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found", + &check->connect_id); check_destroy(check); pthread_mutex_unlock(&(this->mutex)); return; } chunk_t sig = build_signature(this, checklist, check, FALSE); - if (!chunk_equals(sig, check->cookie)) + if (!chunk_equals(sig, check->auth)) { DBG1(DBG_IKE, "connectivity check verification failed"); check_destroy(check); @@ -1365,7 +1387,7 @@ static void process_check(private_connect_manager_t *this, message_t *message) */ static bool check_and_register(private_connect_manager_t *this, identification_t *id, identification_t *peer_id, - ike_sa_id_t *mediated_sa, child_cfg_t *child) + ike_sa_id_t *mediated_sa) { initiated_t *initiated; bool already_there = TRUE; @@ -1380,16 +1402,12 @@ static bool check_and_register(private_connect_manager_t *this, already_there = FALSE; } - waiting_sa_t *waiting_sa; - if (get_waiting_sa(initiated, mediated_sa, &waiting_sa) != SUCCESS) + if (initiated->mediated->find_first(initiated->mediated, + (linked_list_match_t)mediated_sa->equals, NULL, mediated_sa) != SUCCESS) { - waiting_sa = waiting_sa_create(mediated_sa); - initiated->mediated->insert_last(initiated->mediated, waiting_sa); + initiated->mediated->insert_last(initiated->mediated, mediated_sa->clone(mediated_sa)); } - child->get_ref(child); - waiting_sa->childs->insert_last(waiting_sa->childs, child); - pthread_mutex_unlock(&(this->mutex)); return already_there; @@ -1412,14 +1430,14 @@ static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *med return; } - waiting_sa_t *waiting_sa; + ike_sa_id_t *waiting_sa; iterator_t *iterator = initiated->mediated->create_iterator(initiated->mediated, TRUE); while (iterator->iterate(iterator, (void**)&waiting_sa)) { - job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa, - waiting_sa->ike_sa_id); + job_t *job = (job_t*)reinitiate_mediation_job_create(mediation_sa, waiting_sa); charon->processor->queue_job(charon->processor, job); } + iterator->destroy(iterator); pthread_mutex_unlock(&(this->mutex)); } @@ -1429,21 +1447,21 @@ static void check_and_initiate(private_connect_manager_t *this, ike_sa_id_t *med */ static status_t set_initiator_data(private_connect_manager_t *this, identification_t *initiator, identification_t *responder, - chunk_t session_id, chunk_t key, linked_list_t *endpoints, bool is_initiator) + chunk_t connect_id, chunk_t key, linked_list_t *endpoints, bool is_initiator) { check_list_t *checklist; pthread_mutex_lock(&(this->mutex)); - if (get_checklist_by_id(this, session_id, NULL) == SUCCESS) + if (get_checklist_by_id(this, connect_id, NULL) == SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' already exists, aborting", - &session_id); + DBG1(DBG_IKE, "checklist with id '%#B' already exists, aborting", + &connect_id); pthread_mutex_unlock(&(this->mutex)); return FAILED; } - checklist = check_list_create(initiator, responder, session_id, key, endpoints, is_initiator); + checklist = check_list_create(initiator, responder, connect_id, key, endpoints, is_initiator); this->checklists->insert_last(this->checklists, checklist); pthread_mutex_unlock(&(this->mutex)); @@ -1455,16 +1473,16 @@ static status_t set_initiator_data(private_connect_manager_t *this, * Implementation of connect_manager_t.set_responder_data. */ static status_t set_responder_data(private_connect_manager_t *this, - chunk_t session_id, chunk_t key, linked_list_t *endpoints) + chunk_t connect_id, chunk_t key, linked_list_t *endpoints) { check_list_t *checklist; pthread_mutex_lock(&(this->mutex)); - if (get_checklist_by_id(this, session_id, &checklist) != SUCCESS) + if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) { - DBG1(DBG_IKE, "checklist with id '%B' not found", - &session_id); + DBG1(DBG_IKE, "checklist with id '%#B' not found", + &connect_id); pthread_mutex_unlock(&(this->mutex)); return NOT_FOUND; } @@ -1484,6 +1502,33 @@ static status_t set_responder_data(private_connect_manager_t *this, } /** + * Implementation of connect_manager_t.stop_checks. + */ +static status_t stop_checks(private_connect_manager_t *this, chunk_t connect_id) +{ + check_list_t *checklist; + + pthread_mutex_lock(&(this->mutex)); + + if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) + { + DBG1(DBG_IKE, "checklist with id '%#B' not found", + &connect_id); + pthread_mutex_unlock(&(this->mutex)); + return NOT_FOUND; + } + + DBG1(DBG_IKE, "removing checklist with id '%#B'", &connect_id); + + remove_checklist(this, checklist); + check_list_destroy(checklist); + + pthread_mutex_unlock(&(this->mutex)); + + return SUCCESS; +} + +/** * Implementation of connect_manager_t.destroy. */ static void destroy(private_connect_manager_t *this) @@ -1507,13 +1552,21 @@ connect_manager_t *connect_manager_create() private_connect_manager_t *this = malloc_thing(private_connect_manager_t); this->public.destroy = (void(*)(connect_manager_t*))destroy; - this->public.check_and_register = (bool(*)(connect_manager_t*,identification_t*,identification_t*,ike_sa_id_t*,child_cfg_t*))check_and_register; + this->public.check_and_register = (bool(*)(connect_manager_t*,identification_t*,identification_t*,ike_sa_id_t*))check_and_register; this->public.check_and_initiate = (void(*)(connect_manager_t*,ike_sa_id_t*,identification_t*,identification_t*))check_and_initiate; this->public.set_initiator_data = (status_t(*)(connect_manager_t*,identification_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool))set_initiator_data; this->public.set_responder_data = (status_t(*)(connect_manager_t*,chunk_t,chunk_t,linked_list_t*))set_responder_data; this->public.process_check = (void(*)(connect_manager_t*,message_t*))process_check; + this->public.stop_checks = (status_t(*)(connect_manager_t*,chunk_t))stop_checks; + + this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (this->hasher == NULL) + { + DBG1(DBG_IKE, "unable to create connect manager, SHA1 not supported"); + free(this); + return NULL; + } - this->hasher = hasher_create(HASH_SHA1); this->checklists = linked_list_create(); this->initiated = linked_list_create(); diff --git a/src/charon/sa/connect_manager.h b/src/charon/sa/connect_manager.h index 2f3e9109b..38d8e7a49 100644 --- a/src/charon/sa/connect_manager.h +++ b/src/charon/sa/connect_manager.h @@ -1,12 +1,5 @@ -/** - * @file connect_manager.h - * - * @brief Interface of connect_manager_t. - * - */ - /* - * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2007-2008 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +11,13 @@ * 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. + * + * $Id: connect_manager.h 3792 2008-04-10 12:51:04Z tobias $ + */ + +/** + * @defgroup connect_manager connect_manager + * @{ @ingroup sa */ #ifndef CONNECT_MANAGER_H_ @@ -26,43 +26,33 @@ typedef struct connect_manager_t connect_manager_t; #include <encoding/message.h> -#include <config/child_cfg.h> #include <sa/ike_sa_id.h> #include <utils/identification.h> /** - * @brief The connection manager is responsible for establishing a direct + * The connection manager is responsible for establishing a direct * connection with another peer. - * - * @b Constructors: - * - connect_manager_create() - * - * @ingroup sa */ struct connect_manager_t { /** - * @brief Checks if a there is already a mediated connection registered + * Checks if a there is already a mediated connection registered * between two peers. * - * @param this the manager object * @param id my id * @param peer_id the other peer's id * @param mediated_sa the IKE_SA ID of the mediated connection - * @param child the CHILD_SA config of the mediated connection * @returns * - TRUE, if there was already a mediated connection registered * - FALSE, otherwise */ bool (*check_and_register) (connect_manager_t *this, - identification_t *id, identification_t *peer_id, - ike_sa_id_t *mediated_sa, child_cfg_t *child); + identification_t *id, identification_t *peer_id, ike_sa_id_t *mediated_sa); /** - * @brief Checks if there are waiting connections with a specific peer. + * Checks if there are waiting connections with a specific peer. * If so, reinitiate them. * - * @param this the manager object * @param id my id * @param peer_id the other peer's id */ @@ -70,29 +60,26 @@ struct connect_manager_t { identification_t *id, identification_t *peer_id); /** - * @brief Creates a checklist and sets the initiator's data. + * Creates a checklist and sets the initiator's data. * - * @param this the manager object * @param initiator ID of the initiator * @param responder ID of the responder - * @param session_id the session ID provided by the initiator + * @param connect_id the connect ID provided by the initiator * @param key the initiator's key * @param endpoints the initiator's endpoints * @param is_initiator TRUE, if the caller of this method is the initiator * FALSE, otherwise - * @returns - * SUCCESS + * @returns SUCCESS */ status_t (*set_initiator_data) (connect_manager_t *this, identification_t *initiator, identification_t *responder, - chunk_t session_id, chunk_t key, linked_list_t *endpoints, bool is_initiator); + chunk_t connect_id, chunk_t key, linked_list_t *endpoints, bool is_initiator); /** - * @brief Updates a checklist and sets the responder's data. The checklist's + * Updates a checklist and sets the responder's data. The checklist's * state is advanced to WAITING which means that checks will be sent. * - * @param this the manager object - * @param session_id the session ID + * @param connect_id the connect ID * @param chunk_t the responder's key * @param endpoints the responder's endpoints * @returns @@ -100,32 +87,37 @@ struct connect_manager_t { * - SUCCESS, otherwise */ status_t (*set_responder_data) (connect_manager_t *this, - chunk_t session_id, chunk_t key, linked_list_t *endpoints); + chunk_t connect_id, chunk_t key, linked_list_t *endpoints); + /** + * Stops checks for a checklist. Used after the responder received an IKE_SA_INIT + * request which contains a ME_CONNECTID payload. + * + * @param connect_id the connect ID + * @returns + * - NOT_FOUND, if the checklist has not been found + * - SUCCESS, otherwise + */ + status_t (*stop_checks) (connect_manager_t *this, chunk_t connect_id); /** - * @brief Processes a connectivity check + * Processes a connectivity check * - * @param this the manager object * @param message the received message */ void (*process_check) (connect_manager_t *this, message_t *message); /** - * @brief Destroys the manager with all data. - * - * @param this the manager object + * Destroys the manager with all data. */ void (*destroy) (connect_manager_t *this); }; /** - * @brief Create a manager. + * Create a manager. * * @returns connect_manager_t object - * - * @ingroup sa */ connect_manager_t *connect_manager_create(void); -#endif /*CONNECT_MANAGER_H_*/ +#endif /*CONNECT_MANAGER_H_ @} */ diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 93aa08965..384226380 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -1,12 +1,5 @@ -/** - * @file ike_sa.c - * - * @brief Implementation of ike_sa_t. - * - */ - /* - * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -21,6 +14,8 @@ * 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. + * + * $Id: ike_sa.c 4106 2008-06-25 11:40:50Z martin $ */ #include <sys/time.h> @@ -53,7 +48,8 @@ #include <sa/tasks/ike_auth.h> #include <sa/tasks/ike_auth_lifetime.h> #include <sa/tasks/ike_config.h> -#include <sa/tasks/ike_cert.h> +#include <sa/tasks/ike_cert_pre.h> +#include <sa/tasks/ike_cert_post.h> #include <sa/tasks/ike_rekey.h> #include <sa/tasks/ike_reauth.h> #include <sa/tasks/ike_delete.h> @@ -67,8 +63,8 @@ #include <processing/jobs/send_keepalive_job.h> #include <processing/jobs/rekey_ike_sa_job.h> -#ifdef P2P -#include <sa/tasks/ike_p2p.h> +#ifdef ME +#include <sa/tasks/ike_me.h> #include <processing/jobs/initiate_mediation_job.h> #endif @@ -122,6 +118,16 @@ struct private_ike_sa_t { peer_cfg_t *peer_cfg; /** + * associated authentication/authorization info for local peer + */ + auth_info_t *my_auth; + + /** + * associated authentication/authorization info for remote peer + */ + auth_info_t *other_auth; + + /** * Juggles tasks to process messages */ task_manager_t *task_manager; @@ -136,12 +142,22 @@ struct private_ike_sa_t { */ host_t *other_host; -#ifdef P2P +#ifdef ME + /** + * Are we mediation server + */ + bool is_mediation_server; + /** * Server reflexive host */ host_t *server_reflexive_host; -#endif /* P2P */ + + /** + * Connect ID + */ + chunk_t connect_id; +#endif /* ME */ /** * Identification used for us @@ -154,11 +170,6 @@ struct private_ike_sa_t { identification_t *other_id; /** - * CA that issued the certificate of other - */ - ca_info_t *other_ca; - - /** * set of extensions the peer supports */ ike_extension_t extensions; @@ -174,6 +185,11 @@ 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; @@ -390,6 +406,7 @@ static peer_cfg_t* get_peer_cfg(private_ike_sa_t *this) */ static void set_peer_cfg(private_ike_sa_t *this, peer_cfg_t *peer_cfg) { + DESTROY_IF(this->peer_cfg); peer_cfg->get_ref(peer_cfg); this->peer_cfg = peer_cfg; @@ -398,18 +415,6 @@ static void set_peer_cfg(private_ike_sa_t *this, peer_cfg_t *peer_cfg) this->ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); this->ike_cfg->get_ref(this->ike_cfg); } - - /* apply values, so we are ready to initate/acquire */ - if (this->my_host->is_anyaddr(this->my_host)) - { - host_t *me = this->ike_cfg->get_my_host(this->ike_cfg); - set_my_host(this, me->clone(me)); - } - if (this->other_host->is_anyaddr(this->other_host)) - { - host_t *other = this->ike_cfg->get_other_host(this->ike_cfg); - set_other_host(this, other->clone(other)); - } /* apply IDs if they are not already set */ if (this->my_id->contains_wildcards(this->my_id)) { @@ -426,6 +431,22 @@ static void set_peer_cfg(private_ike_sa_t *this, peer_cfg_t *peer_cfg) } /** + * Implementation of ike_sa_t.get_my_auth. + */ +static auth_info_t* get_my_auth(private_ike_sa_t *this) +{ + return this->my_auth; +} + +/** + * Implementation of ike_sa_t.get_other_auth. + */ +static auth_info_t* get_other_auth(private_ike_sa_t *this) +{ + return this->other_auth; +} + +/** * Implementation of ike_sa_t.send_keepalive */ static void send_keepalive(private_ike_sa_t *this) @@ -480,6 +501,15 @@ static void set_ike_cfg(private_ike_sa_t *this, ike_cfg_t *ike_cfg) ike_cfg->get_ref(ike_cfg); this->ike_cfg = 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. */ @@ -562,7 +592,7 @@ static status_t send_dpd(private_ike_sa_t *this) send_dpd_job_t *job; time_t diff, delay; - delay = this->peer_cfg->get_dpd_delay(this->peer_cfg); + delay = this->peer_cfg->get_dpd(this->peer_cfg); if (delay == 0) { @@ -907,7 +937,17 @@ static void send_notify_response(private_ike_sa_t *this, message_t *request, response->destroy(response); } -#ifdef P2P +#ifdef ME +/** + * Implementation of ike_sa_t.act_as_mediation_server. + */ +static void act_as_mediation_server(private_ike_sa_t *this) +{ + charon->mediation_manager->update_sa_id(charon->mediation_manager, + this->other_id, this->ike_sa_id); + this->is_mediation_server = TRUE; +} + /** * Implementation of ike_sa_t.get_server_reflexive_host. */ @@ -926,13 +966,21 @@ static void set_server_reflexive_host(private_ike_sa_t *this, host_t *host) } /** + * Implementation of ike_sa_t.get_connect_id. + */ +static chunk_t get_connect_id(private_ike_sa_t *this) +{ + return this->connect_id; +} + +/** * Implementation of ike_sa_t.respond */ static status_t respond(private_ike_sa_t *this, identification_t *peer_id, - chunk_t session_id) + chunk_t connect_id) { - ike_p2p_t *task = ike_p2p_create(&this->public, TRUE); - task->respond(task, peer_id, session_id); + ike_me_t *task = ike_me_create(&this->public, TRUE); + task->respond(task, peer_id, connect_id); this->task_manager->queue_task(this->task_manager, (task_t*)task); return this->task_manager->initiate(this->task_manager); } @@ -942,7 +990,7 @@ static status_t respond(private_ike_sa_t *this, identification_t *peer_id, */ static status_t callback(private_ike_sa_t *this, identification_t *peer_id) { - ike_p2p_t *task = ike_p2p_create(&this->public, TRUE); + ike_me_t *task = ike_me_create(&this->public, TRUE); task->callback(task, peer_id); this->task_manager->queue_task(this->task_manager, (task_t*)task); return this->task_manager->initiate(this->task_manager); @@ -952,10 +1000,10 @@ static status_t callback(private_ike_sa_t *this, identification_t *peer_id) * Implementation of ike_sa_t.relay */ static status_t relay(private_ike_sa_t *this, identification_t *requester, - chunk_t session_id, chunk_t session_key, linked_list_t *endpoints, bool response) + chunk_t connect_id, chunk_t connect_key, linked_list_t *endpoints, bool response) { - ike_p2p_t *task = ike_p2p_create(&this->public, TRUE); - task->relay(task, requester, session_id, session_key, endpoints, response); + ike_me_t *task = ike_me_create(&this->public, TRUE); + task->relay(task, requester, connect_id, connect_key, endpoints, response); this->task_manager->queue_task(this->task_manager, (task_t*)task); return this->task_manager->initiate(this->task_manager); } @@ -965,7 +1013,7 @@ static status_t relay(private_ike_sa_t *this, identification_t *requester, */ static status_t initiate_mediation(private_ike_sa_t *this, peer_cfg_t *mediated_cfg) { - ike_p2p_t *task = ike_p2p_create(&this->public, TRUE); + ike_me_t *task = ike_me_create(&this->public, TRUE); task->connect(task, mediated_cfg->get_peer_id(mediated_cfg)); this->task_manager->queue_task(this->task_manager, (task_t*)task); return this->task_manager->initiate(this->task_manager); @@ -975,37 +1023,54 @@ static status_t initiate_mediation(private_ike_sa_t *this, peer_cfg_t *mediated_ * Implementation of ike_sa_t.initiate_mediated */ static status_t initiate_mediated(private_ike_sa_t *this, host_t *me, host_t *other, - linked_list_t *childs) + chunk_t connect_id) { - this->my_host = me->clone(me); - this->other_host = other->clone(other); + set_my_host(this, me->clone(me)); + set_other_host(this, other->clone(other)); + chunk_free(&this->connect_id); + this->connect_id = chunk_clone(connect_id); - task_t *task; - child_cfg_t *child_cfg; - iterator_t *iterator = childs->create_iterator(childs, TRUE); - while (iterator->iterate(iterator, (void**)&child_cfg)) + return this->task_manager->initiate(this->task_manager); +} +#endif /* ME */ + +/** + * Resolve DNS host in configuration + */ +static void resolve_hosts(private_ike_sa_t *this) +{ + host_t *host; + + host = host_create_from_dns(this->ike_cfg->get_my_addr(this->ike_cfg), 0, + IKEV2_UDP_PORT); + if (host) { - task = (task_t*)child_create_create(&this->public, child_cfg); - this->task_manager->queue_task(this->task_manager, task); + set_my_host(this, host); + } + host = host_create_from_dns(this->ike_cfg->get_other_addr(this->ike_cfg), + this->my_host->get_family(this->my_host), + IKEV2_UDP_PORT); + if (host) + { + set_other_host(this, host); } - iterator->destroy(iterator); - return this->task_manager->initiate(this->task_manager); } -#endif /* P2P */ /** - * Implementation of ike_sa_t.initiate. + * Initiates a CHILD_SA using the appropriate reqid */ -static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) +static status_t initiate_with_reqid(private_ike_sa_t *this, child_cfg_t *child_cfg, u_int32_t reqid) { task_t *task; if (this->state == IKE_CREATED) { + resolve_hosts(this); + if (this->other_host->is_anyaddr(this->other_host) -#ifdef P2P +#ifdef ME && !this->peer_cfg->get_mediated_by(this->peer_cfg) -#endif /* P2P */ +#endif /* ME */ ) { child_cfg->destroy(child_cfg); @@ -1020,10 +1085,12 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_natd_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_cert_create(&this->public, TRUE); + task = (task_t*)ike_cert_pre_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_auth_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_cert_post_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_config_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_auth_lifetime_create(&this->public, TRUE); @@ -1033,50 +1100,65 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) task = (task_t*)ike_mobike_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); } -#ifdef P2P - task = (task_t*)ike_p2p_create(&this->public, TRUE); +#ifdef ME + task = (task_t*)ike_me_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); -#endif /* P2P */ +#endif /* ME */ } -#ifdef P2P - if (this->peer_cfg->get_mediated_by(this->peer_cfg)) - { - /* mediated connection, initiate mediation process */ - job_t *job = (job_t*)initiate_mediation_job_create(this->ike_sa_id, child_cfg); - child_cfg->destroy(child_cfg); - charon->processor->queue_job(charon->processor, job); - return SUCCESS; - } - else if (this->peer_cfg->is_mediation(this->peer_cfg)) +#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(CHILD_UP_SUCCESS, "mediation connection is already up and running"); } + DESTROY_IF(child_cfg); } else -#endif /* P2P */ +#endif /* ME */ { /* normal IKE_SA with CHILD_SA */ task = (task_t*)child_create_create(&this->public, child_cfg); child_cfg->destroy(child_cfg); + if (reqid) + { + child_create_t *child_create = (child_create_t*)task; + child_create->use_reqid(child_create, reqid); + } this->task_manager->queue_task(this->task_manager, task); + +#ifdef ME + if (this->peer_cfg->get_mediated_by(this->peer_cfg)) + { + /* mediated connection, initiate mediation process */ + job_t *job = (job_t*)initiate_mediation_job_create(this->ike_sa_id); + charon->processor->queue_job(charon->processor, job); + return SUCCESS; + } +#endif /* ME */ } return this->task_manager->initiate(this->task_manager); } /** + * Implementation of ike_sa_t.initiate. + */ +static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) +{ + return initiate_with_reqid(this, child_cfg, 0); +} + +/** * Implementation of ike_sa_t.acquire. */ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) -{ /* FIXME: P2P-NAT-T */ +{ child_cfg_t *child_cfg; iterator_t *iterator; child_sa_t *current, *child_sa = NULL; - task_t *task; - child_create_t *child_create; if (this->state == IKE_DELETING) { @@ -1105,34 +1187,10 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) return FAILED; } - - if (this->state == IKE_CREATED) - { - task = (task_t*)ike_init_create(&this->public, TRUE, NULL); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_natd_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_cert_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_auth_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_config_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - task = (task_t*)ike_auth_lifetime_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - if (this->peer_cfg->use_mobike(this->peer_cfg)) - { - task = (task_t*)ike_mobike_create(&this->public, TRUE); - this->task_manager->queue_task(this->task_manager, task); - } - } - child_cfg = child_sa->get_config(child_sa); - child_create = child_create_create(&this->public, child_cfg); - child_create->use_reqid(child_create, reqid); - this->task_manager->queue_task(this->task_manager, (task_t*)child_create); + child_cfg->get_ref(child_cfg); - return this->task_manager->initiate(this->task_manager); + return initiate_with_reqid(this, child_cfg, reqid); } /** @@ -1175,10 +1233,12 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) default: break; } + + 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, FALSE, 0); + this->other_id, child_cfg, 0, FALSE); me = this->my_host; if (this->my_virtual_ip) { @@ -1394,145 +1454,6 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) } /** - * Implementation of ike_sa_t.retransmit. - */ -static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) -{ /* FIXME: P2P-NAT-T */ - this->time.outbound = time(NULL); - if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) - { - child_cfg_t *child_cfg; - child_sa_t* child_sa; - linked_list_t *to_route, *to_restart; - iterator_t *iterator; - - /* send a proper signal to brief interested bus listeners */ - switch (this->state) - { - case IKE_CONNECTING: - { - /* retry IKE_SA_INIT if we have multiple keyingtries */ - u_int32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg); - 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); - reset(this); - return this->task_manager->initiate(this->task_manager); - } - SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding"); - break; - } - case IKE_REKEYING: - SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); - break; - case IKE_DELETING: - SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); - break; - default: - break; - } - - /* summarize how we have to handle each child */ - to_route = linked_list_create(); - to_restart = linked_list_create(); - iterator = this->child_sas->create_iterator(this->child_sas, TRUE); - while (iterator->iterate(iterator, (void**)&child_sa)) - { - child_cfg = child_sa->get_config(child_sa); - - if (child_sa->get_state(child_sa) == CHILD_ROUTED) - { - /* reroute routed CHILD_SAs */ - to_route->insert_last(to_route, child_cfg); - } - else - { - /* use DPD action for established CHILD_SAs */ - switch (this->peer_cfg->get_dpd_action(this->peer_cfg)) - { - case DPD_ROUTE: - to_route->insert_last(to_route, child_cfg); - break; - case DPD_RESTART: - to_restart->insert_last(to_restart, child_cfg); - break; - default: - break; - } - } - } - iterator->destroy(iterator); - - /* create a new IKE_SA if we have to route or to restart */ - if (to_route->get_count(to_route) || to_restart->get_count(to_restart)) - { - private_ike_sa_t *new; - task_t *task; - - new = (private_ike_sa_t*)charon->ike_sa_manager->checkout_new( - charon->ike_sa_manager, TRUE); - - set_peer_cfg(new, this->peer_cfg); - /* use actual used host, not the wildcarded one in config */ - new->other_host->destroy(new->other_host); - new->other_host = this->other_host->clone(this->other_host); - /* reset port to 500, but only if peer is not NATed */ - if (!has_condition(this, COND_NAT_THERE)) - { - new->other_host->set_port(new->other_host, IKEV2_UDP_PORT); - } - /* take over virtual ip, as we need it for a proper route */ - if (this->my_virtual_ip) - { - set_virtual_ip(new, TRUE, this->my_virtual_ip); - } - - /* install routes */ - while (to_route->remove_last(to_route, (void**)&child_cfg) == SUCCESS) - { - route(new, child_cfg); - } - - /* restart children */ - if (to_restart->get_count(to_restart)) - { - task = (task_t*)ike_init_create(&new->public, TRUE, NULL); - new->task_manager->queue_task(new->task_manager, task); - task = (task_t*)ike_natd_create(&new->public, TRUE); - new->task_manager->queue_task(new->task_manager, task); - task = (task_t*)ike_cert_create(&new->public, TRUE); - new->task_manager->queue_task(new->task_manager, task); - task = (task_t*)ike_config_create(&new->public, TRUE); - new->task_manager->queue_task(new->task_manager, task); - task = (task_t*)ike_auth_create(&new->public, TRUE); - new->task_manager->queue_task(new->task_manager, task); - - while (to_restart->remove_last(to_restart, (void**)&child_cfg) == SUCCESS) - { - task = (task_t*)child_create_create(&new->public, child_cfg); - new->task_manager->queue_task(new->task_manager, task); - } - task = (task_t*)ike_auth_lifetime_create(&new->public, TRUE); - new->task_manager->queue_task(new->task_manager, task); - if (this->peer_cfg->use_mobike(this->peer_cfg)) - { - task = (task_t*)ike_mobike_create(&new->public, TRUE); - new->task_manager->queue_task(new->task_manager, task); - } - new->task_manager->initiate(new->task_manager); - } - charon->ike_sa_manager->checkin(charon->ike_sa_manager, &new->public); - } - to_route->destroy(to_route); - to_restart->destroy(to_restart); - return DESTROY_ME; - } - return SUCCESS; -} - -/** * Implementation of ike_sa_t.get_prf. */ static prf_t *get_prf(private_ike_sa_t *this) @@ -1607,22 +1528,6 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other) } /** - * Implementation of ike_sa_t.get_other_ca. - */ -static ca_info_t* get_other_ca(private_ike_sa_t *this) -{ - return this->other_ca; -} - -/** - * Implementation of ike_sa_t.set_other_ca. - */ -static void set_other_ca(private_ike_sa_t *this, ca_info_t *other_ca) -{ - this->other_ca = other_ca; -} - -/** * Implementation of ike_sa_t.derive_keys. */ static status_t derive_keys(private_ike_sa_t *this, @@ -1631,9 +1536,8 @@ static status_t derive_keys(private_ike_sa_t *this, bool initiator, prf_t *child_prf, prf_t *old_prf) { prf_plus_t *prf_plus; - chunk_t skeyseed, key, nonces, prf_plus_seed; - algorithm_t *algo; - size_t key_size; + 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)]; @@ -1641,24 +1545,42 @@ static status_t derive_keys(private_ike_sa_t *this, 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, &algo)) + if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) { - DBG1(DBG_IKE, "key derivation failed: no PSEUDO_RANDOM_FUNCTION");; + DBG1(DBG_IKE, "no %N selected", + transform_type_names, PSEUDO_RANDOM_FUNCTION); return FAILED; } - this->prf = prf_create(algo->algorithm); + this->prf = lib->crypto->create_prf(lib->crypto, alg); if (this->prf == NULL) { - DBG1(DBG_IKE, "key derivation failed: PSEUDO_RANDOM_FUNCTION " - "%N not supported!", pseudo_random_function_names, algo->algorithm); + 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); - nonces = chunk_cat("cc", nonce_i, nonce_r); + /* 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", nonces, spi_i, spi_r); + prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r); /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) * @@ -1667,7 +1589,7 @@ static status_t derive_keys(private_ike_sa_t *this, if (child_prf == NULL) /* not rekeying */ { /* SKEYSEED = prf(Ni | Nr, g^ir) */ - this->prf->set_key(this->prf, nonces); + 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); @@ -1679,7 +1601,7 @@ static status_t derive_keys(private_ike_sa_t *this, { /* 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, nonces); + 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); @@ -1687,14 +1609,15 @@ static status_t derive_keys(private_ike_sa_t *this, chunk_free(&secret); prf_plus = prf_plus_create(old_prf, prf_plus_seed); } - chunk_free(&nonces); + 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, &algo); - this->child_prf = prf_create(algo->algorithm); + 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); @@ -1702,17 +1625,20 @@ static status_t derive_keys(private_ike_sa_t *this, chunk_free(&key); /* SK_ai/SK_ar used for integrity protection => signer_in/signer_out */ - if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &algo)) + if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) { - DBG1(DBG_IKE, "key derivation failed: no INTEGRITY_ALGORITHM"); + DBG1(DBG_IKE, "no %N selected", + transform_type_names, INTEGRITY_ALGORITHM); return FAILED; } - signer_i = signer_create(algo->algorithm); - signer_r = signer_create(algo->algorithm); + 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, "key derivation failed: INTEGRITY_ALGORITHM " - "%N not supported!", integrity_algorithm_names ,algo->algorithm); + 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); @@ -1739,18 +1665,21 @@ static status_t derive_keys(private_ike_sa_t *this, } /* SK_ei/SK_er used for encryption => crypter_in/crypter_out */ - if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &algo)) + if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size)) { - DBG1(DBG_IKE, "key derivation failed: no ENCRYPTION_ALGORITHM"); + DBG1(DBG_IKE, "no %N selected", + transform_type_names, ENCRYPTION_ALGORITHM); + prf_plus->destroy(prf_plus); return FAILED; } - crypter_i = crypter_create(algo->algorithm, algo->key_size / 8); - crypter_r = crypter_create(algo->algorithm, algo->key_size / 8); + 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, "key derivation failed: ENCRYPTION_ALGORITHM " - "%N (key size %d) not supported!", - encryption_algorithm_names, algo->algorithm, algo->key_size); + 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); @@ -1806,6 +1735,23 @@ static status_t derive_keys(private_ike_sa_t *this, } /** + * 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) @@ -1944,9 +1890,9 @@ static status_t rekey(private_ike_sa_t *this) } /** - * Implementation of ike_sa_t.reestablish + * Implementation of ike_sa_t.reauth */ -static status_t reestablish(private_ike_sa_t *this) +static status_t reauth(private_ike_sa_t *this) { task_t *task; @@ -1957,7 +1903,12 @@ static status_t reestablish(private_ike_sa_t *this) { DBG1(DBG_IKE, "initiator did not reauthenticate as requested"); if (this->other_virtual_ip != NULL || - has_condition(this, COND_EAP_AUTHENTICATED)) + has_condition(this, COND_EAP_AUTHENTICATED) +#ifdef ME + /* if we are mediation server we too cannot reauth the IKE_SA */ + || this->is_mediation_server +#endif /* ME */ + ) { time_t now = time(NULL); @@ -1976,6 +1927,176 @@ static status_t reestablish(private_ike_sa_t *this) } /** + * Implementation of ike_sa_t.reestablish + */ +static status_t reestablish(private_ike_sa_t *this) +{ + ike_sa_t *new; + host_t *host; + action_t action; + iterator_t *iterator; + child_sa_t *child_sa; + child_cfg_t *child_cfg; + bool required = FALSE; + status_t status = FAILED; + + /* check if we have children to keep up at all*/ + iterator = create_child_sa_iterator(this); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + child_cfg = child_sa->get_config(child_sa); + if (this->state == IKE_DELETING) + { + action = child_cfg->get_close_action(child_cfg); + } + else + { + action = child_cfg->get_dpd_action(child_cfg); + } + switch (action) + { + case ACTION_RESTART: + case ACTION_ROUTE: + required = TRUE; + default: + break; + } + } + iterator->destroy(iterator); +#ifdef ME + /* we initiate the new IKE_SA of the mediation connection without CHILD_SA */ + if (this->peer_cfg->is_mediation(this->peer_cfg)) + { + required = TRUE; + } +#endif /* ME */ + if (!required) + { + return FAILED; + } + + /* check if we are able to reestablish this IKE_SA */ + if (!this->ike_initiator && + (this->other_virtual_ip != NULL || + has_condition(this, COND_EAP_AUTHENTICATED) +#ifdef ME + || this->is_mediation_server +#endif /* ME */ + )) + { + DBG1(DBG_IKE, "unable to reestablish IKE_SA due asymetric setup"); + return FAILED; + } + + new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, TRUE); + new->set_peer_cfg(new, this->peer_cfg); + host = this->other_host; + new->set_other_host(new, host->clone(host)); + host = this->my_host; + new->set_my_host(new, host->clone(host)); + /* if we already have a virtual IP, we reuse it */ + host = this->my_virtual_ip; + if (host) + { + new->set_virtual_ip(new, TRUE, host); + } + +#ifdef ME + if (this->peer_cfg->is_mediation(this->peer_cfg)) + { + status = new->initiate(new, NULL); + } + else +#endif /* ME */ + { + iterator = create_child_sa_iterator(this); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + child_cfg = child_sa->get_config(child_sa); + if (this->state == IKE_DELETING) + { + action = child_cfg->get_close_action(child_cfg); + } + else + { + action = child_cfg->get_dpd_action(child_cfg); + } + switch (action) + { + case ACTION_RESTART: + DBG1(DBG_IKE, "restarting CHILD_SA %s", + child_cfg->get_name(child_cfg)); + child_cfg->get_ref(child_cfg); + status = new->initiate(new, child_cfg); + break; + case ACTION_ROUTE: + status = new->route(new, child_cfg); + break; + default: + continue; + } + if (status == DESTROY_ME) + { + break; + } + } + iterator->destroy(iterator); + } + + if (status == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new); + return FAILED; + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); + return SUCCESS; + } +} + +/** + * Implementation of ike_sa_t.retransmit. + */ +static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) +{ + this->time.outbound = time(NULL); + if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) + { + /* send a proper signal to brief interested bus listeners */ + switch (this->state) + { + case IKE_CONNECTING: + { + /* retry IKE_SA_INIT if we have multiple keyingtries */ + u_int32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg); + 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); + reset(this); + return this->task_manager->initiate(this->task_manager); + } + SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding"); + break; + } + case IKE_DELETING: + SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding"); + break; + case IKE_REKEYING: + SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding"); + /* FALL */ + default: + reestablish(this); + break; + } + return DESTROY_ME; + } + return SUCCESS; +} + +/** * Implementation of ike_sa_t.set_auth_lifetime. */ static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) @@ -2009,6 +2130,14 @@ static status_t roam(private_ike_sa_t *this, bool address) host_t *me, *other; ike_mobike_t *mobike; + switch (this->state) + { + case IKE_CREATED: + case IKE_DELETING: + return SUCCESS; + default: + break; + } /* responder just updates the peer about changed address config */ if (!this->ike_sa_id->is_initiator(this->ike_sa_id)) { @@ -2048,9 +2177,9 @@ static status_t roam(private_ike_sa_t *this, bool address) this->task_manager->queue_task(this->task_manager, (task_t*)mobike); return this->task_manager->initiate(this->task_manager); } - DBG1(DBG_IKE, "reestablishing IKE_SA due address change"); - /* ... reestablish if not */ - return reestablish(this); + DBG1(DBG_IKE, "reauthenticating IKE_SA due address change"); + /* ... reauth if not */ + return reauth(this); } /** @@ -2097,6 +2226,18 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) { send_keepalive(this); } + +#ifdef ME + if (other->is_mediation_server) + { + act_as_mediation_server(this); + } + else if (other->server_reflexive_host) + { + this->server_reflexive_host = other->server_reflexive_host->clone( + other->server_reflexive_host); + } +#endif /* ME */ /* adopt all children */ while (other->child_sas->remove_last(other->child_sas, @@ -2270,6 +2411,8 @@ static void destroy(private_ike_sa_t *this) { this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy)); + this->task_manager->destroy(this->task_manager); + DESTROY_IF(this->crypter_in); DESTROY_IF(this->crypter_out); DESTROY_IF(this->signer_in); @@ -2278,6 +2421,7 @@ static void destroy(private_ike_sa_t *this) DESTROY_IF(this->child_prf); chunk_free(&this->skp_verify); chunk_free(&this->skp_build); + free(this->selected_proposal); if (this->my_virtual_ip) { @@ -2285,22 +2429,30 @@ static void destroy(private_ike_sa_t *this) this->my_virtual_ip); this->my_virtual_ip->destroy(this->my_virtual_ip); } - DESTROY_IF(this->other_virtual_ip); + if (this->other_virtual_ip) + { + if (this->peer_cfg && this->peer_cfg->get_pool(this->peer_cfg)) + { + charon->attributes->release_address(charon->attributes, + this->peer_cfg->get_pool(this->peer_cfg), + this->other_virtual_ip); + } + this->other_virtual_ip->destroy(this->other_virtual_ip); + } remove_dns_servers(this); this->dns_servers->destroy_offset(this->dns_servers, offsetof(host_t, destroy)); this->additional_addresses->destroy_offset(this->additional_addresses, offsetof(host_t, destroy)); -#ifdef P2P - if (this->peer_cfg && this->peer_cfg->is_mediation(this->peer_cfg) && - !this->ike_sa_id->is_initiator(this->ike_sa_id)) +#ifdef ME + if (this->is_mediation_server) { - /* mediation server */ charon->mediation_manager->remove(charon->mediation_manager, this->ike_sa_id); } DESTROY_IF(this->server_reflexive_host); -#endif /* P2P */ + chunk_free(&this->connect_id); +#endif /* ME */ DESTROY_IF(this->my_host); DESTROY_IF(this->other_host); @@ -2309,9 +2461,10 @@ static void destroy(private_ike_sa_t *this) DESTROY_IF(this->ike_cfg); DESTROY_IF(this->peer_cfg); + DESTROY_IF(this->my_auth); + DESTROY_IF(this->other_auth); this->ike_sa_id->destroy(this->ike_sa_id); - this->task_manager->destroy(this->task_manager); free(this); } @@ -2337,6 +2490,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_ike_cfg = (void (*)(ike_sa_t*,ike_cfg_t*))set_ike_cfg; this->public.get_peer_cfg = (peer_cfg_t* (*)(ike_sa_t*))get_peer_cfg; 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_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; @@ -2347,14 +2502,13 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_my_id = (void (*)(ike_sa_t*,identification_t*)) set_my_id; this->public.get_other_id = (identification_t* (*)(ike_sa_t*)) get_other_id; this->public.set_other_id = (void (*)(ike_sa_t*,identification_t*)) set_other_id; - this->public.get_other_ca = (ca_info_t* (*)(ike_sa_t*)) get_other_ca; - this->public.set_other_ca = (void (*)(ike_sa_t*,ca_info_t*)) set_other_ca; this->public.enable_extension = (void(*)(ike_sa_t*, ike_extension_t extension))enable_extension; this->public.supports_extension = (bool(*)(ike_sa_t*, ike_extension_t extension))supports_extension; this->public.set_condition = (void (*)(ike_sa_t*, ike_condition_t,bool)) set_condition; 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.retransmit = (status_t (*)(ike_sa_t *, u_int32_t)) retransmit; @@ -2367,6 +2521,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) 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.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; @@ -2374,6 +2530,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.delete_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa; this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa; this->public.rekey = (status_t (*)(ike_sa_t*))rekey; + this->public.reauth = (status_t (*)(ike_sa_t*))reauth; this->public.reestablish = (status_t (*)(ike_sa_t*))reestablish; this->public.set_auth_lifetime = (void(*)(ike_sa_t*, u_int32_t lifetime))set_auth_lifetime; this->public.roam = (status_t(*)(ike_sa_t*,bool))roam; @@ -2384,26 +2541,28 @@ 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; -#ifdef P2P +#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; this->public.set_server_reflexive_host = (void (*)(ike_sa_t*,host_t*)) set_server_reflexive_host; + this->public.get_connect_id = (chunk_t (*)(ike_sa_t*)) get_connect_id; this->public.initiate_mediation = (status_t (*)(ike_sa_t*,peer_cfg_t*)) initiate_mediation; - this->public.initiate_mediated = (status_t (*)(ike_sa_t*,host_t*,host_t*,linked_list_t*)) initiate_mediated; + this->public.initiate_mediated = (status_t (*)(ike_sa_t*,host_t*,host_t*,chunk_t)) initiate_mediated; this->public.relay = (status_t (*)(ike_sa_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool)) relay; this->public.callback = (status_t (*)(ike_sa_t*,identification_t*)) callback; this->public.respond = (status_t (*)(ike_sa_t*,identification_t*,chunk_t)) respond; -#endif /* P2P */ +#endif /* ME */ /* 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_any(AF_INET); - this->other_host = host_create_any(AF_INET); + 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_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); - this->other_ca = NULL; this->extensions = 0; this->conditions = 0; + this->selected_proposal = NULL; this->crypter_in = NULL; this->crypter_out = NULL; this->signer_in = NULL; @@ -2420,6 +2579,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->time.delete = 0; this->ike_cfg = NULL; this->peer_cfg = NULL; + this->my_auth = auth_info_create(); + this->other_auth = auth_info_create(); this->task_manager = task_manager_create(&this->public); this->unique_id = ++unique_id; this->my_virtual_ip = NULL; @@ -2429,9 +2590,11 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->pending_updates = 0; this->keyingtry = 0; this->ike_initiator = FALSE; -#ifdef P2P +#ifdef ME + this->is_mediation_server = FALSE; this->server_reflexive_host = NULL; -#endif /* P2P */ + this->connect_id = chunk_empty; +#endif /* ME */ return &this->public; } diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 975447d9c..0935f5d6b 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -1,12 +1,5 @@ -/** - * @file ike_sa.h - * - * @brief Interface of ike_sa_t. - * - */ - /* - * Copyright (C) 2006-2007 Tobias Brunner + * Copyright (C) 2006-2008 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -21,6 +14,13 @@ * 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. + * + * $Id: ike_sa.h 4086 2008-06-22 11:24:33Z andreas $ + */ + +/** + * @defgroup ike_sa ike_sa + * @{ @ingroup sa */ #ifndef IKE_SA_H_ @@ -38,44 +38,35 @@ typedef struct ike_sa_t ike_sa_t; #include <sa/ike_sa_id.h> #include <sa/child_sa.h> #include <sa/tasks/task.h> -#include <utils/randomizer.h> #include <crypto/prfs/prf.h> #include <crypto/crypters/crypter.h> #include <crypto/signers/signer.h> -#include <crypto/ca.h> #include <config/peer_cfg.h> #include <config/ike_cfg.h> +#include <credentials/auth_info.h> /** * Timeout in milliseconds after that a half open IKE_SA gets deleted. - * - * @ingroup sa */ #define HALF_OPEN_IKE_SA_TIMEOUT 30000 /** * Interval to send keepalives when NATed, in seconds. - * - * @ingroup sa */ #define KEEPALIVE_INTERVAL 20 /** * After which time rekeying should be retried if it failed, in seconds. - * - * @ingroup sa */ #define RETRY_INTERVAL 30 /** * Jitter to subtract from RETRY_INTERVAL to randomize rekey retry. - * - * @ingroup sa */ #define RETRY_JITTER 20 /** - * @brief Extensions (or optional features) the peer supports + * Extensions (or optional features) the peer supports */ enum ike_extension_t { @@ -88,10 +79,15 @@ enum ike_extension_t { * peer supports MOBIKE (RFC4555) */ EXT_MOBIKE = (1<<1), + + /** + * peer supports HTTP cert lookups as specified in RFC4306 + */ + EXT_HASH_AND_URL = (1<<2), }; /** - * @brief Conditions of an IKE_SA, change during its lifetime + * Conditions of an IKE_SA, change during its lifetime */ enum ike_condition_t { @@ -119,6 +115,11 @@ enum ike_condition_t { * peer has ben authenticated using EAP */ COND_EAP_AUTHENTICATED = (1<<4), + + /** + * received a certificate request from the peer + */ + COND_CERTREQ_SEEN = (1<<5), }; /** @@ -138,7 +139,7 @@ enum statistic_t { }; /** - * @brief State of an IKE_SA. + * State of an IKE_SA. * * An IKE_SA passes various states in its lifetime. A newly created * SA is in the state CREATED. @@ -173,8 +174,6 @@ enum statistic_t { X / \ @endverbatim - * - * @ingroup sa */ enum ike_sa_state_t { @@ -210,365 +209,342 @@ enum ike_sa_state_t { extern enum_name_t *ike_sa_state_names; /** - * @brief Class ike_sa_t representing an IKE_SA. + * Class ike_sa_t representing an IKE_SA. * * An IKE_SA contains crypto information related to a connection * with a peer. It contains multiple IPsec CHILD_SA, for which * it is responsible. All traffic is handled by an IKE_SA, using * the task manager and its tasks. - * - * @b Constructors: - * - ike_sa_create() - * - * @ingroup sa */ struct ike_sa_t { /** - * @brief Get the id of the SA. + * Get the id of the SA. * * Returned ike_sa_id_t object is not getting cloned! * - * @param this calling object * @return ike_sa's ike_sa_id_t */ ike_sa_id_t* (*get_id) (ike_sa_t *this); /** - * @brief Get the numerical ID uniquely defining this IKE_SA. + * Get the numerical ID uniquely defining this IKE_SA. * - * @param this calling object * @return unique ID */ u_int32_t (*get_unique_id) (ike_sa_t *this); /** - * @brief Get the state of the IKE_SA. + * Get the state of the IKE_SA. * - * @param this calling object * @return state of the IKE_SA */ ike_sa_state_t (*get_state) (ike_sa_t *this); /** - * @brief Set the state of the IKE_SA. + * Set the state of the IKE_SA. * - * @param this calling object * @param state state to set for the IKE_SA */ void (*set_state) (ike_sa_t *this, ike_sa_state_t ike_sa); /** - * @brief Get the name of the connection this IKE_SA uses. + * Get the name of the connection this IKE_SA uses. * - * @param this calling object * @return name */ char* (*get_name) (ike_sa_t *this); /** - * @brief Get statistic values from the IKE_SA. + * Get statistic values from the IKE_SA. * - * @param this calling object * @param kind kind of requested value * @return value as integer */ u_int32_t (*get_statistic)(ike_sa_t *this, statistic_t kind); /** - * @brief Get the own host address. + * Get the own host address. * - * @param this calling object * @return host address */ host_t* (*get_my_host) (ike_sa_t *this); /** - * @brief Set the own host address. + * Set the own host address. * - * @param this calling object * @param me host address */ void (*set_my_host) (ike_sa_t *this, host_t *me); /** - * @brief Get the other peers host address. + * Get the other peers host address. * - * @param this calling object * @return host address */ host_t* (*get_other_host) (ike_sa_t *this); /** - * @brief Set the others host address. + * Set the others host address. * - * @param this calling object * @param other host address */ void (*set_other_host) (ike_sa_t *this, host_t *other); /** - * @brief Update the IKE_SAs host. + * Update the IKE_SAs host. * * Hosts may be NULL to use current host. * - * @param this calling object * @param me new local host address, or NULL * @param other new remote host address, or NULL */ void (*update_hosts)(ike_sa_t *this, host_t *me, host_t *other); /** - * @brief Get the own identification. + * Get the own identification. * - * @param this calling object * @return identification */ identification_t* (*get_my_id) (ike_sa_t *this); /** - * @brief Set the own identification. + * Set the own identification. * - * @param this calling object * @param me identification */ void (*set_my_id) (ike_sa_t *this, identification_t *me); /** - * @brief Get the other peer's identification. + * Get the other peer's identification. * - * @param this calling object * @return identification */ identification_t* (*get_other_id) (ike_sa_t *this); /** - * @brief Set the other peer's identification. + * Set the other peer's identification. * - * @param this calling object * @param other identification */ void (*set_other_id) (ike_sa_t *this, identification_t *other); /** - * @brief Get the other peer's certification authority + * Get the config used to setup this IKE_SA. * - * @param this calling object - * @return ca_info_t record of other ca - */ - ca_info_t* (*get_other_ca) (ike_sa_t *this); - - /** - * @brief Set the other peer's certification authority - * - * @param this calling object - * @param other_ca ca_info_t record of other ca - */ - void (*set_other_ca) (ike_sa_t *this, ca_info_t *other_ca); - - /** - * @brief Get the config used to setup this IKE_SA. - * - * @param this calling object * @return ike_config */ ike_cfg_t* (*get_ike_cfg) (ike_sa_t *this); /** - * @brief Set the config to setup this IKE_SA. + * Set the config to setup this IKE_SA. * - * @param this calling object * @param config ike_config to use */ void (*set_ike_cfg) (ike_sa_t *this, ike_cfg_t* config); /** - * @brief Get the peer config used by this IKE_SA. + * Get the peer config used by this IKE_SA. * - * @param this calling object * @return peer_config */ peer_cfg_t* (*get_peer_cfg) (ike_sa_t *this); /** - * @brief Set the peer config to use with this IKE_SA. + * Set the peer config to use with this IKE_SA. * - * @param this calling object * @param config peer_config to use */ void (*set_peer_cfg) (ike_sa_t *this, peer_cfg_t *config); /** - * @brief Add an additional address for the peer. + * Get authentication/authorization info for local peer. + * + * @return auth_info for me + */ + auth_info_t* (*get_my_auth)(ike_sa_t *this); + + /** + * Get authentication/authorization info for remote peer. + * + * @return auth_info for me + */ + auth_info_t* (*get_other_auth)(ike_sa_t *this); + + /** + * Add an additional address for the peer. * * In MOBIKE, a peer may transmit additional addresses where it is * reachable. These are stored in the IKE_SA. * The own list of addresses is not stored, they are queried from * the kernel when required. * - * @param this calling object * @param host host to add to list */ void (*add_additional_address)(ike_sa_t *this, host_t *host); /** - * @brief Create an iterator over all additional addresses of the peer. + * Create an iterator over all additional addresses of the peer. * - * @param this calling object * @return iterator over addresses */ iterator_t* (*create_additional_address_iterator)(ike_sa_t *this); /** - * @brief Enable an extension the peer supports. + * Enable an extension the peer supports. * * If support for an IKE extension is detected, this method is called * to enable that extension and behave accordingly. * - * @param this calling object * @param extension extension to enable */ void (*enable_extension)(ike_sa_t *this, ike_extension_t extension); /** - * @brief Check if the peer supports an extension. + * Check if the peer supports an extension. * - * @param this calling object * @param extension extension to check for support * @return TRUE if peer supports it, FALSE otherwise */ bool (*supports_extension)(ike_sa_t *this, ike_extension_t extension); /** - * @brief Enable/disable a condition flag for this IKE_SA. + * Enable/disable a condition flag for this IKE_SA. * - * @param this calling object * @param condition condition to enable/disable * @param enable TRUE to enable condition, FALSE to disable */ void (*set_condition) (ike_sa_t *this, ike_condition_t condition, bool enable); /** - * @brief Check if a condition flag is set. + * Check if a condition flag is set. * - * @param this calling object * @param condition condition to check * @return TRUE if condition flag set, FALSE otherwise */ bool (*has_condition) (ike_sa_t *this, ike_condition_t condition); /** - * @brief Get the number of queued MOBIKE address updates. + * Get the number of queued MOBIKE address updates. * - * @param this calling object * @return number of pending updates */ u_int32_t (*get_pending_updates)(ike_sa_t *this); /** - * @brief Set the number of queued MOBIKE address updates. + * Set the number of queued MOBIKE address updates. * - * @param this calling object * @param updates number of pending updates */ void (*set_pending_updates)(ike_sa_t *this, u_int32_t updates); + + /** + * Check if we are the original initiator of this IKE_SA (rekeying does not + * change this flag). + */ + bool (*is_ike_initiator)(ike_sa_t *this); + -#ifdef P2P +#ifdef ME /** - * @brief Get the server reflexive host. + * Activate mediation server functionality for this IKE_SA. + */ + void (*act_as_mediation_server) (ike_sa_t *this); + + /** + * Get the server reflexive host. * - * @param this calling object * @return server reflexive host */ host_t* (*get_server_reflexive_host) (ike_sa_t *this); /** - * @brief Set the server reflexive host. + * Set the server reflexive host. * - * @param this calling object * @param host server reflexive host */ void (*set_server_reflexive_host) (ike_sa_t *this, host_t *host); /** - * @brief Initiate the mediation of a mediated connection (i.e. initiate a - * P2P_CONNECT exchange). + * Get the connect ID. + * + * @return connect ID + */ + chunk_t (*get_connect_id) (ike_sa_t *this); + + /** + * Initiate the mediation of a mediated connection (i.e. initiate a + * ME_CONNECT exchange). * - * @param this calling object * @param mediated_cfg peer_cfg of the mediated connection * @return - * - SUCCESS if initialization started - * - DESTROY_ME if initialization failed + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed */ status_t (*initiate_mediation) (ike_sa_t *this, peer_cfg_t *mediated_cfg); /** - * @brief Initiate the mediated connection + * Initiate the mediated connection * - * @param this calling object * @param me local endpoint (gets cloned) * @param other remote endpoint (gets cloned) - * @param childs linked list of child_cfg_t of CHILD_SAs (gets cloned) + * @param connect_id connect ID (gets cloned) * @return - * - SUCCESS if initialization started - * - DESTROY_ME if initialization failed + * - SUCCESS if initialization started + * - DESTROY_ME if initialization failed */ status_t (*initiate_mediated) (ike_sa_t *this, host_t *me, host_t *other, - linked_list_t *childs); + chunk_t connect_id); /** - * @brief Relay data from one peer to another (i.e. initiate a - * P2P_CONNECT exchange). + * Relay data from one peer to another (i.e. initiate a + * ME_CONNECT exchange). * * Data is cloned. * - * @param this calling object * @param requester ID of the requesting peer - * @param session_id data of the P2P_SESSIONID payload - * @param session_key data of the P2P_SESSIONKEY payload + * @param connect_id data of the ME_CONNECTID payload + * @param connect_key data of the ME_CONNECTKEY payload * @param endpoints endpoints * @param response TRUE if this is a response * @return - * - SUCCESS if relay started - * - DESTROY_ME if relay failed + * - SUCCESS if relay started + * - DESTROY_ME if relay failed */ - status_t (*relay) (ike_sa_t *this, identification_t *requester, chunk_t session_id, - chunk_t session_key, linked_list_t *endpoints, bool response); + status_t (*relay) (ike_sa_t *this, identification_t *requester, chunk_t connect_id, + chunk_t connect_key, linked_list_t *endpoints, bool response); /** - * @brief Send a callback to a peer. + * Send a callback to a peer. * * Data is cloned. * - * @param this calling object * @param peer_id ID of the other peer * @return - * - SUCCESS if response started - * - DESTROY_ME if response failed + * - SUCCESS if response started + * - DESTROY_ME if response failed */ status_t (*callback) (ike_sa_t *this, identification_t *peer_id); /** - * @brief Respond to a P2P_CONNECT request. + * Respond to a ME_CONNECT request. * * Data is cloned. * - * @param this calling object * @param peer_id ID of the other peer - * @param session_id the session ID supplied by the initiator + * @param connect_id the connect ID supplied by the initiator * @return - * - SUCCESS if response started - * - DESTROY_ME if response failed + * - SUCCESS if response started + * - DESTROY_ME if response failed */ - status_t (*respond) (ike_sa_t *this, identification_t *peer_id, chunk_t session_id); -#endif /* P2P */ + status_t (*respond) (ike_sa_t *this, identification_t *peer_id, chunk_t connect_id); +#endif /* ME */ /** - * @brief Initiate a new connection. + * Initiate a new connection. * * The configs are owned by the IKE_SA after the call. * - * @param this calling object * @param child_cfg child config to create CHILD from * @return * - SUCCESS if initialization started @@ -577,12 +553,11 @@ struct ike_sa_t { status_t (*initiate) (ike_sa_t *this, child_cfg_t *child_cfg); /** - * @brief Route a policy in the kernel. + * Route a policy in the kernel. * * Installs the policies in the kernel. If traffic matches, * the kernel requests connection setup from the IKE_SA via acquire(). * - * @param this calling object * @param child_cfg child config to route * @return * - SUCCESS if routed successfully @@ -591,9 +566,8 @@ struct ike_sa_t { status_t (*route) (ike_sa_t *this, child_cfg_t *child_cfg); /** - * @brief Unroute a policy in the kernel previously routed. + * Unroute a policy in the kernel previously routed. * - * @param this calling object * @param reqid reqid of CHILD_SA to unroute * @return * - SUCCESS if route removed @@ -603,12 +577,11 @@ struct ike_sa_t { status_t (*unroute) (ike_sa_t *this, u_int32_t reqid); /** - * @brief Acquire connection setup for an installed kernel policy. + * Acquire connection setup for an installed kernel policy. * * If an installed policy raises an acquire, the kernel calls * this function to establish the CHILD_SA (and maybe the IKE_SA). * - * @param this calling object * @param reqid reqid of the CHILD_SA the policy belongs to. * @return * - SUCCESS if initialization started @@ -617,13 +590,12 @@ struct ike_sa_t { status_t (*acquire) (ike_sa_t *this, u_int32_t reqid); /** - * @brief Initiates the deletion of an IKE_SA. + * Initiates the deletion of an IKE_SA. * * Sends a delete message to the remote peer and waits for * its response. If the response comes in, or a timeout occurs, * the IKE SA gets deleted. * - * @param this calling object * @return * - SUCCESS if deletion is initialized * - INVALID_STATE, if the IKE_SA is not in @@ -633,7 +605,7 @@ struct ike_sa_t { status_t (*delete) (ike_sa_t *this); /** - * @brief Update IKE_SAs after network interfaces have changed. + * Update IKE_SAs after network interfaces have changed. * * Whenever the network interface configuration changes, the kernel * interface calls roam() on each IKE_SA. The IKE_SA then checks if @@ -641,21 +613,19 @@ struct ike_sa_t { * If MOBIKE is supported, addresses are updated; If not, the tunnel is * restarted. * - * @param this calling object * @param address TRUE if address list changed, FALSE otherwise * @return SUCCESS, FAILED, DESTROY_ME */ status_t (*roam)(ike_sa_t *this, bool address); /** - * @brief Processes a incoming IKEv2-Message. + * Processes a incoming IKEv2-Message. * * Message processing may fail. If a critical failure occurs, * process_message() return DESTROY_ME. Then the caller must * destroy the IKE_SA immediatly, as it is unusable. * - * @param this calling object - * @param message message to process + * @param message message to process * @return * - SUCCESS * - FAILED @@ -664,12 +634,11 @@ struct ike_sa_t { status_t (*process_message) (ike_sa_t *this, message_t *message); /** - * @brief Generate a IKE message to send it to the peer. + * Generate a IKE message to send it to the peer. * * This method generates all payloads in the message and encrypts/signs * the packet. * - * @param this calling object * @param message message to generate * @param packet generated output packet * @return @@ -681,9 +650,8 @@ struct ike_sa_t { packet_t **packet); /** - * @brief Retransmits a request. + * Retransmits a request. * - * @param this calling object * @param message_id ID of the request to retransmit * @return * - SUCCESS @@ -692,13 +660,12 @@ struct ike_sa_t { status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id); /** - * @brief Sends a DPD request to the peer. + * Sends a DPD request to the peer. * * To check if a peer is still alive, periodic * empty INFORMATIONAL messages are sent if no * other traffic was received. * - * @param this calling object * @return * - SUCCESS * - DESTROY_ME, if peer did not respond @@ -706,19 +673,17 @@ struct ike_sa_t { status_t (*send_dpd) (ike_sa_t *this); /** - * @brief Sends a keep alive packet. + * Sends a keep alive packet. * * To refresh NAT tables in a NAT router * between the peers, periodic empty * UDP packets are sent if no other traffic * was sent. - * - * @param this calling object */ void (*send_keepalive) (ike_sa_t *this); /** - * @brief Derive all keys and create the transforms for IKE communication. + * Derive all keys and create the transforms for IKE communication. * * Keys are derived using the diffie hellman secret, nonces and internal * stored SPIs. @@ -726,7 +691,6 @@ struct ike_sa_t { * existing IKE_SA (rekeying). The SK_d key from the old IKE_SA * is included in the derivation process. * - * @param this calling object * @param proposal proposal which contains algorithms to use * @param secret secret derived from DH exchange, gets freed * @param nonce_i initiators nonce @@ -740,49 +704,58 @@ struct ike_sa_t { bool initiator, prf_t *child_prf, prf_t *old_prf); /** - * @brief Get a multi purpose prf for the negotiated PRF function. + * Get the selected IKE proposal string + * + * @return string describing the selected IKE proposal + */ + char* (*get_proposal)(ike_sa_t *this); + + /** + * Set the selected IKE proposal string for status information purposes + * (the "%P" printf format handler is used) + * + * @param proposal string describing the selected IKE proposal + */ + void (*set_proposal)(ike_sa_t *this, char *proposal); + + /** + * Get a multi purpose prf for the negotiated PRF function. * - * @param this calling object * @return pointer to prf_t object */ prf_t *(*get_prf) (ike_sa_t *this); /** - * @brief Get the prf-object, which is used to derive keys for child SAs. + * Get the prf-object, which is used to derive keys for child SAs. * - * @param this calling object * @return pointer to prf_t object */ prf_t *(*get_child_prf) (ike_sa_t *this); /** - * @brief Get the key to build outgoing authentication data. + * Get the key to build outgoing authentication data. * - * @param this calling object * @return pointer to prf_t object */ chunk_t (*get_skp_build) (ike_sa_t *this); /** - * @brief Get the key to verify incoming authentication data. + * Get the key to verify incoming authentication data. * - * @param this calling object * @return pointer to prf_t object */ chunk_t (*get_skp_verify) (ike_sa_t *this); /** - * @brief Associates a child SA to this IKE SA + * Associates a child SA to this IKE SA * - * @param this calling object * @param child_sa child_sa to add */ void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); /** - * @brief Get a CHILD_SA identified by protocol and SPI. + * Get a CHILD_SA identified by protocol and SPI. * - * @param this calling object * @param protocol protocol of the SA * @param spi SPI of the CHILD_SA * @param inbound TRUE if SPI is inbound, FALSE if outbound @@ -792,19 +765,17 @@ struct ike_sa_t { u_int32_t spi, bool inbound); /** - * @brief Create an iterator over all CHILD_SAs. + * Create an iterator over all CHILD_SAs. * - * @param this calling object * @return iterator */ iterator_t* (*create_child_sa_iterator) (ike_sa_t *this); /** - * @brief Rekey the CHILD SA with the specified reqid. + * Rekey the CHILD SA with the specified reqid. * * Looks for a CHILD SA owned by this IKE_SA, and start the rekeing. * - * @param this calling object * @param protocol protocol of the SA * @param spi inbound SPI of the CHILD_SA * @return @@ -814,13 +785,12 @@ struct ike_sa_t { status_t (*rekey_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); /** - * @brief Close the CHILD SA with the specified protocol/SPI. + * Close the CHILD SA with the specified protocol/SPI. * * Looks for a CHILD SA owned by this IKE_SA, deletes it and * notify's the remote peer about the delete. The associated * states and policies in the kernel get deleted, if they exist. * - * @param this calling object * @param protocol protocol of the SA * @param spi inbound SPI of the CHILD_SA * @return @@ -830,11 +800,10 @@ struct ike_sa_t { status_t (*delete_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); /** - * @brief Destroy a CHILD SA with the specified protocol/SPI. + * Destroy a CHILD SA with the specified protocol/SPI. * * Looks for a CHILD SA owned by this IKE_SA and destroys it. * - * @param this calling object * @param protocol protocol of the SA * @param spi inbound SPI of the CHILD_SA * @return @@ -844,99 +813,98 @@ struct ike_sa_t { status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); /** - * @brief Rekey the IKE_SA. + * Rekey the IKE_SA. * * Sets up a new IKE_SA, moves all CHILDs to it and deletes this IKE_SA. * - * @param this calling object * @return - SUCCESS, if IKE_SA rekeying initiated */ status_t (*rekey) (ike_sa_t *this); /** - * @brief Restablish the IKE_SA. + * Reauthenticate the IKE_SA. * * Create a completely new IKE_SA with authentication, recreates all children * within the IKE_SA, closes this IKE_SA. * - * @param this calling object + * @return DESTROY_ME to destroy the IKE_SA + */ + status_t (*reauth) (ike_sa_t *this); + + /** + * Restablish the IKE_SA. + * + * Reestablish an IKE_SA after it has been closed. + * * @return DESTROY_ME to destroy the IKE_SA */ status_t (*reestablish) (ike_sa_t *this); /** - * @brief Set the lifetime limit received from a AUTH_LIFETIME notify. + * Set the lifetime limit received from a AUTH_LIFETIME notify. * - * @param this calling object * @param lifetime lifetime in seconds */ void (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); /** - * @brief Set the virtual IP to use for this IKE_SA and its children. + * Set the virtual IP to use for this IKE_SA and its children. * * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same * lifetime as the IKE_SA. * - * @param this calling object + * @param local TRUE to set local address, FALSE for remote + * @param ip IP to set as virtual IP */ void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); /** - * @brief Get the virtual IP configured. + * Get the virtual IP configured. * - * @param this calling object * @param local TRUE to get local virtual IP, FALSE for remote + * @return host_t *virtual IP */ host_t* (*get_virtual_ip) (ike_sa_t *this, bool local); /** - * @brief Add a DNS server to the system. + * Add a DNS server to the system. * * An IRAS may send a DNS server. To use it, it is installed on the * system. The DNS entry has a lifetime until the IKE_SA gets closed. * - * @param this calling object * @param dns DNS server to install on the system */ void (*add_dns_server) (ike_sa_t *this, host_t *dns); /** - * @brief Inherit all attributes of other to this after rekeying. + * Inherit all attributes of other to this after rekeying. * * When rekeying is completed, all CHILD_SAs, the virtual IP and all * outstanding tasks are moved from other to this. * As this call may initiate inherited tasks, a status is returned. * - * @param this calling object * @param other other task to inherit from * @return DESTROY_ME if initiation of inherited task failed */ status_t (*inherit) (ike_sa_t *this, ike_sa_t *other); /** - * @brief Reset the IKE_SA, useable when initiating fails - * - * @param this calling object + * Reset the IKE_SA, useable when initiating fails */ void (*reset) (ike_sa_t *this); /** - * @brief Destroys a ike_sa_t object. - * - * @param this calling object + * Destroys a ike_sa_t object. */ void (*destroy) (ike_sa_t *this); }; /** - * @brief Creates an ike_sa_t object with a specific ID. + * Creates an ike_sa_t object with a specific ID. * * @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA * @return ike_sa_t object - * - * @ingroup sa */ ike_sa_t *ike_sa_create(ike_sa_id_t *ike_sa_id); -#endif /* IKE_SA_H_ */ +#endif /* IKE_SA_H_ @} */ diff --git a/src/charon/sa/ike_sa_id.c b/src/charon/sa/ike_sa_id.c index a838c0b8a..e012d5944 100644 --- a/src/charon/sa/ike_sa_id.c +++ b/src/charon/sa/ike_sa_id.c @@ -1,10 +1,3 @@ -/** - * @file ike_sa_id.c - * - * @brief Implementation of ike_sa_id_t. - * - */ - /* * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -19,9 +12,10 @@ * 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. + * + * $Id: ike_sa_id.c 3589 2008-03-13 14:14:44Z martin $ */ - #include "ike_sa_id.h" #include <stdio.h> diff --git a/src/charon/sa/ike_sa_id.h b/src/charon/sa/ike_sa_id.h index 0606b7222..652c968b6 100644 --- a/src/charon/sa/ike_sa_id.h +++ b/src/charon/sa/ike_sa_id.h @@ -1,10 +1,3 @@ -/** - * @file ike_sa_id.h - * - * @brief Interface of ike_sa_id_t. - * - */ - /* * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -19,8 +12,14 @@ * 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. + * + * $Id: ike_sa_id.h 3589 2008-03-13 14:14:44Z martin $ */ +/** + * @defgroup ike_sa_id ike_sa_id + * @{ @ingroup sa + */ #ifndef IKE_SA_ID_H_ #define IKE_SA_ID_H_ @@ -29,119 +28,101 @@ typedef struct ike_sa_id_t ike_sa_id_t; #include <library.h> - /** - * @brief An object of type ike_sa_id_t is used to identify an IKE_SA. + * An object of type ike_sa_id_t is used to identify an IKE_SA. * * An IKE_SA is identified by its initiator and responder spi's. * Additionaly it contains the role of the actual running IKEv2-Daemon * for the specific IKE_SA (original initiator or responder). - * - * @b Constructors: - * - ike_sa_id_create() - * - * @ingroup sa */ struct ike_sa_id_t { /** - * @brief Set the SPI of the responder. + * Set the SPI of the responder. * * This function is called when a request or reply of a IKE_SA_INIT is received. * - * @param this calling object * @param responder_spi SPI of responder to set */ void (*set_responder_spi) (ike_sa_id_t *this, u_int64_t responder_spi); /** - * @brief Set the SPI of the initiator. + * Set the SPI of the initiator. * - * @param this calling object * @param initiator_spi SPI to set */ void (*set_initiator_spi) (ike_sa_id_t *this, u_int64_t initiator_spi); /** - * @brief Get the initiator SPI. + * Get the initiator SPI. * - * @param this calling object * @return SPI of the initiator */ u_int64_t (*get_initiator_spi) (ike_sa_id_t *this); /** - * @brief Get the responder SPI. + * Get the responder SPI. * - * @param this calling object * @return SPI of the responder */ u_int64_t (*get_responder_spi) (ike_sa_id_t *this); /** - * @brief Check if two ike_sa_id_t objects are equal. + * Check if two ike_sa_id_t objects are equal. * * Two ike_sa_id_t objects are equal if both SPI values and the role matches. * - * @param this calling object * @param other ike_sa_id_t object to check if equal * @return TRUE if given ike_sa_id_t are equal, FALSE otherwise */ bool (*equals) (ike_sa_id_t *this, ike_sa_id_t *other); /** - * @brief Replace all values of a given ike_sa_id_t object with values. + * Replace all values of a given ike_sa_id_t object with values. * from another ike_sa_id_t object. * * After calling this function, both objects are equal. * - * @param this calling object * @param other ike_sa_id_t object from which values will be taken */ void (*replace_values) (ike_sa_id_t *this, ike_sa_id_t *other); /** - * @brief Get the initiator flag. + * Get the initiator flag. * - * @param this calling object * @return TRUE if we are the original initator */ bool (*is_initiator) (ike_sa_id_t *this); /** - * @brief Switche the original initiator flag. + * Switche the original initiator flag. * - * @param this calling object * @return TRUE if we are the original initator after switch, FALSE otherwise */ bool (*switch_initiator) (ike_sa_id_t *this); /** - * @brief Clones a given ike_sa_id_t object. + * Clones a given ike_sa_id_t object. * - * @param this calling object * @return cloned ike_sa_id_t object */ ike_sa_id_t *(*clone) (ike_sa_id_t *this); /** - * @brief Destroys an ike_sa_id_t object. - * - * @param this calling object + * Destroys an ike_sa_id_t object. */ void (*destroy) (ike_sa_id_t *this); }; /** - * @brief Creates an ike_sa_id_t object with specific SPI's and defined role. + * Creates an ike_sa_id_t object with specific SPI's and defined role. * * @param initiator_spi initiators SPI * @param responder_spi responders SPI * @param is_initiaor TRUE if we are the original initiator * @return ike_sa_id_t object - * - * @ingroup sa */ -ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, bool is_initiaor); +ike_sa_id_t * ike_sa_id_create(u_int64_t initiator_spi, u_int64_t responder_spi, + bool is_initiaor); -#endif /*IKE_SA_ID_H_*/ +#endif /*IKE_SA_ID_H_ @} */ diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index 5e7f78af0..9c1b2d413 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -1,10 +1,3 @@ -/** - * @file ike_sa_manager.c - * - * @brief Implementation of ike_sa_mananger_t. - * - */ - /* * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -19,6 +12,8 @@ * 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. + * + * $Id: ike_sa_manager.c 4044 2008-06-06 15:05:54Z martin $ */ #include <pthread.h> @@ -30,6 +25,7 @@ #include <sa/ike_sa_id.h> #include <bus/bus.h> #include <utils/linked_list.h> +#include <crypto/hashers/hasher.h> typedef struct entry_t entry_t; @@ -79,6 +75,11 @@ struct entry_t { chunk_t init_hash; /** + * remote host address, required for DoS detection + */ + host_t *other; + + /** * message ID currently processing, if any */ u_int32_t message_id; @@ -93,6 +94,7 @@ static status_t entry_destroy(entry_t *this) this->ike_sa->destroy(this->ike_sa); this->ike_sa_id->destroy(this->ike_sa_id); chunk_free(&this->init_hash); + DESTROY_IF(this->other); free(this); return SUCCESS; } @@ -113,6 +115,7 @@ static entry_t *entry_create(ike_sa_id_t *ike_sa_id) this->driveout_waiting_threads = FALSE; this->message_id = -1; this->init_hash = chunk_empty; + this->other = NULL; /* ike_sa_id is always cloned */ this->ike_sa_id = ike_sa_id->clone(ike_sa_id); @@ -146,9 +149,9 @@ struct private_ike_sa_manager_t { linked_list_t *ike_sa_list; /** - * A randomizer, to get random SPIs for our side + * RNG to get random SPIs for our side */ - randomizer_t *randomizer; + rng_t *rng; /** * SHA1 hasher for IKE_SA_INIT retransmit detection @@ -159,20 +162,20 @@ struct private_ike_sa_manager_t { /** * Implementation of private_ike_sa_manager_t.get_entry_by_id. */ -static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry) +static status_t get_entry_by_id(private_ike_sa_manager_t *this, + ike_sa_id_t *ike_sa_id, entry_t **entry) { - linked_list_t *list = this->ike_sa_list; - iterator_t *iterator; + enumerator_t *enumerator; entry_t *current; status_t status; - /* create iterator over list of ike_sa's */ - iterator = list->create_iterator(list, TRUE); + /* create enumerator over list of ike_sa's */ + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); /* default status */ status = NOT_FOUND; - while (iterator->iterate(iterator, (void**)¤t)) + while (enumerator->enumerate(enumerator, ¤t)) { if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id)) { @@ -198,26 +201,26 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike } } - iterator->destroy(iterator); + enumerator->destroy(enumerator); return status; } /** * Implementation of private_ike_sa_manager_t.get_entry_by_sa. */ -static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa, entry_t **entry) +static status_t get_entry_by_sa(private_ike_sa_manager_t *this, + ike_sa_t *ike_sa, entry_t **entry) { - linked_list_t *list = this->ike_sa_list; - iterator_t *iterator; + enumerator_t *enumerator; entry_t *current; status_t status; - iterator = list->create_iterator(list, TRUE); + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); /* default status */ status = NOT_FOUND; - while (iterator->iterate(iterator, (void**)¤t)) + while (enumerator->enumerate(enumerator, ¤t)) { /* only pointers are compared */ if (current->ike_sa == ike_sa) @@ -228,7 +231,7 @@ static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa break; } } - iterator->destroy(iterator); + enumerator->destroy(enumerator); return status; } @@ -238,16 +241,15 @@ static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_t *ike_sa */ static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry) { - linked_list_t *list = this->ike_sa_list; - iterator_t *iterator; + enumerator_t *enumerator; entry_t *current; status_t status; - iterator = list->create_iterator(list, TRUE); + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); status = NOT_FOUND; - while (iterator->iterate(iterator, (void**)¤t)) + while (enumerator->enumerate(enumerator, ¤t)) { if (current == entry) { @@ -263,13 +265,13 @@ static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry) } DBG2(DBG_MGR, "found entry by pointer, deleting it"); - iterator->remove(iterator); + this->ike_sa_list->remove_at(this->ike_sa_list, enumerator); entry_destroy(entry); status = SUCCESS; break; } } - iterator->destroy(iterator); + enumerator->destroy(enumerator); return status; } @@ -309,8 +311,7 @@ static u_int64_t get_next_spi(private_ike_sa_manager_t *this) { u_int64_t spi; - this->randomizer->get_pseudo_random_bytes(this->randomizer, sizeof(spi), - (u_int8_t*)&spi); + this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi); return spi; } @@ -386,7 +387,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, message->get_exchange_type(message) == IKE_SA_INIT) { /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */ - iterator_t *iterator; + enumerator_t *enumerator; chunk_t data, hash; data = message->get_packet_data(message); @@ -394,14 +395,14 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, chunk_free(&data); pthread_mutex_lock(&this->mutex); - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { if (chunk_equals(hash, entry->init_hash)) { if (entry->message_id == 0) { - iterator->destroy(iterator); + enumerator->destroy(enumerator); pthread_mutex_unlock(&this->mutex); chunk_free(&hash); id->destroy(id); @@ -418,7 +419,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, break; } } - iterator->destroy(iterator); + enumerator->destroy(enumerator); pthread_mutex_unlock(&this->mutex); if (ike_sa == NULL) @@ -488,7 +489,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, peer_cfg_t *peer_cfg) { - iterator_t *iterator; + enumerator_t *enumerator; entry_t *entry; ike_sa_t *ike_sa = NULL; identification_t *my_id, *other_id; @@ -496,65 +497,70 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, ike_cfg_t *ike_cfg; ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); - my_host = ike_cfg->get_my_host(ike_cfg); - other_host = ike_cfg->get_other_host(ike_cfg); my_id = peer_cfg->get_my_id(peer_cfg); other_id = peer_cfg->get_other_id(peer_cfg); + my_host = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg), 0, 0); + other_host = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg), 0, 0); pthread_mutex_lock(&(this->mutex)); - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) + if (my_host && other_host) { - identification_t *found_my_id, *found_other_id; - host_t *found_my_host, *found_other_host; - int wc; - - if (!wait_for_entry(this, entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { - continue; - } + identification_t *found_my_id, *found_other_id; + host_t *found_my_host, *found_other_host; - if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) - { - /* skip IKE_SA which are not useable */ - continue; - } + if (!wait_for_entry(this, entry)) + { + continue; + } - found_my_id = entry->ike_sa->get_my_id(entry->ike_sa); - found_other_id = entry->ike_sa->get_other_id(entry->ike_sa); - found_my_host = entry->ike_sa->get_my_host(entry->ike_sa); - found_other_host = entry->ike_sa->get_other_host(entry->ike_sa); + if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) + { + /* skip IKE_SA which are not useable */ + continue; + } - if (found_my_id->get_type(found_my_id) == ID_ANY && - found_other_id->get_type(found_other_id) == ID_ANY) - { - /* IKE_SA has no IDs yet, so we can't use it */ - continue; - } - DBG2(DBG_MGR, "candidate IKE_SA for \n\t%H[%D]...%H[%D]\n\t%H[%D]...%H[%D]", - my_host, my_id, other_host, other_id, - found_my_host, found_my_id, found_other_host, found_other_id); - /* compare ID and hosts. Supplied ID may contain wildcards, and IP - * may be %any. */ - if ((my_host->is_anyaddr(my_host) || - my_host->ip_equals(my_host, found_my_host)) && - (other_host->is_anyaddr(other_host) || - other_host->ip_equals(other_host, found_other_host)) && - found_my_id->matches(found_my_id, my_id, &wc) && - found_other_id->matches(found_other_id, other_id, &wc) && - streq(peer_cfg->get_name(peer_cfg), - entry->ike_sa->get_name(entry->ike_sa))) - { - /* looks good, we take this one */ - DBG2(DBG_MGR, "found an existing IKE_SA for %H[%D]...%H[%D]", - my_host, my_id, other_host, other_id); - entry->checked_out = TRUE; - ike_sa = entry->ike_sa; - break; + found_my_id = entry->ike_sa->get_my_id(entry->ike_sa); + found_other_id = entry->ike_sa->get_other_id(entry->ike_sa); + found_my_host = entry->ike_sa->get_my_host(entry->ike_sa); + found_other_host = entry->ike_sa->get_other_host(entry->ike_sa); + + if (found_my_id->get_type(found_my_id) == ID_ANY && + found_other_id->get_type(found_other_id) == ID_ANY) + { + /* IKE_SA has no IDs yet, so we can't use it */ + continue; + } + DBG2(DBG_MGR, "candidate IKE_SA for \n\t" + "%H[%D]...%H[%D]\n\t%H[%D]...%H[%D]", + my_host, my_id, other_host, other_id, + found_my_host, found_my_id, found_other_host, found_other_id); + /* compare ID and hosts. Supplied ID may contain wildcards, and IP + * may be %any. */ + if ((my_host->is_anyaddr(my_host) || + my_host->ip_equals(my_host, found_my_host)) && + (other_host->is_anyaddr(other_host) || + other_host->ip_equals(other_host, found_other_host)) && + found_my_id->matches(found_my_id, my_id) && + found_other_id->matches(found_other_id, other_id) && + streq(peer_cfg->get_name(peer_cfg), + entry->ike_sa->get_name(entry->ike_sa))) + { + /* looks good, we take this one */ + DBG2(DBG_MGR, "found an existing IKE_SA for %H[%D]...%H[%D]", + my_host, my_id, other_host, other_id); + entry->checked_out = TRUE; + ike_sa = entry->ike_sa; + break; + } } + enumerator->destroy(enumerator); } - iterator->destroy(iterator); + DESTROY_IF(my_host); + DESTROY_IF(other_host); if (!ike_sa) { @@ -589,15 +595,16 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, bool child) { - iterator_t *iterator, *children; + enumerator_t *enumerator; + iterator_t *children; entry_t *entry; ike_sa_t *ike_sa = NULL; child_sa_t *child_sa; pthread_mutex_lock(&(this->mutex)); - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { if (wait_for_entry(this, entry)) { @@ -630,7 +637,7 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, } } } - iterator->destroy(iterator); + enumerator->destroy(enumerator); pthread_mutex_unlock(&(this->mutex)); charon->bus->set_sa(charon->bus, ike_sa); @@ -643,15 +650,16 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, bool child) { - iterator_t *iterator, *children; + enumerator_t *enumerator; + iterator_t *children; entry_t *entry; ike_sa_t *ike_sa = NULL; child_sa_t *child_sa; pthread_mutex_lock(&(this->mutex)); - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { if (wait_for_entry(this, entry)) { @@ -684,39 +692,82 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, } } } - iterator->destroy(iterator); + enumerator->destroy(enumerator); pthread_mutex_unlock(&(this->mutex)); charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } + +/** + * Implementation of ike_sa_manager_t.checkout_duplicate. + */ +static ike_sa_t* checkout_duplicate(private_ike_sa_manager_t *this, + ike_sa_t *ike_sa) +{ + enumerator_t *enumerator; + entry_t *entry; + ike_sa_t *duplicate = NULL; + identification_t *me, *other; + + me = ike_sa->get_my_id(ike_sa); + other = ike_sa->get_other_id(ike_sa); + + pthread_mutex_lock(&this->mutex); + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->ike_sa == ike_sa) + { /* self is not a duplicate */ + continue; + } + if (wait_for_entry(this, entry)) + { + if (me->equals(me, entry->ike_sa->get_my_id(entry->ike_sa)) && + other->equals(other, entry->ike_sa->get_other_id(entry->ike_sa))) + { + duplicate = entry->ike_sa; + entry->checked_out = TRUE; + break; + } + } + } + enumerator->destroy(enumerator); + pthread_mutex_unlock(&this->mutex); + return duplicate; +} + +/** + * enumerator cleanup function + */ +static void enumerator_unlock(private_ike_sa_manager_t *this) +{ + pthread_mutex_unlock(&this->mutex); +} /** - * Iterator hook for iterate, gets ike_sas instead of entries + * enumerator filter function */ -static hook_result_t iterator_hook(private_ike_sa_manager_t* this, entry_t *in, - ike_sa_t **out) +static bool enumerator_filter(private_ike_sa_manager_t *this, + entry_t **in, ike_sa_t **out) { - /* check out entry */ - if (wait_for_entry(this, in)) + if (wait_for_entry(this, *in)) { - *out = in->ike_sa; - return HOOK_NEXT; + *out = (*in)->ike_sa; + return TRUE; } - return HOOK_SKIP; + return FALSE; } /** * Implementation of ike_sa_manager_t.create_iterator. */ -static iterator_t *create_iterator(private_ike_sa_manager_t* this) +static enumerator_t *create_enumerator(private_ike_sa_manager_t* this) { - iterator_t *iterator = this->ike_sa_list->create_iterator_locked( - this->ike_sa_list, &this->mutex); - - /* register hook to iterator over ike_sas, not entries */ - iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this); - return iterator; + pthread_mutex_lock(&this->mutex); + return enumerator_create_filter( + this->ike_sa_list->create_enumerator(this->ike_sa_list), + (void*)enumerator_filter, this, (void*)enumerator_unlock); } /** @@ -732,6 +783,7 @@ static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) status_t retval; entry_t *entry; ike_sa_id_t *ike_sa_id; + host_t *other; ike_sa_id = ike_sa->get_id(ike_sa); @@ -747,6 +799,13 @@ static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) /* signal waiting threads */ entry->checked_out = FALSE; entry->message_id = -1; + /* apply remote address for DoS detection */ + other = ike_sa->get_other_host(ike_sa); + if (!entry->other || !other->equals(other, entry->other)) + { + DESTROY_IF(entry->other); + entry->other = other->clone(other); + } DBG2(DBG_MGR, "check-in of IKE_SA successful."); pthread_cond_signal(&(entry->condvar)); retval = SUCCESS; @@ -783,6 +842,7 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik ike_sa_id = ike_sa->get_id(ike_sa); DBG2(DBG_MGR, "checkin and destroy IKE_SA"); + charon->bus->set_sa(charon->bus, NULL); pthread_mutex_lock(&(this->mutex)); @@ -803,7 +863,6 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik } pthread_mutex_unlock(&(this->mutex)); - charon->bus->set_sa(charon->bus, ike_sa); return retval; } @@ -812,23 +871,22 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik */ static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) { - iterator_t *iterator; + enumerator_t *enumerator; entry_t *entry; int count = 0; pthread_mutex_lock(&(this->mutex)); - iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { /* we check if we have a responder CONNECTING IKE_SA without checkout */ if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && entry->ike_sa->get_state(entry->ike_sa) == IKE_CONNECTING) { - /* if we have a host, we have wait until no other uses the IKE_SA */ + /* if we have a host, count only matching IKE_SAs */ if (ip) { - if (wait_for_entry(this, entry) && ip->ip_equals(ip, - entry->ike_sa->get_other_host(entry->ike_sa))) + if (entry->other && ip->ip_equals(ip, entry->other)) { count++; } @@ -839,37 +897,37 @@ static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) } } } - iterator->destroy(iterator); + enumerator->destroy(enumerator); pthread_mutex_unlock(&(this->mutex)); return count; } /** - * Implementation of ike_sa_manager_t.destroy. + * Implementation of ike_sa_manager_t.flush. */ -static void destroy(private_ike_sa_manager_t *this) +static void flush(private_ike_sa_manager_t *this) { /* destroy all list entries */ - linked_list_t *list = this->ike_sa_list; - iterator_t *iterator; + enumerator_t *enumerator; entry_t *entry; pthread_mutex_lock(&(this->mutex)); DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's"); /* Step 1: drive out all waiting threads */ DBG2(DBG_MGR, "set driveout flags for all stored IKE_SA's"); - iterator = list->create_iterator(list, TRUE); - while (iterator->iterate(iterator, (void**)&entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { /* do not accept new threads, drive out waiting threads */ entry->driveout_new_threads = TRUE; entry->driveout_waiting_threads = TRUE; } + enumerator->destroy(enumerator); DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's"); /* Step 2: wait until all are gone */ - iterator->reset(iterator); - while (iterator->iterate(iterator, (void**)&entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { while (entry->waiting_threads) { @@ -879,21 +937,33 @@ static void destroy(private_ike_sa_manager_t *this) pthread_cond_wait(&(entry->condvar), &(this->mutex)); } } + enumerator->destroy(enumerator); DBG2(DBG_MGR, "delete all IKE_SA's"); /* Step 3: initiate deletion of all IKE_SAs */ - iterator->reset(iterator); - while (iterator->iterate(iterator, (void**)&entry)) + enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + while (enumerator->enumerate(enumerator, &entry)) { entry->ike_sa->delete(entry->ike_sa); } - iterator->destroy(iterator); + enumerator->destroy(enumerator); DBG2(DBG_MGR, "destroy all entries"); /* Step 4: destroy all entries */ - list->destroy_function(list, (void*)entry_destroy); + while (this->ike_sa_list->remove_last(this->ike_sa_list, + (void**)&entry) == SUCCESS) + { + entry_destroy(entry); + } pthread_mutex_unlock(&(this->mutex)); - - this->randomizer->destroy(this->randomizer); +} + +/** + * Implementation of ike_sa_manager_t.destroy. + */ +static void destroy(private_ike_sa_manager_t *this) +{ + this->ike_sa_list->destroy(this->ike_sa_list); + this->rng->destroy(this->rng); this->hasher->destroy(this->hasher); free(this); @@ -907,6 +977,7 @@ ike_sa_manager_t *ike_sa_manager_create() private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t); /* assign public functions */ + this->public.flush = (void(*)(ike_sa_manager_t*))flush; this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; this->public.checkout_new = (ike_sa_t*(*)(ike_sa_manager_t*,bool))checkout_new; @@ -914,16 +985,30 @@ ike_sa_manager_t *ike_sa_manager_create() this->public.checkout_by_config = (ike_sa_t*(*)(ike_sa_manager_t*,peer_cfg_t*))checkout_by_config; this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id; this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name; - this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator; + this->public.checkout_duplicate = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))checkout_duplicate; + this->public.create_enumerator = (enumerator_t*(*)(ike_sa_manager_t*))create_enumerator; this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count; /* initialize private variables */ + this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_PREFERRED); + if (this->hasher == NULL) + { + DBG1(DBG_MGR, "manager initialization failed, no hasher supported"); + free(this); + return NULL; + } + this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (this->rng == NULL) + { + DBG1(DBG_MGR, "manager initialization failed, no RNG supported"); + this->hasher->destroy(this->hasher); + free(this); + return NULL; + } this->ike_sa_list = linked_list_create(); pthread_mutex_init(&this->mutex, NULL); - this->randomizer = randomizer_create(); - this->hasher = hasher_create(HASH_SHA1); - return &this->public; } + diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h index a73a106ba..04b6d96c2 100644 --- a/src/charon/sa/ike_sa_manager.h +++ b/src/charon/sa/ike_sa_manager.h @@ -1,10 +1,3 @@ -/** - * @file ike_sa_manager.h - * - * @brief Interface of ike_sa_manager_t. - * - */ - /* * Copyright (C) 2005-2006 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -19,6 +12,13 @@ * 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. + * + * $Id: ike_sa_manager.h 3819 2008-04-17 10:46:25Z martin $ + */ + +/** + * @defgroup ike_sa_manager ike_sa_manager + * @{ @ingroup sa */ #ifndef IKE_SA_MANAGER_H_ @@ -32,7 +32,7 @@ typedef struct ike_sa_manager_t ike_sa_manager_t; #include <config/peer_cfg.h> /** - * @brief The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's. + * The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's. * * To avoid access from multiple threads, IKE_SAs must be checked out from * the manager, and checked in after usage. @@ -42,18 +42,12 @@ typedef struct ike_sa_manager_t ike_sa_manager_t; * This could be done by comparing thread-ids via pthread_self()... * * @todo Managing of ike_sa_t objects in a hash table instead of linked list. - * - * @b Constructors: - * - ike_sa_manager_create() - * - * @ingroup sa */ struct ike_sa_manager_t { /** - * @brief Checkout an existing IKE_SA. + * Checkout an existing IKE_SA. * - * @param this the manager object * @param ike_sa_id the SA identifier, will be updated * @returns * - checked out IKE_SA if found @@ -62,16 +56,15 @@ struct ike_sa_manager_t { ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id); /** - * @brief Create and check out a new IKE_SA. + * Create and check out a new IKE_SA. * - * @param this the manager object * @param initiator TRUE for initiator, FALSE otherwise * @returns created andchecked out IKE_SA */ ike_sa_t* (*checkout_new) (ike_sa_manager_t* this, bool initiator); /** - * @brief Checkout an IKE_SA by a message. + * Checkout an IKE_SA by a message. * * In some situations, it is necessary that the manager knows the * message to use for the checkout. This has the folloing reasons: @@ -86,7 +79,6 @@ struct ike_sa_manager_t { * If processing the message does not make sense (for the reasons above), * NULL is returned. * - * @param this the manager object * @param ike_sa_id the SA identifier, will be updated * @returns * - checked out/created IKE_SA @@ -95,7 +87,7 @@ struct ike_sa_manager_t { ike_sa_t* (*checkout_by_message) (ike_sa_manager_t* this, message_t *message); /** - * @brief Checkout an IKE_SA for initiation by a peer_config. + * Checkout an IKE_SA for initiation by a peer_config. * * To initiate, a CHILD_SA may be established within an existing IKE_SA. * This call checks for an existing IKE_SA by comparing the configuration. @@ -104,7 +96,6 @@ struct ike_sa_manager_t { * If no IKE_SA is found, a new one is created. This is also the case when * the found IKE_SA is in the DELETING state. * - * @param this the manager object * @param peer_cfg configuration used to find an existing IKE_SA * @return checked out/created IKE_SA */ @@ -112,14 +103,21 @@ struct ike_sa_manager_t { peer_cfg_t *peer_cfg); /** - * @brief Check out an IKE_SA a unique ID. + * Check out a duplicate if ike_sa to do uniqueness tests. + * + * @param ike_sa ike_sa to get a duplicate from + * @return checked out duplicate + */ + ike_sa_t* (*checkout_duplicate)(ike_sa_manager_t *this, ike_sa_t *ike_sa); + + /** + * Check out an IKE_SA a unique ID. * * Every IKE_SA and every CHILD_SA is uniquely identified by an ID. * These checkout function uses, depending * on the child parameter, the unique ID of the IKE_SA or the reqid * of one of a IKE_SAs CHILD_SA. * - * @param this the manager object * @param id unique ID of the object * @param child TRUE to use CHILD, FALSE to use IKE_SA * @return @@ -130,12 +128,11 @@ struct ike_sa_manager_t { bool child); /** - * @brief Check out an IKE_SA by the policy/connection name. + * Check out an IKE_SA by the policy/connection name. * * Check out the IKE_SA by the connections name or by a CHILD_SAs policy * name. * - * @param this the manager object * @param name name of the connection/policy * @param child TRUE to use policy name, FALSE to use conn name * @return @@ -146,24 +143,21 @@ struct ike_sa_manager_t { bool child); /** - * @brief Create an iterator over all stored IKE_SAs. + * Create an enumerator over all stored IKE_SAs. * - * The avoid synchronization issues, the iterator locks access + * The avoid synchronization issues, the enumerator locks access * to the manager exclusively, until it gets destroyed. - * This iterator is for reading only! Writing will corrupt the manager. * - * @param this the manager object - * @return iterator over all IKE_SAs. + * @return enumerator over all IKE_SAs. */ - iterator_t *(*create_iterator) (ike_sa_manager_t* this); + enumerator_t *(*create_enumerator) (ike_sa_manager_t* this); /** - * @brief Checkin the SA after usage. + * Checkin the SA after usage. * * @warning the SA pointer MUST NOT be used after checkin! * The SA must be checked out again! * - * @param this the manager object * @param ike_sa_id the SA identifier, will be updated * @param ike_sa checked out SA * @returns @@ -173,7 +167,7 @@ struct ike_sa_manager_t { status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); /** - * @brief Destroy a checked out SA. + * Destroy a checked out SA. * * The IKE SA is destroyed without notification of the remote peer. * Use this only if the other peer doesn't respond or behaves not @@ -182,7 +176,6 @@ struct ike_sa_manager_t { * so this can be called if the SA is in a "unclean" state, without the * risk that another thread can get the SA. * - * @param this the manager object * @param ike_sa SA to delete * @returns * - SUCCESS if found @@ -191,7 +184,7 @@ struct ike_sa_manager_t { status_t (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa); /** - * @brief Get the number of IKE_SAs which are in the connecting state. + * Get the number of IKE_SAs which are in the connecting state. * * To prevent the server from resource exhaustion, cookies and other * mechanisms are used. The number of half open IKE_SAs is a good @@ -200,29 +193,31 @@ struct ike_sa_manager_t { * from this IP are counted. * Only SAs for which we are the responder are counted. * - * @param this the manager object * @param ip NULL for all, IP for half open IKE_SAs with IP * @return number of half open IKE_SAs */ int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip); /** - * @brief Destroys the manager with all associated SAs. + * Delete all existing IKE_SAs and destroy them immediately. * * Threads will be driven out, so all SAs can be deleted cleanly. - * - * @param this the manager object + */ + void (*flush)(ike_sa_manager_t *this); + + /** + * Destroys the manager with all associated SAs. + * + * A call to flush() is required before calling destroy. */ void (*destroy) (ike_sa_manager_t *this); }; /** - * @brief Create a manager. - * - * @returns ike_sa_manager_t object + * Create a manager. * - * @ingroup sa + * @returns ike_sa_manager_t object, NULL if initialization fails */ ike_sa_manager_t *ike_sa_manager_create(void); -#endif /*IKE_SA_MANAGER_H_*/ +#endif /*IKE_SA_MANAGER_H_ @} */ diff --git a/src/charon/sa/mediation_manager.c b/src/charon/sa/mediation_manager.c index f6137304d..d15f4c100 100644 --- a/src/charon/sa/mediation_manager.c +++ b/src/charon/sa/mediation_manager.c @@ -1,10 +1,3 @@ -/** - * @file mediation_manager.c - * - * @brief Implementation of mediation_manager_t. - * - */ - /* * Copyright (C) 2007 Tobias Brunner * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: mediation_manager.c 3773 2008-04-07 09:36:52Z tobias $ */ #include "mediation_manager.h" @@ -246,6 +241,7 @@ static void update_sa_id(private_mediation_manager_t *this, identification_t *pe { job_t *job = (job_t*)mediation_callback_job_create(requester, peer_id); charon->processor->queue_job(charon->processor, job); + requester->destroy(requester); } pthread_mutex_unlock(&(this->mutex)); diff --git a/src/charon/sa/mediation_manager.h b/src/charon/sa/mediation_manager.h index 74acc4d41..d21c93244 100644 --- a/src/charon/sa/mediation_manager.h +++ b/src/charon/sa/mediation_manager.h @@ -1,10 +1,3 @@ -/** - * @file mediation_manager.h - * - * @brief Interface of mediation_manager_t. - * - */ - /* * Copyright (C) 2007 Tobias Brunner * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: mediation_manager.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup mediation_manager mediation_manager + * @{ @ingroup sa */ #ifndef MEDIATION_MANAGER_H_ @@ -29,29 +29,22 @@ typedef struct mediation_manager_t mediation_manager_t; #include <utils/identification.h> /** - * @brief The mediation manager is responsible for managing currently online + * The mediation manager is responsible for managing currently online * peers and registered requests for offline peers on the mediation server. - * - * @b Constructors: - * - mediation_manager_create() - * - * @ingroup sa */ struct mediation_manager_t { /** - * @brief Remove the IKE_SA of a peer. + * Remove the IKE_SA of a peer. * - * @param this the manager object * @param ike_sa_id the IKE_SA ID of the peer's SA */ void (*remove) (mediation_manager_t* this, ike_sa_id_t *ike_sa_id); /** - * @brief Update the ike_sa_id that is assigned to a peer's ID. If the peer + * Update the ike_sa_id that is assigned to a peer's ID. If the peer * is new, it gets a new record assigned. * - * @param this the manager object * @param peer_id the peer's ID * @param ike_sa_id the IKE_SA ID of the peer's SA */ @@ -59,9 +52,8 @@ struct mediation_manager_t { ike_sa_id_t *ike_sa_id); /** - * @brief Checks if a specific peer is online. + * Checks if a specific peer is online. * - * @param this the manager object * @param peer_id the peer's ID * @returns * - IKE_SA ID of the peer's SA. @@ -71,10 +63,9 @@ struct mediation_manager_t { identification_t *peer_id); /** - * @brief Checks if a specific peer is online and registers the requesting + * Checks if a specific peer is online and registers the requesting * peer if it is not. * - * @param this the manager object * @param peer_id the peer's ID * @param requester the requesters ID * @returns @@ -85,20 +76,16 @@ struct mediation_manager_t { identification_t *peer_id, identification_t *requester); /** - * @brief Destroys the manager with all data. - * - * @param this the manager object + * Destroys the manager with all data. */ void (*destroy) (mediation_manager_t *this); }; /** - * @brief Create a manager. + * Create a manager. * * @returns mediation_manager_t object - * - * @ingroup sa */ mediation_manager_t *mediation_manager_create(void); -#endif /*MEDIATION_MANAGER_H_*/ +#endif /*MEDIATION_MANAGER_H_ @} */ diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c index 89f527aba..e453fff00 100644 --- a/src/charon/sa/task_manager.c +++ b/src/charon/sa/task_manager.c @@ -1,10 +1,3 @@ -/** - * @file task_manager.c - * - * @brief Implementation of task_manager_t. - * - */ - /* * Copyright (C) 2007 Tobias Brunner * Copyright (C) 2007 Martin Willi @@ -19,6 +12,8 @@ * 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. + * + * $Id: task_manager.c 3666 2008-03-26 18:40:19Z tobias $ */ #include "task_manager.h" @@ -31,7 +26,8 @@ #include <sa/tasks/ike_mobike.h> #include <sa/tasks/ike_auth.h> #include <sa/tasks/ike_auth_lifetime.h> -#include <sa/tasks/ike_cert.h> +#include <sa/tasks/ike_cert_pre.h> +#include <sa/tasks/ike_cert_post.h> #include <sa/tasks/ike_rekey.h> #include <sa/tasks/ike_delete.h> #include <sa/tasks/ike_config.h> @@ -42,8 +38,8 @@ #include <encoding/payloads/delete_payload.h> #include <processing/jobs/retransmit_job.h> -#ifdef P2P -#include <sa/tasks/ike_p2p.h> +#ifdef ME +#include <sa/tasks/ike_me.h> #endif typedef struct exchange_t exchange_t; @@ -328,15 +324,16 @@ static status_t build_request(private_task_manager_t *this) this->initiating.mid = 0; exchange = IKE_SA_INIT; activate_task(this, IKE_NATD); - activate_task(this, IKE_CERT); -#ifdef P2P + activate_task(this, IKE_CERT_PRE); +#ifdef ME /* this task has to be activated before the IKE_AUTHENTICATE * task, because that task pregenerates the packet after * which no payloads can be added to the message anymore. */ - activate_task(this, IKE_P2P); -#endif /* P2P */ + activate_task(this, IKE_ME); +#endif /* ME */ activate_task(this, IKE_AUTHENTICATE); + activate_task(this, IKE_CERT_POST); activate_task(this, IKE_CONFIG); activate_task(this, CHILD_CREATE); activate_task(this, IKE_AUTH_LIFETIME); @@ -384,13 +381,13 @@ static status_t build_request(private_task_manager_t *this) exchange = INFORMATIONAL; break; } -#ifdef P2P - if (activate_task(this, IKE_P2P)) +#ifdef ME + if (activate_task(this, IKE_ME)) { - exchange = P2P_CONNECT; + exchange = ME_CONNECT; break; } -#endif /* P2P */ +#endif /* ME */ case IKE_REKEYING: if (activate_task(this, IKE_DELETE)) { @@ -687,14 +684,16 @@ static status_t process_request(private_task_manager_t *this, 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_create(this->ike_sa, FALSE); + task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); -#ifdef P2P - task = (task_t*)ike_p2p_create(this->ike_sa, FALSE); +#ifdef ME + task = (task_t*)ike_me_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); -#endif /* P2P */ +#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); @@ -818,13 +817,13 @@ static status_t process_request(private_task_manager_t *this, this->passive_tasks->insert_last(this->passive_tasks, task); break; } -#ifdef P2P - case P2P_CONNECT: +#ifdef ME + case ME_CONNECT: { - task = (task_t*)ike_p2p_create(this->ike_sa, FALSE); + task = (task_t*)ike_me_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); } -#endif /* P2P */ +#endif /* ME */ default: break; } diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h index 38c63c1a9..6243ac888 100644 --- a/src/charon/sa/task_manager.h +++ b/src/charon/sa/task_manager.h @@ -1,10 +1,3 @@ -/** - * @file task_manager.h - * - * @brief Interface of task_manager_t. - * - */ - /* * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: task_manager.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup task_manager task_manager + * @{ @ingroup sa */ #ifndef TASK_MANAGER_H_ @@ -32,42 +32,32 @@ typedef struct task_manager_t task_manager_t; /** * First retransmit timeout in milliseconds. - * - * @ingroup sa */ #define RETRANSMIT_TIMEOUT 4000 /** * Base which is raised to the power of the retransmission try. - * - * @ingroup sa */ #define RETRANSMIT_BASE 1.8 /** * Number of retransmits done before giving up. - * - * @ingroup sa */ #define RETRANSMIT_TRIES 5 /** * Interval for mobike routability checks in ms. - * - * @ingroup sa */ #define ROUTEABILITY_CHECK_INTERVAL 2500 /** * Number of routability checks before giving up - * - * @ingroup sa */ #define ROUTEABILITY_CHECK_TRIES 10 /** - * @brief The task manager, juggles task and handles message exchanges. + * The task manager, juggles task and handles message exchanges. * * On incoming requests, the task manager creates new tasks on demand and * juggles the request through all available tasks. Each task inspects the @@ -97,18 +87,12 @@ typedef struct task_manager_t task_manager_t; @endberbatim * The peer is considered dead after 2min 45s when no reply comes in. - * - * @b Constructors: - * - task_manager_create() - * - * @ingroup sa */ struct task_manager_t { /** - * @brief Process an incoming message. + * Process an incoming message. * - * @param this calling object * @param message message to add payloads to * @return * - DESTROY_ME if IKE_SA must be closed @@ -117,28 +101,24 @@ struct task_manager_t { status_t (*process_message) (task_manager_t *this, message_t *message); /** - * @brief Initiate an exchange with the currently queued tasks. - * - * @param this calling object + * Initiate an exchange with the currently queued tasks. */ status_t (*initiate) (task_manager_t *this); /** - * @brief Queue a task in the manager. + * Queue a task in the manager. * - * @param this calling object * @param task task to queue */ void (*queue_task) (task_manager_t *this, task_t *task); /** - * @brief Retransmit a request if it hasn't been acknowledged yet. + * Retransmit a request if it hasn't been acknowledged yet. * * A return value of INVALID_STATE means that the message was already * acknowledged and has not to be retransmitted. A return value of SUCCESS * means retransmission was required and the message has been resent. * - * @param this calling object * @param message_id ID of the message to retransmit * @return * - INVALID_STATE if retransmission not required @@ -147,52 +127,45 @@ struct task_manager_t { status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); /** - * @brief Migrate all tasks from other to this. + * Migrate all tasks from other to this. * * To rekey or reestablish an IKE_SA completely, all queued or active * tasks should get migrated to the new IKE_SA. * - * @param this manager which gets all tasks * @param other manager which gives away its tasks */ void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); /** - * @brief Reset message ID counters of the task manager. + * Reset message ID counters of the task manager. * * The IKEv2 protocol requires to restart exchanges with message IDs * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method * resets the message IDs and resets all active tasks using the migrate() * method. * - * @param this calling object * @param other manager which gives away its tasks */ void (*reset) (task_manager_t *this); /** - * @brief Check if we are currently waiting for a reply. + * Check if we are currently waiting for a reply. * - * @param this calling object * @return TRUE if we are waiting, FALSE otherwise */ bool (*busy) (task_manager_t *this); /** - * @brief Destroy the task_manager_t. - * - * @param this calling object + * Destroy the task_manager_t. */ void (*destroy) (task_manager_t *this); }; /** - * @brief Create an instance of the task manager. + * Create an instance of the task manager. * * @param ike_sa IKE_SA to manage. - * - * @ingroup sa */ task_manager_t *task_manager_create(ike_sa_t *ike_sa); -#endif /* TASK_MANAGER_H_ */ +#endif /* TASK_MANAGER_H_ @} */ diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c index 3947a84d1..4638da03e 100644 --- a/src/charon/sa/tasks/child_create.c +++ b/src/charon/sa/tasks/child_create.c @@ -1,11 +1,5 @@ -/** - * @file child_create.c - * - * @brief Implementation of the child_create task. - * - */ - /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -19,6 +13,8 @@ * 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. + * + * $Id: child_create.c 3920 2008-05-08 16:19:11Z tobias $ */ #include "child_create.h" @@ -105,6 +101,21 @@ struct private_child_create_t { mode_t mode; /** + * IPComp transform to use + */ + ipcomp_transform_t ipcomp; + + /** + * IPComp transform proposed or accepted by the other peer + */ + ipcomp_transform_t ipcomp_received; + + /** + * Other Compression Parameter Index (CPI) + */ + u_int16_t other_cpi; + + /** * reqid to use if we are rekeying */ u_int32_t reqid; @@ -141,17 +152,16 @@ static status_t get_nonce(message_t *message, chunk_t *nonce) */ static status_t generate_nonce(chunk_t *nonce) { - status_t status; - randomizer_t *randomizer = randomizer_create(); + rng_t *rng; - status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, - nonce); - randomizer->destroy(randomizer); - if (status != SUCCESS) + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) { - DBG1(DBG_IKE, "error generating random nonce value"); + DBG1(DBG_IKE, "error generating nonce value, no RNG found"); return FAILED; } + rng->allocate_bytes(rng, NONCE_SIZE, nonce); + rng->destroy(rng); return SUCCESS; } @@ -227,11 +237,11 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) if (!this->proposal->has_dh_group(this->proposal, this->dh_group)) { - algorithm_t *algo; + u_int16_t group; + if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, - &algo)) + &group, NULL)) { - u_int16_t group = algo->algorithm; SIG(CHILD_UP_FAILED, "DH group %N inacceptable, requesting %N", diffie_hellman_group_names, this->dh_group, diffie_hellman_group_names, group); @@ -332,6 +342,12 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) } prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed); + if (this->ipcomp != IPCOMP_NONE) + { + this->child_sa->activate_ipcomp(this->child_sa, this->ipcomp, + this->other_cpi); + } + if (this->initiator) { status = this->child_sa->update(this->child_sa, this->proposal, @@ -422,6 +438,36 @@ static void build_payloads(private_child_create_t *this, message_t *message) } /** + * Adds an IPCOMP_SUPPORTED notify to the message, if possible + */ +static void build_ipcomp_supported_notify(private_child_create_t *this, message_t *message) +{ + if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) + { + DBG1(DBG_IKE, "IPComp is not supported if either peer is natted, IPComp is disabled"); + this->ipcomp = IPCOMP_NONE; + return; + } + + u_int16_t cpi = this->child_sa->get_my_cpi(this->child_sa); + if (cpi) + { + chunk_t cpi_chunk, tid_chunk, data; + u_int8_t tid = this->ipcomp; + cpi_chunk = chunk_from_thing(cpi); + tid_chunk = chunk_from_thing(tid); + data = chunk_cat("cc", cpi_chunk, tid_chunk); + message->add_notify(message, FALSE, IPCOMP_SUPPORTED, data); + chunk_free(&data); + } + else + { + DBG1(DBG_IKE, "unable to allocate a CPI from kernel, IPComp is disabled"); + this->ipcomp = IPCOMP_NONE; + } +} + +/** * Read payloads from message */ static void process_payloads(private_child_create_t *this, message_t *message) @@ -450,7 +496,7 @@ static void process_payloads(private_child_create_t *this, message_t *message) if (!this->initiator) { this->dh_group = ke_payload->get_dh_group_number(ke_payload); - this->dh = diffie_hellman_create(this->dh_group); + this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); } if (this->dh) { @@ -476,6 +522,25 @@ static void process_payloads(private_child_create_t *this, message_t *message) case USE_BEET_MODE: this->mode = MODE_BEET; break; + case IPCOMP_SUPPORTED: + { + chunk_t data = notify_payload->get_notification_data(notify_payload); + u_int16_t cpi = *(u_int16_t*)data.ptr; + ipcomp_transform_t ipcomp = (ipcomp_transform_t)(*(data.ptr + 2)); + switch(ipcomp) + { + case IPCOMP_DEFLATE: + this->other_cpi = cpi; + this->ipcomp_received = ipcomp; + break; + case IPCOMP_LZS: + case IPCOMP_LZJH: + default: + DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a transform" + " ID we don't support %N", ipcomp_transform_names, ipcomp); + break; + } + } default: break; } @@ -540,11 +605,10 @@ static status_t build_i(private_child_create_t *this, message_t *message) if (!this->reqid) { peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = peer_cfg->get_my_virtual_ip(peer_cfg); + vip = peer_cfg->get_virtual_ip(peer_cfg); if (vip) { propose_all = TRUE; - vip->destroy(vip); } } @@ -580,7 +644,13 @@ static status_t build_i(private_child_create_t *this, message_t *message) if (this->dh_group != MODP_NONE) { - this->dh = diffie_hellman_create(this->dh_group); + this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); + } + + if (this->config->use_ipcomp(this->config)) { + /* IPCOMP_DEFLATE is the only transform we support at the moment */ + this->ipcomp = IPCOMP_DEFLATE; + build_ipcomp_supported_notify(this, message); } build_payloads(this, message); @@ -700,6 +770,16 @@ static status_t build_r(private_child_create_t *this, message_t *message) this->ike_sa->get_other_id(this->ike_sa), this->config, this->reqid, this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)); + if (this->config->use_ipcomp(this->config) && this->ipcomp_received != IPCOMP_NONE) + { + this->ipcomp = this->ipcomp_received; + build_ipcomp_supported_notify(this, message); + } + else if (this->ipcomp_received != IPCOMP_NONE) + { + DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify but IPComp is disabled, ignoring"); + } + switch (select_and_install(this, no_dh)) { case SUCCESS: @@ -806,6 +886,25 @@ static status_t process_i(private_child_create_t *this, message_t *message) process_payloads(this, message); + if (this->ipcomp == IPCOMP_NONE && this->ipcomp_received != IPCOMP_NONE) + { + SIG(CHILD_UP_FAILED, "received an IPCOMP_SUPPORTED notify but we did not " + "send one previously, no CHILD_SA built"); + return SUCCESS; + } + else if (this->ipcomp != IPCOMP_NONE && this->ipcomp_received == IPCOMP_NONE) + { + DBG1(DBG_IKE, "peer didn't accept our proposed IPComp transforms, " + "IPComp is disabled"); + this->ipcomp = IPCOMP_NONE; + } + else if (this->ipcomp != IPCOMP_NONE && this->ipcomp != this->ipcomp_received) + { + SIG(CHILD_UP_FAILED, "received an IPCOMP_SUPPORTED notify for a transform " + "we did not propose, no CHILD_SA built"); + return SUCCESS; + } + if (select_and_install(this, no_dh) == SUCCESS) { SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' established successfully", @@ -884,6 +983,9 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) this->dh = NULL; this->child_sa = NULL; this->mode = MODE_TUNNEL; + this->ipcomp = IPCOMP_NONE; + this->ipcomp_received = IPCOMP_NONE; + this->other_cpi = 0; this->reqid = 0; this->established = FALSE; } @@ -957,6 +1059,9 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, child_cfg_t *config) this->dh_group = MODP_NONE; this->child_sa = NULL; this->mode = MODE_TUNNEL; + this->ipcomp = IPCOMP_NONE; + this->ipcomp_received = IPCOMP_NONE; + this->other_cpi = 0; this->reqid = 0; this->established = FALSE; diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h index 9f4815215..cee37121e 100644 --- a/src/charon/sa/tasks/child_create.h +++ b/src/charon/sa/tasks/child_create.h @@ -1,10 +1,3 @@ -/** - * @file child_create.h - * - * @brief Interface child_create_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: child_create.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup child_create child_create + * @{ @ingroup tasks */ #ifndef CHILD_CREATE_H_ @@ -31,15 +31,10 @@ typedef struct child_create_t child_create_t; #include <config/child_cfg.h> /** - * @brief Task of type CHILD_CREATE, established a new CHILD_SA. + * Task of type CHILD_CREATE, established a new CHILD_SA. * * This task may be included in the IKE_AUTH message or in a separate * CREATE_CHILD_SA exchange. - * - * @b Constructors: - * - child_create_create() - * - * @ingroup tasks */ struct child_create_t { @@ -49,35 +44,32 @@ struct child_create_t { task_t task; /** - * @brief Use a specific reqid for the CHILD_SA. + * Use a specific reqid for the CHILD_SA. * * When this task is used for rekeying, the same reqid is used * for the new CHILD_SA. * - * @param this calling object * @param reqid reqid to use */ void (*use_reqid) (child_create_t *this, u_int32_t reqid); /** - * @brief Get the lower of the two nonces, used for rekey collisions. + * Get the lower of the two nonces, used for rekey collisions. * - * @param this calling object * @return lower nonce */ chunk_t (*get_lower_nonce) (child_create_t *this); /** - * @brief Get the CHILD_SA established/establishing by this task. + * Get the CHILD_SA established/establishing by this task. * - * @param this calling object * @return child_sa */ child_sa_t* (*get_child) (child_create_t *this); }; /** - * @brief Create a new child_create task. + * Create a new child_create task. * * @param ike_sa IKE_SA this task works for * @param config child_cfg if task initiator, NULL if responder @@ -85,4 +77,4 @@ struct child_create_t { */ child_create_t *child_create_create(ike_sa_t *ike_sa, child_cfg_t *config); -#endif /* CHILD_CREATE_H_ */ +#endif /* CHILD_CREATE_H_ @} */ diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c index d0b34a276..4156f9704 100644 --- a/src/charon/sa/tasks/child_delete.c +++ b/src/charon/sa/tasks/child_delete.c @@ -1,10 +1,3 @@ -/** - * @file child_delete.c - * - * @brief Implementation of the child_delete task. - * - */ - /* * Copyright (C) 2006-2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: child_delete.c 3802 2008-04-14 08:17:18Z martin $ */ #include "child_delete.h" @@ -157,23 +152,48 @@ static void process_payloads(private_child_delete_t *this, message_t *message) } /** - * destroy the children listed in this->child_sas + * destroy the children listed in this->child_sas, reestablish by policy */ -static void destroy_children(private_child_delete_t *this) +static status_t destroy_and_reestablish(private_child_delete_t *this) { iterator_t *iterator; child_sa_t *child_sa; + child_cfg_t *child_cfg; protocol_id_t protocol; u_int32_t spi; + status_t status = SUCCESS; iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) { spi = child_sa->get_spi(child_sa, TRUE); protocol = child_sa->get_protocol(child_sa); + child_cfg = child_sa->get_config(child_sa); + child_cfg->get_ref(child_cfg); this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); + if (!this->initiator) + { /* enforce child_cfg policy if deleted passively */ + switch (child_cfg->get_close_action(child_cfg)) + { + case ACTION_RESTART: + child_cfg->get_ref(child_cfg); + status = this->ike_sa->initiate(this->ike_sa, child_cfg); + break; + case ACTION_ROUTE: + status = this->ike_sa->route(this->ike_sa, child_cfg); + break; + default: + break; + } + } + child_cfg->destroy(child_cfg); + if (status != SUCCESS) + { + break; + } } iterator->destroy(iterator); + return status; } /** @@ -214,9 +234,8 @@ static status_t process_i(private_child_delete_t *this, message_t *message) this->child_sas = linked_list_create(); process_payloads(this, message); - destroy_children(this); SIG(CHILD_DOWN_SUCCESS, "CHILD_SA closed"); - return SUCCESS; + return destroy_and_reestablish(this); } /** @@ -239,9 +258,8 @@ static status_t build_r(private_child_delete_t *this, message_t *message) { build_payloads(this, message); } - destroy_children(this); SIG(CHILD_DOWN_SUCCESS, "CHILD_SA closed"); - return SUCCESS; + return destroy_and_reestablish(this); } /** diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h index a7e676a50..c304ea9d8 100644 --- a/src/charon/sa/tasks/child_delete.h +++ b/src/charon/sa/tasks/child_delete.h @@ -1,10 +1,3 @@ -/** - * @file child_delete.h - * - * @brief Interface child_delete_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: child_delete.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup child_delete child_delete + * @{ @ingroup tasks */ #ifndef CHILD_DELETE_H_ @@ -31,12 +31,7 @@ typedef struct child_delete_t child_delete_t; #include <sa/child_sa.h> /** - * @brief Task of type child_delete, delete a CHILD_SA. - * - * @b Constructors: - * - child_delete_create() - * - * @ingroup tasks + * Task of type child_delete, delete a CHILD_SA. */ struct child_delete_t { @@ -46,16 +41,15 @@ struct child_delete_t { task_t task; /** - * @brief Get the CHILD_SA to delete by this task. + * Get the CHILD_SA to delete by this task. * - * @param this calling object * @return child_sa */ child_sa_t* (*get_child) (child_delete_t *this); }; /** - * @brief Create a new child_delete task. + * Create a new child_delete task. * * @param ike_sa IKE_SA this task works for * @param child_sa CHILD_SA to delete, or NULL as responder @@ -63,4 +57,4 @@ struct child_delete_t { */ child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa); -#endif /* CHILD_DELETE_H_ */ +#endif /* CHILD_DELETE_H_ @} */ diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c index 3667d8fad..3953951a3 100644 --- a/src/charon/sa/tasks/child_rekey.c +++ b/src/charon/sa/tasks/child_rekey.c @@ -1,10 +1,3 @@ -/** - * @file child_rekey.c - * - * @brief Implementation of the child_rekey task. - * - */ - /* * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -19,6 +12,8 @@ * 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. + * + * $Id: child_rekey.c 3589 2008-03-13 14:14:44Z martin $ */ #include "child_rekey.h" diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h index 3515f0c3f..b386ef3c6 100644 --- a/src/charon/sa/tasks/child_rekey.h +++ b/src/charon/sa/tasks/child_rekey.h @@ -1,10 +1,3 @@ -/** - * @file child_rekey.h - * - * @brief Interface child_rekey_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: child_rekey.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup child_rekey child_rekey + * @{ @ingroup tasks */ #ifndef CHILD_REKEY_H_ @@ -31,12 +31,7 @@ typedef struct child_rekey_t child_rekey_t; #include <sa/tasks/task.h> /** - * @brief Task of type CHILD_REKEY, rekey an established CHILD_SA. - * - * @b Constructors: - * - child_rekey_create() - * - * @ingroup tasks + * Task of type CHILD_REKEY, rekey an established CHILD_SA. */ struct child_rekey_t { @@ -46,20 +41,19 @@ struct child_rekey_t { task_t task; /** - * @brief Register a rekeying task which collides with this one + * Register a rekeying task which collides with this one * * If two peers initiate rekeying at the same time, the collision must * be handled gracefully. The task manager is aware of what exchanges * are going on and notifies the outgoing task by passing the incoming. * - * @param this task initated by us * @param other incoming task */ void (*collide)(child_rekey_t* this, task_t *other); }; /** - * @brief Create a new CHILD_REKEY task. + * Create a new CHILD_REKEY task. * * @param ike_sa IKE_SA this task works for * @param child_sa child_sa to rekey, NULL if responder @@ -67,4 +61,4 @@ struct child_rekey_t { */ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa); -#endif /* CHILD_REKEY_H_ */ +#endif /* CHILD_REKEY_H_ @} */ diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c index de88a0abe..fd5012ee6 100644 --- a/src/charon/sa/tasks/ike_auth.c +++ b/src/charon/sa/tasks/ike_auth.c @@ -1,10 +1,3 @@ -/** - * @file ike_auth.c - * - * @brief Implementation of the ike_auth task. - * - */ - /* * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -18,7 +11,9 @@ * 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. + * for more details + * + * $Id: ike_auth.c 4051 2008-06-10 09:08:27Z tobias $ */ #include "ike_auth.h" @@ -94,6 +89,68 @@ struct private_ike_auth_t { }; /** + * check uniqueness and delete duplicates + */ +static bool check_uniqueness(private_ike_auth_t *this) +{ + ike_sa_t *duplicate; + unique_policy_t policy; + status_t status = SUCCESS; + peer_cfg_t *peer_cfg; + bool cancel = FALSE; + + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + policy = peer_cfg->get_unique_policy(peer_cfg); + if (policy == UNIQUE_NO) + { + return FALSE; + } + duplicate = charon->ike_sa_manager->checkout_duplicate( + charon->ike_sa_manager, this->ike_sa); + if (duplicate) + { + peer_cfg = duplicate->get_peer_cfg(duplicate); + if (peer_cfg && + peer_cfg->equals(peer_cfg, this->ike_sa->get_peer_cfg(this->ike_sa))) + { + switch (duplicate->get_state(duplicate)) + { + case IKE_ESTABLISHED: + case IKE_REKEYING: + switch (policy) + { + case UNIQUE_REPLACE: + DBG1(DBG_IKE, "deleting duplicate IKE_SA due " + "uniqueness policy"); + status = duplicate->delete(duplicate); + break; + case UNIQUE_KEEP: + DBG1(DBG_IKE, "cancelling IKE_SA setup due " + "uniqueness policy"); + cancel = TRUE; + break; + default: + break; + } + break; + default: + break; + } + } + if (status == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, + duplicate); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, duplicate); + } + } + return cancel; +} + +/** * build the AUTH payload */ static status_t build_auth(private_ike_auth_t *this, message_t *message) @@ -101,7 +158,7 @@ static status_t build_auth(private_ike_auth_t *this, message_t *message) authenticator_t *auth; auth_payload_t *auth_payload; peer_cfg_t *config; - auth_method_t method; + config_auth_method_t method; status_t status; /* create own authenticator and add auth payload */ @@ -117,7 +174,7 @@ static status_t build_auth(private_ike_auth_t *this, message_t *message) if (auth == NULL) { SIG(IKE_UP_FAILED, "configured authentication method %N not supported", - auth_method_names, method); + config_auth_method_names, method); return FAILED; } @@ -186,13 +243,13 @@ static status_t process_auth(private_ike_auth_t *this, message_t *message) /* AUTH payload is missing, client wants to use EAP authentication */ return NOT_FOUND; } - + auth_method = auth_payload->get_auth_method(auth_payload); - auth = authenticator_create(this->ike_sa, auth_method); + auth = authenticator_create_from_auth_payload(this->ike_sa, auth_payload); if (auth == NULL) { - SIG(IKE_UP_FAILED, "authentication method %N used by %D not " + SIG(IKE_UP_FAILED, "authentication method %N used by '%D' not " "supported", auth_method_names, auth_method, this->ike_sa->get_other_id(this->ike_sa)); return NOT_SUPPORTED; @@ -231,9 +288,9 @@ static status_t process_id(private_ike_auth_t *this, message_t *message) { id = idr->get_identification(idr); req = this->ike_sa->get_other_id(this->ike_sa); - if (!id->matches(id, req, NULL)) + if (!id->matches(id, req)) { - SIG(IKE_UP_FAILED, "peer ID %D unacceptable, %D required", id, req); + SIG(IKE_UP_FAILED, "peer ID '%D' unacceptable, '%D' required", id, req); id->destroy(id); return FAILED; } @@ -321,12 +378,12 @@ static status_t build_auth_eap(private_ike_auth_t *this, message_t *message) if (!this->initiator) { this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %D[%H]...[%H]%D", + SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...[%D]%H", this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); return SUCCESS; } return NEED_MORE; @@ -367,12 +424,12 @@ static status_t process_auth_eap(private_ike_auth_t *this, message_t *message) if (this->initiator) { this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %D[%H]...[%H]%D", + SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...[%D]%H", this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); return SUCCESS; } return NEED_MORE; @@ -404,7 +461,7 @@ static status_t process_eap_i(private_ike_auth_t *this, message_t *message) return NEED_MORE; default: this->eap_payload = NULL; - SIG(IKE_UP_FAILED, "failed to authenticate against %D using EAP", + SIG(IKE_UP_FAILED, "failed to authenticate against '%D' using EAP", this->ike_sa->get_other_id(this->ike_sa)); return FAILED; } @@ -482,7 +539,7 @@ static status_t build_i(private_ike_auth_t *this, message_t *message) } config = this->ike_sa->get_peer_cfg(this->ike_sa); - if (config->get_auth_method(config) == AUTH_EAP) + if (config->get_auth_method(config) == CONF_AUTH_EAP) { this->eap_auth = eap_authenticator_create(this->ike_sa); } @@ -525,13 +582,13 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) this->eap_auth = eap_authenticator_create(this->ike_sa); break; default: - break; + return NEED_MORE; } config = charon->backends->get_peer_cfg(charon->backends, this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa), - this->ike_sa->get_other_ca(this->ike_sa)); + this->ike_sa->get_other_auth(this->ike_sa)); if (config) { this->ike_sa->set_peer_cfg(this->ike_sa, config); @@ -557,10 +614,17 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) return collect_my_init_data(this, message); } + if (!this->peer_authenticated && this->eap_auth == NULL) + { + /* peer not authenticated, nor does it want to use EAP */ + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + config = this->ike_sa->get_peer_cfg(this->ike_sa); if (config == NULL) { - SIG(IKE_UP_FAILED, "no matching config found for %D...%D", + SIG(IKE_UP_FAILED, "no matching config found for '%D'...'%D'", this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); @@ -574,26 +638,25 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) return FAILED; } + if (check_uniqueness(this)) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + /* use "traditional" authentication if we could authenticate peer */ if (this->peer_authenticated) { this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %D[%H]...[%H]%D", + SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...[%D]%H", this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); return SUCCESS; } - if (this->eap_auth == NULL) - { - /* peer not authenticated, nor does it want to use EAP */ - message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); - return FAILED; - } - /* initiate EAP authenitcation */ eap_type = config->get_eap_type(config, &eap_vendor); status = this->eap_auth->initiate(this->eap_auth, eap_type, @@ -618,6 +681,8 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) { iterator_t *iterator; payload_t *payload; + peer_cfg_t *config; + auth_info_t *auth; if (message->get_exchange_type(message) == IKE_SA_INIT) { @@ -652,8 +717,8 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) case AUTH_LIFETIME: /* handled in ike_auth_lifetime task */ break; - case P2P_ENDPOINT: - /* handled in ike_p2p task */ + case ME_ENDPOINT: + /* handled in ike_me task */ break; default: { @@ -664,7 +729,7 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) iterator->destroy(iterator); return FAILED; } - DBG1(DBG_IKE, "received %N notify", + DBG2(DBG_IKE, "received %N notify", notify_type_names, type); break; } @@ -687,13 +752,21 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) return process_eap_i(this, message); } + config = this->ike_sa->get_peer_cfg(this->ike_sa); + auth = this->ike_sa->get_other_auth(this->ike_sa); + if (!auth->complies(auth, config->get_auth(config))) + { + SIG(IKE_UP_FAILED, "authorization of '%D' for config %s failed", + this->ike_sa->get_other_id(this->ike_sa), config->get_name(config)); + return FAILED; + } this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %D[%H]...[%H]%D", + SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...[%D]%H", this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa)); return SUCCESS; } diff --git a/src/charon/sa/tasks/ike_auth.h b/src/charon/sa/tasks/ike_auth.h index d7326c988..15f98f312 100644 --- a/src/charon/sa/tasks/ike_auth.h +++ b/src/charon/sa/tasks/ike_auth.h @@ -1,10 +1,3 @@ -/** - * @file ike_auth.h - * - * @brief Interface ike_auth_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_auth.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_auth ike_auth + * @{ @ingroup tasks */ #ifndef IKE_AUTH_H_ @@ -30,7 +30,7 @@ typedef struct ike_auth_t ike_auth_t; #include <sa/tasks/task.h> /** - * @brief Task of type ike_auth, authenticates an IKE_SA using authenticators. + * Task of type ike_auth, authenticates an IKE_SA using authenticators. * * The ike_auth task authenticates the IKE_SA using the IKE_AUTH * exchange. It processes and build IDi and IDr payloads and also @@ -38,11 +38,6 @@ typedef struct ike_auth_t ike_auth_t; * which do the actual authentication process. If the ike_auth task is used * with EAP authentication, it stays alive over multiple exchanges until * EAP has completed. - * - * @b Constructors: - * - ike_auth_create() - * - * @ingroup tasks */ struct ike_auth_t { @@ -53,7 +48,7 @@ struct ike_auth_t { }; /** - * @brief Create a new task of type IKE_AUTHENTICATE. + * Create a new task of type IKE_AUTHENTICATE. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if thask is the initator of an exchange @@ -61,4 +56,4 @@ struct ike_auth_t { */ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_AUTH_H_ */ +#endif /* IKE_AUTH_H_ @} */ diff --git a/src/charon/sa/tasks/ike_auth_lifetime.c b/src/charon/sa/tasks/ike_auth_lifetime.c index 9d37ec608..2d18c6a1e 100644 --- a/src/charon/sa/tasks/ike_auth_lifetime.c +++ b/src/charon/sa/tasks/ike_auth_lifetime.c @@ -1,10 +1,3 @@ -/** - * @file ike_auth_lifetime.c - * - * @brief Implementation of the ike_auth_lifetime task. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: ike_auth_lifetime.c 3589 2008-03-13 14:14:44Z martin $ */ #include "ike_auth_lifetime.h" diff --git a/src/charon/sa/tasks/ike_auth_lifetime.h b/src/charon/sa/tasks/ike_auth_lifetime.h index 500b89d39..21a3bbfdc 100644 --- a/src/charon/sa/tasks/ike_auth_lifetime.h +++ b/src/charon/sa/tasks/ike_auth_lifetime.h @@ -1,10 +1,3 @@ -/** - * @file ike_auth_lifetime.h - * - * @brief Interface ike_auth_lifetime_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_auth_lifetime.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_auth_lifetime ike_auth_lifetime + * @{ @ingroup tasks */ #ifndef IKE_AUTH_LIFETIME_H_ @@ -30,15 +30,10 @@ typedef struct ike_auth_lifetime_t ike_auth_lifetime_t; #include <sa/tasks/task.h> /** - * @brief Task of type IKE_AUTH_LIFETIME, implements RFC4478. + * Task of type IKE_AUTH_LIFETIME, implements RFC4478. * * This task exchanges lifetimes for IKE_AUTH to force a client to * reauthenticate before the responders lifetime reaches the limit. - * - * @b Constructors: - * - ike_auth_lifetime_create() - * - * @ingroup tasks */ struct ike_auth_lifetime_t { @@ -49,7 +44,7 @@ struct ike_auth_lifetime_t { }; /** - * @brief Create a new IKE_AUTH_LIFETIME task. + * Create a new IKE_AUTH_LIFETIME task. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if taks is initiated by us @@ -57,5 +52,4 @@ struct ike_auth_lifetime_t { */ ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_MOBIKE_H_ */ - +#endif /* IKE_MOBIKE_H_ @} */ diff --git a/src/charon/sa/tasks/ike_cert.c b/src/charon/sa/tasks/ike_cert.c deleted file mode 100644 index 880ed9c42..000000000 --- a/src/charon/sa/tasks/ike_cert.c +++ /dev/null @@ -1,366 +0,0 @@ -/** - * @file ike_cert.c - * - * @brief Implementation of the ike_cert task. - * - */ - -/* - * Copyright (C) 2006-2007 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 <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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 "ike_cert.h" - -#include <daemon.h> -#include <sa/ike_sa.h> -#include <crypto/hashers/hasher.h> -#include <encoding/payloads/cert_payload.h> -#include <encoding/payloads/certreq_payload.h> - - -typedef struct private_ike_cert_t private_ike_cert_t; - -/** - * Private members of a ike_cert_t task. - */ -struct private_ike_cert_t { - - /** - * Public methods and task_t interface. - */ - ike_cert_t public; - - /** - * Assigned IKE_SA. - */ - ike_sa_t *ike_sa; - - /** - * Are we the initiator? - */ - bool initiator; - - /** - * list of CA cert hashes requested, items point to 20 byte chunk - */ - linked_list_t *cas; - - /** - * have we seen a certificate request? - */ - bool certreq_seen; -}; - -/** - * read certificate requests - */ -static void process_certreqs(private_ike_cert_t *this, message_t *message) -{ - iterator_t *iterator; - payload_t *payload; - - iterator = message->get_payload_iterator(message); - while (iterator->iterate(iterator, (void**)&payload)) - { - if (payload->get_type(payload) == CERTIFICATE_REQUEST) - { - certreq_payload_t *certreq = (certreq_payload_t*)payload; - cert_encoding_t encoding; - chunk_t keyids, keyid; - - this->certreq_seen = TRUE; - - encoding = certreq->get_cert_encoding(certreq); - if (encoding != CERT_X509_SIGNATURE) - { - DBG1(DBG_IKE, "certreq payload %N not supported - ignored", - cert_encoding_names, encoding); - continue; - } - - keyids = certreq->get_data(certreq); - - while (keyids.len >= HASH_SIZE_SHA1) - { - keyid = chunk_create(keyids.ptr, HASH_SIZE_SHA1); - keyid = chunk_clone(keyid); - this->cas->insert_last(this->cas, keyid.ptr); - keyids = chunk_skip(keyids, HASH_SIZE_SHA1); - } - } - } - iterator->destroy(iterator); -} - -/** - * import certificates - */ -static void process_certs(private_ike_cert_t *this, message_t *message) -{ - iterator_t *iterator; - payload_t *payload; - - iterator = message->get_payload_iterator(message); - while (iterator->iterate(iterator, (void**)&payload)) - { - if (payload->get_type(payload) == CERTIFICATE) - { - cert_encoding_t encoding; - x509_t *cert; - chunk_t cert_data; - bool found; - cert_payload_t *cert_payload = (cert_payload_t*)payload; - - encoding = cert_payload->get_cert_encoding(cert_payload); - if (encoding != CERT_X509_SIGNATURE) - { - DBG1(DBG_IKE, "certificate payload %N not supported - ignored", - cert_encoding_names, encoding); - continue; - } - - cert_data = cert_payload->get_data_clone(cert_payload); - cert = x509_create_from_chunk(cert_data, 0); - if (cert) - { - if (charon->credentials->verify(charon->credentials, cert, &found)) - { - DBG2(DBG_IKE, "received end entity certificate is trusted - " - "added to store"); - if (found) - { - cert->destroy(cert); - } - else - { - charon->credentials->add_end_certificate(charon->credentials, cert); - } - } - else - { - DBG1(DBG_IKE, "received end entity certificate is not trusted - " - "discarded"); - cert->destroy(cert); - } - } - else - { - DBG1(DBG_IKE, "parsing of received certificate failed - discarded"); - chunk_free(&cert_data); - } - } - } - iterator->destroy(iterator); -} - -/** - * build certificate requests - */ -static void build_certreqs(private_ike_cert_t *this, message_t *message) -{ - ike_cfg_t *ike_cfg; - peer_cfg_t *peer_cfg; - identification_t *ca; - certreq_payload_t *certreq; - - ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); - - if (ike_cfg->send_certreq(ike_cfg) != CERT_NEVER_SEND) - { - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - - if (peer_cfg) - { - ca = peer_cfg->get_other_ca(peer_cfg); - - if (ca && ca->get_type(ca) != ID_ANY) - { - certreq = certreq_payload_create_from_cacert(ca); - } - else - { - certreq = certreq_payload_create_from_cacerts(); - } - } - else - { - certreq = certreq_payload_create_from_cacerts(); - } - - if (certreq) - { - message->add_payload(message, (payload_t*)certreq); - } - } -} - -/** - * add certificates to message - */ -static void build_certs(private_ike_cert_t *this, message_t *message) -{ - peer_cfg_t *peer_cfg; - x509_t *cert; - cert_payload_t *payload; - - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - - if (peer_cfg && peer_cfg->get_auth_method(peer_cfg) == AUTH_RSA) - { - switch (peer_cfg->get_cert_policy(peer_cfg)) - { - case CERT_NEVER_SEND: - break; - case CERT_SEND_IF_ASKED: - if (!this->certreq_seen) - { - break; - } - /* FALL */ - case CERT_ALWAYS_SEND: - { - /* TODO: respect CA cert request */ - cert = charon->credentials->get_certificate(charon->credentials, - peer_cfg->get_my_id(peer_cfg)); - if (cert) - { - payload = cert_payload_create_from_x509(cert); - message->add_payload(message, (payload_t*)payload); - } - } - } - } -} - -/** - * Implementation of task_t.process for initiator - */ -static status_t build_i(private_ike_cert_t *this, message_t *message) -{ - if (message->get_exchange_type(message) == IKE_SA_INIT) - { - return NEED_MORE; - } - - build_certreqs(this, message); - build_certs(this, message); - - return NEED_MORE; -} - -/** - * Implementation of task_t.process for responder - */ -static status_t process_r(private_ike_cert_t *this, message_t *message) -{ - if (message->get_exchange_type(message) == IKE_SA_INIT) - { - return NEED_MORE; - } - - process_certreqs(this, message); - process_certs(this, message); - - return NEED_MORE; -} - -/** - * Implementation of task_t.build for responder - */ -static status_t build_r(private_ike_cert_t *this, message_t *message) -{ - if (message->get_exchange_type(message) == IKE_SA_INIT) - { - build_certreqs(this, message); - return NEED_MORE; - } - - build_certs(this, message); - - return SUCCESS; -} - -/** - * Implementation of task_t.process for initiator - */ -static status_t process_i(private_ike_cert_t *this, message_t *message) -{ - if (message->get_exchange_type(message) == IKE_SA_INIT) - { - process_certreqs(this, message); - return NEED_MORE; - } - - process_certs(this, message); - return SUCCESS; -} - -/** - * Implementation of task_t.get_type - */ -static task_type_t get_type(private_ike_cert_t *this) -{ - return IKE_CERT; -} - -/** - * Implementation of task_t.migrate - */ -static void migrate(private_ike_cert_t *this, ike_sa_t *ike_sa) -{ - this->ike_sa = ike_sa; - - this->cas->destroy_function(this->cas, free); - this->cas = linked_list_create(); - this->certreq_seen = FALSE; -} - -/** - * Implementation of task_t.destroy - */ -static void destroy(private_ike_cert_t *this) -{ - this->cas->destroy_function(this->cas, free); - free(this); -} - -/* - * Described in header. - */ -ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator) -{ - private_ike_cert_t *this = malloc_thing(private_ike_cert_t); - - this->public.task.get_type = (task_type_t(*)(task_t*))get_type; - this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; - this->public.task.destroy = (void(*)(task_t*))destroy; - - if (initiator) - { - this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; - this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; - } - else - { - this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; - this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; - } - - this->ike_sa = ike_sa; - this->initiator = initiator; - this->cas = linked_list_create(); - this->certreq_seen = FALSE; - - return &this->public; -} diff --git a/src/charon/sa/tasks/ike_cert_post.c b/src/charon/sa/tasks/ike_cert_post.c new file mode 100644 index 000000000..184868b28 --- /dev/null +++ b/src/charon/sa/tasks/ike_cert_post.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2006-2008 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * $Id: ike_cert_post.c 4051 2008-06-10 09:08:27Z tobias $ + */ + +#include "ike_cert_post.h" + +#include <daemon.h> +#include <sa/ike_sa.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> +#include <credentials/certificates/x509.h> + + +typedef struct private_ike_cert_post_t private_ike_cert_post_t; + +/** + * Private members of a ike_cert_post_t task. + */ +struct private_ike_cert_post_t { + + /** + * Public methods and task_t interface. + */ + ike_cert_post_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; +}; + +/** + * Generates the cert payload, if possible with "Hash and URL" + */ +static cert_payload_t *build_cert_payload(private_ike_cert_post_t *this, certificate_t *cert) +{ + cert_payload_t *payload = NULL; + + if (this->ike_sa->supports_extension(this->ike_sa, EXT_HASH_AND_URL)) + { + /* ok, our peer sent us a HTTP_CERT_LOOKUP_SUPPORTED Notify */ + hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (hasher != NULL) + { + chunk_t hash, encoded = cert->get_encoding(cert); + enumerator_t *enumerator; + char *url; + + hasher->allocate_hash(hasher, encoded, &hash); + identification_t *id = identification_create_from_encoding(ID_CERT_DER_SHA1, hash); + + enumerator = charon->credentials->create_cdp_enumerator(charon->credentials, CERT_X509, id); + if (enumerator->enumerate(enumerator, &url)) + { + /* if we have an URL available we send that to our peer */ + payload = cert_payload_create_from_hash_and_url(hash, url); + } + enumerator->destroy(enumerator); + + id->destroy(id); + chunk_free(&hash); + chunk_free(&encoded); + hasher->destroy(hasher); + } + else + { + DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported"); + } + } + + if (!payload) + { + /* our peer does not support "Hash and URL" or we do not have an URL + * to send to our peer, just create a normal cert payload */ + payload = cert_payload_create_from_cert(cert); + } + + return payload; +} + +/** + * add certificates to message + */ +static void build_certs(private_ike_cert_post_t *this, message_t *message) +{ + peer_cfg_t *peer_cfg; + + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + if (peer_cfg && peer_cfg->get_auth_method(peer_cfg) == CONF_AUTH_PUBKEY) + { + switch (peer_cfg->get_cert_policy(peer_cfg)) + { + case CERT_NEVER_SEND: + break; + case CERT_SEND_IF_ASKED: + if (!this->ike_sa->has_condition(this->ike_sa, COND_CERTREQ_SEEN)) + { + break; + } + /* FALL */ + case CERT_ALWAYS_SEND: + { + cert_payload_t *payload; + enumerator_t *enumerator; + certificate_t *cert; + auth_info_t *auth; + auth_item_t item; + + auth = this->ike_sa->get_my_auth(this->ike_sa); + /* get subject cert first, then issuing certificates */ + if (!auth->get_item(auth, AUTHZ_SUBJECT_CERT, (void**)&cert)) + { + break; + } + payload = build_cert_payload(this, cert); + if (!payload) + { + break; + } + DBG1(DBG_IKE, "sending end entity cert \"%D\"", + cert->get_subject(cert)); + message->add_payload(message, (payload_t*)payload); + + enumerator = auth->create_item_enumerator(auth); + while (enumerator->enumerate(enumerator, &item, &cert)) + { + if (item == AUTHZ_IM_CERT) + { + payload = cert_payload_create_from_cert(cert); + if (payload) + { + DBG1(DBG_IKE, "sending issuer cert \"%D\"", + cert->get_subject(cert)); + message->add_payload(message, (payload_t*)payload); + } + } + } + enumerator->destroy(enumerator); + } + } + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_cert_post_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + build_certs(this, message); + return SUCCESS; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_cert_post_t *this, message_t *message) +{ + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_cert_post_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + build_certs(this, message); + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_cert_post_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_cert_post_t *this) +{ + return IKE_CERT_POST; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_cert_post_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_cert_post_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_cert_post_t *ike_cert_post_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_cert_post_t *this = malloc_thing(private_ike_cert_post_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + + return &this->public; +} + diff --git a/src/charon/sa/tasks/ike_cert_post.h b/src/charon/sa/tasks/ike_cert_post.h new file mode 100644 index 000000000..78b32d67a --- /dev/null +++ b/src/charon/sa/tasks/ike_cert_post.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007-2008 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * $Id: ike_cert_post.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_cert_post ike_cert_post + * @{ @ingroup tasks + */ + +#ifndef IKE_CERT_POST_H_ +#define IKE_CERT_POST_H_ + +typedef struct ike_cert_post_t ike_cert_post_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * Task of type ike_cert_post, certificate processing after authentication. + */ +struct ike_cert_post_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new ike_cert_post task. + * + * The initiator parameter means the original initiator, not the initiator + * of the certificate request. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if thask is the original initator + * @return ike_cert_post task to handle by the task_manager + */ +ike_cert_post_t *ike_cert_post_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_CERT_POST_H_ @} */ diff --git a/src/charon/sa/tasks/ike_cert_pre.c b/src/charon/sa/tasks/ike_cert_pre.c new file mode 100644 index 000000000..3568a214e --- /dev/null +++ b/src/charon/sa/tasks/ike_cert_pre.c @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2006-2007 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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. + * + * $Id: ike_cert_pre.c 3852 2008-04-18 21:27:08Z andreas $ + */ + +#include "ike_cert_pre.h" + +#include <daemon.h> +#include <sa/ike_sa.h> +#include <encoding/payloads/cert_payload.h> +#include <encoding/payloads/certreq_payload.h> +#include <credentials/certificates/x509.h> + + +typedef struct private_ike_cert_pre_t private_ike_cert_pre_t; + +/** + * Private members of a ike_cert_pre_t task. + */ +struct private_ike_cert_pre_t { + + /** + * Public methods and task_t interface. + */ + ike_cert_pre_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Did we send a HTTP_CERT_LOOKUP_SUPPORTED Notify? + */ + bool http_cert_lookup_supported_sent; +}; + +/** + * read certificate requests + */ +static void process_certreqs(private_ike_cert_pre_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + auth_info_t *auth; + bool ca_found = FALSE; + + auth = this->ike_sa->get_my_auth(this->ike_sa); + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + switch(payload->get_type(payload)) + { + case CERTIFICATE_REQUEST: + { + certreq_payload_t *certreq = (certreq_payload_t*)payload; + chunk_t keyid; + enumerator_t *enumerator; + + this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE); + + if (certreq->get_cert_type(certreq) != CERT_X509) + { + DBG1(DBG_IKE, "cert payload %N not supported - ignored", + certificate_type_names, certreq->get_cert_type(certreq)); + break; + } + enumerator = certreq->create_keyid_enumerator(certreq); + while (enumerator->enumerate(enumerator, &keyid)) + { + identification_t *id; + certificate_t *cert; + + id = identification_create_from_encoding( + ID_PUBKEY_INFO_SHA1, keyid); + cert = charon->credentials->get_cert(charon->credentials, + CERT_X509, KEY_ANY, id, TRUE); + if (cert) + { + DBG1(DBG_IKE, "received cert request for \"%D\"", + cert->get_subject(cert)); + auth->add_item(auth, AUTHN_CA_CERT, cert); + cert->destroy(cert); + ca_found = TRUE; + } + else + { + DBG1(DBG_IKE, "received cert request for unknown ca " + "with keyid %D", id); + auth->add_item(auth, AUTHN_CA_CERT_KEYID, id); + } + id->destroy(id); + } + enumerator->destroy(enumerator); + break; + } + case NOTIFY: + { + notify_payload_t *notify = (notify_payload_t*)payload; + + /* we only handle one type of notify here */ + if (notify->get_notify_type(notify) == HTTP_CERT_LOOKUP_SUPPORTED) + { + this->ike_sa->enable_extension(this->ike_sa, EXT_HASH_AND_URL); + } + break; + } + default: + /* ignore other payloads here, these are handled elsewhere */ + break; + } + } + iterator->destroy(iterator); +} + +/** + * tries to extract a certificate from the cert payload or the credential + * manager (based on the hash of a "Hash and URL" encoded cert). + * Note: the returned certificate (if any) has to be destroyed + */ +static certificate_t *try_get_cert(cert_payload_t *cert_payload) +{ + certificate_t *cert = NULL; + switch (cert_payload->get_cert_encoding(cert_payload)) + { + case ENC_X509_SIGNATURE: + { + cert = cert_payload->get_cert(cert_payload); + break; + } + case ENC_X509_HASH_AND_URL: + { + identification_t *id; + chunk_t hash = cert_payload->get_hash(cert_payload); + if (!hash.ptr) + { + /* invalid "Hash and URL" data (logged elsewhere) */ + break; + } + id = identification_create_from_encoding(ID_CERT_DER_SHA1, hash); + cert = charon->credentials->get_cert(charon->credentials, + CERT_X509, KEY_ANY, id, FALSE); + id->destroy(id); + break; + } + default: + { + break; + } + } + return cert; +} + +/** + * import certificates + */ +static void process_certs(private_ike_cert_pre_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + auth_info_t *auth; + bool first = TRUE; + + auth = this->ike_sa->get_other_auth(this->ike_sa); + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == CERTIFICATE) + { + cert_payload_t *cert_payload = (cert_payload_t*)payload; + cert_encoding_t type = cert_payload->get_cert_encoding(cert_payload); + switch (type) + { + case ENC_X509_SIGNATURE: + case ENC_X509_HASH_AND_URL: + { + if (type == ENC_X509_HASH_AND_URL && + !this->http_cert_lookup_supported_sent) + { + DBG1(DBG_IKE, "received hash-and-url encoded cert, but" + " we don't accept them, ignore"); + break; + } + + certificate_t *cert = try_get_cert(cert_payload); + + if (cert) + { + /* we've got a certificate from the payload or the cache */ + if (first) + { /* the first certificate MUST be an end entity one */ + DBG1(DBG_IKE, "received end entity cert \"%D\"", + cert->get_subject(cert)); + auth->add_item(auth, AUTHN_SUBJECT_CERT, cert); + first = FALSE; + } + else + { + DBG1(DBG_IKE, "received issuer cert \"%D\"", + cert->get_subject(cert)); + auth->add_item(auth, AUTHN_IM_CERT, cert); + } + cert->destroy(cert); + } + else if (type == ENC_X509_HASH_AND_URL) + { + /* we received a "Hash and URL" encoded certificate that + * we haven't fetched yet, we store the URL and fetch + * it later */ + char *url = cert_payload->get_url(cert_payload); + if (!url) + { + DBG1(DBG_IKE, "received invalid hash-and-url encoded" + " cert, ignore"); + break; + } + + if (first) + { /* the first certificate MUST be an end entity one */ + DBG1(DBG_IKE, "received hash-and-url for end" + " entity cert \"%s\"", url); + auth->add_item(auth, AUTHN_SUBJECT_HASH_URL, url); + first = FALSE; + } + else + { + DBG1(DBG_IKE, "received hash-and-url for issuer" + " cert \"%s\"", url); + auth->add_item(auth, AUTHN_IM_HASH_URL, url); + } + } + break; + } + case ENC_PKCS7_WRAPPED_X509: + case ENC_PGP: + case ENC_DNS_SIGNED_KEY: + case ENC_KERBEROS_TOKEN: + case ENC_CRL: + case ENC_ARL: + case ENC_SPKI: + case ENC_X509_ATTRIBUTE: + case ENC_RAW_RSA_KEY: + case ENC_X509_HASH_AND_URL_BUNDLE: + case ENC_OCSP_CONTENT: + default: + DBG1(DBG_ENC, "certificate encoding %N not supported", + cert_encoding_names, cert_payload->get_cert_encoding(cert_payload)); + } + } + } + iterator->destroy(iterator); +} + +/** + * add a certificate request to the message, building request payload if required. + */ +static void add_certreq_payload(message_t *message, certreq_payload_t **reqp, + certificate_t *cert) +{ + public_key_t *public; + certreq_payload_t *req; + + public = cert->get_public_key(cert); + if (!public) + { + return; + } + switch (cert->get_type(cert)) + { + case CERT_X509: + { + identification_t *keyid; + x509_t *x509 = (x509_t*)cert; + + if (!(x509->get_flags(x509) & X509_CA)) + { /* no CA cert, skip */ + break; + } + if (*reqp == NULL) + { + *reqp = certreq_payload_create_type(CERT_X509); + message->add_payload(message, (payload_t*)*reqp); + } + req = *reqp; + keyid = public->get_id(public, ID_PUBKEY_INFO_SHA1); + req->add_keyid(req, keyid->get_encoding(keyid)); + DBG1(DBG_IKE, "sending cert request for \"%D\"", + cert->get_subject(cert)); + break; + } + default: + break; + } + public->destroy(public); +} + +/** + * build certificate requests + */ +static void build_certreqs(private_ike_cert_pre_t *this, message_t *message) +{ + ike_cfg_t *ike_cfg; + enumerator_t *enumerator; + certificate_t *cert; + auth_info_t *auth; + bool restricted = FALSE; + auth_item_t item; + certreq_payload_t *x509_req = NULL; + + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + if (!ike_cfg->send_certreq(ike_cfg)) + { + return; + } + auth = this->ike_sa->get_other_auth(this->ike_sa); + + /* check if we require a specific CA for that peer */ + enumerator = auth->create_item_enumerator(auth); + while (enumerator->enumerate(enumerator, &item, &cert)) + { + if (item == AUTHN_CA_CERT) + { + restricted = TRUE; + add_certreq_payload(message, &x509_req, cert); + } + } + enumerator->destroy(enumerator); + + if (!restricted) + { + /* otherwise include all trusted CA certificates */ + enumerator = charon->credentials->create_cert_enumerator( + charon->credentials, CERT_ANY, KEY_ANY, NULL, TRUE); + while (enumerator->enumerate(enumerator, &cert, TRUE)) + { + add_certreq_payload(message, &x509_req, cert); + } + enumerator->destroy(enumerator); + } + + /* if we've added at least one certreq, we notify our peer that we support + * "Hash and URL" for the requested certificates */ + if (lib->settings->get_bool(lib->settings, "charon.hash_and_url", FALSE) && + message->get_payload(message, CERTIFICATE_REQUEST)) + { + message->add_notify(message, FALSE, HTTP_CERT_LOOKUP_SUPPORTED, chunk_empty); + this->http_cert_lookup_supported_sent = TRUE; + } +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_cert_pre_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + build_certreqs(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_cert_pre_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + return NEED_MORE; + } + process_certreqs(this, message); + process_certs(this, message); + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_cert_pre_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + build_certreqs(this, message); + return NEED_MORE; + } + return SUCCESS; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_cert_pre_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_SA_INIT) + { + process_certreqs(this, message); + return NEED_MORE; + } + process_certs(this, message); + return SUCCESS; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_cert_pre_t *this) +{ + return IKE_CERT_PRE; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_cert_pre_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_cert_pre_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_cert_pre_t *ike_cert_pre_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_cert_pre_t *this = malloc_thing(private_ike_cert_pre_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + this->initiator = initiator; + this->http_cert_lookup_supported_sent = FALSE; + + return &this->public; +} diff --git a/src/charon/sa/tasks/ike_cert.h b/src/charon/sa/tasks/ike_cert_pre.h index ba0283953..d8793a8e2 100644 --- a/src/charon/sa/tasks/ike_cert.h +++ b/src/charon/sa/tasks/ike_cert_pre.h @@ -1,12 +1,5 @@ -/** - * @file ike_cert.h - * - * @brief Interface ike_cert_t. - * - */ - /* - * Copyright (C) 2007 Martin Willi + * Copyright (C) 2007-2008 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,27 +11,28 @@ * 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. + * + * $Id: ike_cert_pre.h 3589 2008-03-13 14:14:44Z martin $ */ -#ifndef IKE_CERT_H_ -#define IKE_CERT_H_ +/** + * @defgroup ike_cert_pre ike_cert_pre + * @{ @ingroup tasks + */ -typedef struct ike_cert_t ike_cert_t; +#ifndef IKE_CERT_PRE_H_ +#define IKE_CERT_PRE_H_ + +typedef struct ike_cert_pre_t ike_cert_pre_t; #include <library.h> #include <sa/ike_sa.h> #include <sa/tasks/task.h> /** - * @brief Task of type ike_cert, exchanges certificates and - * certificate requests. - * - * @b Constructors: - * - ike_cert_create() - * - * @ingroup tasks + * Task of type ike_cert_post, certificate processing before authentication. */ -struct ike_cert_t { +struct ike_cert_pre_t { /** * Implements the task_t interface @@ -47,15 +41,15 @@ struct ike_cert_t { }; /** - * @brief Create a new ike_cert task. + * Create a new ike_cert_pre task. * * The initiator parameter means the original initiator, not the initiator * of the certificate request. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if thask is the original initator - * @return ike_cert task to handle by the task_manager + * @return ike_cert_pre task to handle by the task_manager */ -ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator); +ike_cert_pre_t *ike_cert_pre_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_CERT_H_ */ +#endif /* IKE_CERT_PRE_H_ @} */ diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c index 3c73395a5..c31e62750 100644 --- a/src/charon/sa/tasks/ike_config.c +++ b/src/charon/sa/tasks/ike_config.c @@ -1,10 +1,3 @@ -/** - * @file ike_config.c - * - * @brief Implementation of the ike_config task. - * - */ - /* * Copyright (C) 2007 Martin Willi * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser @@ -19,6 +12,8 @@ * 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. + * + * $Id: ike_config.c 3800 2008-04-14 07:18:16Z martin $ */ #include "ike_config.h" @@ -266,14 +261,14 @@ static status_t build_i(private_ike_config_t *this, message_t *message) /* reuse virtual IP if we already have one */ vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (vip) + if (!vip) { - this->virtual_ip = vip->clone(vip); + config = this->ike_sa->get_peer_cfg(this->ike_sa); + vip = config->get_virtual_ip(config); } - else + if (vip) { - config = this->ike_sa->get_peer_cfg(this->ike_sa); - this->virtual_ip = config->get_my_virtual_ip(config); + this->virtual_ip = vip->clone(vip); } build_payloads(this, message, CFG_REQUEST); @@ -307,14 +302,20 @@ static status_t build_r(private_ike_config_t *this, message_t *message) if (config && this->virtual_ip) { - host_t *ip; + host_t *ip = NULL; DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); - ip = config->get_other_virtual_ip(config, this->virtual_ip); - if (ip == NULL || ip->is_anyaddr(ip)) + if (config->get_pool(config)) + { + ip = charon->attributes->acquire_address(charon->attributes, + config->get_pool(config), + this->ike_sa->get_other_id(this->ike_sa), + this->ike_sa->get_other_auth(this->ike_sa), + this->virtual_ip); + } + if (ip == NULL) { DBG1(DBG_IKE, "not assigning a virtual IP to peer"); - DESTROY_IF(ip); return SUCCESS; } DBG1(DBG_IKE, "assigning virtual IP %H to peer", ip); @@ -323,16 +324,6 @@ static status_t build_r(private_ike_config_t *this, message_t *message) this->virtual_ip->destroy(this->virtual_ip); this->virtual_ip = ip; - /* DNS testing values - if (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS) - { - ip->destroy(ip); - ip = host_create_from_string("10.3.0.1", 0); - this->dns->insert_last(this->dns, ip); - ip = host_create_from_string("10.3.0.2", 0); - this->dns->insert_last(this->dns, ip); - } */ - build_payloads(this, message, CFG_REPLY); } return SUCCESS; @@ -359,7 +350,11 @@ static status_t process_i(private_ike_config_t *this, message_t *message) if (this->virtual_ip == NULL) { /* force a configured virtual IP, even server didn't return one */ config = this->ike_sa->get_peer_cfg(this->ike_sa); - this->virtual_ip = config->get_my_virtual_ip(config); + this->virtual_ip = config->get_virtual_ip(config); + if (this->virtual_ip) + { + this->virtual_ip = this->virtual_ip->clone(this->virtual_ip); + } } if (this->virtual_ip && !this->virtual_ip->is_anyaddr(this->virtual_ip)) diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h index a7cfddff0..23410a196 100644 --- a/src/charon/sa/tasks/ike_config.h +++ b/src/charon/sa/tasks/ike_config.h @@ -1,10 +1,3 @@ -/** - * @file ike_config.h - * - * @brief Interface ike_config_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_config.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_config ike_config + * @{ @ingroup tasks */ #ifndef IKE_CONFIG_H_ @@ -30,13 +30,8 @@ typedef struct ike_config_t ike_config_t; #include <sa/tasks/task.h> /** - * @brief Task of type IKE_CONFIG, sets up a virtual IP and other + * Task of type IKE_CONFIG, sets up a virtual IP and other * configurations for an IKE_SA. - * - * @b Constructors: - * - ike_config_create() - * - * @ingroup tasks */ struct ike_config_t { @@ -47,7 +42,7 @@ struct ike_config_t { }; /** - * @brief Create a new ike_config task. + * Create a new ike_config task. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE for initiator @@ -55,4 +50,4 @@ struct ike_config_t { */ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_CONFIG_H_ */ +#endif /* IKE_CONFIG_H_ @} */ diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c index 1a3656ca6..aa7950ef7 100644 --- a/src/charon/sa/tasks/ike_delete.c +++ b/src/charon/sa/tasks/ike_delete.c @@ -1,10 +1,3 @@ -/** - * @file ike_delete.c - * - * @brief Implementation of the ike_delete task. - * - */ - /* * Copyright (C) 2006-2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: ike_delete.c 3802 2008-04-14 08:17:18Z martin $ */ #include "ike_delete.h" @@ -87,18 +82,18 @@ static status_t process_r(private_ike_delete_t *this, message_t *message) * come so far without being correct */ switch (this->ike_sa->get_state(this->ike_sa)) { - case IKE_DELETING: - this->simultaneous = TRUE; - break; case IKE_ESTABLISHED: DBG1(DBG_IKE, "deleting IKE_SA on request"); + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + this->ike_sa->reestablish(this->ike_sa); break; - case IKE_REKEYING: - break; + case IKE_DELETING: + this->simultaneous = TRUE; + /* FALL */ default: + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); break; } - this->ike_sa->set_state(this->ike_sa, IKE_DELETING); return NEED_MORE; } diff --git a/src/charon/sa/tasks/ike_delete.h b/src/charon/sa/tasks/ike_delete.h index e8ec5ebbe..6d08d345d 100644 --- a/src/charon/sa/tasks/ike_delete.h +++ b/src/charon/sa/tasks/ike_delete.h @@ -1,10 +1,3 @@ -/** - * @file ike_delete.h - * - * @brief Interface ike_delete_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_delete.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_delete ike_delete + * @{ @ingroup tasks */ #ifndef IKE_DELETE_H_ @@ -30,12 +30,7 @@ typedef struct ike_delete_t ike_delete_t; #include <sa/tasks/task.h> /** - * @brief Task of type ike_delete, delete an IKE_SA. - * - * @b Constructors: - * - ike_delete_create() - * - * @ingroup tasks + * Task of type ike_delete, delete an IKE_SA. */ struct ike_delete_t { @@ -46,7 +41,7 @@ struct ike_delete_t { }; /** - * @brief Create a new ike_delete task. + * Create a new ike_delete task. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if we initiate the delete @@ -54,4 +49,4 @@ struct ike_delete_t { */ ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_DELETE_H_ */ +#endif /* IKE_DELETE_H_ @} */ diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c index be751766e..9f1d43cbf 100644 --- a/src/charon/sa/tasks/ike_dpd.c +++ b/src/charon/sa/tasks/ike_dpd.c @@ -1,10 +1,3 @@ -/** - * @file ike_dpd.c - * - * @brief Implementation of the ike_dpd task. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: ike_dpd.c 3589 2008-03-13 14:14:44Z martin $ */ #include "ike_dpd.h" diff --git a/src/charon/sa/tasks/ike_dpd.h b/src/charon/sa/tasks/ike_dpd.h index 531b0502d..62b8a6a10 100644 --- a/src/charon/sa/tasks/ike_dpd.h +++ b/src/charon/sa/tasks/ike_dpd.h @@ -1,10 +1,3 @@ -/** - * @file ike_dpd.h - * - * @brief Interface ike_dpd_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_dpd.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_dpd ike_dpd + * @{ @ingroup tasks */ #ifndef IKE_DPD_H_ @@ -30,14 +30,9 @@ typedef struct ike_dpd_t ike_dpd_t; #include <sa/tasks/task.h> /** - * @brief Task of type ike_dpd, detects dead peers. + * Task of type ike_dpd, detects dead peers. * * The DPD task actually does nothing, as a DPD has no associated payloads. - * - * @b Constructors: - * - ike_dpd_create() - * - * @ingroup tasks */ struct ike_dpd_t { @@ -48,11 +43,11 @@ struct ike_dpd_t { }; /** - * @brief Create a new ike_dpd task. + * Create a new ike_dpd task. * * @param initiator TRUE if thask is the original initator * @return ike_dpd task to handle by the task_manager */ ike_dpd_t *ike_dpd_create(bool initiator); -#endif /* IKE_DPD_H_ */ +#endif /* IKE_DPD_H_ @} */ diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c index 42b47a82f..7def3a556 100644 --- a/src/charon/sa/tasks/ike_init.c +++ b/src/charon/sa/tasks/ike_init.c @@ -1,11 +1,5 @@ -/** - * @file ike_init.c - * - * @brief Implementation of the ike_init task. - * - */ - /* + * Copyright (C) 2008 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -19,6 +13,8 @@ * 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. + * + * $Id: ike_init.c 4086 2008-06-22 11:24:33Z andreas $ */ #include "ike_init.h" @@ -195,7 +191,7 @@ static void process_payloads(private_ike_init_t *this, message_t *message) this->dh_group = ke_payload->get_dh_group_number(ke_payload); if (!this->initiator) { - this->dh = diffie_hellman_create(this->dh_group); + this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); } if (this->dh) { @@ -222,13 +218,12 @@ static void process_payloads(private_ike_init_t *this, message_t *message) */ static status_t build_i(private_ike_init_t *this, message_t *message) { - randomizer_t *randomizer; - status_t status; + rng_t *rng; this->config = this->ike_sa->get_ike_cfg(this->ike_sa); SIG(IKE_UP_START, "initiating IKE_SA '%s' to %H", this->ike_sa->get_name(this->ike_sa), - this->config->get_other_host(this->config)); + this->ike_sa->get_other_host(this->ike_sa)); this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); if (this->retry++ >= MAX_RETRIES) @@ -241,7 +236,7 @@ static status_t build_i(private_ike_init_t *this, message_t *message) if (!this->dh) { this->dh_group = this->config->get_dh_group(this->config); - this->dh = diffie_hellman_create(this->dh_group); + this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); if (this->dh == NULL) { SIG(IKE_UP_FAILED, "configured DH group %N not supported", @@ -253,15 +248,14 @@ static status_t build_i(private_ike_init_t *this, message_t *message) /* generate nonce only when we are trying the first time */ if (this->my_nonce.ptr == NULL) { - randomizer = randomizer_create(); - status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, - &this->my_nonce); - randomizer->destroy(randomizer); - if (status != SUCCESS) + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) { - SIG(IKE_UP_FAILED, "error generating random nonce value"); + SIG(IKE_UP_FAILED, "error generating nonce"); return FAILED; } + rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce); + rng->destroy(rng); } if (this->cookie.ptr) @@ -270,30 +264,90 @@ static status_t build_i(private_ike_init_t *this, message_t *message) } build_payloads(this, message); - + +#ifdef ME + { + chunk_t connect_id = this->ike_sa->get_connect_id(this->ike_sa); + if (connect_id.ptr) + { + message->add_notify(message, FALSE, ME_CONNECTID, connect_id); + } + } +#endif /* ME */ return NEED_MORE; } /** - * Implementation of task_t.process for initiator + * Implementation of task_t.process for responder */ static status_t process_r(private_ike_init_t *this, message_t *message) { - randomizer_t *randomizer; + rng_t *rng; this->config = this->ike_sa->get_ike_cfg(this->ike_sa); - SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA", + SIG(IKE_UP_START, "%H is initiating an IKE_SA", message->get_source(message)); this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING); - randomizer = randomizer_create(); - if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, - &this->my_nonce) != SUCCESS) + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_IKE, "error generating nonce"); + return FAILED; + } + rng->allocate_bytes(rng, NONCE_SIZE, &this->my_nonce); + rng->destroy(rng); + +#ifdef ME { - DBG1(DBG_IKE, "error generating random nonce value"); + chunk_t connect_id = chunk_empty; + iterator_t *iterator; + payload_t *payload; + + /* check for a ME_CONNECTID notify */ + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case ME_CONNECTID: + { + chunk_free(&connect_id); + connect_id = chunk_clone(notify->get_notification_data(notify)); + DBG2(DBG_IKE, "received ME_CONNECTID %#B", &connect_id); + break; + } + default: + { + if (type < 16383) + { + DBG1(DBG_IKE, "received %N notify error", + notify_type_names, type); + break; + } + DBG2(DBG_IKE, "received %N notify", + notify_type_names, type); + break; + } + } + } + } + iterator->destroy(iterator); + + if (connect_id.ptr) + { + charon->connect_manager->stop_checks(charon->connect_manager, + connect_id); + chunk_free(&connect_id); + } } - randomizer->destroy(randomizer); +#endif /* ME */ process_payloads(this, message); @@ -321,11 +375,11 @@ static status_t build_r(private_ike_init_t *this, message_t *message) !this->proposal->has_dh_group(this->proposal, this->dh_group) || this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) { - algorithm_t *algo; + u_int16_t group; + if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, - &algo)) + &group, NULL)) { - u_int16_t group = algo->algorithm; SIG(CHILD_UP_FAILED, "DH group %N inacceptable, requesting %N", diffie_hellman_group_names, this->dh_group, diffie_hellman_group_names, group); @@ -370,9 +424,16 @@ static status_t build_r(private_ike_init_t *this, message_t *message) message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return FAILED; } - - build_payloads(this, message); + /* Keep the selected IKE proposal for status information purposes */ + { + char buf[BUF_LEN]; + + snprintf(buf, BUF_LEN, "%P", this->proposal); + this->ike_sa->set_proposal(this->ike_sa, buf+4); + } + + build_payloads(this, message); return SUCCESS; } @@ -427,7 +488,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) this->cookie = chunk_clone(notify->get_notification_data(notify)); this->ike_sa->reset(this->ike_sa); iterator->destroy(iterator); - DBG1(DBG_IKE, "received %N notify", notify_type_names, type); + DBG2(DBG_IKE, "received %N notify", notify_type_names, type); return NEED_MORE; } default: @@ -439,7 +500,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) iterator->destroy(iterator); return FAILED; } - DBG1(DBG_IKE, "received %N notify", + DBG2(DBG_IKE, "received %N notify", notify_type_names, type); break; } @@ -454,7 +515,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) if (this->proposal == NULL || this->other_nonce.len == 0 || this->my_nonce.len == 0) { - SIG(IKE_UP_FAILED, "peers proposal selection invalid"); + SIG(IKE_UP_FAILED, "peer's proposal selection invalid"); return FAILED; } @@ -462,7 +523,7 @@ static status_t process_i(private_ike_init_t *this, message_t *message) !this->proposal->has_dh_group(this->proposal, this->dh_group) || this->dh->get_shared_secret(this->dh, &secret) != SUCCESS) { - SIG(IKE_UP_FAILED, "peers DH group selection invalid"); + SIG(IKE_UP_FAILED, "peer's DH group selection invalid"); return FAILED; } @@ -494,6 +555,15 @@ static status_t process_i(private_ike_init_t *this, message_t *message) SIG(IKE_UP_FAILED, "key derivation failed"); return FAILED; } + + /* Keep the selected IKE proposal for status information purposes */ + { + char buf[BUF_LEN]; + + snprintf(buf, BUF_LEN, "%P", this->proposal); + this->ike_sa->set_proposal(this->ike_sa, buf+4); + } + return SUCCESS; } @@ -532,7 +602,7 @@ static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa) this->ike_sa = ike_sa; this->proposal = NULL; - this->dh = diffie_hellman_create(this->dh_group); + this->dh = lib->crypto->create_dh(lib->crypto, this->dh_group); } /** diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h index f60c096e8..0e5a913fd 100644 --- a/src/charon/sa/tasks/ike_init.h +++ b/src/charon/sa/tasks/ike_init.h @@ -1,10 +1,3 @@ -/** - * @file ike_init.h - * - * @brief Interface ike_init_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_init.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_init ike_init + * @{ @ingroup tasks */ #ifndef IKE_INIT_H_ @@ -30,14 +30,9 @@ typedef struct ike_init_t ike_init_t; #include <sa/tasks/task.h> /** - * @brief Task of type IKE_INIT, creates an IKE_SA without authentication. + * Task of type IKE_INIT, creates an IKE_SA without authentication. * * The authentication of is handle in the ike_auth task. - * - * @b Constructors: - * - ike_init_create() - * - * @ingroup tasks */ struct ike_init_t { @@ -47,16 +42,15 @@ struct ike_init_t { task_t task; /** - * @brief Get the lower of the two nonces, used for rekey collisions. + * Get the lower of the two nonces, used for rekey collisions. * - * @param this calling object * @return lower nonce */ chunk_t (*get_lower_nonce) (ike_init_t *this); }; /** - * @brief Create a new IKE_INIT task. + * Create a new IKE_INIT task. * * @param ike_sa IKE_SA this task works for (new one when rekeying) * @param initiator TRUE if thask is the original initator @@ -65,4 +59,4 @@ struct ike_init_t { */ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa); -#endif /* IKE_INIT_H_ */ +#endif /* IKE_INIT_H_ @} */ diff --git a/src/charon/sa/tasks/ike_p2p.c b/src/charon/sa/tasks/ike_me.c index 84b88e16b..2d7c64d70 100644 --- a/src/charon/sa/tasks/ike_p2p.c +++ b/src/charon/sa/tasks/ike_me.c @@ -1,12 +1,5 @@ -/** - * @file ike_p2p.c - * - * @brief Implementation of the ike_p2p task. - * - */ - /* - * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2007-2008 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -18,9 +11,11 @@ * 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. + * + * $Id: ike_me.c 3806 2008-04-15 05:56:35Z martin $ */ - -#include "ike_p2p.h" + +#include "ike_me.h" #include <string.h> @@ -31,27 +26,20 @@ #include <encoding/payloads/endpoint_notify.h> #include <processing/jobs/mediation_job.h> -#define P2P_SESSIONID_LEN 8 -#define P2P_SESSIONKEY_LEN 16 +#define ME_CONNECTID_LEN 4 +#define ME_CONNECTKEY_LEN 16 -/* FIXME: proposed values */ -#define P2P_SESSIONID_MIN_LEN 4 -#define P2P_SESSIONID_MAX_LEN 16 -#define P2P_SESSIONKEY_MIN_LEN 8 -#define P2P_SESSIONKEY_MAX_LEN 64 - - -typedef struct private_ike_p2p_t private_ike_p2p_t; +typedef struct private_ike_me_t private_ike_me_t; /** - * Private members of a ike_p2p_t task. + * Private members of a ike_me_t task. */ -struct private_ike_p2p_t { +struct private_ike_me_t { /** * Public methods and task_t interface. */ - ike_p2p_t public; + ike_me_t public; /** * Assigned IKE_SA. @@ -105,12 +93,12 @@ struct private_ike_p2p_t { /** * Received ID used for connectivity checks */ - chunk_t session_id; + chunk_t connect_id; /** * Received key used for connectivity checks */ - chunk_t session_key; + chunk_t connect_key; /** * Peer config of the mediated connection @@ -138,7 +126,7 @@ static void add_endpoints_to_message(message_t *message, linked_list_t *endpoint /** * Gathers endpoints and adds them to the current message */ -static void gather_and_add_endpoints(private_ike_p2p_t *this, message_t *message) +static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message) { iterator_t *iterator; host_t *addr, *host; @@ -176,7 +164,7 @@ static void gather_and_add_endpoints(private_ike_p2p_t *this, message_t *message /** * read notifys from message and evaluate them */ -static void process_payloads(private_ike_p2p_t *this, message_t *message) +static void process_payloads(private_ike_me_t *this, message_t *message) { iterator_t *iterator; payload_t *payload; @@ -193,55 +181,55 @@ static void process_payloads(private_ike_p2p_t *this, message_t *message) switch (notify->get_notify_type(notify)) { - case P2P_CONNECT_FAILED: + case ME_CONNECT_FAILED: { - DBG2(DBG_IKE, "received P2P_CONNECT_FAILED notify"); + DBG2(DBG_IKE, "received ME_CONNECT_FAILED notify"); this->failed = TRUE; break; } - case P2P_MEDIATION: + case ME_MEDIATION: { - DBG2(DBG_IKE, "received P2P_MEDIATION notify"); + DBG2(DBG_IKE, "received ME_MEDIATION notify"); this->mediation = TRUE; break; } - case P2P_ENDPOINT: + case ME_ENDPOINT: { endpoint_notify_t *endpoint = endpoint_notify_create_from_payload(notify); if (!endpoint) { - DBG1(DBG_IKE, "received invalid P2P_ENDPOINT notify"); + DBG1(DBG_IKE, "received invalid ME_ENDPOINT notify"); break; } - DBG1(DBG_IKE, "received %N P2P_ENDPOINT %#H", p2p_endpoint_type_names, + DBG1(DBG_IKE, "received %N ME_ENDPOINT %#H", me_endpoint_type_names, endpoint->get_type(endpoint), endpoint->get_host(endpoint)); this->remote_endpoints->insert_last(this->remote_endpoints, endpoint); break; } - case P2P_CALLBACK: + case ME_CALLBACK: { - DBG2(DBG_IKE, "received P2P_CALLBACK notify"); + DBG2(DBG_IKE, "received ME_CALLBACK notify"); this->callback = TRUE; break; } - case P2P_SESSIONID: + case ME_CONNECTID: { - chunk_free(&this->session_id); - this->session_id = chunk_clone(notify->get_notification_data(notify)); - DBG3(DBG_IKE, "received p2p_sessionid %B", &this->session_id); + chunk_free(&this->connect_id); + this->connect_id = chunk_clone(notify->get_notification_data(notify)); + DBG2(DBG_IKE, "received ME_CONNECTID %#B", &this->connect_id); break; } - case P2P_SESSIONKEY: + case ME_CONNECTKEY: { - chunk_free(&this->session_key); - this->session_key = chunk_clone(notify->get_notification_data(notify)); - DBG4(DBG_IKE, "received p2p_sessionkey %B", &this->session_key); + chunk_free(&this->connect_key); + this->connect_key = chunk_clone(notify->get_notification_data(notify)); + DBG4(DBG_IKE, "received ME_CONNECTKEY %#B", &this->connect_key); break; } - case P2P_RESPONSE: + case ME_RESPONSE: { - DBG2(DBG_IKE, "received P2P_RESPONSE notify"); + DBG2(DBG_IKE, "received ME_RESPONSE notify"); this->response = TRUE; break; } @@ -253,9 +241,9 @@ static void process_payloads(private_ike_p2p_t *this, message_t *message) } /** - * Implementation of task_t.process for initiator + * Implementation of task_t.build for initiator */ -static status_t build_i(private_ike_p2p_t *this, message_t *message) +static status_t build_i(private_ike_me_t *this, message_t *message) { switch(message->get_exchange_type(message)) { @@ -264,8 +252,8 @@ static status_t build_i(private_ike_p2p_t *this, message_t *message) peer_cfg_t *peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (peer_cfg->is_mediation(peer_cfg)) { - DBG2(DBG_IKE, "adding P2P_MEDIATION"); - message->add_notify(message, FALSE, P2P_MEDIATION, chunk_empty); + DBG2(DBG_IKE, "adding ME_MEDIATION"); + message->add_notify(message, FALSE, ME_MEDIATION, chunk_empty); } else { @@ -283,48 +271,40 @@ static status_t build_i(private_ike_p2p_t *this, message_t *message) } break; } - case P2P_CONNECT: + case ME_CONNECT: { id_payload_t *id_payload; - randomizer_t *rand = randomizer_create(); + rng_t *rng; id_payload = id_payload_create_from_identification(ID_PEER, this->peer_id); message->add_payload(message, (payload_t*)id_payload); - if (!this->response) + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (!rng) { - /* only the initiator creates a session ID. the responder returns - * the session ID that it received from the initiator */ - if (rand->allocate_pseudo_random_bytes(rand, - P2P_SESSIONID_LEN, &this->session_id) != SUCCESS) - { - DBG1(DBG_IKE, "unable to generate session ID for P2P_CONNECT"); - rand->destroy(rand); - return FAILED; - } + DBG1(DBG_IKE, "unable to generate connect ID for ME_CONNECT"); + return FAILED; } - - if (rand->allocate_pseudo_random_bytes(rand, - P2P_SESSIONKEY_LEN, &this->session_key) != SUCCESS) + if (!this->response) { - DBG1(DBG_IKE, "unable to generate session key for P2P_CONNECT"); - rand->destroy(rand); - return FAILED; + /* only the initiator creates a connect ID. the responder returns + * the connect ID that it received from the initiator */ + rng->allocate_bytes(rng, ME_CONNECTID_LEN, &this->connect_id); } + rng->allocate_bytes(rng, ME_CONNECTKEY_LEN, &this->connect_key); + rng->destroy(rng); - rand->destroy(rand); - - message->add_notify(message, FALSE, P2P_SESSIONID, this->session_id); - message->add_notify(message, FALSE, P2P_SESSIONKEY, this->session_key); + message->add_notify(message, FALSE, ME_CONNECTID, this->connect_id); + message->add_notify(message, FALSE, ME_CONNECTKEY, this->connect_key); if (this->response) { - message->add_notify(message, FALSE, P2P_RESPONSE, chunk_empty); + message->add_notify(message, FALSE, ME_RESPONSE, chunk_empty); } else { - /* FIXME: should we make that configurable */ - message->add_notify(message, FALSE, P2P_CALLBACK, chunk_empty); + /* FIXME: should we make that configurable? */ + message->add_notify(message, FALSE, ME_CALLBACK, chunk_empty); } gather_and_add_endpoints(this, message); @@ -340,17 +320,17 @@ static status_t build_i(private_ike_p2p_t *this, message_t *message) /** * Implementation of task_t.process for responder */ -static status_t process_r(private_ike_p2p_t *this, message_t *message) +static status_t process_r(private_ike_me_t *this, message_t *message) { switch(message->get_exchange_type(message)) { - case P2P_CONNECT: + case ME_CONNECT: { id_payload_t *id_payload; id_payload = (id_payload_t*)message->get_payload(message, ID_PEER); if (!id_payload) { - DBG1(DBG_IKE, "received P2P_CONNECT without ID_PEER payload, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ID_PEER payload, aborting"); break; } this->peer_id = id_payload->get_identification(id_payload); @@ -359,32 +339,32 @@ static status_t process_r(private_ike_p2p_t *this, message_t *message) if (this->callback) { - DBG1(DBG_IKE, "received P2P_CALLBACK for '%D'", this->peer_id); + DBG1(DBG_IKE, "received ME_CALLBACK for '%D'", this->peer_id); break; } - if (!this->session_id.ptr) + if (!this->connect_id.ptr) { - DBG1(DBG_IKE, "received P2P_CONNECT without P2P_SESSIONID notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTID notify, aborting"); this->invalid_syntax = TRUE; break; } - if (!this->session_key.ptr) + if (!this->connect_key.ptr) { - DBG1(DBG_IKE, "received P2P_CONNECT without P2P_SESSIONKEY notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTKEY notify, aborting"); this->invalid_syntax = TRUE; break; } if (!this->remote_endpoints->get_count(this->remote_endpoints)) { - DBG1(DBG_IKE, "received P2P_CONNECT without any P2P_ENDPOINT payloads, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without any ME_ENDPOINT payloads, aborting"); this->invalid_syntax = TRUE; break; } - DBG1(DBG_IKE, "received P2P_CONNECT"); + DBG1(DBG_IKE, "received ME_CONNECT"); break; } default: @@ -396,11 +376,11 @@ static status_t process_r(private_ike_p2p_t *this, message_t *message) /** * Implementation of task_t.build for responder */ -static status_t build_r(private_ike_p2p_t *this, message_t *message) +static status_t build_r(private_ike_me_t *this, message_t *message) { switch(message->get_exchange_type(message)) { - case P2P_CONNECT: + case ME_CONNECT: { if (this->invalid_syntax) { @@ -422,7 +402,7 @@ static status_t build_r(private_ike_p2p_t *this, message_t *message) * as initiator, upon receiving a response from another peer, * update the checklist and start sending checks */ charon->connect_manager->set_responder_data(charon->connect_manager, - this->session_id, this->session_key, this->remote_endpoints); + this->connect_id, this->connect_key, this->remote_endpoints); } else { @@ -430,10 +410,10 @@ static status_t build_r(private_ike_p2p_t *this, message_t *message) * as responder, create a checklist with the initiator's data */ charon->connect_manager->set_initiator_data(charon->connect_manager, this->peer_id, this->ike_sa->get_my_id(this->ike_sa), - this->session_id, this->session_key, this->remote_endpoints, + this->connect_id, this->connect_key, this->remote_endpoints, FALSE); if (this->ike_sa->respond(this->ike_sa, this->peer_id, - this->session_id) != SUCCESS) + this->connect_id) != SUCCESS) { return FAILED; } @@ -449,7 +429,7 @@ static status_t build_r(private_ike_p2p_t *this, message_t *message) /** * Implementation of task_t.process for initiator */ -static status_t process_i(private_ike_p2p_t *this, message_t *message) +static status_t process_i(private_ike_me_t *this, message_t *message) { switch(message->get_exchange_type(message)) { @@ -459,7 +439,7 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) if (!this->mediation) { - DBG1(DBG_IKE, "server did not return a P2P_MEDIATION, aborting"); + DBG1(DBG_IKE, "server did not return a ME_MEDIATION, aborting"); return FAILED; } @@ -485,16 +465,14 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) break; } - case P2P_CONNECT: + case ME_CONNECT: { process_payloads(this, message); if (this->failed) { DBG1(DBG_IKE, "peer '%D' is not online", this->peer_id); - /* FIXME: notify the mediated connection (job?) - * FIXME: probably delete the created checklist, at least as - * responder */ + /* FIXME: notify the mediated connection (job?) */ } else { @@ -503,7 +481,7 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) /* FIXME: handle result of set_responder_data. * as responder, we update the checklist and start sending checks */ charon->connect_manager->set_responder_data(charon->connect_manager, - this->session_id, this->session_key, this->local_endpoints); + this->connect_id, this->connect_key, this->local_endpoints); } else { @@ -511,8 +489,10 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) * as initiator, we create a checklist and set the initiator's data */ charon->connect_manager->set_initiator_data(charon->connect_manager, this->ike_sa->get_my_id(this->ike_sa), this->peer_id, - this->session_id, this->session_key, this->local_endpoints, + this->connect_id, this->connect_key, this->local_endpoints, TRUE); + /* FIXME: also start a timer for the whole transaction (maybe + * within the connect_manager?) */ } } break; @@ -524,29 +504,29 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) } /** - * Implementation of task_t.process for initiator (mediation server) + * Implementation of task_t.build for initiator (mediation server) */ -static status_t build_i_ms(private_ike_p2p_t *this, message_t *message) +static status_t build_i_ms(private_ike_me_t *this, message_t *message) { switch(message->get_exchange_type(message)) { - case P2P_CONNECT: + case ME_CONNECT: { id_payload_t *id_payload = id_payload_create_from_identification(ID_PEER, this->peer_id); message->add_payload(message, (payload_t*)id_payload); if (this->callback) { - message->add_notify(message, FALSE, P2P_CALLBACK, chunk_empty); + message->add_notify(message, FALSE, ME_CALLBACK, chunk_empty); } else { if (this->response) { - message->add_notify(message, FALSE, P2P_RESPONSE, chunk_empty); + message->add_notify(message, FALSE, ME_RESPONSE, chunk_empty); } - message->add_notify(message, FALSE, P2P_SESSIONID, this->session_id); - message->add_notify(message, FALSE, P2P_SESSIONKEY, this->session_key); + message->add_notify(message, FALSE, ME_CONNECTID, this->connect_id); + message->add_notify(message, FALSE, ME_CONNECTKEY, this->connect_key); add_endpoints_to_message(message, this->remote_endpoints); } @@ -562,27 +542,37 @@ static status_t build_i_ms(private_ike_p2p_t *this, message_t *message) /** * Implementation of task_t.process for responder (mediation server) */ -static status_t process_r_ms(private_ike_p2p_t *this, message_t *message) +static status_t process_r_ms(private_ike_me_t *this, message_t *message) { switch(message->get_exchange_type(message)) { case IKE_SA_INIT: { + /* FIXME: we should check for SA* and TS* payloads + * if any are there send NO_ADDITIONAL_SAS back and delete this SA */ process_payloads(this, message); return this->mediation ? NEED_MORE : SUCCESS; } case IKE_AUTH: { + /* FIXME: we should check whether the current peer_config is configured + * as mediation connection */ process_payloads(this, message); break; } - case P2P_CONNECT: + case CREATE_CHILD_SA: + { + /* FIXME: if this is not to rekey the IKE SA we have to return a + * NO_ADDITIONAL_SAS and then delete the SA */ + break; + } + case ME_CONNECT: { id_payload_t *id_payload; id_payload = (id_payload_t*)message->get_payload(message, ID_PEER); if (!id_payload) { - DBG1(DBG_IKE, "received P2P_CONNECT without ID_PEER payload, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ID_PEER payload, aborting"); this->invalid_syntax = TRUE; break; } @@ -591,23 +581,23 @@ static status_t process_r_ms(private_ike_p2p_t *this, message_t *message) process_payloads(this, message); - if (!this->session_id.ptr) + if (!this->connect_id.ptr) { - DBG1(DBG_IKE, "received P2P_CONNECT without P2P_SESSIONID notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTID notify, aborting"); this->invalid_syntax = TRUE; break; } - if (!this->session_key.ptr) + if (!this->connect_key.ptr) { - DBG1(DBG_IKE, "received P2P_CONNECT without P2P_SESSIONKEY notify, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without ME_CONNECTKEY notify, aborting"); this->invalid_syntax = TRUE; break; } if (!this->remote_endpoints->get_count(this->remote_endpoints)) { - DBG1(DBG_IKE, "received P2P_CONNECT without any P2P_ENDPOINT payloads, aborting"); + DBG1(DBG_IKE, "received ME_CONNECT without any ME_ENDPOINT payloads, aborting"); this->invalid_syntax = TRUE; break; } @@ -623,13 +613,13 @@ static status_t process_r_ms(private_ike_p2p_t *this, message_t *message) /** * Implementation of task_t.build for responder (mediation server) */ -static status_t build_r_ms(private_ike_p2p_t *this, message_t *message) +static status_t build_r_ms(private_ike_me_t *this, message_t *message) { switch(message->get_exchange_type(message)) { case IKE_SA_INIT: { - message->add_notify(message, FALSE, P2P_MEDIATION, chunk_empty); + message->add_notify(message, FALSE, ME_MEDIATION, chunk_empty); return NEED_MORE; } case IKE_AUTH: @@ -645,17 +635,17 @@ static status_t build_r_ms(private_ike_p2p_t *this, message_t *message) endpoint = endpoint_notify_create_from_host(SERVER_REFLEXIVE, host, NULL); message->add_payload(message, (payload_t*)endpoint->build_notify(endpoint)); + endpoint->destroy(endpoint); } - charon->mediation_manager->update_sa_id(charon->mediation_manager, - this->ike_sa->get_other_id(this->ike_sa), - this->ike_sa->get_id(this->ike_sa)); + /* FIXME: we actually must delete any existing IKE_SAs with the same remote id */ + this->ike_sa->act_as_mediation_server(this->ike_sa); SIG(CHILD_UP_SUCCESS, "established mediation connection without CHILD_SA successfully"); break; } - case P2P_CONNECT: + case ME_CONNECT: { if (this->invalid_syntax) { @@ -678,13 +668,13 @@ static status_t build_r_ms(private_ike_p2p_t *this, message_t *message) if (!peer_sa) { /* the peer is not online */ - message->add_notify(message, TRUE, P2P_CONNECT_FAILED, chunk_empty); + message->add_notify(message, TRUE, ME_CONNECT_FAILED, chunk_empty); break; } job_t *job = (job_t*)mediation_job_create(this->peer_id, - this->ike_sa->get_other_id(this->ike_sa), this->session_id, - this->session_key, this->remote_endpoints, this->response); + this->ike_sa->get_other_id(this->ike_sa), this->connect_id, + this->connect_key, this->remote_endpoints, this->response); charon->processor->queue_job(charon->processor, job); break; @@ -698,64 +688,71 @@ static status_t build_r_ms(private_ike_p2p_t *this, message_t *message) /** * Implementation of task_t.process for initiator (mediation server) */ -static status_t process_i_ms(private_ike_p2p_t *this, message_t *message) +static status_t process_i_ms(private_ike_me_t *this, message_t *message) { + /* FIXME: theoretically we should be prepared to receive a ME_CONNECT_FAILED + * here if the responding peer is not able to proceed. in this case we shall + * notify the initiating peer with a ME_CONNECT request containing only a + * ME_CONNECT_FAILED */ return SUCCESS; } /** - * Implementation of ike_p2p.connect + * Implementation of ike_me.connect */ -static void p2p_connect(private_ike_p2p_t *this, identification_t *peer_id) +static void me_connect(private_ike_me_t *this, identification_t *peer_id) { this->peer_id = peer_id->clone(peer_id); } /** - * Implementation of ike_p2p.respond + * Implementation of ike_me.respond */ -static void p2p_respond(private_ike_p2p_t *this, identification_t *peer_id, - chunk_t session_id) +static void me_respond(private_ike_me_t *this, identification_t *peer_id, + chunk_t connect_id) { this->peer_id = peer_id->clone(peer_id); - this->session_id = chunk_clone(session_id); + this->connect_id = chunk_clone(connect_id); this->response = TRUE; } /** - * Implementation of ike_p2p.callback + * Implementation of ike_me.callback */ -static void p2p_callback(private_ike_p2p_t *this, identification_t *peer_id) +static void me_callback(private_ike_me_t *this, identification_t *peer_id) { this->peer_id = peer_id->clone(peer_id); this->callback = TRUE; } /** - * Implementation of ike_p2p.relay + * Implementation of ike_me.relay */ -static void relay(private_ike_p2p_t *this, identification_t *requester, chunk_t session_id, - chunk_t session_key, linked_list_t *endpoints, bool response) +static void relay(private_ike_me_t *this, identification_t *requester, chunk_t connect_id, + chunk_t connect_key, linked_list_t *endpoints, bool response) { this->peer_id = requester->clone(requester); - this->session_id = chunk_clone(session_id); - this->session_key = chunk_clone(session_key); + this->connect_id = chunk_clone(connect_id); + this->connect_key = chunk_clone(connect_key); + + this->remote_endpoints->destroy_offset(this->remote_endpoints, offsetof(endpoint_notify_t, destroy)); this->remote_endpoints = endpoints->clone_offset(endpoints, offsetof(endpoint_notify_t, clone)); + this->response = response; } /** * Implementation of task_t.get_type */ -static task_type_t get_type(private_ike_p2p_t *this) +static task_type_t get_type(private_ike_me_t *this) { - return IKE_P2P; + return IKE_ME; } /** * Implementation of task_t.migrate */ -static void migrate(private_ike_p2p_t *this, ike_sa_t *ike_sa) +static void migrate(private_ike_me_t *this, ike_sa_t *ike_sa) { this->ike_sa = ike_sa; } @@ -763,12 +760,12 @@ static void migrate(private_ike_p2p_t *this, ike_sa_t *ike_sa) /** * Implementation of task_t.destroy */ -static void destroy(private_ike_p2p_t *this) +static void destroy(private_ike_me_t *this) { DESTROY_IF(this->peer_id); - chunk_free(&this->session_id); - chunk_free(&this->session_key); + chunk_free(&this->connect_id); + chunk_free(&this->connect_key); this->local_endpoints->destroy_offset(this->local_endpoints, offsetof(endpoint_notify_t, destroy)); this->remote_endpoints->destroy_offset(this->remote_endpoints, offsetof(endpoint_notify_t, destroy)); @@ -780,16 +777,15 @@ static void destroy(private_ike_p2p_t *this) /* * Described in header. */ -ike_p2p_t *ike_p2p_create(ike_sa_t *ike_sa, bool initiator) +ike_me_t *ike_me_create(ike_sa_t *ike_sa, bool initiator) { - private_ike_p2p_t *this = malloc_thing(private_ike_p2p_t); + private_ike_me_t *this = malloc_thing(private_ike_me_t); this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - ike_sa_id_t *id = ike_sa->get_id(ike_sa); - if (id->is_initiator(id)) + if (ike_sa->is_ike_initiator(ike_sa)) { if (initiator) { @@ -817,17 +813,17 @@ ike_p2p_t *ike_p2p_create(ike_sa_t *ike_sa, bool initiator) } } - this->public.connect = (void(*)(ike_p2p_t*,identification_t*))p2p_connect; - this->public.respond = (void(*)(ike_p2p_t*,identification_t*,chunk_t))p2p_respond; - this->public.callback = (void(*)(ike_p2p_t*,identification_t*))p2p_callback; - this->public.relay = (void(*)(ike_p2p_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool))relay; + this->public.connect = (void(*)(ike_me_t*,identification_t*))me_connect; + this->public.respond = (void(*)(ike_me_t*,identification_t*,chunk_t))me_respond; + this->public.callback = (void(*)(ike_me_t*,identification_t*))me_callback; + this->public.relay = (void(*)(ike_me_t*,identification_t*,chunk_t,chunk_t,linked_list_t*,bool))relay; this->ike_sa = ike_sa; this->initiator = initiator; this->peer_id = NULL; - this->session_id = chunk_empty; - this->session_key = chunk_empty; + this->connect_id = chunk_empty; + this->connect_key = chunk_empty; this->local_endpoints = linked_list_create(); this->remote_endpoints = linked_list_create(); this->mediation = FALSE; diff --git a/src/charon/sa/tasks/ike_p2p.h b/src/charon/sa/tasks/ike_me.h index 327ac49d8..c9a515c8f 100644 --- a/src/charon/sa/tasks/ike_p2p.h +++ b/src/charon/sa/tasks/ike_me.h @@ -1,10 +1,3 @@ -/** - * @file ike_p2p.h - * - * @brief Interface ike_p2p_t. - * - */ - /* * Copyright (C) 2007 Tobias Brunner * Hochschule fuer Technik Rapperswil @@ -18,35 +11,37 @@ * 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. + * + * $Id: ike_me.h 3666 2008-03-26 18:40:19Z tobias $ */ -#ifndef IKE_P2P_H_ -#define IKE_P2P_H_ +/** + * @defgroup ike_me ike_me + * @{ @ingroup tasks + */ + +#ifndef IKE_ME_H_ +#define IKE_ME_H_ -typedef struct ike_p2p_t ike_p2p_t; +typedef struct ike_me_t ike_me_t; #include <library.h> #include <sa/ike_sa.h> #include <sa/tasks/task.h> /** - * @brief Task of type IKE_P2P, detects and handles P2P-NAT-T extensions. + * Task of type IKE_ME, detects and handles IKE-ME extensions. * - * This tasks handles the P2P_MEDIATION notify exchange to setup a mediation - * connection, allows to initiate mediated connections using P2P_CONNECT + * This tasks handles the ME_MEDIATION Notify exchange to setup a mediation + * connection, allows to initiate mediated connections using ME_CONNECT * exchanges and to request reflexive addresses from the mediation server using - * P2P_ENDPOINT notifies. + * ME_ENDPOINT notifies. * * @note This task has to be activated before the IKE_AUTH task, because that * task generates the IKE_SA_INIT message so that no more payloads can be added * to it afterwards. - * - * @b Constructors: - * - ike_p2p_create() - * - * @ingroup tasks */ -struct ike_p2p_t { +struct ike_me_t { /** * Implements the task_t interface @@ -54,57 +49,52 @@ struct ike_p2p_t { task_t task; /** - * @brief Initiates a connection with another peer (i.e. sends a P2P_CONNECT + * Initiates a connection with another peer (i.e. sends a ME_CONNECT * to the mediation server) * - * @param this object * @param peer_id ID of the other peer (gets cloned) */ - void (*connect)(ike_p2p_t *this, identification_t *peer_id); + void (*connect)(ike_me_t *this, identification_t *peer_id); /** - * @brief Responds to a P2P_CONNECT from another peer (i.e. sends a P2P_CONNECT + * Responds to a ME_CONNECT from another peer (i.e. sends a ME_CONNECT * to the mediation server) * - * @param this object * @param peer_id ID of the other peer (gets cloned) - * @param session_id the session ID as provided by the initiator (gets cloned) + * @param connect_id the connect ID as provided by the initiator (gets cloned) */ - void (*respond)(ike_p2p_t *this, identification_t *peer_id, chunk_t session_id); + void (*respond)(ike_me_t *this, identification_t *peer_id, chunk_t connect_id); /** - * @brief Sends a P2P_CALLBACK to a peer that previously requested another peer. + * Sends a ME_CALLBACK to a peer that previously requested another peer. * - * @param this object * @param peer_id ID of the other peer (gets cloned) */ - void (*callback)(ike_p2p_t *this, identification_t *peer_id); + void (*callback)(ike_me_t *this, identification_t *peer_id); /** - * @brief Relays data to another peer (i.e. sends a P2P_CONNECT to the peer) + * Relays data to another peer (i.e. sends a ME_CONNECT to the peer) * * Data gets cloned. * - * @param this object * @param requester ID of the requesting peer - * @param session_id content of the P2P_SESSIONID notify - * @param session_key content of the P2P_SESSIONKEY notify + * @param connect_id content of the ME_CONNECTID notify + * @param connect_key content of the ME_CONNECTKEY notify * @param endpoints endpoints * @param response TRUE if this is a response */ - void (*relay)(ike_p2p_t *this, identification_t *requester, chunk_t session_id, - chunk_t session_key, linked_list_t *endpoints, bool response); + void (*relay)(ike_me_t *this, identification_t *requester, chunk_t connect_id, + chunk_t connect_key, linked_list_t *endpoints, bool response); }; /** - * @brief Create a new ike_p2p task. + * Create a new ike_me task. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if taks is initiated by us - * @return ike_p2p task to handle by the task_manager + * @return ike_me task to handle by the task_manager */ -ike_p2p_t *ike_p2p_create(ike_sa_t *ike_sa, bool initiator); - +ike_me_t *ike_me_create(ike_sa_t *ike_sa, bool initiator); -#endif /*IKE_P2P_H_*/ +#endif /*IKE_ME_H_ @} */ diff --git a/src/charon/sa/tasks/ike_mobike.c b/src/charon/sa/tasks/ike_mobike.c index a53c243f0..23c68b9e9 100644 --- a/src/charon/sa/tasks/ike_mobike.c +++ b/src/charon/sa/tasks/ike_mobike.c @@ -1,10 +1,3 @@ -/** - * @file ike_mobike.c - * - * @brief Implementation of the ike_mobike task. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: ike_mobike.c 4006 2008-05-23 15:43:42Z martin $ */ #include "ike_mobike.h" @@ -28,6 +23,7 @@ #include <sa/tasks/ike_natd.h> #include <encoding/payloads/notify_payload.h> +#define COOKIE2_SIZE 16 typedef struct private_ike_mobike_t private_ike_mobike_t; @@ -125,6 +121,12 @@ static void process_payloads(private_ike_mobike_t *this, message_t *message) this->ike_sa->enable_extension(this->ike_sa, EXT_MOBIKE); break; } + case COOKIE2: + { + chunk_free(&this->cookie2); + this->cookie2 = chunk_clone(notify->get_notification_data(notify)); + break; + } case ADDITIONAL_IP6_ADDRESS: { family = AF_INET6; @@ -211,6 +213,23 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message) } /** + * build a cookie and add it to the message + */ +static void build_cookie(private_ike_mobike_t *this, message_t *message) +{ + rng_t *rng; + + chunk_free(&this->cookie2); + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (rng) + { + rng->allocate_bytes(rng, COOKIE2_SIZE, &this->cookie2); + rng->destroy(rng); + message->add_notify(message, FALSE, COOKIE2, this->cookie2); + } +} + +/** * update addresses of associated CHILD_SAs */ static void update_children(private_ike_mobike_t *this) @@ -262,6 +281,11 @@ static void transmit(private_ike_mobike_t *this, packet_t *packet) charon->kernel_interface, other); if (me) { + if (me->get_family(me) != other->get_family(other)) + { + me->destroy(me); + continue; + } /* reuse port for an active address, 4500 otherwise */ me->set_port(me, me->ip_equals(me, me_old) ? me_old->get_port(me_old) : IKEV2_NATT_PORT); @@ -297,6 +321,7 @@ static status_t build_i(private_ike_mobike_t *this, message_t *message) if (this->update) { message->add_notify(message, FALSE, UPDATE_SA_ADDRESSES, chunk_empty); + build_cookie(this, message); update_children(this); } if (this->address) @@ -363,6 +388,11 @@ static status_t build_r(private_ike_mobike_t *this, message_t *message) { this->natd->task.build(&this->natd->task, message); } + if (this->cookie2.ptr) + { + message->add_notify(message, FALSE, COOKIE2, this->cookie2); + chunk_free(&this->cookie2); + } if (this->update) { update_children(this); @@ -392,7 +422,25 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message) /* newer update queued, ignore this one */ return SUCCESS; } - process_payloads(this, message); + if (this->cookie2.ptr) + { /* check cookie if we included none */ + chunk_t cookie2; + + cookie2 = this->cookie2; + this->cookie2 = chunk_empty; + process_payloads(this, message); + if (!chunk_equals(cookie2, this->cookie2)) + { + chunk_free(&cookie2); + DBG1(DBG_IKE, "COOKIE2 mismatch, closing IKE_SA"); + return FAILED; + } + chunk_free(&cookie2); + } + else + { + process_payloads(this, message); + } if (this->natd) { this->natd->task.process(&this->natd->task, message); diff --git a/src/charon/sa/tasks/ike_mobike.h b/src/charon/sa/tasks/ike_mobike.h index bb5150723..9dd29970e 100644 --- a/src/charon/sa/tasks/ike_mobike.h +++ b/src/charon/sa/tasks/ike_mobike.h @@ -1,10 +1,3 @@ -/** - * @file ike_mobike.h - * - * @brief Interface ike_mobike_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_mobike.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_mobike ike_mobike + * @{ @ingroup tasks */ #ifndef IKE_MOBIKE_H_ @@ -31,7 +31,7 @@ typedef struct ike_mobike_t ike_mobike_t; #include <network/packet.h> /** - * @brief Task of type ike_mobike, detects and handles MOBIKE extension. + * Task of type ike_mobike, detects and handles MOBIKE extension. * * The MOBIKE extension is defined in RFC4555. It allows to update IKE * and IPsec tunnel addresses. @@ -39,11 +39,6 @@ typedef struct ike_mobike_t ike_mobike_t; * support, allows the exchange of ADDITIONAL_*_ADDRESS to exchange additional * endpoints and handles the UPDATE_SA_ADDRESS notify to finally update * endpoints. - * - * @b Constructors: - * - ike_mobike_create() - * - * @ingroup tasks */ struct ike_mobike_t { @@ -53,36 +48,33 @@ struct ike_mobike_t { task_t task; /** - * @brief Use the task to roam to other addresses. + * Use the task to roam to other addresses. * - * @param this calling object * @param address TRUE to include address list update */ void (*roam)(ike_mobike_t *this, bool address); /** - * @brief Transmision hook, called by task manager. + * Transmision hook, called by task manager. * * The task manager calls this hook whenever it transmits a packet. It * allows the mobike task to send the packet on multiple paths to do path * probing. * - * @param this calling object * @param packet the packet to transmit */ void (*transmit)(ike_mobike_t *this, packet_t *packet); /** - * @brief Check if this task is probing for routability. + * Check if this task is probing for routability. * - * @param this calling object * @return TRUE if task is probing */ bool (*is_probing)(ike_mobike_t *this); }; /** - * @brief Create a new ike_mobike task. + * Create a new ike_mobike task. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if taks is initiated by us @@ -90,5 +82,4 @@ struct ike_mobike_t { */ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_MOBIKE_H_ */ - +#endif /* IKE_MOBIKE_H_ @} */ diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c index 4c64ff8ba..69e5bac26 100644 --- a/src/charon/sa/tasks/ike_natd.c +++ b/src/charon/sa/tasks/ike_natd.c @@ -1,10 +1,3 @@ -/** - * @file ike_natd.c - * - * @brief Implementation of the ike_natd task. - * - */ - /* * Copyright (C) 2006-2007 Martin Willi * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger @@ -19,6 +12,8 @@ * 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. + * + * $Id: ike_natd.c 3806 2008-04-15 05:56:35Z martin $ */ #include "ike_natd.h" @@ -118,17 +113,17 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this, */ static chunk_t generate_natd_hash_faked(private_ike_natd_t *this) { - randomizer_t *randomizer; + rng_t *rng; chunk_t chunk; - randomizer = randomizer_create(); - if (randomizer->allocate_pseudo_random_bytes(randomizer, HASH_SIZE_SHA1, - &chunk) != SUCCESS) + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) { DBG1(DBG_IKE, "unable to get random bytes for NATD fake"); - chunk = chunk_empty; + return chunk_empty; } - randomizer->destroy(randomizer); + rng->allocate_bytes(rng, HASH_SIZE_SHA1, &chunk); + rng->destroy(rng); return chunk; } @@ -259,7 +254,7 @@ static status_t process_i(private_ike_natd_t *this, message_t *message) { peer_cfg_t *peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); -#ifdef P2P +#ifdef ME /* if we are on a mediated connection we have already switched to * port 4500 and the correct destination port is already configured, * therefore we must not switch again */ @@ -267,14 +262,14 @@ static status_t process_i(private_ike_natd_t *this, message_t *message) { return SUCCESS; } -#endif /* P2P */ +#endif /* ME */ if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY) || -#ifdef P2P +#ifdef ME /* if we are on a mediation connection we swith to port 4500 even * if no NAT is detected. */ peer_cfg->is_mediation(peer_cfg) || -#endif /* P2P */ +#endif /* ME */ /* if peer supports NAT-T, we switch to port 4500 even * if no NAT is detected. MOBIKE requires this. */ (peer_cfg->use_mobike(peer_cfg) && @@ -308,6 +303,12 @@ static status_t build_i(private_ike_natd_t *this, message_t *message) iterator_t *iterator; host_t *host; + if (this->hasher == NULL) + { + DBG1(DBG_IKE, "unable to build NATD payloads, SHA1 not supported"); + return NEED_MORE; + } + /* destination is always set */ host = message->get_destination(message); notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host); @@ -368,6 +369,12 @@ static status_t build_r(private_ike_natd_t *this, message_t *message) if (this->src_seen && this->dst_seen) { + if (this->hasher == NULL) + { + DBG1(DBG_IKE, "unable to build NATD payloads, SHA1 not supported"); + return SUCCESS; + } + /* initiator seems to support NAT detection, add response */ me = message->get_source(message); notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me); @@ -415,7 +422,7 @@ static void migrate(private_ike_natd_t *this, ike_sa_t *ike_sa) */ static void destroy(private_ike_natd_t *this) { - this->hasher->destroy(this->hasher); + DESTROY_IF(this->hasher); free(this); } @@ -443,7 +450,7 @@ ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator) this->ike_sa = ike_sa; this->initiator = initiator; - this->hasher = hasher_create(HASH_SHA1); + this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); this->src_seen = FALSE; this->dst_seen = FALSE; this->src_matched = FALSE; diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h index 8d0cb58b4..d78c931d9 100644 --- a/src/charon/sa/tasks/ike_natd.h +++ b/src/charon/sa/tasks/ike_natd.h @@ -1,10 +1,3 @@ -/** - * @file ike_natd.h - * - * @brief Interface ike_natd_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_natd.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_natd ike_natd + * @{ @ingroup tasks */ #ifndef IKE_NATD_H_ @@ -30,12 +30,7 @@ typedef struct ike_natd_t ike_natd_t; #include <sa/tasks/task.h> /** - * @brief Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange. - * - * @b Constructors: - * - ike_natd_create() - * - * @ingroup tasks + * Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange. */ struct ike_natd_t { @@ -46,7 +41,7 @@ struct ike_natd_t { }; /** - * @brief Create a new ike_natd task. + * Create a new ike_natd task. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE if thask is the original initator @@ -54,4 +49,4 @@ struct ike_natd_t { */ ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_NATD_H_ */ +#endif /* IKE_NATD_H_ @} */ diff --git a/src/charon/sa/tasks/ike_reauth.c b/src/charon/sa/tasks/ike_reauth.c index 0e98382a8..854e9359d 100644 --- a/src/charon/sa/tasks/ike_reauth.c +++ b/src/charon/sa/tasks/ike_reauth.c @@ -1,10 +1,3 @@ -/** - * @file ike_reauth.c - * - * @brief Implementation of the ike_reauth task. - * - */ - /* * Copyright (C) 2006-2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,8 @@ * 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. + * + * $Id: ike_reauth.c 3793 2008-04-11 08:14:48Z martin $ */ #include "ike_reauth.h" @@ -66,22 +61,30 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) host_t *host; iterator_t *iterator; child_sa_t *child_sa; + peer_cfg_t *peer_cfg; /* process delete response first */ this->ike_delete->task.process(&this->ike_delete->task, message); - /* reestablish only if we have children */ + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + + /* reauthenticate only if we have children */ iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa); - if (iterator->get_count(iterator) == 0) + if (iterator->get_count(iterator) == 0 +#ifdef ME + /* we allow a peer to reauth a mediation connection (without CHILD_SA) */ + && !peer_cfg->is_mediation(peer_cfg) +#endif /* ME */ + ) { - DBG1(DBG_IKE, "unable to reestablish IKE_SA, no CHILD_SA to recreate"); + DBG1(DBG_IKE, "unable to reauthenticate IKE_SA, no CHILD_SA to recreate"); iterator->destroy(iterator); return FAILED; } new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, TRUE); - new->set_peer_cfg(new, this->ike_sa->get_peer_cfg(this->ike_sa)); + new->set_peer_cfg(new, peer_cfg); host = this->ike_sa->get_other_host(this->ike_sa); new->set_other_host(new, host->clone(host)); host = this->ike_sa->get_my_host(this->ike_sa); @@ -93,6 +96,20 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) new->set_virtual_ip(new, TRUE, host); } +#ifdef ME + /* we initiate the new IKE_SA of the mediation connection without CHILD_SA */ + if (peer_cfg->is_mediation(peer_cfg)) + { + if (new->initiate(new, NULL) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, new); + DBG1(DBG_IKE, "reauthenticating IKE_SA failed"); + return FAILED; + } + } +#endif /* ME */ + while (iterator->iterate(iterator, (void**)&child_sa)) { switch (child_sa->get_state(child_sa)) @@ -114,7 +131,7 @@ static status_t process_i(private_ike_reauth_t *this, message_t *message) iterator->destroy(iterator); charon->ike_sa_manager->checkin_and_destroy( charon->ike_sa_manager, new); - DBG1(DBG_IKE, "reestablishing IKE_SA failed"); + DBG1(DBG_IKE, "reauthenticating IKE_SA failed"); return FAILED; } break; diff --git a/src/charon/sa/tasks/ike_reauth.h b/src/charon/sa/tasks/ike_reauth.h index 3c872e1e1..1076cc7cc 100644 --- a/src/charon/sa/tasks/ike_reauth.h +++ b/src/charon/sa/tasks/ike_reauth.h @@ -1,10 +1,3 @@ -/** - * @file ike_reauth.h - * - * @brief Interface ike_reauth_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_reauth.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_reauth ike_reauth + * @{ @ingroup tasks */ #ifndef IKE_REAUTH_H_ @@ -30,12 +30,7 @@ typedef struct ike_reauth_t ike_reauth_t; #include <sa/tasks/task.h> /** - * @brief Task of type ike_reauth, reestablishes an IKE_SA. - * - * @b Constructors: - * - ike_reauth_create() - * - * @ingroup tasks + * Task of type ike_reauth, reestablishes an IKE_SA. */ struct ike_reauth_t { @@ -46,7 +41,7 @@ struct ike_reauth_t { }; /** - * @brief Create a new ike_reauth task. + * Create a new ike_reauth task. * * This task is initiator only. * @@ -55,5 +50,4 @@ struct ike_reauth_t { */ ike_reauth_t *ike_reauth_create(ike_sa_t *ike_sa); -#endif /* IKE_REAUTH_H_ */ - +#endif /* IKE_REAUTH_H_ @} */ diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c index 827f95156..9c0d1805c 100644 --- a/src/charon/sa/tasks/ike_rekey.c +++ b/src/charon/sa/tasks/ike_rekey.c @@ -1,10 +1,3 @@ -/** - * @file ike_rekey.c - * - * @brief Implementation of the ike_rekey task. - * - */ - /* * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -19,6 +12,8 @@ * 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. + * + * $Id: ike_rekey.c 3589 2008-03-13 14:14:44Z martin $ */ #include "ike_rekey.h" diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h index 125422efd..1bfde8a54 100644 --- a/src/charon/sa/tasks/ike_rekey.h +++ b/src/charon/sa/tasks/ike_rekey.h @@ -1,10 +1,3 @@ -/** - * @file ike_rekey.h - * - * @brief Interface ike_rekey_t. - * - */ - /* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil @@ -18,6 +11,13 @@ * 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. + * + * $Id: ike_rekey.h 3589 2008-03-13 14:14:44Z martin $ + */ + +/** + * @defgroup ike_rekey ike_rekey + * @{ @ingroup tasks */ #ifndef IKE_REKEY_H_ @@ -30,12 +30,7 @@ typedef struct ike_rekey_t ike_rekey_t; #include <sa/tasks/task.h> /** - * @brief Task of type IKE_REKEY, rekey an established IKE_SA. - * - * @b Constructors: - * - ike_rekey_create() - * - * @ingroup tasks + * Task of type IKE_REKEY, rekey an established IKE_SA. */ struct ike_rekey_t { @@ -45,20 +40,19 @@ struct ike_rekey_t { task_t task; /** - * @brief Register a rekeying task which collides with this one. + * Register a rekeying task which collides with this one. * * If two peers initiate rekeying at the same time, the collision must * be handled gracefully. The task manager is aware of what exchanges * are going on and notifies the outgoing task by passing the incoming. * - * @param this task initated by us * @param other incoming task */ void (*collide)(ike_rekey_t* this, task_t *other); }; /** - * @brief Create a new IKE_REKEY task. + * Create a new IKE_REKEY task. * * @param ike_sa IKE_SA this task works for * @param initiator TRUE for initiator, FALSE for responder @@ -66,4 +60,4 @@ struct ike_rekey_t { */ ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator); -#endif /* IKE_REKEY_H_ */ +#endif /* IKE_REKEY_H_ @} */ diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c index cc20a8861..3192b688a 100644 --- a/src/charon/sa/tasks/task.c +++ b/src/charon/sa/tasks/task.c @@ -1,10 +1,3 @@ -/** - * @file task.c - * - * @brief Enum values for task types - * - */ - /* * Copyright (C) 2007 Tobias Brunner * Copyright (C) 2007 Martin Willi @@ -19,6 +12,8 @@ * 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. + * + * $Id: task.c 3666 2008-03-26 18:40:19Z tobias $ */ #include "task.h" @@ -29,15 +24,16 @@ ENUM(task_type_names, IKE_INIT, CHILD_REKEY, "IKE_MOBIKE", "IKE_AUTHENTICATE", "IKE_AUTH_LIFETIME", - "IKE_CERT", + "IKE_CERT_PRE", + "IKE_CERT_POST", "IKE_CONFIG", "IKE_REKEY", "IKE_REAUTH", "IKE_DELETE", "IKE_DPD", -#ifdef P2P - "IKE_P2P", -#endif /* P2P */ +#ifdef ME + "IKE_ME", +#endif /* ME */ "CHILD_CREATE", "CHILD_DELETE", "CHILD_REKEY", diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h index a59207711..26b4f214e 100644 --- a/src/charon/sa/tasks/task.h +++ b/src/charon/sa/tasks/task.h @@ -1,10 +1,3 @@ -/** - * @file task.h - * - * @brief Interface task_t. - * - */ - /* * Copyright (C) 2007 Tobias Brunner * Copyright (C) 2006 Martin Willi @@ -19,6 +12,13 @@ * 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. + * + * $Id: task.h 3666 2008-03-26 18:40:19Z tobias $ + */ + +/** + * @defgroup task task + * @{ @ingroup tasks */ #ifndef TASK_H_ @@ -32,9 +32,7 @@ typedef struct task_t task_t; #include <encoding/message.h> /** - * @brief Different kinds of tasks. - * - * @ingroup tasks + * Different kinds of tasks. */ enum task_type_t { /** establish an unauthenticated IKE_SA */ @@ -47,8 +45,10 @@ enum task_type_t { IKE_AUTHENTICATE, /** AUTH_LIFETIME negotiation, RFC4478 */ IKE_AUTH_LIFETIME, - /** exchange certificates and requests */ - IKE_CERT, + /** certificate processing before authentication (certreqs, cert parsing) */ + IKE_CERT_PRE, + /** certificate processing after authentication (certs payload generation) */ + IKE_CERT_POST, /** Configuration payloads, virtual IP and such */ IKE_CONFIG, /** rekey an IKE_SA */ @@ -59,10 +59,10 @@ enum task_type_t { IKE_DELETE, /** liveness check */ IKE_DPD, -#ifdef P2P - /** handle P2P-NAT-T stuff */ - IKE_P2P, -#endif /* P2P */ +#ifdef ME + /** handle ME stuff */ + IKE_ME, +#endif /* ME */ /** establish a CHILD_SA within an IKE_SA */ CHILD_CREATE, /** delete an established CHILD_SA */ @@ -77,7 +77,7 @@ enum task_type_t { extern enum_name_t *task_type_names; /** - * @brief Interface for a task, an operation handled within exchanges. + * Interface for a task, an operation handled within exchanges. * * A task is an elemantary operation. It may be handled by a single or by * multiple exchanges. An exchange may even complete multiple tasks. @@ -94,18 +94,12 @@ extern enum_name_t *task_type_names; * the task needs further build()/process() calls to complete, the manager * leaves the taks in the queue. A returned FAILED indicates a critical failure. * The manager closes the IKE_SA whenever a task returns FAILED. - * - * @b Constructors: - * - None, use implementations specific constructors - * - * @ingroup tasks */ struct task_t { /** - * @brief Build a request or response message for this task. + * Build a request or response message for this task. * - * @param this calling object * @param message message to add payloads to * @return * - FAILED if a critical error occured @@ -115,9 +109,8 @@ struct task_t { status_t (*build) (task_t *this, message_t *message); /** - * @brief Process a request or response message for this task. + * Process a request or response message for this task. * - * @param this calling object * @param message message to read payloads from * @return * - FAILED if a critical error occured @@ -127,14 +120,12 @@ struct task_t { status_t (*process) (task_t *this, message_t *message); /** - * @brief Get the type of the task implementation. - * - * @param this calling object + * Get the type of the task implementation. */ task_type_t (*get_type) (task_t *this); /** - * @brief Migrate a task to a new IKE_SA. + * Migrate a task to a new IKE_SA. * * After migrating a task, it goes back to a state where it can be * used again to initate an exchange. This is useful when a task @@ -144,17 +135,14 @@ struct task_t { * try. * The ike_sa is the new IKE_SA this task belongs to and operates on. * - * @param this calling object * @param ike_sa new IKE_SA this task works for */ void (*migrate) (task_t *this, ike_sa_t *ike_sa); /** - * @brief Destroys a task_t object. - * - * @param this calling object + * Destroys a task_t object. */ void (*destroy) (task_t *this); }; -#endif /* TASK_H_ */ +#endif /* TASK_H_ @} */ |