summaryrefslogtreecommitdiff
path: root/src/openvpn/block_dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/block_dns.c')
-rw-r--r--src/openvpn/block_dns.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/src/openvpn/block_dns.c b/src/openvpn/block_dns.c
new file mode 100644
index 0000000..cb3ce88
--- /dev/null
+++ b/src/openvpn/block_dns.c
@@ -0,0 +1,332 @@
+/*
+ * 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
+);
+
+/* UUID of WFP sublayer used by all instances of openvpn
+ 2f660d7e-6a37-11e6-a181-001e8c6e04a2 */
+DEFINE_GUID(
+ OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER,
+ 0x2f660d7e,
+ 0x6a37,
+ 0x11e6,
+ 0xa1, 0x81, 0x00, 0x1e, 0x8c, 0x6e, 0x04, 0xa2
+);
+
+static WCHAR *FIREWALL_NAME = L"OpenVPN";
+
+/*
+ * 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; }
+
+/*
+ * Add a persistent sublayer with specified uuid.
+ */
+static DWORD
+add_sublayer (GUID uuid)
+{
+ FWPM_SESSION0 session;
+ HANDLE engine = NULL;
+ DWORD err = 0;
+ FWPM_SUBLAYER0 sublayer;
+
+ memset (&session, 0, sizeof(session));
+ memset (&sublayer, 0, sizeof(sublayer));
+
+ err = FwpmEngineOpen0 (NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine);
+ if (err != ERROR_SUCCESS)
+ goto out;
+
+ sublayer.subLayerKey = uuid;
+ sublayer.displayData.name = FIREWALL_NAME;
+ sublayer.displayData.description = FIREWALL_NAME;
+ sublayer.flags = 0;
+ sublayer.weight = 0x100;
+
+ /* Add sublayer to the session */
+ err = FwpmSubLayerAdd0 (engine, &sublayer, NULL);
+
+out:
+ if (engine)
+ FwpmEngineClose0 (engine);
+ return err;
+}
+
+/*
+ * 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_ptr = NULL;
+ NET_LUID tapluid;
+ UINT64 filterid;
+ FWP_BYTE_BLOB *openvpnblob = NULL;
+ FWPM_FILTER0 Filter = {0};
+ FWPM_FILTER_CONDITION0 Condition[2] = {0};
+ 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");
+ msg_handler (0, "Block_DNS: WFP engine opened");
+
+ /* Check sublayer exists and add one if it does not. */
+ if (FwpmSubLayerGetByKey0 (*engine_handle, &OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER, &sublayer_ptr)
+ == ERROR_SUCCESS)
+ {
+ msg_handler (0, "Block_DNS: Using existing sublayer");
+ FwpmFreeMemory0 ((void **)&sublayer_ptr);
+ }
+ else
+ { /* Add a new sublayer -- as another process may add it in the meantime,
+ do not treat "already exists" as an error */
+ err = add_sublayer (OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER);
+
+ if (err == FWP_E_ALREADY_EXISTS || err == ERROR_SUCCESS)
+ msg_handler (0, "Block_DNS: Added a persistent sublayer with pre-defined UUID");
+ else
+ CHECK_ERROR (err, "add_sublayer: failed to add persistent sublayer");
+ }
+
+ 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 = OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER;
+ 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");
+
+ /* 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");
+
+ msg_handler (0, "Block_DNS: Added block filters for all interfaces");
+
+ /* Fifth filter. Permit IPv4 DNS queries from TAP.
+ * Use a non-zero weight so that the permit filters get higher priority
+ * over the block filter added with automatic weighting */
+
+ Filter.weight.type = FWP_UINT8;
+ Filter.weight.uint8 = 0xE;
+ 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.
+ * Use same weight as IPv4 filter */
+ 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