I did some tests yesterday and this syntax works with arbitrary interpreters and scripts:
script-security 2 up 'c:\\Windows\\System32\\wscript.exe c:\\Program\ Files\\OpenVPN\\config\\test.vbs' For test details, look here: <https://community.openvpn.net/openvpn/ticket/228#comment:4> -- Samuli Seppänen Community Manager OpenVPN Technologies, Inc irc freenode net: mattock > From: David Sommerseth <dav...@redhat.com> > > This patch removes the support for the system() call, and enforces the usage > of execve() > on the *nix platform and CreateProcessW() on Windows. This is to enhance the > overall > security when calling external scripts. Using system() is prone to shell > expansions, > which may lead to security breaches. Which is also why the execve() approach > has > been the default since commit a82813527551f0e79c6d6ed5a9c1162e3c171bcf which > re-introduced the system() in Nov. 2008. > > After having asked on the mailing list and checked around on the IRC > channels, the > genereal consensus is that very few uses system() these days. > > The only annoyance I've been made aware of is that this will now require > adding a full > path to the script interpreter together with the script, and not just put in > the > script name alone. But to just use the script name in Windows, you had to > configure > --script-security with the 'system' flag earlier too. So my conclusion is > that it's > better to add a full path to the script interpreter in Windows and raise the > overal > security with OpenVPN, than to continue to have a possible potentially risky > OpenVPN configuration just to make life "easier" for Windows script users. > > Removal of the system() call, also solves a nasty bug related to the usage of > putenv() > on the *nix platforms. > > For more information please see: > http://thread.gmane.org/gmane.network.openvpn.devel/7090 > https://community.openvpn.net/openvpn/ticket/228 > > Trac-ticket: 228 > Signed-off-by: David Sommerseth <dav...@redhat.com> > --- > doc/openvpn.8 | 48 ++++++++++++------ > src/openvpn/init.c | 3 -- > src/openvpn/misc.c | 98 ++++++++----------------------------- > src/openvpn/misc.h | 5 -- > src/openvpn/options.c | 16 +----- > src/openvpn/platform.c | 27 +--------- > src/openvpn/platform.h | 4 +- > src/openvpn/win32.c | 127 > +++++++++++++----------------------------------- > 8 files changed, 89 insertions(+), 239 deletions(-) > > diff --git a/doc/openvpn.8 b/doc/openvpn.8 > index aa653ec..2ed5201 100644 > --- a/doc/openvpn.8 > +++ b/doc/openvpn.8 > @@ -1886,7 +1886,7 @@ is a safety precaution to prevent a LD_PRELOAD style > attack > from a malicious or compromised server. > .\"********************************************************* > .TP > -.B \-\-script-security level [method] > +.B \-\-script-security level > This directive offers policy-level control over OpenVPN's usage of external > programs > and scripts. Lower > .B level > @@ -1905,24 +1905,40 @@ Allow calling of built-in executables and > user-defined scripts. > .B 3 \-\- > Allow passwords to be passed to scripts via environmental variables > (potentially unsafe). > > -The > +OpenVPN releases before v2.3 also supported a > .B method > -parameter indicates how OpenVPN should call external commands and scripts. > -Settings for > -.B method: > +flag which indicated how OpenVPN should call external commands and scripts. > This > +could be either > +.B execve > +or > +.B system. > +As of OpenVPN v2.3, this flag is no longer accepted. In most *nix > environments the execve() > +approach has been used without any issues. > + > +To run scripts in Windows in earlier OpenVPN > +versions you needed to either add a full path to the script interpreter > which can parse the > +script or use the > +.B system > +flag to run these scripts. As of OpenVPN v2.3 it is now a strict > requirement to have > +full path to the script interpreter when running non-executables files. > +This is not needed for executable files, such as .exe, .com, .bat or .cmd > files. For > +example, if you have a Visual Basic script, you must use this syntax now: > > -.B execve \-\- > -(default) Use execve() function on Unix family OSes and CreateProcess() on > Windows. > -.br > -.B system \-\- > -Use system() function (deprecated and less safe since the external program > command > -line is subject to shell expansion). > +.nf > +.ft 3 > +.in +4 > +\-\-up 'C:\\\\Windows\\\\System32\\\\wscript.exe C:\\\\Program\\ > Files\\\\OpenVPN\\\\config\\\\my-up-script.vbs' > +.in -4 > +.ft > +.fi > > -The > -.B \-\-script-security > -option was introduced in OpenVPN 2.1_rc9. For configuration file > compatibility > -with previous OpenVPN versions, use: > -.B \-\-script-security 3 system > +Please note the single quote marks and the escaping of the backslashes (\\) > and > +the space character. > + > +The reason the support for the > +.B system > +flag was removed is due to the security implications with shell expansions > +when executing scripts via the system() call. > .\"********************************************************* > .TP > .B \-\-disable-occ > diff --git a/src/openvpn/init.c b/src/openvpn/init.c > index 270ee6a..25d8225 100644 > --- a/src/openvpn/init.c > +++ b/src/openvpn/init.c > @@ -2487,9 +2487,6 @@ do_option_warnings (struct context *c) > msg (M_WARN, "WARNING: the current --script-security setting may allow > passwords to be passed to scripts via environmental variables"); > else > msg (M_WARN, "NOTE: " PACKAGE_NAME " 2.1 requires '--script-security 2' > or higher to call user-defined scripts or executables"); > - > - if (script_method == SM_SYSTEM) > - msg (M_WARN, "NOTE: --script-security method='system' is deprecated due > to the fact that passed parameters will be subject to shell expansion"); > } > > static void > diff --git a/src/openvpn/misc.c b/src/openvpn/misc.c > index d2882d8..53f6d03 100644 > --- a/src/openvpn/misc.c > +++ b/src/openvpn/misc.c > @@ -53,9 +53,6 @@ const char *iproute_path = IPROUTE_PATH; /* GLOBAL */ > /* contains an SSEC_x value defined in misc.h */ > int script_security = SSEC_BUILT_IN; /* GLOBAL */ > > -/* contains SM_x value defined in misc.h */ > -int script_method = SM_EXECVE; /* GLOBAL */ > - > /* > * Pass tunnel endpoint and MTU parms to a user-supplied script. > * Used to execute the up/down script/plugins. > @@ -303,36 +300,25 @@ openvpn_execve (const struct argv *a, const struct > env_set *es, const unsigned i > #if defined(ENABLE_FEATURE_EXECVE) > if (openvpn_execve_allowed (flags)) > { > - if (script_method == SM_EXECVE) > - { > - 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 */ > - msg (M_ERR, "openvpn_execve: unable to fork"); > - else /* parent side */ > - { > - if (waitpid (pid, &ret, 0) != pid) > - ret = -1; > - } > - } > - else if (script_method == SM_SYSTEM) > - { > - ret = openvpn_system (argv_system_str (a), es, flags); > - } > - else > - { > - ASSERT (0); > - } > - } > + 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 */ > + msg (M_ERR, "openvpn_execve: unable to fork"); > + else /* parent side */ > + { > + if (waitpid (pid, &ret, 0) != pid) > + ret = -1; > + } > + } > else if (!warn_shown && (script_security < SSEC_SCRIPTS)) > { > msg (M_WARN, SCRIPT_SECURITY_WARNING); > @@ -353,52 +339,6 @@ openvpn_execve (const struct argv *a, const struct > env_set *es, const unsigned i > #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; > - > - 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 = platform_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 (); > - 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 > -} > - > -/* > * Run execve() inside a fork(), duping stdout. Designed to replicate the > semantics of popen() 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. > diff --git a/src/openvpn/misc.h b/src/openvpn/misc.h > index b6da3f4..183898e 100644 > --- a/src/openvpn/misc.h > +++ b/src/openvpn/misc.h > @@ -96,7 +96,6 @@ int openvpn_popen (const struct argv *a, const struct > env_set *es); > 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); > -int openvpn_system (const char *command, const struct env_set *es, unsigned > int flags); > > static inline bool > openvpn_run_script (const struct argv *a, const struct env_set *es, const > unsigned int flags, const char *hook) > @@ -322,10 +321,6 @@ extern const char *iproute_path; > #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 */ > > -#define SM_EXECVE 0 /* call external programs with execve() or > CreateProcess() */ > -#define SM_SYSTEM 1 /* call external programs with system() */ > -extern int script_method; /* GLOBAL */ > - > /* return the next largest power of 2 */ > size_t adjust_power_of_2 (size_t u); > > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index edc9195..9baa4ff 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -248,7 +248,7 @@ static const char usage_message[] = > "--setenv name value : Set a custom environmental variable to pass to > script.\n" > "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to > allow\n" > " directives for future OpenVPN versions to be ignored.\n" > - "--script-security level mode : mode='execve' (default) or 'system', > level=\n" > + "--script-security level: Where level can be:\n" > " 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" > @@ -5293,20 +5293,6 @@ add_option (struct options *options, > { > VERIFY_PERMISSION (OPT_P_GENERAL); > script_security = atoi (p[1]); > - if (p[2]) > - { > - if (streq (p[2], "execve")) > - script_method = SM_EXECVE; > - else if (streq (p[2], "system")) > - script_method = SM_SYSTEM; > - else > - { > - msg (msglevel, "unknown --script-security method: %s", p[2]); > - goto err; > - } > - } > - else > - script_method = SM_EXECVE; > } > else if (streq (p[0], "mssfix")) > { > diff --git a/src/openvpn/platform.c b/src/openvpn/platform.c > index c79f680..e79de7a 100644 > --- a/src/openvpn/platform.c > +++ b/src/openvpn/platform.c > @@ -205,7 +205,7 @@ platform_chdir (const char* dir) > } > > /* > - * convert system() return into a success/failure value > + * convert execve() return into a success/failure value > */ > bool > platform_system_ok (int stat) > @@ -217,19 +217,6 @@ platform_system_ok (int stat) > #endif > } > > -/* > - * did system() call execute the given command? > - */ > -bool > -platform_system_executed (int stat) > -{ > -#ifdef WIN32 > - return stat != -1; > -#else > - return stat != -1 && WEXITSTATUS (stat) != 127; > -#endif > -} > - > int > platform_access (const char *path, int mode) > { > @@ -288,18 +275,6 @@ platform_unlink (const char *filename) > #endif > } > > -int platform_system(const char *command) { > - int ret; > -#ifdef WIN32 > - struct gc_arena gc = gc_new (); > - ret = _wsystem (wide_string (command, &gc)); > - gc_free (&gc); > -#else > - ret = system (command); > -#endif > - return ret; > -} > - > int platform_putenv(char *string) > { > int status; > diff --git a/src/openvpn/platform.h b/src/openvpn/platform.h > index 7bd2067..7c0a4d7 100644 > --- a/src/openvpn/platform.h > +++ b/src/openvpn/platform.h > @@ -113,10 +113,8 @@ void platform_mlockall (bool print_msg); /* Disable > paging */ > > int platform_chdir (const char* dir); > > -/* interpret the status code returned by system()/execve() */ > +/* interpret the status code returned by execve() */ > bool platform_system_ok (int stat); > -bool platform_system_executed (int stat); > -int platform_system(const char *command); > > int platform_access (const char *path, int mode); > > diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c > index d00088e..2db96a8 100644 > --- a/src/openvpn/win32.c > +++ b/src/openvpn/win32.c > @@ -82,51 +82,6 @@ struct semaphore netcmd_semaphore; /* GLOBAL */ > */ > static char *win_sys_path = NULL; /* GLOBAL */ > > -/* > - * Configure PATH. On Windows, sometimes PATH is not set correctly > - * by default. > - */ > -static void > -configure_win_path (void) > -{ > - static bool done = false; /* GLOBAL */ > - if (!done) > - { > - FILE *fp; > - fp = fopen ("c:\\windows\\system32\\route.exe", "rb"); > - if (fp) > - { > - const int bufsiz = 4096; > - struct gc_arena gc = gc_new (); > - struct buffer oldpath = alloc_buf_gc (bufsiz, &gc); > - struct buffer newpath = alloc_buf_gc (bufsiz, &gc); > - const char* delim = ";"; > - DWORD status; > - fclose (fp); > - status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), > (DWORD)BCAP(&oldpath)); > -#if 0 > - status = 0; > -#endif > - if (!status) > - { > - *BPTR(&oldpath) = '\0'; > - delim = ""; > - } > - buf_printf (&newpath, > "C:\\WINDOWS\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem%s%s", > - delim, > - BSTR(&oldpath)); > - SetEnvironmentVariable ("PATH", BSTR(&newpath)); > -#if 0 > - status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), > (DWORD)BCAP(&oldpath)); > - if (status > 0) > - printf ("PATH: %s\n", BSTR(&oldpath)); > -#endif > - gc_free (&gc); > - done = true; > - } > - } > -} > - > void > init_win32 (void) > { > @@ -907,53 +862,41 @@ openvpn_execve (const struct argv *a, const struct > env_set *es, const unsigned i > { > if (openvpn_execve_allowed (flags)) > { > - if (script_method == SM_EXECVE) > - { > - struct gc_arena gc = gc_new (); > - STARTUPINFOW start_info; > - PROCESS_INFORMATION proc_info; > - > - char *env = env_block (es); > - WCHAR *cl = wide_cmd_line (a, &gc); > - WCHAR *cmd = wide_string (a->argv[0], &gc); > - > - CLEAR (start_info); > - CLEAR (proc_info); > - > - /* fill in STARTUPINFO struct */ > - GetStartupInfoW(&start_info); > - start_info.cb = sizeof(start_info); > - start_info.dwFlags = STARTF_USESHOWWINDOW; > - start_info.wShowWindow = SW_HIDE; > - > - if (CreateProcessW (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 (env); > - gc_free (&gc); > - } > - else if (script_method == SM_SYSTEM) > - { > - configure_win_path (); > - ret = openvpn_system (argv_system_str (a), es, flags); > - } > - else > - { > - ASSERT (0); > - } > - } > + struct gc_arena gc = gc_new (); > + STARTUPINFOW start_info; > + PROCESS_INFORMATION proc_info; > + > + char *env = env_block (es); > + WCHAR *cl = wide_cmd_line (a, &gc); > + WCHAR *cmd = wide_string (a->argv[0], &gc); > + > + CLEAR (start_info); > + CLEAR (proc_info); > + > + /* fill in STARTUPINFO struct */ > + GetStartupInfoW(&start_info); > + start_info.cb = sizeof(start_info); > + start_info.dwFlags = STARTF_USESHOWWINDOW; > + start_info.wShowWindow = SW_HIDE; > + > + if (CreateProcessW (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 (env); > + gc_free (&gc); > + } > else if (!exec_warn && (script_security < SSEC_SCRIPTS)) > { > msg (M_WARN, SCRIPT_SECURITY_WARNING); > -- > 1.7.10.2 > > > ------------------------------------------------------------------------------ > The Windows 8 Center - In partnership with Sourceforge > Your idea - your app - 30 days. > Get started! > http://windows8center.sourceforge.net/ > what-html-developers-need-to-know-about-coding-windows-8-metro-style-apps/ > _______________________________________________ > Openvpn-devel mailing list > Openvpn-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/openvpn-devel