Skip to content

Commit

Permalink
Windows: enforce 'block-local' with WFP filters
Browse files Browse the repository at this point in the history
In an attempt to better defend against the TunnelCrack attacks, enforce
that no traffic can pass to anything else than the VPN interface when
the 'block-local' flags is given with either --redirect-gateway or
--redirect-private.

Reuse much of the existing --block-outside-dns code, but make it more
general, so that it can also block any traffic, not just port 53.

Uses the Windows Filtering Platform for enforcement in addition to the
routes redirecting the networks into the tunnel.

Change-Id: Ic9bf797bfc7e2d471998a84cb0f071db3e4832ba
Signed-off-by: Heiko Hund <[email protected]>
Acked-by: Lev Stipakov <[email protected]>
Acked-by: Gert Doering <[email protected]>
Message-Id: <[email protected]>
URL: https://www.mail-archive.com/[email protected]/msg28717.html
Signed-off-by: Gert Doering <[email protected]>
  • Loading branch information
d12fk authored and cron2 committed Jun 5, 2024
1 parent 7dfff75 commit bf887c9
Show file tree
Hide file tree
Showing 15 changed files with 357 additions and 235 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,6 @@ set(SOURCE_FILES
src/openvpn/base64.c
src/openvpn/base64.h
src/openvpn/basic.h
src/openvpn/block_dns.h
src/openvpn/block_dns.c
src/openvpn/buffer.c
src/openvpn/buffer.h
src/openvpn/circ_list.h
Expand Down Expand Up @@ -550,6 +548,8 @@ set(SOURCE_FILES
src/openvpn/ssl_util.h
src/openvpn/vlan.c
src/openvpn/vlan.h
src/openvpn/wfp_block.c
src/openvpn/wfp_block.h
src/openvpn/win32.c
src/openvpn/win32-util.c
src/openvpn/win32.h
Expand Down
4 changes: 4 additions & 0 deletions doc/man-sections/vpn-network-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ routing.
Block access to local LAN when the tunnel is active, except for
the LAN gateway itself. This is accomplished by routing the local
LAN (except for the LAN gateway address) into the tunnel.
On Windows WFP filters are added in addition to the routes which
block access to resources not routed through the VPN adapter.
Push this flag to protect against TunnelCrack type of attacks
(see: https://tunnelcrack.mathyvanhoef.com/).

:code:`ipv6`
Redirect IPv6 routing into the tunnel. This works similar to
Expand Down
15 changes: 12 additions & 3 deletions include/openvpn-msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#ifndef OPENVPN_MSG_H_
#define OPENVPN_MSG_H_

#include <windef.h>
#include <ws2tcpip.h>

typedef enum {
msg_acknowledgement,
msg_add_address,
Expand All @@ -35,8 +38,8 @@ typedef enum {
msg_add_nbt_cfg,
msg_del_nbt_cfg,
msg_flush_neighbors,
msg_add_block_dns,
msg_del_block_dns,
msg_add_wfp_block,
msg_del_wfp_block,
msg_register_dns,
msg_enable_dhcp,
msg_register_ring_buffers,
Expand All @@ -61,6 +64,11 @@ typedef struct {
char name[256];
} interface_t;

typedef enum {
wfp_block_local = 1<<0,
wfp_block_dns = 1<<1
} wfp_block_flags_t;

typedef struct {
message_header_t header;
short family;
Expand Down Expand Up @@ -120,8 +128,9 @@ typedef struct {

typedef struct {
message_header_t header;
wfp_block_flags_t flags;
interface_t iface;
} block_dns_message_t;
} wfp_block_message_t;

typedef struct {
message_header_t header;
Expand Down
2 changes: 1 addition & 1 deletion src/openvpn/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,6 @@ openvpn_LDADD = \
$(OPTIONAL_DL_LIBS) \
$(OPTIONAL_INOTIFY_LIBS)
if WIN32
openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h ring_buffer.h
openvpn_SOURCES += openvpn_win32_resources.rc wfp_block.c wfp_block.h ring_buffer.h
openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt -lsetupapi -lbcrypt
endif
98 changes: 56 additions & 42 deletions src/openvpn/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,54 @@ can_preserve_tun(struct tuntap *tt)
#endif
}

/**
* Add WFP filters to block traffic to local networks.
* Depending on the configuration all or just DNS is filtered.
* This functionality is only available on Windows on all other
* systems this function is a noop.
*
* @param c pointer to the connection context
*/
static void
add_wfp_block(struct context *c)
{
#if defined(_WIN32)
/* Fortify 'redirect-gateway block-local' with firewall rules? */
bool block_local = block_local_needed(c->c1.route_list);

if (c->options.block_outside_dns || block_local)
{
BOOL dns_only = !block_local;
if (!win_wfp_block(c->c1.tuntap->adapter_index, c->options.msg_channel, dns_only))
{
msg(M_FATAL, "WFP: initialization failed");
}
}
#endif
}

/**
* Remove any WFP block filters previously added.
* This functionality is only available on Windows on all other
* systems the function is a noop.
*
* @param c pointer to the connection context
* @param adapter_index the VPN adapter index
*/
static void
del_wfp_block(struct context *c, unsigned long adapter_index)
{
#if defined(_WIN32)
if (c->options.block_outside_dns || block_local_needed(c->c1.route_list))
{
if (!win_wfp_uninit(adapter_index, c->options.msg_channel))
{
msg(M_FATAL, "WFP: deinitialization failed");
}
}
#endif
}

static bool
do_open_tun(struct context *c, int *error_flags)
{
Expand Down Expand Up @@ -1904,16 +1952,7 @@ do_open_tun(struct context *c, int *error_flags)
"up",
c->c2.es);

#if defined(_WIN32)
if (c->options.block_outside_dns)
{
dmsg(D_LOW, "Blocking outside DNS");
if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel))
{
msg(M_FATAL, "Blocking DNS failed!");
}
}
#endif
add_wfp_block(c);

/* possibly add routes */
if ((route_order() == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined))
Expand Down Expand Up @@ -1953,17 +1992,8 @@ do_open_tun(struct context *c, int *error_flags)
"up",
c->c2.es);
}
#if defined(_WIN32)
if (c->options.block_outside_dns)
{
dmsg(D_LOW, "Blocking outside DNS");
if (!win_wfp_block_dns(c->c1.tuntap->adapter_index, c->options.msg_channel))
{
msg(M_FATAL, "Blocking DNS failed!");
}
}
#endif

add_wfp_block(c);
}
gc_free(&gc);
return ret;
Expand Down Expand Up @@ -2012,11 +2042,12 @@ do_close_tun(struct context *c, bool force)

struct gc_arena gc = gc_new();
const char *tuntap_actual = string_alloc(c->c1.tuntap->actual_name, &gc);
#ifdef _WIN32
DWORD adapter_index = c->c1.tuntap->adapter_index;
#endif
const in_addr_t local = c->c1.tuntap->local;
const in_addr_t remote_netmask = c->c1.tuntap->remote_netmask;
unsigned long adapter_index = 0;
#ifdef _WIN32
adapter_index = c->c1.tuntap->adapter_index;
#endif

if (force || !(c->sig->signal_received == SIGUSR1 && c->options.persist_tun))
{
Expand Down Expand Up @@ -2081,15 +2112,7 @@ do_close_tun(struct context *c, bool force)
"down",
c->c2.es);

#if defined(_WIN32)
if (c->options.block_outside_dns)
{
if (!win_wfp_uninit(adapter_index, c->options.msg_channel))
{
msg(M_FATAL, "Uninitialising WFP failed!");
}
}
#endif
del_wfp_block(c, adapter_index);

/* actually close tun/tap device based on --down-pre flag */
if (c->options.down_pre)
Expand Down Expand Up @@ -2120,16 +2143,7 @@ do_close_tun(struct context *c, bool force)
c->c2.es);
}

#if defined(_WIN32)
if (c->options.block_outside_dns)
{
if (!win_wfp_uninit(adapter_index, c->options.msg_channel))
{
msg(M_FATAL, "Uninitialising WFP failed!");
}
}
#endif

del_wfp_block(c, adapter_index);
}
gc_free(&gc);
}
Expand Down
60 changes: 33 additions & 27 deletions src/openvpn/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* Support routines for adding/deleting network routes.
*/
#include <stddef.h>
#include <stdbool.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
Expand Down Expand Up @@ -74,7 +75,9 @@ static bool del_route_ipapi(const struct route_ipv4 *r, const struct tuntap *tt)

#endif

static void delete_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es, openvpn_net_ctx_t *ctx);
static void delete_route(struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags,
const struct route_gateway_info *rgi, const struct env_set *es,
openvpn_net_ctx_t *ctx);

static void get_bypass_addresses(struct route_bypass *rb, const unsigned int flags);

Expand Down Expand Up @@ -566,9 +569,7 @@ add_block_local_item(struct route_list *rl,
const struct route_gateway_address *gateway,
in_addr_t target)
{
const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
if ((rl->rgi.flags & rgi_needed) == rgi_needed
&& rl->rgi.gateway.netmask < 0xFFFFFFFF)
if (rl->rgi.gateway.netmask < 0xFFFFFFFF)
{
struct route_ipv4 *r1, *r2;
unsigned int l2;
Expand All @@ -593,38 +594,39 @@ add_block_local_item(struct route_list *rl,
}

static void
add_block_local(struct route_list *rl)
add_block_local_routes(struct route_list *rl)
{
const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
if ((rl->flags & RG_BLOCK_LOCAL)
&& (rl->rgi.flags & rgi_needed) == rgi_needed
&& (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
&& rl->spec.remote_host_local != TLA_LOCAL)
{
size_t i;

#ifndef TARGET_ANDROID
/* add bypass for gateway addr */
add_bypass_address(&rl->spec.bypass, rl->rgi.gateway.addr);
/* add bypass for gateway addr */
add_bypass_address(&rl->spec.bypass, rl->rgi.gateway.addr);
#endif

/* block access to local subnet */
add_block_local_item(rl, &rl->rgi.gateway, rl->spec.remote_endpoint);
/* block access to local subnet */
add_block_local_item(rl, &rl->rgi.gateway, rl->spec.remote_endpoint);

/* process additional subnets on gateway interface */
for (i = 0; i < rl->rgi.n_addrs; ++i)
/* process additional subnets on gateway interface */
for (size_t i = 0; i < rl->rgi.n_addrs; ++i)
{
const struct route_gateway_address *gwa = &rl->rgi.addrs[i];
/* omit the add/subnet in &rl->rgi which we processed above */
if (!((rl->rgi.gateway.addr & rl->rgi.gateway.netmask) == (gwa->addr & gwa->netmask)
&& rl->rgi.gateway.netmask == gwa->netmask))
{
const struct route_gateway_address *gwa = &rl->rgi.addrs[i];
/* omit the add/subnet in &rl->rgi which we processed above */
if (!((rl->rgi.gateway.addr & rl->rgi.gateway.netmask) == (gwa->addr & gwa->netmask)
&& rl->rgi.gateway.netmask == gwa->netmask))
{
add_block_local_item(rl, gwa, rl->spec.remote_endpoint);
}
add_block_local_item(rl, gwa, rl->spec.remote_endpoint);
}
}
}

bool
block_local_needed(const struct route_list *rl)
{
const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
return (rl->flags & RG_BLOCK_LOCAL)
&& (rl->rgi.flags & rgi_needed) == rgi_needed
&& (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
&& rl->spec.remote_host_local != TLA_LOCAL;
}

bool
init_route_list(struct route_list *rl,
const struct route_option_list *opt,
Expand Down Expand Up @@ -698,7 +700,10 @@ init_route_list(struct route_list *rl,

if (rl->flags & RG_ENABLE)
{
add_block_local(rl);
if (block_local_needed(rl))
{
add_block_local_routes(rl);
}
get_bypass_addresses(&rl->spec.bypass, rl->flags);
#ifdef ENABLE_DEBUG
print_bypass_addresses(&rl->spec.bypass);
Expand Down Expand Up @@ -1241,6 +1246,7 @@ add_routes(struct route_list *rl, struct route_ipv6_list *rl6,
}
rl6->iflags |= RL_ROUTES_ADDED;
}

return ret;
}

Expand Down
12 changes: 12 additions & 0 deletions src/openvpn/route.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,18 @@ struct iroute_ipv6 {
struct iroute_ipv6 *next;
};

/**
* Get the decision whether to block traffic to local networks while the VPN
* is connected. This definitely returns false when not redirecting the gateway
* or when the 'block-local' flag is not set. Also checks for other
* prerequisites to redirect local networks into the tunnel.
*
* @param rl const pointer to the struct route_list to base the decision on.
*
* @return boolean indicating whether local traffic should be blocked.
*/
bool block_local_needed(const struct route_list *rl);

struct route_option_list *new_route_option_list(struct gc_arena *a);

struct route_ipv6_option_list *new_route_ipv6_option_list(struct gc_arena *a);
Expand Down
2 changes: 1 addition & 1 deletion src/openvpn/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
#include "manage.h"
#include "route.h"
#include "win32.h"
#include "block_dns.h"
#include "wfp_block.h"
#include "networking.h"

#include "memdbg.h"
Expand Down
Loading

0 comments on commit bf887c9

Please sign in to comment.