OpenVPN
block_dns.c
Go to the documentation of this file.
1 /*
2  * OpenVPN -- An application to securely tunnel IP networks
3  * over a single TCP/UDP port, with support for SSL/TLS-based
4  * session authentication and key exchange,
5  * packet encryption, packet authentication, and
6  * packet compression.
7  *
8  * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
9  * 2015-2016 <iam@valdikss.org.ru>
10  * 2016 Selva Nair <selva.nair@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2
14  * as published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #elif defined(_MSC_VER)
29 #include "config-msvc.h"
30 #endif
31 #ifdef HAVE_CONFIG_VERSION_H
32 #include "config-version.h"
33 #endif
34 
35 #include "syshead.h"
36 
37 #ifdef _WIN32
38 
39 #include <fwpmu.h>
40 #include <initguid.h>
41 #include <fwpmtypes.h>
42 #include <winsock2.h>
43 #include <ws2ipdef.h>
44 #include <iphlpapi.h>
45 #include "block_dns.h"
46 
47 /*
48  * WFP-related defines and GUIDs not in mingw32
49  */
50 
51 #ifndef FWPM_SESSION_FLAG_DYNAMIC
52 #define FWPM_SESSION_FLAG_DYNAMIC 0x00000001
53 #endif
54 
55 /* c38d57d1-05a7-4c33-904f-7fbceee60e82 */
57  FWPM_LAYER_ALE_AUTH_CONNECT_V4,
58  0xc38d57d1,
59  0x05a7,
60  0x4c33,
61  0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82
62  );
63 
64 /* 4a72393b-319f-44bc-84c3-ba54dcb3b6b4 */
66  FWPM_LAYER_ALE_AUTH_CONNECT_V6,
67  0x4a72393b,
68  0x319f,
69  0x44bc,
70  0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4
71  );
72 
73 /* d78e1e87-8644-4ea5-9437-d809ecefc971 */
75  FWPM_CONDITION_ALE_APP_ID,
76  0xd78e1e87,
77  0x8644,
78  0x4ea5,
79  0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71
80  );
81 
82 /* c35a604d-d22b-4e1a-91b4-68f674ee674b */
84  FWPM_CONDITION_IP_REMOTE_PORT,
85  0xc35a604d,
86  0xd22b,
87  0x4e1a,
88  0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b
89  );
90 
91 /* 4cd62a49-59c3-4969-b7f3-bda5d32890a4 */
93  FWPM_CONDITION_IP_LOCAL_INTERFACE,
94  0x4cd62a49,
95  0x59c3,
96  0x4969,
97  0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4
98  );
99 
100 /* UUID of WFP sublayer used by all instances of openvpn
101  * 2f660d7e-6a37-11e6-a181-001e8c6e04a2 */
103  OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER,
104  0x2f660d7e,
105  0x6a37,
106  0x11e6,
107  0xa1, 0x81, 0x00, 0x1e, 0x8c, 0x6e, 0x04, 0xa2
108  );
109 
110 static WCHAR *FIREWALL_NAME = L"OpenVPN";
111 
112 VOID NETIOAPI_API_
113 InitializeIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row);
114 
115 /*
116  * Default msg handler does nothing
117  */
118 static inline void
119 default_msg_handler(DWORD err, const char *msg)
120 {
121  return;
122 }
123 
124 #define CHECK_ERROR(err, msg) \
125  if (err) { msg_handler(err, msg); goto out; }
126 
127 /*
128  * Add a persistent sublayer with specified uuid.
129  */
130 static DWORD
131 add_sublayer(GUID uuid)
132 {
133  FWPM_SESSION0 session;
134  HANDLE engine = NULL;
135  DWORD err = 0;
136  FWPM_SUBLAYER0 sublayer;
137 
138  memset(&session, 0, sizeof(session));
139  memset(&sublayer, 0, sizeof(sublayer));
140 
141  err = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine);
142  if (err != ERROR_SUCCESS)
143  {
144  goto out;
145  }
146 
147  sublayer.subLayerKey = uuid;
148  sublayer.displayData.name = FIREWALL_NAME;
149  sublayer.displayData.description = FIREWALL_NAME;
150  sublayer.flags = 0;
151  sublayer.weight = 0x100;
152 
153  /* Add sublayer to the session */
154  err = FwpmSubLayerAdd0(engine, &sublayer, NULL);
155 
156 out:
157  if (engine)
158  {
159  FwpmEngineClose0(engine);
160  }
161  return err;
162 }
163 
164 /*
165  * Block outgoing port 53 traffic except for
166  * (i) adapter with the specified index
167  * OR
168  * (ii) processes with the specified executable path
169  * The firewall filters added here are automatically removed when the process exits or
170  * on calling delete_block_dns_filters().
171  * Arguments:
172  * engine_handle : On successful return contains the handle for a newly opened fwp session
173  * in which the filters are added.
174  * May be closed by passing to delete_block_dns_filters to remove the filters.
175  * index : The index of adapter for which traffic is permitted.
176  * exe_path : Path of executable for which traffic is permitted.
177  * msg_handler : An optional callback function for error reporting.
178  * Returns 0 on success, a non-zero status code of the last failed action on failure.
179  */
180 
181 DWORD
182 add_block_dns_filters(HANDLE *engine_handle,
183  int index,
184  const WCHAR *exe_path,
185  block_dns_msg_handler_t msg_handler
186  )
187 {
188  FWPM_SESSION0 session = {0};
189  FWPM_SUBLAYER0 *sublayer_ptr = NULL;
190  NET_LUID tapluid;
191  UINT64 filterid;
192  FWP_BYTE_BLOB *openvpnblob = NULL;
193  FWPM_FILTER0 Filter = {0};
194  FWPM_FILTER_CONDITION0 Condition[2] = {0};
195  DWORD err = 0;
196 
197  if (!msg_handler)
198  {
199  msg_handler = default_msg_handler;
200  }
201 
202  /* Add temporary filters which don't survive reboots or crashes. */
203  session.flags = FWPM_SESSION_FLAG_DYNAMIC;
204 
205  *engine_handle = NULL;
206 
207  err = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, engine_handle);
208  CHECK_ERROR(err, "FwpEngineOpen: open fwp session failed");
209  msg_handler(0, "Block_DNS: WFP engine opened");
210 
211  /* Check sublayer exists and add one if it does not. */
212  if (FwpmSubLayerGetByKey0(*engine_handle, &OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER, &sublayer_ptr)
213  == ERROR_SUCCESS)
214  {
215  msg_handler(0, "Block_DNS: Using existing sublayer");
216  FwpmFreeMemory0((void **)&sublayer_ptr);
217  }
218  else
219  { /* Add a new sublayer -- as another process may add it in the meantime,
220  * do not treat "already exists" as an error */
221  err = add_sublayer(OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER);
222 
223  if (err == FWP_E_ALREADY_EXISTS || err == ERROR_SUCCESS)
224  {
225  msg_handler(0, "Block_DNS: Added a persistent sublayer with pre-defined UUID");
226  }
227  else
228  {
229  CHECK_ERROR(err, "add_sublayer: failed to add persistent sublayer");
230  }
231  }
232 
233  err = ConvertInterfaceIndexToLuid(index, &tapluid);
234  CHECK_ERROR(err, "Convert interface index to luid failed");
235 
236  err = FwpmGetAppIdFromFileName0(exe_path, &openvpnblob);
237  CHECK_ERROR(err, "Get byte blob for openvpn executable name failed");
238 
239  /* Prepare filter. */
240  Filter.subLayerKey = OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER;
241  Filter.displayData.name = FIREWALL_NAME;
242  Filter.weight.type = FWP_UINT8;
243  Filter.weight.uint8 = 0xF;
244  Filter.filterCondition = Condition;
245  Filter.numFilterConditions = 2;
246 
247  /* First filter. Permit IPv4 DNS queries from OpenVPN itself. */
248  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
249  Filter.action.type = FWP_ACTION_PERMIT;
250 
251  Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
252  Condition[0].matchType = FWP_MATCH_EQUAL;
253  Condition[0].conditionValue.type = FWP_UINT16;
254  Condition[0].conditionValue.uint16 = 53;
255 
256  Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID;
257  Condition[1].matchType = FWP_MATCH_EQUAL;
258  Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE;
259  Condition[1].conditionValue.byteBlob = openvpnblob;
260 
261  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
262  CHECK_ERROR(err, "Add filter to permit IPv4 port 53 traffic from OpenVPN failed");
263 
264  /* Second filter. Permit IPv6 DNS queries from OpenVPN itself. */
265  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
266 
267  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
268  CHECK_ERROR(err, "Add filter to permit IPv6 port 53 traffic from OpenVPN failed");
269 
270  msg_handler(0, "Block_DNS: Added permit filters for exe_path");
271 
272  /* Third filter. Block all IPv4 DNS queries. */
273  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
274  Filter.action.type = FWP_ACTION_BLOCK;
275  Filter.weight.type = FWP_EMPTY;
276  Filter.numFilterConditions = 1;
277 
278  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
279  CHECK_ERROR(err, "Add filter to block IPv4 DNS traffic failed");
280 
281  /* Forth filter. Block all IPv6 DNS queries. */
282  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
283 
284  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
285  CHECK_ERROR(err, "Add filter to block IPv6 DNS traffic failed");
286 
287  msg_handler(0, "Block_DNS: Added block filters for all interfaces");
288 
289  /* Fifth filter. Permit IPv4 DNS queries from TAP.
290  * Use a non-zero weight so that the permit filters get higher priority
291  * over the block filter added with automatic weighting */
292 
293  Filter.weight.type = FWP_UINT8;
294  Filter.weight.uint8 = 0xE;
295  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
296  Filter.action.type = FWP_ACTION_PERMIT;
297  Filter.numFilterConditions = 2;
298 
299  Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE;
300  Condition[1].matchType = FWP_MATCH_EQUAL;
301  Condition[1].conditionValue.type = FWP_UINT64;
302  Condition[1].conditionValue.uint64 = &tapluid.Value;
303 
304  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
305  CHECK_ERROR(err, "Add filter to permit IPv4 DNS traffic through TAP failed");
306 
307  /* Sixth filter. Permit IPv6 DNS queries from TAP.
308  * Use same weight as IPv4 filter */
309  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
310 
311  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
312  CHECK_ERROR(err, "Add filter to permit IPv6 DNS traffic through TAP failed");
313 
314  msg_handler(0, "Block_DNS: Added permit filters for TAP interface");
315 
316 out:
317 
318  if (openvpnblob)
319  {
320  FwpmFreeMemory0((void **)&openvpnblob);
321  }
322 
323  if (err && *engine_handle)
324  {
325  FwpmEngineClose0(*engine_handle);
326  *engine_handle = NULL;
327  }
328 
329  return err;
330 }
331 
332 DWORD
333 delete_block_dns_filters(HANDLE engine_handle)
334 {
335  DWORD err = 0;
336  /*
337  * For dynamic sessions closing the engine removes all filters added in the session
338  */
339  if (engine_handle)
340  {
341  err = FwpmEngineClose0(engine_handle);
342  }
343  return err;
344 }
345 
346 /*
347  * Return interface metric value for the specified interface index.
348  *
349  * Arguments:
350  * index : The index of TAP adapter.
351  * family : Address family (AF_INET for IPv4 and AF_INET6 for IPv6).
352  * is_auto : On return set to true if automatic metric is in use.
353  * Unused if NULL.
354  *
355  * Returns positive metric value or -1 on error.
356  */
357 int
358 get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
359 {
360  DWORD err = 0;
361  MIB_IPINTERFACE_ROW ipiface;
362  InitializeIpInterfaceEntry(&ipiface);
363  ipiface.Family = family;
364  ipiface.InterfaceIndex = index;
365 
366  if (is_auto)
367  {
368  *is_auto = 0;
369  }
370  err = GetIpInterfaceEntry(&ipiface);
371 
372  /* On Windows metric is never > INT_MAX so return value of int is ok.
373  * But we check for overflow nevertheless.
374  */
375  if (err == NO_ERROR && ipiface.Metric <= INT_MAX)
376  {
377  if (is_auto)
378  {
379  *is_auto = ipiface.UseAutomaticMetric;
380  }
381  return (int)ipiface.Metric;
382  }
383  return -1;
384 }
385 
386 /*
387  * Sets interface metric value for specified interface index.
388  *
389  * Arguments:
390  * index : The index of TAP adapter.
391  * family : Address family (AF_INET for IPv4 and AF_INET6 for IPv6).
392  * metric : Metric value. 0 for automatic metric.
393  * Returns 0 on success, a non-zero status code of the last failed action on failure.
394  */
395 
396 DWORD
397 set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family,
398  const ULONG metric)
399 {
400  DWORD err = 0;
401  MIB_IPINTERFACE_ROW ipiface;
402  InitializeIpInterfaceEntry(&ipiface);
403  ipiface.Family = family;
404  ipiface.InterfaceIndex = index;
405  err = GetIpInterfaceEntry(&ipiface);
406  if (err == NO_ERROR)
407  {
408  if (family == AF_INET)
409  {
410  /* required for IPv4 as per MSDN */
411  ipiface.SitePrefixLength = 0;
412  }
413  ipiface.Metric = metric;
414  if (metric == 0)
415  {
416  ipiface.UseAutomaticMetric = TRUE;
417  }
418  else
419  {
420  ipiface.UseAutomaticMetric = FALSE;
421  }
422  err = SetIpInterfaceEntry(&ipiface);
423  if (err == NO_ERROR)
424  {
425  return 0;
426  }
427  }
428  return err;
429 }
430 
431 #endif /* ifdef _WIN32 */
#define FWPM_SESSION_FLAG_DYNAMIC
Definition: block_dns.c:52
DWORD delete_block_dns_filters(HANDLE engine_handle)
Definition: block_dns.c:333
#define CHECK_ERROR(err, msg)
Definition: block_dns.c:124
int get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
Return interface metric value for the specified interface index.
Definition: block_dns.c:358
DEFINE_GUID(FWPM_LAYER_ALE_AUTH_CONNECT_V4, 0xc38d57d1, 0x05a7, 0x4c33, 0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82)
DWORD add_block_dns_filters(HANDLE *engine_handle, int index, const WCHAR *exe_path, block_dns_msg_handler_t msg_handler)
Definition: block_dns.c:182
#define msg
Definition: error.h:173
static void default_msg_handler(DWORD err, const char *msg)
Definition: block_dns.c:119
#define UINT64(c)
Definition: ntlm.c:54
DWORD set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, const ULONG metric)
Sets interface metric value for specified interface index.
Definition: block_dns.c:397
static WCHAR * FIREWALL_NAME
Definition: block_dns.c:110
VOID NETIOAPI_API_ InitializeIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row)
static DWORD add_sublayer(GUID uuid)
Definition: block_dns.c:131
void(* block_dns_msg_handler_t)(DWORD err, const char *msg)
Definition: block_dns.h:32