diff options
Diffstat (limited to 'src/charon/sa')
-rw-r--r-- | src/charon/sa/authenticators/authenticator.c | 3 | ||||
-rw-r--r-- | src/charon/sa/authenticators/eap/eap_aka.c | 1399 | ||||
-rw-r--r-- | src/charon/sa/authenticators/eap/eap_aka.h | 133 | ||||
-rw-r--r-- | src/charon/sa/authenticators/eap/eap_method.c | 243 | ||||
-rw-r--r-- | src/charon/sa/authenticators/eap/eap_method.h | 241 | ||||
-rw-r--r-- | src/charon/sa/authenticators/eap_authenticator.c | 348 | ||||
-rw-r--r-- | src/charon/sa/authenticators/eap_authenticator.h | 156 | ||||
-rw-r--r-- | src/charon/sa/authenticators/psk_authenticator.c | 41 | ||||
-rw-r--r-- | src/charon/sa/authenticators/rsa_authenticator.c | 28 | ||||
-rw-r--r-- | src/charon/sa/ike_sa.c | 8 | ||||
-rw-r--r-- | src/charon/sa/transactions/create_child_sa.c | 6 | ||||
-rw-r--r-- | src/charon/sa/transactions/ike_auth.c | 606 |
12 files changed, 3010 insertions, 202 deletions
diff --git a/src/charon/sa/authenticators/authenticator.c b/src/charon/sa/authenticators/authenticator.c index 2460181f9..707aae9ad 100644 --- a/src/charon/sa/authenticators/authenticator.c +++ b/src/charon/sa/authenticators/authenticator.c @@ -26,6 +26,7 @@ #include <sa/authenticators/rsa_authenticator.h> #include <sa/authenticators/psk_authenticator.h> +#include <sa/authenticators/eap_authenticator.h> ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS, @@ -47,6 +48,8 @@ authenticator_t *authenticator_create(ike_sa_t *ike_sa, auth_method_t auth_metho return (authenticator_t*)rsa_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/eap/eap_aka.c b/src/charon/sa/authenticators/eap/eap_aka.c new file mode 100644 index 000000000..0c1bb3379 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_aka.c @@ -0,0 +1,1399 @@ +/** + * @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 "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_shared_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; + } + /* 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; + } +# 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); + /* XOR AK into SQN to decrypt it */ + sqn = chunk_clonea(sqn_ak); + memxor(sqn.ptr, ak.ptr, sqn.len); + + /* 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 XMAC %B", &mac, &xmac); + return NEED_MORE; + } + + 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; + } + + /* 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: + if (attr.len != 2) + { + DBG1(DBG_IKE, "received invalid AKA notification, ignored"); + } + else + { + DBG1(DBG_IKE, "received AKA notification code %d, ignored", + ntohs(*(u_int16_t*)attr.ptr)); + } + 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) +{ + 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*))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 new file mode 100644 index 000000000..cc740f6ad --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_aka.h @@ -0,0 +1,133 @@ +/** + * @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; + + +/** + * @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. + * + * @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_method.c b/src/charon/sa/authenticators/eap/eap_method.c new file mode 100644 index 000000000..435aa9d1f --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -0,0 +1,243 @@ +/** + * @file eap_method.c + * + * @brief Generic constructor for eap_methods. + * + */ + +/* + * 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. + */ + +#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", + "EAP_NAK", + "EAP_MD5", + "EAP_ONE_TIME_PASSWORD", + "EAP_TOKEN_CARD"); +ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_TOKEN_CARD, + "EAP_AKA"); +ENUM_END(eap_type_names, EAP_AKA); + +ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE, + "EAP_REQUEST", + "EAP_RESPONSE", + "EAP_SUCCESS", + "EAP_FAILURE", +); + +ENUM(eap_role_names, EAP_SERVER, EAP_PEER, + "EAP_SERVER", + "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; + 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 for %s", eap_type_names, entry->type); + 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; + struct stat stb; + DIR* dir; + + eap_method_unload(); + modules = linked_list_create(); + + if (stat(directory, &stb) == -1 || !(stb.st_mode & S_IFDIR)) + { + DBG1(DBG_CFG, "error opening EAP modules directory %s", directory); + return; + } + if (stb.st_uid != 0) + { + DBG1(DBG_CFG, "EAP modules directory %s not owned by root, skipped", directory); + return; + } + if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) + { + DBG1(DBG_CFG, "EAP modules directory %s writable by others, skipped", directory); + return; + } + + 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); + + if (stat(file, &stb) == -1 || !(stb.st_mode & S_IFREG)) + { + DBG2(DBG_CFG, " skipping %s, doesn't look like a file", + entry->d_name); + continue; + } + 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; + } + if (stb.st_uid != 0) + { + DBG1(DBG_CFG, " skipping %s, file is not owned by root", entry->d_name); + return; + } + if (stb.st_mode & S_IWOTH || stb.st_mode & S_IWGRP) + { + DBG1(DBG_CFG, " skipping %s, file is writeable by others", 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); + method->destroy(method); + + 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, 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) + { + method = entry->constructor(role, server, peer); + if (method) + { + break; + } + } + } + iterator->destroy(iterator); + + if (method == NULL) + { + 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 new file mode 100644 index 000000000..2b09c0ff2 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_method.h @@ -0,0 +1,241 @@ +/** + * @file eap_method.h + * + * @brief Interface eap_method_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_METHOD_H_ +#define EAP_METHOD_H_ + +typedef struct eap_method_t eap_method_t; +typedef enum eap_role_t eap_role_t; +typedef enum eap_type_t eap_type_t; +typedef enum eap_code_t eap_code_t; + +#include <library.h> +#include <utils/identification.h> +#include <encoding/payloads/eap_payload.h> + +/** + * Role of an eap_method, SERVER or PEER (client) + * + * @ingroup eap + */ +enum eap_role_t { + EAP_SERVER, + EAP_PEER, +}; +/** + * 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, + EAP_NOTIFICATION = 2, + EAP_NAK = 3, + EAP_MD5 = 4, + EAP_ONE_TIME_PASSWORD = 5, + EAP_TOKEN_CARD = 6, + EAP_AKA = 23, +}; + +/** + * 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, + EAP_RESPONSE = 2, + EAP_SUCCESS = 3, + EAP_FAILURE = 4, +}; + +/** + * 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. + * + * An EAP method initiates an EAP exchange and processes requests and + * responses. An EAP method may need multiple exchanges before succeeding, and + * the eap_authentication may use multiple EAP methods to authenticate a peer. + * To accomplish these requirements, all EAP methods have their own + * implementation while the eap_authenticatior uses one or more of these + * EAP methods. Sending of EAP(SUCCESS/FAILURE) message is not the job + * of the method, the eap_authenticator does this. + * An EAP method may establish a MSK, this is used the complete the + * 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() 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 + * - FAILED, if unable to create eap request payload + */ + status_t (*initiate) (eap_method_t *this, eap_payload_t **out); + + /** + * @brief 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 + * - NEED_MORE, if an other exchange is required + * - FAILED, if EAP method failed + * - SUCCESS, if EAP method succeeded + */ + status_t (*process) (eap_method_t *this, eap_payload_t *in, + eap_payload_t **out); + + /** + * @brief Get the EAP type implemented in this method. + * + * @param this calling object + * @return type of the EAP method + */ + eap_type_t (*get_type) (eap_method_t *this); + + /** + * @brief 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. + * + * Not all EAP methods establish a shared secret. + * + * @param this calling object + * @param msk chunk receiving internal stored MSK + * @return + * - SUCCESS, or + * - FAILED, if MSK not established (yet) + */ + status_t (*get_msk) (eap_method_t *this, chunk_t *msk); + + /** + * @brief Destroys a eap_method_t object. + * + * @param this calling 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 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, 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. + * + * 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. + * + * @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, + identification_t *peer); + +#endif /* EAP_METHOD_H_ */ diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c new file mode 100644 index 000000000..8736e05c3 --- /dev/null +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -0,0 +1,348 @@ +/** + * @file eap_authenticator.c + * + * @brief Implementation of eap_authenticator_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. + */ + +#include <string.h> + +#include "eap_authenticator.h" + +#include <daemon.h> +#include <config/policies/policy.h> +#include <sa/authenticators/eap/eap_method.h> + +typedef struct private_eap_authenticator_t private_eap_authenticator_t; + +/** + * Private data of an eap_authenticator_t object. + */ +struct private_eap_authenticator_t { + + /** + * Public authenticator_t interface. + */ + eap_authenticator_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * Role of this authenticator, PEER or SERVER + */ + eap_role_t role; + + /** + * Current EAP method processing + */ + eap_method_t *method; + + /** + * MSK used to build and verify auth payload + */ + chunk_t msk; +}; + +extern chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, + chunk_t secret, identification_t *id, + prf_t *prf); + +/** + * Implementation of authenticator_t.verify. + */ +static status_t verify(private_eap_authenticator_t *this, chunk_t ike_sa_init, + chunk_t my_nonce, auth_payload_t *auth_payload) +{ + chunk_t auth_data, recv_auth_data; + identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa); + prf_t *prf = this->ike_sa->get_auth_verify(this->ike_sa); + + auth_data = build_shared_key_signature(ike_sa_init, my_nonce, this->msk, + other_id, prf); + + recv_auth_data = auth_payload->get_data(auth_payload); + if (!chunk_equals(auth_data, recv_auth_data)) + { + DBG1(DBG_IKE, "verification of AUTH payload created from EAP MSK failed"); + chunk_free(&auth_data); + return FAILED; + } + chunk_free(&auth_data); + + DBG1(DBG_IKE, "authentication of '%D' with %N successful", + other_id, auth_method_names, AUTH_EAP); + return SUCCESS; +} + +/** + * Implementation of authenticator_t.build. + */ +static status_t build(private_eap_authenticator_t *this, chunk_t ike_sa_init, + chunk_t other_nonce, auth_payload_t **auth_payload) +{ + chunk_t auth_data; + identification_t *my_id = this->ike_sa->get_my_id(this->ike_sa); + prf_t *prf = this->ike_sa->get_auth_build(this->ike_sa); + + DBG1(DBG_IKE, "authentication of '%D' (myself) with %N", + my_id, auth_method_names, AUTH_EAP); + + auth_data = build_shared_key_signature(ike_sa_init, other_nonce, + this->msk, my_id, prf); + + *auth_payload = auth_payload_create(); + (*auth_payload)->set_auth_method(*auth_payload, AUTH_PSK); + (*auth_payload)->set_data(*auth_payload, auth_data); + chunk_free(&auth_data); + + return SUCCESS; +} + +/** + * Implementation of eap_authenticator_t.initiate + */ +static status_t initiate(private_eap_authenticator_t *this, eap_type_t type, + eap_payload_t **out) +{ + /* if initiate() is called, role is always server */ + this->role = EAP_SERVER; + + if (type == 0) + { + DBG1(DBG_IKE, + "client requested EAP authentication, but configuration forbids it"); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + + DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type); + this->method = eap_method_create(type, this->role, + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + + if (this->method == NULL) + { + DBG1(DBG_IKE, "configured EAP server method %N not supported, sending %N", + eap_type_names, type, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + if (this->method->initiate(this->method, out) != NEED_MORE) + { + DBG1(DBG_IKE, "failed to initiate %N, sending %N", + eap_type_names, type, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + return NEED_MORE; +} + +/** + * Processing method for a peer + */ +static status_t process_peer(private_eap_authenticator_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + eap_type_t type = in->get_type(in); + + /* create an eap_method for the first call */ + if (this->method == NULL) + { + DBG1(DBG_IKE, "EAP server requested %N authentication", + eap_type_names, type); + this->method = eap_method_create(type, 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 " + "EAP method %N, sending EAP_NAK", eap_type_names, type); + *out = eap_payload_create_nak(); + return NEED_MORE; + } + } + + switch (this->method->process(this->method, in, out)) + { + case NEED_MORE: + return NEED_MORE; + case SUCCESS: + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) + { + DBG1(DBG_IKE, "EAP method %N succeded, MSK established", + eap_type_names, this->method->get_type(this->method)); + this->msk = chunk_clone(this->msk); + return SUCCESS; + } + DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established", + eap_type_names, this->method->get_type(this->method)); + return FAILED; + case FAILED: + default: + DBG1(DBG_IKE, "EAP method %N failed", + eap_type_names, this->method->get_type(this->method)); + return FAILED; + } +} + +/** + * Processing method for a server + */ +static status_t process_server(private_eap_authenticator_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + switch (this->method->process(this->method, in, out)) + { + case NEED_MORE: + return NEED_MORE; + case SUCCESS: + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) + { + DBG1(DBG_IKE, "EAP method %N succeded, MSK established", + eap_type_names, this->method->get_type(this->method)); + this->msk = chunk_clone(this->msk); + *out = eap_payload_create_code(EAP_SUCCESS); + return SUCCESS; + } + DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established", + eap_type_names, this->method->get_type(this->method)); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + case FAILED: + default: + DBG1(DBG_IKE, "EAP method %N failed for peer %D", + eap_type_names, this->method->get_type(this->method), + this->ike_sa->get_other_id(this->ike_sa)); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } +} + +/** + * Implementation of eap_authenticator_t.process + */ +static status_t process(private_eap_authenticator_t *this, eap_payload_t *in, + eap_payload_t **out) +{ + eap_code_t code = in->get_code(in); + + switch (this->role) + { + case EAP_SERVER: + { + switch (code) + { + case EAP_RESPONSE: + { + return process_server(this, in, out); + } + default: + { + DBG1(DBG_IKE, "received %N, sending %N", + eap_code_names, code, eap_code_names, EAP_FAILURE); + *out = eap_payload_create_code(EAP_FAILURE); + return FAILED; + } + } + } + case EAP_PEER: + { + switch (code) + { + case EAP_REQUEST: + { + return process_peer(this, in, out); + } + case EAP_SUCCESS: + { + if (this->method->get_msk(this->method, &this->msk) == SUCCESS) + { + DBG1(DBG_IKE, "EAP method %N succeded, MSK established", + eap_type_names, this->method->get_type(this->method)); + this->msk = chunk_clone(this->msk); + return SUCCESS; + } + DBG1(DBG_IKE, "EAP method %N succeded, but no MSK established", + eap_type_names, this->method->get_type(this->method)); + return FAILED; + } + case EAP_FAILURE: + default: + { + DBG1(DBG_IKE, "received %N, EAP authentication failed", + eap_code_names, code); + return FAILED; + } + } + } + default: + { + return FAILED; + } + } +} + +/** + * Implementation of authenticator_t.is_mutual. + */ +static bool is_mutual(private_eap_authenticator_t *this) +{ + if (this->method) + { + return this->method->is_mutual(this->method); + } + return FALSE; +} + +/** + * Implementation of authenticator_t.destroy. + */ +static void destroy(private_eap_authenticator_t *this) +{ + DESTROY_IF(this->method); + chunk_free(&this->msk); + free(this); +} + +/* + * Described in header. + */ +eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa) +{ + private_eap_authenticator_t *this = malloc_thing(private_eap_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; + + this->public.is_mutual = (bool(*)(eap_authenticator_t*))is_mutual; + this->public.initiate = (status_t(*)(eap_authenticator_t*,eap_type_t,eap_payload_t**))initiate; + this->public.process = (status_t(*)(eap_authenticator_t*,eap_payload_t*,eap_payload_t**))process; + + /* private data */ + this->ike_sa = ike_sa; + this->role = EAP_PEER; + this->method = NULL; + this->msk = chunk_empty; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap_authenticator.h b/src/charon/sa/authenticators/eap_authenticator.h new file mode 100644 index 000000000..ffa162343 --- /dev/null +++ b/src/charon/sa/authenticators/eap_authenticator.h @@ -0,0 +1,156 @@ +/** + * @file eap_authenticator.h + * + * @brief Interface of eap_authenticator_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_AUTHENTICATOR_H_ +#define EAP_AUTHENTICATOR_H_ + +typedef struct eap_authenticator_t eap_authenticator_t; + +#include <sa/authenticators/authenticator.h> +#include <encoding/payloads/eap_payload.h> + +/** + * @brief 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 + * messages. + * EAP authentication must be clearly distinguished between using + * mutual EAP methods and using methods not providing server authentication. + * If no mutual authentication is used, the server must prove it's identity + * by traditional AUTH methods (RSA, psk). Only when the EAP method is mutual, + * the client should accept an EAP-only authentication. + * RFC4306 does always use traditional authentiction, EAP only authentication + * is described in the internet draft draft-eronen-ipsec-ikev2-eap-auth-05.txt. + * + * @verbatim + ike_sa_init + -------------------------> + <------------------------- + followed by multiple ike_auth: + + +--------+ +--------+ + | EAP | ID, SA, TS, N(EAP_ONLY) | EAP | + | client | ---------------------------> | server | + | | ID, [AUTH,] EAP | | AUTH payload is + | | <--------------------------- | | only included if + | | EAP | | authentication + | | ---------------------------> | | is not mutual. + | | EAP | | + | | <--------------------------- | | + | | EAP | | + | | ---------------------------> | | + | | EAP(SUCCESS) | | + | | <--------------------------- | | + | | AUTH | | If EAP establishes + | | ---------------------------> | | a session key, AUTH + | | AUTH, SA, TS | | payloads use this + | | <--------------------------- | | key, not SK_pi/pr + +--------+ +--------+ + + @endverbatim + * @b Constructors: + * - eap_authenticator_create() + * - authenticator_create() using auth_method AUTH_EAP + * + * @ingroup authenticators + */ +struct eap_authenticator_t { + + /** + * Implemented authenticator_t interface. + */ + authenticator_t authenticator_interface; + + /** + * @brief 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 + * provide mutual authentication, which would result in a redundant + * authentication. If the client supports EAP_ONLY_AUTHENTICATION, and + * the the server provides mutual authentication, authentication using + * RSA/PSK may be omitted. If the server did not include a traditional + * 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. + * + * 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 out created initiaal EAP message to send + * @return + * - FAILED, if initiation failed + * - NEED_MORE, if more EAP exchanges reqired + */ + status_t (*initiate) (eap_authenticator_t* this, eap_type_t type, + eap_payload_t **out); + + /** + * @brief Process an EAP message. + * + * After receiving an EAP message "in", the peer/server processes + * the payload and creates a reply/subsequent request. + * The server side always returns NEED_MORE if another EAP message + * is excepted from the client, SUCCESS if EAP exchange completed and + * "out" is EAP_SUCCES, or FAILED if the EAP exchange failed with + * a EAP_FAILURE payload in "out". Anyway, a payload in "out" is always + * created. + * The peer (client) side only creates a "out" payload if result is + * NEED_MORE, a SUCCESS/FAILED is returned whenever a + * EAP_SUCCESS/EAP_FAILURE message is received in "in". + * 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 + * - FAILED, if authentication/EAP exchange failed + * - SUCCESS, if authentication completed + * - NEED_MORE, if more EAP exchanges reqired + */ + status_t (*process) (eap_authenticator_t* this, + eap_payload_t *in, eap_payload_t **out); +}; + +/** + * @brief 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_ */ diff --git a/src/charon/sa/authenticators/psk_authenticator.c b/src/charon/sa/authenticators/psk_authenticator.c index b3f49eff1..28edaaa8e 100644 --- a/src/charon/sa/authenticators/psk_authenticator.c +++ b/src/charon/sa/authenticators/psk_authenticator.c @@ -1,7 +1,7 @@ /** - * @file authenticator.c + * @file psk_authenticator.c * - * @brief Implementation of authenticator_t. + * @brief Implementation of psk_authenticator_t. * */ @@ -54,24 +54,35 @@ struct private_psk_authenticator_t { }; /** - * Function implemented in rsa_authenticator.c + * Builds the octets to be signed as described in section 2.15 of RFC 4306 */ -extern chunk_t build_tbs_octets(private_psk_authenticator_t *this, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, prf_t *prf); +chunk_t build_tbs_octets(chunk_t ike_sa_init, chunk_t nonce, + identification_t *id, prf_t *prf) +{ + u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00}; + chunk_t id_header = chunk_from_buf(id_header_buf); + chunk_t id_with_header, id_prfd, id_encoding; + + id_header_buf[0] = id->get_type(id); + id_encoding = id->get_encoding(id); + + id_with_header = chunk_cat("cc", id_header, id_encoding); + prf->allocate_bytes(prf, id_with_header, &id_prfd); + chunk_free(&id_with_header); + + return chunk_cat("ccm", ike_sa_init, nonce, id_prfd); +} /** * Creates the AUTH data using auth method SHARED_KEY_MESSAGE_INTEGRITY_CODE. */ -static chunk_t build_shared_key_signature(private_psk_authenticator_t *this, - chunk_t ike_sa_init, - chunk_t nonce, - chunk_t secret, - identification_t *id, - prf_t *prf) +chunk_t build_shared_key_signature(chunk_t ike_sa_init, chunk_t nonce, + chunk_t secret, identification_t *id, + prf_t *prf) { chunk_t key_pad, key, auth_data, octets; - octets = build_tbs_octets(this, ike_sa_init, nonce, id, prf); + octets = build_tbs_octets(ike_sa_init, nonce, id, prf); /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */ key_pad.ptr = IKEV2_KEY_PAD; key_pad.len = IKEV2_KEY_PAD_LENGTH; @@ -94,7 +105,7 @@ static chunk_t build_shared_key_signature(private_psk_authenticator_t *this, * 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; @@ -110,7 +121,7 @@ static status_t verify(private_psk_authenticator_t *this, chunk_t ike_sa_init, return status; } - auth_data = build_shared_key_signature(this, ike_sa_init, my_nonce, + auth_data = build_shared_key_signature(ike_sa_init, my_nonce, shared_key, other_id, this->ike_sa->get_auth_verify(this->ike_sa)); chunk_free(&shared_key); @@ -153,7 +164,7 @@ static status_t build(private_psk_authenticator_t *this, chunk_t ike_sa_init, return status; } - auth_data = build_shared_key_signature(this, ike_sa_init, + auth_data = build_shared_key_signature(ike_sa_init, other_nonce, shared_key, my_id, this->ike_sa->get_auth_build(this->ike_sa)); DBG2(DBG_IKE, "successfully created shared key MAC"); diff --git a/src/charon/sa/authenticators/rsa_authenticator.c b/src/charon/sa/authenticators/rsa_authenticator.c index c5b9983a1..dfa01e332 100644 --- a/src/charon/sa/authenticators/rsa_authenticator.c +++ b/src/charon/sa/authenticators/rsa_authenticator.c @@ -1,7 +1,7 @@ /** - * @file authenticator.c + * @file rsa_authenticator.c * - * @brief Implementation of authenticator_t. + * @brief Implementation of rsa_authenticator_t. * */ @@ -48,24 +48,10 @@ struct private_rsa_authenticator_t { }; /** - * Builds the octets to be signed as described in section 2.15 of RFC 4306 + * Function implemented in psk_authenticator.c */ -chunk_t build_tbs_octets(private_rsa_authenticator_t *this, chunk_t ike_sa_init, - chunk_t nonce, identification_t *id, prf_t *prf) -{ - u_int8_t id_header_buf[] = {0x00, 0x00, 0x00, 0x00}; - chunk_t id_header = chunk_from_buf(id_header_buf); - chunk_t id_with_header, id_prfd, id_encoding; - - id_header_buf[0] = id->get_type(id); - id_encoding = id->get_encoding(id); - - id_with_header = chunk_cat("cc", id_header, id_encoding); - prf->allocate_bytes(prf, id_with_header, &id_prfd); - chunk_free(&id_with_header); - - return chunk_cat("ccm", ike_sa_init, nonce, id_prfd); -} +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. @@ -92,7 +78,7 @@ static status_t verify(private_rsa_authenticator_t *this, chunk_t ike_sa_init, DBG1(DBG_IKE, "no RSA public key found for '%D'", other_id); return NOT_FOUND; } - octets = build_tbs_octets(this, ike_sa_init, my_nonce, other_id, + octets = build_tbs_octets(ike_sa_init, my_nonce, other_id, this->ike_sa->get_auth_verify(this->ike_sa)); status = public_key->verify_emsa_pkcs1_signature(public_key, octets, auth_data); chunk_free(&octets); @@ -145,7 +131,7 @@ static status_t build(private_rsa_authenticator_t *this, chunk_t ike_sa_init, } DBG2(DBG_IKE, "matching RSA private key found"); - octets = build_tbs_octets(this, ike_sa_init, other_nonce, my_id, + octets = build_tbs_octets(ike_sa_init, other_nonce, my_id, this->ike_sa->get_auth_build(this->ike_sa)); status = my_key->build_emsa_pkcs1_signature(my_key, HASH_SHA1, octets, &auth_data); chunk_free(&octets); diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 62f18b5b1..e5a77edad 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -454,8 +454,7 @@ static void dpd_detected(private_ike_sa_t *this) policy = charon->policies->get_policy(charon->policies, this->my_id, this->other_id, my_ts, other_ts, - this->my_host, this->other_host, - NULL); + this->my_host, this->other_host); if (policy == NULL) { DBG1(DBG_IKE, "no policy for CHILD to handle DPD"); @@ -1021,8 +1020,7 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) policy = charon->policies->get_policy(charon->policies, this->my_id, this->other_id, my_ts, other_ts, - this->my_host, this->other_host, - NULL); + this->my_host, this->other_host); if (policy == NULL) { SIG(CHILD_UP_START, "acquiring CHILD_SA with reqid %d", reqid); @@ -1878,7 +1876,7 @@ static status_t reauth(private_ike_sa_t *this) other_ts = child_sa->get_other_traffic_selectors(child_sa); policy = charon->policies->get_policy(charon->policies, this->my_id, this->other_id, my_ts, other_ts, - this->my_host, this->other_host, NULL); + this->my_host, this->other_host); if (policy == NULL) { DBG1(DBG_IKE, "policy not found to recreate CHILD_SA, skipped"); diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c index 1d5142b3b..67e4d782b 100644 --- a/src/charon/sa/transactions/create_child_sa.c +++ b/src/charon/sa/transactions/create_child_sa.c @@ -291,8 +291,7 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result) this->policy = charon->policies->get_policy(charon->policies, my_id, other_id, my_ts, other_ts, - me, other, - NULL); + me, other); this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); @@ -694,8 +693,7 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request this->policy = charon->policies->get_policy(charon->policies, my_id, other_id, my_ts, other_ts, - me, other, - NULL); + me, other); if (this->policy) { this->tsr = this->policy->select_my_traffic_selectors(this->policy, my_ts, me); diff --git a/src/charon/sa/transactions/ike_auth.c b/src/charon/sa/transactions/ike_auth.c index 017372b7e..bf7fd6d12 100644 --- a/src/charon/sa/transactions/ike_auth.c +++ b/src/charon/sa/transactions/ike_auth.c @@ -34,6 +34,7 @@ #include <encoding/payloads/auth_payload.h> #include <encoding/payloads/ts_payload.h> #include <sa/authenticators/authenticator.h> +#include <sa/authenticators/eap_authenticator.h> #include <sa/child_sa.h> @@ -130,6 +131,31 @@ struct private_ike_auth_t { u_int32_t reqid; /** + * List of CA certificates the other peer trusts + */ + linked_list_t *cacerts; + + /** + * EAP uses this authentication, which is passed along multiple ike_auths + */ + eap_authenticator_t *eap_auth; + + /** + * if the client receives a EAP request, it is stored here for later use + */ + eap_payload_t *eap_next; + + /** + * set to TRUE if authentication should be done with EAP only + */ + bool eap_only; + + /** + * has the other peer been authenticated yet? + */ + bool peer_authenticated; + + /** * mode the CHILD_SA uses: tranport, tunnel, BEET */ mode_t mode; @@ -245,6 +271,57 @@ static status_t get_request(private_ike_auth_t *this, message_t **result) /* store for retransmission */ this->message = request; + this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); + request->set_message_id(request, this->message_id); + + if (this->eap_auth) + { + /* we already sent ID, SA, TS in an earlier ike_auth, we now + * continiue EAP processing */ + if (this->eap_next) + { + /* if we have another outstanding EAP response, send it */ + request->add_payload(request, (payload_t*)this->eap_next); + this->eap_next = NULL; + return SUCCESS; + } + else + { + /* if not, we have received an EAP_SUCCESS, send AUTH payload. + * we only send our data if: + * a) The peer has been authenticated using RSA/PSK, or + * b) The EAP method is mutual and gives us enough security + */ + if (this->eap_auth->is_mutual(this->eap_auth) || + this->peer_authenticated) + { + auth_payload_t *auth_payload; + authenticator_t *authenticator = (authenticator_t*)this->eap_auth; + + if (authenticator->build(authenticator, this->init_request, + this->nonce_r, &auth_payload) != SUCCESS) + { + SIG(IKE_UP_FAILED, + "EAP authentication data generation failed, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, + "initiating CHILD_SA failed, unable to create IKE_SA"); + return DESTROY_ME; + } + request->add_payload(request, (payload_t*)auth_payload); + return SUCCESS; + } + else + { + SIG(IKE_UP_FAILED, + "peer didn't send authentication data, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, + "initiating CHILD_SA failed, unable to create IKE_SA"); + return DESTROY_ME; + } + } + } + /* otherwise we do a normal ike_auth request... */ + { /* build ID payload */ my_id_payload = id_payload_create_from_identification(TRUE, my_id); request->add_payload(request, (payload_t*)my_id_payload); @@ -274,8 +351,8 @@ static status_t get_request(private_ike_auth_t *this, message_t **result) } /* build certificate payload. TODO: Handle certreq from init_ike_sa. */ - if (this->policy->get_auth_method(this->policy) == AUTH_RSA - && this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) + if (this->policy->get_auth_method(this->policy) == AUTH_RSA && + this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) { cert_payload_t *cert_payload; @@ -301,6 +378,7 @@ static status_t get_request(private_ike_auth_t *this, message_t **result) } } + if (this->policy->get_auth_method(this->policy) != AUTH_EAP) { /* build auth payload */ authenticator_t *authenticator; auth_payload_t *auth_payload; @@ -327,6 +405,12 @@ static status_t get_request(private_ike_auth_t *this, message_t **result) } request->add_payload(request, (payload_t*)auth_payload); } + else + { + this->eap_auth = eap_authenticator_create(this->ike_sa); + /* include notify that we support EAP only authentication */ + build_notify(EAP_ONLY_AUTHENTICATION, request, FALSE); + } { /* build SA payload for CHILD_SA */ linked_list_t *proposal_list; @@ -399,8 +483,6 @@ static status_t get_request(private_ike_auth_t *this, message_t **result) request->add_payload(request, (payload_t*)ts_payload); } - this->message_id = this->ike_sa->get_next_message_id(this->ike_sa); - request->set_message_id(request, this->message_id); return SUCCESS; } @@ -434,6 +516,12 @@ static status_t process_notifies(private_ike_auth_t *this, notify_payload_t *not this->build_child = FALSE; return SUCCESS; } + case EAP_ONLY_AUTHENTICATION: + { + DBG1(DBG_IKE, "peer requested EAP_ONLY_AUTHENTICATION"); + this->eap_only = TRUE; + return SUCCESS; + } case USE_TRANSPORT_MODE: { this->mode = MODE_TRANSPORT; @@ -465,38 +553,37 @@ static status_t process_notifies(private_ike_auth_t *this, notify_payload_t *not /** * Import certificate requests from a certreq payload */ -static void add_certificate_request(certreq_payload_t *certreq_payload, - linked_list_t *requested_ca_keyids) +static void process_certificate_request(private_ike_auth_t *this, + certreq_payload_t *certreq_payload) { chunk_t keyids; - cert_encoding_t encoding = certreq_payload->get_cert_encoding(certreq_payload); - + if (encoding != CERT_X509_SIGNATURE) { DBG1(DBG_IKE, "certreq payload %N not supported, ignored", cert_encoding_names, encoding); return; } - + keyids = certreq_payload->get_data(certreq_payload); - while (keyids.len >= HASH_SIZE_SHA1) { chunk_t keyid = { keyids.ptr, HASH_SIZE_SHA1}; - x509_t *cacert = charon->credentials->get_ca_certificate_by_keyid(charon->credentials, keyid); - + x509_t *cacert = charon->credentials->get_ca_certificate_by_keyid( + charon->credentials, keyid); if (cacert) { - DBG2(DBG_IKE, "request for certificate issued by ca '%D'", cacert->get_subject(cacert)); - requested_ca_keyids->insert_last(requested_ca_keyids, (void *)&keyid); + DBG2(DBG_IKE, "request for certificate issued by ca '%D'", + cacert->get_subject(cacert)); + this->cacerts->insert_last(this->cacerts, cacert); } else { DBG2(DBG_IKE, "request for certificate issued by unknown ca"); } DBG2(DBG_IKE, " with keyid %#B", &keyid); - + keyids.ptr += HASH_SIZE_SHA1; keyids.len -= HASH_SIZE_SHA1; } @@ -615,6 +702,113 @@ static status_t install_child_sa(private_ike_auth_t *this, bool initiator) } /** + * create a CHILD SA, install it, and build response message + */ +static void setup_child_sa(private_ike_auth_t *this, message_t *response) +{ + bool use_natt; + u_int32_t soft_lifetime, hard_lifetime; + host_t *me, *other; + identification_t *my_id, *other_id; + + me = this->ike_sa->get_my_host(this->ike_sa); + other = this->ike_sa->get_other_host(this->ike_sa); + my_id = this->ike_sa->get_my_id(this->ike_sa); + other_id = this->ike_sa->get_other_id(this->ike_sa); + + soft_lifetime = this->policy->get_soft_lifetime(this->policy); + hard_lifetime = this->policy->get_hard_lifetime(this->policy); + use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); + this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id, + soft_lifetime, hard_lifetime, + this->policy->get_updown(this->policy), + this->policy->get_hostaccess(this->policy), + use_natt); + this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); + /* check mode, and include notify into reply */ + switch (this->mode) + { + case MODE_TUNNEL: + /* is the default */ + break; + case MODE_TRANSPORT: + if (!ts_list_is_host(this->tsi, other) || + !ts_list_is_host(this->tsr, me)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); + } + else if (this->ike_sa->is_natt_enabled(this->ike_sa)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); + } + else + { + build_notify(USE_TRANSPORT_MODE, response, FALSE); + } + break; + case MODE_BEET: + if (!ts_list_is_host(this->tsi, NULL) || + !ts_list_is_host(this->tsr, NULL)) + { + this->mode = MODE_TUNNEL; + DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); + } + else + { + build_notify(USE_BEET_MODE, response, FALSE); + } + break; + } + + if (install_child_sa(this, FALSE) != SUCCESS) + { + SIG(CHILD_UP_FAILED, "installing CHILD_SA %s failed, no CHILD_SA created", + this->policy->get_name(this->policy)); + DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response"); + build_notify(NO_PROPOSAL_CHOSEN, response, FALSE); + } + else + { + /* build SA and TS payloads */ + SIG(CHILD_UP_SUCCESS, "CHILD_SA created"); + ts_payload_t *ts_response; + sa_payload_t *sa_response = sa_payload_create(); + sa_response->add_proposal(sa_response, this->proposal); + response->add_payload(response, (payload_t*)sa_response); + ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); + response->add_payload(response, (payload_t*)ts_response); + ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); + response->add_payload(response, (payload_t*)ts_response); + } +} + +/** + * clone the transaction to requeue it for EAP handling + */ +static private_ike_auth_t *clone_for_eap(private_ike_auth_t *this) +{ + private_ike_auth_t *clone; + clone = (private_ike_auth_t*)ike_auth_create(this->ike_sa); + + clone->tsi = this->tsi; this->tsi = NULL; + clone->tsr = this->tsr; this->tsr = NULL; + clone->eap_auth = this->eap_auth; this->eap_auth = NULL; + clone->eap_next = this->eap_next; this->eap_next = NULL; + clone->build_child = this->build_child; + clone->nonce_i = this->nonce_i; this->nonce_i = chunk_empty; + clone->nonce_r = this->nonce_r; this->nonce_r = chunk_empty; + clone->child_sa = this->child_sa; this->child_sa = NULL; + clone->proposal = this->proposal; this->proposal = NULL; + clone->peer_authenticated = this->peer_authenticated; + clone->connection = this->connection; this->connection = NULL; + clone->policy = this->policy; this->policy = NULL; + + return clone; +} + +/** * Implementation of transaction_t.get_response. */ static status_t get_response(private_ike_auth_t *this, message_t *request, @@ -622,7 +816,6 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, { host_t *me, *other; identification_t *my_id, *other_id; - linked_list_t *requested_ca_keyids; message_t *response; status_t status; iterator_t *payloads; @@ -635,6 +828,7 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, sa_payload_t *sa_request = NULL; ts_payload_t *tsi_request = NULL; ts_payload_t *tsr_request = NULL; + eap_payload_t *eap_request = NULL; id_payload_t *idr_response; /* check if we already have built a response (retransmission) */ @@ -648,6 +842,8 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); + my_id = this->ike_sa->get_my_id(this->ike_sa); + other_id = this->ike_sa->get_other_id(this->ike_sa); this->message_id = request->get_message_id(request); /* set up response */ @@ -668,9 +864,6 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); return DESTROY_ME; } - - /* initialize list of requested ca keyids */ - requested_ca_keyids = linked_list_create(); /* Iterate over all payloads. */ payloads = request->get_payload_iterator(request); @@ -689,7 +882,7 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, break; case CERTIFICATE_REQUEST: certreq_request = (certreq_payload_t*)payload; - add_certificate_request(certreq_request, requested_ca_keyids); + process_certificate_request(this, certreq_request); break; case CERTIFICATE: cert_request = (cert_payload_t*)payload; @@ -703,20 +896,21 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, case TRAFFIC_SELECTOR_RESPONDER: tsr_request = (ts_payload_t*)payload; break; + case EXTENSIBLE_AUTHENTICATION: + eap_request = (eap_payload_t*)payload; + break; case NOTIFY: { status = process_notifies(this, (notify_payload_t*)payload); if (status == FAILED) { payloads->destroy(payloads); - requested_ca_keyids->destroy(requested_ca_keyids); /* we return SUCCESS, returned FAILED means do next transaction */ return SUCCESS; } if (status == DESTROY_ME) { payloads->destroy(payloads); - requested_ca_keyids->destroy(requested_ca_keyids); SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); return DESTROY_ME; } @@ -732,13 +926,67 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, } payloads->destroy(payloads); - /* check if we have all payloads */ - if (!(idi_request && auth_request && sa_request && tsi_request && tsr_request)) + /* if message contains an EAP payload, we process it */ + if (eap_request && this->eap_auth) + { + eap_payload_t *eap_response; + private_ike_auth_t *next_auth; + + status = this->eap_auth->process(this->eap_auth, eap_request, &eap_response); + response->add_payload(response, (payload_t*)eap_response); + + if (status == FAILED) + { + /* shut down if EAP message is EAP_FAILURE */ + return DESTROY_ME; + } + + next_auth = clone_for_eap(this); + next_auth->message_id = this->message_id + 1; + *next = (transaction_t*)next_auth; + return SUCCESS; + } + + /* if we do EAP authentication and a AUTH payload comes in, verify it */ + if (auth_request && this->eap_auth) + { + auth_payload_t *auth_response; + authenticator_t *authenticator = (authenticator_t*)this->eap_auth; + + if (authenticator->verify(authenticator, this->init_request, + this->nonce_r, auth_request) != SUCCESS) + { + SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); + build_notify(AUTHENTICATION_FAILED, response, TRUE); + return DESTROY_ME; + } + + if (authenticator->build(authenticator, this->init_response, + this->nonce_i, &auth_response) != SUCCESS) + { + SIG(IKE_UP_FAILED, "EAP authentication data generation failed, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); + build_notify(AUTHENTICATION_FAILED, response, TRUE); + return DESTROY_ME; + } + response->add_payload(response, (payload_t*)auth_response); + + SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]", + this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + + setup_child_sa(this, response); + + return SUCCESS; + } + + /* check if we have all payloads (AUTH is not checked, not required with EAP) */ + if (!(idi_request && sa_request && tsi_request && tsr_request)) { build_notify(INVALID_SYNTAX, response, TRUE); SIG(IKE_UP_FAILED, "request message incomplete, deleting IKE_SA"); SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - requested_ca_keyids->destroy(requested_ca_keyids); return DESTROY_ME; } @@ -754,7 +1002,6 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, } } - { /* get a policy and process traffic selectors */ linked_list_t *my_ts, *other_ts; @@ -764,9 +1011,7 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, this->policy = charon->policies->get_policy(charon->policies, my_id, other_id, my_ts, other_ts, - me, other, - requested_ca_keyids); - requested_ca_keyids->destroy(requested_ca_keyids); + me, other); if (this->policy) { @@ -799,8 +1044,8 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, response->add_payload(response, (payload_t*)idr_response); } - if (this->policy->get_auth_method(this->policy) == AUTH_RSA - && this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) + if (this->policy->get_auth_method(this->policy) == AUTH_RSA && + this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND) { /* build certificate payload */ x509_t *cert; cert_payload_t *cert_payload; @@ -822,33 +1067,80 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, import_certificate(cert_request); } - { /* process auth payload */ - authenticator_t *authenticator; + { /* process SA payload */ + linked_list_t *proposal_list; + + /* get proposals from request, and select one with ours */ + proposal_list = sa_request->get_proposals(sa_request); + DBG2(DBG_IKE, "selecting proposals:"); + this->proposal = this->policy->select_proposal(this->policy, proposal_list); + proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); + + /* do we have a proposal? */ + if (this->proposal == NULL) + { + SIG(CHILD_UP_FAILED, "CHILD_SA proposals unacceptable, no CHILD_SA created"); + DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response"); + build_notify(NO_PROPOSAL_CHOSEN, response, FALSE); + this->build_child = FALSE; + } + /* do we have traffic selectors? */ + else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0) + { + SIG(CHILD_UP_FAILED, "CHILD_SA traffic selectors unacceptable, no CHILD_SA created"); + DBG1(DBG_IKE, "adding TS_UNACCEPTABLE notify to response"); + build_notify(TS_UNACCEPTABLE, response, FALSE); + this->build_child = FALSE; + } + } + + if (!this->eap_only || this->policy->get_auth_method(this->policy) != AUTH_EAP) + { /* build response AUTH payload when not using a mutual EAP authentication */ auth_payload_t *auth_response; + authenticator_t *authenticator; auth_method_t auth_method; status_t status; - auth_method = auth_request->get_auth_method(auth_request); + auth_method = this->policy->get_auth_method(this->policy); + if (auth_method == AUTH_EAP) + { + SIG(IKE_UP_FAILED, + "peer does not support EAP only authentication, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, + "initiating CHILD_SA failed, unable to create IKE_SA"); + build_notify(AUTHENTICATION_FAILED, response, TRUE); + return DESTROY_ME; + } + authenticator = authenticator_create(this->ike_sa, auth_method); if (authenticator == NULL) { SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA", auth_method_names, auth_method); SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); + build_notify(AUTHENTICATION_FAILED, response, TRUE); return DESTROY_ME; } - status = authenticator->verify(authenticator, this->init_request, - this->nonce_r, auth_request); + status = authenticator->build(authenticator, this->init_response, + this->nonce_i, &auth_response); authenticator->destroy(authenticator); if (status != SUCCESS) { - SIG(IKE_UP_FAILED, "authentication failed, deleting IKE_SA"); + SIG(IKE_UP_FAILED, "authentication data generation failed, deleting IKE_SA"); SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); build_notify(AUTHENTICATION_FAILED, response, TRUE); return DESTROY_ME; } + response->add_payload(response, (payload_t*)auth_response); + } + + if (auth_request) + { /* process auth payload, if not using EAP */ + authenticator_t *authenticator; + auth_method_t auth_method; + status_t status; - auth_method = this->policy->get_auth_method(this->policy); + auth_method = auth_request->get_auth_method(auth_request); authenticator = authenticator_create(this->ike_sa, auth_method); if (authenticator == NULL) { @@ -857,136 +1149,62 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); return DESTROY_ME; } - status = authenticator->build(authenticator, this->init_response, - this->nonce_i, &auth_response); + status = authenticator->verify(authenticator, this->init_request, + this->nonce_r, auth_request); authenticator->destroy(authenticator); if (status != SUCCESS) { - SIG(IKE_UP_FAILED, "authentication data generation failed, deleting IKE_SA"); + SIG(IKE_UP_FAILED, "authentication failed, deleting IKE_SA"); SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); build_notify(AUTHENTICATION_FAILED, response, TRUE); return DESTROY_ME; } - response->add_payload(response, (payload_t*)auth_response); - } - - SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]", - this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id); - - { /* process SA payload */ - linked_list_t *proposal_list; - sa_payload_t *sa_response; - ts_payload_t *ts_response; - bool use_natt; - u_int32_t soft_lifetime, hard_lifetime; - /* prepare reply */ - sa_response = sa_payload_create(); + SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]", + this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - /* get proposals from request, and select one with ours */ - proposal_list = sa_request->get_proposals(sa_request); - DBG2(DBG_IKE, "selecting proposals:"); - this->proposal = this->policy->select_proposal(this->policy, proposal_list); - proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy)); - - /* do we have a proposal? */ - if (this->proposal == NULL) + /* set up CHILD_SA if negotiation succeded */ + if (this->build_child) { - SIG(CHILD_UP_FAILED, "CHILD_SA proposals unacceptable, no CHILD_SA created"); - DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response"); - build_notify(NO_PROPOSAL_CHOSEN, response, FALSE); + setup_child_sa(this, response); } - /* do we have traffic selectors? */ - else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0) - { - SIG(CHILD_UP_FAILED, "CHILD_SA traffic selectors unacceptable, no CHILD_SA created"); - DBG1(DBG_IKE, "adding TS_UNACCEPTABLE notify to response"); - build_notify(TS_UNACCEPTABLE, response, FALSE); - } - else + + this->peer_authenticated = TRUE; + } + else + { + /* if no AUTH payload was included, we start with an EAP exchange. + * eap_response is a request in the EAP meaning, but is + * contained in a IKEv2 response */ + eap_payload_t *eap_response; + private_ike_auth_t *next_auth; + eap_type_t eap_type; + + eap_type = this->policy->get_eap_type(this->policy); + this->eap_auth = eap_authenticator_create(this->ike_sa); + status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_response); + response->add_payload(response, (payload_t*)eap_response); + + if (status == FAILED) { - /* create child sa */ - soft_lifetime = this->policy->get_soft_lifetime(this->policy); - hard_lifetime = this->policy->get_hard_lifetime(this->policy); - use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id, - soft_lifetime, hard_lifetime, - this->policy->get_updown(this->policy), - this->policy->get_hostaccess(this->policy), - use_natt); - this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); - - /* check mode, and include notify into reply */ - switch (this->mode) - { - case MODE_TUNNEL: - /* is the default */ - break; - case MODE_TRANSPORT: - if (!ts_list_is_host(this->tsi, other) || - !ts_list_is_host(this->tsr, me)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, not host-to-host"); - } - else if (this->ike_sa->is_natt_enabled(this->ike_sa)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using tranport mode, as connection NATed"); - } - else - { - build_notify(USE_TRANSPORT_MODE, response, FALSE); - } - break; - case MODE_BEET: - if (!ts_list_is_host(this->tsi, NULL) || - !ts_list_is_host(this->tsr, NULL)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); - } - else - { - build_notify(USE_BEET_MODE, response, FALSE); - } - break; - } - - if (install_child_sa(this, FALSE) != SUCCESS) - { - SIG(CHILD_UP_FAILED, "installing CHILD_SA '%s' failed, no CHILD_SA created", - this->policy->get_name(this->policy)); - DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response"); - build_notify(NO_PROPOSAL_CHOSEN, response, FALSE); - } - else - { - /* add proposal to sa payload */ - sa_response->add_proposal(sa_response, this->proposal); - SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created", - this->policy->get_name(this->policy)); - } + /* EAP initiaton failed, we send the EAP_FAILURE message and quit */ + return DESTROY_ME; } - response->add_payload(response, (payload_t*)sa_response); - - /* add ts payload after sa payload */ - ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi); - response->add_payload(response, (payload_t*)ts_response); - ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr); - response->add_payload(response, (payload_t*)ts_response); + /* we send an EAP request. to handle the reply, we reschedule + * this transaction, as it knows how to handle the reply */ + next_auth = clone_for_eap(this); + next_auth->message_id = this->message_id + 1; + *next = (transaction_t*)next_auth; } - /* set established state */ - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); return SUCCESS; } - /** * Implementation of transaction_t.conclude */ static status_t conclude(private_ike_auth_t *this, message_t *response, - transaction_t **transaction) + transaction_t **next) { iterator_t *payloads; payload_t *payload; @@ -998,6 +1216,7 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, cert_payload_t *cert_payload = NULL; auth_payload_t *auth_payload = NULL; sa_payload_t *sa_payload = NULL; + eap_payload_t *eap_payload = NULL; status_t status; /* check message type */ @@ -1010,6 +1229,8 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); + my_id = this->ike_sa->get_my_id(this->ike_sa); + other_id = this->ike_sa->get_other_id(this->ike_sa); /* Iterate over all payloads to collect them */ payloads = response->get_payload_iterator(response); @@ -1035,6 +1256,9 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, case TRAFFIC_SELECTOR_RESPONDER: tsr_payload = (ts_payload_t*)payload; break; + case EXTENSIBLE_AUTHENTICATION: + eap_payload = (eap_payload_t*)payload; + break; case NOTIFY: { status = process_notifies(this, (notify_payload_t*)payload); @@ -1062,13 +1286,7 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, } payloads->destroy(payloads); - if (!(idr_payload && auth_payload && sa_payload && tsi_payload && tsr_payload)) - { - SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA"); - SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); - return DESTROY_ME; - } - + if (idr_payload) { /* process idr payload */ identification_t *configured_other_id; int wildcards; @@ -1080,7 +1298,7 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, { other_id->destroy(other_id); SIG(IKE_UP_FAILED, "other peer uses unacceptable ID (%D, excepted " - "%D), deleting IKE_SA", other_id, configured_other_id); + "%D), deleting IKE_SA", other_id, configured_other_id); SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); return DESTROY_ME; } @@ -1093,6 +1311,7 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, import_certificate(cert_payload); } + if (auth_payload && idr_payload) { /* authenticate peer */ authenticator_t *authenticator; auth_method_t auth_method; @@ -1118,10 +1337,72 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); return DESTROY_ME; } + this->peer_authenticated = TRUE; + } + + if (eap_payload && this->eap_auth) + { + switch (this->eap_auth->process(this->eap_auth, eap_payload, &this->eap_next)) + { + case SUCCESS: + { + /* EAP message was EAP_SUCCESS, send AUTH in next transaction */ + DBG2(DBG_IKE, "EAP authentication exchanges completed successful"); + this->eap_next = NULL; + /* fall through */ + } + case NEED_MORE: + { + /* EAP message was a EAP_REQUEST, handle it in next transaction */ + private_ike_auth_t *next_auth = clone_for_eap(this); + next_auth->message_id = this->message_id + 1; + *next = (transaction_t*)next_auth; + return SUCCESS; + } + case FAILED: + default: + { + /* EAP message was EAP_FAILURE */ + SIG(IKE_UP_FAILED, "EAP authentication failed, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); + return DESTROY_ME; + } + } + } + + if (!(auth_payload && sa_payload && tsi_payload && tsr_payload)) + { + SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); + return DESTROY_ME; + } + + /* if we do EAP authentication and a AUTH payload comes in, verify it */ + if (this->eap_auth && + (this->eap_auth->is_mutual(this->eap_auth) || this->peer_authenticated)) + { + authenticator_t *authenticator = (authenticator_t*)this->eap_auth; + + if (authenticator->verify(authenticator, this->init_response, + this->nonce_i, auth_payload) != SUCCESS) + { + SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); + return DESTROY_ME; + } + this->peer_authenticated = TRUE; + } + + if (!this->peer_authenticated) + { + SIG(IKE_UP_FAILED, "server didn't send authentication data, deleting IKE_SA"); + SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA"); + return DESTROY_ME; } SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]", this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); { /* process traffic selectors for us */ linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload); @@ -1198,8 +1479,6 @@ static status_t conclude(private_ike_auth_t *this, message_t *response, } } } - /* set new state */ - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); return SUCCESS; } @@ -1213,6 +1492,12 @@ static void destroy(private_ike_auth_t *this) DESTROY_IF(this->child_sa); DESTROY_IF(this->policy); DESTROY_IF(this->connection); + DESTROY_IF(this->cacerts); + if (this->eap_auth) + { + this->eap_auth->authenticator_interface.destroy(&this->eap_auth->authenticator_interface); + } + DESTROY_IF(this->eap_next); if (this->tsi) { this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy)); @@ -1260,10 +1545,17 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa) this->init_response = chunk_empty; this->child_sa = NULL; this->proposal = NULL; + this->policy = NULL; + this->connection = NULL; this->tsi = NULL; this->tsr = NULL; this->build_child = TRUE; + this->eap_auth = NULL; + this->eap_next = NULL; + this->eap_only = FALSE; + this->peer_authenticated = FALSE; this->reqid = 0; + this->cacerts = linked_list_create(); this->mode = MODE_TUNNEL; return &this->public; |