6fbf66fa |
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* 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.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif |
6a40276c |
#ifdef HAVE_CONFIG_VERSION_H
#include "config-version.h"
#endif |
c110b289 |
|
6fbf66fa |
#include "syshead.h"
#ifdef ENABLE_PLUGIN
|
bdae4110 |
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
|
6fbf66fa |
#include "buffer.h"
#include "error.h"
#include "misc.h"
#include "plugin.h" |
587df08a |
#include "ssl_backend.h" |
71bbbd76 |
#include "win32.h" |
6fbf66fa |
#include "memdbg.h"
#define PLUGIN_SYMBOL_REQUIRED (1<<0)
/* used only for program aborts */ |
3c7f2f55 |
static struct plugin_common *static_plugin_common = NULL; /* GLOBAL */ |
6fbf66fa |
static void |
81d882d5 |
plugin_show_string_array(int msglevel, const char *name, const char *array[]) |
6fbf66fa |
{ |
81d882d5 |
int i;
for (i = 0; array[i]; ++i) |
093e7eba |
{ |
81d882d5 |
if (env_safe_to_print(array[i]))
{
msg(msglevel, "%s[%d] = '%s'", name, i, array[i]);
} |
093e7eba |
} |
6fbf66fa |
}
static void |
81d882d5 |
plugin_show_args_env(int msglevel, const char *argv[], const char *envp[]) |
6fbf66fa |
{ |
81d882d5 |
if (check_debug_level(msglevel)) |
6fbf66fa |
{ |
81d882d5 |
plugin_show_string_array(msglevel, "ARGV", argv);
plugin_show_string_array(msglevel, "ENVP", envp); |
6fbf66fa |
}
}
static const char * |
81d882d5 |
plugin_type_name(const int type)
{
switch (type)
{
case OPENVPN_PLUGIN_UP:
return "PLUGIN_UP";
case OPENVPN_PLUGIN_DOWN:
return "PLUGIN_DOWN";
case OPENVPN_PLUGIN_ROUTE_UP:
return "PLUGIN_ROUTE_UP";
case OPENVPN_PLUGIN_IPCHANGE:
return "PLUGIN_IPCHANGE";
case OPENVPN_PLUGIN_TLS_VERIFY:
return "PLUGIN_TLS_VERIFY";
case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
return "PLUGIN_AUTH_USER_PASS_VERIFY";
case OPENVPN_PLUGIN_CLIENT_CONNECT:
return "PLUGIN_CLIENT_CONNECT";
case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
return "PLUGIN_CLIENT_CONNECT";
case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
return "PLUGIN_CLIENT_DISCONNECT";
case OPENVPN_PLUGIN_LEARN_ADDRESS:
return "PLUGIN_LEARN_ADDRESS";
case OPENVPN_PLUGIN_TLS_FINAL:
return "PLUGIN_TLS_FINAL";
case OPENVPN_PLUGIN_ENABLE_PF:
return "PLUGIN_ENABLE_PF";
case OPENVPN_PLUGIN_ROUTE_PREDOWN:
return "PLUGIN_ROUTE_PREDOWN";
default:
return "PLUGIN_???"; |
6fbf66fa |
}
}
static const char * |
81d882d5 |
plugin_mask_string(const unsigned int type_mask, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
struct buffer out = alloc_buf_gc(256, gc);
bool first = true;
int i; |
6fbf66fa |
|
81d882d5 |
for (i = 0; i < OPENVPN_PLUGIN_N; ++i) |
6fbf66fa |
{ |
81d882d5 |
if (OPENVPN_PLUGIN_MASK(i) & type_mask)
{
if (!first)
{
buf_printf(&out, "|");
}
buf_printf(&out, "%s", plugin_type_name(i));
first = false;
} |
6fbf66fa |
} |
81d882d5 |
return BSTR(&out); |
6fbf66fa |
}
static inline unsigned int |
81d882d5 |
plugin_supported_types(void) |
6fbf66fa |
{ |
81d882d5 |
return ((1<<OPENVPN_PLUGIN_N)-1); |
6fbf66fa |
}
struct plugin_option_list * |
81d882d5 |
plugin_option_list_new(struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
struct plugin_option_list *ret;
ALLOC_OBJ_CLEAR_GC(ret, struct plugin_option_list, gc);
return ret; |
6fbf66fa |
}
bool |
81d882d5 |
plugin_option_list_add(struct plugin_option_list *list, char **p, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
if (list->n < MAX_PLUGINS) |
6fbf66fa |
{ |
81d882d5 |
struct plugin_option *o = &list->plugins[list->n++];
o->argv = make_extended_arg_array(p, gc);
if (o->argv[0])
{
o->so_pathname = o->argv[0];
}
return true;
}
else
{
return false; |
6fbf66fa |
}
}
|
6c61d0dd |
#ifndef ENABLE_SMALL |
6fbf66fa |
void |
81d882d5 |
plugin_option_list_print(const struct plugin_option_list *list, int msglevel) |
6fbf66fa |
{ |
81d882d5 |
int i;
struct gc_arena gc = gc_new(); |
eadf16a6 |
|
81d882d5 |
for (i = 0; i < list->n; ++i) |
6fbf66fa |
{ |
81d882d5 |
const struct plugin_option *o = &list->plugins[i];
msg(msglevel, " plugin[%d] %s '%s'", i, o->so_pathname, print_argv(o->argv, &gc, PA_BRACKET)); |
6fbf66fa |
} |
eadf16a6 |
|
81d882d5 |
gc_free(&gc); |
6fbf66fa |
}
#endif
|
445b192a |
#ifndef _WIN32 |
6fbf66fa |
static void |
81d882d5 |
libdl_resolve_symbol(void *handle, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags) |
6fbf66fa |
{ |
81d882d5 |
*dest = dlsym(handle, symbol);
if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
{
msg(M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin shared object %s: %s", symbol, plugin_name, dlerror());
} |
6fbf66fa |
}
|
81d882d5 |
#else /* ifndef _WIN32 */ |
6fbf66fa |
static void |
81d882d5 |
dll_resolve_symbol(HMODULE module, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags) |
6fbf66fa |
{ |
81d882d5 |
*dest = GetProcAddress(module, symbol);
if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
{
msg(M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin DLL %s", symbol, plugin_name);
} |
6fbf66fa |
}
|
81d882d5 |
#endif /* ifndef _WIN32 */ |
6fbf66fa |
static void |
81d882d5 |
plugin_init_item(struct plugin *p, const struct plugin_option *o) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
bool rel = false; |
d1dcc3e7 |
|
81d882d5 |
p->so_pathname = o->so_pathname;
p->plugin_type_mask = plugin_supported_types(); |
6fbf66fa |
|
445b192a |
#ifndef _WIN32 |
3c7f2f55 |
|
81d882d5 |
p->handle = NULL; |
4590c383 |
|
f9609f1d |
/* If the plug-in filename is not an absolute path,
* or beginning with '.', it should use the PLUGIN_LIBDIR
* as the base directory for loading the plug-in.
*
* This means the following scenarios are loaded from these places:
* --plugin fancyplug.so -> $PLUGIN_LIBDIR/fancyplug.so
* --plugin my/fancyplug.so -> $PLUGIN_LIBDIR/my/fancyplug.so
* --plugin ./fancyplug.so -> $CWD/fancyplug.so
* --plugin /usr/lib/my/fancyplug.so -> /usr/lib/my/fancyplug.so
*
* Please note that $CWD means the directory OpenVPN is either started from
* or the directory OpenVPN have changed into using --cd before --plugin
* was parsed.
*
*/ |
b7bea782 |
if (!platform_absolute_pathname(p->so_pathname) |
f9609f1d |
&& p->so_pathname[0] != '.') |
e342be3f |
{ |
81d882d5 |
char full[PATH_MAX]; |
e342be3f |
|
81d882d5 |
openvpn_snprintf(full, sizeof(full), "%s/%s", PLUGIN_LIBDIR, p->so_pathname);
p->handle = dlopen(full, RTLD_NOW); |
e342be3f |
} |
81d882d5 |
else |
e342be3f |
{ |
b7bea782 |
rel = !platform_absolute_pathname(p->so_pathname); |
81d882d5 |
p->handle = dlopen(p->so_pathname, RTLD_NOW);
}
if (!p->handle)
{
msg(M_ERR, "PLUGIN_INIT: could not load plugin shared object %s: %s", p->so_pathname, dlerror()); |
e342be3f |
} |
3c7f2f55 |
|
81d882d5 |
#define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol(p->handle, (void *)&p->var, name, p->so_pathname, flags) |
3c7f2f55 |
|
81d882d5 |
#else /* ifndef _WIN32 */ |
3c7f2f55 |
|
b7bea782 |
rel = !platform_absolute_pathname(p->so_pathname); |
81d882d5 |
p->module = LoadLibraryW(wide_string(p->so_pathname, &gc));
if (!p->module)
{
msg(M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname);
} |
3c7f2f55 |
|
81d882d5 |
#define PLUGIN_SYM(var, name, flags) dll_resolve_symbol(p->module, (void *)&p->var, name, p->so_pathname, flags) |
3c7f2f55 |
|
81d882d5 |
#endif /* ifndef _WIN32 */ |
6fbf66fa |
|
81d882d5 |
PLUGIN_SYM(open1, "openvpn_plugin_open_v1", 0);
PLUGIN_SYM(open2, "openvpn_plugin_open_v2", 0);
PLUGIN_SYM(open3, "openvpn_plugin_open_v3", 0);
PLUGIN_SYM(func1, "openvpn_plugin_func_v1", 0);
PLUGIN_SYM(func2, "openvpn_plugin_func_v2", 0);
PLUGIN_SYM(func3, "openvpn_plugin_func_v3", 0);
PLUGIN_SYM(close, "openvpn_plugin_close_v1", PLUGIN_SYMBOL_REQUIRED);
PLUGIN_SYM(abort, "openvpn_plugin_abort_v1", 0);
PLUGIN_SYM(client_constructor, "openvpn_plugin_client_constructor_v1", 0);
PLUGIN_SYM(client_destructor, "openvpn_plugin_client_destructor_v1", 0);
PLUGIN_SYM(min_version_required, "openvpn_plugin_min_version_required_v1", 0);
PLUGIN_SYM(initialization_point, "openvpn_plugin_select_initialization_point_v1", 0); |
3c7f2f55 |
|
81d882d5 |
if (!p->open1 && !p->open2 && !p->open3)
{
msg(M_FATAL, "PLUGIN: symbol openvpn_plugin_open_vX is undefined in plugin: %s", p->so_pathname);
} |
3c7f2f55 |
|
81d882d5 |
if (!p->func1 && !p->func2 && !p->func3)
{
msg(M_FATAL, "PLUGIN: symbol openvpn_plugin_func_vX is undefined in plugin: %s", p->so_pathname);
} |
3c7f2f55 |
|
81d882d5 |
/*
* Verify that we are sufficiently up-to-date to handle the plugin
*/
if (p->min_version_required) |
3c7f2f55 |
{ |
81d882d5 |
const int plugin_needs_version = (*p->min_version_required)();
if (plugin_needs_version > OPENVPN_PLUGIN_VERSION)
{
msg(M_FATAL, "PLUGIN_INIT: plugin needs interface version %d, but this version of OpenVPN only supports version %d: %s",
plugin_needs_version,
OPENVPN_PLUGIN_VERSION,
p->so_pathname);
} |
3c7f2f55 |
}
|
81d882d5 |
if (p->initialization_point)
{
p->requested_initialization_point = (*p->initialization_point)();
}
else
{
p->requested_initialization_point = OPENVPN_PLUGIN_INIT_PRE_DAEMON;
} |
e1791bb1 |
|
81d882d5 |
if (rel)
{
msg(M_WARN, "WARNING: plugin '%s' specified by a relative pathname -- using an absolute pathname would be more secure", p->so_pathname);
} |
d1dcc3e7 |
|
81d882d5 |
p->initialized = true; |
e1791bb1 |
|
81d882d5 |
gc_free(&gc); |
e1791bb1 |
}
static void |
81d882d5 |
plugin_vlog(openvpn_plugin_log_flags_t flags, const char *name, const char *format, va_list arglist) |
be532e0d |
{ |
81d882d5 |
unsigned int msg_flags = 0; |
be532e0d |
|
81d882d5 |
if (!format)
{
return;
} |
be532e0d |
|
81d882d5 |
if (!name || name[0] == '\0') |
be532e0d |
{ |
81d882d5 |
msg(D_PLUGIN_DEBUG, "PLUGIN: suppressed log message from plugin with unknown name");
return; |
be532e0d |
}
|
81d882d5 |
if (flags & PLOG_ERR)
{
msg_flags = M_INFO | M_NONFATAL;
}
else if (flags & PLOG_WARN)
{
msg_flags = M_INFO | M_WARN;
}
else if (flags & PLOG_NOTE)
{
msg_flags = M_INFO;
}
else if (flags & PLOG_DEBUG)
{
msg_flags = D_PLUGIN_DEBUG;
} |
be532e0d |
|
81d882d5 |
if (flags & PLOG_ERRNO)
{
msg_flags |= M_ERRNO;
}
if (flags & PLOG_NOMUTE)
{
msg_flags |= M_NOMUTE;
} |
be532e0d |
|
81d882d5 |
if (msg_test(msg_flags)) |
be532e0d |
{ |
81d882d5 |
struct gc_arena gc;
char *msg_fmt; |
be532e0d |
|
81d882d5 |
/* Never add instance prefix; not thread safe */
msg_flags |= M_NOIPREFIX; |
be532e0d |
|
81d882d5 |
gc_init(&gc);
msg_fmt = gc_malloc(ERR_BUF_SIZE, false, &gc);
openvpn_snprintf(msg_fmt, ERR_BUF_SIZE, "PLUGIN %s: %s", name, format);
x_msg_va(msg_flags, msg_fmt, arglist); |
be532e0d |
|
81d882d5 |
gc_free(&gc); |
be532e0d |
}
}
static void |
81d882d5 |
plugin_log(openvpn_plugin_log_flags_t flags, const char *name, const char *format, ...) |
be532e0d |
{ |
81d882d5 |
va_list arglist;
va_start(arglist, format);
plugin_vlog(flags, name, format, arglist);
va_end(arglist); |
be532e0d |
}
static struct openvpn_plugin_callbacks callbacks = { |
81d882d5 |
plugin_log, |
f018dfcc |
plugin_vlog,
secure_memzero /* plugin_secure_memzero */ |
be532e0d |
};
|
6a40276c |
/* Provide a wrapper macro for a version patch level string to plug-ins.
* This is located here purely to not make the code too messy with #ifndef
* inside a struct declaration
*/
#ifndef CONFIGURE_GIT_REVISION |
81d882d5 |
#define _OPENVPN_PATCH_LEVEL OPENVPN_VERSION_PATCH |
6a40276c |
#else |
81d882d5 |
#define _OPENVPN_PATCH_LEVEL "git:" CONFIGURE_GIT_REVISION CONFIGURE_GIT_FLAGS |
6a40276c |
#endif
|
be532e0d |
static void |
81d882d5 |
plugin_open_item(struct plugin *p,
const struct plugin_option *o,
struct openvpn_plugin_string_list **retlist,
const char **envp,
const int init_point)
{
ASSERT(p->initialized);
/* clear return list */
if (retlist)
{
*retlist = NULL;
}
if (!p->plugin_handle && init_point == p->requested_initialization_point)
{
struct gc_arena gc = gc_new();
dmsg(D_PLUGIN_DEBUG, "PLUGIN_INIT: PRE");
plugin_show_args_env(D_PLUGIN_DEBUG, o->argv, envp);
/*
* Call the plugin initialization
*/
if (p->open3)
{
struct openvpn_plugin_args_open_in args = { p->plugin_type_mask,
(const char **const) o->argv,
(const char **const) envp,
&callbacks,
SSLAPI,
PACKAGE_VERSION,
OPENVPN_VERSION_MAJOR,
OPENVPN_VERSION_MINOR,
_OPENVPN_PATCH_LEVEL};
struct openvpn_plugin_args_open_return retargs;
CLEAR(retargs);
retargs.return_list = retlist;
if ((*p->open3)(OPENVPN_PLUGINv3_STRUCTVER, &args, &retargs) == OPENVPN_PLUGIN_FUNC_SUCCESS)
{
p->plugin_type_mask = retargs.type_mask;
p->plugin_handle = retargs.handle;
}
else
{
p->plugin_handle = NULL;
}
}
else if (p->open2)
{
p->plugin_handle = (*p->open2)(&p->plugin_type_mask, o->argv, envp, retlist);
}
else if (p->open1)
{
p->plugin_handle = (*p->open1)(&p->plugin_type_mask, o->argv, envp);
}
else
{
ASSERT(0); |
76b41d7f |
} |
6fbf66fa |
|
81d882d5 |
msg(D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s %s",
p->so_pathname,
print_argv(o->argv, &gc, PA_BRACKET),
plugin_mask_string(p->plugin_type_mask, &gc),
(retlist && *retlist) ? "[RETLIST]" : "");
if ((p->plugin_type_mask | plugin_supported_types()) != plugin_supported_types())
{
msg(M_FATAL, "PLUGIN_INIT: plugin %s expressed interest in unsupported plugin types: [want=0x%08x, have=0x%08x]",
p->so_pathname,
p->plugin_type_mask,
plugin_supported_types());
} |
e1791bb1 |
|
81d882d5 |
if (p->plugin_handle == NULL)
{
msg(M_FATAL, "PLUGIN_INIT: plugin initialization function failed: %s",
p->so_pathname);
} |
6fbf66fa |
|
81d882d5 |
gc_free(&gc); |
e1791bb1 |
} |
6fbf66fa |
}
static int |
81d882d5 |
plugin_call_item(const struct plugin *p,
void *per_client_context,
const int type,
const struct argv *av,
struct openvpn_plugin_string_list **retlist, |
c7ca9133 |
const char **envp,
int certdepth, |
81d882d5 |
openvpn_x509_cert_t *current_cert
) |
6fbf66fa |
{ |
81d882d5 |
int status = OPENVPN_PLUGIN_FUNC_SUCCESS; |
e1791bb1 |
|
81d882d5 |
/* clear return list */
if (retlist) |
6fbf66fa |
{ |
81d882d5 |
*retlist = NULL;
} |
6fbf66fa |
|
81d882d5 |
if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK(type)))
{
struct gc_arena gc = gc_new();
struct argv a = argv_insert_head(av, p->so_pathname);
dmsg(D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name(type));
plugin_show_args_env(D_PLUGIN_DEBUG, (const char **)a.argv, envp);
/*
* Call the plugin work function
*/
if (p->func3)
{
struct openvpn_plugin_args_func_in args = { type,
(const char **const) a.argv,
(const char **const) envp,
p->plugin_handle,
per_client_context,
(current_cert ? certdepth : -1),
current_cert
}; |
76b41d7f |
|
81d882d5 |
struct openvpn_plugin_args_func_return retargs; |
6fbf66fa |
|
81d882d5 |
CLEAR(retargs);
retargs.return_list = retlist;
status = (*p->func3)(OPENVPN_PLUGINv3_STRUCTVER, &args, &retargs);
}
else if (p->func2)
{
status = (*p->func2)(p->plugin_handle, type, (const char **)a.argv, envp, per_client_context, retlist);
}
else if (p->func1)
{
status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp);
}
else
{
ASSERT(0);
} |
6fbf66fa |
|
81d882d5 |
msg(D_PLUGIN, "PLUGIN_CALL: POST %s/%s status=%d",
p->so_pathname,
plugin_type_name(type),
status);
if (status == OPENVPN_PLUGIN_FUNC_ERROR)
{
msg(M_WARN, "PLUGIN_CALL: plugin function %s failed with status %d: %s",
plugin_type_name(type),
status,
p->so_pathname);
} |
6fbf66fa |
|
81d882d5 |
argv_reset(&a);
gc_free(&gc); |
6fbf66fa |
} |
81d882d5 |
return status; |
6fbf66fa |
}
static void |
81d882d5 |
plugin_close_item(struct plugin *p) |
6fbf66fa |
{ |
81d882d5 |
if (p->initialized) |
e1791bb1 |
{ |
81d882d5 |
msg(D_PLUGIN, "PLUGIN_CLOSE: %s", p->so_pathname);
/*
* Call the plugin close function
*/
if (p->plugin_handle)
{
(*p->close)(p->plugin_handle);
} |
6fbf66fa |
|
445b192a |
#ifndef _WIN32 |
81d882d5 |
if (dlclose(p->handle))
{
msg(M_WARN, "PLUGIN_CLOSE: dlclose() failed on plugin: %s", p->so_pathname);
} |
445b192a |
#elif defined(_WIN32) |
81d882d5 |
if (!FreeLibrary(p->module))
{
msg(M_WARN, "PLUGIN_CLOSE: FreeLibrary() failed on plugin: %s", p->so_pathname);
} |
6fbf66fa |
#endif |
e1791bb1 |
|
81d882d5 |
p->initialized = false; |
e1791bb1 |
} |
6fbf66fa |
}
static void |
81d882d5 |
plugin_abort_item(const struct plugin *p) |
6fbf66fa |
{ |
81d882d5 |
/*
* Call the plugin abort function
*/
if (p->abort)
{
(*p->abort)(p->plugin_handle);
} |
6fbf66fa |
}
|
3c7f2f55 |
static void |
81d882d5 |
plugin_per_client_init(const struct plugin_common *pc,
struct plugin_per_client *cli,
const int init_point) |
3c7f2f55 |
{ |
81d882d5 |
const int n = pc->n;
int i; |
3c7f2f55 |
|
81d882d5 |
for (i = 0; i < n; ++i) |
3c7f2f55 |
{ |
81d882d5 |
const struct plugin *p = &pc->plugins[i];
if (p->plugin_handle
&& (init_point < 0 || init_point == p->requested_initialization_point)
&& p->client_constructor)
{
cli->per_client_context[i] = (*p->client_constructor)(p->plugin_handle);
} |
3c7f2f55 |
}
}
static void |
81d882d5 |
plugin_per_client_destroy(const struct plugin_common *pc, struct plugin_per_client *cli) |
3c7f2f55 |
{ |
81d882d5 |
const int n = pc->n;
int i; |
3c7f2f55 |
|
81d882d5 |
for (i = 0; i < n; ++i) |
e1791bb1 |
{ |
81d882d5 |
const struct plugin *p = &pc->plugins[i];
void *cc = cli->per_client_context[i]; |
3c7f2f55 |
|
81d882d5 |
if (p->client_destructor && cc)
{
(*p->client_destructor)(p->plugin_handle, cc);
} |
3c7f2f55 |
} |
81d882d5 |
CLEAR(*cli); |
3c7f2f55 |
}
|
6fbf66fa |
struct plugin_list * |
81d882d5 |
plugin_list_inherit(const struct plugin_list *src) |
3c7f2f55 |
{ |
81d882d5 |
struct plugin_list *pl;
ALLOC_OBJ_CLEAR(pl, struct plugin_list);
pl->common = src->common;
ASSERT(pl->common);
plugin_per_client_init(pl->common, &pl->per_client, -1);
return pl; |
3c7f2f55 |
}
static struct plugin_common * |
81d882d5 |
plugin_common_init(const struct plugin_option_list *list) |
6fbf66fa |
{ |
81d882d5 |
int i;
struct plugin_common *pc; |
6fbf66fa |
|
81d882d5 |
ALLOC_OBJ_CLEAR(pc, struct plugin_common); |
6fbf66fa |
|
81d882d5 |
for (i = 0; i < list->n; ++i) |
e1791bb1 |
{ |
81d882d5 |
plugin_init_item(&pc->plugins[i],
&list->plugins[i]);
pc->n = i + 1; |
e1791bb1 |
}
|
81d882d5 |
static_plugin_common = pc;
return pc; |
e1791bb1 |
}
static void |
81d882d5 |
plugin_common_open(struct plugin_common *pc,
const struct plugin_option_list *list,
struct plugin_return *pr,
const struct env_set *es,
const int init_point) |
e1791bb1 |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i;
const char **envp; |
e1791bb1 |
|
81d882d5 |
envp = make_env_array(es, false, &gc); |
6fbf66fa |
|
81d882d5 |
if (pr)
{
plugin_return_init(pr);
} |
3c7f2f55 |
|
81d882d5 |
for (i = 0; i < pc->n; ++i) |
6fbf66fa |
{ |
81d882d5 |
plugin_open_item(&pc->plugins[i],
&list->plugins[i],
pr ? &pr->list[i] : NULL,
envp,
init_point); |
6fbf66fa |
}
|
81d882d5 |
if (pr)
{
pr->n = i;
} |
3c7f2f55 |
|
81d882d5 |
gc_free(&gc); |
3c7f2f55 |
}
static void |
81d882d5 |
plugin_common_close(struct plugin_common *pc) |
3c7f2f55 |
{ |
81d882d5 |
static_plugin_common = NULL;
if (pc) |
3c7f2f55 |
{ |
81d882d5 |
int i; |
3c7f2f55 |
|
81d882d5 |
for (i = 0; i < pc->n; ++i) |
4cd4899e |
{ |
81d882d5 |
plugin_close_item(&pc->plugins[i]); |
4cd4899e |
} |
81d882d5 |
free(pc); |
3c7f2f55 |
}
}
struct plugin_list * |
81d882d5 |
plugin_list_init(const struct plugin_option_list *list) |
3c7f2f55 |
{ |
81d882d5 |
struct plugin_list *pl;
ALLOC_OBJ_CLEAR(pl, struct plugin_list);
pl->common = plugin_common_init(list);
pl->common_owned = true;
return pl; |
6fbf66fa |
}
|
e1791bb1 |
void |
81d882d5 |
plugin_list_open(struct plugin_list *pl,
const struct plugin_option_list *list,
struct plugin_return *pr,
const struct env_set *es,
const int init_point) |
e1791bb1 |
{ |
81d882d5 |
plugin_common_open(pl->common, list, pr, es, init_point);
plugin_per_client_init(pl->common, &pl->per_client, init_point); |
e1791bb1 |
}
|
6fbf66fa |
int |
81d882d5 |
plugin_call_ssl(const struct plugin_list *pl,
const int type,
const struct argv *av,
struct plugin_return *pr, |
c7ca9133 |
struct env_set *es,
int certdepth, |
81d882d5 |
openvpn_x509_cert_t *current_cert
)
{
if (pr)
{
plugin_return_init(pr);
}
if (plugin_defined(pl, type))
{
struct gc_arena gc = gc_new();
int i;
const char **envp;
const int n = plugin_n(pl);
bool success = false;
bool error = false;
bool deferred = false;
setenv_del(es, "script_type");
envp = make_env_array(es, false, &gc);
for (i = 0; i < n; ++i)
{
const int status = plugin_call_item(&pl->common->plugins[i],
pl->per_client.per_client_context[i],
type,
av,
pr ? &pr->list[i] : NULL, |
c7ca9133 |
envp,
certdepth, |
81d882d5 |
current_cert
);
switch (status)
{
case OPENVPN_PLUGIN_FUNC_SUCCESS:
success = true;
break;
case OPENVPN_PLUGIN_FUNC_DEFERRED:
deferred = true;
break;
default:
error = true;
break;
}
} |
6fbf66fa |
|
81d882d5 |
if (pr)
{
pr->n = i;
} |
3c7f2f55 |
|
81d882d5 |
gc_free(&gc); |
6fbf66fa |
|
81d882d5 |
if (type == OPENVPN_PLUGIN_ENABLE_PF && success)
{
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
else if (error)
{
return OPENVPN_PLUGIN_FUNC_ERROR;
}
else if (deferred)
{
return OPENVPN_PLUGIN_FUNC_DEFERRED;
} |
3c7f2f55 |
} |
344ee918 |
|
81d882d5 |
return OPENVPN_PLUGIN_FUNC_SUCCESS; |
6fbf66fa |
}
void |
81d882d5 |
plugin_list_close(struct plugin_list *pl) |
6fbf66fa |
{ |
81d882d5 |
if (pl) |
6fbf66fa |
{ |
81d882d5 |
if (pl->common)
{
plugin_per_client_destroy(pl->common, &pl->per_client);
if (pl->common_owned)
{
plugin_common_close(pl->common);
}
} |
3c7f2f55 |
|
81d882d5 |
free(pl); |
6fbf66fa |
}
}
void |
81d882d5 |
plugin_abort(void) |
6fbf66fa |
{ |
81d882d5 |
struct plugin_common *pc = static_plugin_common;
static_plugin_common = NULL;
if (pc) |
6fbf66fa |
{ |
81d882d5 |
int i; |
6fbf66fa |
|
81d882d5 |
for (i = 0; i < pc->n; ++i) |
4cd4899e |
{ |
81d882d5 |
plugin_abort_item(&pc->plugins[i]); |
4cd4899e |
} |
6fbf66fa |
}
}
bool |
81d882d5 |
plugin_defined(const struct plugin_list *pl, const int type) |
6fbf66fa |
{ |
81d882d5 |
bool ret = false; |
3c7f2f55 |
|
81d882d5 |
if (pl) |
6fbf66fa |
{ |
81d882d5 |
const struct plugin_common *pc = pl->common;
if (pc)
{
int i;
const unsigned int mask = OPENVPN_PLUGIN_MASK(type);
for (i = 0; i < pc->n; ++i)
{
if (pc->plugins[i].plugin_type_mask & mask)
{
ret = true;
break;
}
}
} |
6fbf66fa |
} |
81d882d5 |
return ret; |
6fbf66fa |
}
|
3c7f2f55 |
/*
* Plugin return functions
*/
static void |
81d882d5 |
openvpn_plugin_string_list_item_free(struct openvpn_plugin_string_list *l) |
3c7f2f55 |
{ |
81d882d5 |
if (l) |
3c7f2f55 |
{ |
81d882d5 |
free(l->name);
string_clear(l->value);
free(l->value);
free(l); |
3c7f2f55 |
}
}
static void |
81d882d5 |
openvpn_plugin_string_list_free(struct openvpn_plugin_string_list *l) |
3c7f2f55 |
{ |
81d882d5 |
struct openvpn_plugin_string_list *next;
while (l) |
3c7f2f55 |
{ |
81d882d5 |
next = l->next;
openvpn_plugin_string_list_item_free(l);
l = next; |
3c7f2f55 |
}
}
static struct openvpn_plugin_string_list * |
81d882d5 |
openvpn_plugin_string_list_find(struct openvpn_plugin_string_list *l, const char *name) |
3c7f2f55 |
{ |
81d882d5 |
while (l) |
3c7f2f55 |
{ |
81d882d5 |
if (!strcmp(l->name, name))
{
return l;
}
l = l->next; |
3c7f2f55 |
} |
81d882d5 |
return NULL; |
3c7f2f55 |
}
void |
81d882d5 |
plugin_return_get_column(const struct plugin_return *src,
struct plugin_return *dest,
const char *colname) |
3c7f2f55 |
{ |
81d882d5 |
int i; |
3c7f2f55 |
|
81d882d5 |
dest->n = 0;
for (i = 0; i < src->n; ++i) |
4cd4899e |
{ |
81d882d5 |
dest->list[i] = openvpn_plugin_string_list_find(src->list[i], colname); |
4cd4899e |
} |
81d882d5 |
dest->n = i; |
3c7f2f55 |
}
void |
81d882d5 |
plugin_return_free(struct plugin_return *pr) |
3c7f2f55 |
{ |
81d882d5 |
int i;
for (i = 0; i < pr->n; ++i) |
4cd4899e |
{ |
81d882d5 |
openvpn_plugin_string_list_free(pr->list[i]); |
4cd4899e |
} |
81d882d5 |
pr->n = 0; |
3c7f2f55 |
}
#ifdef ENABLE_DEBUG
void |
81d882d5 |
plugin_return_print(const int msglevel, const char *prefix, const struct plugin_return *pr) |
3c7f2f55 |
{ |
81d882d5 |
int i;
msg(msglevel, "PLUGIN_RETURN_PRINT %s", prefix);
for (i = 0; i < pr->n; ++i) |
3c7f2f55 |
{ |
81d882d5 |
struct openvpn_plugin_string_list *l = pr->list[i];
int count = 0;
msg(msglevel, "PLUGIN #%d (%s)", i, prefix);
while (l)
{
msg(msglevel, "[%d] '%s' -> '%s'\n",
++count,
l->name,
l->value);
l = l->next;
} |
3c7f2f55 |
}
} |
81d882d5 |
#endif /* ifdef ENABLE_DEBUG */ |
3c7f2f55 |
|
81d882d5 |
#else /* ifdef ENABLE_PLUGIN */
static void |
4cd4899e |
dummy(void)
{ |
81d882d5 |
} |
6fbf66fa |
#endif /* ENABLE_PLUGIN */ |