diff options
Diffstat (limited to 'src/charon/sa')
22 files changed, 2631 insertions, 212 deletions
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..8fb1f85cd --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_aka.c @@ -0,0 +1,1440 @@ +/** + * @file eap_aka.c + * + * @brief Implementation of eap_aka_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + + +/* The EAP-AKA method uses it's own simple parser for processing EAP-AKA + * payloads, as the IKEv2 parser is not suitable for that job. There are + * two simple methods for parsing payloads, read_header() and read_attribute(). + * Every EAP-AKA payload consists of a header and a list of attributes. Those + * functions mentioned read the data and return the type of the found + * attribute/EAP-AKA-type. For generating a EAP-AKA message, we have a + * build_aka_payload(), which builds the whole message from a variable + * argument list containing its attributes. + * The processing of messages is split up in various functions: + * - peer_process() - General processing multiplexer for the peer + * - peer_process_challenge() - Specific AKA-Challenge processor + * - peer_process_notification() - Processing of AKA-Notification + * - server_process() - General processing multiplexer for the server + * - peer_process_challenge() - Processing of a received Challenge response + * - peer_process_synchronize() - Process a sequence number synchronization + * - server_initiate() - Initiation method for the server, calls + * - server_initiate_challenge() - Initiation of AKA-Challenge + */ + +#include <string.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> + +#include "eap_aka.h" + +#include <daemon.h> +#include <library.h> +#include <utils/randomizer.h> +#include <crypto/hashers/hasher.h> +#include <crypto/prfs/fips_prf.h> + +/* Use test vectors specified in S.S0055 +#define TEST_VECTORS */ + +#define RAND_LENGTH 16 +#define RES_LENGTH 16 +#define SQN_LENGTH 6 +#define K_LENGTH 16 +#define MAC_LENGTH 8 +#define CK_LENGTH 16 +#define IK_LENGTH 16 +#define AK_LENGTH 6 +#define AMF_LENGTH 2 +#define FMK_LENGTH 4 +#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH) +#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH) +#define PAYLOAD_LENGTH 64 +#define MK_LENGTH 20 +#define MSK_LENGTH 64 +#define EMSK_LENGTH 64 +#define KAUTH_LENGTH 16 +#define KENCR_LENGTH 16 +#define AT_MAC_LENGTH 16 + +#define F1 0x42 +#define F1STAR 0x43 +#define F2 0x44 +#define F3 0x45 +#define F4 0x46 +#define F5 0x47 +#define F5STAR 0x48 + +ENUM_BEGIN(aka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY, + "AKA_CHALLENGE", + "AKA_AUTHENTICATION_REJECT", + "AKA_3", + "AKA_SYNCHRONIZATION_FAILURE", + "AKA_IDENTITY"); +ENUM_NEXT(aka_subtype_names, AKA_NOTIFICATION, AKA_CLIENT_ERROR, AKA_IDENTITY, + "AKA_NOTIFICATION", + "AKA_REAUTHENTICATION", + "AKA_CLIENT_ERROR"); +ENUM_END(aka_subtype_names, AKA_CLIENT_ERROR); + + +ENUM_BEGIN(aka_attribute_names, AT_END, AT_CLIENT_ERROR_CODE, + "AT_END", + "AT_0", + "AT_RAND", + "AT_AUTN", + "AT_RES", + "AT_AUTS", + "AT_5", + "AT_PADDING", + "AT_NONCE_MT", + "AT_8", + "AT_9", + "AT_PERMANENT_ID_REQ", + "AT_MAC", + "AT_NOTIFICATION", + "AT_ANY_ID_REQ", + "AT_IDENTITY", + "AT_VERSION_LIST", + "AT_SELECTED_VERSION", + "AT_FULLAUTH_ID_REQ", + "AT_18", + "AT_COUNTER", + "AT_COUNTER_TOO_SMALL", + "AT_NONCE_S", + "AT_CLIENT_ERROR_CODE"); +ENUM_NEXT(aka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE, + "AT_IV", + "AT_ENCR_DATA", + "AT_131", + "AT_NEXT_PSEUDONYM", + "AT_NEXT_REAUTH_ID", + "AT_CHECKCODE", + "AT_RESULT_IND"); +ENUM_END(aka_attribute_names, AT_RESULT_IND); + + +typedef struct private_eap_aka_t private_eap_aka_t; + +/** + * Private data of an eap_aka_t object. + */ +struct private_eap_aka_t { + + /** + * Public authenticator_t interface. + */ + eap_aka_t public; + + /** + * ID of the server + */ + identification_t *server; + + /** + * ID of the peer + */ + identification_t *peer; + + /** + * Key for EAP MAC + */ + chunk_t k_auth; + + /** + * Key for EAP encryption + */ + chunk_t k_encr; + + /** + * MSK + */ + chunk_t msk; + + /** + * Extendend MSK + */ + chunk_t emsk; + + /** + * Expected result from client XRES + */ + chunk_t xres; + + /** + * Shared secret K from ipsec.conf (padded) + */ + chunk_t k; + + /** + * random value RAND generated by server + */ + chunk_t rand; +}; + +/** Family key, as proposed in S.S0055 */ +static u_int8_t fmk_buf[] = {0x41, 0x48, 0x41, 0x47}; +static chunk_t fmk = chunk_from_buf(fmk_buf); + +/** Authentication management field */ +static u_int8_t amf_buf[] = {0x00, 0x01}; +static chunk_t amf = chunk_from_buf(amf_buf); + +/** AT_CLIENT_ERROR_CODE AKA attribute */ +static u_int8_t client_error_code_buf[] = {0, 0}; +static chunk_t client_error_code = chunk_from_buf(client_error_code_buf); + +/** previously used sqn by peer, next one must be greater */ +static u_int8_t peer_sqn_buf[6]; +static chunk_t peer_sqn = chunk_from_buf(peer_sqn_buf); + +/** set SQN to the current time */ +static void update_sqn(u_int8_t *sqn, time_t offset) +{ + timeval_t time; + gettimeofday(&time, NULL); + /* set sqb_sqn to an integer containing seconds followed by most + * significant useconds */ + time.tv_sec = htonl(time.tv_sec + offset); + /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */ + time.tv_usec <<= 12; + time.tv_usec = htonl(time.tv_usec); + memcpy(sqn, &time.tv_sec, 4); + memcpy(sqn + 4, &time.tv_usec, 2); +} + +/** initialize peers SQN to the current system time at startup */ +static void __attribute__ ((constructor))init_sqn(void) +{ + update_sqn(peer_sqn_buf, 0); +} + +/** + * Binary represnation of the polynom T^160 + T^5 + T^3 + T^2 + 1 + */ +static u_int8_t g[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2d +}; + +/** + * Predefined random bits from the RAND Corporation book + */ +static u_int8_t a[] = { + 0x9d, 0xe9, 0xc9, 0xc8, 0xef, 0xd5, 0x78, 0x11, + 0x48, 0x23, 0x14, 0x01, 0x90, 0x1f, 0x2d, 0x49, + 0x3f, 0x4c, 0x63, 0x65 +}; + +/** + * Predefined random bits from the RAND Corporation book + */ +static u_int8_t b[] = { + 0x75, 0xef, 0xd1, 0x5c, 0x4b, 0x8f, 0x8f, 0x51, + 0x4e, 0xf3, 0xbc, 0xc3, 0x79, 0x4a, 0x76, 0x5e, + 0x7e, 0xec, 0x45, 0xe0 +}; + +/** + * Multiplicate two mpz_t with bits interpreted as polynoms. + */ +static void mpz_mul_poly(mpz_t r, mpz_t a, mpz_t b) +{ + mpz_t bm, rm; + int current = 0, shifted = 0, shift; + + mpz_init_set(bm, b); + mpz_init_set_ui(rm, 0); + /* scan through a, for each found bit: */ + while ((current = mpz_scan1(a, current)) != ULONG_MAX) + { + /* XOR shifted b into r */ + shift = current - shifted; + mpz_mul_2exp(bm, bm, shift); + shifted += shift; + mpz_xor(rm, rm, bm); + current++; + } + + mpz_swap(r, rm); + mpz_clear(rm); + mpz_clear(bm); +} + +/** + * Calculate the sum of a + b interpreted as polynoms. + */ +static void mpz_add_poly(mpz_t res, mpz_t a, mpz_t b) +{ + /* addition of polynominals is just the XOR */ + mpz_xor(res, a, b); +} + +/** + * Calculate the remainder of a/b interpreted as polynoms. + */ +static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b) +{ + /* Example: + * a = 10001010 + * b = 00000101 + */ + int a_bit, b_bit, diff; + mpz_t bm, am; + + mpz_init_set(am, a); + mpz_init(bm); + + a_bit = mpz_sizeinbase(a, 2); + b_bit = mpz_sizeinbase(b, 2); + + /* don't do anything if b > a */ + if (a_bit >= b_bit) + { + /* shift b left to align up most signaficant "1" to a: + * a = 10001010 + * b = 10100000 + */ + mpz_mul_2exp(bm, b, a_bit - b_bit); + do + { + /* XOR b into a, this kills the most significant "1": + * a = 00101010 + */ + mpz_xor(am, am, bm); + /* find the next most significant "1" in a, and align up b: + * a = 00101010 + * b = 00101000 + */ + diff = a_bit - mpz_sizeinbase(am, 2); + mpz_div_2exp(bm, bm, diff); + a_bit -= diff; + } + while (b_bit <= mpz_sizeinbase(bm, 2)); + /* While b is not shifted to its original value */ + } + /* after another iteration: + * a = 00000010 + * which is the polynomial modulo + */ + + mpz_swap(r, am); + mpz_clear(am); + mpz_clear(bm); +} + +/** + * Step 4 of the various fx() functions: + * Polynomial whiten calculations + */ +static void step4(u_int8_t x[]) +{ + mpz_t xm, am, bm, gm; + + mpz_init(xm); + mpz_init(am); + mpz_init(bm); + mpz_init(gm); + + mpz_import(xm, HASH_SIZE_SHA1, 1, 1, 1, 0, x); + mpz_import(am, sizeof(a), 1, 1, 1, 0, a); + mpz_import(bm, sizeof(b), 1, 1, 1, 0, b); + mpz_import(gm, sizeof(g), 1, 1, 1, 0, g); + + mpz_mul_poly(xm, am, xm); + mpz_add_poly(xm, bm, xm); + mpz_mod_poly(xm, xm, gm); + + mpz_export(x, NULL, 1, HASH_SIZE_SHA1, 1, 0, xm); + + mpz_clear(xm); + mpz_clear(am); + mpz_clear(bm); + mpz_clear(gm); +} + +/** + * Step 3 of the various fx() functions: + * XOR the key into the SHA1 IV + */ +static void step3(chunk_t k, chunk_t payload, u_int8_t h[]) +{ + u_int8_t iv[] = { + 0x67,0x45,0x23,0x01,0xEF,0xCD,0xAB,0x89,0x98,0xBA, + 0xDC,0xFE,0x10,0x32,0x54,0x76,0xC3,0xD2,0xE1,0xF0, + }; + + /* XOR key into IV */ + memxor(iv, k.ptr, k.len); + + /* hash it with the G() function defined in FIPS 186-2 from fips_prf.h */ + g_sha1(iv, payload, h); +} + +/** + * Calculation function for f2(), f3(), f4() + */ +static void fx(u_int8_t f, chunk_t k, chunk_t rand, u_int8_t out[]) +{ + chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); + u_int8_t h[HASH_SIZE_SHA1]; + u_int8_t i; + + for (i = 0; i < 2; i++) + { + memset(payload.ptr, 0x5c, payload.len); + payload.ptr[11] ^= f; + memxor(payload.ptr + 12, fmk.ptr, fmk.len); + memxor(payload.ptr + 24, rand.ptr, rand.len); + + payload.ptr[3] ^= i; + payload.ptr[19] ^= i; + payload.ptr[35] ^= i; + payload.ptr[51] ^= i; + + step3(k, payload, h); + step4(h); + memcpy(out + i * 8, h, 8); + } +} + +/** + * Calculation function of f1() and f1star() + */ +static void f1x(u_int8_t f, chunk_t k, chunk_t rand, chunk_t sqn, + chunk_t amf, u_int8_t mac[]) +{ + /* generate MAC = f1(FMK, SQN, RAND, AMF) + * K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit + * payload which gets hashed + */ + chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); + u_int8_t h[HASH_SIZE_SHA1]; + + memset(payload.ptr, 0x5c, PAYLOAD_LENGTH); + payload.ptr[11] ^= f; + memxor(payload.ptr + 12, fmk.ptr, fmk.len); + memxor(payload.ptr + 16, rand.ptr, rand.len); + memxor(payload.ptr + 34, sqn.ptr, sqn.len); + memxor(payload.ptr + 42, amf.ptr, amf.len); + + step3(k, payload, h); + step4(h); + memcpy(mac, h, MAC_LENGTH); +} + +/** + * Calculation function of f5() and f5star() + */ +static void f5x(u_int8_t f, chunk_t k, chunk_t rand, u_int8_t ak[]) +{ + chunk_t payload = chunk_alloca(PAYLOAD_LENGTH); + u_int8_t h[HASH_SIZE_SHA1]; + + memset(payload.ptr, 0x5c, payload.len); + payload.ptr[11] ^= f; + memxor(payload.ptr + 12, fmk.ptr, fmk.len); + memxor(payload.ptr + 16, rand.ptr, rand.len); + + step3(k, payload, h); + step4(h); + memcpy(ak, h, AK_LENGTH); +} + +/** + * Calculate the MAC from a RAND, SQN, AMF value using K + */ +static void f1(chunk_t k, chunk_t rand, chunk_t sqn, chunk_t amf, u_int8_t mac[]) +{ + f1x(F1, k, rand, sqn, amf, mac); + DBG3(DBG_IKE, "MAC %b", mac, MAC_LENGTH); +} + +/** + * Calculate the MACS from a RAND, SQN, AMF value using K + */ +static void f1star(chunk_t k, chunk_t rand, chunk_t sqn, chunk_t amf, u_int8_t macs[]) +{ + f1x(F1STAR, k, rand, sqn, amf, macs); + DBG3(DBG_IKE, "MACS %b", macs, MAC_LENGTH); +} + +/** + * Calculate RES from RAND using K + */ +static void f2(chunk_t k, chunk_t rand, u_int8_t res[]) +{ + fx(F2, k, rand, res); + DBG3(DBG_IKE, "RES %b", res, RES_LENGTH); +} + +/** + * Calculate CK from RAND using K + */ +static void f3(chunk_t k, chunk_t rand, u_int8_t ck[]) +{ + fx(F3, k, rand, ck); + DBG3(DBG_IKE, "CK %b", ck, CK_LENGTH); +} + +/** + * Calculate IK from RAND using K + */ +static void f4(chunk_t k, chunk_t rand, u_int8_t ik[]) +{ + fx(F4, k, rand, ik); + DBG3(DBG_IKE, "IK %b", ik, IK_LENGTH); +} + +/** + * Calculate AK from a RAND using K + */ +static void f5(chunk_t k, chunk_t rand, u_int8_t ak[]) +{ + f5x(F5, k, rand, ak); + DBG3(DBG_IKE, "AK %b", ak, AK_LENGTH); +} + +/** + * Calculate AKS from a RAND using K + */ +static void f5star(chunk_t k, chunk_t rand, u_int8_t aks[]) +{ + f5x(F5STAR, k, rand, aks); + DBG3(DBG_IKE, "AKS %b", aks, AK_LENGTH); +} + +/** + * derive the keys needed for EAP_AKA + */ +static void derive_keys(private_eap_aka_t *this, identification_t *id) +{ + hasher_t *hasher; + prf_t *prf; + chunk_t ck, ik, mk, identity, tmp; + + ck = chunk_alloca(CK_LENGTH); + ik = chunk_alloca(IK_LENGTH); + mk = chunk_alloca(MK_LENGTH); + identity = id->get_encoding(id); + + /* MK = SHA1( Identity | IK | CK ) */ + f3(this->k, this->rand, ck.ptr); + f4(this->k, this->rand, ik.ptr); + DBG3(DBG_IKE, "Identity %B", &identity); + tmp = chunk_cata("ccc", identity, ik, ck); + DBG3(DBG_IKE, "Identity|IK|CK %B", &tmp); + hasher = hasher_create(HASH_SHA1); + hasher->get_hash(hasher, tmp, mk.ptr); + hasher->destroy(hasher); + + /* K_encr | K_auth | MSK | EMSK = prf(0) | prf(0) + * FIPS PRF has 320 bit block size, we need 160 byte for keys + * => run prf four times */ + prf = prf_create(PRF_FIPS_SHA1_160); + prf->set_key(prf, mk); + tmp = chunk_alloca(prf->get_block_size(prf) * 4); + prf->get_bytes(prf, chunk_empty, tmp.ptr); + prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1); + prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2); + prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3); + prf->destroy(prf); + chunk_free(&this->k_encr); + chunk_free(&this->k_auth); + chunk_free(&this->msk); + chunk_free(&this->emsk); + chunk_split(tmp, "aaaa", 16, &this->k_encr, 16, &this->k_auth, + 64, &this->msk, 64, &this->emsk); + DBG3(DBG_IKE, "MK %B", &mk); + DBG3(DBG_IKE, "PRF res %B", &tmp); + DBG3(DBG_IKE, "K_encr %B", &this->k_encr); + DBG3(DBG_IKE, "K_auth %B", &this->k_auth); + DBG3(DBG_IKE, "MSK %B", &this->msk); + DBG3(DBG_IKE, "EMSK %B", &this->emsk); +} + +/* + * Get a shared key from ipsec.secrets. + * We use the standard keys as used in preshared key authentication. As + * these keys have an undefined length, we: + * - strip them if they are longer + * - fill them up with '\0' if they are shorter + */ +static status_t load_key(identification_t *me, identification_t *other, chunk_t *k) +{ + chunk_t shared_key; + + if (charon->credentials->get_eap_key(charon->credentials, me, + other, &shared_key) != SUCCESS) + { + return NOT_FOUND; + } + chunk_free(k); + *k = chunk_alloc(K_LENGTH); + memset(k->ptr, '\0', k->len); + memcpy(k->ptr, shared_key.ptr, min(shared_key.len, k->len)); + chunk_free(&shared_key); + return SUCCESS; +} + +/** + * skip EAP_AKA header in message and returns its AKA subtype + */ +static aka_subtype_t read_header(chunk_t *message) +{ + aka_subtype_t type; + + if (message->len < 8) + { + *message = chunk_empty; + return 0; + } + type = *(message->ptr + 5); + *message = chunk_skip(*message, 8); + return type; +} + +/** + * read the next attribute from the chunk data + */ +static aka_attribute_t read_attribute(chunk_t *data, chunk_t *attr_data) +{ + aka_attribute_t attribute; + size_t length; + + DBG3(DBG_IKE, "reading attribute from %B", data); + + if (data->len < 2) + { + return AT_END; + } + /* read attribute and length */ + attribute = *data->ptr++; + length = *data->ptr++ * 4 - 2; + data->len -= 2; + DBG3(DBG_IKE, "found attribute %N with length %d", + aka_attribute_names, attribute, length); + if (length > data->len) + { + return AT_END; + } + /* apply attribute value to attr_data */ + attr_data->len = length; + attr_data->ptr = data->ptr; + /* update data to point to next attribute */ + *data = chunk_skip(*data, length); + return attribute; +} + +/** + * Build an AKA payload from different attributes. + * The variable argument takes an aka_attribute_t + * followed by its data in a chunk. + */ +static eap_payload_t *build_aka_payload(private_eap_aka_t *this, eap_code_t code, + u_int8_t identifier, aka_subtype_t type, ...) +{ + chunk_t message = chunk_alloca(512); /* is enought for all current messages */ + chunk_t pos = message; + eap_payload_t *payload; + va_list args; + aka_attribute_t attr; + u_int8_t *mac_pos = NULL; + + /* write EAP header, skip length bytes */ + *pos.ptr++ = code; + *pos.ptr++ = identifier; + pos.ptr += 2; + pos.len -= 4; + /* write AKA header with type and subtype, null reserved bytes */ + *pos.ptr++ = EAP_AKA; + *pos.ptr++ = type; + *pos.ptr++ = 0; + *pos.ptr++ = 0; + pos.len -= 4; + + va_start(args, type); + while ((attr = va_arg(args, aka_attribute_t)) != AT_END) + { + chunk_t data = va_arg(args, chunk_t); + + DBG3(DBG_IKE, "building %N %B", aka_attribute_names, attr, &data); + + /* write attribute header */ + *pos.ptr++ = attr; + pos.len--; + + switch (attr) + { + case AT_RES: + { + /* attribute length in 4byte words */ + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + /* RES length in bits */ + *(u_int16_t*)pos.ptr = htons(data.len * 8); + pos = chunk_skip(pos, sizeof(u_int16_t)); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_AUTN: + case AT_RAND: + { + *pos.ptr++ = data.len/4 + 1; pos.len--; + *pos.ptr++ = 0; pos.len--; + *pos.ptr++ = 0; pos.len--; + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + break; + } + case AT_MAC: + { + *pos.ptr++ = 5; pos.len--; + *pos.ptr++ = 0; pos.len--; + *pos.ptr++ = 0; pos.len--; + mac_pos = pos.ptr; + /* MAC is calculated over message including zeroed AT_MAC attribute */ + memset(mac_pos, 0, AT_MAC_LENGTH); + pos.ptr += AT_MAC_LENGTH; + pos.len -= AT_MAC_LENGTH; + break; + } + default: + { + /* length is data length in 4-bytes + 1 for header */ + *pos.ptr = data.len/4 + 1; + pos = chunk_skip(pos, 1); + memcpy(pos.ptr, data.ptr, data.len); + pos = chunk_skip(pos, data.len); + } + } + } + va_end(args); + + /* calculate message length, write into header */ + message.len = pos.ptr - message.ptr; + *(u_int16_t*)(message.ptr + 2) = htons(message.len); + + /* create MAC if AT_MAC attribte was included */ + if (mac_pos) + { + signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); + signer->set_key(signer, this->k_auth); + DBG3(DBG_IKE, "AT_MAC signature of %B", &message); + DBG3(DBG_IKE, "using key %B", &this->k_auth); + signer->get_signature(signer, message, mac_pos); + DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LENGTH); + signer->destroy(signer); + } + + /* payload constructor takes data with some bytes skipped */ + payload = eap_payload_create_data(message); + + DBG3(DBG_IKE, "created EAP message %B", &message); + return payload; +} + +/** + * Initiate a AKA-Challenge using SQN + */ +static status_t server_initiate_challenge(private_eap_aka_t *this, chunk_t sqn, eap_payload_t **out) +{ + randomizer_t *randomizer; + status_t status; + chunk_t mac, ak, autn; + + mac = chunk_alloca(MAC_LENGTH); + ak = chunk_alloca(AK_LENGTH); + chunk_free(&this->rand); + chunk_free(&this->xres); + + /* generate RAND: + * we use our standard randomizer, not f0() proposed in S.S0055 + */ + randomizer = randomizer_create(); + status = randomizer->allocate_pseudo_random_bytes(randomizer, RAND_LENGTH, &this->rand); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "generating RAND for EAP-AKA authentication failed"); + return FAILED; + } + +# ifdef TEST_VECTORS + /* Test vector for RAND */ + u_int8_t test_rand[] = { + 0x4b,0x05,0x2b,0x20,0xe2,0xa0,0x6c,0x8f, + 0xf7,0x00,0xda,0x51,0x2b,0x4e,0x11,0x1e, + }; + memcpy(this->rand.ptr, test_rand, this->rand.len); +# endif /* TEST_VECTORS */ + + /* Get the shared key K: */ + if (load_key(this->server, this->peer, &this->k) != SUCCESS) + { + DBG1(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate " + "with EAP-AKA", this->server, this->peer); + return FAILED; + } + +# ifdef TEST_VECTORS + /* Test vector for K */ + u_int8_t test_k[] = { + 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, + 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, + }; + memcpy(this->k.ptr, test_k, this->k.len); +# endif /* TEST_VECTORS */ + + /* generate MAC */ + f1(this->k, this->rand, sqn, amf, mac.ptr); + + /* generate AK */ + f5(this->k, this->rand, ak.ptr); + + /* precalculate XRES as expected from client */ + this->xres = chunk_alloc(RES_LENGTH); + f2(this->k, this->rand, this->xres.ptr); + + /* calculate AUTN = (SQN xor AK) || AMF || MAC */ + autn = chunk_cata("ccc", sqn, amf, mac); + memxor(autn.ptr, ak.ptr, ak.len); + DBG3(DBG_IKE, "AUTN %B", &autn); + + + /* derive K_encr, K_auth, MSK, EMSK */ + derive_keys(this, this->peer); + + /* build payload */ + *out = build_aka_payload(this, EAP_REQUEST, 0, AKA_CHALLENGE, + AT_RAND, this->rand, AT_AUTN, autn, AT_MAC, + chunk_empty, AT_END); + return NEED_MORE; +} + +/** + * Implementation of eap_method_t.initiate for an EAP_AKA server + */ +static status_t server_initiate(private_eap_aka_t *this, eap_payload_t **out) +{ + chunk_t sqn = chunk_alloca(SQN_LENGTH); + + /* we use an offset of 3 minutes to tolerate clock inaccuracy + * without the need to synchronize sequence numbers */ + update_sqn(sqn.ptr, 180); + +# ifdef TEST_VECTORS + /* Test vector for SQN */ + u_int8_t test_sqn[] = {0x00,0x00,0x00,0x00,0x00,0x01}; + memcpy(sqn.ptr, test_sqn, sqn.len); +# endif /* TEST_VECTORS */ + + return server_initiate_challenge(this, sqn, out); +} + +static status_t server_process_synchronize(private_eap_aka_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t attr, auts = chunk_empty, pos, message, macs, xmacs, sqn, aks, amf; + u_int i; + + message = in->get_data(in); + pos = message; + read_header(&pos); + + /* iterate over attributes */ + while (TRUE) + { + aka_attribute_t attribute = read_attribute(&pos, &attr); + switch (attribute) + { + case AT_END: + break; + case AT_AUTS: + auts = attr; + continue; + default: + if (attribute >= 0 && attribute <= 127) + { + DBG1(DBG_IKE, "found non skippable attribute %N", + aka_attribute_names, attribute); + return FAILED; + } + DBG1(DBG_IKE, "ignoring skippable attribute %N", + aka_attribute_names, attribute); + continue; + } + break; + } + + if (auts.len != AUTS_LENGTH) + { + DBG1(DBG_IKE, "synchronization request didn't contain useable AUTS"); + return FAILED; + } + + chunk_split(auts, "mm", SQN_LENGTH, &sqn, MAC_LENGTH, &macs); + aks = chunk_alloca(AK_LENGTH); + f5star(this->k, this->rand, aks.ptr); + /* decrypt serial number by XORing AKS */ + memxor(sqn.ptr, aks.ptr, aks.len); + + /* verify MACS */ + xmacs = chunk_alloca(MAC_LENGTH); + amf = chunk_alloca(AMF_LENGTH); + /* an AMF of zero is used for MACS calculation */ + memset(amf.ptr, 0, amf.len); + f1star(this->k, this->rand, sqn, amf, xmacs.ptr); + if (!chunk_equals(macs, xmacs)) + { + DBG1(DBG_IKE, "received MACS does not match XMACS"); + DBG3(DBG_IKE, "MACS %B XMACS %B", &macs, &xmacs); + return FAILED; + } + + /* retry the challenge with the received SQN + 1*/ + for (i = SQN_LENGTH - 1; i >= 0; i--) + { + if (++sqn.ptr[i] != 0) + { + break; + } + } + return server_initiate_challenge(this, sqn, out); +} + +/** + * process an AKA_Challenge response + */ +static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t *in) +{ + chunk_t attr, res = chunk_empty, at_mac = chunk_empty, pos, message; + + message = in->get_data(in); + pos = message; + read_header(&pos); + + /* iterate over attributes */ + while (TRUE) + { + aka_attribute_t attribute = read_attribute(&pos, &attr); + switch (attribute) + { + case AT_END: + break; + case AT_RES: + res = attr; + if (attr.len == 2 + RES_LENGTH && + *(u_int16_t*)attr.ptr == htons(RES_LENGTH * 8)) + { + res = chunk_skip(attr, 2); + } + continue; + + case AT_MAC: + attr = chunk_skip(attr, 2); + at_mac = chunk_clonea(attr); + /* zero MAC in message for MAC verification */ + memset(attr.ptr, 0, attr.len); + continue; + default: + if (attribute >= 0 && attribute <= 127) + { + DBG1(DBG_IKE, "found non skippable attribute %N", + aka_attribute_names, attribute); + return FAILED; + } + DBG1(DBG_IKE, "ignoring skippable attribute %N", + aka_attribute_names, attribute); + continue; + } + break; + } + + /* verify EAP message MAC AT_MAC */ + { + bool valid; + signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); + signer->set_key(signer, this->k_auth); + DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); + DBG3(DBG_IKE, "using key %B", &this->k_auth); + valid = signer->verify_signature(signer, message, at_mac); + signer->destroy(signer); + if (!valid) + { + DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed"); + return FAILED; + } + } + + /* compare received RES against stored precalculated XRES */ + if (!chunk_equals(res, this->xres)) + { + DBG1(DBG_IKE, "received RES does not match XRES"); + DBG3(DBG_IKE, "RES %Bb XRES %B", &res, &this->xres); + return FAILED; + } + return SUCCESS; +} + +/** + * Implementation of eap_method_t.process for EAP_AKA servers + */ +static status_t server_process(private_eap_aka_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t message; + aka_subtype_t type; + + message = in->get_data(in); + type = read_header(&message); + + DBG3(DBG_IKE, "received EAP message %B", &message); + + switch (type) + { + case AKA_CHALLENGE: + { + return server_process_challenge(this, in); + } + case AKA_AUTHENTICATION_REJECT: + case AKA_CLIENT_ERROR: + { + DBG1(DBG_IKE, "received %N, authentication failed", + aka_subtype_names, type); + return FAILED; + } + case AKA_SYNCHRONIZATION_FAILURE: + { + DBG1(DBG_IKE, "received %N, retrying with received SQN", + aka_subtype_names, type); + return server_process_synchronize(this, in, out); + } + default: + DBG1(DBG_IKE, "received unknown AKA subtype %N, authentication failed", + aka_subtype_names, type); + return FAILED; + } +} + +/** + * Process an incoming AKA-Challenge client side + */ +static status_t peer_process_challenge(private_eap_aka_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t attr = chunk_empty; + chunk_t autn = chunk_empty, at_mac = chunk_empty; + chunk_t ak, sqn, sqn_ak, mac, xmac, res, amf, message, pos; + u_int8_t identifier; + + ak = chunk_alloca(AK_LENGTH); + xmac = chunk_alloca(MAC_LENGTH); + res = chunk_alloca(RES_LENGTH); + chunk_free(&this->rand); + + message = in->get_data(in); + pos = message; + read_header(&pos); + identifier = in->get_identifier(in); + + DBG3(DBG_IKE, "reading attributes from %B", &pos); + + /* iterate over attributes */ + while (TRUE) + { + aka_attribute_t attribute = read_attribute(&pos, &attr); + switch (attribute) + { + case AT_END: + break; + case AT_RAND: + this->rand = chunk_clone(chunk_skip(attr, 2)); + continue; + case AT_AUTN: + autn = chunk_skip(attr, 2); + continue; + case AT_MAC: + attr = chunk_skip(attr, 2); + at_mac = chunk_clonea(attr); + /* set MAC in message to zero for own MAC verification */ + memset(attr.ptr, 0, attr.len); + continue; + default: + if (attribute >= 0 && attribute <= 127) + { + /* non skippable attribute, abort */ + *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_code, AT_END); + DBG1(DBG_IKE, "found non skippable attribute %N, sending %N %d", + aka_attribute_names, attribute, + aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); + return NEED_MORE; + } + DBG1(DBG_IKE, "ignoring skippable attribute %N", + aka_attribute_names, attribute); + continue; + } + break; + } + + if (this->rand.len != RAND_LENGTH || autn.len != AUTN_LENGTH) + { + /* required attributes wrong/not found, abort */ + *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_code, AT_END); + DBG1(DBG_IKE, "could not find valid RAND/AUTN attribute, sending %N %d", + aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); + return NEED_MORE; + } + + DBG3(DBG_IKE, "using autn %B", &autn); + /* split up AUTN = SQN xor AK | AMF | MAC */ + chunk_split(autn, "mmm", SQN_LENGTH, &sqn_ak, AMF_LENGTH, &amf, MAC_LENGTH, &mac); + + /* Get the shared key K: */ + chunk_free(&this->k); + if (load_key(this->peer, this->server, &this->k) != SUCCESS) + { + *out = build_aka_payload(this, EAP_RESPONSE, identifier, + AKA_AUTHENTICATION_REJECT, AT_END); + DBG3(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate " + "with EAP-AKA, sending %N", this->peer, this->server, + aka_subtype_names, AKA_AUTHENTICATION_REJECT); + return NEED_MORE; + } + DBG3(DBG_IKE, "using K %B", &this->k); +# ifdef TEST_VECTORS + /* Test vector for K */ + u_int8_t test_k[] = { + 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c, + 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d, + }; + memcpy(this->k.ptr, test_k, this->k.len); +# endif /* TEST_VECTORS */ + + /* calculate anonymity key AK */ + f5(this->k, this->rand, ak.ptr); + DBG3(DBG_IKE, "using rand %B", &this->rand); + DBG3(DBG_IKE, "using ak %B", &ak); + /* XOR AK into SQN to decrypt it */ + + sqn = chunk_clonea(sqn_ak); + + DBG3(DBG_IKE, "using ak xor sqn %B", &sqn_ak); + memxor(sqn.ptr, ak.ptr, sqn.len); + DBG3(DBG_IKE, "using sqn %B", &sqn); + + /* calculate expected MAC and compare against received one */ + f1(this->k, this->rand, sqn, amf, xmac.ptr); + if (!chunk_equals(mac, xmac)) + { + *out = build_aka_payload(this, EAP_RESPONSE, identifier, + AKA_AUTHENTICATION_REJECT, AT_END); + DBG1(DBG_IKE, "received MAC does not match XMAC, sending %N", + aka_subtype_names, AKA_AUTHENTICATION_REJECT); + DBG3(DBG_IKE, "MAC %B\nXMAC %B", &mac, &xmac); + return NEED_MORE; + } + +#if SEQ_CHECK + if (memcmp(peer_sqn.ptr, sqn.ptr, sqn.len) >= 0) + { + /* sequence number invalid. send AUTS */ + chunk_t auts, macs, aks, amf; + + macs = chunk_alloca(MAC_LENGTH); + aks = chunk_alloca(AK_LENGTH); + amf = chunk_alloca(AMF_LENGTH); + + /* AMF is set to zero in AKA_SYNCHRONIZATION_FAILURE */ + memset(amf.ptr, 0, amf.len); + /* AKS = f5*(RAND) */ + f5star(this->k, this->rand, aks.ptr); + /* MACS = f1*(RAND) */ + f1star(this->k, this->rand, peer_sqn, amf, macs.ptr); + /* AUTS = SQN xor AKS | MACS */ + memxor(aks.ptr, peer_sqn.ptr, aks.len); + auts = chunk_cata("cc", aks, macs); + + *out = build_aka_payload(this, EAP_RESPONSE, identifier, + AKA_SYNCHRONIZATION_FAILURE, + AT_AUTS, auts, AT_END); + DBG1(DBG_IKE, "received SQN invalid, sending %N", + aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE); + DBG3(DBG_IKE, "received SQN %B\ncurrent SQN %B", &sqn, &peer_sqn); + return NEED_MORE; + } +#endif /* SEQ_CHECK */ + + /* derive K_encr, K_auth, MSK, EMSK */ + derive_keys(this, this->peer); + + /* verify EAP message MAC AT_MAC */ + { + bool valid; + signer_t *signer = signer_create(AUTH_HMAC_SHA1_128); + signer->set_key(signer, this->k_auth); + + DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message); + DBG3(DBG_IKE, "using key %B", &this->k_auth); + valid = signer->verify_signature(signer, message, at_mac); + signer->destroy(signer); + if (!valid) + { + *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_code, AT_END); + DBG1(DBG_IKE, "MAC in AT_MAC attribute verification " + "failed, sending %N %d", aka_attribute_names, + AT_CLIENT_ERROR_CODE, 0); + return NEED_MORE; + } + } + + /* update stored SQN to the received one */ + memcpy(peer_sqn.ptr, sqn.ptr, sqn.len); + + /* calculate RES */ + f2(this->k, this->rand, res.ptr); + + /* build response */ + *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CHALLENGE, + AT_RES, res, AT_MAC, chunk_empty, AT_END); + return NEED_MORE; +} + +/** + * Process an incoming AKA-Notification as client + */ +static status_t peer_process_notification(private_eap_aka_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t message, pos, attr; + u_int8_t identifier; + + message = in->get_data(in); + pos = message; + read_header(&pos); + identifier = in->get_identifier(in); + + DBG3(DBG_IKE, "reading attributes from %B", &pos); + + /* iterate over attributes */ + while (TRUE) + { + aka_attribute_t attribute = read_attribute(&pos, &attr); + switch (attribute) + { + case AT_END: + break; + case AT_NOTIFICATION: + { + u_int16_t code; + + if (attr.len != 2) + { + DBG1(DBG_IKE, "received invalid AKA notification, ignored"); + continue; + } + code = ntohs(*(u_int16_t*)attr.ptr); + switch (code) + { + case 0: + DBG1(DBG_IKE, "received AKA notification 'general " + "failure after authentication' (%d)", code); + return FAILED; + case 16384: + DBG1(DBG_IKE, "received AKA notification 'general " + "failure' (%d)", code); + return FAILED; + case 32768: + DBG1(DBG_IKE, "received AKA notification 'successfully " + "authenticated' (%d)", code); + continue; + case 1026: + DBG1(DBG_IKE, "received AKA notification 'access " + "temporarily denied' (%d)", code); + return FAILED; + case 1031: + DBG1(DBG_IKE, "received AKA notification 'not " + "subscribed to service' (%d)", code); + return FAILED; + default: + DBG1(DBG_IKE, "received AKA notification code %d, " + "ignored", code); + continue; + } + } + default: + if (attribute >= 0 && attribute <= 127) + { + DBG1(DBG_IKE, "ignoring non-skippable attribute %N in %N", + aka_attribute_names, attribute, aka_subtype_names, + AKA_NOTIFICATION); + } + else + { + DBG1(DBG_IKE, "ignoring skippable attribute %N", + aka_attribute_names, attribute); + } + continue; + } + break; + } + return NEED_MORE; +} + +/** + * Implementation of eap_method_t.process for an EAP_AKA peer + */ +static status_t peer_process(private_eap_aka_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + aka_subtype_t type; + chunk_t message; + u_int8_t identifier; + + message = in->get_data(in); + type = read_header(&message); + identifier = in->get_identifier(in); + + DBG3(DBG_IKE, "received EAP message %B", &message); + + switch (type) + { + case AKA_CHALLENGE: + { + return peer_process_challenge(this, in, out); + } + case AKA_NOTIFICATION: + { + return peer_process_notification(this, in, out); + } + default: + { + *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR, + AT_CLIENT_ERROR_CODE, client_error_code, AT_END); + DBG1(DBG_IKE, "received unsupported %N request, sending %N %d", + aka_subtype_names, type, + aka_attribute_names, AT_CLIENT_ERROR_CODE, 0); + return NEED_MORE; + } + } +} + +/** + * Implementation of eap_method_t.initiate for an EAP AKA peer + */ +static status_t peer_initiate(private_eap_aka_t *this, eap_payload_t **out) +{ + /* peer never initiates */ + return FAILED; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_aka_t *this, u_int32_t *vendor) +{ + *vendor = 0; + return EAP_AKA; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_aka_t *this, chunk_t *msk) +{ + if (this->msk.ptr) + { + *msk = this->msk; + return SUCCESS; + } + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_aka_t *this) +{ + return TRUE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_aka_t *this) +{ + chunk_free(&this->k_encr); + chunk_free(&this->k_auth); + chunk_free(&this->msk); + chunk_free(&this->emsk); + chunk_free(&this->xres); + chunk_free(&this->k); + chunk_free(&this->rand); + free(this); +} + +/* + * Described in header. + */ +eap_aka_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer) +{ + private_eap_aka_t *this = malloc_thing(private_eap_aka_t); + + /* public functions */ + switch (role) + { + case EAP_SERVER: + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process; + break; + case EAP_PEER: + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process; + break; + default: + free(this); + return NULL; + } + this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; + this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; + + /* private data */ + this->server = server; + this->peer = peer; + this->k_encr = chunk_empty; + this->k_auth = chunk_empty; + this->msk = chunk_empty; + this->emsk = chunk_empty; + this->xres = chunk_empty; + this->k = chunk_empty; + this->rand = chunk_empty; + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap/eap_aka.h b/src/charon/sa/authenticators/eap/eap_aka.h new file mode 100644 index 000000000..a886863be --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_aka.h @@ -0,0 +1,141 @@ +/** + * @file eap_aka.h + * + * @brief Interface of eap_aka_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef EAP_AKA_H_ +#define EAP_AKA_H_ + +typedef struct eap_aka_t eap_aka_t; +typedef enum aka_subtype_t aka_subtype_t; +typedef enum aka_attribute_t aka_attribute_t; + +#include <sa/authenticators/eap/eap_method.h> + + +/** + * Subtypes of AKA messages + */ +enum aka_subtype_t { + AKA_CHALLENGE = 1, + AKA_AUTHENTICATION_REJECT = 2, + AKA_SYNCHRONIZATION_FAILURE = 4, + AKA_IDENTITY = 5, + AKA_NOTIFICATION = 12, + AKA_REAUTHENTICATION = 13, + AKA_CLIENT_ERROR = 14, +}; + +/** + * enum names for aka_subtype_t + */ +extern enum_name_t *aka_subtype_names; + +/** + * Attribute types in AKA messages + */ +enum aka_attribute_t { + /** defines the end of attribute list */ + AT_END = -1, + AT_RAND = 1, + AT_AUTN = 2, + AT_RES = 3, + AT_AUTS = 4, + AT_PADDING = 6, + AT_NONCE_MT = 7, + AT_PERMANENT_ID_REQ = 10, + AT_MAC = 11, + AT_NOTIFICATION = 12, + AT_ANY_ID_REQ = 13, + AT_IDENTITY = 14, + AT_VERSION_LIST = 15, + AT_SELECTED_VERSION = 16, + AT_FULLAUTH_ID_REQ = 17, + AT_COUNTER = 19, + AT_COUNTER_TOO_SMALL = 20, + AT_NONCE_S = 21, + AT_CLIENT_ERROR_CODE = 22, + AT_IV = 129, + AT_ENCR_DATA = 130, + AT_NEXT_PSEUDONYM = 132, + AT_NEXT_REAUTH_ID = 133, + AT_CHECKCODE = 134, + AT_RESULT_IND = 135, +}; + +/** + * enum names for aka_attribute_t + */ +extern enum_name_t *aka_attribute_names; + +/** check SEQ values as client for validity, disabled by default */ +#ifndef SEQ_CHECK +# define SEQ_CHECK 0 +#endif + +/** + * @brief Implementation of the eap_method_t interface using EAP-AKA. + * + * EAP-AKA uses 3rd generation mobile phone standard authentication + * mechanism for authentication. It is a mutual authentication + * mechanism which establishs a shared key and therefore supports EAP_ONLY + * authentication. This implementation follows the standard of the + * 3GPP2 (S.S0055) and not the one of 3GGP. + * The shared key used for authentication is from ipsec.secrets. The + * peers ID is used to query it. + * The AKA mechanism uses sequence numbers to detect replay attacks. The + * peer stores the sequence number normally in a USIM and accepts + * incremental sequence numbers (incremental for lifetime of the USIM). To + * prevent a complex sequence number management, this implementation uses + * a sequence number derived from time. It is initialized to the startup + * time of the daemon. As long as the (UTC) time of the system is not + * turned back while the daemon is not running, this method is secure. + * To enable time based SEQs, #define SEQ_CHECK as 1. Default is to accept + * any SEQ numbers. This allows an attacker to do replay attacks. But since + * the server has proven his identity via IKE, such an attack is only + * possible between server and AAA (if any). + * + * @b Constructors: + * - eap_aka_create() + * - eap_client_create() using eap_method EAP_AKA + * + * @ingroup eap + */ +struct eap_aka_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t eap_method_interface; +}; + +/** + * @brief Creates the EAP method EAP-AKA. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_aka_t object + * + * @ingroup eap + */ +eap_aka_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer); + +#endif /* EAP_AKA_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_md5.c b/src/charon/sa/authenticators/eap/eap_md5.c new file mode 100644 index 000000000..0ca9fc566 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_md5.c @@ -0,0 +1,282 @@ +/** + * @file eap_md5.c + * + * @brief Implementation of eap_md5_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "eap_md5.h" + +#include <daemon.h> +#include <library.h> + +typedef struct private_eap_md5_t private_eap_md5_t; + +/** + * Private data of an eap_md5_t object. + */ +struct private_eap_md5_t { + + /** + * Public authenticator_t interface. + */ + eap_md5_t public; + + /** + * ID of the server + */ + identification_t *server; + + /** + * ID of the peer + */ + identification_t *peer; + + /** + * challenge sent by the server + */ + chunk_t challenge; + + /** + * EAP message identififier + */ + u_int8_t identifier; +}; + +typedef struct eap_md5_header_t eap_md5_header_t; + +/** + * packed eap MD5 header struct + */ +struct eap_md5_header_t { + /** EAP code (REQUEST/RESPONSE) */ + u_int8_t code; + /** unique message identifier */ + u_int8_t identifier; + /** length of whole message */ + u_int16_t length; + /** EAP type */ + u_int8_t type; + /** length of value (challenge) */ + u_int8_t value_size; + /** actual value */ + u_int8_t value[]; +} __attribute__((__packed__)); + +#define CHALLENGE_LEN 16 +#define PAYLOAD_LEN (CHALLENGE_LEN + sizeof(eap_md5_header_t)) + +/** + * Hash the challenge string, create response + */ +static status_t hash_challenge(private_eap_md5_t *this, chunk_t *response) +{ + chunk_t concat, secret; + hasher_t *hasher; + + if (charon->credentials->get_eap_key(charon->credentials, this->server, + this->peer, &secret) != SUCCESS) + { + DBG1(DBG_IKE, "no EAP key found for hosts '%D' - '%D'", + this->server, this->peer); + return NOT_FOUND; + } + concat = chunk_cata("cmc", chunk_from_thing(this->identifier), + secret, this->challenge); + hasher = hasher_create(HASH_MD5); + hasher->allocate_hash(hasher, concat, response); + hasher->destroy(hasher); + return SUCCESS; +} + +/** + * Implementation of eap_method_t.initiate for the peer + */ +static status_t initiate_peer(private_eap_md5_t *this, eap_payload_t **out) +{ + /* peer never initiates */ + return FAILED; +} + +/** + * Implementation of eap_method_t.initiate for the server + */ +static status_t initiate_server(private_eap_md5_t *this, eap_payload_t **out) +{ + randomizer_t *randomizer; + status_t status; + eap_md5_header_t *req; + + randomizer = randomizer_create(); + status = randomizer->allocate_pseudo_random_bytes(randomizer, CHALLENGE_LEN, + &this->challenge); + randomizer->destroy(randomizer); + if (status != SUCCESS) + { + return FAILED; + } + + req = alloca(PAYLOAD_LEN); + req->length = htons(PAYLOAD_LEN); + req->code = EAP_REQUEST; + req->identifier = this->identifier; + req->type = EAP_MD5; + req->value_size = this->challenge.len; + memcpy(req->value, this->challenge.ptr, this->challenge.len); + + *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN)); + return NEED_MORE; +} + +/** + * Implementation of eap_method_t.process for the peer + */ +static status_t process_peer(private_eap_md5_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t response; + chunk_t data; + eap_md5_header_t *req; + + this->identifier = in->get_identifier(in); + data = in->get_data(in); + this->challenge = chunk_clone(chunk_skip(data, 6)); + if (data.len < 6 || this->challenge.len < *(data.ptr + 5)) + { + DBG1(DBG_IKE, "received invalid EAP-MD5 message"); + return FAILED; + } + if (hash_challenge(this, &response) != SUCCESS) + { + return FAILED; + } + req = alloca(PAYLOAD_LEN); + req->length = htons(PAYLOAD_LEN); + req->code = EAP_RESPONSE; + req->identifier = this->identifier; + req->type = EAP_MD5; + req->value_size = response.len; + memcpy(req->value, response.ptr, response.len); + chunk_free(&response); + + *out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN)); + return NEED_MORE; +} + +/** + * Implementation of eap_method_t.process for the server + */ +static status_t process_server(private_eap_md5_t *this, + eap_payload_t *in, eap_payload_t **out) +{ + chunk_t response, expected; + chunk_t data; + + if (this->identifier != in->get_identifier(in)) + { + DBG1(DBG_IKE, "received invalid EAP-MD5 message"); + return FAILED; + } + if (hash_challenge(this, &expected) != SUCCESS) + { + return FAILED; + } + data = in->get_data(in); + response = chunk_skip(data, 6); + + if (response.len < expected.len || + !memeq(response.ptr, expected.ptr, expected.len)) + { + chunk_free(&expected); + DBG1(DBG_IKE, "EAP-MD5 verification failed"); + return FAILED; + } + chunk_free(&expected); + return SUCCESS; +} + +/** + * Implementation of eap_method_t.get_type. + */ +static eap_type_t get_type(private_eap_md5_t *this, u_int32_t *vendor) +{ + *vendor = 0; + return EAP_MD5; +} + +/** + * Implementation of eap_method_t.get_msk. + */ +static status_t get_msk(private_eap_md5_t *this, chunk_t *msk) +{ + return FAILED; +} + +/** + * Implementation of eap_method_t.is_mutual. + */ +static bool is_mutual(private_eap_md5_t *this) +{ + return FALSE; +} + +/** + * Implementation of eap_method_t.destroy. + */ +static void destroy(private_eap_md5_t *this) +{ + chunk_free(&this->challenge); + free(this); +} + +/* + * Described in header. + */ +eap_md5_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer) +{ + private_eap_md5_t *this = malloc_thing(private_eap_md5_t); + + /* public functions */ + switch (role) + { + case EAP_SERVER: + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server; + break; + case EAP_PEER: + this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer; + this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer; + break; + default: + free(this); + return NULL; + } + this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; + this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; + this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; + this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; + + /* private data */ + this->peer = peer; + this->server = server; + this->challenge = chunk_empty; + this->identifier = random(); + + return &this->public; +} diff --git a/src/charon/sa/authenticators/eap/eap_md5.h b/src/charon/sa/authenticators/eap/eap_md5.h new file mode 100644 index 000000000..260210b59 --- /dev/null +++ b/src/charon/sa/authenticators/eap/eap_md5.h @@ -0,0 +1,59 @@ +/** + * @file eap_md5.h + * + * @brief Interface of eap_md5_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef EAP_MD5_H_ +#define EAP_MD5_H_ + +typedef struct eap_md5_t eap_md5_t; + +#include <sa/authenticators/eap/eap_method.h> + +/** + * @brief Implementation of the eap_method_t interface using EAP-MD5 (CHAP). + * + * @b Constructors: + * - eap_md5_create() + * - eap_client_create() using eap_method EAP_MD5 + * + * @ingroup eap + */ +struct eap_md5_t { + + /** + * Implemented eap_method_t interface. + */ + eap_method_t eap_method_interface; +}; + +/** + * @brief Creates the EAP method EAP-MD5. + * + * @param server ID of the EAP server + * @param peer ID of the EAP client + * @return eap_md5_t object + * + * @ingroup eap + */ +eap_md5_t *eap_create(eap_role_t role, + identification_t *server, identification_t *peer); + +#endif /* EAP_MD5_H_ */ diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c index e4a58f0a3..7434ca2a1 100644 --- a/src/charon/sa/authenticators/eap/eap_method.c +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -45,7 +45,10 @@ ENUM_NEXT(eap_type_names, EAP_SIM, EAP_SIM, EAP_TOKEN_CARD, "EAP_SIM"); ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM, "EAP_AKA"); -ENUM_END(eap_type_names, EAP_AKA); +ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_EXPERIMENTAL, EAP_AKA, + "EAP_EXPANDED", + "EAP_EXPERIMENTAL"); +ENUM_END(eap_type_names, EAP_EXPERIMENTAL); ENUM(eap_code_names, EAP_REQUEST, EAP_FAILURE, "EAP_REQUEST", @@ -67,6 +70,7 @@ typedef struct module_entry_t module_entry_t; */ struct module_entry_t { eap_type_t type; + u_int32_t vendor; void *handle; eap_constructor_t constructor; }; @@ -85,7 +89,8 @@ void eap_method_unload() while (modules->remove_last(modules, (void**)&entry) == SUCCESS) { - DBG2(DBG_CFG, "unloaded module for %N", eap_type_names, entry->type); + DBG2(DBG_CFG, "unloaded module EAP module %d-%d", + entry->type, entry->vendor); dlclose(entry->handle); free(entry); } @@ -165,11 +170,19 @@ void eap_method_load(char *directory) dlclose(module.handle); continue; } - module.type = method->get_type(method); + module.type = method->get_type(method, &module.vendor); method->destroy(method); - DBG1(DBG_CFG, " loaded EAP method %N successfully from %s", - eap_type_names, module.type, entry->d_name); + if (module.vendor) + { + DBG1(DBG_CFG, " loaded EAP method %d, vendor %d successfully from %s", + module.type, module.vendor, entry->d_name); + } + else + { + DBG1(DBG_CFG, " loaded EAP method %N successfully from %s", + eap_type_names, module.type, entry->d_name); + } loaded_module = malloc_thing(module_entry_t); memcpy(loaded_module, &module, sizeof(module)); @@ -181,9 +194,8 @@ void eap_method_load(char *directory) /* * 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 *eap_method_create(eap_type_t type, u_int32_t vendor, eap_role_t role, + identification_t *server, identification_t *peer) { eap_method_t *method = NULL; iterator_t *iterator; @@ -192,7 +204,7 @@ eap_method_t *eap_method_create(eap_type_t type, eap_role_t role, iterator = modules->create_iterator(modules, TRUE); while (iterator->iterate(iterator, (void**)&entry)) { - if (entry->type == type) + if (entry->type == type && entry->vendor == vendor) { method = entry->constructor(role, server, peer); if (method) @@ -205,8 +217,16 @@ eap_method_t *eap_method_create(eap_type_t type, eap_role_t role, if (method == NULL) { - DBG1(DBG_CFG, "no EAP module found for %N %N", - eap_type_names, type, eap_role_names, role); + if (vendor) + { + DBG1(DBG_CFG, "no vendor %d specific EAP module found for method " + "%d %N", vendor, type, eap_role_names, role); + } + else + { + DBG1(DBG_CFG, "no EAP module found for %N %N", + eap_type_names, type, eap_role_names, role); + } } return method; } diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h index d43dc001f..8675fd8ec 100644 --- a/src/charon/sa/authenticators/eap/eap_method.h +++ b/src/charon/sa/authenticators/eap/eap_method.h @@ -62,6 +62,8 @@ enum eap_type_t { EAP_TOKEN_CARD = 6, EAP_SIM = 18, EAP_AKA = 23, + EAP_EXPANDED = 254, + EAP_EXPERIMENTAL = 255, }; /** @@ -148,9 +150,10 @@ struct eap_method_t { * @brief Get the EAP type implemented in this method. * * @param this calling object + * @param vendor pointer receiving vendor identifier for type, 0 for none * @return type of the EAP method */ - eap_type_t (*get_type) (eap_method_t *this); + eap_type_t (*get_type) (eap_method_t *this, u_int32_t *vendor); /** * @brief Check if this EAP method authenticates the server. @@ -188,6 +191,7 @@ struct eap_method_t { * @brief Creates an EAP method for a specific type and role. * * @param eap_type EAP type to use + * @param eap_vendor vendor identifier if a vendor specifc EAP type is used * @param role role of the eap_method, server or peer * @param server ID of acting server * @param peer ID of involved peer (client) @@ -195,8 +199,9 @@ struct eap_method_t { * * @ingroup eap */ -eap_method_t *eap_method_create(eap_type_t eap_type, eap_role_t role, - identification_t *server, identification_t *peer); +eap_method_t *eap_method_create(eap_type_t eap_type, u_int32_t eap_vendor, + eap_role_t role, identification_t *server, + identification_t *peer); /** * @brief (Re-)Load all EAP modules in the EAP modules directory. diff --git a/src/charon/sa/authenticators/eap/eap_sim.c b/src/charon/sa/authenticators/eap/eap_sim.c index 38d7f2534..c9eb5ce8f 100644 --- a/src/charon/sa/authenticators/eap/eap_sim.c +++ b/src/charon/sa/authenticators/eap/eap_sim.c @@ -264,6 +264,7 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier } case AT_IDENTITY: { + u_int16_t act_len = data.len; /* align up to four byte */ if (data.len % 4) { @@ -275,7 +276,7 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier *pos.ptr = data.len/4 + 1; pos = chunk_skip(pos, 1); /* actual length in bytes */ - *(u_int16_t*)pos.ptr = htons(data.len); + *(u_int16_t*)pos.ptr = htons(act_len); pos = chunk_skip(pos, sizeof(u_int16_t)); memcpy(pos.ptr, data.ptr, data.len); pos = chunk_skip(pos, data.len); @@ -697,8 +698,9 @@ static status_t initiate(private_eap_sim_t *this, eap_payload_t **out) /** * Implementation of eap_method_t.get_type. */ -static eap_type_t get_type(private_eap_sim_t *this) +static eap_type_t get_type(private_eap_sim_t *this, u_int32_t *vendor) { + *vendor = 0; return EAP_SIM; } @@ -785,7 +787,7 @@ eap_sim_t *eap_create(eap_role_t role, /* public functions */ this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate; this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process; - this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type; + this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type; this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual; this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk; this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy; diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c index 6e2f73a43..6250604a6 100644 --- a/src/charon/sa/authenticators/eap_authenticator.c +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -138,7 +138,7 @@ static status_t build(private_eap_authenticator_t *this, chunk_t ike_sa_init, * Implementation of eap_authenticator_t.initiate */ static status_t initiate(private_eap_authenticator_t *this, eap_type_t type, - eap_payload_t **out) + u_int32_t vendor, eap_payload_t **out) { /* if initiate() is called, role is always server */ this->role = EAP_SERVER; @@ -151,21 +151,30 @@ static status_t initiate(private_eap_authenticator_t *this, eap_type_t type, return FAILED; } - DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type); - this->method = eap_method_create(type, this->role, + if (vendor) + { + DBG1(DBG_IKE, "requesting vendor specific EAP authentication %d-%d", + type, vendor); + } + else + { + DBG1(DBG_IKE, "requesting %N authentication", eap_type_names, type); + } + this->method = eap_method_create(type, vendor, this->role, this->ike_sa->get_my_id(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); 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); + + DBG1(DBG_IKE, "configured EAP server method not supported, sending %N", + 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", + DBG1(DBG_IKE, "failed to initiate EAP exchange, sending %N", eap_type_names, type, eap_code_names, EAP_FAILURE); *out = eap_payload_create_code(EAP_FAILURE); return FAILED; @@ -179,11 +188,14 @@ static status_t initiate(private_eap_authenticator_t *this, eap_type_t type, 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); + eap_type_t type; + u_int32_t vendor; - if (type == EAP_IDENTITY) + type = in->get_type(in, &vendor); + + if (!vendor && type == EAP_IDENTITY) { - eap_method_t *method = eap_method_create(type, EAP_PEER, + eap_method_t *method = eap_method_create(type, 0, EAP_PEER, this->ike_sa->get_other_id(this->ike_sa), this->ike_sa->get_my_id(this->ike_sa)); @@ -205,32 +217,57 @@ static status_t process_peer(private_eap_authenticator_t *this, /* 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, + if (vendor) + { + DBG1(DBG_IKE, "EAP server requested vendor specific EAP method %d-%d", + type, vendor); + } + else + { + DBG1(DBG_IKE, "EAP server requested %N authentication", + eap_type_names, type); + } + this->method = eap_method_create(type, vendor, EAP_PEER, this->ike_sa->get_other_id(this->ike_sa), this->ike_sa->get_my_id(this->ike_sa)); if (this->method == NULL) { DBG1(DBG_IKE, "EAP server requested unsupported " - "EAP method %N, sending EAP_NAK", eap_type_names, type); + "EAP method, sending EAP_NAK"); *out = eap_payload_create_nak(); return NEED_MORE; } } + type = this->method->get_type(this->method, &vendor); + switch (this->method->process(this->method, in, out)) { case NEED_MORE: return NEED_MORE; case SUCCESS: - DBG1(DBG_IKE, "EAP method %N succeded", - eap_type_names, this->method->get_type(this->method)); + if (vendor) + { + DBG1(DBG_IKE, "EAP vendor specific method %d-%d succeded", + type, vendor); + } + else + { + DBG1(DBG_IKE, "EAP method %N succeded", eap_type_names, type); + } return SUCCESS; case FAILED: default: - DBG1(DBG_IKE, "EAP method %N failed", - eap_type_names, this->method->get_type(this->method)); + if (vendor) + { + DBG1(DBG_IKE, "EAP vendor specific method %d-%d failed", + type, vendor); + } + else + { + DBG1(DBG_IKE, "EAP method %N failed", + eap_type_names, type); + } return FAILED; } } @@ -241,6 +278,11 @@ static status_t process_peer(private_eap_authenticator_t *this, static status_t process_server(private_eap_authenticator_t *this, eap_payload_t *in, eap_payload_t **out) { + eap_type_t type; + u_int32_t vendor; + + type = this->method->get_type(this->method, &vendor); + switch (this->method->process(this->method, in, out)) { case NEED_MORE: @@ -248,22 +290,35 @@ static status_t process_server(private_eap_authenticator_t *this, 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); } + if (vendor) + { + DBG1(DBG_IKE, "EAP vendor specific method %d-%d succeded, " + "%sMSK established", type, vendor, + this->msk.ptr ? "" : "no "); + } else { - DBG1(DBG_IKE, "EAP method %N succeded, no MSK established", - eap_type_names, this->method->get_type(this->method)); + DBG1(DBG_IKE, "EAP method %N succeded, %sMSK established", + eap_type_names, type, this->msk.ptr ? "" : "no "); } *out = eap_payload_create_code(EAP_SUCCESS); return SUCCESS; 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)); + if (vendor) + { + DBG1(DBG_IKE, "EAP vendor specific method %d-%d failed for " + "peer %D", type, vendor, + this->ike_sa->get_other_id(this->ike_sa)); + } + else + { + DBG1(DBG_IKE, "EAP method %N failed for peer %D", + eap_type_names, type, + this->ike_sa->get_other_id(this->ike_sa)); + } *out = eap_payload_create_code(EAP_FAILURE); return FAILED; } @@ -363,7 +418,7 @@ eap_authenticator_t *eap_authenticator_create(ike_sa_t *ike_sa) 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.initiate = (status_t(*)(eap_authenticator_t*,eap_type_t,u_int32_t,eap_payload_t**))initiate; this->public.process = (status_t(*)(eap_authenticator_t*,eap_payload_t*,eap_payload_t**))process; /* private data */ diff --git a/src/charon/sa/authenticators/eap_authenticator.h b/src/charon/sa/authenticators/eap_authenticator.h index 64a3267d7..cf2180ee3 100644 --- a/src/charon/sa/authenticators/eap_authenticator.h +++ b/src/charon/sa/authenticators/eap_authenticator.h @@ -105,15 +105,16 @@ struct eap_authenticator_t { * 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 + * @param this calling object + * @param type EAP method to use to authenticate client + * @param vendor EAP vendor identifier, if type is vendor specific, or 0 + * @param out created initiaal EAP message to send * @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); + u_int32_t vendor, eap_payload_t **out); /** * @brief Process an EAP message. diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 44f0298d5..b6c71a8b5 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -354,7 +354,7 @@ static void updown(private_child_sa_t *this, bool up) up ? "up" : "down", policy->my_ts->is_host(policy->my_ts, this->me.addr) ? "-host" : "-client", - this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6", + this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-v6", this->config->get_name(this->config), this->iface ? this->iface : "unknown", this->reqid, diff --git a/src/charon/sa/connect_manager.c b/src/charon/sa/connect_manager.c index d583e01bb..d0f3cde8d 100644 --- a/src/charon/sa/connect_manager.c +++ b/src/charon/sa/connect_manager.c @@ -32,13 +32,13 @@ #include <processing/jobs/initiate_mediation_job.h> #include <encoding/payloads/endpoint_notify.h> -// base timeout -// the sending interval is P2P_INTERVAL * active checklists (N) -// retransmission timeout is P2P_INTERVAL * N * checks in waiting state (NW) -#define P2P_INTERVAL 20 // 20 ms -// min retransmission timeout (RTO is P2P_INTERVAL * N * checks in waiting state) -#define P2P_RTO_MIN 100 // 100 ms -// max number of retransmissions (+ the initial check) +/* base timeout + * the sending interval is P2P_INTERVAL * active checklists (N) + * retransmission timeout is P2P_INTERVAL * N * checks in waiting state (NW) */ +#define P2P_INTERVAL 20 /* ms */ +/* min retransmission timeout (RTO is P2P_INTERVAL * N * checks in waiting state) */ +#define P2P_RTO_MIN 100 /* ms */ +/* max number of retransmissions (+ the initial check) */ #define P2P_MAX_RETRANS 2 @@ -212,7 +212,8 @@ static void check_list_destroy(check_list_t *this) DESTROY_OFFSET_IF(this->responder.endpoints, offsetof(endpoint_notify_t, destroy)); DESTROY_FUNCTION_IF(this->pairs, (void*)endpoint_pair_destroy); - DESTROY_IF(this->triggered); // this list contains some of the same elements as contained in this->pairs + /* this list contains some of the same elements as contained in this->pairs */ + DESTROY_IF(this->triggered); free(this); } @@ -489,8 +490,6 @@ static initiate_data_t *initiate_data_create(check_list_t *checklist, initiated_ return this; } -// ----------------------------------------------------------------------------- - /** * Find an initiated connection by the peers' ids */ @@ -641,9 +640,6 @@ static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, endpoi return status; } -// ----------------------------------------------------------------------------- - - /** * Updates the state of the whole checklist */ @@ -659,7 +655,8 @@ static void update_checklist_state(check_list_t *checklist) switch(current->state) { case CHECK_WAITING: - // at least one is still waiting -> checklist remains in waiting state + /* at least one is still waiting -> checklist remains + * in waiting state */ iterator->destroy(iterator); return; case CHECK_IN_PROGRESS: @@ -668,6 +665,8 @@ static void update_checklist_state(check_list_t *checklist) case CHECK_SUCCEEDED: succeeded = TRUE; break; + default: + break; } } iterator->destroy(iterator); @@ -832,7 +831,6 @@ static void prune_pairs(linked_list_t *pairs) { iterator_t *iterator, *search; endpoint_pair_t *current, *other; - bool inserted = FALSE; u_int32_t id = 0; iterator = pairs->create_iterator(pairs, TRUE); @@ -851,10 +849,10 @@ static void prune_pairs(linked_list_t *pairs) if (current->local->equals(current->local, other->local) && current->remote->equals(current->remote, other->remote)) { - // since the list of pairs is sorted by priority in descending - // order, and we iterate the list from the beginning, we are - // sure that the priority of 'other' is lower than that of - // 'current', remove it + /* since the list of pairs is sorted by priority in descending + * order, and we iterate the list from the beginning, we are + * sure that the priority of 'other' is lower than that of + * 'current', remove it */ DBG1(DBG_IKE, "pruning endpoint pair %H - %H with priority %d", other->local, other->remote, other->priority); search->remove(search); @@ -896,8 +894,6 @@ static void build_pairs(check_list_t *checklist) prune_pairs(checklist->pairs); } -// ----------------------------------------------------------------------------- - /** * Processes the payloads of a connectivity check and returns the extracted data */ @@ -936,7 +932,7 @@ static status_t process_payloads(message_t *message, check_t *check) } check->endpoint = endpoint; check->endpoint_raw = chunk_clone(notify->get_notification_data(notify)); - DBG3(DBG_IKE, "received P2P_ENDPOINT notify"); + DBG2(DBG_IKE, "received P2P_ENDPOINT notify"); break; } case P2P_SESSIONID: @@ -1000,9 +996,6 @@ static chunk_t build_signature(private_connect_manager_t *this, return sig_hash; } -// ----------------------------------------------------------------------------- - -// forward declarations static void queue_retransmission(private_connect_manager_t *this, chunk_t session_id, u_int32_t mid); static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time); static void finish_checks(private_connect_manager_t *this, check_list_t *checklist); @@ -1061,11 +1054,13 @@ retransmit_end: case CHECK_FAILED: finish_checks(this, checklist); break; + default: + break; } pthread_mutex_unlock(&(this->mutex)); - // we reschedule it manually + /* we reschedule it manually */ return JOB_REQUEUE_NONE; } @@ -1192,13 +1187,13 @@ static job_requeue_t sender(sender_data_t *data) check_destroy(check); - // schedule this job again + /* schedule this job again */ u_int32_t N = this->checklists->get_count(this->checklists); schedule_checks(this, checklist, P2P_INTERVAL * N); pthread_mutex_unlock(&(this->mutex)); - // we reschedule it manually + /* we reschedule it manually */ return JOB_REQUEUE_NONE; } @@ -1240,8 +1235,10 @@ static job_requeue_t initiate_mediated(initiate_data_t *data) } else { - // this should (can?) not happen + /* this should (can?) not happen */ } + + return JOB_REQUEUE_NONE; } /** @@ -1270,9 +1267,10 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli } } - //remove_checklist(this, checklist); - //check_list_destroy(checklist); - // FIXME: we should do this ^^^ after a specific timeout on the responder side + /* remove_checklist(this, checklist); + * check_list_destroy(checklist); + * FIXME: we should do this ^^^ after a specific timeout on the + * responder side */ } /** @@ -1313,6 +1311,8 @@ static void process_response(private_connect_manager_t *this, check_t *check, case CHECK_FAILED: finish_checks(this, checklist); break; + default: + break; } } else @@ -1343,16 +1343,16 @@ static void process_request(private_connect_manager_t *this, check_t *check, switch(pair->state) { case CHECK_IN_PROGRESS: - pair->retransmitted = P2P_MAX_RETRANS; // prevent retransmissions - // FIXME: we should wait to the next rto to send the triggered check - // fall-through + /* prevent retransmissions */ + pair->retransmitted = P2P_MAX_RETRANS; + /* FIXME: we should wait to the next rto to send the triggered check + * fall-through */ case CHECK_WAITING: case CHECK_FAILED: queue_triggered_check(checklist, pair); break; case CHECK_SUCCEEDED: default: - // do nothing break; } } @@ -1451,8 +1451,6 @@ static void process_check(private_connect_manager_t *this, message_t *message) check_destroy(check); } -// ----------------------------------------------------------------------------- - /** * Implementation of connect_manager_t.check_and_register. */ @@ -1568,7 +1566,8 @@ static status_t set_responder_data(private_connect_manager_t *this, build_pairs(checklist); - schedule_checks(this, checklist, 0); // send the first check immediately + /* send the first check immediately */ + schedule_checks(this, checklist, 0); pthread_mutex_unlock(&(this->mutex)); diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 9d7a17e89..9cada2cb5 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -51,6 +51,7 @@ #include <sa/tasks/ike_natd.h> #include <sa/tasks/ike_mobike.h> #include <sa/tasks/ike_auth.h> +#include <sa/tasks/ike_auth_lifetime.h> #include <sa/tasks/ike_config.h> #include <sa/tasks/ike_cert.h> #include <sa/tasks/ike_rekey.h> @@ -68,6 +69,7 @@ #ifdef P2P #include <sa/tasks/ike_p2p.h> +#include <processing/jobs/initiate_mediation_job.h> #endif #ifndef RESOLV_CONF @@ -248,6 +250,8 @@ struct private_ike_sa_t { u_int32_t established; /** when IKE_SA gets rekeyed */ u_int32_t rekey; + /** when IKE_SA gets reauthenticated */ + u_int32_t reauth; /** when IKE_SA gets deleted */ u_int32_t delete; } time; @@ -256,6 +260,11 @@ struct private_ike_sa_t { * how many times we have retried so far (keyingtries) */ u_int32_t keyingtry; + + /** + * are we the initiator of this IKE_SA (rekeying does not affect this flag) + */ + bool ike_initiator; }; /** @@ -307,16 +316,31 @@ static char *get_name(private_ike_sa_t *this) return "(unnamed)"; } - /** - * Implementation of ike_sa_t.get_stats. + * Implementation of ike_sa_t.get_statistic. */ -static void get_stats(private_ike_sa_t *this, u_int32_t *next_rekeying) +static u_int32_t get_statistic(private_ike_sa_t *this, statistic_t kind) { - if (next_rekeying) + time_t now = time(NULL); + + switch (kind) { - *next_rekeying = this->time.rekey; + case STAT_REKEY_TIME: + if (this->time.rekey > now) + { + return this->time.rekey - now; + } + break; + case STAT_REAUTH_TIME: + if (this->time.reauth > now) + { + return this->time.reauth - now; + } + break; + default: + break; } + return 0; } /** @@ -493,10 +517,6 @@ static void set_condition(private_ike_sa_t *this, ike_condition_t condition, this->conditions |= condition; switch (condition) { - case COND_STALE: - DBG1(DBG_IKE, "no route to %H, setting IKE_SA to stale", - this->other_host); - break; case COND_NAT_HERE: DBG1(DBG_IKE, "local host is behind NAT, sending keep alives"); this->conditions |= COND_NAT_ANY; @@ -519,9 +539,6 @@ static void set_condition(private_ike_sa_t *this, ike_condition_t condition, this->conditions &= ~condition; switch (condition) { - case COND_STALE: - DBG1(DBG_IKE, "new route to %H found", this->other_host); - break; case COND_NAT_HERE: case COND_NAT_FAKE: case COND_NAT_THERE: @@ -610,36 +627,58 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) if (this->state == IKE_CONNECTING) { job_t *job; - u_int32_t now = time(NULL); - u_int32_t soft, hard; - bool reauth; + u_int32_t t; - this->time.established = now; - /* start DPD checks */ - send_dpd(this); + /* calculate rekey, reauth and lifetime */ + this->time.established = time(NULL); - /* schedule rekeying/reauthentication */ - soft = this->peer_cfg->get_lifetime(this->peer_cfg, TRUE); - hard = this->peer_cfg->get_lifetime(this->peer_cfg, FALSE); - reauth = this->peer_cfg->use_reauth(this->peer_cfg); - DBG1(DBG_IKE, "scheduling %s in %ds, maximum lifetime %ds", - reauth ? "reauthentication": "rekeying", soft, hard); - - if (soft) + /* schedule rekeying if we have a time which is smaller than + * an already scheduled rekeying */ + t = this->peer_cfg->get_rekey_time(this->peer_cfg); + if (t && (this->time.rekey == 0 || + (this->time.rekey > t + this->time.established))) { - this->time.rekey = now + soft; - job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth); - charon->scheduler->schedule_job(charon->scheduler, job, - soft * 1000); + this->time.rekey = t + this->time.established; + job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, FALSE); + charon->scheduler->schedule_job(charon->scheduler, + job, t * 1000); + DBG1(DBG_IKE, "scheduling rekeying in %ds", t); } - - if (hard) + t = this->peer_cfg->get_reauth_time(this->peer_cfg); + if (t && (this->time.reauth == 0 || + (this->time.reauth > t + this->time.established))) { - this->time.delete = now + hard; + this->time.reauth = t + this->time.established; + job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE); + charon->scheduler->schedule_job(charon->scheduler, + job, t * 1000); + DBG1(DBG_IKE, "scheduling reauthentication in %ds", t); + } + t = this->peer_cfg->get_over_time(this->peer_cfg); + if (this->time.rekey || this->time.reauth) + { + if (this->time.reauth == 0) + { + this->time.delete = this->time.rekey; + } + else if (this->time.rekey == 0) + { + this->time.delete = this->time.reauth; + } + else + { + this->time.delete = min(this->time.rekey, this->time.reauth); + } + this->time.delete += t; + t = this->time.delete - this->time.established; job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); charon->scheduler->schedule_job(charon->scheduler, job, - hard * 1000); + t * 1000); + DBG1(DBG_IKE, "maximum IKE_SA lifetime %ds", t); } + + /* start DPD checks */ + send_dpd(this); } break; } @@ -681,17 +720,17 @@ static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip) { if (local) { - if (this->my_virtual_ip) - { - DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip); - charon->kernel_interface->del_ip(charon->kernel_interface, - this->my_virtual_ip); - this->my_virtual_ip->destroy(this->my_virtual_ip); - } DBG1(DBG_IKE, "installing new virtual IP %H", ip); if (charon->kernel_interface->add_ip(charon->kernel_interface, ip, this->my_host) == SUCCESS) { + if (this->my_virtual_ip) + { + DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip); + charon->kernel_interface->del_ip(charon->kernel_interface, + this->my_virtual_ip); + } + DESTROY_IF(this->my_virtual_ip); this->my_virtual_ip = ip->clone(ip); } else @@ -859,6 +898,8 @@ static void send_notify_response(private_ike_sa_t *this, message_t *request, this->other_host = request->get_source(request); this->other_host = this->other_host->clone(this->other_host); } + response->set_source(response, this->my_host->clone(this->my_host)); + response->set_destination(response, this->other_host->clone(this->other_host)); if (generate_message(this, response, &packet) == SUCCESS) { charon->sender->send(charon->sender, packet); @@ -973,6 +1014,8 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) return DESTROY_ME; } + this->ike_initiator = TRUE; + task = (task_t*)ike_init_create(&this->public, TRUE, NULL); this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_natd_create(&this->public, TRUE); @@ -983,6 +1026,8 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_config_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_lifetime_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); if (this->peer_cfg->use_mobike(this->peer_cfg)) { task = (task_t*)ike_mobike_create(&this->public, TRUE); @@ -997,7 +1042,7 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) #ifdef P2P if (this->peer_cfg->get_mediated_by(this->peer_cfg)) { - // mediated connection, initiate mediation process + /* mediated connection, initiate mediation process */ job_t *job = (job_t*)initiate_mediation_job_create(this->ike_sa_id, child_cfg); child_cfg->destroy(child_cfg); charon->processor->queue_job(charon->processor, job); @@ -1006,14 +1051,14 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) else if (this->peer_cfg->is_mediation(this->peer_cfg)) { if (this->state == IKE_ESTABLISHED) - {// FIXME: we should try to find a better solution to this + { /* FIXME: we should try to find a better solution to this */ SIG(CHILD_UP_SUCCESS, "mediation connection is already up and running"); } } else #endif /* P2P */ { - // normal IKE_SA with CHILD_SA + /* normal IKE_SA with CHILD_SA */ task = (task_t*)child_create_create(&this->public, child_cfg); child_cfg->destroy(child_cfg); this->task_manager->queue_task(this->task_manager, task); @@ -1026,7 +1071,7 @@ static status_t initiate(private_ike_sa_t *this, child_cfg_t *child_cfg) * Implementation of ike_sa_t.acquire. */ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) -{// FIXME: P2P-NAT-T +{ /* FIXME: P2P-NAT-T */ child_cfg_t *child_cfg; iterator_t *iterator; child_sa_t *current, *child_sa = NULL; @@ -1073,6 +1118,8 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) this->task_manager->queue_task(this->task_manager, task); task = (task_t*)ike_config_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_auth_lifetime_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); if (this->peer_cfg->use_mobike(this->peer_cfg)) { task = (task_t*)ike_mobike_create(&this->public, TRUE); @@ -1350,7 +1397,7 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) * Implementation of ike_sa_t.retransmit. */ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) -{// FIXME: P2P-NAT-T +{ /* FIXME: P2P-NAT-T */ this->time.outbound = time(NULL); if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS) { @@ -1467,6 +1514,8 @@ static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id) task = (task_t*)child_create_create(&new->public, child_cfg); new->task_manager->queue_task(new->task_manager, task); } + task = (task_t*)ike_auth_lifetime_create(&new->public, TRUE); + new->task_manager->queue_task(new->task_manager, task); if (this->peer_cfg->use_mobike(this->peer_cfg)) { task = (task_t*)ike_mobike_create(&new->public, TRUE); @@ -1900,14 +1949,59 @@ static status_t rekey(private_ike_sa_t *this) static status_t reestablish(private_ike_sa_t *this) { task_t *task; - + + /* we can't reauthenticate as responder when we use EAP or virtual IPs. + * If the peer does not support RFC4478, there is no way to keep the + * IKE_SA up. */ + if (!this->ike_initiator) + { + DBG1(DBG_IKE, "initiator did not reauthenticate as requested"); + if (this->other_virtual_ip != NULL || + has_condition(this, COND_EAP_AUTHENTICATED)) + { + time_t now = time(NULL); + + DBG1(DBG_IKE, "IKE_SA will timeout in %#V", &now, &this->time.delete); + return FAILED; + } + else + { + DBG1(DBG_IKE, "reauthenticating actively"); + } + } task = (task_t*)ike_reauth_create(&this->public); this->task_manager->queue_task(this->task_manager, task); - + return this->task_manager->initiate(this->task_manager); } /** + * Implementation of ike_sa_t.set_auth_lifetime. + */ +static void set_auth_lifetime(private_ike_sa_t *this, u_int32_t lifetime) +{ + job_t *job; + u_int32_t reduction = this->peer_cfg->get_over_time(this->peer_cfg); + + this->time.reauth = time(NULL) + lifetime - reduction; + job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE); + + if (lifetime < reduction) + { + DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, starting reauthentication", + lifetime); + charon->processor->queue_job(charon->processor, job); + } + else + { + DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling reauthentication" + " in %ds", lifetime, lifetime - reduction); + charon->scheduler->schedule_job(charon->scheduler, job, + (lifetime - reduction) * 1000); + } +} + +/** * Implementation of ike_sa_t.roam. */ static status_t roam(private_ike_sa_t *this, bool address) @@ -1933,7 +2027,6 @@ static status_t roam(private_ike_sa_t *this, bool address) me = charon->kernel_interface->get_source_addr(charon->kernel_interface, other); - set_condition(this, COND_STALE, FALSE); if (me) { if (me->ip_equals(me, this->my_host) && @@ -1977,6 +2070,7 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) this->other_host = other->other_host->clone(other->other_host); this->my_id = other->my_id->clone(other->my_id); this->other_id = other->other_id->clone(other->other_id); + this->ike_initiator = other->ike_initiator; /* apply virtual assigned IPs... */ if (other->my_virtual_ip) @@ -2007,6 +2101,24 @@ static status_t inherit(private_ike_sa_t *this, private_ike_sa_t *other) /* move pending tasks to the new IKE_SA */ this->task_manager->adopt_tasks(this->task_manager, other->task_manager); + /* reauthentication timeout survives a rekeying */ + if (other->time.reauth) + { + time_t reauth, delete, now = time(NULL); + + this->time.reauth = other->time.reauth; + reauth = this->time.reauth - now; + delete = reauth + this->peer_cfg->get_over_time(this->peer_cfg); + this->time.delete = this->time.reauth + delete; + DBG1(DBG_IKE, "rescheduling reauthentication in %ds after rekeying, " + "lifetime reduced to %ds", reauth, delete); + charon->scheduler->schedule_job(charon->scheduler, + (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE), + reauth * 1000); + charon->scheduler->schedule_job(charon->scheduler, + (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE), + delete * 1000); + } /* we have to initate here, there may be new tasks to handle */ return this->task_manager->initiate(this->task_manager); } @@ -2177,7 +2289,7 @@ static void destroy(private_ike_sa_t *this) if (this->peer_cfg && this->peer_cfg->is_mediation(this->peer_cfg) && !this->ike_sa_id->is_initiator(this->ike_sa_id)) { - // mediation server + /* mediation server */ charon->mediation_manager->remove(charon->mediation_manager, this->ike_sa_id); } DESTROY_IF(this->server_reflexive_host); @@ -2207,8 +2319,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) /* Public functions */ this->public.get_state = (ike_sa_state_t (*)(ike_sa_t*)) get_state; this->public.set_state = (void (*)(ike_sa_t*,ike_sa_state_t)) set_state; - this->public.get_stats = (void (*)(ike_sa_t*,u_int32_t*))get_stats; this->public.get_name = (char* (*)(ike_sa_t*))get_name; + this->public.get_statistic = (u_int32_t(*)(ike_sa_t*, statistic_t kind))get_statistic; this->public.process_message = (status_t (*)(ike_sa_t*, message_t*)) process_message; this->public.initiate = (status_t (*)(ike_sa_t*,child_cfg_t*)) initiate; this->public.route = (status_t (*)(ike_sa_t*,child_cfg_t*)) route; @@ -2256,6 +2368,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa; this->public.rekey = (status_t (*)(ike_sa_t*))rekey; this->public.reestablish = (status_t (*)(ike_sa_t*))reestablish; + this->public.set_auth_lifetime = (void(*)(ike_sa_t*, u_int32_t lifetime))set_auth_lifetime; this->public.roam = (status_t(*)(ike_sa_t*,bool))roam; this->public.inherit = (status_t (*)(ike_sa_t*,ike_sa_t*))inherit; this->public.generate_message = (status_t (*)(ike_sa_t*,message_t*,packet_t**))generate_message; @@ -2296,6 +2409,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->time.inbound = this->time.outbound = time(NULL); this->time.established = 0; this->time.rekey = 0; + this->time.reauth = 0; this->time.delete = 0; this->ike_cfg = NULL; this->peer_cfg = NULL; @@ -2307,6 +2421,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->additional_addresses = linked_list_create(); this->pending_updates = 0; this->keyingtry = 0; + this->ike_initiator = FALSE; #ifdef P2P this->server_reflexive_host = NULL; #endif /* P2P */ diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 99f09e98a..975447d9c 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -29,6 +29,7 @@ typedef enum ike_extension_t ike_extension_t; typedef enum ike_condition_t ike_condition_t; typedef enum ike_sa_state_t ike_sa_state_t; +typedef enum statistic_t statistic_t; typedef struct ike_sa_t ike_sa_t; #include <library.h> @@ -115,9 +116,25 @@ enum ike_condition_t { COND_NAT_FAKE = (1<<3), /** - * peer is currently not reachable (due missing route, ...) + * peer has ben authenticated using EAP */ - COND_STALE = (1<<4), + COND_EAP_AUTHENTICATED = (1<<4), +}; + +/** + * Information and statistics to query from an SA + */ +enum statistic_t { + + /** + * Relative time for scheduled rekeying + */ + STAT_REKEY_TIME, + + /** + * Relative time for scheduled reauthentication + */ + STAT_REAUTH_TIME, }; /** @@ -234,13 +251,6 @@ struct ike_sa_t { ike_sa_state_t (*get_state) (ike_sa_t *this); /** - * @brief Get some statistics about this IKE_SA. - * - * @param next_rekeying when the next rekeying is scheduled - */ - void (*get_stats)(ike_sa_t *this, u_int32_t *next_rekeying); - - /** * @brief Set the state of the IKE_SA. * * @param this calling object @@ -257,6 +267,15 @@ struct ike_sa_t { char* (*get_name) (ike_sa_t *this); /** + * @brief Get statistic values from the IKE_SA. + * + * @param this calling object + * @param kind kind of requested value + * @return value as integer + */ + u_int32_t (*get_statistic)(ike_sa_t *this, statistic_t kind); + + /** * @brief Get the own host address. * * @param this calling object @@ -846,6 +865,14 @@ struct ike_sa_t { status_t (*reestablish) (ike_sa_t *this); /** + * @brief Set the lifetime limit received from a AUTH_LIFETIME notify. + * + * @param this calling object + * @param lifetime lifetime in seconds + */ + void (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); + + /** * @brief Set the virtual IP to use for this IKE_SA and its children. * * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same diff --git a/src/charon/sa/mediation_manager.c b/src/charon/sa/mediation_manager.c index fca53a940..f6137304d 100644 --- a/src/charon/sa/mediation_manager.c +++ b/src/charon/sa/mediation_manager.c @@ -240,7 +240,7 @@ static void update_sa_id(private_mediation_manager_t *this, identification_t *pe DBG2(DBG_IKE, "changing registered IKE_SA ID of peer '%D'", peer_id); peer->ike_sa_id = ike_sa_id ? ike_sa_id->clone(ike_sa_id) : NULL; - // send callbacks to registered peers + /* send callbacks to registered peers */ identification_t *requester; while(peer->requested_by->remove_last(peer->requested_by, (void**)&requester) == SUCCESS) { @@ -295,7 +295,7 @@ static ike_sa_id_t *check_and_register(private_mediation_manager_t *this, if (!peer->ike_sa_id) { - // the peer is not online + /* the peer is not online */ DBG2(DBG_IKE, "requested peer '%D' is offline, registering peer '%D'", peer_id, requester); register_peer(peer, requester); pthread_mutex_unlock(&(this->mutex)); diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c index f4484774e..89f527aba 100644 --- a/src/charon/sa/task_manager.c +++ b/src/charon/sa/task_manager.c @@ -30,6 +30,7 @@ #include <sa/tasks/ike_natd.h> #include <sa/tasks/ike_mobike.h> #include <sa/tasks/ike_auth.h> +#include <sa/tasks/ike_auth_lifetime.h> #include <sa/tasks/ike_cert.h> #include <sa/tasks/ike_rekey.h> #include <sa/tasks/ike_delete.h> @@ -338,6 +339,7 @@ static status_t build_request(private_task_manager_t *this) activate_task(this, IKE_AUTHENTICATE); activate_task(this, IKE_CONFIG); activate_task(this, CHILD_CREATE); + activate_task(this, IKE_AUTH_LIFETIME); activate_task(this, IKE_MOBIKE); } break; @@ -690,19 +692,21 @@ static status_t process_request(private_task_manager_t *this, #ifdef P2P task = (task_t*)ike_p2p_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); -#endif /* P2P */ +#endif /* P2P */ task = (task_t*)ike_auth_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t*)ike_config_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t*)child_create_create(this->ike_sa, NULL); this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t*)ike_mobike_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); break; } case CREATE_CHILD_SA: - {//FIXME: we should prevent this on mediation connections + { /* FIXME: we should prevent this on mediation connections */ bool notify_found = FALSE, ts_found = FALSE; iterator = message->get_payload_iterator(message); while (iterator->iterate(iterator, (void**)&payload)) @@ -772,8 +776,12 @@ static status_t process_request(private_task_manager_t *this, case UNACCEPTABLE_ADDRESSES: case UNEXPECTED_NAT_DETECTED: case COOKIE2: - task = (task_t*)ike_mobike_create(this->ike_sa, - FALSE); + task = (task_t*)ike_mobike_create( + this->ike_sa, FALSE); + break; + case AUTH_LIFETIME: + task = (task_t*)ike_auth_lifetime_create( + this->ike_sa, FALSE); break; default: break; diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c index a3cd6a2bc..de88a0abe 100644 --- a/src/charon/sa/tasks/ike_auth.c +++ b/src/charon/sa/tasks/ike_auth.c @@ -297,6 +297,7 @@ static status_t collect_other_init_data(private_ike_auth_t *this, message_t *mes return NEED_MORE; } + /** * Implementation of task_t.build to create AUTH payload from EAP data */ @@ -520,6 +521,7 @@ static status_t process_r(private_ike_auth_t *this, message_t *message) break; case NOT_FOUND: /* use EAP if no AUTH payload found */ + this->ike_sa->set_condition(this->ike_sa, COND_EAP_AUTHENTICATED, TRUE); this->eap_auth = eap_authenticator_create(this->ike_sa); break; default: @@ -546,6 +548,7 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) { peer_cfg_t *config; eap_type_t eap_type; + u_int32_t eap_vendor; eap_payload_t *eap_payload; status_t status; @@ -590,10 +593,11 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); return FAILED; } - + /* initiate EAP authenitcation */ - eap_type = config->get_eap_type(config); - status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_payload); + eap_type = config->get_eap_type(config, &eap_vendor); + status = this->eap_auth->initiate(this->eap_auth, eap_type, + eap_vendor, &eap_payload); message->add_payload(message, (payload_t*)eap_payload); if (status != NEED_MORE) { @@ -645,6 +649,12 @@ static status_t process_i(private_ike_auth_t *this, message_t *message) case ADDITIONAL_IP6_ADDRESS: /* handled in ike_mobike task */ break; + case AUTH_LIFETIME: + /* handled in ike_auth_lifetime task */ + break; + case P2P_ENDPOINT: + /* handled in ike_p2p task */ + break; default: { if (type < 16383) diff --git a/src/charon/sa/tasks/ike_auth_lifetime.c b/src/charon/sa/tasks/ike_auth_lifetime.c new file mode 100644 index 000000000..9d37ec608 --- /dev/null +++ b/src/charon/sa/tasks/ike_auth_lifetime.c @@ -0,0 +1,200 @@ +/** + * @file ike_auth_lifetime.c + * + * @brief Implementation of the ike_auth_lifetime task. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "ike_auth_lifetime.h" + +#include <daemon.h> +#include <encoding/payloads/notify_payload.h> + + +typedef struct private_ike_auth_lifetime_t private_ike_auth_lifetime_t; + +/** + * Private members of a ike_auth_lifetime_t task. + */ +struct private_ike_auth_lifetime_t { + + /** + * Public methods and task_t interface. + */ + ike_auth_lifetime_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; +}; + +/** + * add the AUTH_LIFETIME notify to the message + */ +static void add_auth_lifetime(private_ike_auth_lifetime_t *this, message_t *message) +{ + chunk_t chunk; + u_int32_t lifetime; + + lifetime = this->ike_sa->get_statistic(this->ike_sa, STAT_REAUTH_TIME); + if (lifetime) + { + chunk = chunk_from_thing(lifetime); + *(u_int32_t*)chunk.ptr = htonl(lifetime); + message->add_notify(message, FALSE, AUTH_LIFETIME, chunk); + } +} + +/** + * read notifys from message and evaluate them + */ +static void process_payloads(private_ike_auth_lifetime_t *this, message_t *message) +{ + iterator_t *iterator; + payload_t *payload; + notify_payload_t *notify; + + iterator = message->get_payload_iterator(message); + while (iterator->iterate(iterator, (void**)&payload)) + { + if (payload->get_type(payload) == NOTIFY) + { + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + case AUTH_LIFETIME: + { + chunk_t data = notify->get_notification_data(notify); + u_int32_t lifetime = ntohl(*(u_int32_t*)data.ptr); + this->ike_sa->set_auth_lifetime(this->ike_sa, lifetime); + break; + } + default: + break; + } + } + } + iterator->destroy(iterator); +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t build_i(private_ike_auth_lifetime_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == INFORMATIONAL) + { + add_auth_lifetime(this, message); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process for responder + */ +static status_t process_r(private_ike_auth_lifetime_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == INFORMATIONAL) + { + process_payloads(this, message); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_auth_lifetime_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) + { + add_auth_lifetime(this, message); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.process for initiator + */ +static status_t process_i(private_ike_auth_lifetime_t *this, message_t *message) +{ + if (message->get_exchange_type(message) == IKE_AUTH && + this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) + { + process_payloads(this, message); + return SUCCESS; + } + return NEED_MORE; +} + +/** + * Implementation of task_t.get_type + */ +static task_type_t get_type(private_ike_auth_lifetime_t *this) +{ + return IKE_AUTH_LIFETIME; +} + +/** + * Implementation of task_t.migrate + */ +static void migrate(private_ike_auth_lifetime_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; +} + +/** + * Implementation of task_t.destroy + */ +static void destroy(private_ike_auth_lifetime_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_auth_lifetime_t *this = malloc_thing(private_ike_auth_lifetime_t); + + this->public.task.get_type = (task_type_t(*)(task_t*))get_type; + this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; + this->public.task.destroy = (void(*)(task_t*))destroy; + + if (initiator) + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; + } + else + { + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; + } + + this->ike_sa = ike_sa; + + return &this->public; +} + diff --git a/src/charon/sa/tasks/ike_auth_lifetime.h b/src/charon/sa/tasks/ike_auth_lifetime.h new file mode 100644 index 000000000..500b89d39 --- /dev/null +++ b/src/charon/sa/tasks/ike_auth_lifetime.h @@ -0,0 +1,61 @@ +/** + * @file ike_auth_lifetime.h + * + * @brief Interface ike_auth_lifetime_t. + * + */ + +/* + * Copyright (C) 2007 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef IKE_AUTH_LIFETIME_H_ +#define IKE_AUTH_LIFETIME_H_ + +typedef struct ike_auth_lifetime_t ike_auth_lifetime_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * @brief Task of type IKE_AUTH_LIFETIME, implements RFC4478. + * + * This task exchanges lifetimes for IKE_AUTH to force a client to + * reauthenticate before the responders lifetime reaches the limit. + * + * @b Constructors: + * - ike_auth_lifetime_create() + * + * @ingroup tasks + */ +struct ike_auth_lifetime_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * @brief Create a new IKE_AUTH_LIFETIME task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if taks is initiated by us + * @return ike_auth_lifetime task to handle by the task_manager + */ +ike_auth_lifetime_t *ike_auth_lifetime_create(ike_sa_t *ike_sa, bool initiator); + +#endif /* IKE_MOBIKE_H_ */ + diff --git a/src/charon/sa/tasks/ike_mobike.c b/src/charon/sa/tasks/ike_mobike.c index d1fc8c695..a53c243f0 100644 --- a/src/charon/sa/tasks/ike_mobike.c +++ b/src/charon/sa/tasks/ike_mobike.c @@ -287,7 +287,7 @@ static void transmit(private_ike_mobike_t *this, packet_t *packet) static status_t build_i(private_ike_mobike_t *this, message_t *message) { if (message->get_exchange_type(message) == IKE_AUTH && - message->get_payload(message, SECURITY_ASSOCIATION)) + message->get_payload(message, ID_INITIATOR)) { message->add_notify(message, FALSE, MOBIKE_SUPPORTED, chunk_empty); build_address_list(this, message); @@ -317,7 +317,7 @@ static status_t build_i(private_ike_mobike_t *this, message_t *message) static status_t process_r(private_ike_mobike_t *this, message_t *message) { if (message->get_exchange_type(message) == IKE_AUTH && - message->get_payload(message, SECURITY_ASSOCIATION)) + message->get_payload(message, ID_INITIATOR)) { process_payloads(this, message); } @@ -348,7 +348,7 @@ static status_t process_r(private_ike_mobike_t *this, message_t *message) static status_t build_r(private_ike_mobike_t *this, message_t *message) { if (message->get_exchange_type(message) == IKE_AUTH && - message->get_payload(message, SECURITY_ASSOCIATION)) + this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) { if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) { @@ -378,7 +378,7 @@ static status_t build_r(private_ike_mobike_t *this, message_t *message) static status_t process_i(private_ike_mobike_t *this, message_t *message) { if (message->get_exchange_type(message) == IKE_AUTH && - message->get_payload(message, SECURITY_ASSOCIATION)) + this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) { process_payloads(this, message); return SUCCESS; diff --git a/src/charon/sa/tasks/ike_p2p.c b/src/charon/sa/tasks/ike_p2p.c index de5a2e30e..84b88e16b 100644 --- a/src/charon/sa/tasks/ike_p2p.c +++ b/src/charon/sa/tasks/ike_p2p.c @@ -34,7 +34,7 @@ #define P2P_SESSIONID_LEN 8 #define P2P_SESSIONKEY_LEN 16 -// FIXME: proposed values +/* FIXME: proposed values */ #define P2P_SESSIONID_MIN_LEN 4 #define P2P_SESSIONID_MAX_LEN 16 #define P2P_SESSIONKEY_MIN_LEN 8 @@ -119,8 +119,6 @@ struct private_ike_p2p_t { }; -// ----------------------------------------------------------------------------- - /** * Adds a list of endpoints as notifies to a given message */ @@ -146,7 +144,7 @@ static void gather_and_add_endpoints(private_ike_p2p_t *this, message_t *message host_t *addr, *host; u_int16_t port; - // get the port that is used to communicate with the ms + /* get the port that is used to communicate with the ms */ host = this->ike_sa->get_my_host(this->ike_sa); port = host->get_port(host); @@ -215,7 +213,8 @@ static void process_payloads(private_ike_p2p_t *this, message_t *message) DBG1(DBG_IKE, "received invalid P2P_ENDPOINT notify"); break; } - DBG2(DBG_IKE, "received P2P_ENDPOINT notify"); + DBG1(DBG_IKE, "received %N P2P_ENDPOINT %#H", p2p_endpoint_type_names, + endpoint->get_type(endpoint), endpoint->get_host(endpoint)); this->remote_endpoints->insert_last(this->remote_endpoints, endpoint); break; @@ -253,8 +252,6 @@ static void process_payloads(private_ike_p2p_t *this, message_t *message) iterator->destroy(iterator); } -// ----------------------------------------------------------------------------- - /** * Implementation of task_t.process for initiator */ @@ -296,8 +293,8 @@ static status_t build_i(private_ike_p2p_t *this, message_t *message) if (!this->response) { - // only the initiator creates a session ID. the responder returns - // the session ID that it received from the initiator + /* only the initiator creates a session ID. the responder returns + * the session ID that it received from the initiator */ if (rand->allocate_pseudo_random_bytes(rand, P2P_SESSIONID_LEN, &this->session_id) != SUCCESS) { @@ -326,7 +323,7 @@ static status_t build_i(private_ike_p2p_t *this, message_t *message) } else { - // FIXME: should we make that configurable + /* FIXME: should we make that configurable */ message->add_notify(message, FALSE, P2P_CALLBACK, chunk_empty); } @@ -334,8 +331,9 @@ static status_t build_i(private_ike_p2p_t *this, message_t *message) break; } + default: + break; } - return NEED_MORE; } @@ -387,11 +385,11 @@ static status_t process_r(private_ike_p2p_t *this, message_t *message) } DBG1(DBG_IKE, "received P2P_CONNECT"); - break; } + default: + break; } - return NEED_MORE; } @@ -420,16 +418,16 @@ static status_t build_r(private_ike_p2p_t *this, message_t *message) if (this->response) { - // FIXME: handle result of set_responder_data - // as initiator, upon receiving a response from another peer, - // update the checklist and start sending checks + /* FIXME: handle result of set_responder_data + * as initiator, upon receiving a response from another peer, + * update the checklist and start sending checks */ charon->connect_manager->set_responder_data(charon->connect_manager, this->session_id, this->session_key, this->remote_endpoints); } else { - // FIXME: handle result of set_initiator_data - // as responder, create a checklist with the initiator's data + /* FIXME: handle result of set_initiator_data + * as responder, create a checklist with the initiator's data */ charon->connect_manager->set_initiator_data(charon->connect_manager, this->peer_id, this->ike_sa->get_my_id(this->ike_sa), this->session_id, this->session_key, this->remote_endpoints, @@ -440,9 +438,10 @@ static status_t build_r(private_ike_p2p_t *this, message_t *message) return FAILED; } } - break; } + default: + break; } return SUCCESS; } @@ -469,20 +468,19 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) case IKE_AUTH: { process_payloads(this, message); - - //FIXME: we should update the server reflexive endpoint somehow, if mobike notices a change - + /* FIXME: we should update the server reflexive endpoint somehow, + * if mobike notices a change */ endpoint_notify_t *reflexive; - if (this->remote_endpoints->get_first(this->remote_endpoints, (void**)&reflexive) == SUCCESS && - reflexive->get_type(reflexive) == SERVER_REFLEXIVE) - {//FIXME: should we accept this endpoint even if we did not send a request? + if (this->remote_endpoints->get_first(this->remote_endpoints, + (void**)&reflexive) == SUCCESS && + reflexive->get_type(reflexive) == SERVER_REFLEXIVE) + { /* FIXME: should we accept this endpoint even if we did not send + * a request? */ host_t *endpoint = reflexive->get_host(reflexive); - DBG2(DBG_IKE, "received server reflexive endpoint %#H", endpoint); this->ike_sa->set_server_reflexive_host(this->ike_sa, endpoint->clone(endpoint)); } - - // FIXME: what if it failed? e.g. AUTH failure + /* FIXME: what if it failed? e.g. AUTH failure */ SIG(CHILD_UP_SUCCESS, "established mediation connection without CHILD_SA successfully"); break; @@ -494,22 +492,23 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) if (this->failed) { DBG1(DBG_IKE, "peer '%D' is not online", this->peer_id); - // FIXME: notify the mediated connection (job?) - // FIXME: probably delete the created checklist, at least as responder + /* FIXME: notify the mediated connection (job?) + * FIXME: probably delete the created checklist, at least as + * responder */ } else { if (this->response) { - // FIXME: handle result of set_responder_data - // as responder, we update the checklist and start sending checks + /* FIXME: handle result of set_responder_data. + * as responder, we update the checklist and start sending checks */ charon->connect_manager->set_responder_data(charon->connect_manager, this->session_id, this->session_key, this->local_endpoints); } else { - // FIXME: handle result of set_initiator_data - // as initiator, we create a checklist and set the initiator's data + /* FIXME: handle result of set_initiator_data + * as initiator, we create a checklist and set the initiator's data */ charon->connect_manager->set_initiator_data(charon->connect_manager, this->ike_sa->get_my_id(this->ike_sa), this->peer_id, this->session_id, this->session_key, this->local_endpoints, @@ -518,12 +517,12 @@ static status_t process_i(private_ike_p2p_t *this, message_t *message) } break; } + default: + break; } return SUCCESS; } -// ----------------------------------------------------------------------------- - /** * Implementation of task_t.process for initiator (mediation server) */ @@ -542,21 +541,19 @@ static status_t build_i_ms(private_ike_p2p_t *this, message_t *message) } else { - notify_payload_t *notify; - if (this->response) { message->add_notify(message, FALSE, P2P_RESPONSE, chunk_empty); - } - + } message->add_notify(message, FALSE, P2P_SESSIONID, this->session_id); message->add_notify(message, FALSE, P2P_SESSIONKEY, this->session_key); add_endpoints_to_message(message, this->remote_endpoints); } - break; } + default: + break; } return NEED_MORE; @@ -614,9 +611,10 @@ static status_t process_r_ms(private_ike_p2p_t *this, message_t *message) this->invalid_syntax = TRUE; break; } - break; } + default: + break; } return NEED_MORE; @@ -679,7 +677,7 @@ static status_t build_r_ms(private_ike_p2p_t *this, message_t *message) if (!peer_sa) { - // the peer is not online + /* the peer is not online */ message->add_notify(message, TRUE, P2P_CONNECT_FAILED, chunk_empty); break; } @@ -691,6 +689,8 @@ static status_t build_r_ms(private_ike_p2p_t *this, message_t *message) break; } + default: + break; } return SUCCESS; } @@ -700,18 +700,9 @@ static status_t build_r_ms(private_ike_p2p_t *this, message_t *message) */ static status_t process_i_ms(private_ike_p2p_t *this, message_t *message) { - switch(message->get_exchange_type(message)) - { - case P2P_CONNECT: - { - break; - } - } return SUCCESS; } -// ----------------------------------------------------------------------------- - /** * Implementation of ike_p2p.connect */ @@ -813,7 +804,7 @@ ike_p2p_t *ike_p2p_create(ike_sa_t *ike_sa, bool initiator) } else { - // mediation server + /* mediation server */ if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_ms; diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c index e9d0c4da1..cc20a8861 100644 --- a/src/charon/sa/tasks/task.c +++ b/src/charon/sa/tasks/task.c @@ -28,6 +28,7 @@ ENUM(task_type_names, IKE_INIT, CHILD_REKEY, "IKE_NATD", "IKE_MOBIKE", "IKE_AUTHENTICATE", + "IKE_AUTH_LIFETIME", "IKE_CERT", "IKE_CONFIG", "IKE_REKEY", diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h index dd2bb8a83..a59207711 100644 --- a/src/charon/sa/tasks/task.h +++ b/src/charon/sa/tasks/task.h @@ -45,6 +45,8 @@ enum task_type_t { IKE_MOBIKE, /** authenticate the initiated IKE_SA */ IKE_AUTHENTICATE, + /** AUTH_LIFETIME negotiation, RFC4478 */ + IKE_AUTH_LIFETIME, /** exchange certificates and requests */ IKE_CERT, /** Configuration payloads, virtual IP and such */ |