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
This commit is contained in:
james 2008-07-26 07:27:03 +00:00
parent 26bb4c740b
commit 5a2e9a2587
23 changed files with 1095 additions and 417 deletions

132
buffer.c
View File

@ -240,6 +240,14 @@ argv_init (struct argv *a)
a->argv = NULL;
}
struct argv
argv_new (void)
{
struct argv ret;
argv_init (&ret);
return ret;
}
void
argv_reset (struct argv *a)
{
@ -266,6 +274,23 @@ argv_argc (const char *format)
return argc;
}
struct argv
argv_insert_head (const struct argv *a, const char *head)
{
struct argv r;
size_t i;
r.argc = (a ? a->argc : 0) + 1;
ALLOC_ARRAY_CLEAR (r.argv, char *, r.argc + 1);
r.argv[0] = string_alloc (head, NULL);
if (a)
{
for (i = 0; i < a->argc; ++i)
r.argv[i+1] = string_alloc (a->argv[i], NULL);
}
return r;
}
char *
argv_term (const char **f)
{
@ -323,6 +348,22 @@ argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags)
return "";
}
void
argv_msg (const int msglev, const struct argv *a)
{
struct gc_arena gc = gc_new ();
msg (msglev, "%s", argv_str (a, &gc, 0));
gc_free (&gc);
}
void
argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix)
{
struct gc_arena gc = gc_new ();
msg (msglev, "%s: %s", prefix, argv_str (a, &gc, 0));
gc_free (&gc);
}
void
argv_printf (struct argv *a, const char *format, ...)
{
@ -373,7 +414,10 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
{
if (!strcmp (term, "%s"))
{
a->argv[argc++] = string_alloc (va_arg (arglist, char *), NULL);
char *s = va_arg (arglist, char *);
if (!s)
s = "";
a->argv[argc++] = string_alloc (s, NULL);
}
else if (!strcmp (term, "%d"))
{
@ -387,6 +431,41 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
openvpn_snprintf (numstr, sizeof (numstr), "%u", va_arg (arglist, unsigned int));
a->argv[argc++] = string_alloc (numstr, NULL);
}
else if (!strcmp (term, "%s/%d"))
{
char numstr[64];
char *s = va_arg (arglist, char *);
if (!s)
s = "";
openvpn_snprintf (numstr, sizeof (numstr), "%d", va_arg (arglist, int));
{
const size_t len = strlen(s) + strlen(numstr) + 2;
char *combined = (char *) malloc (len);
check_malloc_return (combined);
strcpy (combined, s);
strcat (combined, "/");
strcat (combined, numstr);
a->argv[argc++] = combined;
}
}
else if (!strcmp (term, "%s%s"))
{
char *s1 = va_arg (arglist, char *);
char *s2 = va_arg (arglist, char *);
char *combined;
if (!s1) s1 = "";
if (!s2) s2 = "";
combined = (char *) malloc (strlen(s1) + strlen(s2) + 1);
check_malloc_return (combined);
strcpy (combined, s1);
strcat (combined, s2);
a->argv[argc++] = combined;
}
else
ASSERT (0);
free (term);
@ -399,57 +478,6 @@ argv_printf_arglist (struct argv *a, const char *format, const unsigned int flag
ASSERT (argc == a->argc);
}
#ifdef ARGV_TEST
void
argv_test (void)
{
struct gc_arena gc = gc_new ();
char line[512];
const char *s;
struct argv a;
argv_init (&a);
argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
s = argv_str (&a, &gc, PA_BRACKET);
argv_reset (&a);
printf ("%s\n", s);
argv_init (&a);
argv_printf (&a, "foo bar %d", 99);
s = argv_str (&a, &gc, PA_BRACKET);
argv_reset (&a);
printf ("%s\n", s);
argv_init (&a);
s = argv_str (&a, &gc, PA_BRACKET);
argv_reset (&a);
printf ("%s\n", s);
argv_init (&a);
argv_printf (&a, "foo bar %d", 99);
argv_printf_cat (&a, "bar %d foo", 42);
argv_printf_cat (&a, "cool %s %d u", "frood", 4);
s = argv_str (&a, &gc, PA_BRACKET);
argv_reset (&a);
printf ("%s\n", s);
while (fgets (line, sizeof(line), stdin) != NULL)
{
char *term;
const char *f = line;
int i = 0;
while ((term = argv_term (&f)) != NULL)
{
printf ("[%d] '%s'\n", i, term);
++i;
free (term);
}
}
gc_free (&gc);
}
#endif
/*
* write a string to the end of a buffer that was
* truncated by buf_printf

View File

@ -234,10 +234,14 @@ int openvpn_snprintf(char *str, size_t size, const char *format, ...)
* to execve.
*/
void argv_init (struct argv *a);
struct argv argv_new (void);
void argv_reset (struct argv *a);
size_t argv_argc (const char *format);
char *argv_term (const char **f);
const char *argv_str (const struct argv *a, struct gc_arena *gc, const unsigned int flags);
struct argv argv_insert_head (const struct argv *a, const char *head);
void argv_msg (const int msglev, const struct argv *a);
void argv_msg_prefix (const int msglev, const struct argv *a, const char *prefix);
#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);

View File

@ -462,7 +462,8 @@ AC_CHECK_FUNCS(daemon chroot getpwnam setuid nice system getpid dup dup2 dnl
getpass strerror syslog openlog mlockall getgrnam setgid dnl
setgroups stat flock readv writev setsockopt getsockopt dnl
setsid chdir putenv getpeername unlink dnl
poll chsize ftruncate sendmsg recvmsg getsockname)
poll chsize ftruncate sendmsg recvmsg getsockname dnl
execve)
AC_CACHE_SAVE
if test "${WIN32}" = "yes"; then

View File

@ -392,6 +392,24 @@
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

7
init.c
View File

@ -370,7 +370,7 @@ init_port_share (struct context *c)
bool
init_static (void)
{
configure_path ();
/* configure_path (); */
#if defined(USE_CRYPTO) && defined(DMALLOC)
openssl_dmalloc_init ();
@ -921,8 +921,11 @@ do_route (const struct options *options,
if (options->route_script)
{
struct argv argv = argv_new ();
setenv_str (es, "script_type", "route-up");
system_check (options->route_script, es, S_SCRIPT, "Route script failed");
argv_printf (&argv, "%s", options->route_script);
openvpn_execve_check (&argv, es, S_SCRIPT, "Route script failed");
argv_reset (&argv);
}
#ifdef WIN32

View File

@ -9,7 +9,7 @@
int set_lladdr(const char *ifname, const char *lladdr,
const struct env_set *es)
{
char cmd[256];
struct argv argv = argv_new ();
int r;
if (!ifname || !lladdr)
@ -17,37 +17,45 @@ int set_lladdr(const char *ifname, const char *lladdr,
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
openvpn_snprintf (cmd, sizeof (cmd),
argv_printf (&argv,
"%s link set addr %s dev %s",
iproute_path, lladdr, ifname);
#else
openvpn_snprintf (cmd, sizeof (cmd),
IFCONFIG_PATH " %s hw ether %s",
argv_printf (&argv,
"%s %s hw ether %s",
IFCONFIG_PATH,
ifname, lladdr);
#endif
#elif defined(TARGET_SOLARIS)
openvpn_snprintf (cmd, sizeof (cmd),
IFCONFIG_PATH " %s ether %s",
argv_printf (&argv,
"%s %s ether %s",
IFCONFIG_PATH,
ifname, lladdr);
#elif defined(TARGET_OPENBSD)
openvpn_snprintf (cmd, sizeof (cmd),
IFCONFIG_PATH " %s lladdr %s",
argv_printf (&argv,
"%s %s lladdr %s",
IFCONFIG_PATH,
ifname, lladdr);
#elif defined(TARGET_DARWIN)
openvpn_snprintf (cmd, sizeof (cmd),
IFCONFIG_PATH " %s lladdr %s",
argv_printf (&argv,
"%s %s lladdr %s",
IFCONFIG_PATH,
ifname, lladdr);
#elif defined(TARGET_FREEBSD)
openvpn_snprintf (cmd, sizeof (cmd),
IFCONFIG_PATH " %s ether %s",
argv_printf (&argv,
"%s %s ether %s",
IFCONFIG_PATH,
ifname, lladdr);
#else
msg (M_WARN, "Sorry, but I don't know how to configure link layer addresses on this operating system.");
return -1;
#endif
r = system_check (cmd, es, M_WARN, "ERROR: Unable to set link layer address.");
argv_msg (M_INFO, &argv);
r = openvpn_execve_check (&argv, es, M_WARN, "ERROR: Unable to set link layer address.");
if (r)
msg (M_INFO, "TUN/TAP link layer address set to %s", lladdr);
argv_reset (&argv);
return r;
}

273
misc.c
View File

@ -43,6 +43,9 @@
const char *iproute_path = IPROUTE_PATH;
#endif
/* contains an SSEC_x value defined in misc.h */
int script_security = SSEC_BUILT_IN; /* GLOBAL */
/* Redefine the top level directory of the filesystem
to restrict access to files for security */
void
@ -196,38 +199,36 @@ run_up_down (const char *command,
if (plugin_defined (plugins, plugin_type))
{
struct buffer cmd = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
ASSERT (arg);
argv_printf (&argv,
"%s %d %d %s %s %s",
arg,
tun_mtu, link_mtu,
ifconfig_local, ifconfig_remote,
context);
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)
if (plugin_call (plugins, plugin_type, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
msg (M_FATAL, "ERROR: up/down plugin call failed");
argv_reset (&argv);
}
if (command)
{
struct buffer cmd = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
ASSERT (arg);
setenv_str (es, "script_type", script_type);
buf_printf (&cmd,
"%s \"%s\" %d %d %s %s %s",
argv_printf (&argv,
"%s %s %d %d %s %s %s",
command,
arg,
tun_mtu, link_mtu,
ifconfig_local, ifconfig_remote,
context);
msg (M_INFO, "%s", BSTR (&cmd));
system_check (BSTR (&cmd), es, S_SCRIPT|S_FATAL, "script failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_SCRIPT|S_FATAL, "script failed");
argv_reset (&argv);
}
gc_free (&gc);
@ -374,59 +375,6 @@ save_inetd_socket_descriptor (void)
#endif
}
/*
* Wrapper around the system() call.
*/
int
openvpn_system (const char *command, const struct env_set *es, unsigned int flags)
{
#ifdef HAVE_SYSTEM
int ret;
/*
* We need to bracket this code by mutex because system() doesn't
* accept an environment list, so we have to use the process-wide
* list which is shared between all threads.
*/
mutex_lock_static (L_SYSTEM);
perf_push (PERF_SCRIPT);
/*
* add env_set to environment.
*/
if (flags & S_SCRIPT)
env_set_add_to_environment (es);
/* debugging */
dmsg (D_SCRIPT, "SYSTEM[%u] '%s'", flags, command);
if (flags & S_SCRIPT)
env_set_print (D_SCRIPT, es);
/*
* execute the command
*/
ret = system (command);
/* debugging */
dmsg (D_SCRIPT, "SYSTEM return=%u", ret);
/*
* remove env_set from environment
*/
if (flags & S_SCRIPT)
env_set_remove_from_environment (es);
perf_pop ();
mutex_unlock_static (L_SYSTEM);
return ret;
#else
msg (M_FATAL, "Sorry but I can't execute the shell command '%s' because this operating system doesn't appear to support the system() call", command);
return -1; /* NOTREACHED */
#endif
}
/*
* Warn if a given file is group/others accessible.
*/
@ -489,35 +437,35 @@ system_error_message (int stat, struct gc_arena *gc)
struct buffer out = alloc_buf_gc (256, gc);
#ifdef WIN32
if (stat == -1)
buf_printf (&out, "shell command did not execute -- ");
buf_printf (&out, "system() returned error code %d", stat);
buf_printf (&out, "external program did not execute -- ");
buf_printf (&out, "returned error code %d", stat);
#else
if (stat == -1)
buf_printf (&out, "shell command fork failed");
buf_printf (&out, "external program fork failed");
else if (!WIFEXITED (stat))
buf_printf (&out, "shell command did not exit normally");
buf_printf (&out, "external program did not exit normally");
else
{
const int cmd_ret = WEXITSTATUS (stat);
if (!cmd_ret)
buf_printf (&out, "shell command exited normally");
buf_printf (&out, "external program exited normally");
else if (cmd_ret == 127)
buf_printf (&out, "could not execute shell command");
buf_printf (&out, "could not execute external program");
else
buf_printf (&out, "shell command exited with error status: %d", cmd_ret);
buf_printf (&out, "external program exited with error status: %d", cmd_ret);
}
#endif
return (const char *)out.data;
}
/*
* Run system(), exiting on error.
* Wrapper around openvpn_execve
*/
bool
system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message)
openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
{
struct gc_arena gc = gc_new ();
const int stat = openvpn_system (command, es, flags);
const int stat = openvpn_execve (a, es, flags);
int ret = false;
if (system_ok (stat))
@ -533,6 +481,69 @@ system_check (const char *command, const struct env_set *es, unsigned int flags,
return ret;
}
bool
openvpn_execve_allowed (const unsigned int flags)
{
if (flags & S_SCRIPT)
return script_security >= SSEC_SCRIPTS;
else
return script_security >= SSEC_BUILT_IN;
}
#ifndef WIN32
/*
* Run execve() inside a fork(). Designed to replicate the semantics of system() but
* in a safer way that doesn't require the invocation of a shell or the risks
* assocated with formatting and parsing a command line.
*/
int
openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
{
struct gc_arena gc = gc_new ();
int ret = -1;
if (a && a->argv[0])
{
#if defined(ENABLE_EXECVE)
if (openvpn_execve_allowed (flags))
{
const char *cmd = a->argv[0];
char *const *argv = a->argv;
char *const *envp = (char *const *)make_env_array (es, true, &gc);
pid_t pid;
pid = fork ();
if (pid == (pid_t)0) /* child side */
{
execve (cmd, argv, envp);
exit (127);
}
else if (pid < (pid_t)0) /* fork failed */
;
else /* parent side */
{
if (waitpid (pid, &ret, 0) != pid)
ret = -1;
}
}
else
{
msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
}
#else
msg (M_WARN, "openvpn_execve: execve function not available");
#endif
}
else
{
msg (M_WARN, "openvpn_execve: called with empty argv");
}
gc_free (&gc);
return ret;
}
#endif
/*
* Initialize random number seed. random() is only used
* when "weak" random numbers are acceptable.
@ -1479,11 +1490,23 @@ safe_print (const char *str, struct gc_arena *gc)
return string_mod_const (str, CC_PRINT, CC_CRLF, '.', gc);
}
static bool
is_password_env_var (const char *str)
{
return (strncmp (str, "password", 8) == 0);
}
bool
env_allowed (const char *str)
{
return (script_security >= SSEC_PW_ENV || !is_password_env_var (str));
}
bool
env_safe_to_print (const char *str)
{
#ifndef UNSAFE_DEBUG
if (strncmp (str, "password", 8) == 0)
if (is_password_env_var (str))
return false;
#endif
return true;
@ -1492,7 +1515,9 @@ env_safe_to_print (const char *str)
/* Make arrays of strings */
const char **
make_env_array (const struct env_set *es, struct gc_arena *gc)
make_env_array (const struct env_set *es,
const bool check_allowed,
struct gc_arena *gc)
{
char **ret = NULL;
struct env_item *e = NULL;
@ -1511,12 +1536,14 @@ make_env_array (const struct env_set *es, struct gc_arena *gc)
/* fill return array */
if (es)
{
e = es->list;
for (i = 0; i < n; ++i)
i = 0;
for (e = es->list; e != NULL; e = e->next)
{
ASSERT (e);
ret[i] = e->string;
e = e->next;
if (!check_allowed || env_allowed (e->string))
{
ASSERT (i < n);
ret[i++] = e->string;
}
}
}
@ -1631,6 +1658,7 @@ openvpn_sleep (const int n)
sleep (n);
}
#if 0
/*
* Configure PATH. On Windows, sometimes PATH is not set correctly
* by default.
@ -1672,3 +1700,72 @@ configure_path (void)
}
#endif
}
#endif
#ifdef ARGV_TEST
void
argv_test (void)
{
struct gc_arena gc = gc_new ();
char line[512];
const char *s;
struct argv a;
argv_init (&a);
#ifdef WIN32
argv_printf (&a, "%s foo bar %s", "c:\\src\\test\\jyargs.exe", "foo bar");
//argv_printf (&a, "%s %s %s", "c:\\src\\test files\\batargs.bat", "foo", "bar");
#else
argv_printf (&a, "./myechox foo bar");
#endif
argv_msg_prefix (M_INFO, &a, "ARGV");
openvpn_execve_check (&a, NULL, 0, "command failed");
argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
s = argv_str (&a, &gc, PA_BRACKET);
printf ("%s\n", s);
{
struct argv b = argv_insert_head (&a, "MARK");
s = argv_str (&b, &gc, PA_BRACKET);
argv_reset (&b);
printf ("%s\n", s);
}
argv_printf (&a, "foo bar %d", 99);
s = argv_str (&a, &gc, PA_BRACKET);
argv_reset (&a);
printf ("%s\n", s);
s = argv_str (&a, &gc, PA_BRACKET);
argv_reset (&a);
printf ("%s\n", s);
argv_printf (&a, "foo bar %d", 99);
argv_printf_cat (&a, "bar %d foo", 42);
argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7);
s = argv_str (&a, &gc, PA_BRACKET);
printf ("%s\n", s);
#if 0
while (fgets (line, sizeof(line), stdin) != NULL)
{
char *term;
const char *f = line;
int i = 0;
while ((term = argv_term (&f)) != NULL)
{
printf ("[%d] '%s'\n", i, term);
++i;
free (term);
}
}
#endif
argv_reset (&a);
gc_free (&gc);
}
#endif

26
misc.h
View File

@ -117,17 +117,15 @@ void warn_if_group_others_accessible(const char* filename);
#define S_SCRIPT (1<<0)
#define S_FATAL (1<<1)
/* wrapper around the system() call. */
int openvpn_system (const char *command, const struct env_set *es, unsigned int flags);
/* interpret the status code returned by system() */
/* interpret the status code returned by system()/execve() */
bool system_ok(int);
int system_executed (int stat);
const char *system_error_message (int, struct gc_arena *gc);
/* run system() with error check, return true if success,
false if error, exit if error and fatal==true */
bool system_check (const char *command, const struct env_set *es, unsigned int flags, const char *error_message);
/* wrapper around the execve() call */
int openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags);
bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message);
bool openvpn_execve_allowed (const unsigned int flags);
#ifdef HAVE_STRERROR
/* a thread-safe version of strerror */
@ -184,7 +182,10 @@ void env_set_remove_from_environment (const struct env_set *es);
/* Make arrays of strings */
const char **make_env_array (const struct env_set *es, struct gc_arena *gc);
const char **make_env_array (const struct env_set *es,
const bool check_allowed,
struct gc_arena *gc);
const char **make_arg_array (const char *first, const char *parms, struct gc_arena *gc);
const char **make_extended_arg_array (char **p, struct gc_arena *gc);
@ -271,6 +272,9 @@ const char *safe_print (const char *str, struct gc_arena *gc);
/* returns true if environmental variable safe to print to log */
bool env_safe_to_print (const char *str);
/* returns true if environmental variable may be passed to an external program */
bool env_allowed (const char *str);
/*
* A sleep function that services the management layer for n
* seconds rather than doing nothing.
@ -290,4 +294,10 @@ void get_user_pass_auto_userid (struct user_pass *up, const char *tag);
extern const char *iproute_path;
#endif
#define SSEC_NONE 0 /* strictly no calling of external programs */
#define SSEC_BUILT_IN 1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/
#define SSEC_SCRIPTS 2 /* allow calling of built-in programs and user-defined scripts */
#define SSEC_PW_ENV 3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */
extern int script_security; /* GLOBAL */
#endif

65
multi.c
View File

@ -85,36 +85,33 @@ learn_address_script (const struct multi_context *m,
if (plugin_defined (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS))
{
struct buffer cmd = alloc_buf_gc (256, &gc);
buf_printf (&cmd, "\"%s\" \"%s\"",
op,
mroute_addr_print (addr, &gc));
struct argv argv = argv_new ();
argv_printf (&argv, "%s %s",
op,
mroute_addr_print (addr, &gc));
if (mi)
buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, BSTR (&cmd), NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
{
msg (M_WARN, "WARNING: learn-address plugin call failed");
ret = false;
}
argv_reset (&argv);
}
if (m->top.options.learn_address_script)
{
struct buffer cmd = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
setenv_str (es, "script_type", "learn-address");
buf_printf (&cmd, "%s \"%s\" \"%s\"",
m->top.options.learn_address_script,
op,
mroute_addr_print (addr, &gc));
argv_printf (&argv, "%s %s %s",
m->top.options.learn_address_script,
op,
mroute_addr_print (addr, &gc));
if (mi)
buf_printf (&cmd, " \"%s\"", tls_common_name (mi->context.c2.tls_multi, false));
if (!system_check (BSTR (&cmd), es, S_SCRIPT, "WARNING: learn-address command failed"))
argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
if (!openvpn_execve_check (&argv, es, S_SCRIPT, "WARNING: learn-address command failed"))
ret = false;
argv_reset (&argv);
}
gc_free (&gc);
@ -474,16 +471,11 @@ multi_client_disconnect_script (struct multi_context *m,
if (mi->context.options.client_disconnect_script)
{
struct gc_arena gc = gc_new ();
struct buffer cmd = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
setenv_str (mi->context.c2.es, "script_type", "client-disconnect");
buf_printf (&cmd, "%s", mi->context.options.client_disconnect_script);
system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
gc_free (&gc);
argv_printf (&argv, "%s", mi->context.options.client_disconnect_script);
openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-disconnect command failed");
argv_reset (&argv);
}
#ifdef MANAGEMENT_DEF_AUTH
if (management)
@ -1523,11 +1515,11 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
/* deprecated callback, use a file for passing back return info */
if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
{
struct argv argv = argv_new ();
const char *dc_file = create_temp_filename (mi->context.options.tmp_dir, "cc", &gc);
argv_printf (&argv, "%s", dc_file);
delete_file (dc_file);
if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, dc_file, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
{
msg (M_WARN, "WARNING: client-connect plugin call failed");
cc_succeeded = false;
@ -1537,6 +1529,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
++cc_succeeded_count;
}
argv_reset (&argv);
}
/* V2 callback, use a plugin_return struct for passing back return info */
@ -1566,7 +1559,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
*/
if (mi->context.options.client_connect_script && cc_succeeded)
{
struct buffer cmd = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
const char *dc_file = NULL;
setenv_str (mi->context.c2.es, "script_type", "client-connect");
@ -1575,17 +1568,19 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
delete_file (dc_file);
buf_printf (&cmd, "%s %s",
mi->context.options.client_connect_script,
dc_file);
argv_printf (&argv, "%s %s",
mi->context.options.client_connect_script,
dc_file);
if (system_check (BSTR (&cmd), mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
if (openvpn_execve_check (&argv, mi->context.c2.es, S_SCRIPT, "client-connect command failed"))
{
multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
++cc_succeeded_count;
}
else
cc_succeeded = false;
argv_reset (&argv);
}
/*

View File

@ -252,6 +252,7 @@ openvpn \- secure IP tunnel daemon.
[\ \fB\-\-route\-up\fR\ \fIcmd\fR\ ]
[\ \fB\-\-route\fR\ \fInetwork\ [netmask]\ [gateway]\ [metric]\fR\ ]
[\ \fB\-\-rport\fR\ \fIport\fR\ ]
[\ \fB\-\-script\-security\fR\ \fIlevel\fR\ ]
[\ \fB\-\-secret\fR\ \fIfile\ [direction]\fR\ ]
[\ \fB\-\-secret\fR\ \fIfile\fR\ ]
[\ \fB\-\-server\-bridge\fR\ \fIgateway\ netmask\ pool\-start\-IP\ pool\-end\-IP\fR\ ]
@ -300,6 +301,7 @@ openvpn \- secure IP tunnel daemon.
[\ \fB\-\-user\fR\ \fIuser\fR\ ]
[\ \fB\-\-username\-as\-common\-name\fR\ ]
[\ \fB\-\-verb\fR\ \fIn\fR\ ]
[\ \fB\-\-win\-sys\fR\ \fIpath|'env'\fR\ ]
[\ \fB\-\-writepid\fR\ \fIfile\fR\ ]
.in -4
.ti +4
@ -1998,6 +2000,24 @@ is a safety precaution to prevent a LD_PRELOAD style attack
from a malicious or compromised server.
.\"*********************************************************
.TP
.B --script-security level
This directive offers policy-level control over OpenVPN's usage of external programs
and scripts. Lower values are more restrictive, higher values are more permissive. Settings for
.B level:
.B 0 --
Strictly no calling of external programs.
.br
.B 1 --
(Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
.br
.B 2 --
Allow calling of built-in executables and user-defined scripts.
.br
.B 3 --
Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
.\"*********************************************************
.TP
.B --disable-occ
Don't output a warning message if option inconsistencies are detected between
peers. An example of an option inconsistency would be where one peer uses
@ -4481,6 +4501,22 @@ Optional group to be owner of this tunnel.
.SS Windows-Specific Options:
.\"*********************************************************
.TP
.B --win-sys path|'env'
Set the Windows system directory pathname to use when looking for system
executables such as
.B route.exe
and
.B netsh.exe.
By default, if this directive is
not specified, the pathname will be set to "C:\\WINDOWS"
The special string
.B 'env'
indicates that the pathname should be read from the
.B SystemRoot
environmental variable.
.\"*********************************************************
.TP
.B --ip-win32 method
When using
.B --ifconfig

View File

@ -27,6 +27,7 @@
#include "init.h"
#include "forward.h"
#include "multi.h"
#include "win32.h"
#include "memdbg.h"
@ -131,6 +132,9 @@ main (int argc, char *argv[])
/* initialize environmental variable store */
c.es = env_set_create (NULL);
#ifdef WIN32
env_set_add_win32 (c.es);
#endif
#ifdef ENABLE_MANAGEMENT
/* initialize management subsystem */

View File

@ -189,6 +189,10 @@ static const char usage_message[] =
" flag to add a direct route to DHCP server, bypassing tunnel.\n"
" Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
"--setenv name value : Set a custom environmental variable to pass to script.\n"
"--script-security level : 0 -- strictly no calling of external programs\n"
" 1 -- (default) only call built-ins such as ifconfig\n"
" 2 -- allow calling of built-ins and scripts\n"
" 3 -- allow password to be passed to scripts via env\n"
"--shaper n : Restrict output to peer to n bytes per second.\n"
"--keepalive n m : Helper option for setting timeouts in server mode. Send\n"
" ping once every n seconds, restart if ping not received\n"
@ -536,6 +540,8 @@ static const char usage_message[] =
#ifdef WIN32
"\n"
"Windows Specific:\n"
"--win-sys path|'env' : Pathname of Windows system directory, C:\\WINDOWS by default.\n"
" If specified as 'env', read the pathname from SystemRoot env var.\n"
"--ip-win32 method : When using --ifconfig on Windows, set TAP-Win32 adapter\n"
" IP address using method = manual, netsh, ipapi,\n"
" dynamic, or adaptive (default = adaptive).\n"
@ -4249,6 +4255,11 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_SETENV);
setenv_str_safe (es, p[1], p[2] ? p[2] : "");
}
else if (streq (p[0], "script-security") && p[1])
{
VERIFY_PERMISSION (OPT_P_GENERAL);
script_security = atoi (p[1]);
}
else if (streq (p[0], "mssfix"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
@ -4618,6 +4629,14 @@ add_option (struct options *options,
}
#endif
#ifdef WIN32
else if (streq (p[0], "win-sys") && p[1])
{
VERIFY_PERMISSION (OPT_P_GENERAL);
if (streq (p[1], "env"))
set_win_sys_path_via_env (es);
else
set_win_sys_path (p[1], es);
}
else if (streq (p[0], "route-method") && p[1])
{
VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);

View File

@ -327,7 +327,7 @@ static int
plugin_call_item (const struct plugin *p,
void *per_client_context,
const int type,
const char *args,
const struct argv *av,
struct openvpn_plugin_string_list **retlist,
const char **envp)
{
@ -340,18 +340,18 @@ plugin_call_item (const struct plugin *p,
if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type)))
{
struct gc_arena gc = gc_new ();
const char **argv = make_arg_array (p->so_pathname, args, &gc);
struct argv a = argv_insert_head (av, p->so_pathname);
dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type));
plugin_show_args_env (D_PLUGIN_DEBUG, argv, envp);
plugin_show_args_env (D_PLUGIN_DEBUG, (const char **)a.argv, envp);
/*
* Call the plugin work function
*/
if (p->func2)
status = (*p->func2)(p->plugin_handle, type, argv, envp, per_client_context, retlist);
status = (*p->func2)(p->plugin_handle, type, (const char **)a.argv, envp, per_client_context, retlist);
else if (p->func1)
status = (*p->func1)(p->plugin_handle, type, argv, envp);
status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp);
else
ASSERT (0);
@ -366,6 +366,7 @@ plugin_call_item (const struct plugin *p,
status,
p->so_pathname);
argv_reset (&a);
gc_free (&gc);
}
return status;
@ -482,7 +483,7 @@ plugin_common_open (struct plugin_common *pc,
int i;
const char **envp;
envp = make_env_array (es, &gc);
envp = make_env_array (es, false, &gc);
if (pr)
plugin_return_init (pr);
@ -540,7 +541,7 @@ plugin_list_open (struct plugin_list *pl,
int
plugin_call (const struct plugin_list *pl,
const int type,
const char *args,
const struct argv *av,
struct plugin_return *pr,
struct env_set *es)
{
@ -560,14 +561,14 @@ plugin_call (const struct plugin_list *pl,
mutex_lock_static (L_PLUGIN);
setenv_del (es, "script_type");
envp = make_env_array (es, &gc);
envp = make_env_array (es, false, &gc);
for (i = 0; i < n; ++i)
{
const int status = plugin_call_item (&pl->common->plugins[i],
pl->per_client.per_client_context[i],
type,
args,
av,
pr ? &pr->list[i] : NULL,
envp);
switch (status)

View File

@ -116,7 +116,7 @@ struct plugin_list *plugin_list_inherit (const struct plugin_list *src);
int plugin_call (const struct plugin_list *pl,
const int type,
const char *args,
const struct argv *av,
struct plugin_return *pr,
struct env_set *es);
@ -168,7 +168,7 @@ plugin_defined (const struct plugin_list *pl, const int type)
static inline int
plugin_call (const struct plugin_list *pl,
const int type,
const char *args,
const struct argv *av,
struct plugin_return *pr,
struct env_set *es)
{

184
plugin/examples/log.c Normal file
View File

@ -0,0 +1,184 @@
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
*
* Copyright (C) 2002-2008 Telethra, Inc. <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING included with this
* distribution); if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This plugin is similar to simple.c, except it also logs extra information
* to stdout for every plugin method called by OpenVPN.
*
* See the README file for build instructions.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "openvpn-plugin.h"
/*
* Our context, where we keep our state.
*/
struct plugin_context {
const char *username;
const char *password;
};
/*
* Given an environmental variable name, search
* the envp array for its value, returning it
* if found or NULL otherwise.
*/
static const char *
get_env (const char *name, const char *envp[])
{
if (envp)
{
int i;
const int namelen = strlen (name);
for (i = 0; envp[i]; ++i)
{
if (!strncmp (envp[i], name, namelen))
{
const char *cp = envp[i] + namelen;
if (*cp == '=')
return cp + 1;
}
}
}
return NULL;
}
OPENVPN_EXPORT openvpn_plugin_handle_t
openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
{
struct plugin_context *context;
/*
* Allocate our context
*/
context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
/*
* Set the username/password we will require.
*/
context->username = "foo";
context->password = "bar";
/*
* Which callbacks to intercept.
*/
*type_mask =
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL);
return (openvpn_plugin_handle_t) context;
}
void
show (const int type, const char *argv[], const char *envp[])
{
size_t i;
switch (type)
{
case OPENVPN_PLUGIN_UP:
printf ("OPENVPN_PLUGIN_UP\n");
break;
case OPENVPN_PLUGIN_DOWN:
printf ("OPENVPN_PLUGIN_DOWN\n");
break;
case OPENVPN_PLUGIN_ROUTE_UP:
printf ("OPENVPN_PLUGIN_ROUTE_UP\n");
break;
case OPENVPN_PLUGIN_IPCHANGE:
printf ("OPENVPN_PLUGIN_IPCHANGE\n");
break;
case OPENVPN_PLUGIN_TLS_VERIFY:
printf ("OPENVPN_PLUGIN_TLS_VERIFY\n");
break;
case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
break;
case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
break;
case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
break;
case OPENVPN_PLUGIN_LEARN_ADDRESS:
printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
break;
case OPENVPN_PLUGIN_TLS_FINAL:
printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
break;
default:
printf ("OPENVPN_PLUGIN_?\n");
break;
}
printf ("ARGV\n");
for (i = 0; argv[i] != NULL; ++i)
printf ("%d '%s'\n", (int)i, argv[i]);
printf ("ENVP\n");
for (i = 0; envp[i] != NULL; ++i)
printf ("%d '%s'\n", (int)i, envp[i]);
}
OPENVPN_EXPORT int
openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
{
struct plugin_context *context = (struct plugin_context *) handle;
show (type, argv, envp);
/* check entered username/password against what we require */
if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
{
/* get username/password from envp string array */
const char *username = get_env ("username", envp);
const char *password = get_env ("password", envp);
if (username && !strcmp (username, context->username)
&& password && !strcmp (password, context->password))
return OPENVPN_PLUGIN_FUNC_SUCCESS;
else
return OPENVPN_PLUGIN_FUNC_ERROR;
}
else
return OPENVPN_PLUGIN_FUNC_SUCCESS;
}
OPENVPN_EXPORT void
openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
{
struct plugin_context *context = (struct plugin_context *) handle;
free (context);
}

159
route.c
View File

@ -34,6 +34,7 @@
#include "misc.h"
#include "socket.h"
#include "manage.h"
#include "win32.h"
#include "memdbg.h"
@ -743,7 +744,7 @@ void
add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
struct buffer buf;
struct argv argv;
const char *network;
const char *netmask;
const char *gateway;
@ -753,7 +754,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
return;
gc_init (&gc);
buf = alloc_buf_gc (256, &gc);
argv_init (&argv);
network = print_in_addr_t (r->network, 0, &gc);
netmask = print_in_addr_t (r->netmask, 0, &gc);
@ -771,35 +772,38 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
buf_printf (&buf, "%s route add %s/%d via %s",
argv_printf (&argv, "%s route add %s/%d via %s",
iproute_path,
network,
count_netmask_bits(netmask),
gateway);
if (r->metric_defined)
buf_printf (&buf, " metric %d", r->metric);
argv_printf_cat (&argv, "metric %d", r->metric);
#else
buf_printf (&buf, ROUTE_PATH " add -net %s netmask %s gw %s",
argv_printf (&argv, "%s add -net %s netmask %s gw %s",
ROUTE_PATH,
network,
netmask,
gateway);
if (r->metric_defined)
buf_printf (&buf, " metric %d", r->metric);
argv_printf_cat (&argv, "metric %d", r->metric);
#endif /*CONFIG_FEATURE_IPROUTE*/
msg (D_ROUTE, "%s", BSTR (&buf));
status = system_check (BSTR (&buf), es, 0, "ERROR: Linux route add command failed");
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
#elif defined (WIN32)
buf_printf (&buf, ROUTE_PATH " ADD %s MASK %s %s",
network,
netmask,
gateway);
argv_printf (&argv, "%s%s ADD %s MASK %s %s",
get_win_sys_path(),
WIN_ROUTE_PATH_SUFFIX,
network,
netmask,
gateway);
if (r->metric_defined)
buf_printf (&buf, " METRIC %d", r->metric);
argv_printf_cat (&argv, "METRIC %d", r->metric);
msg (D_ROUTE, "%s", BSTR (&buf));
argv_msg (D_ROUTE, &argv);
if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
{
@ -809,7 +813,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
{
netcmd_semaphore_lock ();
status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed");
status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
netcmd_semaphore_release ();
}
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
@ -820,7 +824,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
{
msg (D_ROUTE, "Route addition fallback to route.exe");
netcmd_semaphore_lock ();
status = system_check (BSTR (&buf), es, 0, "ERROR: Windows route add command failed [adaptive]");
status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
netcmd_semaphore_release ();
}
}
@ -833,88 +837,93 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
/* example: route add 192.0.2.32 -netmask 255.255.255.224 somegateway */
buf_printf (&buf, ROUTE_PATH " add");
argv_printf (&argv, "%s add",
ROUTE_PATH);
#if 0
if (r->metric_defined)
buf_printf (&buf, " -rtt %d", r->metric);
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
buf_printf (&buf, " %s -netmask %s %s",
argv_printf_cat (&argv, "%s -netmask %s %s",
network,
netmask,
gateway);
msg (D_ROUTE, "%s", BSTR (&buf));
status = system_check (BSTR (&buf), es, 0, "ERROR: Solaris route add command failed");
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
#elif defined(TARGET_FREEBSD)
buf_printf (&buf, ROUTE_PATH " add");
argv_printf (&argv, "%s add",
ROUTE_PATH);
#if 0
if (r->metric_defined)
buf_printf (&buf, " -rtt %d", r->metric);
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
buf_printf (&buf, " -net %s %s %s",
argv_printf_cat (&argv, "-net %s %s %s",
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
status = system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route add command failed");
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
#elif defined(TARGET_DRAGONFLY)
buf_printf (&buf, ROUTE_PATH " add");
argv_printf (&argv, "%s add",
ROUTE_PATH);
#if 0
if (r->metric_defined)
buf_printf (&buf, " -rtt %d", r->metric);
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
buf_printf (&buf, " -net %s %s %s",
argv_printf_cat (&argv, "-net %s %s %s",
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
status = system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route add command failed");
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
#elif defined(TARGET_DARWIN)
buf_printf (&buf, ROUTE_PATH " add");
argv_printf (&argv, "%s add",
ROUTE_PATH);
#if 0
if (r->metric_defined)
buf_printf (&buf, " -rtt %d", r->metric);
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
buf_printf (&buf, " -net %s %s %s",
argv_printf_cat (&argv, "-net %s %s %s",
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
status = system_check (BSTR (&buf), es, 0, "ERROR: OS X route add command failed");
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
buf_printf (&buf, ROUTE_PATH " add");
argv_printf (&argv, "%s add",
ROUTE_PATH);
#if 0
if (r->metric_defined)
buf_printf (&buf, " -rtt %d", r->metric);
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
buf_printf (&buf, " -net %s %s -netmask %s",
argv_printf_cat (&argv, "-net %s %s -netmask %s",
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
status = system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
@ -922,6 +931,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
done:
r->defined = status;
argv_reset (&argv);
gc_free (&gc);
}
@ -929,7 +939,7 @@ static void
delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
struct gc_arena gc;
struct buffer buf;
struct argv argv;
const char *network;
const char *netmask;
const char *gateway;
@ -938,37 +948,40 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
return;
gc_init (&gc);
argv_init (&argv);
buf = alloc_buf_gc (256, &gc);
network = print_in_addr_t (r->network, 0, &gc);
netmask = print_in_addr_t (r->netmask, 0, &gc);
gateway = print_in_addr_t (r->gateway, 0, &gc);
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
buf_printf (&buf, "%s route del %s/%d",
argv_printf (&argv, "%s route del %s/%d",
iproute_path,
network,
count_netmask_bits(netmask));
#else
buf_printf (&buf, ROUTE_PATH " del -net %s netmask %s",
argv_printf (&argv, "%s del -net %s netmask %s",
ROUTE_PATH,
network,
netmask);
#endif /*CONFIG_FEATURE_IPROUTE*/
if (r->metric_defined)
buf_printf (&buf, " metric %d", r->metric);
msg (D_ROUTE, "%s", BSTR (&buf));
system_check (BSTR (&buf), es, 0, "ERROR: Linux route delete command failed");
argv_printf_cat (&argv, "metric %d", r->metric);
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
#elif defined (WIN32)
buf_printf (&buf, ROUTE_PATH " DELETE %s MASK %s %s",
network,
netmask,
gateway);
argv_printf (&argv, "%s%s DELETE %s MASK %s %s",
get_win_sys_path(),
WIN_ROUTE_PATH_SUFFIX,
network,
netmask,
gateway);
msg (D_ROUTE, "%s", BSTR (&buf));
argv_msg (D_ROUTE, &argv);
if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
{
@ -978,7 +991,7 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
{
netcmd_semaphore_lock ();
system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed");
openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed");
netcmd_semaphore_release ();
}
else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
@ -989,7 +1002,7 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
{
msg (D_ROUTE, "Route deletion fallback to route.exe");
netcmd_semaphore_lock ();
system_check (BSTR (&buf), es, 0, "ERROR: Windows route delete command failed [adaptive]");
openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed [adaptive]");
netcmd_semaphore_release ();
}
}
@ -1000,58 +1013,64 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
#elif defined (TARGET_SOLARIS)
buf_printf (&buf, ROUTE_PATH " delete %s -netmask %s %s",
argv_printf (&argv, "%s delete %s -netmask %s %s",
ROUTE_PATH,
network,
netmask,
gateway);
msg (D_ROUTE, "%s", BSTR (&buf));
system_check (BSTR (&buf), es, 0, "ERROR: Solaris route delete command failed");
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete command failed");
#elif defined(TARGET_FREEBSD)
buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
argv_printf (&argv, "%s delete -net %s %s %s",
ROUTE_PATH,
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
system_check (BSTR (&buf), es, 0, "ERROR: FreeBSD route delete command failed");
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route delete command failed");
#elif defined(TARGET_DRAGONFLY)
buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
argv_printf (&argv, "%s delete -net %s %s %s",
ROUTE_PATH,
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
system_check (BSTR (&buf), es, 0, "ERROR: DragonFly route delete command failed");
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route delete command failed");
#elif defined(TARGET_DARWIN)
buf_printf (&buf, ROUTE_PATH " delete -net %s %s %s",
argv_printf (&argv, "%s delete -net %s %s %s",
ROUTE_PATH,
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
system_check (BSTR (&buf), es, 0, "ERROR: OS X route delete command failed");
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
buf_printf (&buf, ROUTE_PATH " delete -net %s %s -netmask %s",
argv_printf (&argv, "%s delete -net %s %s -netmask %s",
ROUTE_PATH,
network,
gateway,
netmask);
msg (D_ROUTE, "%s", BSTR (&buf));
system_check (BSTR (&buf), es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
#endif
argv_reset (&argv);
gc_free (&gc);
}

View File

@ -1480,6 +1480,22 @@ setenv_trusted (struct env_set *es, const struct link_socket_info *info)
setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT);
}
static void
ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc)
{
const char *ip = print_sockaddr_ex (&info->lsa->actual.dest, NULL, 0, gc);
const char *port = print_sockaddr_ex (&info->lsa->actual.dest, NULL, PS_DONT_SHOW_ADDR|PS_SHOW_PORT, gc);
if (include_cmd)
argv_printf (argv, "%s %s %s",
info->ipchange_command,
ip,
port);
else
argv_printf (argv, "%s %s",
ip,
port);
}
void
link_socket_connection_initiated (const struct buffer *buf,
struct link_socket_info *info,
@ -1508,20 +1524,21 @@ link_socket_connection_initiated (const struct buffer *buf,
/* Process --ipchange plugin */
if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE))
{
const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc);
if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
struct argv argv = argv_new ();
ipchange_fmt (false, &argv, info, &gc);
if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
msg (M_WARN, "WARNING: ipchange plugin call failed");
argv_reset (&argv);
}
/* Process --ipchange option */
if (info->ipchange_command)
{
struct buffer out = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
setenv_str (es, "script_type", "ipchange");
buf_printf (&out, "%s %s",
info->ipchange_command,
print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc));
system_check (BSTR (&out), es, S_SCRIPT, "ip-change command failed");
ipchange_fmt (true, &argv, info, &gc);
openvpn_execve_check (&argv, es, S_SCRIPT, "ip-change command failed");
argv_reset (&argv);
}
gc_free (&gc);
@ -1791,7 +1808,8 @@ print_sockaddr_ex (const struct openvpn_sockaddr *addr,
const int port = ntohs (addr->sa.sin_port);
mutex_lock_static (L_INET_NTOA);
buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
if (!(flags & PS_DONT_SHOW_ADDR))
buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
mutex_unlock_static (L_INET_NTOA);
if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))

View File

@ -325,6 +325,7 @@ void link_socket_close (struct link_socket *sock);
#define PS_SHOW_PORT_IF_DEFINED (1<<0)
#define PS_SHOW_PORT (1<<1)
#define PS_SHOW_PKTINFO (1<<2)
#define PS_DONT_SHOW_ADDR (1<<3)
const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr,
const char* separator,

40
ssl.c
View File

@ -544,6 +544,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
struct tls_session *session;
const struct tls_options *opt;
const int max_depth = 8;
struct argv argv = argv_new ();
/* get the tls_session pointer */
ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
@ -689,16 +690,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
/* call --tls-verify plug-in(s) */
if (plugin_defined (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY))
{
char command[256];
struct buffer out;
int ret;
buf_set_write (&out, (uint8_t*)command, sizeof (command));
buf_printf (&out, "%d %s",
ctx->error_depth,
subject);
argv_printf (&argv, "%d %s",
ctx->error_depth,
subject);
ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, command, NULL, opt->es);
ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es);
if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
{
@ -716,19 +714,16 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
/* run --tls-verify script */
if (opt->verify_command)
{
char command[256];
struct buffer out;
int ret;
setenv_str (opt->es, "script_type", "tls-verify");
buf_set_write (&out, (uint8_t*)command, sizeof (command));
buf_printf (&out, "%s %d %s",
opt->verify_command,
ctx->error_depth,
subject);
dmsg (D_TLS_DEBUG, "TLS: executing verify command: %s", command);
ret = openvpn_system (command, opt->es, S_SCRIPT);
argv_printf (&argv, "%s %d %s",
opt->verify_command,
ctx->error_depth,
subject);
argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
ret = openvpn_execve (&argv, opt->es, S_SCRIPT);
if (system_ok (ret))
{
@ -738,7 +733,7 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
else
{
if (!system_executed (ret))
msg (M_ERR, "Verify command failed to execute: %s", command);
argv_msg_prefix (M_ERR, &argv, "Verify command failed to execute");
msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
ctx->error_depth, subject);
goto err; /* Reject connection */
@ -801,11 +796,13 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
session->verified = true;
free (subject);
argv_reset (&argv);
return 1; /* Accept connection */
err:
ERR_clear_error ();
free (subject);
argv_reset (&argv);
return 0; /* Reject connection */
}
@ -2901,7 +2898,7 @@ static bool
verify_user_pass_script (struct tls_session *session, const struct user_pass *up)
{
struct gc_arena gc = gc_new ();
struct buffer cmd = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
const char *tmp_file = "";
int retval;
bool ret = false;
@ -2940,16 +2937,16 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
setenv_untrusted (session);
/* format command line */
buf_printf (&cmd, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
argv_printf (&argv, "%s %s", session->opt->auth_user_pass_verify_script, tmp_file);
/* call command */
retval = openvpn_system (BSTR (&cmd), session->opt->es, S_SCRIPT);
retval = openvpn_execve (&argv, session->opt->es, S_SCRIPT);
/* test return status of command */
if (system_ok (retval))
ret = true;
else if (!system_executed (retval))
msg (D_TLS_ERRORS, "TLS Auth Error: user-pass-verify script failed to execute: %s", BSTR (&cmd));
argv_msg_prefix (D_TLS_ERRORS, &argv, "TLS Auth Error: user-pass-verify script failed to execute");
if (!session->opt->auth_user_pass_verify_script_via_file)
setenv_del (session->opt->es, "password");
@ -2963,6 +2960,7 @@ verify_user_pass_script (struct tls_session *session, const struct user_pass *up
if (strlen (tmp_file) > 0)
delete_file (tmp_file);
argv_reset (&argv);
gc_free (&gc);
return ret;
}

View File

@ -447,6 +447,14 @@ socket_defined (const socket_descriptor_t sd)
*/
#define USE_64_BIT_COUNTERS
/*
* Should we enable the use of execve() for calling subprocesses,
* instead of system()?
*/
#if defined(HAVE_EXECVE) && defined(HAVE_FORK)
#define ENABLE_EXECVE
#endif
/*
* Do we have point-to-multipoint capability?
*/

250
tun.c
View File

@ -39,6 +39,7 @@
#include "socket.h"
#include "manage.h"
#include "route.h"
#include "win32.h"
#include "memdbg.h"
@ -534,7 +535,9 @@ do_ifconfig (struct tuntap *tt,
const char *ifconfig_local = NULL;
const char *ifconfig_remote_netmask = NULL;
const char *ifconfig_broadcast = NULL;
char command_line[256];
struct argv argv;
argv_init (&argv);
/*
* We only handle TUN/TAP devices here, not --dev null devices.
@ -570,31 +573,31 @@ do_ifconfig (struct tuntap *tt,
/*
* Set the MTU for the device
*/
openvpn_snprintf (command_line, sizeof (command_line),
argv_printf (&argv,
"%s link set dev %s up mtu %d",
iproute_path,
actual,
tun_mtu
);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "Linux ip link set failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ip link set failed");
if (tun) {
/*
* Set the address for the device
*/
openvpn_snprintf (command_line, sizeof (command_line),
argv_printf (&argv,
"%s addr add dev %s local %s peer %s",
iproute_path,
actual,
ifconfig_local,
ifconfig_remote_netmask
);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
} else {
openvpn_snprintf (command_line, sizeof (command_line),
argv_printf (&argv,
"%s addr add dev %s %s/%d broadcast %s",
iproute_path,
actual,
@ -602,30 +605,32 @@ do_ifconfig (struct tuntap *tt,
count_netmask_bits(ifconfig_remote_netmask),
ifconfig_broadcast
);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "Linux ip addr add failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
}
tt->did_ifconfig = true;
#else
if (tun)
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s pointopoint %s mtu %d",
argv_printf (&argv,
"%s %s %s pointopoint %s mtu %d",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
else
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
argv_printf (&argv,
"%s %s %s netmask %s mtu %d broadcast %s",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu,
ifconfig_broadcast
);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "Linux ifconfig failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
tt->did_ifconfig = true;
#endif /*CONFIG_FEATURE_IPROUTE*/
@ -638,28 +643,30 @@ do_ifconfig (struct tuntap *tt,
*/
if (tun)
{
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s %s mtu %d up",
argv_printf (&argv,
"%s %s %s %s mtu %d up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
msg (M_INFO, "%s", command_line);
if (!system_check (command_line, es, 0, "Solaris ifconfig phase-1 failed"))
argv_msg (M_INFO, &argv);
if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
solaris_error_close (tt, es, actual);
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s netmask 255.255.255.255",
argv_printf (&argv,
"%s %s netmask 255.255.255.255",
IFCONFIG_PATH,
actual
);
}
else
no_tap_ifconfig ();
msg (M_INFO, "%s", command_line);
if (!system_check (command_line, es, 0, "Solaris ifconfig phase-2 failed"))
argv_msg (M_INFO, &argv);
if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
solaris_error_close (tt, es, actual);
tt->did_ifconfig = true;
@ -672,45 +679,50 @@ do_ifconfig (struct tuntap *tt,
* (if it exists), and re-ifconfig. Let me know if you know a better way.
*/
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s destroy",
argv_printf (&argv,
"%s %s destroy",
IFCONFIG_PATH,
actual);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, 0, NULL);
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s create",
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, 0, NULL);
argv_printf (&argv,
"%s %s create",
IFCONFIG_PATH,
actual);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, 0, NULL);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, 0, NULL);
msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if (tun)
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
argv_printf (&argv,
"%s %s %s %s mtu %d netmask 255.255.255.255 up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
else
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s link0",
argv_printf (&argv,
"%s %s %s netmask %s mtu %d broadcast %s link0",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu,
ifconfig_broadcast
);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "OpenBSD ifconfig failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
tt->did_ifconfig = true;
#elif defined(TARGET_NETBSD)
if (tun)
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
argv_printf (&argv,
"%s %s %s %s mtu %d netmask 255.255.255.255 up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
@ -722,16 +734,17 @@ do_ifconfig (struct tuntap *tt,
* so we don't need the "link0" extra parameter to specify we want to do
* tunneling at the ethernet level
*/
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s netmask %s mtu %d broadcast %s",
argv_printf (&argv,
"%s %s %s netmask %s mtu %d broadcast %s",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu,
ifconfig_broadcast
);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "NetBSD ifconfig failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
tt->did_ifconfig = true;
#elif defined(TARGET_DARWIN)
@ -740,18 +753,20 @@ do_ifconfig (struct tuntap *tt,
* Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
*/
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s delete",
argv_printf (&argv,
"%s %s delete",
IFCONFIG_PATH,
actual);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, 0, NULL);
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, 0, NULL);
msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if (tun)
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
argv_printf (&argv,
"%s %s %s %s mtu %d netmask 255.255.255.255 up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
@ -760,8 +775,9 @@ do_ifconfig (struct tuntap *tt,
else
{
if (tt->topology == TOP_SUBNET)
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
argv_printf (&argv,
"%s %s %s %s netmask %s mtu %d up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_local,
@ -769,16 +785,17 @@ do_ifconfig (struct tuntap *tt,
tun_mtu
);
else
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s netmask %s mtu %d up",
argv_printf (&argv,
"%s %s %s netmask %s mtu %d up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
tun_mtu
);
}
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "Mac OS X ifconfig failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "Mac OS X ifconfig failed");
tt->did_ifconfig = true;
/* Add a network route for the local tun interface */
@ -797,8 +814,9 @@ do_ifconfig (struct tuntap *tt,
/* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
if (tun)
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s %s mtu %d netmask 255.255.255.255 up",
argv_printf (&argv,
"%s %s %s %s mtu %d netmask 255.255.255.255 up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
@ -806,8 +824,9 @@ do_ifconfig (struct tuntap *tt,
);
else {
if (tt->topology == TOP_SUBNET)
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s %s netmask %s mtu %d up",
argv_printf (&argv,
"%s %s %s %s netmask %s mtu %d up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_local,
@ -815,8 +834,9 @@ do_ifconfig (struct tuntap *tt,
tun_mtu
);
else
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s %s netmask %s mtu %d up",
argv_printf (&argv,
"%s %s %s netmask %s mtu %d up",
IFCONFIG_PATH,
actual,
ifconfig_local,
ifconfig_remote_netmask,
@ -824,8 +844,8 @@ do_ifconfig (struct tuntap *tt,
);
}
msg (M_INFO, "%s", command_line);
system_check (command_line, es, S_FATAL, "FreeBSD ifconfig failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig failed");
tt->did_ifconfig = true;
/* Add a network route for the local tun interface */
@ -882,6 +902,7 @@ do_ifconfig (struct tuntap *tt,
#else
msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script.");
#endif
argv_reset (&argv);
}
gc_free (&gc);
}
@ -1216,13 +1237,14 @@ close_tun (struct tuntap *tt)
{
if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
{
char command_line[256];
struct argv argv;
struct gc_arena gc = gc_new ();
argv_init (&argv);
#ifdef CONFIG_FEATURE_IPROUTE
if (is_tun_p2p (tt))
{
openvpn_snprintf (command_line, sizeof (command_line),
argv_printf (&argv,
"%s addr del dev %s local %s peer %s",
iproute_path,
tt->actual_name,
@ -1232,7 +1254,7 @@ close_tun (struct tuntap *tt)
}
else
{
openvpn_snprintf (command_line, sizeof (command_line),
argv_printf (&argv,
"%s addr del dev %s %s/%d",
iproute_path,
tt->actual_name,
@ -1241,15 +1263,17 @@ close_tun (struct tuntap *tt)
);
}
#else
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s 0.0.0.0",
argv_printf (&argv,
"%s %s 0.0.0.0",
IFCONFIG_PATH,
tt->actual_name
);
#endif
msg (M_INFO, "%s", command_line);
system_check (command_line, NULL, 0, "Linux ip addr del failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed");
argv_reset (&argv);
gc_free (&gc);
}
close_tun_generic (tt);
@ -1471,16 +1495,19 @@ close_tun (struct tuntap *tt)
static void
solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
{
char command_line[256];
struct argv argv;
argv_init (&argv);
openvpn_snprintf (command_line, sizeof (command_line),
IFCONFIG_PATH " %s unplumb",
argv_printf (&argv,
"%s %s unplumb",
IFCONFIG_PATH,
actual);
msg (M_INFO, "%s", command_line);
system_check (command_line, es, 0, "Solaris ifconfig unplumb failed");
argv_msg (M_INFO, &argv);
openvpn_execve_check (&argv, es, 0, "Solaris ifconfig unplumb failed");
close_tun (tt);
msg (M_FATAL, "Solaris ifconfig failed");
argv_reset (&argv);
}
int
@ -3275,7 +3302,7 @@ dhcp_renew (const struct tuntap *tt)
*/
static void
netsh_command (const char *cmd, int n)
netsh_command (const struct argv *a, int n)
{
int i;
for (i = 0; i < n; ++i)
@ -3283,8 +3310,8 @@ netsh_command (const char *cmd, int n)
bool status;
openvpn_sleep (1);
netcmd_semaphore_lock ();
msg (M_INFO, "NETSH: %s", cmd);
status = system_check (cmd, NULL, 0, "ERROR: netsh command failed");
argv_msg_prefix (M_INFO, a, "NETSH");
status = openvpn_execve_check (a, NULL, 0, "ERROR: netsh command failed");
netcmd_semaphore_release ();
if (status)
return;
@ -3376,7 +3403,7 @@ netsh_ifconfig_options (const char *type,
const bool test_first)
{
struct gc_arena gc = gc_new ();
struct buffer out = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
bool delete_first = false;
/* first check if we should delete existing DNS/WINS settings from TAP interface */
@ -3391,11 +3418,12 @@ netsh_ifconfig_options (const char *type,
/* delete existing DNS/WINS settings from TAP interface */
if (delete_first)
{
buf_init (&out, 0);
buf_printf (&out, "netsh interface ip delete %s \"%s\" all",
type,
flex_name);
netsh_command (BSTR(&out), 2);
argv_printf (&argv, "%s%s interface ip delete %s %s all",
get_win_sys_path(),
NETSH_PATH_SUFFIX,
type,
flex_name);
netsh_command (&argv, 2);
}
/* add new DNS/WINS settings to TAP interface */
@ -3407,15 +3435,16 @@ netsh_ifconfig_options (const char *type,
if (delete_first || !test_first || !ip_addr_member_of (addr_list[i], current))
{
const char *fmt = count ?
"netsh interface ip add %s \"%s\" %s"
: "netsh interface ip set %s \"%s\" static %s";
"%s%s interface ip add %s %s %s"
: "%s%s 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);
argv_printf (&argv, fmt,
get_win_sys_path(),
NETSH_PATH_SUFFIX,
type,
flex_name,
print_in_addr_t (addr_list[i], 0, &gc));
netsh_command (&argv, 2);
++count;
}
@ -3429,6 +3458,7 @@ netsh_ifconfig_options (const char *type,
}
}
argv_reset (&argv);
gc_free (&gc);
}
@ -3458,7 +3488,7 @@ netsh_ifconfig (const struct tuntap_options *to,
const unsigned int flags)
{
struct gc_arena gc = gc_new ();
struct buffer out = alloc_buf_gc (256, &gc);
struct argv argv = argv_new ();
const IP_ADAPTER_INFO *ai = NULL;
const IP_PER_ADAPTER_INFO *pai = NULL;
@ -3482,14 +3512,15 @@ netsh_ifconfig (const struct tuntap_options *to,
else
{
/* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
buf_init (&out, 0);
buf_printf (&out,
"netsh interface ip set address \"%s\" static %s %s",
flex_name,
print_in_addr_t (ip, 0, &gc),
print_in_addr_t (netmask, 0, &gc));
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 (BSTR(&out), 4);
netsh_command (&argv, 4);
}
}
@ -3517,6 +3548,7 @@ netsh_ifconfig (const struct tuntap_options *to,
BOOL_CAST (flags & NI_TEST_FIRST));
}
argv_reset (&argv);
gc_free (&gc);
}
@ -3524,17 +3556,19 @@ static void
netsh_enable_dhcp (const struct tuntap_options *to,
const char *actual_name)
{
struct gc_arena gc = gc_new ();
struct buffer out = alloc_buf_gc (256, &gc);
struct argv argv;
argv_init (&argv);
/* example: netsh interface ip set address my-tap dhcp */
buf_printf (&out,
"netsh interface ip set address \"%s\" dhcp",
actual_name);
argv_printf (&argv,
"%s%s interface ip set address %s dhcp",
get_win_sys_path(),
NETSH_PATH_SUFFIX,
actual_name);
netsh_command (BSTR(&out), 4);
netsh_command (&argv, 4);
gc_free (&gc);
argv_reset (&argv);
}
/*

177
win32.c
View File

@ -35,6 +35,7 @@
#include "mtu.h"
#include "sig.h"
#include "win32.h"
#include "misc.h"
#include "memdbg.h"
@ -69,6 +70,11 @@ struct window_title window_title; /* GLOBAL*/
struct semaphore netcmd_semaphore; /* GLOBAL */
/*
* Windows system pathname such as c:\windows
*/
static char *win_sys_path = NULL; /* GLOBAL */
void
init_win32 (void)
{
@ -100,6 +106,7 @@ uninit_win32 (void)
window_title_restore (&window_title);
win32_signal_close (&win32_signal);
WSACleanup ();
free (win_sys_path);
}
void
@ -816,4 +823,174 @@ win_safe_filename (const char *fn)
return true;
}
/*
* Service functions for openvpn_execve
*/
static char *
env_block (const struct env_set *es)
{
if (es)
{
struct env_item *e;
char *ret;
char *p;
size_t nchars = 1;
for (e = es->list; e != NULL; e = e->next)
nchars += strlen (e->string) + 1;
ret = (char *) malloc (nchars);
check_malloc_return (ret);
p = ret;
for (e = es->list; e != NULL; e = e->next)
{
if (env_allowed (e->string))
{
strcpy (p, e->string);
p += strlen (e->string) + 1;
}
}
*p = '\0';
return ret;
}
else
return NULL;
}
static char *
cmd_line (const struct argv *a)
{
size_t nchars = 1;
size_t maxlen = 0;
size_t i;
struct buffer buf;
char *work = NULL;
if (!a)
return NULL;
for (i = 0; i < a->argc; ++i)
{
const char *arg = a->argv[i];
const size_t len = strlen (arg);
nchars += len + 3;
if (len > maxlen)
maxlen = len;
}
work = (char *) malloc (maxlen + 1);
check_malloc_return (work);
buf = alloc_buf (nchars);
for (i = 0; i < a->argc; ++i)
{
const char *arg = a->argv[i];
strcpy (work, arg);
string_mod (work, CC_PRINT, CC_DOUBLE_QUOTE|CC_CRLF, '_');
if (i)
buf_printf (&buf, " ");
if (string_class (work, CC_ANY, CC_SPACE))
buf_printf (&buf, "%s", work);
else
buf_printf (&buf, "\"%s\"", work);
}
free (work);
return BSTR(&buf);
}
/*
* Attempt to simulate fork/execve on Windows
*/
int
openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
{
int ret = -1;
if (a && a->argv[0])
{
if (openvpn_execve_allowed (flags))
{
STARTUPINFO start_info;
PROCESS_INFORMATION proc_info;
char *env = env_block (es);
char *cl = cmd_line (a);
char *cmd = a->argv[0];
CLEAR (start_info);
CLEAR (proc_info);
/* fill in STARTUPINFO struct */
GetStartupInfo(&start_info);
start_info.cb = sizeof(start_info);
start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
start_info.wShowWindow = SW_HIDE;
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
start_info.hStdOutput = start_info.hStdError = GetStdHandle(STD_OUTPUT_HANDLE);
if (CreateProcess (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info))
{
DWORD exit_status = 0;
CloseHandle (proc_info.hThread);
WaitForSingleObject (proc_info.hProcess, INFINITE);
if (GetExitCodeProcess (proc_info.hProcess, &exit_status))
ret = (int)exit_status;
else
msg (M_WARN|M_ERRNO, "openvpn_execve: GetExitCodeProcess %s failed", cmd);
CloseHandle (proc_info.hProcess);
}
else
{
msg (M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %s failed", cmd);
}
free (cl);
free (env);
}
else
{
msg (M_WARN, "openvpn_execve: external program may not be called due to setting of --script-security level");
}
}
else
{
msg (M_WARN, "openvpn_execve: called with empty argv");
}
return ret;
}
char *
get_win_sys_path (void)
{
ASSERT (win_sys_path);
return win_sys_path;
}
void
set_win_sys_path (const char *newpath, struct env_set *es)
{
free (win_sys_path);
win_sys_path = string_alloc (newpath, NULL);
setenv_str (es, SYS_PATH_ENV_VAR_NAME, win_sys_path); /* route.exe needs this */
}
void
set_win_sys_path_via_env (struct env_set *es)
{
char buf[256];
DWORD status = GetEnvironmentVariable (SYS_PATH_ENV_VAR_NAME, buf, sizeof(buf));
if (!status)
msg (M_ERR, "Cannot find environmental variable %s", SYS_PATH_ENV_VAR_NAME);
if (status > sizeof (buf) - 1)
msg (M_FATAL, "String overflow attempting to read environmental variable %s", SYS_PATH_ENV_VAR_NAME);
set_win_sys_path (buf, es);
}
void
env_set_add_win32 (struct env_set *es)
{
set_win_sys_path (DEFAULT_WIN_SYS_PATH, es);
}
#endif

15
win32.h
View File

@ -28,6 +28,12 @@
#include "mtu.h"
/* location of executables */
#define SYS_PATH_ENV_VAR_NAME "SystemRoot" /* environmental variable name that normally contains the system path */
#define DEFAULT_WIN_SYS_PATH "C:\\WINDOWS" /* --win-sys default value */
#define NETSH_PATH_SUFFIX "\\system32\\netsh.exe"
#define WIN_ROUTE_PATH_SUFFIX "\\system32\\route.exe"
/*
* Win32-specific OpenVPN code, targetted at the mingw
* development environment.
@ -250,5 +256,14 @@ bool init_security_attributes_allow_all (struct security_attributes *obj);
/* return true if filename is safe to be used on Windows */
bool win_safe_filename (const char *fn);
/* add constant environmental variables needed by Windows */
struct env_set;
void env_set_add_win32 (struct env_set *es);
/* get and set the current windows system path */
void set_win_sys_path (const char *newpath, struct env_set *es);
void set_win_sys_path_via_env (struct env_set *es);
char *get_win_sys_path (void);
#endif
#endif