ext-auth: Add an ext-auth plugin invoking an external authorization script
authorMartin Willi <martin@revosec.ch>
Mon, 6 Oct 2014 09:52:49 +0000 (11:52 +0200)
committerMartin Willi <martin@revosec.ch>
Mon, 6 Oct 2014 16:30:46 +0000 (18:30 +0200)
Original patch courtesy of Vyronas Tsingaras.

conf/Makefile.am
conf/plugins/ext-auth.opt [new file with mode: 0644]
configure.ac
src/libcharon/Makefile.am
src/libcharon/plugins/ext_auth/Makefile.am [new file with mode: 0644]
src/libcharon/plugins/ext_auth/ext_auth_listener.c [new file with mode: 0644]
src/libcharon/plugins/ext_auth/ext_auth_listener.h [new file with mode: 0644]
src/libcharon/plugins/ext_auth/ext_auth_plugin.c [new file with mode: 0644]
src/libcharon/plugins/ext_auth/ext_auth_plugin.h [new file with mode: 0644]

index ee9ce72..e507739 100644 (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 \
diff --git a/conf/plugins/ext-auth.opt b/conf/plugins/ext-auth.opt
new file mode 100644 (file)
index 0000000..bf127b9
--- /dev/null
@@ -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.
index 85f7cb3..100e29a 100644 (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
index e81c424..0eaabf5 100644 (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
diff --git a/src/libcharon/plugins/ext_auth/Makefile.am b/src/libcharon/plugins/ext_auth/Makefile.am
new file mode 100644 (file)
index 0000000..d51ea88
--- /dev/null
@@ -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
diff --git a/src/libcharon/plugins/ext_auth/ext_auth_listener.c b/src/libcharon/plugins/ext_auth/ext_auth_listener.c
new file mode 100644 (file)
index 0000000..06cec20
--- /dev/null
@@ -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;
+}
diff --git a/src/libcharon/plugins/ext_auth/ext_auth_listener.h b/src/libcharon/plugins/ext_auth/ext_auth_listener.h
new file mode 100644 (file)
index 0000000..3fec830
--- /dev/null
@@ -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_ @}*/
diff --git a/src/libcharon/plugins/ext_auth/ext_auth_plugin.c b/src/libcharon/plugins/ext_auth/ext_auth_plugin.c
new file mode 100644 (file)
index 0000000..b3698c7
--- /dev/null
@@ -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;
+}
diff --git a/src/libcharon/plugins/ext_auth/ext_auth_plugin.h b/src/libcharon/plugins/ext_auth/ext_auth_plugin.h
new file mode 100644 (file)
index 0000000..1288e24
--- /dev/null
@@ -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_ @}*/