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