svn cleanup
[anytun.git] / openvpn / plugin.c
blobf0d14baf2f0f3502890390d32578285efeedc1bf
1 /*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
8 * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program (see the file COPYING included with this
21 * distribution); if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifdef WIN32
26 #include "config-win32.h"
27 #else
28 #include "config.h"
29 #endif
31 #include "syshead.h"
33 #ifdef ENABLE_PLUGIN
35 #include "buffer.h"
36 #include "error.h"
37 #include "misc.h"
38 #include "plugin.h"
40 #include "memdbg.h"
42 #define PLUGIN_SYMBOL_REQUIRED (1<<0)
44 /* used only for program aborts */
45 static struct plugin_list *static_plugin_list = NULL; /* GLOBAL */
47 static void
48 plugin_show_string_array (int msglevel, const char *name, const char *array[])
50 int i;
51 for (i = 0; array[i]; ++i)
52 msg (msglevel, "%s[%d] = '%s'", name, i, array[i]);
55 static void
56 plugin_show_args_env (int msglevel, const char *argv[], const char *envp[])
58 if (check_debug_level (msglevel))
60 plugin_show_string_array (msglevel, "ARGV", argv);
61 plugin_show_string_array (msglevel, "ENVP", envp);
65 static const char *
66 plugin_type_name (const int type)
68 switch (type)
70 case OPENVPN_PLUGIN_UP:
71 return "PLUGIN_UP";
72 case OPENVPN_PLUGIN_DOWN:
73 return "PLUGIN_DOWN";
74 case OPENVPN_PLUGIN_ROUTE_UP:
75 return "PLUGIN_ROUTE_UP";
76 case OPENVPN_PLUGIN_IPCHANGE:
77 return "PLUGIN_IPCHANGE";
78 case OPENVPN_PLUGIN_TLS_VERIFY:
79 return "PLUGIN_TLS_VERIFY";
80 case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
81 return "PLUGIN_AUTH_USER_PASS_VERIFY";
82 case OPENVPN_PLUGIN_CLIENT_CONNECT:
83 return "PLUGIN_CLIENT_CONNECT";
84 case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
85 return "PLUGIN_CLIENT_DISCONNECT";
86 case OPENVPN_PLUGIN_LEARN_ADDRESS:
87 return "PLUGIN_LEARN_ADDRESS";
88 default:
89 return "PLUGIN_???";
93 static const char *
94 plugin_mask_string (const unsigned int type_mask, struct gc_arena *gc)
96 struct buffer out = alloc_buf_gc (256, gc);
97 bool first = true;
98 int i;
100 for (i = 0; i < OPENVPN_PLUGIN_N; ++i)
102 if (OPENVPN_PLUGIN_MASK (i) & type_mask)
104 if (!first)
105 buf_printf (&out, "|");
106 buf_printf (&out, "%s", plugin_type_name (i));
107 first = false;
110 return BSTR (&out);
113 static inline unsigned int
114 plugin_supported_types (void)
116 return ((1<<OPENVPN_PLUGIN_N)-1);
119 struct plugin_option_list *
120 plugin_option_list_new (struct gc_arena *gc)
122 struct plugin_option_list *ret;
123 ALLOC_OBJ_CLEAR_GC (ret, struct plugin_option_list, gc);
124 return ret;
127 bool
128 plugin_option_list_add (struct plugin_option_list *list, const char *so_pathname, const char *args)
130 if (list->n < MAX_PLUGINS)
132 struct plugin_option *o = &list->plugins[list->n++];
133 o->so_pathname = so_pathname;
134 o->args = args;
135 return true;
137 else
138 return false;
141 #ifdef ENABLE_DEBUG
142 void
143 plugin_option_list_print (const struct plugin_option_list *list, int msglevel)
145 int i;
146 for (i = 0; i < list->n; ++i)
148 const struct plugin_option *o = &list->plugins[i];
149 msg (msglevel, " plugin[%d] %s '%s'", i, o->so_pathname, o->args);
152 #endif
154 #if defined(USE_LIBDL)
156 static void
157 libdl_resolve_symbol (void *handle, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
159 *dest = dlsym (handle, symbol);
160 if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
161 msg (M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin shared object %s: %s", symbol, plugin_name, dlerror());
164 #elif defined(USE_LOAD_LIBRARY)
166 static void
167 dll_resolve_symbol (HMODULE module, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
169 *dest = GetProcAddress (module, symbol);
170 if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
171 msg (M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin DLL %s", symbol, plugin_name);
174 #endif
176 static void
177 plugin_init_item (struct plugin *p, const struct plugin_option *o, const char **envp)
179 struct gc_arena gc = gc_new ();
180 const char **argv = make_arg_array (o->so_pathname, o->args, &gc);
181 p->so_pathname = o->so_pathname;
182 p->plugin_type_mask = plugin_supported_types ();
184 #if defined(USE_LIBDL)
185 p->handle = dlopen (p->so_pathname, RTLD_NOW);
186 if (!p->handle)
187 msg (M_ERR, "PLUGIN_INIT: could not load plugin shared object %s: %s", p->so_pathname, dlerror());
188 libdl_resolve_symbol (p->handle, (void*)&p->open, "openvpn_plugin_open_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
189 libdl_resolve_symbol (p->handle, (void*)&p->func, "openvpn_plugin_func_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
190 libdl_resolve_symbol (p->handle, (void*)&p->close, "openvpn_plugin_close_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
191 libdl_resolve_symbol (p->handle, (void*)&p->abort, "openvpn_plugin_abort_v1", p->so_pathname, 0);
192 #elif defined(USE_LOAD_LIBRARY)
193 p->module = LoadLibrary (p->so_pathname);
194 if (!p->module)
195 msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname);
196 dll_resolve_symbol (p->module, (void*)&p->open, "openvpn_plugin_open_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
197 dll_resolve_symbol (p->module, (void*)&p->func, "openvpn_plugin_func_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
198 dll_resolve_symbol (p->module, (void*)&p->close, "openvpn_plugin_close_v1", p->so_pathname, PLUGIN_SYMBOL_REQUIRED);
199 dll_resolve_symbol (p->module, (void*)&p->abort, "openvpn_plugin_abort_v1", p->so_pathname, 0);
200 #endif
202 dmsg (D_PLUGIN_DEBUG, "PLUGIN_INIT: PRE");
203 plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp);
206 * Call the plugin initialization
208 p->plugin_handle = (*p->open)(&p->plugin_type_mask, argv, envp);
210 msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s",
211 p->so_pathname,
212 o->args ? o->args : "[NULL]",
213 plugin_mask_string (p->plugin_type_mask, &gc));
215 if ((p->plugin_type_mask | plugin_supported_types()) != plugin_supported_types())
216 msg (M_FATAL, "PLUGIN_INIT: plugin %s expressed interest in unsupported plugin types: [want=0x%08x, have=0x%08x]",
217 p->so_pathname,
218 p->plugin_type_mask,
219 plugin_supported_types());
221 if (p->plugin_handle == NULL)
222 msg (M_FATAL, "PLUGIN_INIT: plugin initialization function failed: %s",
223 p->so_pathname);
225 gc_free (&gc);
228 static int
229 plugin_call_item (const struct plugin *p, const int type, const char *args, const char **envp)
231 int status = OPENVPN_PLUGIN_FUNC_SUCCESS;
233 if (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type))
235 struct gc_arena gc = gc_new ();
236 const char **argv = make_arg_array (p->so_pathname, args, &gc);
238 dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type));
239 plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp);
242 * Call the plugin work function
244 status = (*p->func)(p->plugin_handle, type, argv, envp);
246 msg (D_PLUGIN, "PLUGIN_CALL: POST %s/%s status=%d",
247 p->so_pathname,
248 plugin_type_name (type),
249 status);
251 if (status != OPENVPN_PLUGIN_FUNC_SUCCESS)
252 msg (M_WARN, "PLUGIN_CALL: plugin function %s failed with status %d: %s",
253 plugin_type_name (type),
254 status,
255 p->so_pathname);
257 gc_free (&gc);
259 return status;
262 static void
263 plugin_close_item (const struct plugin *p)
265 msg (D_PLUGIN, "PLUGIN_CLOSE: %s", p->so_pathname);
268 * Call the plugin close function
270 (*p->close)(p->plugin_handle);
272 #if defined(USE_LIBDL)
273 if (dlclose (p->handle))
274 msg (M_WARN, "PLUGIN_CLOSE: dlclose() failed on plugin: %s", p->so_pathname);
275 #elif defined(USE_LOAD_LIBRARY)
276 if (!FreeLibrary (p->module))
277 msg (M_WARN, "PLUGIN_CLOSE: FreeLibrary() failed on plugin: %s", p->so_pathname);
278 #endif
281 static void
282 plugin_abort_item (const struct plugin *p)
285 * Call the plugin abort function
287 if (p->abort)
288 (*p->abort)(p->plugin_handle);
291 struct plugin_list *
292 plugin_list_open (const struct plugin_option_list *list, const struct env_set *es)
294 struct gc_arena gc = gc_new ();
295 int i;
296 struct plugin_list *pl;
297 const char **envp;
299 ALLOC_OBJ_CLEAR (pl, struct plugin_list);
300 static_plugin_list = pl;
302 envp = make_env_array (es, &gc);
304 for (i = 0; i < list->n; ++i)
306 plugin_init_item (&pl->plugins[i], &list->plugins[i], envp);
307 pl->n = i + 1;
310 gc_free (&gc);
311 return pl;
315 plugin_call (const struct plugin_list *pl, const int type, const char *args, struct env_set *es)
317 int count = 0;
319 if (plugin_defined (pl, type))
321 struct gc_arena gc = gc_new ();
322 int i;
323 const char **envp;
325 mutex_lock_static (L_PLUGIN);
327 setenv_del (es, "script_type");
328 envp = make_env_array (es, &gc);
330 for (i = 0; i < pl->n; ++i)
332 if (!plugin_call_item (&pl->plugins[i], type, args, envp))
333 ++count;
336 mutex_unlock_static (L_PLUGIN);
338 gc_free (&gc);
341 return count == pl->n ? 0 : 1; /* if any one plugin in the chain failed, return failure (1) */
344 void
345 plugin_list_close (struct plugin_list *pl)
347 static_plugin_list = NULL;
348 if (pl)
350 int i;
352 for (i = 0; i < pl->n; ++i)
353 plugin_close_item (&pl->plugins[i]);
354 free (pl);
358 void
359 plugin_abort (void)
361 struct plugin_list *pl = static_plugin_list;
362 static_plugin_list = NULL;
363 if (pl)
365 int i;
367 for (i = 0; i < pl->n; ++i)
368 plugin_abort_item (&pl->plugins[i]);
372 bool
373 plugin_defined (const struct plugin_list *pl, const int type)
375 bool ret = false;
376 if (pl)
378 int i;
379 const unsigned int mask = OPENVPN_PLUGIN_MASK (type);
380 for (i = 0; i < pl->n; ++i)
382 if (pl->plugins[i].plugin_type_mask & mask)
384 ret = true;
385 break;
389 return ret;
392 #else
393 static void dummy(void) {}
394 #endif /* ENABLE_PLUGIN */