Moving charon to libcharon.
This commit is contained in:
@@ -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_ @}*/
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ @}*/
|
||||
@@ -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 @}*/
|
||||
@@ -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_ @}*/
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ @}*/
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ @}*/
|
||||
@@ -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
Reference in New Issue
Block a user