aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMartin Willi <martin@strongswan.org>2009-10-22 13:04:50 +0200
committerMartin Willi <martin@strongswan.org>2009-11-12 10:33:59 +0100
commitaea334ec1cbf9b4bff2006b4e8c516fc404fa363 (patch)
treee1f18b216ccbe0ef4d755a6621047d2332d80d02 /src
parent6d90881573b470335d9ef40c3b830230bd533e41 (diff)
downloadstrongswan-aea334ec1cbf9b4bff2006b4e8c516fc404fa363.tar.bz2
strongswan-aea334ec1cbf9b4bff2006b4e8c516fc404fa363.tar.xz
Splitted EAP-AKA in peer and server implementations, use libsimaka helper library
Diffstat (limited to 'src')
-rw-r--r--src/charon/plugins/eap_aka/Makefile.am8
-rw-r--r--src/charon/plugins/eap_aka/eap_aka.c1090
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_peer.c397
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_peer.h49
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_plugin.c13
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_plugin.h7
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_server.c394
-rw-r--r--src/charon/plugins/eap_aka/eap_aka_server.h (renamed from src/charon/plugins/eap_aka/eap_aka.h)33
8 files changed, 869 insertions, 1122 deletions
diff --git a/src/charon/plugins/eap_aka/Makefile.am b/src/charon/plugins/eap_aka/Makefile.am
index 7f8a37f40..4d0385b5b 100644
--- a/src/charon/plugins/eap_aka/Makefile.am
+++ b/src/charon/plugins/eap_aka/Makefile.am
@@ -1,10 +1,14 @@
-INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon \
+ -I$(top_srcdir)/src/libsimaka
AM_CFLAGS = -rdynamic
plugin_LTLIBRARIES = libstrongswan-eap-aka.la
-libstrongswan_eap_aka_la_SOURCES = eap_aka_plugin.h eap_aka_plugin.c eap_aka.h eap_aka.c
+libstrongswan_eap_aka_la_SOURCES = eap_aka_plugin.h eap_aka_plugin.c \
+ eap_aka_peer.h eap_aka_peer.c \
+ eap_aka_server.h eap_aka_server.c
+libstrongswan_eap_aka_la_LIBADD = $(top_builddir)/src/libsimaka/libsimaka.a
libstrongswan_eap_aka_la_LDFLAGS = -module -avoid-version
diff --git a/src/charon/plugins/eap_aka/eap_aka.c b/src/charon/plugins/eap_aka/eap_aka.c
deleted file mode 100644
index ab1f69de1..000000000
--- a/src/charon/plugins/eap_aka/eap_aka.c
+++ /dev/null
@@ -1,1090 +0,0 @@
-/*
- * Copyright (C) 2006-2009 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 <limits.h>
-#include <string.h>
-#include <unistd.h>
-#include <gmp.h>
-
-#include "eap_aka.h"
-
-#include <daemon.h>
-#include <library.h>
-#include <crypto/hashers/hasher.h>
-
-#define MK_LEN 20
-#define MSK_LEN 64
-#define KAUTH_LEN 16
-#define KENCR_LEN 16
-#define AT_MAC_LEN 16
-
-typedef enum aka_subtype_t aka_subtype_t;
-typedef enum aka_attribute_t aka_attribute_t;
-
-/**
- * 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,
-};
-
-/**
- * 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_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;
-typedef struct eap_aka_header_t eap_aka_header_t;
-typedef struct aka_attribute_header_t aka_attribute_header_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 peer
- */
- identification_t *peer;
-
- /**
- * SHA11 hasher
- */
- hasher_t *sha1;
-
- /**
- * MAC function used in EAP-AKA
- */
- signer_t *signer;
-
- /**
- * pseudo random function used in EAP-AKA
- */
- prf_t *prf;
-
- /**
- * MSK
- */
- char msk[MSK_LEN];
-
- /**
- * Has the MSK been calculated?
- */
- bool derived;
-
- /**
- * (Expected) Result (X)RES
- */
- char res[AKA_RES_LEN];
-
- /**
- * random value RAND (used by server only)
- */
- char rand[AKA_RAND_LEN];
-};
-
-/**
- * packed EAP AKA header struct
- */
-struct eap_aka_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 => EAP_AKA */
- u_int8_t type;
- /** AKA subtype */
- u_int8_t subtype;
- /** reserved bytes */
- u_int16_t reserved;
-} __attribute__((__packed__));
-
-/**
- * packed EAP AKA attribute header struct
- */
-struct aka_attribute_header_t {
- /** attribute type */
- u_int8_t type;
- /** attibute length */
- u_int8_t length;
-} __attribute__((__packed__));
-
-/** AT_CLIENT_ERROR_CODE AKA attribute */
-static chunk_t client_error_code = chunk_from_chars(0, 0);
-
-/**
- * derive the keys needed for EAP_AKA
- */
-static void derive_keys(private_eap_aka_t *this, identification_t *id,
- chunk_t ck, chunk_t ik)
-{
- char mk[MK_LEN];
- chunk_t tmp, k_auth, identity;
-
- /* MK = SHA1( Identity | IK | CK ) */
- identity = id->get_encoding(id);
- DBG3(DBG_IKE, "Identity %B", &identity);
- this->sha1->get_hash(this->sha1, identity, NULL);
- this->sha1->get_hash(this->sha1, ik, NULL);
- this->sha1->get_hash(this->sha1, ck, mk);
- DBG3(DBG_IKE, "MK %b", mk, MK_LEN);
-
- /* 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 */
- this->prf->set_key(this->prf, chunk_create(mk, MK_LEN));
- tmp = chunk_alloca(this->prf->get_block_size(this->prf) * 4);
- this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr);
- this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1);
- this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2);
- this->prf->get_bytes(this->prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3);
-
- /* skip K_encr, not required */
- tmp = chunk_skip(tmp, KENCR_LEN);
- k_auth = chunk_create(tmp.ptr, KAUTH_LEN);
- tmp = chunk_skip(tmp, KAUTH_LEN);
- memcpy(this->msk, tmp.ptr, MSK_LEN);
- /* ignore EMSK, not required */
-
- this->signer->set_key(this->signer, k_auth);
-
- DBG3(DBG_IKE, "PRF res %B", &tmp);
- DBG3(DBG_IKE, "K_auth %B", &k_auth);
- DBG3(DBG_IKE, "MSK %b", this->msk, MSK_LEN);
-
- this->derived = TRUE;
-}
-
-/**
- * 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 message data
- */
-static aka_attribute_t read_attribute(chunk_t *message, chunk_t *data)
-{
- aka_attribute_t attribute;
- size_t length;
-
- DBG3(DBG_IKE, "reading attribute from %B", message);
-
- if (message->len < 2)
- {
- return AT_END;
- }
- attribute = message->ptr[0];
- length = message->ptr[1] * 4 - 2;
- *message = chunk_skip(*message, 2);
- DBG3(DBG_IKE, "found attribute %N with length %d",
- aka_attribute_names, attribute, length);
-
- if (length > message->len)
- {
- return AT_END;
- }
- data->len = length;
- data->ptr = message->ptr;
- *message = chunk_skip(*message, length);
- return attribute;
-}
-
-/**
- * Build an 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 pos, data, message;
- eap_payload_t *payload;
- va_list args;
- u_int8_t *mac_pos = NULL;
- u_int16_t len;
- eap_aka_header_t *hdr;
- aka_attribute_t attr;
- aka_attribute_header_t *ahdr;
-
- pos = message = chunk_alloca(512);
-
- hdr = (eap_aka_header_t*)message.ptr;
- hdr->code = code;
- hdr->identifier = identifier;
- hdr->length = 0;
- hdr->type = EAP_AKA;
- hdr->subtype = type;
- hdr->reserved = 0;
-
- pos = chunk_skip(pos, sizeof(eap_aka_header_t));
-
- va_start(args, type);
- while ((attr = va_arg(args, aka_attribute_t)) != AT_END)
- {
- data = va_arg(args, chunk_t);
-
- DBG3(DBG_IKE, "building %N %B", aka_attribute_names, attr, &data);
-
- ahdr = (aka_attribute_header_t*)pos.ptr;
- ahdr->type = attr;
- pos = chunk_skip(pos, sizeof(aka_attribute_header_t));
-
- switch (attr)
- {
- case AT_RES:
- {
- ahdr->length = data.len / 4 + 1;
- /* RES length in bits */
- len = htons(data.len * 8);
- memcpy(pos.ptr, &len, sizeof(len));
- pos = chunk_skip(pos, sizeof(len));
- memcpy(pos.ptr, data.ptr, data.len);
- pos = chunk_skip(pos, data.len);
- break;
- }
- case AT_AUTN:
- case AT_RAND:
- {
- ahdr->length = data.len / 4 + 1;
- memset(pos.ptr, 0, 2);
- pos = chunk_skip(pos, 2);
- memcpy(pos.ptr, data.ptr, data.len);
- pos = chunk_skip(pos, data.len);
- break;
- }
- case AT_MAC:
- {
- ahdr->length = 5;
- memset(pos.ptr, 0, 2);
- pos = chunk_skip(pos, 2);
- mac_pos = pos.ptr;
- memset(mac_pos, 0, AT_MAC_LEN);
- pos = chunk_skip(pos, AT_MAC_LEN);
- break;
- }
- case AT_IDENTITY:
- {
- len = data.len;
- /* align up to four bytes */
- if (data.len % 4)
- {
- chunk_t tmp = chunk_alloca((data.len/4)*4 + 4);
- memset(tmp.ptr, 0, tmp.len);
- memcpy(tmp.ptr, data.ptr, data.len);
- data = tmp;
- }
- ahdr->length = data.len / 4 + 1;
- /* actual length in bytes */
- len = htons(len);
- memcpy(pos.ptr, &len, sizeof(len));
- pos = chunk_skip(pos, sizeof(len));
- memcpy(pos.ptr, data.ptr, data.len);
- pos = chunk_skip(pos, data.len);
- break;
- }
- default:
- {
- ahdr->length = data.len / 4 + 1;
- memcpy(pos.ptr, data.ptr, data.len);
- pos = chunk_skip(pos, data.len);
- break;
- }
- }
- }
- va_end(args);
-
- /* calculate message length, write into header */
- message.len = pos.ptr - message.ptr;
- len = htons(message.len);
- memcpy(&hdr->length, &len, sizeof(len));
-
- /* create MAC if AT_MAC attribte was included */
- if (mac_pos)
- {
- DBG3(DBG_IKE, "AT_MAC signature of %B", &message);
- this->signer->get_signature(this->signer, message, mac_pos);
- DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LEN);
- }
-
- /* payload constructor takes data with some bytes skipped */
- payload = eap_payload_create_data(message);
-
- DBG3(DBG_IKE, "created EAP message %B", &message);
- return payload;
-}
-
-/**
- * check if an unknown attribute is skippable
- */
-static bool attribute_skippable(aka_attribute_t attribute)
-{
- if (attribute >= 0 && attribute <= 127)
- {
- DBG1(DBG_IKE, "ignoring skippable attribute %N",
- aka_attribute_names, attribute);
- return TRUE;
- }
- return FALSE;
-}
-
-/**
- * build the error response if we received an unknown non-skippable attribute
- */
-static eap_payload_t *build_non_skippable_error(private_eap_aka_t *this,
- aka_attribute_t attribute, u_char identifier)
-{
- DBG1(DBG_IKE, "found non skippable attribute %N, sending %N %d",
- aka_attribute_names, attribute,
- aka_attribute_names, AT_CLIENT_ERROR_CODE, 0);
- return build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
- AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
-}
-
-/**
- * generate a new non-zero identifier
- */
-static u_char get_identifier()
-{
- while (TRUE)
- {
- u_char id = random();
-
- if (id)
- {
- return id;
- }
- }
-}
-
-/**
- * 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)
-{
- enumerator_t *enumerator;
- sim_provider_t *provider;
- char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN];
- bool found = FALSE;
-
- enumerator = charon->sim->create_provider_enumerator(charon->sim);
- while (enumerator->enumerate(enumerator, &provider))
- {
- if (provider->get_quintuplet(provider, this->peer, this->rand,
- this->res, ck, ik, autn))
- {
- found = TRUE;
- break;
- }
- }
- enumerator->destroy(enumerator);
- if (!found)
- {
- DBG1(DBG_IKE, "no AKA provider found with quintuplets for %Y",
- this->peer);
- return FAILED;
- }
-
- derive_keys(this, this->peer, chunk_create(ck, AKA_CK_LEN),
- chunk_create(ik, AKA_IK_LEN));
-
- *out = build_aka_payload(this, EAP_REQUEST, get_identifier(), AKA_CHALLENGE,
- AT_RAND, chunk_create(this->rand, AKA_RAND_LEN),
- AT_AUTN, chunk_create(autn, AKA_AUTN_LEN),
- AT_MAC, chunk_empty, AT_END);
- return NEED_MORE;
-}
-
-/**
- * Process synchronization request from peer
- */
-static status_t server_process_synchronize(private_eap_aka_t *this,
- eap_payload_t *in, eap_payload_t **out)
-{
- chunk_t attr, message, pos, auts = chunk_empty;
- aka_attribute_t attribute;
- enumerator_t *enumerator;
- sim_provider_t *provider;
- bool found = FALSE;
-
- message = in->get_data(in);
- pos = message;
- read_header(&pos);
-
- while (TRUE)
- {
- attribute = read_attribute(&pos, &attr);
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_AUTS:
- auts = attr;
- continue;
- default:
- if (attribute_skippable(attribute))
- {
- continue;
- }
- DBG1(DBG_IKE, "found non skippable attribute %N",
- aka_attribute_names, attribute);
- return FAILED;
- }
- break;
- }
-
- if (auts.len != AKA_AUTS_LEN)
- {
- DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
- return FAILED;
- }
-
- enumerator = charon->sim->create_provider_enumerator(charon->sim);
- while (enumerator->enumerate(enumerator, &provider))
- {
- if (provider->resync(provider, this->peer, this->rand, auts.ptr))
- {
- found = TRUE;
- break;
- }
- }
- enumerator->destroy(enumerator);
-
- if (!found)
- {
- return FAILED;
- }
- return server_initiate(this, 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;
- aka_attribute_t attribute;
- u_int16_t len;
-
- message = in->get_data(in);
- pos = message;
- read_header(&pos);
-
- while (TRUE)
- {
- attribute = read_attribute(&pos, &attr);
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_RES:
- res = attr;
- if (attr.len == 2 + AKA_RES_LEN)
- {
- memcpy(&len, attr.ptr, 2);
- if (ntohs(len) == AKA_RES_LEN * 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_skippable(attribute))
- {
- continue;
- }
- DBG1(DBG_IKE, "found non skippable attribute %N",
- aka_attribute_names, attribute);
- return FAILED;
- }
- break;
- }
-
- /* verify EAP message MAC AT_MAC */
- DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message);
- if (!this->signer->verify_signature(this->signer, message, at_mac))
- {
- DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed");
- return FAILED;
- }
-
- /* compare received RES against stored precalculated XRES */
- if (!chunk_equals(res, chunk_create(this->res, AKA_RES_LEN)))
- {
- DBG1(DBG_IKE, "received RES does not match XRES");
- DBG3(DBG_IKE, "RES %B XRES %b", &res, this->res, AKA_RES_LEN);
- 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 autn = chunk_empty, rand = chunk_empty, at_mac = chunk_empty;
- chunk_t message, pos, attr = chunk_empty;
- aka_attribute_t attribute;
- u_int8_t identifier;
- enumerator_t *enumerator;
- sim_card_t *card;
- u_char res[AKA_RES_LEN], ck[AKA_CK_LEN], ik[AKA_IK_LEN], auts[AKA_AUTS_LEN];
- status_t status = NOT_FOUND;
-
- message = in->get_data(in);
- pos = message;
- read_header(&pos);
- identifier = in->get_identifier(in);
-
- DBG3(DBG_IKE, "reading attributes from %B", &pos);
-
- while (TRUE)
- {
- attribute = read_attribute(&pos, &attr);
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_RAND:
- rand = 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_skippable(attribute))
- {
- continue;
- }
- *out = build_non_skippable_error(this, attribute, identifier);
- return NEED_MORE;
- }
- break;
- }
-
- if (rand.len != AKA_RAND_LEN || autn.len != AKA_AUTN_LEN)
- {
- /* 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;
- }
-
- enumerator = charon->sim->create_card_enumerator(charon->sim);
- while (enumerator->enumerate(enumerator, &card))
- {
- status = card->get_quintuplet(card, this->peer, rand.ptr, autn.ptr,
- ck, ik, res);
- if (status != FAILED)
- { /* try next on error */
- break;
- }
- }
- enumerator->destroy(enumerator);
-
- if (status == INVALID_STATE &&
- card->resync(card, this->peer, rand.ptr, auts))
- {
- *out = build_aka_payload(this, EAP_RESPONSE,
- identifier, AKA_SYNCHRONIZATION_FAILURE,
- AT_AUTS, chunk_create(auts, AKA_AUTS_LEN), AT_END);
- DBG1(DBG_IKE, "received SQN invalid, sending %N",
- aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
- return NEED_MORE;
- }
- if (status != SUCCESS)
- {
- *out = build_aka_payload(this, EAP_RESPONSE, identifier,
- AKA_AUTHENTICATION_REJECT, AT_END);
- DBG1(DBG_IKE, "no USIM found with quintuplets for %Y, sending %N",
- this->peer, aka_subtype_names, AKA_AUTHENTICATION_REJECT);
- return NEED_MORE;
- }
-
- derive_keys(this, this->peer, chunk_create(ck, AKA_CK_LEN),
- chunk_create(ik, AKA_IK_LEN));
-
- /* verify EAP message MAC AT_MAC */
- DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message);
- if (!this->signer->verify_signature(this->signer, message, at_mac))
- {
- *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;
- }
-
- *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CHALLENGE,
- AT_RES, chunk_create(res, AKA_RES_LEN),
- AT_MAC, chunk_empty, AT_END);
- return NEED_MORE;
-}
-
-/**
- * Process an incoming AKA-Identity client side
- */
-static status_t peer_process_identity(private_eap_aka_t *this,
- eap_payload_t *in, eap_payload_t **out)
-{
- chunk_t identity = chunk_empty, message, pos, attr;
- u_int8_t identifier;
-
- identifier = in->get_identifier(in);
- pos = message = in->get_data(in);
- read_header(&pos);
-
- DBG3(DBG_IKE, "reading attributes from %B", &pos);
-
- while (TRUE)
- {
- aka_attribute_t attribute = read_attribute(&pos, &attr);
-
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_PERMANENT_ID_REQ:
- case AT_FULLAUTH_ID_REQ:
- case AT_ANY_ID_REQ:
- /* always respond with full identity */
- identity = this->peer->get_encoding(this->peer);
- DBG1(DBG_IKE, "server requested %N, sending '%Y'",
- aka_attribute_names, attribute, this->peer);
- continue;
- default:
- if (attribute_skippable(attribute))
- {
- continue;
- }
- *out = build_non_skippable_error(this, attribute, identifier);
- return NEED_MORE;
- }
- break;
- }
-
- /* build response */
- *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_IDENTITY,
- AT_IDENTITY, identity, 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;
- aka_attribute_t attribute;
- 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);
-
- while (TRUE)
- {
- 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;
- }
- memcpy(&code, attr.ptr, 2);
- code = ntohs(code);
- 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_skippable(attribute))
- {
- DBG1(DBG_IKE, "ignoring non-skippable attribute %N in %N",
- aka_attribute_names, attribute, aka_subtype_names,
- AKA_NOTIFICATION);
- }
- 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_IDENTITY:
- {
- return peer_process_identity(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->derived)
- {
- *msk = chunk_create(this->msk, MSK_LEN);
- 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)
-{
- this->peer->destroy(this->peer);
- DESTROY_IF(this->sha1);
- DESTROY_IF(this->signer);
- DESTROY_IF(this->prf);
- free(this);
-}
-
-/**
- * generic constructor used by client & server
- */
-static private_eap_aka_t *eap_aka_create_generic(identification_t *peer)
-{
- private_eap_aka_t *this = malloc_thing(private_eap_aka_t);
-
- this->public.eap_method_interface.initiate = NULL;
- this->public.eap_method_interface.process = 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;
-
- this->peer = peer->clone(peer);
- this->derived = FALSE;
-
- this->sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
- this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_SHA1_128);
- this->prf = lib->crypto->create_prf(lib->crypto, PRF_FIPS_SHA1_160);
- if (!this->sha1 || !this->signer || !this->prf)
- {
- DBG1(DBG_IKE, "unable to initiate EAP-AKA, FIPS-PRF/SHA1 not supported");
- destroy(this);
- return NULL;
- }
- return this;
-}
-
-/*
- * Described in header.
- */
-eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *peer)
-{
- private_eap_aka_t *this = eap_aka_create_generic(peer);
-
- if (this)
- {
- 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;
- }
- return (eap_aka_t*)this;
-}
-
-/*
- * Described in header.
- */
-eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer)
-{
- private_eap_aka_t *this = eap_aka_create_generic(peer);
-
- if (this)
- {
- 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;
- }
- return (eap_aka_t*)this;
-}
-
diff --git a/src/charon/plugins/eap_aka/eap_aka_peer.c b/src/charon/plugins/eap_aka/eap_aka_peer.c
new file mode 100644
index 000000000..079ab13a6
--- /dev/null
+++ b/src/charon/plugins/eap_aka/eap_aka_peer.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2006-2009 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_aka_peer.h"
+
+#include <library.h>
+#include <daemon.h>
+
+#include <simaka_message.h>
+#include <simaka_crypto.h>
+
+typedef struct private_eap_aka_peer_t private_eap_aka_peer_t;
+
+/**
+ * Private data of an eap_aka_peer_t object.
+ */
+struct private_eap_aka_peer_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_aka_peer_t public;
+
+ /**
+ * EAP-AKA crypto helper
+ */
+ simaka_crypto_t *crypto;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+
+ /**
+ * MSK
+ */
+ chunk_t msk;
+};
+
+/**
+ * Create a AKA_CLIENT_ERROR: "Unable to process"
+ */
+static eap_payload_t* create_client_error(private_eap_aka_peer_t *this,
+ u_int8_t identifier)
+{
+ simaka_message_t *message;
+ eap_payload_t *out;
+ u_int16_t encoded;
+
+ DBG1(DBG_IKE, "sending client error '%N'",
+ simaka_client_error_names, AKA_UNABLE_TO_PROCESS);
+
+ message = simaka_message_create(FALSE, identifier,
+ EAP_AKA, AKA_CLIENT_ERROR);
+ encoded = htons(AKA_UNABLE_TO_PROCESS);
+ message->add_attribute(message, AT_CLIENT_ERROR_CODE,
+ chunk_create((char*)&encoded, sizeof(encoded)));
+ out = message->generate(message, this->crypto, chunk_empty);
+ message->destroy(message);
+ return out;
+}
+
+/**
+ * Process an EAP-AKA/Request/Challenge message
+ */
+static status_t process_challenge(private_eap_aka_peer_t *this,
+ simaka_message_t *in, eap_payload_t **out)
+{
+ simaka_message_t *message;
+ enumerator_t *enumerator;
+ simaka_attribute_t type;
+ sim_card_t *card;
+ chunk_t data, rand = chunk_empty, autn = chunk_empty;
+ u_char res[AKA_RES_LEN], ck[AKA_CK_LEN], ik[AKA_IK_LEN], auts[AKA_AUTS_LEN];
+ status_t status = NOT_FOUND;
+
+ enumerator = in->create_attribute_enumerator(in);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ switch (type)
+ {
+ case AT_RAND:
+ rand = data;
+ break;
+ case AT_AUTN:
+ autn = data;
+ break;
+ default:
+ if (!simaka_attribute_skippable(type))
+ {
+ *out = create_client_error(this, in->get_identifier(in));
+ enumerator->destroy(enumerator);
+ return NEED_MORE;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (!rand.len || !autn.len)
+ {
+ DBG1(DBG_IKE, "received invalid EAP-AKA challenge message");
+ *out = create_client_error(this, in->get_identifier(in));
+ return NEED_MORE;
+ }
+
+ enumerator = charon->sim->create_card_enumerator(charon->sim);
+ while (enumerator->enumerate(enumerator, &card))
+ {
+ status = card->get_quintuplet(card, this->peer, rand.ptr, autn.ptr,
+ ck, ik, res);
+ if (status != FAILED)
+ { /* try next on error */
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (status == INVALID_STATE &&
+ card->resync(card, this->peer, rand.ptr, auts))
+ {
+ DBG1(DBG_IKE, "received SQN invalid, sending %N",
+ simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
+ message = simaka_message_create(FALSE, in->get_identifier(in),
+ EAP_AKA, AKA_SYNCHRONIZATION_FAILURE);
+ message->add_attribute(message, AT_AUTS,
+ chunk_create(auts, AKA_AUTS_LEN));
+ *out = message->generate(message, this->crypto, chunk_empty);
+ message->destroy(message);
+ return NEED_MORE;
+ }
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "no USIM found with quintuplets for '%Y', sending %N",
+ this->peer, simaka_subtype_names, AKA_AUTHENTICATION_REJECT);
+ message = simaka_message_create(FALSE, in->get_identifier(in),
+ EAP_AKA, AKA_AUTHENTICATION_REJECT);
+ *out = message->generate(message, this->crypto, chunk_empty);
+ message->destroy(message);
+ return NEED_MORE;
+ }
+
+ data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
+ chunk_create(ck, AKA_CK_LEN));
+ free(this->msk.ptr);
+ this->msk = this->crypto->derive_keys_full(this->crypto, this->peer, data);
+
+ /* verify EAP message MAC AT_MAC */
+ if (!in->verify(in, this->crypto, chunk_empty))
+ {
+ DBG1(DBG_IKE, "AT_MAC verification failed ");
+ *out = create_client_error(this, in->get_identifier(in));
+ return NEED_MORE;
+ }
+
+ message = simaka_message_create(FALSE, in->get_identifier(in),
+ EAP_AKA, AKA_CHALLENGE);
+ message->add_attribute(message, AT_RES, chunk_create(res, AKA_RES_LEN));
+ *out = message->generate(message, this->crypto, chunk_empty);
+ message->destroy(message);
+ return NEED_MORE;
+}
+
+/**
+ * Process an EAP-AKA/Request/Identity message
+ */
+static status_t process_identity(private_eap_aka_peer_t *this,
+ simaka_message_t *in, eap_payload_t **out)
+{
+ simaka_message_t *message;
+ enumerator_t *enumerator;
+ simaka_attribute_t type;
+ chunk_t data;
+
+ enumerator = in->create_attribute_enumerator(in);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ switch (type)
+ {
+ case AT_PERMANENT_ID_REQ:
+ case AT_FULLAUTH_ID_REQ:
+ case AT_ANY_ID_REQ:
+ DBG1(DBG_IKE, "server requested %N, sending '%Y'",
+ simaka_attribute_names, type, this->peer);
+ /* we reply with our permanent identity in any case */
+ break;
+ default:
+ if (!simaka_attribute_skippable(type))
+ {
+ *out = create_client_error(this, in->get_identifier(in));
+ enumerator->destroy(enumerator);
+ return NEED_MORE;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ message = simaka_message_create(FALSE, in->get_identifier(in),
+ EAP_AKA, AKA_IDENTITY);
+ message->add_attribute(message, AT_IDENTITY,
+ this->peer->get_encoding(this->peer));
+ *out = message->generate(message, this->crypto, chunk_empty);
+ message->destroy(message);
+ return NEED_MORE;
+}
+
+/**
+ * Process an EAP-AKA/Request/Notification message
+ */
+static status_t process_notification(private_eap_aka_peer_t *this,
+ simaka_message_t *in, eap_payload_t **out)
+{
+ simaka_message_t *message;
+ enumerator_t *enumerator;
+ simaka_attribute_t type;
+ chunk_t data;
+ bool success = TRUE;
+
+ enumerator = in->create_attribute_enumerator(in);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ if (type == AT_NOTIFICATION)
+ {
+ u_int16_t code;
+
+ memcpy(&code, data.ptr, sizeof(code));
+ code = ntohs(code);
+
+ /* test success bit */
+ if (!(data.ptr[0] & 0x80))
+ {
+ success = FALSE;
+ DBG1(DBG_IKE, "received EAP-AKA notification error '%N'",
+ simaka_notification_names, code);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received EAP-AKA notification '%N'",
+ simaka_notification_names, code);
+ }
+ }
+ else if (!simaka_attribute_skippable(type))
+ {
+ success = FALSE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (success)
+ { /* empty notification reply */
+ message = simaka_message_create(FALSE, in->get_identifier(in),
+ EAP_AKA, AKA_NOTIFICATION);
+ *out = message->generate(message, this->crypto, chunk_empty);
+ message->destroy(message);
+ }
+ else
+ {
+ *out = create_client_error(this, in->get_identifier(in));
+ }
+ return NEED_MORE;
+}
+
+
+/**
+ * Implementation of eap_method_t.process
+ */
+static status_t process(private_eap_aka_peer_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ simaka_message_t *message;
+ status_t status;
+
+ message = simaka_message_create_from_payload(in);
+ if (!message)
+ {
+ *out = create_client_error(this, in->get_identifier(in));
+ return NEED_MORE;
+ }
+ if (!message->parse(message, this->crypto))
+ {
+ message->destroy(message);
+ *out = create_client_error(this, in->get_identifier(in));
+ return NEED_MORE;
+ }
+ switch (message->get_subtype(message))
+ {
+ case AKA_IDENTITY:
+ status = process_identity(this, message, out);
+ break;
+ case AKA_CHALLENGE:
+ status = process_challenge(this, message, out);
+ break;
+ case AKA_NOTIFICATION:
+ status = process_notification(this, message, out);
+ break;
+ default:
+ DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
+ simaka_subtype_names, message->get_subtype(message));
+ *out = create_client_error(this, in->get_identifier(in));
+ status = NEED_MORE;
+ break;
+ }
+ message->destroy(message);
+ return status;
+}
+
+/**
+ * Implementation of eap_method_t.initiate
+ */
+static status_t initiate(private_eap_aka_peer_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_peer_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_peer_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_peer_t *this)
+{
+ return TRUE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_aka_peer_t *this)
+{
+ this->crypto->destroy(this->crypto);
+ this->peer->destroy(this->peer);
+ free(this->msk.ptr);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+eap_aka_peer_t *eap_aka_peer_create(identification_t *server,
+ identification_t *peer)
+{
+ private_eap_aka_peer_t *this = malloc_thing(private_eap_aka_peer_t);
+
+ this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
+ this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
+ this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
+ this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ this->crypto = simaka_crypto_create();
+ if (!this->crypto)
+ {
+ free(this);
+ return NULL;
+ }
+ this->peer = peer->clone(peer);
+ this->msk = chunk_empty;
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/eap_aka/eap_aka_peer.h b/src/charon/plugins/eap_aka/eap_aka_peer.h
new file mode 100644
index 000000000..65a210406
--- /dev/null
+++ b/src/charon/plugins/eap_aka/eap_aka_peer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008-2009 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.
+ */
+
+/**
+ * @defgroup eap_aka_peer eap_aka_peer
+ * @{ @ingroup eap_aka
+ */
+
+#ifndef EAP_AKA_PEER_H_
+#define EAP_AKA_PEER_H_
+
+typedef struct eap_aka_peer_t eap_aka_peer_t;
+
+#include <sa/authenticators/eap/eap_method.h>
+
+/**
+ * Implementation of the eap_method_t interface using EAP-AKA as a client.
+ */
+struct eap_aka_peer_t {
+
+ /**
+ * Implemented eap_method_t interface.
+ */
+ eap_method_t interface;
+};
+
+/**
+ * Creates the peer implementation of the EAP method EAP-AKA.
+ *
+ * @param server ID of the EAP server
+ * @param peer ID of the EAP client
+ * @return eap_aka_peer_t object
+ */
+eap_aka_peer_t *eap_aka_peer_create(identification_t *server,
+ identification_t *peer);
+
+#endif /** EAP_AKA_PEER_H_ @}*/
diff --git a/src/charon/plugins/eap_aka/eap_aka_plugin.c b/src/charon/plugins/eap_aka/eap_aka_plugin.c
index 20c249d6b..c44a08966 100644
--- a/src/charon/plugins/eap_aka/eap_aka_plugin.c
+++ b/src/charon/plugins/eap_aka/eap_aka_plugin.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Martin Willi
+ * Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -15,7 +15,8 @@
#include "eap_aka_plugin.h"
-#include "eap_aka.h"
+#include "eap_aka_peer.h"
+#include "eap_aka_server.h"
#include <daemon.h>
@@ -25,9 +26,9 @@
static void destroy(eap_aka_plugin_t *this)
{
charon->eap->remove_method(charon->eap,
- (eap_constructor_t)eap_aka_create_server);
+ (eap_constructor_t)eap_aka_server_create);
charon->eap->remove_method(charon->eap,
- (eap_constructor_t)eap_aka_create_peer);
+ (eap_constructor_t)eap_aka_peer_create);
free(this);
}
@@ -41,9 +42,9 @@ plugin_t *plugin_create()
this->plugin.destroy = (void(*)(plugin_t*))destroy;
charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_SERVER,
- (eap_constructor_t)eap_aka_create_server);
+ (eap_constructor_t)eap_aka_server_create);
charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_PEER,
- (eap_constructor_t)eap_aka_create_peer);
+ (eap_constructor_t)eap_aka_peer_create);
return &this->plugin;
}
diff --git a/src/charon/plugins/eap_aka/eap_aka_plugin.h b/src/charon/plugins/eap_aka/eap_aka_plugin.h
index 2c086ca80..938e5ecbd 100644
--- a/src/charon/plugins/eap_aka/eap_aka_plugin.h
+++ b/src/charon/plugins/eap_aka/eap_aka_plugin.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Martin Willi
+ * Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -29,7 +29,10 @@
typedef struct eap_aka_plugin_t eap_aka_plugin_t;
/**
- * EAP-AKA plugin
+ * EAP-AKA plugin.
+ *
+ * EAP-AKA uses 3rd generation mobile phone standard authentication
+ * mechanism for authentication, as defined RFC4187.
*/
struct eap_aka_plugin_t {
diff --git a/src/charon/plugins/eap_aka/eap_aka_server.c b/src/charon/plugins/eap_aka/eap_aka_server.c
new file mode 100644
index 000000000..6a2f970ab
--- /dev/null
+++ b/src/charon/plugins/eap_aka/eap_aka_server.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2006-2009 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_aka_server.h"
+
+#include <daemon.h>
+#include <library.h>
+
+#include <simaka_message.h>
+#include <simaka_crypto.h>
+
+typedef struct private_eap_aka_server_t private_eap_aka_server_t;
+
+/**
+ * Private data of an eap_aka_server_t object.
+ */
+struct private_eap_aka_server_t {
+
+ /**
+ * Public authenticator_t interface.
+ */
+ eap_aka_server_t public;
+
+ /**
+ * EAP-AKA crypto helper
+ */
+ simaka_crypto_t *crypto;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+
+ /**
+ * EAP identifier value
+ */
+ u_int8_t identifier;
+
+ /**
+ * MSK
+ */
+ chunk_t msk;
+
+ /**
+ * Expected Result XRES
+ */
+ chunk_t xres;
+
+ /**
+ * Random value RAND
+ */
+ chunk_t rand;
+};
+
+/**
+ * Check if an unknown attribute is skippable
+ */
+static bool attribute_skippable(simaka_attribute_t attribute)
+{
+ if (attribute >= 0 && attribute <= 127)
+ {
+ DBG1(DBG_IKE, "ignoring skippable attribute %N",
+ simaka_attribute_names, attribute);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of eap_method_t.initiate
+ */
+static status_t initiate(private_eap_aka_server_t *this, eap_payload_t **out)
+{
+ simaka_message_t *message;
+ enumerator_t *enumerator;
+ sim_provider_t *provider;
+ char rand[AKA_RAND_LEN], xres[AKA_RES_LEN];
+ char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN];
+ chunk_t data;
+ bool found = FALSE;
+
+ enumerator = charon->sim->create_provider_enumerator(charon->sim);
+ while (enumerator->enumerate(enumerator, &provider))
+ {
+ if (provider->get_quintuplet(provider, this->peer,
+ rand, xres, ck, ik, autn))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ if (!found)
+ {
+ DBG1(DBG_IKE, "no AKA provider found with quintuplets for '%Y'",
+ this->peer);
+ return FAILED;
+ }
+
+ data = chunk_cata("cc", chunk_create(ik, AKA_IK_LEN),
+ chunk_create(ck, AKA_CK_LEN));
+ free(this->msk.ptr);
+ this->msk = this->crypto->derive_keys_full(this->crypto, this->peer, data);
+ this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN));
+ this->xres = chunk_clone(chunk_create(xres, AKA_RES_LEN));
+
+ message = simaka_message_create(TRUE, this->identifier++,
+ EAP_AKA, AKA_CHALLENGE);
+ message->add_attribute(message, AT_RAND, this->rand);
+ message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN));
+ *out = message->generate(message, this->crypto, chunk_empty);
+ message->destroy(message);
+ return NEED_MORE;
+}
+
+/**
+ * Process EAP-AKA/Response/Challenge message
+ */
+static status_t process_challenge(private_eap_aka_server_t *this,
+ simaka_message_t *in)
+{
+ enumerator_t *enumerator;
+ simaka_attribute_t type;
+ chunk_t data, res = chunk_empty;
+
+ enumerator = in->create_attribute_enumerator(in);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ switch (type)
+ {
+ case AT_RES:
+ res = data;
+ break;
+ default:
+ if (!attribute_skippable(type))
+ {
+ enumerator->destroy(enumerator);
+ DBG1(DBG_IKE, "found non skippable attribute %N",
+ simaka_attribute_names, type);
+ return FAILED;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* verify MAC of EAP message, AT_MAC */
+ if (!in->verify(in, this->crypto, chunk_empty))
+ {
+ DBG1(DBG_IKE, "AT_MAC verification failed");
+ return FAILED;
+ }
+ /* compare received RES against stored XRES */
+ if (!chunk_equals(res, this->xres))
+ {
+ DBG1(DBG_IKE, "received RES does not match XRES");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Process EAP-AKA/Response/SynchronizationFailure message
+ */
+static status_t process_synchronize(private_eap_aka_server_t *this,
+ simaka_message_t *in, eap_payload_t **out)
+{
+ sim_provider_t *provider;
+ enumerator_t *enumerator;
+ simaka_attribute_t type;
+ chunk_t data, auts = chunk_empty;
+ bool found = FALSE;
+
+ DBG1(DBG_IKE, "received synchronization request, retrying...");
+
+ enumerator = in->create_attribute_enumerator(in);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ switch (type)
+ {
+ case AT_AUTS:
+ auts = data;
+ break;
+ default:
+ if (!attribute_skippable(type))
+ {
+ enumerator->destroy(enumerator);
+ DBG1(DBG_IKE, "found non skippable attribute %N",
+ simaka_attribute_names, type);
+ return FAILED;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (!auts.len)
+ {
+ DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
+ return FAILED;
+ }
+
+ enumerator = charon->sim->create_provider_enumerator(charon->sim);
+ while (enumerator->enumerate(enumerator, &provider))
+ {
+ if (provider->resync(provider, this->peer, this->rand.ptr, auts.ptr))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (!found)
+ {
+ DBG1(DBG_IKE, "no AKA provider found supporting "
+ "resynchronization for '%Y'", this->peer);
+ return FAILED;
+ }
+ return initiate(this, out);
+}
+
+/**
+ * Process EAP-AKA/Response/ClientErrorCode message
+ */
+static status_t process_client_error(private_eap_aka_server_t *this,
+ simaka_message_t *in)
+{
+ enumerator_t *enumerator;
+ simaka_attribute_t type;
+ chunk_t data;
+
+ enumerator = in->create_attribute_enumerator(in);
+ while (enumerator->enumerate(enumerator, &type, &data))
+ {
+ if (type == AT_CLIENT_ERROR_CODE)
+ {
+ u_int16_t code;
+
+ memcpy(&code, data.ptr, sizeof(code));
+ DBG1(DBG_IKE, "received EAP-AKA client error '%N'",
+ simaka_client_error_names, ntohs(code));
+ }
+ else if (!simaka_attribute_skippable(type))
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return FAILED;
+}
+
+/**
+ * Process EAP-AKA/Response/AuthenticationReject message
+ */
+static status_t process_authentication_reject(private_eap_aka_server_t *this,
+ simaka_message_t *in)
+{
+ DBG1(DBG_IKE, "received %N, authentication failed",
+ simaka_subtype_names, in->get_subtype(in));
+ return FAILED;
+}
+
+/**
+ * Implementation of eap_method_t.process
+ */
+static status_t process(private_eap_aka_server_t *this,
+ eap_payload_t *in, eap_payload_t **out)
+{
+ simaka_message_t *message;
+ status_t status;
+
+ message = simaka_message_create_from_payload(in);
+ if (!message)
+ {
+ return FAILED;
+ }
+ if (!message->parse(message, this->crypto))
+ {
+ message->destroy(message);
+ return FAILED;
+ }
+ switch (message->get_subtype(message))
+ {
+ case AKA_CHALLENGE:
+ status = process_challenge(this, message);
+ break;
+ case AKA_SYNCHRONIZATION_FAILURE:
+ status = process_synchronize(this, message, out);
+ break;
+ case AKA_CLIENT_ERROR:
+ status = process_client_error(this, message);
+ break;
+ case AKA_AUTHENTICATION_REJECT:
+ status = process_authentication_reject(this, message);
+ break;
+ default:
+ DBG1(DBG_IKE, "unable to process EAP-AKA subtype %N",
+ simaka_subtype_names, message->get_subtype(message));
+ status = FAILED;
+ break;
+ }
+ message->destroy(message);
+ return status;
+}
+
+/**
+ * Implementation of eap_method_t.get_type.
+ */
+static eap_type_t get_type(private_eap_aka_server_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_server_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_server_t *this)
+{
+ return TRUE;
+}
+
+/**
+ * Implementation of eap_method_t.destroy.
+ */
+static void destroy(private_eap_aka_server_t *this)
+{
+ this->crypto->destroy(this->crypto);
+ this->peer->destroy(this->peer);
+ free(this->msk.ptr);
+ free(this->xres.ptr);
+ free(this->rand.ptr);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+eap_aka_server_t *eap_aka_server_create(identification_t *server,
+ identification_t *peer)
+{
+ private_eap_aka_server_t *this = malloc_thing(private_eap_aka_server_t);
+
+ this->public.interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
+ this->public.interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
+ this->public.interface.get_type = (eap_type_t(*)(eap_method_t*,u_int32_t*))get_type;
+ this->public.interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
+ this->public.interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
+ this->public.interface.destroy = (void(*)(eap_method_t*))destroy;
+
+ this->crypto = simaka_crypto_create();
+ if (!this->crypto)
+ {
+ free(this);
+ return NULL;
+ }
+ this->peer = peer->clone(peer);
+ this->msk = chunk_empty;
+ this->xres = chunk_empty;
+ this->rand = chunk_empty;
+ /* generate a non-zero identifier */
+ do {
+ this->identifier = random();
+ } while (!this->identifier);
+
+ return &this->public;
+}
+
diff --git a/src/charon/plugins/eap_aka/eap_aka.h b/src/charon/plugins/eap_aka/eap_aka_server.h
index e12270c9f..d48fc4c34 100644
--- a/src/charon/plugins/eap_aka/eap_aka.h
+++ b/src/charon/plugins/eap_aka/eap_aka_server.h
@@ -14,29 +14,26 @@
*/
/**
- * @defgroup eap_aka_i eap_aka
+ * @defgroup eap_aka_server eap_aka_server
* @{ @ingroup eap_aka
*/
-#ifndef EAP_AKA_H_
-#define EAP_AKA_H_
+#ifndef EAP_AKA_SERVER_H_
+#define EAP_AKA_SERVER_H_
-typedef struct eap_aka_t eap_aka_t;
+typedef struct eap_aka_server_t eap_aka_server_t;
#include <sa/authenticators/eap/eap_method.h>
/**
- * Implementation of the eap_method_t interface using EAP-AKA.
- *
- * EAP-AKA uses 3rd generation mobile phone standard authentication
- * mechanism for authentication, as defined RFC4187.
+ * Implementation of the eap_method_t interface using EAP-AKA as server.
*/
-struct eap_aka_t {
+struct eap_aka_server_t {
/**
* Implemented eap_method_t interface.
*/
- eap_method_t eap_method_interface;
+ eap_method_t interface;
};
/**
@@ -44,17 +41,9 @@ struct eap_aka_t {
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
- * @return eap_aka_t object
- */
-eap_aka_t *eap_aka_create_server(identification_t *server, identification_t *peer);
-
-/**
- * Creates the peer implementation of the EAP method EAP-AKA.
- *
- * @param server ID of the EAP server
- * @param peer ID of the EAP client
- * @return eap_aka_t object
+ * @return eap_aka_server_t object
*/
-eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer);
+eap_aka_server_t *eap_aka_server_create(identification_t *server,
+ identification_t *peer);
-#endif /** EAP_AKA_H_ @}*/
+#endif /** EAP_AKA_SERVER_H_ @}*/