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