diff options
Diffstat (limited to 'src/openvpn/win32.c')
-rw-r--r-- | src/openvpn/win32.c | 335 |
1 files changed, 334 insertions, 1 deletions
diff --git a/src/openvpn/win32.c b/src/openvpn/win32.c index f35c96b..6c6ac4c 100644 --- a/src/openvpn/win32.c +++ b/src/openvpn/win32.c @@ -46,6 +46,34 @@ #include "memdbg.h" +#include "win32_wfp.h" + +#ifdef HAVE_VERSIONHELPERS_H +#include <versionhelpers.h> +#else +#include "compat-versionhelpers.h" +#endif + +/* WFP function pointers. Initialized in win_wfp_init_funcs() */ +func_ConvertInterfaceIndexToLuid ConvertInterfaceIndexToLuid = NULL; +func_FwpmEngineOpen0 FwpmEngineOpen0 = NULL; +func_FwpmEngineClose0 FwpmEngineClose0 = NULL; +func_FwpmFilterAdd0 FwpmFilterAdd0 = NULL; +func_FwpmSubLayerAdd0 FwpmSubLayerAdd0 = NULL; +func_FwpmSubLayerDeleteByKey0 FwpmSubLayerDeleteByKey0 = NULL; +func_FwpmFreeMemory0 FwpmFreeMemory0 = NULL; +func_FwpmGetAppIdFromFileName0 FwpmGetAppIdFromFileName0 = NULL; + +/* + * WFP firewall name. + */ +WCHAR * FIREWALL_NAME = L"OpenVPN"; /* GLOBAL */ + +/* + * WFP handle and GUID. + */ +static HANDLE m_hEngineHandle = NULL; /* GLOBAL */ + /* * Windows internal socket API state (opaque). */ @@ -324,6 +352,53 @@ net_event_win32_close (struct net_event_win32 *ne) * (2) Service mode -- map Windows event object to SIGTERM */ +static void +win_trigger_event(struct win32_signal *ws) +{ + if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED(ws->in.read)) + 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"); + } +} + +/* + * Callback to handle console ctrl events + */ +static bool WINAPI +win_ctrl_handler (DWORD signum) +{ + 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; +} + void win32_signal_clear (struct win32_signal *ws) { @@ -403,6 +478,9 @@ win32_signal_open (struct win32_signal *ws, ws->mode = WSO_MODE_SERVICE; } } + /* set the ctrl handler in both console and service modes */ + if (!SetConsoleCtrlHandler ((PHANDLER_ROUTINE) win_ctrl_handler, true)) + msg (M_WARN|M_ERRNO, "WARN: SetConsoleCtrlHandler failed"); } static bool @@ -512,6 +590,9 @@ win32_signal_get (struct win32_signal *ws) case 0x3E: /* F4 -> TERM */ ret = SIGTERM; break; + case 0x03: /* CTRL-C -> TERM */ + ret = SIGTERM; + break; } } if (ret) @@ -763,7 +844,12 @@ win_safe_filename (const char *fn) static char * env_block (const struct env_set *es) { - char * force_path = "PATH=C:\\Windows\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem"; + 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) { @@ -1019,4 +1105,251 @@ win_get_tempdir() WideCharToMultiByte (CP_UTF8, 0, wtmpdir, -1, tmpdir, sizeof (tmpdir), NULL, NULL); return tmpdir; } + +bool +win_wfp_init_funcs () +{ + /* Initialize all WFP-related function pointers */ + HMODULE iphlpapiHandle; + HMODULE fwpuclntHandle; + + iphlpapiHandle = LoadLibrary("iphlpapi.dll"); + if (iphlpapiHandle == NULL) + { + msg (M_NONFATAL, "Can't load iphlpapi.dll"); + return false; + } + + fwpuclntHandle = LoadLibrary("fwpuclnt.dll"); + if (fwpuclntHandle == NULL) + { + msg (M_NONFATAL, "Can't load fwpuclnt.dll"); + return false; + } + + ConvertInterfaceIndexToLuid = (func_ConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHandle, "ConvertInterfaceIndexToLuid"); + FwpmFilterAdd0 = (func_FwpmFilterAdd0)GetProcAddress(fwpuclntHandle, "FwpmFilterAdd0"); + FwpmEngineOpen0 = (func_FwpmEngineOpen0)GetProcAddress(fwpuclntHandle, "FwpmEngineOpen0"); + FwpmEngineClose0 = (func_FwpmEngineClose0)GetProcAddress(fwpuclntHandle, "FwpmEngineClose0"); + FwpmSubLayerAdd0 = (func_FwpmSubLayerAdd0)GetProcAddress(fwpuclntHandle, "FwpmSubLayerAdd0"); + FwpmSubLayerDeleteByKey0 = (func_FwpmSubLayerDeleteByKey0)GetProcAddress(fwpuclntHandle, "FwpmSubLayerDeleteByKey0"); + FwpmFreeMemory0 = (func_FwpmFreeMemory0)GetProcAddress(fwpuclntHandle, "FwpmFreeMemory0"); + FwpmGetAppIdFromFileName0 = (func_FwpmGetAppIdFromFileName0)GetProcAddress(fwpuclntHandle, "FwpmGetAppIdFromFileName0"); + + if (!ConvertInterfaceIndexToLuid || + !FwpmFilterAdd0 || + !FwpmEngineOpen0 || + !FwpmEngineClose0 || + !FwpmSubLayerAdd0 || + !FwpmSubLayerDeleteByKey0 || + !FwpmFreeMemory0 || + !FwpmGetAppIdFromFileName0) + { + msg (M_NONFATAL, "Can't get address for all WFP-related procedures."); + return false; + } + + return true; +} + +bool +win_wfp_add_filter (HANDLE engineHandle, + const FWPM_FILTER0 *filter, + PSECURITY_DESCRIPTOR sd, + UINT64 *id) +{ + if (FwpmFilterAdd0(engineHandle, filter, sd, id) != ERROR_SUCCESS) + { + msg (M_NONFATAL, "Can't add WFP filter"); + return false; + } + return true; +} + +bool +win_wfp_block_dns (const NET_IFINDEX index) +{ + FWPM_SESSION0 session = {0}; + FWPM_SUBLAYER0 SubLayer = {0}; + NET_LUID tapluid; + UINT64 filterid; + WCHAR openvpnpath[MAX_PATH]; + FWP_BYTE_BLOB *openvpnblob = NULL; + FWPM_FILTER0 Filter = {0}; + FWPM_FILTER_CONDITION0 Condition[2] = {0}; + + /* Add temporary filters which don't survive reboots or crashes. */ + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + dmsg (D_LOW, "Opening WFP engine"); + + if (FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &m_hEngineHandle) != ERROR_SUCCESS) + { + msg (M_NONFATAL, "Can't open WFP engine"); + return false; + } + + if (UuidCreate(&SubLayer.subLayerKey) != NO_ERROR) + return false; + + /* Populate packet filter layer information. */ + SubLayer.displayData.name = FIREWALL_NAME; + SubLayer.displayData.description = FIREWALL_NAME; + SubLayer.flags = 0; + SubLayer.weight = 0x100; + + /* Add packet filter to our interface. */ + dmsg (D_LOW, "Adding WFP sublayer"); + if (FwpmSubLayerAdd0(m_hEngineHandle, &SubLayer, NULL) != ERROR_SUCCESS) + { + msg (M_NONFATAL, "Can't add WFP sublayer"); + return false; + } + + dmsg (D_LOW, "Blocking DNS using WFP"); + if (ConvertInterfaceIndexToLuid(index, &tapluid) != NO_ERROR) + { + msg (M_NONFATAL, "Can't convert interface index to LUID"); + return false; + } + dmsg (D_LOW, "Tap Luid: %I64d", tapluid.Value); + + /* Get OpenVPN path. */ + GetModuleFileNameW(NULL, openvpnpath, MAX_PATH); + + if (FwpmGetAppIdFromFileName0(openvpnpath, &openvpnblob) != ERROR_SUCCESS) + return false; + + /* Prepare filter. */ + Filter.subLayerKey = SubLayer.subLayerKey; + Filter.displayData.name = FIREWALL_NAME; + Filter.weight.type = FWP_EMPTY; + Filter.filterCondition = Condition; + Filter.numFilterConditions = 2; + + /* First filter. Block IPv4 DNS queries except from OpenVPN itself. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + Filter.action.type = FWP_ACTION_BLOCK; + + Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + Condition[0].matchType = FWP_MATCH_EQUAL; + Condition[0].conditionValue.type = FWP_UINT16; + Condition[0].conditionValue.uint16 = 53; + + Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID; + Condition[1].matchType = FWP_MATCH_NOT_EQUAL; + Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE; + Condition[1].conditionValue.byteBlob = openvpnblob; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Block IPv4 DNS) added with ID=%I64d", filterid); + + /* Second filter. Block IPv6 DNS queries except from OpenVPN itself. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Block IPv6 DNS) added with ID=%I64d", filterid); + + /* Third filter. Permit IPv4 DNS queries from TAP. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; + Filter.action.type = FWP_ACTION_PERMIT; + + Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE; + Condition[1].matchType = FWP_MATCH_EQUAL; + Condition[1].conditionValue.type = FWP_UINT64; + Condition[1].conditionValue.uint64 = &tapluid.Value; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Permit IPv4 DNS queries from TAP) added with ID=%I64d", filterid); + + /* Forth filter. Permit IPv6 DNS queries from TAP. */ + Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; + + /* Add filter condition to our interface. */ + if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid)) + goto err; + dmsg (D_LOW, "Filter (Permit IPv6 DNS queries from TAP) added with ID=%I64d", filterid); + + FwpmFreeMemory0((void **)&openvpnblob); + return true; + + err: + FwpmFreeMemory0((void **)&openvpnblob); + return false; +} + +bool +win_wfp_uninit() +{ + dmsg (D_LOW, "Uninitializing WFP"); + if (m_hEngineHandle) { + FwpmEngineClose0(m_hEngineHandle); + m_hEngineHandle = NULL; + } + return true; +} + +int +win32_version_info() +{ + if (!IsWindowsXPOrGreater()) + { + msg (M_FATAL, "Error: Windows version must be XP or greater."); + } + + if (!IsWindowsVistaOrGreater()) + { + return WIN_XP; + } + + if (!IsWindows7OrGreater()) + { + return WIN_VISTA; + } + + if (!IsWindows8OrGreater()) + { + return WIN_7; + } + else + { + return WIN_8; + } +} + +const char * +win32_version_string(struct gc_arena *gc, bool add_name) +{ + int version = win32_version_info(); + struct buffer out = alloc_buf_gc (256, gc); + + switch (version) + { + case WIN_XP: + buf_printf (&out, "5.1%s", add_name ? " (Windows XP)" : ""); + break; + case WIN_VISTA: + buf_printf (&out, "6.0%s", add_name ? " (Windows Vista)" : ""); + break; + case WIN_7: + buf_printf (&out, "6.1%s", add_name ? " (Windows 7)" : ""); + break; + case WIN_8: + buf_printf (&out, "6.2%s", add_name ? " (Windows 8 or greater)" : ""); + break; + default: + msg (M_NONFATAL, "Unknown Windows version: %d", version); + buf_printf (&out, "0.0%s", add_name ? " (unknown)" : ""); + break; + } + + return (const char *)out.data; +} + #endif |