mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-08 00:02:03 -04:00
Merge branch 'ext-auth'
Integrates the ext-auth plugin by Vyronas Tsingaras. The new child process abstraction simplifies implementation in both the new ext-auth and the existing updown plugin, and makes them available on the Windows platform.
This commit is contained in:
commit
7d3c58a511
3
NEWS
3
NEWS
@ -11,6 +11,9 @@ strongswan-5.2.1
|
|||||||
and IETF/Installed Packages attributes can be processed incrementally on a
|
and IETF/Installed Packages attributes can be processed incrementally on a
|
||||||
per segment basis.
|
per segment basis.
|
||||||
|
|
||||||
|
- The new ext-auth plugin calls an external script to implement custom IKE_SA
|
||||||
|
authorization logic, courtesy of Vyronas Tsingaras.
|
||||||
|
|
||||||
|
|
||||||
strongswan-5.2.0
|
strongswan-5.2.0
|
||||||
----------------
|
----------------
|
||||||
|
@ -45,6 +45,7 @@ plugins = \
|
|||||||
plugins/eap-tnc.opt \
|
plugins/eap-tnc.opt \
|
||||||
plugins/eap-ttls.opt \
|
plugins/eap-ttls.opt \
|
||||||
plugins/error-notify.opt \
|
plugins/error-notify.opt \
|
||||||
|
plugins/ext-auth.opt \
|
||||||
plugins/gcrypt.opt \
|
plugins/gcrypt.opt \
|
||||||
plugins/ha.opt \
|
plugins/ha.opt \
|
||||||
plugins/imc-attestation.opt \
|
plugins/imc-attestation.opt \
|
||||||
|
15
conf/plugins/ext-auth.opt
Normal file
15
conf/plugins/ext-auth.opt
Normal 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.
|
@ -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-tnc], [enable EAP TNC trusted network connect module.])
|
||||||
ARG_ENABL_SET([eap-dynamic], [enable dynamic EAP proxy module.])
|
ARG_ENABL_SET([eap-dynamic], [enable dynamic EAP proxy module.])
|
||||||
ARG_ENABL_SET([eap-radius], [enable RADIUS proxy authentication 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([ipseckey], [enable IPSECKEY authentication plugin.])
|
||||||
ARG_ENABL_SET([keychain], [enables OS X Keychain Services credential set.])
|
ARG_ENABL_SET([keychain], [enables OS X Keychain Services credential set.])
|
||||||
ARG_ENABL_SET([pkcs11], [enables the PKCS11 token support plugin.])
|
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([android-log], [c charon])
|
||||||
ADD_PLUGIN([ha], [c charon])
|
ADD_PLUGIN([ha], [c charon])
|
||||||
ADD_PLUGIN([whitelist], [c charon])
|
ADD_PLUGIN([whitelist], [c charon])
|
||||||
|
ADD_PLUGIN([ext-auth], [c charon])
|
||||||
ADD_PLUGIN([lookip], [c charon])
|
ADD_PLUGIN([lookip], [c charon])
|
||||||
ADD_PLUGIN([error-notify], [c charon])
|
ADD_PLUGIN([error-notify], [c charon])
|
||||||
ADD_PLUGIN([certexpire], [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_WFP, test x$kernel_wfp = xtrue)
|
||||||
AM_CONDITIONAL(USE_KERNEL_IPH, test x$kernel_iph = xtrue)
|
AM_CONDITIONAL(USE_KERNEL_IPH, test x$kernel_iph = xtrue)
|
||||||
AM_CONDITIONAL(USE_WHITELIST, test x$whitelist = 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_LOOKIP, test x$lookip = xtrue)
|
||||||
AM_CONDITIONAL(USE_ERROR_NOTIFY, test x$error_notify = xtrue)
|
AM_CONDITIONAL(USE_ERROR_NOTIFY, test x$error_notify = xtrue)
|
||||||
AM_CONDITIONAL(USE_CERTEXPIRE, test x$certexpire = 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_wfp/Makefile
|
||||||
src/libcharon/plugins/kernel_iph/Makefile
|
src/libcharon/plugins/kernel_iph/Makefile
|
||||||
src/libcharon/plugins/whitelist/Makefile
|
src/libcharon/plugins/whitelist/Makefile
|
||||||
|
src/libcharon/plugins/ext_auth/Makefile
|
||||||
src/libcharon/plugins/lookip/Makefile
|
src/libcharon/plugins/lookip/Makefile
|
||||||
src/libcharon/plugins/error_notify/Makefile
|
src/libcharon/plugins/error_notify/Makefile
|
||||||
src/libcharon/plugins/certexpire/Makefile
|
src/libcharon/plugins/certexpire/Makefile
|
||||||
|
@ -258,6 +258,13 @@ if MONOLITHIC
|
|||||||
endif
|
endif
|
||||||
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
|
if USE_EAP_IDENTITY
|
||||||
SUBDIRS += plugins/eap_identity
|
SUBDIRS += plugins/eap_identity
|
||||||
if MONOLITHIC
|
if MONOLITHIC
|
||||||
|
18
src/libcharon/plugins/ext_auth/Makefile.am
Normal file
18
src/libcharon/plugins/ext_auth/Makefile.am
Normal 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
|
203
src/libcharon/plugins/ext_auth/ext_auth_listener.c
Normal file
203
src/libcharon/plugins/ext_auth/ext_auth_listener.c
Normal 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;
|
||||||
|
}
|
59
src/libcharon/plugins/ext_auth/ext_auth_listener.h
Normal file
59
src/libcharon/plugins/ext_auth/ext_auth_listener.h
Normal 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_ @}*/
|
156
src/libcharon/plugins/ext_auth/ext_auth_plugin.c
Normal file
156
src/libcharon/plugins/ext_auth/ext_auth_plugin.c
Normal 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;
|
||||||
|
}
|
49
src/libcharon/plugins/ext_auth/ext_auth_plugin.h
Normal file
49
src/libcharon/plugins/ext_auth/ext_auth_plugin.h
Normal 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_ @}*/
|
@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "updown_listener.h"
|
#include "updown_listener.h"
|
||||||
|
|
||||||
|
#include <utils/process.h>
|
||||||
#include <hydra.h>
|
#include <hydra.h>
|
||||||
#include <daemon.h>
|
#include <daemon.h>
|
||||||
#include <config/child_cfg.h>
|
#include <config/child_cfg.h>
|
||||||
@ -97,53 +99,84 @@ static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create variables for handled DNS attributes
|
* Allocate and push a format string to the environment
|
||||||
*/
|
*/
|
||||||
static char *make_dns_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
|
static bool push_env(char *envp[], u_int count, char *fmt, ...)
|
||||||
{
|
{
|
||||||
enumerator_t *enumerator;
|
int i = 0;
|
||||||
host_t *host;
|
char *str;
|
||||||
int v4 = 0, v6 = 0;
|
va_list args;
|
||||||
char total[512] = "", current[64];
|
|
||||||
|
|
||||||
if (!this->handler)
|
while (envp[i])
|
||||||
{
|
{
|
||||||
return strdup("");
|
if (++i + 1 >= count)
|
||||||
}
|
|
||||||
|
|
||||||
enumerator = this->handler->create_dns_enumerator(this->handler,
|
|
||||||
ike_sa->get_unique_id(ike_sa));
|
|
||||||
while (enumerator->enumerate(enumerator, &host))
|
|
||||||
{
|
|
||||||
switch (host->get_family(host))
|
|
||||||
{
|
{
|
||||||
case AF_INET:
|
return FALSE;
|
||||||
snprintf(current, sizeof(current),
|
|
||||||
"PLUTO_DNS4_%d='%H' ", ++v4, host);
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
snprintf(current, sizeof(current),
|
|
||||||
"PLUTO_DNS6_%d='%H' ", ++v6, host);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
strncat(total, current, sizeof(total) - strlen(total) - 1);
|
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
va_start(args, fmt);
|
||||||
|
if (vasprintf(&str, fmt, args) >= 0)
|
||||||
return strdup(total);
|
{
|
||||||
|
envp[i] = str;
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
return envp[i] != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create variables for local virtual IPs
|
* Free all allocated environment strings
|
||||||
*/
|
*/
|
||||||
static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
|
static void free_env(char *envp[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; envp[i]; i++)
|
||||||
|
{
|
||||||
|
free(envp[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push variables for handled DNS attributes
|
||||||
|
*/
|
||||||
|
static void push_dns_env(private_updown_listener_t *this, ike_sa_t *ike_sa,
|
||||||
|
char *envp[], u_int count)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
host_t *host;
|
||||||
|
int v4 = 0, v6 = 0;
|
||||||
|
|
||||||
|
if (this->handler)
|
||||||
|
{
|
||||||
|
enumerator = this->handler->create_dns_enumerator(this->handler,
|
||||||
|
ike_sa->get_unique_id(ike_sa));
|
||||||
|
while (enumerator->enumerate(enumerator, &host))
|
||||||
|
{
|
||||||
|
switch (host->get_family(host))
|
||||||
|
{
|
||||||
|
case AF_INET:
|
||||||
|
push_env(envp, count, "PLUTO_DNS4_%d=%H", ++v4, host);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
push_env(envp, count, "PLUTO_DNS6_%d=%H", ++v6, host);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push variables for local virtual IPs
|
||||||
|
*/
|
||||||
|
static void push_vip_env(private_updown_listener_t *this, ike_sa_t *ike_sa,
|
||||||
|
char *envp[], u_int count)
|
||||||
{
|
{
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
host_t *host;
|
host_t *host;
|
||||||
int v4 = 0, v6 = 0;
|
int v4 = 0, v6 = 0;
|
||||||
char total[512] = "", current[64];
|
|
||||||
bool first = TRUE;
|
bool first = TRUE;
|
||||||
|
|
||||||
enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
|
enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
|
||||||
@ -151,28 +184,22 @@ static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa)
|
|||||||
{
|
{
|
||||||
if (first)
|
if (first)
|
||||||
{ /* legacy variable for first VIP */
|
{ /* legacy variable for first VIP */
|
||||||
snprintf(current, sizeof(current),
|
first = FALSE;
|
||||||
"PLUTO_MY_SOURCEIP='%H' ", host);
|
push_env(envp, count, "PLUTO_MY_SOURCEIP=%H", host);
|
||||||
strncat(total, current, sizeof(total) - strlen(total) - 1);
|
|
||||||
}
|
}
|
||||||
switch (host->get_family(host))
|
switch (host->get_family(host))
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
snprintf(current, sizeof(current),
|
push_env(envp, count, "PLUTO_MY_SOURCEIP4_%d=%H", ++v4, host);
|
||||||
"PLUTO_MY_SOURCEIP4_%d='%H' ", ++v4, host);
|
|
||||||
break;
|
break;
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
snprintf(current, sizeof(current),
|
push_env(envp, count, "PLUTO_MY_SOURCEIP6_%d=%H", ++v6, host);
|
||||||
"PLUTO_MY_SOURCEIP6_%d='%H' ", ++v6, host);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
strncat(total, current, sizeof(total) - strlen(total) - 1);
|
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
return strdup(total);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,6 +223,164 @@ static u_int16_t get_port(traffic_selector_t *me,
|
|||||||
return local ? me->get_from_port(me) : other->get_from_port(other);
|
return local ? me->get_from_port(me) : other->get_from_port(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke the updown script once for given traffic selectors
|
||||||
|
*/
|
||||||
|
static void invoke_once(private_updown_listener_t *this, ike_sa_t *ike_sa,
|
||||||
|
child_sa_t *child_sa, child_cfg_t *config, bool up,
|
||||||
|
traffic_selector_t *my_ts, traffic_selector_t *other_ts)
|
||||||
|
{
|
||||||
|
host_t *me, *other, *host;
|
||||||
|
char *iface;
|
||||||
|
u_int8_t mask;
|
||||||
|
mark_t mark;
|
||||||
|
bool is_host, is_ipv6;
|
||||||
|
int out;
|
||||||
|
FILE *shell;
|
||||||
|
process_t *process;
|
||||||
|
char *envp[128] = {};
|
||||||
|
|
||||||
|
me = ike_sa->get_my_host(ike_sa);
|
||||||
|
other = ike_sa->get_other_host(ike_sa);
|
||||||
|
|
||||||
|
push_env(envp, countof(envp), "PLUTO_VERSION=1.1");
|
||||||
|
is_host = my_ts->is_host(my_ts, me);
|
||||||
|
if (is_host)
|
||||||
|
{
|
||||||
|
is_ipv6 = me->get_family(me) == AF_INET6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is_ipv6 = my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE;
|
||||||
|
}
|
||||||
|
push_env(envp, countof(envp), "PLUTO_VERB=%s%s%s",
|
||||||
|
up ? "up" : "down",
|
||||||
|
is_host ? "-host" : "-client",
|
||||||
|
is_ipv6 ? "-v6" : "");
|
||||||
|
push_env(envp, countof(envp), "PLUTO_CONNECTION=%s",
|
||||||
|
config->get_name(config));
|
||||||
|
if (up)
|
||||||
|
{
|
||||||
|
if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
|
||||||
|
me, &iface))
|
||||||
|
{
|
||||||
|
cache_iface(this, child_sa->get_reqid(child_sa), iface);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iface = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iface = uncache_iface(this, child_sa->get_reqid(child_sa));
|
||||||
|
}
|
||||||
|
push_env(envp, countof(envp), "PLUTO_INTERFACE=%s",
|
||||||
|
iface ? iface : "unknown");
|
||||||
|
push_env(envp, countof(envp), "PLUTO_REQID=%u",
|
||||||
|
child_sa->get_reqid(child_sa));
|
||||||
|
push_env(envp, countof(envp), "PLUTO_PROTO=%s",
|
||||||
|
child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah");
|
||||||
|
push_env(envp, countof(envp), "PLUTO_UNIQUEID=%u",
|
||||||
|
ike_sa->get_unique_id(ike_sa));
|
||||||
|
push_env(envp, countof(envp), "PLUTO_ME=%H", me);
|
||||||
|
push_env(envp, countof(envp), "PLUTO_MY_ID=%Y", ike_sa->get_my_id(ike_sa));
|
||||||
|
if (my_ts->to_subnet(my_ts, &host, &mask))
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_MY_CLIENT=%+H/%u", host, mask);
|
||||||
|
host->destroy(host);
|
||||||
|
}
|
||||||
|
push_env(envp, countof(envp), "PLUTO_MY_PORT=%u",
|
||||||
|
get_port(my_ts, other_ts, TRUE));
|
||||||
|
push_env(envp, countof(envp), "PLUTO_MY_PROTOCOL=%u",
|
||||||
|
my_ts->get_protocol(my_ts));
|
||||||
|
push_env(envp, countof(envp), "PLUTO_PEER=%H", other);
|
||||||
|
push_env(envp, countof(envp), "PLUTO_PEER_ID=%Y",
|
||||||
|
ike_sa->get_other_id(ike_sa));
|
||||||
|
if (other_ts->to_subnet(other_ts, &host, &mask))
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_PEER_CLIENT=%+H/%u", host, mask);
|
||||||
|
host->destroy(host);
|
||||||
|
}
|
||||||
|
push_env(envp, countof(envp), "PLUTO_PEER_PORT=%u",
|
||||||
|
get_port(my_ts, other_ts, FALSE));
|
||||||
|
push_env(envp, countof(envp), "PLUTO_PEER_PROTOCOL=%u",
|
||||||
|
other_ts->get_protocol(other_ts));
|
||||||
|
if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
|
||||||
|
ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_XAUTH_ID=%Y",
|
||||||
|
ike_sa->get_other_eap_id(ike_sa));
|
||||||
|
}
|
||||||
|
push_vip_env(this, ike_sa, envp, countof(envp));
|
||||||
|
mark = config->get_mark(config, TRUE);
|
||||||
|
if (mark.value)
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_MARK_IN=%u/0x%08x",
|
||||||
|
mark.value, mark.mask);
|
||||||
|
}
|
||||||
|
mark = config->get_mark(config, FALSE);
|
||||||
|
if (mark.value)
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_MARK_OUT=%u/0x%08x",
|
||||||
|
mark.value, mark.mask);
|
||||||
|
}
|
||||||
|
if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_UDP_ENC=%u",
|
||||||
|
other->get_port(other));
|
||||||
|
}
|
||||||
|
if (child_sa->get_ipcomp(child_sa) != IPCOMP_NONE)
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_IPCOMP=1");
|
||||||
|
}
|
||||||
|
push_dns_env(this, ike_sa, envp, countof(envp));
|
||||||
|
if (config->get_hostaccess(config))
|
||||||
|
{
|
||||||
|
push_env(envp, countof(envp), "PLUTO_HOST_ACCESS=1");
|
||||||
|
}
|
||||||
|
|
||||||
|
process = process_start_shell(envp, NULL, &out, NULL, "2>&1 %s",
|
||||||
|
config->get_updown(config));
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
shell = fdopen(out, "r");
|
||||||
|
if (shell)
|
||||||
|
{
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
char resp[128];
|
||||||
|
|
||||||
|
if (fgets(resp, sizeof(resp), shell) == NULL)
|
||||||
|
{
|
||||||
|
if (ferror(shell))
|
||||||
|
{
|
||||||
|
DBG1(DBG_CHD, "error reading from updown script");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *e = resp + strlen(resp);
|
||||||
|
if (e > resp && e[-1] == '\n')
|
||||||
|
{
|
||||||
|
e[-1] = '\0';
|
||||||
|
}
|
||||||
|
DBG1(DBG_CHD, "updown: %s", resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(shell);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(out);
|
||||||
|
}
|
||||||
|
process->wait(process, NULL);
|
||||||
|
}
|
||||||
|
free(iface);
|
||||||
|
free_env(envp);
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(listener_t, child_updown, bool,
|
METHOD(listener_t, child_updown, bool,
|
||||||
private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
|
private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
|
||||||
bool up)
|
bool up)
|
||||||
@ -203,233 +388,17 @@ METHOD(listener_t, child_updown, bool,
|
|||||||
traffic_selector_t *my_ts, *other_ts;
|
traffic_selector_t *my_ts, *other_ts;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
child_cfg_t *config;
|
child_cfg_t *config;
|
||||||
host_t *me, *other;
|
|
||||||
char *script;
|
|
||||||
|
|
||||||
config = child_sa->get_config(child_sa);
|
config = child_sa->get_config(child_sa);
|
||||||
script = config->get_updown(config);
|
if (config->get_updown(config))
|
||||||
me = ike_sa->get_my_host(ike_sa);
|
|
||||||
other = ike_sa->get_other_host(ike_sa);
|
|
||||||
|
|
||||||
if (script == NULL)
|
|
||||||
{
|
{
|
||||||
return TRUE;
|
enumerator = child_sa->create_policy_enumerator(child_sa);
|
||||||
|
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
|
||||||
|
{
|
||||||
|
invoke_once(this, ike_sa, child_sa, config, up, my_ts, other_ts);
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
enumerator = child_sa->create_policy_enumerator(child_sa);
|
|
||||||
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
|
|
||||||
{
|
|
||||||
char command[2048];
|
|
||||||
host_t *my_client, *other_client;
|
|
||||||
u_int8_t my_client_mask, other_client_mask;
|
|
||||||
char *virtual_ip, *iface, *mark_in, *mark_out, *udp_enc, *dns, *xauth;
|
|
||||||
mark_t mark;
|
|
||||||
bool is_host, is_ipv6, use_ipcomp;
|
|
||||||
FILE *shell;
|
|
||||||
|
|
||||||
my_ts->to_subnet(my_ts, &my_client, &my_client_mask);
|
|
||||||
other_ts->to_subnet(other_ts, &other_client, &other_client_mask);
|
|
||||||
|
|
||||||
virtual_ip = make_vip_vars(this, ike_sa);
|
|
||||||
|
|
||||||
/* check for the presence of an inbound mark */
|
|
||||||
mark = config->get_mark(config, TRUE);
|
|
||||||
if (mark.value)
|
|
||||||
{
|
|
||||||
if (asprintf(&mark_in, "PLUTO_MARK_IN='%u/0x%08x' ",
|
|
||||||
mark.value, mark.mask ) < 0)
|
|
||||||
{
|
|
||||||
mark_in = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (asprintf(&mark_in, "") < 0)
|
|
||||||
{
|
|
||||||
mark_in = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for the presence of an outbound mark */
|
|
||||||
mark = config->get_mark(config, FALSE);
|
|
||||||
if (mark.value)
|
|
||||||
{
|
|
||||||
if (asprintf(&mark_out, "PLUTO_MARK_OUT='%u/0x%08x' ",
|
|
||||||
mark.value, mark.mask ) < 0)
|
|
||||||
{
|
|
||||||
mark_out = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (asprintf(&mark_out, "") < 0)
|
|
||||||
{
|
|
||||||
mark_out = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for a NAT condition causing ESP_IN_UDP encapsulation */
|
|
||||||
if (ike_sa->has_condition(ike_sa, COND_NAT_ANY))
|
|
||||||
{
|
|
||||||
if (asprintf(&udp_enc, "PLUTO_UDP_ENC='%u' ",
|
|
||||||
other->get_port(other)) < 0)
|
|
||||||
{
|
|
||||||
udp_enc = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (asprintf(&udp_enc, "") < 0)
|
|
||||||
{
|
|
||||||
udp_enc = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ike_sa->has_condition(ike_sa, COND_EAP_AUTHENTICATED) ||
|
|
||||||
ike_sa->has_condition(ike_sa, COND_XAUTH_AUTHENTICATED))
|
|
||||||
{
|
|
||||||
if (asprintf(&xauth, "PLUTO_XAUTH_ID='%Y' ",
|
|
||||||
ike_sa->get_other_eap_id(ike_sa)) < 0)
|
|
||||||
{
|
|
||||||
xauth = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (asprintf(&xauth, "") < 0)
|
|
||||||
{
|
|
||||||
xauth = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (up)
|
|
||||||
{
|
|
||||||
if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
|
|
||||||
me, &iface))
|
|
||||||
{
|
|
||||||
cache_iface(this, child_sa->get_reqid(child_sa), iface);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iface = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iface = uncache_iface(this, child_sa->get_reqid(child_sa));
|
|
||||||
}
|
|
||||||
|
|
||||||
dns = make_dns_vars(this, ike_sa);
|
|
||||||
|
|
||||||
/* check for IPComp */
|
|
||||||
use_ipcomp = child_sa->get_ipcomp(child_sa) != IPCOMP_NONE;
|
|
||||||
|
|
||||||
/* determine IPv4/IPv6 and client/host situation */
|
|
||||||
is_host = my_ts->is_host(my_ts, me);
|
|
||||||
is_ipv6 = is_host ? (me->get_family(me) == AF_INET6) :
|
|
||||||
(my_ts->get_type(my_ts) == TS_IPV6_ADDR_RANGE);
|
|
||||||
|
|
||||||
/* build the command with all env variables.
|
|
||||||
*/
|
|
||||||
snprintf(command, sizeof(command),
|
|
||||||
"2>&1 "
|
|
||||||
"PLUTO_VERSION='1.1' "
|
|
||||||
"PLUTO_VERB='%s%s%s' "
|
|
||||||
"PLUTO_CONNECTION='%s' "
|
|
||||||
"PLUTO_INTERFACE='%s' "
|
|
||||||
"PLUTO_REQID='%u' "
|
|
||||||
"PLUTO_PROTO='%s' "
|
|
||||||
"PLUTO_UNIQUEID='%u' "
|
|
||||||
"PLUTO_ME='%H' "
|
|
||||||
"PLUTO_MY_ID='%Y' "
|
|
||||||
"PLUTO_MY_CLIENT='%+H/%u' "
|
|
||||||
"PLUTO_MY_PORT='%u' "
|
|
||||||
"PLUTO_MY_PROTOCOL='%u' "
|
|
||||||
"PLUTO_PEER='%H' "
|
|
||||||
"PLUTO_PEER_ID='%Y' "
|
|
||||||
"PLUTO_PEER_CLIENT='%+H/%u' "
|
|
||||||
"PLUTO_PEER_PORT='%u' "
|
|
||||||
"PLUTO_PEER_PROTOCOL='%u' "
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s"
|
|
||||||
"%s",
|
|
||||||
up ? "up" : "down",
|
|
||||||
is_host ? "-host" : "-client",
|
|
||||||
is_ipv6 ? "-v6" : "",
|
|
||||||
config->get_name(config),
|
|
||||||
iface ? iface : "unknown",
|
|
||||||
child_sa->get_reqid(child_sa),
|
|
||||||
child_sa->get_protocol(child_sa) == PROTO_ESP ? "esp" : "ah",
|
|
||||||
ike_sa->get_unique_id(ike_sa),
|
|
||||||
me, ike_sa->get_my_id(ike_sa),
|
|
||||||
my_client, my_client_mask,
|
|
||||||
get_port(my_ts, other_ts, TRUE),
|
|
||||||
my_ts->get_protocol(my_ts),
|
|
||||||
other, ike_sa->get_other_id(ike_sa),
|
|
||||||
other_client, other_client_mask,
|
|
||||||
get_port(my_ts, other_ts, FALSE),
|
|
||||||
other_ts->get_protocol(other_ts),
|
|
||||||
xauth,
|
|
||||||
virtual_ip,
|
|
||||||
mark_in,
|
|
||||||
mark_out,
|
|
||||||
udp_enc,
|
|
||||||
use_ipcomp ? "PLUTO_IPCOMP='1' " : "",
|
|
||||||
config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "",
|
|
||||||
dns,
|
|
||||||
script);
|
|
||||||
my_client->destroy(my_client);
|
|
||||||
other_client->destroy(other_client);
|
|
||||||
free(virtual_ip);
|
|
||||||
free(mark_in);
|
|
||||||
free(mark_out);
|
|
||||||
free(udp_enc);
|
|
||||||
free(dns);
|
|
||||||
free(iface);
|
|
||||||
free(xauth);
|
|
||||||
|
|
||||||
DBG3(DBG_CHD, "running updown script: %s", command);
|
|
||||||
shell = popen(command, "r");
|
|
||||||
|
|
||||||
if (shell == NULL)
|
|
||||||
{
|
|
||||||
DBG1(DBG_CHD, "could not execute updown script '%s'", script);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (TRUE)
|
|
||||||
{
|
|
||||||
char resp[128];
|
|
||||||
|
|
||||||
if (fgets(resp, sizeof(resp), shell) == NULL)
|
|
||||||
{
|
|
||||||
if (ferror(shell))
|
|
||||||
{
|
|
||||||
DBG1(DBG_CHD, "error reading output from updown script");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *e = resp + strlen(resp);
|
|
||||||
if (e > resp && e[-1] == '\n')
|
|
||||||
{ /* trim trailing '\n' */
|
|
||||||
e[-1] = '\0';
|
|
||||||
}
|
|
||||||
DBG1(DBG_CHD, "updown: %s", resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pclose(shell);
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ selectors/traffic_selector.c settings/settings.c settings/settings_types.c \
|
|||||||
settings/settings_parser.c settings/settings_lexer.c \
|
settings/settings_parser.c settings/settings_lexer.c \
|
||||||
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
|
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
|
||||||
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
|
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
|
||||||
utils/parser_helper.c utils/test.c utils/utils/strerror.c
|
utils/parser_helper.c utils/test.c utils/process.c utils/utils/strerror.c
|
||||||
|
|
||||||
libstrongswan_la_SOURCES += \
|
libstrongswan_la_SOURCES += \
|
||||||
threading/thread.c \
|
threading/thread.c \
|
||||||
|
@ -35,7 +35,7 @@ selectors/traffic_selector.c settings/settings.c settings/settings_types.c \
|
|||||||
settings/settings_parser.y settings/settings_lexer.l \
|
settings/settings_parser.y settings/settings_lexer.l \
|
||||||
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
|
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
|
||||||
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
|
utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \
|
||||||
utils/parser_helper.c utils/test.c utils/utils/strerror.c
|
utils/parser_helper.c utils/test.c utils/process.c utils/utils/strerror.c
|
||||||
|
|
||||||
if !USE_WINDOWS
|
if !USE_WINDOWS
|
||||||
libstrongswan_la_SOURCES += \
|
libstrongswan_la_SOURCES += \
|
||||||
@ -102,7 +102,7 @@ utils/lexparser.h utils/optionsfrom.h utils/capabilities.h utils/backtrace.h \
|
|||||||
utils/leak_detective.h utils/printf_hook/printf_hook.h \
|
utils/leak_detective.h utils/printf_hook/printf_hook.h \
|
||||||
utils/printf_hook/printf_hook_vstr.h utils/printf_hook/printf_hook_builtin.h \
|
utils/printf_hook/printf_hook_vstr.h utils/printf_hook/printf_hook_builtin.h \
|
||||||
utils/parser_helper.h utils/test.h utils/integrity_checker.h utils/windows.h \
|
utils/parser_helper.h utils/test.h utils/integrity_checker.h utils/windows.h \
|
||||||
utils/utils/strerror.h
|
utils/process.h utils/utils/strerror.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
library.lo : $(top_builddir)/config.status
|
library.lo : $(top_builddir)/config.status
|
||||||
|
@ -30,6 +30,7 @@ tests_SOURCES = tests.h tests.c \
|
|||||||
suites/test_hashtable.c \
|
suites/test_hashtable.c \
|
||||||
suites/test_identification.c \
|
suites/test_identification.c \
|
||||||
suites/test_threading.c \
|
suites/test_threading.c \
|
||||||
|
suites/test_process.c \
|
||||||
suites/test_watcher.c \
|
suites/test_watcher.c \
|
||||||
suites/test_stream.c \
|
suites/test_stream.c \
|
||||||
suites/test_fetch_http.c \
|
suites/test_fetch_http.c \
|
||||||
|
221
src/libstrongswan/tests/suites/test_process.c
Normal file
221
src/libstrongswan/tests/suites/test_process.c
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Martin Willi
|
||||||
|
* Copyright (C) 2014 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 "test_suite.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <utils/process.h>
|
||||||
|
|
||||||
|
START_TEST(test_retval_true)
|
||||||
|
{
|
||||||
|
process_t *process;
|
||||||
|
char *argv[] = {
|
||||||
|
#ifdef WIN32
|
||||||
|
"C:\\Windows\\system32\\cmd.exe",
|
||||||
|
"/C",
|
||||||
|
"exit 0",
|
||||||
|
#else
|
||||||
|
"/bin/true",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
process = process_start(argv, NULL, NULL, NULL, NULL, TRUE);
|
||||||
|
ck_assert(process != NULL);
|
||||||
|
ck_assert(process->wait(process, &retval));
|
||||||
|
ck_assert_int_eq(retval, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_retval_false)
|
||||||
|
{
|
||||||
|
process_t *process;
|
||||||
|
char *argv[] = {
|
||||||
|
#ifdef WIN32
|
||||||
|
"C:\\Windows\\system32\\cmd.exe",
|
||||||
|
"/C",
|
||||||
|
"exit 1",
|
||||||
|
#else
|
||||||
|
"/bin/false",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
process = process_start(argv, NULL, NULL, NULL, NULL, TRUE);
|
||||||
|
ck_assert(process != NULL);
|
||||||
|
ck_assert(process->wait(process, &retval));
|
||||||
|
ck_assert(retval != 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_not_found)
|
||||||
|
{
|
||||||
|
process_t *process;
|
||||||
|
char *argv[] = {
|
||||||
|
"/bin/does-not-exist",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
process = process_start(argv, NULL, NULL, NULL, NULL, TRUE);
|
||||||
|
/* both is acceptable behavior */
|
||||||
|
ck_assert(process == NULL || !process->wait(process, NULL));
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_echo)
|
||||||
|
{
|
||||||
|
process_t *process;
|
||||||
|
char *argv[] = {
|
||||||
|
#ifdef WIN32
|
||||||
|
"C:\\Windows\\system32\\more.com",
|
||||||
|
#else
|
||||||
|
"/bin/cat",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int retval, in, out;
|
||||||
|
char *msg = "test";
|
||||||
|
char buf[strlen(msg) + 1];
|
||||||
|
|
||||||
|
memset(buf, 0, strlen(msg) + 1);
|
||||||
|
|
||||||
|
process = process_start(argv, NULL, &in, &out, NULL, TRUE);
|
||||||
|
ck_assert(process != NULL);
|
||||||
|
ck_assert_int_eq(write(in, msg, strlen(msg)), strlen(msg));
|
||||||
|
ck_assert(close(in) == 0);
|
||||||
|
ck_assert_int_eq(read(out, buf, strlen(msg) + 1), strlen(msg));
|
||||||
|
ck_assert_str_eq(buf, msg);
|
||||||
|
ck_assert(close(out) == 0);
|
||||||
|
ck_assert(process->wait(process, &retval));
|
||||||
|
ck_assert_int_eq(retval, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_echo_err)
|
||||||
|
{
|
||||||
|
process_t *process;
|
||||||
|
char *argv[] = {
|
||||||
|
#ifdef WIN32
|
||||||
|
"C:\\Windows\\system32\\cmd.exe",
|
||||||
|
"/C",
|
||||||
|
"1>&2 C:\\Windows\\system32\\more.com",
|
||||||
|
#else
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"1>&2 /bin/cat",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int retval, in, err;
|
||||||
|
char *msg = "a longer test message";
|
||||||
|
char buf[strlen(msg) + 1];
|
||||||
|
|
||||||
|
memset(buf, 0, strlen(msg) + 1);
|
||||||
|
|
||||||
|
process = process_start(argv, NULL, &in, NULL, &err, TRUE);
|
||||||
|
ck_assert(process != NULL);
|
||||||
|
ck_assert_int_eq(write(in, msg, strlen(msg)), strlen(msg));
|
||||||
|
ck_assert(close(in) == 0);
|
||||||
|
ck_assert_int_eq(read(err, buf, strlen(msg) + 1), strlen(msg));
|
||||||
|
ck_assert_str_eq(buf, msg);
|
||||||
|
ck_assert(close(err) == 0);
|
||||||
|
ck_assert(process->wait(process, &retval));
|
||||||
|
ck_assert_int_eq(retval, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_env)
|
||||||
|
{
|
||||||
|
process_t *process;
|
||||||
|
char *argv[] = {
|
||||||
|
#ifdef WIN32
|
||||||
|
"C:\\Windows\\system32\\cmd.exe",
|
||||||
|
"/C",
|
||||||
|
"echo %A% %B%",
|
||||||
|
#else
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"echo -n $A $B",
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
char *envp[] = {
|
||||||
|
"A=atest",
|
||||||
|
"B=bstring",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int retval, out;
|
||||||
|
char buf[64] = {};
|
||||||
|
|
||||||
|
process = process_start(argv, envp, NULL, &out, NULL, TRUE);
|
||||||
|
ck_assert(process != NULL);
|
||||||
|
ck_assert(read(out, buf, sizeof(buf)) > 0);
|
||||||
|
#ifdef WIN32
|
||||||
|
ck_assert_str_eq(buf, "atest bstring\r\n");
|
||||||
|
#else
|
||||||
|
ck_assert_str_eq(buf, "atest bstring");
|
||||||
|
#endif
|
||||||
|
ck_assert(close(out) == 0);
|
||||||
|
ck_assert(process->wait(process, &retval));
|
||||||
|
ck_assert_int_eq(retval, 0);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_shell)
|
||||||
|
{
|
||||||
|
process_t *process;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
process = process_start_shell(NULL, NULL, NULL, NULL, "exit %d", 3);
|
||||||
|
ck_assert(process != NULL);
|
||||||
|
ck_assert(process->wait(process, &retval));
|
||||||
|
ck_assert_int_eq(retval, 3);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *process_suite_create()
|
||||||
|
{
|
||||||
|
Suite *s;
|
||||||
|
TCase *tc;
|
||||||
|
|
||||||
|
s = suite_create("process");
|
||||||
|
|
||||||
|
tc = tcase_create("return values");
|
||||||
|
tcase_add_test(tc, test_retval_true);
|
||||||
|
tcase_add_test(tc, test_retval_false);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("not found");
|
||||||
|
tcase_add_test(tc, test_not_found);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("echo");
|
||||||
|
tcase_add_test(tc, test_echo);
|
||||||
|
tcase_add_test(tc, test_echo_err);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("env");
|
||||||
|
tcase_add_test(tc, test_env);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("shell");
|
||||||
|
tcase_add_test(tc, test_shell);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
@ -24,6 +24,7 @@ TEST_SUITE(hashtable_suite_create)
|
|||||||
TEST_SUITE(array_suite_create)
|
TEST_SUITE(array_suite_create)
|
||||||
TEST_SUITE(identification_suite_create)
|
TEST_SUITE(identification_suite_create)
|
||||||
TEST_SUITE(threading_suite_create)
|
TEST_SUITE(threading_suite_create)
|
||||||
|
TEST_SUITE(process_suite_create)
|
||||||
TEST_SUITE(watcher_suite_create)
|
TEST_SUITE(watcher_suite_create)
|
||||||
TEST_SUITE(stream_suite_create)
|
TEST_SUITE(stream_suite_create)
|
||||||
TEST_SUITE(utils_suite_create)
|
TEST_SUITE(utils_suite_create)
|
||||||
|
591
src/libstrongswan/utils/process.c
Normal file
591
src/libstrongswan/utils/process.c
Normal file
@ -0,0 +1,591 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Martin Willi
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* vasprintf() */
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
|
#include <library.h>
|
||||||
|
#include <utils/debug.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
typedef struct private_process_t private_process_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends of a pipe()
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
PIPE_READ = 0,
|
||||||
|
PIPE_WRITE = 1,
|
||||||
|
PIPE_ENDS,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data of an process_t object.
|
||||||
|
*/
|
||||||
|
struct private_process_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public process_t interface.
|
||||||
|
*/
|
||||||
|
process_t public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child stdin pipe
|
||||||
|
*/
|
||||||
|
int in[PIPE_ENDS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child stdout pipe
|
||||||
|
*/
|
||||||
|
int out[PIPE_ENDS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child stderr pipe
|
||||||
|
*/
|
||||||
|
int err[PIPE_ENDS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child process
|
||||||
|
*/
|
||||||
|
int pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a file descriptor if it is not -1
|
||||||
|
*/
|
||||||
|
static void close_if(int *fd)
|
||||||
|
{
|
||||||
|
if (*fd != -1)
|
||||||
|
{
|
||||||
|
close(*fd);
|
||||||
|
*fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a process structure, close all pipes
|
||||||
|
*/
|
||||||
|
static void process_destroy(private_process_t *this)
|
||||||
|
{
|
||||||
|
close_if(&this->in[PIPE_READ]);
|
||||||
|
close_if(&this->in[PIPE_WRITE]);
|
||||||
|
close_if(&this->out[PIPE_READ]);
|
||||||
|
close_if(&this->out[PIPE_WRITE]);
|
||||||
|
close_if(&this->err[PIPE_READ]);
|
||||||
|
close_if(&this->err[PIPE_WRITE]);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(process_t, wait_, bool,
|
||||||
|
private_process_t *this, int *code)
|
||||||
|
{
|
||||||
|
int status, ret;
|
||||||
|
|
||||||
|
ret = waitpid(this->pid, &status, 0);
|
||||||
|
process_destroy(this);
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!WIFEXITED(status))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (code)
|
||||||
|
{
|
||||||
|
*code = WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header
|
||||||
|
*/
|
||||||
|
process_t* process_start(char *const argv[], char *const envp[],
|
||||||
|
int *in, int *out, int *err, bool close_all)
|
||||||
|
{
|
||||||
|
private_process_t *this;
|
||||||
|
char *empty[] = { NULL };
|
||||||
|
|
||||||
|
INIT(this,
|
||||||
|
.public = {
|
||||||
|
.wait = _wait_,
|
||||||
|
},
|
||||||
|
.in = { -1, -1 },
|
||||||
|
.out = { -1, -1 },
|
||||||
|
.err = { -1, -1 },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (in && pipe(this->in) != 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "creating stdin pipe failed: %s", strerror(errno));
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (out && pipe(this->out) != 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "creating stdout pipe failed: %s", strerror(errno));
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (err && pipe(this->err) != 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "creating stderr pipe failed: %s", strerror(errno));
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pid = fork();
|
||||||
|
switch (this->pid)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
DBG1(DBG_LIB, "forking process failed: %s", strerror(errno));
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
case 0:
|
||||||
|
/* child */
|
||||||
|
close_if(&this->in[PIPE_WRITE]);
|
||||||
|
close_if(&this->out[PIPE_READ]);
|
||||||
|
close_if(&this->err[PIPE_READ]);
|
||||||
|
if (this->in[PIPE_READ] != -1)
|
||||||
|
{
|
||||||
|
if (dup2(this->in[PIPE_READ], 0) == -1)
|
||||||
|
{
|
||||||
|
raise(SIGKILL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->out[PIPE_WRITE] != -1)
|
||||||
|
{
|
||||||
|
if (dup2(this->out[PIPE_WRITE], 1) == -1)
|
||||||
|
{
|
||||||
|
raise(SIGKILL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->err[PIPE_WRITE] != -1)
|
||||||
|
{
|
||||||
|
if (dup2(this->err[PIPE_WRITE], 2) == -1)
|
||||||
|
{
|
||||||
|
raise(SIGKILL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (close_all)
|
||||||
|
{
|
||||||
|
closefrom(3);
|
||||||
|
}
|
||||||
|
if (execve(argv[0], argv, envp ?: empty) == -1)
|
||||||
|
{
|
||||||
|
raise(SIGKILL);
|
||||||
|
}
|
||||||
|
/* not reached */
|
||||||
|
default:
|
||||||
|
/* parent */
|
||||||
|
close_if(&this->in[PIPE_READ]);
|
||||||
|
close_if(&this->out[PIPE_WRITE]);
|
||||||
|
close_if(&this->err[PIPE_WRITE]);
|
||||||
|
if (in)
|
||||||
|
{
|
||||||
|
*in = this->in[PIPE_WRITE];
|
||||||
|
this->in[PIPE_WRITE] = -1;
|
||||||
|
}
|
||||||
|
if (out)
|
||||||
|
{
|
||||||
|
*out = this->out[PIPE_READ];
|
||||||
|
this->out[PIPE_READ] = -1;
|
||||||
|
}
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
*err = this->err[PIPE_READ];
|
||||||
|
this->err[PIPE_READ] = -1;
|
||||||
|
}
|
||||||
|
return &this->public;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header
|
||||||
|
*/
|
||||||
|
process_t* process_start_shell(char *const envp[], int *in, int *out, int *err,
|
||||||
|
char *fmt, ...)
|
||||||
|
{
|
||||||
|
char *argv[] = {
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
process_t *process;
|
||||||
|
va_list args;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
len = vasprintf(&argv[2], fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
if (len < 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
process = process_start(argv, envp, in, out, err, TRUE);
|
||||||
|
free(argv[2]);
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* WIN32 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private data of an process_t object.
|
||||||
|
*/
|
||||||
|
struct private_process_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public process_t interface.
|
||||||
|
*/
|
||||||
|
process_t public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child stdin pipe
|
||||||
|
*/
|
||||||
|
HANDLE in[PIPE_ENDS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child stdout pipe
|
||||||
|
*/
|
||||||
|
HANDLE out[PIPE_ENDS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child stderr pipe
|
||||||
|
*/
|
||||||
|
HANDLE err[PIPE_ENDS];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* child process information
|
||||||
|
*/
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up state associated to child process
|
||||||
|
*/
|
||||||
|
static void process_destroy(private_process_t *this)
|
||||||
|
{
|
||||||
|
if (this->in[PIPE_READ])
|
||||||
|
{
|
||||||
|
CloseHandle(this->in[PIPE_READ]);
|
||||||
|
}
|
||||||
|
if (this->in[PIPE_WRITE])
|
||||||
|
{
|
||||||
|
CloseHandle(this->in[PIPE_WRITE]);
|
||||||
|
}
|
||||||
|
if (this->out[PIPE_READ])
|
||||||
|
{
|
||||||
|
CloseHandle(this->out[PIPE_READ]);
|
||||||
|
}
|
||||||
|
if (this->out[PIPE_WRITE])
|
||||||
|
{
|
||||||
|
CloseHandle(this->out[PIPE_WRITE]);
|
||||||
|
}
|
||||||
|
if (this->err[PIPE_READ])
|
||||||
|
{
|
||||||
|
CloseHandle(this->err[PIPE_READ]);
|
||||||
|
}
|
||||||
|
if (this->err[PIPE_WRITE])
|
||||||
|
{
|
||||||
|
CloseHandle(this->err[PIPE_WRITE]);
|
||||||
|
}
|
||||||
|
if (this->pi.hProcess)
|
||||||
|
{
|
||||||
|
CloseHandle(this->pi.hProcess);
|
||||||
|
CloseHandle(this->pi.hThread);
|
||||||
|
}
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(process_t, wait_, bool,
|
||||||
|
private_process_t *this, int *code)
|
||||||
|
{
|
||||||
|
DWORD ec;
|
||||||
|
|
||||||
|
if (WaitForSingleObject(this->pi.hProcess, INFINITE) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "waiting for child process failed: 0x%08x",
|
||||||
|
GetLastError());
|
||||||
|
process_destroy(this);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (code)
|
||||||
|
{
|
||||||
|
if (!GetExitCodeProcess(this->pi.hProcess, &ec))
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "getting child process exit code failed: 0x%08x",
|
||||||
|
GetLastError());
|
||||||
|
process_destroy(this);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*code = ec;
|
||||||
|
}
|
||||||
|
process_destroy(this);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a command line argument to buf, optionally quoted
|
||||||
|
*/
|
||||||
|
static void append_arg(char *buf, u_int len, char *arg, char *quote)
|
||||||
|
{
|
||||||
|
char *space = "";
|
||||||
|
int current;
|
||||||
|
|
||||||
|
current = strlen(buf);
|
||||||
|
if (current)
|
||||||
|
{
|
||||||
|
space = " ";
|
||||||
|
}
|
||||||
|
snprintf(buf + current, len - current, "%s%s%s%s", space, quote, arg, quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a null-terminate env string to buf
|
||||||
|
*/
|
||||||
|
static void append_env(char *buf, u_int len, char *env)
|
||||||
|
{
|
||||||
|
char *pos = buf;
|
||||||
|
int current;
|
||||||
|
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
pos += strlen(pos);
|
||||||
|
if (!pos[1])
|
||||||
|
{
|
||||||
|
if (pos == buf)
|
||||||
|
{
|
||||||
|
current = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = pos - buf + 1;
|
||||||
|
}
|
||||||
|
snprintf(buf + current, len - current, "%s", env);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header
|
||||||
|
*/
|
||||||
|
process_t* process_start(char *const argv[], char *const envp[],
|
||||||
|
int *in, int *out, int *err, bool close_all)
|
||||||
|
{
|
||||||
|
private_process_t *this;
|
||||||
|
char arg[32768], env[32768];
|
||||||
|
SECURITY_ATTRIBUTES sa = {
|
||||||
|
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||||
|
.bInheritHandle = TRUE,
|
||||||
|
};
|
||||||
|
STARTUPINFO sui = {
|
||||||
|
.cb = sizeof(STARTUPINFO),
|
||||||
|
};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(arg, 0, sizeof(arg));
|
||||||
|
memset(env, 0, sizeof(env));
|
||||||
|
|
||||||
|
for (i = 0; argv[i]; i++)
|
||||||
|
{
|
||||||
|
if (!strchr(argv[i], ' '))
|
||||||
|
{ /* no spaces, fine for appending */
|
||||||
|
append_arg(arg, sizeof(arg) - 1, argv[i], "");
|
||||||
|
}
|
||||||
|
else if (argv[i][0] == '"' &&
|
||||||
|
argv[i][strlen(argv[i]) - 1] == '"' &&
|
||||||
|
strchr(argv[i] + 1, '"') == argv[i] + strlen(argv[i]) - 1)
|
||||||
|
{ /* already properly quoted */
|
||||||
|
append_arg(arg, sizeof(arg) - 1, argv[i], "");
|
||||||
|
}
|
||||||
|
else if (strchr(argv[i], ' ') && !strchr(argv[i], '"'))
|
||||||
|
{ /* spaces, but no quotes; append quoted */
|
||||||
|
append_arg(arg, sizeof(arg) - 1, argv[i], "\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "invalid command line argument: %s", argv[i]);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (envp)
|
||||||
|
{
|
||||||
|
for (i = 0; envp[i]; i++)
|
||||||
|
{
|
||||||
|
append_env(env, sizeof(env) - 1, envp[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT(this,
|
||||||
|
.public = {
|
||||||
|
.wait = _wait_,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (in)
|
||||||
|
{
|
||||||
|
sui.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
if (!CreatePipe(&this->in[PIPE_READ], &this->in[PIPE_WRITE], &sa, 0))
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!SetHandleInformation(this->in[PIPE_WRITE], HANDLE_FLAG_INHERIT, 0))
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sui.hStdInput = this->in[PIPE_READ];
|
||||||
|
*in = _open_osfhandle((uintptr_t)this->in[PIPE_WRITE], 0);
|
||||||
|
if (*in == -1)
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (out)
|
||||||
|
{
|
||||||
|
sui.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
if (!CreatePipe(&this->out[PIPE_READ], &this->out[PIPE_WRITE], &sa, 0))
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!SetHandleInformation(this->out[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sui.hStdOutput = this->out[PIPE_WRITE];
|
||||||
|
*out = _open_osfhandle((uintptr_t)this->out[PIPE_READ], 0);
|
||||||
|
if (*out == -1)
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
sui.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
if (!CreatePipe(&this->err[PIPE_READ], &this->err[PIPE_WRITE], &sa, 0))
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!SetHandleInformation(this->err[PIPE_READ], HANDLE_FLAG_INHERIT, 0))
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
sui.hStdError = this->err[PIPE_WRITE];
|
||||||
|
*err = _open_osfhandle((uintptr_t)this->err[PIPE_READ], 0);
|
||||||
|
if (*err == -1)
|
||||||
|
{
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateProcess(argv[0], arg, NULL, NULL, TRUE,
|
||||||
|
NORMAL_PRIORITY_CLASS, env, NULL, &sui, &this->pi))
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "creating process '%s' failed: 0x%08x",
|
||||||
|
argv[0], GetLastError());
|
||||||
|
process_destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close child process end of pipes */
|
||||||
|
if (this->in[PIPE_READ])
|
||||||
|
{
|
||||||
|
CloseHandle(this->in[PIPE_READ]);
|
||||||
|
this->in[PIPE_READ] = NULL;
|
||||||
|
}
|
||||||
|
if (this->out[PIPE_WRITE])
|
||||||
|
{
|
||||||
|
CloseHandle(this->out[PIPE_WRITE]);
|
||||||
|
this->out[PIPE_WRITE] = NULL;
|
||||||
|
}
|
||||||
|
if (this->err[PIPE_WRITE])
|
||||||
|
{
|
||||||
|
CloseHandle(this->err[PIPE_WRITE]);
|
||||||
|
this->err[PIPE_WRITE] = NULL;
|
||||||
|
}
|
||||||
|
/* our side gets closed over the osf_handle closed by caller */
|
||||||
|
this->in[PIPE_WRITE] = NULL;
|
||||||
|
this->out[PIPE_READ] = NULL;
|
||||||
|
this->err[PIPE_READ] = NULL;
|
||||||
|
return &this->public;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header
|
||||||
|
*/
|
||||||
|
process_t* process_start_shell(char *const envp[], int *in, int *out, int *err,
|
||||||
|
char *fmt, ...)
|
||||||
|
{
|
||||||
|
char path[MAX_PATH], *exe = "system32\\cmd.exe";
|
||||||
|
char *argv[] = {
|
||||||
|
path,
|
||||||
|
"/C",
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
process_t *process;
|
||||||
|
va_list args;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = GetSystemWindowsDirectory(path, sizeof(path));
|
||||||
|
if (len == 0 || len >= sizeof(path) - strlen(exe))
|
||||||
|
{
|
||||||
|
DBG1(DBG_LIB, "resolving Windows directory failed: 0x%08x",
|
||||||
|
GetLastError());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (path[len + 1] != '\\')
|
||||||
|
{
|
||||||
|
strncat(path, "\\", sizeof(path) - len++);
|
||||||
|
}
|
||||||
|
strncat(path, exe, sizeof(path) - len);
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
len = vasprintf(&argv[2], fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
if (len < 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
process = process_start(argv, envp, in, out, err, TRUE);
|
||||||
|
free(argv[2]);
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* WIN32 */
|
97
src/libstrongswan/utils/process.h
Normal file
97
src/libstrongswan/utils/process.h
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Martin Willi
|
||||||
|
* Copyright (C) 2014 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 process process
|
||||||
|
* @{ @ingroup utils
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROCESS_H_
|
||||||
|
#define PROCESS_H_
|
||||||
|
|
||||||
|
#include <utils/utils.h>
|
||||||
|
|
||||||
|
typedef struct process_t process_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Child process spawning abstraction
|
||||||
|
*/
|
||||||
|
struct process_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for a started process to terminate.
|
||||||
|
*
|
||||||
|
* The process object gets destroyed by this call, regardless of the
|
||||||
|
* return value.
|
||||||
|
*
|
||||||
|
* The returned code is the exit code, not the status returned by waitpid().
|
||||||
|
* If the program could not be executed or has terminated abnormally
|
||||||
|
* (by signals etc.), FALSE is returned.
|
||||||
|
*
|
||||||
|
* @param code process exit code, set only if TRUE returned
|
||||||
|
* @return TRUE if program exited normally through exit()
|
||||||
|
*/
|
||||||
|
bool (*wait)(process_t *this, int *code);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn a child process with redirected I/O.
|
||||||
|
*
|
||||||
|
* Forks the current process, optionally redirects stdin/out/err to the current
|
||||||
|
* process, and executes the provided program with arguments.
|
||||||
|
*
|
||||||
|
* The process to execute is specified as argv[0], followed by the process
|
||||||
|
* arguments, followed by NULL. envp[] has a NULL terminated list of arguments
|
||||||
|
* to invoke the process with.
|
||||||
|
*
|
||||||
|
* If any of in/out/err is given, stdin/out/err from the child process get
|
||||||
|
* connected over pipe()s to the caller. If close_all is TRUE, all other
|
||||||
|
* open file descriptors get closed, regardless of any CLOEXEC setting.
|
||||||
|
*
|
||||||
|
* A caller must close all of the returned file descriptors to avoid file
|
||||||
|
* descriptor leaks.
|
||||||
|
*
|
||||||
|
* A non-NULL return value does not guarantee that the process has been
|
||||||
|
* invoked successfully.
|
||||||
|
*
|
||||||
|
* @param argv NULL terminated process arguments, with argv[0] as program
|
||||||
|
* @param envp NULL terminated list of environment variables
|
||||||
|
* @param in pipe fd returned for redirecting data to child stdin
|
||||||
|
* @param out pipe fd returned to redirect child stdout data to
|
||||||
|
* @param err pipe fd returned to redirect child stderr data to
|
||||||
|
* @param close_all close all open file descriptors above 2 before execve()
|
||||||
|
* @return process, NULL on failure
|
||||||
|
*/
|
||||||
|
process_t* process_start(char *const argv[], char *const envp[],
|
||||||
|
int *in, int *out, int *err, bool close_all);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn a command in a shell child process.
|
||||||
|
*
|
||||||
|
* Same as process_start(), but passes a single command to a shell, such as
|
||||||
|
* "sh -c". See process_start() for I/O redirection notes.
|
||||||
|
*
|
||||||
|
* @param envp NULL terminated list of environment variables
|
||||||
|
* @param in pipe fd returned for redirecting data to child stdin
|
||||||
|
* @param out pipe fd returned to redirect child stdout data to
|
||||||
|
* @param err pipe fd returned to redirect child stderr data to
|
||||||
|
* @param fmt printf format string for command
|
||||||
|
* @param ... arguments for fmt
|
||||||
|
* @return process, NULL on failure
|
||||||
|
*/
|
||||||
|
process_t* process_start_shell(char *const envp[], int *in, int *out, int *err,
|
||||||
|
char *fmt, ...);
|
||||||
|
|
||||||
|
#endif /** PROCESS_H_ @}*/
|
Loading…
x
Reference in New Issue
Block a user