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