Moving charon to libcharon.

This commit is contained in:
Tobias Brunner
2010-03-19 13:34:52 +01:00
parent 7c11d10eb8
commit 08c5572602
480 changed files with 0 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-android.la
else
plugin_LTLIBRARIES = libstrongswan-android.la
endif
libstrongswan_android_la_SOURCES = \
android_plugin.c android_plugin.h \
android_handler.c android_handler.h
libstrongswan_android_la_LDFLAGS = -module -avoid-version
libstrongswan_android_la_LIBADD = -lcutils
@@ -0,0 +1,225 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 Tobias Brunner
* 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 "android_handler.h"
#include <utils/linked_list.h>
#include <cutils/properties.h>
typedef struct private_android_handler_t private_android_handler_t;
/**
* Private data of an android_handler_t object.
*/
struct private_android_handler_t {
/**
* Public android_handler_t interface.
*/
android_handler_t public;
/**
* List of registered DNS servers
*/
linked_list_t *dns;
};
/**
* Struct to store a pair of old and installed DNS servers
*/
typedef struct {
/** installed dns server */
host_t *dns;
/** old dns server */
host_t *old;
} dns_pair_t;
/**
* Destroy a pair of old and installed DNS servers
*/
void destroy_dns_pair(dns_pair_t *this)
{
DESTROY_IF(this->dns);
DESTROY_IF(this->old);
free(this);
}
/**
* Filter pairs of DNS servers
*/
bool filter_dns_pair(void *data, dns_pair_t **in, host_t **out)
{
*out = (*in)->dns;
return TRUE;
}
/**
* Read DNS server property with a given index
*/
host_t *get_dns_server(int index)
{
host_t *dns = NULL;
char key[10], value[PROPERTY_VALUE_MAX];
if (snprintf(key, sizeof(key), "net.dns%d", index) >= sizeof(key))
{
return NULL;
}
if (property_get(key, value, NULL) > 0)
{
dns = host_create_from_string(value, 0);
}
return dns;
}
/**
* Set DNS server property with a given index
*/
bool set_dns_server(int index, host_t *dns)
{
char key[10], value[PROPERTY_VALUE_MAX];
if (snprintf(key, sizeof(key), "net.dns%d", index) >= sizeof(key))
{
return FALSE;
}
if (dns)
{
if (snprintf(value, sizeof(value), "%H", dns) >= sizeof(value))
{
return FALSE;
}
}
else
{
value[0] = '\0';
}
if (property_set(key, value) != 0)
{
return FALSE;
}
return TRUE;
}
METHOD(attribute_handler_t, handle, bool,
private_android_handler_t *this, identification_t *id,
configuration_attribute_type_t type, chunk_t data)
{
switch (type)
{
case INTERNAL_IP4_DNS:
{
host_t *dns;
dns_pair_t *pair;
int index;
dns = host_create_from_chunk(AF_INET, data, 0);
if (dns)
{
pair = malloc_thing(dns_pair_t);
pair->dns = dns;
index = this->dns->get_count(this->dns) + 1;
pair->old = get_dns_server(index);
set_dns_server(index, dns);
this->dns->insert_last(this->dns, pair);
return TRUE;
}
return FALSE;
}
default:
return FALSE;
}
}
METHOD(attribute_handler_t, release, void,
private_android_handler_t *this, identification_t *server,
configuration_attribute_type_t type, chunk_t data)
{
if (type == INTERNAL_IP4_DNS)
{
enumerator_t *enumerator;
dns_pair_t *pair;
int index;
enumerator = this->dns->create_enumerator(this->dns);
for (index = 1; enumerator->enumerate(enumerator, &pair); index++)
{
if (chunk_equals(pair->dns->get_address(pair->dns), data))
{
this->dns->remove_at(this->dns, enumerator);
set_dns_server(index, pair->old);
destroy_dns_pair(pair);
}
}
enumerator->destroy(enumerator);
}
}
METHOD(enumerator_t, enumerate_dns, bool,
enumerator_t *this, configuration_attribute_type_t *type, chunk_t *data)
{
*type = INTERNAL_IP4_DNS;
*data = chunk_empty;
/* stop enumeration */
this->enumerate = (void*)return_false;
return TRUE;
}
METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
android_handler_t *this, identification_t *id, host_t *vip)
{
enumerator_t *enumerator;
INIT(enumerator,
.enumerate = (void*)_enumerate_dns,
.destroy = (void*)free,
);
return enumerator;
}
METHOD(android_handler_t, destroy, void,
private_android_handler_t *this)
{
this->dns->destroy_function(this->dns, (void*)destroy_dns_pair);
free(this);
}
/**
* See header
*/
android_handler_t *android_handler_create()
{
private_android_handler_t *this;
INIT(this,
.public = {
.handler = {
.handle = _handle,
.release = _release,
.create_attribute_enumerator = _create_attribute_enumerator,
},
.destroy = _destroy,
},
.dns = linked_list_create(),
);
return &this->public;
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2010 Martin Willi
* Copyright (C) 2010 Tobias Brunner
* 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 android_handler android_handler
* @{ @ingroup android
*/
#ifndef ANDROID_HANDLER_H_
#define ANDROID_HANDLER_H_
#include <attributes/attribute_handler.h>
typedef struct android_handler_t android_handler_t;
/**
* Android specific DNS attribute handler.
*/
struct android_handler_t {
/**
* Implements attribute_handler_t.
*/
attribute_handler_t handler;
/**
* Destroy a android_handler_t.
*/
void (*destroy)(android_handler_t *this);
};
/**
* Create a android_handler instance.
*/
android_handler_t *android_handler_create();
#endif /** ANDROID_HANDLER_H_ @}*/
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2010 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 "android_plugin.h"
#include "android_handler.h"
#include <library.h>
#include <daemon.h>
typedef struct private_android_plugin_t private_android_plugin_t;
/**
* Private data of an android_plugin_t object.
*/
struct private_android_plugin_t {
/**
* Public android_plugin_t interface.
*/
android_plugin_t public;
/**
* Android specific DNS handler
*/
android_handler_t *handler;
};
METHOD(plugin_t, destroy, void,
private_android_plugin_t *this)
{
lib->attributes->remove_handler(lib->attributes, &this->handler->handler);
this->handler->destroy(this->handler);
free(this);
}
/**
* See header
*/
plugin_t *android_plugin_create()
{
private_android_plugin_t *this;
INIT(this,
.public.plugin = {
.destroy = _destroy,
},
.handler = android_handler_create(),
);
lib->attributes->add_handler(lib->attributes, &this->handler->handler);
return &this->public.plugin;
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2010 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 android android
* @ingroup cplugins
*
* @defgroup android_plugin android_plugin
* @{ @ingroup android
*/
#ifndef ANDROID_PLUGIN_H_
#define ANDROID_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct android_plugin_t android_plugin_t;
/**
* Plugin providing functionality specific to the Android platform.
*/
struct android_plugin_t {
/**
* Implements plugin interface.
*/
plugin_t plugin;
};
#endif /** ANDROID_PLUGIN_H_ @}*/
+16
View File
@@ -0,0 +1,16 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-attr.la
else
plugin_LTLIBRARIES = libstrongswan-attr.la
endif
libstrongswan_attr_la_SOURCES = \
attr_plugin.h attr_plugin.c \
attr_provider.h attr_provider.c
libstrongswan_attr_la_LDFLAGS = -module -avoid-version
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (C) 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 "attr_plugin.h"
#include "attr_provider.h"
#include <daemon.h>
typedef struct private_attr_plugin_t private_attr_plugin_t;
/**
* private data of attr plugin
*/
struct private_attr_plugin_t {
/**
* implements plugin interface
*/
attr_plugin_t public;
/**
* CFG attributes provider
*/
attr_provider_t *provider;
};
/**
* Implementation of plugin_t.destroy
*/
static void destroy(private_attr_plugin_t *this)
{
lib->attributes->remove_provider(lib->attributes, &this->provider->provider);
this->provider->destroy(this->provider);
free(this);
}
/*
* see header file
*/
plugin_t *attr_plugin_create()
{
private_attr_plugin_t *this = malloc_thing(private_attr_plugin_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
this->provider = attr_provider_create();
lib->attributes->add_provider(lib->attributes, &this->provider->provider);
return &this->public.plugin;
}
+42
View File
@@ -0,0 +1,42 @@
/*
* Copyright (C) 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 attr attr
* @ingroup cplugins
*
* @defgroup attr_plugin attr_plugin
* @{ @ingroup attr
*/
#ifndef ATTR_PLUGIN_H_
#define ATTR_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct attr_plugin_t attr_plugin_t;
/**
* Plugin providing configuration attribute through strongswan.conf.
*/
struct attr_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** ATTR_PLUGIN_H_ @}*/
+236
View File
@@ -0,0 +1,236 @@
/*
* Copyright (C) 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 "attr_provider.h"
#include <time.h>
#include <daemon.h>
#define SERVER_MAX 2
typedef struct private_attr_provider_t private_attr_provider_t;
typedef struct attribute_entry_t attribute_entry_t;
/**
* private data of attr_provider
*/
struct private_attr_provider_t {
/**
* public functions
*/
attr_provider_t public;
/**
* List of attributes, attribute_entry_t
*/
linked_list_t *attributes;
};
struct attribute_entry_t {
/** type of attribute */
configuration_attribute_type_t type;
/** attribute value */
chunk_t value;
};
/**
* convert enumerator value from attribute_entry
*/
static bool attr_enum_filter(void *null, attribute_entry_t **in,
configuration_attribute_type_t *type, void* none, chunk_t *value)
{
*type = (*in)->type;
*value = (*in)->value;
return TRUE;
}
/**
* Implementation of attribute_provider_t.create_attribute_enumerator
*/
static enumerator_t* create_attribute_enumerator(private_attr_provider_t *this,
identification_t *id, host_t *vip)
{
if (vip)
{
return enumerator_create_filter(
this->attributes->create_enumerator(this->attributes),
(void*)attr_enum_filter, NULL, NULL);
}
return enumerator_create_empty();
}
/**
* Implementation of attr_provider_t.destroy
*/
static void destroy(private_attr_provider_t *this)
{
attribute_entry_t *entry;
while (this->attributes->remove_last(this->attributes,
(void**)&entry) == SUCCESS)
{
free(entry->value.ptr);
free(entry);
}
this->attributes->destroy(this->attributes);
free(this);
}
/**
* Add an attribute entry to the list
*/
static void add_legacy_entry(private_attr_provider_t *this, char *key, int nr,
configuration_attribute_type_t type)
{
attribute_entry_t *entry;
host_t *host;
char *str;
str = lib->settings->get_str(lib->settings, "charon.%s%d", NULL, key, nr);
if (str)
{
host = host_create_from_string(str, 0);
if (host)
{
entry = malloc_thing(attribute_entry_t);
if (host->get_family(host) == AF_INET6)
{
switch (type)
{
case INTERNAL_IP4_DNS:
type = INTERNAL_IP6_DNS;
break;
case INTERNAL_IP4_NBNS:
type = INTERNAL_IP6_NBNS;
break;
default:
break;
}
}
entry->type = type;
entry->value = chunk_clone(host->get_address(host));
host->destroy(host);
this->attributes->insert_last(this->attributes, entry);
}
}
}
/**
* Key to attribute type mappings, for v4 and v6 attributes
*/
static struct {
char *name;
configuration_attribute_type_t v4;
configuration_attribute_type_t v6;
} keys[] = {
{"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS},
{"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS},
{"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS},
{"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP},
{"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK},
{"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER},
};
/**
* Load (numerical) entries from the plugins.attr namespace
*/
static void load_entries(private_attr_provider_t *this)
{
enumerator_t *enumerator, *tokens;
char *key, *value, *token;
enumerator = lib->settings->create_key_value_enumerator(lib->settings,
"charon.plugins.attr");
while (enumerator->enumerate(enumerator, &key, &value))
{
configuration_attribute_type_t type;
attribute_entry_t *entry;
host_t *host;
int i;
type = atoi(key);
tokens = enumerator_create_token(value, ",", " ");
while (tokens->enumerate(tokens, &token))
{
host = host_create_from_string(token, 0);
if (!host)
{
DBG1(DBG_CFG, "invalid host in key %s: %s", key, token);
continue;
}
if (!type)
{
for (i = 0; i < countof(keys); i++)
{
if (streq(key, keys[i].name))
{
if (host->get_family(host) == AF_INET)
{
type = keys[i].v4;
}
else
{
type = keys[i].v6;
}
}
}
if (!type)
{
DBG1(DBG_CFG, "mapping attribute type %s failed", key);
break;
}
}
entry = malloc_thing(attribute_entry_t);
entry->type = type;
entry->value = chunk_clone(host->get_address(host));
host->destroy(host);
this->attributes->insert_last(this->attributes, entry);
}
tokens->destroy(tokens);
}
enumerator->destroy(enumerator);
}
/*
* see header file
*/
attr_provider_t *attr_provider_create(database_t *db)
{
private_attr_provider_t *this;
int i;
this = malloc_thing(private_attr_provider_t);
this->public.provider.acquire_address = (host_t*(*)(attribute_provider_t *this, char*, identification_t *, host_t *))return_null;
this->public.provider.release_address = (bool(*)(attribute_provider_t *this, char*,host_t *, identification_t*))return_false;
this->public.provider.create_attribute_enumerator = (enumerator_t*(*)(attribute_provider_t*, identification_t *id, host_t *vip))create_attribute_enumerator;
this->public.destroy = (void(*)(attr_provider_t*))destroy;
this->attributes = linked_list_create();
for (i = 1; i <= SERVER_MAX; i++)
{
add_legacy_entry(this, "dns", i, INTERNAL_IP4_DNS);
add_legacy_entry(this, "nbns", i, INTERNAL_IP4_NBNS);
}
load_entries(this);
return &this->public;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 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 attr_provider attr_provider
* @{ @ingroup attr
*/
#ifndef ATTR_PROVIDER_H_
#define ATTR_PROVIDER_H_
#include <attributes/attribute_provider.h>
typedef struct attr_provider_t attr_provider_t;
/**
* Provide configuration attributes through static strongswan.conf definition.
*/
struct attr_provider_t {
/**
* Implements attribute provider interface
*/
attribute_provider_t provider;
/**
* Destroy a attr_provider instance.
*/
void (*destroy)(attr_provider_t *this);
};
/**
* Create a attr_provider instance.
*/
attr_provider_t *attr_provider_create();
#endif /** ATTR_PROVIDER @}*/
+19
View File
@@ -0,0 +1,19 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon \
-I$(top_srcdir)/src/libsimaka
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-aka.la
else
plugin_LTLIBRARIES = libstrongswan-eap-aka.la
libstrongswan_eap_aka_la_LIBADD = $(top_builddir)/src/libsimaka/libsimaka.la
endif
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_LDFLAGS = -module -avoid-version
@@ -0,0 +1,583 @@
/*
* 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;
/**
* permanent ID of peer
*/
identification_t *permanent;
/**
* Pseudonym identity the peer uses
*/
identification_t *pseudonym;
/**
* Reauthentication identity the peer uses
*/
identification_t *reauth;
/**
* MSK
*/
chunk_t msk;
/**
* Master key, if reauthentication is used
*/
char mk[HASH_SIZE_SHA1];
/**
* Counter value if reauthentication is used
*/
u_int16_t counter;
};
/**
* 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, this->crypto);
encoded = htons(AKA_UNABLE_TO_PROCESS);
message->add_attribute(message, AT_CLIENT_ERROR_CODE,
chunk_create((char*)&encoded, sizeof(encoded)));
out = message->generate(message, chunk_empty);
message->destroy(message);
return out;
}
/**
* 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, id = chunk_empty;
simaka_attribute_t id_req = 0;
/* reset previously uses reauthentication/pseudonym data */
this->crypto->clear_keys(this->crypto);
DESTROY_IF(this->pseudonym);
this->pseudonym = NULL;
DESTROY_IF(this->reauth);
this->reauth = NULL;
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_ANY_ID_REQ:
case AT_FULLAUTH_ID_REQ:
case AT_PERMANENT_ID_REQ:
id_req = type;
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);
switch (id_req)
{
case AT_ANY_ID_REQ:
this->reauth = charon->sim->card_get_reauth(charon->sim,
this->permanent, this->mk, &this->counter);
if (this->reauth)
{
id = this->reauth->get_encoding(this->reauth);
break;
}
/* FALL */
case AT_FULLAUTH_ID_REQ:
this->pseudonym = charon->sim->card_get_pseudonym(charon->sim,
this->permanent);
if (this->pseudonym)
{
id = this->pseudonym->get_encoding(this->pseudonym);
break;
}
/* FALL */
case AT_PERMANENT_ID_REQ:
id = this->permanent->get_encoding(this->permanent);
break;
default:
break;
}
message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
AKA_IDENTITY, this->crypto);
if (id.len)
{
message->add_attribute(message, AT_IDENTITY, id);
}
*out = message->generate(message, chunk_empty);
message->destroy(message);
return NEED_MORE;
}
/**
* 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;
chunk_t data, rand = chunk_empty, autn = chunk_empty, mk;
u_char res[AKA_RES_MAX], ck[AKA_CK_LEN], ik[AKA_IK_LEN], auts[AKA_AUTS_LEN];
int res_len;
identification_t *id;
status_t status;
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;
}
status = charon->sim->card_get_quintuplet(charon->sim, this->permanent,
rand.ptr, autn.ptr, ck, ik, res, &res_len);
if (status == INVALID_STATE &&
charon->sim->card_resync(charon->sim, this->permanent, 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, this->crypto);
message->add_attribute(message, AT_AUTS,
chunk_create(auts, AKA_AUTS_LEN));
*out = message->generate(message, chunk_empty);
message->destroy(message);
return NEED_MORE;
}
if (status != SUCCESS)
{
DBG1(DBG_IKE, "no USIM found with quintuplets for '%Y', sending %N",
this->permanent, simaka_subtype_names, AKA_AUTHENTICATION_REJECT);
message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
AKA_AUTHENTICATION_REJECT, this->crypto);
*out = message->generate(message, chunk_empty);
message->destroy(message);
return NEED_MORE;
}
id = this->permanent;
if (this->pseudonym)
{
id = this->pseudonym;
}
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, id, data, &mk);
memcpy(this->mk, mk.ptr, mk.len);
free(mk.ptr);
/* Verify AT_MAC attribute and parse() again after key derivation,
* reading encrypted attributes */
if (!in->verify(in, chunk_empty) || !in->parse(in))
{
*out = create_client_error(this, in->get_identifier(in));
return NEED_MORE;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_NEXT_REAUTH_ID:
this->counter = 0;
id = identification_create_from_data(data);
charon->sim->card_set_reauth(charon->sim, this->permanent, id,
this->mk, this->counter);
id->destroy(id);
break;
case AT_NEXT_PSEUDONYM:
id = identification_create_from_data(data);
charon->sim->card_set_pseudonym(charon->sim, this->permanent, id);
id->destroy(id);
break;
default:
break;
}
}
enumerator->destroy(enumerator);
message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
AKA_CHALLENGE, this->crypto);
message->add_attribute(message, AT_RES, chunk_create(res, res_len));
*out = message->generate(message, chunk_empty);
message->destroy(message);
return NEED_MORE;
}
/**
* Check if a received counter value is acceptable
*/
static bool counter_too_small(private_eap_aka_peer_t *this, chunk_t chunk)
{
u_int16_t counter;
memcpy(&counter, chunk.ptr, sizeof(counter));
counter = htons(counter);
return counter < this->counter;
}
/**
* process an EAP-AKA/Request/Reauthentication message
*/
static status_t process_reauthentication(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, counter = chunk_empty, nonce = chunk_empty, id = chunk_empty;
if (!this->reauth)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, AKA_REAUTHENTICATION);
*out = create_client_error(this, in->get_identifier(in));
return NEED_MORE;
}
this->crypto->derive_keys_reauth(this->crypto,
chunk_create(this->mk, HASH_SIZE_SHA1));
/* verify MAC and parse again with decryption key */
if (!in->verify(in, chunk_empty) || !in->parse(in))
{
*out = create_client_error(this, in->get_identifier(in));
return NEED_MORE;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_COUNTER:
counter = data;
break;
case AT_NONCE_S:
nonce = data;
break;
case AT_NEXT_REAUTH_ID:
id = 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 (!nonce.len || !counter.len)
{
DBG1(DBG_IKE, "EAP-AKA/Request/Reauthentication message incomplete");
*out = create_client_error(this, in->get_identifier(in));
return NEED_MORE;
}
message = simaka_message_create(FALSE, in->get_identifier(in), EAP_AKA,
AKA_REAUTHENTICATION, this->crypto);
if (counter_too_small(this, counter))
{
DBG1(DBG_IKE, "reauthentication counter too small");
message->add_attribute(message, AT_COUNTER_TOO_SMALL, chunk_empty);
}
else
{
free(this->msk.ptr);
this->msk = this->crypto->derive_keys_reauth_msk(this->crypto,
this->reauth, counter, nonce,
chunk_create(this->mk, HASH_SIZE_SHA1));
if (id.len)
{
identification_t *reauth;
reauth = identification_create_from_data(data);
charon->sim->card_set_reauth(charon->sim, this->permanent, reauth,
this->mk, this->counter);
reauth->destroy(reauth);
}
}
message->add_attribute(message, AT_COUNTER, counter);
*out = message->generate(message, nonce);
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, this->crypto);
*out = message->generate(message, 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, this->crypto);
if (!message)
{
*out = create_client_error(this, in->get_identifier(in));
return NEED_MORE;
}
if (!message->parse(message))
{
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_REAUTHENTICATION:
status = process_reauthentication(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->permanent->destroy(this->permanent);
DESTROY_IF(this->pseudonym);
DESTROY_IF(this->reauth);
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->permanent = peer->clone(peer);
this->pseudonym = NULL;
this->reauth = NULL;
this->msk = chunk_empty;
return &this->public;
}
@@ -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_ @}*/
@@ -0,0 +1,51 @@
/*
* 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.
*/
#include "eap_aka_plugin.h"
#include "eap_aka_peer.h"
#include "eap_aka_server.h"
#include <daemon.h>
/**
* Implementation of plugin_t.destroy
*/
static void destroy(eap_aka_plugin_t *this)
{
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_aka_server_create);
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_aka_peer_create);
free(this);
}
/*
* see header file
*/
plugin_t *eap_aka_plugin_create()
{
eap_aka_plugin_t *this = malloc_thing(eap_aka_plugin_t);
this->plugin.destroy = (void(*)(plugin_t*))destroy;
charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_SERVER,
(eap_constructor_t)eap_aka_server_create);
charon->eap->add_method(charon->eap, EAP_AKA, 0, EAP_PEER,
(eap_constructor_t)eap_aka_peer_create);
return &this->plugin;
}
@@ -0,0 +1,45 @@
/*
* 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 eap_aka
* @ingroup cplugins
*
* @defgroup eap_aka_plugin eap_aka_plugin
* @{ @ingroup eap_aka
*/
#ifndef EAP_AKA_PLUGIN_H_
#define EAP_AKA_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_aka_plugin_t eap_aka_plugin_t;
/**
* EAP-AKA plugin.
*
* EAP-AKA uses 3rd generation mobile phone standard authentication
* mechanism for authentication, as defined RFC4187.
*/
struct eap_aka_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_AKA_PLUGIN_H_ @}*/
@@ -0,0 +1,700 @@
/*
* 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>
/** length of the AT_NONCE_S value */
#define NONCE_LEN 16
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;
/**
* permanent ID of the peer
*/
identification_t *permanent;
/**
* pseudonym ID of peer
*/
identification_t *pseudonym;
/**
* reauthentication ID of peer
*/
identification_t *reauth;
/**
* EAP identifier value
*/
u_int8_t identifier;
/**
* Expected Result XRES
*/
chunk_t xres;
/**
* Random value RAND
*/
chunk_t rand;
/**
* MSK
*/
chunk_t msk;
/**
* Nonce value used in AT_NONCE_S
*/
chunk_t nonce;
/**
* Counter value negotiated, network order
*/
chunk_t counter;
/**
* Do we request fast reauthentication?
*/
bool use_reauth;
/**
* Do we request pseudonym identities?
*/
bool use_pseudonym;
/**
* Do we request permanent identities?
*/
bool use_permanent;
/**
* EAP-AKA message we have initiated
*/
simaka_subtype_t pending;
/**
* Did the client send a synchronize request?
*/
bool synchronized;
};
/**
* Create EAP-AKA/Request/Identity message
*/
static status_t identity(private_eap_aka_server_t *this, eap_payload_t **out)
{
simaka_message_t *message;
message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
AKA_IDENTITY, this->crypto);
if (this->use_reauth)
{
message->add_attribute(message, AT_ANY_ID_REQ, chunk_empty);
}
else if (this->use_pseudonym)
{
message->add_attribute(message, AT_FULLAUTH_ID_REQ, chunk_empty);
}
else if (this->use_permanent)
{
message->add_attribute(message, AT_PERMANENT_ID_REQ, chunk_empty);
}
*out = message->generate(message, chunk_empty);
message->destroy(message);
this->pending = AKA_IDENTITY;
return NEED_MORE;
}
/**
* Create EAP-AKA/Request/Challenge message
*/
static status_t challenge(private_eap_aka_server_t *this, eap_payload_t **out)
{
simaka_message_t *message;
char rand[AKA_RAND_LEN], xres[AKA_RES_MAX];
char ck[AKA_CK_LEN], ik[AKA_IK_LEN], autn[AKA_AUTN_LEN];
int xres_len;
chunk_t data, mk;
identification_t *id;
if (!charon->sim->provider_get_quintuplet(charon->sim, this->permanent,
rand, xres, &xres_len, ck, ik, autn))
{
if (this->use_pseudonym)
{
/* probably received a pseudonym/reauth id we couldn't map */
DBG1(DBG_IKE, "failed to map pseudonym/reauth identity '%Y', "
"fallback to permanent identity request", this->permanent);
this->use_pseudonym = FALSE;
DESTROY_IF(this->pseudonym);
this->pseudonym = NULL;
return identity(this, out);
}
return FAILED;
}
id = this->permanent;
if (this->pseudonym)
{
id = this->pseudonym;
}
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, id, data, &mk);
this->rand = chunk_clone(chunk_create(rand, AKA_RAND_LEN));
this->xres = chunk_clone(chunk_create(xres, xres_len));
message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
AKA_CHALLENGE, this->crypto);
message->add_attribute(message, AT_RAND, this->rand);
message->add_attribute(message, AT_AUTN, chunk_create(autn, AKA_AUTN_LEN));
id = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk.ptr);
if (id)
{
message->add_attribute(message, AT_NEXT_REAUTH_ID,
id->get_encoding(id));
id->destroy(id);
}
else
{
id = charon->sim->provider_gen_pseudonym(charon->sim, this->permanent);
if (id)
{
message->add_attribute(message, AT_NEXT_PSEUDONYM,
id->get_encoding(id));
id->destroy(id);
}
}
*out = message->generate(message, chunk_empty);
message->destroy(message);
free(mk.ptr);
this->pending = AKA_CHALLENGE;
return NEED_MORE;
}
/**
* Initiate EAP-AKA/Request/Re-authentication message
*/
static status_t reauthenticate(private_eap_aka_server_t *this,
char mk[HASH_SIZE_SHA1], u_int16_t counter,
eap_payload_t **out)
{
simaka_message_t *message;
identification_t *next;
chunk_t mkc;
rng_t *rng;
DBG1(DBG_IKE, "initiating EAP-AKA reauthentication");
rng = this->crypto->get_rng(this->crypto);
rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
mkc = chunk_create(mk, HASH_SIZE_SHA1);
counter = htons(counter);
this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter)));
this->crypto->derive_keys_reauth(this->crypto, mkc);
this->msk = this->crypto->derive_keys_reauth_msk(this->crypto,
this->reauth, this->counter, this->nonce, mkc);
message = simaka_message_create(TRUE, this->identifier++, EAP_AKA,
AKA_REAUTHENTICATION, this->crypto);
message->add_attribute(message, AT_COUNTER, this->counter);
message->add_attribute(message, AT_NONCE_S, this->nonce);
next = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk);
if (next)
{
message->add_attribute(message, AT_NEXT_REAUTH_ID,
next->get_encoding(next));
next->destroy(next);
}
*out = message->generate(message, chunk_empty);
message->destroy(message);
this->pending = SIM_REAUTHENTICATION;
return NEED_MORE;
}
/**
* Implementation of eap_method_t.initiate
*/
static status_t initiate(private_eap_aka_server_t *this, eap_payload_t **out)
{
if (this->use_permanent || this->use_pseudonym || this->use_reauth)
{
return identity(this, out);
}
return challenge(this, out);
}
/**
* Process EAP-AKA/Response/Identity message
*/
static status_t process_identity(private_eap_aka_server_t *this,
simaka_message_t *in, eap_payload_t **out)
{
identification_t *permanent, *id;
enumerator_t *enumerator;
simaka_attribute_t type;
chunk_t data, identity = chunk_empty;
if (this->pending != AKA_IDENTITY)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, AKA_IDENTITY);
return FAILED;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_IDENTITY:
identity = data;
break;
default:
if (!simaka_attribute_skippable(type))
{
enumerator->destroy(enumerator);
return FAILED;
}
break;
}
}
enumerator->destroy(enumerator);
if (!identity.len)
{
DBG1(DBG_IKE, "received incomplete Identity response");
return FAILED;
}
id = identification_create_from_data(identity);
if (this->use_reauth)
{
char mk[HASH_SIZE_SHA1];
u_int16_t counter;
permanent = charon->sim->provider_is_reauth(charon->sim, id,
mk, &counter);
if (permanent)
{
this->permanent->destroy(this->permanent);
this->permanent = permanent;
this->reauth = id;
return reauthenticate(this, mk, counter, out);
}
/* unable to map, maybe a pseudonym? */
DBG1(DBG_IKE, "'%Y' is not a reauth identity", id);
this->use_reauth = FALSE;
}
if (this->use_pseudonym)
{
permanent = charon->sim->provider_is_pseudonym(charon->sim, id);
if (permanent)
{
this->permanent->destroy(this->permanent);
this->permanent = permanent;
this->pseudonym = id->clone(id);
/* we already have a new permanent identity now */
this->use_permanent = FALSE;
}
else
{
DBG1(DBG_IKE, "'%Y' is not a pseudonym", id);
}
}
if (!this->pseudonym && this->use_permanent)
{
/* got a permanent identity or a pseudonym reauth id wou couldn't map,
* try to get quintuplets */
DBG1(DBG_IKE, "received identity '%Y'", id);
this->permanent->destroy(this->permanent);
this->permanent = id->clone(id);
}
id->destroy(id);
return challenge(this, out);
}
/**
* 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;
if (this->pending != AKA_CHALLENGE)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, AKA_CHALLENGE);
return FAILED;
}
/* verify MAC of EAP message, AT_MAC */
if (!in->verify(in, chunk_empty))
{
return FAILED;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_RES:
res = data;
break;
default:
if (!simaka_attribute_skippable(type))
{
enumerator->destroy(enumerator);
return FAILED;
}
break;
}
}
enumerator->destroy(enumerator);
/* 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 an EAP-AKA/Response/Reauthentication message
*/
static status_t process_reauthentication(private_eap_aka_server_t *this,
simaka_message_t *in, eap_payload_t **out)
{
enumerator_t *enumerator;
simaka_attribute_t type;
chunk_t data, counter = chunk_empty;
bool too_small = FALSE;
if (this->pending != AKA_REAUTHENTICATION)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, AKA_REAUTHENTICATION);
return FAILED;
}
/* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S" */
if (!in->verify(in, this->nonce))
{
return FAILED;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_COUNTER:
counter = data;
break;
case AT_COUNTER_TOO_SMALL:
too_small = TRUE;
break;
default:
if (!simaka_attribute_skippable(type))
{
enumerator->destroy(enumerator);
return FAILED;
}
break;
}
}
enumerator->destroy(enumerator);
if (too_small)
{
DBG1(DBG_IKE, "received %N, initiating full authentication",
simaka_attribute_names, AT_COUNTER_TOO_SMALL);
this->use_reauth = FALSE;
this->crypto->clear_keys(this->crypto);
return challenge(this, out);
}
if (!chunk_equals(counter, this->counter))
{
DBG1(DBG_IKE, "received counter does not match");
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)
{
enumerator_t *enumerator;
simaka_attribute_t type;
chunk_t data, auts = chunk_empty;
if (this->synchronized)
{
DBG1(DBG_IKE, "received %N, but peer did already resynchronize",
simaka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
return FAILED;
}
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 (!simaka_attribute_skippable(type))
{
enumerator->destroy(enumerator);
return FAILED;
}
break;
}
}
enumerator->destroy(enumerator);
if (!auts.len)
{
DBG1(DBG_IKE, "synchronization request didn't contain usable AUTS");
return FAILED;
}
if (!charon->sim->provider_resync(charon->sim, this->permanent,
this->rand.ptr, auts.ptr))
{
DBG1(DBG_IKE, "no AKA provider found supporting "
"resynchronization for '%Y'", this->permanent);
return FAILED;
}
this->synchronized = TRUE;
return challenge(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, this->crypto);
if (!message)
{
return FAILED;
}
if (!message->parse(message))
{
message->destroy(message);
return FAILED;
}
switch (message->get_subtype(message))
{
case AKA_IDENTITY:
status = process_identity(this, message, out);
break;
case AKA_CHALLENGE:
status = process_challenge(this, message);
break;
case AKA_REAUTHENTICATION:
status = process_reauthentication(this, message, out);
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->permanent->destroy(this->permanent);
DESTROY_IF(this->pseudonym);
DESTROY_IF(this->reauth);
free(this->xres.ptr);
free(this->rand.ptr);
free(this->nonce.ptr);
free(this->msk.ptr);
free(this->counter.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->permanent = peer->clone(peer);
this->pseudonym = NULL;
this->reauth = NULL;
this->xres = chunk_empty;
this->rand = chunk_empty;
this->nonce = chunk_empty;
this->msk = chunk_empty;
this->counter = chunk_empty;
this->pending = 0;
this->synchronized = FALSE;
this->use_reauth = this->use_pseudonym = this->use_permanent =
lib->settings->get_bool(lib->settings,
"charon.plugins.eap-aka.request_identity", TRUE);
/* generate a non-zero identifier */
do {
this->identifier = random();
} while (!this->identifier);
return &this->public;
}
@@ -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_ @}*/
@@ -0,0 +1,19 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-aka-3gpp2.la
else
plugin_LTLIBRARIES = libstrongswan-eap-aka-3gpp2.la
endif
libstrongswan_eap_aka_3gpp2_la_SOURCES = \
eap_aka_3gpp2_plugin.h eap_aka_3gpp2_plugin.c \
eap_aka_3gpp2_card.h eap_aka_3gpp2_card.c \
eap_aka_3gpp2_provider.h eap_aka_3gpp2_provider.c \
eap_aka_3gpp2_functions.h eap_aka_3gpp2_functions.c
libstrongswan_eap_aka_3gpp2_la_LDFLAGS = -module -avoid-version
libstrongswan_eap_aka_3gpp2_la_LIBADD = -lgmp
@@ -0,0 +1,178 @@
/*
* 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.
*/
#include "eap_aka_3gpp2_card.h"
#include <daemon.h>
typedef struct private_eap_aka_3gpp2_card_t private_eap_aka_3gpp2_card_t;
/**
* Private data of an eap_aka_3gpp2_card_t object.
*/
struct private_eap_aka_3gpp2_card_t {
/**
* Public eap_aka_3gpp2_card_t interface.
*/
eap_aka_3gpp2_card_t public;
/**
* AKA functions
*/
eap_aka_3gpp2_functions_t *f;
/**
* do sequence number checking?
*/
bool seq_check;
/**
* SQN stored in this pseudo-USIM
*/
char sqn[AKA_SQN_LEN];
};
/**
* Functions from eap_aka_3gpp2_provider.c
*/
bool eap_aka_3gpp2_get_k(identification_t *id, char k[AKA_K_LEN]);
void eap_aka_3gpp2_get_sqn(char sqn[AKA_SQN_LEN], int offset);
/**
* Implementation of sim_card_t.get_quintuplet
*/
static status_t get_quintuplet(private_eap_aka_3gpp2_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)
{
char *amf, *mac;
char k[AKA_K_LEN], ak[AKA_AK_LEN], sqn[AKA_SQN_LEN], xmac[AKA_MAC_LEN];
if (!eap_aka_3gpp2_get_k(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
return FAILED;
}
/* AUTN = SQN xor AK | AMF | MAC */
DBG3(DBG_IKE, "received autn %b", autn, AKA_AUTN_LEN);
DBG3(DBG_IKE, "using K %b", k, AKA_K_LEN);
DBG3(DBG_IKE, "using rand %b", rand, AKA_RAND_LEN);
memcpy(sqn, autn, AKA_SQN_LEN);
amf = autn + AKA_SQN_LEN;
mac = autn + AKA_SQN_LEN + AKA_AMF_LEN;
/* XOR anonymity key AK into SQN to decrypt it */
this->f->f5(this->f, k, rand, ak);
DBG3(DBG_IKE, "using ak %b", ak, AKA_AK_LEN);
memxor(sqn, ak, AKA_SQN_LEN);
DBG3(DBG_IKE, "using sqn %b", sqn, AKA_SQN_LEN);
/* calculate expected MAC and compare against received one */
this->f->f1(this->f, k, rand, sqn, amf, xmac);
if (!memeq(mac, xmac, AKA_MAC_LEN))
{
DBG1(DBG_IKE, "received MAC does not match XMAC");
DBG3(DBG_IKE, "MAC %b\nXMAC %b", mac, AKA_MAC_LEN, xmac, AKA_MAC_LEN);
return FAILED;
}
if (this->seq_check && memcmp(this->sqn, sqn, AKA_SQN_LEN) >= 0)
{
DBG3(DBG_IKE, "received SQN %b\ncurrent SQN %b",
sqn, AKA_SQN_LEN, this->sqn, AKA_SQN_LEN);
return INVALID_STATE;
}
/* update stored SQN to the received one */
memcpy(this->sqn, sqn, AKA_SQN_LEN);
/* CK/IK */
this->f->f3(this->f, k, rand, ck);
this->f->f4(this->f, k, rand, ik);
/* calculate RES */
this->f->f2(this->f, k, rand, res);
*res_len = AKA_RES_MAX;
return SUCCESS;
}
/**
* Implementation of sim_card_t.resync
*/
static bool resync(private_eap_aka_3gpp2_card_t *this, identification_t *id,
char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
{
char amf[AKA_AMF_LEN], k[AKA_K_LEN], aks[AKA_AK_LEN], macs[AKA_MAC_LEN];
if (!eap_aka_3gpp2_get_k(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to resync AKA", id);
return FALSE;
}
/* AMF is set to zero in resync */
memset(amf, 0, AKA_AMF_LEN);
this->f->f5star(this->f, k, rand, aks);
this->f->f1star(this->f, k, rand, this->sqn, amf, macs);
/* AUTS = SQN xor AKS | MACS */
memcpy(auts, this->sqn, AKA_SQN_LEN);
memxor(auts, aks, AKA_AK_LEN);
memcpy(auts + AKA_AK_LEN, macs, AKA_MAC_LEN);
return TRUE;
}
/**
* Implementation of eap_aka_3gpp2_card_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_card_t *this)
{
free(this);
}
/**
* See header
*/
eap_aka_3gpp2_card_t *eap_aka_3gpp2_card_create(eap_aka_3gpp2_functions_t *f)
{
private_eap_aka_3gpp2_card_t *this = malloc_thing(private_eap_aka_3gpp2_card_t);
this->public.card.get_triplet = (bool(*)(sim_card_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
this->public.card.get_quintuplet = (status_t(*)(sim_card_t*, 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))get_quintuplet;
this->public.card.resync = (bool(*)(sim_card_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))resync;
this->public.card.get_pseudonym = (identification_t*(*)(sim_card_t*, identification_t *id))return_null;
this->public.card.set_pseudonym = (void(*)(sim_card_t*, identification_t *id, identification_t *pseudonym))nop;
this->public.card.get_reauth = (identification_t*(*)(sim_card_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.card.set_reauth = (void(*)(sim_card_t*, identification_t *id, identification_t* next, char mk[HASH_SIZE_SHA1], u_int16_t counter))nop;
this->public.destroy = (void(*)(eap_aka_3gpp2_card_t*))destroy;
this->f = f;
this->seq_check = lib->settings->get_bool(lib->settings,
"charon.plugins.eap-aka-3gpp2.seq_check",
#ifdef SEQ_CHECK /* handle legacy compile time configuration as default */
TRUE);
#else /* !SEQ_CHECK */
FALSE);
#endif /* SEQ_CHECK */
eap_aka_3gpp2_get_sqn(this->sqn, 0);
return &this->public;
}
@@ -0,0 +1,53 @@
/*
* 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_3gpp2_card eap_aka_3gpp2_card
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_CARD_H_
#define EAP_AKA_3GPP2_CARD_H_
#include "eap_aka_3gpp2_functions.h"
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_aka_3gpp2_card_t eap_aka_3gpp2_card_t;
/**
* SIM card implementation using a set of AKA functions.
*/
struct eap_aka_3gpp2_card_t {
/**
* Implements sim_card_t interface
*/
sim_card_t card;
/**
* Destroy a eap_aka_3gpp2_card_t.
*/
void (*destroy)(eap_aka_3gpp2_card_t *this);
};
/**
* Create a eap_aka_3gpp2_card instance.
*
* @param f AKA functions
*/
eap_aka_3gpp2_card_t *eap_aka_3gpp2_card_create(eap_aka_3gpp2_functions_t *f);
#endif /** EAP_AKA_3GPP2_CARD_H_ @}*/
@@ -0,0 +1,394 @@
/*
* 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.
*/
#include "eap_aka_3gpp2_functions.h"
#include <gmp.h>
#include <limits.h>
#include <daemon.h>
typedef struct private_eap_aka_3gpp2_functions_t private_eap_aka_3gpp2_functions_t;
/**
* Private data of an eap_aka_3gpp2_functions_t object.
*/
struct private_eap_aka_3gpp2_functions_t {
/**
* Public eap_aka_3gpp2_functions_t interface.
*/
eap_aka_3gpp2_functions_t public;
/**
* Used keyed SHA1 function, as PRF
*/
prf_t *prf;
};
#define AKA_PAYLOAD_LEN 64
#define F1 0x42
#define F1STAR 0x43
#define F2 0x44
#define F3 0x45
#define F4 0x46
#define F5 0x47
#define F5STAR 0x48
/** Family key, as proposed in S.S0055 */
static chunk_t fmk = chunk_from_chars(0x41, 0x48, 0x41, 0x47);
/**
* Binary represnation of the polynom T^160 + T^5 + T^3 + T^2 + 1
*/
static u_int8_t g[] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2d
};
/**
* Predefined random bits from the RAND Corporation book
*/
static u_int8_t a[] = {
0x9d, 0xe9, 0xc9, 0xc8, 0xef, 0xd5, 0x78, 0x11,
0x48, 0x23, 0x14, 0x01, 0x90, 0x1f, 0x2d, 0x49,
0x3f, 0x4c, 0x63, 0x65
};
/**
* Predefined random bits from the RAND Corporation book
*/
static u_int8_t b[] = {
0x75, 0xef, 0xd1, 0x5c, 0x4b, 0x8f, 0x8f, 0x51,
0x4e, 0xf3, 0xbc, 0xc3, 0x79, 0x4a, 0x76, 0x5e,
0x7e, 0xec, 0x45, 0xe0
};
/**
* Multiplicate two mpz_t with bits interpreted as polynoms.
*/
static void mpz_mul_poly(mpz_t r, mpz_t a, mpz_t b)
{
mpz_t bm, rm;
int current = 0, shifted = 0, shift;
mpz_init_set(bm, b);
mpz_init_set_ui(rm, 0);
/* scan through a, for each found bit: */
while ((current = mpz_scan1(a, current)) != ULONG_MAX)
{
/* XOR shifted b into r */
shift = current - shifted;
mpz_mul_2exp(bm, bm, shift);
shifted += shift;
mpz_xor(rm, rm, bm);
current++;
}
mpz_swap(r, rm);
mpz_clear(rm);
mpz_clear(bm);
}
/**
* Calculate the sum of a + b interpreted as polynoms.
*/
static void mpz_add_poly(mpz_t res, mpz_t a, mpz_t b)
{
/* addition of polynominals is just the XOR */
mpz_xor(res, a, b);
}
/**
* Calculate the remainder of a/b interpreted as polynoms.
*/
static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b)
{
/* Example:
* a = 10001010
* b = 00000101
*/
int a_bit, b_bit, diff;
mpz_t bm, am;
mpz_init_set(am, a);
mpz_init(bm);
a_bit = mpz_sizeinbase(a, 2);
b_bit = mpz_sizeinbase(b, 2);
/* don't do anything if b > a */
if (a_bit >= b_bit)
{
/* shift b left to align up most signaficant "1" to a:
* a = 10001010
* b = 10100000
*/
mpz_mul_2exp(bm, b, a_bit - b_bit);
do
{
/* XOR b into a, this kills the most significant "1":
* a = 00101010
*/
mpz_xor(am, am, bm);
/* find the next most significant "1" in a, and align up b:
* a = 00101010
* b = 00101000
*/
diff = a_bit - mpz_sizeinbase(am, 2);
mpz_div_2exp(bm, bm, diff);
a_bit -= diff;
}
while (b_bit <= mpz_sizeinbase(bm, 2));
/* While b is not shifted to its original value */
}
/* after another iteration:
* a = 00000010
* which is the polynomial modulo
*/
mpz_swap(r, am);
mpz_clear(am);
mpz_clear(bm);
}
/**
* Step 3 of the various fx() functions:
* XOR the key into the SHA1 IV
*/
static void step3(prf_t *prf, u_char k[AKA_K_LEN],
u_char payload[AKA_PAYLOAD_LEN], u_int8_t h[HASH_SIZE_SHA1])
{
/* use the keyed hasher to build the hash */
prf->set_key(prf, chunk_create(k, AKA_K_LEN));
prf->get_bytes(prf, chunk_create(payload, AKA_PAYLOAD_LEN), h);
}
/**
* Step 4 of the various fx() functions:
* Polynomial whiten calculations
*/
static void step4(u_char x[HASH_SIZE_SHA1])
{
mpz_t xm, am, bm, gm;
mpz_init(xm);
mpz_init(am);
mpz_init(bm);
mpz_init(gm);
mpz_import(xm, HASH_SIZE_SHA1, 1, 1, 1, 0, x);
mpz_import(am, sizeof(a), 1, 1, 1, 0, a);
mpz_import(bm, sizeof(b), 1, 1, 1, 0, b);
mpz_import(gm, sizeof(g), 1, 1, 1, 0, g);
mpz_mul_poly(xm, am, xm);
mpz_add_poly(xm, bm, xm);
mpz_mod_poly(xm, xm, gm);
mpz_export(x, NULL, 1, HASH_SIZE_SHA1, 1, 0, xm);
mpz_clear(xm);
mpz_clear(am);
mpz_clear(bm);
mpz_clear(gm);
}
/**
* Calculation function for f2(), f3(), f4()
*/
static void fx(prf_t *prf, u_char f, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char out[AKA_MAC_LEN])
{
u_char payload[AKA_PAYLOAD_LEN];
u_char h[HASH_SIZE_SHA1];
u_char i;
for (i = 0; i < 2; i++)
{
memset(payload, 0x5c, AKA_PAYLOAD_LEN);
payload[11] ^= f;
memxor(payload + 12, fmk.ptr, fmk.len);
memxor(payload + 24, rand, AKA_RAND_LEN);
payload[3] ^= i;
payload[19] ^= i;
payload[35] ^= i;
payload[51] ^= i;
step3(prf, k, payload, h);
step4(h);
memcpy(out + i * 8, h, 8);
}
}
/**
* Calculation function of f1() and f1star()
*/
static void f1x(prf_t *prf, u_int8_t f, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN],
u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN])
{
/* generate MAC = f1(FMK, SQN, RAND, AMF)
* K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit
* payload which gets hashed
*/
u_char payload[AKA_PAYLOAD_LEN];
u_char h[HASH_SIZE_SHA1];
memset(payload, 0x5c, AKA_PAYLOAD_LEN);
payload[11] ^= f;
memxor(payload + 12, fmk.ptr, fmk.len);
memxor(payload + 16, rand, AKA_RAND_LEN);
memxor(payload + 34, sqn, AKA_SQN_LEN);
memxor(payload + 42, amf, AKA_AMF_LEN);
step3(prf, k, payload, h);
step4(h);
memcpy(mac, h, AKA_MAC_LEN);
}
/**
* Calculation function of f5() and f5star()
*/
static void f5x(prf_t *prf, u_char f, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN])
{
u_char payload[AKA_PAYLOAD_LEN];
u_char h[HASH_SIZE_SHA1];
memset(payload, 0x5c, AKA_PAYLOAD_LEN);
payload[11] ^= f;
memxor(payload + 12, fmk.ptr, fmk.len);
memxor(payload + 16, rand, AKA_RAND_LEN);
step3(prf, k, payload, h);
step4(h);
memcpy(ak, h, AKA_AK_LEN);
}
/**
* Calculate MAC from RAND, SQN, AMF using K
*/
static void f1(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN],
u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN])
{
f1x(this->prf, F1, k, rand, sqn, amf, mac);
DBG3(DBG_IKE, "MAC %b", mac, AKA_MAC_LEN);
}
/**
* Calculate MACS from RAND, SQN, AMF using K
*/
static void f1star(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN],
u_char amf[AKA_AMF_LEN], u_char macs[AKA_MAC_LEN])
{
f1x(this->prf, F1STAR, k, rand, sqn, amf, macs);
DBG3(DBG_IKE, "MACS %b", macs, AKA_MAC_LEN);
}
/**
* Calculate RES from RAND using K
*/
static void f2(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char res[AKA_RES_MAX])
{
fx(this->prf, F2, k, rand, res);
DBG3(DBG_IKE, "RES %b", res, AKA_RES_MAX);
}
/**
* Calculate CK from RAND using K
*/
static void f3(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char ck[AKA_CK_LEN])
{
fx(this->prf, F3, k, rand, ck);
DBG3(DBG_IKE, "CK %b", ck, AKA_CK_LEN);
}
/**
* Calculate IK from RAND using K
*/
static void f4(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char ik[AKA_IK_LEN])
{
fx(this->prf, F4, k, rand, ik);
DBG3(DBG_IKE, "IK %b", ik, AKA_IK_LEN);
}
/**
* Calculate AK from a RAND using K
*/
static void f5(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN])
{
f5x(this->prf, F5, k, rand, ak);
DBG3(DBG_IKE, "AK %b", ak, AKA_AK_LEN);
}
/**
* Calculate AKS from a RAND using K
*/
static void f5star(private_eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char aks[AKA_AK_LEN])
{
f5x(this->prf, F5STAR, k, rand, aks);
DBG3(DBG_IKE, "AKS %b", aks, AKA_AK_LEN);
}
/**
* Implementation of eap_aka_3gpp2_functions_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_functions_t *this)
{
this->prf->destroy(this->prf);
free(this);
}
/**
* See header
*/
eap_aka_3gpp2_functions_t *eap_aka_3gpp2_functions_create()
{
private_eap_aka_3gpp2_functions_t *this;
this = malloc_thing(private_eap_aka_3gpp2_functions_t);
this->public.f1 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN]))f1;
this->public.f1star = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN], u_char amf[AKA_AMF_LEN], u_char macs[AKA_MAC_LEN]))f1star;
this->public.f2 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char res[AKA_RES_MAX]))f2;
this->public.f3 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char ck[AKA_CK_LEN]))f3;
this->public.f4 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char ik[AKA_IK_LEN]))f4;
this->public.f5 = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN]))f5;
this->public.f5star = (void(*)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN], u_char rand[AKA_RAND_LEN], u_char aks[AKA_AK_LEN]))f5star;
this->public.destroy = (void(*)(eap_aka_3gpp2_functions_t*))destroy;
this->prf = lib->crypto->create_prf(lib->crypto, PRF_KEYED_SHA1);
if (!this->prf)
{
DBG1(DBG_CFG, "%N not supported, unable to use 3GPP2 algorithm",
pseudo_random_function_names, PRF_KEYED_SHA1);
free(this);
return NULL;
}
return &this->public;
}
@@ -0,0 +1,125 @@
/*
* 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_3gpp2_functions eap_aka_3gpp2_functions
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_FUNCTIONS_H_
#define EAP_AKA_3GPP2_FUNCTIONS_H_
#include <sa/authenticators/eap/sim_manager.h>
#define AKA_SQN_LEN 6
#define AKA_K_LEN 16
#define AKA_MAC_LEN 8
#define AKA_AK_LEN 6
#define AKA_AMF_LEN 2
#define AKA_FMK_LEN 4
typedef struct eap_aka_3gpp2_functions_t eap_aka_3gpp2_functions_t;
/**
* f1-f5(), f1*() and f5*() functions from the 3GPP2 (S.S0055) standard.
*/
struct eap_aka_3gpp2_functions_t {
/**
* Calculate MAC from RAND, SQN, AMF using K.
*
* @param k secret key K
* @param rand random value rand
* @param sqn sequence number
* @param amf authentication management field
* @param mac buffer receiving mac MAC
*/
void (*f1)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN],
u_char amf[AKA_AMF_LEN], u_char mac[AKA_MAC_LEN]);
/**
* Calculate MACS from RAND, SQN, AMF using K
*
* @param k secret key K
* @param rand random value RAND
* @param sqn sequence number
* @param amf authentication management field
* @param macs buffer receiving resynchronization mac MACS
*/
void (*f1star)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char sqn[AKA_SQN_LEN],
u_char amf[AKA_AMF_LEN], u_char macs[AKA_MAC_LEN]);
/**
* Calculate RES from RAND using K
*
* @param k secret key K
* @param rand random value RAND
* @param res buffer receiving result RES, uses full 128 bit
*/
void (*f2)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char res[AKA_RES_MAX]);
/**
* Calculate CK from RAND using K
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving encryption key CK
*/
void (*f3)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char ck[AKA_CK_LEN]);
/**
* Calculate IK from RAND using K
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving integrity key IK
*/
void (*f4)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char ik[AKA_IK_LEN]);
/**
* Calculate AK from a RAND using K
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving anonymity key AK
*/
void (*f5)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char ak[AKA_AK_LEN]);
/**
* Calculate AKS from a RAND using K
*
* @param k secret key K
* @param rand random value RAND
* @param macs buffer receiving resynchronization anonymity key AKS
*/
void (*f5star)(eap_aka_3gpp2_functions_t *this, u_char k[AKA_K_LEN],
u_char rand[AKA_RAND_LEN], u_char aks[AKA_AK_LEN]);
/**
* Destroy a eap_aka_3gpp2_functions_t.
*/
void (*destroy)(eap_aka_3gpp2_functions_t *this);
};
/**
* Create a eap_aka_3gpp2_functions instance.
*
* @return function set, NULL on error
*/
eap_aka_3gpp2_functions_t *eap_aka_3gpp2_functions_create();
#endif /** EAP_AKA_3GPP2_FUNCTIONS_H_ @}*/
@@ -0,0 +1,87 @@
/*
* 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.
*/
#include "eap_aka_3gpp2_plugin.h"
#include "eap_aka_3gpp2_card.h"
#include "eap_aka_3gpp2_provider.h"
#include "eap_aka_3gpp2_functions.h"
#include <daemon.h>
typedef struct private_eap_aka_3gpp2_t private_eap_aka_3gpp2_t;
/**
* Private data of an eap_aka_3gpp2_t object.
*/
struct private_eap_aka_3gpp2_t {
/**
* Public eap_aka_3gpp2_plugin_t interface.
*/
eap_aka_3gpp2_plugin_t public;
/**
* SIM card
*/
eap_aka_3gpp2_card_t *card;
/**
* SIM provider
*/
eap_aka_3gpp2_provider_t *provider;
/**
* AKA functions
*/
eap_aka_3gpp2_functions_t *functions;
};
/**
* Implementation of eap_aka_3gpp2_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_t *this)
{
charon->sim->remove_card(charon->sim, &this->card->card);
charon->sim->remove_provider(charon->sim, &this->provider->provider);
this->card->destroy(this->card);
this->provider->destroy(this->provider);
this->functions->destroy(this->functions);
free(this);
}
/**
* See header
*/
plugin_t *eap_aka_3gpp2_plugin_create()
{
private_eap_aka_3gpp2_t *this = malloc_thing(private_eap_aka_3gpp2_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
this->functions = eap_aka_3gpp2_functions_create();
if (!this->functions)
{
free(this);
return NULL;
}
this->card = eap_aka_3gpp2_card_create(this->functions);
this->provider = eap_aka_3gpp2_provider_create(this->functions);
charon->sim->add_card(charon->sim, &this->card->card);
charon->sim->add_provider(charon->sim, &this->provider->provider);
return &this->public.plugin;
}
@@ -0,0 +1,57 @@
/*
* 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_3gpp2 eap_aka_3gpp2
* @ingroup cplugins
*
* @defgroup eap_aka_3gpp2_plugin eap_aka_3gpp2_plugin
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_PLUGIN_H_
#define EAP_AKA_3GPP2_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_aka_3gpp2_plugin_t eap_aka_3gpp2_plugin_t;
/**
* Plugin to provide a SIM card/provider using the 3GPP2 (S.S0055) standard.
*
* This plugin implements the standard of the 3GPP2 (S.S0055) and not the one
* of 3GGP, completely in software using the libgmp library..
* The shared key used for authentication is from ipsec.secrets. The
* peers ID is used to query it.
* The AKA mechanism uses sequence numbers to detect replay attacks. The
* peer stores the sequence number normally in a USIM and accepts
* incremental sequence numbers (incremental for lifetime of the USIM). To
* prevent a complex sequence number management, this implementation uses
* a sequence number derived from time. It is initialized to the startup
* time of the daemon.
* To enable time based SEQs, define SEQ_CHECK as 1. Default is to accept
* any SEQ numbers. This allows an attacker to do replay attacks. But since
* the server has proven his identity via IKE, such an attack is only
* possible between server and AAA (if any).
*/
struct eap_aka_3gpp2_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_AKA_3GPP2_PLUGIN_H_ @}*/
@@ -0,0 +1,204 @@
/*
* 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.
*/
#include "eap_aka_3gpp2_provider.h"
#include <daemon.h>
#include <credentials/keys/shared_key.h>
typedef struct private_eap_aka_3gpp2_provider_t private_eap_aka_3gpp2_provider_t;
/**
* Private data of an eap_aka_3gpp2_provider_t object.
*/
struct private_eap_aka_3gpp2_provider_t {
/**
* Public eap_aka_3gpp2_provider_t interface.
*/
eap_aka_3gpp2_provider_t public;
/**
* AKA functions
*/
eap_aka_3gpp2_functions_t *f;
/**
* time based SQN, we use the same for all peers
*/
char sqn[AKA_SQN_LEN];
};
/** Authentication management field */
static char amf[AKA_AMF_LEN] = {0x00, 0x01};
/**
* Get a shared key K from the credential database
*/
bool eap_aka_3gpp2_get_k(identification_t *id, char k[AKA_K_LEN])
{
shared_key_t *shared;
chunk_t key;
shared = charon->credentials->get_shared(charon->credentials,
SHARED_EAP, id, NULL);
if (shared == NULL)
{
return FALSE;
}
key = shared->get_key(shared);
memset(k, '\0', AKA_K_LEN);
memcpy(k, key.ptr, min(key.len, AKA_K_LEN));
shared->destroy(shared);
return TRUE;
}
/**
* get SQN using current time
*/
void eap_aka_3gpp2_get_sqn(char sqn[AKA_SQN_LEN], int offset)
{
timeval_t time;
gettimeofday(&time, NULL);
/* set sqn to an integer containing 4 bytes seconds + 2 bytes usecs */
time.tv_sec = htonl(time.tv_sec + offset);
/* usec's are never larger than 0x000f423f, so we shift the 12 first bits */
time.tv_usec = htonl(time.tv_usec << 12);
memcpy(sqn, (char*)&time.tv_sec + sizeof(time_t) - 4, 4);
memcpy(sqn + 4, &time.tv_usec, 2);
}
/**
* Implementation of usim_provider_t.get_quintuplet
*/
static bool get_quintuplet(private_eap_aka_3gpp2_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])
{
rng_t *rng;
char mac[AKA_MAC_LEN], ak[AKA_AK_LEN], k[AKA_K_LEN];
/* generate RAND: we use a registered RNG, not f0() proposed in S.S0055 */
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!rng)
{
DBG1(DBG_IKE, "generating RAND for AKA failed");
return FALSE;
}
rng->get_bytes(rng, AKA_RAND_LEN, rand);
rng->destroy(rng);
if (!eap_aka_3gpp2_get_k(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
return FALSE;
}
DBG3(DBG_IKE, "generated rand %b", rand, AKA_RAND_LEN);
DBG3(DBG_IKE, "using K %b", k, AKA_K_LEN);
/* MAC */
this->f->f1(this->f, k, rand, this->sqn, amf, mac);
/* AK */
this->f->f5(this->f, k, rand, ak);
/* XRES as expected from client */
this->f->f2(this->f, k, rand, xres);
*xres_len = AKA_RES_MAX;
/* AUTN = (SQN xor AK) || AMF || MAC */
memcpy(autn, this->sqn, AKA_SQN_LEN);
memxor(autn, ak, AKA_AK_LEN);
memcpy(autn + AKA_SQN_LEN, amf, AKA_AMF_LEN);
memcpy(autn + AKA_SQN_LEN + AKA_AMF_LEN, mac, AKA_MAC_LEN);
DBG3(DBG_IKE, "AUTN %b", autn, AKA_AUTN_LEN);
/* CK/IK */
this->f->f3(this->f, k, rand, ck);
this->f->f4(this->f, k, rand, ik);
return TRUE;
}
/**
* Implementation of usim_provider_t.resync
*/
static bool resync(private_eap_aka_3gpp2_provider_t *this,
identification_t *id, char rand[AKA_RAND_LEN],
char auts[AKA_AUTS_LEN])
{
char *sqn, *macs;
char aks[AKA_AK_LEN], k[AKA_K_LEN], amf[AKA_AMF_LEN], xmacs[AKA_MAC_LEN];
if (!eap_aka_3gpp2_get_k(id, k))
{
DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
return FALSE;
}
/* AUTHS = (AK xor SQN) | MAC */
sqn = auts;
macs = auts + AKA_SQN_LEN;
this->f->f5star(this->f, k, rand, aks);
memxor(sqn, aks, AKA_AK_LEN);
/* verify XMACS, AMF of zero is used in resynchronization */
memset(amf, 0, AKA_AMF_LEN);
this->f->f1star(this->f, k, rand, sqn, amf, xmacs);
if (!memeq(macs, xmacs, AKA_MAC_LEN))
{
DBG1(DBG_IKE, "received MACS does not match XMACS");
DBG3(DBG_IKE, "MACS %b XMACS %b",
macs, AKA_MAC_LEN, xmacs, AKA_MAC_LEN);
return FALSE;
}
/* update stored SQN to received SQN + 1 */
memcpy(this->sqn, sqn, AKA_SQN_LEN);
chunk_increment(chunk_create(this->sqn, AKA_SQN_LEN));
return TRUE;
}
/**
* Implementation of eap_aka_3gpp2_provider_t.destroy.
*/
static void destroy(private_eap_aka_3gpp2_provider_t *this)
{
free(this);
}
/**
* See header
*/
eap_aka_3gpp2_provider_t *eap_aka_3gpp2_provider_create(
eap_aka_3gpp2_functions_t *f)
{
private_eap_aka_3gpp2_provider_t *this = malloc_thing(private_eap_aka_3gpp2_provider_t);
this->public.provider.get_triplet = (bool(*)(sim_provider_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
this->public.provider.get_quintuplet = (bool(*)(sim_provider_t*, 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]))get_quintuplet;
this->public.provider.resync = (bool(*)(sim_provider_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))resync;
this->public.provider.is_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.gen_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.is_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char [HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.provider.gen_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char mk[HASH_SIZE_SHA1]))return_null;
this->public.destroy = (void(*)(eap_aka_3gpp2_provider_t*))destroy;
this->f = f;
/* use an offset to accept clock skew between client/server without resync */
eap_aka_3gpp2_get_sqn(this->sqn, 180);
return &this->public;
}
@@ -0,0 +1,52 @@
/*
* 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_3gpp2_provider eap_aka_3gpp2_provider
* @{ @ingroup eap_aka_3gpp2
*/
#ifndef EAP_AKA_3GPP2_PROVIDER_H_
#define EAP_AKA_3GPP2_PROVIDER_H_
#include "eap_aka_3gpp2_functions.h"
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_aka_3gpp2_provider_t eap_aka_3gpp2_provider_t;
/**
* SIM provider implementation using a set of AKA functions.
*/
struct eap_aka_3gpp2_provider_t {
/**
* Implements sim_provider_t interface.
*/
sim_provider_t provider;
/**
* Destroy a eap_aka_3gpp2_provider_t.
*/
void (*destroy)(eap_aka_3gpp2_provider_t *this);
};
/**
* Create a eap_aka_3gpp2_provider instance.
*/
eap_aka_3gpp2_provider_t *eap_aka_3gpp2_provider_create(
eap_aka_3gpp2_functions_t *f);
#endif /** EAP_AKA_3GPP2_PROVIDER_H_ @}*/
+15
View File
@@ -0,0 +1,15 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-gtc.la
else
plugin_LTLIBRARIES = libstrongswan-eap-gtc.la
endif
libstrongswan_eap_gtc_la_SOURCES = \
eap_gtc_plugin.h eap_gtc_plugin.c eap_gtc.h eap_gtc.c
libstrongswan_eap_gtc_la_LDFLAGS = -module -avoid-version -lpam
+327
View File
@@ -0,0 +1,327 @@
/*
* Copyright (C) 2007 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "eap_gtc.h"
#include <daemon.h>
#include <library.h>
#include <crypto/hashers/hasher.h>
#include <security/pam_appl.h>
#define GTC_REQUEST_MSG "password"
#define GTC_PAM_SERVICE "login"
typedef struct private_eap_gtc_t private_eap_gtc_t;
/**
* Private data of an eap_gtc_t object.
*/
struct private_eap_gtc_t {
/**
* Public authenticator_t interface.
*/
eap_gtc_t public;
/**
* ID of the server
*/
identification_t *server;
/**
* ID of the peer
*/
identification_t *peer;
/**
* EAP message identififier
*/
u_int8_t identifier;
};
typedef struct eap_gtc_header_t eap_gtc_header_t;
/**
* packed eap GTC header struct
*/
struct eap_gtc_header_t {
/** EAP code (REQUEST/RESPONSE) */
u_int8_t code;
/** unique message identifier */
u_int8_t identifier;
/** length of whole message */
u_int16_t length;
/** EAP type */
u_int8_t type;
/** type data */
u_int8_t data[];
} __attribute__((__packed__));
/**
* Implementation of eap_method_t.initiate for the peer
*/
static status_t initiate_peer(private_eap_gtc_t *this, eap_payload_t **out)
{
/* peer never initiates */
return FAILED;
}
/**
* PAM conv callback function
*/
static int auth_conv(int num_msg, const struct pam_message **msg,
struct pam_response **resp, char *password)
{
struct pam_response *response;
if (num_msg != 1)
{
return PAM_CONV_ERR;
}
response = malloc(sizeof(struct pam_response));
response->resp = strdup(password);
response->resp_retcode = 0;
*resp = response;
return PAM_SUCCESS;
}
/**
* Authenticate a username/password using PAM
*/
static bool authenticate(char *service, char *user, char *password)
{
pam_handle_t *pamh = NULL;
static struct pam_conv conv;
int ret;
conv.conv = (void*)auth_conv;
conv.appdata_ptr = password;
ret = pam_start(service, user, &conv, &pamh);
if (ret != PAM_SUCCESS)
{
DBG1(DBG_IKE, "EAP-GTC pam_start failed: %s",
pam_strerror(pamh, ret));
return FALSE;
}
ret = pam_authenticate(pamh, 0);
if (ret == PAM_SUCCESS)
{
ret = pam_acct_mgmt(pamh, 0);
if (ret != PAM_SUCCESS)
{
DBG1(DBG_IKE, "EAP-GTC pam_acct_mgmt failed: %s",
pam_strerror(pamh, ret));
}
}
else
{
DBG1(DBG_IKE, "EAP-GTC pam_authenticate failed: %s",
pam_strerror(pamh, ret));
}
pam_end(pamh, ret);
return ret == PAM_SUCCESS;
}
/**
* Implementation of eap_method_t.initiate for the server
*/
static status_t initiate_server(private_eap_gtc_t *this, eap_payload_t **out)
{
eap_gtc_header_t *req;
size_t len;
len = strlen(GTC_REQUEST_MSG);
req = alloca(sizeof(eap_gtc_header_t) + len);
req->length = htons(sizeof(eap_gtc_header_t) + len);
req->code = EAP_REQUEST;
req->identifier = this->identifier;
req->type = EAP_GTC;
memcpy(req->data, GTC_REQUEST_MSG, len);
*out = eap_payload_create_data(chunk_create((void*)req,
sizeof(eap_gtc_header_t) + len));
return NEED_MORE;
}
/**
* Implementation of eap_method_t.process for the peer
*/
static status_t process_peer(private_eap_gtc_t *this,
eap_payload_t *in, eap_payload_t **out)
{
eap_gtc_header_t *res;
shared_key_t *shared;
chunk_t key;
size_t len;
shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
this->peer, this->server);
if (shared == NULL)
{
DBG1(DBG_IKE, "no EAP key found for '%Y' - '%Y'",
this->peer, this->server);
return FAILED;
}
key = shared->get_key(shared);
len = key.len;
/* TODO: According to the draft we should "SASLprep" password, RFC4013. */
res = alloca(sizeof(eap_gtc_header_t) + len);
res->length = htons(sizeof(eap_gtc_header_t) + len);
res->code = EAP_RESPONSE;
res->identifier = in->get_identifier(in);
res->type = EAP_GTC;
memcpy(res->data, key.ptr, len);
shared->destroy(shared);
*out = eap_payload_create_data(chunk_create((void*)res,
sizeof(eap_gtc_header_t) + len));
return NEED_MORE;
}
/**
* Implementation of eap_method_t.process for the server
*/
static status_t process_server(private_eap_gtc_t *this,
eap_payload_t *in, eap_payload_t **out)
{
chunk_t data, encoding;
char *user, *password, *service, *pos;
data = chunk_skip(in->get_data(in), 5);
if (this->identifier != in->get_identifier(in) || !data.len)
{
DBG1(DBG_IKE, "received invalid EAP-GTC message");
return FAILED;
}
encoding = this->peer->get_encoding(this->peer);
/* if a RFC822_ADDR id is provided, we use the username part only */
pos = memchr(encoding.ptr, '@', encoding.len);
if (pos)
{
encoding.len = (u_char*)pos - encoding.ptr;
}
user = alloca(encoding.len + 1);
memcpy(user, encoding.ptr, encoding.len);
user[encoding.len] = '\0';
password = alloca(data.len + 1);
memcpy(password, data.ptr, data.len);
password[data.len] = '\0';
service = lib->settings->get_str(lib->settings,
"charon.plugins.eap-gtc.pam_service", GTC_PAM_SERVICE);
if (!authenticate(service, user, password))
{
return FAILED;
}
return SUCCESS;
}
/**
* Implementation of eap_method_t.get_type.
*/
static eap_type_t get_type(private_eap_gtc_t *this, u_int32_t *vendor)
{
*vendor = 0;
return EAP_GTC;
}
/**
* Implementation of eap_method_t.get_msk.
*/
static status_t get_msk(private_eap_gtc_t *this, chunk_t *msk)
{
return FAILED;
}
/**
* Implementation of eap_method_t.is_mutual.
*/
static bool is_mutual(private_eap_gtc_t *this)
{
return FALSE;
}
/**
* Implementation of eap_method_t.destroy.
*/
static void destroy(private_eap_gtc_t *this)
{
this->peer->destroy(this->peer);
this->server->destroy(this->server);
free(this);
}
/**
* Generic constructor
*/
static private_eap_gtc_t *eap_gtc_create_generic(identification_t *server,
identification_t *peer)
{
private_eap_gtc_t *this = malloc_thing(private_eap_gtc_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;
/* private data */
this->peer = peer->clone(peer);
this->server = server->clone(server);
this->identifier = 0;
return this;
}
/*
* see header
*/
eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer)
{
private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
/* generate a non-zero identifier */
do {
this->identifier = random();
} while (!this->identifier);
return &this->public;
}
/*
* see header
*/
eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer)
{
private_eap_gtc_t *this = eap_gtc_create_generic(server, peer);
this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
return &this->public;
}
+60
View File
@@ -0,0 +1,60 @@
/*
* 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.
*/
/**
* @defgroup eap_gtc_i eap_gtc
* @{ @ingroup eap_gtc
*/
#ifndef EAP_GTC_H_
#define EAP_GTC_H_
typedef struct eap_gtc_t eap_gtc_t;
#include <sa/authenticators/eap/eap_method.h>
/**
* Implementation of the eap_method_t interface using EAP-GTC.
*
* This implementation of draft-sheffer-ikev2-gtc-00.txt uses PAM to
* verify user credentials.
*/
struct eap_gtc_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t eap_method_interface;
};
/**
* Creates the EAP method EAP-GTC acting as server.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_gtc_t object
*/
eap_gtc_t *eap_gtc_create_server(identification_t *server, identification_t *peer);
/**
* Creates the EAP method EAP-GTC acting as peer.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_gtc_t object
*/
eap_gtc_t *eap_gtc_create_peer(identification_t *server, identification_t *peer);
#endif /** EAP_GTC_H_ @}*/
@@ -0,0 +1,56 @@
/*
* 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 "eap_gtc_plugin.h"
#include "eap_gtc.h"
#include <daemon.h>
/* missing in cababilities.h */
#define CAP_AUDIT_WRITE 29
/**
* Implementation of plugin_t.destroy
*/
static void destroy(eap_gtc_plugin_t *this)
{
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_gtc_create_server);
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_gtc_create_peer);
free(this);
}
/*
* see header file
*/
plugin_t *eap_gtc_plugin_create()
{
eap_gtc_plugin_t *this = malloc_thing(eap_gtc_plugin_t);
this->plugin.destroy = (void(*)(plugin_t*))destroy;
/* required for PAM authentication */
charon->keep_cap(charon, CAP_AUDIT_WRITE);
charon->eap->add_method(charon->eap, EAP_GTC, 0, EAP_SERVER,
(eap_constructor_t)eap_gtc_create_server);
charon->eap->add_method(charon->eap, EAP_GTC, 0, EAP_PEER,
(eap_constructor_t)eap_gtc_create_peer);
return &this->plugin;
}
@@ -0,0 +1,42 @@
/*
* 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.
*/
/**
* @defgroup eap_gtc eap_gtc
* @ingroup cplugins
*
* @defgroup eap_gtc_plugin eap_gtc_plugin
* @{ @ingroup eap_gtc
*/
#ifndef EAP_GTC_PLUGIN_H_
#define EAP_GTC_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_gtc_plugin_t eap_gtc_plugin_t;
/**
* EAP-GTC plugin
*/
struct eap_gtc_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_GTC_PLUGIN_H_ @}*/
@@ -0,0 +1,15 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-identity.la
else
plugin_LTLIBRARIES = libstrongswan-eap-identity.la
endif
libstrongswan_eap_identity_la_SOURCES = \
eap_identity_plugin.h eap_identity_plugin.c eap_identity.h eap_identity.c
libstrongswan_eap_identity_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,218 @@
/*
* Copyright (C) 2007-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 "eap_identity.h"
#include <daemon.h>
#include <library.h>
typedef struct private_eap_identity_t private_eap_identity_t;
/**
* Private data of an eap_identity_t object.
*/
struct private_eap_identity_t {
/**
* Public authenticator_t interface.
*/
eap_identity_t public;
/**
* ID of the peer
*/
identification_t *peer;
/**
* received identity chunk
*/
chunk_t identity;
};
typedef struct eap_identity_header_t eap_identity_header_t;
/**
* packed EAP Identity header struct
*/
struct eap_identity_header_t {
/** EAP code (REQUEST/RESPONSE) */
u_int8_t code;
/** unique message identifier */
u_int8_t identifier;
/** length of whole message */
u_int16_t length;
/** EAP type */
u_int8_t type;
/** identity data */
u_int8_t data[];
} __attribute__((__packed__));
/**
* Implementation of eap_method_t.process for the peer
*/
static status_t process_peer(private_eap_identity_t *this,
eap_payload_t *in, eap_payload_t **out)
{
chunk_t id;
eap_identity_header_t *hdr;
size_t len;
id = this->peer->get_encoding(this->peer);
len = sizeof(eap_identity_header_t) + id.len;
hdr = alloca(len);
hdr->code = EAP_RESPONSE;
hdr->identifier = in->get_identifier(in);
hdr->length = htons(len);
hdr->type = EAP_IDENTITY;
memcpy(hdr->data, id.ptr, id.len);
*out = eap_payload_create_data(chunk_create((u_char*)hdr, len));
return SUCCESS;
}
/**
* Implementation of eap_method_t.initiate for the peer
*/
static status_t initiate_peer(private_eap_identity_t *this, eap_payload_t **out)
{
/* peer never initiates */
return FAILED;
}
/**
* Implementation of eap_method_t.process for the server
*/
static status_t process_server(private_eap_identity_t *this,
eap_payload_t *in, eap_payload_t **out)
{
chunk_t data;
data = chunk_skip(in->get_data(in), 5);
if (data.len)
{
this->identity = chunk_clone(data);
}
return SUCCESS;
}
/**
* Implementation of eap_method_t.initiate for the server
*/
static status_t initiate_server(private_eap_identity_t *this, eap_payload_t **out)
{
eap_identity_header_t hdr;
hdr.code = EAP_REQUEST;
hdr.identifier = 0;
hdr.length = htons(sizeof(eap_identity_header_t));
hdr.type = EAP_IDENTITY;
*out = eap_payload_create_data(chunk_create((u_char*)&hdr,
sizeof(eap_identity_header_t)));
return NEED_MORE;
}
/**
* Implementation of eap_method_t.get_type.
*/
static eap_type_t get_type(private_eap_identity_t *this, u_int32_t *vendor)
{
*vendor = 0;
return EAP_IDENTITY;
}
/**
* Implementation of eap_method_t.get_msk.
*/
static status_t get_msk(private_eap_identity_t *this, chunk_t *msk)
{
if (this->identity.ptr)
{
*msk = this->identity;
return SUCCESS;
}
return FAILED;
}
/**
* Implementation of eap_method_t.is_mutual.
*/
static bool is_mutual(private_eap_identity_t *this)
{
return FALSE;
}
/**
* Implementation of eap_method_t.destroy.
*/
static void destroy(private_eap_identity_t *this)
{
this->peer->destroy(this->peer);
free(this->identity.ptr);
free(this);
}
/**
* Generic constructor
*/
static private_eap_identity_t *eap_identity_create(identification_t *server,
identification_t *peer)
{
private_eap_identity_t *this = malloc_thing(private_eap_identity_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->identity = chunk_empty;
return this;
}
/*
* Described in header.
*/
eap_identity_t *eap_identity_create_peer(identification_t *server,
identification_t *peer)
{
private_eap_identity_t *this = eap_identity_create(server, peer);
/* public functions */
this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
return &this->public;
}
/*
* Described in header.
*/
eap_identity_t *eap_identity_create_server(identification_t *server,
identification_t *peer)
{
private_eap_identity_t *this = eap_identity_create(server, peer);
/* public functions */
this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
return &this->public;
}
@@ -0,0 +1,59 @@
/*
* 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.
*/
/**
* @defgroup eap_identity_i eap_identity
* @{ @ingroup eap_identity
*/
#ifndef EAP_IDENTITY_H_
#define EAP_IDENTITY_H_
typedef struct eap_identity_t eap_identity_t;
#include <sa/authenticators/eap/eap_method.h>
/**
* Implementation of the eap_method_t interface using EAP Identity.
*/
struct eap_identity_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t eap_method_interface;
};
/**
* Creates the EAP method EAP Identity, acting as server.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_identity_t object
*/
eap_identity_t *eap_identity_create_server(identification_t *server,
identification_t *peer);
/**
* Creates the EAP method EAP Identity, acting as peer.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_identity_t object
*/
eap_identity_t *eap_identity_create_peer(identification_t *server,
identification_t *peer);
#endif /** EAP_IDENTITY_H_ @}*/
@@ -0,0 +1,50 @@
/*
* 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 "eap_identity_plugin.h"
#include "eap_identity.h"
#include <daemon.h>
/**
* Implementation of plugin_t.destroy
*/
static void destroy(eap_identity_plugin_t *this)
{
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_identity_create_server);
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_identity_create_peer);
free(this);
}
/*
* see header file
*/
plugin_t *eap_identity_plugin_create()
{
eap_identity_plugin_t *this = malloc_thing(eap_identity_plugin_t);
this->plugin.destroy = (void(*)(plugin_t*))destroy;
charon->eap->add_method(charon->eap, EAP_IDENTITY, 0, EAP_SERVER,
(eap_constructor_t)eap_identity_create_server);
charon->eap->add_method(charon->eap, EAP_IDENTITY, 0, EAP_PEER,
(eap_constructor_t)eap_identity_create_peer);
return &this->plugin;
}
@@ -0,0 +1,42 @@
/*
* 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.
*/
/**
* @defgroup eap_identity eap_identity
* @ingroup cplugins
*
* @defgroup eap_identity_plugin eap_identity_plugin
* @{ @ingroup eap_identity
*/
#ifndef EAP_IDENTITY_PLUGIN_H_
#define EAP_IDENTITY_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_identity_plugin_t eap_identity_plugin_t;
/**
* EAP-IDENTITY plugin.
*/
struct eap_identity_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_IDENTITY_PLUGIN_H_ @}*/
+15
View File
@@ -0,0 +1,15 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-md5.la
else
plugin_LTLIBRARIES = libstrongswan-eap-md5.la
endif
libstrongswan_eap_md5_la_SOURCES = \
eap_md5_plugin.h eap_md5_plugin.c eap_md5.h eap_md5.c
libstrongswan_eap_md5_la_LDFLAGS = -module -avoid-version
+303
View File
@@ -0,0 +1,303 @@
/*
* Copyright (C) 2007 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "eap_md5.h"
#include <daemon.h>
#include <library.h>
#include <crypto/hashers/hasher.h>
typedef struct private_eap_md5_t private_eap_md5_t;
/**
* Private data of an eap_md5_t object.
*/
struct private_eap_md5_t {
/**
* Public authenticator_t interface.
*/
eap_md5_t public;
/**
* ID of the server
*/
identification_t *server;
/**
* ID of the peer
*/
identification_t *peer;
/**
* challenge sent by the server
*/
chunk_t challenge;
/**
* EAP message identififier
*/
u_int8_t identifier;
};
typedef struct eap_md5_header_t eap_md5_header_t;
/**
* packed eap MD5 header struct
*/
struct eap_md5_header_t {
/** EAP code (REQUEST/RESPONSE) */
u_int8_t code;
/** unique message identifier */
u_int8_t identifier;
/** length of whole message */
u_int16_t length;
/** EAP type */
u_int8_t type;
/** length of value (challenge) */
u_int8_t value_size;
/** actual value */
u_int8_t value[];
} __attribute__((__packed__));
#define CHALLENGE_LEN 16
#define PAYLOAD_LEN (CHALLENGE_LEN + sizeof(eap_md5_header_t))
/**
* Hash the challenge string, create response
*/
static status_t hash_challenge(private_eap_md5_t *this, chunk_t *response,
identification_t *me, identification_t *other)
{
shared_key_t *shared;
chunk_t concat;
hasher_t *hasher;
shared = charon->credentials->get_shared(charon->credentials, SHARED_EAP,
me, other);
if (shared == NULL)
{
DBG1(DBG_IKE, "no EAP key found for hosts '%Y' - '%Y'", me, other);
return NOT_FOUND;
}
concat = chunk_cata("ccc", chunk_from_thing(this->identifier),
shared->get_key(shared), this->challenge);
shared->destroy(shared);
hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
if (hasher == NULL)
{
DBG1(DBG_IKE, "EAP-MD5 failed, MD5 not supported");
return FAILED;
}
hasher->allocate_hash(hasher, concat, response);
hasher->destroy(hasher);
return SUCCESS;
}
/**
* Implementation of eap_method_t.initiate for the peer
*/
static status_t initiate_peer(private_eap_md5_t *this, eap_payload_t **out)
{
/* peer never initiates */
return FAILED;
}
/**
* Implementation of eap_method_t.initiate for the server
*/
static status_t initiate_server(private_eap_md5_t *this, eap_payload_t **out)
{
rng_t *rng;
eap_md5_header_t *req;
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!rng)
{
return FAILED;
}
rng->allocate_bytes(rng, CHALLENGE_LEN, &this->challenge);
rng->destroy(rng);
req = alloca(PAYLOAD_LEN);
req->length = htons(PAYLOAD_LEN);
req->code = EAP_REQUEST;
req->identifier = this->identifier;
req->type = EAP_MD5;
req->value_size = this->challenge.len;
memcpy(req->value, this->challenge.ptr, this->challenge.len);
*out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
return NEED_MORE;
}
/**
* Implementation of eap_method_t.process for the peer
*/
static status_t process_peer(private_eap_md5_t *this,
eap_payload_t *in, eap_payload_t **out)
{
chunk_t response;
chunk_t data;
eap_md5_header_t *req;
this->identifier = in->get_identifier(in);
data = in->get_data(in);
this->challenge = chunk_clone(chunk_skip(data, 6));
if (data.len < 6 || this->challenge.len < *(data.ptr + 5))
{
DBG1(DBG_IKE, "received invalid EAP-MD5 message");
return FAILED;
}
if (hash_challenge(this, &response, this->peer, this->server) != SUCCESS)
{
return FAILED;
}
req = alloca(PAYLOAD_LEN);
req->length = htons(PAYLOAD_LEN);
req->code = EAP_RESPONSE;
req->identifier = this->identifier;
req->type = EAP_MD5;
req->value_size = response.len;
memcpy(req->value, response.ptr, response.len);
chunk_free(&response);
*out = eap_payload_create_data(chunk_create((void*)req, PAYLOAD_LEN));
return NEED_MORE;
}
/**
* Implementation of eap_method_t.process for the server
*/
static status_t process_server(private_eap_md5_t *this,
eap_payload_t *in, eap_payload_t **out)
{
chunk_t response, expected;
chunk_t data;
if (this->identifier != in->get_identifier(in))
{
DBG1(DBG_IKE, "received invalid EAP-MD5 message");
return FAILED;
}
if (hash_challenge(this, &expected, this->server, this->peer) != SUCCESS)
{
return FAILED;
}
data = in->get_data(in);
response = chunk_skip(data, 6);
if (response.len < expected.len ||
!memeq(response.ptr, expected.ptr, expected.len))
{
chunk_free(&expected);
DBG1(DBG_IKE, "EAP-MD5 verification failed");
return FAILED;
}
chunk_free(&expected);
return SUCCESS;
}
/**
* Implementation of eap_method_t.get_type.
*/
static eap_type_t get_type(private_eap_md5_t *this, u_int32_t *vendor)
{
*vendor = 0;
return EAP_MD5;
}
/**
* Implementation of eap_method_t.get_msk.
*/
static status_t get_msk(private_eap_md5_t *this, chunk_t *msk)
{
return FAILED;
}
/**
* Implementation of eap_method_t.is_mutual.
*/
static bool is_mutual(private_eap_md5_t *this)
{
return FALSE;
}
/**
* Implementation of eap_method_t.destroy.
*/
static void destroy(private_eap_md5_t *this)
{
this->peer->destroy(this->peer);
this->server->destroy(this->server);
chunk_free(&this->challenge);
free(this);
}
/**
* Generic constructor
*/
static private_eap_md5_t *eap_md5_create_generic(identification_t *server,
identification_t *peer)
{
private_eap_md5_t *this = malloc_thing(private_eap_md5_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;
/* private data */
this->peer = peer->clone(peer);
this->server = server->clone(server);
this->challenge = chunk_empty;
this->identifier = 0;
return this;
}
/*
* see header
*/
eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer)
{
private_eap_md5_t *this = eap_md5_create_generic(server, peer);
this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_server;
this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_server;
/* generate a non-zero identifier */
do {
this->identifier = random();
} while (!this->identifier);
return &this->public;
}
/*
* see header
*/
eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer)
{
private_eap_md5_t *this = eap_md5_create_generic(server, peer);
this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate_peer;
this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process_peer;
return &this->public;
}
+57
View File
@@ -0,0 +1,57 @@
/*
* 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.
*/
/**
* @defgroup eap_md5_i eap_md5
* @{ @ingroup eap_md5
*/
#ifndef EAP_MD5_H_
#define EAP_MD5_H_
typedef struct eap_md5_t eap_md5_t;
#include <sa/authenticators/eap/eap_method.h>
/**
* Implementation of the eap_method_t interface using EAP-MD5 (CHAP).
*/
struct eap_md5_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t eap_method_interface;
};
/**
* Creates the EAP method EAP-MD5 acting as server.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_md5_t object
*/
eap_md5_t *eap_md5_create_server(identification_t *server, identification_t *peer);
/**
* Creates the EAP method EAP-MD5 acting as peer.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_md5_t object
*/
eap_md5_t *eap_md5_create_peer(identification_t *server, identification_t *peer);
#endif /** EAP_MD5_H_ @}*/
@@ -0,0 +1,50 @@
/*
* 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 "eap_md5_plugin.h"
#include "eap_md5.h"
#include <daemon.h>
/**
* Implementation of plugin_t.destroy
*/
static void destroy(eap_md5_plugin_t *this)
{
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_md5_create_server);
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_md5_create_peer);
free(this);
}
/*
* see header file
*/
plugin_t *eap_md5_plugin_create()
{
eap_md5_plugin_t *this = malloc_thing(eap_md5_plugin_t);
this->plugin.destroy = (void(*)(plugin_t*))destroy;
charon->eap->add_method(charon->eap, EAP_MD5, 0, EAP_SERVER,
(eap_constructor_t)eap_md5_create_server);
charon->eap->add_method(charon->eap, EAP_MD5, 0, EAP_PEER,
(eap_constructor_t)eap_md5_create_peer);
return &this->plugin;
}
@@ -0,0 +1,42 @@
/*
* 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.
*/
/**
* @defgroup eap_md5 eap_md5
* @ingroup cplugins
*
* @defgroup eap_md5_plugin eap_md5_plugin
* @{ @ingroup eap_md5
*/
#ifndef EAP_MD5_PLUGIN_H_
#define EAP_MD5_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_md5_plugin_t eap_md5_plugin_t;
/**
* EAP-MD5 plugin
*/
struct eap_md5_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_MD5_PLUGIN_H_ @}*/
@@ -0,0 +1,16 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-mschapv2.la
else
plugin_LTLIBRARIES = libstrongswan-eap-mschapv2.la
endif
libstrongswan_eap_mschapv2_la_SOURCES = \
eap_mschapv2_plugin.h eap_mschapv2_plugin.c \
eap_mschapv2.h eap_mschapv2.c
libstrongswan_eap_mschapv2_la_LDFLAGS = -module -avoid-version
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2009 Tobias Brunner
* 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_mschapv2_i eap_mschapv2
* @{ @ingroup eap_mschapv2
*/
#ifndef EAP_MSCHAPV2_H_
#define EAP_MSCHAPV2_H_
typedef struct eap_mschapv2_t eap_mschapv2_t;
#include <sa/authenticators/eap/eap_method.h>
/**
* Implementation of the eap_method_t interface using EAP-MS-CHAPv2.
*/
struct eap_mschapv2_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t eap_method_interface;
};
/**
* Creates the EAP method EAP-MS-CHAPv2 acting as server.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_mschapv2_t object
*/
eap_mschapv2_t *eap_mschapv2_create_server(identification_t *server, identification_t *peer);
/**
* Creates the EAP method EAP-MS-CHAPv2 acting as peer.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_mschapv2_t object
*/
eap_mschapv2_t *eap_mschapv2_create_peer(identification_t *server, identification_t *peer);
#endif /** EAP_MSCHAPV2_H_ @}*/
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2009 Tobias Brunner
* 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_mschapv2_plugin.h"
#include "eap_mschapv2.h"
#include <daemon.h>
/**
* Implementation of plugin_t.destroy
*/
static void destroy(eap_mschapv2_plugin_t *this)
{
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_mschapv2_create_server);
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_mschapv2_create_peer);
free(this);
}
/*
* see header file
*/
plugin_t *eap_mschapv2_plugin_create()
{
eap_mschapv2_plugin_t *this = malloc_thing(eap_mschapv2_plugin_t);
this->plugin.destroy = (void(*)(plugin_t*))destroy;
charon->eap->add_method(charon->eap, EAP_MSCHAPV2, 0, EAP_SERVER,
(eap_constructor_t)eap_mschapv2_create_server);
charon->eap->add_method(charon->eap, EAP_MSCHAPV2, 0, EAP_PEER,
(eap_constructor_t)eap_mschapv2_create_peer);
return &this->plugin;
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2009 Tobias Brunner
* 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_mschapv2 eap_mschapv2
* @ingroup cplugins
*
* @defgroup eap_mschapv2_plugin eap_mschapv2_plugin
* @{ @ingroup eap_mschapv2
*/
#ifndef EAP_MSCHAPV2_PLUGIN_H_
#define EAP_MSCHAPV2_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_mschapv2_plugin_t eap_mschapv2_plugin_t;
/**
* EAP-MS-CHAPv2 plugin
*/
struct eap_mschapv2_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_MSCHAPV2_PLUGIN_H_ @}*/
@@ -0,0 +1,18 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-radius.la
else
plugin_LTLIBRARIES = libstrongswan-eap-radius.la
endif
libstrongswan_eap_radius_la_SOURCES = \
eap_radius_plugin.h eap_radius_plugin.c \
eap_radius.h eap_radius.c \
radius_client.h radius_client.c \
radius_message.h radius_message.c
libstrongswan_eap_radius_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,312 @@
/*
* Copyright (C) 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_radius.h"
#include "radius_message.h"
#include "radius_client.h"
#include <daemon.h>
typedef struct private_eap_radius_t private_eap_radius_t;
/**
* Private data of an eap_radius_t object.
*/
struct private_eap_radius_t {
/**
* Public authenticator_t interface.
*/
eap_radius_t public;
/**
* ID of the server
*/
identification_t *server;
/**
* ID of the peer
*/
identification_t *peer;
/**
* EAP method type we are proxying
*/
eap_type_t type;
/**
* EAP vendor, if any
*/
u_int32_t vendor;
/**
* EAP MSK, if method established one
*/
chunk_t msk;
/**
* RADIUS client instance
*/
radius_client_t *client;
/**
* TRUE to use EAP-Start, FALSE to send EAP-Identity Response directly
*/
bool eap_start;
/**
* Prefix to prepend to EAP identity
*/
char *id_prefix;
};
/**
* Add EAP-Identity to RADIUS message
*/
static void add_eap_identity(private_eap_radius_t *this,
radius_message_t *request)
{
struct {
/** EAP code (REQUEST/RESPONSE) */
u_int8_t code;
/** unique message identifier */
u_int8_t identifier;
/** length of whole message */
u_int16_t length;
/** EAP type */
u_int8_t type;
/** identity data */
u_int8_t data[];
} __attribute__((__packed__)) *hdr;
chunk_t id, prefix;
size_t len;
id = this->peer->get_encoding(this->peer);
prefix = chunk_create(this->id_prefix, strlen(this->id_prefix));
len = sizeof(*hdr) + prefix.len + id.len;
hdr = alloca(len);
hdr->code = EAP_RESPONSE;
hdr->identifier = 0;
hdr->length = htons(len);
hdr->type = EAP_IDENTITY;
memcpy(hdr->data, prefix.ptr, prefix.len);
memcpy(hdr->data + prefix.len, id.ptr, id.len);
request->add(request, RAT_EAP_MESSAGE, chunk_create((u_char*)hdr, len));
}
/**
* Copy EAP-Message attribute from RADIUS message to an new EAP payload
*/
static bool radius2ike(private_eap_radius_t *this,
radius_message_t *msg, eap_payload_t **out)
{
enumerator_t *enumerator;
eap_payload_t *payload;
chunk_t data, message = chunk_empty;
int type;
enumerator = msg->create_enumerator(msg);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (type == RAT_EAP_MESSAGE && data.len)
{
message = chunk_cat("mc", message, data);
}
}
enumerator->destroy(enumerator);
if (message.len)
{
*out = payload = eap_payload_create_data(message);
free(message.ptr);
/* apply EAP method selected by RADIUS server */
this->type = payload->get_type(payload, &this->vendor);
return TRUE;
}
return FALSE;
}
/**
* Implementation of eap_method_t.initiate
*/
static status_t initiate(private_eap_radius_t *this, eap_payload_t **out)
{
radius_message_t *request, *response;
status_t status = FAILED;
chunk_t username;
request = radius_message_create_request();
username = chunk_create(this->id_prefix, strlen(this->id_prefix));
username = chunk_cata("cc", username, this->peer->get_encoding(this->peer));
request->add(request, RAT_USER_NAME, username);
if (this->eap_start)
{
request->add(request, RAT_EAP_MESSAGE, chunk_empty);
}
else
{
add_eap_identity(this, request);
}
response = this->client->request(this->client, request);
if (response)
{
if (radius2ike(this, response, out))
{
status = NEED_MORE;
}
response->destroy(response);
}
request->destroy(request);
return status;
}
/**
* Implementation of eap_method_t.process
*/
static status_t process(private_eap_radius_t *this,
eap_payload_t *in, eap_payload_t **out)
{
radius_message_t *request, *response;
status_t status = FAILED;
chunk_t data;
request = radius_message_create_request();
request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer));
data = in->get_data(in);
/* fragment data suitable for RADIUS (not more than 253 bytes) */
while (data.len > 253)
{
request->add(request, RAT_EAP_MESSAGE, chunk_create(data.ptr, 253));
data = chunk_skip(data, 253);
}
request->add(request, RAT_EAP_MESSAGE, data);
response = this->client->request(this->client, request);
if (response)
{
switch (response->get_code(response))
{
case RMC_ACCESS_CHALLENGE:
if (radius2ike(this, response, out))
{
status = NEED_MORE;
break;
}
status = FAILED;
break;
case RMC_ACCESS_ACCEPT:
this->msk = this->client->decrypt_msk(this->client,
response, request);
status = SUCCESS;
break;
case RMC_ACCESS_REJECT:
default:
DBG1(DBG_CFG, "received %N from RADIUS server",
radius_message_code_names, response->get_code(response));
status = FAILED;
break;
}
response->destroy(response);
}
request->destroy(request);
return status;
}
/**
* Implementation of eap_method_t.get_type.
*/
static eap_type_t get_type(private_eap_radius_t *this, u_int32_t *vendor)
{
*vendor = this->vendor;
return this->type;
}
/**
* Implementation of eap_method_t.get_msk.
*/
static status_t get_msk(private_eap_radius_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_radius_t *this)
{
switch (this->type)
{
case EAP_AKA:
case EAP_SIM:
return TRUE;
default:
return FALSE;
}
}
/**
* Implementation of eap_method_t.destroy.
*/
static void destroy(private_eap_radius_t *this)
{
this->peer->destroy(this->peer);
this->server->destroy(this->server);
this->client->destroy(this->client);
chunk_clear(&this->msk);
free(this);
}
/**
* Generic constructor
*/
eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer)
{
private_eap_radius_t *this = malloc_thing(private_eap_radius_t);
this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))initiate;
this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))process;
this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*,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->client = radius_client_create();
if (!this->client)
{
free(this);
return NULL;
}
this->peer = peer->clone(peer);
this->server = server->clone(server);
/* initially EAP_RADIUS, but is set to the method selected by RADIUS */
this->type = EAP_RADIUS;
this->vendor = 0;
this->msk = chunk_empty;
this->eap_start = lib->settings->get_bool(lib->settings,
"charon.plugins.eap-radius.eap_start", FALSE);
this->id_prefix = lib->settings->get_str(lib->settings,
"charon.plugins.eap-radius.id_prefix", "");
return &this->public;
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 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_radius_i eap_radius
* @{ @ingroup eap_radius
*/
#ifndef EAP_RADIUS_H_
#define EAP_RADIUS_H_
typedef struct eap_radius_t eap_radius_t;
#include <sa/authenticators/eap/eap_method.h>
/**
* Implementation of the eap_method_t interface using a RADIUS server.
*/
struct eap_radius_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t eap_method_interface;
};
/**
* Create a EAP RADIUS proxy.
*
* @param server ID of the EAP server
* @param peer ID of the EAP client
* @return eap_radius_t object
*/
eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer);
#endif /** EAP_RADIUS_H_ @}*/
@@ -0,0 +1,54 @@
/*
* Copyright (C) 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_radius_plugin.h"
#include "eap_radius.h"
#include "radius_client.h"
#include <daemon.h>
/**
* Implementation of plugin_t.destroy
*/
static void destroy(eap_radius_plugin_t *this)
{
charon->eap->remove_method(charon->eap, (eap_constructor_t)eap_radius_create);
radius_client_cleanup();
free(this);
}
/*
* see header file
*/
plugin_t *eap_radius_plugin_create()
{
eap_radius_plugin_t *this;
if (!radius_client_init())
{
DBG1(DBG_CFG, "RADIUS plugin initialization failed");
return NULL;
}
this = malloc_thing(eap_radius_plugin_t);
this->plugin.destroy = (void(*)(plugin_t*))destroy;
charon->eap->add_method(charon->eap, EAP_RADIUS, 0,
EAP_SERVER, (eap_constructor_t)eap_radius_create);
return &this->plugin;
}
@@ -0,0 +1,45 @@
/*
* Copyright (C) 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_radius eap_radius
* @ingroup cplugins
*
* @defgroup eap_radius_plugin eap_radius_plugin
* @{ @ingroup eap_radius
*/
#ifndef EAP_RADIUS_PLUGIN_H_
#define EAP_RADIUS_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_radius_plugin_t eap_radius_plugin_t;
/**
* EAP RADIUS proxy plugin.
*
* This plugin provides not a single EAP method, but a proxy to forwared
* EAP packets to a RADIUS server. It only provides server implementations.
*/
struct eap_radius_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_RADIUS_PLUGIN_H_ @}*/
@@ -0,0 +1,495 @@
/*
* Copyright (C) 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 "radius_client.h"
#include <unistd.h>
#include <errno.h>
#include <daemon.h>
#include <utils/host.h>
#include <utils/linked_list.h>
#include <threading/condvar.h>
#include <threading/mutex.h>
/**
* Default RADIUS server port, when not configured
*/
#define RADIUS_PORT 1812
/**
* Vendor-Id of Microsoft specific attributes
*/
#define VENDOR_ID_MICROSOFT 311
/**
* Microsoft specific vendor attributes
*/
#define MS_MPPE_SEND_KEY 16
#define MS_MPPE_RECV_KEY 17
typedef struct private_radius_client_t private_radius_client_t;
typedef struct entry_t entry_t;
/**
* A socket pool entry.
*/
struct entry_t {
/** socket file descriptor */
int fd;
/** current RADIUS identifier */
u_int8_t identifier;
/** hasher to use for response verification */
hasher_t *hasher;
/** HMAC-MD5 signer to build Message-Authenticator attribute */
signer_t *signer;
/** random number generator for RADIUS request authenticator */
rng_t *rng;
};
/**
* Private data of an radius_client_t object.
*/
struct private_radius_client_t {
/**
* Public radius_client_t interface.
*/
radius_client_t public;
/**
* RADIUS servers State attribute
*/
chunk_t state;
};
/**
* Global list of radius sockets, contains entry_t's
*/
static linked_list_t *sockets;
/**
* mutex to lock sockets list
*/
static mutex_t *mutex;
/**
* condvar to wait for sockets
*/
static condvar_t *condvar;
/**
* RADIUS secret
*/
static chunk_t secret;
/**
* NAS-Identifier
*/
static chunk_t nas_identifier;
/**
* Clean up socket list
*/
void radius_client_cleanup()
{
entry_t *entry;
mutex->destroy(mutex);
condvar->destroy(condvar);
while (sockets->remove_last(sockets, (void**)&entry) == SUCCESS)
{
entry->rng->destroy(entry->rng);
entry->hasher->destroy(entry->hasher);
entry->signer->destroy(entry->signer);
close(entry->fd);
free(entry);
}
sockets->destroy(sockets);
}
/**
* Initialize the socket list
*/
bool radius_client_init()
{
int i, count, fd;
u_int16_t port;
entry_t *entry;
host_t *host;
char *server;
nas_identifier.ptr = lib->settings->get_str(lib->settings,
"charon.plugins.eap-radius.nas_identifier", "strongSwan");
nas_identifier.len = strlen(nas_identifier.ptr);
secret.ptr = lib->settings->get_str(lib->settings,
"charon.plugins.eap-radius.secret", NULL);
if (!secret.ptr)
{
DBG1(DBG_CFG, "no RADUIS secret defined");
return FALSE;
}
secret.len = strlen(secret.ptr);
server = lib->settings->get_str(lib->settings,
"charon.plugins.eap-radius.server", NULL);
if (!server)
{
DBG1(DBG_CFG, "no RADUIS server defined");
return FALSE;
}
port = lib->settings->get_int(lib->settings,
"charon.plugins.eap-radius.port", RADIUS_PORT);
host = host_create_from_dns(server, 0, port);
if (!host)
{
return FALSE;
}
count = lib->settings->get_int(lib->settings,
"charon.plugins.eap-radius.sockets", 1);
sockets = linked_list_create();
mutex = mutex_create(MUTEX_TYPE_DEFAULT);
condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
for (i = 0; i < count; i++)
{
fd = socket(host->get_family(host), SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0)
{
DBG1(DBG_CFG, "opening RADIUS socket failed");
host->destroy(host);
radius_client_cleanup();
return FALSE;
}
if (connect(fd, host->get_sockaddr(host),
*host->get_sockaddr_len(host)) < 0)
{
DBG1(DBG_CFG, "connecting RADIUS socket failed");
host->destroy(host);
radius_client_cleanup();
return FALSE;
}
entry = malloc_thing(entry_t);
entry->fd = fd;
/* we use per-socket crypto elements: this reduces overhead, but
* is still thread-save. */
entry->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
entry->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
entry->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!entry->hasher || !entry->signer || !entry->rng)
{
DBG1(DBG_CFG, "RADIUS initialization failed, HMAC/MD5/RNG required");
DESTROY_IF(entry->hasher);
DESTROY_IF(entry->signer);
DESTROY_IF(entry->rng);
free(entry);
host->destroy(host);
radius_client_cleanup();
return FALSE;
}
entry->signer->set_key(entry->signer, secret);
/* we use a random identifier, helps if we restart often (testing) */
entry->identifier = random();
sockets->insert_last(sockets, entry);
}
host->destroy(host);
return TRUE;
}
/**
* Get a socket from the pool, block if none available
*/
static entry_t* get_socket()
{
entry_t *entry;
mutex->lock(mutex);
while (sockets->remove_first(sockets, (void**)&entry) != SUCCESS)
{
condvar->wait(condvar, mutex);
}
mutex->unlock(mutex);
return entry;
}
/**
* Release a socket to the pool
*/
static void put_socket(entry_t *entry)
{
mutex->lock(mutex);
sockets->insert_last(sockets, entry);
mutex->unlock(mutex);
condvar->signal(condvar);
}
/**
* Save the state attribute to include in further request
*/
static void save_state(private_radius_client_t *this, radius_message_t *msg)
{
enumerator_t *enumerator;
int type;
chunk_t data;
enumerator = msg->create_enumerator(msg);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (type == RAT_STATE)
{
free(this->state.ptr);
this->state = chunk_clone(data);
enumerator->destroy(enumerator);
return;
}
}
enumerator->destroy(enumerator);
/* no state attribute found, remove state */
chunk_free(&this->state);
}
/**
* Implementation of radius_client_t.request
*/
static radius_message_t* request(private_radius_client_t *this,
radius_message_t *req)
{
char virtual[] = {0x00,0x00,0x00,0x05};
entry_t *socket;
chunk_t data;
int i;
socket = get_socket();
/* set Message Identifier */
req->set_identifier(req, socket->identifier++);
/* we add the "Virtual" NAS-Port-Type, as we SHOULD include one */
req->add(req, RAT_NAS_PORT_TYPE, chunk_create(virtual, sizeof(virtual)));
/* add our NAS-Identifier */
req->add(req, RAT_NAS_IDENTIFIER, nas_identifier);
/* add State attribute, if server sent one */
if (this->state.ptr)
{
req->add(req, RAT_STATE, this->state);
}
/* sign the request */
req->sign(req, socket->rng, socket->signer);
data = req->get_encoding(req);
/* timeout after 2, 3, 4, 5 seconds */
for (i = 2; i <= 5; i++)
{
radius_message_t *response;
bool retransmit = FALSE;
struct timeval tv;
char buf[4096];
fd_set fds;
int res;
if (send(socket->fd, data.ptr, data.len, 0) != data.len)
{
DBG1(DBG_CFG, "sending RADIUS message failed: %s", strerror(errno));
put_socket(socket);
return NULL;
}
tv.tv_sec = i;
tv.tv_usec = 0;
while (TRUE)
{
FD_ZERO(&fds);
FD_SET(socket->fd, &fds);
res = select(socket->fd + 1, &fds, NULL, NULL, &tv);
/* TODO: updated tv to time not waited. Linux does this for us. */
if (res < 0)
{ /* failed */
DBG1(DBG_CFG, "waiting for RADIUS message failed: %s",
strerror(errno));
break;
}
if (res == 0)
{ /* timeout */
DBG1(DBG_CFG, "retransmitting RADIUS message");
retransmit = TRUE;
break;
}
res = recv(socket->fd, buf, sizeof(buf), MSG_DONTWAIT);
if (res <= 0)
{
DBG1(DBG_CFG, "receiving RADIUS message failed: %s",
strerror(errno));
break;
}
response = radius_message_parse_response(chunk_create(buf, res));
if (response)
{
if (response->verify(response, req->get_authenticator(req),
secret, socket->hasher, socket->signer))
{
save_state(this, response);
put_socket(socket);
return response;
}
response->destroy(response);
}
DBG1(DBG_CFG, "received invalid RADIUS message, ignored");
}
if (!retransmit)
{
break;
}
}
DBG1(DBG_CFG, "RADIUS server is not responding");
put_socket(socket);
charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING);
return NULL;
}
/**
* Decrypt a MS-MPPE-Send/Recv-Key
*/
static chunk_t decrypt_mppe_key(private_radius_client_t *this, u_int16_t salt,
chunk_t C, radius_message_t *request)
{
chunk_t A, R, P, seed;
u_char *c, *p;
hasher_t *hasher;
/**
* From RFC2548 (encryption):
* b(1) = MD5(S + R + A) c(1) = p(1) xor b(1) C = c(1)
* b(2) = MD5(S + c(1)) c(2) = p(2) xor b(2) C = C + c(2)
* . . .
* b(i) = MD5(S + c(i-1)) c(i) = p(i) xor b(i) C = C + c(i)
*/
if (C.len % HASH_SIZE_MD5 || C.len < HASH_SIZE_MD5)
{
return chunk_empty;
}
hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
if (!hasher)
{
return chunk_empty;
}
A = chunk_create((u_char*)&salt, sizeof(salt));
R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
P = chunk_alloca(C.len);
p = P.ptr;
c = C.ptr;
seed = chunk_cata("cc", R, A);
while (c < C.ptr + C.len)
{
/* b(i) = MD5(S + c(i-1)) */
hasher->get_hash(hasher, secret, NULL);
hasher->get_hash(hasher, seed, p);
/* p(i) = b(i) xor c(1) */
memxor(p, c, HASH_SIZE_MD5);
/* prepare next round */
seed = chunk_create(c, HASH_SIZE_MD5);
c += HASH_SIZE_MD5;
p += HASH_SIZE_MD5;
}
hasher->destroy(hasher);
/* remove truncation, first byte is key length */
if (*P.ptr >= P.len)
{ /* decryption failed? */
return chunk_empty;
}
return chunk_clone(chunk_create(P.ptr + 1, *P.ptr));
}
/**
* Implementation of radius_client_t.decrypt_msk
*/
static chunk_t decrypt_msk(private_radius_client_t *this,
radius_message_t *response, radius_message_t *request)
{
struct {
u_int32_t id;
u_int8_t type;
u_int8_t length;
u_int16_t salt;
u_int8_t key[];
} __attribute__((packed)) *mppe_key;
enumerator_t *enumerator;
chunk_t data, send = chunk_empty, recv = chunk_empty;
int type;
enumerator = response->create_enumerator(response);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (type == RAT_VENDOR_SPECIFIC &&
data.len > sizeof(*mppe_key))
{
mppe_key = (void*)data.ptr;
if (ntohl(mppe_key->id) == VENDOR_ID_MICROSOFT &&
mppe_key->length == data.len - sizeof(mppe_key->id))
{
data = chunk_create(mppe_key->key, data.len - sizeof(*mppe_key));
if (mppe_key->type == MS_MPPE_SEND_KEY)
{
send = decrypt_mppe_key(this, mppe_key->salt, data, request);
}
if (mppe_key->type == MS_MPPE_RECV_KEY)
{
recv = decrypt_mppe_key(this, mppe_key->salt, data, request);
}
}
}
}
enumerator->destroy(enumerator);
if (send.ptr && recv.ptr)
{
return chunk_cat("mm", recv, send);
}
chunk_clear(&send);
chunk_clear(&recv);
return chunk_empty;
}
/**
* Implementation of radius_client_t.destroy.
*/
static void destroy(private_radius_client_t *this)
{
free(this->state.ptr);
free(this);
}
/**
* See header
*/
radius_client_t *radius_client_create()
{
private_radius_client_t *this = malloc_thing(private_radius_client_t);
this->public.request = (radius_message_t*(*)(radius_client_t*, radius_message_t *msg))request;
this->public.decrypt_msk = (chunk_t(*)(radius_client_t*, radius_message_t *, radius_message_t *))decrypt_msk;
this->public.destroy = (void(*)(radius_client_t*))destroy;
this->state = chunk_empty;
return &this->public;
}
@@ -0,0 +1,88 @@
/*
* Copyright (C) 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 radius_client radius_client
* @{ @ingroup eap_radius
*/
#ifndef RADIUS_CLIENT_H_
#define RADIUS_CLIENT_H_
#include "radius_message.h"
typedef struct radius_client_t radius_client_t;
/**
* RADIUS client functionality.
*
* To communicate with a RADIUS server, create a client and send messages over
* it. All instances share a fixed size pool of sockets. The client reserves
* a socket during request() and releases it afterwards.
*/
struct radius_client_t {
/**
* Send a RADIUS request and wait for the response.
*
* The client fills in RADIUS Message identifier, NAS-Identifier,
* NAS-Port-Type, builds a Request-Authenticator and calculates the
* Message-Authenticator attribute.
* The received response gets verified using the Response-Identifier
* and the Message-Authenticator attribute.
*
* @param msg RADIUS request message to send
* @return response, NULL if timed out/verification failed
*/
radius_message_t* (*request)(radius_client_t *this, radius_message_t *msg);
/**
* Decrypt the MSK encoded in a messages MS-MPPE-Send/Recv-Key.
*
* @param response RADIUS response message containing attributes
* @param request associated RADIUS request message
* @return allocated MSK, empty chunk if none found
*/
chunk_t (*decrypt_msk)(radius_client_t *this, radius_message_t *response,
radius_message_t *request);
/**
* Destroy the client, release the socket.
*/
void (*destroy)(radius_client_t *this);
};
/**
* Create a RADIUS client, acquire a socket.
*
* This call might block if the socket pool is empty.
*
* @return radius_client_t object
*/
radius_client_t *radius_client_create();
/**
* Initialize the socket pool.
*
* @return TRUE if initialization successful
*/
bool radius_client_init();
/**
* Cleanup the socket pool.
*/
void radius_client_cleanup();
#endif /** RADIUS_CLIENT_H_ @}*/
@@ -0,0 +1,476 @@
/*
* Copyright (C) 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 "radius_message.h"
#include <daemon.h>
#include <crypto/hashers/hasher.h>
typedef struct private_radius_message_t private_radius_message_t;
typedef struct rmsg_t rmsg_t;
typedef struct rattr_t rattr_t;
/**
* RADIUS message header
*/
struct rmsg_t {
/** message code, radius_message_code_t */
u_int8_t code;
/** message identifier */
u_int8_t identifier;
/** length of Code, Identifier, Length, Authenticator and Attributes */
u_int16_t length;
/** message authenticator, MD5 hash */
u_int8_t authenticator[HASH_SIZE_MD5];
/** variable list of packed attributes */
u_int8_t attributes[];
} __attribute__((packed));
/**
* RADIUS message attribute.
*/
struct rattr_t {
/** attribute type, radius_attribute_type_t */
u_int8_t type;
/** length of the attriubte, including the Type, Length and Value fields */
u_int8_t length;
/** variable length attribute value */
u_int8_t value[];
} __attribute__((packed));
/**
* Private data of an radius_message_t object.
*/
struct private_radius_message_t {
/**
* Public radius_message_t interface.
*/
radius_message_t public;
/**
* message data, allocated
*/
rmsg_t *msg;
};
ENUM_BEGIN(radius_message_code_names, RMC_ACCESS_REQUEST, RMC_ACCOUNTING_RESPONSE,
"Access-Request",
"Access-Accept",
"Access-Reject",
"Accounting-Request",
"Accounting-Response");
ENUM_NEXT(radius_message_code_names, RMC_ACCESS_CHALLENGE, RMC_ACCESS_CHALLENGE, RMC_ACCOUNTING_RESPONSE,
"Access-Challenge");
ENUM_END(radius_message_code_names, RMC_ACCESS_CHALLENGE);
ENUM(radius_attribute_type_names, RAT_USER_NAME, RAT_MIP6_HOME_LINK_PREFIX,
"User-Name",
"User-Password",
"CHAP-Password",
"NAS-IP-Address",
"NAS-Port",
"Service-Type",
"Framed-Protocol",
"Framed-IP-Address",
"Framed-IP-Netmask",
"Framed-Routing",
"Filter-Id",
"Framed-MTU",
"Framed-Compression",
"Login-IP-Host",
"Login-Service",
"Login-TCP-Port",
"Unassigned",
"Reply-Message",
"Callback-Number",
"Callback-Id",
"Unassigned",
"Framed-Route",
"Framed-IPX-Network",
"State",
"Class",
"Vendor-Specific",
"Session-Timeout",
"Idle-Timeout",
"Termination-Action",
"Called-Station-Id",
"Calling-Station-Id",
"NAS-Identifier",
"Proxy-State",
"Login-LAT-Service",
"Login-LAT-Node",
"Login-LAT-Group",
"Framed-AppleTalk-Link",
"Framed-AppleTalk-Network",
"Framed-AppleTalk-Zone",
"Acct-Status-Type",
"Acct-Delay-Time",
"Acct-Input-Octets",
"Acct-Output-Octets",
"Acct-Session-Id",
"Acct-Authentic",
"Acct-Session-Time",
"Acct-Input-Packets",
"Acct-Output-Packets",
"Acct-Terminate-Cause",
"Acct-Multi-Session-Id",
"Acct-Link-Count",
"Acct-Input-Gigawords",
"Acct-Output-Gigawords",
"Unassigned",
"Event-Timestamp",
"Egress-VLANID",
"Ingress-Filters",
"Egress-VLAN-Name",
"User-Priority-Table",
"CHAP-Challenge",
"NAS-Port-Type",
"Port-Limit",
"Login-LAT-Port",
"Tunnel-Type",
"Tunnel-Medium-Type",
"Tunnel-Client-Endpoint",
"Tunnel-Server-Endpoint",
"Acct-Tunnel-Connection",
"Tunnel-Password",
"ARAP-Password",
"ARAP-Features",
"ARAP-Zone-Access",
"ARAP-Security",
"ARAP-Security-Data",
"Password-Retry",
"Prompt",
"Connect-Info",
"Configuration-Token",
"EAP-Message",
"Message-Authenticator",
"Tunnel-Private-Group-ID",
"Tunnel-Assignment-ID",
"Tunnel-Preference",
"ARAP-Challenge-Response",
"Acct-Interim-Interval",
"Acct-Tunnel-Packets-Lost",
"NAS-Port-Id",
"Framed-Pool",
"CUI",
"Tunnel-Client-Auth-ID",
"Tunnel-Server-Auth-ID",
"NAS-Filter-Rule",
"Unassigned",
"Originating-Line-Info",
"NAS-IPv6-Address",
"Framed-Interface-Id",
"Framed-IPv6-Prefix",
"Login-IPv6-Host",
"Framed-IPv6-Route",
"Framed-IPv6-Pool",
"Error-Cause",
"EAP-Key-Name",
"Digest-Response",
"Digest-Realm",
"Digest-Nonce",
"Digest-Response-Auth",
"Digest-Nextnonce",
"Digest-Method",
"Digest-URI",
"Digest-Qop",
"Digest-Algorithm",
"Digest-Entity-Body-Hash",
"Digest-CNonce",
"Digest-Nonce-Count",
"Digest-Username",
"Digest-Opaque",
"Digest-Auth-Param",
"Digest-AKA-Auts",
"Digest-Domain",
"Digest-Stale",
"Digest-HA1",
"SIP-AOR",
"Delegated-IPv6-Prefix",
"MIP6-Feature-Vector",
"MIP6-Home-Link-Prefix");
/**
* Attribute enumerator implementation
*/
typedef struct {
/** implements enumerator interface */
enumerator_t public;
/** currently pointing attribute */
rattr_t *next;
/** bytes left */
int left;
} attribute_enumerator_t;
/**
* Implementation of attribute_enumerator_t.enumerate
*/
static bool attribute_enumerate(attribute_enumerator_t *this,
int *type, chunk_t *data)
{
if (this->left == 0)
{
return FALSE;
}
if (this->left < sizeof(rattr_t) ||
this->left < this->next->length)
{
DBG1(DBG_IKE, "RADIUS message truncated");
return FALSE;
}
*type = this->next->type;
data->ptr = this->next->value;
data->len = this->next->length - sizeof(rattr_t);
this->left -= this->next->length;
this->next = ((void*)this->next) + this->next->length;
return TRUE;
}
/**
* Implementation of radius_message_t.create_enumerator
*/
static enumerator_t* create_enumerator(private_radius_message_t *this)
{
attribute_enumerator_t *e;
if (ntohs(this->msg->length) < sizeof(rmsg_t) + sizeof(rattr_t))
{
return enumerator_create_empty();
}
e = malloc_thing(attribute_enumerator_t);
e->public.enumerate = (void*)attribute_enumerate;
e->public.destroy = (void*)free;
e->next = (rattr_t*)this->msg->attributes;
e->left = ntohs(this->msg->length) - sizeof(rmsg_t);
return &e->public;
}
/**
* Implementation of radius_message_t.add
*/
static void add(private_radius_message_t *this, radius_attribute_type_t type,
chunk_t data)
{
rattr_t *attribute;
data.len = min(data.len, 253);
this->msg = realloc(this->msg,
ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
attribute = ((void*)this->msg) + ntohs(this->msg->length);
attribute->type = type;
attribute->length = data.len + sizeof(rattr_t);
memcpy(attribute->value, data.ptr, data.len);
this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
}
/**
* Implementation of radius_message_t.sign
*/
static void sign(private_radius_message_t *this, rng_t *rng, signer_t *signer)
{
char buf[HASH_SIZE_MD5];
/* build Request-Authenticator */
rng->get_bytes(rng, HASH_SIZE_MD5, this->msg->authenticator);
/* build Message-Authenticator attribute, using 16 null bytes */
memset(buf, 0, sizeof(buf));
add(this, RAT_MESSAGE_AUTHENTICATOR, chunk_create(buf, sizeof(buf)));
signer->get_signature(signer,
chunk_create((u_char*)this->msg, ntohs(this->msg->length)),
((u_char*)this->msg) + ntohs(this->msg->length) - HASH_SIZE_MD5);
}
/**
* Implementation of radius_message_t.verify
*/
static bool verify(private_radius_message_t *this, u_int8_t *req_auth,
chunk_t secret, hasher_t *hasher, signer_t *signer)
{
char buf[HASH_SIZE_MD5], res_auth[HASH_SIZE_MD5];
enumerator_t *enumerator;
int type;
chunk_t data, msg;
bool has_eap = FALSE, has_auth = FALSE;
/* replace Response by Request Authenticator for verification */
memcpy(res_auth, this->msg->authenticator, HASH_SIZE_MD5);
memcpy(this->msg->authenticator, req_auth, HASH_SIZE_MD5);
msg = chunk_create((u_char*)this->msg, ntohs(this->msg->length));
/* verify Response-Authenticator */
hasher->get_hash(hasher, msg, NULL);
hasher->get_hash(hasher, secret, buf);
if (!memeq(buf, res_auth, HASH_SIZE_MD5))
{
DBG1(DBG_CFG, "RADIUS Response-Authenticator verification failed");
return FALSE;
}
/* verify Message-Authenticator attribute */
enumerator = create_enumerator(this);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (type == RAT_MESSAGE_AUTHENTICATOR)
{
if (data.len != HASH_SIZE_MD5)
{
DBG1(DBG_CFG, "RADIUS Message-Authenticator invalid length");
enumerator->destroy(enumerator);
return FALSE;
}
memcpy(buf, data.ptr, data.len);
memset(data.ptr, 0, data.len);
if (signer->verify_signature(signer, msg,
chunk_create(buf, sizeof(buf))))
{
/* restore Message-Authenticator */
memcpy(data.ptr, buf, data.len);
has_auth = TRUE;
break;
}
else
{
DBG1(DBG_CFG, "RADIUS Message-Authenticator verification failed");
enumerator->destroy(enumerator);
return FALSE;
}
}
else if (type == RAT_EAP_MESSAGE)
{
has_eap = TRUE;
}
}
enumerator->destroy(enumerator);
/* restore Response-Authenticator */
memcpy(this->msg->authenticator, res_auth, HASH_SIZE_MD5);
if (has_eap && !has_auth)
{ /* Message-Authenticator is required if we have an EAP-Message */
DBG1(DBG_CFG, "RADIUS Message-Authenticator attribute missing");
return FALSE;
}
return TRUE;
}
/**
* Implementation of radius_message_t.get_code
*/
static radius_message_code_t get_code(private_radius_message_t *this)
{
return this->msg->code;
}
/**
* Implementation of radius_message_t.get_identifier
*/
static u_int8_t get_identifier(private_radius_message_t *this)
{
return this->msg->identifier;
}
/**
* Implementation of radius_message_t.set_identifier
*/
static void set_identifier(private_radius_message_t *this, u_int8_t identifier)
{
this->msg->identifier = identifier;
}
/**
* Implementation of radius_message_t.get_authenticator
*/
static u_int8_t* get_authenticator(private_radius_message_t *this)
{
return this->msg->authenticator;
}
/**
* Implementation of radius_message_t.get_encoding
*/
static chunk_t get_encoding(private_radius_message_t *this)
{
return chunk_create((u_char*)this->msg, ntohs(this->msg->length));
}
/**
* Implementation of radius_message_t.destroy.
*/
static void destroy(private_radius_message_t *this)
{
free(this->msg);
free(this);
}
/**
* Generic constructor
*/
static private_radius_message_t *radius_message_create()
{
private_radius_message_t *this = malloc_thing(private_radius_message_t);
this->public.create_enumerator = (enumerator_t*(*)(radius_message_t*))create_enumerator;
this->public.add = (void(*)(radius_message_t*, radius_attribute_type_t,chunk_t))add;
this->public.get_code = (radius_message_code_t(*)(radius_message_t*))get_code;
this->public.get_identifier = (u_int8_t(*)(radius_message_t*))get_identifier;
this->public.set_identifier = (void(*)(radius_message_t*, u_int8_t identifier))set_identifier;
this->public.get_authenticator = (u_int8_t*(*)(radius_message_t*))get_authenticator;
this->public.get_encoding = (chunk_t(*)(radius_message_t*))get_encoding;
this->public.sign = (void(*)(radius_message_t*, rng_t *rng, signer_t *signer))sign;
this->public.verify = (bool(*)(radius_message_t*, u_int8_t *req_auth, chunk_t secret, hasher_t *hasher, signer_t *signer))verify;
this->public.destroy = (void(*)(radius_message_t*))destroy;
return this;
}
/**
* See header
*/
radius_message_t *radius_message_create_request()
{
private_radius_message_t *this = radius_message_create();
this->msg = malloc_thing(rmsg_t);
this->msg->code = RMC_ACCESS_REQUEST;
this->msg->identifier = 0;
this->msg->length = htons(sizeof(rmsg_t));
return &this->public;
}
/**
* See header
*/
radius_message_t *radius_message_parse_response(chunk_t data)
{
private_radius_message_t *this = radius_message_create();
this->msg = malloc(data.len);
memcpy(this->msg, data.ptr, data.len);
if (data.len < sizeof(rmsg_t) ||
ntohs(this->msg->length) != data.len)
{
DBG1(DBG_IKE, "RADIUS message has invalid length");
destroy(this);
return NULL;
}
return &this->public;
}
@@ -0,0 +1,276 @@
/*
* Copyright (C) 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 radius_message radius_message
* @{ @ingroup eap_radius
*/
#ifndef RADIUS_MESSAGE_H_
#define RADIUS_MESSAGE_H_
#include <library.h>
typedef struct radius_message_t radius_message_t;
typedef enum radius_message_code_t radius_message_code_t;
typedef enum radius_attribute_type_t radius_attribute_type_t;
/**
* RADIUS Message Codes.
*/
enum radius_message_code_t {
RMC_ACCESS_REQUEST = 1,
RMC_ACCESS_ACCEPT = 2,
RMC_ACCESS_REJECT = 3,
RMC_ACCOUNTING_REQUEST = 4,
RMC_ACCOUNTING_RESPONSE = 5,
RMC_ACCESS_CHALLENGE = 11,
};
/**
* Enum names for radius_attribute_type_t.
*/
extern enum_name_t *radius_message_code_names;
/**
* RADIUS Attribute Types.
*/
enum radius_attribute_type_t {
RAT_USER_NAME = 1,
RAT_USER_PASSWORD = 2,
RAT_CHAP_PASSWORD = 3,
RAT_NAS_IP_ADDRESS = 4,
RAT_NAS_PORT = 5,
RAT_SERVICE_TYPE = 6,
RAT_FRAMED_PROTOCOL = 7,
RAT_FRAMED_IP_ADDRESS = 8,
RAT_FRAMED_IP_NETMASK = 9,
RAT_FRAMED_ROUTING = 10,
RAT_FILTER_ID = 11,
RAT_FRAMED_MTU = 12,
RAT_FRAMED_COMPRESSION = 13,
RAT_LOGIN_IP_HOST = 14,
RAT_LOGIN_SERVICE = 15,
RAT_LOGIN_TCP_PORT = 16,
RAT_REPLY_MESSAGE = 18,
RAT_CALLBACK_NUMBER = 19,
RAT_CALLBACK_ID = 20,
RAT_FRAMED_ROUTE = 22,
RAT_FRAMED_IPX_NETWORK = 23,
RAT_STATE = 24,
RAT_CLASS = 25,
RAT_VENDOR_SPECIFIC = 26,
RAT_SESSION_TIMEOUT = 27,
RAT_IDLE_TIMEOUT = 28,
RAT_TERMINATION_ACTION = 29,
RAT_CALLED_STATION_ID = 30,
RAT_CALLING_STATION_ID = 31,
RAT_NAS_IDENTIFIER = 32,
RAT_PROXY_STATE = 33,
RAT_LOGIN_LAT_SERVICE = 34,
RAT_LOGIN_LAT_NODE = 35,
RAT_LOGIN_LAT_GROUP = 36,
RAT_FRAMED_APPLETALK_LINK = 37,
RAT_FRAMED_APPLETALK_NETWORK = 38,
RAT_FRAMED_APPLETALK_ZONE = 39,
RAT_ACCT_STATUS_TYPE = 40,
RAT_ACCT_DELAY_TIME = 41,
RAT_ACCT_INPUT_OCTETS = 42,
RAT_ACCT_OUTPUT_OCTETS = 43,
RAT_ACCT_SESSION_ID = 44,
RAT_ACCT_AUTHENTIC = 45,
RAT_ACCT_SESSION_TIME = 46,
RAT_ACCT_INPUT_PACKETS = 47,
RAT_ACCT_OUTPUT_PACKETS = 48,
RAT_ACCT_TERMINATE_CAUSE = 49,
RAT_ACCT_MULTI_SESSION_ID = 50,
RAT_ACCT_LINK_COUNT = 51,
RAT_ACCT_INPUT_GIGAWORDS = 52,
RAT_ACCT_OUTPUT_GIGAWORDS = 53,
RAT_EVENT_TIMESTAMP = 55,
RAT_EGRESS_VLANID = 56,
RAT_INGRESS_FILTERS = 57,
RAT_EGRESS_VLAN_NAME = 58,
RAT_USER_PRIORITY_TABLE = 59,
RAT_CHAP_CHALLENGE = 60,
RAT_NAS_PORT_TYPE = 61,
RAT_PORT_LIMIT = 62,
RAT_LOGIN_LAT_PORT = 63,
RAT_TUNNEL_TYPE = 64,
RAT_TUNNEL_MEDIUM_TYPE = 65,
RAT_TUNNEL_CLIENT_ENDPOINT = 66,
RAT_TUNNEL_SERVER_ENDPOINT = 67,
RAT_ACCT_TUNNEL_CONNECTION = 68,
RAT_TUNNEL_PASSWORD = 69,
RAT_ARAP_PASSWORD = 70,
RAT_ARAP_FEATURES = 71,
RAT_ARAP_ZONE_ACCESS = 72,
RAT_ARAP_SECURITY = 73,
RAT_ARAP_SECURITY_DATA = 74,
RAT_PASSWORD_RETRY = 75,
RAT_PROMPT = 76,
RAT_CONNECT_INFO = 77,
RAT_CONFIGURATION_TOKEN = 78,
RAT_EAP_MESSAGE = 79,
RAT_MESSAGE_AUTHENTICATOR = 80,
RAT_TUNNEL_PRIVATE_GROUP_ID = 81,
RAT_TUNNEL_ASSIGNMENT_ID = 82,
RAT_TUNNEL_PREFERENCE = 83,
RAT_ARAP_CHALLENGE_RESPONSE = 84,
RAT_ACCT_INTERIM_INTERVAL = 85,
RAT_ACCT_TUNNEL_PACKETS_LOST = 86,
RAT_NAS_PORT_ID = 87,
RAT_FRAMED_POOL = 88,
RAT_CUI = 89,
RAT_TUNNEL_CLIENT_AUTH_ID = 90,
RAT_TUNNEL_SERVER_AUTH_ID = 91,
RAT_NAS_FILTER_RULE = 92,
RAT_UNASSIGNED = 93,
RAT_ORIGINATING_LINE_INFO = 94,
RAT_NAS_IPV6_ADDRESS = 95,
RAT_FRAMED_INTERFACE_ID = 96,
RAT_FRAMED_IPV6_PREFIX = 97,
RAT_LOGIN_IPV6_HOST = 98,
RAT_FRAMED_IPV6_ROUTE = 99,
RAT_FRAMED_IPV6_POOL = 100,
RAT_ERROR_CAUSE = 101,
RAT_EAP_KEY_NAME = 102,
RAT_DIGEST_RESPONSE = 103,
RAT_DIGEST_REALM = 104,
RAT_DIGEST_NONCE = 105,
RAT_DIGEST_RESPONSE_AUTH = 106,
RAT_DIGEST_NEXTNONCE = 107,
RAT_DIGEST_METHOD = 108,
RAT_DIGEST_URI = 109,
RAT_DIGEST_QOP = 110,
RAT_DIGEST_ALGORITHM = 111,
RAT_DIGEST_ENTITY_BODY_HASH = 112,
RAT_DIGEST_CNONCE = 113,
RAT_DIGEST_NONCE_COUNT = 114,
RAT_DIGEST_USERNAME = 115,
RAT_DIGEST_OPAQUE = 116,
RAT_DIGEST_AUTH_PARAM = 117,
RAT_DIGEST_AKA_AUTS = 118,
RAT_DIGEST_DOMAIN = 119,
RAT_DIGEST_STALE = 120,
RAT_DIGEST_HA1 = 121,
RAT_SIP_AOR = 122,
RAT_DELEGATED_IPV6_PREFIX = 123,
RAT_MIP6_FEATURE_VECTOR = 124,
RAT_MIP6_HOME_LINK_PREFIX = 125,
};
/**
* Enum names for radius_attribute_type_t.
*/
extern enum_name_t *radius_attribute_type_names;
/**
* A RADIUS message, contains attributes.
*/
struct radius_message_t {
/**
* Create an enumerator over contained RADIUS attributes.
*
* @return enumerator over (int type, chunk_t data)
*/
enumerator_t* (*create_enumerator)(radius_message_t *this);
/**
* Add a RADIUS attribute to the message.
*
* @param type type of attribute to add
* @param attribute data, gets cloned
*/
void (*add)(radius_message_t *this, radius_attribute_type_t type,
chunk_t data);
/**
* Get the message type (code).
*
* @return message code
*/
radius_message_code_t (*get_code)(radius_message_t *this);
/**
* Get the message identifier.
*
* @return message identifier
*/
u_int8_t (*get_identifier)(radius_message_t *this);
/**
* Set the message identifier.
*
* @param identifier message identifier
*/
void (*set_identifier)(radius_message_t *this, u_int8_t identifier);
/**
* Get the 16 byte authenticator.
*
* @return pointer to the Authenticator field
*/
u_int8_t* (*get_authenticator)(radius_message_t *this);
/**
* Get the RADIUS message in its encoded form.
*
* @return chunk pointing to internal RADIUS message.
*/
chunk_t (*get_encoding)(radius_message_t *this);
/**
* Calculate and add the Message-Authenticator attribute to the message.
*
* @param rng RNG to create Request-Authenticator
* @param signer HMAC-MD5 signer with secret set
*/
void (*sign)(radius_message_t *this, rng_t *rng, signer_t *signer);
/**
* Verify the integrity of a received RADIUS response.
*
* @param req_auth 16 byte Authenticator of the corresponding request
* @param secret shared RADIUS secret
* @param hasher hasher to verify Response-Authenticator
* @param signer signer to verify Message-Authenticator attribute
*/
bool (*verify)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
hasher_t *hasher, signer_t *signer);
/**
* Destroy the message.
*/
void (*destroy)(radius_message_t *this);
};
/**
* Create an empty RADIUS request message (RMT_ACCESS_REQUEST).
*
* @return radius_message_t object
*/
radius_message_t *radius_message_create_request();
/**
* Parse and verify a recevied RADIUS response.
*
* @param data received message data
* @return radius_message_t object, NULL if length invalid
*/
radius_message_t *radius_message_parse_response(chunk_t data);
#endif /** RADIUS_MESSAGE_H_ @}*/
+19
View File
@@ -0,0 +1,19 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon \
-I$(top_srcdir)/src/libsimaka
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-sim.la
else
plugin_LTLIBRARIES = libstrongswan-eap-sim.la
libstrongswan_eap_sim_la_LIBADD = $(top_builddir)/src/libsimaka/libsimaka.la
endif
libstrongswan_eap_sim_la_SOURCES = \
eap_sim_plugin.h eap_sim_plugin.c \
eap_sim_peer.h eap_sim_peer.c \
eap_sim_server.h eap_sim_server.c
libstrongswan_eap_sim_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,654 @@
/*
* Copyright (C) 2007-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_sim_peer.h"
#include <daemon.h>
#include <simaka_message.h>
/* number of tries we do authenticate */
#define MAX_TRIES 3
/* number of triplets for one authentication */
#define TRIPLET_COUNT 3
/** length of the AT_NONCE_MT nonce value */
#define NONCE_LEN 16
typedef struct private_eap_sim_peer_t private_eap_sim_peer_t;
/**
* Private data of an eap_sim_peer_t object.
*/
struct private_eap_sim_peer_t {
/**
* Public authenticator_t interface.
*/
eap_sim_peer_t public;
/**
* permanent ID of peer
*/
identification_t *permanent;
/**
* Pseudonym identity the peer uses
*/
identification_t *pseudonym;
/**
* Reauthentication identity the peer uses
*/
identification_t *reauth;
/**
* EAP-SIM crypto helper
*/
simaka_crypto_t *crypto;
/**
* how many times we try to authenticate
*/
int tries;
/**
* version list received from server
*/
chunk_t version_list;
/**
* Nonce value used in AT_NONCE_MT/AT_NONCE_S
*/
chunk_t nonce;
/**
* MSK, used for EAP-SIM based IKEv2 authentication
*/
chunk_t msk;
/**
* Master key, if reauthentication is used
*/
char mk[HASH_SIZE_SHA1];
/**
* Counter value if reauthentication is used
*/
u_int16_t counter;
};
/* version of SIM protocol we speak */
static chunk_t version = chunk_from_chars(0x00,0x01);
/**
* Create a SIM_CLIENT_ERROR
*/
static eap_payload_t* create_client_error(private_eap_sim_peer_t *this,
u_int8_t identifier, simaka_client_error_t code)
{
simaka_message_t *message;
eap_payload_t *out;
u_int16_t encoded;
DBG1(DBG_IKE, "sending client error '%N'", simaka_client_error_names, code);
message = simaka_message_create(FALSE, identifier, EAP_SIM,
SIM_CLIENT_ERROR, this->crypto);
encoded = htons(code);
message->add_attribute(message, AT_CLIENT_ERROR_CODE,
chunk_create((char*)&encoded, sizeof(encoded)));
out = message->generate(message, chunk_empty);
message->destroy(message);
return out;
}
/**
* process an EAP-SIM/Request/Start message
*/
static status_t process_start(private_eap_sim_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, id = chunk_empty;
rng_t *rng;
bool supported = FALSE;
simaka_attribute_t id_req = 0;
/* reset previously uses reauthentication/pseudonym data */
this->crypto->clear_keys(this->crypto);
DESTROY_IF(this->pseudonym);
this->pseudonym = NULL;
DESTROY_IF(this->reauth);
this->reauth = NULL;
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_VERSION_LIST:
{
free(this->version_list.ptr);
this->version_list = chunk_clone(data);
while (data.len >= version.len)
{
if (memeq(data.ptr, version.ptr, version.len))
{
supported = TRUE;
break;
}
}
break;
}
case AT_ANY_ID_REQ:
case AT_FULLAUTH_ID_REQ:
case AT_PERMANENT_ID_REQ:
id_req = type;
break;
default:
if (!simaka_attribute_skippable(type))
{
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
enumerator->destroy(enumerator);
return NEED_MORE;
}
break;
}
}
enumerator->destroy(enumerator);
if (!supported)
{
DBG1(DBG_IKE, "server does not support EAP-SIM version number 1");
*out = create_client_error(this, in->get_identifier(in),
SIM_UNSUPPORTED_VERSION);
return NEED_MORE;
}
switch (id_req)
{
case AT_ANY_ID_REQ:
this->reauth = charon->sim->card_get_reauth(charon->sim,
this->permanent, this->mk, &this->counter);
if (this->reauth)
{
id = this->reauth->get_encoding(this->reauth);
break;
}
/* FALL */
case AT_FULLAUTH_ID_REQ:
this->pseudonym = charon->sim->card_get_pseudonym(charon->sim,
this->permanent);
if (this->pseudonym)
{
id = this->pseudonym->get_encoding(this->pseudonym);
break;
}
/* FALL */
case AT_PERMANENT_ID_REQ:
id = this->permanent->get_encoding(this->permanent);
break;
default:
break;
}
/* generate AT_NONCE_MT value */
rng = this->crypto->get_rng(this->crypto);
free(this->nonce.ptr);
rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
message = simaka_message_create(FALSE, in->get_identifier(in), EAP_SIM,
SIM_START, this->crypto);
if (!this->reauth)
{
message->add_attribute(message, AT_SELECTED_VERSION, version);
message->add_attribute(message, AT_NONCE_MT, this->nonce);
}
if (id.len)
{
message->add_attribute(message, AT_IDENTITY, id);
}
*out = message->generate(message, chunk_empty);
message->destroy(message);
return NEED_MORE;
}
/**
* process an EAP-SIM/Request/Challenge message
*/
static status_t process_challenge(private_eap_sim_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, rands = chunk_empty, kcs, kc, sreses, sres, mk;
identification_t *id;
if (this->tries-- <= 0)
{
/* give up without notification. This hack is required as some buggy
* server implementations won't respect our client-error. */
return FAILED;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_RAND:
rands = data;
break;
default:
if (!simaka_attribute_skippable(type))
{
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
enumerator->destroy(enumerator);
return NEED_MORE;
}
break;
}
}
enumerator->destroy(enumerator);
/* excepting two or three RAND, each 16 bytes. We require two valid
* and different RANDs */
if ((rands.len != 2 * SIM_RAND_LEN && rands.len != 3 * SIM_RAND_LEN) ||
memeq(rands.ptr, rands.ptr + SIM_RAND_LEN, SIM_RAND_LEN))
{
DBG1(DBG_IKE, "no valid AT_RAND received");
*out = create_client_error(this, in->get_identifier(in),
SIM_INSUFFICIENT_CHALLENGES);
return NEED_MORE;
}
/* get two or three KCs/SRESes from SIM using RANDs */
kcs = kc = chunk_alloca(rands.len / 2);
sreses = sres = chunk_alloca(rands.len / 4);
while (rands.len >= SIM_RAND_LEN)
{
if (!charon->sim->card_get_triplet(charon->sim, this->permanent,
rands.ptr, sres.ptr, kc.ptr))
{
DBG1(DBG_IKE, "unable to get EAP-SIM triplet");
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
return NEED_MORE;
}
DBG3(DBG_IKE, "got triplet for RAND %b\n Kc %b\n SRES %b",
rands.ptr, SIM_RAND_LEN, sres.ptr, SIM_SRES_LEN, kc.ptr, SIM_KC_LEN);
kc = chunk_skip(kc, SIM_KC_LEN);
sres = chunk_skip(sres, SIM_SRES_LEN);
rands = chunk_skip(rands, SIM_RAND_LEN);
}
id = this->permanent;
if (this->pseudonym)
{
id = this->pseudonym;
}
data = chunk_cata("cccc", kcs, this->nonce, this->version_list, version);
free(this->msk.ptr);
this->msk = this->crypto->derive_keys_full(this->crypto, id, data, &mk);
memcpy(this->mk, mk.ptr, mk.len);
free(mk.ptr);
/* Verify AT_MAC attribute, signature is over "EAP packet | NONCE_MT", and
* parse() again after key derivation, reading encrypted attributes */
if (!in->verify(in, this->nonce) || !in->parse(in))
{
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
return NEED_MORE;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_NEXT_REAUTH_ID:
this->counter = 0;
id = identification_create_from_data(data);
charon->sim->card_set_reauth(charon->sim, this->permanent, id,
this->mk, this->counter);
id->destroy(id);
break;
case AT_NEXT_PSEUDONYM:
id = identification_create_from_data(data);
charon->sim->card_set_pseudonym(charon->sim, this->permanent, id);
id->destroy(id);
break;
default:
break;
}
}
enumerator->destroy(enumerator);
/* build response with AT_MAC, built over "EAP packet | n*SRES" */
message = simaka_message_create(FALSE, in->get_identifier(in), EAP_SIM,
SIM_CHALLENGE, this->crypto);
*out = message->generate(message, sreses);
message->destroy(message);
return NEED_MORE;
}
/**
* Check if a received counter value is acceptable
*/
static bool counter_too_small(private_eap_sim_peer_t *this, chunk_t chunk)
{
u_int16_t counter;
memcpy(&counter, chunk.ptr, sizeof(counter));
counter = htons(counter);
return counter < this->counter;
}
/**
* process an EAP-SIM/Request/Re-Authentication message
*/
static status_t process_reauthentication(private_eap_sim_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, counter = chunk_empty, nonce = chunk_empty, id = chunk_empty;
if (!this->reauth)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, SIM_REAUTHENTICATION);
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
return NEED_MORE;
}
this->crypto->derive_keys_reauth(this->crypto,
chunk_create(this->mk, HASH_SIZE_SHA1));
/* verify MAC and parse again with decryption key */
if (!in->verify(in, chunk_empty) || !in->parse(in))
{
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
return NEED_MORE;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_COUNTER:
counter = data;
break;
case AT_NONCE_S:
nonce = data;
break;
case AT_NEXT_REAUTH_ID:
id = data;
break;
default:
if (!simaka_attribute_skippable(type))
{
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
enumerator->destroy(enumerator);
return NEED_MORE;
}
break;
}
}
enumerator->destroy(enumerator);
if (!nonce.len || !counter.len)
{
DBG1(DBG_IKE, "EAP-SIM/Request/Re-Authentication message incomplete");
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
return NEED_MORE;
}
message = simaka_message_create(FALSE, in->get_identifier(in), EAP_SIM,
SIM_REAUTHENTICATION, this->crypto);
if (counter_too_small(this, counter))
{
DBG1(DBG_IKE, "reauthentication counter too small");
message->add_attribute(message, AT_COUNTER_TOO_SMALL, chunk_empty);
}
else
{
free(this->msk.ptr);
this->msk = this->crypto->derive_keys_reauth_msk(this->crypto,
this->reauth, counter, nonce,
chunk_create(this->mk, HASH_SIZE_SHA1));
if (id.len)
{
identification_t *reauth;
reauth = identification_create_from_data(data);
charon->sim->card_set_reauth(charon->sim, this->permanent, reauth,
this->mk, this->counter);
reauth->destroy(reauth);
}
}
message->add_attribute(message, AT_COUNTER, counter);
*out = message->generate(message, nonce);
message->destroy(message);
return NEED_MORE;
}
/**
* process an EAP-SIM/Request/Notification message
*/
static status_t process_notification(private_eap_sim_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-SIM notification error '%N'",
simaka_notification_names, code);
}
else
{
DBG1(DBG_IKE, "received EAP-SIM 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_SIM,
SIM_NOTIFICATION, this->crypto);
*out = message->generate(message, chunk_empty);
message->destroy(message);
}
else
{
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
}
return NEED_MORE;
}
/**
* Implementation of eap_method_t.process
*/
static status_t process(private_eap_sim_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, this->crypto);
if (!message)
{
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
return NEED_MORE;
}
if (!message->parse(message))
{
message->destroy(message);
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
return NEED_MORE;
}
switch (message->get_subtype(message))
{
case SIM_START:
status = process_start(this, message, out);
break;
case SIM_CHALLENGE:
status = process_challenge(this, message, out);
break;
case SIM_REAUTHENTICATION:
status = process_reauthentication(this, message, out);
break;
case SIM_NOTIFICATION:
status = process_notification(this, message, out);
break;
default:
DBG1(DBG_IKE, "unable to process EAP-SIM subtype %N",
simaka_subtype_names, message->get_subtype(message));
*out = create_client_error(this, in->get_identifier(in),
SIM_UNABLE_TO_PROCESS);
status = NEED_MORE;
break;
}
message->destroy(message);
return status;
}
/**
* Implementation of eap_method_t.initiate
*/
static status_t initiate(private_eap_sim_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_sim_peer_t *this, u_int32_t *vendor)
{
*vendor = 0;
return EAP_SIM;
}
/**
* Implementation of eap_method_t.get_msk.
*/
static status_t get_msk(private_eap_sim_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_sim_peer_t *this)
{
return TRUE;
}
/**
* Implementation of eap_method_t.destroy.
*/
static void destroy(private_eap_sim_peer_t *this)
{
this->permanent->destroy(this->permanent);
DESTROY_IF(this->pseudonym);
DESTROY_IF(this->reauth);
this->crypto->destroy(this->crypto);
free(this->version_list.ptr);
free(this->nonce.ptr);
free(this->msk.ptr);
free(this);
}
/*
* Described in header.
*/
eap_sim_peer_t *eap_sim_peer_create(identification_t *server,
identification_t *peer)
{
private_eap_sim_peer_t *this = malloc_thing(private_eap_sim_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->permanent = peer->clone(peer);
this->pseudonym = NULL;
this->reauth = NULL;
this->tries = MAX_TRIES;
this->version_list = chunk_empty;
this->nonce = chunk_empty;
this->msk = chunk_empty;
return &this->public;
}
@@ -0,0 +1,57 @@
/*
* Copyright (C) 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_sim_peer eap_sim_peer
* @{ @ingroup eap_sim
*/
#ifndef EAP_SIM_PEER_H_
#define EAP_SIM_PEER_H_
#include <sa/authenticators/eap/eap_method.h>
typedef struct eap_sim_peer_t eap_sim_peer_t;
/**
* EAP-SIM peer implementation.
*
* This EAP-SIM module uses sim_card_t implementations for triplet calculation,
* found via the eap_sim_manager_t.
*/
struct eap_sim_peer_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t interface;
/**
* Destroy a eap_sim_peer_t.
*/
void (*destroy)(eap_sim_peer_t *this);
};
/**
* Creates the EAP method EAP-SIM acting as peer.
*
* @param server ID of the EAP server
* @param peer ID of the EAP peer
* @return eap_sim_t object
*/
eap_sim_peer_t *eap_sim_peer_create(identification_t *server,
identification_t *peer);
#endif /** EAP_SIM_PEER_H_ @}*/
@@ -0,0 +1,51 @@
/*
* 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.
*/
#include "eap_sim_plugin.h"
#include "eap_sim_server.h"
#include "eap_sim_peer.h"
#include <daemon.h>
/**
* Implementation of plugin_t.destroy
*/
static void destroy(eap_sim_plugin_t *this)
{
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_sim_server_create);
charon->eap->remove_method(charon->eap,
(eap_constructor_t)eap_sim_peer_create);
free(this);
}
/*
* see header file
*/
plugin_t *eap_sim_plugin_create()
{
eap_sim_plugin_t *this = malloc_thing(eap_sim_plugin_t);
this->plugin.destroy = (void(*)(plugin_t*))destroy;
charon->eap->add_method(charon->eap, EAP_SIM, 0, EAP_SERVER,
(eap_constructor_t)eap_sim_server_create);
charon->eap->add_method(charon->eap, EAP_SIM, 0, EAP_PEER,
(eap_constructor_t)eap_sim_peer_create);
return &this->plugin;
}
@@ -0,0 +1,42 @@
/*
* 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.
*/
/**
* @defgroup eap_sim eap_sim
* @ingroup cplugins
*
* @defgroup eap_sim_plugin eap_sim_plugin
* @{ @ingroup eap_sim
*/
#ifndef EAP_SIM_PLUGIN_H_
#define EAP_SIM_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_sim_plugin_t eap_sim_plugin_t;
/**
* EAP-SIM plugin.
*/
struct eap_sim_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_SIM_PLUGIN_H_ @}*/
@@ -0,0 +1,611 @@
/*
* Copyright (C) 2007-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_sim_server.h"
#include <daemon.h>
#include <simaka_message.h>
#include <simaka_crypto.h>
/* number of triplets for one authentication */
#define TRIPLET_COUNT 3
/** length of the AT_NONCE_S value */
#define NONCE_LEN 16
typedef struct private_eap_sim_server_t private_eap_sim_server_t;
/**
* Private data of an eap_sim_server_t object.
*/
struct private_eap_sim_server_t {
/**
* Public authenticator_t interface.
*/
eap_sim_server_t public;
/**
* permanent ID of peer
*/
identification_t *permanent;
/**
* pseudonym ID of peer
*/
identification_t *pseudonym;
/**
* reauthentication ID of peer
*/
identification_t *reauth;
/**
* EAP-SIM/AKA crypto helper
*/
simaka_crypto_t *crypto;
/**
* unique EAP identifier
*/
u_int8_t identifier;
/**
* concatenated SRES values
*/
chunk_t sreses;
/**
* Nonce value used in AT_NONCE_S
*/
chunk_t nonce;
/**
* Counter value negotiated, network order
*/
chunk_t counter;
/**
* MSK, used for EAP-SIM based IKEv2 authentication
*/
chunk_t msk;
/**
* Do we request fast reauthentication?
*/
bool use_reauth;
/**
* Do we request pseudonym identities?
*/
bool use_pseudonym;
/**
* Do we request permanent identities?
*/
bool use_permanent;
/**
* EAP-SIM message we have initiated
*/
simaka_subtype_t pending;
};
/* version of SIM protocol we speak */
static chunk_t version = chunk_from_chars(0x00,0x01);
/**
* Implementation of eap_method_t.initiate
*/
static status_t initiate(private_eap_sim_server_t *this, eap_payload_t **out)
{
simaka_message_t *message;
message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
SIM_START, this->crypto);
message->add_attribute(message, AT_VERSION_LIST, version);
if (this->use_reauth)
{
message->add_attribute(message, AT_ANY_ID_REQ, chunk_empty);
}
else if (this->use_pseudonym)
{
message->add_attribute(message, AT_FULLAUTH_ID_REQ, chunk_empty);
}
else if (this->use_permanent)
{
message->add_attribute(message, AT_PERMANENT_ID_REQ, chunk_empty);
}
*out = message->generate(message, chunk_empty);
message->destroy(message);
this->pending = SIM_START;
return NEED_MORE;
}
/**
* Initiate EAP-SIM/Request/Re-authentication message
*/
static status_t reauthenticate(private_eap_sim_server_t *this,
char mk[HASH_SIZE_SHA1], u_int16_t counter,
eap_payload_t **out)
{
simaka_message_t *message;
identification_t *next;
chunk_t mkc;
rng_t *rng;
DBG1(DBG_IKE, "initiating EAP-SIM reauthentication");
rng = this->crypto->get_rng(this->crypto);
rng->allocate_bytes(rng, NONCE_LEN, &this->nonce);
mkc = chunk_create(mk, HASH_SIZE_SHA1);
counter = htons(counter);
this->counter = chunk_clone(chunk_create((char*)&counter, sizeof(counter)));
this->crypto->derive_keys_reauth(this->crypto, mkc);
this->msk = this->crypto->derive_keys_reauth_msk(this->crypto,
this->reauth, this->counter, this->nonce, mkc);
message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
SIM_REAUTHENTICATION, this->crypto);
message->add_attribute(message, AT_COUNTER, this->counter);
message->add_attribute(message, AT_NONCE_S, this->nonce);
next = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk);
if (next)
{
message->add_attribute(message, AT_NEXT_REAUTH_ID,
next->get_encoding(next));
next->destroy(next);
}
*out = message->generate(message, chunk_empty);
message->destroy(message);
this->pending = SIM_REAUTHENTICATION;
return NEED_MORE;
}
/**
* process an EAP-SIM/Response/Reauthentication message
*/
static status_t process_reauthentication(private_eap_sim_server_t *this,
simaka_message_t *in, eap_payload_t **out)
{
enumerator_t *enumerator;
simaka_attribute_t type;
chunk_t data, counter = chunk_empty;
bool too_small = FALSE;
if (this->pending != SIM_REAUTHENTICATION)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, SIM_REAUTHENTICATION);
return FAILED;
}
/* verify AT_MAC attribute, signature is over "EAP packet | NONCE_S" */
if (!in->verify(in, this->nonce))
{
return FAILED;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_COUNTER:
counter = data;
break;
case AT_COUNTER_TOO_SMALL:
too_small = TRUE;
break;
default:
if (!simaka_attribute_skippable(type))
{
enumerator->destroy(enumerator);
return FAILED;
}
break;
}
}
enumerator->destroy(enumerator);
if (too_small)
{
DBG1(DBG_IKE, "received %N, initiating full authentication",
simaka_attribute_names, AT_COUNTER_TOO_SMALL);
this->use_reauth = FALSE;
this->crypto->clear_keys(this->crypto);
return initiate(this, out);
}
if (!chunk_equals(counter, this->counter))
{
DBG1(DBG_IKE, "received counter does not match");
return FAILED;
}
return SUCCESS;
}
/**
* process an EAP-SIM/Response/Start message
*/
static status_t process_start(private_eap_sim_server_t *this,
simaka_message_t *in, eap_payload_t **out)
{
simaka_message_t *message;
enumerator_t *enumerator;
simaka_attribute_t type;
chunk_t data, identity = chunk_empty, nonce = chunk_empty, mk;
chunk_t rands, rand, kcs, kc, sreses, sres;
bool supported = FALSE;
identification_t *id;
int i;
if (this->pending != SIM_START)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, SIM_START);
return FAILED;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
switch (type)
{
case AT_NONCE_MT:
nonce = data;
break;
case AT_SELECTED_VERSION:
if (chunk_equals(data, version))
{
supported = TRUE;
}
break;
case AT_IDENTITY:
identity = data;
break;
default:
if (!simaka_attribute_skippable(type))
{
enumerator->destroy(enumerator);
return FAILED;
}
break;
}
}
enumerator->destroy(enumerator);
if (identity.len)
{
identification_t *permanent;
id = identification_create_from_data(identity);
if (this->use_reauth && !nonce.len)
{
char mk[HASH_SIZE_SHA1];
u_int16_t counter;
permanent = charon->sim->provider_is_reauth(charon->sim, id,
mk, &counter);
if (permanent)
{
this->permanent->destroy(this->permanent);
this->permanent = permanent;
this->reauth = id;
return reauthenticate(this, mk, counter, out);
}
DBG1(DBG_IKE, "received unknown reauthentication identity '%Y', "
"initiating full authentication", id);
this->use_reauth = FALSE;
id->destroy(id);
return initiate(this, out);
}
if (this->use_pseudonym)
{
permanent = charon->sim->provider_is_pseudonym(charon->sim, id);
if (permanent)
{
this->permanent->destroy(this->permanent);
this->permanent = permanent;
this->pseudonym = id->clone(id);
/* we already have a new permanent identity now */
this->use_permanent = FALSE;
}
}
if (!this->pseudonym && this->use_permanent)
{
DBG1(DBG_IKE, "received %spermanent identity '%Y'",
this->use_pseudonym ? "pseudonym or " : "", id);
this->permanent->destroy(this->permanent);
this->permanent = id->clone(id);
}
id->destroy(id);
}
if (!supported || !nonce.len)
{
DBG1(DBG_IKE, "received incomplete EAP-SIM/Response/Start");
return FAILED;
}
/* read triplets from provider */
rand = rands = chunk_alloca(SIM_RAND_LEN * TRIPLET_COUNT);
kc = kcs = chunk_alloca(SIM_KC_LEN * TRIPLET_COUNT);
sres = sreses = chunk_alloca(SIM_SRES_LEN * TRIPLET_COUNT);
rands.len = kcs.len = sreses.len = 0;
for (i = 0; i < TRIPLET_COUNT; i++)
{
if (!charon->sim->provider_get_triplet(charon->sim, this->permanent,
rand.ptr, sres.ptr, kc.ptr))
{
if (this->use_pseudonym)
{
/* probably received a pseudonym we couldn't map */
DBG1(DBG_IKE, "failed to map pseudonym identity '%Y', "
"fallback to permanent identity request", this->permanent);
this->use_pseudonym = FALSE;
DESTROY_IF(this->pseudonym);
this->pseudonym = NULL;
return initiate(this, out);
}
return FAILED;
}
rands.len += SIM_RAND_LEN;
sreses.len += SIM_SRES_LEN;
kcs.len += SIM_KC_LEN;
rand = chunk_skip(rand, SIM_RAND_LEN);
sres = chunk_skip(sres, SIM_SRES_LEN);
kc = chunk_skip(kc, SIM_KC_LEN);
}
free(this->sreses.ptr);
this->sreses = chunk_clone(sreses);
data = chunk_cata("cccc", kcs, nonce, version, version);
free(this->msk.ptr);
id = this->permanent;
if (this->pseudonym)
{
id = this->pseudonym;
}
this->msk = this->crypto->derive_keys_full(this->crypto, id, data, &mk);
/* build response with AT_MAC, built over "EAP packet | NONCE_MT" */
message = simaka_message_create(TRUE, this->identifier++, EAP_SIM,
SIM_CHALLENGE, this->crypto);
message->add_attribute(message, AT_RAND, rands);
id = charon->sim->provider_gen_reauth(charon->sim, this->permanent, mk.ptr);
if (id)
{
message->add_attribute(message, AT_NEXT_REAUTH_ID,
id->get_encoding(id));
id->destroy(id);
}
else
{
id = charon->sim->provider_gen_pseudonym(charon->sim, this->permanent);
if (id)
{
message->add_attribute(message, AT_NEXT_PSEUDONYM,
id->get_encoding(id));
id->destroy(id);
}
}
*out = message->generate(message, nonce);
message->destroy(message);
free(mk.ptr);
this->pending = SIM_CHALLENGE;
return NEED_MORE;
}
/**
* process an EAP-SIM/Response/Challenge message
*/
static status_t process_challenge(private_eap_sim_server_t *this,
simaka_message_t *in, eap_payload_t **out)
{
enumerator_t *enumerator;
simaka_attribute_t type;
chunk_t data;
if (this->pending != SIM_CHALLENGE)
{
DBG1(DBG_IKE, "received %N, but not expected",
simaka_subtype_names, SIM_CHALLENGE);
return FAILED;
}
/* verify AT_MAC attribute, signature is over "EAP packet | n*SRES" */
if (!in->verify(in, this->sreses))
{
return FAILED;
}
enumerator = in->create_attribute_enumerator(in);
while (enumerator->enumerate(enumerator, &type, &data))
{
if (!simaka_attribute_skippable(type))
{
enumerator->destroy(enumerator);
return FAILED;
}
}
enumerator->destroy(enumerator);
return SUCCESS;
}
/**
* EAP-SIM/Response/ClientErrorCode message
*/
static status_t process_client_error(private_eap_sim_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-SIM client error '%N'",
simaka_client_error_names, ntohs(code));
}
else if (!simaka_attribute_skippable(type))
{
break;
}
}
enumerator->destroy(enumerator);
return FAILED;
}
/**
* Implementation of eap_method_t.process
*/
static status_t process(private_eap_sim_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, this->crypto);
if (!message)
{
return FAILED;
}
if (!message->parse(message))
{
message->destroy(message);
return FAILED;
}
switch (message->get_subtype(message))
{
case SIM_START:
status = process_start(this, message, out);
break;
case SIM_CHALLENGE:
status = process_challenge(this, message, out);
break;
case SIM_REAUTHENTICATION:
status = process_reauthentication(this, message, out);
break;
case SIM_CLIENT_ERROR:
status = process_client_error(this, message);
break;
default:
DBG1(DBG_IKE, "unable to process EAP-SIM 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_sim_server_t *this, u_int32_t *vendor)
{
*vendor = 0;
return EAP_SIM;
}
/**
* Implementation of eap_method_t.get_msk.
*/
static status_t get_msk(private_eap_sim_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_sim_server_t *this)
{
return TRUE;
}
/**
* Implementation of eap_method_t.destroy.
*/
static void destroy(private_eap_sim_server_t *this)
{
this->crypto->destroy(this->crypto);
this->permanent->destroy(this->permanent);
DESTROY_IF(this->pseudonym);
DESTROY_IF(this->reauth);
free(this->sreses.ptr);
free(this->nonce.ptr);
free(this->msk.ptr);
free(this->counter.ptr);
free(this);
}
/*
* Described in header.
*/
eap_sim_server_t *eap_sim_server_create(identification_t *server,
identification_t *peer)
{
private_eap_sim_server_t *this = malloc_thing(private_eap_sim_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->permanent = peer->clone(peer);
this->pseudonym = NULL;
this->reauth = NULL;
this->sreses = chunk_empty;
this->nonce = chunk_empty;
this->msk = chunk_empty;
this->counter = chunk_empty;
this->pending = 0;
this->use_reauth = this->use_pseudonym = this->use_permanent =
lib->settings->get_bool(lib->settings,
"charon.plugins.eap-sim.request_identity", TRUE);
/* generate a non-zero identifier */
do {
this->identifier = random();
} while (!this->identifier);
return &this->public;
}
@@ -0,0 +1,57 @@
/*
* Copyright (C) 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_sim_server eap_sim_server
* @{ @ingroup eap_sim
*/
#ifndef EAP_SIM_SERVER_H_
#define EAP_SIM_SERVER_H_
#include <sa/authenticators/eap/eap_method.h>
typedef struct eap_sim_server_t eap_sim_server_t;
/**
* EAP-SIM server implementation.
*
* This EAP-SIM module uses sim_provider_t implementations for triplet
* calculation, found via the eap_sim_manager_t.
*/
struct eap_sim_server_t {
/**
* Implemented eap_method_t interface.
*/
eap_method_t interface;
/**
* Destroy a eap_sim_server_t.
*/
void (*destroy)(eap_sim_server_t *this);
};
/**
* Creates the EAP method EAP-SIM acting as server.
*
* @param server ID of the EAP server
* @param peer ID of the EAP peer
* @return eap_sim_t object
*/
eap_sim_server_t *eap_sim_server_create(identification_t *server,
identification_t *peer);
#endif /** EAP_SIM_SERVER_H_ @}*/
@@ -0,0 +1,18 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic -DIPSEC_CONFDIR=\"${sysconfdir}\"
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-sim-file.la
else
plugin_LTLIBRARIES = libstrongswan-eap-sim-file.la
endif
libstrongswan_eap_sim_file_la_SOURCES = \
eap_sim_file_plugin.h eap_sim_file_plugin.c \
eap_sim_file_card.h eap_sim_file_card.c \
eap_sim_file_provider.h eap_sim_file_provider.c \
eap_sim_file_triplets.h eap_sim_file_triplets.c
libstrongswan_eap_sim_file_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,107 @@
/*
* 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.
*/
#include "eap_sim_file_card.h"
#include <daemon.h>
typedef struct private_eap_sim_file_card_t private_eap_sim_file_card_t;
/**
* Private data of an eap_sim_file_card_t object.
*/
struct private_eap_sim_file_card_t {
/**
* Public eap_sim_file_card_t interface.
*/
eap_sim_file_card_t public;
/**
* source of triplets
*/
eap_sim_file_triplets_t *triplets;
};
/**
* Implementation of sim_card_t.get_triplet
*/
static bool get_triplet(private_eap_sim_file_card_t *this,
identification_t *id, char *rand, char *sres, char *kc)
{
enumerator_t *enumerator;
identification_t *cand;
char *c_rand, *c_sres, *c_kc;
DBG2(DBG_CFG, "looking for triplet: %Y rand %b", id, rand, SIM_RAND_LEN);
enumerator = this->triplets->create_enumerator(this->triplets);
while (enumerator->enumerate(enumerator, &cand, &c_rand, &c_sres, &c_kc))
{
DBG2(DBG_CFG, "got a triplet: %Y rand %b\nsres %b\n kc %b", cand,
c_rand, SIM_RAND_LEN, c_sres, SIM_SRES_LEN, c_kc, SIM_KC_LEN);
if (id->matches(id, cand))
{
if (memeq(c_rand, rand, SIM_RAND_LEN))
{
DBG2(DBG_CFG, " => triplet matches");
memcpy(sres, c_sres, SIM_SRES_LEN);
memcpy(kc, c_kc, SIM_KC_LEN);
enumerator->destroy(enumerator);
return TRUE;
}
}
}
enumerator->destroy(enumerator);
return FALSE;
}
/**
* Implementation of sim_card_t.get_quintuplet
*/
static status_t get_quintuplet()
{
return NOT_SUPPORTED;
}
/**
* Implementation of eap_sim_file_card_t.destroy.
*/
static void destroy(private_eap_sim_file_card_t *this)
{
free(this);
}
/**
* See header
*/
eap_sim_file_card_t *eap_sim_file_card_create(eap_sim_file_triplets_t *triplets)
{
private_eap_sim_file_card_t *this = malloc_thing(private_eap_sim_file_card_t);
this->public.card.get_triplet = (bool(*)(sim_card_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))get_triplet;
this->public.card.get_quintuplet = (status_t(*)(sim_card_t*, 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))get_quintuplet;
this->public.card.resync = (bool(*)(sim_card_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))return_false;
this->public.card.get_pseudonym = (identification_t*(*)(sim_card_t*, identification_t *perm))return_null;
this->public.card.set_pseudonym = (void(*)(sim_card_t*, identification_t *id, identification_t *pseudonym))nop;
this->public.card.get_reauth = (identification_t*(*)(sim_card_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.card.set_reauth = (void(*)(sim_card_t*, identification_t *id, identification_t* next, char mk[HASH_SIZE_SHA1], u_int16_t counter))nop;
this->public.destroy = (void(*)(eap_sim_file_card_t*))destroy;
this->triplets = triplets;
return &this->public;
}
@@ -0,0 +1,53 @@
/*
* 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.
*/
/**
* @defgroup eap_sim_file_card eap_sim_file_card
* @{ @ingroup eap_sim_file
*/
#ifndef EAP_SIM_FILE_CARD_H_
#define EAP_SIM_FILE_CARD_H_
#include "eap_sim_file_triplets.h"
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_sim_file_card_t eap_sim_file_card_t;
/**
* SIM card implementation on top of a triplet file.
*/
struct eap_sim_file_card_t {
/**
* Implements sim_card_t interface
*/
sim_card_t card;
/**
* Destroy a eap_sim_file_card_t.
*/
void (*destroy)(eap_sim_file_card_t *this);
};
/**
* Create a eap_sim_file_card instance.
*
* @param triplets source of triplets
*/
eap_sim_file_card_t *eap_sim_file_card_create(eap_sim_file_triplets_t *triplets);
#endif /** EAP_SIM_FILE_CARD_H_ @}*/
@@ -0,0 +1,90 @@
/*
* 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 "eap_sim_file_plugin.h"
#include "eap_sim_file_card.h"
#include "eap_sim_file_provider.h"
#include "eap_sim_file_triplets.h"
#include <daemon.h>
#define TRIPLET_FILE IPSEC_CONFDIR "/ipsec.d/triplets.dat"
typedef struct private_eap_sim_file_t private_eap_sim_file_t;
/**
* Private data of an eap_sim_file_t object.
*/
struct private_eap_sim_file_t {
/**
* Public eap_sim_file_plugin_t interface.
*/
eap_sim_file_plugin_t public;
/**
* SIM card
*/
eap_sim_file_card_t *card;
/**
* SIM provider
*/
eap_sim_file_provider_t *provider;
/**
* Triplet source
*/
eap_sim_file_triplets_t *triplets;
};
/**
* Implementation of eap_sim_file_t.destroy.
*/
static void destroy(private_eap_sim_file_t *this)
{
charon->sim->remove_card(charon->sim, &this->card->card);
charon->sim->remove_provider(charon->sim, &this->provider->provider);
this->card->destroy(this->card);
this->provider->destroy(this->provider);
this->triplets->destroy(this->triplets);
free(this);
}
/**
* See header
*/
plugin_t *eap_sim_file_plugin_create()
{
private_eap_sim_file_t *this = malloc_thing(private_eap_sim_file_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
this->triplets = eap_sim_file_triplets_create(TRIPLET_FILE);
this->provider = eap_sim_file_provider_create(this->triplets);
if (!this->provider)
{
this->triplets->destroy(this->triplets);
free(this);
return NULL;
}
this->card = eap_sim_file_card_create(this->triplets);
charon->sim->add_card(charon->sim, &this->card->card);
charon->sim->add_provider(charon->sim, &this->provider->provider);
return &this->public.plugin;
}
@@ -0,0 +1,42 @@
/*
* 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.
*/
/**
* @defgroup eap_sim_file eap_sim_file
* @ingroup cplugins
*
* @defgroup eap_sim_file_plugin eap_sim_file_plugin
* @{ @ingroup eap_sim_file
*/
#ifndef EAP_SIM_FILE_PLUGIN_H_
#define EAP_SIM_FILE_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_sim_file_plugin_t eap_sim_file_plugin_t;
/**
* Plugin to provide a SIM card/provider on top of a triplet file.
*/
struct eap_sim_file_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_SIM_FILE_PLUGIN_H_ @}*/
@@ -0,0 +1,93 @@
/*
* 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.
*/
#include "eap_sim_file_provider.h"
#include <daemon.h>
typedef struct private_eap_sim_file_provider_t private_eap_sim_file_provider_t;
/**
* Private data of an eap_sim_file_provider_t object.
*/
struct private_eap_sim_file_provider_t {
/**
* Public eap_sim_file_provider_t interface.
*/
eap_sim_file_provider_t public;
/**
* source of triplets
*/
eap_sim_file_triplets_t *triplets;
};
/**
* Implementation of sim_provider_t.get_triplet
*/
static bool get_triplet(private_eap_sim_file_provider_t *this,
identification_t *id, char *rand, char *sres, char *kc)
{
enumerator_t *enumerator;
identification_t *cand;
char *c_rand, *c_sres, *c_kc;
enumerator = this->triplets->create_enumerator(this->triplets);
while (enumerator->enumerate(enumerator, &cand, &c_rand, &c_sres, &c_kc))
{
if (id->matches(id, cand))
{
memcpy(rand, c_rand, SIM_RAND_LEN);
memcpy(sres, c_sres, SIM_SRES_LEN);
memcpy(kc, c_kc, SIM_KC_LEN);
enumerator->destroy(enumerator);
return TRUE;
}
}
enumerator->destroy(enumerator);
return FALSE;
}
/**
* Implementation of eap_sim_file_provider_t.destroy.
*/
static void destroy(private_eap_sim_file_provider_t *this)
{
free(this);
}
/**
* See header
*/
eap_sim_file_provider_t *eap_sim_file_provider_create(
eap_sim_file_triplets_t *triplets)
{
private_eap_sim_file_provider_t *this = malloc_thing(private_eap_sim_file_provider_t);
this->public.provider.get_triplet = (bool(*)(sim_provider_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))get_triplet;
this->public.provider.get_quintuplet = (bool(*)(sim_provider_t*, 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]))return_false;
this->public.provider.resync = (bool(*)(sim_provider_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))return_false;
this->public.provider.is_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.gen_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.is_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char [HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.provider.gen_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char mk[HASH_SIZE_SHA1]))return_null;
this->public.destroy = (void(*)(eap_sim_file_provider_t*))destroy;
this->triplets = triplets;
return &this->public;
}
@@ -0,0 +1,50 @@
/*
* 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.
*/
/**
* @defgroup eap_sim_file_provider eap_sim_file_provider
* @{ @ingroup eap_sim_file
*/
#ifndef EAP_SIM_FILE_PROVIDER_H_
#define EAP_SIM_FILE_PROVIDER_H_
#include "eap_sim_file_triplets.h"
typedef struct eap_sim_file_provider_t eap_sim_file_provider_t;
/**
* SIM provider implementation on top of triplets file.
*/
struct eap_sim_file_provider_t {
/**
* Implements sim_provider_t interface.
*/
sim_provider_t provider;
/**
* Destroy a eap_sim_file_provider_t.
*/
void (*destroy)(eap_sim_file_provider_t *this);
};
/**
* Create a eap_sim_file_provider instance.
*/
eap_sim_file_provider_t *eap_sim_file_provider_create(
eap_sim_file_triplets_t *triplets);
#endif /** EAP_SIM_FILE_PROVIDER_H_ @}*/
@@ -0,0 +1,260 @@
/*
* 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 "eap_sim_file_triplets.h"
#include <stdio.h>
#include <errno.h>
#include <daemon.h>
#include <utils/linked_list.h>
#include <threading/mutex.h>
typedef struct private_eap_sim_file_triplets_t private_eap_sim_file_triplets_t;
/**
* Private data of an eap_sim_file_triplets_t object.
*/
struct private_eap_sim_file_triplets_t {
/**
* Public eap_sim_file_triplets_t interface.
*/
eap_sim_file_triplets_t public;
/**
* List of triplets, as triplet_t
*/
linked_list_t *triplets;
/**
* mutex to lock triplets list
*/
mutex_t *mutex;
};
/**
* A single triplet
*/
typedef struct {
identification_t *imsi;
char rand[SIM_RAND_LEN];
char sres[SIM_SRES_LEN];
char kc[SIM_KC_LEN];
} triplet_t;
/**
* Destroy a triplet
*/
static void triplet_destroy(triplet_t *this)
{
DESTROY_IF(this->imsi);
free(this);
}
/**
* triplet enumerator
*/
typedef struct {
/** implements enumerator */
enumerator_t public;
/** inner enumerator */
enumerator_t *inner;
/** current enumerating triplet */
triplet_t *current;
/** back ptr */
private_eap_sim_file_triplets_t *this;
} triplet_enumerator_t;
/**
* destroy a triplet enumerator
*/
static void enumerator_destroy(triplet_enumerator_t *e)
{
if (e->current)
{
/* We assume that the current element is used on invocation if destroy.
* We move that triplet to the end to avoid handout of the same triplet
* next time. */
e->this->triplets->remove_at(e->this->triplets, e->inner);
e->this->triplets->insert_last(e->this->triplets, e->current);
}
e->inner->destroy(e->inner);
e->this->mutex->unlock(e->this->mutex);
free(e);
}
/**
* enumerate through triplets
*/
static bool enumerator_enumerate(triplet_enumerator_t *e, identification_t **imsi,
char **rand, char **sres, char **kc)
{
triplet_t *triplet;
if (e->inner->enumerate(e->inner, &triplet))
{
e->current = triplet;
*imsi = triplet->imsi;
*rand = triplet->rand;
*sres = triplet->sres;
*kc = triplet->kc;
return TRUE;
}
e->current = NULL;
return FALSE;
}
/**
* Implementation of eap_sim_file_triplets_t.create_enumerator
*/
static enumerator_t* create_enumerator(private_eap_sim_file_triplets_t *this)
{
triplet_enumerator_t *enumerator = malloc_thing(triplet_enumerator_t);
this->mutex->lock(this->mutex);
enumerator->public.enumerate = (void*)enumerator_enumerate;
enumerator->public.destroy = (void*)enumerator_destroy;
enumerator->inner = this->triplets->create_enumerator(this->triplets);
enumerator->current = NULL;
enumerator->this = this;
return &enumerator->public;
}
/**
* convert to token into the array
*/
static void parse_token(char *to, char *from, size_t len)
{
chunk_t chunk;
chunk = chunk_create(from, min(strlen(from), len * 2));
chunk = chunk_from_hex(chunk, NULL);
memset(to, 0, len);
memcpy(to + len - chunk.len, chunk.ptr, chunk.len);
free(chunk.ptr);
}
/**
* Read the triplets from the file
*/
static void read_triplets(private_eap_sim_file_triplets_t *this, char *path)
{
char line[512];
FILE *file;
int i, nr = 0;
file = fopen(path, "r");
if (file == NULL)
{
DBG1(DBG_CFG, "opening triplet file %s failed: %s",
path, strerror(errno));
return;
}
/* read line by line */
while (fgets(line, sizeof(line), file))
{
triplet_t *triplet;
enumerator_t *enumerator;
char *token;
nr++;
/* skip comments, empty lines */
switch (line[0])
{
case '\n':
case '\r':
case '#':
case '\0':
continue;
default:
break;
}
triplet = malloc_thing(triplet_t);
memset(triplet, 0, sizeof(triplet_t));
i = 0;
enumerator = enumerator_create_token(line, ",", " \n\r#");
while (enumerator->enumerate(enumerator, &token))
{
switch (i++)
{
case 0: /* IMSI */
triplet->imsi = identification_create_from_string(token);
continue;
case 1: /* rand */
parse_token(triplet->rand, token, SIM_RAND_LEN);
continue;
case 2: /* sres */
parse_token(triplet->sres, token, SIM_SRES_LEN);
continue;
case 3: /* kc */
parse_token(triplet->kc, token, SIM_KC_LEN);
continue;
default:
break;;
}
break;
}
enumerator->destroy(enumerator);
if (i < 4)
{
DBG1(DBG_CFG, "error in triplet file, line %d", nr);
triplet_destroy(triplet);
continue;
}
DBG2(DBG_CFG, "triplet: imsi %Y\nrand %b\nsres %b\nkc %b",
triplet->imsi, triplet->rand, SIM_RAND_LEN,
triplet->sres, SIM_SRES_LEN, triplet->kc, SIM_KC_LEN);
this->triplets->insert_last(this->triplets, triplet);
}
fclose(file);
DBG1(DBG_CFG, "read %d triplets from %s",
this->triplets->get_count(this->triplets), path);
}
/**
* Implementation of eap_sim_file_triplets_t.destroy.
*/
static void destroy(private_eap_sim_file_triplets_t *this)
{
this->triplets->destroy_function(this->triplets, (void*)triplet_destroy);
this->mutex->destroy(this->mutex);
free(this);
}
/**
* See header
*/
eap_sim_file_triplets_t *eap_sim_file_triplets_create(char *file)
{
private_eap_sim_file_triplets_t *this = malloc_thing(private_eap_sim_file_triplets_t);
this->public.create_enumerator = (enumerator_t*(*)(eap_sim_file_triplets_t*))create_enumerator;
this->public.destroy = (void(*)(eap_sim_file_triplets_t*))destroy;
this->triplets = linked_list_create();
this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
read_triplets(this, file);
return &this->public;
}
@@ -0,0 +1,56 @@
/*
* 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.
*/
/**
* @defgroup eap_sim_file_triplets eap_sim_file_triplets
* @{ @ingroup eap_sim_file
*/
#ifndef EAP_SIM_FILE_TRIPLETS_H_
#define EAP_SIM_FILE_TRIPLETS_H_
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_sim_file_triplets_t eap_sim_file_triplets_t;
/**
* Reads triplets from a triplets.dat file.
*
* The file is in freeradius triplet file syntax:
* http://www.freeradius.org/radiusd/doc/rlm_sim_triplets
*/
struct eap_sim_file_triplets_t {
/**
* Create an enumerator over the file's triplets.
*
* @return enumerator over (identity, rand, sres, kc)
*/
enumerator_t* (*create_enumerator)(eap_sim_file_triplets_t *this);
/**
* Destroy a eap_sim_file_triplets_t.
*/
void (*destroy)(eap_sim_file_triplets_t *this);
};
/**
* Create a eap_sim_file_triplets instance.
*
* @param file triplet file to read from
*/
eap_sim_file_triplets_t *eap_sim_file_triplets_create(char *file);
#endif /** EAP_SIM_FILE_TRIPLETS_H_ @}*/
@@ -0,0 +1,17 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-simaka-pseudonym.la
else
plugin_LTLIBRARIES = libstrongswan-eap-simaka-pseudonym.la
endif
libstrongswan_eap_simaka_pseudonym_la_SOURCES = \
eap_simaka_pseudonym_plugin.h eap_simaka_pseudonym_plugin.c \
eap_simaka_pseudonym_card.h eap_simaka_pseudonym_card.c \
eap_simaka_pseudonym_provider.h eap_simaka_pseudonym_provider.c
libstrongswan_eap_simaka_pseudonym_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,154 @@
/*
* Copyright (C) 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_simaka_pseudonym_card.h"
#include <daemon.h>
#include <utils/hashtable.h>
typedef struct private_eap_simaka_pseudonym_card_t private_eap_simaka_pseudonym_card_t;
/**
* Private data of an eap_simaka_pseudonym_card_t object.
*/
struct private_eap_simaka_pseudonym_card_t {
/**
* Public eap_simaka_pseudonym_card_t interface.
*/
eap_simaka_pseudonym_card_t public;
/**
* Permanent -> pseudonym mappings
*/
hashtable_t *pseudonym;
/**
* Reverse pseudonym -> permanent mappings
*/
hashtable_t *permanent;
};
/**
* hashtable hash function
*/
static u_int hash(identification_t *key)
{
return chunk_hash(key->get_encoding(key));
}
/**
* hashtable equals function
*/
static bool equals(identification_t *key1, identification_t *key2)
{
return key1->equals(key1, key2);
}
/**
* Implementation of sim_card_t.get_pseudonym
*/
static identification_t *get_pseudonym(private_eap_simaka_pseudonym_card_t *this,
identification_t *id)
{
identification_t *pseudonym;
pseudonym = this->pseudonym->get(this->pseudonym, id);
if (pseudonym)
{
return pseudonym->clone(pseudonym);
}
return NULL;
}
/**
* Implementation of sim_card_t.set_pseudonym
*/
static void set_pseudonym(private_eap_simaka_pseudonym_card_t *this,
identification_t *id, identification_t *pseudonym)
{
identification_t *permanent;
/* create new entries */
id = id->clone(id);
pseudonym = pseudonym->clone(pseudonym);
permanent = this->permanent->put(this->permanent, pseudonym, id);
pseudonym = this->pseudonym->put(this->pseudonym, id, pseudonym);
/* delete old entries */
DESTROY_IF(permanent);
DESTROY_IF(pseudonym);
}
/**
* Implementation of sim_card_t.get_quintuplet
*/
static status_t get_quintuplet()
{
return NOT_SUPPORTED;
}
/**
* Implementation of eap_simaka_pseudonym_card_t.destroy.
*/
static void destroy(private_eap_simaka_pseudonym_card_t *this)
{
enumerator_t *enumerator;
identification_t *id;
void *key;
enumerator = this->pseudonym->create_enumerator(this->pseudonym);
while (enumerator->enumerate(enumerator, &key, &id))
{
id->destroy(id);
}
enumerator->destroy(enumerator);
enumerator = this->permanent->create_enumerator(this->permanent);
while (enumerator->enumerate(enumerator, &key, &id))
{
id->destroy(id);
}
enumerator->destroy(enumerator);
this->pseudonym->destroy(this->pseudonym);
this->permanent->destroy(this->permanent);
free(this);
}
/**
* See header
*/
eap_simaka_pseudonym_card_t *eap_simaka_pseudonym_card_create()
{
private_eap_simaka_pseudonym_card_t *this;
this = malloc_thing(private_eap_simaka_pseudonym_card_t);
this->public.card.get_triplet = (bool(*)(sim_card_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
this->public.card.get_quintuplet = (status_t(*)(sim_card_t*, 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))get_quintuplet;
this->public.card.resync = (bool(*)(sim_card_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))return_false;
this->public.card.get_pseudonym = (identification_t*(*)(sim_card_t*, identification_t *perm))get_pseudonym;
this->public.card.set_pseudonym = (void(*)(sim_card_t*, identification_t *id, identification_t *pseudonym))set_pseudonym;
this->public.card.get_reauth = (identification_t*(*)(sim_card_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.card.set_reauth = (void(*)(sim_card_t*, identification_t *id, identification_t* next, char mk[HASH_SIZE_SHA1], u_int16_t counter))nop;
this->public.destroy = (void(*)(eap_simaka_pseudonym_card_t*))destroy;
this->pseudonym = hashtable_create((void*)hash, (void*)equals, 0);
this->permanent = hashtable_create((void*)hash, (void*)equals, 0);
return &this->public;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 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_simaka_pseudonym_card eap_simaka_pseudonym_card
* @{ @ingroup eap_simaka_pseudonym
*/
#ifndef EAP_SIMAKA_PSEUDONYM_CARD_H_
#define EAP_SIMAKA_PSEUDONYM_CARD_H_
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_simaka_pseudonym_card_t eap_simaka_pseudonym_card_t;
/**
* SIM card implementing volatile in-memory pseudonym storage.
*/
struct eap_simaka_pseudonym_card_t {
/**
* Implements sim_card_t interface
*/
sim_card_t card;
/**
* Destroy a eap_simaka_pseudonym_card_t.
*/
void (*destroy)(eap_simaka_pseudonym_card_t *this);
};
/**
* Create a eap_simaka_pseudonym_card instance.
*/
eap_simaka_pseudonym_card_t *eap_simaka_pseudonym_card_create();
#endif /** EAP_SIMAKA_PSEUDONYM_CARD_H_ @}*/
@@ -0,0 +1,81 @@
/*
* Copyright (C) 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_simaka_pseudonym_plugin.h"
#include "eap_simaka_pseudonym_card.h"
#include "eap_simaka_pseudonym_provider.h"
#include <daemon.h>
typedef struct private_eap_simaka_pseudonym_t private_eap_simaka_pseudonym_t;
/**
* Private data of an eap_simaka_pseudonym_t object.
*/
struct private_eap_simaka_pseudonym_t {
/**
* Public eap_simaka_pseudonym_plugin_t interface.
*/
eap_simaka_pseudonym_plugin_t public;
/**
* SIM card
*/
eap_simaka_pseudonym_card_t *card;
/**
* SIM provider
*/
eap_simaka_pseudonym_provider_t *provider;
};
/**
* Implementation of eap_simaka_pseudonym_t.destroy.
*/
static void destroy(private_eap_simaka_pseudonym_t *this)
{
charon->sim->remove_card(charon->sim, &this->card->card);
charon->sim->remove_provider(charon->sim, &this->provider->provider);
this->card->destroy(this->card);
this->provider->destroy(this->provider);
free(this);
}
/**
* See header
*/
plugin_t *eap_simaka_pseudonym_plugin_create()
{
private_eap_simaka_pseudonym_t *this;
this = malloc_thing(private_eap_simaka_pseudonym_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
this->provider = eap_simaka_pseudonym_provider_create();
if (!this->provider)
{
free(this);
return NULL;
}
this->card = eap_simaka_pseudonym_card_create();
charon->sim->add_card(charon->sim, &this->card->card);
charon->sim->add_provider(charon->sim, &this->provider->provider);
return &this->public.plugin;
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 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_simaka_pseudonym eap_simaka_pseudonym
* @ingroup cplugins
*
* @defgroup eap_simaka_pseudonym_plugin eap_simaka_pseudonym_plugin
* @{ @ingroup eap_simaka_pseudonym
*/
#ifndef EAP_SIMAKA_PSEUDONYM_PLUGIN_H_
#define EAP_SIMAKA_PSEUDONYM_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_simaka_pseudonym_plugin_t eap_simaka_pseudonym_plugin_t;
/**
* Plugin to provide in-memory storage of EAP-SIM/AKA pseudonyms.
*/
struct eap_simaka_pseudonym_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_SIMAKA_PSEUDONYM_PLUGIN_H_ @}*/
@@ -0,0 +1,182 @@
/*
* Copyright (C) 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_simaka_pseudonym_provider.h"
#include <utils/hashtable.h>
typedef struct private_eap_simaka_pseudonym_provider_t private_eap_simaka_pseudonym_provider_t;
/**
* Private data of an eap_simaka_pseudonym_provider_t object.
*/
struct private_eap_simaka_pseudonym_provider_t {
/**
* Public eap_simaka_pseudonym_provider_t interface.
*/
eap_simaka_pseudonym_provider_t public;
/**
* Permanent -> pseudonym mappings
*/
hashtable_t *pseudonym;
/**
* Reverse pseudonym -> permanent mappings
*/
hashtable_t *permanent;
/**
* RNG for pseudonyms/reauth identities
*/
rng_t *rng;
};
/**
* hashtable hash function
*/
static u_int hash(identification_t *key)
{
return chunk_hash(key->get_encoding(key));
}
/**
* hashtable equals function
*/
static bool equals(identification_t *key1, identification_t *key2)
{
return key1->equals(key1, key2);
}
/**
* Implementation of sim_provider_t.is_pseudonym
*/
static identification_t* is_pseudonym(
private_eap_simaka_pseudonym_provider_t *this, identification_t *id)
{
identification_t *permanent;
permanent = this->permanent->get(this->permanent, id);
if (permanent)
{
return permanent->clone(permanent);
}
return NULL;
}
/**
* Generate a random identity
*/
static identification_t *gen_identity(
private_eap_simaka_pseudonym_provider_t *this)
{
char buf[8], hex[sizeof(buf) * 2 + 1];
this->rng->get_bytes(this->rng, sizeof(buf), buf);
chunk_to_hex(chunk_create(buf, sizeof(buf)), hex, FALSE);
return identification_create_from_string(hex);
}
/**
* Implementation of sim_provider_t.get_pseudonym
*/
static identification_t* gen_pseudonym(
private_eap_simaka_pseudonym_provider_t *this, identification_t *id)
{
identification_t *pseudonym, *permanent;
/* remove old entry */
pseudonym = this->pseudonym->remove(this->pseudonym, id);
if (pseudonym)
{
permanent = this->permanent->remove(this->permanent, pseudonym);
if (permanent)
{
permanent->destroy(permanent);
}
pseudonym->destroy(pseudonym);
}
pseudonym = gen_identity(this);
/* create new entries */
id = id->clone(id);
this->pseudonym->put(this->pseudonym, id, pseudonym);
this->permanent->put(this->permanent, pseudonym, id);
return pseudonym->clone(pseudonym);
}
/**
* Implementation of eap_simaka_pseudonym_provider_t.destroy.
*/
static void destroy(private_eap_simaka_pseudonym_provider_t *this)
{
enumerator_t *enumerator;
identification_t *id;
void *key;
enumerator = this->pseudonym->create_enumerator(this->pseudonym);
while (enumerator->enumerate(enumerator, &key, &id))
{
id->destroy(id);
}
enumerator->destroy(enumerator);
enumerator = this->permanent->create_enumerator(this->permanent);
while (enumerator->enumerate(enumerator, &key, &id))
{
id->destroy(id);
}
enumerator->destroy(enumerator);
this->pseudonym->destroy(this->pseudonym);
this->permanent->destroy(this->permanent);
this->rng->destroy(this->rng);
free(this);
}
/**
* See header
*/
eap_simaka_pseudonym_provider_t *eap_simaka_pseudonym_provider_create()
{
private_eap_simaka_pseudonym_provider_t *this;
this = malloc_thing(private_eap_simaka_pseudonym_provider_t);
this->public.provider.get_triplet = (bool(*)(sim_provider_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
this->public.provider.get_quintuplet = (bool(*)(sim_provider_t*, 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]))return_false;
this->public.provider.resync = (bool(*)(sim_provider_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))return_false;
this->public.provider.is_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))is_pseudonym;
this->public.provider.gen_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))gen_pseudonym;
this->public.provider.is_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char [HASH_SIZE_SHA1], u_int16_t *counter))return_null;
this->public.provider.gen_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char mk[HASH_SIZE_SHA1]))return_null;
this->public.destroy = (void(*)(eap_simaka_pseudonym_provider_t*))destroy;
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!this->rng)
{
free(this);
return NULL;
}
this->pseudonym = hashtable_create((void*)hash, (void*)equals, 0);
this->permanent = hashtable_create((void*)hash, (void*)equals, 0);
return &this->public;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 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_simaka_pseudonym_provider eap_simaka_pseudonym_provider
* @{ @ingroup eap_simaka_pseudonym
*/
#ifndef EAP_SIMAKA_PSEDUONYM_PROVIDER_H_
#define EAP_SIMAKA_PSEDUONYM_PROVIDER_H_
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_simaka_pseudonym_provider_t eap_simaka_pseudonym_provider_t;
/**
* SIM provider implementing volatile in-memory pseudonym storage.
*/
struct eap_simaka_pseudonym_provider_t {
/**
* Implements sim_provider_t interface.
*/
sim_provider_t provider;
/**
* Destroy a eap_simaka_pseudonym_provider_t.
*/
void (*destroy)(eap_simaka_pseudonym_provider_t *this);
};
/**
* Create a eap_simaka_pseudonym_provider instance.
*/
eap_simaka_pseudonym_provider_t *eap_simaka_pseudonym_provider_create();
#endif /** EAP_SIMAKA_PSEDUONYM_PROVIDER_H_ @}*/
@@ -0,0 +1,17 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-eap-simaka-reauth.la
else
plugin_LTLIBRARIES = libstrongswan-eap-simaka-reauth.la
endif
libstrongswan_eap_simaka_reauth_la_SOURCES = \
eap_simaka_reauth_plugin.h eap_simaka_reauth_plugin.c \
eap_simaka_reauth_card.h eap_simaka_reauth_card.c \
eap_simaka_reauth_provider.h eap_simaka_reauth_provider.c
libstrongswan_eap_simaka_reauth_la_LDFLAGS = -module -avoid-version
@@ -0,0 +1,170 @@
/*
* Copyright (C) 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_simaka_reauth_card.h"
#include <daemon.h>
#include <utils/hashtable.h>
typedef struct private_eap_simaka_reauth_card_t private_eap_simaka_reauth_card_t;
/**
* Private data of an eap_simaka_reauth_card_t object.
*/
struct private_eap_simaka_reauth_card_t {
/**
* Public eap_simaka_reauth_card_t interface.
*/
eap_simaka_reauth_card_t public;
/**
* Permanent -> reauth_data_t mappings
*/
hashtable_t *reauth;
};
/**
* Data associated to a reauthentication identity
*/
typedef struct {
/** currently used reauthentication identity */
identification_t *id;
/** associated permanent identity */
identification_t *permanent;
/** counter value */
u_int16_t counter;
/** master key */
char mk[HASH_SIZE_SHA1];
} reauth_data_t;
/**
* hashtable hash function
*/
static u_int hash(identification_t *key)
{
return chunk_hash(key->get_encoding(key));
}
/**
* hashtable equals function
*/
static bool equals(identification_t *key1, identification_t *key2)
{
return key1->equals(key1, key2);
}
/**
* Implementation of sim_card_t.get_reauth
*/
static identification_t *get_reauth(private_eap_simaka_reauth_card_t *this,
identification_t *id, char mk[HASH_SIZE_SHA1],
u_int16_t *counter)
{
reauth_data_t *data;
identification_t *reauth;
/* look up reauthentication data */
data = this->reauth->remove(this->reauth, id);
if (!data)
{
return NULL;
}
*counter = ++data->counter;
memcpy(mk, data->mk, HASH_SIZE_SHA1);
reauth = data->id;
data->permanent->destroy(data->permanent);
free(data);
return reauth;
}
/**
* Implementation of sim_card_t.set_reauth
*/
static void set_reauth(private_eap_simaka_reauth_card_t *this,
identification_t *id, identification_t* next,
char mk[HASH_SIZE_SHA1], u_int16_t counter)
{
reauth_data_t *data;
data = this->reauth->get(this->reauth, id);
if (data)
{
data->id->destroy(data->id);
}
else
{
data = malloc_thing(reauth_data_t);
data->permanent = id->clone(id);
this->reauth->put(this->reauth, data->permanent, data);
}
data->counter = counter;
data->id = next->clone(next);
memcpy(data->mk, mk, HASH_SIZE_SHA1);
}
/**
* Implementation of sim_card_t.get_quintuplet
*/
static status_t get_quintuplet()
{
return NOT_SUPPORTED;
}
/**
* Implementation of eap_simaka_reauth_card_t.destroy.
*/
static void destroy(private_eap_simaka_reauth_card_t *this)
{
enumerator_t *enumerator;
reauth_data_t *data;
void *key;
enumerator = this->reauth->create_enumerator(this->reauth);
while (enumerator->enumerate(enumerator, &key, &data))
{
data->id->destroy(data->id);
data->permanent->destroy(data->permanent);
free(data);
}
enumerator->destroy(enumerator);
this->reauth->destroy(this->reauth);
free(this);
}
/**
* See header
*/
eap_simaka_reauth_card_t *eap_simaka_reauth_card_create()
{
private_eap_simaka_reauth_card_t *this;
this = malloc_thing(private_eap_simaka_reauth_card_t);
this->public.card.get_triplet = (bool(*)(sim_card_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_null;
this->public.card.get_quintuplet = (status_t(*)(sim_card_t*, 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))get_quintuplet;
this->public.card.resync = (bool(*)(sim_card_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))return_false;
this->public.card.get_pseudonym = (identification_t*(*)(sim_card_t*, identification_t *perm))return_null;
this->public.card.set_pseudonym = (void(*)(sim_card_t*, identification_t *id, identification_t *pseudonym))nop;
this->public.card.get_reauth = (identification_t*(*)(sim_card_t*, identification_t *id, char mk[HASH_SIZE_SHA1], u_int16_t *counter))get_reauth;
this->public.card.set_reauth = (void(*)(sim_card_t*, identification_t *id, identification_t* next, char mk[HASH_SIZE_SHA1], u_int16_t counter))set_reauth;
this->public.destroy = (void(*)(eap_simaka_reauth_card_t*))destroy;
this->reauth = hashtable_create((void*)hash, (void*)equals, 0);
return &this->public;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 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_simaka_reauth_card eap_simaka_reauth_card
* @{ @ingroup eap_simaka_reauth
*/
#ifndef EAP_SIMAKA_REAUTH_CARD_H_
#define EAP_SIMAKA_REAUTH_CARD_H_
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_simaka_reauth_card_t eap_simaka_reauth_card_t;
/**
* SIM card implementing volatile in-memory reauthentication data storage.
*/
struct eap_simaka_reauth_card_t {
/**
* Implements sim_card_t interface
*/
sim_card_t card;
/**
* Destroy a eap_simaka_reauth_card_t.
*/
void (*destroy)(eap_simaka_reauth_card_t *this);
};
/**
* Create a eap_simaka_reauth_card instance.
*/
eap_simaka_reauth_card_t *eap_simaka_reauth_card_create();
#endif /** EAP_SIMAKA_REAUTH_CARD_H_ @}*/
@@ -0,0 +1,79 @@
/*
* Copyright (C) 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_simaka_reauth_plugin.h"
#include "eap_simaka_reauth_card.h"
#include "eap_simaka_reauth_provider.h"
#include <daemon.h>
typedef struct private_eap_simaka_reauth_t private_eap_simaka_reauth_t;
/**
* Private data of an eap_simaka_reauth_t object.
*/
struct private_eap_simaka_reauth_t {
/**
* Public eap_simaka_reauth_plugin_t interface.
*/
eap_simaka_reauth_plugin_t public;
/**
* SIM card
*/
eap_simaka_reauth_card_t *card;
/**
* SIM provider
*/
eap_simaka_reauth_provider_t *provider;
};
/**
* Implementation of eap_simaka_reauth_t.destroy.
*/
static void destroy(private_eap_simaka_reauth_t *this)
{
charon->sim->remove_card(charon->sim, &this->card->card);
charon->sim->remove_provider(charon->sim, &this->provider->provider);
this->card->destroy(this->card);
this->provider->destroy(this->provider);
free(this);
}
/**
* See header
*/
plugin_t *eap_simaka_reauth_plugin_create()
{
private_eap_simaka_reauth_t *this = malloc_thing(private_eap_simaka_reauth_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
this->provider = eap_simaka_reauth_provider_create();
if (!this->provider)
{
free(this);
return NULL;
}
this->card = eap_simaka_reauth_card_create();
charon->sim->add_card(charon->sim, &this->card->card);
charon->sim->add_provider(charon->sim, &this->provider->provider);
return &this->public.plugin;
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 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_simaka_reauth eap_simaka_reauth
* @ingroup cplugins
*
* @defgroup eap_simaka_reauth_plugin eap_simaka_reauth_plugin
* @{ @ingroup eap_simaka_reauth
*/
#ifndef EAP_SIMAKA_REAUTH_PLUGIN_H_
#define EAP_SIMAKA_REAUTH_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct eap_simaka_reauth_plugin_t eap_simaka_reauth_plugin_t;
/**
* Plugin to provide in-memory EAP-SIM/AKA reauthentication data storage.
*/
struct eap_simaka_reauth_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** EAP_SIMAKA_REAUTH_PLUGIN_H_ @}*/
@@ -0,0 +1,209 @@
/*
* Copyright (C) 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_simaka_reauth_provider.h"
#include <daemon.h>
#include <utils/hashtable.h>
typedef struct private_eap_simaka_reauth_provider_t private_eap_simaka_reauth_provider_t;
/**
* Private data of an eap_simaka_reauth_provider_t object.
*/
struct private_eap_simaka_reauth_provider_t {
/**
* Public eap_simaka_reauth_provider_t interface.
*/
eap_simaka_reauth_provider_t public;
/**
* Permanent -> reauth_data_t mappings
*/
hashtable_t *reauth;
/**
* Reverse reauth -> permanent mappings
*/
hashtable_t *permanent;
/**
* RNG for pseudonyms/reauth identities
*/
rng_t *rng;
};
/**
* Data associated to a reauthentication identity
*/
typedef struct {
/** currently used reauthentication identity */
identification_t *id;
/** counter value */
u_int16_t counter;
/** master key */
char mk[HASH_SIZE_SHA1];
} reauth_data_t;
/**
* hashtable hash function
*/
static u_int hash(identification_t *key)
{
return chunk_hash(key->get_encoding(key));
}
/**
* hashtable equals function
*/
static bool equals(identification_t *key1, identification_t *key2)
{
return key1->equals(key1, key2);
}
/**
* Generate a random identity
*/
static identification_t *gen_identity(private_eap_simaka_reauth_provider_t *this)
{
char buf[8], hex[sizeof(buf) * 2 + 1];
this->rng->get_bytes(this->rng, sizeof(buf), buf);
chunk_to_hex(chunk_create(buf, sizeof(buf)), hex, FALSE);
return identification_create_from_string(hex);
}
/**
* Implementation of sim_provider_t.is_reauth
*/
static identification_t *is_reauth(private_eap_simaka_reauth_provider_t *this,
identification_t *id, char mk[HASH_SIZE_SHA1],
u_int16_t *counter)
{
identification_t *permanent;
reauth_data_t *data;
/* look up permanent identity */
permanent = this->permanent->get(this->permanent, id);
if (!permanent)
{
return NULL;
}
/* look up reauthentication data */
data = this->reauth->get(this->reauth, permanent);
if (!data)
{
return NULL;
}
*counter = ++data->counter;
memcpy(mk, data->mk, HASH_SIZE_SHA1);
return permanent->clone(permanent);
}
/**
* Implementation of sim_provider_t.gen_reauth
*/
static identification_t *gen_reauth(private_eap_simaka_reauth_provider_t *this,
identification_t *id, char mk[HASH_SIZE_SHA1])
{
reauth_data_t *data;
identification_t *permanent;
data = this->reauth->get(this->reauth, id);
if (data)
{ /* update existing entry */
permanent = this->permanent->remove(this->permanent, data->id);
if (permanent)
{
data->id->destroy(data->id);
data->id = gen_identity(this);
this->permanent->put(this->permanent, data->id, permanent);
}
}
else
{ /* generate new entry */
data = malloc_thing(reauth_data_t);
data->counter = 0;
data->id = gen_identity(this);
id = id->clone(id);
this->reauth->put(this->reauth, id, data);
this->permanent->put(this->permanent, data->id, id);
}
memcpy(data->mk, mk, HASH_SIZE_SHA1);
return data->id->clone(data->id);
}
/**
* Implementation of eap_simaka_reauth_provider_t.destroy.
*/
static void destroy(private_eap_simaka_reauth_provider_t *this)
{
enumerator_t *enumerator;
identification_t *id;
reauth_data_t *data;
void *key;
enumerator = this->permanent->create_enumerator(this->permanent);
while (enumerator->enumerate(enumerator, &key, &id))
{
id->destroy(id);
}
enumerator->destroy(enumerator);
enumerator = this->reauth->create_enumerator(this->reauth);
while (enumerator->enumerate(enumerator, &key, &data))
{
data->id->destroy(data->id);
free(data);
}
enumerator->destroy(enumerator);
this->permanent->destroy(this->permanent);
this->reauth->destroy(this->reauth);
this->rng->destroy(this->rng);
free(this);
}
/**
* See header
*/
eap_simaka_reauth_provider_t *eap_simaka_reauth_provider_create()
{
private_eap_simaka_reauth_provider_t *this = malloc_thing(private_eap_simaka_reauth_provider_t);
this->public.provider.get_triplet = (bool(*)(sim_provider_t*, identification_t *id, char rand[SIM_RAND_LEN], char sres[SIM_SRES_LEN], char kc[SIM_KC_LEN]))return_false;
this->public.provider.get_quintuplet = (bool(*)(sim_provider_t*, 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]))return_false;
this->public.provider.resync = (bool(*)(sim_provider_t*, identification_t *id, char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN]))return_false;
this->public.provider.is_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.gen_pseudonym = (identification_t*(*)(sim_provider_t*, identification_t *id))return_null;
this->public.provider.is_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char [HASH_SIZE_SHA1], u_int16_t *counter))is_reauth;
this->public.provider.gen_reauth = (identification_t*(*)(sim_provider_t*, identification_t *id, char mk[HASH_SIZE_SHA1]))gen_reauth;
this->public.destroy = (void(*)(eap_simaka_reauth_provider_t*))destroy;
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (!this->rng)
{
free(this);
return NULL;
}
this->permanent = hashtable_create((void*)hash, (void*)equals, 0);
this->reauth = hashtable_create((void*)hash, (void*)equals, 0);
return &this->public;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 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_simaka_reauth_provider eap_simaka_reauth_provider
* @{ @ingroup eap_simaka_reauth
*/
#ifndef EAP_SIMAKA_REAUTH_PROVIDER_H_
#define EAP_SIMAKA_REAUTH_PROVIDER_H_
#include <sa/authenticators/eap/sim_manager.h>
typedef struct eap_simaka_reauth_provider_t eap_simaka_reauth_provider_t;
/**
* SIM provider implementing volatile in-memory reauthentication data storage.
*/
struct eap_simaka_reauth_provider_t {
/**
* Implements sim_provider_t interface.
*/
sim_provider_t provider;
/**
* Destroy a eap_simaka_reauth_provider_t.
*/
void (*destroy)(eap_simaka_reauth_provider_t *this);
};
/**
* Create a eap_simaka_reauth_provider instance.
*/
eap_simaka_reauth_provider_t *eap_simaka_reauth_provider_create();
#endif /** EAP_SIMAKA_REAUTH_PROVIDER_H_ @}*/
@@ -0,0 +1,16 @@
INCLUDES = -I${linux_headers} -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-kernel-klips.la
else
plugin_LTLIBRARIES = libstrongswan-kernel-klips.la
endif
libstrongswan_kernel_klips_la_SOURCES = \
kernel_klips_plugin.h kernel_klips_plugin.c \
kernel_klips_ipsec.h kernel_klips_ipsec.c pfkeyv2.h
libstrongswan_kernel_klips_la_LDFLAGS = -module -avoid-version
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 kernel_klips_ipsec_i kernel_klips_ipsec
* @{ @ingroup kernel_klips
*/
#ifndef KERNEL_KLIPS_IPSEC_H_
#define KERNEL_KLIPS_IPSEC_H_
#include <kernel/kernel_ipsec.h>
typedef struct kernel_klips_ipsec_t kernel_klips_ipsec_t;
/**
* Implementation of the kernel ipsec interface using PF_KEY.
*/
struct kernel_klips_ipsec_t {
/**
* Implements kernel_ipsec_t interface
*/
kernel_ipsec_t interface;
};
/**
* Create a PF_KEY kernel ipsec interface instance.
*
* @return kernel_klips_ipsec_t instance
*/
kernel_klips_ipsec_t *kernel_klips_ipsec_create();
#endif /** KERNEL_KLIPS_IPSEC_H_ @}*/
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 "kernel_klips_plugin.h"
#include "kernel_klips_ipsec.h"
#include <daemon.h>
typedef struct private_kernel_klips_plugin_t private_kernel_klips_plugin_t;
/**
* private data of kernel PF_KEY plugin
*/
struct private_kernel_klips_plugin_t {
/**
* implements plugin interface
*/
kernel_klips_plugin_t public;
};
/**
* Implementation of plugin_t.destroy
*/
static void destroy(private_kernel_klips_plugin_t *this)
{
charon->kernel_interface->remove_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_klips_ipsec_create);
free(this);
}
/*
* see header file
*/
plugin_t *kernel_klips_plugin_create()
{
private_kernel_klips_plugin_t *this = malloc_thing(private_kernel_klips_plugin_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
charon->kernel_interface->add_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_klips_ipsec_create);
return &this->public.plugin;
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 kernel_klips kernel_klips
* @ingroup cplugins
*
* @defgroup kernel_klips_plugin kernel_klips_plugin
* @{ @ingroup kernel_klips
*/
#ifndef KERNEL_KLIPS_PLUGIN_H_
#define KERNEL_KLIPS_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct kernel_klips_plugin_t kernel_klips_plugin_t;
/**
* PF_KEY kernel interface plugin
*/
struct kernel_klips_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** KERNEL_KLIPS_PLUGIN_H_ @}*/
@@ -0,0 +1,322 @@
/*
RFC 2367 PF_KEY Key Management API July 1998
Appendix D: Sample Header File
This file defines structures and symbols for the PF_KEY Version 2
key management interface. It was written at the U.S. Naval Research
Laboratory. This file is in the public domain. The authors ask that
you leave this credit intact on any copies of this file.
*/
#ifndef __PFKEY_V2_H
#define __PFKEY_V2_H 1
#define PF_KEY_V2 2
#define PFKEYV2_REVISION 199806L
#define SADB_RESERVED 0
#define SADB_GETSPI 1
#define SADB_UPDATE 2
#define SADB_ADD 3
#define SADB_DELETE 4
#define SADB_GET 5
#define SADB_ACQUIRE 6
#define SADB_REGISTER 7
#define SADB_EXPIRE 8
#define SADB_FLUSH 9
#define SADB_DUMP 10
#define SADB_X_PROMISC 11
#define SADB_X_PCHANGE 12
#define SADB_X_GRPSA 13
#define SADB_X_ADDFLOW 14
#define SADB_X_DELFLOW 15
#define SADB_X_DEBUG 16
#define SADB_X_NAT_T_NEW_MAPPING 17
#define SADB_MAX 17
struct sadb_msg {
uint8_t sadb_msg_version;
uint8_t sadb_msg_type;
uint8_t sadb_msg_errno;
uint8_t sadb_msg_satype;
uint16_t sadb_msg_len;
uint16_t sadb_msg_reserved;
uint32_t sadb_msg_seq;
uint32_t sadb_msg_pid;
};
struct sadb_ext {
uint16_t sadb_ext_len;
uint16_t sadb_ext_type;
};
struct sadb_sa {
uint16_t sadb_sa_len;
uint16_t sadb_sa_exttype;
uint32_t sadb_sa_spi;
uint8_t sadb_sa_replay;
uint8_t sadb_sa_state;
uint8_t sadb_sa_auth;
uint8_t sadb_sa_encrypt;
uint32_t sadb_sa_flags;
};
struct sadb_lifetime {
uint16_t sadb_lifetime_len;
uint16_t sadb_lifetime_exttype;
uint32_t sadb_lifetime_allocations;
uint64_t sadb_lifetime_bytes;
uint64_t sadb_lifetime_addtime;
uint64_t sadb_lifetime_usetime;
uint32_t sadb_x_lifetime_packets;
uint32_t sadb_x_lifetime_reserved;
};
struct sadb_address {
uint16_t sadb_address_len;
uint16_t sadb_address_exttype;
uint8_t sadb_address_proto;
uint8_t sadb_address_prefixlen;
uint16_t sadb_address_reserved;
};
struct sadb_key {
uint16_t sadb_key_len;
uint16_t sadb_key_exttype;
uint16_t sadb_key_bits;
uint16_t sadb_key_reserved;
};
struct sadb_ident {
uint16_t sadb_ident_len;
uint16_t sadb_ident_exttype;
uint16_t sadb_ident_type;
uint16_t sadb_ident_reserved;
uint64_t sadb_ident_id;
};
struct sadb_sens {
uint16_t sadb_sens_len;
uint16_t sadb_sens_exttype;
uint32_t sadb_sens_dpd;
uint8_t sadb_sens_sens_level;
uint8_t sadb_sens_sens_len;
uint8_t sadb_sens_integ_level;
uint8_t sadb_sens_integ_len;
uint32_t sadb_sens_reserved;
};
struct sadb_prop {
uint16_t sadb_prop_len;
uint16_t sadb_prop_exttype;
uint8_t sadb_prop_replay;
uint8_t sadb_prop_reserved[3];
};
struct sadb_comb {
uint8_t sadb_comb_auth;
uint8_t sadb_comb_encrypt;
uint16_t sadb_comb_flags;
uint16_t sadb_comb_auth_minbits;
uint16_t sadb_comb_auth_maxbits;
uint16_t sadb_comb_encrypt_minbits;
uint16_t sadb_comb_encrypt_maxbits;
uint32_t sadb_comb_reserved;
uint32_t sadb_comb_soft_allocations;
uint32_t sadb_comb_hard_allocations;
uint64_t sadb_comb_soft_bytes;
uint64_t sadb_comb_hard_bytes;
uint64_t sadb_comb_soft_addtime;
uint64_t sadb_comb_hard_addtime;
uint64_t sadb_comb_soft_usetime;
uint64_t sadb_comb_hard_usetime;
uint32_t sadb_x_comb_soft_packets;
uint32_t sadb_x_comb_hard_packets;
};
struct sadb_supported {
uint16_t sadb_supported_len;
uint16_t sadb_supported_exttype;
uint32_t sadb_supported_reserved;
};
struct sadb_alg {
uint8_t sadb_alg_id;
uint8_t sadb_alg_ivlen;
uint16_t sadb_alg_minbits;
uint16_t sadb_alg_maxbits;
uint16_t sadb_alg_reserved;
};
struct sadb_spirange {
uint16_t sadb_spirange_len;
uint16_t sadb_spirange_exttype;
uint32_t sadb_spirange_min;
uint32_t sadb_spirange_max;
uint32_t sadb_spirange_reserved;
};
struct sadb_x_kmprivate {
uint16_t sadb_x_kmprivate_len;
uint16_t sadb_x_kmprivate_exttype;
uint32_t sadb_x_kmprivate_reserved;
};
struct sadb_x_satype {
uint16_t sadb_x_satype_len;
uint16_t sadb_x_satype_exttype;
uint8_t sadb_x_satype_satype;
uint8_t sadb_x_satype_reserved[3];
};
struct sadb_x_debug {
uint16_t sadb_x_debug_len;
uint16_t sadb_x_debug_exttype;
uint32_t sadb_x_debug_tunnel;
uint32_t sadb_x_debug_netlink;
uint32_t sadb_x_debug_xform;
uint32_t sadb_x_debug_eroute;
uint32_t sadb_x_debug_spi;
uint32_t sadb_x_debug_radij;
uint32_t sadb_x_debug_esp;
uint32_t sadb_x_debug_ah;
uint32_t sadb_x_debug_rcv;
uint32_t sadb_x_debug_pfkey;
uint32_t sadb_x_debug_ipcomp;
uint32_t sadb_x_debug_verbose;
uint8_t sadb_x_debug_reserved[4];
};
struct sadb_x_nat_t_type {
uint16_t sadb_x_nat_t_type_len;
uint16_t sadb_x_nat_t_type_exttype;
uint8_t sadb_x_nat_t_type_type;
uint8_t sadb_x_nat_t_type_reserved[3];
};
struct sadb_x_nat_t_port {
uint16_t sadb_x_nat_t_port_len;
uint16_t sadb_x_nat_t_port_exttype;
uint16_t sadb_x_nat_t_port_port;
uint16_t sadb_x_nat_t_port_reserved;
};
/*
* A protocol structure for passing through the transport level
* protocol. It contains more fields than are actually used/needed
* but it is this way to be compatible with the structure used in
* OpenBSD (http://www.openbsd.org/cgi-bin/cvsweb/src/sys/net/pfkeyv2.h)
*/
struct sadb_protocol {
uint16_t sadb_protocol_len;
uint16_t sadb_protocol_exttype;
uint8_t sadb_protocol_proto;
uint8_t sadb_protocol_direction;
uint8_t sadb_protocol_flags;
uint8_t sadb_protocol_reserved2;
};
#define SADB_EXT_RESERVED 0
#define SADB_EXT_SA 1
#define SADB_EXT_LIFETIME_CURRENT 2
#define SADB_EXT_LIFETIME_HARD 3
#define SADB_EXT_LIFETIME_SOFT 4
#define SADB_EXT_ADDRESS_SRC 5
#define SADB_EXT_ADDRESS_DST 6
#define SADB_EXT_ADDRESS_PROXY 7
#define SADB_EXT_KEY_AUTH 8
#define SADB_EXT_KEY_ENCRYPT 9
#define SADB_EXT_IDENTITY_SRC 10
#define SADB_EXT_IDENTITY_DST 11
#define SADB_EXT_SENSITIVITY 12
#define SADB_EXT_PROPOSAL 13
#define SADB_EXT_SUPPORTED_AUTH 14
#define SADB_EXT_SUPPORTED_ENCRYPT 15
#define SADB_EXT_SPIRANGE 16
#define SADB_X_EXT_KMPRIVATE 17
#define SADB_X_EXT_SATYPE2 18
#define SADB_X_EXT_SA2 19
#define SADB_X_EXT_ADDRESS_DST2 20
#define SADB_X_EXT_ADDRESS_SRC_FLOW 21
#define SADB_X_EXT_ADDRESS_DST_FLOW 22
#define SADB_X_EXT_ADDRESS_SRC_MASK 23
#define SADB_X_EXT_ADDRESS_DST_MASK 24
#define SADB_X_EXT_DEBUG 25
#define SADB_X_EXT_PROTOCOL 26
#define SADB_X_EXT_NAT_T_TYPE 27
#define SADB_X_EXT_NAT_T_SPORT 28
#define SADB_X_EXT_NAT_T_DPORT 29
#define SADB_X_EXT_NAT_T_OA 30
#define SADB_EXT_MAX 30
/* SADB_X_DELFLOW required over and above SADB_X_SAFLAGS_CLEARFLOW */
#define SADB_X_EXT_ADDRESS_DELFLOW \
( (1<<SADB_X_EXT_ADDRESS_SRC_FLOW) \
| (1<<SADB_X_EXT_ADDRESS_DST_FLOW) \
| (1<<SADB_X_EXT_ADDRESS_SRC_MASK) \
| (1<<SADB_X_EXT_ADDRESS_DST_MASK))
#define SADB_SATYPE_UNSPEC 0
#define SADB_SATYPE_AH 2
#define SADB_SATYPE_ESP 3
#define SADB_SATYPE_RSVP 5
#define SADB_SATYPE_OSPFV2 6
#define SADB_SATYPE_RIPV2 7
#define SADB_SATYPE_MIP 8
#define SADB_X_SATYPE_IPIP 9
#define SADB_X_SATYPE_COMP 10
#define SADB_X_SATYPE_INT 11
#define SADB_SATYPE_MAX 11
#define SADB_SASTATE_LARVAL 0
#define SADB_SASTATE_MATURE 1
#define SADB_SASTATE_DYING 2
#define SADB_SASTATE_DEAD 3
#define SADB_SASTATE_MAX 3
#define SADB_SAFLAGS_PFS 1
#define SADB_X_SAFLAGS_REPLACEFLOW 2
#define SADB_X_SAFLAGS_CLEARFLOW 4
#define SADB_X_SAFLAGS_INFLOW 8
#define SADB_AALG_NONE 0
#define SADB_AALG_MD5HMAC 2
#define SADB_AALG_SHA1HMAC 3
#define SADB_AALG_SHA256_HMAC 5
#define SADB_AALG_SHA384_HMAC 6
#define SADB_AALG_SHA512_HMAC 7
#define SADB_AALG_RIPEMD160HMAC 8
#define SADB_AALG_MAX 15
#define SADB_EALG_NONE 0
#define SADB_EALG_DESCBC 2
#define SADB_EALG_3DESCBC 3
#define SADB_EALG_BFCBC 7
#define SADB_EALG_NULL 11
#define SADB_EALG_AESCBC 12
#define SADB_EALG_MAX 255
#define SADB_X_CALG_NONE 0
#define SADB_X_CALG_OUI 1
#define SADB_X_CALG_DEFLATE 2
#define SADB_X_CALG_LZS 3
#define SADB_X_CALG_V42BIS 4
#define SADB_X_CALG_MAX 4
#define SADB_X_TALG_NONE 0
#define SADB_X_TALG_IPv4_in_IPv4 1
#define SADB_X_TALG_IPv6_in_IPv4 2
#define SADB_X_TALG_IPv4_in_IPv6 3
#define SADB_X_TALG_IPv6_in_IPv6 4
#define SADB_X_TALG_MAX 4
#define SADB_IDENTTYPE_RESERVED 0
#define SADB_IDENTTYPE_PREFIX 1
#define SADB_IDENTTYPE_FQDN 2
#define SADB_IDENTTYPE_USERFQDN 3
#define SADB_X_IDENTTYPE_CONNECTION 4
#define SADB_IDENTTYPE_MAX 4
#define SADB_KEY_FLAGS_MAX 0
#endif /* __PFKEY_V2_H */
@@ -0,0 +1,19 @@
INCLUDES = -I${linux_headers} -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
AM_CFLAGS = -rdynamic \
-DROUTING_TABLE=${routing_table} \
-DROUTING_TABLE_PRIO=${routing_table_prio}
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-kernel-netlink.la
else
plugin_LTLIBRARIES = libstrongswan-kernel-netlink.la
endif
libstrongswan_kernel_netlink_la_SOURCES = \
kernel_netlink_plugin.h kernel_netlink_plugin.c \
kernel_netlink_ipsec.h kernel_netlink_ipsec.c kernel_netlink_net.h kernel_netlink_net.c \
kernel_netlink_shared.h kernel_netlink_shared.c
libstrongswan_kernel_netlink_la_LDFLAGS = -module -avoid-version
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 kernel_netlink_ipsec_i kernel_netlink_ipsec
* @{ @ingroup kernel_netlink
*/
#ifndef KERNEL_NETLINK_IPSEC_H_
#define KERNEL_NETLINK_IPSEC_H_
#include <kernel/kernel_ipsec.h>
typedef struct kernel_netlink_ipsec_t kernel_netlink_ipsec_t;
/**
* Implementation of the kernel ipsec interface using Netlink.
*/
struct kernel_netlink_ipsec_t {
/**
* Implements kernel_ipsec_t interface
*/
kernel_ipsec_t interface;
};
/**
* Create a netlink kernel ipsec interface instance.
*
* @return kernel_netlink_ipsec_t instance
*/
kernel_netlink_ipsec_t *kernel_netlink_ipsec_create();
#endif /** KERNEL_NETLINK_IPSEC_H_ @}*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 kernel_netlink_net_i kernel_netlink_net
* @{ @ingroup kernel_netlink
*/
#ifndef KERNEL_NETLINK_NET_H_
#define KERNEL_NETLINK_NET_H_
#include <kernel/kernel_net.h>
typedef struct kernel_netlink_net_t kernel_netlink_net_t;
/**
* Implementation of the kernel network interface using Netlink.
*/
struct kernel_netlink_net_t {
/**
* Implements kernel_net_t interface
*/
kernel_net_t interface;
};
/**
* Create a netlink kernel network interface instance.
*
* @return kernel_netlink_net_t instance
*/
kernel_netlink_net_t *kernel_netlink_net_create();
#endif /** KERNEL_NETLINK_NET_H_ @}*/
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 "kernel_netlink_plugin.h"
#include "kernel_netlink_ipsec.h"
#include "kernel_netlink_net.h"
#include <daemon.h>
typedef struct private_kernel_netlink_plugin_t private_kernel_netlink_plugin_t;
/**
* private data of kernel netlink plugin
*/
struct private_kernel_netlink_plugin_t {
/**
* implements plugin interface
*/
kernel_netlink_plugin_t public;
};
/**
* Implementation of plugin_t.destroy
*/
static void destroy(private_kernel_netlink_plugin_t *this)
{
charon->kernel_interface->remove_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_netlink_ipsec_create);
charon->kernel_interface->remove_net_interface(charon->kernel_interface, (kernel_net_constructor_t)kernel_netlink_net_create);
free(this);
}
/*
* see header file
*/
plugin_t *kernel_netlink_plugin_create()
{
private_kernel_netlink_plugin_t *this = malloc_thing(private_kernel_netlink_plugin_t);
this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
charon->kernel_interface->add_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_netlink_ipsec_create);
charon->kernel_interface->add_net_interface(charon->kernel_interface, (kernel_net_constructor_t)kernel_netlink_net_create);
return &this->public.plugin;
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 kernel_netlink kernel_netlink
* @ingroup cplugins
*
* @defgroup kernel_netlink_plugin kernel_netlink_plugin
* @{ @ingroup kernel_netlink
*/
#ifndef KERNEL_NETLINK_PLUGIN_H_
#define KERNEL_NETLINK_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct kernel_netlink_plugin_t kernel_netlink_plugin_t;
/**
* netlink kernel interface plugin
*/
struct kernel_netlink_plugin_t {
/**
* implements plugin interface
*/
plugin_t plugin;
};
#endif /** KERNEL_NETLINK_PLUGIN_H_ @}*/
@@ -0,0 +1,306 @@
/*
* Copyright (C) 2008 Tobias Brunner
* 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 <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <errno.h>
#include <unistd.h>
#include "kernel_netlink_shared.h"
#include <daemon.h>
#include <threading/mutex.h>
typedef struct private_netlink_socket_t private_netlink_socket_t;
/**
* Private variables and functions of netlink_socket_t class.
*/
struct private_netlink_socket_t {
/**
* public part of the netlink_socket_t object.
*/
netlink_socket_t public;
/**
* mutex to lock access to netlink socket
*/
mutex_t *mutex;
/**
* current sequence number for netlink request
*/
int seq;
/**
* netlink socket protocol
*/
int protocol;
/**
* netlink socket
*/
int socket;
};
/**
* Imported from kernel_netlink_ipsec.c
*/
extern enum_name_t *xfrm_msg_names;
/**
* Implementation of netlink_socket_t.send
*/
static status_t netlink_send(private_netlink_socket_t *this, struct nlmsghdr *in,
struct nlmsghdr **out, size_t *out_len)
{
int len, addr_len;
struct sockaddr_nl addr;
chunk_t result = chunk_empty, tmp;
struct nlmsghdr *msg, peek;
this->mutex->lock(this->mutex);
in->nlmsg_seq = ++this->seq;
in->nlmsg_pid = getpid();
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
addr.nl_groups = 0;
if (this->protocol == NETLINK_XFRM)
{
chunk_t in_chunk = { (u_char*)in, in->nlmsg_len };
DBG3(DBG_KNL, "sending %N: %B", xfrm_msg_names, in->nlmsg_type, &in_chunk);
}
while (TRUE)
{
len = sendto(this->socket, in, in->nlmsg_len, 0,
(struct sockaddr*)&addr, sizeof(addr));
if (len != in->nlmsg_len)
{
if (errno == EINTR)
{
/* interrupted, try again */
continue;
}
this->mutex->unlock(this->mutex);
DBG1(DBG_KNL, "error sending to netlink socket: %s", strerror(errno));
return FAILED;
}
break;
}
while (TRUE)
{
char buf[4096];
tmp.len = sizeof(buf);
tmp.ptr = buf;
msg = (struct nlmsghdr*)tmp.ptr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 0;
addr_len = sizeof(addr);
len = recvfrom(this->socket, tmp.ptr, tmp.len, 0,
(struct sockaddr*)&addr, &addr_len);
if (len < 0)
{
if (errno == EINTR)
{
DBG1(DBG_KNL, "got interrupted");
/* interrupted, try again */
continue;
}
DBG1(DBG_KNL, "error reading from netlink socket: %s", strerror(errno));
this->mutex->unlock(this->mutex);
free(result.ptr);
return FAILED;
}
if (!NLMSG_OK(msg, len))
{
DBG1(DBG_KNL, "received corrupted netlink message");
this->mutex->unlock(this->mutex);
free(result.ptr);
return FAILED;
}
if (msg->nlmsg_seq != this->seq)
{
DBG1(DBG_KNL, "received invalid netlink sequence number");
if (msg->nlmsg_seq < this->seq)
{
continue;
}
this->mutex->unlock(this->mutex);
free(result.ptr);
return FAILED;
}
tmp.len = len;
result.ptr = realloc(result.ptr, result.len + tmp.len);
memcpy(result.ptr + result.len, tmp.ptr, tmp.len);
result.len += tmp.len;
/* NLM_F_MULTI flag does not seem to be set correctly, we use sequence
* numbers to detect multi header messages */
len = recvfrom(this->socket, &peek, sizeof(peek), MSG_PEEK | MSG_DONTWAIT,
(struct sockaddr*)&addr, &addr_len);
if (len == sizeof(peek) && peek.nlmsg_seq == this->seq)
{
/* seems to be multipart */
continue;
}
break;
}
*out_len = result.len;
*out = (struct nlmsghdr*)result.ptr;
this->mutex->unlock(this->mutex);
return SUCCESS;
}
/**
* Implementation of netlink_socket_t.send_ack.
*/
static status_t netlink_send_ack(private_netlink_socket_t *this, struct nlmsghdr *in)
{
struct nlmsghdr *out, *hdr;
size_t len;
if (netlink_send(this, in, &out, &len) != SUCCESS)
{
return FAILED;
}
hdr = out;
while (NLMSG_OK(hdr, len))
{
switch (hdr->nlmsg_type)
{
case NLMSG_ERROR:
{
struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(hdr);
if (err->error)
{
if (-err->error == EEXIST)
{ /* do not report existing routes */
free(out);
return ALREADY_DONE;
}
DBG1(DBG_KNL, "received netlink error: %s (%d)",
strerror(-err->error), -err->error);
free(out);
return FAILED;
}
free(out);
return SUCCESS;
}
default:
hdr = NLMSG_NEXT(hdr, len);
continue;
case NLMSG_DONE:
break;
}
break;
}
DBG1(DBG_KNL, "netlink request not acknowledged");
free(out);
return FAILED;
}
/**
* Implementation of netlink_socket_t.destroy.
*/
static void destroy(private_netlink_socket_t *this)
{
if (this->socket > 0)
{
close(this->socket);
}
this->mutex->destroy(this->mutex);
free(this);
}
/**
* Described in header.
*/
netlink_socket_t *netlink_socket_create(int protocol)
{
private_netlink_socket_t *this = malloc_thing(private_netlink_socket_t);
struct sockaddr_nl addr;
/* public functions */
this->public.send = (status_t(*)(netlink_socket_t*,struct nlmsghdr*, struct nlmsghdr**, size_t*))netlink_send;
this->public.send_ack = (status_t(*)(netlink_socket_t*,struct nlmsghdr*))netlink_send_ack;
this->public.destroy = (void(*)(netlink_socket_t*))destroy;
/* private members */
this->seq = 200;
this->mutex = mutex_create(MUTEX_TYPE_DEFAULT);
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
this->protocol = protocol;
this->socket = socket(AF_NETLINK, SOCK_RAW, protocol);
if (this->socket < 0)
{
DBG1(DBG_KNL, "unable to create netlink socket");
destroy(this);
return NULL;
}
addr.nl_groups = 0;
if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)))
{
DBG1(DBG_KNL, "unable to bind netlink socket");
destroy(this);
return NULL;
}
return &this->public;
}
/**
* Described in header.
*/
void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data,
size_t buflen)
{
struct rtattr *rta;
if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data.len) > buflen)
{
DBG1(DBG_KNL, "unable to add attribute, buffer too small");
return;
}
rta = (struct rtattr*)(((char*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len));
rta->rta_type = rta_type;
rta->rta_len = RTA_LENGTH(data.len);
memcpy(RTA_DATA(rta), data.ptr, data.len);
hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len;
}
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2008 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef KERNEL_NETLINK_SHARED_H_
#define KERNEL_NETLINK_SHARED_H_
#include <library.h>
#include <linux/rtnetlink.h>
/**
* General purpose netlink buffer.
*
* 1024 byte is currently sufficient for all operations. Some platform
* require an enforced aligment to four bytes (e.g. ARM).
*/
typedef u_char netlink_buf_t[1024] __attribute__((aligned(RTA_ALIGNTO)));
typedef struct netlink_socket_t netlink_socket_t;
/**
* Wrapper around a netlink socket.
*/
struct netlink_socket_t {
/**
* Send a netlink message and wait for a reply.
*
* @param in netlink message to send
* @param out received netlink message
* @param out_len length of the received message
*/
status_t (*send)(netlink_socket_t *this, struct nlmsghdr *in, struct nlmsghdr **out, size_t *out_len);
/**
* Send a netlink message and wait for its acknowledge.
*
* @param in netlink message to send
*/
status_t (*send_ack)(netlink_socket_t *this, struct nlmsghdr *in);
/**
* Destroy the socket.
*/
void (*destroy)(netlink_socket_t *this);
};
/**
* Create a netlink_socket_t object.
*
* @param protocol protocol type (e.g. NETLINK_XFRM or NETLINK_ROUTE)
*/
netlink_socket_t *netlink_socket_create(int protocol);
/**
* Creates an rtattr and adds it to the given netlink message.
*
* @param hdr netlink message
* @param rta_type type of the rtattr
* @param data data to add to the rtattr
* @param buflen length of the netlink message buffer
*/
void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, size_t buflen);
#endif /* KERNEL_NETLINK_SHARED_H_ */

Some files were not shown because too many files have changed in this diff Show More