a->argv = NULL;
}
+struct argv
+argv_new (void)
+{
+ struct argv ret;
+ argv_init (&ret);
+ return ret;
+}
+
void
argv_reset (struct argv *a)
{
return argc;
}
+struct argv
+argv_insert_head (const struct argv *a, const char *head)
+{
+ struct argv r;
+ size_t i;
+
+ r.argc = (a ? a->argc : 0) + 1;
+ ALLOC_ARRAY_CLEAR (r.argv, char *, r.argc + 1);
+ r.argv[0] = string_alloc (head, NULL);
+ if (a)
+ {
+ for (i = 0; i < a->argc; ++i)
+ r.argv[i+1] = string_alloc (a->argv[i], NULL);
+ }
+ return r;
+}
+
char *
argv_term (const char **f)
{
return "";
}
+void
+argv_msg (const int msglev, const struct argv *a)
+{
+ struct gc_arena gc = gc_new ();
+ msg (msglev, "%s", argv_str (a, &gc, 0));
+ gc_free (&gc);
+}
+
+void
+argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+ msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
+ gc_free (&gc);
+}
+
void
argv_printf (struct argv *a, const char *format, ...)
{
{
if (!strcmp (term, "%s"))
{
- a->argv[argc++] = string_alloc (va_arg (arglist, char *), NULL);
+ char *s = va_arg (arglist, char *);
+ if (!s)
+ s = "";
+ a->argv[argc++] = string_alloc (s, NULL);
}
else if (!strcmp (term, "%d"))
{
openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
a->argv[argc++] = string_alloc (numstr, NULL);
}
+ else if (!strcmp (term, "%s/%d"))
+ {
+ char numstr[64];
+ char *s = va_arg (arglist, char *);
+
+ if (!s)
+ s = "";
+
+ openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
+
+ {
+ const size_t len = strlen(s) + strlen(numstr) + 2;
+ char *combined = (char *) malloc (len);
+ check_malloc_return (combined);
+
+ strcpy (combined, s);
+ strcat (combined, "/");
+ strcat (combined, numstr);
+ a->argv[argc++] = combined;
+ }
+ }
+ else if (!strcmp (term, "%s%s"))
+ {
+ char *s1 = va_arg (arglist, char *);
+ char *s2 = va_arg (arglist, char *);
+ char *combined;
+
+ if (!s1) s1 = "";
+ if (!s2) s2 = "";
+ combined = (char *) malloc (strlen(s1) + strlen(s2) + 1);
+ check_malloc_return (combined);
+ strcpy (combined, s1);
+ strcat (combined, s2);
+ a->argv[argc++] = combined;
+ }
else
ASSERT (0);
free (term);
ASSERT (argc == a->argc);
}
-#ifdef ARGV_TEST
-void
-argv_test (void)
-{
- struct gc_arena gc = gc_new ();
- char line[512];
- const char *s;
-
- struct argv a;
- argv_init (&a);
- argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
- s = argv_str (&a, &gc, PA_BRACKET);
- argv_reset (&a);
- printf ("%s\n", s);
-
- argv_init (&a);
- argv_printf (&a, "foo bar %d", 99);
- s = argv_str (&a, &gc, PA_BRACKET);
- argv_reset (&a);
- printf ("%s\n", s);
-
- argv_init (&a);
- s = argv_str (&a, &gc, PA_BRACKET);
- argv_reset (&a);
- printf ("%s\n", s);
-
- argv_init (&a);
- argv_printf (&a, "foo bar %d", 99);
- argv_printf_cat (&a, "bar %d foo", 42);
- argv_printf_cat (&a, "cool %s %d u", "frood", 4);
- s = argv_str (&a, &gc, PA_BRACKET);
- argv_reset (&a);
- printf ("%s\n", s);
-
- while (fgets (line, sizeof(line), stdin) != NULL)
- {
- char *term;
- const char *f = line;
- int i = 0;
-
- while ((term = argv_term (&f)) != NULL)
- {
- printf ("[%d] '%s'\n", i, term);
- ++i;
- free (term);
- }
- }
- gc_free (&gc);
-}
-#endif
-
/*
* write a string to the end of a buffer that was
* truncated by buf_printf
* to execve.
*/
void argv_init (struct argv *a);
+struct argv argv_new (void);
void argv_reset (struct argv *a);
size_t argv_argc (const char *format);
char *argv_term (const char **f);
const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
+struct argv argv_insert_head (const struct argv *a, const char *head);
+void argv_msg (const int msglev, const struct argv *a);
+void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
getpass strerror syslog openlog mlockall getgrnam setgid dnl
setgroups stat flock readv writev setsockopt getsockopt dnl
setsid chdir putenv getpeername unlink dnl
- poll chsize ftruncate sendmsg recvmsg getsockname)
+ poll chsize ftruncate sendmsg recvmsg getsockname dnl
+ execve)
AC_CACHE_SAVE
if test "${WIN32}" = "yes"; then
fun:main
}
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/libdl-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ fun:dlsym
+ fun:libdl_resolve_symbol
+ fun:plugin_list_init
+ fun:init_plugins
+ fun:main
+}
+
{
<insert a suppression name here>
Memcheck:Cond
bool
init_static (void)
{
- configure_path ();
+ /* configure_path (); */
#if defined(USE_CRYPTO) && defined(DMALLOC)
openssl_dmalloc_init ();
if (options->route_script)
{
+ struct argv argv = argv_new ();
setenv_str (es, "script_type", "route-up");
- system_check (options->route_script, es, S_SCRIPT, "Route script failed");
+ argv_printf (&argv, "%s", options->route_script);
+ openvpn_execve_check (&argv, es, S_SCRIPT, "Route script failed");
+ argv_reset (&argv);
}
#ifdef WIN32
int set_lladdr(const char *ifname, const char *lladdr,
const struct env_set *es)
{
- char cmd[256];
+ struct argv argv = argv_new ();
int r;
if (!ifname || !lladdr)
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
- openvpn_snprintf (cmd, sizeof (cmd),
+ argv_printf (&argv,
"%s link set addr %s dev %s",
iproute_path, lladdr, ifname);
#else
- openvpn_snprintf (cmd, sizeof (cmd),
- IFCONFIG_PATH " %s hw ether %s",
+ argv_printf (&argv,
+ "%s %s hw ether %s",
+ IFCONFIG_PATH,
ifname, lladdr);
#endif
#elif defined(TARGET_SOLARIS)
- openvpn_snprintf (cmd, sizeof (cmd),
- IFCONFIG_PATH " %s ether %s",
+ argv_printf (&argv,
+ "%s %s ether %s",
+ IFCONFIG_PATH,
ifname, lladdr);
#elif defined(TARGET_OPENBSD)
- openvpn_snprintf (cmd, sizeof (cmd),
- IFCONFIG_PATH " %s lladdr %s",
+ argv_printf (&argv,
+ "%s %s lladdr %s",
+ IFCONFIG_PATH,
ifname, lladdr);
#elif defined(TARGET_DARWIN)
- openvpn_snprintf (cmd, sizeof (cmd),
- IFCONFIG_PATH " %s lladdr %s",
+ argv_printf (&argv,
+ "%s %s lladdr %s",
+ IFCONFIG_PATH,
ifname, lladdr);
#elif defined(TARGET_FREEBSD)
- openvpn_snprintf (cmd, sizeof (cmd),
- IFCONFIG_PATH " %s ether %s",
+ argv_printf (&argv,
+ "%s %s ether %s",
+ IFCONFIG_PATH,
ifname, lladdr);
#else
msg (M_WARN, "Sorry, but I don't know how to configure link layer addresses on this operating system.");
return -1;
#endif
- r = system_check (cmd, es, M_WARN, "ERROR: Unable to set link layer address.");
+ argv_msg (M_INFO, &argv);
+ r = openvpn_execve_check (&argv, es, M_WARN, "ERROR: Unable to set link layer address.");
if (r)
msg (M_INFO, "TUN/TAP link layer address set to %s", lladdr);
+
+ argv_reset (&argv);
return r;
}
const char *iproute_path = IPROUTE_PATH;
#endif
+/* contains an SSEC_x value defined in misc.h */
+int script_security = SSEC_BUILT_IN; /* GLOBAL */
+
/* Redefine the top level directory of the filesystem
to restrict access to files for security */
void
if (plugin_defined (plugins, plugin_type))
{
- struct buffer cmd = alloc_buf_gc (256, &gc);
-
+ struct argv argv = argv_new ();
ASSERT (arg);
-
- buf_printf (&cmd,
- "\"%s\" %d %d %s %s %s",
- arg,
- tun_mtu, link_mtu,
- ifconfig_local, ifconfig_remote,
- context);
-
- if (plugin_call (plugins, plugin_type, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ argv_printf (&argv,
+ "%s %d %d %s %s %s",
+ arg,
+ tun_mtu, link_mtu,
+ ifconfig_local, ifconfig_remote,
+ context);
+
+ if (plugin_call (plugins, plugin_type, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
msg (M_FATAL, "ERROR: up/down plugin call failed");
+
+ argv_reset (&argv);
}
if (command)
{
- struct buffer cmd = alloc_buf_gc (256, &gc);
-
+ struct argv argv = argv_new ();
ASSERT (arg);
-
setenv_str (es, "script_type", script_type);
-
- buf_printf (&cmd,
- "%s \"%s\" %d %d %s %s %s",
+ argv_printf (&argv,
+ "%s %s %d %d %s %s %s",
command,
arg,
tun_mtu, link_mtu,
ifconfig_local, ifconfig_remote,
context);
- msg (M_INFO, "%s", BSTR (&cmd));
- system_check (BSTR (&cmd), es, S_SCRIPT|S_FATAL, "script failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_SCRIPT|S_FATAL, "script failed");
+ argv_reset (&argv);
}
gc_free (&gc);
#endif
}
-/*
- * Wrapper around the system() call.
- */
-int
-openvpn_system (const char *command, const struct env_set *es, unsigned int flags)
-{
-#ifdef HAVE_SYSTEM
- int ret;
-
- /*
- * We need to bracket this code by mutex because system() doesn't
- * accept an environment list, so we have to use the process-wide
- * list which is shared between all threads.
- */
- mutex_lock_static (L_SYSTEM);
- perf_push (PERF_SCRIPT);
-
- /*
- * add env_set to environment.
- */
- if (flags & S_SCRIPT)
- env_set_add_to_environment (es);
-
-
- /* debugging */
- dmsg (D_SCRIPT, "SYSTEM[%u] '%s'", flags, command);
- if (flags & S_SCRIPT)
- env_set_print (D_SCRIPT, es);
-
- /*
- * execute the command
- */
- ret = system (command);
-
- /* debugging */
- dmsg (D_SCRIPT, "SYSTEM return=%u", ret);
-
- /*
- * remove env_set from environment
- */
- if (flags & S_SCRIPT)
- env_set_remove_from_environment (es);
-
- perf_pop ();
- mutex_unlock_static (L_SYSTEM);
- return ret;
-
-#else
- msg (M_FATAL, "Sorry but I can't execute the shell command '%s' because this operating system doesn't appear to support the system() call", command);
- return -1; /* NOTREACHED */
-#endif
-}
-
/*
* Warn if a given file is group/others accessible.
*/
struct buffer out = alloc_buf_gc (256, gc);
#ifdef WIN32
if (stat == -1)
- buf_printf (&out, "shell command did not execute -- ");
- buf_printf (&out, "system() returned error code %d", stat);
+ buf_printf (&out, "external program did not execute -- ");
+ buf_printf (&out, "returned error code %d", stat);
#else
if (stat == -1)
- buf_printf (&out, "shell command fork failed");
+ buf_printf (&out, "external program fork failed");
else if (!WIFEXITED (stat))
- buf_printf (&out, "shell command did not exit normally");
+ buf_printf (&out, "external program did not exit normally");
else
{
const int cmd_ret = WEXITSTATUS (stat);
if (!cmd_ret)
- buf_printf (&out, "shell command exited normally");
+ buf_printf (&out, "external program exited normally");
else if (cmd_ret == 127)
- buf_printf (&out, "could not execute shell command");
+ buf_printf (&out, "could not execute external program");
else
- buf_printf (&out, "shell command exited with error status: %d", cmd_ret);
+ buf_printf (&out, "external program exited with error status: %d", cmd_ret);
}
#endif
return (const char *)out.data;
}
/*
- * Run system(), exiting on error.
+ * Wrapper around openvpn_execve
*/
bool
-system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message)
+openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
{
struct gc_arena gc = gc_new ();
- const int stat = openvpn_system (command, es, flags);
+ const int stat = openvpn_execve (a, es, flags);
int ret = false;
if (system_ok (stat))
return ret;
}
+bool
+openvpn_execve_allowed (const unsigned int flags)
+{
+ if (flags & S_SCRIPT)
+ return script_security >= SSEC_SCRIPTS;
+ else
+ return script_security >= SSEC_BUILT_IN;
+}
+
+#ifndef WIN32
+/*
+ * Run execve() inside a fork(). Designed to replicate the semantics of system() but
+ * in a safer way that doesn't require the invocation of a shell or the risks
+ * assocated with formatting and parsing a command line.
+ */
+int
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
+{
+ struct gc_arena gc = gc_new ();
+ int ret = -1;
+
+ if (a && a->argv[0])
+ {
+#if defined(ENABLE_EXECVE)
+ if (openvpn_execve_allowed (flags))
+ {
+ const char *cmd = a->argv[0];
+ char *const *argv = a->argv;
+ char *const *envp = (char *const *)make_env_array (es, true, &gc);
+ pid_t pid;
+
+ pid = fork ();
+ if (pid == (pid_t)0) /* child side */
+ {
+ execve (cmd, argv, envp);
+ exit (127);
+ }
+ else if (pid < (pid_t)0) /* fork failed */
+ ;
+ else /* parent side */
+ {
+ if (waitpid (pid, &ret, 0) != pid)
+ ret = -1;
+ }
+ }
+ else
+ {
+ msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
+ }
+#else
+ msg (M_WARN, "openvpn_execve: execve function not available");
+#endif
+ }
+ else
+ {
+ msg (M_WARN, "openvpn_execve: called with empty argv");
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+#endif
+
/*
* Initialize random number seed. random() is only used
* when "weak" random numbers are acceptable.
return string_mod_const (str, CC_PRINT, CC_CRLF, '.', gc);
}
+static bool
+is_password_env_var (const char *str)
+{
+ return (strncmp (str, "password", 8) == 0);
+}
+
+bool
+env_allowed (const char *str)
+{
+ return (script_security >= SSEC_PW_ENV || !is_password_env_var (str));
+}
+
bool
env_safe_to_print (const char *str)
{
#ifndef UNSAFE_DEBUG
- if (strncmp (str, "password", 8) == 0)
+ if (is_password_env_var (str))
return false;
#endif
return true;
/* Make arrays of strings */
const char **
-make_env_array (const struct env_set *es, struct gc_arena *gc)
+make_env_array (const struct env_set *es,
+ const bool check_allowed,
+ struct gc_arena *gc)
{
char **ret = NULL;
struct env_item *e = NULL;
/* fill return array */
if (es)
{
- e = es->list;
- for (i = 0; i < n; ++i)
+ i = 0;
+ for (e = es->list; e != NULL; e = e->next)
{
- ASSERT (e);
- ret[i] = e->string;
- e = e->next;
+ if (!check_allowed || env_allowed (e->string))
+ {
+ ASSERT (i < n);
+ ret[i++] = e->string;
+ }
}
}
sleep (n);
}
+#if 0
/*
* Configure PATH. On Windows, sometimes PATH is not set correctly
* by default.
}
#endif
}
+#endif
+
+#ifdef ARGV_TEST
+void
+argv_test (void)
+{
+ struct gc_arena gc = gc_new ();
+ char line[512];
+ const char *s;
+
+ struct argv a;
+ argv_init (&a);
+
+#ifdef WIN32
+ argv_printf (&a, "%s foo bar %s", "c:\\src\\test\\jyargs.exe", "foo bar");
+ //argv_printf (&a, "%s %s %s", "c:\\src\\test files\\batargs.bat", "foo", "bar");
+#else
+ argv_printf (&a, "./myechox foo bar");
+#endif
+
+ argv_msg_prefix (M_INFO, &a, "ARGV");
+ openvpn_execve_check (&a, NULL, 0, "command failed");
+
+ argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
+ s = argv_str (&a, &gc, PA_BRACKET);
+ printf ("%s\n", s);
+
+ {
+ struct argv b = argv_insert_head (&a, "MARK");
+ s = argv_str (&b, &gc, PA_BRACKET);
+ argv_reset (&b);
+ printf ("%s\n", s);
+ }
+
+ argv_printf (&a, "foo bar %d", 99);
+ s = argv_str (&a, &gc, PA_BRACKET);
+ argv_reset (&a);
+ printf ("%s\n", s);
+
+ s = argv_str (&a, &gc, PA_BRACKET);
+ argv_reset (&a);
+ printf ("%s\n", s);
+
+ argv_printf (&a, "foo bar %d", 99);
+ argv_printf_cat (&a, "bar %d foo", 42);
+ argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7);
+ s = argv_str (&a, &gc, PA_BRACKET);
+ printf ("%s\n", s);
+
+#if 0
+ while (fgets (line, sizeof(line), stdin) != NULL)
+ {
+ char *term;
+ const char *f = line;
+ int i = 0;
+
+ while ((term = argv_term (&f)) != NULL)
+ {
+ printf ("[%d] '%s'\n", i, term);
+ ++i;
+ free (term);
+ }
+ }
+#endif
+
+ argv_reset (&a);
+ gc_free (&gc);
+}
+#endif
#define S_SCRIPT (1<<0)
#define S_FATAL (1<<1)
-/* wrapper around the system() call. */
-int openvpn_system (const char *command, const struct env_set *es, unsigned int flags);
-
-/* interpret the status code returned by system() */
+/* interpret the status code returned by system()/execve() */
bool system_ok(int);
int system_executed (int stat);
const char *system_error_message (int, struct gc_arena *gc);
-/* run system() with error check, return true if success,
- false if error, exit if error and fatal==true */
-bool system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message);
+/* wrapper around the execve() call */
+int openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags);
+bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message);
+bool openvpn_execve_allowed (const unsigned int flags);
#ifdef HAVE_STRERROR
/* a thread-safe version of strerror */
/* Make arrays of strings */
-const char **make_env_array (const struct env_set *es, struct gc_arena *gc);
+const char **make_env_array (const struct env_set *es,
+ const bool check_allowed,
+ struct gc_arena *gc);
+
const char **make_arg_array (const char *first, const char *parms, struct gc_arena *gc);
const char **make_extended_arg_array (char **p, struct gc_arena *gc);
/* returns true if environmental variable safe to print to log */
bool env_safe_to_print (const char *str);
+/* returns true if environmental variable may be passed to an external program */
+bool env_allowed (const char *str);
+
/*
* A sleep function that services the management layer for n
* seconds rather than doing nothing.
extern const char *iproute_path;
#endif
+#define SSEC_NONE 0 /* strictly no calling of external programs */
+#define SSEC_BUILT_IN 1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/
+#define SSEC_SCRIPTS 2 /* allow calling of built-in programs and user-defined scripts */
+#define SSEC_PW_ENV 3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */
+extern int script_security; /* GLOBAL */
+
#endif
if (plugin_defined (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS))
{
- struct buffer cmd = alloc_buf_gc (256, &gc);
-
- buf_printf (&cmd, "\"%s\" \"%s\"",
- op,
- mroute_addr_print (addr, &gc));
+ struct argv argv = argv_new ();
+ argv_printf (&argv, "%s %s",
+ op,
+ mroute_addr_print (addr, &gc));
if (mi)
- buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
-
- if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
+ if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
{
msg (M_WARN, "WARNING: learn-address plugin call failed");
ret = false;
}
+ argv_reset (&argv);
}
if (m->top.options.learn_address_script)
{
- struct buffer cmd = alloc_buf_gc (256, &gc);
-
+ struct argv argv = argv_new ();
setenv_str (es, "script_type", "learn-address");
-
- buf_printf (&cmd, "%s \"%s\" \"%s\"",
- m->top.options.learn_address_script,
- op,
- mroute_addr_print (addr, &gc));
+ argv_printf (&argv, "%s %s %s",
+ m->top.options.learn_address_script,
+ op,
+ mroute_addr_print (addr, &gc));
if (mi)
- buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
-
- if (!system_check (BSTR (&cmd), es, S_SCRIPT, "WARNING: learn-address command failed"))
+ argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
+ if (!openvpn_execve_check (&argv, es, S_SCRIPT, "WARNING: learn-address command failed"))
ret = false;
+ argv_reset (&argv);
}
gc_free (&gc);
if (mi->context.options.client_disconnect_script)
{
- struct gc_arena gc = gc_new ();
- struct buffer cmd = alloc_buf_gc (256, &gc);
-
+ struct argv argv = argv_new ();
setenv_str (mi->context.c2.es, "script_type", "client-disconnect");
-
- buf_printf (&cmd, "%s", mi->context.options.client_disconnect_script);
-
- system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
-
- gc_free (&gc);
+ argv_printf (&argv, "%s", mi->context.options.client_disconnect_script);
+ openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
+ argv_reset (&argv);
}
#ifdef MANAGEMENT_DEF_AUTH
if (management)
/* deprecated callback, use a file for passing back return info */
if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
{
+ struct argv argv = argv_new ();
const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc);
-
+ argv_printf (&argv, "%s", dc_file);
delete_file (dc_file);
-
- if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
{
msg (M_WARN, "WARNING: client-connect plugin call failed");
cc_succeeded = false;
multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
++cc_succeeded_count;
}
+ argv_reset (&argv);
}
/* V2 callback, use a plugin_return struct for passing back return info */
*/
if (mi->context.options.client_connect_script && cc_succeeded)
{
- struct buffer cmd = alloc_buf_gc (256, &gc);
+ struct argv argv = argv_new ();
const char *dc_file = NULL;
setenv_str (mi->context.c2.es, "script_type", "client-connect");
delete_file (dc_file);
- buf_printf (&cmd, "%s %s",
- mi->context.options.client_connect_script,
- dc_file);
+ argv_printf (&argv, "%s %s",
+ mi->context.options.client_connect_script,
+ dc_file);
- if (system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
+ if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
{
multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
++cc_succeeded_count;
}
else
cc_succeeded = false;
+
+ argv_reset (&argv);
}
/*
[\ \fB\-\-route\-up\fR\ \fIcmd\fR\ ]
[\ \fB\-\-route\fR\ \fInetwork\ [netmask]\ [gateway]\ [metric]\fR\ ]
[\ \fB\-\-rport\fR\ \fIport\fR\ ]
+[\ \fB\-\-script\-security\fR\ \fIlevel\fR\ ]
[\ \fB\-\-secret\fR\ \fIfile\ [direction]\fR\ ]
[\ \fB\-\-secret\fR\ \fIfile\fR\ ]
[\ \fB\-\-server\-bridge\fR\ \fIgateway\ netmask\ pool\-start\-IP\ pool\-end\-IP\fR\ ]
[\ \fB\-\-user\fR\ \fIuser\fR\ ]
[\ \fB\-\-username\-as\-common\-name\fR\ ]
[\ \fB\-\-verb\fR\ \fIn\fR\ ]
+[\ \fB\-\-win\-sys\fR\ \fIpath|'env'\fR\ ]
[\ \fB\-\-writepid\fR\ \fIfile\fR\ ]
.in -4
.ti +4
from a malicious or compromised server.
.\"*********************************************************
.TP
+.B --script-security level
+This directive offers policy-level control over OpenVPN's usage of external programs
+and scripts. Lower values are more restrictive, higher values are more permissive. Settings for
+.B level:
+
+.B 0 --
+Strictly no calling of external programs.
+.br
+.B 1 --
+(Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
+.br
+.B 2 --
+Allow calling of built-in executables and user-defined scripts.
+.br
+.B 3 --
+Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
+.\"*********************************************************
+.TP
.B --disable-occ
Don't output a warning message if option inconsistencies are detected between
peers. An example of an option inconsistency would be where one peer uses
.SS Windows-Specific Options:
.\"*********************************************************
.TP
+.B --win-sys path|'env'
+Set the Windows system directory pathname to use when looking for system
+executables such as
+.B route.exe
+and
+.B netsh.exe.
+By default, if this directive is
+not specified, the pathname will be set to "C:\\WINDOWS"
+
+The special string
+.B 'env'
+indicates that the pathname should be read from the
+.B SystemRoot
+environmental variable.
+.\"*********************************************************
+.TP
.B --ip-win32 method
When using
.B --ifconfig
#include "init.h"
#include "forward.h"
#include "multi.h"
+#include "win32.h"
#include "memdbg.h"
/* initialize environmental variable store */
c.es = env_set_create (NULL);
+#ifdef WIN32
+ env_set_add_win32 (c.es);
+#endif
#ifdef ENABLE_MANAGEMENT
/* initialize management subsystem */
" flag to add a direct route to DHCP server, bypassing tunnel.\n"
" Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
"--setenv name value : Set a custom environmental variable to pass to script.\n"
+ "--script-security level : 0 -- strictly no calling of external programs\n"
+ " 1 -- (default) only call built-ins such as ifconfig\n"
+ " 2 -- allow calling of built-ins and scripts\n"
+ " 3 -- allow password to be passed to scripts via env\n"
"--shaper n : Restrict output to peer to n bytes per second.\n"
"--keepalive n m : Helper option for setting timeouts in server mode. Send\n"
" ping once every n seconds, restart if ping not received\n"
#ifdef WIN32
"\n"
"Windows Specific:\n"
+ "--win-sys path|'env' : Pathname of Windows system directory, C:\\WINDOWS by default.\n"
+ " If specified as 'env', read the pathname from SystemRoot env var.\n"
"--ip-win32 method : When using --ifconfig on Windows, set TAP-Win32 adapter\n"
" IP address using method = manual, netsh, ipapi,\n"
" dynamic, or adaptive (default = adaptive).\n"
VERIFY_PERMISSION (OPT_P_SETENV);
setenv_str_safe (es, p[1], p[2] ? p[2] : "");
}
+ else if (streq (p[0], "script-security") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ script_security = atoi (p[1]);
+ }
else if (streq (p[0], "mssfix"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
}
#endif
#ifdef WIN32
+ else if (streq (p[0], "win-sys") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], "env"))
+ set_win_sys_path_via_env (es);
+ else
+ set_win_sys_path (p[1], es);
+ }
else if (streq (p[0], "route-method") && p[1])
{
VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
plugin_call_item (const struct plugin *p,
void *per_client_context,
const int type,
- const char *args,
+ const struct argv *av,
struct openvpn_plugin_string_list **retlist,
const char **envp)
{
if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type)))
{
struct gc_arena gc = gc_new ();
- const char **argv = make_arg_array (p->so_pathname, args, &gc);
+ struct argv a = argv_insert_head (av, p->so_pathname);
dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type));
- plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp);
+ plugin_show_args_env (D_PLUGIN_DEBUG, (const char **)a.argv, envp);
/*
* Call the plugin work function
*/
if (p->func2)
- status = (*p->func2)(p->plugin_handle, type, argv, envp, per_client_context, retlist);
+ status = (*p->func2)(p->plugin_handle, type, (const char **)a.argv, envp, per_client_context, retlist);
else if (p->func1)
- status = (*p->func1)(p->plugin_handle, type, argv, envp);
+ status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp);
else
ASSERT (0);
status,
p->so_pathname);
+ argv_reset (&a);
gc_free (&gc);
}
return status;
int i;
const char **envp;
- envp = make_env_array (es, &gc);
+ envp = make_env_array (es, false, &gc);
if (pr)
plugin_return_init (pr);
int
plugin_call (const struct plugin_list *pl,
const int type,
- const char *args,
+ const struct argv *av,
struct plugin_return *pr,
struct env_set *es)
{
mutex_lock_static (L_PLUGIN);
setenv_del (es, "script_type");
- envp = make_env_array (es, &gc);
+ envp = make_env_array (es, false, &gc);
for (i = 0; i < n; ++i)
{
const int status = plugin_call_item (&pl->common->plugins[i],
pl->per_client.per_client_context[i],
type,
- args,
+ av,
pr ? &pr->list[i] : NULL,
envp);
switch (status)
int plugin_call (const struct plugin_list *pl,
const int type,
- const char *args,
+ const struct argv *av,
struct plugin_return *pr,
struct env_set *es);
static inline int
plugin_call (const struct plugin_list *pl,
const int type,
- const char *args,
+ const struct argv *av,
struct plugin_return *pr,
struct env_set *es)
{
--- /dev/null
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2008 Telethra, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This plugin is similar to simple.c, except it also logs extra information
+ * to stdout for every plugin method called by OpenVPN.
+ *
+ * See the README file for build instructions.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "openvpn-plugin.h"
+
+/*
+ * Our context, where we keep our state.
+ */
+struct plugin_context {
+ const char *username;
+ const char *password;
+};
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env (const char *name, const char *envp[])
+{
+ if (envp)
+ {
+ int i;
+ const int namelen = strlen (name);
+ for (i = 0; envp[i]; ++i)
+ {
+ if (!strncmp (envp[i], name, namelen))
+ {
+ const char *cp = envp[i] + namelen;
+ if (*cp == '=')
+ return cp + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+OPENVPN_EXPORT openvpn_plugin_handle_t
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
+{
+ struct plugin_context *context;
+
+ /*
+ * Allocate our context
+ */
+ context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
+
+ /*
+ * Set the username/password we will require.
+ */
+ context->username = "foo";
+ context->password = "bar";
+
+ /*
+ * Which callbacks to intercept.
+ */
+ *type_mask =
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL);
+
+ return (openvpn_plugin_handle_t) context;
+}
+
+void
+show (const int type, const char *argv[], const char *envp[])
+{
+ size_t i;
+ switch (type)
+ {
+ case OPENVPN_PLUGIN_UP:
+ printf ("OPENVPN_PLUGIN_UP\n");
+ break;
+ case OPENVPN_PLUGIN_DOWN:
+ printf ("OPENVPN_PLUGIN_DOWN\n");
+ break;
+ case OPENVPN_PLUGIN_ROUTE_UP:
+ printf ("OPENVPN_PLUGIN_ROUTE_UP\n");
+ break;
+ case OPENVPN_PLUGIN_IPCHANGE:
+ printf ("OPENVPN_PLUGIN_IPCHANGE\n");
+ break;
+ case OPENVPN_PLUGIN_TLS_VERIFY:
+ printf ("OPENVPN_PLUGIN_TLS_VERIFY\n");
+ break;
+ case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
+ printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
+ break;
+ case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
+ printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
+ break;
+ case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
+ printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
+ break;
+ case OPENVPN_PLUGIN_LEARN_ADDRESS:
+ printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
+ break;
+ case OPENVPN_PLUGIN_TLS_FINAL:
+ printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
+ break;
+ default:
+ printf ("OPENVPN_PLUGIN_?\n");
+ break;
+ }
+
+ printf ("ARGV\n");
+ for (i = 0; argv[i] != NULL; ++i)
+ printf ("%d '%s'\n", (int)i, argv[i]);
+
+ printf ("ENVP\n");
+ for (i = 0; envp[i] != NULL; ++i)
+ printf ("%d '%s'\n", (int)i, envp[i]);
+}
+
+OPENVPN_EXPORT int
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+
+ show (type, argv, envp);
+
+ /* check entered username/password against what we require */
+ if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
+ {
+ /* get username/password from envp string array */
+ const char *username = get_env ("username", envp);
+ const char *password = get_env ("password", envp);
+
+ if (username && !strcmp (username, context->username)
+ && password && !strcmp (password, context->password))
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ else
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ else
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+ free (context);
+}
#include "misc.h"
#include "socket.h"
#include "manage.h"
+#include "win32.h"
#include "memdbg.h"
add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
- struct buffer buf;
+ struct argv argv;
const char *network;
const char *netmask;
const char *gateway;
return;
gc_init (&gc);
- buf = alloc_buf_gc (256, &gc);
+ argv_init (&argv);
network = print_in_addr_t (r->network, 0, &gc);
netmask = print_in_addr_t (r->netmask, 0, &gc);
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
- buf_printf (&buf, "%s route add %s/%d via %s",
+ argv_printf (&argv, "%s route add %s/%d via %s",
iproute_path,
network,
count_netmask_bits(netmask),
gateway);
if (r->metric_defined)
- buf_printf (&buf, " metric %d", r->metric);
+ argv_printf_cat (&argv, "metric %d", r->metric);
#else
- buf_printf (&buf, ROUTE_PATH " add -net %s netmask %s gw %s",
+ argv_printf (&argv, "%s add -net %s netmask %s gw %s",
+ ROUTE_PATH,
network,
netmask,
gateway);
if (r->metric_defined)
- buf_printf (&buf, " metric %d", r->metric);
+ argv_printf_cat (&argv, "metric %d", r->metric);
#endif /*CONFIG_FEATURE_IPROUTE*/
- msg (D_ROUTE, "%s", BSTR (&buf));
- status = system_check (BSTR (&buf), es, 0, "ERROR: Linux route add command failed");
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
#elif defined (WIN32)
- buf_printf (&buf, ROUTE_PATH " ADD %s MASK %s %s",
- network,
- netmask,
- gateway);
+ argv_printf (&argv, "%s%s ADD %s MASK %s %s",
+ get_win_sys_path(),
+ WIN_ROUTE_PATH_SUFFIX,
+ network,
+ netmask,
+ gateway);
if (r->metric_defined)
- buf_printf (&buf, " METRIC %d", r->metric);
+ argv_printf_cat (&argv, "METRIC %d", r->metric);
- msg (D_ROUTE, "%s", BSTR (&buf));
+ argv_msg (D_ROUTE, &argv);
if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
{
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
{
netcmd_semaphore_lock ();
- status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed");
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
netcmd_semaphore_release ();
}
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
{
msg (D_ROUTE, "Route addition fallback to route.exe");
netcmd_semaphore_lock ();
- status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed [adaptive]");
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
netcmd_semaphore_release ();
}
}
/* example: route add 192.0.2.32 -netmask 255.255.255.224 somegateway */
- buf_printf (&buf, ROUTE_PATH " add");
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
#if 0
if (r->metric_defined)
- buf_printf (&buf, " -rtt %d", r->metric);
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- buf_printf (&buf, " %s -netmask %s %s",
+ argv_printf_cat (&argv, "%s -netmask %s %s",
network,
netmask,
gateway);
- msg (D_ROUTE, "%s", BSTR (&buf));
- status = system_check (BSTR (&buf), es, 0, "ERROR: Solaris route add command failed");
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
#elif defined(TARGET_FREEBSD)
- buf_printf (&buf, ROUTE_PATH " add");
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
#if 0
if (r->metric_defined)
- buf_printf (&buf, " -rtt %d", r->metric);
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- buf_printf (&buf, " -net %s %s %s",
+ argv_printf_cat (&argv, "-net %s %s %s",
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- status = system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route add command failed");
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
#elif defined(TARGET_DRAGONFLY)
- buf_printf (&buf, ROUTE_PATH " add");
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
#if 0
if (r->metric_defined)
- buf_printf (&buf, " -rtt %d", r->metric);
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- buf_printf (&buf, " -net %s %s %s",
+ argv_printf_cat (&argv, "-net %s %s %s",
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- status = system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route add command failed");
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
#elif defined(TARGET_DARWIN)
- buf_printf (&buf, ROUTE_PATH " add");
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
#if 0
if (r->metric_defined)
- buf_printf (&buf, " -rtt %d", r->metric);
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- buf_printf (&buf, " -net %s %s %s",
+ argv_printf_cat (&argv, "-net %s %s %s",
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- status = system_check (BSTR (&buf), es, 0, "ERROR: OS X route add command failed");
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
- buf_printf (&buf, ROUTE_PATH " add");
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
#if 0
if (r->metric_defined)
- buf_printf (&buf, " -rtt %d", r->metric);
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- buf_printf (&buf, " -net %s %s -netmask %s",
+ argv_printf_cat (&argv, "-net %s %s -netmask %s",
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- status = system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
done:
r->defined = status;
+ argv_reset (&argv);
gc_free (&gc);
}
delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
- struct buffer buf;
+ struct argv argv;
const char *network;
const char *netmask;
const char *gateway;
return;
gc_init (&gc);
+ argv_init (&argv);
- buf = alloc_buf_gc (256, &gc);
network = print_in_addr_t (r->network, 0, &gc);
netmask = print_in_addr_t (r->netmask, 0, &gc);
gateway = print_in_addr_t (r->gateway, 0, &gc);
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
- buf_printf (&buf, "%s route del %s/%d",
+ argv_printf (&argv, "%s route del %s/%d",
iproute_path,
network,
count_netmask_bits(netmask));
#else
- buf_printf (&buf, ROUTE_PATH " del -net %s netmask %s",
+ argv_printf (&argv, "%s del -net %s netmask %s",
+ ROUTE_PATH,
network,
netmask);
#endif /*CONFIG_FEATURE_IPROUTE*/
if (r->metric_defined)
- buf_printf (&buf, " metric %d", r->metric);
- msg (D_ROUTE, "%s", BSTR (&buf));
- system_check (BSTR (&buf), es, 0, "ERROR: Linux route delete command failed");
+ argv_printf_cat (&argv, "metric %d", r->metric);
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
#elif defined (WIN32)
- buf_printf (&buf, ROUTE_PATH " DELETE %s MASK %s %s",
- network,
- netmask,
- gateway);
+ argv_printf (&argv, "%s%s DELETE %s MASK %s %s",
+ get_win_sys_path(),
+ WIN_ROUTE_PATH_SUFFIX,
+ network,
+ netmask,
+ gateway);
- msg (D_ROUTE, "%s", BSTR (&buf));
+ argv_msg (D_ROUTE, &argv);
if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
{
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
{
netcmd_semaphore_lock ();
- system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed");
+ openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed");
netcmd_semaphore_release ();
}
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
{
msg (D_ROUTE, "Route deletion fallback to route.exe");
netcmd_semaphore_lock ();
- system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed [adaptive]");
+ openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed [adaptive]");
netcmd_semaphore_release ();
}
}
#elif defined (TARGET_SOLARIS)
- buf_printf (&buf, ROUTE_PATH " delete %s -netmask %s %s",
+ argv_printf (&argv, "%s delete %s -netmask %s %s",
+ ROUTE_PATH,
network,
netmask,
gateway);
- msg (D_ROUTE, "%s", BSTR (&buf));
- system_check (BSTR (&buf), es, 0, "ERROR: Solaris route delete command failed");
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete command failed");
#elif defined(TARGET_FREEBSD)
- buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route delete command failed");
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route delete command failed");
#elif defined(TARGET_DRAGONFLY)
- buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route delete command failed");
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route delete command failed");
#elif defined(TARGET_DARWIN)
- buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- system_check (BSTR (&buf), es, 0, "ERROR: OS X route delete command failed");
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
- buf_printf (&buf, ROUTE_PATH " delete -net %s %s -netmask %s",
+ argv_printf (&argv, "%s delete -net %s %s -netmask %s",
+ ROUTE_PATH,
network,
gateway,
netmask);
- msg (D_ROUTE, "%s", BSTR (&buf));
- system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
#endif
+ argv_reset (&argv);
gc_free (&gc);
}
setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT);
}
+static void
+ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc)
+{
+ const char *ip = print_sockaddr_ex (&info->lsa->actual.dest, NULL, 0, gc);
+ const char *port = print_sockaddr_ex (&info->lsa->actual.dest, NULL, PS_DONT_SHOW_ADDR|PS_SHOW_PORT, gc);
+ if (include_cmd)
+ argv_printf (argv, "%s %s %s",
+ info->ipchange_command,
+ ip,
+ port);
+ else
+ argv_printf (argv, "%s %s",
+ ip,
+ port);
+}
+
void
link_socket_connection_initiated (const struct buffer *buf,
struct link_socket_info *info,
/* Process --ipchange plugin */
if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE))
{
- const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc);
- if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ struct argv argv = argv_new ();
+ ipchange_fmt (false, &argv, info, &gc);
+ if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
msg (M_WARN, "WARNING: ipchange plugin call failed");
+ argv_reset (&argv);
}
/* Process --ipchange option */
if (info->ipchange_command)
{
- struct buffer out = alloc_buf_gc (256, &gc);
+ struct argv argv = argv_new ();
setenv_str (es, "script_type", "ipchange");
- buf_printf (&out, "%s %s",
- info->ipchange_command,
- print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc));
- system_check (BSTR (&out), es, S_SCRIPT, "ip-change command failed");
+ ipchange_fmt (true, &argv, info, &gc);
+ openvpn_execve_check (&argv, es, S_SCRIPT, "ip-change command failed");
+ argv_reset (&argv);
}
gc_free (&gc);
const int port = ntohs (addr->sa.sin_port);
mutex_lock_static (L_INET_NTOA);
- buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
+ if (!(flags & PS_DONT_SHOW_ADDR))
+ buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
mutex_unlock_static (L_INET_NTOA);
if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
#define PS_SHOW_PORT_IF_DEFINED (1<<0)
#define PS_SHOW_PORT (1<<1)
#define PS_SHOW_PKTINFO (1<<2)
+#define PS_DONT_SHOW_ADDR (1<<3)
const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr,
const char* separator,
struct tls_session *session;
const struct tls_options *opt;
const int max_depth = 8;
+ struct argv argv = argv_new ();
/* get the tls_session pointer */
ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
/* call --tls-verify plug-in(s) */
if (plugin_defined (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY))
{
- char command[256];
- struct buffer out;
int ret;
- buf_set_write (&out, (uint8_t*)command, sizeof (command));
- buf_printf (&out, "%d %s",
- ctx->error_depth,
- subject);
+ argv_printf (&argv, "%d %s",
+ ctx->error_depth,
+ subject);
- ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, NULL, opt->es);
+ ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es);
if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
{
/* run --tls-verify script */
if (opt->verify_command)
{
- char command[256];
- struct buffer out;
int ret;
setenv_str (opt->es, "script_type", "tls-verify");
- buf_set_write (&out, (uint8_t*)command, sizeof (command));
- buf_printf (&out, "%s %d %s",
- opt->verify_command,
- ctx->error_depth,
- subject);
- dmsg (D_TLS_DEBUG, "TLS: executing verify command: %s", command);
- ret = openvpn_system (command, opt->es, S_SCRIPT);
+ argv_printf (&argv, "%s %d %s",
+ opt->verify_command,
+ ctx->error_depth,
+ subject);
+ argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
+ ret = openvpn_execve (&argv, opt->es, S_SCRIPT);
if (system_ok (ret))
{
else
{
if (!system_executed (ret))
- msg (M_ERR, "Verify command failed to execute: %s", command);
+ argv_msg_prefix (M_ERR, &argv, "Verify command failed to execute");
msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
ctx->error_depth, subject);
goto err; /* Reject connection */
session->verified = true;
free (subject);
+ argv_reset (&argv);
return 1; /* Accept connection */
err:
ERR_clear_error ();
free (subject);
+ argv_reset (&argv);
return 0; /* Reject connection */
}
verify_user_pass_script (struct tls_session *session, const struct user_pass *up)
{
struct gc_arena gc = gc_new ();
- struct buffer cmd = alloc_buf_gc (256, &gc);
+ struct argv argv = argv_new ();
const char *tmp_file = "";
int retval;
bool ret = false;
setenv_untrusted (session);
/* format command line */
- buf_printf (&cmd, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
+ argv_printf (&argv, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
/* call command */
- retval = openvpn_system (BSTR (&cmd), session->opt->es, S_SCRIPT);
+ retval = openvpn_execve (&argv, session->opt->es, S_SCRIPT);
/* test return status of command */
if (system_ok (retval))
ret = true;
else if (!system_executed (retval))
- msg (D_TLS_ERRORS, "TLS Auth Error: user-pass-verify script failed to execute: %s", BSTR (&cmd));
+ argv_msg_prefix (D_TLS_ERRORS, &argv, "TLS Auth Error: user-pass-verify script failed to execute");
if (!session->opt->auth_user_pass_verify_script_via_file)
setenv_del (session->opt->es, "password");
if (strlen (tmp_file) > 0)
delete_file (tmp_file);
+ argv_reset (&argv);
gc_free (&gc);
return ret;
}
*/
#define USE_64_BIT_COUNTERS
+/*
+ * Should we enable the use of execve() for calling subprocesses,
+ * instead of system()?
+ */
+#if defined(HAVE_EXECVE) && defined(HAVE_FORK)
+#define ENABLE_EXECVE
+#endif
+
/*
* Do we have point-to-multipoint capability?
*/
#include "socket.h"
#include "manage.h"
#include "route.h"
+#include "win32.h"
#include "memdbg.h"
const char *ifconfig_local = NULL;
const char *ifconfig_remote_netmask = NULL;
const char *ifconfig_broadcast = NULL;
- char command_line[256];
+ struct argv argv;
+
+ argv_init (&argv);
/*
* We only handle TUN/TAP devices here, not --dev null devices.
/*
* Set the MTU for the device
*/
- openvpn_snprintf (command_line, sizeof (command_line),
+ argv_printf (&argv,
"%s link set dev %s up mtu %d",
iproute_path,
actual,
tun_mtu
);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "Linux ip link set failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip link set failed");
if (tun) {
/*
* Set the address for the device
*/
- openvpn_snprintf (command_line, sizeof (command_line),
+ argv_printf (&argv,
"%s addr add dev %s local %s peer %s",
iproute_path,
actual,
ifconfig_local,
ifconfig_remote_netmask
);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
} else {
- openvpn_snprintf (command_line, sizeof (command_line),
+ argv_printf (&argv,
"%s addr add dev %s %s/%d broadcast %s",
iproute_path,
actual,
count_netmask_bits(ifconfig_remote_netmask),
ifconfig_broadcast
);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
}
tt->did_ifconfig = true;
#else
if (tun)
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s pointopoint %s mtu %d",
+ argv_printf (&argv,
+ "%s %s %s pointopoint %s mtu %d",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
else
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d broadcast %s",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu,
ifconfig_broadcast
);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "Linux ifconfig failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
tt->did_ifconfig = true;
#endif /*CONFIG_FEATURE_IPROUTE*/
*/
if (tun)
{
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s %s mtu %d up",
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
- msg (M_INFO, "%s", command_line);
- if (!system_check (command_line, es, 0, "Solaris ifconfig phase-1 failed"))
+ argv_msg (M_INFO, &argv);
+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
solaris_error_close (tt, es, actual);
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s netmask 255.255.255.255",
+ argv_printf (&argv,
+ "%s %s netmask 255.255.255.255",
+ IFCONFIG_PATH,
actual
);
}
else
no_tap_ifconfig ();
- msg (M_INFO, "%s", command_line);
- if (!system_check (command_line, es, 0, "Solaris ifconfig phase-2 failed"))
+ argv_msg (M_INFO, &argv);
+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
solaris_error_close (tt, es, actual);
tt->did_ifconfig = true;
* (if it exists), and re-ifconfig. Let me know if you know a better way.
*/
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s destroy",
+ argv_printf (&argv,
+ "%s %s destroy",
+ IFCONFIG_PATH,
actual);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, 0, NULL);
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s create",
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, NULL);
+ argv_printf (&argv,
+ "%s %s create",
+ IFCONFIG_PATH,
actual);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, 0, NULL);
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, NULL);
msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if (tun)
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
else
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s link0",
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d broadcast %s link0",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu,
ifconfig_broadcast
);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "OpenBSD ifconfig failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
tt->did_ifconfig = true;
#elif defined(TARGET_NETBSD)
if (tun)
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
* so we don't need the "link0" extra parameter to specify we want to do
* tunneling at the ethernet level
*/
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d broadcast %s",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu,
ifconfig_broadcast
);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "NetBSD ifconfig failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
tt->did_ifconfig = true;
#elif defined(TARGET_DARWIN)
* Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
*/
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s delete",
+ argv_printf (&argv,
+ "%s %s delete",
+ IFCONFIG_PATH,
actual);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, 0, NULL);
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, NULL);
msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if (tun)
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
else
{
if (tt->topology == TOP_SUBNET)
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
+ argv_printf (&argv,
+ "%s %s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_local,
tun_mtu
);
else
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s netmask %s mtu %d up",
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
}
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "Mac OS X ifconfig failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Mac OS X ifconfig failed");
tt->did_ifconfig = true;
/* Add a network route for the local tun interface */
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if (tun)
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
);
else {
if (tt->topology == TOP_SUBNET)
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
+ argv_printf (&argv,
+ "%s %s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_local,
tun_mtu
);
else
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s %s netmask %s mtu %d up",
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
);
}
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, S_FATAL, "FreeBSD ifconfig failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig failed");
tt->did_ifconfig = true;
/* Add a network route for the local tun interface */
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script.");
#endif
+ argv_reset (&argv);
}
gc_free (&gc);
}
{
if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
{
- char command_line[256];
+ struct argv argv;
struct gc_arena gc = gc_new ();
+ argv_init (&argv);
#ifdef CONFIG_FEATURE_IPROUTE
if (is_tun_p2p (tt))
{
- openvpn_snprintf (command_line, sizeof (command_line),
+ argv_printf (&argv,
"%s addr del dev %s local %s peer %s",
iproute_path,
tt->actual_name,
}
else
{
- openvpn_snprintf (command_line, sizeof (command_line),
+ argv_printf (&argv,
"%s addr del dev %s %s/%d",
iproute_path,
tt->actual_name,
);
}
#else
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s 0.0.0.0",
+ argv_printf (&argv,
+ "%s %s 0.0.0.0",
+ IFCONFIG_PATH,
tt->actual_name
);
#endif
- msg (M_INFO, "%s", command_line);
- system_check (command_line, NULL, 0, "Linux ip addr del failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed");
+ argv_reset (&argv);
gc_free (&gc);
}
close_tun_generic (tt);
static void
solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
{
- char command_line[256];
+ struct argv argv;
+ argv_init (&argv);
- openvpn_snprintf (command_line, sizeof (command_line),
- IFCONFIG_PATH " %s unplumb",
+ argv_printf (&argv,
+ "%s %s unplumb",
+ IFCONFIG_PATH,
actual);
- msg (M_INFO, "%s", command_line);
- system_check (command_line, es, 0, "Solaris ifconfig unplumb failed");
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, "Solaris ifconfig unplumb failed");
close_tun (tt);
msg (M_FATAL, "Solaris ifconfig failed");
+ argv_reset (&argv);
}
int
*/
static void
-netsh_command (const char *cmd, int n)
+netsh_command (const struct argv *a, int n)
{
int i;
for (i = 0; i < n; ++i)
bool status;
openvpn_sleep (1);
netcmd_semaphore_lock ();
- msg (M_INFO, "NETSH: %s", cmd);
- status = system_check (cmd, NULL, 0, "ERROR: netsh command failed");
+ argv_msg_prefix (M_INFO, a, "NETSH");
+ status = openvpn_execve_check (a, NULL, 0, "ERROR: netsh command failed");
netcmd_semaphore_release ();
if (status)
return;
const bool test_first)
{
struct gc_arena gc = gc_new ();
- struct buffer out = alloc_buf_gc (256, &gc);
+ struct argv argv = argv_new ();
bool delete_first = false;
/* first check if we should delete existing DNS/WINS settings from TAP interface */
/* delete existing DNS/WINS settings from TAP interface */
if (delete_first)
{
- buf_init (&out, 0);
- buf_printf (&out, "netsh interface ip delete %s \"%s\" all",
- type,
- flex_name);
- netsh_command (BSTR(&out), 2);
+ argv_printf (&argv, "%s%s interface ip delete %s %s all",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ type,
+ flex_name);
+ netsh_command (&argv, 2);
}
/* add new DNS/WINS settings to TAP interface */
if (delete_first || !test_first || !ip_addr_member_of (addr_list[i], current))
{
const char *fmt = count ?
- "netsh interface ip add %s \"%s\" %s"
- : "netsh interface ip set %s \"%s\" static %s";
-
- buf_init (&out, 0);
- buf_printf (&out, fmt,
- type,
- flex_name,
- print_in_addr_t (addr_list[i], 0, &gc));
- netsh_command (BSTR(&out), 2);
+ "%s%s interface ip add %s %s %s"
+ : "%s%s interface ip set %s %s static %s";
+
+ argv_printf (&argv, fmt,
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ type,
+ flex_name,
+ print_in_addr_t (addr_list[i], 0, &gc));
+ netsh_command (&argv, 2);
++count;
}
}
}
+ argv_reset (&argv);
gc_free (&gc);
}
const unsigned int flags)
{
struct gc_arena gc = gc_new ();
- struct buffer out = alloc_buf_gc (256, &gc);
+ struct argv argv = argv_new ();
const IP_ADAPTER_INFO *ai = NULL;
const IP_PER_ADAPTER_INFO *pai = NULL;
else
{
/* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
- buf_init (&out, 0);
- buf_printf (&out,
- "netsh interface ip set address \"%s\" static %s %s",
- flex_name,
- print_in_addr_t (ip, 0, &gc),
- print_in_addr_t (netmask, 0, &gc));
-
- netsh_command (BSTR(&out), 4);
+ argv_printf (&argv,
+ "%s%s interface ip set address %s static %s %s",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ flex_name,
+ print_in_addr_t (ip, 0, &gc),
+ print_in_addr_t (netmask, 0, &gc));
+
+ netsh_command (&argv, 4);
}
}
BOOL_CAST (flags & NI_TEST_FIRST));
}
+ argv_reset (&argv);
gc_free (&gc);
}
netsh_enable_dhcp (const struct tuntap_options *to,
const char *actual_name)
{
- struct gc_arena gc = gc_new ();
- struct buffer out = alloc_buf_gc (256, &gc);
+ struct argv argv;
+ argv_init (&argv);
/* example: netsh interface ip set address my-tap dhcp */
- buf_printf (&out,
- "netsh interface ip set address \"%s\" dhcp",
- actual_name);
+ argv_printf (&argv,
+ "%s%s interface ip set address %s dhcp",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ actual_name);
- netsh_command (BSTR(&out), 4);
+ netsh_command (&argv, 4);
- gc_free (&gc);
+ argv_reset (&argv);
}
/*
#include "mtu.h"
#include "sig.h"
#include "win32.h"
+#include "misc.h"
#include "memdbg.h"
struct semaphore netcmd_semaphore; /* GLOBAL */
+/*
+ * Windows system pathname such as c:\windows
+ */
+static char *win_sys_path = NULL; /* GLOBAL */
+
void
init_win32 (void)
{
window_title_restore (&window_title);
win32_signal_close (&win32_signal);
WSACleanup ();
+ free (win_sys_path);
}
void
return true;
}
+/*
+ * Service functions for openvpn_execve
+ */
+
+static char *
+env_block (const struct env_set *es)
+{
+ if (es)
+ {
+ struct env_item *e;
+ char *ret;
+ char *p;
+ size_t nchars = 1;
+
+ for (e = es->list; e != NULL; e = e->next)
+ nchars += strlen (e->string) + 1;
+
+ ret = (char *) malloc (nchars);
+ check_malloc_return (ret);
+
+ p = ret;
+ for (e = es->list; e != NULL; e = e->next)
+ {
+ if (env_allowed (e->string))
+ {
+ strcpy (p, e->string);
+ p += strlen (e->string) + 1;
+ }
+ }
+ *p = '\0';
+ return ret;
+ }
+ else
+ return NULL;
+}
+
+static char *
+cmd_line (const struct argv *a)
+{
+ size_t nchars = 1;
+ size_t maxlen = 0;
+ size_t i;
+ struct buffer buf;
+ char *work = NULL;
+
+ if (!a)
+ return NULL;
+
+ for (i = 0; i < a->argc; ++i)
+ {
+ const char *arg = a->argv[i];
+ const size_t len = strlen (arg);
+ nchars += len + 3;
+ if (len > maxlen)
+ maxlen = len;
+ }
+
+ work = (char *) malloc (maxlen + 1);
+ check_malloc_return (work);
+ buf = alloc_buf (nchars);
+
+ for (i = 0; i < a->argc; ++i)
+ {
+ const char *arg = a->argv[i];
+ strcpy (work, arg);
+ string_mod (work, CC_PRINT, CC_DOUBLE_QUOTE|CC_CRLF, '_');
+ if (i)
+ buf_printf (&buf, " ");
+ if (string_class (work, CC_ANY, CC_SPACE))
+ buf_printf (&buf, "%s", work);
+ else
+ buf_printf (&buf, "\"%s\"", work);
+ }
+
+ free (work);
+ return BSTR(&buf);
+}
+
+/*
+ * Attempt to simulate fork/execve on Windows
+ */
+int
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
+{
+ int ret = -1;
+ if (a && a->argv[0])
+ {
+ if (openvpn_execve_allowed (flags))
+ {
+ STARTUPINFO start_info;
+ PROCESS_INFORMATION proc_info;
+
+ char *env = env_block (es);
+ char *cl = cmd_line (a);
+ char *cmd = a->argv[0];
+
+ CLEAR (start_info);
+ CLEAR (proc_info);
+
+ /* fill in STARTUPINFO struct */
+ GetStartupInfo(&start_info);
+ start_info.cb = sizeof(start_info);
+ start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = SW_HIDE;
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start_info.hStdOutput = start_info.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ if (CreateProcess (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info))
+ {
+ DWORD exit_status = 0;
+ CloseHandle (proc_info.hThread);
+ WaitForSingleObject (proc_info.hProcess, INFINITE);
+ if (GetExitCodeProcess (proc_info.hProcess, &exit_status))
+ ret = (int)exit_status;
+ else
+ msg (M_WARN|M_ERRNO, "openvpn_execve: GetExitCodeProcess %s failed", cmd);
+ CloseHandle (proc_info.hProcess);
+ }
+ else
+ {
+ msg (M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %s failed", cmd);
+ }
+ free (cl);
+ free (env);
+ }
+ else
+ {
+ msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
+ }
+ }
+ else
+ {
+ msg (M_WARN, "openvpn_execve: called with empty argv");
+ }
+ return ret;
+}
+
+char *
+get_win_sys_path (void)
+{
+ ASSERT (win_sys_path);
+ return win_sys_path;
+}
+
+void
+set_win_sys_path (const char *newpath, struct env_set *es)
+{
+ free (win_sys_path);
+ win_sys_path = string_alloc (newpath, NULL);
+ setenv_str (es, SYS_PATH_ENV_VAR_NAME, win_sys_path); /* route.exe needs this */
+}
+
+void
+set_win_sys_path_via_env (struct env_set *es)
+{
+ char buf[256];
+ DWORD status = GetEnvironmentVariable (SYS_PATH_ENV_VAR_NAME, buf, sizeof(buf));
+ if (!status)
+ msg (M_ERR, "Cannot find environmental variable %s", SYS_PATH_ENV_VAR_NAME);
+ if (status > sizeof (buf) - 1)
+ msg (M_FATAL, "String overflow attempting to read environmental variable %s", SYS_PATH_ENV_VAR_NAME);
+ set_win_sys_path (buf, es);
+}
+
+void
+env_set_add_win32 (struct env_set *es)
+{
+ set_win_sys_path (DEFAULT_WIN_SYS_PATH, es);
+}
+
#endif
#include "mtu.h"
+/* location of executables */
+#define SYS_PATH_ENV_VAR_NAME "SystemRoot" /* environmental variable name that normally contains the system path */
+#define DEFAULT_WIN_SYS_PATH "C:\\WINDOWS" /* --win-sys default value */
+#define NETSH_PATH_SUFFIX "\\system32\\netsh.exe"
+#define WIN_ROUTE_PATH_SUFFIX "\\system32\\route.exe"
+
/*
* Win32-specific OpenVPN code, targetted at the mingw
* development environment.
/* return true if filename is safe to be used on Windows */
bool win_safe_filename (const char *fn);
+/* add constant environmental variables needed by Windows */
+struct env_set;
+void env_set_add_win32 (struct env_set *es);
+
+/* get and set the current windows system path */
+void set_win_sys_path (const char *newpath, struct env_set *es);
+void set_win_sys_path_via_env (struct env_set *es);
+char *get_win_sys_path (void);
+
#endif
#endif