summaryrefslogtreecommitdiff
path: root/src/charon/sa/authenticators
diff options
context:
space:
mode:
authorRene Mayrhofer <rene@mayrhofer.eu.org>2008-02-07 13:56:17 +0000
committerRene Mayrhofer <rene@mayrhofer.eu.org>2008-02-07 13:56:17 +0000
commitbcc8f7ca7fd8e8ff6e8a4d579251458313133598 (patch)
treea86b42b486c954937b32ffeaaa725804cb1458ec /src/charon/sa/authenticators
parent49104abddf3d71d5abf5cf75dc7f95fa6c55fa63 (diff)
downloadvyos-strongswan-bcc8f7ca7fd8e8ff6e8a4d579251458313133598.tar.gz
vyos-strongswan-bcc8f7ca7fd8e8ff6e8a4d579251458313133598.zip
[svn-upgrade] Integrating new upstream version, strongswan (4.1.10)
Diffstat (limited to 'src/charon/sa/authenticators')
-rw-r--r--src/charon/sa/authenticators/eap/eap_aka.c1440
-rw-r--r--src/charon/sa/authenticators/eap/eap_aka.h141
-rw-r--r--src/charon/sa/authenticators/eap/eap_md5.c282
-rw-r--r--src/charon/sa/authenticators/eap/eap_md5.h59
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.c42
-rw-r--r--src/charon/sa/authenticators/eap/eap_method.h11
-rw-r--r--src/charon/sa/authenticators/eap/eap_sim.c8
-rw-r--r--src/charon/sa/authenticators/eap_authenticator.c105
-rw-r--r--src/charon/sa/authenticators/eap_authenticator.h9
9 files changed, 2051 insertions, 46 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.