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-2022 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 */
56 DEFINE_GUID(
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 */
65 DEFINE_GUID(
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 */
74 DEFINE_GUID(
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 */
83 DEFINE_GUID(
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 */
92 DEFINE_GUID(
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 */
102 DEFINE_GUID(
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
default_msg_handler(DWORD err,const char * msg)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
add_sublayer(GUID uuid)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
add_block_dns_filters(HANDLE * engine_handle,int index,const WCHAR * exe_path,block_dns_msg_handler_t msg_handler)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
delete_block_dns_filters(HANDLE engine_handle)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
get_interface_metric(const NET_IFINDEX index,const ADDRESS_FAMILY family,int * is_auto)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
set_interface_metric(const NET_IFINDEX index,const ADDRESS_FAMILY family,const ULONG metric)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 */
429