2021-02-17 15:38:07 +01:00
2021-02-02 13:30:19 +01:00
2021-02-02 13:30:19 +01:00
2021-02-05 11:16:50 +01:00
2021-02-02 13:30:19 +01:00

Mullvad split tunnel driver for Windows

This is a non-PnP KMDF driver suitable for implementing split tunneling in VPN client software. The driver works on Windows 7 through 10.

Main features:

  • Exclude network traffic from VPN tunnel based on process paths.
  • Tracking of arriving and departing processes.
  • Atomic process classifications remove any races that could enable traffic leaks.
  • Propagation of exclusion flag to child processes.
  • Dynamic reconfiguration.
  • Blocking of pre-existing unwanted connections.
  • Blocking of IPv6 traffic in cases where it would otherwise leak inside the tunnel.

Development environment

Visual Studio 2019, any edition.

WDK, recent version.

Architecture

Overview

The features mentioned above are wholly implemented in the driver. However, the driver needs a user mode agent to initially and continuously provide it with configuration data.

Specifically, the agent provides a set of application paths that should be excluded from the tunnel. It also communicates the tunnel IPs (IPv4/IPv6) as well as IPs of the primary network interface.

The agent is required to monitor network interfaces and update the driver with new IPs, as they change.

The code in ./testing gives an example of building blocks needed in the agent. This code is mostly useful for manual testing. For an implementation that's more suited for production use, refer to relevant sections of the Mullvad VPN app

Major subsystems

Firewall

The firewall subsystem integrates with WFP and manages things such as:

  • Socket bind redirection. Excluded apps aren't allowed to bind to inaddr_any/in6addr_any or bind to the tunnel interface.
  • Traffic blocking. Excluded apps aren't allowed to communicate inside the tunnel.
  • Traffic approval to override Mullvad VPN app WFP filters and enable excluded apps to communicate outside the tunnel. For more information on how the Mullvad VPN app configures WFP, refer to Mullvad VPN app security

This subsystem is fairly large, and part of the reason is because we have to carefully track our modifications in WFP. This is addressed using a transactional system which is employed in parallell with the standard WFP transactions.

Process management

The driver maintains a complete and updated process tree, and is registered with the system to be notified of arriving/departing processes.

  • Examine arriving processes to determine if they should be excluded.
  • Discover child processes of excluded processes and propagate exclusion flag.
  • Examine departing processes and update WFP and internal state, as applicable.

Eventing

The eventing subsystem is used to keep the user mode agent up-to-date with important events in the driver. Events are delivered using the inverted call model. An event is sent when e.g. a process starts being excluded, or can't be excluded event though it should be, etc.

Major data structures

Process registry

The process registry is used to track all processes in the system. Each entry corresponds to a single process and has information on PID, imagename, and whether the process should be excluded or not.

A single process registry instance is shared between most parts of the driver.

Registered image

The registered image data structure is used to maintain a set of device paths.

A single instance identifies all executable images that should be excluded from the tunnel. Said instance is shared between IOCTL handlers and the process management subsystem.

Driver states

The driver uses a fixed sequence of initial state transitions:

  1. ST_DRIVER_STATE_STARTED. The driver has loaded successfully and completed the most basic initialization. To advance the state, ask the driver to initialize subsystems (IOCTL_ST_INITIALIZE).

  2. ST_DRIVER_STATE_INITIALIZED. All subsystems have been initialized. To advance the state, provide the driver with a complete process tree (IOCTL_ST_REGISTER_PROCESSES).

  3. ST_DRIVER_STATE_READY. The driver is initialized but idle. To advance the state, configure the set of images that should be excluded (IOCTL_ST_SET_CONFIGURATION) and register relevant IP addresses (IOCTL_ST_REGISTER_IP_ADDRESSES).

  4. ST_DRIVER_STATE_ENGAGED. The driver is actively excluding traffic, as applicable.

Having reached the engaged state, it's possible to return the driver to the ready state. You can do this by either clearing the configuration (IOCTL_ST_CLEAR_CONFIGURATION) or by registering all-zeros IPs on interfaces that are or should be seen as unavailable.

Limitations

DNS

Most of the time, DNS requests are not sent from the process where the request originates. The request is first transferred via IPC to the dnscache service. If the response is not cached, the dnscache service proceeds to make an actual DNS request.

From the point of view of the driver, all DNS requests are made by a particular instance of svchost. Because svchost is not excluded, and because we can't easily know which process initiated the request, default processing takes precedence and sends the traffic inside the tunnel.

This can be mitigated for individual apps if they can be configured to use DoT/DoH.

Localhost communications

Excluded apps aren't allowed to bind to inaddr_any. If a client socket isn't explicitly bound prior to calling connect(), the socket will momentarily be seen in the system as binding/bound towards inaddr_any, before the correct binding is realized. The driver sees the initial bind and redirects it to the primary network interface.

This means that, for excluded apps, if a socket isn't explicitly bound to 127.0.0.1 before connecting, it won't be able to talk to localhost.

Because explicitly bound client sockets are rare, it can be expected that most excluded apps are affected. No generally applicable mitigations are available.

License

Copyright (C) 2021 Mullvad VPN AB

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

For the full license agreement, see the LICENSE.md file

Description
No description provided
Readme 412 KiB
Languages
C++ 94.2%
C 4.2%
Batchfile 1.6%