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 /*
113  * Default msg handler does nothing
114  */
115 static inline void
116 default_msg_handler(DWORD err, const char *msg)
117 {
118  return;
119 }
120 
121 #define CHECK_ERROR(err, msg) \
122  if (err) { msg_handler(err, msg); goto out; }
123 
124 /*
125  * Add a persistent sublayer with specified uuid.
126  */
127 static DWORD
128 add_sublayer(GUID uuid)
129 {
130  FWPM_SESSION0 session;
131  HANDLE engine = NULL;
132  DWORD err = 0;
133  FWPM_SUBLAYER0 sublayer;
134 
135  memset(&session, 0, sizeof(session));
136  memset(&sublayer, 0, sizeof(sublayer));
137 
138  err = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine);
139  if (err != ERROR_SUCCESS)
140  {
141  goto out;
142  }
143 
144  sublayer.subLayerKey = uuid;
145  sublayer.displayData.name = FIREWALL_NAME;
146  sublayer.displayData.description = FIREWALL_NAME;
147  sublayer.flags = 0;
148  sublayer.weight = 0x100;
149 
150  /* Add sublayer to the session */
151  err = FwpmSubLayerAdd0(engine, &sublayer, NULL);
152 
153 out:
154  if (engine)
155  {
156  FwpmEngineClose0(engine);
157  }
158  return err;
159 }
160 
161 /*
162  * Block outgoing port 53 traffic except for
163  * (i) adapter with the specified index
164  * OR
165  * (ii) processes with the specified executable path
166  * The firewall filters added here are automatically removed when the process exits or
167  * on calling delete_block_dns_filters().
168  * Arguments:
169  * engine_handle : On successful return contains the handle for a newly opened fwp session
170  * in which the filters are added.
171  * May be closed by passing to delete_block_dns_filters to remove the filters.
172  * index : The index of adapter for which traffic is permitted.
173  * exe_path : Path of executable for which traffic is permitted.
174  * msg_handler : An optional callback function for error reporting.
175  * Returns 0 on success, a non-zero status code of the last failed action on failure.
176  */
177 
178 DWORD
179 add_block_dns_filters(HANDLE *engine_handle,
180  int index,
181  const WCHAR *exe_path,
182  block_dns_msg_handler_t msg_handler
183  )
184 {
185  FWPM_SESSION0 session = {0};
186  FWPM_SUBLAYER0 *sublayer_ptr = NULL;
187  NET_LUID tapluid;
188  UINT64 filterid;
189  FWP_BYTE_BLOB *openvpnblob = NULL;
190  FWPM_FILTER0 Filter = {0};
191  FWPM_FILTER_CONDITION0 Condition[2] = {0};
192  DWORD err = 0;
193 
194  if (!msg_handler)
195  {
196  msg_handler = default_msg_handler;
197  }
198 
199  /* Add temporary filters which don't survive reboots or crashes. */
200  session.flags = FWPM_SESSION_FLAG_DYNAMIC;
201 
202  *engine_handle = NULL;
203 
204  err = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, engine_handle);
205  CHECK_ERROR(err, "FwpEngineOpen: open fwp session failed");
206  msg_handler(0, "Block_DNS: WFP engine opened");
207 
208  /* Check sublayer exists and add one if it does not. */
209  if (FwpmSubLayerGetByKey0(*engine_handle, &OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER, &sublayer_ptr)
210  == ERROR_SUCCESS)
211  {
212  msg_handler(0, "Block_DNS: Using existing sublayer");
213  FwpmFreeMemory0((void **)&sublayer_ptr);
214  }
215  else
216  { /* Add a new sublayer -- as another process may add it in the meantime,
217  * do not treat "already exists" as an error */
218  err = add_sublayer(OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER);
219 
220  if (err == FWP_E_ALREADY_EXISTS || err == ERROR_SUCCESS)
221  {
222  msg_handler(0, "Block_DNS: Added a persistent sublayer with pre-defined UUID");
223  }
224  else
225  {
226  CHECK_ERROR(err, "add_sublayer: failed to add persistent sublayer");
227  }
228  }
229 
230  err = ConvertInterfaceIndexToLuid(index, &tapluid);
231  CHECK_ERROR(err, "Convert interface index to luid failed");
232 
233  err = FwpmGetAppIdFromFileName0(exe_path, &openvpnblob);
234  CHECK_ERROR(err, "Get byte blob for openvpn executable name failed");
235 
236  /* Prepare filter. */
237  Filter.subLayerKey = OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER;
238  Filter.displayData.name = FIREWALL_NAME;
239  Filter.weight.type = FWP_UINT8;
240  Filter.weight.uint8 = 0xF;
241  Filter.filterCondition = Condition;
242  Filter.numFilterConditions = 2;
243 
244  /* First filter. Permit IPv4 DNS queries from OpenVPN itself. */
245  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
246  Filter.action.type = FWP_ACTION_PERMIT;
247 
248  Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
249  Condition[0].matchType = FWP_MATCH_EQUAL;
250  Condition[0].conditionValue.type = FWP_UINT16;
251  Condition[0].conditionValue.uint16 = 53;
252 
253  Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID;
254  Condition[1].matchType = FWP_MATCH_EQUAL;
255  Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE;
256  Condition[1].conditionValue.byteBlob = openvpnblob;
257 
258  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
259  CHECK_ERROR(err, "Add filter to permit IPv4 port 53 traffic from OpenVPN failed");
260 
261  /* Second filter. Permit IPv6 DNS queries from OpenVPN itself. */
262  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
263 
264  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
265  CHECK_ERROR(err, "Add filter to permit IPv6 port 53 traffic from OpenVPN failed");
266 
267  msg_handler(0, "Block_DNS: Added permit filters for exe_path");
268 
269  /* Third filter. Block all IPv4 DNS queries. */
270  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
271  Filter.action.type = FWP_ACTION_BLOCK;
272  Filter.weight.type = FWP_EMPTY;
273  Filter.numFilterConditions = 1;
274 
275  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
276  CHECK_ERROR(err, "Add filter to block IPv4 DNS traffic failed");
277 
278  /* Forth filter. Block all IPv6 DNS queries. */
279  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
280 
281  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
282  CHECK_ERROR(err, "Add filter to block IPv6 DNS traffic failed");
283 
284  msg_handler(0, "Block_DNS: Added block filters for all interfaces");
285 
286  /* Fifth filter. Permit IPv4 DNS queries from TAP.
287  * Use a non-zero weight so that the permit filters get higher priority
288  * over the block filter added with automatic weighting */
289 
290  Filter.weight.type = FWP_UINT8;
291  Filter.weight.uint8 = 0xE;
292  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
293  Filter.action.type = FWP_ACTION_PERMIT;
294  Filter.numFilterConditions = 2;
295 
296  Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE;
297  Condition[1].matchType = FWP_MATCH_EQUAL;
298  Condition[1].conditionValue.type = FWP_UINT64;
299  Condition[1].conditionValue.uint64 = &tapluid.Value;
300 
301  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
302  CHECK_ERROR(err, "Add filter to permit IPv4 DNS traffic through TAP failed");
303 
304  /* Sixth filter. Permit IPv6 DNS queries from TAP.
305  * Use same weight as IPv4 filter */
306  Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
307 
308  err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
309  CHECK_ERROR(err, "Add filter to permit IPv6 DNS traffic through TAP failed");
310 
311  msg_handler(0, "Block_DNS: Added permit filters for TAP interface");
312 
313 out:
314 
315  if (openvpnblob)
316  {
317  FwpmFreeMemory0((void **)&openvpnblob);
318  }
319 
320  if (err && *engine_handle)
321  {
322  FwpmEngineClose0(*engine_handle);
323  *engine_handle = NULL;
324  }
325 
326  return err;
327 }
328 
329 DWORD
330 delete_block_dns_filters(HANDLE engine_handle)
331 {
332  DWORD err = 0;
333  /*
334  * For dynamic sessions closing the engine removes all filters added in the session
335  */
336  if (engine_handle)
337  {
338  err = FwpmEngineClose0(engine_handle);
339  }
340  return err;
341 }
342 
343 /*
344  * Return interface metric value for the specified interface index.
345  *
346  * Arguments:
347  * index : The index of TAP adapter.
348  * family : Address family (AF_INET for IPv4 and AF_INET6 for IPv6).
349  * is_auto : On return set to true if automatic metric is in use.
350  * Unused if NULL.
351  *
352  * Returns positive metric value or -1 on error.
353  */
354 int
355 get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
356 {
357  DWORD err = 0;
358  MIB_IPINTERFACE_ROW ipiface;
359  InitializeIpInterfaceEntry(&ipiface);
360  ipiface.Family = family;
361  ipiface.InterfaceIndex = index;
362 
363  if (is_auto)
364  {
365  *is_auto = 0;
366  }
367  err = GetIpInterfaceEntry(&ipiface);
368 
369  /* On Windows metric is never > INT_MAX so return value of int is ok.
370  * But we check for overflow nevertheless.
371  */
372  if (err == NO_ERROR && ipiface.Metric <= INT_MAX)
373  {
374  if (is_auto)
375  {
376  *is_auto = ipiface.UseAutomaticMetric;
377  }
378  return (int)ipiface.Metric;
379  }
380  return -1;
381 }
382 
383 /*
384  * Sets interface metric value for specified interface index.
385  *
386  * Arguments:
387  * index : The index of TAP adapter.
388  * family : Address family (AF_INET for IPv4 and AF_INET6 for IPv6).
389  * metric : Metric value. 0 for automatic metric.
390  * Returns 0 on success, a non-zero status code of the last failed action on failure.
391  */
392 
393 DWORD
394 set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family,
395  const ULONG metric)
396 {
397  DWORD err = 0;
398  MIB_IPINTERFACE_ROW ipiface;
399  InitializeIpInterfaceEntry(&ipiface);
400  ipiface.Family = family;
401  ipiface.InterfaceIndex = index;
402  err = GetIpInterfaceEntry(&ipiface);
403  if (err == NO_ERROR)
404  {
405  if (family == AF_INET)
406  {
407  /* required for IPv4 as per MSDN */
408  ipiface.SitePrefixLength = 0;
409  }
410  ipiface.Metric = metric;
411  if (metric == 0)
412  {
413  ipiface.UseAutomaticMetric = TRUE;
414  }
415  else
416  {
417  ipiface.UseAutomaticMetric = FALSE;
418  }
419  err = SetIpInterfaceEntry(&ipiface);
420  if (err == NO_ERROR)
421  {
422  return 0;
423  }
424  }
425  return err;
426 }
427 
428 #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:330
#define CHECK_ERROR(err, msg)
Definition: block_dns.c:121
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:355
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:179
static void default_msg_handler(DWORD err, const char *msg)
Definition: block_dns.c:116
#define msg(flags,...)
Definition: error.h:153
#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:394
static WCHAR * FIREWALL_NAME
Definition: block_dns.c:110
static DWORD add_sublayer(GUID uuid)
Definition: block_dns.c:128
void(* block_dns_msg_handler_t)(DWORD err, const char *msg)
Definition: block_dns.h:32