aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/charon-cmd/charon-cmd.8.in6
-rw-r--r--src/charon-cmd/cmd/cmd_connection.c25
-rw-r--r--src/charon-cmd/cmd/cmd_options.c4
-rw-r--r--src/charon-cmd/cmd/cmd_options.h2
-rw-r--r--src/libcharon/plugins/eap_radius/Makefile.am1
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius.c108
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius.h22
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_forward.c3
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_plugin.c4
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_xauth.c202
-rw-r--r--src/libcharon/plugins/eap_radius/eap_radius_xauth.h49
-rw-r--r--src/libradius/radius_message.c89
-rw-r--r--src/libradius/radius_message.h16
-rw-r--r--src/libradius/radius_socket.c51
-rw-r--r--src/libstrongswan/tests/test_utils.c26
-rw-r--r--src/libstrongswan/utils/utils.h23
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/description.txt7
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/evaltest.dat9
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/eap.conf5
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/proxy.conf5
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/sites-available/default39
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/users1
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.conf23
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.secrets5
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/strongswan.conf9
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.conf22
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.secrets3
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/iptables.rules32
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/strongswan.conf11
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/posttest.dat5
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/pretest.dat8
-rw-r--r--testing/tests/ikev1/xauth-rsa-radius/test.conf25
32 files changed, 740 insertions, 100 deletions
diff --git a/src/charon-cmd/charon-cmd.8.in b/src/charon-cmd/charon-cmd.8.in
index c15c7d91f..c9d52c92f 100644
--- a/src/charon-cmd/charon-cmd.8.in
+++ b/src/charon-cmd/charon-cmd.8.in
@@ -77,6 +77,12 @@ DNS name or IP address to connect to.
.BI "\-\-identity " identity
Identity the client uses for the IKE exchange.
.TP
+.BI "\-\-eap\-identity " identity
+Identity the client uses for EAP authentication.
+.TP
+.BI "\-\-xauth\-username " username
+Username the client uses for XAuth authentication.
+.TP
.BI "\-\-remote\-identity " identity
Server identity to expect, defaults to
.IR hostname .
diff --git a/src/charon-cmd/cmd/cmd_connection.c b/src/charon-cmd/cmd/cmd_connection.c
index 2d995f0cd..5c459f99f 100644
--- a/src/charon-cmd/cmd/cmd_connection.c
+++ b/src/charon-cmd/cmd/cmd_connection.c
@@ -102,6 +102,11 @@ struct private_cmd_connection_t {
char *identity;
/**
+ * XAuth/EAP identity
+ */
+ char *xautheap;
+
+ /**
* Is a private key configured
*/
bool key_seen;
@@ -187,6 +192,22 @@ static void add_auth_cfg(private_cmd_connection_t *this, peer_cfg_t *peer_cfg,
if (local)
{
id = identification_create_from_string(this->identity);
+ if (this->xautheap)
+ {
+ switch (class)
+ {
+ case AUTH_CLASS_EAP:
+ auth->add(auth, AUTH_RULE_EAP_IDENTITY,
+ identification_create_from_string(this->xautheap));
+ break;
+ case AUTH_CLASS_XAUTH:
+ auth->add(auth, AUTH_RULE_XAUTH_IDENTITY,
+ identification_create_from_string(this->xautheap));
+ break;
+ default:
+ break;
+ }
+ }
}
else
{
@@ -411,6 +432,10 @@ METHOD(cmd_connection_t, handle, bool,
case CMD_OPT_IDENTITY:
this->identity = arg;
break;
+ case CMD_OPT_EAP_IDENTITY:
+ case CMD_OPT_XAUTH_USER:
+ this->xautheap = arg;
+ break;
case CMD_OPT_RSA:
case CMD_OPT_AGENT:
case CMD_OPT_PKCS12:
diff --git a/src/charon-cmd/cmd/cmd_options.c b/src/charon-cmd/cmd/cmd_options.c
index 175aa4269..597ccda1f 100644
--- a/src/charon-cmd/cmd/cmd_options.c
+++ b/src/charon-cmd/cmd/cmd_options.c
@@ -34,6 +34,10 @@ cmd_option_t cmd_options[CMD_OPT_COUNT] = {
"DNS name or address to connect to", {}},
{ CMD_OPT_IDENTITY, "identity", required_argument, "identity",
"identity the client uses for the IKE exchange", {}},
+ { CMD_OPT_EAP_IDENTITY, "eap-identity", required_argument, "eap-identity",
+ "identity the client uses for EAP authentication", {}},
+ { CMD_OPT_XAUTH_USER, "xauth-username", required_argument, "xauth-username",
+ "username the client uses for XAuth authentication", {}},
{ CMD_OPT_REMOTE_IDENTITY, "remote-identity", required_argument, "identity",
"server identity to expect, defaults to host", {}},
{ CMD_OPT_CERT, "cert", required_argument, "path",
diff --git a/src/charon-cmd/cmd/cmd_options.h b/src/charon-cmd/cmd/cmd_options.h
index 4e5ba6780..6b8b04cdf 100644
--- a/src/charon-cmd/cmd/cmd_options.h
+++ b/src/charon-cmd/cmd/cmd_options.h
@@ -36,6 +36,8 @@ enum cmd_option_type_t {
CMD_OPT_DEBUG,
CMD_OPT_HOST,
CMD_OPT_IDENTITY,
+ CMD_OPT_EAP_IDENTITY,
+ CMD_OPT_XAUTH_USER,
CMD_OPT_REMOTE_IDENTITY,
CMD_OPT_CERT,
CMD_OPT_RSA,
diff --git a/src/libcharon/plugins/eap_radius/Makefile.am b/src/libcharon/plugins/eap_radius/Makefile.am
index 67b540407..6fdb0d099 100644
--- a/src/libcharon/plugins/eap_radius/Makefile.am
+++ b/src/libcharon/plugins/eap_radius/Makefile.am
@@ -17,6 +17,7 @@ endif
libstrongswan_eap_radius_la_SOURCES = \
eap_radius_plugin.h eap_radius_plugin.c \
eap_radius.h eap_radius.c \
+ eap_radius_xauth.h eap_radius_xauth.c \
eap_radius_accounting.h eap_radius_accounting.c \
eap_radius_provider.h eap_radius_provider.c \
eap_radius_dae.h eap_radius_dae.c \
diff --git a/src/libcharon/plugins/eap_radius/eap_radius.c b/src/libcharon/plugins/eap_radius/eap_radius.c
index 1400c75a1..b06b6c392 100644
--- a/src/libcharon/plugins/eap_radius/eap_radius.c
+++ b/src/libcharon/plugins/eap_radius/eap_radius.c
@@ -75,21 +75,6 @@ struct private_eap_radius_t {
* Prefix to prepend to EAP identity
*/
char *id_prefix;
-
- /**
- * Handle the Class attribute as group membership information?
- */
- bool class_group;
-
- /**
- * Handle the Filter-Id attribute as IPsec CHILD_SA name?
- */
- bool filter_id;
-
- /**
- * Format string we use for Called/Calling-Station-Id for a host
- */
- char *station_id_fmt;
};
/**
@@ -163,21 +148,16 @@ static bool radius2ike(private_eap_radius_t *this,
}
/**
- * Add a set of RADIUS attributes to a request message
+ * See header.
*/
-static void add_radius_request_attrs(private_eap_radius_t *this,
- radius_message_t *request)
+void eap_radius_build_attributes(radius_message_t *request)
{
ike_sa_t *ike_sa;
host_t *host;
- char buf[40];
+ char buf[40], *station_id_fmt;;
u_int32_t value;
chunk_t chunk;
- chunk = chunk_from_str(this->id_prefix);
- chunk = chunk_cata("cc", chunk, this->peer->get_encoding(this->peer));
- request->add(request, RAT_USER_NAME, chunk);
-
/* virtual NAS-Port-Type */
value = htonl(5);
request->add(request, RAT_NAS_PORT_TYPE, chunk_from_thing(value));
@@ -205,13 +185,37 @@ static void add_radius_request_attrs(private_eap_radius_t *this,
default:
break;
}
- snprintf(buf, sizeof(buf), this->station_id_fmt, host);
+ if (lib->settings->get_bool(lib->settings,
+ "%s.plugins.eap-radius.station_id_with_port",
+ TRUE, charon->name))
+ {
+ station_id_fmt = "%#H";
+ }
+ else
+ {
+ station_id_fmt = "%H";
+ }
+ snprintf(buf, sizeof(buf), station_id_fmt, host);
request->add(request, RAT_CALLED_STATION_ID, chunk_from_str(buf));
host = ike_sa->get_other_host(ike_sa);
- snprintf(buf, sizeof(buf), this->station_id_fmt, host);
+ snprintf(buf, sizeof(buf), station_id_fmt, host);
request->add(request, RAT_CALLING_STATION_ID, chunk_from_str(buf));
}
+}
+
+/**
+ * Add a set of RADIUS attributes to a request message
+ */
+static void add_radius_request_attrs(private_eap_radius_t *this,
+ radius_message_t *request)
+{
+ chunk_t chunk;
+ chunk = chunk_from_str(this->id_prefix);
+ chunk = chunk_cata("cc", chunk, this->peer->get_encoding(this->peer));
+ request->add(request, RAT_USER_NAME, chunk);
+
+ eap_radius_build_attributes(request);
eap_radius_forward_from_ike(request);
}
@@ -268,7 +272,7 @@ METHOD(eap_method_t, initiate, status_t,
/**
* Handle the Class attribute as group membership information
*/
-static void process_class(private_eap_radius_t *this, radius_message_t *msg)
+static void process_class(radius_message_t *msg)
{
enumerator_t *enumerator;
chunk_t data;
@@ -305,7 +309,7 @@ static void process_class(private_eap_radius_t *this, radius_message_t *msg)
/**
* Handle the Filter-Id attribute as IPsec CHILD_SA name
*/
-static void process_filter_id(private_eap_radius_t *this, radius_message_t *msg)
+static void process_filter_id(radius_message_t *msg)
{
enumerator_t *enumerator;
int type;
@@ -361,7 +365,7 @@ static void process_filter_id(private_eap_radius_t *this, radius_message_t *msg)
/**
* Handle Session-Timeout attribte and Interim updates
*/
-static void process_timeout(private_eap_radius_t *this, radius_message_t *msg)
+static void process_timeout(radius_message_t *msg)
{
enumerator_t *enumerator;
ike_sa_t *ike_sa;
@@ -390,8 +394,7 @@ static void process_timeout(private_eap_radius_t *this, radius_message_t *msg)
/**
* Handle Framed-IP-Address and other IKE configuration attributes
*/
-static void process_cfg_attributes(private_eap_radius_t *this,
- radius_message_t *msg)
+static void process_cfg_attributes(radius_message_t *msg)
{
eap_radius_provider_t *provider;
enumerator_t *enumerator;
@@ -444,6 +447,25 @@ static void process_cfg_attributes(private_eap_radius_t *this,
}
}
+/**
+ * See header.
+ */
+void eap_radius_process_attributes(radius_message_t *message)
+{
+ if (lib->settings->get_bool(lib->settings,
+ "%s.plugins.eap-radius.class_group", FALSE, charon->name))
+ {
+ process_class(message);
+ }
+ if (lib->settings->get_bool(lib->settings,
+ "%s.plugins.eap-radius.filter_id", FALSE, charon->name))
+ {
+ process_filter_id(message);
+ }
+ process_timeout(message);
+ process_cfg_attributes(message);
+}
+
METHOD(eap_method_t, process, status_t,
private_eap_radius_t *this, eap_payload_t *in, eap_payload_t **out)
{
@@ -481,16 +503,7 @@ METHOD(eap_method_t, process, status_t,
status = FAILED;
break;
case RMC_ACCESS_ACCEPT:
- if (this->class_group)
- {
- process_class(this, response);
- }
- if (this->filter_id)
- {
- process_filter_id(this, response);
- }
- process_timeout(this, response);
- process_cfg_attributes(this, response);
+ eap_radius_process_attributes(response);
DBG1(DBG_IKE, "RADIUS authentication of '%Y' successful",
this->peer);
status = SUCCESS;
@@ -591,22 +604,7 @@ eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer
.id_prefix = lib->settings->get_str(lib->settings,
"%s.plugins.eap-radius.id_prefix", "",
charon->name),
- .class_group = lib->settings->get_bool(lib->settings,
- "%s.plugins.eap-radius.class_group", FALSE,
- charon->name),
- .filter_id = lib->settings->get_bool(lib->settings,
- "%s.plugins.eap-radius.filter_id", FALSE,
- charon->name),
);
- if (lib->settings->get_bool(lib->settings,
- "%s.plugins.eap-radius.station_id_with_port", TRUE, charon->name))
- {
- this->station_id_fmt = "%#H";
- }
- else
- {
- this->station_id_fmt = "%H";
- }
this->client = eap_radius_create_client();
if (!this->client)
{
diff --git a/src/libcharon/plugins/eap_radius/eap_radius.h b/src/libcharon/plugins/eap_radius/eap_radius.h
index 875543554..ce583ac44 100644
--- a/src/libcharon/plugins/eap_radius/eap_radius.h
+++ b/src/libcharon/plugins/eap_radius/eap_radius.h
@@ -24,6 +24,7 @@
typedef struct eap_radius_t eap_radius_t;
#include <sa/eap/eap_method.h>
+#include <radius_message.h>
/**
* Implementation of the eap_method_t interface using a RADIUS server.
@@ -45,4 +46,25 @@ struct eap_radius_t {
*/
eap_radius_t *eap_radius_create(identification_t *server, identification_t *peer);
+/**
+ * Process additional attributes from an Access-Accept.
+ *
+ * Parses and applies additional authorization attributes from an Accept
+ * message, such as group membership information or IKE configuration
+ * attributes.
+ *
+ * @param message Access-Accept message to process
+ */
+void eap_radius_process_attributes(radius_message_t *message);
+
+/**
+ * Build additional attributes for an Access-Request.
+ *
+ * Adds additional RADIUS attributes to use with Access-Request, such as
+ * different NAS specific attributes.
+ *
+ * @param message Access-Request message to add attributes to
+ */
+void eap_radius_build_attributes(radius_message_t *message);
+
#endif /** EAP_RADIUS_H_ @}*/
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_forward.c b/src/libcharon/plugins/eap_radius/eap_radius_forward.c
index e9124877c..3e80e8918 100644
--- a/src/libcharon/plugins/eap_radius/eap_radius_forward.c
+++ b/src/libcharon/plugins/eap_radius/eap_radius_forward.c
@@ -248,7 +248,8 @@ static void ike2queue(message_t *message, linked_list_t *queue,
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
- if (payload->get_type(payload) == NOTIFY)
+ if (payload->get_type(payload) == NOTIFY ||
+ payload->get_type(payload) == NOTIFY_V1)
{
notify = (notify_payload_t*)payload;
if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE)
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c
index 63592db06..90a4ef6de 100644
--- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c
+++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c
@@ -17,6 +17,7 @@
#include "eap_radius_plugin.h"
#include "eap_radius.h"
+#include "eap_radius_xauth.h"
#include "eap_radius_accounting.h"
#include "eap_radius_dae.h"
#include "eap_radius_forward.h"
@@ -236,6 +237,9 @@ METHOD(plugin_t, get_features, int,
PLUGIN_CALLBACK(eap_method_register, eap_radius_create),
PLUGIN_PROVIDE(EAP_SERVER, EAP_RADIUS),
PLUGIN_DEPENDS(CUSTOM, "eap-radius"),
+ PLUGIN_CALLBACK(xauth_method_register, eap_radius_xauth_create_server),
+ PLUGIN_PROVIDE(XAUTH_SERVER, "radius"),
+ PLUGIN_DEPENDS(CUSTOM, "eap-radius"),
PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
PLUGIN_PROVIDE(CUSTOM, "eap-radius"),
PLUGIN_DEPENDS(HASHER, HASH_MD5),
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_xauth.c b/src/libcharon/plugins/eap_radius/eap_radius_xauth.c
new file mode 100644
index 000000000..bd960d2bc
--- /dev/null
+++ b/src/libcharon/plugins/eap_radius/eap_radius_xauth.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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_xauth.h"
+#include "eap_radius_plugin.h"
+#include "eap_radius.h"
+#include "eap_radius_forward.h"
+
+#include <daemon.h>
+#include <radius_client.h>
+
+
+typedef struct private_eap_radius_xauth_t private_eap_radius_xauth_t;
+
+/**
+ * Private data of an eap_radius_xauth_t object.
+ */
+struct private_eap_radius_xauth_t {
+
+ /**
+ * Public interface.
+ */
+ eap_radius_xauth_t public;
+
+ /**
+ * ID of the server
+ */
+ identification_t *server;
+
+ /**
+ * ID of the peer
+ */
+ identification_t *peer;
+
+ /**
+ * RADIUS connection
+ */
+ radius_client_t *client;
+};
+
+METHOD(xauth_method_t, initiate, status_t,
+ private_eap_radius_xauth_t *this, cp_payload_t **out)
+{
+ cp_payload_t *cp;
+
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST);
+ cp->add_attribute(cp, configuration_attribute_create_chunk(
+ CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_NAME, chunk_empty));
+ cp->add_attribute(cp, configuration_attribute_create_chunk(
+ CONFIGURATION_ATTRIBUTE_V1, XAUTH_USER_PASSWORD, chunk_empty));
+ *out = cp;
+ return NEED_MORE;
+}
+
+/**
+ * Verify a password using RADIUS User-Name/User-Password attributes
+ */
+static status_t verify_radius(private_eap_radius_xauth_t *this, chunk_t pass)
+{
+ radius_message_t *request, *response;
+ status_t status = FAILED;
+
+ request = radius_message_create(RMC_ACCESS_REQUEST);
+ request->add(request, RAT_USER_NAME, this->peer->get_encoding(this->peer));
+ request->add(request, RAT_USER_PASSWORD, pass);
+
+ eap_radius_build_attributes(request);
+ eap_radius_forward_from_ike(request);
+
+ response = this->client->request(this->client, request);
+ if (response)
+ {
+ eap_radius_forward_to_ike(response);
+ switch (response->get_code(response))
+ {
+ case RMC_ACCESS_ACCEPT:
+ eap_radius_process_attributes(response);
+ status = SUCCESS;
+ break;
+ case RMC_ACCESS_CHALLENGE:
+ DBG1(DBG_IKE, "RADIUS Access-Challenge not supported");
+ /* FALL */
+ case RMC_ACCESS_REJECT:
+ default:
+ DBG1(DBG_IKE, "RADIUS authentication of '%Y' failed",
+ this->peer);
+ break;
+ }
+ response->destroy(response);
+ }
+ else
+ {
+ eap_radius_handle_timeout(NULL);
+ }
+ request->destroy(request);
+ return status;
+}
+
+METHOD(xauth_method_t, process, status_t,
+ private_eap_radius_xauth_t *this, cp_payload_t *in, cp_payload_t **out)
+{
+ configuration_attribute_t *attr;
+ enumerator_t *enumerator;
+ identification_t *id;
+ chunk_t user = chunk_empty, pass = chunk_empty;
+
+ enumerator = in->create_attribute_enumerator(in);
+ while (enumerator->enumerate(enumerator, &attr))
+ {
+ switch (attr->get_type(attr))
+ {
+ case XAUTH_USER_NAME:
+ user = attr->get_chunk(attr);
+ break;
+ case XAUTH_USER_PASSWORD:
+ pass = attr->get_chunk(attr);
+ /* trim password to any null termination. As User-Password
+ * uses null padding, we can't have any null in it, and some
+ * clients actually send null terminated strings (Android). */
+ pass.len = strnlen(pass.ptr, pass.len);
+ break;
+ default:
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (!user.ptr || !pass.ptr)
+ {
+ DBG1(DBG_IKE, "peer did not respond to our XAuth request");
+ return FAILED;
+ }
+ if (user.len)
+ {
+ id = identification_create_from_data(user);
+ if (!id)
+ {
+ DBG1(DBG_IKE, "failed to parse provided XAuth username");
+ return FAILED;
+ }
+ this->peer->destroy(this->peer);
+ this->peer = id;
+ }
+ return verify_radius(this, pass);
+}
+
+METHOD(xauth_method_t, get_identity, identification_t*,
+ private_eap_radius_xauth_t *this)
+{
+ return this->peer;
+}
+
+METHOD(xauth_method_t, destroy, void,
+ private_eap_radius_xauth_t *this)
+{
+ DESTROY_IF(this->client);
+ this->server->destroy(this->server);
+ this->peer->destroy(this->peer);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+eap_radius_xauth_t *eap_radius_xauth_create_server(identification_t *server,
+ identification_t *peer)
+{
+ private_eap_radius_xauth_t *this;
+
+ INIT(this,
+ .public = {
+ .xauth_method = {
+ .initiate = _initiate,
+ .process = _process,
+ .get_identity = _get_identity,
+ .destroy = _destroy,
+ },
+ },
+ .server = server->clone(server),
+ .peer = peer->clone(peer),
+ .client = eap_radius_create_client(),
+ );
+
+ if (!this->client)
+ {
+ destroy(this);
+ return NULL;
+ }
+ return &this->public;
+}
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_xauth.h b/src/libcharon/plugins/eap_radius/eap_radius_xauth.h
new file mode 100644
index 000000000..8571bbc9f
--- /dev/null
+++ b/src/libcharon/plugins/eap_radius/eap_radius_xauth.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 Martin Willi
+ * Copyright (C) 2013 revosec AG
+ *
+ * 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_xauth eap_radius_xauth
+ * @{ @ingroup eap_radius
+ */
+
+#ifndef EAP_RADIUS_XAUTH_H_
+#define EAP_RADIUS_XAUTH_H_
+
+#include <sa/xauth/xauth_method.h>
+
+typedef struct eap_radius_xauth_t eap_radius_xauth_t;
+
+/**
+ * XAuth backend using plain RADIUS authentication (no EAP involved).
+ */
+struct eap_radius_xauth_t {
+
+ /**
+ * Implements XAuth module interface
+ */
+ xauth_method_t xauth_method;
+};
+
+/**
+ * Creates the RADIUS XAuth method, acting as server.
+ *
+ * @param server ID of the XAuth server
+ * @param peer ID of the XAuth client
+ * @return xauth_generic_t object
+ */
+eap_radius_xauth_t *eap_radius_xauth_create_server(identification_t *server,
+ identification_t *peer);
+
+#endif /** EAP_RADIUS_XAUTH_H_ @}*/
diff --git a/src/libradius/radius_message.c b/src/libradius/radius_message.c
index e7717ff7a..3905a06c7 100644
--- a/src/libradius/radius_message.c
+++ b/src/libradius/radius_message.c
@@ -65,6 +65,11 @@ struct private_radius_message_t {
* message data, allocated
*/
rmsg_t *msg;
+
+ /**
+ * User-Password to encrypt and encode, if any
+ */
+ chunk_t password;
};
/**
@@ -356,6 +361,15 @@ METHOD(radius_message_t, add, void,
{
rattr_t *attribute;
+ if (type == RAT_USER_PASSWORD && !this->password.len)
+ {
+ /* store a null-padded password */
+ this->password = chunk_alloc(round_up(data.len, HASH_SIZE_MD5));
+ memset(this->password.ptr + data.len, 0, this->password.len - data.len);
+ memcpy(this->password.ptr, data.ptr, data.len);
+ return;
+ }
+
data.len = min(data.len, MAX_RADIUS_ATTRIBUTE_SIZE);
this->msg = realloc(this->msg,
ntohs(this->msg->length) + sizeof(rattr_t) + data.len);
@@ -366,6 +380,67 @@ METHOD(radius_message_t, add, void,
this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
}
+METHOD(radius_message_t, crypt, bool,
+ private_radius_message_t *this, chunk_t salt, chunk_t in, chunk_t out,
+ chunk_t secret, hasher_t *hasher)
+{
+ char b[HASH_SIZE_MD5];
+
+ /**
+ * 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)
+ *
+ * P/C = Plain/Crypted => in/out
+ * S = secret
+ * R = authenticator
+ * A = salt
+ */
+ if (in.len != out.len)
+ {
+ return FALSE;
+ }
+ if (in.len % HASH_SIZE_MD5 || in.len < HASH_SIZE_MD5)
+ {
+ return FALSE;
+ }
+ if (out.ptr != in.ptr)
+ {
+ memcpy(out.ptr, in.ptr, in.len);
+ }
+ /* Preparse seed for first round:
+ * b(1) = MD5(S + R + A) */
+ if (!hasher->get_hash(hasher, secret, NULL) ||
+ !hasher->get_hash(hasher,
+ chunk_from_thing(this->msg->authenticator), NULL) ||
+ !hasher->get_hash(hasher, salt, b))
+ {
+ return FALSE;
+ }
+ while (in.len)
+ {
+ /* p(i) = b(i) xor c(1) */
+ memxor(out.ptr, b, HASH_SIZE_MD5);
+
+ out = chunk_skip(out, HASH_SIZE_MD5);
+ if (out.len)
+ {
+ /* Prepare seed for next round::
+ * b(i) = MD5(S + c(i-1)) */
+ if (!hasher->get_hash(hasher, secret, NULL) ||
+ !hasher->get_hash(hasher,
+ chunk_create(in.ptr, HASH_SIZE_MD5), b))
+ {
+ return FALSE;
+ }
+ }
+ in = chunk_skip(in, HASH_SIZE_MD5);
+ }
+ return TRUE;
+}
+
METHOD(radius_message_t, sign, bool,
private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
hasher_t *hasher, signer_t *signer, rng_t *rng, bool msg_auth)
@@ -391,6 +466,18 @@ METHOD(radius_message_t, sign, bool,
}
}
+ if (this->password.len)
+ {
+ /* encrypt password inline */
+ if (!crypt(this, chunk_empty, this->password, this->password,
+ secret, hasher))
+ {
+ return FALSE;
+ }
+ add(this, RAT_USER_PASSWORD, this->password);
+ chunk_clear(&this->password);
+ }
+
if (msg_auth)
{
char buf[HASH_SIZE_MD5];
@@ -540,6 +627,7 @@ METHOD(radius_message_t, get_encoding, chunk_t,
METHOD(radius_message_t, destroy, void,
private_radius_message_t *this)
{
+ chunk_clear(&this->password);
free(this->msg);
free(this);
}
@@ -563,6 +651,7 @@ static private_radius_message_t *radius_message_create_empty()
.get_encoding = _get_encoding,
.sign = _sign,
.verify = _verify,
+ .crypt = _crypt,
.destroy = _destroy,
},
);
diff --git a/src/libradius/radius_message.h b/src/libradius/radius_message.h
index c49323490..4ce03a44e 100644
--- a/src/libradius/radius_message.h
+++ b/src/libradius/radius_message.h
@@ -285,6 +285,22 @@ struct radius_message_t {
hasher_t *hasher, signer_t *signer);
/**
+ * Perform RADIUS attribute en-/decryption.
+ *
+ * Performs en-/decryption by XOring the hash-extended secret into data,
+ * as specified in RFC 2865 5.2 and used by RFC 2548.
+ *
+ * @param salt salt to append to message authenticator, if any
+ * @param in data to en-/decrypt, multiple of HASH_SIZE_MD5
+ * @param out en-/decrypted data, length equal to in
+ * @param secret RADIUS secret
+ * @param hasher MD5 hasher
+ * @return TRUE if en-/decryption successful
+ */
+ bool (*crypt)(radius_message_t *this, chunk_t salt, chunk_t in, chunk_t out,
+ chunk_t secret, hasher_t *hasher);
+
+ /**
* Destroy the message.
*/
void (*destroy)(radius_message_t *this);
diff --git a/src/libradius/radius_socket.c b/src/libradius/radius_socket.c
index 7dab968d8..f432151c0 100644
--- a/src/libradius/radius_socket.c
+++ b/src/libradius/radius_socket.c
@@ -233,54 +233,17 @@ METHOD(radius_socket_t, request, radius_message_t*,
static chunk_t decrypt_mppe_key(private_radius_socket_t *this, u_int16_t salt,
chunk_t C, radius_message_t *request)
{
- chunk_t A, R, P, seed;
- u_char *c, *p;
+ chunk_t decrypted;
- /**
- * 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;
- }
-
- 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)) */
- if (!this->hasher->get_hash(this->hasher, this->secret, NULL) ||
- !this->hasher->get_hash(this->hasher, seed, p))
- {
- return chunk_empty;
- }
-
- /* 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;
- }
-
- /* remove truncation, first byte is key length */
- if (*P.ptr >= P.len)
+ decrypted = chunk_alloca(C.len);
+ if (!request->crypt(request, chunk_from_thing(salt), C, decrypted,
+ this->secret, this->hasher) ||
+ decrypted.ptr[0] >= decrypted.len)
{ /* decryption failed? */
return chunk_empty;
}
- return chunk_clone(chunk_create(P.ptr + 1, *P.ptr));
+ /* remove truncation, first byte is key length */
+ return chunk_clone(chunk_create(decrypted.ptr + 1, decrypted.ptr[0]));
}
METHOD(radius_socket_t, decrypt_msk, chunk_t,
diff --git a/src/libstrongswan/tests/test_utils.c b/src/libstrongswan/tests/test_utils.c
index 811882e53..d9f1726ff 100644
--- a/src/libstrongswan/tests/test_utils.c
+++ b/src/libstrongswan/tests/test_utils.c
@@ -166,6 +166,28 @@ START_TEST(test_untoh)
END_TEST
/*******************************************************************************
+ * round_up/down
+ */
+
+START_TEST(test_round)
+{
+ ck_assert_int_eq(round_up(0, 4), 0);
+ ck_assert_int_eq(round_up(1, 4), 4);
+ ck_assert_int_eq(round_up(2, 4), 4);
+ ck_assert_int_eq(round_up(3, 4), 4);
+ ck_assert_int_eq(round_up(4, 4), 4);
+ ck_assert_int_eq(round_up(5, 4), 8);
+
+ ck_assert_int_eq(round_down(0, 4), 0);
+ ck_assert_int_eq(round_down(1, 4), 0);
+ ck_assert_int_eq(round_down(2, 4), 0);
+ ck_assert_int_eq(round_down(3, 4), 0);
+ ck_assert_int_eq(round_down(4, 4), 4);
+ ck_assert_int_eq(round_down(5, 4), 4);
+}
+END_TEST
+
+/*******************************************************************************
* memxor
*/
@@ -416,6 +438,10 @@ Suite *utils_suite_create()
tcase_add_test(tc, test_untoh);
suite_add_tcase(s, tc);
+ tc = tcase_create("round");
+ tcase_add_test(tc, test_round);
+ suite_add_tcase(s, tc);
+
tc = tcase_create("memxor");
tcase_add_test(tc, test_memxor);
tcase_add_test(tc, test_memxor_aligned);
diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h
index 0e103de9c..d055f712d 100644
--- a/src/libstrongswan/utils/utils.h
+++ b/src/libstrongswan/utils/utils.h
@@ -671,6 +671,29 @@ static inline u_int64_t untoh64(void *network)
}
/**
+ * Round up size to be multiple of alignement
+ */
+static inline size_t round_up(size_t size, int alignement)
+{
+ int remainder;
+
+ remainder = size % alignement;
+ if (remainder)
+ {
+ size += alignement - remainder;
+ }
+ return size;
+}
+
+/**
+ * Round down size to be a multiple of alignement
+ */
+static inline size_t round_down(size_t size, int alignement)
+{
+ return size - (size % alignement);
+}
+
+/**
* Special type to count references
*/
typedef u_int refcount_t;
diff --git a/testing/tests/ikev1/xauth-rsa-radius/description.txt b/testing/tests/ikev1/xauth-rsa-radius/description.txt
new file mode 100644
index 000000000..fb30d163e
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/description.txt
@@ -0,0 +1,7 @@
+The roadwarrior <b>carol</b> sets up a connection to gateway <b>moon</b>.
+The authentication is based on RSA signatures (<b>RSASIG</b>) using X.509 certificates
+followed by extended authentication (<b>XAUTH</b>) of <b>carol</b> based on a user name
+equal to the <b>IKEv1</b> identity (<b>carol@strongswan.org</b>) and a user password
+defined and stored by <b>carol</b> in ipsec.secrets. Gateway <b>moon</b> verifies
+<b>carol</b>'s XAUTH user credentials using a RADIUS connection with AAA server
+<b>alice</b>.
diff --git a/testing/tests/ikev1/xauth-rsa-radius/evaltest.dat b/testing/tests/ikev1/xauth-rsa-radius/evaltest.dat
new file mode 100644
index 000000000..ee60292a3
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/evaltest.dat
@@ -0,0 +1,9 @@
+carol::cat /var/log/daemon.log::authentication of 'moon.strongswan.org' with RSA successful::YES
+moon:: cat /var/log/daemon.log::XAuth authentication of 'carol@strongswan.org' successful::YES
+moon:: ipsec status 2> /dev/null::rw.*ESTABLISHED.*moon.strongswan.org.*carol@strongswan.org::YES
+carol::ipsec status 2> /dev/null::home.*ESTABLISHED.*carol@strongswan.org.*moon.strongswan.org::YES
+moon:: ipsec status 2> /dev/null::rw.*INSTALLED, TUNNEL::YES
+carol::ipsec status 2> /dev/null::home.*INSTALLED, TUNNEL::YES
+carol::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_req=1::YES
+moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES
+moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/eap.conf b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/eap.conf
new file mode 100644
index 000000000..623f42904
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/eap.conf
@@ -0,0 +1,5 @@
+eap {
+ default_eap_type = md5
+ md5 {
+ }
+}
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/proxy.conf b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/proxy.conf
new file mode 100644
index 000000000..23cba8d11
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/proxy.conf
@@ -0,0 +1,5 @@
+realm strongswan.org {
+ type = radius
+ authhost = LOCAL
+ accthost = LOCAL
+}
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/sites-available/default b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/sites-available/default
new file mode 100644
index 000000000..929b6cd74
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/sites-available/default
@@ -0,0 +1,39 @@
+authorize {
+ suffix
+ files
+}
+
+authenticate {
+ pap
+}
+
+preacct {
+ preprocess
+ acct_unique
+ suffix
+ files
+}
+
+accounting {
+ detail
+ unix
+ radutmp
+ attr_filter.accounting_response
+}
+
+session {
+ radutmp
+}
+
+post-auth {
+ exec
+ Post-Auth-Type REJECT {
+ attr_filter.access_reject
+ }
+}
+
+pre-proxy {
+}
+
+post-proxy {
+}
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/users b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/users
new file mode 100644
index 000000000..4fb07b912
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/alice/etc/freeradius/users
@@ -0,0 +1 @@
+carol Cleartext-Password := "4iChxLT3"
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.conf b/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.conf
new file mode 100644
index 000000000..2fdd60f00
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.conf
@@ -0,0 +1,23 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+ ikelifetime=60m
+ keylife=20m
+ rekeymargin=3m
+ keyingtries=1
+ keyexchange=ikev1
+
+conn home
+ left=PH_IP_CAROL
+ leftcert=carolCert.pem
+ leftid=carol@strongswan.org
+ leftauth=pubkey
+ leftauth2=xauth
+ leftfirewall=yes
+ right=PH_IP_MOON
+ rightsubnet=10.1.0.0/16
+ rightid=@moon.strongswan.org
+ rightauth=pubkey
+ auto=add
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.secrets b/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.secrets
new file mode 100644
index 000000000..d66f3fc24
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/ipsec.secrets
@@ -0,0 +1,5 @@
+# /etc/ipsec.secrets - strongSwan IPsec secrets file
+
+: RSA carolKey.pem "nH5ZQEWtku0RJEZ6"
+
+carol@strongswan.org : XAUTH "4iChxLT3"
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/strongswan.conf b/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/strongswan.conf
new file mode 100644
index 000000000..5cd9bf11e
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/carol/etc/strongswan.conf
@@ -0,0 +1,9 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = sha1 sha2 md5 aes des hmac pem pkcs1 x509 revocation gmp random nonce curl xauth-generic kernel-netlink socket-default updown stroke
+}
+
+libstrongswan {
+ dh_exponent_ansi_x9_42 = no
+}
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.conf b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.conf
new file mode 100644
index 000000000..f4ee067d5
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.conf
@@ -0,0 +1,22 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+ ikelifetime=60m
+ keylife=20m
+ rekeymargin=3m
+ keyingtries=1
+ keyexchange=ikev1
+
+conn rw
+ left=PH_IP_MOON
+ leftsubnet=10.1.0.0/16
+ leftid=@moon.strongswan.org
+ leftcert=moonCert.pem
+ leftauth=pubkey
+ leftfirewall=yes
+ right=%any
+ rightauth=pubkey
+ rightauth2=xauth-radius
+ auto=add
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.secrets b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.secrets
new file mode 100644
index 000000000..e86d6aa5c
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/ipsec.secrets
@@ -0,0 +1,3 @@
+# /etc/ipsec.secrets - strongSwan IPsec secrets file
+
+: RSA moonKey.pem
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/iptables.rules b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/iptables.rules
new file mode 100644
index 000000000..1eb755354
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/iptables.rules
@@ -0,0 +1,32 @@
+*filter
+
+# default policy is DROP
+-P INPUT DROP
+-P OUTPUT DROP
+-P FORWARD DROP
+
+# allow esp
+-A INPUT -i eth0 -p 50 -j ACCEPT
+-A OUTPUT -o eth0 -p 50 -j ACCEPT
+
+# allow IKE
+-A INPUT -i eth0 -p udp --sport 500 --dport 500 -j ACCEPT
+-A OUTPUT -o eth0 -p udp --dport 500 --sport 500 -j ACCEPT
+
+# allow MobIKE
+-A INPUT -i eth0 -p udp --sport 4500 --dport 4500 -j ACCEPT
+-A OUTPUT -o eth0 -p udp --dport 4500 --sport 4500 -j ACCEPT
+
+# allow ssh
+-A INPUT -p tcp --dport 22 -j ACCEPT
+-A OUTPUT -p tcp --sport 22 -j ACCEPT
+
+# allow crl fetch from winnetou
+-A INPUT -i eth0 -p tcp --sport 80 -s PH_IP_WINNETOU -j ACCEPT
+-A OUTPUT -o eth0 -p tcp --dport 80 -d PH_IP_WINNETOU -j ACCEPT
+
+# allow RADIUS protocol with alice
+-A INPUT -i eth1 -p udp --sport 1812 -s PH_IP_ALICE -j ACCEPT
+-A OUTPUT -o eth1 -p udp --dport 1812 -d PH_IP_ALICE -j ACCEPT
+
+COMMIT
diff --git a/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/strongswan.conf b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/strongswan.conf
new file mode 100644
index 000000000..77266cfa0
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/hosts/moon/etc/strongswan.conf
@@ -0,0 +1,11 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = curl aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 revocation hmac stroke kernel-netlink socket-default fips-prf eap-radius updown
+ plugins {
+ eap-radius {
+ secret = gv6URkSs
+ server = PH_IP_ALICE
+ }
+ }
+}
diff --git a/testing/tests/ikev1/xauth-rsa-radius/posttest.dat b/testing/tests/ikev1/xauth-rsa-radius/posttest.dat
new file mode 100644
index 000000000..181949fb5
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/posttest.dat
@@ -0,0 +1,5 @@
+moon::ipsec stop
+carol::ipsec stop
+alice::killall radiusd
+moon::iptables-restore < /etc/iptables.flush
+carol::iptables-restore < /etc/iptables.flush
diff --git a/testing/tests/ikev1/xauth-rsa-radius/pretest.dat b/testing/tests/ikev1/xauth-rsa-radius/pretest.dat
new file mode 100644
index 000000000..9adc43d3e
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/pretest.dat
@@ -0,0 +1,8 @@
+moon::iptables-restore < /etc/iptables.rules
+carol::iptables-restore < /etc/iptables.rules
+alice::radiusd
+moon::ipsec start
+carol::ipsec start
+carol::sleep 1
+carol::ipsec up home
+carol::sleep 1
diff --git a/testing/tests/ikev1/xauth-rsa-radius/test.conf b/testing/tests/ikev1/xauth-rsa-radius/test.conf
new file mode 100644
index 000000000..b4088e8b4
--- /dev/null
+++ b/testing/tests/ikev1/xauth-rsa-radius/test.conf
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# guest instances used for this test
+
+# All guest instances that are required for this test
+#
+VIRTHOSTS="alice carol moon"
+
+# Corresponding block diagram
+#
+DIAGRAM="a-m-c.png"
+
+# Guest instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="moon"
+
+# Guest instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="moon carol"
+
+# Guest instances on which FreeRadius is started
+#
+RADIUSHOSTS="alice"