]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Completely revamped the system for calling external programs and scripts:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sat, 26 Jul 2008 07:27:03 +0000 (07:27 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Sat, 26 Jul 2008 07:27:03 +0000 (07:27 +0000)
* All external programs and scripts are now called by execve() on unix and
  CreateProcess on Windows.

* The system() function is no longer used.

* Argument lists for external programs and scripts are now built by the new
  argv_printf function which natively outputs to string arrays (i.e.
  char *argv[] lists), never truncates its output, and eliminates the security
  issues inherent in formatting and parsing command lines, and dealing with
  argument quoting.

* The --script-security directive has been added to offer policy controls on
  OpenVPN's execution of external programs and scripts.

Also added a new plugin example (openvpn/plugin/examples/log.c) that logs
information to stdout for every plugin method called by OpenVPN.

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@3122 e7ae566f-a301-0410-adde-c780ea21d3b5

23 files changed:
buffer.c
buffer.h
configure.ac
debug/valgrind-suppress
init.c
lladdr.c
misc.c
misc.h
multi.c
openvpn.8
openvpn.c
options.c
plugin.c
plugin.h
plugin/examples/log.c [new file with mode: 0644]
route.c
socket.c
socket.h
ssl.c
syshead.h
tun.c
win32.c
win32.h

index c7eab0796fe36d4e10cbd860f5627bd9030e0950..b62d4b88e0bd4773b4f2a9020a49fffb9c8371f1 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -240,6 +240,14 @@ argv_init (struct argv *a)
   a->argv = NULL;
 }
 
+struct argv
+argv_new (void)
+{
+  struct argv ret;
+  argv_init (&ret);
+  return ret;
+}
+
 void
 argv_reset (struct argv *a)
 {
@@ -266,6 +274,23 @@ argv_argc (const char *format)
   return argc;
 }
 
+struct argv
+argv_insert_head (const struct argv *a, const char *head)
+{
+  struct argv r;
+  size_t i;
+
+  r.argc = (a ? a->argc : 0) + 1;
+  ALLOC_ARRAY_CLEAR (r.argv, char *, r.argc + 1);
+  r.argv[0] = string_alloc (head, NULL);
+  if (a)
+    {
+      for (i = 0; i < a->argc; ++i)
+       r.argv[i+1] = string_alloc (a->argv[i], NULL);
+    }
+  return r;
+}
+
 char *
 argv_term (const char **f)
 {
@@ -323,6 +348,22 @@ argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags)
     return "";
 }
 
+void
+argv_msg (const int msglev, const struct argv *a)
+{
+  struct gc_arena gc = gc_new ();
+  msg (msglev, "%s", argv_str (a, &gc, 0));
+  gc_free (&gc);
+}
+
+void
+argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
+{
+  struct gc_arena gc = gc_new ();
+  msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
+  gc_free (&gc);
+}
+
 void
 argv_printf (struct argv *a, const char *format, ...)
 {
@@ -373,7 +414,10 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
        {
          if (!strcmp (term, "%s"))
            {
-             a->argv[argc++] = string_alloc (va_arg (arglist, char *), NULL);
+             char *s = va_arg (arglist, char *);
+             if (!s)
+               s = "";
+             a->argv[argc++] = string_alloc (s, NULL);
            }
          else if (!strcmp (term, "%d"))
            {
@@ -387,6 +431,41 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
              openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
              a->argv[argc++] = string_alloc (numstr, NULL);
            }
+         else if (!strcmp (term, "%s/%d"))
+           {
+             char numstr[64];
+             char *s = va_arg (arglist, char *);
+
+             if (!s)
+               s = "";
+
+             openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
+
+             {
+               const size_t len = strlen(s) + strlen(numstr) + 2;
+               char *combined = (char *) malloc (len);
+               check_malloc_return (combined);
+
+               strcpy (combined, s);
+               strcat (combined, "/");
+               strcat (combined, numstr);
+               a->argv[argc++] = combined;
+             }
+           }
+         else if (!strcmp (term, "%s%s"))
+           {
+             char *s1 = va_arg (arglist, char *);
+             char *s2 = va_arg (arglist, char *);
+             char *combined;
+
+             if (!s1) s1 = "";
+             if (!s2) s2 = "";
+             combined = (char *) malloc (strlen(s1) + strlen(s2) + 1);
+             check_malloc_return (combined);
+             strcpy (combined, s1);
+             strcat (combined, s2);
+             a->argv[argc++] = combined;
+           }
          else
            ASSERT (0);
          free (term);
@@ -399,57 +478,6 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
   ASSERT (argc == a->argc);
 }
 
-#ifdef ARGV_TEST
-void
-argv_test (void)
-{
-  struct gc_arena gc = gc_new ();
-  char line[512];
-  const char *s;
-
-  struct argv a;
-  argv_init (&a);
-  argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
-  s = argv_str (&a, &gc, PA_BRACKET);
-  argv_reset (&a);
-  printf ("%s\n", s);
-
-  argv_init (&a);
-  argv_printf (&a, "foo bar %d", 99);
-  s = argv_str (&a, &gc, PA_BRACKET);
-  argv_reset (&a);
-  printf ("%s\n", s);
-
-  argv_init (&a);
-  s = argv_str (&a, &gc, PA_BRACKET);
-  argv_reset (&a);
-  printf ("%s\n", s);
-
-  argv_init (&a);
-  argv_printf (&a, "foo bar %d", 99);
-  argv_printf_cat (&a, "bar %d foo", 42);
-  argv_printf_cat (&a, "cool %s %d u", "frood", 4);
-  s = argv_str (&a, &gc, PA_BRACKET);
-  argv_reset (&a);
-  printf ("%s\n", s);
-
-  while (fgets (line, sizeof(line), stdin) != NULL)
-    {
-      char *term;
-      const char *f = line;
-      int i = 0;
-
-      while ((term = argv_term (&f)) != NULL) 
-       {
-         printf ("[%d] '%s'\n", i, term);
-         ++i;
-         free (term);
-       }
-    }
-  gc_free (&gc);
-}
-#endif
-
 /*
  * write a string to the end of a buffer that was
  * truncated by buf_printf
index 31902292b36144f19aef1f869199c681d1822a05..577041cb438ef302fb579471077cba96be032255 100644 (file)
--- a/buffer.h
+++ b/buffer.h
@@ -234,10 +234,14 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
  * to execve.
  */
 void argv_init (struct argv *a);
+struct argv argv_new (void);
 void argv_reset (struct argv *a);
 size_t argv_argc (const char *format);
 char *argv_term (const char **f);
 const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
+struct argv argv_insert_head (const struct argv *a, const char *head);
+void argv_msg (const int msglev, const struct argv *a);
+void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
 
 #define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
 void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
index 4da3e85ebb1bf1b0d241a012341d3f22224c869e..da534072c8a923444b2f3fe0606cb5e3a607a144 100644 (file)
@@ -462,7 +462,8 @@ AC_CHECK_FUNCS(daemon chroot getpwnam setuid nice system getpid dup dup2 dnl
               getpass strerror syslog openlog mlockall getgrnam setgid dnl
               setgroups stat flock readv writev setsockopt getsockopt dnl
               setsid chdir putenv getpeername unlink dnl
-              poll chsize ftruncate sendmsg recvmsg getsockname)
+              poll chsize ftruncate sendmsg recvmsg getsockname dnl
+              execve)
 AC_CACHE_SAVE
 
 if test "${WIN32}" = "yes"; then
index a94c61a788d52c5f020b273bebe572b5678e616c..c62e5692893cd8d8b251421e23c0b930114331f9 100644 (file)
    fun:main
 }
 
+{
+   <insert a suppression name here>
+   Memcheck:Cond
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/libdl-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   fun:dlsym
+   fun:libdl_resolve_symbol
+   fun:plugin_list_init
+   fun:init_plugins
+   fun:main
+}
+
 {
    <insert a suppression name here>
    Memcheck:Cond
diff --git a/init.c b/init.c
index ebdc9f38ba2d0c45989fb052221f62e024538a98..6f762c9fa85e5e95f99e2203e0249602281e506b 100644 (file)
--- a/init.c
+++ b/init.c
@@ -370,7 +370,7 @@ init_port_share (struct context *c)
 bool
 init_static (void)
 {
-  configure_path ();
+  /* configure_path (); */
 
 #if defined(USE_CRYPTO) && defined(DMALLOC)
   openssl_dmalloc_init ();
@@ -921,8 +921,11 @@ do_route (const struct options *options,
 
   if (options->route_script)
     {
+      struct argv argv = argv_new ();
       setenv_str (es, "script_type", "route-up");
-      system_check (options->route_script, es, S_SCRIPT, "Route script failed");
+      argv_printf (&argv, "%s", options->route_script);
+      openvpn_execve_check (&argv, es, S_SCRIPT, "Route script failed");
+      argv_reset (&argv);
     }
 
 #ifdef WIN32
index ad28dcf7effd3685d2d9abc64bbc1f75db2ac5af..7aefdba31fb71d88ff6a13344e968e0140fa8852 100644 (file)
--- a/lladdr.c
+++ b/lladdr.c
@@ -9,7 +9,7 @@
 int set_lladdr(const char *ifname, const char *lladdr,
                const struct env_set *es)
 {
-  char cmd[256];
+  struct argv argv = argv_new ();
   int r;
 
   if (!ifname || !lladdr)
@@ -17,37 +17,45 @@ int set_lladdr(const char *ifname, const char *lladdr,
   
 #if defined(TARGET_LINUX)
 #ifdef CONFIG_FEATURE_IPROUTE
-  openvpn_snprintf (cmd, sizeof (cmd),
+  argv_printf (&argv,
                    "%s link set addr %s dev %s",
                    iproute_path, lladdr, ifname);
 #else
-  openvpn_snprintf (cmd, sizeof (cmd),
-                   IFCONFIG_PATH " %s hw ether %s",
+  argv_printf (&argv,
+                   "%s %s hw ether %s",
+                   IFCONFIG_PATH,
                    ifname, lladdr);
 #endif
 #elif defined(TARGET_SOLARIS)
-  openvpn_snprintf (cmd, sizeof (cmd),
-                   IFCONFIG_PATH " %s ether %s",
+  argv_printf (&argv,
+                   "%s %s ether %s",
+                   IFCONFIG_PATH,
                    ifname, lladdr);
 #elif defined(TARGET_OPENBSD)
-  openvpn_snprintf (cmd, sizeof (cmd),
-                   IFCONFIG_PATH " %s lladdr %s",
+  argv_printf (&argv,
+                   "%s %s lladdr %s",
+                   IFCONFIG_PATH,
                    ifname, lladdr);
 #elif defined(TARGET_DARWIN)
-  openvpn_snprintf (cmd, sizeof (cmd),
-                   IFCONFIG_PATH " %s lladdr %s",
+  argv_printf (&argv,
+                   "%s %s lladdr %s",
+                   IFCONFIG_PATH,
                    ifname, lladdr);
 #elif defined(TARGET_FREEBSD)
-  openvpn_snprintf (cmd, sizeof (cmd),
-                   IFCONFIG_PATH " %s ether %s",
+  argv_printf (&argv,
+                   "%s %s ether %s",
+                   IFCONFIG_PATH,
                    ifname, lladdr);
 #else
       msg (M_WARN, "Sorry, but I don't know how to configure link layer addresses on this operating system.");
       return -1;
 #endif
 
-  r = system_check (cmd, es, M_WARN, "ERROR: Unable to set link layer address.");
+  argv_msg (M_INFO, &argv);
+  r = openvpn_execve_check (&argv, es, M_WARN, "ERROR: Unable to set link layer address.");
   if (r)
     msg (M_INFO, "TUN/TAP link layer address set to %s", lladdr);
+
+  argv_reset (&argv);
   return r;
 }
diff --git a/misc.c b/misc.c
index 744cfc94607ddf52f69b82b3031f6d1f3bf629cb..5b7cf3ec6e55b4e9bc56ce07b3a4a5acd18d0d0f 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -43,6 +43,9 @@
 const char *iproute_path = IPROUTE_PATH;
 #endif
 
+/* contains an SSEC_x value defined in misc.h */
+int script_security = SSEC_BUILT_IN; /* GLOBAL */
+
 /* Redefine the top level directory of the filesystem
    to restrict access to files for security */
 void
@@ -196,38 +199,36 @@ run_up_down (const char *command,
 
   if (plugin_defined (plugins, plugin_type))
     {
-      struct buffer cmd = alloc_buf_gc (256, &gc);
-
+      struct argv argv = argv_new ();
       ASSERT (arg);
-
-      buf_printf (&cmd,
-                 "\"%s\" %d %d %s %s %s",
-                 arg,
-                 tun_mtu, link_mtu,
-                 ifconfig_local, ifconfig_remote,
-                 context);
-
-      if (plugin_call (plugins, plugin_type, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+      argv_printf (&argv,
+                  "%s %d %d %s %s %s",
+                  arg,
+                  tun_mtu, link_mtu,
+                  ifconfig_local, ifconfig_remote,
+                  context);
+
+      if (plugin_call (plugins, plugin_type, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
        msg (M_FATAL, "ERROR: up/down plugin call failed");
+
+      argv_reset (&argv);
     }
 
   if (command)
     {
-      struct buffer cmd = alloc_buf_gc (256, &gc);
-
+      struct argv argv = argv_new ();
       ASSERT (arg);
-
       setenv_str (es, "script_type", script_type);
-
-      buf_printf (&cmd,
-                 "%s \"%s\" %d %d %s %s %s",
+      argv_printf (&argv,
+                 "%s %s %d %d %s %s %s",
                  command,
                  arg,
                  tun_mtu, link_mtu,
                  ifconfig_local, ifconfig_remote,
                  context);
-      msg (M_INFO, "%s", BSTR (&cmd));
-      system_check (BSTR (&cmd), es, S_SCRIPT|S_FATAL, "script failed");
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, S_SCRIPT|S_FATAL, "script failed");
+      argv_reset (&argv);
     }
 
   gc_free (&gc);
@@ -374,59 +375,6 @@ save_inetd_socket_descriptor (void)
 #endif
 }
 
-/*
- * Wrapper around the system() call.
- */
-int
-openvpn_system (const char *command, const struct env_set *es, unsigned int flags)
-{
-#ifdef HAVE_SYSTEM
-  int ret;
-
-  /*
-   * We need to bracket this code by mutex because system() doesn't
-   * accept an environment list, so we have to use the process-wide
-   * list which is shared between all threads.
-   */
-  mutex_lock_static (L_SYSTEM);
-  perf_push (PERF_SCRIPT);
-
-  /*
-   * add env_set to environment.
-   */
-  if (flags & S_SCRIPT)
-    env_set_add_to_environment (es);
-
-
-  /* debugging */
-  dmsg (D_SCRIPT, "SYSTEM[%u] '%s'", flags, command);
-  if (flags & S_SCRIPT)
-    env_set_print (D_SCRIPT, es);
-
-  /*
-   * execute the command
-   */
-  ret = system (command);
-
-  /* debugging */
-  dmsg (D_SCRIPT, "SYSTEM return=%u", ret);
-
-  /*
-   * remove env_set from environment
-   */
-  if (flags & S_SCRIPT)
-    env_set_remove_from_environment (es);
-
-  perf_pop ();
-  mutex_unlock_static (L_SYSTEM);
-  return ret;
-
-#else
-  msg (M_FATAL, "Sorry but I can't execute the shell command '%s' because this operating system doesn't appear to support the system() call", command);
-  return -1; /* NOTREACHED */
-#endif
-}
-
 /*
  * Warn if a given file is group/others accessible.
  */
@@ -489,35 +437,35 @@ system_error_message (int stat, struct gc_arena *gc)
   struct buffer out = alloc_buf_gc (256, gc);
 #ifdef WIN32
   if (stat == -1)
-    buf_printf (&out, "shell command did not execute -- ");
-  buf_printf (&out, "system() returned error code %d", stat);
+    buf_printf (&out, "external program did not execute -- ");
+  buf_printf (&out, "returned error code %d", stat);
 #else
   if (stat == -1)
-    buf_printf (&out, "shell command fork failed");
+    buf_printf (&out, "external program fork failed");
   else if (!WIFEXITED (stat))
-    buf_printf (&out, "shell command did not exit normally");
+    buf_printf (&out, "external program did not exit normally");
   else
     {
       const int cmd_ret = WEXITSTATUS (stat);
       if (!cmd_ret)
-       buf_printf (&out, "shell command exited normally");
+       buf_printf (&out, "external program exited normally");
       else if (cmd_ret == 127)
-       buf_printf (&out, "could not execute shell command");
+       buf_printf (&out, "could not execute external program");
       else
-       buf_printf (&out, "shell command exited with error status: %d", cmd_ret);
+       buf_printf (&out, "external program exited with error status: %d", cmd_ret);
     }
 #endif
   return (const char *)out.data;
 }
 
 /*
- * Run system(), exiting on error.
+ * Wrapper around openvpn_execve
  */
 bool
-system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message)
+openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
 {
   struct gc_arena gc = gc_new ();
-  const int stat = openvpn_system (command, es, flags);
+  const int stat = openvpn_execve (a, es, flags);
   int ret = false;
 
   if (system_ok (stat))
@@ -533,6 +481,69 @@ system_check (const char *command, const struct env_set *es, unsigned int flags,
   return ret;
 }
 
+bool
+openvpn_execve_allowed (const unsigned int flags)
+{
+  if (flags & S_SCRIPT)
+    return script_security >= SSEC_SCRIPTS;
+  else
+    return script_security >= SSEC_BUILT_IN;
+}
+
+#ifndef WIN32
+/*
+ * Run execve() inside a fork().  Designed to replicate the semantics of system() but
+ * in a safer way that doesn't require the invocation of a shell or the risks
+ * assocated with formatting and parsing a command line.
+ */
+int
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
+{
+  struct gc_arena gc = gc_new ();
+  int ret = -1;
+
+  if (a && a->argv[0])
+    {
+#if defined(ENABLE_EXECVE)
+      if (openvpn_execve_allowed (flags))
+       {
+         const char *cmd = a->argv[0];
+         char *const *argv = a->argv;
+         char *const *envp = (char *const *)make_env_array (es, true, &gc);
+         pid_t pid;
+
+         pid = fork ();
+         if (pid == (pid_t)0) /* child side */
+           {
+             execve (cmd, argv, envp);
+             exit (127);
+           }
+         else if (pid < (pid_t)0) /* fork failed */
+           ;
+         else /* parent side */
+           {
+             if (waitpid (pid, &ret, 0) != pid)
+               ret = -1;
+           }
+       }
+      else
+       {
+         msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
+       }
+#else
+      msg (M_WARN, "openvpn_execve: execve function not available");
+#endif
+    }
+  else
+    {
+      msg (M_WARN, "openvpn_execve: called with empty argv");
+    }
+
+  gc_free (&gc);
+  return ret;
+}
+#endif
+
 /*
  * Initialize random number seed.  random() is only used
  * when "weak" random numbers are acceptable.
@@ -1479,11 +1490,23 @@ safe_print (const char *str, struct gc_arena *gc)
   return string_mod_const (str, CC_PRINT, CC_CRLF, '.', gc);
 }
 
+static bool
+is_password_env_var (const char *str)
+{
+  return (strncmp (str, "password", 8) == 0);
+}
+
+bool
+env_allowed (const char *str)
+{
+  return (script_security >= SSEC_PW_ENV || !is_password_env_var (str));
+}
+
 bool
 env_safe_to_print (const char *str)
 {
 #ifndef UNSAFE_DEBUG
-  if (strncmp (str, "password", 8) == 0)
+  if (is_password_env_var (str))
     return false;
 #endif
   return true;
@@ -1492,7 +1515,9 @@ env_safe_to_print (const char *str)
 /* Make arrays of strings */
 
 const char **
-make_env_array (const struct env_set *es, struct gc_arena *gc)
+make_env_array (const struct env_set *es,
+               const bool check_allowed,
+               struct gc_arena *gc)
 {
   char **ret = NULL;
   struct env_item *e = NULL;
@@ -1511,12 +1536,14 @@ make_env_array (const struct env_set *es, struct gc_arena *gc)
   /* fill return array */
   if (es)
     {
-      e = es->list;
-      for (i = 0; i < n; ++i)
+      i = 0;
+      for (e = es->list; e != NULL; e = e->next)
        {
-         ASSERT (e);
-         ret[i] = e->string;
-         e = e->next;
+         if (!check_allowed || env_allowed (e->string))
+           {
+             ASSERT (i < n);
+             ret[i++] = e->string;
+           }
        }
     }
 
@@ -1631,6 +1658,7 @@ openvpn_sleep (const int n)
   sleep (n);
 }
 
+#if 0
 /*
  * Configure PATH.  On Windows, sometimes PATH is not set correctly
  * by default.
@@ -1672,3 +1700,72 @@ configure_path (void)
     }
 #endif
 }
+#endif
+
+#ifdef ARGV_TEST
+void
+argv_test (void)
+{
+  struct gc_arena gc = gc_new ();
+  char line[512];
+  const char *s;
+
+  struct argv a;
+  argv_init (&a);
+
+#ifdef WIN32
+  argv_printf (&a, "%s foo bar %s", "c:\\src\\test\\jyargs.exe", "foo bar");
+  //argv_printf (&a, "%s %s %s", "c:\\src\\test files\\batargs.bat", "foo", "bar");  
+#else
+  argv_printf (&a, "./myechox foo bar");
+#endif
+
+  argv_msg_prefix (M_INFO, &a, "ARGV");
+  openvpn_execve_check (&a, NULL, 0, "command failed");
+
+  argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
+  s = argv_str (&a, &gc, PA_BRACKET);
+  printf ("%s\n", s);
+
+  {
+    struct argv b = argv_insert_head (&a, "MARK");
+    s = argv_str (&b, &gc, PA_BRACKET);
+    argv_reset (&b);
+    printf ("%s\n", s);
+  }
+
+  argv_printf (&a, "foo bar %d", 99);
+  s = argv_str (&a, &gc, PA_BRACKET);
+  argv_reset (&a);
+  printf ("%s\n", s);
+
+  s = argv_str (&a, &gc, PA_BRACKET);
+  argv_reset (&a);
+  printf ("%s\n", s);
+
+  argv_printf (&a, "foo bar %d", 99);
+  argv_printf_cat (&a, "bar %d foo", 42);
+  argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7);
+  s = argv_str (&a, &gc, PA_BRACKET);
+  printf ("%s\n", s);
+
+#if 0
+  while (fgets (line, sizeof(line), stdin) != NULL)
+    {
+      char *term;
+      const char *f = line;
+      int i = 0;
+
+      while ((term = argv_term (&f)) != NULL) 
+       {
+         printf ("[%d] '%s'\n", i, term);
+         ++i;
+         free (term);
+       }
+    }
+#endif
+
+  argv_reset (&a);
+  gc_free (&gc);
+}
+#endif
diff --git a/misc.h b/misc.h
index 5343c028b90017b61919b56ff82e8f1435db46c2..87cdb31ec3426723b3bfb64c09bb93cf038192e5 100644 (file)
--- a/misc.h
+++ b/misc.h
@@ -117,17 +117,15 @@ void warn_if_group_others_accessible(const char* filename);
 #define S_SCRIPT (1<<0)
 #define S_FATAL  (1<<1)
 
-/* wrapper around the system() call. */
-int openvpn_system (const char *command, const struct env_set *es, unsigned int flags);
-
-/* interpret the status code returned by system() */
+/* interpret the status code returned by system()/execve() */
 bool system_ok(int);
 int system_executed (int stat);
 const char *system_error_message (int, struct gc_arena *gc);
 
-/* run system() with error check, return true if success,
-   false if error, exit if error and fatal==true */
-bool system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message);
+/* wrapper around the execve() call */
+int openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags);
+bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message);
+bool openvpn_execve_allowed (const unsigned int flags);
 
 #ifdef HAVE_STRERROR
 /* a thread-safe version of strerror */
@@ -184,7 +182,10 @@ void env_set_remove_from_environment (const struct env_set *es);
 
 /* Make arrays of strings */
 
-const char **make_env_array (const struct env_set *es, struct gc_arena *gc);
+const char **make_env_array (const struct env_set *es,
+                            const bool check_allowed,
+                            struct gc_arena *gc);
+
 const char **make_arg_array (const char *first, const char *parms, struct gc_arena *gc);
 const char **make_extended_arg_array (char **p, struct gc_arena *gc);
 
@@ -271,6 +272,9 @@ const char *safe_print (const char *str, struct gc_arena *gc);
 /* returns true if environmental variable safe to print to log */
 bool env_safe_to_print (const char *str);
 
+/* returns true if environmental variable may be passed to an external program */
+bool env_allowed (const char *str);
+
 /*
  * A sleep function that services the management layer for n
  * seconds rather than doing nothing.
@@ -290,4 +294,10 @@ void get_user_pass_auto_userid (struct user_pass *up, const char *tag);
 extern const char *iproute_path;
 #endif
 
+#define SSEC_NONE      0 /* strictly no calling of external programs */
+#define SSEC_BUILT_IN  1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/
+#define SSEC_SCRIPTS   2 /* allow calling of built-in programs and user-defined scripts */
+#define SSEC_PW_ENV    3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */
+extern int script_security; /* GLOBAL */
+
 #endif
diff --git a/multi.c b/multi.c
index c5bbcfdd38ab3fd20e253f31681e18b73290a6ce..5f70b6f3d1eaa29ec68f9e78273c1a91f1bbc489 100644 (file)
--- a/multi.c
+++ b/multi.c
@@ -85,36 +85,33 @@ learn_address_script (const struct multi_context *m,
 
   if (plugin_defined (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS))
     {
-      struct buffer cmd = alloc_buf_gc (256, &gc);
-
-      buf_printf (&cmd, "\"%s\" \"%s\"",
-                 op,
-                 mroute_addr_print (addr, &gc));
+      struct argv argv = argv_new ();
+      argv_printf (&argv, "%s %s",
+                  op,
+                  mroute_addr_print (addr, &gc));
       if (mi)
-       buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
-
-      if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+       argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
+      if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
        {
          msg (M_WARN, "WARNING: learn-address plugin call failed");
          ret = false;
        }
+      argv_reset (&argv);
     }
 
   if (m->top.options.learn_address_script)
     {
-      struct buffer cmd = alloc_buf_gc (256, &gc);
-
+      struct argv argv = argv_new ();
       setenv_str (es, "script_type", "learn-address");
-
-      buf_printf (&cmd, "%s \"%s\" \"%s\"",
-                 m->top.options.learn_address_script,
-                 op,
-                 mroute_addr_print (addr, &gc));
+      argv_printf (&argv, "%s %s %s",
+                  m->top.options.learn_address_script,
+                  op,
+                  mroute_addr_print (addr, &gc));
       if (mi)
-       buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
-
-      if (!system_check (BSTR (&cmd), es, S_SCRIPT, "WARNING: learn-address command failed"))
+       argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
+      if (!openvpn_execve_check (&argv, es, S_SCRIPT, "WARNING: learn-address command failed"))
        ret = false;
+      argv_reset (&argv);
     }
 
   gc_free (&gc);
@@ -474,16 +471,11 @@ multi_client_disconnect_script (struct multi_context *m,
 
       if (mi->context.options.client_disconnect_script)
        {
-         struct gc_arena gc = gc_new ();
-         struct buffer cmd = alloc_buf_gc (256, &gc);
-
+         struct argv argv = argv_new ();
          setenv_str (mi->context.c2.es, "script_type", "client-disconnect");
-         
-         buf_printf (&cmd, "%s", mi->context.options.client_disconnect_script);
-
-         system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
-         
-         gc_free (&gc);
+         argv_printf (&argv, "%s", mi->context.options.client_disconnect_script);
+         openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
+         argv_reset (&argv);
        }
 #ifdef MANAGEMENT_DEF_AUTH
       if (management)
@@ -1523,11 +1515,11 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
       /* deprecated callback, use a file for passing back return info */
       if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
        {
+         struct argv argv = argv_new ();
          const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc);
-
+         argv_printf (&argv, "%s", dc_file);
          delete_file (dc_file);
-
-         if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+         if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
            {
              msg (M_WARN, "WARNING: client-connect plugin call failed");
              cc_succeeded = false;
@@ -1537,6 +1529,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
              multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
              ++cc_succeeded_count;
            }
+         argv_reset (&argv);
        }
 
       /* V2 callback, use a plugin_return struct for passing back return info */
@@ -1566,7 +1559,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
        */
       if (mi->context.options.client_connect_script && cc_succeeded)
        {
-         struct buffer cmd = alloc_buf_gc (256, &gc);
+         struct argv argv = argv_new ();
          const char *dc_file = NULL;
 
          setenv_str (mi->context.c2.es, "script_type", "client-connect");
@@ -1575,17 +1568,19 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
 
          delete_file (dc_file);
 
-         buf_printf (&cmd, "%s %s",
-                     mi->context.options.client_connect_script,
-                     dc_file);
+         argv_printf (&argv, "%s %s",
+                      mi->context.options.client_connect_script,
+                      dc_file);
 
-         if (system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
+         if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
            {
              multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
              ++cc_succeeded_count;
            }
          else
            cc_succeeded = false;
+
+         argv_reset (&argv);
        }
 
       /*
index c4060147471afddecd2762bea6157e7a7303cc59..b9f0016eb1ef783d81a960965917908679595569 100644 (file)
--- a/openvpn.8
+++ b/openvpn.8
@@ -252,6 +252,7 @@ openvpn \- secure IP tunnel daemon.
 [\ \fB\-\-route\-up\fR\ \fIcmd\fR\ ]
 [\ \fB\-\-route\fR\ \fInetwork\ [netmask]\ [gateway]\ [metric]\fR\ ]
 [\ \fB\-\-rport\fR\ \fIport\fR\ ]
+[\ \fB\-\-script\-security\fR\ \fIlevel\fR\ ]
 [\ \fB\-\-secret\fR\ \fIfile\ [direction]\fR\ ]
 [\ \fB\-\-secret\fR\ \fIfile\fR\ ]
 [\ \fB\-\-server\-bridge\fR\ \fIgateway\ netmask\ pool\-start\-IP\ pool\-end\-IP\fR\ ]
@@ -300,6 +301,7 @@ openvpn \- secure IP tunnel daemon.
 [\ \fB\-\-user\fR\ \fIuser\fR\ ]
 [\ \fB\-\-username\-as\-common\-name\fR\ ]
 [\ \fB\-\-verb\fR\ \fIn\fR\ ]
+[\ \fB\-\-win\-sys\fR\ \fIpath|'env'\fR\ ]
 [\ \fB\-\-writepid\fR\ \fIfile\fR\ ]
 .in -4
 .ti +4
@@ -1998,6 +2000,24 @@ is a safety precaution to prevent a LD_PRELOAD style attack
 from a malicious or compromised server.
 .\"*********************************************************
 .TP
+.B --script-security level
+This directive offers policy-level control over OpenVPN's usage of external programs
+and scripts.  Lower values are more restrictive, higher values are more permissive.  Settings for
+.B level:
+
+.B 0 --
+Strictly no calling of external programs.
+.br
+.B 1 --
+(Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
+.br
+.B 2 --
+Allow calling of built-in executables and user-defined scripts.
+.br
+.B 3 --
+Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
+.\"*********************************************************
+.TP
 .B --disable-occ
 Don't output a warning message if option inconsistencies are detected between
 peers.  An example of an option inconsistency would be where one peer uses
@@ -4481,6 +4501,22 @@ Optional group to be owner of this tunnel.
 .SS Windows-Specific Options:
 .\"*********************************************************
 .TP
+.B --win-sys path|'env'
+Set the Windows system directory pathname to use when looking for system
+executables such as
+.B route.exe
+and
+.B netsh.exe.
+By default, if this directive is
+not specified, the pathname will be set to "C:\\WINDOWS"
+
+The special string
+.B 'env'
+indicates that the pathname should be read from the
+.B SystemRoot
+environmental variable.
+.\"*********************************************************
+.TP
 .B --ip-win32 method
 When using
 .B --ifconfig
index f089fb237e30f646a9efe150d661774db0477d25..fb8afe66f62fe1aa28e56aaa8cfaeadafcf636fc 100644 (file)
--- a/openvpn.c
+++ b/openvpn.c
@@ -27,6 +27,7 @@
 #include "init.h"
 #include "forward.h"
 #include "multi.h"
+#include "win32.h"
 
 #include "memdbg.h"
 
@@ -131,6 +132,9 @@ main (int argc, char *argv[])
 
          /* initialize environmental variable store */
          c.es = env_set_create (NULL);
+#ifdef WIN32
+         env_set_add_win32 (c.es);
+#endif
 
 #ifdef ENABLE_MANAGEMENT
          /* initialize management subsystem */
index 197a0581618762ba9780eba3c19d891a0053df4c..3a53c3142d9bc257b6f7e6b7f2b15a6769f84ab3 100644 (file)
--- a/options.c
+++ b/options.c
@@ -189,6 +189,10 @@ static const char usage_message[] =
   "                  flag to add a direct route to DHCP server, bypassing tunnel.\n"
   "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
   "--setenv name value : Set a custom environmental variable to pass to script.\n"
+  "--script-security level : 0 -- strictly no calling of external programs\n"
+  "                          1 -- (default) only call built-ins such as ifconfig\n"
+  "                          2 -- allow calling of built-ins and scripts\n"
+  "                          3 -- allow password to be passed to scripts via env\n"
   "--shaper n      : Restrict output to peer to n bytes per second.\n"
   "--keepalive n m : Helper option for setting timeouts in server mode.  Send\n"
   "                  ping once every n seconds, restart if ping not received\n"
@@ -536,6 +540,8 @@ static const char usage_message[] =
 #ifdef WIN32
   "\n"
   "Windows Specific:\n"
+  "--win-sys path|'env' : Pathname of Windows system directory, C:\\WINDOWS by default.\n"
+  "                       If specified as 'env', read the pathname from SystemRoot env var.\n"
   "--ip-win32 method : When using --ifconfig on Windows, set TAP-Win32 adapter\n"
   "                    IP address using method = manual, netsh, ipapi,\n"
   "                    dynamic, or adaptive (default = adaptive).\n"
@@ -4249,6 +4255,11 @@ add_option (struct options *options,
       VERIFY_PERMISSION (OPT_P_SETENV);
       setenv_str_safe (es, p[1], p[2] ? p[2] : "");
     }
+  else if (streq (p[0], "script-security") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      script_security = atoi (p[1]);
+    }  
   else if (streq (p[0], "mssfix"))
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
@@ -4618,6 +4629,14 @@ add_option (struct options *options,
     }
 #endif
 #ifdef WIN32
+  else if (streq (p[0], "win-sys") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      if (streq (p[1], "env"))
+       set_win_sys_path_via_env (es);
+      else
+       set_win_sys_path (p[1], es);
+    }
   else if (streq (p[0], "route-method") && p[1])
     {
       VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
index 432ed23276b838ac2e16e685346b336a86aa16dd..eac301e48352f538cb7fd3fc63f9289beb9b1bf3 100644 (file)
--- a/plugin.c
+++ b/plugin.c
@@ -327,7 +327,7 @@ static int
 plugin_call_item (const struct plugin *p,
                  void *per_client_context,
                  const int type,
-                 const char *args,
+                 const struct argv *av,
                  struct openvpn_plugin_string_list **retlist,
                  const char **envp)
 {
@@ -340,18 +340,18 @@ plugin_call_item (const struct plugin *p,
   if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type)))
     {
       struct gc_arena gc = gc_new ();
-      const char **argv = make_arg_array (p->so_pathname, args, &gc);
+      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, argv, envp);
+      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, argv, envp, per_client_context, retlist);
+       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, argv, envp);
+       status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp);
       else
        ASSERT (0);
 
@@ -366,6 +366,7 @@ plugin_call_item (const struct plugin *p,
             status,
             p->so_pathname);
 
+      argv_reset (&a);
       gc_free (&gc);
     }
   return status;
@@ -482,7 +483,7 @@ plugin_common_open (struct plugin_common *pc,
   int i;
   const char **envp;
 
-  envp = make_env_array (es, &gc);
+  envp = make_env_array (es, false, &gc);
 
   if (pr)
     plugin_return_init (pr);
@@ -540,7 +541,7 @@ plugin_list_open (struct plugin_list *pl,
 int
 plugin_call (const struct plugin_list *pl,
             const int type,
-            const char *args,
+            const struct argv *av,
             struct plugin_return *pr,
             struct env_set *es)
 {
@@ -560,14 +561,14 @@ plugin_call (const struct plugin_list *pl,
       mutex_lock_static (L_PLUGIN);
 
       setenv_del (es, "script_type");
-      envp = make_env_array (es, &gc);
+      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,
-                                              args,
+                                              av,
                                               pr ? &pr->list[i] : NULL,
                                               envp);
          switch (status)
index 8e46eeb51cc9e5ff8cda46a5e8624e2cb0398200..61c36450e5113ec8e71072fa89af2dcac7fe69c3 100644 (file)
--- a/plugin.h
+++ b/plugin.h
@@ -116,7 +116,7 @@ struct plugin_list *plugin_list_inherit (const struct plugin_list *src);
 
 int plugin_call (const struct plugin_list *pl,
                 const int type,
-                const char *args,
+                const struct argv *av,
                 struct plugin_return *pr,
                 struct env_set *es);
 
@@ -168,7 +168,7 @@ plugin_defined (const struct plugin_list *pl, const int type)
 static inline int
 plugin_call (const struct plugin_list *pl,
             const int type,
-            const char *args,
+            const struct argv *av,
             struct plugin_return *pr,
             struct env_set *es)
 {
diff --git a/plugin/examples/log.c b/plugin/examples/log.c
new file mode 100644 (file)
index 0000000..31cbb8c
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ *  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-2008 Telethra, 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
+ */
+
+/*
+ * This plugin is similar to simple.c, except it also logs extra information
+ * to stdout for every plugin method called by OpenVPN.
+ *
+ * See the README file for build instructions.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "openvpn-plugin.h"
+
+/*
+ * Our context, where we keep our state.
+ */
+struct plugin_context {
+  const char *username;
+  const char *password;
+};
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env (const char *name, const char *envp[])
+{
+  if (envp)
+    {
+      int i;
+      const int namelen = strlen (name);
+      for (i = 0; envp[i]; ++i)
+       {
+         if (!strncmp (envp[i], name, namelen))
+           {
+             const char *cp = envp[i] + namelen;
+             if (*cp == '=')
+               return cp + 1;
+           }
+       }
+    }
+  return NULL;
+}
+
+OPENVPN_EXPORT openvpn_plugin_handle_t
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
+{
+  struct plugin_context *context;
+
+  /*
+   * Allocate our context
+   */
+  context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
+
+  /*
+   * Set the username/password we will require.
+   */
+  context->username = "foo";
+  context->password = "bar";
+
+  /*
+   * Which callbacks to intercept.
+   */
+  *type_mask =
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL);
+
+  return (openvpn_plugin_handle_t) context;
+}
+
+void
+show (const int type, const char *argv[], const char *envp[])
+{
+  size_t i;
+  switch (type)
+    {
+    case OPENVPN_PLUGIN_UP:
+      printf ("OPENVPN_PLUGIN_UP\n");
+      break;
+    case OPENVPN_PLUGIN_DOWN:
+      printf ("OPENVPN_PLUGIN_DOWN\n");
+      break;
+    case OPENVPN_PLUGIN_ROUTE_UP:
+      printf ("OPENVPN_PLUGIN_ROUTE_UP\n");
+      break;
+    case OPENVPN_PLUGIN_IPCHANGE:
+      printf ("OPENVPN_PLUGIN_IPCHANGE\n");
+      break;
+    case OPENVPN_PLUGIN_TLS_VERIFY:
+      printf ("OPENVPN_PLUGIN_TLS_VERIFY\n");
+      break;
+    case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
+      printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
+      break;
+    case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
+      printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
+      break;
+    case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
+      printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
+      break;
+    case OPENVPN_PLUGIN_LEARN_ADDRESS:
+      printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
+      break;
+    case OPENVPN_PLUGIN_TLS_FINAL:
+      printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
+      break;
+    default:
+      printf ("OPENVPN_PLUGIN_?\n");
+      break;
+    }
+
+  printf ("ARGV\n");
+  for (i = 0; argv[i] != NULL; ++i)
+    printf ("%d '%s'\n", (int)i, argv[i]);
+
+  printf ("ENVP\n");
+  for (i = 0; envp[i] != NULL; ++i)
+    printf ("%d '%s'\n", (int)i, envp[i]);
+}
+
+OPENVPN_EXPORT int
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
+{
+  struct plugin_context *context = (struct plugin_context *) handle;
+
+  show (type, argv, envp);
+
+  /* check entered username/password against what we require */
+  if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
+    {
+      /* get username/password from envp string array */
+      const char *username = get_env ("username", envp);
+      const char *password = get_env ("password", envp);
+
+      if (username && !strcmp (username, context->username)
+         && password && !strcmp (password, context->password))
+       return OPENVPN_PLUGIN_FUNC_SUCCESS;
+      else
+       return OPENVPN_PLUGIN_FUNC_ERROR;
+    }
+  else
+    return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
+{
+  struct plugin_context *context = (struct plugin_context *) handle;
+  free (context);
+}
diff --git a/route.c b/route.c
index 2d9e85afe29580230bc7463cc56279d04ca54dfd..47a6013f628719e041b7b297b87981b29bc3ae8d 100644 (file)
--- a/route.c
+++ b/route.c
@@ -34,6 +34,7 @@
 #include "misc.h"
 #include "socket.h"
 #include "manage.h"
+#include "win32.h"
 
 #include "memdbg.h"
 
@@ -743,7 +744,7 @@ void
 add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
 {
   struct gc_arena gc;
-  struct buffer buf;
+  struct argv argv;
   const char *network;
   const char *netmask;
   const char *gateway;
@@ -753,7 +754,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
     return;
 
   gc_init (&gc);
-  buf = alloc_buf_gc (256, &gc);
+  argv_init (&argv);
 
   network = print_in_addr_t (r->network, 0, &gc);
   netmask = print_in_addr_t (r->netmask, 0, &gc);
@@ -771,35 +772,38 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
 
 #if defined(TARGET_LINUX)
 #ifdef CONFIG_FEATURE_IPROUTE
-  buf_printf (&buf, "%s route add %s/%d via %s",
+  argv_printf (&argv, "%s route add %s/%d via %s",
              iproute_path,
              network,
              count_netmask_bits(netmask),
              gateway);
   if (r->metric_defined)
-    buf_printf (&buf, " metric %d", r->metric);
+    argv_printf_cat (&argv, "metric %d", r->metric);
 
 #else
-  buf_printf (&buf, ROUTE_PATH " add -net %s netmask %s gw %s",
+  argv_printf (&argv, "%s add -net %s netmask %s gw %s",
+               ROUTE_PATH,
              network,
              netmask,
              gateway);
   if (r->metric_defined)
-    buf_printf (&buf, " metric %d", r->metric);
+    argv_printf_cat (&argv, "metric %d", r->metric);
 #endif  /*CONFIG_FEATURE_IPROUTE*/
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  status = system_check (BSTR (&buf), es, 0, "ERROR: Linux route add command failed");
+  argv_msg (D_ROUTE, &argv);
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
 
 #elif defined (WIN32)
 
-  buf_printf (&buf, ROUTE_PATH " ADD %s MASK %s %s",
-             network,
-             netmask,
-             gateway);
+  argv_printf (&argv, "%s%s ADD %s MASK %s %s",
+              get_win_sys_path(),
+              WIN_ROUTE_PATH_SUFFIX,
+              network,
+              netmask,
+              gateway);
   if (r->metric_defined)
-    buf_printf (&buf, " METRIC %d", r->metric);
+    argv_printf_cat (&argv, "METRIC %d", r->metric);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
+  argv_msg (D_ROUTE, &argv);
 
   if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
     {
@@ -809,7 +813,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
   else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
     {
       netcmd_semaphore_lock ();
-      status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed");
+      status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
       netcmd_semaphore_release ();
     }
   else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
@@ -820,7 +824,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
        {
          msg (D_ROUTE, "Route addition fallback to route.exe");
          netcmd_semaphore_lock ();
-         status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed [adaptive]");
+         status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
          netcmd_semaphore_release ();
        }
     }
@@ -833,88 +837,93 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
 
   /* example: route add 192.0.2.32 -netmask 255.255.255.224 somegateway */
 
-  buf_printf (&buf, ROUTE_PATH " add");
+  argv_printf (&argv, "%s add",
+               ROUTE_PATH);
 
 #if 0
   if (r->metric_defined)
-    buf_printf (&buf, " -rtt %d", r->metric);
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
 #endif
 
-  buf_printf (&buf, " %s -netmask %s %s",
+  argv_printf_cat (&argv, "%s -netmask %s %s",
              network,
              netmask,
              gateway);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  status = system_check (BSTR (&buf), es, 0, "ERROR: Solaris route add command failed");
+  argv_msg (D_ROUTE, &argv);
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
 
 #elif defined(TARGET_FREEBSD)
 
-  buf_printf (&buf, ROUTE_PATH " add");
+  argv_printf (&argv, "%s add",
+               ROUTE_PATH);
 
 #if 0
   if (r->metric_defined)
-    buf_printf (&buf, " -rtt %d", r->metric);
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
 #endif
 
-  buf_printf (&buf, " -net %s %s %s",
+  argv_printf_cat (&argv, "-net %s %s %s",
              network,
              gateway,
              netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  status = system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route add command failed");
+  argv_msg (D_ROUTE, &argv);
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
 
 #elif defined(TARGET_DRAGONFLY)
 
-  buf_printf (&buf, ROUTE_PATH " add");
+  argv_printf (&argv, "%s add",
+               ROUTE_PATH);
 
 #if 0
   if (r->metric_defined)
-    buf_printf (&buf, " -rtt %d", r->metric);
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
 #endif
 
-  buf_printf (&buf, " -net %s %s %s",
+  argv_printf_cat (&argv, "-net %s %s %s",
              network,
              gateway,
              netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  status = system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route add command failed");
+  argv_msg (D_ROUTE, &argv);
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
 
 #elif defined(TARGET_DARWIN)
 
-  buf_printf (&buf, ROUTE_PATH " add");
+  argv_printf (&argv, "%s add",
+               ROUTE_PATH);
 
 #if 0
   if (r->metric_defined)
-    buf_printf (&buf, " -rtt %d", r->metric);
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
 #endif
 
-  buf_printf (&buf, " -net %s %s %s",
+  argv_printf_cat (&argv, "-net %s %s %s",
               network,
               gateway,
               netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  status = system_check (BSTR (&buf), es, 0, "ERROR: OS X route add command failed");
+  argv_msg (D_ROUTE, &argv);
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
 
 #elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
 
-  buf_printf (&buf, ROUTE_PATH " add");
+  argv_printf (&argv, "%s add",
+               ROUTE_PATH);
 
 #if 0
   if (r->metric_defined)
-    buf_printf (&buf, " -rtt %d", r->metric);
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
 #endif
 
-  buf_printf (&buf, " -net %s %s -netmask %s",
+  argv_printf_cat (&argv, "-net %s %s -netmask %s",
              network,
              gateway,
              netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  status = system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
+  argv_msg (D_ROUTE, &argv);
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
 
 #else
   msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system.  Try putting your routes in a --route-up script");
@@ -922,6 +931,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
 
  done:
   r->defined = status;
+  argv_reset (&argv);
   gc_free (&gc);
 }
 
@@ -929,7 +939,7 @@ static void
 delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
 {
   struct gc_arena gc;
-  struct buffer buf;
+  struct argv argv;
   const char *network;
   const char *netmask;
   const char *gateway;
@@ -938,37 +948,40 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
     return;
 
   gc_init (&gc);
+  argv_init (&argv);
 
-  buf = alloc_buf_gc (256, &gc);
   network = print_in_addr_t (r->network, 0, &gc);
   netmask = print_in_addr_t (r->netmask, 0, &gc);
   gateway = print_in_addr_t (r->gateway, 0, &gc);
 
 #if defined(TARGET_LINUX)
 #ifdef CONFIG_FEATURE_IPROUTE
-  buf_printf (&buf, "%s route del %s/%d",
+  argv_printf (&argv, "%s route del %s/%d",
              iproute_path,
              network,
              count_netmask_bits(netmask));
 #else
 
-  buf_printf (&buf, ROUTE_PATH " del -net %s netmask %s",
+  argv_printf (&argv, "%s del -net %s netmask %s",
+               ROUTE_PATH,
              network,
              netmask);
 #endif /*CONFIG_FEATURE_IPROUTE*/
   if (r->metric_defined)
-    buf_printf (&buf, " metric %d", r->metric);
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  system_check (BSTR (&buf), es, 0, "ERROR: Linux route delete command failed");
+    argv_printf_cat (&argv, "metric %d", r->metric);
+  argv_msg (D_ROUTE, &argv);
+  openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
 
 #elif defined (WIN32)
   
-  buf_printf (&buf, ROUTE_PATH " DELETE %s MASK %s %s",
-             network,
-              netmask,
-              gateway);
+  argv_printf (&argv, "%s%s DELETE %s MASK %s %s",
+              get_win_sys_path(),
+              WIN_ROUTE_PATH_SUFFIX,
+              network,
+              netmask,
+              gateway);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
+  argv_msg (D_ROUTE, &argv);
 
   if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
     {
@@ -978,7 +991,7 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
   else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
     {
       netcmd_semaphore_lock ();
-      system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed");
+      openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed");
       netcmd_semaphore_release ();
     }
   else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
@@ -989,7 +1002,7 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
        {
          msg (D_ROUTE, "Route deletion fallback to route.exe");
          netcmd_semaphore_lock ();
-         system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed [adaptive]");
+         openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed [adaptive]");
          netcmd_semaphore_release ();
        }
     }
@@ -1000,58 +1013,64 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
 
 #elif defined (TARGET_SOLARIS)
 
-  buf_printf (&buf, ROUTE_PATH " delete %s -netmask %s %s",
+  argv_printf (&argv, "%s delete %s -netmask %s %s",
+               ROUTE_PATH,
              network,
              netmask,
              gateway);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  system_check (BSTR (&buf), es, 0, "ERROR: Solaris route delete command failed");
+  argv_msg (D_ROUTE, &argv);
+  openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete command failed");
 
 #elif defined(TARGET_FREEBSD)
 
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
+  argv_printf (&argv, "%s delete -net %s %s %s",
+               ROUTE_PATH,
              network,
              gateway,
              netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route delete command failed");
+  argv_msg (D_ROUTE, &argv);
+  openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route delete command failed");
 
 #elif defined(TARGET_DRAGONFLY)
 
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
+  argv_printf (&argv, "%s delete -net %s %s %s",
+               ROUTE_PATH,
              network,
              gateway,
              netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route delete command failed");
+  argv_msg (D_ROUTE, &argv);
+  openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route delete command failed");
 
 #elif defined(TARGET_DARWIN)
 
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
+  argv_printf (&argv, "%s delete -net %s %s %s",
+               ROUTE_PATH,
               network,
               gateway,
               netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  system_check (BSTR (&buf), es, 0, "ERROR: OS X route delete command failed");
+  argv_msg (D_ROUTE, &argv);
+  openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");
 
 #elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
 
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s -netmask %s",
+  argv_printf (&argv, "%s delete -net %s %s -netmask %s",
+               ROUTE_PATH,
              network,
              gateway,
              netmask);
 
-  msg (D_ROUTE, "%s", BSTR (&buf));
-  system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
+  argv_msg (D_ROUTE, &argv);
+  openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
 
 #else
   msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system.  Try putting your routes in a --route-up script");
 #endif
 
+  argv_reset (&argv);
   gc_free (&gc);
 }
 
index 135fb0ea4d1b36059d8f45f59fbd44ef3566bebd..c1b16ad1bfa199b68b0d189387ba433aaeeb7352 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -1480,6 +1480,22 @@ setenv_trusted (struct env_set *es, const struct link_socket_info *info)
   setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT);
 }
 
+static void
+ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc)
+{
+  const char *ip = print_sockaddr_ex (&info->lsa->actual.dest, NULL, 0, gc);
+  const char *port = print_sockaddr_ex (&info->lsa->actual.dest, NULL, PS_DONT_SHOW_ADDR|PS_SHOW_PORT, gc);
+  if (include_cmd)
+    argv_printf (argv, "%s %s %s",
+                info->ipchange_command,
+                ip,
+                port);
+  else
+    argv_printf (argv, "%s %s",
+                ip,
+                port);
+}
+
 void
 link_socket_connection_initiated (const struct buffer *buf,
                                  struct link_socket_info *info,
@@ -1508,20 +1524,21 @@ link_socket_connection_initiated (const struct buffer *buf,
   /* Process --ipchange plugin */
   if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE))
     {
-      const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc);
-      if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+      struct argv argv = argv_new ();
+      ipchange_fmt (false, &argv, info, &gc);
+      if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
        msg (M_WARN, "WARNING: ipchange plugin call failed");
+      argv_reset (&argv);
     }
 
   /* Process --ipchange option */
   if (info->ipchange_command)
     {
-      struct buffer out = alloc_buf_gc (256, &gc);
+      struct argv argv = argv_new ();
       setenv_str (es, "script_type", "ipchange");
-      buf_printf (&out, "%s %s",
-                 info->ipchange_command,
-                 print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc));
-      system_check (BSTR (&out), es, S_SCRIPT, "ip-change command failed");
+      ipchange_fmt (true, &argv, info, &gc);
+      openvpn_execve_check (&argv, es, S_SCRIPT, "ip-change command failed");
+      argv_reset (&argv);
     }
 
   gc_free (&gc);
@@ -1791,7 +1808,8 @@ print_sockaddr_ex (const struct openvpn_sockaddr *addr,
       const int port = ntohs (addr->sa.sin_port);
 
       mutex_lock_static (L_INET_NTOA);
-      buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
+      if (!(flags & PS_DONT_SHOW_ADDR))
+       buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
       mutex_unlock_static (L_INET_NTOA);
 
       if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
index 3d25f5e7b2fcc5c4064693bb6769cb5ce1316927..9084fa06e703dbb9a6cef75fbf64f334c0f827be 100644 (file)
--- a/socket.h
+++ b/socket.h
@@ -325,6 +325,7 @@ void link_socket_close (struct link_socket *sock);
 #define PS_SHOW_PORT_IF_DEFINED (1<<0)
 #define PS_SHOW_PORT            (1<<1)
 #define PS_SHOW_PKTINFO         (1<<2)
+#define PS_DONT_SHOW_ADDR       (1<<3)
 
 const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr,
                               const char* separator,
diff --git a/ssl.c b/ssl.c
index 55a3830d6d0f4f42f3608e876bd31f16abc57b92..9318b8a1986dcd6f1de2c4ef2ff153141b67b38e 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -544,6 +544,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
   struct tls_session *session;
   const struct tls_options *opt;
   const int max_depth = 8;
+  struct argv argv = argv_new ();
 
   /* get the tls_session pointer */
   ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
@@ -689,16 +690,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
   /* call --tls-verify plug-in(s) */
   if (plugin_defined (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY))
     {
-      char command[256];
-      struct buffer out;
       int ret;
 
-      buf_set_write (&out, (uint8_t*)command, sizeof (command));
-      buf_printf (&out, "%d %s",
-                 ctx->error_depth,
-                 subject);
+      argv_printf (&argv, "%d %s",
+                  ctx->error_depth,
+                  subject);
 
-      ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, NULL, opt->es);
+      ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es);
 
       if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
        {
@@ -716,19 +714,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
   /* run --tls-verify script */
   if (opt->verify_command)
     {
-      char command[256];
-      struct buffer out;
       int ret;
 
       setenv_str (opt->es, "script_type", "tls-verify");
 
-      buf_set_write (&out, (uint8_t*)command, sizeof (command));
-      buf_printf (&out, "%s %d %s",
-                 opt->verify_command,
-                 ctx->error_depth,
-                 subject);
-      dmsg (D_TLS_DEBUG, "TLS: executing verify command: %s", command);
-      ret = openvpn_system (command, opt->es, S_SCRIPT);
+      argv_printf (&argv, "%s %d %s",
+                  opt->verify_command,
+                  ctx->error_depth,
+                  subject);
+      argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
+      ret = openvpn_execve (&argv, opt->es, S_SCRIPT);
 
       if (system_ok (ret))
        {
@@ -738,7 +733,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
       else
        {
          if (!system_executed (ret))
-           msg (M_ERR, "Verify command failed to execute: %s", command);
+           argv_msg_prefix (M_ERR, &argv, "Verify command failed to execute");
          msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
               ctx->error_depth, subject);
          goto err;             /* Reject connection */
@@ -801,11 +796,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
 
   session->verified = true;
   free (subject);
+  argv_reset (&argv);
   return 1;                    /* Accept connection */
 
  err:
   ERR_clear_error ();
   free (subject);
+  argv_reset (&argv);
   return 0;                     /* Reject connection */
 }
 
@@ -2901,7 +2898,7 @@ static bool
 verify_user_pass_script (struct tls_session *session, const struct user_pass *up)
 {
   struct gc_arena gc = gc_new ();
-  struct buffer cmd = alloc_buf_gc (256, &gc);
+  struct argv argv = argv_new ();
   const char *tmp_file = "";
   int retval;
   bool ret = false;
@@ -2940,16 +2937,16 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
       setenv_untrusted (session);
 
       /* format command line */
-      buf_printf (&cmd, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
+      argv_printf (&argv, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
       
       /* call command */
-      retval = openvpn_system (BSTR (&cmd), session->opt->es, S_SCRIPT);
+      retval = openvpn_execve (&argv, session->opt->es, S_SCRIPT);
 
       /* test return status of command */
       if (system_ok (retval))
        ret = true;
       else if (!system_executed (retval))
-       msg (D_TLS_ERRORS, "TLS Auth Error: user-pass-verify script failed to execute: %s", BSTR (&cmd));
+       argv_msg_prefix (D_TLS_ERRORS, &argv, "TLS Auth Error: user-pass-verify script failed to execute");
          
       if (!session->opt->auth_user_pass_verify_script_via_file)
        setenv_del (session->opt->es, "password");
@@ -2963,6 +2960,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
   if (strlen (tmp_file) > 0)
     delete_file (tmp_file);
 
+  argv_reset (&argv);
   gc_free (&gc);
   return ret;
 }
index da07a7152c91fd17371b1703fa82a6a964e8c022..e87814eecb7d598bcdd3f18a80808d1c94171148 100644 (file)
--- a/syshead.h
+++ b/syshead.h
@@ -447,6 +447,14 @@ socket_defined (const socket_descriptor_t sd)
  */
 #define USE_64_BIT_COUNTERS
 
+/*
+ * Should we enable the use of execve() for calling subprocesses,
+ * instead of system()?
+ */
+#if defined(HAVE_EXECVE) && defined(HAVE_FORK)
+#define ENABLE_EXECVE
+#endif
+
 /*
  * Do we have point-to-multipoint capability?
  */
diff --git a/tun.c b/tun.c
index c1494d9548158d87e6d79626471d1725deee1191..c6916ef00dd6a98332541b40849b7fe2f3a3b626 100644 (file)
--- a/tun.c
+++ b/tun.c
@@ -39,6 +39,7 @@
 #include "socket.h"
 #include "manage.h"
 #include "route.h"
+#include "win32.h"
 
 #include "memdbg.h"
 
@@ -534,7 +535,9 @@ do_ifconfig (struct tuntap *tt,
       const char *ifconfig_local = NULL;
       const char *ifconfig_remote_netmask = NULL;
       const char *ifconfig_broadcast = NULL;
-      char command_line[256];
+      struct argv argv;
+
+      argv_init (&argv);
 
       /*
        * We only handle TUN/TAP devices here, not --dev null devices.
@@ -570,31 +573,31 @@ do_ifconfig (struct tuntap *tt,
        /*
         * Set the MTU for the device
         */
-       openvpn_snprintf (command_line, sizeof (command_line),
+       argv_printf (&argv,
                          "%s link set dev %s up mtu %d",
                          iproute_path,
                          actual,
                          tun_mtu
                          );
-         msg (M_INFO, "%s", command_line);
-         system_check (command_line, es, S_FATAL, "Linux ip link set failed");
+         argv_msg (M_INFO, &argv);
+         openvpn_execve_check (&argv, es, S_FATAL, "Linux ip link set failed");
 
        if (tun) {
 
                /*
                 * Set the address for the device
                 */
-               openvpn_snprintf (command_line, sizeof (command_line),
+               argv_printf (&argv,
                                  "%s addr add dev %s local %s peer %s",
                                  iproute_path,
                                  actual,
                                  ifconfig_local,
                                  ifconfig_remote_netmask
                                  );
-                 msg (M_INFO, "%s", command_line);
-                 system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
+                 argv_msg (M_INFO, &argv);
+                 openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
        } else {
-               openvpn_snprintf (command_line, sizeof (command_line),
+               argv_printf (&argv,
                                  "%s addr add dev %s %s/%d broadcast %s",
                                  iproute_path,
                                  actual,
@@ -602,30 +605,32 @@ do_ifconfig (struct tuntap *tt,
                                  count_netmask_bits(ifconfig_remote_netmask),
                                  ifconfig_broadcast
                                  );
-                 msg (M_INFO, "%s", command_line);
-                 system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
+                 argv_msg (M_INFO, &argv);
+                 openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
        }
        tt->did_ifconfig = true;
 #else
       if (tun)
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s pointopoint %s mtu %d",
+       argv_printf (&argv,
+                         "%s %s %s pointopoint %s mtu %d",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
                          tun_mtu
                          );
       else
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
+       argv_printf (&argv,
+                         "%s %s %s netmask %s mtu %d broadcast %s",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
                          tun_mtu,
                          ifconfig_broadcast
                          );
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, S_FATAL, "Linux ifconfig failed");
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
       tt->did_ifconfig = true;
 
 #endif /*CONFIG_FEATURE_IPROUTE*/
@@ -638,28 +643,30 @@ do_ifconfig (struct tuntap *tt,
        */
       if (tun)
        {
-         openvpn_snprintf (command_line, sizeof (command_line),
-                           IFCONFIG_PATH " %s %s %s mtu %d up",
+         argv_printf (&argv,
+                           "%s %s %s %s mtu %d up",
+                           IFCONFIG_PATH,
                            actual,
                            ifconfig_local,
                            ifconfig_remote_netmask,
                            tun_mtu
                            );
 
-         msg (M_INFO, "%s", command_line);
-         if (!system_check (command_line, es, 0, "Solaris ifconfig phase-1 failed"))
+         argv_msg (M_INFO, &argv);
+         if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
            solaris_error_close (tt, es, actual);
 
-         openvpn_snprintf (command_line, sizeof (command_line),
-                           IFCONFIG_PATH " %s netmask 255.255.255.255",
+         argv_printf (&argv,
+                           "%s %s netmask 255.255.255.255",
+                           IFCONFIG_PATH,
                            actual
                            );
        }
       else
        no_tap_ifconfig ();
 
-      msg (M_INFO, "%s", command_line);
-      if (!system_check (command_line, es, 0, "Solaris ifconfig phase-2 failed"))
+      argv_msg (M_INFO, &argv);
+      if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
        solaris_error_close (tt, es, actual);
 
       tt->did_ifconfig = true;
@@ -672,45 +679,50 @@ do_ifconfig (struct tuntap *tt,
        * (if it exists), and re-ifconfig.  Let me know if you know a better way.
        */
 
-      openvpn_snprintf (command_line, sizeof (command_line),
-                       IFCONFIG_PATH " %s destroy",
+      argv_printf (&argv,
+                       "%s %s destroy",
+                       IFCONFIG_PATH,
                        actual);
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, 0, NULL);
-      openvpn_snprintf (command_line, sizeof (command_line),
-                       IFCONFIG_PATH " %s create",
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, 0, NULL);
+      argv_printf (&argv,
+                       "%s %s create",
+                       IFCONFIG_PATH,
                        actual);
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, 0, NULL);
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, 0, NULL);
       msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
 
       /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
       if (tun)
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+       argv_printf (&argv,
+                         "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
                          tun_mtu
                          );
       else
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s link0",
+       argv_printf (&argv,
+                         "%s %s %s netmask %s mtu %d broadcast %s link0",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
                          tun_mtu,
                          ifconfig_broadcast
                          );
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, S_FATAL, "OpenBSD ifconfig failed");
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
       tt->did_ifconfig = true;
 
 #elif defined(TARGET_NETBSD)
 
       if (tun)
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+       argv_printf (&argv,
+                         "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
@@ -722,16 +734,17 @@ do_ifconfig (struct tuntap *tt,
        * so we don't need the "link0" extra parameter to specify we want to do 
        * tunneling at the ethernet level
        */
-               openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
+               argv_printf (&argv,
+                         "%s %s %s netmask %s mtu %d broadcast %s",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
                          tun_mtu,
                          ifconfig_broadcast
                          );
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, S_FATAL, "NetBSD ifconfig failed");
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
       tt->did_ifconfig = true;
 
 #elif defined(TARGET_DARWIN)
@@ -740,18 +753,20 @@ do_ifconfig (struct tuntap *tt,
        * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
        */
 
-      openvpn_snprintf (command_line, sizeof (command_line),
-                       IFCONFIG_PATH " %s delete",
+      argv_printf (&argv,
+                       "%s %s delete",
+                       IFCONFIG_PATH,
                        actual);
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, 0, NULL);
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, 0, NULL);
       msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
 
 
       /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
       if (tun)
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+       argv_printf (&argv,
+                         "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
@@ -760,8 +775,9 @@ do_ifconfig (struct tuntap *tt,
       else
         {
           if (tt->topology == TOP_SUBNET)
-           openvpn_snprintf (command_line, sizeof (command_line),
-                             IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
+           argv_printf (&argv,
+                             "%s %s %s %s netmask %s mtu %d up",
+                             IFCONFIG_PATH,
                              actual,
                              ifconfig_local,
                              ifconfig_local,
@@ -769,16 +785,17 @@ do_ifconfig (struct tuntap *tt,
                              tun_mtu
                              );
          else
-           openvpn_snprintf (command_line, sizeof (command_line),
-                             IFCONFIG_PATH " %s %s netmask %s mtu %d up",
+           argv_printf (&argv,
+                             "%s %s %s netmask %s mtu %d up",
+                             IFCONFIG_PATH,
                              actual,
                              ifconfig_local,
                              ifconfig_remote_netmask,
                              tun_mtu
                              );
        }
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, S_FATAL, "Mac OS X ifconfig failed");
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, S_FATAL, "Mac OS X ifconfig failed");
       tt->did_ifconfig = true;
 
       /* Add a network route for the local tun interface */
@@ -797,8 +814,9 @@ do_ifconfig (struct tuntap *tt,
 
       /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
       if (tun)
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+       argv_printf (&argv,
+                         "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
@@ -806,8 +824,9 @@ do_ifconfig (struct tuntap *tt,
                          );
       else {
        if (tt->topology == TOP_SUBNET)
-            openvpn_snprintf (command_line, sizeof (command_line),
-                              IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
+            argv_printf (&argv,
+                              "%s %s %s %s netmask %s mtu %d up",
+                              IFCONFIG_PATH,
                               actual,
                               ifconfig_local,
                               ifconfig_local,
@@ -815,8 +834,9 @@ do_ifconfig (struct tuntap *tt,
                               tun_mtu
                               );
        else
-           openvpn_snprintf (command_line, sizeof (command_line),
-                         IFCONFIG_PATH " %s %s netmask %s mtu %d up",
+           argv_printf (&argv,
+                         "%s %s %s netmask %s mtu %d up",
+                         IFCONFIG_PATH,
                          actual,
                          ifconfig_local,
                          ifconfig_remote_netmask,
@@ -824,8 +844,8 @@ do_ifconfig (struct tuntap *tt,
                          );
       }
        
-      msg (M_INFO, "%s", command_line);
-      system_check (command_line, es, S_FATAL, "FreeBSD ifconfig failed");
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig failed");
       tt->did_ifconfig = true;
 
        /* Add a network route for the local tun interface */
@@ -882,6 +902,7 @@ do_ifconfig (struct tuntap *tt,
 #else
       msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system.  You should ifconfig your TUN/TAP device manually or use an --up script.");
 #endif
+      argv_reset (&argv);
     }
   gc_free (&gc);
 }
@@ -1216,13 +1237,14 @@ close_tun (struct tuntap *tt)
     {
        if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
          {
-           char command_line[256];
+           struct argv argv;
            struct gc_arena gc = gc_new ();
+           argv_init (&argv);
 
 #ifdef CONFIG_FEATURE_IPROUTE
            if (is_tun_p2p (tt))
              {
-               openvpn_snprintf (command_line, sizeof (command_line),
+               argv_printf (&argv,
                        "%s addr del dev %s local %s peer %s",
                        iproute_path,
                        tt->actual_name,
@@ -1232,7 +1254,7 @@ close_tun (struct tuntap *tt)
              }
            else
              {
-               openvpn_snprintf (command_line, sizeof (command_line),
+               argv_printf (&argv,
                        "%s addr del dev %s %s/%d",
                        iproute_path,
                        tt->actual_name,
@@ -1241,15 +1263,17 @@ close_tun (struct tuntap *tt)
                        );
              }
 #else
-           openvpn_snprintf (command_line, sizeof (command_line),
-                       IFCONFIG_PATH " %s 0.0.0.0",
+           argv_printf (&argv,
+                       "%s %s 0.0.0.0",
+                       IFCONFIG_PATH,
                        tt->actual_name
                        );
 #endif
 
-           msg (M_INFO, "%s", command_line);
-           system_check (command_line, NULL, 0, "Linux ip addr del failed");
+           argv_msg (M_INFO, &argv);
+           openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed");
 
+           argv_reset (&argv);
            gc_free (&gc);
          }
       close_tun_generic (tt);
@@ -1471,16 +1495,19 @@ close_tun (struct tuntap *tt)
 static void
 solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
 {
-  char command_line[256];
+  struct argv argv;
+  argv_init (&argv);
 
-  openvpn_snprintf (command_line, sizeof (command_line),
-                   IFCONFIG_PATH " %s unplumb",
+  argv_printf (&argv,
+                   "%s %s unplumb",
+                   IFCONFIG_PATH,
                    actual);
 
-  msg (M_INFO, "%s", command_line);
-  system_check (command_line, es, 0, "Solaris ifconfig unplumb failed");
+  argv_msg (M_INFO, &argv);
+  openvpn_execve_check (&argv, es, 0, "Solaris ifconfig unplumb failed");
   close_tun (tt);
   msg (M_FATAL, "Solaris ifconfig failed");
+  argv_reset (&argv);
 }
 
 int
@@ -3275,7 +3302,7 @@ dhcp_renew (const struct tuntap *tt)
  */
 
 static void
-netsh_command (const char *cmd, int n)
+netsh_command (const struct argv *a, int n)
 {
   int i;
   for (i = 0; i < n; ++i)
@@ -3283,8 +3310,8 @@ netsh_command (const char *cmd, int n)
       bool status;
       openvpn_sleep (1);
       netcmd_semaphore_lock ();
-      msg (M_INFO, "NETSH: %s", cmd);
-      status = system_check (cmd, NULL, 0, "ERROR: netsh command failed");
+      argv_msg_prefix (M_INFO, a, "NETSH");
+      status = openvpn_execve_check (a, NULL, 0, "ERROR: netsh command failed");
       netcmd_semaphore_release ();
       if (status)
        return;
@@ -3376,7 +3403,7 @@ netsh_ifconfig_options (const char *type,
                        const bool test_first)
 {
   struct gc_arena gc = gc_new ();
-  struct buffer out = alloc_buf_gc (256, &gc);
+  struct argv argv = argv_new ();
   bool delete_first = false;
 
   /* first check if we should delete existing DNS/WINS settings from TAP interface */
@@ -3391,11 +3418,12 @@ netsh_ifconfig_options (const char *type,
   /* delete existing DNS/WINS settings from TAP interface */
   if (delete_first)
     {
-      buf_init (&out, 0);
-      buf_printf (&out, "netsh interface ip delete %s \"%s\" all",
-                 type,
-                 flex_name);
-      netsh_command (BSTR(&out), 2);
+      argv_printf (&argv, "%s%s interface ip delete %s %s all",
+                  get_win_sys_path(),
+                  NETSH_PATH_SUFFIX,
+                  type,
+                  flex_name);
+      netsh_command (&argv, 2);
     }
 
   /* add new DNS/WINS settings to TAP interface */
@@ -3407,15 +3435,16 @@ netsh_ifconfig_options (const char *type,
        if (delete_first || !test_first || !ip_addr_member_of (addr_list[i], current))
          {
            const char *fmt = count ?
-               "netsh interface ip add %s \"%s\" %s"
-             : "netsh interface ip set %s \"%s\" static %s";
-
-           buf_init (&out, 0);
-           buf_printf (&out, fmt,
-                       type,
-                       flex_name,
-                       print_in_addr_t (addr_list[i], 0, &gc));
-           netsh_command (BSTR(&out), 2);
+               "%s%s interface ip add %s %s %s"
+             : "%s%s interface ip set %s %s static %s";
+
+           argv_printf (&argv, fmt,
+                        get_win_sys_path(),
+                        NETSH_PATH_SUFFIX,
+                        type,
+                        flex_name,
+                        print_in_addr_t (addr_list[i], 0, &gc));
+           netsh_command (&argv, 2);
          
            ++count;
          }
@@ -3429,6 +3458,7 @@ netsh_ifconfig_options (const char *type,
       }
   }
 
+  argv_reset (&argv);
   gc_free (&gc);
 }
 
@@ -3458,7 +3488,7 @@ netsh_ifconfig (const struct tuntap_options *to,
                const unsigned int flags)
 {
   struct gc_arena gc = gc_new ();
-  struct buffer out = alloc_buf_gc (256, &gc);
+  struct argv argv = argv_new ();
   const IP_ADAPTER_INFO *ai = NULL;
   const IP_PER_ADAPTER_INFO *pai = NULL;
 
@@ -3482,14 +3512,15 @@ netsh_ifconfig (const struct tuntap_options *to,
       else
        {
          /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
-         buf_init (&out, 0);
-         buf_printf (&out,
-                     "netsh interface ip set address \"%s\" static %s %s",
-                     flex_name,
-                     print_in_addr_t (ip, 0, &gc),
-                     print_in_addr_t (netmask, 0, &gc));
-
-         netsh_command (BSTR(&out), 4);
+         argv_printf (&argv,
+                      "%s%s interface ip set address %s static %s %s",
+                      get_win_sys_path(),
+                      NETSH_PATH_SUFFIX,
+                      flex_name,
+                      print_in_addr_t (ip, 0, &gc),
+                      print_in_addr_t (netmask, 0, &gc));
+
+         netsh_command (&argv, 4);
        }
     }
 
@@ -3517,6 +3548,7 @@ netsh_ifconfig (const struct tuntap_options *to,
                              BOOL_CAST (flags & NI_TEST_FIRST));
     }
   
+  argv_reset (&argv);
   gc_free (&gc);
 }
 
@@ -3524,17 +3556,19 @@ static void
 netsh_enable_dhcp (const struct tuntap_options *to,
                   const char *actual_name)
 {
-  struct gc_arena gc = gc_new ();
-  struct buffer out = alloc_buf_gc (256, &gc);
+  struct argv argv;
+  argv_init (&argv);
 
   /* example: netsh interface ip set address my-tap dhcp */
-  buf_printf (&out,
-             "netsh interface ip set address \"%s\" dhcp",
-             actual_name);
+  argv_printf (&argv,
+             "%s%s interface ip set address %s dhcp",
+              get_win_sys_path(),
+              NETSH_PATH_SUFFIX,
+              actual_name);
 
-  netsh_command (BSTR(&out), 4);
+  netsh_command (&argv, 4);
 
-  gc_free (&gc);
+  argv_reset (&argv);
 }
 
 /*
diff --git a/win32.c b/win32.c
index 516bf8e966035bb2c4d7374b37f82d853ee5b9d7..ec9247eaa50ad360313a7ab62d8863b848236a76 100644 (file)
--- a/win32.c
+++ b/win32.c
@@ -35,6 +35,7 @@
 #include "mtu.h"
 #include "sig.h"
 #include "win32.h"
+#include "misc.h"
 
 #include "memdbg.h"
 
@@ -69,6 +70,11 @@ struct window_title window_title; /* GLOBAL*/
 
 struct semaphore netcmd_semaphore; /* GLOBAL */
 
+/*
+ * Windows system pathname such as c:\windows
+ */
+static char *win_sys_path = NULL; /* GLOBAL */
+
 void
 init_win32 (void)
 {
@@ -100,6 +106,7 @@ uninit_win32 (void)
   window_title_restore (&window_title);
   win32_signal_close (&win32_signal);
   WSACleanup ();
+  free (win_sys_path);
 }
 
 void
@@ -816,4 +823,174 @@ win_safe_filename (const char *fn)
   return true;
 }
 
+/*
+ * Service functions for openvpn_execve
+ */
+
+static char *
+env_block (const struct env_set *es)
+{
+  if (es)
+    {
+      struct env_item *e;
+      char *ret;
+      char *p;
+      size_t nchars = 1;
+      
+      for (e = es->list; e != NULL; e = e->next)
+       nchars += strlen (e->string) + 1;
+
+      ret = (char *) malloc (nchars);
+      check_malloc_return (ret);
+
+      p = ret;
+      for (e = es->list; e != NULL; e = e->next)
+       {
+         if (env_allowed (e->string))
+           {
+             strcpy (p, e->string);
+             p += strlen (e->string) + 1;
+           }
+       }
+      *p = '\0';
+      return ret;
+    }
+  else
+    return NULL;
+}
+
+static char *
+cmd_line (const struct argv *a)
+{
+  size_t nchars = 1;
+  size_t maxlen = 0;
+  size_t i;
+  struct buffer buf;
+  char *work = NULL;
+
+  if (!a)
+    return NULL;
+
+  for (i = 0; i < a->argc; ++i)
+    {
+      const char *arg = a->argv[i];
+      const size_t len = strlen (arg);
+      nchars += len + 3;
+      if (len > maxlen)
+       maxlen = len;
+    }
+
+  work = (char *) malloc (maxlen + 1);
+  check_malloc_return (work);
+  buf = alloc_buf (nchars);
+
+  for (i = 0; i < a->argc; ++i)
+    {
+      const char *arg = a->argv[i];
+      strcpy (work, arg);
+      string_mod (work, CC_PRINT, CC_DOUBLE_QUOTE|CC_CRLF, '_');
+      if (i)
+       buf_printf (&buf, " ");
+      if (string_class (work, CC_ANY, CC_SPACE))
+       buf_printf (&buf, "%s", work);
+      else
+       buf_printf (&buf, "\"%s\"", work);
+    }
+
+  free (work);
+  return BSTR(&buf);
+}
+
+/*
+ * Attempt to simulate fork/execve on Windows
+ */
+int
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
+{
+  int ret = -1;
+  if (a && a->argv[0])
+    {
+      if (openvpn_execve_allowed (flags))
+       {
+         STARTUPINFO start_info;
+         PROCESS_INFORMATION proc_info;
+
+         char *env = env_block (es);
+         char *cl = cmd_line (a);
+         char *cmd = a->argv[0];
+
+         CLEAR (start_info);
+         CLEAR (proc_info);
+
+         /* fill in STARTUPINFO struct */
+         GetStartupInfo(&start_info);
+         start_info.cb = sizeof(start_info);
+         start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+         start_info.wShowWindow = SW_HIDE;
+         start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+         start_info.hStdOutput = start_info.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
+
+         if (CreateProcess (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info))
+           {
+             DWORD exit_status = 0;
+             CloseHandle (proc_info.hThread);
+             WaitForSingleObject (proc_info.hProcess, INFINITE);
+             if (GetExitCodeProcess (proc_info.hProcess, &exit_status))
+               ret = (int)exit_status;
+             else
+               msg (M_WARN|M_ERRNO, "openvpn_execve: GetExitCodeProcess %s failed", cmd);
+             CloseHandle (proc_info.hProcess);
+           }
+         else
+           {
+             msg (M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %s failed", cmd);
+           }
+         free (cl);
+         free (env);
+       }
+      else
+       {
+         msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
+       }
+    }
+  else
+    {
+      msg (M_WARN, "openvpn_execve: called with empty argv");
+    }
+  return ret;
+}
+
+char *
+get_win_sys_path (void)
+{
+  ASSERT (win_sys_path);
+  return win_sys_path;
+}
+
+void
+set_win_sys_path (const char *newpath, struct env_set *es)
+{
+  free (win_sys_path);
+  win_sys_path = string_alloc (newpath, NULL);
+  setenv_str (es, SYS_PATH_ENV_VAR_NAME, win_sys_path); /* route.exe needs this */
+}
+
+void
+set_win_sys_path_via_env (struct env_set *es)
+{
+  char buf[256];
+  DWORD status = GetEnvironmentVariable (SYS_PATH_ENV_VAR_NAME, buf, sizeof(buf));
+  if (!status)
+    msg (M_ERR, "Cannot find environmental variable %s", SYS_PATH_ENV_VAR_NAME);
+  if (status > sizeof (buf) - 1)
+    msg (M_FATAL, "String overflow attempting to read environmental variable %s", SYS_PATH_ENV_VAR_NAME);
+  set_win_sys_path (buf, es);
+}
+
+void
+env_set_add_win32 (struct env_set *es)
+{
+  set_win_sys_path (DEFAULT_WIN_SYS_PATH, es);
+}
+
 #endif
diff --git a/win32.h b/win32.h
index 8cf8af2842f58435a9275674ae9e673088ee030e..b243bf26b85cda53576f033d2800c0602e761885 100644 (file)
--- a/win32.h
+++ b/win32.h
 
 #include "mtu.h"
 
+/* location of executables */
+#define SYS_PATH_ENV_VAR_NAME "SystemRoot"  /* environmental variable name that normally contains the system path */
+#define DEFAULT_WIN_SYS_PATH  "C:\\WINDOWS" /* --win-sys default value */
+#define NETSH_PATH_SUFFIX     "\\system32\\netsh.exe"
+#define WIN_ROUTE_PATH_SUFFIX "\\system32\\route.exe"
+
 /*
  * Win32-specific OpenVPN code, targetted at the mingw
  * development environment.
@@ -250,5 +256,14 @@ bool init_security_attributes_allow_all (struct security_attributes *obj);
 /* return true if filename is safe to be used on Windows */
 bool win_safe_filename (const char *fn);
 
+/* add constant environmental variables needed by Windows */
+struct env_set;
+void env_set_add_win32 (struct env_set *es);
+
+/* get and set the current windows system path */
+void set_win_sys_path (const char *newpath, struct env_set *es);
+void set_win_sys_path_via_env (struct env_set *es);
+char *get_win_sys_path (void);
+
 #endif
 #endif