diff options
author | Martin Willi <martin@strongswan.org> | 2009-10-22 13:04:50 +0200 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2009-11-12 10:33:59 +0100 |
commit | aea334ec1cbf9b4bff2006b4e8c516fc404fa363 (patch) | |
tree | e1f18b216ccbe0ef4d755a6621047d2332d80d02 | |
parent | 6d90881573b470335d9ef40c3b830230bd533e41 (diff) | |
download | strongswan-aea334ec1cbf9b4bff2006b4e8c516fc404fa363.tar.bz2 strongswan-aea334ec1cbf9b4bff2006b4e8c516fc404fa363.tar.xz |
Splitted EAP-AKA in peer and server implementations, use libsimaka helper library
-rw-r--r-- | src/charon/plugins/eap_aka/Makefile.am | 8 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka.c | 1090 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_peer.c | 397 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_peer.h | 49 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_plugin.c | 13 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_plugin.h | 7 | ||||
-rw-r--r-- | src/charon/plugins/eap_aka/eap_aka_server.c | 394 | ||||
-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_ @}*/ |