Browse code

Add Windows DNS Leak fix using WFP ('block-outside-dns')

This option blocks all out-of-tunnel communication on TCP/UDP port 53
(except for OpenVPN itself), preventing DNS Leaks on Windows 8.1 and 10.

This is the same patch as dd628d2e0d786e4 in release/2.3, except that it
is always compiled (on WIN32) here - we already require compilation for
Vista+ in master (-> 2.4).

Reviewed-by: Selva Nair <selva.nair@gmail.com>
Reviewed-by: Lev Stipakov <lstipakov@gmail.com>
Reviewed-by: James Yonan <james@openvpn.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1449780715-4027-1-git-send-email-iam@valdikss.org.ru>
URL: http://article.gmane.org/gmane.network.openvpn.devel/10744

Signed-off-by: Gert Doering <gert@greenie.muc.de>

ValdikSS authored on 2015/12/11 05:51:55
Showing 8 changed files
... ...
@@ -1129,8 +1129,8 @@ When used with
1129 1129
 .B \-\-client
1130 1130
 or
1131 1131
 .B \-\-pull,
1132
-accept options pushed by server EXCEPT for routes and dhcp options
1133
-like DNS servers.
1132
+accept options pushed by server EXCEPT for routes, block-outside-dns and dhcp
1133
+options like DNS servers.
1134 1134
 
1135 1135
 When used on the client, this option effectively bars the
1136 1136
 server from adding routes to the client's routing table,
... ...
@@ -5568,6 +5568,14 @@ adapter list to the syslog or log file after the TUN/TAP adapter
5568 5568
 has been brought up and any routes have been added.
5569 5569
 .\"*********************************************************
5570 5570
 .TP
5571
+.B \-\-block\-outside\-dns
5572
+Block DNS servers on other network adapters to prevent
5573
+DNS leaks. This option prevents any application from accessing
5574
+TCP or UDP port 53 except one inside the tunnel. It uses
5575
+Windows Filtering Platform (WFP) and works on Windows Vista or
5576
+later.
5577
+.\"*********************************************************
5578
+.TP
5571 5579
 .B \-\-dhcp\-renew
5572 5580
 Ask Windows to renew the TAP adapter lease on startup.
5573 5581
 This option is normally unnecessary, as Windows automatically
... ...
@@ -127,5 +127,5 @@ openvpn_LDADD = \
127 127
 	$(OPTIONAL_DL_LIBS)
128 128
 if WIN32
129 129
 openvpn_SOURCES += openvpn_win32_resources.rc
130
-openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm
130
+openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4
131 131
 endif
... ...
@@ -1495,6 +1495,15 @@ do_open_tun (struct context *c)
1495 1495
 		   "up",
1496 1496
 		   c->c2.es);
1497 1497
 
1498
+#if defined(WIN32)
1499
+      if (c->options.block_outside_dns)
1500
+      {
1501
+        dmsg (D_LOW, "Blocking outside DNS");
1502
+        if (!win_wfp_block_dns(c->c1.tuntap->adapter_index))
1503
+            msg (M_FATAL, "Blocking DNS failed!");
1504
+      }
1505
+#endif
1506
+
1498 1507
       /* possibly add routes */
1499 1508
       if ((route_order() == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined))
1500 1509
 	do_route (&c->options, c->c1.route_list, c->c1.route_ipv6_list,
... ...
@@ -1623,6 +1632,14 @@ do_close_tun (struct context *c, bool force)
1623 1623
 		       "down",
1624 1624
 		       c->c2.es);
1625 1625
 
1626
+#if defined(WIN32)
1627
+            if (c->options.block_outside_dns)
1628
+            {
1629
+                if (!win_wfp_uninit())
1630
+                    msg (M_FATAL, "Uninitialising WFP failed!");
1631
+            }
1632
+#endif
1633
+
1626 1634
 	  /* actually close tun/tap device based on --down-pre flag */
1627 1635
 	  if (c->options.down_pre)
1628 1636
 	    do_close_tun_simple (c);
1629 1637
old mode 100755
1630 1638
new mode 100644
... ...
@@ -64,7 +64,7 @@
64 64
       <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
65 65
     </ResourceCompile>
66 66
     <Link>
67
-      <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
67
+      <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
68 68
       <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
69 69
       <GenerateDebugInformation>true</GenerateDebugInformation>
70 70
       <SubSystem>Console</SubSystem>
... ...
@@ -89,7 +89,7 @@
89 89
       <AdditionalIncludeDirectories>$(SOURCEBASE);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
90 90
     </ResourceCompile>
91 91
     <Link>
92
-      <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
92
+      <AdditionalDependencies>libeay32.lib;ssleay32.lib;lzo2.lib;pkcs11-helper.dll.lib;gdi32.lib;ws2_32.lib;wininet.lib;crypt32.lib;iphlpapi.lib;winmm.lib;Fwpuclnt.lib;Rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
93 93
       <AdditionalLibraryDirectories>$(OPENSSL_HOME)/lib;$(LZO_HOME)/lib;$(PKCS11H_HOME)/lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
94 94
       <GenerateDebugInformation>true</GenerateDebugInformation>
95 95
       <SubSystem>Console</SubSystem>
... ...
@@ -704,6 +704,9 @@ static const char usage_message[] =
704 704
   "                       optional parameter controls the initial state of ex.\n"
705 705
   "--show-net-up   : Show " PACKAGE_NAME "'s view of routing table and net adapter list\n"
706 706
   "                  after TAP adapter is up and routes have been added.\n"
707
+#ifdef WIN32
708
+  "--block-outside-dns   : Block DNS on other network adapters to prevent DNS leaks\n"
709
+#endif
707 710
   "Windows Standalone Options:\n"
708 711
   "\n"
709 712
   "--show-adapters : Show all TAP-Windows adapters.\n"
... ...
@@ -805,6 +808,7 @@ init_options (struct options *o, const bool init_gc)
805 805
   o->tuntap_options.dhcp_lease_time = 31536000; /* one year */
806 806
   o->tuntap_options.dhcp_masq_offset = 0;       /* use network address as internal DHCP server address */
807 807
   o->route_method = ROUTE_METHOD_ADAPTIVE;
808
+  o->block_outside_dns = false;
808 809
 #endif
809 810
 #if P2MP_SERVER
810 811
   o->real_hash_size = 256;
... ...
@@ -1673,6 +1677,7 @@ show_settings (const struct options *o)
1673 1673
 #ifdef WIN32
1674 1674
   SHOW_BOOL (show_net_up);
1675 1675
   SHOW_INT (route_method);
1676
+  SHOW_BOOL (block_outside_dns);
1676 1677
   show_tuntap_options (&o->tuntap_options);
1677 1678
 #endif
1678 1679
 #endif
... ...
@@ -6196,6 +6201,11 @@ add_option (struct options *options,
6196 6196
       VERIFY_PERMISSION (OPT_P_IPWIN32);
6197 6197
       options->tuntap_options.register_dns = true;
6198 6198
     }
6199
+  else if (streq (p[0], "block-outside-dns") && !p[1])
6200
+    {
6201
+      VERIFY_PERMISSION (OPT_P_IPWIN32);
6202
+      options->block_outside_dns = true;
6203
+    }
6199 6204
   else if (streq (p[0], "rdns-internal") && !p[1])
6200 6205
      /* standalone method for internal use
6201 6206
       *
... ...
@@ -585,6 +585,7 @@ struct options
585 585
   bool exit_event_initial_state;
586 586
   bool show_net_up;
587 587
   int route_method;
588
+  bool block_outside_dns;
588 589
 #endif
589 590
 
590 591
   bool use_peer_id;
... ...
@@ -47,6 +47,73 @@
47 47
 #include "memdbg.h"
48 48
 
49 49
 /*
50
+ * WFP-related defines and GUIDs.
51
+ */
52
+#include <fwpmu.h>
53
+#include <initguid.h>
54
+#include <fwpmtypes.h>
55
+#include <iphlpapi.h>
56
+
57
+#ifndef FWPM_SESSION_FLAG_DYNAMIC
58
+#define FWPM_SESSION_FLAG_DYNAMIC 0x00000001
59
+#endif
60
+
61
+// c38d57d1-05a7-4c33-904f-7fbceee60e82
62
+DEFINE_GUID(
63
+   FWPM_LAYER_ALE_AUTH_CONNECT_V4,
64
+   0xc38d57d1,
65
+   0x05a7,
66
+   0x4c33,
67
+   0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82
68
+);
69
+
70
+// 4a72393b-319f-44bc-84c3-ba54dcb3b6b4
71
+DEFINE_GUID(
72
+   FWPM_LAYER_ALE_AUTH_CONNECT_V6,
73
+   0x4a72393b,
74
+   0x319f,
75
+   0x44bc,
76
+   0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4
77
+);
78
+
79
+// d78e1e87-8644-4ea5-9437-d809ecefc971
80
+DEFINE_GUID(
81
+   FWPM_CONDITION_ALE_APP_ID,
82
+   0xd78e1e87,
83
+   0x8644,
84
+   0x4ea5,
85
+   0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71
86
+);
87
+
88
+// c35a604d-d22b-4e1a-91b4-68f674ee674b
89
+DEFINE_GUID(
90
+   FWPM_CONDITION_IP_REMOTE_PORT,
91
+   0xc35a604d,
92
+   0xd22b,
93
+   0x4e1a,
94
+   0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b
95
+);
96
+
97
+// 4cd62a49-59c3-4969-b7f3-bda5d32890a4
98
+DEFINE_GUID(
99
+   FWPM_CONDITION_IP_LOCAL_INTERFACE,
100
+   0x4cd62a49,
101
+   0x59c3,
102
+   0x4969,
103
+   0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4
104
+);
105
+
106
+/*
107
+ * WFP firewall name.
108
+ */
109
+WCHAR * FIREWALL_NAME = L"OpenVPN"; /* GLOBAL */
110
+
111
+/*
112
+ * WFP handle and GUID.
113
+ */
114
+static HANDLE m_hEngineHandle = NULL; /* GLOBAL */
115
+
116
+/*
50 117
  * Windows internal socket API state (opaque).
51 118
  */
52 119
 static struct WSAData wsa_state; /* GLOBAL */
... ...
@@ -1077,4 +1144,148 @@ win_get_tempdir()
1077 1077
   WideCharToMultiByte (CP_UTF8, 0, wtmpdir, -1, tmpdir, sizeof (tmpdir), NULL, NULL);
1078 1078
   return tmpdir;
1079 1079
 }
1080
+
1081
+bool
1082
+win_wfp_add_filter (HANDLE engineHandle,
1083
+                    const FWPM_FILTER0 *filter,
1084
+                    PSECURITY_DESCRIPTOR sd,
1085
+                    UINT64 *id)
1086
+{
1087
+    if (FwpmFilterAdd0(engineHandle, filter, sd, id) != ERROR_SUCCESS)
1088
+    {
1089
+        msg (M_NONFATAL, "Can't add WFP filter");
1090
+        return false;
1091
+    }
1092
+    return true;
1093
+}
1094
+
1095
+bool
1096
+win_wfp_block_dns (const NET_IFINDEX index)
1097
+{
1098
+    FWPM_SESSION0 session = {0};
1099
+    FWPM_SUBLAYER0 SubLayer = {0};
1100
+    NET_LUID tapluid;
1101
+    UINT64 filterid;
1102
+    WCHAR openvpnpath[MAX_PATH];
1103
+    FWP_BYTE_BLOB *openvpnblob = NULL;
1104
+    FWPM_FILTER0 Filter = {0};
1105
+    FWPM_FILTER_CONDITION0 Condition[2] = {0};
1106
+
1107
+    /* Add temporary filters which don't survive reboots or crashes. */
1108
+    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
1109
+
1110
+    dmsg (D_LOW, "Opening WFP engine");
1111
+
1112
+    if (FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &m_hEngineHandle) != ERROR_SUCCESS)
1113
+    {
1114
+        msg (M_NONFATAL, "Can't open WFP engine");
1115
+        return false;
1116
+    }
1117
+
1118
+    if (UuidCreate(&SubLayer.subLayerKey) != NO_ERROR)
1119
+        return false;
1120
+
1121
+    /* Populate packet filter layer information. */
1122
+    SubLayer.displayData.name = FIREWALL_NAME;
1123
+    SubLayer.displayData.description = FIREWALL_NAME;
1124
+    SubLayer.flags = 0;
1125
+    SubLayer.weight = 0x100;
1126
+
1127
+    /* Add packet filter to our interface. */
1128
+    dmsg (D_LOW, "Adding WFP sublayer");
1129
+    if (FwpmSubLayerAdd0(m_hEngineHandle, &SubLayer, NULL) != ERROR_SUCCESS)
1130
+    {
1131
+        msg (M_NONFATAL, "Can't add WFP sublayer");
1132
+        return false;
1133
+    }
1134
+
1135
+    dmsg (D_LOW, "Blocking DNS using WFP");
1136
+    if (ConvertInterfaceIndexToLuid(index, &tapluid) != NO_ERROR)
1137
+    {
1138
+        msg (M_NONFATAL, "Can't convert interface index to LUID");
1139
+        return false;
1140
+    }
1141
+    dmsg (D_LOW, "Tap Luid: %I64d", tapluid.Value);
1142
+
1143
+    /* Get OpenVPN path. */
1144
+    GetModuleFileNameW(NULL, openvpnpath, MAX_PATH);
1145
+
1146
+    if (FwpmGetAppIdFromFileName0(openvpnpath, &openvpnblob) != ERROR_SUCCESS)
1147
+        return false;
1148
+
1149
+    /* Prepare filter. */
1150
+    Filter.subLayerKey = SubLayer.subLayerKey;
1151
+    Filter.displayData.name = FIREWALL_NAME;
1152
+    Filter.weight.type = FWP_EMPTY;
1153
+    Filter.filterCondition = Condition;
1154
+    Filter.numFilterConditions = 2;
1155
+
1156
+    /* First filter. Block IPv4 DNS queries except from OpenVPN itself. */
1157
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
1158
+    Filter.action.type = FWP_ACTION_BLOCK;
1159
+
1160
+    Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
1161
+    Condition[0].matchType = FWP_MATCH_EQUAL;
1162
+    Condition[0].conditionValue.type = FWP_UINT16;
1163
+    Condition[0].conditionValue.uint16 = 53;
1164
+
1165
+    Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID;
1166
+    Condition[1].matchType = FWP_MATCH_NOT_EQUAL;
1167
+    Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE;
1168
+    Condition[1].conditionValue.byteBlob = openvpnblob;
1169
+
1170
+    /* Add filter condition to our interface. */
1171
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
1172
+        goto err;
1173
+    dmsg (D_LOW, "Filter (Block IPv4 DNS) added with ID=%I64d", filterid);
1174
+
1175
+    /* Second filter. Block IPv6 DNS queries except from OpenVPN itself. */
1176
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
1177
+
1178
+    /* Add filter condition to our interface. */
1179
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
1180
+        goto err;
1181
+    dmsg (D_LOW, "Filter (Block IPv6 DNS) added with ID=%I64d", filterid);
1182
+
1183
+    /* Third filter. Permit IPv4 DNS queries from TAP. */
1184
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
1185
+    Filter.action.type = FWP_ACTION_PERMIT;
1186
+
1187
+    Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE;
1188
+    Condition[1].matchType = FWP_MATCH_EQUAL;
1189
+    Condition[1].conditionValue.type = FWP_UINT64;
1190
+    Condition[1].conditionValue.uint64 = &tapluid.Value;
1191
+
1192
+    /* Add filter condition to our interface. */
1193
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
1194
+        goto err;
1195
+    dmsg (D_LOW, "Filter (Permit IPv4 DNS queries from TAP) added with ID=%I64d", filterid);
1196
+
1197
+    /* Forth filter. Permit IPv6 DNS queries from TAP. */
1198
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
1199
+
1200
+    /* Add filter condition to our interface. */
1201
+    if (!win_wfp_add_filter(m_hEngineHandle, &Filter, NULL, &filterid))
1202
+        goto err;
1203
+    dmsg (D_LOW, "Filter (Permit IPv6 DNS queries from TAP) added with ID=%I64d", filterid);
1204
+
1205
+    FwpmFreeMemory0((void **)&openvpnblob);
1206
+    return true;
1207
+
1208
+    err:
1209
+        FwpmFreeMemory0((void **)&openvpnblob);
1210
+        return false;
1211
+}
1212
+
1213
+bool
1214
+win_wfp_uninit()
1215
+{
1216
+    dmsg (D_LOW, "Uninitializing WFP");
1217
+    if (m_hEngineHandle) {
1218
+        FwpmEngineClose0(m_hEngineHandle);
1219
+        m_hEngineHandle = NULL;
1220
+    }
1221
+    return true;
1222
+}
1223
+
1080 1224
 #endif
... ...
@@ -271,5 +271,8 @@ const char *win_get_tempdir();
271 271
 /* Convert a string from UTF-8 to UCS-2 */
272 272
 WCHAR *wide_string (const char* utf8, struct gc_arena *gc);
273 273
 
274
+bool win_wfp_block_dns(const NET_IFINDEX index);
275
+bool win_wfp_uninit();
276
+
274 277
 #endif
275 278
 #endif