aboutsummaryrefslogtreecommitdiff
path: root/plugin.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plugin.c341
1 files changed, 299 insertions, 42 deletions
diff --git a/plugin.c b/plugin.c
index f0d14ba..72cdbf7 100644
--- a/plugin.c
+++ b/plugin.c
@@ -42,7 +42,7 @@
#define PLUGIN_SYMBOL_REQUIRED (1<<0)
/* used only for program aborts */
-static struct plugin_list *static_plugin_list = NULL; /* GLOBAL */
+static struct plugin_common *static_plugin_common = NULL; /* GLOBAL */
static void
plugin_show_string_array (int msglevel, const char *name, const char *array[])
@@ -81,6 +81,8 @@ plugin_type_name (const int type)
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:
@@ -174,7 +176,10 @@ dll_resolve_symbol (HMODULE module, void **dest, const char *symbol, const char
#endif
static void
-plugin_init_item (struct plugin *p, const struct plugin_option *o, const char **envp)
+plugin_init_item (struct plugin *p,
+ const struct plugin_option *o,
+ struct openvpn_plugin_string_list **retlist,
+ const char **envp)
{
struct gc_arena gc = gc_new ();
const char **argv = make_arg_array (o->so_pathname, o->args, &gc);
@@ -182,35 +187,73 @@ plugin_init_item (struct plugin *p, const struct plugin_option *o, const char **
p->plugin_type_mask = plugin_supported_types ();
#if defined(USE_LIBDL)
+
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());
- libdl_resolve_symbol (p->handle, (void*)&p->open, "openvpn_plugin_open_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
- libdl_resolve_symbol (p->handle, (void*)&p->func, "openvpn_plugin_func_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
- libdl_resolve_symbol (p->handle, (void*)&p->close, "openvpn_plugin_close_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
- libdl_resolve_symbol (p->handle, (void*)&p->abort, "openvpn_plugin_abort_v1", p->so_pathname, 0);
+
+# define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol (p->handle, (void*)&p->var, name, p->so_pathname, flags)
+
#elif defined(USE_LOAD_LIBRARY)
+
p->module = LoadLibrary (p->so_pathname);
if (!p->module)
msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname);
- dll_resolve_symbol (p->module, (void*)&p->open, "openvpn_plugin_open_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
- dll_resolve_symbol (p->module, (void*)&p->func, "openvpn_plugin_func_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
- dll_resolve_symbol (p->module, (void*)&p->close, "openvpn_plugin_close_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
- dll_resolve_symbol (p->module, (void*)&p->abort, "openvpn_plugin_abort_v1", p->so_pathname, 0);
+
+# 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 (min_version_required, "openvpn_plugin_min_version_required_v1", 0);
+ PLUGIN_SYM (client_constructor, "openvpn_plugin_client_constructor_v1", 0);
+ PLUGIN_SYM (client_destructor, "openvpn_plugin_client_destructor_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);
+
dmsg (D_PLUGIN_DEBUG, "PLUGIN_INIT: PRE");
plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp);
/*
+ * 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 (retlist)
+ *retlist = NULL;
+
+ /*
* Call the plugin initialization
*/
- p->plugin_handle = (*p->open)(&p->plugin_type_mask, argv, envp);
+ if (p->open2)
+ p->plugin_handle = (*p->open2)(&p->plugin_type_mask, argv, envp, retlist);
+ else if (p->open1)
+ p->plugin_handle = (*p->open1)(&p->plugin_type_mask, argv, envp);
+ else
+ ASSERT (0);
- msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s",
+ msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s %s",
p->so_pathname,
o->args ? o->args : "[NULL]",
- plugin_mask_string (p->plugin_type_mask, &gc));
+ 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]",
@@ -226,7 +269,12 @@ plugin_init_item (struct plugin *p, const struct plugin_option *o, const char **
}
static int
-plugin_call_item (const struct plugin *p, const int type, const char *args, const char **envp)
+plugin_call_item (const struct plugin *p,
+ void *per_client_context,
+ const int type,
+ const char *args,
+ struct openvpn_plugin_string_list **retlist,
+ const char **envp)
{
int status = OPENVPN_PLUGIN_FUNC_SUCCESS;
@@ -238,10 +286,18 @@ plugin_call_item (const struct plugin *p, const int type, const char *args, cons
dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type));
plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp);
+ if (retlist)
+ *retlist = NULL;
+
/*
* Call the plugin work function
*/
- status = (*p->func)(p->plugin_handle, type, argv, envp);
+ if (p->func2)
+ status = (*p->func2)(p->plugin_handle, type, argv, envp, per_client_context, retlist);
+ else if (p->func1)
+ status = (*p->func1)(p->plugin_handle, type, argv, envp);
+ else
+ ASSERT (0);
msg (D_PLUGIN, "PLUGIN_CALL: POST %s/%s status=%d",
p->so_pathname,
@@ -288,69 +344,179 @@ plugin_abort_item (const struct plugin *p)
(*p->abort)(p->plugin_handle);
}
+static void
+plugin_per_client_init (const struct plugin_common *pc, struct plugin_per_client *cli)
+{
+ const int n = pc->n;
+ int i;
+
+ CLEAR (*cli);
+ for (i = 0; i < n; ++i)
+ {
+ const struct plugin *p = &pc->plugins[i];
+
+ if (p->client_constructor)
+ cli->per_client_context[i] = (*p->client_constructor)(p->plugin_handle);
+ }
+ cli->initialized = true;
+}
+
+static void
+plugin_per_client_destroy (const struct plugin_common *pc, struct plugin_per_client *cli)
+{
+ if (cli->initialized)
+ {
+ 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_open (const struct plugin_option_list *list, const struct env_set *es)
+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);
+ return pl;
+}
+
+static struct plugin_common *
+plugin_common_open (const struct plugin_option_list *list,
+ struct plugin_return *pr,
+ const struct env_set *es)
{
struct gc_arena gc = gc_new ();
int i;
- struct plugin_list *pl;
+ struct plugin_common *pc;
const char **envp;
- ALLOC_OBJ_CLEAR (pl, struct plugin_list);
- static_plugin_list = pl;
+ ALLOC_OBJ_CLEAR (pc, struct plugin_common);
envp = make_env_array (es, &gc);
+ if (pr)
+ plugin_return_init (pr);
+
for (i = 0; i < list->n; ++i)
{
- plugin_init_item (&pl->plugins[i], &list->plugins[i], envp);
- pl->n = i + 1;
+ plugin_init_item (&pc->plugins[i],
+ &list->plugins[i],
+ pr ? &pr->list[i] : NULL,
+ envp);
+ pc->n = i + 1;
}
+ if (pr)
+ pr->n = i;
+
gc_free (&gc);
+ static_plugin_common = pc;
+ return pc;
+}
+
+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_open (const struct plugin_option_list *list,
+ struct plugin_return *pr,
+ const struct env_set *es)
+{
+ struct plugin_list *pl;
+ ALLOC_OBJ_CLEAR (pl, struct plugin_list);
+ pl->common = plugin_common_open (list, pr, es);
+ pl->common_owned = true;
+ plugin_per_client_init (pl->common, &pl->per_client);
return pl;
}
int
-plugin_call (const struct plugin_list *pl, const int type, const char *args, struct env_set *es)
+plugin_call (const struct plugin_list *pl,
+ const int type,
+ const char *args,
+ struct plugin_return *pr,
+ struct env_set *es)
{
- int count = 0;
+ 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);
+ int count = 0;
mutex_lock_static (L_PLUGIN);
setenv_del (es, "script_type");
envp = make_env_array (es, &gc);
- for (i = 0; i < pl->n; ++i)
+ for (i = 0; i < n; ++i)
{
- if (!plugin_call_item (&pl->plugins[i], type, args, envp))
+ if (!plugin_call_item (&pl->common->plugins[i],
+ pl->per_client.initialized
+ ? pl->per_client.per_client_context[i] : NULL,
+ type,
+ args,
+ pr ? &pr->list[i] : NULL,
+ envp))
++count;
}
+ if (pr)
+ pr->n = i;
+
mutex_unlock_static (L_PLUGIN);
gc_free (&gc);
- }
- return count == pl->n ? 0 : 1; /* if any one plugin in the chain failed, return failure (1) */
+ return count == n ? 0 : 1; /* if any one plugin in the chain failed, return failure (1) */
+ }
+ else
+ {
+ return 0;
+ }
}
void
plugin_list_close (struct plugin_list *pl)
{
- static_plugin_list = NULL;
if (pl)
{
- int i;
- for (i = 0; i < pl->n; ++i)
- plugin_close_item (&pl->plugins[i]);
+ if (pl->common)
+ {
+ plugin_per_client_destroy (pl->common, &pl->per_client);
+
+ if (pl->common_owned)
+ plugin_common_close (pl->common);
+ }
+
free (pl);
}
}
@@ -358,14 +524,14 @@ plugin_list_close (struct plugin_list *pl)
void
plugin_abort (void)
{
- struct plugin_list *pl = static_plugin_list;
- static_plugin_list = NULL;
- if (pl)
+ struct plugin_common *pc = static_plugin_common;
+ static_plugin_common = NULL;
+ if (pc)
{
int i;
- for (i = 0; i < pl->n; ++i)
- plugin_abort_item (&pl->plugins[i]);
+ for (i = 0; i < pc->n; ++i)
+ plugin_abort_item (&pc->plugins[i]);
}
}
@@ -373,22 +539,113 @@ bool
plugin_defined (const struct plugin_list *pl, const int type)
{
bool ret = false;
+
if (pl)
{
- int i;
- const unsigned int mask = OPENVPN_PLUGIN_MASK (type);
- for (i = 0; i < pl->n; ++i)
+ const struct plugin_common *pc = pl->common;
+
+ if (pc)
{
- if (pl->plugins[i].plugin_type_mask & mask)
+ int i;
+ const unsigned int mask = OPENVPN_PLUGIN_MASK (type);
+ for (i = 0; i < pc->n; ++i)
{
- ret = true;
- break;
+ 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);
+ 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 */