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.
7 *
8 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
9 *
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.
13 *
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.
18 *
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
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#elif defined(_MSC_VER)
28#include "config-msvc.h"
29#endif
30
31#include "syshead.h"
32
33#ifdef ENABLE_PLUGIN
34
35#ifdef HAVE_DLFCN_H
36#include <dlfcn.h>
37#endif
38
39#include "buffer.h"
40#include "error.h"
41#include "misc.h"
42#include "plugin.h"
43#include "win32.h"
44
45#include "memdbg.h"
46
47#define PLUGIN_SYMBOL_REQUIRED (1<<0)
48
49/* used only for program aborts */
50static struct plugin_common *static_plugin_common = NULL; /* GLOBAL */
51
52static void
53plugin_show_string_array (int msglevel, const char *name, const char *array[])
54{
55  int i;
56  for (i = 0; array[i]; ++i)
57    {
58      if (env_safe_to_print (array[i]))
59	msg (msglevel, "%s[%d] = '%s'", name, i, array[i]);
60    }
61}
62
63static void
64plugin_show_args_env (int msglevel, const char *argv[], const char *envp[])
65{
66  if (check_debug_level (msglevel))
67    {
68      plugin_show_string_array (msglevel, "ARGV", argv);
69      plugin_show_string_array (msglevel, "ENVP", envp);
70    }
71}
72
73static const char *
74plugin_type_name (const int type)
75{
76  switch (type)
77    {
78    case OPENVPN_PLUGIN_UP:
79      return "PLUGIN_UP";
80    case OPENVPN_PLUGIN_DOWN:
81      return "PLUGIN_DOWN";
82    case OPENVPN_PLUGIN_ROUTE_UP:
83      return "PLUGIN_ROUTE_UP";
84    case OPENVPN_PLUGIN_IPCHANGE:
85      return "PLUGIN_IPCHANGE";
86    case OPENVPN_PLUGIN_TLS_VERIFY:
87      return "PLUGIN_TLS_VERIFY";
88    case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
89      return "PLUGIN_AUTH_USER_PASS_VERIFY";
90    case OPENVPN_PLUGIN_CLIENT_CONNECT:
91      return "PLUGIN_CLIENT_CONNECT";
92    case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
93      return "PLUGIN_CLIENT_CONNECT";
94    case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
95      return "PLUGIN_CLIENT_DISCONNECT";
96    case OPENVPN_PLUGIN_LEARN_ADDRESS:
97      return "PLUGIN_LEARN_ADDRESS";
98    case OPENVPN_PLUGIN_TLS_FINAL:
99      return "PLUGIN_TLS_FINAL";
100    case OPENVPN_PLUGIN_ENABLE_PF:
101      return "PLUGIN_ENABLE_PF";
102    case OPENVPN_PLUGIN_ROUTE_PREDOWN:
103      return "PLUGIN_ROUTE_PREDOWN";
104    default:
105      return "PLUGIN_???";
106    }
107}
108
109static const char *
110plugin_mask_string (const unsigned int type_mask, struct gc_arena *gc)
111{
112  struct buffer out = alloc_buf_gc (256, gc);
113  bool first = true;
114  int i;
115
116  for (i = 0; i < OPENVPN_PLUGIN_N; ++i)
117    {
118      if (OPENVPN_PLUGIN_MASK (i) & type_mask)
119	{
120	  if (!first)
121	    buf_printf (&out, "|");
122	  buf_printf (&out, "%s", plugin_type_name (i));
123	  first = false;
124	}
125    }
126  return BSTR (&out);
127}
128
129static inline unsigned int
130plugin_supported_types (void)
131{
132  return ((1<<OPENVPN_PLUGIN_N)-1);
133}
134
135struct plugin_option_list *
136plugin_option_list_new (struct gc_arena *gc)
137{
138  struct plugin_option_list *ret;
139  ALLOC_OBJ_CLEAR_GC (ret, struct plugin_option_list, gc);
140  return ret;
141}
142
143bool
144plugin_option_list_add (struct plugin_option_list *list, char **p, struct gc_arena *gc)
145{
146  if (list->n < MAX_PLUGINS)
147    {
148      struct plugin_option *o = &list->plugins[list->n++];
149      o->argv = make_extended_arg_array (p, gc);
150      if (o->argv[0])
151	o->so_pathname = o->argv[0];
152      return true;
153    }
154  else
155    return false;
156}
157
158#ifndef ENABLE_SMALL
159void
160plugin_option_list_print (const struct plugin_option_list *list, int msglevel)
161{
162  int i;
163  struct gc_arena gc = gc_new ();
164
165  for (i = 0; i < list->n; ++i)
166    {
167      const struct plugin_option *o = &list->plugins[i];
168      msg (msglevel, "  plugin[%d] %s '%s'", i, o->so_pathname, print_argv (o->argv, &gc, PA_BRACKET));
169    }
170
171  gc_free (&gc);
172}
173#endif
174
175#ifndef WIN32
176
177static void
178libdl_resolve_symbol (void *handle, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
179{
180  *dest = dlsym (handle, symbol);
181  if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
182    msg (M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin shared object %s: %s", symbol, plugin_name, dlerror());
183}
184
185#else
186
187static void
188dll_resolve_symbol (HMODULE module, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
189{
190  *dest = GetProcAddress (module, symbol);
191  if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
192    msg (M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin DLL %s", symbol, plugin_name);
193}
194
195#endif
196
197static void
198plugin_init_item (struct plugin *p, const struct plugin_option *o)
199{
200  struct gc_arena gc = gc_new ();
201  bool rel = false;
202
203  p->so_pathname = o->so_pathname;
204  p->plugin_type_mask = plugin_supported_types ();
205
206#ifndef WIN32
207
208  p->handle = NULL;
209#if defined(PLUGIN_LIBDIR)
210  if (!absolute_pathname (p->so_pathname))
211    {
212      char full[PATH_MAX];
213
214      openvpn_snprintf (full, sizeof(full), "%s/%s", PLUGIN_LIBDIR, p->so_pathname);
215      p->handle = dlopen (full, RTLD_NOW);
216#if defined(ENABLE_PLUGIN_SEARCH)
217      if (!p->handle)
218	{
219	  rel = true;
220	  p->handle = dlopen (p->so_pathname, RTLD_NOW);
221	}
222#endif
223    }
224  else
225#endif
226    {
227      rel = !absolute_pathname (p->so_pathname);
228      p->handle = dlopen (p->so_pathname, RTLD_NOW);
229    }
230  if (!p->handle)
231    msg (M_ERR, "PLUGIN_INIT: could not load plugin shared object %s: %s", p->so_pathname, dlerror());
232
233# define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol (p->handle, (void*)&p->var, name, p->so_pathname, flags)
234
235#else
236
237  rel = !absolute_pathname (p->so_pathname);
238  p->module = LoadLibraryW (wide_string (p->so_pathname, &gc));
239  if (!p->module)
240    msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname);
241
242# define PLUGIN_SYM(var, name, flags) dll_resolve_symbol (p->module, (void*)&p->var, name, p->so_pathname, flags)
243
244#endif
245
246  PLUGIN_SYM (open1, "openvpn_plugin_open_v1", 0);
247  PLUGIN_SYM (open2, "openvpn_plugin_open_v2", 0);
248  PLUGIN_SYM (open3, "openvpn_plugin_open_v3", 0);
249  PLUGIN_SYM (func1, "openvpn_plugin_func_v1", 0);
250  PLUGIN_SYM (func2, "openvpn_plugin_func_v2", 0);
251  PLUGIN_SYM (func3, "openvpn_plugin_func_v3", 0);
252  PLUGIN_SYM (close, "openvpn_plugin_close_v1", PLUGIN_SYMBOL_REQUIRED);
253  PLUGIN_SYM (abort, "openvpn_plugin_abort_v1", 0);
254  PLUGIN_SYM (client_constructor, "openvpn_plugin_client_constructor_v1", 0);
255  PLUGIN_SYM (client_destructor, "openvpn_plugin_client_destructor_v1", 0);
256  PLUGIN_SYM (min_version_required, "openvpn_plugin_min_version_required_v1", 0);
257  PLUGIN_SYM (initialization_point, "openvpn_plugin_select_initialization_point_v1", 0);
258
259  if (!p->open1 && !p->open2 && !p->open3)
260    msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_open_vX is undefined in plugin: %s", p->so_pathname);
261
262  if (!p->func1 && !p->func2 && !p->func3)
263    msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_func_vX is undefined in plugin: %s", p->so_pathname);
264
265  /*
266   * Verify that we are sufficiently up-to-date to handle the plugin
267   */
268  if (p->min_version_required)
269    {
270      const int plugin_needs_version = (*p->min_version_required)();
271      if (plugin_needs_version > OPENVPN_PLUGIN_VERSION)
272	msg (M_FATAL, "PLUGIN_INIT: plugin needs interface version %d, but this version of OpenVPN only supports version %d: %s",
273	     plugin_needs_version,
274	     OPENVPN_PLUGIN_VERSION,
275	     p->so_pathname);
276    }
277
278  if (p->initialization_point)
279    p->requested_initialization_point = (*p->initialization_point)();
280  else
281    p->requested_initialization_point = OPENVPN_PLUGIN_INIT_PRE_DAEMON;
282
283  if (rel)
284    msg (M_WARN, "WARNING: plugin '%s' specified by a relative pathname -- using an absolute pathname would be more secure", p->so_pathname);
285
286  p->initialized = true;
287
288  gc_free (&gc);
289}
290
291static void
292plugin_vlog (openvpn_plugin_log_flags_t flags, const char *name, const char *format, va_list arglist)
293{
294  unsigned int msg_flags;
295
296  if (!format)
297    return;
298
299  if (!name || name[0] == '\0')
300    {
301      msg (D_PLUGIN_DEBUG, "PLUGIN: suppressed log message from plugin with unknown name");
302      return;
303    }
304
305  if (flags & PLOG_ERR)
306    msg_flags = M_INFO | M_NONFATAL;
307  else if (flags & PLOG_WARN)
308    msg_flags = M_INFO | M_WARN;
309  else if (flags & PLOG_NOTE)
310    msg_flags = M_INFO;
311  else if (flags & PLOG_DEBUG)
312    msg_flags = D_PLUGIN_DEBUG;
313
314  if (flags & PLOG_ERRNO)
315    msg_flags |= M_ERRNO;
316  if (flags & PLOG_NOMUTE)
317    msg_flags |= M_NOMUTE;
318
319  if (MSG_TEST (msg_flags))
320    {
321      struct gc_arena gc;
322      char* msg_fmt;
323
324      /* Never add instance prefix; not thread safe */
325      msg_flags |= M_NOIPREFIX;
326
327      gc_init (&gc);
328      msg_fmt = gc_malloc (ERR_BUF_SIZE, false, &gc);
329      openvpn_snprintf (msg_fmt, ERR_BUF_SIZE, "PLUGIN %s: %s", name, format);
330      x_msg_va (msg_flags, msg_fmt, arglist);
331
332      gc_free (&gc);
333    }
334}
335
336static void
337plugin_log (openvpn_plugin_log_flags_t flags, const char *name, const char *format, ...)
338{
339  va_list arglist;
340  va_start (arglist, format);
341  plugin_vlog (flags, name, format, arglist);
342  va_end (arglist);
343}
344
345static struct openvpn_plugin_callbacks callbacks = {
346  plugin_log,
347  plugin_vlog
348};
349
350static void
351plugin_open_item (struct plugin *p,
352		  const struct plugin_option *o,
353		  struct openvpn_plugin_string_list **retlist,
354		  const char **envp,
355		  const int init_point)
356{
357  ASSERT (p->initialized);
358
359  /* clear return list */
360  if (retlist)
361    *retlist = NULL;
362
363  if (!p->plugin_handle && init_point == p->requested_initialization_point)
364    {
365      struct gc_arena gc = gc_new ();
366
367      dmsg (D_PLUGIN_DEBUG, "PLUGIN_INIT: PRE");
368      plugin_show_args_env (D_PLUGIN_DEBUG, o->argv, envp);
369
370      /*
371       * Call the plugin initialization
372       */
373      if (p->open3) {
374        struct openvpn_plugin_args_open_in args = { p->plugin_type_mask,
375                                                    (const char ** const) o->argv,
376                                                    (const char ** const) envp,
377                                                    &callbacks };
378        struct openvpn_plugin_args_open_return retargs;
379
380        CLEAR(retargs);
381        retargs.return_list = retlist;
382        if ((*p->open3)(OPENVPN_PLUGINv3_STRUCTVER, &args, &retargs) == OPENVPN_PLUGIN_FUNC_SUCCESS) {
383          p->plugin_type_mask = retargs.type_mask;
384          p->plugin_handle = retargs.handle;
385        } else {
386          p->plugin_handle = NULL;
387        }
388      } else if (p->open2)
389	p->plugin_handle = (*p->open2)(&p->plugin_type_mask, o->argv, envp, retlist);
390      else if (p->open1)
391	p->plugin_handle = (*p->open1)(&p->plugin_type_mask, o->argv, envp);
392      else
393	ASSERT (0);
394
395      msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s %s",
396	   p->so_pathname,
397	   print_argv (o->argv, &gc, PA_BRACKET),
398	   plugin_mask_string (p->plugin_type_mask, &gc),
399	   (retlist && *retlist) ? "[RETLIST]" : "");
400
401      if ((p->plugin_type_mask | plugin_supported_types()) != plugin_supported_types())
402	msg (M_FATAL, "PLUGIN_INIT: plugin %s expressed interest in unsupported plugin types: [want=0x%08x, have=0x%08x]",
403	     p->so_pathname,
404	     p->plugin_type_mask,
405	     plugin_supported_types());
406
407      if (p->plugin_handle == NULL)
408	msg (M_FATAL, "PLUGIN_INIT: plugin initialization function failed: %s",
409	     p->so_pathname);
410
411      gc_free (&gc);
412    }
413}
414
415static int
416plugin_call_item (const struct plugin *p,
417		  void *per_client_context,
418		  const int type,
419		  const struct argv *av,
420		  struct openvpn_plugin_string_list **retlist,
421		  const char **envp
422#ifdef ENABLE_SSL
423		  , int certdepth,
424		  openvpn_x509_cert_t *current_cert
425#endif
426		 )
427{
428  int status = OPENVPN_PLUGIN_FUNC_SUCCESS;
429
430  /* clear return list */
431  if (retlist)
432    *retlist = NULL;
433
434  if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type)))
435    {
436      struct gc_arena gc = gc_new ();
437      struct argv a = argv_insert_head (av, p->so_pathname);
438
439      dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type));
440      plugin_show_args_env (D_PLUGIN_DEBUG, (const char **)a.argv, envp);
441
442      /*
443       * Call the plugin work function
444       */
445      if (p->func3) {
446        struct openvpn_plugin_args_func_in args = { type,
447                                                    (const char ** const) a.argv,
448                                                    (const char ** const) envp,
449                                                    p->plugin_handle,
450                                                    per_client_context,
451#ifdef ENABLE_SSL
452						    (current_cert ? certdepth : -1),
453						    current_cert
454#else
455						    -1,
456						    NULL
457#endif
458	  };
459
460        struct openvpn_plugin_args_func_return retargs;
461
462        CLEAR(retargs);
463        retargs.return_list = retlist;
464        status = (*p->func3)(OPENVPN_PLUGINv3_STRUCTVER, &args, &retargs);
465      } else if (p->func2)
466	status = (*p->func2)(p->plugin_handle, type, (const char **)a.argv, envp, per_client_context, retlist);
467      else if (p->func1)
468	status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp);
469      else
470	ASSERT (0);
471
472      msg (D_PLUGIN, "PLUGIN_CALL: POST %s/%s status=%d",
473	   p->so_pathname,
474	   plugin_type_name (type),
475	   status);
476
477      if (status == OPENVPN_PLUGIN_FUNC_ERROR)
478	msg (M_WARN, "PLUGIN_CALL: plugin function %s failed with status %d: %s",
479	     plugin_type_name (type),
480	     status,
481	     p->so_pathname);
482
483      argv_reset (&a);
484      gc_free (&gc);
485    }
486  return status;
487}
488
489static void
490plugin_close_item (struct plugin *p)
491{
492  if (p->initialized)
493    {
494      msg (D_PLUGIN, "PLUGIN_CLOSE: %s", p->so_pathname);
495
496      /*
497       * Call the plugin close function
498       */
499      if (p->plugin_handle)
500	(*p->close)(p->plugin_handle);
501
502#ifndef WIN32
503      if (dlclose (p->handle))
504	msg (M_WARN, "PLUGIN_CLOSE: dlclose() failed on plugin: %s", p->so_pathname);
505#elif defined(WIN32)
506      if (!FreeLibrary (p->module))
507	msg (M_WARN, "PLUGIN_CLOSE: FreeLibrary() failed on plugin: %s", p->so_pathname);
508#endif
509
510      p->initialized = false;
511    }
512}
513
514static void
515plugin_abort_item (const struct plugin *p)
516{
517  /*
518   * Call the plugin abort function
519   */
520  if (p->abort)
521    (*p->abort)(p->plugin_handle);
522}
523
524static void
525plugin_per_client_init (const struct plugin_common *pc,
526			struct plugin_per_client *cli,
527			const int init_point)
528{
529  const int n = pc->n;
530  int i;
531
532  for (i = 0; i < n; ++i)
533    {
534      const struct plugin *p = &pc->plugins[i];
535      if (p->plugin_handle
536	  && (init_point < 0 || init_point == p->requested_initialization_point)
537	  && p->client_constructor)
538	cli->per_client_context[i] = (*p->client_constructor)(p->plugin_handle);
539    }
540}
541
542static void
543plugin_per_client_destroy (const struct plugin_common *pc, struct plugin_per_client *cli)
544{
545  const int n = pc->n;
546  int i;
547
548  for (i = 0; i < n; ++i)
549    {
550      const struct plugin *p = &pc->plugins[i];
551      void *cc = cli->per_client_context[i];
552
553      if (p->client_destructor && cc)
554	(*p->client_destructor)(p->plugin_handle, cc);
555    }
556  CLEAR (*cli);
557}
558
559struct plugin_list *
560plugin_list_inherit (const struct plugin_list *src)
561{
562  struct plugin_list *pl;
563  ALLOC_OBJ_CLEAR (pl, struct plugin_list);
564  pl->common = src->common;
565  ASSERT (pl->common);
566  plugin_per_client_init (pl->common, &pl->per_client, -1);
567  return pl;
568}
569
570static struct plugin_common *
571plugin_common_init (const struct plugin_option_list *list)
572{
573  int i;
574  struct plugin_common *pc;
575
576  ALLOC_OBJ_CLEAR (pc, struct plugin_common);
577
578  for (i = 0; i < list->n; ++i)
579    {
580      plugin_init_item (&pc->plugins[i],
581			&list->plugins[i]);
582      pc->n = i + 1;
583    }
584
585  static_plugin_common = pc;
586  return pc;
587}
588
589static void
590plugin_common_open (struct plugin_common *pc,
591		    const struct plugin_option_list *list,
592		    struct plugin_return *pr,
593		    const struct env_set *es,
594		    const int init_point)
595{
596  struct gc_arena gc = gc_new ();
597  int i;
598  const char **envp;
599
600  envp = make_env_array (es, false, &gc);
601
602  if (pr)
603    plugin_return_init (pr);
604
605  for (i = 0; i < pc->n; ++i)
606    {
607      plugin_open_item (&pc->plugins[i],
608			&list->plugins[i],
609			pr ? &pr->list[i] : NULL,
610			envp,
611			init_point);
612    }
613
614  if (pr)
615    pr->n = i;
616
617  gc_free (&gc);
618}
619
620static void
621plugin_common_close (struct plugin_common *pc)
622{
623  static_plugin_common = NULL;
624  if (pc)
625    {
626      int i;
627
628      for (i = 0; i < pc->n; ++i)
629	plugin_close_item (&pc->plugins[i]);
630      free (pc);
631    }
632}
633
634struct plugin_list *
635plugin_list_init (const struct plugin_option_list *list)
636{
637  struct plugin_list *pl;
638  ALLOC_OBJ_CLEAR (pl, struct plugin_list);
639  pl->common = plugin_common_init (list);
640  pl->common_owned = true;
641  return pl;
642}
643
644void
645plugin_list_open (struct plugin_list *pl,
646		  const struct plugin_option_list *list,
647		  struct plugin_return *pr,
648		  const struct env_set *es,
649		  const int init_point)
650{
651  plugin_common_open (pl->common, list, pr, es, init_point);
652  plugin_per_client_init (pl->common, &pl->per_client, init_point);
653}
654
655int
656plugin_call_ssl (const struct plugin_list *pl,
657	     const int type,
658	     const struct argv *av,
659	     struct plugin_return *pr,
660	     struct env_set *es
661#ifdef ENABLE_SSL
662             , int certdepth,
663	     openvpn_x509_cert_t *current_cert
664#endif
665	    )
666{
667  if (pr)
668    plugin_return_init (pr);
669
670  if (plugin_defined (pl, type))
671    {
672      struct gc_arena gc = gc_new ();
673      int i;
674      const char **envp;
675      const int n = plugin_n (pl);
676      bool success = false;
677      bool error = false;
678      bool deferred = false;
679
680      setenv_del (es, "script_type");
681      envp = make_env_array (es, false, &gc);
682
683      for (i = 0; i < n; ++i)
684	{
685	  const int status = plugin_call_item (&pl->common->plugins[i],
686					       pl->per_client.per_client_context[i],
687					       type,
688					       av,
689					       pr ? &pr->list[i] : NULL,
690					       envp
691#ifdef ENABLE_SSL
692					       ,certdepth,
693					       current_cert
694#endif
695					      );
696	  switch (status)
697	    {
698	    case OPENVPN_PLUGIN_FUNC_SUCCESS:
699	      success = true;
700	      break;
701	    case OPENVPN_PLUGIN_FUNC_DEFERRED:
702	      deferred = true;
703	      break;
704	    default:
705	      error = true;
706	      break;
707	    }
708	}
709
710      if (pr)
711	pr->n = i;
712
713      gc_free (&gc);
714
715      if (type == OPENVPN_PLUGIN_ENABLE_PF && success)
716	return OPENVPN_PLUGIN_FUNC_SUCCESS;
717      else if (error)
718	return OPENVPN_PLUGIN_FUNC_ERROR;
719      else if (deferred)
720	return OPENVPN_PLUGIN_FUNC_DEFERRED;
721    }
722
723  return OPENVPN_PLUGIN_FUNC_SUCCESS;
724}
725
726void
727plugin_list_close (struct plugin_list *pl)
728{
729  if (pl)
730    {
731      if (pl->common)
732	{
733	  plugin_per_client_destroy (pl->common, &pl->per_client);
734
735	  if (pl->common_owned)
736	    plugin_common_close (pl->common);
737	}
738
739      free (pl);
740    }
741}
742
743void
744plugin_abort (void)
745{
746  struct plugin_common *pc = static_plugin_common;
747  static_plugin_common = NULL;
748  if (pc)
749    {
750      int i;
751
752      for (i = 0; i < pc->n; ++i)
753	plugin_abort_item (&pc->plugins[i]);
754    }
755}
756
757bool
758plugin_defined (const struct plugin_list *pl, const int type)
759{
760  bool ret = false;
761
762  if (pl)
763    {
764      const struct plugin_common *pc = pl->common;
765
766      if (pc)
767	{
768	  int i;
769	  const unsigned int mask = OPENVPN_PLUGIN_MASK (type);
770	  for (i = 0; i < pc->n; ++i)
771	    {
772	      if (pc->plugins[i].plugin_type_mask & mask)
773		{
774		  ret = true;
775		  break;
776		}
777	    }
778	}
779    }
780  return ret;
781}
782
783/*
784 * Plugin return functions
785 */
786
787static void
788openvpn_plugin_string_list_item_free (struct openvpn_plugin_string_list *l)
789{
790  if (l)
791    {
792      free (l->name);
793      string_clear (l->value);
794      free (l->value);
795      free (l);
796    }
797}
798
799static void
800openvpn_plugin_string_list_free (struct openvpn_plugin_string_list *l)
801{
802  struct openvpn_plugin_string_list *next;
803  while (l)
804    {
805      next = l->next;
806      openvpn_plugin_string_list_item_free (l);
807      l = next;
808    }
809}
810
811static struct openvpn_plugin_string_list *
812openvpn_plugin_string_list_find (struct openvpn_plugin_string_list *l, const char *name)
813{
814  while (l)
815    {
816      if (!strcmp (l->name, name))
817	return l;
818      l = l->next;
819    }
820  return NULL;
821}
822
823void
824plugin_return_get_column (const struct plugin_return *src,
825			  struct plugin_return *dest,
826			  const char *colname)
827{
828  int i;
829
830  dest->n = 0;
831  for (i = 0; i < src->n; ++i)
832    dest->list[i] = openvpn_plugin_string_list_find (src->list[i], colname);
833  dest->n = i;
834}
835
836void
837plugin_return_free (struct plugin_return *pr)
838{
839  int i;
840  for (i = 0; i < pr->n; ++i)
841    openvpn_plugin_string_list_free (pr->list[i]);
842  pr->n = 0;
843}
844
845#ifdef ENABLE_DEBUG
846void
847plugin_return_print (const int msglevel, const char *prefix, const struct plugin_return *pr)
848{
849  int i;
850  msg (msglevel, "PLUGIN_RETURN_PRINT %s", prefix);
851  for (i = 0; i < pr->n; ++i)
852    {
853      struct openvpn_plugin_string_list *l = pr->list[i];
854      int count = 0;
855
856      msg (msglevel, "PLUGIN #%d (%s)", i, prefix);
857      while (l)
858	{
859	  msg (msglevel, "[%d] '%s' -> '%s'\n",
860	       ++count,
861	       l->name,
862	       l->value);
863	  l = l->next;
864	}
865    }
866}
867#endif
868
869#else
870static void dummy(void) {}
871#endif /* ENABLE_PLUGIN */
872