Browse code

Completely revamped the system for calling external programs and scripts:

* 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

james authored on 2008/07/26 16:27:03
Showing 23 changed files
... ...
@@ -240,6 +240,14 @@ argv_init (struct argv *a)
240 240
   a->argv = NULL;
241 241
 }
242 242
 
243
+struct argv
244
+argv_new (void)
245
+{
246
+  struct argv ret;
247
+  argv_init (&ret);
248
+  return ret;
249
+}
250
+
243 251
 void
244 252
 argv_reset (struct argv *a)
245 253
 {
... ...
@@ -266,6 +274,23 @@ argv_argc (const char *format)
266 266
   return argc;
267 267
 }
268 268
 
269
+struct argv
270
+argv_insert_head (const struct argv *a, const char *head)
271
+{
272
+  struct argv r;
273
+  size_t i;
274
+
275
+  r.argc = (a ? a->argc : 0) + 1;
276
+  ALLOC_ARRAY_CLEAR (r.argv, char *, r.argc + 1);
277
+  r.argv[0] = string_alloc (head, NULL);
278
+  if (a)
279
+    {
280
+      for (i = 0; i < a->argc; ++i)
281
+	r.argv[i+1] = string_alloc (a->argv[i], NULL);
282
+    }
283
+  return r;
284
+}
285
+
269 286
 char *
270 287
 argv_term (const char **f)
271 288
 {
... ...
@@ -324,6 +349,22 @@ argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags)
324 324
 }
325 325
 
326 326
 void
327
+argv_msg (const int msglev, const struct argv *a)
328
+{
329
+  struct gc_arena gc = gc_new ();
330
+  msg (msglev, "%s", argv_str (a, &gc, 0));
331
+  gc_free (&gc);
332
+}
333
+
334
+void
335
+argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
336
+{
337
+  struct gc_arena gc = gc_new ();
338
+  msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
339
+  gc_free (&gc);
340
+}
341
+
342
+void
327 343
 argv_printf (struct argv *a, const char *format, ...)
328 344
 {
329 345
   va_list arglist;
... ...
@@ -373,7 +414,10 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
373 373
 	{
374 374
 	  if (!strcmp (term, "%s"))
375 375
 	    {
376
-	      a->argv[argc++] = string_alloc (va_arg (arglist, char *), NULL);
376
+	      char *s = va_arg (arglist, char *);
377
+	      if (!s)
378
+		s = "";
379
+	      a->argv[argc++] = string_alloc (s, NULL);
377 380
 	    }
378 381
 	  else if (!strcmp (term, "%d"))
379 382
 	    {
... ...
@@ -387,6 +431,41 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
387 387
 	      openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
388 388
 	      a->argv[argc++] = string_alloc (numstr, NULL);
389 389
 	    }
390
+	  else if (!strcmp (term, "%s/%d"))
391
+	    {
392
+	      char numstr[64];
393
+	      char *s = va_arg (arglist, char *);
394
+
395
+	      if (!s)
396
+		s = "";
397
+
398
+	      openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
399
+
400
+	      {
401
+		const size_t len = strlen(s) + strlen(numstr) + 2;
402
+		char *combined = (char *) malloc (len);
403
+		check_malloc_return (combined);
404
+
405
+		strcpy (combined, s);
406
+		strcat (combined, "/");
407
+		strcat (combined, numstr);
408
+		a->argv[argc++] = combined;
409
+	      }
410
+	    }
411
+	  else if (!strcmp (term, "%s%s"))
412
+	    {
413
+	      char *s1 = va_arg (arglist, char *);
414
+	      char *s2 = va_arg (arglist, char *);
415
+	      char *combined;
416
+
417
+	      if (!s1) s1 = "";
418
+	      if (!s2) s2 = "";
419
+	      combined = (char *) malloc (strlen(s1) + strlen(s2) + 1);
420
+	      check_malloc_return (combined);
421
+	      strcpy (combined, s1);
422
+	      strcat (combined, s2);
423
+	      a->argv[argc++] = combined;
424
+	    }
390 425
 	  else
391 426
 	    ASSERT (0);
392 427
 	  free (term);
... ...
@@ -399,57 +478,6 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
399 399
   ASSERT (argc == a->argc);
400 400
 }
401 401
 
402
-#ifdef ARGV_TEST
403
-void
404
-argv_test (void)
405
-{
406
-  struct gc_arena gc = gc_new ();
407
-  char line[512];
408
-  const char *s;
409
-
410
-  struct argv a;
411
-  argv_init (&a);
412
-  argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
413
-  s = argv_str (&a, &gc, PA_BRACKET);
414
-  argv_reset (&a);
415
-  printf ("%s\n", s);
416
-
417
-  argv_init (&a);
418
-  argv_printf (&a, "foo bar %d", 99);
419
-  s = argv_str (&a, &gc, PA_BRACKET);
420
-  argv_reset (&a);
421
-  printf ("%s\n", s);
422
-
423
-  argv_init (&a);
424
-  s = argv_str (&a, &gc, PA_BRACKET);
425
-  argv_reset (&a);
426
-  printf ("%s\n", s);
427
-
428
-  argv_init (&a);
429
-  argv_printf (&a, "foo bar %d", 99);
430
-  argv_printf_cat (&a, "bar %d foo", 42);
431
-  argv_printf_cat (&a, "cool %s %d u", "frood", 4);
432
-  s = argv_str (&a, &gc, PA_BRACKET);
433
-  argv_reset (&a);
434
-  printf ("%s\n", s);
435
-
436
-  while (fgets (line, sizeof(line), stdin) != NULL)
437
-    {
438
-      char *term;
439
-      const char *f = line;
440
-      int i = 0;
441
-
442
-      while ((term = argv_term (&f)) != NULL) 
443
-	{
444
-	  printf ("[%d] '%s'\n", i, term);
445
-	  ++i;
446
-	  free (term);
447
-	}
448
-    }
449
-  gc_free (&gc);
450
-}
451
-#endif
452
-
453 402
 /*
454 403
  * write a string to the end of a buffer that was
455 404
  * truncated by buf_printf
... ...
@@ -234,10 +234,14 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
234 234
  * to execve.
235 235
  */
236 236
 void argv_init (struct argv *a);
237
+struct argv argv_new (void);
237 238
 void argv_reset (struct argv *a);
238 239
 size_t argv_argc (const char *format);
239 240
 char *argv_term (const char **f);
240 241
 const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
242
+struct argv argv_insert_head (const struct argv *a, const char *head);
243
+void argv_msg (const int msglev, const struct argv *a);
244
+void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
241 245
 
242 246
 #define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
243 247
 void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
... ...
@@ -462,7 +462,8 @@ AC_CHECK_FUNCS(daemon chroot getpwnam setuid nice system getpid dup dup2 dnl
462 462
 	       getpass strerror syslog openlog mlockall getgrnam setgid dnl
463 463
 	       setgroups stat flock readv writev setsockopt getsockopt dnl
464 464
 	       setsid chdir putenv getpeername unlink dnl
465
-	       poll chsize ftruncate sendmsg recvmsg getsockname)
465
+	       poll chsize ftruncate sendmsg recvmsg getsockname dnl
466
+	       execve)
466 467
 AC_CACHE_SAVE
467 468
 
468 469
 if test "${WIN32}" = "yes"; then
... ...
@@ -399,6 +399,24 @@
399 399
    obj:/lib/ld-2.5.so
400 400
    obj:/lib/ld-2.5.so
401 401
    obj:/lib/ld-2.5.so
402
+   obj:/lib/libc-2.5.so
403
+   obj:/lib/libdl-2.5.so
404
+   obj:/lib/ld-2.5.so
405
+   obj:/lib/libdl-2.5.so
406
+   fun:dlsym
407
+   fun:libdl_resolve_symbol
408
+   fun:plugin_list_init
409
+   fun:init_plugins
410
+   fun:main
411
+}
412
+
413
+{
414
+   <insert a suppression name here>
415
+   Memcheck:Cond
416
+   obj:/lib/ld-2.5.so
417
+   obj:/lib/ld-2.5.so
418
+   obj:/lib/ld-2.5.so
419
+   obj:/lib/ld-2.5.so
402 420
    obj:/lib/libdl-2.5.so
403 421
    obj:/lib/ld-2.5.so
404 422
    obj:/lib/libdl-2.5.so
... ...
@@ -370,7 +370,7 @@ init_port_share (struct context *c)
370 370
 bool
371 371
 init_static (void)
372 372
 {
373
-  configure_path ();
373
+  /* configure_path (); */
374 374
 
375 375
 #if defined(USE_CRYPTO) && defined(DMALLOC)
376 376
   openssl_dmalloc_init ();
... ...
@@ -921,8 +921,11 @@ do_route (const struct options *options,
921 921
 
922 922
   if (options->route_script)
923 923
     {
924
+      struct argv argv = argv_new ();
924 925
       setenv_str (es, "script_type", "route-up");
925
-      system_check (options->route_script, es, S_SCRIPT, "Route script failed");
926
+      argv_printf (&argv, "%s", options->route_script);
927
+      openvpn_execve_check (&argv, es, S_SCRIPT, "Route script failed");
928
+      argv_reset (&argv);
926 929
     }
927 930
 
928 931
 #ifdef WIN32
... ...
@@ -9,7 +9,7 @@
9 9
 int set_lladdr(const char *ifname, const char *lladdr,
10 10
 		const struct env_set *es)
11 11
 {
12
-  char cmd[256];
12
+  struct argv argv = argv_new ();
13 13
   int r;
14 14
 
15 15
   if (!ifname || !lladdr)
... ...
@@ -17,37 +17,45 @@ int set_lladdr(const char *ifname, const char *lladdr,
17 17
   
18 18
 #if defined(TARGET_LINUX)
19 19
 #ifdef CONFIG_FEATURE_IPROUTE
20
-  openvpn_snprintf (cmd, sizeof (cmd),
20
+  argv_printf (&argv,
21 21
 		    "%s link set addr %s dev %s",
22 22
 		    iproute_path, lladdr, ifname);
23 23
 #else
24
-  openvpn_snprintf (cmd, sizeof (cmd),
25
-		    IFCONFIG_PATH " %s hw ether %s",
24
+  argv_printf (&argv,
25
+		    "%s %s hw ether %s",
26
+		    IFCONFIG_PATH,
26 27
 		    ifname, lladdr);
27 28
 #endif
28 29
 #elif defined(TARGET_SOLARIS)
29
-  openvpn_snprintf (cmd, sizeof (cmd),
30
-		    IFCONFIG_PATH " %s ether %s",
30
+  argv_printf (&argv,
31
+		    "%s %s ether %s",
32
+		    IFCONFIG_PATH,
31 33
 		    ifname, lladdr);
32 34
 #elif defined(TARGET_OPENBSD)
33
-  openvpn_snprintf (cmd, sizeof (cmd),
34
-		    IFCONFIG_PATH " %s lladdr %s",
35
+  argv_printf (&argv,
36
+		    "%s %s lladdr %s",
37
+		    IFCONFIG_PATH,
35 38
 		    ifname, lladdr);
36 39
 #elif defined(TARGET_DARWIN)
37
-  openvpn_snprintf (cmd, sizeof (cmd),
38
-		    IFCONFIG_PATH " %s lladdr %s",
40
+  argv_printf (&argv,
41
+		    "%s %s lladdr %s",
42
+		    IFCONFIG_PATH,
39 43
 		    ifname, lladdr);
40 44
 #elif defined(TARGET_FREEBSD)
41
-  openvpn_snprintf (cmd, sizeof (cmd),
42
-		    IFCONFIG_PATH " %s ether %s",
45
+  argv_printf (&argv,
46
+		    "%s %s ether %s",
47
+		    IFCONFIG_PATH,
43 48
 		    ifname, lladdr);
44 49
 #else
45 50
       msg (M_WARN, "Sorry, but I don't know how to configure link layer addresses on this operating system.");
46 51
       return -1;
47 52
 #endif
48 53
 
49
-  r = system_check (cmd, es, M_WARN, "ERROR: Unable to set link layer address.");
54
+  argv_msg (M_INFO, &argv);
55
+  r = openvpn_execve_check (&argv, es, M_WARN, "ERROR: Unable to set link layer address.");
50 56
   if (r)
51 57
     msg (M_INFO, "TUN/TAP link layer address set to %s", lladdr);
58
+
59
+  argv_reset (&argv);
52 60
   return r;
53 61
 }
... ...
@@ -43,6 +43,9 @@
43 43
 const char *iproute_path = IPROUTE_PATH;
44 44
 #endif
45 45
 
46
+/* contains an SSEC_x value defined in misc.h */
47
+int script_security = SSEC_BUILT_IN; /* GLOBAL */
48
+
46 49
 /* Redefine the top level directory of the filesystem
47 50
    to restrict access to files for security */
48 51
 void
... ...
@@ -196,38 +199,36 @@ run_up_down (const char *command,
196 196
 
197 197
   if (plugin_defined (plugins, plugin_type))
198 198
     {
199
-      struct buffer cmd = alloc_buf_gc (256, &gc);
200
-
199
+      struct argv argv = argv_new ();
201 200
       ASSERT (arg);
202
-
203
-      buf_printf (&cmd,
204
-		  "\"%s\" %d %d %s %s %s",
205
-		  arg,
206
-		  tun_mtu, link_mtu,
207
-		  ifconfig_local, ifconfig_remote,
208
-		  context);
209
-
210
-      if (plugin_call (plugins, plugin_type, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
201
+      argv_printf (&argv,
202
+		   "%s %d %d %s %s %s",
203
+		   arg,
204
+		   tun_mtu, link_mtu,
205
+		   ifconfig_local, ifconfig_remote,
206
+		   context);
207
+
208
+      if (plugin_call (plugins, plugin_type, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
211 209
 	msg (M_FATAL, "ERROR: up/down plugin call failed");
210
+
211
+      argv_reset (&argv);
212 212
     }
213 213
 
214 214
   if (command)
215 215
     {
216
-      struct buffer cmd = alloc_buf_gc (256, &gc);
217
-
216
+      struct argv argv = argv_new ();
218 217
       ASSERT (arg);
219
-
220 218
       setenv_str (es, "script_type", script_type);
221
-
222
-      buf_printf (&cmd,
223
-		  "%s \"%s\" %d %d %s %s %s",
219
+      argv_printf (&argv,
220
+		  "%s %s %d %d %s %s %s",
224 221
 		  command,
225 222
 		  arg,
226 223
 		  tun_mtu, link_mtu,
227 224
 		  ifconfig_local, ifconfig_remote,
228 225
 		  context);
229
-      msg (M_INFO, "%s", BSTR (&cmd));
230
-      system_check (BSTR (&cmd), es, S_SCRIPT|S_FATAL, "script failed");
226
+      argv_msg (M_INFO, &argv);
227
+      openvpn_execve_check (&argv, es, S_SCRIPT|S_FATAL, "script failed");
228
+      argv_reset (&argv);
231 229
     }
232 230
 
233 231
   gc_free (&gc);
... ...
@@ -375,59 +376,6 @@ save_inetd_socket_descriptor (void)
375 375
 }
376 376
 
377 377
 /*
378
- * Wrapper around the system() call.
379
- */
380
-int
381
-openvpn_system (const char *command, const struct env_set *es, unsigned int flags)
382
-{
383
-#ifdef HAVE_SYSTEM
384
-  int ret;
385
-
386
-  /*
387
-   * We need to bracket this code by mutex because system() doesn't
388
-   * accept an environment list, so we have to use the process-wide
389
-   * list which is shared between all threads.
390
-   */
391
-  mutex_lock_static (L_SYSTEM);
392
-  perf_push (PERF_SCRIPT);
393
-
394
-  /*
395
-   * add env_set to environment.
396
-   */
397
-  if (flags & S_SCRIPT)
398
-    env_set_add_to_environment (es);
399
-
400
-
401
-  /* debugging */
402
-  dmsg (D_SCRIPT, "SYSTEM[%u] '%s'", flags, command);
403
-  if (flags & S_SCRIPT)
404
-    env_set_print (D_SCRIPT, es);
405
-
406
-  /*
407
-   * execute the command
408
-   */
409
-  ret = system (command);
410
-
411
-  /* debugging */
412
-  dmsg (D_SCRIPT, "SYSTEM return=%u", ret);
413
-
414
-  /*
415
-   * remove env_set from environment
416
-   */
417
-  if (flags & S_SCRIPT)
418
-    env_set_remove_from_environment (es);
419
-
420
-  perf_pop ();
421
-  mutex_unlock_static (L_SYSTEM);
422
-  return ret;
423
-
424
-#else
425
-  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);
426
-  return -1; /* NOTREACHED */
427
-#endif
428
-}
429
-
430
-/*
431 378
  * Warn if a given file is group/others accessible.
432 379
  */
433 380
 void
... ...
@@ -489,35 +437,35 @@ system_error_message (int stat, struct gc_arena *gc)
489 489
   struct buffer out = alloc_buf_gc (256, gc);
490 490
 #ifdef WIN32
491 491
   if (stat == -1)
492
-    buf_printf (&out, "shell command did not execute -- ");
493
-  buf_printf (&out, "system() returned error code %d", stat);
492
+    buf_printf (&out, "external program did not execute -- ");
493
+  buf_printf (&out, "returned error code %d", stat);
494 494
 #else
495 495
   if (stat == -1)
496
-    buf_printf (&out, "shell command fork failed");
496
+    buf_printf (&out, "external program fork failed");
497 497
   else if (!WIFEXITED (stat))
498
-    buf_printf (&out, "shell command did not exit normally");
498
+    buf_printf (&out, "external program did not exit normally");
499 499
   else
500 500
     {
501 501
       const int cmd_ret = WEXITSTATUS (stat);
502 502
       if (!cmd_ret)
503
-	buf_printf (&out, "shell command exited normally");
503
+	buf_printf (&out, "external program exited normally");
504 504
       else if (cmd_ret == 127)
505
-	buf_printf (&out, "could not execute shell command");
505
+	buf_printf (&out, "could not execute external program");
506 506
       else
507
-	buf_printf (&out, "shell command exited with error status: %d", cmd_ret);
507
+	buf_printf (&out, "external program exited with error status: %d", cmd_ret);
508 508
     }
509 509
 #endif
510 510
   return (const char *)out.data;
511 511
 }
512 512
 
513 513
 /*
514
- * Run system(), exiting on error.
514
+ * Wrapper around openvpn_execve
515 515
  */
516 516
 bool
517
-system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message)
517
+openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
518 518
 {
519 519
   struct gc_arena gc = gc_new ();
520
-  const int stat = openvpn_system (command, es, flags);
520
+  const int stat = openvpn_execve (a, es, flags);
521 521
   int ret = false;
522 522
 
523 523
   if (system_ok (stat))
... ...
@@ -533,6 +481,69 @@ system_check (const char *command, const struct env_set *es, unsigned int flags,
533 533
   return ret;
534 534
 }
535 535
 
536
+bool
537
+openvpn_execve_allowed (const unsigned int flags)
538
+{
539
+  if (flags & S_SCRIPT)
540
+    return script_security >= SSEC_SCRIPTS;
541
+  else
542
+    return script_security >= SSEC_BUILT_IN;
543
+}
544
+
545
+#ifndef WIN32
546
+/*
547
+ * Run execve() inside a fork().  Designed to replicate the semantics of system() but
548
+ * in a safer way that doesn't require the invocation of a shell or the risks
549
+ * assocated with formatting and parsing a command line.
550
+ */
551
+int
552
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
553
+{
554
+  struct gc_arena gc = gc_new ();
555
+  int ret = -1;
556
+
557
+  if (a && a->argv[0])
558
+    {
559
+#if defined(ENABLE_EXECVE)
560
+      if (openvpn_execve_allowed (flags))
561
+	{
562
+	  const char *cmd = a->argv[0];
563
+	  char *const *argv = a->argv;
564
+	  char *const *envp = (char *const *)make_env_array (es, true, &gc);
565
+	  pid_t pid;
566
+
567
+	  pid = fork ();
568
+	  if (pid == (pid_t)0) /* child side */
569
+	    {
570
+	      execve (cmd, argv, envp);
571
+	      exit (127);
572
+	    }
573
+	  else if (pid < (pid_t)0) /* fork failed */
574
+	    ;
575
+	  else /* parent side */
576
+	    {
577
+	      if (waitpid (pid, &ret, 0) != pid)
578
+		ret = -1;
579
+	    }
580
+	}
581
+      else
582
+	{
583
+	  msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
584
+	}
585
+#else
586
+      msg (M_WARN, "openvpn_execve: execve function not available");
587
+#endif
588
+    }
589
+  else
590
+    {
591
+      msg (M_WARN, "openvpn_execve: called with empty argv");
592
+    }
593
+
594
+  gc_free (&gc);
595
+  return ret;
596
+}
597
+#endif
598
+
536 599
 /*
537 600
  * Initialize random number seed.  random() is only used
538 601
  * when "weak" random numbers are acceptable.
... ...
@@ -1479,11 +1490,23 @@ safe_print (const char *str, struct gc_arena *gc)
1479 1479
   return string_mod_const (str, CC_PRINT, CC_CRLF, '.', gc);
1480 1480
 }
1481 1481
 
1482
+static bool
1483
+is_password_env_var (const char *str)
1484
+{
1485
+  return (strncmp (str, "password", 8) == 0);
1486
+}
1487
+
1488
+bool
1489
+env_allowed (const char *str)
1490
+{
1491
+  return (script_security >= SSEC_PW_ENV || !is_password_env_var (str));
1492
+}
1493
+
1482 1494
 bool
1483 1495
 env_safe_to_print (const char *str)
1484 1496
 {
1485 1497
 #ifndef UNSAFE_DEBUG
1486
-  if (strncmp (str, "password", 8) == 0)
1498
+  if (is_password_env_var (str))
1487 1499
     return false;
1488 1500
 #endif
1489 1501
   return true;
... ...
@@ -1492,7 +1515,9 @@ env_safe_to_print (const char *str)
1492 1492
 /* Make arrays of strings */
1493 1493
 
1494 1494
 const char **
1495
-make_env_array (const struct env_set *es, struct gc_arena *gc)
1495
+make_env_array (const struct env_set *es,
1496
+		const bool check_allowed,
1497
+		struct gc_arena *gc)
1496 1498
 {
1497 1499
   char **ret = NULL;
1498 1500
   struct env_item *e = NULL;
... ...
@@ -1511,12 +1536,14 @@ make_env_array (const struct env_set *es, struct gc_arena *gc)
1511 1511
   /* fill return array */
1512 1512
   if (es)
1513 1513
     {
1514
-      e = es->list;
1515
-      for (i = 0; i < n; ++i)
1514
+      i = 0;
1515
+      for (e = es->list; e != NULL; e = e->next)
1516 1516
 	{
1517
-	  ASSERT (e);
1518
-	  ret[i] = e->string;
1519
-	  e = e->next;
1517
+	  if (!check_allowed || env_allowed (e->string))
1518
+	    {
1519
+	      ASSERT (i < n);
1520
+	      ret[i++] = e->string;
1521
+	    }
1520 1522
 	}
1521 1523
     }
1522 1524
 
... ...
@@ -1631,6 +1658,7 @@ openvpn_sleep (const int n)
1631 1631
   sleep (n);
1632 1632
 }
1633 1633
 
1634
+#if 0
1634 1635
 /*
1635 1636
  * Configure PATH.  On Windows, sometimes PATH is not set correctly
1636 1637
  * by default.
... ...
@@ -1672,3 +1700,72 @@ configure_path (void)
1672 1672
     }
1673 1673
 #endif
1674 1674
 }
1675
+#endif
1676
+
1677
+#ifdef ARGV_TEST
1678
+void
1679
+argv_test (void)
1680
+{
1681
+  struct gc_arena gc = gc_new ();
1682
+  char line[512];
1683
+  const char *s;
1684
+
1685
+  struct argv a;
1686
+  argv_init (&a);
1687
+
1688
+#ifdef WIN32
1689
+  argv_printf (&a, "%s foo bar %s", "c:\\src\\test\\jyargs.exe", "foo bar");
1690
+  //argv_printf (&a, "%s %s %s", "c:\\src\\test files\\batargs.bat", "foo", "bar");  
1691
+#else
1692
+  argv_printf (&a, "./myechox foo bar");
1693
+#endif
1694
+
1695
+  argv_msg_prefix (M_INFO, &a, "ARGV");
1696
+  openvpn_execve_check (&a, NULL, 0, "command failed");
1697
+
1698
+  argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
1699
+  s = argv_str (&a, &gc, PA_BRACKET);
1700
+  printf ("%s\n", s);
1701
+
1702
+  {
1703
+    struct argv b = argv_insert_head (&a, "MARK");
1704
+    s = argv_str (&b, &gc, PA_BRACKET);
1705
+    argv_reset (&b);
1706
+    printf ("%s\n", s);
1707
+  }
1708
+
1709
+  argv_printf (&a, "foo bar %d", 99);
1710
+  s = argv_str (&a, &gc, PA_BRACKET);
1711
+  argv_reset (&a);
1712
+  printf ("%s\n", s);
1713
+
1714
+  s = argv_str (&a, &gc, PA_BRACKET);
1715
+  argv_reset (&a);
1716
+  printf ("%s\n", s);
1717
+
1718
+  argv_printf (&a, "foo bar %d", 99);
1719
+  argv_printf_cat (&a, "bar %d foo", 42);
1720
+  argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7);
1721
+  s = argv_str (&a, &gc, PA_BRACKET);
1722
+  printf ("%s\n", s);
1723
+
1724
+#if 0
1725
+  while (fgets (line, sizeof(line), stdin) != NULL)
1726
+    {
1727
+      char *term;
1728
+      const char *f = line;
1729
+      int i = 0;
1730
+
1731
+      while ((term = argv_term (&f)) != NULL) 
1732
+	{
1733
+	  printf ("[%d] '%s'\n", i, term);
1734
+	  ++i;
1735
+	  free (term);
1736
+	}
1737
+    }
1738
+#endif
1739
+
1740
+  argv_reset (&a);
1741
+  gc_free (&gc);
1742
+}
1743
+#endif
... ...
@@ -117,17 +117,15 @@ void warn_if_group_others_accessible(const char* filename);
117 117
 #define S_SCRIPT (1<<0)
118 118
 #define S_FATAL  (1<<1)
119 119
 
120
-/* wrapper around the system() call. */
121
-int openvpn_system (const char *command, const struct env_set *es, unsigned int flags);
122
-
123
-/* interpret the status code returned by system() */
120
+/* interpret the status code returned by system()/execve() */
124 121
 bool system_ok(int);
125 122
 int system_executed (int stat);
126 123
 const char *system_error_message (int, struct gc_arena *gc);
127 124
 
128
-/* run system() with error check, return true if success,
129
-   false if error, exit if error and fatal==true */
130
-bool system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message);
125
+/* wrapper around the execve() call */
126
+int openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags);
127
+bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message);
128
+bool openvpn_execve_allowed (const unsigned int flags);
131 129
 
132 130
 #ifdef HAVE_STRERROR
133 131
 /* a thread-safe version of strerror */
... ...
@@ -184,7 +182,10 @@ void env_set_remove_from_environment (const struct env_set *es);
184 184
 
185 185
 /* Make arrays of strings */
186 186
 
187
-const char **make_env_array (const struct env_set *es, struct gc_arena *gc);
187
+const char **make_env_array (const struct env_set *es,
188
+			     const bool check_allowed,
189
+			     struct gc_arena *gc);
190
+
188 191
 const char **make_arg_array (const char *first, const char *parms, struct gc_arena *gc);
189 192
 const char **make_extended_arg_array (char **p, struct gc_arena *gc);
190 193
 
... ...
@@ -271,6 +272,9 @@ const char *safe_print (const char *str, struct gc_arena *gc);
271 271
 /* returns true if environmental variable safe to print to log */
272 272
 bool env_safe_to_print (const char *str);
273 273
 
274
+/* returns true if environmental variable may be passed to an external program */
275
+bool env_allowed (const char *str);
276
+
274 277
 /*
275 278
  * A sleep function that services the management layer for n
276 279
  * seconds rather than doing nothing.
... ...
@@ -290,4 +294,10 @@ void get_user_pass_auto_userid (struct user_pass *up, const char *tag);
290 290
 extern const char *iproute_path;
291 291
 #endif
292 292
 
293
+#define SSEC_NONE      0 /* strictly no calling of external programs */
294
+#define SSEC_BUILT_IN  1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/
295
+#define SSEC_SCRIPTS   2 /* allow calling of built-in programs and user-defined scripts */
296
+#define SSEC_PW_ENV    3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */
297
+extern int script_security; /* GLOBAL */
298
+
293 299
 #endif
... ...
@@ -85,36 +85,33 @@ learn_address_script (const struct multi_context *m,
85 85
 
86 86
   if (plugin_defined (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS))
87 87
     {
88
-      struct buffer cmd = alloc_buf_gc (256, &gc);
89
-
90
-      buf_printf (&cmd, "\"%s\" \"%s\"",
91
-		  op,
92
-		  mroute_addr_print (addr, &gc));
88
+      struct argv argv = argv_new ();
89
+      argv_printf (&argv, "%s %s",
90
+		   op,
91
+		   mroute_addr_print (addr, &gc));
93 92
       if (mi)
94
-	buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
95
-
96
-      if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
93
+	argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
94
+      if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
97 95
 	{
98 96
 	  msg (M_WARN, "WARNING: learn-address plugin call failed");
99 97
 	  ret = false;
100 98
 	}
99
+      argv_reset (&argv);
101 100
     }
102 101
 
103 102
   if (m->top.options.learn_address_script)
104 103
     {
105
-      struct buffer cmd = alloc_buf_gc (256, &gc);
106
-
104
+      struct argv argv = argv_new ();
107 105
       setenv_str (es, "script_type", "learn-address");
108
-
109
-      buf_printf (&cmd, "%s \"%s\" \"%s\"",
110
-		  m->top.options.learn_address_script,
111
-		  op,
112
-		  mroute_addr_print (addr, &gc));
106
+      argv_printf (&argv, "%s %s %s",
107
+		   m->top.options.learn_address_script,
108
+		   op,
109
+		   mroute_addr_print (addr, &gc));
113 110
       if (mi)
114
-	buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
115
-
116
-      if (!system_check (BSTR (&cmd), es, S_SCRIPT, "WARNING: learn-address command failed"))
111
+	argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
112
+      if (!openvpn_execve_check (&argv, es, S_SCRIPT, "WARNING: learn-address command failed"))
117 113
 	ret = false;
114
+      argv_reset (&argv);
118 115
     }
119 116
 
120 117
   gc_free (&gc);
... ...
@@ -474,16 +471,11 @@ multi_client_disconnect_script (struct multi_context *m,
474 474
 
475 475
       if (mi->context.options.client_disconnect_script)
476 476
 	{
477
-	  struct gc_arena gc = gc_new ();
478
-	  struct buffer cmd = alloc_buf_gc (256, &gc);
479
-
477
+	  struct argv argv = argv_new ();
480 478
 	  setenv_str (mi->context.c2.es, "script_type", "client-disconnect");
481
-	  
482
-	  buf_printf (&cmd, "%s", mi->context.options.client_disconnect_script);
483
-
484
-	  system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
485
-	  
486
-	  gc_free (&gc);
479
+	  argv_printf (&argv, "%s", mi->context.options.client_disconnect_script);
480
+	  openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
481
+	  argv_reset (&argv);
487 482
 	}
488 483
 #ifdef MANAGEMENT_DEF_AUTH
489 484
       if (management)
... ...
@@ -1523,11 +1515,11 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
1523 1523
       /* deprecated callback, use a file for passing back return info */
1524 1524
       if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
1525 1525
 	{
1526
+	  struct argv argv = argv_new ();
1526 1527
 	  const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc);
1527
-
1528
+	  argv_printf (&argv, "%s", dc_file);
1528 1529
 	  delete_file (dc_file);
1529
-
1530
-	  if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
1530
+	  if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
1531 1531
 	    {
1532 1532
 	      msg (M_WARN, "WARNING: client-connect plugin call failed");
1533 1533
 	      cc_succeeded = false;
... ...
@@ -1537,6 +1529,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
1537 1537
 	      multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
1538 1538
 	      ++cc_succeeded_count;
1539 1539
 	    }
1540
+	  argv_reset (&argv);
1540 1541
 	}
1541 1542
 
1542 1543
       /* 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
1566 1566
        */
1567 1567
       if (mi->context.options.client_connect_script && cc_succeeded)
1568 1568
 	{
1569
-	  struct buffer cmd = alloc_buf_gc (256, &gc);
1569
+	  struct argv argv = argv_new ();
1570 1570
 	  const char *dc_file = NULL;
1571 1571
 
1572 1572
 	  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
1575 1575
 
1576 1576
 	  delete_file (dc_file);
1577 1577
 
1578
-	  buf_printf (&cmd, "%s %s",
1579
-		      mi->context.options.client_connect_script,
1580
-		      dc_file);
1578
+	  argv_printf (&argv, "%s %s",
1579
+		       mi->context.options.client_connect_script,
1580
+		       dc_file);
1581 1581
 
1582
-	  if (system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
1582
+	  if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
1583 1583
 	    {
1584 1584
 	      multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
1585 1585
 	      ++cc_succeeded_count;
1586 1586
 	    }
1587 1587
 	  else
1588 1588
 	    cc_succeeded = false;
1589
+
1590
+	  argv_reset (&argv);
1589 1591
 	}
1590 1592
 
1591 1593
       /*
... ...
@@ -252,6 +252,7 @@ openvpn \- secure IP tunnel daemon.
252 252
 [\ \fB\-\-route\-up\fR\ \fIcmd\fR\ ]
253 253
 [\ \fB\-\-route\fR\ \fInetwork\ [netmask]\ [gateway]\ [metric]\fR\ ]
254 254
 [\ \fB\-\-rport\fR\ \fIport\fR\ ]
255
+[\ \fB\-\-script\-security\fR\ \fIlevel\fR\ ]
255 256
 [\ \fB\-\-secret\fR\ \fIfile\ [direction]\fR\ ]
256 257
 [\ \fB\-\-secret\fR\ \fIfile\fR\ ]
257 258
 [\ \fB\-\-server\-bridge\fR\ \fIgateway\ netmask\ pool\-start\-IP\ pool\-end\-IP\fR\ ]
... ...
@@ -300,6 +301,7 @@ openvpn \- secure IP tunnel daemon.
300 300
 [\ \fB\-\-user\fR\ \fIuser\fR\ ]
301 301
 [\ \fB\-\-username\-as\-common\-name\fR\ ]
302 302
 [\ \fB\-\-verb\fR\ \fIn\fR\ ]
303
+[\ \fB\-\-win\-sys\fR\ \fIpath|'env'\fR\ ]
303 304
 [\ \fB\-\-writepid\fR\ \fIfile\fR\ ]
304 305
 .in -4
305 306
 .ti +4
... ...
@@ -1998,6 +2000,24 @@ is a safety precaution to prevent a LD_PRELOAD style attack
1998 1998
 from a malicious or compromised server.
1999 1999
 .\"*********************************************************
2000 2000
 .TP
2001
+.B --script-security level
2002
+This directive offers policy-level control over OpenVPN's usage of external programs
2003
+and scripts.  Lower values are more restrictive, higher values are more permissive.  Settings for
2004
+.B level:
2005
+
2006
+.B 0 --
2007
+Strictly no calling of external programs.
2008
+.br
2009
+.B 1 --
2010
+(Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
2011
+.br
2012
+.B 2 --
2013
+Allow calling of built-in executables and user-defined scripts.
2014
+.br
2015
+.B 3 --
2016
+Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
2017
+.\"*********************************************************
2018
+.TP
2001 2019
 .B --disable-occ
2002 2020
 Don't output a warning message if option inconsistencies are detected between
2003 2021
 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.
4481 4481
 .SS Windows-Specific Options:
4482 4482
 .\"*********************************************************
4483 4483
 .TP
4484
+.B --win-sys path|'env'
4485
+Set the Windows system directory pathname to use when looking for system
4486
+executables such as
4487
+.B route.exe
4488
+and
4489
+.B netsh.exe.
4490
+By default, if this directive is
4491
+not specified, the pathname will be set to "C:\\WINDOWS"
4492
+
4493
+The special string
4494
+.B 'env'
4495
+indicates that the pathname should be read from the
4496
+.B SystemRoot
4497
+environmental variable.
4498
+.\"*********************************************************
4499
+.TP
4484 4500
 .B --ip-win32 method
4485 4501
 When using
4486 4502
 .B --ifconfig
... ...
@@ -27,6 +27,7 @@
27 27
 #include "init.h"
28 28
 #include "forward.h"
29 29
 #include "multi.h"
30
+#include "win32.h"
30 31
 
31 32
 #include "memdbg.h"
32 33
 
... ...
@@ -131,6 +132,9 @@ main (int argc, char *argv[])
131 131
 
132 132
 	  /* initialize environmental variable store */
133 133
 	  c.es = env_set_create (NULL);
134
+#ifdef WIN32
135
+	  env_set_add_win32 (c.es);
136
+#endif
134 137
 
135 138
 #ifdef ENABLE_MANAGEMENT
136 139
 	  /* initialize management subsystem */
... ...
@@ -189,6 +189,10 @@ static const char usage_message[] =
189 189
   "                  flag to add a direct route to DHCP server, bypassing tunnel.\n"
190 190
   "                  Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
191 191
   "--setenv name value : Set a custom environmental variable to pass to script.\n"
192
+  "--script-security level : 0 -- strictly no calling of external programs\n"
193
+  "                          1 -- (default) only call built-ins such as ifconfig\n"
194
+  "                          2 -- allow calling of built-ins and scripts\n"
195
+  "                          3 -- allow password to be passed to scripts via env\n"
192 196
   "--shaper n      : Restrict output to peer to n bytes per second.\n"
193 197
   "--keepalive n m : Helper option for setting timeouts in server mode.  Send\n"
194 198
   "                  ping once every n seconds, restart if ping not received\n"
... ...
@@ -536,6 +540,8 @@ static const char usage_message[] =
536 536
 #ifdef WIN32
537 537
   "\n"
538 538
   "Windows Specific:\n"
539
+  "--win-sys path|'env' : Pathname of Windows system directory, C:\\WINDOWS by default.\n"
540
+  "                       If specified as 'env', read the pathname from SystemRoot env var.\n"
539 541
   "--ip-win32 method : When using --ifconfig on Windows, set TAP-Win32 adapter\n"
540 542
   "                    IP address using method = manual, netsh, ipapi,\n"
541 543
   "                    dynamic, or adaptive (default = adaptive).\n"
... ...
@@ -4249,6 +4255,11 @@ add_option (struct options *options,
4249 4249
       VERIFY_PERMISSION (OPT_P_SETENV);
4250 4250
       setenv_str_safe (es, p[1], p[2] ? p[2] : "");
4251 4251
     }
4252
+  else if (streq (p[0], "script-security") && p[1])
4253
+    {
4254
+      VERIFY_PERMISSION (OPT_P_GENERAL);
4255
+      script_security = atoi (p[1]);
4256
+    }  
4252 4257
   else if (streq (p[0], "mssfix"))
4253 4258
     {
4254 4259
       VERIFY_PERMISSION (OPT_P_GENERAL);
... ...
@@ -4618,6 +4629,14 @@ add_option (struct options *options,
4618 4618
     }
4619 4619
 #endif
4620 4620
 #ifdef WIN32
4621
+  else if (streq (p[0], "win-sys") && p[1])
4622
+    {
4623
+      VERIFY_PERMISSION (OPT_P_GENERAL);
4624
+      if (streq (p[1], "env"))
4625
+	set_win_sys_path_via_env (es);
4626
+      else
4627
+	set_win_sys_path (p[1], es);
4628
+    }
4621 4629
   else if (streq (p[0], "route-method") && p[1])
4622 4630
     {
4623 4631
       VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
... ...
@@ -327,7 +327,7 @@ static int
327 327
 plugin_call_item (const struct plugin *p,
328 328
 		  void *per_client_context,
329 329
 		  const int type,
330
-		  const char *args,
330
+		  const struct argv *av,
331 331
 		  struct openvpn_plugin_string_list **retlist,
332 332
 		  const char **envp)
333 333
 {
... ...
@@ -340,18 +340,18 @@ plugin_call_item (const struct plugin *p,
340 340
   if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type)))
341 341
     {
342 342
       struct gc_arena gc = gc_new ();
343
-      const char **argv = make_arg_array (p->so_pathname, args, &gc);
343
+      struct argv a = argv_insert_head (av, p->so_pathname);
344 344
 
345 345
       dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type));
346
-      plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp);
346
+      plugin_show_args_env (D_PLUGIN_DEBUG, (const char **)a.argv, envp);
347 347
 
348 348
       /*
349 349
        * Call the plugin work function
350 350
        */
351 351
       if (p->func2)
352
-	status = (*p->func2)(p->plugin_handle, type, argv, envp, per_client_context, retlist);
352
+	status = (*p->func2)(p->plugin_handle, type, (const char **)a.argv, envp, per_client_context, retlist);
353 353
       else if (p->func1)
354
-	status = (*p->func1)(p->plugin_handle, type, argv, envp);
354
+	status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp);
355 355
       else
356 356
 	ASSERT (0);
357 357
 
... ...
@@ -366,6 +366,7 @@ plugin_call_item (const struct plugin *p,
366 366
 	     status,
367 367
 	     p->so_pathname);
368 368
 
369
+      argv_reset (&a);
369 370
       gc_free (&gc);
370 371
     }
371 372
   return status;
... ...
@@ -482,7 +483,7 @@ plugin_common_open (struct plugin_common *pc,
482 482
   int i;
483 483
   const char **envp;
484 484
 
485
-  envp = make_env_array (es, &gc);
485
+  envp = make_env_array (es, false, &gc);
486 486
 
487 487
   if (pr)
488 488
     plugin_return_init (pr);
... ...
@@ -540,7 +541,7 @@ plugin_list_open (struct plugin_list *pl,
540 540
 int
541 541
 plugin_call (const struct plugin_list *pl,
542 542
 	     const int type,
543
-	     const char *args,
543
+	     const struct argv *av,
544 544
 	     struct plugin_return *pr,
545 545
 	     struct env_set *es)
546 546
 {
... ...
@@ -560,14 +561,14 @@ plugin_call (const struct plugin_list *pl,
560 560
       mutex_lock_static (L_PLUGIN);
561 561
 
562 562
       setenv_del (es, "script_type");
563
-      envp = make_env_array (es, &gc);
563
+      envp = make_env_array (es, false, &gc);
564 564
 
565 565
       for (i = 0; i < n; ++i)
566 566
 	{
567 567
 	  const int status = plugin_call_item (&pl->common->plugins[i],
568 568
 					       pl->per_client.per_client_context[i],
569 569
 					       type,
570
-					       args,
570
+					       av,
571 571
 					       pr ? &pr->list[i] : NULL,
572 572
 					       envp);
573 573
 	  switch (status)
... ...
@@ -116,7 +116,7 @@ struct plugin_list *plugin_list_inherit (const struct plugin_list *src);
116 116
 
117 117
 int plugin_call (const struct plugin_list *pl,
118 118
 		 const int type,
119
-		 const char *args,
119
+		 const struct argv *av,
120 120
 		 struct plugin_return *pr,
121 121
 		 struct env_set *es);
122 122
 
... ...
@@ -168,7 +168,7 @@ plugin_defined (const struct plugin_list *pl, const int type)
168 168
 static inline int
169 169
 plugin_call (const struct plugin_list *pl,
170 170
 	     const int type,
171
-	     const char *args,
171
+	     const struct argv *av,
172 172
 	     struct plugin_return *pr,
173 173
 	     struct env_set *es)
174 174
 {
175 175
new file mode 100644
... ...
@@ -0,0 +1,184 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2002-2008 Telethra, Inc. <sales@openvpn.net>
8
+ *
9
+ *  This program is free software; you can redistribute it and/or modify
10
+ *  it under the terms of the GNU General Public License version 2
11
+ *  as published by the Free Software Foundation.
12
+ *
13
+ *  This program is distributed in the hope that it will be useful,
14
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ *  GNU General Public License for more details.
17
+ *
18
+ *  You should have received a copy of the GNU General Public License
19
+ *  along with this program (see the file COPYING included with this
20
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
21
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ */
23
+
24
+/*
25
+ * This plugin is similar to simple.c, except it also logs extra information
26
+ * to stdout for every plugin method called by OpenVPN.
27
+ *
28
+ * See the README file for build instructions.
29
+ */
30
+
31
+#include <stdio.h>
32
+#include <string.h>
33
+#include <stdlib.h>
34
+
35
+#include "openvpn-plugin.h"
36
+
37
+/*
38
+ * Our context, where we keep our state.
39
+ */
40
+struct plugin_context {
41
+  const char *username;
42
+  const char *password;
43
+};
44
+
45
+/*
46
+ * Given an environmental variable name, search
47
+ * the envp array for its value, returning it
48
+ * if found or NULL otherwise.
49
+ */
50
+static const char *
51
+get_env (const char *name, const char *envp[])
52
+{
53
+  if (envp)
54
+    {
55
+      int i;
56
+      const int namelen = strlen (name);
57
+      for (i = 0; envp[i]; ++i)
58
+	{
59
+	  if (!strncmp (envp[i], name, namelen))
60
+	    {
61
+	      const char *cp = envp[i] + namelen;
62
+	      if (*cp == '=')
63
+		return cp + 1;
64
+	    }
65
+	}
66
+    }
67
+  return NULL;
68
+}
69
+
70
+OPENVPN_EXPORT openvpn_plugin_handle_t
71
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
72
+{
73
+  struct plugin_context *context;
74
+
75
+  /*
76
+   * Allocate our context
77
+   */
78
+  context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
79
+
80
+  /*
81
+   * Set the username/password we will require.
82
+   */
83
+  context->username = "foo";
84
+  context->password = "bar";
85
+
86
+  /*
87
+   * Which callbacks to intercept.
88
+   */
89
+  *type_mask =
90
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
91
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) |
92
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) |
93
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) |
94
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) |
95
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
96
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
97
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
98
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
99
+    OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL);
100
+
101
+  return (openvpn_plugin_handle_t) context;
102
+}
103
+
104
+void
105
+show (const int type, const char *argv[], const char *envp[])
106
+{
107
+  size_t i;
108
+  switch (type)
109
+    {
110
+    case OPENVPN_PLUGIN_UP:
111
+      printf ("OPENVPN_PLUGIN_UP\n");
112
+      break;
113
+    case OPENVPN_PLUGIN_DOWN:
114
+      printf ("OPENVPN_PLUGIN_DOWN\n");
115
+      break;
116
+    case OPENVPN_PLUGIN_ROUTE_UP:
117
+      printf ("OPENVPN_PLUGIN_ROUTE_UP\n");
118
+      break;
119
+    case OPENVPN_PLUGIN_IPCHANGE:
120
+      printf ("OPENVPN_PLUGIN_IPCHANGE\n");
121
+      break;
122
+    case OPENVPN_PLUGIN_TLS_VERIFY:
123
+      printf ("OPENVPN_PLUGIN_TLS_VERIFY\n");
124
+      break;
125
+    case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
126
+      printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
127
+      break;
128
+    case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
129
+      printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
130
+      break;
131
+    case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
132
+      printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
133
+      break;
134
+    case OPENVPN_PLUGIN_LEARN_ADDRESS:
135
+      printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
136
+      break;
137
+    case OPENVPN_PLUGIN_TLS_FINAL:
138
+      printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
139
+      break;
140
+    default:
141
+      printf ("OPENVPN_PLUGIN_?\n");
142
+      break;
143
+    }
144
+
145
+  printf ("ARGV\n");
146
+  for (i = 0; argv[i] != NULL; ++i)
147
+    printf ("%d '%s'\n", (int)i, argv[i]);
148
+
149
+  printf ("ENVP\n");
150
+  for (i = 0; envp[i] != NULL; ++i)
151
+    printf ("%d '%s'\n", (int)i, envp[i]);
152
+}
153
+
154
+OPENVPN_EXPORT int
155
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
156
+{
157
+  struct plugin_context *context = (struct plugin_context *) handle;
158
+
159
+  show (type, argv, envp);
160
+
161
+  /* check entered username/password against what we require */
162
+  if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
163
+    {
164
+      /* get username/password from envp string array */
165
+      const char *username = get_env ("username", envp);
166
+      const char *password = get_env ("password", envp);
167
+
168
+      if (username && !strcmp (username, context->username)
169
+	  && password && !strcmp (password, context->password))
170
+	return OPENVPN_PLUGIN_FUNC_SUCCESS;
171
+      else
172
+	return OPENVPN_PLUGIN_FUNC_ERROR;
173
+    }
174
+  else
175
+    return OPENVPN_PLUGIN_FUNC_SUCCESS;
176
+}
177
+
178
+OPENVPN_EXPORT void
179
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
180
+{
181
+  struct plugin_context *context = (struct plugin_context *) handle;
182
+  free (context);
183
+}
... ...
@@ -34,6 +34,7 @@
34 34
 #include "misc.h"
35 35
 #include "socket.h"
36 36
 #include "manage.h"
37
+#include "win32.h"
37 38
 
38 39
 #include "memdbg.h"
39 40
 
... ...
@@ -743,7 +744,7 @@ void
743 743
 add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
744 744
 {
745 745
   struct gc_arena gc;
746
-  struct buffer buf;
746
+  struct argv argv;
747 747
   const char *network;
748 748
   const char *netmask;
749 749
   const char *gateway;
... ...
@@ -753,7 +754,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
753 753
     return;
754 754
 
755 755
   gc_init (&gc);
756
-  buf = alloc_buf_gc (256, &gc);
756
+  argv_init (&argv);
757 757
 
758 758
   network = print_in_addr_t (r->network, 0, &gc);
759 759
   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
771 771
 
772 772
 #if defined(TARGET_LINUX)
773 773
 #ifdef CONFIG_FEATURE_IPROUTE
774
-  buf_printf (&buf, "%s route add %s/%d via %s",
774
+  argv_printf (&argv, "%s route add %s/%d via %s",
775 775
   	      iproute_path,
776 776
 	      network,
777 777
 	      count_netmask_bits(netmask),
778 778
 	      gateway);
779 779
   if (r->metric_defined)
780
-    buf_printf (&buf, " metric %d", r->metric);
780
+    argv_printf_cat (&argv, "metric %d", r->metric);
781 781
 
782 782
 #else
783
-  buf_printf (&buf, ROUTE_PATH " add -net %s netmask %s gw %s",
783
+  argv_printf (&argv, "%s add -net %s netmask %s gw %s",
784
+		ROUTE_PATH,
784 785
 	      network,
785 786
 	      netmask,
786 787
 	      gateway);
787 788
   if (r->metric_defined)
788
-    buf_printf (&buf, " metric %d", r->metric);
789
+    argv_printf_cat (&argv, "metric %d", r->metric);
789 790
 #endif  /*CONFIG_FEATURE_IPROUTE*/
790
-  msg (D_ROUTE, "%s", BSTR (&buf));
791
-  status = system_check (BSTR (&buf), es, 0, "ERROR: Linux route add command failed");
791
+  argv_msg (D_ROUTE, &argv);
792
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
792 793
 
793 794
 #elif defined (WIN32)
794 795
 
795
-  buf_printf (&buf, ROUTE_PATH " ADD %s MASK %s %s",
796
-	      network,
797
-	      netmask,
798
-	      gateway);
796
+  argv_printf (&argv, "%s%s ADD %s MASK %s %s",
797
+	       get_win_sys_path(),
798
+	       WIN_ROUTE_PATH_SUFFIX,
799
+	       network,
800
+	       netmask,
801
+	       gateway);
799 802
   if (r->metric_defined)
800
-    buf_printf (&buf, " METRIC %d", r->metric);
803
+    argv_printf_cat (&argv, "METRIC %d", r->metric);
801 804
 
802
-  msg (D_ROUTE, "%s", BSTR (&buf));
805
+  argv_msg (D_ROUTE, &argv);
803 806
 
804 807
   if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
805 808
     {
... ...
@@ -809,7 +813,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
809 809
   else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
810 810
     {
811 811
       netcmd_semaphore_lock ();
812
-      status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed");
812
+      status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
813 813
       netcmd_semaphore_release ();
814 814
     }
815 815
   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
820 820
 	{
821 821
 	  msg (D_ROUTE, "Route addition fallback to route.exe");
822 822
 	  netcmd_semaphore_lock ();
823
-	  status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed [adaptive]");
823
+	  status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
824 824
 	  netcmd_semaphore_release ();
825 825
 	}
826 826
     }
... ...
@@ -833,88 +837,93 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
833 833
 
834 834
   /* example: route add 192.0.2.32 -netmask 255.255.255.224 somegateway */
835 835
 
836
-  buf_printf (&buf, ROUTE_PATH " add");
836
+  argv_printf (&argv, "%s add",
837
+		ROUTE_PATH);
837 838
 
838 839
 #if 0
839 840
   if (r->metric_defined)
840
-    buf_printf (&buf, " -rtt %d", r->metric);
841
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
841 842
 #endif
842 843
 
843
-  buf_printf (&buf, " %s -netmask %s %s",
844
+  argv_printf_cat (&argv, "%s -netmask %s %s",
844 845
 	      network,
845 846
 	      netmask,
846 847
 	      gateway);
847 848
 
848
-  msg (D_ROUTE, "%s", BSTR (&buf));
849
-  status = system_check (BSTR (&buf), es, 0, "ERROR: Solaris route add command failed");
849
+  argv_msg (D_ROUTE, &argv);
850
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
850 851
 
851 852
 #elif defined(TARGET_FREEBSD)
852 853
 
853
-  buf_printf (&buf, ROUTE_PATH " add");
854
+  argv_printf (&argv, "%s add",
855
+		ROUTE_PATH);
854 856
 
855 857
 #if 0
856 858
   if (r->metric_defined)
857
-    buf_printf (&buf, " -rtt %d", r->metric);
859
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
858 860
 #endif
859 861
 
860
-  buf_printf (&buf, " -net %s %s %s",
862
+  argv_printf_cat (&argv, "-net %s %s %s",
861 863
 	      network,
862 864
 	      gateway,
863 865
 	      netmask);
864 866
 
865
-  msg (D_ROUTE, "%s", BSTR (&buf));
866
-  status = system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route add command failed");
867
+  argv_msg (D_ROUTE, &argv);
868
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
867 869
 
868 870
 #elif defined(TARGET_DRAGONFLY)
869 871
 
870
-  buf_printf (&buf, ROUTE_PATH " add");
872
+  argv_printf (&argv, "%s add",
873
+		ROUTE_PATH);
871 874
 
872 875
 #if 0
873 876
   if (r->metric_defined)
874
-    buf_printf (&buf, " -rtt %d", r->metric);
877
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
875 878
 #endif
876 879
 
877
-  buf_printf (&buf, " -net %s %s %s",
880
+  argv_printf_cat (&argv, "-net %s %s %s",
878 881
 	      network,
879 882
 	      gateway,
880 883
 	      netmask);
881 884
 
882
-  msg (D_ROUTE, "%s", BSTR (&buf));
883
-  status = system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route add command failed");
885
+  argv_msg (D_ROUTE, &argv);
886
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
884 887
 
885 888
 #elif defined(TARGET_DARWIN)
886 889
 
887
-  buf_printf (&buf, ROUTE_PATH " add");
890
+  argv_printf (&argv, "%s add",
891
+		ROUTE_PATH);
888 892
 
889 893
 #if 0
890 894
   if (r->metric_defined)
891
-    buf_printf (&buf, " -rtt %d", r->metric);
895
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
892 896
 #endif
893 897
 
894
-  buf_printf (&buf, " -net %s %s %s",
898
+  argv_printf_cat (&argv, "-net %s %s %s",
895 899
               network,
896 900
               gateway,
897 901
               netmask);
898 902
 
899
-  msg (D_ROUTE, "%s", BSTR (&buf));
900
-  status = system_check (BSTR (&buf), es, 0, "ERROR: OS X route add command failed");
903
+  argv_msg (D_ROUTE, &argv);
904
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
901 905
 
902 906
 #elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
903 907
 
904
-  buf_printf (&buf, ROUTE_PATH " add");
908
+  argv_printf (&argv, "%s add",
909
+		ROUTE_PATH);
905 910
 
906 911
 #if 0
907 912
   if (r->metric_defined)
908
-    buf_printf (&buf, " -rtt %d", r->metric);
913
+    argv_printf_cat (&argv, "-rtt %d", r->metric);
909 914
 #endif
910 915
 
911
-  buf_printf (&buf, " -net %s %s -netmask %s",
916
+  argv_printf_cat (&argv, "-net %s %s -netmask %s",
912 917
 	      network,
913 918
 	      gateway,
914 919
 	      netmask);
915 920
 
916
-  msg (D_ROUTE, "%s", BSTR (&buf));
917
-  status = system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
921
+  argv_msg (D_ROUTE, &argv);
922
+  status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
918 923
 
919 924
 #else
920 925
   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
922 922
 
923 923
  done:
924 924
   r->defined = status;
925
+  argv_reset (&argv);
925 926
   gc_free (&gc);
926 927
 }
927 928
 
... ...
@@ -929,7 +939,7 @@ static void
929 929
 delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
930 930
 {
931 931
   struct gc_arena gc;
932
-  struct buffer buf;
932
+  struct argv argv;
933 933
   const char *network;
934 934
   const char *netmask;
935 935
   const char *gateway;
... ...
@@ -938,37 +948,40 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
938 938
     return;
939 939
 
940 940
   gc_init (&gc);
941
+  argv_init (&argv);
941 942
 
942
-  buf = alloc_buf_gc (256, &gc);
943 943
   network = print_in_addr_t (r->network, 0, &gc);
944 944
   netmask = print_in_addr_t (r->netmask, 0, &gc);
945 945
   gateway = print_in_addr_t (r->gateway, 0, &gc);
946 946
 
947 947
 #if defined(TARGET_LINUX)
948 948
 #ifdef CONFIG_FEATURE_IPROUTE
949
-  buf_printf (&buf, "%s route del %s/%d",
949
+  argv_printf (&argv, "%s route del %s/%d",
950 950
   	      iproute_path,
951 951
 	      network,
952 952
 	      count_netmask_bits(netmask));
953 953
 #else
954 954
 
955
-  buf_printf (&buf, ROUTE_PATH " del -net %s netmask %s",
955
+  argv_printf (&argv, "%s del -net %s netmask %s",
956
+		ROUTE_PATH,
956 957
 	      network,
957 958
 	      netmask);
958 959
 #endif /*CONFIG_FEATURE_IPROUTE*/
959 960
   if (r->metric_defined)
960
-    buf_printf (&buf, " metric %d", r->metric);
961
-  msg (D_ROUTE, "%s", BSTR (&buf));
962
-  system_check (BSTR (&buf), es, 0, "ERROR: Linux route delete command failed");
961
+    argv_printf_cat (&argv, "metric %d", r->metric);
962
+  argv_msg (D_ROUTE, &argv);
963
+  openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
963 964
 
964 965
 #elif defined (WIN32)
965 966
   
966
-  buf_printf (&buf, ROUTE_PATH " DELETE %s MASK %s %s",
967
-	      network,
968
-              netmask,
969
-              gateway);
967
+  argv_printf (&argv, "%s%s DELETE %s MASK %s %s",
968
+	       get_win_sys_path(),
969
+	       WIN_ROUTE_PATH_SUFFIX,
970
+	       network,
971
+	       netmask,
972
+	       gateway);
970 973
 
971
-  msg (D_ROUTE, "%s", BSTR (&buf));
974
+  argv_msg (D_ROUTE, &argv);
972 975
 
973 976
   if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
974 977
     {
... ...
@@ -978,7 +991,7 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
978 978
   else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
979 979
     {
980 980
       netcmd_semaphore_lock ();
981
-      system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed");
981
+      openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed");
982 982
       netcmd_semaphore_release ();
983 983
     }
984 984
   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
989 989
 	{
990 990
 	  msg (D_ROUTE, "Route deletion fallback to route.exe");
991 991
 	  netcmd_semaphore_lock ();
992
-	  system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed [adaptive]");
992
+	  openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed [adaptive]");
993 993
 	  netcmd_semaphore_release ();
994 994
 	}
995 995
     }
... ...
@@ -1000,58 +1013,64 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
1000 1000
 
1001 1001
 #elif defined (TARGET_SOLARIS)
1002 1002
 
1003
-  buf_printf (&buf, ROUTE_PATH " delete %s -netmask %s %s",
1003
+  argv_printf (&argv, "%s delete %s -netmask %s %s",
1004
+		ROUTE_PATH,
1004 1005
 	      network,
1005 1006
 	      netmask,
1006 1007
 	      gateway);
1007 1008
 
1008
-  msg (D_ROUTE, "%s", BSTR (&buf));
1009
-  system_check (BSTR (&buf), es, 0, "ERROR: Solaris route delete command failed");
1009
+  argv_msg (D_ROUTE, &argv);
1010
+  openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete command failed");
1010 1011
 
1011 1012
 #elif defined(TARGET_FREEBSD)
1012 1013
 
1013
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
1014
+  argv_printf (&argv, "%s delete -net %s %s %s",
1015
+		ROUTE_PATH,
1014 1016
 	      network,
1015 1017
 	      gateway,
1016 1018
 	      netmask);
1017 1019
 
1018
-  msg (D_ROUTE, "%s", BSTR (&buf));
1019
-  system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route delete command failed");
1020
+  argv_msg (D_ROUTE, &argv);
1021
+  openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route delete command failed");
1020 1022
 
1021 1023
 #elif defined(TARGET_DRAGONFLY)
1022 1024
 
1023
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
1025
+  argv_printf (&argv, "%s delete -net %s %s %s",
1026
+		ROUTE_PATH,
1024 1027
 	      network,
1025 1028
 	      gateway,
1026 1029
 	      netmask);
1027 1030
 
1028
-  msg (D_ROUTE, "%s", BSTR (&buf));
1029
-  system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route delete command failed");
1031
+  argv_msg (D_ROUTE, &argv);
1032
+  openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route delete command failed");
1030 1033
 
1031 1034
 #elif defined(TARGET_DARWIN)
1032 1035
 
1033
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
1036
+  argv_printf (&argv, "%s delete -net %s %s %s",
1037
+		ROUTE_PATH,
1034 1038
               network,
1035 1039
               gateway,
1036 1040
               netmask);
1037 1041
 
1038
-  msg (D_ROUTE, "%s", BSTR (&buf));
1039
-  system_check (BSTR (&buf), es, 0, "ERROR: OS X route delete command failed");
1042
+  argv_msg (D_ROUTE, &argv);
1043
+  openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");
1040 1044
 
1041 1045
 #elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
1042 1046
 
1043
-  buf_printf (&buf, ROUTE_PATH " delete -net %s %s -netmask %s",
1047
+  argv_printf (&argv, "%s delete -net %s %s -netmask %s",
1048
+		ROUTE_PATH,
1044 1049
 	      network,
1045 1050
 	      gateway,
1046 1051
 	      netmask);
1047 1052
 
1048
-  msg (D_ROUTE, "%s", BSTR (&buf));
1049
-  system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
1053
+  argv_msg (D_ROUTE, &argv);
1054
+  openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
1050 1055
 
1051 1056
 #else
1052 1057
   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");
1053 1058
 #endif
1054 1059
 
1060
+  argv_reset (&argv);
1055 1061
   gc_free (&gc);
1056 1062
 }
1057 1063
 
... ...
@@ -1480,6 +1480,22 @@ setenv_trusted (struct env_set *es, const struct link_socket_info *info)
1480 1480
   setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT);
1481 1481
 }
1482 1482
 
1483
+static void
1484
+ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc)
1485
+{
1486
+  const char *ip = print_sockaddr_ex (&info->lsa->actual.dest, NULL, 0, gc);
1487
+  const char *port = print_sockaddr_ex (&info->lsa->actual.dest, NULL, PS_DONT_SHOW_ADDR|PS_SHOW_PORT, gc);
1488
+  if (include_cmd)
1489
+    argv_printf (argv, "%s %s %s",
1490
+		 info->ipchange_command,
1491
+		 ip,
1492
+		 port);
1493
+  else
1494
+    argv_printf (argv, "%s %s",
1495
+		 ip,
1496
+		 port);
1497
+}
1498
+
1483 1499
 void
1484 1500
 link_socket_connection_initiated (const struct buffer *buf,
1485 1501
 				  struct link_socket_info *info,
... ...
@@ -1508,20 +1524,21 @@ link_socket_connection_initiated (const struct buffer *buf,
1508 1508
   /* Process --ipchange plugin */
1509 1509
   if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE))
1510 1510
     {
1511
-      const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc);
1512
-      if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
1511
+      struct argv argv = argv_new ();
1512
+      ipchange_fmt (false, &argv, info, &gc);
1513
+      if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
1513 1514
 	msg (M_WARN, "WARNING: ipchange plugin call failed");
1515
+      argv_reset (&argv);
1514 1516
     }
1515 1517
 
1516 1518
   /* Process --ipchange option */
1517 1519
   if (info->ipchange_command)
1518 1520
     {
1519
-      struct buffer out = alloc_buf_gc (256, &gc);
1521
+      struct argv argv = argv_new ();
1520 1522
       setenv_str (es, "script_type", "ipchange");
1521
-      buf_printf (&out, "%s %s",
1522
-		  info->ipchange_command,
1523
-		  print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc));
1524
-      system_check (BSTR (&out), es, S_SCRIPT, "ip-change command failed");
1523
+      ipchange_fmt (true, &argv, info, &gc);
1524
+      openvpn_execve_check (&argv, es, S_SCRIPT, "ip-change command failed");
1525
+      argv_reset (&argv);
1525 1526
     }
1526 1527
 
1527 1528
   gc_free (&gc);
... ...
@@ -1791,7 +1808,8 @@ print_sockaddr_ex (const struct openvpn_sockaddr *addr,
1791 1791
       const int port = ntohs (addr->sa.sin_port);
1792 1792
 
1793 1793
       mutex_lock_static (L_INET_NTOA);
1794
-      buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
1794
+      if (!(flags & PS_DONT_SHOW_ADDR))
1795
+	buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
1795 1796
       mutex_unlock_static (L_INET_NTOA);
1796 1797
 
1797 1798
       if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
... ...
@@ -325,6 +325,7 @@ void link_socket_close (struct link_socket *sock);
325 325
 #define PS_SHOW_PORT_IF_DEFINED (1<<0)
326 326
 #define PS_SHOW_PORT            (1<<1)
327 327
 #define PS_SHOW_PKTINFO         (1<<2)
328
+#define PS_DONT_SHOW_ADDR       (1<<3)
328 329
 
329 330
 const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr,
330 331
 			       const char* separator,
... ...
@@ -544,6 +544,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
544 544
   struct tls_session *session;
545 545
   const struct tls_options *opt;
546 546
   const int max_depth = 8;
547
+  struct argv argv = argv_new ();
547 548
 
548 549
   /* get the tls_session pointer */
549 550
   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)
689 689
   /* call --tls-verify plug-in(s) */
690 690
   if (plugin_defined (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY))
691 691
     {
692
-      char command[256];
693
-      struct buffer out;
694 692
       int ret;
695 693
 
696
-      buf_set_write (&out, (uint8_t*)command, sizeof (command));
697
-      buf_printf (&out, "%d %s",
698
-		  ctx->error_depth,
699
-		  subject);
694
+      argv_printf (&argv, "%d %s",
695
+		   ctx->error_depth,
696
+		   subject);
700 697
 
701
-      ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, NULL, opt->es);
698
+      ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es);
702 699
 
703 700
       if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
704 701
 	{
... ...
@@ -716,19 +714,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
716 716
   /* run --tls-verify script */
717 717
   if (opt->verify_command)
718 718
     {
719
-      char command[256];
720
-      struct buffer out;
721 719
       int ret;
722 720
 
723 721
       setenv_str (opt->es, "script_type", "tls-verify");
724 722
 
725
-      buf_set_write (&out, (uint8_t*)command, sizeof (command));
726
-      buf_printf (&out, "%s %d %s",
727
-		  opt->verify_command,
728
-		  ctx->error_depth,
729
-		  subject);
730
-      dmsg (D_TLS_DEBUG, "TLS: executing verify command: %s", command);
731
-      ret = openvpn_system (command, opt->es, S_SCRIPT);
723
+      argv_printf (&argv, "%s %d %s",
724
+		   opt->verify_command,
725
+		   ctx->error_depth,
726
+		   subject);
727
+      argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
728
+      ret = openvpn_execve (&argv, opt->es, S_SCRIPT);
732 729
 
733 730
       if (system_ok (ret))
734 731
 	{
... ...
@@ -738,7 +733,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
738 738
       else
739 739
 	{
740 740
 	  if (!system_executed (ret))
741
-	    msg (M_ERR, "Verify command failed to execute: %s", command);
741
+	    argv_msg_prefix (M_ERR, &argv, "Verify command failed to execute");
742 742
 	  msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
743 743
 	       ctx->error_depth, subject);
744 744
 	  goto err;		/* Reject connection */
... ...
@@ -801,11 +796,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
801 801
 
802 802
   session->verified = true;
803 803
   free (subject);
804
+  argv_reset (&argv);
804 805
   return 1;			/* Accept connection */
805 806
 
806 807
  err:
807 808
   ERR_clear_error ();
808 809
   free (subject);
810
+  argv_reset (&argv);
809 811
   return 0;                     /* Reject connection */
810 812
 }
811 813
 
... ...
@@ -2901,7 +2898,7 @@ static bool
2901 2901
 verify_user_pass_script (struct tls_session *session, const struct user_pass *up)
2902 2902
 {
2903 2903
   struct gc_arena gc = gc_new ();
2904
-  struct buffer cmd = alloc_buf_gc (256, &gc);
2904
+  struct argv argv = argv_new ();
2905 2905
   const char *tmp_file = "";
2906 2906
   int retval;
2907 2907
   bool ret = false;
... ...
@@ -2940,16 +2937,16 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
2940 2940
       setenv_untrusted (session);
2941 2941
 
2942 2942
       /* format command line */
2943
-      buf_printf (&cmd, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
2943
+      argv_printf (&argv, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
2944 2944
       
2945 2945
       /* call command */
2946
-      retval = openvpn_system (BSTR (&cmd), session->opt->es, S_SCRIPT);
2946
+      retval = openvpn_execve (&argv, session->opt->es, S_SCRIPT);
2947 2947
 
2948 2948
       /* test return status of command */
2949 2949
       if (system_ok (retval))
2950 2950
 	ret = true;
2951 2951
       else if (!system_executed (retval))
2952
-	msg (D_TLS_ERRORS, "TLS Auth Error: user-pass-verify script failed to execute: %s", BSTR (&cmd));
2952
+	argv_msg_prefix (D_TLS_ERRORS, &argv, "TLS Auth Error: user-pass-verify script failed to execute");
2953 2953
 	  
2954 2954
       if (!session->opt->auth_user_pass_verify_script_via_file)
2955 2955
 	setenv_del (session->opt->es, "password");
... ...
@@ -2963,6 +2960,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
2963 2963
   if (strlen (tmp_file) > 0)
2964 2964
     delete_file (tmp_file);
2965 2965
 
2966
+  argv_reset (&argv);
2966 2967
   gc_free (&gc);
2967 2968
   return ret;
2968 2969
 }
... ...
@@ -448,6 +448,14 @@ socket_defined (const socket_descriptor_t sd)
448 448
 #define USE_64_BIT_COUNTERS
449 449
 
450 450
 /*
451
+ * Should we enable the use of execve() for calling subprocesses,
452
+ * instead of system()?
453
+ */
454
+#if defined(HAVE_EXECVE) && defined(HAVE_FORK)
455
+#define ENABLE_EXECVE
456
+#endif
457
+
458
+/*
451 459
  * Do we have point-to-multipoint capability?
452 460
  */
453 461
 
... ...
@@ -39,6 +39,7 @@
39 39
 #include "socket.h"
40 40
 #include "manage.h"
41 41
 #include "route.h"
42
+#include "win32.h"
42 43
 
43 44
 #include "memdbg.h"
44 45
 
... ...
@@ -534,7 +535,9 @@ do_ifconfig (struct tuntap *tt,
534 534
       const char *ifconfig_local = NULL;
535 535
       const char *ifconfig_remote_netmask = NULL;
536 536
       const char *ifconfig_broadcast = NULL;
537
-      char command_line[256];
537
+      struct argv argv;
538
+
539
+      argv_init (&argv);
538 540
 
539 541
       /*
540 542
        * We only handle TUN/TAP devices here, not --dev null devices.
... ...
@@ -570,31 +573,31 @@ do_ifconfig (struct tuntap *tt,
570 570
 	/*
571 571
 	 * Set the MTU for the device
572 572
 	 */
573
-	openvpn_snprintf (command_line, sizeof (command_line),
573
+	argv_printf (&argv,
574 574
 			  "%s link set dev %s up mtu %d",
575 575
 			  iproute_path,
576 576
 			  actual,
577 577
 			  tun_mtu
578 578
 			  );
579
-	  msg (M_INFO, "%s", command_line);
580
-	  system_check (command_line, es, S_FATAL, "Linux ip link set failed");
579
+	  argv_msg (M_INFO, &argv);
580
+	  openvpn_execve_check (&argv, es, S_FATAL, "Linux ip link set failed");
581 581
 
582 582
 	if (tun) {
583 583
 
584 584
 		/*
585 585
 		 * Set the address for the device
586 586
 		 */
587
-		openvpn_snprintf (command_line, sizeof (command_line),
587
+		argv_printf (&argv,
588 588
 				  "%s addr add dev %s local %s peer %s",
589 589
 				  iproute_path,
590 590
 				  actual,
591 591
 				  ifconfig_local,
592 592
 				  ifconfig_remote_netmask
593 593
 				  );
594
-		  msg (M_INFO, "%s", command_line);
595
-		  system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
594
+		  argv_msg (M_INFO, &argv);
595
+		  openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
596 596
 	} else {
597
-		openvpn_snprintf (command_line, sizeof (command_line),
597
+		argv_printf (&argv,
598 598
 				  "%s addr add dev %s %s/%d broadcast %s",
599 599
 				  iproute_path,
600 600
 				  actual,
... ...
@@ -602,30 +605,32 @@ do_ifconfig (struct tuntap *tt,
602 602
 				  count_netmask_bits(ifconfig_remote_netmask),
603 603
 				  ifconfig_broadcast
604 604
 				  );
605
-		  msg (M_INFO, "%s", command_line);
606
-		  system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
605
+		  argv_msg (M_INFO, &argv);
606
+		  openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
607 607
 	}
608 608
 	tt->did_ifconfig = true;
609 609
 #else
610 610
       if (tun)
611
-	openvpn_snprintf (command_line, sizeof (command_line),
612
-			  IFCONFIG_PATH " %s %s pointopoint %s mtu %d",
611
+	argv_printf (&argv,
612
+			  "%s %s %s pointopoint %s mtu %d",
613
+			  IFCONFIG_PATH,
613 614
 			  actual,
614 615
 			  ifconfig_local,
615 616
 			  ifconfig_remote_netmask,
616 617
 			  tun_mtu
617 618
 			  );
618 619
       else
619
-	openvpn_snprintf (command_line, sizeof (command_line),
620
-			  IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
620
+	argv_printf (&argv,
621
+			  "%s %s %s netmask %s mtu %d broadcast %s",
622
+			  IFCONFIG_PATH,
621 623
 			  actual,
622 624
 			  ifconfig_local,
623 625
 			  ifconfig_remote_netmask,
624 626
 			  tun_mtu,
625 627
 			  ifconfig_broadcast
626 628
 			  );
627
-      msg (M_INFO, "%s", command_line);
628
-      system_check (command_line, es, S_FATAL, "Linux ifconfig failed");
629
+      argv_msg (M_INFO, &argv);
630
+      openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
629 631
       tt->did_ifconfig = true;
630 632
 
631 633
 #endif /*CONFIG_FEATURE_IPROUTE*/
... ...
@@ -638,28 +643,30 @@ do_ifconfig (struct tuntap *tt,
638 638
        */
639 639
       if (tun)
640 640
 	{
641
-	  openvpn_snprintf (command_line, sizeof (command_line),
642
-			    IFCONFIG_PATH " %s %s %s mtu %d up",
641
+	  argv_printf (&argv,
642
+			    "%s %s %s %s mtu %d up",
643
+			    IFCONFIG_PATH,
643 644
 			    actual,
644 645
 			    ifconfig_local,
645 646
 			    ifconfig_remote_netmask,
646 647
 			    tun_mtu
647 648
 			    );
648 649
 
649
-	  msg (M_INFO, "%s", command_line);
650
-	  if (!system_check (command_line, es, 0, "Solaris ifconfig phase-1 failed"))
650
+	  argv_msg (M_INFO, &argv);
651
+	  if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
651 652
 	    solaris_error_close (tt, es, actual);
652 653
 
653
-	  openvpn_snprintf (command_line, sizeof (command_line),
654
-			    IFCONFIG_PATH " %s netmask 255.255.255.255",
654
+	  argv_printf (&argv,
655
+			    "%s %s netmask 255.255.255.255",
656
+			    IFCONFIG_PATH,
655 657
 			    actual
656 658
 			    );
657 659
 	}
658 660
       else
659 661
 	no_tap_ifconfig ();
660 662
 
661
-      msg (M_INFO, "%s", command_line);
662
-      if (!system_check (command_line, es, 0, "Solaris ifconfig phase-2 failed"))
663
+      argv_msg (M_INFO, &argv);
664
+      if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
663 665
 	solaris_error_close (tt, es, actual);
664 666
 
665 667
       tt->did_ifconfig = true;
... ...
@@ -672,45 +679,50 @@ do_ifconfig (struct tuntap *tt,
672 672
        * (if it exists), and re-ifconfig.  Let me know if you know a better way.
673 673
        */
674 674
 
675
-      openvpn_snprintf (command_line, sizeof (command_line),
676
-			IFCONFIG_PATH " %s destroy",
675
+      argv_printf (&argv,
676
+			"%s %s destroy",
677
+			IFCONFIG_PATH,
677 678
 			actual);
678
-      msg (M_INFO, "%s", command_line);
679
-      system_check (command_line, es, 0, NULL);
680
-      openvpn_snprintf (command_line, sizeof (command_line),
681
-			IFCONFIG_PATH " %s create",
679
+      argv_msg (M_INFO, &argv);
680
+      openvpn_execve_check (&argv, es, 0, NULL);
681
+      argv_printf (&argv,
682
+			"%s %s create",
683
+			IFCONFIG_PATH,
682 684
 			actual);
683
-      msg (M_INFO, "%s", command_line);
684
-      system_check (command_line, es, 0, NULL);
685
+      argv_msg (M_INFO, &argv);
686
+      openvpn_execve_check (&argv, es, 0, NULL);
685 687
       msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
686 688
 
687 689
       /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
688 690
       if (tun)
689
-	openvpn_snprintf (command_line, sizeof (command_line),
690
-			  IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
691
+	argv_printf (&argv,
692
+			  "%s %s %s %s mtu %d netmask 255.255.255.255 up",
693
+			  IFCONFIG_PATH,
691 694
 			  actual,
692 695
 			  ifconfig_local,
693 696
 			  ifconfig_remote_netmask,
694 697
 			  tun_mtu
695 698
 			  );
696 699
       else
697
-	openvpn_snprintf (command_line, sizeof (command_line),
698
-			  IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s link0",
700
+	argv_printf (&argv,
701
+			  "%s %s %s netmask %s mtu %d broadcast %s link0",
702
+			  IFCONFIG_PATH,
699 703
 			  actual,
700 704
 			  ifconfig_local,
701 705
 			  ifconfig_remote_netmask,
702 706
 			  tun_mtu,
703 707
 			  ifconfig_broadcast
704 708
 			  );
705
-      msg (M_INFO, "%s", command_line);
706
-      system_check (command_line, es, S_FATAL, "OpenBSD ifconfig failed");
709
+      argv_msg (M_INFO, &argv);
710
+      openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
707 711
       tt->did_ifconfig = true;
708 712
 
709 713
 #elif defined(TARGET_NETBSD)
710 714
 
711 715
       if (tun)
712
-	openvpn_snprintf (command_line, sizeof (command_line),
713
-			  IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
716
+	argv_printf (&argv,
717
+			  "%s %s %s %s mtu %d netmask 255.255.255.255 up",
718
+			  IFCONFIG_PATH,
714 719
 			  actual,
715 720
 			  ifconfig_local,
716 721
 			  ifconfig_remote_netmask,
... ...
@@ -722,16 +734,17 @@ do_ifconfig (struct tuntap *tt,
722 722
        * so we don't need the "link0" extra parameter to specify we want to do 
723 723
        * tunneling at the ethernet level
724 724
        */
725
-		openvpn_snprintf (command_line, sizeof (command_line),
726
-			  IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
725
+		argv_printf (&argv,
726
+			  "%s %s %s netmask %s mtu %d broadcast %s",
727
+			  IFCONFIG_PATH,
727 728
 			  actual,
728 729
 			  ifconfig_local,
729 730
 			  ifconfig_remote_netmask,
730 731
 			  tun_mtu,
731 732
 			  ifconfig_broadcast
732 733
 			  );
733
-      msg (M_INFO, "%s", command_line);
734
-      system_check (command_line, es, S_FATAL, "NetBSD ifconfig failed");
734
+      argv_msg (M_INFO, &argv);
735
+      openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
735 736
       tt->did_ifconfig = true;
736 737
 
737 738
 #elif defined(TARGET_DARWIN)
... ...
@@ -740,18 +753,20 @@ do_ifconfig (struct tuntap *tt,
740 740
        * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
741 741
        */
742 742
 
743
-      openvpn_snprintf (command_line, sizeof (command_line),
744
-			IFCONFIG_PATH " %s delete",
743
+      argv_printf (&argv,
744
+			"%s %s delete",
745
+			IFCONFIG_PATH,
745 746
 			actual);
746
-      msg (M_INFO, "%s", command_line);
747
-      system_check (command_line, es, 0, NULL);
747
+      argv_msg (M_INFO, &argv);
748
+      openvpn_execve_check (&argv, es, 0, NULL);
748 749
       msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
749 750
 
750 751
 
751 752
       /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
752 753
       if (tun)
753
-	openvpn_snprintf (command_line, sizeof (command_line),
754
-			  IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
754
+	argv_printf (&argv,
755
+			  "%s %s %s %s mtu %d netmask 255.255.255.255 up",
756
+			  IFCONFIG_PATH,
755 757
 			  actual,
756 758
 			  ifconfig_local,
757 759
 			  ifconfig_remote_netmask,
... ...
@@ -760,8 +775,9 @@ do_ifconfig (struct tuntap *tt,
760 760
       else
761 761
         {
762 762
           if (tt->topology == TOP_SUBNET)
763
-    	    openvpn_snprintf (command_line, sizeof (command_line),
764
-			      IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
763
+    	    argv_printf (&argv,
764
+			      "%s %s %s %s netmask %s mtu %d up",
765
+			      IFCONFIG_PATH,
765 766
 			      actual,
766 767
 			      ifconfig_local,
767 768
 			      ifconfig_local,
... ...
@@ -769,16 +785,17 @@ do_ifconfig (struct tuntap *tt,
769 769
 			      tun_mtu
770 770
 			      );
771 771
 	  else
772
-    	    openvpn_snprintf (command_line, sizeof (command_line),
773
-			      IFCONFIG_PATH " %s %s netmask %s mtu %d up",
772
+    	    argv_printf (&argv,
773
+			      "%s %s %s netmask %s mtu %d up",
774
+			      IFCONFIG_PATH,
774 775
 			      actual,
775 776
 			      ifconfig_local,
776 777
 			      ifconfig_remote_netmask,
777 778
 			      tun_mtu
778 779
 			      );
779 780
 	}
780
-      msg (M_INFO, "%s", command_line);
781
-      system_check (command_line, es, S_FATAL, "Mac OS X ifconfig failed");
781
+      argv_msg (M_INFO, &argv);
782
+      openvpn_execve_check (&argv, es, S_FATAL, "Mac OS X ifconfig failed");
782 783
       tt->did_ifconfig = true;
783 784
 
784 785
       /* Add a network route for the local tun interface */
... ...
@@ -797,8 +814,9 @@ do_ifconfig (struct tuntap *tt,
797 797
 
798 798
       /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
799 799
       if (tun)
800
-	openvpn_snprintf (command_line, sizeof (command_line),
801
-			  IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
800
+	argv_printf (&argv,
801
+			  "%s %s %s %s mtu %d netmask 255.255.255.255 up",
802
+			  IFCONFIG_PATH,
802 803
 			  actual,
803 804
 			  ifconfig_local,
804 805
 			  ifconfig_remote_netmask,
... ...
@@ -806,8 +824,9 @@ do_ifconfig (struct tuntap *tt,
806 806
 			  );
807 807
       else {
808 808
 	if (tt->topology == TOP_SUBNET)
809
-            openvpn_snprintf (command_line, sizeof (command_line),
810
-                              IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
809
+            argv_printf (&argv,
810
+                              "%s %s %s %s netmask %s mtu %d up",
811
+                              IFCONFIG_PATH,
811 812
                               actual,
812 813
                               ifconfig_local,
813 814
                               ifconfig_local,
... ...
@@ -815,8 +834,9 @@ do_ifconfig (struct tuntap *tt,
815 815
                               tun_mtu
816 816
                               );
817 817
 	else
818
-  	    openvpn_snprintf (command_line, sizeof (command_line),
819
-			  IFCONFIG_PATH " %s %s netmask %s mtu %d up",
818
+  	    argv_printf (&argv,
819
+			  "%s %s %s netmask %s mtu %d up",
820
+			  IFCONFIG_PATH,
820 821
 			  actual,
821 822
 			  ifconfig_local,
822 823
 			  ifconfig_remote_netmask,
... ...
@@ -824,8 +844,8 @@ do_ifconfig (struct tuntap *tt,
824 824
 			  );
825 825
       }
826 826
 	
827
-      msg (M_INFO, "%s", command_line);
828
-      system_check (command_line, es, S_FATAL, "FreeBSD ifconfig failed");
827
+      argv_msg (M_INFO, &argv);
828
+      openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig failed");
829 829
       tt->did_ifconfig = true;
830 830
 
831 831
 	/* Add a network route for the local tun interface */
... ...
@@ -882,6 +902,7 @@ do_ifconfig (struct tuntap *tt,
882 882
 #else
883 883
       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.");
884 884
 #endif
885
+      argv_reset (&argv);
885 886
     }
886 887
   gc_free (&gc);
887 888
 }
... ...
@@ -1216,13 +1237,14 @@ close_tun (struct tuntap *tt)
1216 1216
     {
1217 1217
 	if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
1218 1218
 	  {
1219
-	    char command_line[256];
1219
+	    struct argv argv;
1220 1220
 	    struct gc_arena gc = gc_new ();
1221
+	    argv_init (&argv);
1221 1222
 
1222 1223
 #ifdef CONFIG_FEATURE_IPROUTE
1223 1224
 	    if (is_tun_p2p (tt))
1224 1225
 	      {
1225
-		openvpn_snprintf (command_line, sizeof (command_line),
1226
+		argv_printf (&argv,
1226 1227
 			"%s addr del dev %s local %s peer %s",
1227 1228
 			iproute_path,
1228 1229
 			tt->actual_name,
... ...
@@ -1232,7 +1254,7 @@ close_tun (struct tuntap *tt)
1232 1232
 	      }
1233 1233
 	    else
1234 1234
 	      {
1235
-		openvpn_snprintf (command_line, sizeof (command_line),
1235
+		argv_printf (&argv,
1236 1236
 			"%s addr del dev %s %s/%d",
1237 1237
 			iproute_path,
1238 1238
 			tt->actual_name,
... ...
@@ -1241,15 +1263,17 @@ close_tun (struct tuntap *tt)
1241 1241
 			);
1242 1242
 	      }
1243 1243
 #else
1244
-	    openvpn_snprintf (command_line, sizeof (command_line),
1245
-			IFCONFIG_PATH " %s 0.0.0.0",
1244
+	    argv_printf (&argv,
1245
+			"%s %s 0.0.0.0",
1246
+			IFCONFIG_PATH,
1246 1247
 			tt->actual_name
1247 1248
 			);
1248 1249
 #endif
1249 1250
 
1250
-	    msg (M_INFO, "%s", command_line);
1251
-	    system_check (command_line, NULL, 0, "Linux ip addr del failed");
1251
+	    argv_msg (M_INFO, &argv);
1252
+	    openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed");
1252 1253
 
1254
+	    argv_reset (&argv);
1253 1255
 	    gc_free (&gc);
1254 1256
 	  }
1255 1257
       close_tun_generic (tt);
... ...
@@ -1471,16 +1495,19 @@ close_tun (struct tuntap *tt)
1471 1471
 static void
1472 1472
 solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
1473 1473
 {
1474
-  char command_line[256];
1474
+  struct argv argv;
1475
+  argv_init (&argv);
1475 1476
 
1476
-  openvpn_snprintf (command_line, sizeof (command_line),
1477
-		    IFCONFIG_PATH " %s unplumb",
1477
+  argv_printf (&argv,
1478
+		    "%s %s unplumb",
1479
+		    IFCONFIG_PATH,
1478 1480
 		    actual);
1479 1481
 
1480
-  msg (M_INFO, "%s", command_line);
1481
-  system_check (command_line, es, 0, "Solaris ifconfig unplumb failed");
1482
+  argv_msg (M_INFO, &argv);
1483
+  openvpn_execve_check (&argv, es, 0, "Solaris ifconfig unplumb failed");
1482 1484
   close_tun (tt);
1483 1485
   msg (M_FATAL, "Solaris ifconfig failed");
1486
+  argv_reset (&argv);
1484 1487
 }
1485 1488
 
1486 1489
 int
... ...
@@ -3275,7 +3302,7 @@ dhcp_renew (const struct tuntap *tt)
3275 3275
  */
3276 3276
 
3277 3277
 static void
3278
-netsh_command (const char *cmd, int n)
3278
+netsh_command (const struct argv *a, int n)
3279 3279
 {
3280 3280
   int i;
3281 3281
   for (i = 0; i < n; ++i)
... ...
@@ -3283,8 +3310,8 @@ netsh_command (const char *cmd, int n)
3283 3283
       bool status;
3284 3284
       openvpn_sleep (1);
3285 3285
       netcmd_semaphore_lock ();
3286
-      msg (M_INFO, "NETSH: %s", cmd);
3287
-      status = system_check (cmd, NULL, 0, "ERROR: netsh command failed");
3286
+      argv_msg_prefix (M_INFO, a, "NETSH");
3287
+      status = openvpn_execve_check (a, NULL, 0, "ERROR: netsh command failed");
3288 3288
       netcmd_semaphore_release ();
3289 3289
       if (status)
3290 3290
 	return;
... ...
@@ -3376,7 +3403,7 @@ netsh_ifconfig_options (const char *type,
3376 3376
 			const bool test_first)
3377 3377
 {
3378 3378
   struct gc_arena gc = gc_new ();
3379
-  struct buffer out = alloc_buf_gc (256, &gc);
3379
+  struct argv argv = argv_new ();
3380 3380
   bool delete_first = false;
3381 3381
 
3382 3382
   /* first check if we should delete existing DNS/WINS settings from TAP interface */
... ...
@@ -3391,11 +3418,12 @@ netsh_ifconfig_options (const char *type,
3391 3391
   /* delete existing DNS/WINS settings from TAP interface */
3392 3392
   if (delete_first)
3393 3393
     {
3394
-      buf_init (&out, 0);
3395
-      buf_printf (&out, "netsh interface ip delete %s \"%s\" all",
3396
-		  type,
3397
-		  flex_name);
3398
-      netsh_command (BSTR(&out), 2);
3394
+      argv_printf (&argv, "%s%s interface ip delete %s %s all",
3395
+		   get_win_sys_path(),
3396
+		   NETSH_PATH_SUFFIX,
3397
+		   type,
3398
+		   flex_name);
3399
+      netsh_command (&argv, 2);
3399 3400
     }
3400 3401
 
3401 3402
   /* add new DNS/WINS settings to TAP interface */
... ...
@@ -3407,15 +3435,16 @@ netsh_ifconfig_options (const char *type,
3407 3407
 	if (delete_first || !test_first || !ip_addr_member_of (addr_list[i], current))
3408 3408
 	  {
3409 3409
 	    const char *fmt = count ?
3410
-	        "netsh interface ip add %s \"%s\" %s"
3411
-	      : "netsh interface ip set %s \"%s\" static %s";
3412
-
3413
-	    buf_init (&out, 0);
3414
-	    buf_printf (&out, fmt,
3415
-			type,
3416
-			flex_name,
3417
-			print_in_addr_t (addr_list[i], 0, &gc));
3418
-	    netsh_command (BSTR(&out), 2);
3410
+	        "%s%s interface ip add %s %s %s"
3411
+	      : "%s%s interface ip set %s %s static %s";
3412
+
3413
+	    argv_printf (&argv, fmt,
3414
+			 get_win_sys_path(),
3415
+			 NETSH_PATH_SUFFIX,
3416
+			 type,
3417
+			 flex_name,
3418
+			 print_in_addr_t (addr_list[i], 0, &gc));
3419
+	    netsh_command (&argv, 2);
3419 3420
 	  
3420 3421
 	    ++count;
3421 3422
 	  }
... ...
@@ -3429,6 +3458,7 @@ netsh_ifconfig_options (const char *type,
3429 3429
       }
3430 3430
   }
3431 3431
 
3432
+  argv_reset (&argv);
3432 3433
   gc_free (&gc);
3433 3434
 }
3434 3435
 
... ...
@@ -3458,7 +3488,7 @@ netsh_ifconfig (const struct tuntap_options *to,
3458 3458
 		const unsigned int flags)
3459 3459
 {
3460 3460
   struct gc_arena gc = gc_new ();
3461
-  struct buffer out = alloc_buf_gc (256, &gc);
3461
+  struct argv argv = argv_new ();
3462 3462
   const IP_ADAPTER_INFO *ai = NULL;
3463 3463
   const IP_PER_ADAPTER_INFO *pai = NULL;
3464 3464
 
... ...
@@ -3482,14 +3512,15 @@ netsh_ifconfig (const struct tuntap_options *to,
3482 3482
       else
3483 3483
 	{
3484 3484
 	  /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
3485
-	  buf_init (&out, 0);
3486
-	  buf_printf (&out,
3487
-		      "netsh interface ip set address \"%s\" static %s %s",
3488
-		      flex_name,
3489
-		      print_in_addr_t (ip, 0, &gc),
3490
-		      print_in_addr_t (netmask, 0, &gc));
3491
-
3492
-	  netsh_command (BSTR(&out), 4);
3485
+	  argv_printf (&argv,
3486
+		       "%s%s interface ip set address %s static %s %s",
3487
+		       get_win_sys_path(),
3488
+		       NETSH_PATH_SUFFIX,
3489
+		       flex_name,
3490
+		       print_in_addr_t (ip, 0, &gc),
3491
+		       print_in_addr_t (netmask, 0, &gc));
3492
+
3493
+	  netsh_command (&argv, 4);
3493 3494
 	}
3494 3495
     }
3495 3496
 
... ...
@@ -3517,6 +3548,7 @@ netsh_ifconfig (const struct tuntap_options *to,
3517 3517
 			      BOOL_CAST (flags & NI_TEST_FIRST));
3518 3518
     }
3519 3519
   
3520
+  argv_reset (&argv);
3520 3521
   gc_free (&gc);
3521 3522
 }
3522 3523
 
... ...
@@ -3524,17 +3556,19 @@ static void
3524 3524
 netsh_enable_dhcp (const struct tuntap_options *to,
3525 3525
 		   const char *actual_name)
3526 3526
 {
3527
-  struct gc_arena gc = gc_new ();
3528
-  struct buffer out = alloc_buf_gc (256, &gc);
3527
+  struct argv argv;
3528
+  argv_init (&argv);
3529 3529
 
3530 3530
   /* example: netsh interface ip set address my-tap dhcp */
3531
-  buf_printf (&out,
3532
-	      "netsh interface ip set address \"%s\" dhcp",
3533
-	      actual_name);
3531
+  argv_printf (&argv,
3532
+	      "%s%s interface ip set address %s dhcp",
3533
+	       get_win_sys_path(),
3534
+	       NETSH_PATH_SUFFIX,
3535
+	       actual_name);
3534 3536
 
3535
-  netsh_command (BSTR(&out), 4);
3537
+  netsh_command (&argv, 4);
3536 3538
 
3537
-  gc_free (&gc);
3539
+  argv_reset (&argv);
3538 3540
 }
3539 3541
 
3540 3542
 /*
... ...
@@ -35,6 +35,7 @@
35 35
 #include "mtu.h"
36 36
 #include "sig.h"
37 37
 #include "win32.h"
38
+#include "misc.h"
38 39
 
39 40
 #include "memdbg.h"
40 41
 
... ...
@@ -69,6 +70,11 @@ struct window_title window_title; /* GLOBAL*/
69 69
 
70 70
 struct semaphore netcmd_semaphore; /* GLOBAL */
71 71
 
72
+/*
73
+ * Windows system pathname such as c:\windows
74
+ */
75
+static char *win_sys_path = NULL; /* GLOBAL */
76
+
72 77
 void
73 78
 init_win32 (void)
74 79
 {
... ...
@@ -100,6 +106,7 @@ uninit_win32 (void)
100 100
   window_title_restore (&window_title);
101 101
   win32_signal_close (&win32_signal);
102 102
   WSACleanup ();
103
+  free (win_sys_path);
103 104
 }
104 105
 
105 106
 void
... ...
@@ -816,4 +823,174 @@ win_safe_filename (const char *fn)
816 816
   return true;
817 817
 }
818 818
 
819
+/*
820
+ * Service functions for openvpn_execve
821
+ */
822
+
823
+static char *
824
+env_block (const struct env_set *es)
825
+{
826
+  if (es)
827
+    {
828
+      struct env_item *e;
829
+      char *ret;
830
+      char *p;
831
+      size_t nchars = 1;
832
+      
833
+      for (e = es->list; e != NULL; e = e->next)
834
+	nchars += strlen (e->string) + 1;
835
+
836
+      ret = (char *) malloc (nchars);
837
+      check_malloc_return (ret);
838
+
839
+      p = ret;
840
+      for (e = es->list; e != NULL; e = e->next)
841
+	{
842
+	  if (env_allowed (e->string))
843
+	    {
844
+	      strcpy (p, e->string);
845
+	      p += strlen (e->string) + 1;
846
+	    }
847
+	}
848
+      *p = '\0';
849
+      return ret;
850
+    }
851
+  else
852
+    return NULL;
853
+}
854
+
855
+static char *
856
+cmd_line (const struct argv *a)
857
+{
858
+  size_t nchars = 1;
859
+  size_t maxlen = 0;
860
+  size_t i;
861
+  struct buffer buf;
862
+  char *work = NULL;
863
+
864
+  if (!a)
865
+    return NULL;
866
+
867
+  for (i = 0; i < a->argc; ++i)
868
+    {
869
+      const char *arg = a->argv[i];
870
+      const size_t len = strlen (arg);
871
+      nchars += len + 3;
872
+      if (len > maxlen)
873
+	maxlen = len;
874
+    }
875
+
876
+  work = (char *) malloc (maxlen + 1);
877
+  check_malloc_return (work);
878
+  buf = alloc_buf (nchars);
879
+
880
+  for (i = 0; i < a->argc; ++i)
881
+    {
882
+      const char *arg = a->argv[i];
883
+      strcpy (work, arg);
884
+      string_mod (work, CC_PRINT, CC_DOUBLE_QUOTE|CC_CRLF, '_');
885
+      if (i)
886
+	buf_printf (&buf, " ");
887
+      if (string_class (work, CC_ANY, CC_SPACE))
888
+	buf_printf (&buf, "%s", work);
889
+      else
890
+	buf_printf (&buf, "\"%s\"", work);
891
+    }
892
+
893
+  free (work);
894
+  return BSTR(&buf);
895
+}
896
+
897
+/*
898
+ * Attempt to simulate fork/execve on Windows
899
+ */
900
+int
901
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
902
+{
903
+  int ret = -1;
904
+  if (a && a->argv[0])
905
+    {
906
+      if (openvpn_execve_allowed (flags))
907
+	{
908
+	  STARTUPINFO start_info;
909
+	  PROCESS_INFORMATION proc_info;
910
+
911
+	  char *env = env_block (es);
912
+	  char *cl = cmd_line (a);
913
+	  char *cmd = a->argv[0];
914
+
915
+	  CLEAR (start_info);
916
+	  CLEAR (proc_info);
917
+
918
+	  /* fill in STARTUPINFO struct */
919
+	  GetStartupInfo(&start_info);
920
+	  start_info.cb = sizeof(start_info);
921
+	  start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
922
+	  start_info.wShowWindow = SW_HIDE;
923
+	  start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
924
+	  start_info.hStdOutput = start_info.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
925
+
926
+	  if (CreateProcess (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info))
927
+	    {
928
+	      DWORD exit_status = 0;
929
+	      CloseHandle (proc_info.hThread);
930
+	      WaitForSingleObject (proc_info.hProcess, INFINITE);
931
+	      if (GetExitCodeProcess (proc_info.hProcess, &exit_status))
932
+		ret = (int)exit_status;
933
+	      else
934
+		msg (M_WARN|M_ERRNO, "openvpn_execve: GetExitCodeProcess %s failed", cmd);
935
+	      CloseHandle (proc_info.hProcess);
936
+	    }
937
+	  else
938
+	    {
939
+	      msg (M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %s failed", cmd);
940
+	    }
941
+	  free (cl);
942
+	  free (env);
943
+	}
944
+      else
945
+	{
946
+	  msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
947
+	}
948
+    }
949
+  else
950
+    {
951
+      msg (M_WARN, "openvpn_execve: called with empty argv");
952
+    }
953
+  return ret;
954
+}
955
+
956
+char *
957
+get_win_sys_path (void)
958
+{
959
+  ASSERT (win_sys_path);
960
+  return win_sys_path;
961
+}
962
+
963
+void
964
+set_win_sys_path (const char *newpath, struct env_set *es)
965
+{
966
+  free (win_sys_path);
967
+  win_sys_path = string_alloc (newpath, NULL);
968
+  setenv_str (es, SYS_PATH_ENV_VAR_NAME, win_sys_path); /* route.exe needs this */
969
+}
970
+
971
+void
972
+set_win_sys_path_via_env (struct env_set *es)
973
+{
974
+  char buf[256];
975
+  DWORD status = GetEnvironmentVariable (SYS_PATH_ENV_VAR_NAME, buf, sizeof(buf));
976
+  if (!status)
977
+    msg (M_ERR, "Cannot find environmental variable %s", SYS_PATH_ENV_VAR_NAME);
978
+  if (status > sizeof (buf) - 1)
979
+    msg (M_FATAL, "String overflow attempting to read environmental variable %s", SYS_PATH_ENV_VAR_NAME);
980
+  set_win_sys_path (buf, es);
981
+}
982
+
983
+void
984
+env_set_add_win32 (struct env_set *es)
985
+{
986
+  set_win_sys_path (DEFAULT_WIN_SYS_PATH, es);
987
+}
988
+
819 989
 #endif
... ...
@@ -28,6 +28,12 @@
28 28
 
29 29
 #include "mtu.h"
30 30
 
31
+/* location of executables */
32
+#define SYS_PATH_ENV_VAR_NAME "SystemRoot"  /* environmental variable name that normally contains the system path */
33
+#define DEFAULT_WIN_SYS_PATH  "C:\\WINDOWS" /* --win-sys default value */
34
+#define NETSH_PATH_SUFFIX     "\\system32\\netsh.exe"
35
+#define WIN_ROUTE_PATH_SUFFIX "\\system32\\route.exe"
36
+
31 37
 /*
32 38
  * Win32-specific OpenVPN code, targetted at the mingw
33 39
  * development environment.
... ...
@@ -250,5 +256,14 @@ bool init_security_attributes_allow_all (struct security_attributes *obj);
250 250
 /* return true if filename is safe to be used on Windows */
251 251
 bool win_safe_filename (const char *fn);
252 252
 
253
+/* add constant environmental variables needed by Windows */
254
+struct env_set;
255
+void env_set_add_win32 (struct env_set *es);
256
+
257
+/* get and set the current windows system path */
258
+void set_win_sys_path (const char *newpath, struct env_set *es);
259
+void set_win_sys_path_via_env (struct env_set *es);
260
+char *get_win_sys_path (void);
261
+
253 262
 #endif
254 263
 #endif