/* * 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-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> * 2015-2016 <iam@valdikss.org.ru> * 2016 Selva Nair <selva.nair@gmail.com> * * 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 */ #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) #include "config-msvc.h" #endif #ifdef HAVE_CONFIG_VERSION_H #include "config-version.h" #endif #include "syshead.h" #ifdef WIN32 #include <fwpmu.h> #include <initguid.h> #include <fwpmtypes.h> #include <winsock2.h> #include <ws2ipdef.h> #include <iphlpapi.h> #include "block_dns.h" /* * WFP-related defines and GUIDs not in mingw32 */ #ifndef FWPM_SESSION_FLAG_DYNAMIC #define FWPM_SESSION_FLAG_DYNAMIC 0x00000001 #endif // c38d57d1-05a7-4c33-904f-7fbceee60e82 DEFINE_GUID( FWPM_LAYER_ALE_AUTH_CONNECT_V4, 0xc38d57d1, 0x05a7, 0x4c33, 0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82 ); // 4a72393b-319f-44bc-84c3-ba54dcb3b6b4 DEFINE_GUID( FWPM_LAYER_ALE_AUTH_CONNECT_V6, 0x4a72393b, 0x319f, 0x44bc, 0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4 ); // d78e1e87-8644-4ea5-9437-d809ecefc971 DEFINE_GUID( FWPM_CONDITION_ALE_APP_ID, 0xd78e1e87, 0x8644, 0x4ea5, 0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71 ); // c35a604d-d22b-4e1a-91b4-68f674ee674b DEFINE_GUID( FWPM_CONDITION_IP_REMOTE_PORT, 0xc35a604d, 0xd22b, 0x4e1a, 0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b ); // 4cd62a49-59c3-4969-b7f3-bda5d32890a4 DEFINE_GUID( FWPM_CONDITION_IP_LOCAL_INTERFACE, 0x4cd62a49, 0x59c3, 0x4969, 0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4 ); /* * Default msg handler does nothing */ static inline void default_msg_handler (DWORD err, const char *msg) { return; } #define CHECK_ERROR(err, msg) \ if (err) { msg_handler (err, msg); goto out; } /* * Block outgoing port 53 traffic except for * (i) adapter with the specified index * OR * (ii) processes with the specified executable path * The firewall filters added here are automatically removed when the process exits or * on calling delete_block_dns_filters(). * Arguments: * engine_handle : On successful return contains the handle for a newly opened fwp session * in which the filters are added. * May be closed by passing to delete_block_dns_filters to remove the filters. * index : The index of adapter for which traffic is permitted. * exe_path : Path of executable for which traffic is permitted. * msg_handler : An optional callback function for error reporting. * Returns 0 on success, a non-zero status code of the last failed action on failure. */ DWORD add_block_dns_filters (HANDLE *engine_handle, int index, const WCHAR *exe_path, block_dns_msg_handler_t msg_handler ) { FWPM_SESSION0 session = {0}; FWPM_SUBLAYER0 SubLayer = {0}; NET_LUID tapluid; UINT64 filterid; FWP_BYTE_BLOB *openvpnblob = NULL; FWPM_FILTER0 Filter = {0}; FWPM_FILTER_CONDITION0 Condition[2] = {0}; WCHAR *FIREWALL_NAME = L"OpenVPN"; DWORD err = 0; if (!msg_handler) msg_handler = default_msg_handler; /* Add temporary filters which don't survive reboots or crashes. */ session.flags = FWPM_SESSION_FLAG_DYNAMIC; *engine_handle = NULL; err = FwpmEngineOpen0 (NULL, RPC_C_AUTHN_WINNT, NULL, &session, engine_handle); CHECK_ERROR (err, "FwpEngineOpen: open fwp session failed"); err = UuidCreate (&SubLayer.subLayerKey); CHECK_ERROR (err, "UuidCreate: create sublayer key failed"); /* Populate packet filter layer information. */ SubLayer.displayData.name = FIREWALL_NAME; SubLayer.displayData.description = FIREWALL_NAME; SubLayer.flags = 0; SubLayer.weight = 0x100; /* Add sublayer to the session */ err = FwpmSubLayerAdd0 (*engine_handle, &SubLayer, NULL); CHECK_ERROR (err, "FwpmSubLayerAdd: add sublayer to session failed"); msg_handler (0, "Block_DNS: WFP engine opened"); err = ConvertInterfaceIndexToLuid (index, &tapluid); CHECK_ERROR (err, "Convert interface index to luid failed"); err = FwpmGetAppIdFromFileName0 (exe_path, &openvpnblob); CHECK_ERROR (err, "Get byte blob for openvpn executable name failed"); /* Prepare filter. */ Filter.subLayerKey = SubLayer.subLayerKey; Filter.displayData.name = FIREWALL_NAME; Filter.weight.type = FWP_UINT8; Filter.weight.uint8 = 0xF; Filter.filterCondition = Condition; Filter.numFilterConditions = 2; /* First filter. Permit IPv4 DNS queries from OpenVPN itself. */ Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; Filter.action.type = FWP_ACTION_PERMIT; 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_EQUAL; Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE; Condition[1].conditionValue.byteBlob = openvpnblob; err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); CHECK_ERROR (err, "Add filter to permit IPv4 port 53 traffic from OpenVPN failed"); /* Second filter. Permit IPv6 DNS queries from OpenVPN itself. */ Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); CHECK_ERROR (err, "Add filter to permit IPv6 port 53 traffic from OpenVPN failed"); msg_handler (0, "Block_DNS: Added permit filters for exe_path"); /* Third filter. Block all IPv4 DNS queries. */ Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; Filter.action.type = FWP_ACTION_BLOCK; Filter.weight.type = FWP_EMPTY; Filter.numFilterConditions = 1; err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); CHECK_ERROR (err, "Add filter to block IPv4 DNS traffic failed"); msg_handler (0, "Block_DNS: Added block filters for all"); /* Forth filter. Block all IPv6 DNS queries. */ Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); CHECK_ERROR (err, "Add filter to block IPv6 DNS traffic failed"); /* Fifth filter. Permit IPv4 DNS queries from TAP. */ Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; Filter.action.type = FWP_ACTION_PERMIT; Filter.numFilterConditions = 2; 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; err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); CHECK_ERROR (err, "Add filter to permit IPv4 DNS traffic through TAP failed"); /* Sixth filter. Permit IPv6 DNS queries from TAP. */ Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid); CHECK_ERROR (err, "Add filter to permit IPv6 DNS traffic through TAP failed"); msg_handler (0, "Block_DNS: Added permit filters for TAP interface"); out: if (openvpnblob) FwpmFreeMemory0 ((void **)&openvpnblob); if (err && *engine_handle) { FwpmEngineClose0 (*engine_handle); *engine_handle = NULL; } return err; } DWORD delete_block_dns_filters (HANDLE engine_handle) { DWORD err = 0; /* * For dynamic sessions closing the engine removes all filters added in the session */ if (engine_handle) { err = FwpmEngineClose0(engine_handle); } return err; } #endif