ext-auth: Add an ext-auth plugin invoking an external authorization script

Original patch courtesy of Vyronas Tsingaras.
This commit is contained in:
Martin Willi 2014-10-06 11:52:49 +02:00
parent 6890bdc7a0
commit b2c1973ffb
9 changed files with 512 additions and 0 deletions

View File

@ -45,6 +45,7 @@ plugins = \
plugins/eap-tnc.opt \
plugins/eap-ttls.opt \
plugins/error-notify.opt \
plugins/ext-auth.opt \
plugins/gcrypt.opt \
plugins/ha.opt \
plugins/imc-attestation.opt \

15
conf/plugins/ext-auth.opt Normal file
View File

@ -0,0 +1,15 @@
charon.plugins.ext-auth.script =
Shell script to invoke for peer authorization.
Command to pass to the system shell for peer authorization. Authorization
is considered successful if the command executes normally with an exit code
of zero. For all other exit codes IKE_SA authorization is rejected.
The following environment variables get passed to the script:
_IKE_UNIQUE_ID_: The IKE_SA numerical unique identifier.
_IKE_NAME_: The peer configuration connection name.
_IKE_LOCAL_HOST_: Local IKE IP address.
_IKE_REMOTE_HOST_: Remote IKE IP address.
_IKE_LOCAL_ID_: Local IKE identity.
_IKE_REMOTE_ID_: Remote IKE identity.
_IKE_REMOTE_EAP_ID_: Remote EAP or XAuth identity, if used.

View File

@ -189,6 +189,7 @@ ARG_ENABL_SET([eap-peap], [enable EAP PEAP authentication module.])
ARG_ENABL_SET([eap-tnc], [enable EAP TNC trusted network connect module.])
ARG_ENABL_SET([eap-dynamic], [enable dynamic EAP proxy module.])
ARG_ENABL_SET([eap-radius], [enable RADIUS proxy authentication module.])
ARG_ENABL_SET([ext-auth], [enable plugin calling an external authorization script.])
ARG_ENABL_SET([ipseckey], [enable IPSECKEY authentication plugin.])
ARG_ENABL_SET([keychain], [enables OS X Keychain Services credential set.])
ARG_ENABL_SET([pkcs11], [enables the PKCS11 token support plugin.])
@ -1285,6 +1286,7 @@ ADD_PLUGIN([android-dns], [c charon])
ADD_PLUGIN([android-log], [c charon])
ADD_PLUGIN([ha], [c charon])
ADD_PLUGIN([whitelist], [c charon])
ADD_PLUGIN([ext-auth], [c charon])
ADD_PLUGIN([lookip], [c charon])
ADD_PLUGIN([error-notify], [c charon])
ADD_PLUGIN([certexpire], [c charon])
@ -1396,6 +1398,7 @@ AM_CONDITIONAL(USE_KERNEL_LIBIPSEC, test x$kernel_libipsec = xtrue)
AM_CONDITIONAL(USE_KERNEL_WFP, test x$kernel_wfp = xtrue)
AM_CONDITIONAL(USE_KERNEL_IPH, test x$kernel_iph = xtrue)
AM_CONDITIONAL(USE_WHITELIST, test x$whitelist = xtrue)
AM_CONDITIONAL(USE_EXT_AUTH, test x$ext_auth = xtrue)
AM_CONDITIONAL(USE_LOOKIP, test x$lookip = xtrue)
AM_CONDITIONAL(USE_ERROR_NOTIFY, test x$error_notify = xtrue)
AM_CONDITIONAL(USE_CERTEXPIRE, test x$certexpire = xtrue)
@ -1695,6 +1698,7 @@ AC_CONFIG_FILES([
src/libcharon/plugins/kernel_wfp/Makefile
src/libcharon/plugins/kernel_iph/Makefile
src/libcharon/plugins/whitelist/Makefile
src/libcharon/plugins/ext_auth/Makefile
src/libcharon/plugins/lookip/Makefile
src/libcharon/plugins/error_notify/Makefile
src/libcharon/plugins/certexpire/Makefile

View File

@ -258,6 +258,13 @@ if MONOLITHIC
endif
endif
if USE_EXT_AUTH
SUBDIRS += plugins/ext_auth
if MONOLITHIC
libcharon_la_LIBADD += plugins/ext_auth/libstrongswan-ext-auth.la
endif
endif
if USE_EAP_IDENTITY
SUBDIRS += plugins/eap_identity
if MONOLITHIC

View File

@ -0,0 +1,18 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libhydra \
-I$(top_srcdir)/src/libcharon
AM_CFLAGS = \
$(PLUGIN_CFLAGS)
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-ext-auth.la
else
plugin_LTLIBRARIES = libstrongswan-ext-auth.la
endif
libstrongswan_ext_auth_la_SOURCES = ext_auth_plugin.h ext_auth_plugin.c \
ext_auth_listener.h ext_auth_listener.c
libstrongswan_ext_auth_la_LDFLAGS = -module -avoid-version

View File

@ -0,0 +1,203 @@
/*
* Copyright (c) 2014 Vyronas Tsingaras (vtsingaras@it.auth.gr)
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* for vasprintf() */
#define _GNU_SOURCE
#include "ext_auth_listener.h"
#include <daemon.h>
#include <utils/process.h>
#include <stdio.h>
#include <unistd.h>
typedef struct private_ext_auth_listener_t private_ext_auth_listener_t;
/**
* Private data of an ext_auth_listener_t object.
*/
struct private_ext_auth_listener_t {
/**
* Public ext_auth_listener_listener_t interface.
*/
ext_auth_listener_t public;
/**
* Path to authorization program
*/
char *script;
};
/**
* Allocate and push a format string to the environment
*/
static bool push_env(char *envp[], u_int count, char *fmt, ...)
{
int i = 0;
char *str;
va_list args;
while (envp[i])
{
if (++i + 1 >= count)
{
return FALSE;
}
}
va_start(args, fmt);
if (vasprintf(&str, fmt, args) >= 0)
{
envp[i] = str;
}
va_end(args);
return envp[i] != NULL;
}
/**
* Free all allocated environment strings
*/
static void free_env(char *envp[])
{
int i;
for (i = 0; envp[i]; i++)
{
free(envp[i]);
}
}
METHOD(listener_t, authorize, bool,
private_ext_auth_listener_t *this, ike_sa_t *ike_sa,
bool final, bool *success)
{
if (final)
{
FILE *shell;
process_t *process;
char *envp[32] = {};
int out, retval;
*success = FALSE;
push_env(envp, countof(envp), "IKE_UNIQUE_ID=%u",
ike_sa->get_unique_id(ike_sa));
push_env(envp, countof(envp), "IKE_NAME=%s",
ike_sa->get_name(ike_sa));
push_env(envp, countof(envp), "IKE_LOCAL_HOST=%H",
ike_sa->get_my_host(ike_sa));
push_env(envp, countof(envp), "IKE_REMOTE_HOST=%H",
ike_sa->get_other_host(ike_sa));
push_env(envp, countof(envp), "IKE_LOCAL_ID=%Y",
ike_sa->get_my_id(ike_sa));
push_env(envp, countof(envp), "IKE_REMOTE_ID=%Y",
ike_sa->get_other_id(ike_sa));
if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
{
push_env(envp, countof(envp), "IKE_REMOTE_EAP_ID=%Y",
ike_sa->get_other_eap_id(ike_sa));
}
process = process_start_shell(envp, NULL, &out, NULL,
"2>&1 %s", this->script);
if (process)
{
shell = fdopen(out, "r");
if (shell)
{
while (TRUE)
{
char resp[128], *e;
if (fgets(resp, sizeof(resp), shell) == NULL)
{
if (ferror(shell))
{
DBG1(DBG_CFG, "error reading from ext-auth script");
}
break;
}
else
{
e = resp + strlen(resp);
if (e > resp && e[-1] == '\n')
{
e[-1] = '\0';
}
DBG1(DBG_CHD, "ext-auth: %s", resp);
}
}
fclose(shell);
}
else
{
close(out);
}
if (process->wait(process, &retval))
{
if (retval == EXIT_SUCCESS)
{
*success = TRUE;
}
else
{
DBG1(DBG_CFG, "rejecting IKE_SA for ext-auth result: %d",
retval);
}
}
}
free_env(envp);
}
return TRUE;
}
METHOD(ext_auth_listener_t, destroy, void,
private_ext_auth_listener_t *this)
{
free(this);
}
/**
* See header
*/
ext_auth_listener_t *ext_auth_listener_create(char *script)
{
private_ext_auth_listener_t *this;
INIT(this,
.public = {
.listener = {
.authorize = _authorize,
},
.destroy = _destroy,
},
.script = script,
);
return &this->public;
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2014 Vyronas Tsingaras (vtsingaras@it.auth.gr)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup ext_auth_listener ext_auth_listener
* @{ @ingroup ext_auth
*/
#ifndef EXT_AUTH_LISTENER_H_
#define EXT_AUTH_LISTENER_H_
#include <bus/listeners/listener.h>
typedef struct ext_auth_listener_t ext_auth_listener_t;
/**
* Listener using an external script to authorize connection
*/
struct ext_auth_listener_t {
/**
* Implements listener_t interface.
*/
listener_t listener;
/**
* Destroy the listener.
*/
void (*destroy)(ext_auth_listener_t *this);
};
/**
* Create ext_auth_listener instance.
*
* @param script path to authorization script
* @return listener instance
*/
ext_auth_listener_t *ext_auth_listener_create(char *script);
#endif /** ext_auth_LISTENER_H_ @}*/

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2014 Vyronas Tsingaras (vtsingaras@it.auth.gr)
* Copyright (C) 2014 Martin Willi
* Copyright (C) 2014 revosec AG
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "ext_auth_plugin.h"
#include "ext_auth_listener.h"
#include <daemon.h>
typedef struct private_ext_auth_plugin_t private_ext_auth_plugin_t;
/**
* private data of ext_auth plugin
*/
struct private_ext_auth_plugin_t {
/**
* implements plugin interface
*/
ext_auth_plugin_t public;
/**
* Listener verifying peers during authorization
*/
ext_auth_listener_t *listener;
};
METHOD(plugin_t, get_name, char*,
private_ext_auth_plugin_t *this)
{
return "ext-auth";
}
/**
* Create a listener instance, NULL on error
*/
static ext_auth_listener_t* create_listener()
{
char *script;
script = lib->settings->get_str(lib->settings,
"%s.plugins.ext-auth.script", NULL, lib->ns);
if (!script)
{
DBG1(DBG_CFG, "no script for ext-auth script defined, disabled");
return NULL;
}
DBG1(DBG_CFG, "using ext-auth script '%s'", script);
return ext_auth_listener_create(script);
}
/**
* Register listener
*/
static bool plugin_cb(private_ext_auth_plugin_t *this,
plugin_feature_t *feature, bool reg, void *cb_data)
{
if (reg)
{
this->listener = create_listener();
if (!this->listener)
{
return FALSE;
}
charon->bus->add_listener(charon->bus, &this->listener->listener);
}
else
{
if (this->listener)
{
charon->bus->remove_listener(charon->bus, &this->listener->listener);
this->listener->destroy(this->listener);
}
}
return TRUE;
}
METHOD(plugin_t, get_features, int,
private_ext_auth_plugin_t *this, plugin_feature_t *features[])
{
static plugin_feature_t f[] = {
PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
PLUGIN_PROVIDE(CUSTOM, "ext_auth"),
};
*features = f;
return countof(f);
}
METHOD(plugin_t, reload, bool,
private_ext_auth_plugin_t *this)
{
ext_auth_listener_t *listener;
/* reload new listener overlapped */
listener = create_listener();
if (listener)
{
charon->bus->add_listener(charon->bus, &listener->listener);
}
if (this->listener)
{
charon->bus->remove_listener(charon->bus, &this->listener->listener);
this->listener->destroy(this->listener);
}
this->listener = listener;
return TRUE;
}
METHOD(plugin_t, destroy, void,
private_ext_auth_plugin_t *this)
{
free(this);
}
/**
* Plugin constructor
*/
plugin_t *ext_auth_plugin_create()
{
private_ext_auth_plugin_t *this;
INIT(this,
.public = {
.plugin = {
.get_name = _get_name,
.get_features = _get_features,
.reload = _reload,
.destroy = _destroy,
},
},
);
return &this->public.plugin;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2014 Vyronas Tsingaras (vtsingaras@it.auth.gr)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* @defgroup ext_auth ext_auth
* @ingroup cplugins
*
* @defgroup ext_auth_plugin ext_auth_plugin
* @{ @ingroup ext_auth
*/
#ifndef EXT_AUTH_PLUGIN_H_
#define EXT_AUTH_PLUGIN_H_
#include <plugins/plugin.h>
typedef struct ext_auth_plugin_t ext_auth_plugin_t;
/**
* Plugin using an external script to authorize connections.
*/
struct ext_auth_plugin_t {
/**
* Implements plugin interface.
*/
plugin_t plugin;
};
#endif /** EXT_AUTH_PLUGIN_H_ @}*/