openvpn/plugin.c
David Sommerseth 7aa6c12a44 Clean-up: Remove pthread and mutex locking code
This code was not activated at all, and hard coded as disabled in syshead.h
with this code snippet:

   /*
    * Pthread support is currently experimental (and quite unfinished).
    */
   #if 1 /* JYFIXME -- if defined, disable pthread */
   #undef USE_PTHREAD
   #endif

So no matter if --enable-pthread when running ./configure or not, this feature
was never enabled in reality.  Further, by removing the blocker code above made
OpenVPN uncompilable in the current state.

As the threading part needs to be completely rewritten and pthreading will not be
supported in OpenVPN 2.x, removing this code seems most reasonable.

In addition, a lot of mutex locking code was also removed, as they were practically
NOP functions, due to pthreading being forcefully disabled

Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>
Acked-by: James Yonan <james@openvpn.net>
2010-11-14 22:05:45 +01:00

748 lines
18 KiB
C

/*
* 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.
*
* Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@openvpn.net>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "syshead.h"
#ifdef ENABLE_PLUGIN
#include "buffer.h"
#include "error.h"
#include "misc.h"
#include "plugin.h"
#include "memdbg.h"
#define PLUGIN_SYMBOL_REQUIRED (1<<0)
/* used only for program aborts */
static struct plugin_common *static_plugin_common = NULL; /* GLOBAL */
static void
plugin_show_string_array (int msglevel, const char *name, const char *array[])
{
int i;
for (i = 0; array[i]; ++i)
{
if (env_safe_to_print (array[i]))
msg (msglevel, "%s[%d] = '%s'", name, i, array[i]);
}
}
static void
plugin_show_args_env (int msglevel, const char *argv[], const char *envp[])
{
if (check_debug_level (msglevel))
{
plugin_show_string_array (msglevel, "ARGV", argv);
plugin_show_string_array (msglevel, "ENVP", envp);
}
}
static const char *
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 "OPENVPN_PLUGIN_ENABLE_PF";
default:
return "PLUGIN_???";
}
}
static const char *
plugin_mask_string (const unsigned int type_mask, struct gc_arena *gc)
{
struct buffer out = alloc_buf_gc (256, gc);
bool first = true;
int i;
for (i = 0; i < OPENVPN_PLUGIN_N; ++i)
{
if (OPENVPN_PLUGIN_MASK (i) & type_mask)
{
if (!first)
buf_printf (&out, "|");
buf_printf (&out, "%s", plugin_type_name (i));
first = false;
}
}
return BSTR (&out);
}
static inline unsigned int
plugin_supported_types (void)
{
return ((1<<OPENVPN_PLUGIN_N)-1);
}
struct plugin_option_list *
plugin_option_list_new (struct gc_arena *gc)
{
struct plugin_option_list *ret;
ALLOC_OBJ_CLEAR_GC (ret, struct plugin_option_list, gc);
return ret;
}
bool
plugin_option_list_add (struct plugin_option_list *list, char **p, struct gc_arena *gc)
{
if (list->n < MAX_PLUGINS)
{
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;
}
#ifdef ENABLE_DEBUG
void
plugin_option_list_print (const struct plugin_option_list *list, int msglevel)
{
int i;
struct gc_arena gc = gc_new ();
for (i = 0; i < list->n; ++i)
{
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));
}
gc_free (&gc);
}
#endif
#if defined(USE_LIBDL)
static void
libdl_resolve_symbol (void *handle, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
{
*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());
}
#elif defined(USE_LOAD_LIBRARY)
static void
dll_resolve_symbol (HMODULE module, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
{
*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);
}
#endif
static void
plugin_init_item (struct plugin *p, const struct plugin_option *o)
{
struct gc_arena gc = gc_new ();
bool rel = false;
p->so_pathname = o->so_pathname;
p->plugin_type_mask = plugin_supported_types ();
#if defined(USE_LIBDL)
p->handle = NULL;
#if defined(PLUGIN_LIBDIR)
if (!absolute_pathname (p->so_pathname))
{
char full[PATH_MAX];
openvpn_snprintf (full, sizeof(full), "%s/%s", PLUGIN_LIBDIR, p->so_pathname);
p->handle = dlopen (full, RTLD_NOW);
#if defined(ENABLE_PLUGIN_SEARCH)
if (!p->handle)
{
rel = true;
p->handle = dlopen (p->so_pathname, RTLD_NOW);
}
#endif
}
else
#endif
{
rel = !absolute_pathname (p->so_pathname);
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());
# define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol (p->handle, (void*)&p->var, name, p->so_pathname, flags)
#elif defined(USE_LOAD_LIBRARY)
rel = !absolute_pathname (p->so_pathname);
p->module = LoadLibrary (p->so_pathname);
if (!p->module)
msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname);
# define PLUGIN_SYM(var, name, flags) dll_resolve_symbol (p->module, (void*)&p->var, name, p->so_pathname, flags)
#endif
PLUGIN_SYM (open1, "openvpn_plugin_open_v1", 0);
PLUGIN_SYM (open2, "openvpn_plugin_open_v2", 0);
PLUGIN_SYM (func1, "openvpn_plugin_func_v1", 0);
PLUGIN_SYM (func2, "openvpn_plugin_func_v2", 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);
if (!p->open1 && !p->open2)
msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_open_vX is undefined in plugin: %s", p->so_pathname);
if (!p->func1 && !p->func2)
msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_func_vX is undefined in plugin: %s", p->so_pathname);
/*
* Verify that we are sufficiently up-to-date to handle the plugin
*/
if (p->min_version_required)
{
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);
}
if (p->initialization_point)
p->requested_initialization_point = (*p->initialization_point)();
else
p->requested_initialization_point = OPENVPN_PLUGIN_INIT_PRE_DAEMON;
if (rel)
msg (M_WARN, "WARNING: plugin '%s' specified by a relative pathname -- using an absolute pathname would be more secure", p->so_pathname);
p->initialized = true;
gc_free (&gc);
}
static void
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->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);
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());
if (p->plugin_handle == NULL)
msg (M_FATAL, "PLUGIN_INIT: plugin initialization function failed: %s",
p->so_pathname);
gc_free (&gc);
}
}
static int
plugin_call_item (const struct plugin *p,
void *per_client_context,
const int type,
const struct argv *av,
struct openvpn_plugin_string_list **retlist,
const char **envp)
{
int status = OPENVPN_PLUGIN_FUNC_SUCCESS;
/* clear return list */
if (retlist)
*retlist = NULL;
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->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);
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);
argv_reset (&a);
gc_free (&gc);
}
return status;
}
static void
plugin_close_item (struct plugin *p)
{
if (p->initialized)
{
msg (D_PLUGIN, "PLUGIN_CLOSE: %s", p->so_pathname);
/*
* Call the plugin close function
*/
if (p->plugin_handle)
(*p->close)(p->plugin_handle);
#if defined(USE_LIBDL)
if (dlclose (p->handle))
msg (M_WARN, "PLUGIN_CLOSE: dlclose() failed on plugin: %s", p->so_pathname);
#elif defined(USE_LOAD_LIBRARY)
if (!FreeLibrary (p->module))
msg (M_WARN, "PLUGIN_CLOSE: FreeLibrary() failed on plugin: %s", p->so_pathname);
#endif
p->initialized = false;
}
}
static void
plugin_abort_item (const struct plugin *p)
{
/*
* Call the plugin abort function
*/
if (p->abort)
(*p->abort)(p->plugin_handle);
}
static void
plugin_per_client_init (const struct plugin_common *pc,
struct plugin_per_client *cli,
const int init_point)
{
const int n = pc->n;
int i;
for (i = 0; i < n; ++i)
{
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);
}
}
static void
plugin_per_client_destroy (const struct plugin_common *pc, struct plugin_per_client *cli)
{
const int n = pc->n;
int i;
for (i = 0; i < n; ++i)
{
const struct plugin *p = &pc->plugins[i];
void *cc = cli->per_client_context[i];
if (p->client_destructor && cc)
(*p->client_destructor)(p->plugin_handle, cc);
}
CLEAR (*cli);
}
struct plugin_list *
plugin_list_inherit (const struct plugin_list *src)
{
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;
}
static struct plugin_common *
plugin_common_init (const struct plugin_option_list *list)
{
int i;
struct plugin_common *pc;
ALLOC_OBJ_CLEAR (pc, struct plugin_common);
for (i = 0; i < list->n; ++i)
{
plugin_init_item (&pc->plugins[i],
&list->plugins[i]);
pc->n = i + 1;
}
static_plugin_common = pc;
return pc;
}
static void
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)
{
struct gc_arena gc = gc_new ();
int i;
const char **envp;
envp = make_env_array (es, false, &gc);
if (pr)
plugin_return_init (pr);
for (i = 0; i < pc->n; ++i)
{
plugin_open_item (&pc->plugins[i],
&list->plugins[i],
pr ? &pr->list[i] : NULL,
envp,
init_point);
}
if (pr)
pr->n = i;
gc_free (&gc);
}
static void
plugin_common_close (struct plugin_common *pc)
{
static_plugin_common = NULL;
if (pc)
{
int i;
for (i = 0; i < pc->n; ++i)
plugin_close_item (&pc->plugins[i]);
free (pc);
}
}
struct plugin_list *
plugin_list_init (const struct plugin_option_list *list)
{
struct plugin_list *pl;
ALLOC_OBJ_CLEAR (pl, struct plugin_list);
pl->common = plugin_common_init (list);
pl->common_owned = true;
return pl;
}
void
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)
{
plugin_common_open (pl->common, list, pr, es, init_point);
plugin_per_client_init (pl->common, &pl->per_client, init_point);
}
int
plugin_call (const struct plugin_list *pl,
const int type,
const struct argv *av,
struct plugin_return *pr,
struct env_set *es)
{
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,
envp);
switch (status)
{
case OPENVPN_PLUGIN_FUNC_SUCCESS:
success = true;
break;
case OPENVPN_PLUGIN_FUNC_DEFERRED:
deferred = true;
break;
default:
error = true;
break;
}
}
if (pr)
pr->n = i;
gc_free (&gc);
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;
}
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
void
plugin_list_close (struct plugin_list *pl)
{
if (pl)
{
if (pl->common)
{
plugin_per_client_destroy (pl->common, &pl->per_client);
if (pl->common_owned)
plugin_common_close (pl->common);
}
free (pl);
}
}
void
plugin_abort (void)
{
struct plugin_common *pc = static_plugin_common;
static_plugin_common = NULL;
if (pc)
{
int i;
for (i = 0; i < pc->n; ++i)
plugin_abort_item (&pc->plugins[i]);
}
}
bool
plugin_defined (const struct plugin_list *pl, const int type)
{
bool ret = false;
if (pl)
{
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;
}
}
}
}
return ret;
}
/*
* Plugin return functions
*/
static void
openvpn_plugin_string_list_item_free (struct openvpn_plugin_string_list *l)
{
if (l)
{
free (l->name);
string_clear (l->value);
free (l->value);
free (l);
}
}
static void
openvpn_plugin_string_list_free (struct openvpn_plugin_string_list *l)
{
struct openvpn_plugin_string_list *next;
while (l)
{
next = l->next;
openvpn_plugin_string_list_item_free (l);
l = next;
}
}
static struct openvpn_plugin_string_list *
openvpn_plugin_string_list_find (struct openvpn_plugin_string_list *l, const char *name)
{
while (l)
{
if (!strcmp (l->name, name))
return l;
l = l->next;
}
return NULL;
}
void
plugin_return_get_column (const struct plugin_return *src,
struct plugin_return *dest,
const char *colname)
{
int i;
dest->n = 0;
for (i = 0; i < src->n; ++i)
dest->list[i] = openvpn_plugin_string_list_find (src->list[i], colname);
dest->n = i;
}
void
plugin_return_free (struct plugin_return *pr)
{
int i;
for (i = 0; i < pr->n; ++i)
openvpn_plugin_string_list_free (pr->list[i]);
pr->n = 0;
}
#ifdef ENABLE_DEBUG
void
plugin_return_print (const int msglevel, const char *prefix, const struct plugin_return *pr)
{
int i;
msg (msglevel, "PLUGIN_RETURN_PRINT %s", prefix);
for (i = 0; i < pr->n; ++i)
{
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;
}
}
}
#endif
#else
static void dummy(void) {}
#endif /* ENABLE_PLUGIN */