src/openvpn/win32.c
6fbf66fa
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6fbf66fa
  *
  *  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.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
 /*
ccb636c7
  * Win32-specific OpenVPN code, targeted at the mingw
6fbf66fa
  * development environment.
  */
c110b289
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
1bda73a7
 #include "syshead.h"
6fbf66fa
 
445b192a
 #ifdef _WIN32
6fbf66fa
 
 #include "buffer.h"
 #include "error.h"
 #include "mtu.h"
bf97c00f
 #include "run_command.h"
6fbf66fa
 #include "sig.h"
 #include "win32.h"
6a33a34d
 #include "openvpn-msg.h"
6fbf66fa
 
 #include "memdbg.h"
 
cdc65ea0
 #ifdef HAVE_VERSIONHELPERS_H
 #include <versionhelpers.h>
 #else
 #include "compat-versionhelpers.h"
 #endif
 
6a33a34d
 #include "block_dns.h"
38c85658
 
 /*
6a33a34d
  * WFP handle
38c85658
  */
 static HANDLE m_hEngineHandle = NULL; /* GLOBAL */
 
 /*
27aa8728
  * TAP adapter original metric value
  */
 static int tap_metric_v4 = -1; /* GLOBAL */
 static int tap_metric_v6 = -1; /* GLOBAL */
 
 /*
6fbf66fa
  * Windows internal socket API state (opaque).
  */
 static struct WSAData wsa_state; /* GLOBAL */
 
 /*
  * Should we call win32_pause() on program exit?
  */
 static bool pause_exit_enabled = false; /* GLOBAL */
 
 /*
  * win32_signal is used to get input from the keyboard
  * if we are running in a console, or get input from an
  * event object if we are running as a service.
  */
 
 struct win32_signal win32_signal; /* GLOBAL */
 
 /*
  * Save our old window title so we can restore
  * it on exit.
  */
 struct window_title window_title; /* GLOBAL*/
 
 /*
  * Special global semaphore used to protect network
  * shell commands from simultaneous instantiation.
  */
 
 struct semaphore netcmd_semaphore; /* GLOBAL */
 
5a2e9a25
 /*
  * Windows system pathname such as c:\windows
  */
 static char *win_sys_path = NULL; /* GLOBAL */
 
6fbf66fa
 void
81d882d5
 init_win32(void)
6fbf66fa
 {
81d882d5
     if (WSAStartup(0x0101, &wsa_state))
6fbf66fa
     {
81d882d5
         msg(M_ERR, "WSAStartup failed");
6fbf66fa
     }
81d882d5
     window_title_clear(&window_title);
     win32_signal_clear(&win32_signal);
6fbf66fa
 }
 
 void
81d882d5
 uninit_win32(void)
6fbf66fa
 {
81d882d5
     netcmd_semaphore_close();
     if (pause_exit_enabled)
     {
         if (win32_signal.mode == WSO_MODE_UNDEF)
         {
             struct win32_signal w;
             win32_signal_open(&w, WSO_FORCE_CONSOLE, NULL, false);
             win32_pause(&w);
             win32_signal_close(&w);
         }
         else
         {
             win32_pause(&win32_signal);
         }
     }
     window_title_restore(&window_title);
     win32_signal_close(&win32_signal);
     WSACleanup();
     free(win_sys_path);
6fbf66fa
 }
 
 void
81d882d5
 set_pause_exit_win32(void)
6fbf66fa
 {
81d882d5
     pause_exit_enabled = true;
6fbf66fa
 }
 
 bool
81d882d5
 init_security_attributes_allow_all(struct security_attributes *obj)
6fbf66fa
 {
81d882d5
     CLEAR(*obj);
6fbf66fa
 
81d882d5
     obj->sa.nLength = sizeof(SECURITY_ATTRIBUTES);
     obj->sa.lpSecurityDescriptor = &obj->sd;
     obj->sa.bInheritHandle = FALSE;
     if (!InitializeSecurityDescriptor(&obj->sd, SECURITY_DESCRIPTOR_REVISION))
     {
         return false;
     }
     if (!SetSecurityDescriptorDacl(&obj->sd, TRUE, NULL, FALSE))
     {
         return false;
     }
     return true;
6fbf66fa
 }
 
 void
81d882d5
 overlapped_io_init(struct overlapped_io *o,
                    const struct frame *frame,
                    BOOL event_state,
                    bool tuntap_buffer)  /* if true: tuntap buffer, if false: socket buffer */
6fbf66fa
 {
81d882d5
     CLEAR(*o);
6fbf66fa
 
81d882d5
     /* manual reset event, initially set according to event_state */
     o->overlapped.hEvent = CreateEvent(NULL, TRUE, event_state, NULL);
     if (o->overlapped.hEvent == NULL)
     {
         msg(M_ERR, "Error: overlapped_io_init: CreateEvent failed");
     }
6fbf66fa
 
81d882d5
     /* allocate buffer for overlapped I/O */
     alloc_buf_sock_tun(&o->buf_init, frame, tuntap_buffer, 0);
6fbf66fa
 }
 
 void
81d882d5
 overlapped_io_close(struct overlapped_io *o)
6fbf66fa
 {
81d882d5
     if (o->overlapped.hEvent)
6fbf66fa
     {
81d882d5
         if (!CloseHandle(o->overlapped.hEvent))
         {
             msg(M_WARN | M_ERRNO, "Warning: CloseHandle failed on overlapped I/O event object");
         }
6fbf66fa
     }
81d882d5
     free_buf(&o->buf_init);
6fbf66fa
 }
 
 char *
81d882d5
 overlapped_io_state_ascii(const struct overlapped_io *o)
6fbf66fa
 {
81d882d5
     switch (o->iostate)
6fbf66fa
     {
81d882d5
         case IOSTATE_INITIAL:
             return "0";
 
         case IOSTATE_QUEUED:
             return "Q";
 
         case IOSTATE_IMMEDIATE_RETURN:
             return "1";
6fbf66fa
     }
81d882d5
     return "?";
6fbf66fa
 }
 
 /*
  * Event-based notification of network events
  */
 
 void
81d882d5
 init_net_event_win32(struct rw_handle *event, long network_events, socket_descriptor_t sd, unsigned int flags)
6fbf66fa
 {
81d882d5
     /* manual reset events, initially set to unsignaled */
6fbf66fa
 
81d882d5
     /* initialize write event */
     if (!(flags & NE32_PERSIST_EVENT) || !event->write)
6fbf66fa
     {
81d882d5
         if (flags & NE32_WRITE_EVENT)
         {
             event->write = CreateEvent(NULL, TRUE, FALSE, NULL);
             if (event->write == NULL)
             {
                 msg(M_ERR, "Error: init_net_event_win32: CreateEvent (write) failed");
             }
         }
         else
         {
             event->write = NULL;
         }
6fbf66fa
     }
 
81d882d5
     /* initialize read event */
     if (!(flags & NE32_PERSIST_EVENT) || !event->read)
6fbf66fa
     {
81d882d5
         event->read = CreateEvent(NULL, TRUE, FALSE, NULL);
         if (event->read == NULL)
         {
             msg(M_ERR, "Error: init_net_event_win32: CreateEvent (read) failed");
         }
     }
 
     /* setup network events to change read event state */
     if (WSAEventSelect(sd, event->read, network_events) != 0)
     {
         msg(M_FATAL | M_ERRNO, "Error: init_net_event_win32: WSAEventSelect call failed");
6fbf66fa
     }
 }
 
 long
81d882d5
 reset_net_event_win32(struct rw_handle *event, socket_descriptor_t sd)
6fbf66fa
 {
81d882d5
     WSANETWORKEVENTS wne;
     if (WSAEnumNetworkEvents(sd, event->read, &wne) != 0)
     {
         msg(M_FATAL | M_ERRNO, "Error: reset_net_event_win32: WSAEnumNetworkEvents call failed");
         return 0; /* NOTREACHED */
     }
     else
6fbf66fa
     {
81d882d5
         return wne.lNetworkEvents;
6fbf66fa
     }
 }
 
 void
81d882d5
 close_net_event_win32(struct rw_handle *event, socket_descriptor_t sd, unsigned int flags)
6fbf66fa
 {
81d882d5
     if (event->read)
     {
         if (socket_defined(sd))
         {
             if (WSAEventSelect(sd, event->read, 0) != 0)
             {
                 msg(M_WARN | M_ERRNO, "Warning: close_net_event_win32: WSAEventSelect call failed");
             }
         }
         if (!ResetEvent(event->read))
         {
             msg(M_WARN | M_ERRNO, "Warning: ResetEvent (read) failed in close_net_event_win32");
         }
         if (!(flags & NE32_PERSIST_EVENT))
         {
             if (!CloseHandle(event->read))
             {
                 msg(M_WARN | M_ERRNO, "Warning: CloseHandle (read) failed in close_net_event_win32");
             }
             event->read = NULL;
         }
     }
 
     if (event->write)
     {
         if (!ResetEvent(event->write))
         {
             msg(M_WARN | M_ERRNO, "Warning: ResetEvent (write) failed in close_net_event_win32");
         }
         if (!(flags & NE32_PERSIST_EVENT))
         {
             if (!CloseHandle(event->write))
             {
                 msg(M_WARN | M_ERRNO, "Warning: CloseHandle (write) failed in close_net_event_win32");
             }
             event->write = NULL;
         }
6fbf66fa
     }
 }
 
 /*
  * struct net_event_win32
  */
 
 void
81d882d5
 net_event_win32_init(struct net_event_win32 *ne)
6fbf66fa
 {
81d882d5
     CLEAR(*ne);
     ne->sd = SOCKET_UNDEFINED;
6fbf66fa
 }
 
 void
81d882d5
 net_event_win32_start(struct net_event_win32 *ne, long network_events, socket_descriptor_t sd)
6fbf66fa
 {
81d882d5
     ASSERT(!socket_defined(ne->sd));
     ne->sd = sd;
     ne->event_mask = 0;
     init_net_event_win32(&ne->handle, network_events, sd, NE32_PERSIST_EVENT|NE32_WRITE_EVENT);
6fbf66fa
 }
 
 void
81d882d5
 net_event_win32_reset_write(struct net_event_win32 *ne)
6fbf66fa
 {
81d882d5
     BOOL status;
     if (ne->event_mask & FD_WRITE)
     {
         status = SetEvent(ne->handle.write);
     }
     else
     {
         status = ResetEvent(ne->handle.write);
     }
     if (!status)
     {
         msg(M_WARN | M_ERRNO, "Warning: SetEvent/ResetEvent failed in net_event_win32_reset_write");
     }
6fbf66fa
 }
 
 void
81d882d5
 net_event_win32_reset(struct net_event_win32 *ne)
6fbf66fa
 {
81d882d5
     ne->event_mask |= reset_net_event_win32(&ne->handle, ne->sd);
6fbf66fa
 }
 
 void
81d882d5
 net_event_win32_stop(struct net_event_win32 *ne)
6fbf66fa
 {
81d882d5
     if (net_event_win32_defined(ne))
     {
         close_net_event_win32(&ne->handle, ne->sd, NE32_PERSIST_EVENT);
     }
     ne->sd = SOCKET_UNDEFINED;
     ne->event_mask = 0;
6fbf66fa
 }
 
 void
81d882d5
 net_event_win32_close(struct net_event_win32 *ne)
6fbf66fa
 {
81d882d5
     if (net_event_win32_defined(ne))
     {
         close_net_event_win32(&ne->handle, ne->sd, 0);
     }
     net_event_win32_init(ne);
6fbf66fa
 }
 
 /*
  * Simulate *nix signals on Windows.
  *
  * Two modes:
  * (1) Console mode -- map keyboard function keys to signals
  * (2) Service mode -- map Windows event object to SIGTERM
  */
 
87f1be66
 static void
 win_trigger_event(struct win32_signal *ws)
 {
81d882d5
     if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED(ws->in.read))
87f1be66
     {
81d882d5
         SetEvent(ws->in.read);
     }
     else /* generate a key-press event */
     {
         DWORD tmp;
         INPUT_RECORD ir;
         HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
 
         CLEAR(ir);
         ir.EventType = KEY_EVENT;
         ir.Event.KeyEvent.bKeyDown = true;
         if (!stdin_handle || !WriteConsoleInput(stdin_handle, &ir, 1, &tmp))
         {
             msg(M_WARN|M_ERRNO, "WARN: win_trigger_event: WriteConsoleInput");
         }
87f1be66
     }
 }
 
 /*
  * Callback to handle console ctrl events
  */
 static bool WINAPI
81d882d5
 win_ctrl_handler(DWORD signum)
87f1be66
 {
81d882d5
     msg(D_LOW, "win_ctrl_handler: signal received (code=%lu)", (unsigned long) signum);
 
     if (siginfo_static.signal_received == SIGTERM)
     {
         return true;
     }
 
     switch (signum)
     {
         case CTRL_C_EVENT:
         case CTRL_BREAK_EVENT:
             throw_signal(SIGTERM);
             /* trigget the win32_signal to interrupt the event loop */
             win_trigger_event(&win32_signal);
             return true;
             break;
 
         default:
             msg(D_LOW, "win_ctrl_handler: signal (code=%lu) not handled", (unsigned long) signum);
             break;
     }
     /* pass all other signals to the next handler */
     return false;
87f1be66
 }
 
6fbf66fa
 void
81d882d5
 win32_signal_clear(struct win32_signal *ws)
6fbf66fa
 {
81d882d5
     CLEAR(*ws);
6fbf66fa
 }
 
 void
81d882d5
 win32_signal_open(struct win32_signal *ws,
                   int force,
                   const char *exit_event_name,
                   bool exit_event_initial_state)
6fbf66fa
 {
81d882d5
     CLEAR(*ws);
 
     ws->mode = WSO_MODE_UNDEF;
     ws->in.read = INVALID_HANDLE_VALUE;
     ws->in.write = INVALID_HANDLE_VALUE;
     ws->console_mode_save = 0;
     ws->console_mode_save_defined = false;
 
     if (force == WSO_NOFORCE || force == WSO_FORCE_CONSOLE)
     {
         /*
          * Try to open console.
          */
         ws->in.read = GetStdHandle(STD_INPUT_HANDLE);
         if (ws->in.read != INVALID_HANDLE_VALUE)
         {
             if (GetConsoleMode(ws->in.read, &ws->console_mode_save))
             {
                 /* running on a console */
                 const DWORD new_console_mode = ws->console_mode_save
                                                & ~(ENABLE_WINDOW_INPUT
                                                    | ENABLE_PROCESSED_INPUT
                                                    | ENABLE_LINE_INPUT
                                                    | ENABLE_ECHO_INPUT
                                                    | ENABLE_MOUSE_INPUT);
 
                 if (new_console_mode != ws->console_mode_save)
                 {
                     if (!SetConsoleMode(ws->in.read, new_console_mode))
                     {
                         msg(M_ERR, "Error: win32_signal_open: SetConsoleMode failed");
                     }
                     ws->console_mode_save_defined = true;
                 }
                 ws->mode = WSO_MODE_CONSOLE;
             }
             else
             {
                 ws->in.read = INVALID_HANDLE_VALUE; /* probably running as a service */
             }
         }
     }
 
     /*
      * If console open failed, assume we are running
      * as a service.
      */
     if ((force == WSO_NOFORCE || force == WSO_FORCE_SERVICE)
         && !HANDLE_DEFINED(ws->in.read) && exit_event_name)
     {
         struct security_attributes sa;
 
         if (!init_security_attributes_allow_all(&sa))
         {
             msg(M_ERR, "Error: win32_signal_open: init SA failed");
         }
 
         ws->in.read = CreateEvent(&sa.sa,
                                   TRUE,
                                   exit_event_initial_state ? TRUE : FALSE,
                                   exit_event_name);
         if (ws->in.read == NULL)
         {
             msg(M_WARN|M_ERRNO, "NOTE: CreateEvent '%s' failed", exit_event_name);
         }
         else
         {
             if (WaitForSingleObject(ws->in.read, 0) != WAIT_TIMEOUT)
             {
                 msg(M_FATAL, "ERROR: Exit Event ('%s') is signaled", exit_event_name);
             }
             else
             {
                 ws->mode = WSO_MODE_SERVICE;
             }
         }
6fbf66fa
     }
87f1be66
     /* set the ctrl handler in both console and service modes */
81d882d5
     if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) win_ctrl_handler, true))
     {
         msg(M_WARN|M_ERRNO, "WARN: SetConsoleCtrlHandler failed");
     }
6fbf66fa
 }
 
 static bool
81d882d5
 keyboard_input_available(struct win32_signal *ws)
6fbf66fa
 {
81d882d5
     ASSERT(ws->mode == WSO_MODE_CONSOLE);
     if (HANDLE_DEFINED(ws->in.read))
6fbf66fa
     {
81d882d5
         DWORD n;
         if (GetNumberOfConsoleInputEvents(ws->in.read, &n))
         {
             return n > 0;
         }
6fbf66fa
     }
81d882d5
     return false;
6fbf66fa
 }
 
 static unsigned int
81d882d5
 keyboard_ir_to_key(INPUT_RECORD *ir)
6fbf66fa
 {
81d882d5
     if (ir->Event.KeyEvent.uChar.AsciiChar == 0)
     {
         return ir->Event.KeyEvent.wVirtualScanCode;
     }
6fbf66fa
 
81d882d5
     if ((ir->Event.KeyEvent.dwControlKeyState
          & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
         && (ir->Event.KeyEvent.wVirtualKeyCode != 18))
     {
         return ir->Event.KeyEvent.wVirtualScanCode * 256;
     }
6fbf66fa
 
81d882d5
     return ir->Event.KeyEvent.uChar.AsciiChar;
6fbf66fa
 }
 
 static unsigned int
81d882d5
 win32_keyboard_get(struct win32_signal *ws)
6fbf66fa
 {
81d882d5
     ASSERT(ws->mode == WSO_MODE_CONSOLE);
     if (HANDLE_DEFINED(ws->in.read))
     {
         INPUT_RECORD ir;
4cd4899e
         do
         {
81d882d5
             DWORD n;
             if (!keyboard_input_available(ws))
             {
                 return 0;
             }
             if (!ReadConsoleInput(ws->in.read, &ir, 1, &n))
             {
                 return 0;
             }
         } while (ir.EventType != KEY_EVENT || ir.Event.KeyEvent.bKeyDown != TRUE);
 
         return keyboard_ir_to_key(&ir);
     }
     else
     {
         return 0;
     }
6fbf66fa
 }
 
 void
81d882d5
 win32_signal_close(struct win32_signal *ws)
6fbf66fa
 {
81d882d5
     if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED(ws->in.read))
6fbf66fa
     {
81d882d5
         CloseHandle(ws->in.read);
6fbf66fa
     }
81d882d5
     if (ws->console_mode_save_defined)
     {
         if (!SetConsoleMode(ws->in.read, ws->console_mode_save))
         {
             msg(M_ERR, "Error: win32_signal_close: SetConsoleMode failed");
         }
     }
     CLEAR(*ws);
6fbf66fa
 }
 
 /*
  * Return true if interrupt occurs in service mode.
  */
72c7b12c
 bool
81d882d5
 win32_service_interrupt(struct win32_signal *ws)
6fbf66fa
 {
81d882d5
     if (ws->mode == WSO_MODE_SERVICE)
6fbf66fa
     {
81d882d5
         if (HANDLE_DEFINED(ws->in.read)
             && WaitForSingleObject(ws->in.read, 0) == WAIT_OBJECT_0)
         {
             return true;
         }
6fbf66fa
     }
81d882d5
     return false;
6fbf66fa
 }
 
 int
81d882d5
 win32_signal_get(struct win32_signal *ws)
6fbf66fa
 {
81d882d5
     int ret = 0;
     if (siginfo_static.signal_received)
     {
         ret = siginfo_static.signal_received;
     }
     else
     {
         if (ws->mode == WSO_MODE_SERVICE)
         {
             if (win32_service_interrupt(ws))
             {
                 ret = SIGTERM;
             }
         }
         else if (ws->mode == WSO_MODE_CONSOLE)
         {
             switch (win32_keyboard_get(ws))
             {
                 case 0x3B: /* F1 -> USR1 */
                     ret = SIGUSR1;
                     break;
 
                 case 0x3C: /* F2 -> USR2 */
                     ret = SIGUSR2;
                     break;
 
                 case 0x3D: /* F3 -> HUP */
                     ret = SIGHUP;
                     break;
 
                 case 0x3E: /* F4 -> TERM */
                     ret = SIGTERM;
                     break;
 
                 case 0x03: /* CTRL-C -> TERM */
                     ret = SIGTERM;
                     break;
             }
         }
         if (ret)
         {
             siginfo_static.signal_received = ret;
             siginfo_static.source = SIG_SOURCE_HARD;
         }
     }
     return ret;
6fbf66fa
 }
 
 void
81d882d5
 win32_pause(struct win32_signal *ws)
6fbf66fa
 {
81d882d5
     if (ws->mode == WSO_MODE_CONSOLE && HANDLE_DEFINED(ws->in.read))
6fbf66fa
     {
81d882d5
         int status;
         msg(M_INFO|M_NOPREFIX, "Press any key to continue...");
4cd4899e
         do
         {
81d882d5
             status = WaitForSingleObject(ws->in.read, INFINITE);
         } while (!win32_keyboard_get(ws));
6fbf66fa
     }
 }
 
 /* window functions */
 
 void
81d882d5
 window_title_clear(struct window_title *wt)
6fbf66fa
 {
81d882d5
     CLEAR(*wt);
6fbf66fa
 }
 
 void
81d882d5
 window_title_save(struct window_title *wt)
6fbf66fa
 {
81d882d5
     if (!wt->saved)
6fbf66fa
     {
81d882d5
         if (!GetConsoleTitle(wt->old_window_title, sizeof(wt->old_window_title)))
         {
             wt->old_window_title[0] = 0;
             wt->saved = false;
         }
         else
         {
             wt->saved = true;
         }
6fbf66fa
     }
 }
 
 void
81d882d5
 window_title_restore(const struct window_title *wt)
6fbf66fa
 {
81d882d5
     if (wt->saved)
     {
         SetConsoleTitle(wt->old_window_title);
     }
6fbf66fa
 }
 
 void
81d882d5
 window_title_generate(const char *title)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct buffer out = alloc_buf_gc(256, &gc);
     if (!title)
     {
         title = "";
     }
     buf_printf(&out, "[%s] " PACKAGE_NAME " " PACKAGE_VERSION " F4:EXIT F1:USR1 F2:USR2 F3:HUP", title);
     SetConsoleTitle(BSTR(&out));
     gc_free(&gc);
6fbf66fa
 }
 
 /* semaphore functions */
 
 void
81d882d5
 semaphore_clear(struct semaphore *s)
6fbf66fa
 {
81d882d5
     CLEAR(*s);
6fbf66fa
 }
 
 void
81d882d5
 semaphore_open(struct semaphore *s, const char *name)
6fbf66fa
 {
81d882d5
     struct security_attributes sa;
6fbf66fa
 
81d882d5
     s->locked = false;
     s->name = name;
     s->hand = NULL;
6fbf66fa
 
81d882d5
     if (init_security_attributes_allow_all(&sa))
     {
         s->hand = CreateSemaphore(&sa.sa, 1, 1, name);
     }
6fbf66fa
 
81d882d5
     if (s->hand == NULL)
     {
         msg(M_WARN|M_ERRNO, "WARNING: Cannot create Win32 semaphore '%s'", name);
     }
     else
     {
         dmsg(D_SEMAPHORE, "Created Win32 semaphore '%s'", s->name);
     }
6fbf66fa
 }
 
 bool
81d882d5
 semaphore_lock(struct semaphore *s, int timeout_milliseconds)
6fbf66fa
 {
81d882d5
     bool ret = true;
 
     if (s->hand)
     {
         DWORD status;
         ASSERT(!s->locked);
 
         dmsg(D_SEMAPHORE_LOW, "Attempting to lock Win32 semaphore '%s' prior to net shell command (timeout = %d sec)",
              s->name,
              timeout_milliseconds / 1000);
         status = WaitForSingleObject(s->hand, timeout_milliseconds);
         if (status == WAIT_FAILED)
         {
             msg(M_ERR, "Wait failed on Win32 semaphore '%s'", s->name);
         }
         ret = (status == WAIT_TIMEOUT) ? false : true;
         if (ret)
         {
             dmsg(D_SEMAPHORE, "Locked Win32 semaphore '%s'", s->name);
             s->locked = true;
         }
         else
         {
             dmsg(D_SEMAPHORE, "Wait on Win32 semaphore '%s' timed out after %d milliseconds",
                  s->name,
                  timeout_milliseconds);
         }
     }
     return ret;
6fbf66fa
 }
 
 void
81d882d5
 semaphore_release(struct semaphore *s)
6fbf66fa
 {
81d882d5
     if (s->hand)
6fbf66fa
     {
81d882d5
         ASSERT(s->locked);
         dmsg(D_SEMAPHORE, "Releasing Win32 semaphore '%s'", s->name);
         if (!ReleaseSemaphore(s->hand, 1, NULL))
         {
             msg(M_WARN | M_ERRNO, "ReleaseSemaphore failed on Win32 semaphore '%s'",
                 s->name);
         }
         s->locked = false;
6fbf66fa
     }
 }
 
 void
81d882d5
 semaphore_close(struct semaphore *s)
6fbf66fa
 {
81d882d5
     if (s->hand)
6fbf66fa
     {
81d882d5
         if (s->locked)
         {
             semaphore_release(s);
         }
         dmsg(D_SEMAPHORE, "Closing Win32 semaphore '%s'", s->name);
         CloseHandle(s->hand);
         s->hand = NULL;
6fbf66fa
     }
 }
 
 /*
  * Special global semaphore used to protect network
  * shell commands from simultaneous instantiation.
  */
 
 void
81d882d5
 netcmd_semaphore_init(void)
6fbf66fa
 {
81d882d5
     semaphore_open(&netcmd_semaphore, PACKAGE "_netcmd");
6fbf66fa
 }
 
 void
81d882d5
 netcmd_semaphore_close(void)
6fbf66fa
 {
81d882d5
     semaphore_close(&netcmd_semaphore);
6fbf66fa
 }
 
 void
81d882d5
 netcmd_semaphore_lock(void)
6fbf66fa
 {
81d882d5
     const int timeout_seconds = 600;
6aa4c909
 
81d882d5
     if (!netcmd_semaphore.hand)
     {
         netcmd_semaphore_init();
     }
6aa4c909
 
81d882d5
     if (!semaphore_lock(&netcmd_semaphore, timeout_seconds * 1000))
     {
         msg(M_FATAL, "Cannot lock net command semaphore");
     }
6fbf66fa
 }
 
 void
81d882d5
 netcmd_semaphore_release(void)
6fbf66fa
 {
81d882d5
     semaphore_release(&netcmd_semaphore);
     /* netcmd_semaphore has max count of 1 - safe to close after release */
     semaphore_close(&netcmd_semaphore);
6fbf66fa
 }
 
 /*
73b7e698
  * Return true if filename is safe to be used on Windows,
  * by avoiding the following reserved names:
  *
  * CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9,
  * LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, and CLOCK$
  *
  * See: http://msdn.microsoft.com/en-us/library/aa365247.aspx
  *  and http://msdn.microsoft.com/en-us/library/86k9f82k(VS.80).aspx
  */
 
 static bool
81d882d5
 cmp_prefix(const char *str, const bool n, const char *pre)
73b7e698
 {
81d882d5
     size_t i = 0;
73b7e698
 
81d882d5
     if (!str)
     {
         return false;
     }
73b7e698
 
81d882d5
     while (true)
     {
         const int c1 = pre[i];
         int c2 = str[i];
         ++i;
         if (c1 == '\0')
         {
             if (n)
             {
                 if (isdigit(c2))
                 {
                     c2 = str[i];
                 }
                 else
                 {
                     return false;
                 }
             }
             return c2 == '\0' || c2 == '.';
         }
         else if (c2 == '\0')
         {
             return false;
         }
         if (c1 != tolower(c2))
         {
             return false;
         }
73b7e698
     }
 }
 
 bool
81d882d5
 win_safe_filename(const char *fn)
73b7e698
 {
81d882d5
     if (cmp_prefix(fn, false, "con"))
     {
         return false;
     }
     if (cmp_prefix(fn, false, "prn"))
     {
         return false;
     }
     if (cmp_prefix(fn, false, "aux"))
     {
         return false;
     }
     if (cmp_prefix(fn, false, "nul"))
     {
         return false;
     }
     if (cmp_prefix(fn, true, "com"))
     {
         return false;
     }
     if (cmp_prefix(fn, true, "lpt"))
     {
         return false;
     }
     if (cmp_prefix(fn, false, "clock$"))
     {
         return false;
     }
     return true;
73b7e698
 }
 
5a2e9a25
 /*
  * Service functions for openvpn_execve
  */
 
 static char *
81d882d5
 env_block(const struct env_set *es)
5a2e9a25
 {
81d882d5
     char force_path[256];
     char *sysroot = get_win_sys_path();
 
     if (!openvpn_snprintf(force_path, sizeof(force_path), "PATH=%s\\System32;%s;%s\\System32\\Wbem",
                           sysroot, sysroot, sysroot))
     {
         msg(M_WARN, "env_block: default path truncated to %s", force_path);
     }
 
     if (es)
     {
         struct env_item *e;
         char *ret;
         char *p;
         size_t nchars = 1;
         bool path_seen = false;
 
         for (e = es->list; e != NULL; e = e->next)
4cd4899e
         {
81d882d5
             nchars += strlen(e->string) + 1;
4cd4899e
         }
81d882d5
 
         nchars += strlen(force_path)+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;
             }
             if (strncmp(e->string, "PATH=", 5 ) == 0)
             {
                 path_seen = true;
             }
         }
 
         /* make sure PATH is set */
         if (!path_seen)
         {
             msg( M_INFO, "env_block: add %s", force_path );
             strcpy( p, force_path );
             p += strlen(force_path) + 1;
         }
 
         *p = '\0';
         return ret;
     }
     else
     {
         return NULL;
     }
5a2e9a25
 }
 
71bbbd76
 static WCHAR *
81d882d5
 wide_cmd_line(const struct argv *a, struct gc_arena *gc)
5a2e9a25
 {
81d882d5
     size_t nchars = 1;
     size_t maxlen = 0;
     size_t i;
     struct buffer buf;
     char *work = NULL;
5a2e9a25
 
81d882d5
     if (!a)
     {
         return NULL;
     }
5a2e9a25
 
81d882d5
     for (i = 0; i < a->argc; ++i)
5a2e9a25
     {
81d882d5
         const char *arg = a->argv[i];
         const size_t len = strlen(arg);
         nchars += len + 3;
         if (len > maxlen)
         {
             maxlen = len;
         }
5a2e9a25
     }
 
81d882d5
     work = gc_malloc(maxlen + 1, false, gc);
     check_malloc_return(work);
     buf = alloc_buf_gc(nchars, gc);
5a2e9a25
 
81d882d5
     for (i = 0; i < a->argc; ++i)
5a2e9a25
     {
81d882d5
         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);
         }
5a2e9a25
     }
 
81d882d5
     return wide_string(BSTR(&buf), gc);
5a2e9a25
 }
 
 /*
  * Attempt to simulate fork/execve on Windows
  */
 int
81d882d5
 openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags)
5a2e9a25
 {
01a3c876
     int ret = OPENVPN_EXECVE_ERROR;
81d882d5
     static bool exec_warn = false;
c2533d18
 
81d882d5
     if (a && a->argv[0])
5a2e9a25
     {
81d882d5
         if (openvpn_execve_allowed(flags))
         {
             struct gc_arena gc = gc_new();
             STARTUPINFOW start_info;
             PROCESS_INFORMATION proc_info;
05634736
 
81d882d5
             char *env = env_block(es);
             WCHAR *cl = wide_cmd_line(a, &gc);
             WCHAR *cmd = wide_string(a->argv[0], &gc);
05634736
 
81d882d5
             /* this allows console programs to run, and is ignored otherwise */
             DWORD proc_flags = CREATE_NO_WINDOW;
46e02127
 
81d882d5
             CLEAR(start_info);
             CLEAR(proc_info);
05634736
 
81d882d5
             /* fill in STARTUPINFO struct */
             GetStartupInfoW(&start_info);
             start_info.cb = sizeof(start_info);
             start_info.dwFlags = STARTF_USESHOWWINDOW;
             start_info.wShowWindow = SW_HIDE;
05634736
 
81d882d5
             if (CreateProcessW(cmd, cl, NULL, NULL, FALSE, proc_flags, env, NULL, &start_info, &proc_info))
05634736
             {
81d882d5
                 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);
05634736
             }
81d882d5
             else
05634736
             {
81d882d5
                 msg(M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %S failed", cmd);
05634736
             }
81d882d5
             free(env);
             gc_free(&gc);
         }
01a3c876
         else
81d882d5
         {
01a3c876
             ret = OPENVPN_EXECVE_NOT_ALLOWED;
             if (!exec_warn && (script_security() < SSEC_SCRIPTS))
             {
                 msg(M_WARN, SCRIPT_SECURITY_WARNING);
                 exec_warn = true;
             }
05634736
         }
5a2e9a25
     }
81d882d5
     else
5a2e9a25
     {
81d882d5
         msg(M_WARN, "openvpn_execve: called with empty argv");
5a2e9a25
     }
81d882d5
     return ret;
5a2e9a25
 }
 
71bbbd76
 WCHAR *
81d882d5
 wide_string(const char *utf8, struct gc_arena *gc)
71bbbd76
 {
81d882d5
     int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
     WCHAR *ucs16 = gc_malloc(n * sizeof(WCHAR), false, gc);
     MultiByteToWideChar(CP_UTF8, 0, utf8, -1, ucs16, n);
     return ucs16;
71bbbd76
 }
 
5c30df12
 /*
  * call ourself in another process
  */
 void
81d882d5
 fork_to_self(const char *cmdline)
5c30df12
 {
81d882d5
     STARTUPINFO start_info;
     PROCESS_INFORMATION proc_info;
     char self_exe[256];
     char *cl = string_alloc(cmdline, NULL);
     DWORD status;
 
     CLEAR(start_info);
     CLEAR(proc_info);
     CLEAR(self_exe);
 
     status = GetModuleFileName(NULL, self_exe, sizeof(self_exe));
     if (status == 0 || status == sizeof(self_exe))
5c30df12
     {
81d882d5
         msg(M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: cannot get module name via GetModuleFileName");
         goto done;
5c30df12
     }
 
81d882d5
     /* fill in STARTUPINFO struct */
     GetStartupInfo(&start_info);
     start_info.cb = sizeof(start_info);
     start_info.dwFlags = STARTF_USESHOWWINDOW;
     start_info.wShowWindow = SW_HIDE;
5c30df12
 
81d882d5
     if (CreateProcess(self_exe, cl, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, &proc_info))
5c30df12
     {
81d882d5
         CloseHandle(proc_info.hThread);
         CloseHandle(proc_info.hProcess);
5c30df12
     }
81d882d5
     else
5c30df12
     {
81d882d5
         msg(M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: %s", cmdline);
5c30df12
     }
 
81d882d5
 done:
     free(cl);
5c30df12
 }
 
5a2e9a25
 char *
81d882d5
 get_win_sys_path(void)
5a2e9a25
 {
81d882d5
     ASSERT(win_sys_path);
     return win_sys_path;
5a2e9a25
 }
 
 void
81d882d5
 set_win_sys_path(const char *newpath, struct env_set *es)
5a2e9a25
 {
81d882d5
     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 */
5a2e9a25
 }
 
 void
81d882d5
 set_win_sys_path_via_env(struct env_set *es)
5a2e9a25
 {
81d882d5
     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);
5a2e9a25
 }
 
ca4c6d61
 
 const char *
e2a0cad4
 win_get_tempdir(void)
ca4c6d61
 {
81d882d5
     static char tmpdir[MAX_PATH];
     WCHAR wtmpdir[MAX_PATH];
925b8a46
 
81d882d5
     if (!GetTempPathW(_countof(wtmpdir), wtmpdir))
925b8a46
     {
81d882d5
         /* Warn if we can't find a valid temporary directory, which should
          * be unlikely.
          */
         msg(M_WARN, "Could not find a suitable temporary directory."
             " (GetTempPath() failed).  Consider using --tmp-dir");
         return NULL;
925b8a46
     }
 
81d882d5
     if (WideCharToMultiByte(CP_UTF8, 0, wtmpdir, -1, NULL, 0, NULL, NULL) > sizeof(tmpdir))
925b8a46
     {
81d882d5
         msg(M_WARN, "Could not get temporary directory. Path is too long."
             "  Consider using --tmp-dir");
         return NULL;
925b8a46
     }
 
81d882d5
     WideCharToMultiByte(CP_UTF8, 0, wtmpdir, -1, tmpdir, sizeof(tmpdir), NULL, NULL);
     return tmpdir;
ca4c6d61
 }
38c85658
 
2282b1be
 static bool
81d882d5
 win_block_dns_service(bool add, int index, const HANDLE pipe)
2282b1be
 {
81d882d5
     bool ret = false;
     ack_message_t ack;
     struct gc_arena gc = gc_new();
 
     block_dns_message_t data = {
         .header = {
             (add ? msg_add_block_dns : msg_del_block_dns),
             sizeof(block_dns_message_t),
             0
         },
         .iface = { .index = index, .name = "" }
     };
 
717c1930
     if (!send_msg_iservice(pipe, &data, sizeof(data), &ack, "Block_DNS"))
2282b1be
     {
81d882d5
         goto out;
2282b1be
     }
 
81d882d5
     if (ack.error_number != NO_ERROR)
2282b1be
     {
81d882d5
         msg(M_WARN, "Block_DNS: %s block dns filters using service failed: %s [status=0x%x if_index=%d]",
             (add ? "adding" : "deleting"), strerror_win32(ack.error_number, &gc),
             ack.error_number, data.iface.index);
         goto out;
2282b1be
     }
 
81d882d5
     ret = true;
     msg(M_INFO, "%s outside dns using service succeeded.", (add ? "Blocking" : "Unblocking"));
2282b1be
 out:
81d882d5
     gc_free(&gc);
     return ret;
2282b1be
 }
 
6a33a34d
 static void
81d882d5
 block_dns_msg_handler(DWORD err, const char *msg)
38c85658
 {
81d882d5
     struct gc_arena gc = gc_new();
6a33a34d
 
81d882d5
     if (err == 0)
38c85658
     {
81d882d5
         msg(M_INFO, "%s", msg);
38c85658
     }
81d882d5
     else
6a33a34d
     {
81d882d5
         msg(M_WARN, "Error in add_block_dns_filters(): %s : %s [status=0x%lx]",
             msg, strerror_win32(err, &gc), err);
6a33a34d
     }
 
81d882d5
     gc_free(&gc);
38c85658
 }
 
 bool
81d882d5
 win_wfp_block_dns(const NET_IFINDEX index, const HANDLE msg_channel)
38c85658
 {
81d882d5
     WCHAR openvpnpath[MAX_PATH];
     bool ret = false;
     DWORD status;
38c85658
 
81d882d5
     if (msg_channel)
2282b1be
     {
81d882d5
         dmsg(D_LOW, "Using service to add block dns filters");
         ret = win_block_dns_service(true, index, msg_channel);
         goto out;
2282b1be
     }
 
986b9308
     status = GetModuleFileNameW(NULL, openvpnpath, _countof(openvpnpath));
     if (status == 0 || status == _countof(openvpnpath))
38c85658
     {
81d882d5
         msg(M_WARN|M_ERRNO, "block_dns: cannot get executable path");
         goto out;
38c85658
     }
 
81d882d5
     status = add_block_dns_filters(&m_hEngineHandle, index, openvpnpath,
                                    block_dns_msg_handler);
27aa8728
     if (status == 0)
     {
42292435
         int is_auto = 0;
         tap_metric_v4 = get_interface_metric(index, AF_INET, &is_auto);
         if (is_auto)
27aa8728
         {
42292435
             tap_metric_v4 = 0;
27aa8728
         }
42292435
         tap_metric_v6 = get_interface_metric(index, AF_INET6, &is_auto);
         if (is_auto)
27aa8728
         {
42292435
             tap_metric_v6 = 0;
27aa8728
         }
         status = set_interface_metric(index, AF_INET, BLOCK_DNS_IFACE_METRIC);
         if (!status)
         {
             set_interface_metric(index, AF_INET6, BLOCK_DNS_IFACE_METRIC);
         }
     }
 
81d882d5
     ret = (status == 0);
38c85658
 
6a33a34d
 out:
38c85658
 
81d882d5
     return ret;
38c85658
 }
 
 bool
27aa8728
 win_wfp_uninit(const NET_IFINDEX index, const HANDLE msg_channel)
38c85658
 {
81d882d5
     dmsg(D_LOW, "Uninitializing WFP");
6a33a34d
 
2282b1be
     if (msg_channel)
81d882d5
     {
         msg(D_LOW, "Using service to delete block dns filters");
27aa8728
         win_block_dns_service(false, index, msg_channel);
81d882d5
     }
2282b1be
     else
81d882d5
     {
         delete_block_dns_filters(m_hEngineHandle);
895d75cf
         m_hEngineHandle = NULL;
27aa8728
         if (tap_metric_v4 >= 0)
         {
             set_interface_metric(index, AF_INET, tap_metric_v4);
         }
         if (tap_metric_v6 >= 0)
         {
             set_interface_metric(index, AF_INET6, tap_metric_v6);
         }
81d882d5
     }
6a33a34d
 
38c85658
     return true;
 }
 
cdc65ea0
 int
e2a0cad4
 win32_version_info(void)
cdc65ea0
 {
     if (!IsWindowsXPOrGreater())
     {
81d882d5
         msg(M_FATAL, "Error: Windows version must be XP or greater.");
cdc65ea0
     }
 
     if (!IsWindowsVistaOrGreater())
     {
         return WIN_XP;
     }
 
     if (!IsWindows7OrGreater())
     {
         return WIN_VISTA;
     }
 
     if (!IsWindows8OrGreater())
     {
         return WIN_7;
     }
     else
     {
         return WIN_8;
     }
 }
 
15f78acf
 bool
e2a0cad4
 win32_is_64bit(void)
15f78acf
 {
 #if defined(_WIN64)
81d882d5
     return true;  /* 64-bit programs run only on Win64 */
15f78acf
 #elif defined(_WIN32)
81d882d5
     /* 32-bit programs run on both 32-bit and 64-bit Windows */
15f78acf
     BOOL f64 = FALSE;
     return IsWow64Process(GetCurrentProcess(), &f64) && f64;
81d882d5
 #else  /* if defined(_WIN64) */
     return false; /* Win64 does not support Win16 */
15f78acf
 #endif
 }
 
cdc65ea0
 const char *
 win32_version_string(struct gc_arena *gc, bool add_name)
 {
     int version = win32_version_info();
81d882d5
     struct buffer out = alloc_buf_gc(256, gc);
cdc65ea0
 
     switch (version)
     {
         case WIN_XP:
81d882d5
             buf_printf(&out, "5.1%s", add_name ? " (Windows XP)" : "");
cdc65ea0
             break;
81d882d5
 
cdc65ea0
         case WIN_VISTA:
81d882d5
             buf_printf(&out, "6.0%s", add_name ? " (Windows Vista)" : "");
cdc65ea0
             break;
81d882d5
 
cdc65ea0
         case WIN_7:
81d882d5
             buf_printf(&out, "6.1%s", add_name ? " (Windows 7)" : "");
cdc65ea0
             break;
81d882d5
 
cdc65ea0
         case WIN_8:
81d882d5
             buf_printf(&out, "6.2%s", add_name ? " (Windows 8 or greater)" : "");
cdc65ea0
             break;
81d882d5
 
cdc65ea0
         default:
81d882d5
             msg(M_NONFATAL, "Unknown Windows version: %d", version);
             buf_printf(&out, "0.0%s", add_name ? " (unknown)" : "");
cdc65ea0
             break;
     }
 
81d882d5
     buf_printf(&out, win32_is_64bit() ? " 64bit" : " 32bit");
15f78acf
 
cdc65ea0
     return (const char *)out.data;
 }
 
717c1930
 bool
 send_msg_iservice(HANDLE pipe, const void *data, size_t size,
                   ack_message_t *ack, const char *context)
 {
     struct gc_arena gc = gc_new();
     DWORD len;
     bool ret = true;
 
     if (!WriteFile(pipe, data, size, &len, NULL)
         || !ReadFile(pipe, ack, sizeof(*ack), &len, NULL))
     {
         msg(M_WARN, "%s: could not talk to service: %s [%lu]",
f57431cd
             context ? context : "Unknown",
717c1930
             strerror_win32(GetLastError(), &gc), GetLastError());
         ret = false;
     }
 
     gc_free(&gc);
     return ret;
 }
 
81d882d5
 #endif /* ifdef _WIN32 */