Splitted EAP-AKA in peer and server implementations, use libsimaka helper library
authorMartin Willi <martin@strongswan.org>
Thu, 22 Oct 2009 11:04:50 +0000 (13:04 +0200)
committerMartin Willi <martin@strongswan.org>
Thu, 12 Nov 2009 09:33:59 +0000 (10:33 +0100)
src/charon/plugins/eap_aka/Makefile.am
src/charon/plugins/eap_aka/eap_aka.c [deleted file]
src/charon/plugins/eap_aka/eap_aka.h [deleted file]
src/charon/plugins/eap_aka/eap_aka_peer.c [new file with mode: 0644]
src/charon/plugins/eap_aka/eap_aka_peer.h [new file with mode: 0644]
src/charon/plugins/eap_aka/eap_aka_plugin.c
src/charon/plugins/eap_aka/eap_aka_plugin.h
src/charon/plugins/eap_aka/eap_aka_server.c [new file with mode: 0644]
src/charon/plugins/eap_aka/eap_aka_server.h [new file with mode: 0644]

index 7f8a37f..4d0385b 100644 (file)
@@ -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 (file)
index ab1f69d..0000000
+++ /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.h b/src/charon/plugins/eap_aka/eap_aka.h
deleted file mode 100644 (file)
index e12270c..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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_i eap_aka
- * @{ @ingroup eap_aka
- */
-
-#ifndef EAP_AKA_H_
-#define EAP_AKA_H_
-
-typedef struct eap_aka_t eap_aka_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.
- */
-struct eap_aka_t {
-
-       /**
-        * Implemented eap_method_t interface.
-        */
-       eap_method_t eap_method_interface;
-};
-
-/**
- * Creates the server 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
- */
-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
- */
-eap_aka_t *eap_aka_create_peer(identification_t *server, identification_t *peer);
-
-#endif /** EAP_AKA_H_ @}*/
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 (file)
index 0000000..079ab13
--- /dev/null
@@ -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 (file)
index 0000000..65a2104
--- /dev/null
@@ -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_ @}*/
index 20c249d..c44a089 100644 (file)
@@ -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;
 }
index 2c086ca..938e5ec 100644 (file)
@@ -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
 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 (file)
index 0000000..6a2f970
--- /dev/null
@@ -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_server.h b/src/charon/plugins/eap_aka/eap_aka_server.h
new file mode 100644 (file)
index 0000000..d48fc4c
--- /dev/null
@@ -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_server eap_aka_server
+ * @{ @ingroup eap_aka
+ */
+
+#ifndef EAP_AKA_SERVER_H_
+#define EAP_AKA_SERVER_H_
+
+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 as server.
+ */
+struct eap_aka_server_t {
+
+       /**
+        * Implemented eap_method_t interface.
+        */
+       eap_method_t interface;
+};
+
+/**
+ * Creates the server implementation of the EAP method EAP-AKA.
+ *
+ * @param server       ID of the EAP server
+ * @param peer         ID of the EAP client
+ * @return                     eap_aka_server_t object
+ */
+eap_aka_server_t *eap_aka_server_create(identification_t *server,
+                                                                               identification_t *peer);
+
+#endif /** EAP_AKA_SERVER_H_ @}*/