Migrated all SIM/AKA code to libsimaka, use SIM and AKA backend managers registered by name
This commit is contained in:
@@ -3,4 +3,5 @@ INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra -I$(to
|
||||
|
||||
ipseclib_LTLIBRARIES = libsimaka.la
|
||||
libsimaka_la_SOURCES = simaka_message.h simaka_message.c \
|
||||
simaka_crypto.h simaka_crypto.c
|
||||
simaka_crypto.h simaka_crypto.c simaka_manager.h simaka_manager.c \
|
||||
simaka_card.h simaka_provider.h simaka_hooks.h
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2011 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 simaka_card simaka_card
|
||||
* @{ @ingroup libsimaka
|
||||
*/
|
||||
|
||||
#ifndef SIMAKA_CARD_H_
|
||||
#define SIMAKA_CARD_H_
|
||||
|
||||
typedef struct simaka_card_t simaka_card_t;
|
||||
|
||||
#include "simaka_manager.h"
|
||||
|
||||
#include <utils/identification.h>
|
||||
|
||||
/**
|
||||
* Interface for a (U)SIM card (used as EAP client).
|
||||
*
|
||||
* The SIM card completes triplets/quintuplets requested in a challenge
|
||||
* received from the server.
|
||||
* An implementation supporting only one of SIM/AKA authentication may
|
||||
* implement the other methods with return_false()/return NOT_SUPPORTED/NULL.
|
||||
*/
|
||||
struct simaka_card_t {
|
||||
|
||||
/**
|
||||
* Calculate SRES/KC from a RAND for SIM authentication.
|
||||
*
|
||||
* @param id permanent identity to get a triplet for
|
||||
* @param rand RAND input buffer, fixed size 16 bytes
|
||||
* @param sres SRES output buffer, fixed size 4 byte
|
||||
* @param kc KC output buffer, fixed size 8 bytes
|
||||
* @return TRUE if SRES/KC calculated, FALSE on error/wrong identity
|
||||
*/
|
||||
bool (*get_triplet)(simaka_card_t *this, identification_t *id,
|
||||
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN],
|
||||
char kc[SIM_KC_LEN]);
|
||||
|
||||
/**
|
||||
* Calculate CK/IK/RES from RAND/AUTN for AKA authentication.
|
||||
*
|
||||
* If the received sequence number (in autn) is out of sync, INVALID_STATE
|
||||
* is returned.
|
||||
* The RES value is the only one with variable length. Pass a buffer
|
||||
* of at least AKA_RES_MAX, the actual number of bytes is written to the
|
||||
* res_len value. While the standard would allow any bit length between
|
||||
* 32 and 128 bits, we support only full bytes for now.
|
||||
*
|
||||
* @param id permanent identity to request quintuplet for
|
||||
* @param rand random value rand
|
||||
* @param autn authentication token autn
|
||||
* @param ck buffer receiving encryption key ck
|
||||
* @param ik buffer receiving integrity key ik
|
||||
* @param res buffer receiving authentication result res
|
||||
* @param res_len nubmer of bytes written to res buffer
|
||||
* @return SUCCESS, FAILED, or INVALID_STATE if out of sync
|
||||
*/
|
||||
status_t (*get_quintuplet)(simaka_card_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN],
|
||||
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN],
|
||||
char res[AKA_RES_MAX], int *res_len);
|
||||
|
||||
/**
|
||||
* Calculate AUTS from RAND for AKA resynchronization.
|
||||
*
|
||||
* @param id permanent identity to request quintuplet for
|
||||
* @param rand random value rand
|
||||
* @param auts resynchronization parameter auts
|
||||
* @return TRUE if parameter generated successfully
|
||||
*/
|
||||
bool (*resync)(simaka_card_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]);
|
||||
|
||||
/**
|
||||
* Set the pseudonym to use for next authentication.
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @param pseudonym pseudonym identity received from the server
|
||||
*/
|
||||
void (*set_pseudonym)(simaka_card_t *this, identification_t *id,
|
||||
identification_t *pseudonym);
|
||||
|
||||
/**
|
||||
* Get the pseudonym previously stored via set_pseudonym().
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @return associated pseudonym identity, NULL if none stored
|
||||
*/
|
||||
identification_t* (*get_pseudonym)(simaka_card_t *this, identification_t *id);
|
||||
|
||||
/**
|
||||
* Store parameters to use for the next fast reauthentication.
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @param next next fast reauthentication identity to use
|
||||
* @param mk master key MK to store for reauthentication
|
||||
* @param counter counter value to store, host order
|
||||
*/
|
||||
void (*set_reauth)(simaka_card_t *this, identification_t *id,
|
||||
identification_t *next, char mk[HASH_SIZE_SHA1],
|
||||
u_int16_t counter);
|
||||
|
||||
/**
|
||||
* Retrieve parameters for fast reauthentication stored via set_reauth().
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @param mk buffer receiving master key MK
|
||||
* @param counter pointer receiving counter value, in host order
|
||||
* @return fast reauthentication identity, NULL if not found
|
||||
*/
|
||||
identification_t* (*get_reauth)(simaka_card_t *this, identification_t *id,
|
||||
char mk[HASH_SIZE_SHA1], u_int16_t *counter);
|
||||
};
|
||||
|
||||
#endif /** SIMAKA_CARD_H_ @}*/
|
||||
@@ -15,7 +15,9 @@
|
||||
|
||||
#include "simaka_crypto.h"
|
||||
|
||||
#include <daemon.h>
|
||||
#include "simaka_manager.h"
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
/** length of the k_encr key */
|
||||
#define KENCR_LEN 16
|
||||
@@ -38,6 +40,11 @@ struct private_simaka_crypto_t {
|
||||
*/
|
||||
simaka_crypto_t public;
|
||||
|
||||
/**
|
||||
* EAP type this crypto is used, SIM or AKA
|
||||
*/
|
||||
eap_type_t type;
|
||||
|
||||
/**
|
||||
* signer to create/verify AT_MAC
|
||||
*/
|
||||
@@ -93,6 +100,27 @@ static rng_t* get_rng(private_simaka_crypto_t *this)
|
||||
return this->rng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call SIM/AKA key hook
|
||||
*/
|
||||
static void call_hook(private_simaka_crypto_t *this, chunk_t encr, chunk_t auth)
|
||||
{
|
||||
simaka_manager_t *mgr;
|
||||
|
||||
switch (this->type)
|
||||
{
|
||||
case EAP_SIM:
|
||||
mgr = lib->get(lib, "sim-manager");
|
||||
break;
|
||||
case EAP_AKA:
|
||||
mgr = lib->get(lib, "aka-manager");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
mgr->key_hook(mgr, encr, auth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of simaka_crypto_t.derive_keys_full
|
||||
*/
|
||||
@@ -106,7 +134,7 @@ static chunk_t derive_keys_full(private_simaka_crypto_t *this,
|
||||
* For AKA: MK = SHA1(Identity|IK|CK) */
|
||||
this->hasher->get_hash(this->hasher, id->get_encoding(id), NULL);
|
||||
this->hasher->allocate_hash(this->hasher, data, mk);
|
||||
DBG3(DBG_IKE, "MK %B", mk);
|
||||
DBG3(DBG_LIB, "MK %B", mk);
|
||||
|
||||
/* K_encr | K_auth | MSK | EMSK = prf() | prf() | prf() | prf() */
|
||||
this->prf->set_key(this->prf, *mk);
|
||||
@@ -119,12 +147,12 @@ static chunk_t derive_keys_full(private_simaka_crypto_t *this,
|
||||
k_encr = chunk_create(str.ptr, KENCR_LEN);
|
||||
k_auth = chunk_create(str.ptr + KENCR_LEN, KAUTH_LEN);
|
||||
msk = chunk_create(str.ptr + KENCR_LEN + KAUTH_LEN, MSK_LEN);
|
||||
DBG3(DBG_IKE, "K_encr %B\nK_auth %B\nMSK %B", &k_encr, &k_auth, &msk);
|
||||
DBG3(DBG_LIB, "K_encr %B\nK_auth %B\nMSK %B", &k_encr, &k_auth, &msk);
|
||||
|
||||
this->signer->set_key(this->signer, k_auth);
|
||||
this->crypter->set_key(this->crypter, k_encr);
|
||||
|
||||
charon->sim->key_hook(charon->sim, k_encr, k_auth);
|
||||
call_hook(this, k_encr, k_auth);
|
||||
|
||||
this->derived = TRUE;
|
||||
return chunk_clone(msk);
|
||||
@@ -147,12 +175,12 @@ static void derive_keys_reauth(private_simaka_crypto_t *this, chunk_t mk)
|
||||
}
|
||||
k_encr = chunk_create(str.ptr, KENCR_LEN);
|
||||
k_auth = chunk_create(str.ptr + KENCR_LEN, KAUTH_LEN);
|
||||
DBG3(DBG_IKE, "K_encr %B\nK_auth %B", &k_encr, &k_auth);
|
||||
DBG3(DBG_LIB, "K_encr %B\nK_auth %B", &k_encr, &k_auth);
|
||||
|
||||
this->signer->set_key(this->signer, k_auth);
|
||||
this->crypter->set_key(this->crypter, k_encr);
|
||||
|
||||
charon->sim->key_hook(charon->sim, k_encr, k_auth);
|
||||
call_hook(this, k_encr, k_auth);
|
||||
|
||||
this->derived = TRUE;
|
||||
}
|
||||
@@ -181,7 +209,7 @@ static chunk_t derive_keys_reauth_msk(private_simaka_crypto_t *this,
|
||||
this->prf->get_bytes(this->prf, chunk_empty, str.ptr + str.len / 2 * i);
|
||||
}
|
||||
msk = chunk_create(str.ptr, MSK_LEN);
|
||||
DBG3(DBG_IKE, "MSK %B", &msk);
|
||||
DBG3(DBG_LIB, "MSK %B", &msk);
|
||||
|
||||
return chunk_clone(msk);
|
||||
}
|
||||
@@ -210,7 +238,7 @@ static void destroy(private_simaka_crypto_t *this)
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
simaka_crypto_t *simaka_crypto_create()
|
||||
simaka_crypto_t *simaka_crypto_create(eap_type_t type)
|
||||
{
|
||||
private_simaka_crypto_t *this = malloc_thing(private_simaka_crypto_t);
|
||||
|
||||
@@ -223,6 +251,7 @@ simaka_crypto_t *simaka_crypto_create()
|
||||
this->public.clear_keys = (void(*)(simaka_crypto_t*))clear_keys;
|
||||
this->public.destroy = (void(*)(simaka_crypto_t*))destroy;
|
||||
|
||||
this->type = type;
|
||||
this->derived = FALSE;
|
||||
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
|
||||
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
||||
@@ -232,7 +261,8 @@ simaka_crypto_t *simaka_crypto_create()
|
||||
if (!this->rng || !this->hasher || !this->prf ||
|
||||
!this->signer || !this->crypter)
|
||||
{
|
||||
DBG1(DBG_IKE, "unable to use EAP-SIM, missing algorithms");
|
||||
DBG1(DBG_LIB, "unable to use %N, missing algorithms",
|
||||
eap_type_names, type);
|
||||
destroy(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2011 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 simaka_hooks simaka_hooks
|
||||
* @{ @ingroup libsimaka
|
||||
*/
|
||||
|
||||
#ifndef SIMAKA_HOOKS_H_
|
||||
#define SIMAKA_HOOKS_H_
|
||||
|
||||
typedef struct simaka_hooks_t simaka_hooks_t;
|
||||
|
||||
#include "simaka_message.h"
|
||||
|
||||
/**
|
||||
* Additional hooks invoked during EAP-SIM/AKA message processing.
|
||||
*/
|
||||
struct simaka_hooks_t {
|
||||
|
||||
/**
|
||||
* SIM/AKA message parsing.
|
||||
*
|
||||
* As a SIM/AKA optionally contains encrypted attributes, the hook
|
||||
* might get invoked twice, once before and once after decryption.
|
||||
*
|
||||
* @param message SIM/AKA message
|
||||
* @param inbound TRUE for incoming messages, FALSE for outgoing
|
||||
* @param decrypted TRUE if AT_ENCR_DATA has been decrypted
|
||||
*/
|
||||
void (*message)(simaka_hooks_t *this, simaka_message_t *message,
|
||||
bool inbound, bool decrypted);
|
||||
|
||||
/**
|
||||
* SIM/AKA encryption/authentication key hooks.
|
||||
*
|
||||
* @param k_encr derived SIM/AKA encryption key k_encr
|
||||
* @param k_auth derived SIM/AKA authentication key k_auth
|
||||
*/
|
||||
void (*keys)(simaka_hooks_t *this, chunk_t k_encr, chunk_t k_auth);
|
||||
};
|
||||
|
||||
#endif /** SIMAKA_HOOKS_H_ @}*/
|
||||
@@ -0,0 +1,533 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 "simaka_manager.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <utils/linked_list.h>
|
||||
#include <threading/rwlock.h>
|
||||
|
||||
typedef struct private_simaka_manager_t private_simaka_manager_t;
|
||||
|
||||
/**
|
||||
* Private data of an simaka_manager_t object.
|
||||
*/
|
||||
struct private_simaka_manager_t {
|
||||
|
||||
/**
|
||||
* Public simaka_manager_t interface.
|
||||
*/
|
||||
simaka_manager_t public;
|
||||
|
||||
/**
|
||||
* list of added cards
|
||||
*/
|
||||
linked_list_t *cards;
|
||||
|
||||
/**
|
||||
* list of added provider
|
||||
*/
|
||||
linked_list_t *providers;
|
||||
|
||||
/**
|
||||
* list of added hooks
|
||||
*/
|
||||
linked_list_t *hooks;
|
||||
|
||||
/**
|
||||
* lock for lists above
|
||||
*/
|
||||
rwlock_t *lock;
|
||||
};
|
||||
|
||||
METHOD(simaka_manager_t, add_card, void,
|
||||
private_simaka_manager_t *this, simaka_card_t *card)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->cards->insert_last(this->cards, card);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, remove_card, void,
|
||||
private_simaka_manager_t *this, simaka_card_t *card)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->cards->remove(this->cards, card, NULL);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, card_get_triplet, bool,
|
||||
private_simaka_manager_t *this, identification_t *id,
|
||||
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN])
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_card_t *card;
|
||||
int tried = 0;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->cards->create_enumerator(this->cards);
|
||||
while (enumerator->enumerate(enumerator, &card))
|
||||
{
|
||||
if (card->get_triplet(card, id, rand, sres, kc))
|
||||
{
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
tried++;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
DBG1(DBG_LIB, "tried %d SIM cards, but none has triplets for '%Y'",
|
||||
tried, id);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, card_get_quintuplet, status_t,
|
||||
private_simaka_manager_t *this, identification_t *id, char rand[AKA_RAND_LEN],
|
||||
char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN], char ik[AKA_IK_LEN],
|
||||
char res[AKA_RES_MAX], int *res_len)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_card_t *card;
|
||||
status_t status = NOT_FOUND;
|
||||
int tried = 0;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->cards->create_enumerator(this->cards);
|
||||
while (enumerator->enumerate(enumerator, &card))
|
||||
{
|
||||
status = card->get_quintuplet(card, id, rand, autn, ck, ik, res, res_len);
|
||||
switch (status)
|
||||
{ /* try next on error, but not on INVALID_STATE */
|
||||
case SUCCESS:
|
||||
case INVALID_STATE:
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return status;
|
||||
case NOT_SUPPORTED:
|
||||
case FAILED:
|
||||
default:
|
||||
tried++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
DBG1(DBG_LIB, "tried %d SIM cards, but none has quintuplets for '%Y'",
|
||||
tried, id);
|
||||
return status;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, card_resync, bool,
|
||||
private_simaka_manager_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_card_t *card;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->cards->create_enumerator(this->cards);
|
||||
while (enumerator->enumerate(enumerator, &card))
|
||||
{
|
||||
if (card->resync(card, id, rand, auts))
|
||||
{
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, card_set_pseudonym, void,
|
||||
private_simaka_manager_t *this, identification_t *id,
|
||||
identification_t *pseudonym)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_card_t *card;
|
||||
|
||||
DBG1(DBG_LIB, "storing pseudonym '%Y' for '%Y'", pseudonym, id);
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->cards->create_enumerator(this->cards);
|
||||
while (enumerator->enumerate(enumerator, &card))
|
||||
{
|
||||
card->set_pseudonym(card, id, pseudonym);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, card_get_pseudonym, identification_t*,
|
||||
private_simaka_manager_t *this, identification_t *id)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_card_t *card;
|
||||
identification_t *pseudonym = NULL;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->cards->create_enumerator(this->cards);
|
||||
while (enumerator->enumerate(enumerator, &card))
|
||||
{
|
||||
pseudonym = card->get_pseudonym(card, id);
|
||||
if (pseudonym)
|
||||
{
|
||||
DBG1(DBG_LIB, "using stored pseudonym identity '%Y' "
|
||||
"instead of '%Y'", pseudonym, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return pseudonym;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, card_set_reauth, void,
|
||||
private_simaka_manager_t *this, identification_t *id, identification_t *next,
|
||||
char mk[HASH_SIZE_SHA1], u_int16_t counter)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_card_t *card;
|
||||
|
||||
DBG1(DBG_LIB, "storing next reauthentication identity '%Y' for '%Y'",
|
||||
next, id);
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->cards->create_enumerator(this->cards);
|
||||
while (enumerator->enumerate(enumerator, &card))
|
||||
{
|
||||
card->set_reauth(card, id, next, mk, counter);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, card_get_reauth, identification_t*,
|
||||
private_simaka_manager_t *this, identification_t *id, char mk[HASH_SIZE_SHA1],
|
||||
u_int16_t *counter)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_card_t *card;
|
||||
identification_t *reauth = NULL;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->cards->create_enumerator(this->cards);
|
||||
while (enumerator->enumerate(enumerator, &card))
|
||||
{
|
||||
reauth = card->get_reauth(card, id, mk, counter);
|
||||
if (reauth)
|
||||
{
|
||||
DBG1(DBG_LIB, "using stored reauthentication identity '%Y' "
|
||||
"instead of '%Y'", reauth, id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return reauth;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, add_provider, void,
|
||||
private_simaka_manager_t *this, simaka_provider_t *provider)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->providers->insert_last(this->providers, provider);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, remove_provider, void,
|
||||
private_simaka_manager_t *this, simaka_provider_t *provider)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->providers->remove(this->providers, provider, NULL);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, provider_get_triplet, bool,
|
||||
private_simaka_manager_t *this, identification_t *id,
|
||||
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN])
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_provider_t *provider;
|
||||
int tried = 0;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->providers->create_enumerator(this->providers);
|
||||
while (enumerator->enumerate(enumerator, &provider))
|
||||
{
|
||||
if (provider->get_triplet(provider, id, rand, sres, kc))
|
||||
{
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
tried++;
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
DBG1(DBG_LIB, "tried %d SIM providers, but none had a triplet for '%Y'",
|
||||
tried, id);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, provider_get_quintuplet, bool,
|
||||
private_simaka_manager_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len,
|
||||
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN])
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_provider_t *provider;
|
||||
int tried = 0;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->providers->create_enumerator(this->providers);
|
||||
while (enumerator->enumerate(enumerator, &provider))
|
||||
{
|
||||
if (provider->get_quintuplet(provider, id, rand, xres, xres_len,
|
||||
ck, ik, autn))
|
||||
{
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
DBG1(DBG_LIB, "tried %d SIM providers, but none had a quintuplet for '%Y'",
|
||||
tried, id);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, provider_resync, bool,
|
||||
private_simaka_manager_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_provider_t *provider;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->providers->create_enumerator(this->providers);
|
||||
while (enumerator->enumerate(enumerator, &provider))
|
||||
{
|
||||
if (provider->resync(provider, id, rand, auts))
|
||||
{
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, provider_is_pseudonym, identification_t*,
|
||||
private_simaka_manager_t *this, identification_t *id)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_provider_t *provider;
|
||||
identification_t *permanent = NULL;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->providers->create_enumerator(this->providers);
|
||||
while (enumerator->enumerate(enumerator, &provider))
|
||||
{
|
||||
permanent = provider->is_pseudonym(provider, id);
|
||||
if (permanent)
|
||||
{
|
||||
DBG1(DBG_LIB, "received pseudonym identity '%Y' "
|
||||
"mapping to '%Y'", id, permanent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return permanent;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, provider_gen_pseudonym, identification_t*,
|
||||
private_simaka_manager_t *this, identification_t *id)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_provider_t *provider;
|
||||
identification_t *pseudonym = NULL;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->providers->create_enumerator(this->providers);
|
||||
while (enumerator->enumerate(enumerator, &provider))
|
||||
{
|
||||
pseudonym = provider->gen_pseudonym(provider, id);
|
||||
if (pseudonym)
|
||||
{
|
||||
DBG1(DBG_LIB, "proposing new pseudonym '%Y'", pseudonym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return pseudonym;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, provider_is_reauth, identification_t*,
|
||||
private_simaka_manager_t *this, identification_t *id, char mk[HASH_SIZE_SHA1],
|
||||
u_int16_t *counter)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_provider_t *provider;
|
||||
identification_t *permanent = NULL;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->providers->create_enumerator(this->providers);
|
||||
while (enumerator->enumerate(enumerator, &provider))
|
||||
{
|
||||
permanent = provider->is_reauth(provider, id, mk, counter);
|
||||
if (permanent)
|
||||
{
|
||||
DBG1(DBG_LIB, "received reauthentication identity '%Y' "
|
||||
"mapping to '%Y'", id, permanent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return permanent;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, provider_gen_reauth, identification_t*,
|
||||
private_simaka_manager_t *this, identification_t *id, char mk[HASH_SIZE_SHA1])
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_provider_t *provider;
|
||||
identification_t *reauth = NULL;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->providers->create_enumerator(this->providers);
|
||||
while (enumerator->enumerate(enumerator, &provider))
|
||||
{
|
||||
reauth = provider->gen_reauth(provider, id, mk);
|
||||
if (reauth)
|
||||
{
|
||||
DBG1(DBG_LIB, "proposing new reauthentication identity '%Y'", reauth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
return reauth;
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, add_hooks, void,
|
||||
private_simaka_manager_t *this, simaka_hooks_t *hooks)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->hooks->insert_last(this->hooks, hooks);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, remove_hooks, void,
|
||||
private_simaka_manager_t *this, simaka_hooks_t *hooks)
|
||||
{
|
||||
this->lock->write_lock(this->lock);
|
||||
this->hooks->remove(this->hooks, hooks, NULL);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, message_hook, void,
|
||||
private_simaka_manager_t *this, simaka_message_t *message,
|
||||
bool inbound, bool decrypted)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_hooks_t *hooks;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->hooks->create_enumerator(this->hooks);
|
||||
while (enumerator->enumerate(enumerator, &hooks))
|
||||
{
|
||||
hooks->message(hooks, message, inbound, decrypted);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, key_hook, void,
|
||||
private_simaka_manager_t *this, chunk_t k_encr, chunk_t k_auth)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
simaka_hooks_t *hooks;
|
||||
|
||||
this->lock->read_lock(this->lock);
|
||||
enumerator = this->hooks->create_enumerator(this->hooks);
|
||||
while (enumerator->enumerate(enumerator, &hooks))
|
||||
{
|
||||
hooks->keys(hooks, k_encr, k_auth);
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
this->lock->unlock(this->lock);
|
||||
}
|
||||
|
||||
METHOD(simaka_manager_t, destroy, void,
|
||||
private_simaka_manager_t *this)
|
||||
{
|
||||
this->cards->destroy(this->cards);
|
||||
this->providers->destroy(this->providers);
|
||||
this->hooks->destroy(this->hooks);
|
||||
this->lock->destroy(this->lock);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* See header
|
||||
*/
|
||||
simaka_manager_t *simaka_manager_create()
|
||||
{
|
||||
private_simaka_manager_t *this;
|
||||
|
||||
INIT(this,
|
||||
.public = {
|
||||
.add_card = _add_card,
|
||||
.remove_card = _remove_card,
|
||||
.card_get_triplet = _card_get_triplet,
|
||||
.card_get_quintuplet = _card_get_quintuplet,
|
||||
.card_resync = _card_resync,
|
||||
.card_set_pseudonym = _card_set_pseudonym,
|
||||
.card_get_pseudonym = _card_get_pseudonym,
|
||||
.card_set_reauth = _card_set_reauth,
|
||||
.card_get_reauth = _card_get_reauth,
|
||||
.add_provider = _add_provider,
|
||||
.remove_provider = _remove_provider,
|
||||
.provider_get_triplet = _provider_get_triplet,
|
||||
.provider_get_quintuplet = _provider_get_quintuplet,
|
||||
.provider_resync = _provider_resync,
|
||||
.provider_is_pseudonym = _provider_is_pseudonym,
|
||||
.provider_gen_pseudonym = _provider_gen_pseudonym,
|
||||
.provider_is_reauth = _provider_is_reauth,
|
||||
.provider_gen_reauth = _provider_gen_reauth,
|
||||
.add_hooks = _add_hooks,
|
||||
.remove_hooks = _remove_hooks,
|
||||
.message_hook = _message_hook,
|
||||
.key_hook = _key_hook,
|
||||
.destroy = _destroy,
|
||||
},
|
||||
.cards = linked_list_create(),
|
||||
.providers = linked_list_create(),
|
||||
.hooks = linked_list_create(),
|
||||
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
|
||||
);
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2011 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 simaka_manager simaka_manager
|
||||
* @{ @ingroup libsimaka
|
||||
*/
|
||||
|
||||
#ifndef SIMAKA_MANAGER_H_
|
||||
#define SIMAKA_MANAGER_H_
|
||||
|
||||
#include <crypto/hashers/hasher.h>
|
||||
#include <utils/identification.h>
|
||||
#include <utils/enumerator.h>
|
||||
|
||||
typedef struct simaka_manager_t simaka_manager_t;
|
||||
|
||||
#define SIM_RAND_LEN 16
|
||||
#define SIM_SRES_LEN 4
|
||||
#define SIM_KC_LEN 8
|
||||
|
||||
#define AKA_RAND_LEN 16
|
||||
#define AKA_RES_MAX 16
|
||||
#define AKA_CK_LEN 16
|
||||
#define AKA_IK_LEN 16
|
||||
#define AKA_AUTN_LEN 16
|
||||
#define AKA_AUTS_LEN 14
|
||||
|
||||
#include "simaka_card.h"
|
||||
#include "simaka_provider.h"
|
||||
#include "simaka_hooks.h"
|
||||
|
||||
/**
|
||||
* The SIM manager handles multiple (U)SIM cards/providers and hooks.
|
||||
*/
|
||||
struct simaka_manager_t {
|
||||
|
||||
/**
|
||||
* Register a SIM card (client) at the manager.
|
||||
*
|
||||
* @param card sim card to register
|
||||
*/
|
||||
void (*add_card)(simaka_manager_t *this, simaka_card_t *card);
|
||||
|
||||
/**
|
||||
* Unregister a previously registered card from the manager.
|
||||
*
|
||||
* @param card sim card to unregister
|
||||
*/
|
||||
void (*remove_card)(simaka_manager_t *this, simaka_card_t *card);
|
||||
|
||||
/**
|
||||
* Calculate SIM triplets on one of the registered SIM cards.
|
||||
*
|
||||
* @param id permanent identity to get a triplet for
|
||||
* @param rand RAND input buffer, fixed size 16 bytes
|
||||
* @param sres SRES output buffer, fixed size 4 byte
|
||||
* @param kc KC output buffer, fixed size 8 bytes
|
||||
* @return TRUE if calculated, FALSE if no matching card found
|
||||
*/
|
||||
bool (*card_get_triplet)(simaka_manager_t *this, identification_t *id,
|
||||
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN],
|
||||
char kc[SIM_KC_LEN]);
|
||||
|
||||
/**
|
||||
* Calculate AKA quitpulets on one of the registered SIM cards.
|
||||
*
|
||||
* @param id permanent identity to request quintuplet for
|
||||
* @param rand random value rand
|
||||
* @param autn authentication token autn
|
||||
* @param ck buffer receiving encryption key ck
|
||||
* @param ik buffer receiving integrity key ik
|
||||
* @param res buffer receiving authentication result res
|
||||
* @param res_len nubmer of bytes written to res buffer
|
||||
* @return SUCCESS, FAILED, or INVALID_STATE if out of sync
|
||||
*/
|
||||
status_t (*card_get_quintuplet)(simaka_manager_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN],
|
||||
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN],
|
||||
char res[AKA_RES_MAX], int *res_len);
|
||||
|
||||
/**
|
||||
* Calculate resynchronization data on one of the registered SIM cards.
|
||||
*
|
||||
* @param id permanent identity to request quintuplet for
|
||||
* @param rand random value rand
|
||||
* @param auts resynchronization parameter auts
|
||||
* @return TRUE if calculated, FALSE if no matcing card found
|
||||
*/
|
||||
bool (*card_resync)(simaka_manager_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]);
|
||||
|
||||
/**
|
||||
* Store a received pseudonym on one of the registered SIM cards.
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @param pseudonym pseudonym identity received from the server
|
||||
*/
|
||||
void (*card_set_pseudonym)(simaka_manager_t *this, identification_t *id,
|
||||
identification_t *pseudonym);
|
||||
|
||||
/**
|
||||
* Get a stored pseudonym from one of the registerd SIM cards.
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @return associated pseudonym identity, NULL if none found
|
||||
*/
|
||||
identification_t* (*card_get_pseudonym)(simaka_manager_t *this,
|
||||
identification_t *id);
|
||||
|
||||
/**
|
||||
* Store fast reauthentication parameters on one of the registered cards.
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @param next next fast reauthentication identity to use
|
||||
* @param mk master key MK to store for reauthentication
|
||||
* @param counter counter value to store, host order
|
||||
*/
|
||||
void (*card_set_reauth)(simaka_manager_t *this, identification_t *id,
|
||||
identification_t *next, char mk[HASH_SIZE_SHA1],
|
||||
u_int16_t counter);
|
||||
|
||||
/**
|
||||
* Retrieve fast reauthentication parameters from one of the registerd cards.
|
||||
*
|
||||
* @param id permanent identity of the peer
|
||||
* @param mk buffer receiving master key MK
|
||||
* @param counter pointer receiving counter value, in host order
|
||||
* @return fast reauthentication identity, NULL if none found
|
||||
*/
|
||||
identification_t* (*card_get_reauth)(simaka_manager_t *this,
|
||||
identification_t *id, char mk[HASH_SIZE_SHA1],
|
||||
u_int16_t *counter);
|
||||
|
||||
/**
|
||||
* Register a triplet provider (server) at the manager.
|
||||
*
|
||||
* @param card sim card to register
|
||||
*/
|
||||
void (*add_provider)(simaka_manager_t *this, simaka_provider_t *provider);
|
||||
|
||||
/**
|
||||
* Unregister a previously registered provider from the manager.
|
||||
*
|
||||
* @param card sim card to unregister
|
||||
*/
|
||||
void (*remove_provider)(simaka_manager_t *this, simaka_provider_t *provider);
|
||||
|
||||
/**
|
||||
* Get a SIM triplet from one of the registered providers.
|
||||
*
|
||||
* @param id permanent identity of peer to gen triplet for
|
||||
* @param rand RAND output buffer, fixed size 16 bytes
|
||||
* @param sres SRES output buffer, fixed size 4 byte
|
||||
* @param kc KC output buffer, fixed size 8 bytes
|
||||
* @return TRUE if triplet received, FALSE if no match found
|
||||
*/
|
||||
bool (*provider_get_triplet)(simaka_manager_t *this, identification_t *id,
|
||||
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN],
|
||||
char kc[SIM_KC_LEN]);
|
||||
|
||||
/**
|
||||
* Get a AKA quintuplet from one of the registered providers.
|
||||
*
|
||||
* @param id permanent identity of peer to create challenge for
|
||||
* @param rand buffer receiving random value rand
|
||||
* @param xres buffer receiving expected authentication result xres
|
||||
* @param ck buffer receiving encryption key ck
|
||||
* @param ik buffer receiving integrity key ik
|
||||
* @param autn authentication token autn
|
||||
* @return TRUE if quintuplet received, FALSE if no match found
|
||||
*/
|
||||
bool (*provider_get_quintuplet)(simaka_manager_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN],
|
||||
char xres[AKA_RES_MAX], int *xres_len,
|
||||
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN],
|
||||
char autn[AKA_AUTN_LEN]);
|
||||
|
||||
/**
|
||||
* Pass AKA resynchronization data to one of the registered providers.
|
||||
*
|
||||
* @param id permanent identity of peer requesting resynchronisation
|
||||
* @param rand random value rand
|
||||
* @param auts synchronization parameter auts
|
||||
* @return TRUE if resynchronized, FALSE if not handled
|
||||
*/
|
||||
bool (*provider_resync)(simaka_manager_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]);
|
||||
|
||||
/**
|
||||
* Check if a peer uses a pseudonym using one of the registered providers.
|
||||
*
|
||||
* @param id pseudonym identity candidate
|
||||
* @return permanent identity, NULL if id not a pseudonym
|
||||
*/
|
||||
identification_t* (*provider_is_pseudonym)(simaka_manager_t *this,
|
||||
identification_t *id);
|
||||
|
||||
/**
|
||||
* Generate a new pseudonym using one of the registered providers.
|
||||
*
|
||||
* @param id permanent identity to generate a pseudonym for
|
||||
* @return generated pseudonym, NULL to not use a pseudonym identity
|
||||
*/
|
||||
identification_t* (*provider_gen_pseudonym)(simaka_manager_t *this,
|
||||
identification_t *id);
|
||||
|
||||
/**
|
||||
* Check if a peer uses a reauth id using one of the registered providers.
|
||||
*
|
||||
* @param id reauthentication identity (candidate)
|
||||
* @param mk buffer receiving master key MK
|
||||
* @param counter pointer receiving current counter value, host order
|
||||
* @return permanent identity, NULL if not a known reauth identity
|
||||
*/
|
||||
identification_t* (*provider_is_reauth)(simaka_manager_t *this,
|
||||
identification_t *id, char mk[HASH_SIZE_SHA1],
|
||||
u_int16_t *counter);
|
||||
|
||||
/**
|
||||
* Generate a fast reauth id using one of the registered providers.
|
||||
*
|
||||
* @param id permanent peer identity
|
||||
* @param mk master key to store along with generated identity
|
||||
* @return fast reauthentication identity, NULL to not use reauth
|
||||
*/
|
||||
identification_t* (*provider_gen_reauth)(simaka_manager_t *this,
|
||||
identification_t *id, char mk[HASH_SIZE_SHA1]);
|
||||
|
||||
/**
|
||||
* Register a set of hooks to the manager.
|
||||
*
|
||||
* @param hooks hook interface implementation to register
|
||||
*/
|
||||
void (*add_hooks)(simaka_manager_t *this, simaka_hooks_t *hooks);
|
||||
|
||||
/**
|
||||
* Unregister a set of hooks from the manager.
|
||||
*
|
||||
* @param hooks hook interface implementation to unregister
|
||||
*/
|
||||
void (*remove_hooks)(simaka_manager_t *this, simaka_hooks_t *hooks);
|
||||
|
||||
/**
|
||||
* Invoke SIM/AKA message hook.
|
||||
*
|
||||
* @param message SIM message
|
||||
* @param inbound TRUE for incoming messages, FALSE for outgoing
|
||||
* @param decrypted TRUE if AT_ENCR_DATA has been decrypted
|
||||
*/
|
||||
void (*message_hook)(simaka_manager_t *this, simaka_message_t *message,
|
||||
bool inbound, bool decrypted);
|
||||
|
||||
/**
|
||||
* Invoke SIM/AKA key hook.
|
||||
*
|
||||
* @param k_encr SIM/AKA encryption key k_encr
|
||||
* @param k_auth SIM/AKA authentication key k_auth
|
||||
*/
|
||||
void (*key_hook)(simaka_manager_t *this, chunk_t k_encr, chunk_t k_auth);
|
||||
|
||||
/**
|
||||
* Destroy a manager instance.
|
||||
*/
|
||||
void (*destroy)(simaka_manager_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an SIM/AKA manager to handle multiple (U)SIM cards/providers.
|
||||
*
|
||||
* @return simaka_t object
|
||||
*/
|
||||
simaka_manager_t *simaka_manager_create();
|
||||
|
||||
#endif /** SIMAKA_MANAGER_H_ @}*/
|
||||
@@ -15,6 +15,11 @@
|
||||
|
||||
#include "simaka_message.h"
|
||||
|
||||
#include "simaka_manager.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <utils/linked_list.h>
|
||||
|
||||
typedef struct private_simaka_message_t private_simaka_message_t;
|
||||
typedef struct hdr_t hdr_t;
|
||||
typedef struct attr_hdr_t attr_hdr_t;
|
||||
@@ -136,7 +141,7 @@ bool simaka_attribute_skippable(simaka_attribute_t attribute)
|
||||
{
|
||||
bool skippable = !(attribute >= 0 && attribute <= 127);
|
||||
|
||||
DBG1(DBG_IKE, "%sskippable EAP-SIM/AKA attribute %N",
|
||||
DBG1(DBG_LIB, "%sskippable EAP-SIM/AKA attribute %N",
|
||||
skippable ? "ignoring " : "found non-",
|
||||
simaka_attribute_names, attribute);
|
||||
return skippable;
|
||||
@@ -269,7 +274,7 @@ static void add_attribute(private_simaka_message_t *this,
|
||||
*/
|
||||
static bool not_encrypted(simaka_attribute_t type)
|
||||
{
|
||||
DBG1(DBG_IKE, "received unencrypted %N", simaka_attribute_names, type);
|
||||
DBG1(DBG_LIB, "received unencrypted %N", simaka_attribute_names, type);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -278,10 +283,32 @@ static bool not_encrypted(simaka_attribute_t type)
|
||||
*/
|
||||
static bool invalid_length(simaka_attribute_t type)
|
||||
{
|
||||
DBG1(DBG_IKE, "invalid length of %N", simaka_attribute_names, type);
|
||||
DBG1(DBG_LIB, "invalid length of %N", simaka_attribute_names, type);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call SIM/AKA message hooks
|
||||
*/
|
||||
static void call_hook(private_simaka_message_t *this,
|
||||
bool inbound, bool decrypted)
|
||||
{
|
||||
simaka_manager_t *mgr;
|
||||
|
||||
switch (this->hdr->type)
|
||||
{
|
||||
case EAP_SIM:
|
||||
mgr = lib->get(lib, "sim-manager");
|
||||
break;
|
||||
case EAP_AKA:
|
||||
mgr = lib->get(lib, "aka-manager");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
mgr->message_hook(mgr, &this->public, inbound, decrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse attributes from a chunk of data
|
||||
*/
|
||||
@@ -294,7 +321,7 @@ static bool parse_attributes(private_simaka_message_t *this, chunk_t in)
|
||||
|
||||
if (in.len < sizeof(attr_hdr_t))
|
||||
{
|
||||
DBG1(DBG_IKE, "found short %N attribute header",
|
||||
DBG1(DBG_LIB, "found short %N attribute header",
|
||||
eap_type_names, this->hdr->type);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -450,7 +477,7 @@ static bool parse_attributes(private_simaka_message_t *this, chunk_t in)
|
||||
}
|
||||
else if (!this->encrypted)
|
||||
{
|
||||
DBG1(DBG_IKE, "found P-bit 0 notify in unencrypted message");
|
||||
DBG1(DBG_LIB, "found P-bit 0 notify in unencrypted message");
|
||||
return FALSE;
|
||||
}
|
||||
/* FALL */
|
||||
@@ -460,7 +487,7 @@ static bool parse_attributes(private_simaka_message_t *this, chunk_t in)
|
||||
}
|
||||
}
|
||||
|
||||
charon->sim->message_hook(charon->sim, &this->public, TRUE, this->encrypted);
|
||||
call_hook(this, TRUE, this->encrypted);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -481,7 +508,7 @@ static bool decrypt(private_simaka_message_t *this)
|
||||
}
|
||||
if (this->encr.len % crypter->get_block_size(crypter))
|
||||
{
|
||||
DBG1(DBG_IKE, "%N ENCR_DATA not a multiple of block size",
|
||||
DBG1(DBG_LIB, "%N ENCR_DATA not a multiple of block size",
|
||||
eap_type_names, this->hdr->type);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -543,7 +570,7 @@ static bool verify(private_simaka_message_t *this, chunk_t sigdata)
|
||||
{
|
||||
if (!this->mac.ptr || !signer)
|
||||
{ /* require MAC, but not found */
|
||||
DBG1(DBG_IKE, "%N message requires a MAC, but none found",
|
||||
DBG1(DBG_LIB, "%N message requires a MAC, but none found",
|
||||
simaka_subtype_names, this->hdr->subtype);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -558,7 +585,7 @@ static bool verify(private_simaka_message_t *this, chunk_t sigdata)
|
||||
}
|
||||
if (!this->mac.ptr || !signer)
|
||||
{
|
||||
DBG1(DBG_IKE, "%N message has a phase 0 notify, but "
|
||||
DBG1(DBG_LIB, "%N message has a phase 0 notify, but "
|
||||
"no MAC found", simaka_subtype_names, this->hdr->subtype);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -566,7 +593,7 @@ static bool verify(private_simaka_message_t *this, chunk_t sigdata)
|
||||
}
|
||||
default:
|
||||
/* unknown message? */
|
||||
DBG1(DBG_IKE, "signature rule for %N messages missing",
|
||||
DBG1(DBG_LIB, "signature rule for %N messages missing",
|
||||
simaka_subtype_names, this->hdr->subtype);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -582,7 +609,7 @@ static bool verify(private_simaka_message_t *this, chunk_t sigdata)
|
||||
}
|
||||
if (!signer->verify_signature(signer, data, backup))
|
||||
{
|
||||
DBG1(DBG_IKE, "%N MAC verification failed",
|
||||
DBG1(DBG_LIB, "%N MAC verification failed",
|
||||
eap_type_names, this->hdr->type);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -592,7 +619,7 @@ static bool verify(private_simaka_message_t *this, chunk_t sigdata)
|
||||
/**
|
||||
* Implementation of simaka_message_t.generate
|
||||
*/
|
||||
static eap_payload_t* generate(private_simaka_message_t *this, chunk_t sigdata)
|
||||
static chunk_t generate(private_simaka_message_t *this, chunk_t sigdata)
|
||||
{
|
||||
/* buffers large enough for messages we generate */
|
||||
char out_buf[1024], encr_buf[512];
|
||||
@@ -603,7 +630,7 @@ static eap_payload_t* generate(private_simaka_message_t *this, chunk_t sigdata)
|
||||
u_int16_t len;
|
||||
signer_t *signer;
|
||||
|
||||
charon->sim->message_hook(charon->sim, &this->public, FALSE, TRUE);
|
||||
call_hook(this, FALSE, TRUE);
|
||||
|
||||
out = chunk_create(out_buf, sizeof(out_buf));
|
||||
encr = chunk_create(encr_buf, sizeof(encr_buf));
|
||||
@@ -723,7 +750,7 @@ static eap_payload_t* generate(private_simaka_message_t *this, chunk_t sigdata)
|
||||
}
|
||||
default:
|
||||
{
|
||||
DBG1(DBG_IKE, "no rule to encode %N, skipped",
|
||||
DBG1(DBG_LIB, "no rule to encode %N, skipped",
|
||||
simaka_attribute_names, type);
|
||||
break;
|
||||
}
|
||||
@@ -817,9 +844,9 @@ static eap_payload_t* generate(private_simaka_message_t *this, chunk_t sigdata)
|
||||
signer->get_signature(signer, data, mac.ptr);
|
||||
}
|
||||
|
||||
charon->sim->message_hook(charon->sim, &this->public, FALSE, FALSE);
|
||||
call_hook(this, FALSE, FALSE);
|
||||
|
||||
return eap_payload_create_data(out);
|
||||
return chunk_clone(out);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -843,18 +870,18 @@ static simaka_message_t *simaka_message_create_data(chunk_t data,
|
||||
|
||||
if (data.len < sizeof(hdr_t) || hdr->length != htons(data.len))
|
||||
{
|
||||
DBG1(DBG_IKE, "EAP-SIM/AKA header has invalid length");
|
||||
DBG1(DBG_LIB, "EAP-SIM/AKA header has invalid length");
|
||||
return NULL;
|
||||
}
|
||||
if (hdr->code != EAP_REQUEST && hdr->code != EAP_RESPONSE)
|
||||
{
|
||||
DBG1(DBG_IKE, "invalid EAP code in EAP-SIM/AKA message",
|
||||
DBG1(DBG_LIB, "invalid EAP code in EAP-SIM/AKA message",
|
||||
eap_type_names, hdr->type);
|
||||
return NULL;
|
||||
}
|
||||
if (hdr->type != EAP_SIM && hdr->type != EAP_AKA)
|
||||
{
|
||||
DBG1(DBG_IKE, "invalid EAP type in EAP-SIM/AKA message",
|
||||
DBG1(DBG_LIB, "invalid EAP type in EAP-SIM/AKA message",
|
||||
eap_type_names, hdr->type);
|
||||
return NULL;
|
||||
}
|
||||
@@ -869,7 +896,7 @@ static simaka_message_t *simaka_message_create_data(chunk_t data,
|
||||
this->public.add_attribute = (void(*)(simaka_message_t*, simaka_attribute_t type, chunk_t data))add_attribute;
|
||||
this->public.parse = (bool(*)(simaka_message_t*))parse;
|
||||
this->public.verify = (bool(*)(simaka_message_t*, chunk_t sigdata))verify;
|
||||
this->public.generate = (eap_payload_t*(*)(simaka_message_t*, chunk_t sigdata))generate;
|
||||
this->public.generate = (chunk_t(*)(simaka_message_t*, chunk_t sigdata))generate;
|
||||
this->public.destroy = (void(*)(simaka_message_t*))destroy;
|
||||
|
||||
this->attributes = linked_list_create();
|
||||
@@ -888,10 +915,10 @@ static simaka_message_t *simaka_message_create_data(chunk_t data,
|
||||
/**
|
||||
* See header.
|
||||
*/
|
||||
simaka_message_t *simaka_message_create_from_payload(eap_payload_t *payload,
|
||||
simaka_message_t *simaka_message_create_from_payload(chunk_t data,
|
||||
simaka_crypto_t *crypto)
|
||||
{
|
||||
return simaka_message_create_data(payload->get_data(payload), crypto);
|
||||
return simaka_message_create_data(data, crypto);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#define SIMAKA_MESSAGE_H_
|
||||
|
||||
#include <enum.h>
|
||||
#include <daemon.h>
|
||||
#include <eap/eap.h>
|
||||
|
||||
#include "simaka_crypto.h"
|
||||
|
||||
@@ -35,6 +35,7 @@ typedef enum simaka_attribute_t simaka_attribute_t;
|
||||
typedef enum simaka_subtype_t simaka_subtype_t;
|
||||
typedef enum simaka_notification_t simaka_notification_t;
|
||||
typedef enum simaka_client_error_t simaka_client_error_t;
|
||||
typedef struct simaka_message_t simaka_message_t;
|
||||
|
||||
/**
|
||||
* Subtypes of EAP-SIM/AKA messages
|
||||
@@ -235,9 +236,9 @@ struct simaka_message_t {
|
||||
* Generate a message, optionally encrypt attributes and create a MAC.
|
||||
*
|
||||
* @param sigdata additional data to include in signature, if any
|
||||
* @return generated eap payload, NULL if failed
|
||||
* @return allocated data of generated message
|
||||
*/
|
||||
eap_payload_t* (*generate)(simaka_message_t *this, chunk_t sigdata);
|
||||
chunk_t (*generate)(simaka_message_t *this, chunk_t sigdata);
|
||||
|
||||
/**
|
||||
* Destroy a simaka_message_t.
|
||||
@@ -262,11 +263,11 @@ simaka_message_t *simaka_message_create(bool request, u_int8_t identifier,
|
||||
/**
|
||||
* Create an simaka_message from a chunk of data.
|
||||
*
|
||||
* @param payload payload to create message from
|
||||
* @param data message data to parse
|
||||
* @param crypto EAP-SIM/AKA crypto helper
|
||||
* @return EAP message, NULL on error
|
||||
*/
|
||||
simaka_message_t *simaka_message_create_from_payload(eap_payload_t *payload,
|
||||
simaka_message_t *simaka_message_create_from_payload(chunk_t data,
|
||||
simaka_crypto_t *crypto);
|
||||
|
||||
#endif /** SIMAKA_MESSAGE_H_ @}*/
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2011 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 simaka_provider simaka_provider
|
||||
* @{ @ingroup libsimaka
|
||||
*/
|
||||
|
||||
#ifndef SIMAKA_PROVIDER_H_
|
||||
#define SIMAKA_PROVIDER_H_
|
||||
|
||||
typedef struct simaka_provider_t simaka_provider_t;
|
||||
|
||||
#include "simaka_manager.h"
|
||||
|
||||
#include <utils/identification.h>
|
||||
|
||||
/**
|
||||
* Interface for a triplet/quintuplet provider (used as EAP server).
|
||||
*
|
||||
* A SIM provider hands out triplets for SIM authentication and quintuplets
|
||||
* for AKA authentication. Multiple SIM provider instances can serve as
|
||||
* authentication backend to authenticate clients using SIM/AKA.
|
||||
* An implementation supporting only one of SIM/AKA authentication may
|
||||
* implement the other methods with return_false().
|
||||
*/
|
||||
struct simaka_provider_t {
|
||||
|
||||
/**
|
||||
* Create a challenge for SIM authentication.
|
||||
*
|
||||
* @param id permanent identity of peer to gen triplet for
|
||||
* @param rand RAND output buffer, fixed size 16 bytes
|
||||
* @param sres SRES output buffer, fixed size 4 byte
|
||||
* @param kc KC output buffer, fixed size 8 bytes
|
||||
* @return TRUE if triplet received, FALSE otherwise
|
||||
*/
|
||||
bool (*get_triplet)(simaka_provider_t *this, identification_t *id,
|
||||
char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN],
|
||||
char kc[SIM_KC_LEN]);
|
||||
|
||||
/**
|
||||
* Create a challenge for AKA authentication.
|
||||
*
|
||||
* The XRES value is the only one with variable length. Pass a buffer
|
||||
* of at least AKA_RES_MAX, the actual number of bytes is written to the
|
||||
* xres_len value. While the standard would allow any bit length between
|
||||
* 32 and 128 bits, we support only full bytes for now.
|
||||
*
|
||||
* @param id permanent identity of peer to create challenge for
|
||||
* @param rand buffer receiving random value rand
|
||||
* @param xres buffer receiving expected authentication result xres
|
||||
* @param xres_len nubmer of bytes written to xres buffer
|
||||
* @param ck buffer receiving encryption key ck
|
||||
* @param ik buffer receiving integrity key ik
|
||||
* @param autn authentication token autn
|
||||
* @return TRUE if quintuplet generated successfully
|
||||
*/
|
||||
bool (*get_quintuplet)(simaka_provider_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN],
|
||||
char xres[AKA_RES_MAX], int *xres_len,
|
||||
char ck[AKA_CK_LEN], char ik[AKA_IK_LEN],
|
||||
char autn[AKA_AUTN_LEN]);
|
||||
|
||||
/**
|
||||
* Process AKA resynchroniusation request of a peer.
|
||||
*
|
||||
* @param id permanent identity of peer requesting resynchronisation
|
||||
* @param rand random value rand
|
||||
* @param auts synchronization parameter auts
|
||||
* @return TRUE if resynchronized successfully
|
||||
*/
|
||||
bool (*resync)(simaka_provider_t *this, identification_t *id,
|
||||
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]);
|
||||
|
||||
/**
|
||||
* Check if peer uses a pseudonym, get permanent identity.
|
||||
*
|
||||
* @param id pseudonym identity candidate
|
||||
* @return permanent identity, NULL if id not a pseudonym
|
||||
*/
|
||||
identification_t* (*is_pseudonym)(simaka_provider_t *this,
|
||||
identification_t *id);
|
||||
|
||||
/**
|
||||
* Generate a pseudonym identitiy for a given peer identity.
|
||||
*
|
||||
* @param id permanent identity to generate a pseudonym for
|
||||
* @return generated pseudonym, NULL to not use a pseudonym identity
|
||||
*/
|
||||
identification_t* (*gen_pseudonym)(simaka_provider_t *this,
|
||||
identification_t *id);
|
||||
|
||||
/**
|
||||
* Check if peer uses reauthentication, retrieve reauth parameters.
|
||||
*
|
||||
* @param id reauthentication identity (candidate)
|
||||
* @param mk buffer receiving master key MK
|
||||
* @param counter pointer receiving current counter value, host order
|
||||
* @return permanent identity, NULL if id not a reauth identity
|
||||
*/
|
||||
identification_t* (*is_reauth)(simaka_provider_t *this, identification_t *id,
|
||||
char mk[HASH_SIZE_SHA1], u_int16_t *counter);
|
||||
|
||||
/**
|
||||
* Generate a fast reauthentication identity, associated to a master key.
|
||||
*
|
||||
* @param id permanent peer identity
|
||||
* @param mk master key to store along with generated identity
|
||||
* @return fast reauthentication identity, NULL to not use reauth
|
||||
*/
|
||||
identification_t* (*gen_reauth)(simaka_provider_t *this, identification_t *id,
|
||||
char mk[HASH_SIZE_SHA1]);
|
||||
};
|
||||
|
||||
#endif /** SIMAKA_CARD_H_ @}*/
|
||||
Reference in New Issue
Block a user