summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2023-02-28 10:09:54 +0100
committerDavid Lönnhager <david.l@mullvad.net>2023-02-28 10:09:54 +0100
commitdc10117f1cf3ed0cc8a26f4066844778bb061263 (patch)
treea81f034861f02fb0c4eb5dc11b3065c1d652851f
parentace5f82b50c6165f0c7f0023a0f4d5aab030d0bd (diff)
parent8eb6c3dd395ed7a451908cee5a4708066ed0b029 (diff)
downloadmullvadvpn-dc10117f1cf3ed0cc8a26f4066844778bb061263.tar.xz
mullvadvpn-dc10117f1cf3ed0cc8a26f4066844778bb061263.zip
Merge branch 'pq-multihop'
-rw-r--r--CHANGELOG.md1
-rw-r--r--docs/architecture.md25
-rw-r--r--mullvad-cli/src/cmds/relay.rs20
-rw-r--r--mullvad-cli/src/cmds/tunnel.rs21
-rw-r--r--talpid-core/Cargo.toml1
-rw-r--r--talpid-core/src/firewall/linux.rs6
-rw-r--r--talpid-core/src/firewall/macos.rs53
-rw-r--r--talpid-core/src/firewall/windows.rs73
-rw-r--r--talpid-core/src/tunnel/mod.rs13
-rw-r--r--talpid-tunnel-config-client/examples/psk-exchange.rs13
-rw-r--r--talpid-tunnel-config-client/src/lib.rs10
-rw-r--r--talpid-types/src/net/mod.rs12
-rw-r--r--talpid-wireguard/src/config.rs2
-rw-r--r--talpid-wireguard/src/lib.rs258
-rw-r--r--windows/winfw/src/winfw/fwcontext.cpp37
-rw-r--r--windows/winfw/src/winfw/mullvadguids.cpp77
-rw-r--r--windows/winfw/src/winfw/mullvadguids.h12
-rw-r--r--windows/winfw/src/winfw/rules/baseline/permitvpntunnel.cpp101
-rw-r--r--windows/winfw/src/winfw/rules/baseline/permitvpntunnel.h10
-rw-r--r--windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.cpp90
-rw-r--r--windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.h5
-rw-r--r--windows/winfw/src/winfw/winfw.h10
22 files changed, 582 insertions, 268 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2791a37fa..3c47e21d55 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@ Line wrap the file at 100 chars. Th
"off", but this might change in the future.
- Update OpenVPN to 2.6.0 from 2.5.3.
- Update OpenSSL to 1.1.1t from 1.1.1j.
+- Post-Quantum secure tunnels and multihop can now be used at the same time.
#### Windows
- Remove automatic fallback to wireguard-go. This is done as a first step before fully
diff --git a/docs/architecture.md b/docs/architecture.md
index 22b3ac394d..4837758a45 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -190,6 +190,31 @@ metadata that might be useful.
### Firewall integration
+### Connection logic
+
+#### Quantum-resistant tunnels
+
+To establish a quantum-resistant tunnel, a pre-shared key (PSK) is derived using a quantum-safe
+key encapsulation mechanism (KEM) with the relay. This is achieved by initiating a regular
+WireGuard tunnel to the relay and deriving the PSK within the tunnel.
+The PSK is stored in memory on the relay and the client, along with a new client generated ephemeral
+WireGuard key. Subsequently, a new tunnel is created using the new WireGuard key and the PSK,
+ensuring that the tunnel is quantum-resistant.
+See the [protocol definition file](../talpid-tunnel-config-client/proto/tunnel_config.proto) for
+more details on the protocol.
+
+#### Quantum-resistant tunnels & Multihop
+
+To create a multihop tunnel where both hops are quantum resistant the client must negotiate a unique
+PSK with both the entry and the exit relay separately. It must use the same ephemeral WireGuard key
+on both relays since the end result (just as with regular multihop tunnels) is two peers on a
+single WireGuard interface, which can only have a single key for the local peer.
+
+The PSKs are established by first creating a regular multihop tunnel to the exit via the entry relay
+and negotiate a PSK with the exit. Then establish a regular tunnel to just the entry and negotiate a
+PSK with it. Lastly the client can set up a multihop tunnel using the new ephemeral WireGuard key
+and the two PSKs via the entry to the exit.
+
### Detecting device offline
The tunnel state machine has an offline monitor that tries to detect when a device will certainly
diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs
index 27e8df55b9..1497a7da41 100644
--- a/mullvad-cli/src/cmds/relay.rs
+++ b/mullvad-cli/src/cmds/relay.rs
@@ -592,26 +592,6 @@ impl Relay {
if let Some(entry) = matches.values_of("entry location") {
wireguard_constraints.entry_location = parse_entry_location_constraint(entry);
let use_multihop = wireguard_constraints.entry_location.is_some();
- if use_multihop {
- let quantum_resistant = rpc
- .get_settings(())
- .await?
- .into_inner()
- .tunnel_options
- .unwrap()
- .wireguard
- .unwrap()
- .quantum_resistant;
- if quantum_resistant
- == Some(types::QuantumResistantState {
- state: i32::from(types::quantum_resistant_state::State::On),
- })
- {
- return Err(Error::CommandFailed(
- "Quantum resistant tunnels do not work when multihop is enabled",
- ));
- }
- }
wireguard_constraints.use_multihop = use_multihop;
}
diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs
index 042590fcbb..1043769f66 100644
--- a/mullvad-cli/src/cmds/tunnel.rs
+++ b/mullvad-cli/src/cmds/tunnel.rs
@@ -251,27 +251,6 @@ impl Tunnel {
_ => unreachable!("invalid PQ state"),
};
let mut rpc = new_rpc_client().await?;
- let settings = rpc.get_settings(()).await?;
- if quantum_resistant == types::quantum_resistant_state::State::On {
- let multihop_is_enabled = settings
- .into_inner()
- .relay_settings
- .unwrap()
- .endpoint
- .and_then(|endpoint| {
- if let types::relay_settings::Endpoint::Normal(settings) = endpoint {
- Some(settings.wireguard_constraints.unwrap().use_multihop)
- } else {
- None
- }
- })
- .unwrap_or(false);
- if multihop_is_enabled {
- return Err(Error::CommandFailed(
- "Quantum resistant tunnels do not work when multihop is enabled",
- ));
- }
- }
rpc.set_quantum_resistant_tunnel(types::QuantumResistantState {
state: i32::from(quantum_resistant),
})
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index d8b8a6ab82..24203d4a11 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -103,6 +103,7 @@ features = [
"Win32_NetworkManagement_Ndis",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
+ "Win32_System_SystemInformation",
]
[build-dependencies]
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs
index 14900afb51..ced8390693 100644
--- a/talpid-core/src/firewall/linux.rs
+++ b/talpid-core/src/firewall/linux.rs
@@ -576,9 +576,13 @@ impl<'a> PolicyBatch<'a> {
self.add_allow_tunnel_rules(&tunnel.interface)?;
}
AllowedTunnelTraffic::None => (),
- AllowedTunnelTraffic::Only(endpoint) => {
+ AllowedTunnelTraffic::One(endpoint) => {
self.add_allow_in_tunnel_endpoint_rules(&tunnel.interface, endpoint)?;
}
+ AllowedTunnelTraffic::Two(endpoint1, endpoint2) => {
+ self.add_allow_in_tunnel_endpoint_rules(&tunnel.interface, endpoint1)?;
+ self.add_allow_in_tunnel_endpoint_rules(&tunnel.interface, endpoint2)?;
+ }
}
if *allow_lan {
self.add_block_cve_2019_14899(tunnel);
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs
index 6f59020990..6ca12d0924 100644
--- a/talpid-core/src/firewall/macos.rs
+++ b/talpid-core/src/firewall/macos.rs
@@ -130,7 +130,7 @@ impl Firewall {
if let Some(tunnel) = tunnel {
rules.extend(
- self.get_allow_tunnel_rule(&tunnel.interface, allowed_tunnel_traffic)?
+ self.get_allow_tunnel_rules(&tunnel.interface, allowed_tunnel_traffic)?
.into_iter(),
);
}
@@ -159,7 +159,7 @@ impl Firewall {
rules.append(&mut self.get_block_dns_rules()?);
rules.extend(
- self.get_allow_tunnel_rule(
+ self.get_allow_tunnel_rules(
tunnel.interface.as_str(),
&AllowedTunnelTraffic::All,
)?
@@ -330,28 +330,55 @@ impl Firewall {
Ok(vec![block_tcp_dns_rule, block_udp_dns_rule])
}
- fn get_allow_tunnel_rule(
+ fn base_rule(
&self,
+ action: FilterRuleAction,
tunnel_interface: &str,
- allowed_traffic: &AllowedTunnelTraffic,
- ) -> Result<Option<pfctl::FilterRule>> {
- let mut rule_builder = self.create_rule_builder(FilterRuleAction::Pass);
- let mut base_rule = rule_builder
+ ) -> pfctl::FilterRuleBuilder {
+ let mut rule_builder = self.create_rule_builder(action);
+ rule_builder
.quick(true)
.interface(tunnel_interface)
.keep_state(pfctl::StatePolicy::Keep)
.tcp_flags(Self::get_tcp_flags());
- match allowed_traffic {
- AllowedTunnelTraffic::Only(endpoint) => {
+ rule_builder
+ }
+
+ fn get_allow_tunnel_rules(
+ &self,
+ tunnel_interface: &str,
+ allowed_traffic: &AllowedTunnelTraffic,
+ ) -> Result<Vec<pfctl::FilterRule>> {
+ Ok(match allowed_traffic {
+ AllowedTunnelTraffic::One(endpoint) => {
+ let mut base_rule = &mut self.base_rule(FilterRuleAction::Pass, tunnel_interface);
let pfctl_proto = as_pfctl_proto(endpoint.protocol);
base_rule = base_rule.to(endpoint.address).proto(pfctl_proto);
+ vec![base_rule.build()?]
+ }
+ AllowedTunnelTraffic::Two(endpoint1, endpoint2) => {
+ let mut rules = Vec::with_capacity(2);
+
+ let mut base_rule = &mut self.base_rule(FilterRuleAction::Pass, tunnel_interface);
+ let pfctl_proto = as_pfctl_proto(endpoint1.protocol);
+ base_rule = base_rule.to(endpoint1.address).proto(pfctl_proto);
+ rules.push(base_rule.build()?);
+
+ let mut base_rule = &mut self.base_rule(FilterRuleAction::Pass, tunnel_interface);
+ let pfctl_proto = as_pfctl_proto(endpoint2.protocol);
+ base_rule = base_rule.to(endpoint2.address).proto(pfctl_proto);
+ rules.push(base_rule.build()?);
+
+ rules
+ }
+ AllowedTunnelTraffic::All => {
+ let base_rule = &mut self.base_rule(FilterRuleAction::Pass, tunnel_interface);
+ vec![base_rule.build()?]
}
- AllowedTunnelTraffic::All => {}
AllowedTunnelTraffic::None => {
- return Ok(None);
+ vec![]
}
- };
- Ok(Some(base_rule.build()?))
+ })
}
fn get_allow_loopback_rules(&self) -> Result<Vec<pfctl::FilterRule>> {
diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs
index e2854e0197..fa40fdcb42 100644
--- a/talpid-core/src/firewall/windows.rs
+++ b/talpid-core/src/firewall/windows.rs
@@ -168,27 +168,53 @@ impl Firewall {
ptr::null()
};
- let allowed_tun_ip;
- let allowed_tunnel_endpoint =
- if let AllowedTunnelTraffic::Only(endpoint) = allowed_tunnel_traffic {
- allowed_tun_ip = widestring_ip(endpoint.address.ip());
- Some(WinFwEndpoint {
- ip: allowed_tun_ip.as_ptr(),
- port: endpoint.address.port(),
- protocol: WinFwProt::from(endpoint.protocol),
- })
- } else {
- None
- };
+ // SAFETY: `endpoint1_ip`, `endpoint2_ip`, `endpoint1` and `endpoint2` must not be dropped until
+ // `WinFw_ApplyPolicyConnecting` has returned.
+ let mut endpoint1_ip = WideCString::new();
+ let mut endpoint2_ip = WideCString::new();
+ let (endpoint1, endpoint2) = match allowed_tunnel_traffic {
+ AllowedTunnelTraffic::One(endpoint) => {
+ endpoint1_ip = widestring_ip(endpoint.address.ip());
+ (
+ Some(WinFwEndpoint {
+ ip: endpoint1_ip.as_ptr(),
+ port: endpoint.address.port(),
+ protocol: WinFwProt::from(endpoint.protocol),
+ }),
+ None,
+ )
+ }
+ AllowedTunnelTraffic::Two(endpoint1, endpoint2) => {
+ endpoint1_ip = widestring_ip(endpoint1.address.ip());
+ let endpoint1 = Some(WinFwEndpoint {
+ ip: endpoint1_ip.as_ptr(),
+ port: endpoint1.address.port(),
+ protocol: WinFwProt::from(endpoint1.protocol),
+ });
+ endpoint2_ip = widestring_ip(endpoint2.address.ip());
+ let endpoint2 = Some(WinFwEndpoint {
+ ip: endpoint2_ip.as_ptr(),
+ port: endpoint2.address.port(),
+ protocol: WinFwProt::from(endpoint2.protocol),
+ });
+ (endpoint1, endpoint2)
+ }
+ AllowedTunnelTraffic::None | AllowedTunnelTraffic::All => (None, None),
+ };
+
let allowed_tunnel_traffic = WinFwAllowedTunnelTraffic {
type_: WinFwAllowedTunnelTrafficType::from(allowed_tunnel_traffic),
- endpoint: allowed_tunnel_endpoint
+ endpoint1: endpoint1
+ .as_ref()
+ .map(|ep| ep as *const _)
+ .unwrap_or(ptr::null()),
+ endpoint2: endpoint2
.as_ref()
.map(|ep| ep as *const _)
.unwrap_or(ptr::null()),
};
- unsafe {
+ let res = unsafe {
WinFw_ApplyPolicyConnecting(
winfw_settings,
&winfw_relay,
@@ -199,7 +225,15 @@ impl Firewall {
)
.into_result()
.map_err(Error::ApplyingConnectingPolicy)
- }
+ };
+ // SAFETY: All of these hold stack allocated memory which is pointed to by
+ // `allowed_tunnel_traffic` and must remain allocated until `WinFw_ApplyPolicyConnecting`
+ // has returned.
+ drop(endpoint1_ip);
+ drop(endpoint2_ip);
+ drop(endpoint1);
+ drop(endpoint2);
+ res
}
fn set_connected_state(
@@ -439,7 +473,8 @@ mod winfw {
#[repr(C)]
pub struct WinFwAllowedTunnelTraffic {
pub type_: WinFwAllowedTunnelTrafficType,
- pub endpoint: *const WinFwEndpoint,
+ pub endpoint1: *const WinFwEndpoint,
+ pub endpoint2: *const WinFwEndpoint,
}
#[repr(u8)]
@@ -447,7 +482,8 @@ mod winfw {
pub enum WinFwAllowedTunnelTrafficType {
None,
All,
- Only,
+ One,
+ Two,
}
impl From<&AllowedTunnelTraffic> for WinFwAllowedTunnelTrafficType {
@@ -455,7 +491,8 @@ mod winfw {
match traffic {
AllowedTunnelTraffic::None => WinFwAllowedTunnelTrafficType::None,
AllowedTunnelTraffic::All => WinFwAllowedTunnelTrafficType::All,
- AllowedTunnelTraffic::Only(..) => WinFwAllowedTunnelTrafficType::Only,
+ AllowedTunnelTraffic::One(..) => WinFwAllowedTunnelTrafficType::One,
+ AllowedTunnelTraffic::Two(..) => WinFwAllowedTunnelTrafficType::Two,
}
}
}
diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs
index 29f3f9c746..15422f0b94 100644
--- a/talpid-core/src/tunnel/mod.rs
+++ b/talpid-core/src/tunnel/mod.rs
@@ -143,18 +143,7 @@ impl TunnelMonitor {
let config = talpid_wireguard::config::Config::from_parameters(params)?;
let monitor = talpid_wireguard::WireguardMonitor::start(
config,
- if params.options.quantum_resistant {
- Some(
- params
- .connection
- .exit_peer
- .as_ref()
- .map(|peer| peer.public_key.clone())
- .unwrap_or_else(|| params.connection.peer.public_key.clone()),
- )
- } else {
- None
- },
+ params.options.quantum_resistant,
log.as_deref(),
args,
)?;
diff --git a/talpid-tunnel-config-client/examples/psk-exchange.rs b/talpid-tunnel-config-client/examples/psk-exchange.rs
index 7256ca4426..87200d0336 100644
--- a/talpid-tunnel-config-client/examples/psk-exchange.rs
+++ b/talpid-tunnel-config-client/examples/psk-exchange.rs
@@ -2,7 +2,7 @@
//! Useful to test this crate's implementation.
use std::net::IpAddr;
-use talpid_types::net::wireguard::PublicKey;
+use talpid_types::net::wireguard::{PrivateKey, PublicKey};
#[tokio::main]
async fn main() {
@@ -16,10 +16,15 @@ async fn main() {
.next()
.expect("Give WireGuard public key as second argument");
let pubkey = PublicKey::from_base64(pubkey_string.trim()).expect("Invalid public key");
+ let private_key = PrivateKey::new_from_random();
- let (private_key, psk) = talpid_tunnel_config_client::push_pq_key(tuncfg_server_ip, pubkey)
- .await
- .unwrap();
+ let psk = talpid_tunnel_config_client::push_pq_key(
+ tuncfg_server_ip,
+ pubkey,
+ private_key.public_key(),
+ )
+ .await
+ .unwrap();
println!("private key: {private_key:?}");
println!("psk: {psk:?}");
diff --git a/talpid-tunnel-config-client/src/lib.rs b/talpid-tunnel-config-client/src/lib.rs
index a4beee9be3..b795f6b814 100644
--- a/talpid-tunnel-config-client/src/lib.rs
+++ b/talpid-tunnel-config-client/src/lib.rs
@@ -1,5 +1,5 @@
use std::{fmt, net::IpAddr};
-use talpid_types::net::wireguard::{PresharedKey, PrivateKey, PublicKey};
+use talpid_types::net::wireguard::{PresharedKey, PublicKey};
use tonic::transport::Channel;
use zeroize::Zeroize;
@@ -70,8 +70,8 @@ pub const CONFIG_SERVICE_PORT: u16 = 1337;
pub async fn push_pq_key(
service_address: IpAddr,
wg_pubkey: PublicKey,
-) -> Result<(PrivateKey, PresharedKey), Error> {
- let wg_psk_privkey = PrivateKey::new_from_random();
+ wg_psk_pubkey: PublicKey,
+) -> Result<PresharedKey, Error> {
let (cme_kem_pubkey, cme_kem_secret) = classic_mceliece::generate_keys().await;
let kyber_keypair = kyber::keypair(&mut rand::thread_rng());
@@ -79,7 +79,7 @@ pub async fn push_pq_key(
let response = client
.psk_exchange_v1(proto::PskRequestV1 {
wg_pubkey: wg_pubkey.as_bytes().to_vec(),
- wg_psk_pubkey: wg_psk_privkey.public_key().as_bytes().to_vec(),
+ wg_psk_pubkey: wg_psk_pubkey.as_bytes().to_vec(),
kem_pubkeys: vec![
proto::KemPubkeyV1 {
algorithm_name: classic_mceliece::ALGORITHM_NAME.to_owned(),
@@ -127,7 +127,7 @@ pub async fn push_pq_key(
shared_secret.zeroize();
}
- Ok((wg_psk_privkey, PresharedKey::from(psk_data)))
+ Ok(PresharedKey::from(psk_data))
}
/// Performs `dst = dst ^ src`.
diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs
index b957e59a34..126b721105 100644
--- a/talpid-types/src/net/mod.rs
+++ b/talpid-types/src/net/mod.rs
@@ -284,15 +284,21 @@ impl fmt::Display for AllowedEndpoint {
pub enum AllowedTunnelTraffic {
None,
All,
- Only(Endpoint),
+ One(Endpoint),
+ Two(Endpoint, Endpoint),
}
impl fmt::Display for AllowedTunnelTraffic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- match *self {
+ match self {
AllowedTunnelTraffic::None => "None".fmt(f),
AllowedTunnelTraffic::All => "All".fmt(f),
- AllowedTunnelTraffic::Only(endpoint) => endpoint.fmt(f),
+ AllowedTunnelTraffic::One(endpoint) => endpoint.fmt(f),
+ AllowedTunnelTraffic::Two(endpoint1, endpoint2) => {
+ endpoint1.fmt(f)?;
+ f.write_str(", ")?;
+ endpoint2.fmt(f)
+ }
}
}
}
diff --git a/talpid-wireguard/src/config.rs b/talpid-wireguard/src/config.rs
index 2198b5a3fe..fc1333b631 100644
--- a/talpid-wireguard/src/config.rs
+++ b/talpid-wireguard/src/config.rs
@@ -6,7 +6,7 @@ use std::{
use talpid_types::net::{obfuscation::ObfuscatorConfig, wireguard, GenericTunnelOptions};
/// Config required to set up a single WireGuard tunnel
-#[derive(Clone)]
+#[derive(Debug, Clone)]
pub struct Config {
/// Contains tunnel endpoint specific config
pub tunnel: wireguard::TunnelConfig,
diff --git a/talpid-wireguard/src/lib.rs b/talpid-wireguard/src/lib.rs
index 9eaa477430..e81d7bf4b6 100644
--- a/talpid-wireguard/src/lib.rs
+++ b/talpid-wireguard/src/lib.rs
@@ -9,12 +9,13 @@ use futures::future::{abortable, AbortHandle as FutureAbortHandle, BoxFuture, Fu
use futures::{channel::mpsc, StreamExt};
#[cfg(target_os = "linux")]
use lazy_static::lazy_static;
+#[cfg(target_os = "android")]
+use std::borrow::Cow;
#[cfg(target_os = "linux")]
use std::env;
#[cfg(windows)]
use std::io;
use std::{
- borrow::Cow,
convert::Infallible,
net::IpAddr,
path::Path,
@@ -28,12 +29,14 @@ use talpid_routing::{self, RequiredRoute};
use talpid_tunnel::tun_provider;
use talpid_tunnel::{tun_provider::TunProvider, TunnelArgs, TunnelEvent, TunnelMetadata};
+use ipnetwork::IpNetwork;
#[cfg(windows)]
use talpid_types::BoxedError;
use talpid_types::{
net::{
- obfuscation::ObfuscatorConfig, wireguard::PublicKey, AllowedTunnelTraffic, Endpoint,
- TransportProtocol,
+ obfuscation::ObfuscatorConfig,
+ wireguard::{PresharedKey, PrivateKey, PublicKey},
+ AllowedTunnelTraffic, Endpoint, TransportProtocol,
},
ErrorExt,
};
@@ -91,6 +94,10 @@ pub enum Error {
#[error(display = "Failed to negotiate PQ PSK")]
PskNegotiationError(#[error(source)] talpid_tunnel_config_client::Error),
+ /// Too many peers in the config
+ #[error(display = "There are too many peers in the tunnel config")]
+ TooManyPeers,
+
/// Failed to set up IP interfaces.
#[cfg(windows)]
#[error(display = "Failed to set up IP interfaces")]
@@ -230,27 +237,29 @@ impl WireguardMonitor {
+ 'static,
>(
mut config: Config,
- psk_negotiation: Option<PublicKey>,
+ psk_negotiation: bool,
log_path: Option<&Path>,
args: TunnelArgs<'_, F>,
) -> Result<WireguardMonitor> {
- let on_event = args.on_event;
+ let on_event = args.on_event.clone();
let endpoint_addrs: Vec<IpAddr> =
config.peers.iter().map(|peer| peer.endpoint.ip()).collect();
- let (close_msg_sender, close_msg_receiver) = sync_mpsc::channel();
+ let (close_obfs_sender, close_obfs_listener) = sync_mpsc::channel();
let obfuscator = args.runtime.block_on(maybe_create_obfuscator(
&mut config,
- close_msg_sender.clone(),
+ close_obfs_sender.clone(),
))?;
#[cfg(target_os = "windows")]
let (setup_done_tx, setup_done_rx) = mpsc::channel(0);
-
let tunnel = Self::open_tunnel(
args.runtime.clone(),
- &Self::patch_allowed_ips(&config, psk_negotiation.is_some()),
+ #[cfg(target_os = "android")]
+ &Self::patch_allowed_ips(&config, psk_negotiation),
+ #[cfg(not(target_os = "android"))]
+ &config,
log_path,
args.resource_dir,
args.tun_provider.clone(),
@@ -270,15 +279,17 @@ impl WireguardMonitor {
}
}
+ let obfuscator = Arc::new(AsyncMutex::new(obfuscator));
+
let event_callback = Box::new(on_event.clone());
let (pinger_tx, pinger_rx) = sync_mpsc::channel();
let monitor = WireguardMonitor {
runtime: args.runtime.clone(),
tunnel: Arc::new(Mutex::new(Some(tunnel))),
event_callback,
- close_msg_receiver,
+ close_msg_receiver: close_obfs_listener,
pinger_stop_sender: pinger_tx,
- obfuscator: Arc::new(AsyncMutex::new(obfuscator)),
+ obfuscator,
};
let gateway = config.ipv4_gateway;
@@ -291,18 +302,20 @@ impl WireguardMonitor {
)
.map_err(Error::ConnectivityMonitorError)?;
- let metadata = Self::tunnel_metadata(&iface_name, &config);
- let tunnel = monitor.tunnel.clone();
- let obfs_handle = monitor.obfuscator.clone();
- let obfs_close_sender = close_msg_sender.clone();
-
+ let moved_tunnel = monitor.tunnel.clone();
+ let moved_close_obfs_sender = close_obfs_sender.clone();
+ let moved_obfuscator = monitor.obfuscator.clone();
let tunnel_fut = async move {
+ let mut tunnel = moved_tunnel;
+ let close_obfs_sender: sync_mpsc::Sender<CloseMsg> = moved_close_obfs_sender;
+ let obfuscator = moved_obfuscator;
#[cfg(windows)]
Self::add_device_ip_addresses(&iface_name, &config.tunnel.addresses, setup_done_rx)
.await?;
- let allowed_traffic = if psk_negotiation.is_some() {
- AllowedTunnelTraffic::Only(Endpoint::new(
+ let metadata = Self::tunnel_metadata(&iface_name, &config);
+ let allowed_traffic = if psk_negotiation {
+ AllowedTunnelTraffic::One(Endpoint::new(
config.ipv4_gateway,
talpid_tunnel_config_client::CONFIG_SERVICE_PORT,
TransportProtocol::Tcp,
@@ -329,21 +342,18 @@ impl WireguardMonitor {
.map_err(Error::SetupRoutingError)
.map_err(CloseMsg::SetupError)?;
- if let Some(pubkey) = psk_negotiation {
- Self::perform_psk_negotiation(
- tunnel,
- obfs_handle,
- obfs_close_sender,
- args.retry_attempt,
- pubkey,
+ let psk_obfs_sender = close_obfs_sender.clone();
+ if psk_negotiation {
+ Self::psk_negotiation(
+ &mut tunnel,
&mut config,
+ args.retry_attempt,
+ args.on_event.clone(),
+ &iface_name,
+ obfuscator.clone(),
+ psk_obfs_sender,
)
.await?;
- (on_event)(TunnelEvent::InterfaceUp(
- metadata.clone(),
- AllowedTunnelTraffic::All,
- ))
- .await;
}
let mut connectivity_monitor = tokio::task::spawn_blocking(move || {
@@ -372,6 +382,7 @@ impl WireguardMonitor {
.map_err(Error::SetupRoutingError)
.map_err(CloseMsg::SetupError)?;
+ let metadata = Self::tunnel_metadata(&iface_name, &config);
(on_event)(TunnelEvent::Up(metadata)).await;
tokio::task::spawn_blocking(move || {
@@ -388,7 +399,7 @@ impl WireguardMonitor {
Err::<Infallible, CloseMsg>(CloseMsg::PingErr)
};
- let close_sender = close_msg_sender.clone();
+ let close_sender = close_obfs_sender.clone();
let monitor_handle = tokio::spawn(async move {
// This is safe to unwrap because the future resolves to `Result<Infallible, E>`.
let close_msg = tunnel_fut.await.unwrap_err();
@@ -398,15 +409,147 @@ impl WireguardMonitor {
tokio::spawn(async move {
if args.tunnel_close_rx.await.is_ok() {
monitor_handle.abort();
- let _ = close_msg_sender.send(CloseMsg::Stop);
+ let _ = close_obfs_sender.send(CloseMsg::Stop);
}
});
Ok(monitor)
}
+ async fn psk_negotiation<F>(
+ tunnel: &mut Arc<Mutex<Option<Box<dyn Tunnel>>>>,
+ config: &mut Config,
+ retry_attempt: u32,
+ on_event: F,
+ iface_name: &str,
+ obfuscator: Arc<AsyncMutex<Option<ObfuscatorHandle>>>,
+ close_obfs_sender: sync_mpsc::Sender<CloseMsg>,
+ ) -> std::result::Result<(), CloseMsg>
+ where
+ F: (Fn(TunnelEvent) -> Pin<Box<dyn std::future::Future<Output = ()> + Send>>)
+ + Send
+ + Sync
+ + Clone
+ + 'static,
+ {
+ let wg_psk_privkey = PrivateKey::new_from_random();
+ let close_obfs_sender = close_obfs_sender.clone();
+
+ let allowed_traffic = Endpoint::new(
+ config.ipv4_gateway,
+ talpid_tunnel_config_client::CONFIG_SERVICE_PORT,
+ TransportProtocol::Tcp,
+ );
+ let allowed_traffic = if config.peers.len() > 1 {
+ // NOTE: We need to let traffic meant for the exit IP through the firewall. This
+ // should not allow any non-PQ traffic to leak since you can only reach the
+ // exit peer with these rules and not the broader internet.
+ AllowedTunnelTraffic::Two(
+ allowed_traffic,
+ Endpoint::from_socket_address(config.peers[1].endpoint, TransportProtocol::Udp),
+ )
+ } else {
+ AllowedTunnelTraffic::One(allowed_traffic)
+ };
+ let metadata = Self::tunnel_metadata(iface_name, config);
+ (on_event)(TunnelEvent::InterfaceUp(metadata, allowed_traffic.clone())).await;
+
+ let exit_psk =
+ Self::perform_psk_negotiation(retry_attempt, config, wg_psk_privkey.public_key())
+ .await?;
+
+ log::debug!("Successfully exchanged PSK with exit peer");
+
+ let mut entry_psk = None;
+
+ if config.peers.len() > 1 {
+ if config.peers.len() != 2 {
+ return Err(CloseMsg::TooManyPeers);
+ }
+ // Set up tunnel to lead to entry
+ let mut entry_tun_config = config.clone();
+ entry_tun_config
+ .peers
+ .get_mut(0)
+ .expect("entry peer not found")
+ .allowed_ips
+ .push(IpNetwork::new(IpAddr::V4(config.ipv4_gateway), 32).unwrap());
+
+ let close_obfs_sender = close_obfs_sender.clone();
+ let entry_config = Self::reconfigure_tunnel(
+ tunnel,
+ entry_tun_config,
+ obfuscator.clone(),
+ close_obfs_sender,
+ )
+ .await?;
+ entry_psk = Some(
+ Self::perform_psk_negotiation(
+ retry_attempt,
+ &entry_config,
+ wg_psk_privkey.public_key(),
+ )
+ .await?,
+ );
+ log::debug!("Successfully exchanged PSK with entry peer");
+ }
+
+ // Set new priv key and psks
+ config.tunnel.private_key = wg_psk_privkey;
+ if let Some(entry_psk) = entry_psk {
+ // The first peer is the entry peer and there is guaranteed to be a second peer
+ // which is the exit
+ config.peers.get_mut(0).expect("entry peer not found").psk = Some(entry_psk);
+ config.peers.get_mut(1).expect("exit peer not found").psk = Some(exit_psk);
+ } else {
+ config.peers.get_mut(0).expect("peer not found").psk = Some(exit_psk);
+ }
+
+ *config =
+ Self::reconfigure_tunnel(tunnel, config.clone(), obfuscator, close_obfs_sender).await?;
+ let metadata = Self::tunnel_metadata(iface_name, config);
+ (on_event)(TunnelEvent::InterfaceUp(
+ metadata,
+ AllowedTunnelTraffic::All,
+ ))
+ .await;
+
+ Ok(())
+ }
+
+ /// Reconfigures the tunnel to use the provided config while potentially modifying the config
+ /// and restarting the obfuscation provider. Returns the new config used by the new tunnel.
+ async fn reconfigure_tunnel(
+ tunnel: &Arc<Mutex<Option<Box<dyn Tunnel>>>>,
+ mut config: Config,
+ obfuscator: Arc<AsyncMutex<Option<ObfuscatorHandle>>>,
+ close_obfs_sender: sync_mpsc::Sender<CloseMsg>,
+ ) -> std::result::Result<Config, CloseMsg> {
+ let mut obfs_guard = obfuscator.lock().await;
+ if let Some(obfuscator_handle) = obfs_guard.take() {
+ obfuscator_handle.abort();
+ *obfs_guard = maybe_create_obfuscator(&mut config, close_obfs_sender)
+ .await
+ .map_err(CloseMsg::ObfuscatorFailed)?;
+ }
+
+ let set_config_future = tunnel
+ .lock()
+ .unwrap()
+ .as_ref()
+ .map(|tunnel| tunnel.set_config(config.clone()));
+ if let Some(f) = set_config_future {
+ f.await
+ .map_err(Error::TunnelError)
+ .map_err(CloseMsg::SetupError)?;
+ }
+
+ Ok(config)
+ }
+
/// Replace `0.0.0.0/0`/`::/0` with the gateway IPs when `gateway_only` is true.
/// Used to block traffic to other destinations while connecting on Android.
+ #[cfg(target_os = "android")]
fn patch_allowed_ips(config: &Config, gateway_only: bool) -> Cow<'_, Config> {
if gateway_only {
let mut patched_config = config.clone();
@@ -473,13 +616,10 @@ impl WireguardMonitor {
}
async fn perform_psk_negotiation(
- tunnel: Arc<Mutex<Option<Box<dyn Tunnel>>>>,
- obfuscation_handle: Arc<AsyncMutex<Option<ObfuscatorHandle>>>,
- obfs_close_sender: sync_mpsc::Sender<CloseMsg>,
retry_attempt: u32,
- current_pubkey: PublicKey,
- config: &mut Config,
- ) -> std::result::Result<(), CloseMsg> {
+ config: &Config,
+ wg_psk_pubkey: PublicKey,
+ ) -> std::result::Result<PresharedKey, CloseMsg> {
log::debug!("Performing PQ-safe PSK exchange");
let timeout = std::cmp::min(
@@ -488,11 +628,12 @@ impl WireguardMonitor {
.saturating_mul(PSK_EXCHANGE_TIMEOUT_MULTIPLIER.saturating_pow(retry_attempt)),
);
- let (private_key, psk) = tokio::time::timeout(
+ let psk = tokio::time::timeout(
timeout,
talpid_tunnel_config_client::push_pq_key(
IpAddr::V4(config.ipv4_gateway),
config.tunnel.private_key.public_key(),
+ wg_psk_pubkey,
),
)
.await
@@ -503,41 +644,7 @@ impl WireguardMonitor {
.map_err(Error::PskNegotiationError)
.map_err(CloseMsg::SetupError)?;
- config.tunnel.private_key = private_key;
-
- for peer in &mut config.peers {
- if current_pubkey == peer.public_key {
- peer.psk = Some(psk);
- break;
- }
- }
-
- log::trace!(
- "Ephemeral pubkey: {}",
- config.tunnel.private_key.public_key()
- );
-
- // Restart the obfuscation server
- let mut obfs_guard = obfuscation_handle.lock().await;
- if let Some(obfs_abort_handle) = obfs_guard.take() {
- obfs_abort_handle.abort();
- *obfs_guard = maybe_create_obfuscator(config, obfs_close_sender)
- .await
- .map_err(CloseMsg::SetupError)?;
- }
-
- let set_config_future = tunnel
- .lock()
- .unwrap()
- .as_ref()
- .map(|tunnel| tunnel.set_config(config.clone()));
- if let Some(f) = set_config_future {
- f.await
- .map_err(Error::TunnelError)
- .map_err(CloseMsg::SetupError)?;
- }
-
- Ok(())
+ Ok(psk)
}
#[allow(unused_variables)]
@@ -626,6 +733,7 @@ impl WireguardMonitor {
Ok(CloseMsg::Stop) | Ok(CloseMsg::ObfuscatorExpired) => Ok(()),
Ok(CloseMsg::SetupError(error)) => Err(error),
Ok(CloseMsg::ObfuscatorFailed(error)) => Err(error),
+ Ok(CloseMsg::TooManyPeers) => Err(Error::TooManyPeers),
Err(_) => Ok(()),
};
@@ -779,6 +887,7 @@ impl WireguardMonitor {
}
}
+#[derive(Debug)]
enum CloseMsg {
Stop,
PskNegotiationTimeout,
@@ -786,6 +895,7 @@ enum CloseMsg {
SetupError(Error),
ObfuscatorExpired,
ObfuscatorFailed(Error),
+ TooManyPeers,
}
pub(crate) trait Tunnel: Send {
diff --git a/windows/winfw/src/winfw/fwcontext.cpp b/windows/winfw/src/winfw/fwcontext.cpp
index 3a8b5d2fe5..6f6a73baf4 100644
--- a/windows/winfw/src/winfw/fwcontext.cpp
+++ b/windows/winfw/src/winfw/fwcontext.cpp
@@ -218,12 +218,15 @@ bool FwContext::applyPolicyConnecting
));
break;
}
- case WinFwAllowedTunnelTrafficType::Only:
+ case WinFwAllowedTunnelTrafficType::One:
{
- const auto onlyEndpoint = std::make_optional(baseline::PermitVpnTunnel::Endpoint{
- wfp::IpAddress(allowedTunnelTraffic.endpoint->ip),
- allowedTunnelTraffic.endpoint->port,
- allowedTunnelTraffic.endpoint->protocol
+ auto onlyEndpoint = std::make_optional<baseline::PermitVpnTunnel::Endpoints>({
+ baseline::PermitVpnTunnel::Endpoint{
+ wfp::IpAddress(allowedTunnelTraffic.endpoint1->ip),
+ allowedTunnelTraffic.endpoint1->port,
+ allowedTunnelTraffic.endpoint1->protocol
+ },
+ std::nullopt,
});
ruleset.emplace_back(std::make_unique<baseline::PermitVpnTunnel>(
*tunnelInterfaceAlias,
@@ -235,6 +238,30 @@ bool FwContext::applyPolicyConnecting
));
break;
}
+ case WinFwAllowedTunnelTrafficType::Two:
+ {
+ auto endpoints = std::make_optional<baseline::PermitVpnTunnel::Endpoints>({
+ baseline::PermitVpnTunnel::Endpoint{
+ wfp::IpAddress(allowedTunnelTraffic.endpoint1->ip),
+ allowedTunnelTraffic.endpoint1->port,
+ allowedTunnelTraffic.endpoint1->protocol
+ },
+ std::make_optional<baseline::PermitVpnTunnel::Endpoint>({
+ wfp::IpAddress(allowedTunnelTraffic.endpoint2->ip),
+ allowedTunnelTraffic.endpoint2->port,
+ allowedTunnelTraffic.endpoint2->protocol
+ })
+ });
+ ruleset.emplace_back(std::make_unique<baseline::PermitVpnTunnel>(
+ *tunnelInterfaceAlias,
+ endpoints
+ ));
+ ruleset.emplace_back(std::make_unique<baseline::PermitVpnTunnelService>(
+ *tunnelInterfaceAlias,
+ endpoints
+ ));
+ break;
+ }
// For the "None" case, do nothing.
}
}
diff --git a/windows/winfw/src/winfw/mullvadguids.cpp b/windows/winfw/src/winfw/mullvadguids.cpp
index aeab958554..890c8b0c2e 100644
--- a/windows/winfw/src/winfw/mullvadguids.cpp
+++ b/windows/winfw/src/winfw/mullvadguids.cpp
@@ -130,10 +130,14 @@ MullvadGuids::DetailedIdentityRegistry MullvadGuids::DetailedRegistry(IdentityQu
registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitDhcpServer_Outbound_Response_Ipv4()));
registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnRelay()));
registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitEndpoint()));
- registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4()));
- registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6()));
- registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv4()));
- registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_1()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_1()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_2()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_2()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv4_1()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv6_1()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv4_2()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv6_2()));
registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitNdp_Outbound_Router_Solicitation()));
registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitNdp_Inbound_Router_Advertisement()));
registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitNdp_Outbound_Neighbor_Solicitation()));
@@ -663,7 +667,7 @@ const GUID &MullvadGuids::Filter_Baseline_PermitEndpoint()
}
//static
-const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4()
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_1()
{
static const GUID g =
{
@@ -677,7 +681,7 @@ const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4()
}
//static
-const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6()
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_1()
{
static const GUID g =
{
@@ -691,7 +695,35 @@ const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6()
}
//static
-const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4()
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_2()
+{
+ static const GUID g =
+ {
+ 0x7e09435c,
+ 0xefd7,
+ 0x482d,
+ { 0xa1, 0xec, 0x6c, 0xc3, 0x80, 0xac, 0xf3, 0xf1 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_2()
+{
+ static const GUID g =
+ {
+ 0x276bc66f,
+ 0xf9ef,
+ 0x4428,
+ { 0xb1, 0x5e, 0xd9, 0xe2, 0x6e, 0xf4, 0xf0, 0x06 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_1()
{
static const GUID g =
{
@@ -705,7 +737,7 @@ const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4()
}
//static
-const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6()
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_1()
{
static const GUID g =
{
@@ -719,6 +751,35 @@ const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6()
}
//static
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_2()
+{
+ static const GUID g =
+ {
+ 0x98c99ac3,
+ 0xaa54,
+ 0x45e7,
+ { 0x91, 0xc4, 0x61, 0x1a, 0x1e, 0xe2, 0x64, 0x83 }
+ };
+
+ return g;
+}
+
+//static
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_2()
+{
+
+ static const GUID g =
+ {
+ 0x01deb2b8,
+ 0xb25d,
+ 0x4e60,
+ { 0x81, 0x52, 0xef, 0x3b, 0x40, 0xc0, 0x8e, 0xdc }
+ };
+
+ return g;
+}
+
+//static
const GUID &MullvadGuids::Filter_Baseline_PermitNdp_Outbound_Router_Solicitation()
{
static const GUID g =
diff --git a/windows/winfw/src/winfw/mullvadguids.h b/windows/winfw/src/winfw/mullvadguids.h
index abd06dc102..d63af0206d 100644
--- a/windows/winfw/src/winfw/mullvadguids.h
+++ b/windows/winfw/src/winfw/mullvadguids.h
@@ -71,11 +71,15 @@ public:
static const GUID &Filter_Baseline_PermitEndpoint();
- static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4();
- static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6();
+ static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_1();
+ static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_1();
+ static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_2();
+ static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_2();
- static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv4();
- static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv6();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv4_1();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv6_1();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv4_2();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv6_2();
static const GUID &Filter_Baseline_PermitNdp_Outbound_Router_Solicitation();
static const GUID &Filter_Baseline_PermitNdp_Inbound_Router_Advertisement();
diff --git a/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.cpp b/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.cpp
index 9c45d63c92..3aaff601a3 100644
--- a/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.cpp
+++ b/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.cpp
@@ -17,78 +17,101 @@ namespace rules::baseline
PermitVpnTunnel::PermitVpnTunnel(
const std::wstring &tunnelInterfaceAlias,
- const std::optional<Endpoint> &onlyEndpoint
+ const std::optional<Endpoints> &potentialEndpoints
)
: m_tunnelInterfaceAlias(tunnelInterfaceAlias)
- , m_tunnelOnlyEndpoint(onlyEndpoint)
+ , m_potentialEndpoints(potentialEndpoints)
{
}
-bool PermitVpnTunnel::apply(IObjectInstaller &objectInstaller)
+bool PermitVpnTunnel::AddEndpointFilter(const std::optional<Endpoint> &endpoint, const GUID &ipv4Guid, const GUID &ipv6Guid, IObjectInstaller &objectInstaller)
{
wfp::FilterBuilder filterBuilder;
- bool includeV4 = !m_tunnelOnlyEndpoint.has_value() || m_tunnelOnlyEndpoint->ip.type() == wfp::IpAddress::Ipv4;
- bool includeV6 = !m_tunnelOnlyEndpoint.has_value() || m_tunnelOnlyEndpoint->ip.type() == wfp::IpAddress::Ipv6;
-
- //
- // #1 Permit outbound connections, IPv4.
- //
-
filterBuilder
- .key(MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4())
- .name(L"Permit outbound connections on tunnel interface (IPv4)")
.description(L"This filter is part of a rule that permits communications inside the VPN tunnel")
.provider(MullvadGuids::Provider())
- .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4)
.sublayer(MullvadGuids::SublayerBaseline())
.weight(wfp::FilterBuilder::WeightClass::Medium)
.permit();
+ bool shouldAddV4Filter = !endpoint.has_value() || endpoint.value().ip.type() == wfp::IpAddress::Ipv4;
+ bool shouldAddV6Filter = !endpoint.has_value() || endpoint.value().ip.type() == wfp::IpAddress::Ipv6;
- if (includeV4)
+ if (shouldAddV4Filter)
{
+ filterBuilder
+ .key(ipv4Guid)
+ .name(L"Permit outbound connections on tunnel interface (IPv4)")
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
+
wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
-
+
conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
-
- if (m_tunnelOnlyEndpoint.has_value())
+ if (endpoint.has_value())
{
- conditionBuilder.add_condition(ConditionIp::Remote(m_tunnelOnlyEndpoint->ip));
- conditionBuilder.add_condition(ConditionPort::Remote(m_tunnelOnlyEndpoint->port));
- conditionBuilder.add_condition(CreateProtocolCondition(m_tunnelOnlyEndpoint->protocol));
+ conditionBuilder.add_condition(ConditionIp::Remote(endpoint.value().ip));
+ conditionBuilder.add_condition(ConditionPort::Remote(endpoint.value().port));
+ conditionBuilder.add_condition(CreateProtocolCondition(endpoint.value().protocol));
}
-
+
if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
{
return false;
}
}
-
- //
- // #2 Permit outbound connections, IPv6.
- //
-
- filterBuilder
- .key(MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6())
- .name(L"Permit outbound connections on tunnel interface (IPv6)")
- .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
-
- if (includeV6)
+
+ if (shouldAddV6Filter)
{
+ filterBuilder
+ .key(ipv6Guid)
+ .name(L"Permit outbound connections on tunnel interface (IPv6)")
+ .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
+
wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
-
+
conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
-
- if (m_tunnelOnlyEndpoint.has_value())
+ if (endpoint.has_value())
{
- conditionBuilder.add_condition(ConditionIp::Remote(m_tunnelOnlyEndpoint->ip));
- conditionBuilder.add_condition(ConditionPort::Remote(m_tunnelOnlyEndpoint->port));
- conditionBuilder.add_condition(CreateProtocolCondition(m_tunnelOnlyEndpoint->protocol));
+ conditionBuilder.add_condition(ConditionIp::Remote(endpoint.value().ip));
+ conditionBuilder.add_condition(ConditionPort::Remote(endpoint.value().port));
+ conditionBuilder.add_condition(CreateProtocolCondition(endpoint.value().protocol));
}
- return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
}
+ return true;
+}
+
+bool PermitVpnTunnel::apply(IObjectInstaller &objectInstaller)
+{
+ if (!m_potentialEndpoints.has_value())
+ {
+ return AddEndpointFilter(
+ std::nullopt,
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_1(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_1(),
+ objectInstaller
+ );
+ }
+ AddEndpointFilter(
+ std::make_optional<Endpoint>(m_potentialEndpoints.value().entryEndpoint),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_1(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_1(),
+ objectInstaller
+ );
+ if (m_potentialEndpoints.value().exitEndpoint.has_value())
+ {
+ AddEndpointFilter(
+ m_potentialEndpoints.value().exitEndpoint.value(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_2(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_2(),
+ objectInstaller
+ );
+ }
return true;
}
diff --git a/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.h b/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.h
index ee030a9f43..a89ac46d98 100644
--- a/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.h
+++ b/windows/winfw/src/winfw/rules/baseline/permitvpntunnel.h
@@ -19,17 +19,23 @@ public:
WinFwProtocol protocol;
};
+ struct Endpoints {
+ Endpoint entryEndpoint;
+ std::optional<Endpoint> exitEndpoint;
+ };
+
PermitVpnTunnel(
const std::wstring &tunnelInterfaceAlias,
- const std::optional<Endpoint> &onlyEndpoint
+ const std::optional<Endpoints> &potentialEndpoints
);
bool apply(IObjectInstaller &objectInstaller) override;
private:
+ bool AddEndpointFilter(const std::optional<Endpoint> &endpoint, const GUID &ipv4Guid, const GUID &ipv6Guid, IObjectInstaller &objectInstaller);
const std::wstring m_tunnelInterfaceAlias;
- const std::optional<Endpoint> m_tunnelOnlyEndpoint;
+ const std::optional<Endpoints> m_potentialEndpoints;
};
}
diff --git a/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.cpp b/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.cpp
index a4ff6a65e5..e5b7ab1f5c 100644
--- a/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.cpp
+++ b/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.cpp
@@ -14,30 +14,22 @@ using namespace wfp::conditions;
namespace rules::baseline
{
+using Endpoint = PermitVpnTunnel::Endpoint;
PermitVpnTunnelService::PermitVpnTunnelService(
const std::wstring &tunnelInterfaceAlias,
- const std::optional<PermitVpnTunnel::Endpoint> &onlyEndpoint
+ const std::optional<PermitVpnTunnel::Endpoints> &potentialEndpoints
)
: m_tunnelInterfaceAlias(tunnelInterfaceAlias)
- , m_tunnelOnlyEndpoint(onlyEndpoint)
+ , m_potentialEndpoints(potentialEndpoints)
{
}
-bool PermitVpnTunnelService::apply(IObjectInstaller &objectInstaller)
+bool PermitVpnTunnelService::AddEndpointFilter(const std::optional<PermitVpnTunnel::Endpoint> &endpoint, const GUID &ipv4Guid, const GUID &ipv6Guid, IObjectInstaller &objectInstaller)
{
wfp::FilterBuilder filterBuilder;
- bool includeV4 = !m_tunnelOnlyEndpoint.has_value() || m_tunnelOnlyEndpoint->ip.type() == wfp::IpAddress::Ipv4;
- bool includeV6 = !m_tunnelOnlyEndpoint.has_value() || m_tunnelOnlyEndpoint->ip.type() == wfp::IpAddress::Ipv6;
-
- //
- // #1 Permit inbound connections, IPv4.
- //
-
filterBuilder
- .key(MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4())
- .name(L"Permit inbound connections on tunnel interface (IPv4)")
.description(L"This filter is part of a rule that permits hosting services that listen on the tunnel interface")
.provider(MullvadGuids::Provider())
.layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4)
@@ -45,17 +37,24 @@ bool PermitVpnTunnelService::apply(IObjectInstaller &objectInstaller)
.weight(wfp::FilterBuilder::WeightClass::Medium)
.permit();
- wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+ bool shouldAddV4Filter = !endpoint.has_value() || endpoint.value().ip.type() == wfp::IpAddress::Ipv4;
+ bool shouldAddV6Filter = !endpoint.has_value() || endpoint.value().ip.type() == wfp::IpAddress::Ipv6;
- if (includeV4)
+ if (shouldAddV4Filter)
{
- conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
+ filterBuilder
+ .key(ipv4Guid)
+ .name(L"Permit inbound connections on tunnel interface (IPv4)")
+ .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4);
+
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4);
- if (m_tunnelOnlyEndpoint.has_value())
+ conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
+ if (endpoint.has_value())
{
- conditionBuilder.add_condition(ConditionIp::Remote(m_tunnelOnlyEndpoint->ip));
- conditionBuilder.add_condition(ConditionPort::Remote(m_tunnelOnlyEndpoint->port));
- conditionBuilder.add_condition(CreateProtocolCondition(m_tunnelOnlyEndpoint->protocol));
+ conditionBuilder.add_condition(ConditionIp::Remote(endpoint.value().ip));
+ conditionBuilder.add_condition(ConditionPort::Remote(endpoint.value().port));
+ conditionBuilder.add_condition(CreateProtocolCondition(endpoint.value().protocol));
}
if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
@@ -64,30 +63,57 @@ bool PermitVpnTunnelService::apply(IObjectInstaller &objectInstaller)
}
}
- //
- // #2 Permit inbound connections, IPv6.
- //
-
- if (includeV6)
+ if (shouldAddV6Filter)
{
filterBuilder
- .key(MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6())
+ .key(ipv6Guid)
.name(L"Permit inbound connections on tunnel interface (IPv6)")
.layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6);
- conditionBuilder.reset(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6);
- conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
+ wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6);
- if (m_tunnelOnlyEndpoint.has_value())
+ conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias));
+ if (endpoint.has_value())
{
- conditionBuilder.add_condition(ConditionIp::Remote(m_tunnelOnlyEndpoint->ip));
- conditionBuilder.add_condition(ConditionPort::Remote(m_tunnelOnlyEndpoint->port));
- conditionBuilder.add_condition(CreateProtocolCondition(m_tunnelOnlyEndpoint->protocol));
+ conditionBuilder.add_condition(ConditionIp::Remote(endpoint.value().ip));
+ conditionBuilder.add_condition(ConditionPort::Remote(endpoint.value().port));
+ conditionBuilder.add_condition(CreateProtocolCondition(endpoint.value().protocol));
}
- return objectInstaller.addFilter(filterBuilder, conditionBuilder);
+ if (!objectInstaller.addFilter(filterBuilder, conditionBuilder))
+ {
+ return false;
+ }
}
+ return true;
+}
+bool PermitVpnTunnelService::apply(IObjectInstaller &objectInstaller)
+{
+ if (!m_potentialEndpoints.has_value())
+ {
+ return AddEndpointFilter(
+ std::nullopt,
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_1(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_1(),
+ objectInstaller
+ );
+ }
+ AddEndpointFilter(
+ std::make_optional<Endpoint>(m_potentialEndpoints.value().entryEndpoint),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_1(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_1(),
+ objectInstaller
+ );
+ if (m_potentialEndpoints.value().exitEndpoint.has_value())
+ {
+ AddEndpointFilter(
+ m_potentialEndpoints.value().exitEndpoint.value(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_2(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_2(),
+ objectInstaller
+ );
+ }
return true;
}
diff --git a/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.h b/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.h
index 8011659b97..8b21dc2264 100644
--- a/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.h
+++ b/windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.h
@@ -16,15 +16,16 @@ public:
PermitVpnTunnelService(
const std::wstring &tunnelInterfaceAlias,
- const std::optional<PermitVpnTunnel::Endpoint> &onlyEndpoint
+ const std::optional<PermitVpnTunnel::Endpoints> &potentialEndpoints
);
bool apply(IObjectInstaller &objectInstaller) override;
private:
+ bool AddEndpointFilter(const std::optional<PermitVpnTunnel::Endpoint> &endpoint, const GUID &ipv4Guid, const GUID &ipv6Guid, IObjectInstaller &objectInstaller);
const std::wstring m_tunnelInterfaceAlias;
- const std::optional<PermitVpnTunnel::Endpoint> m_tunnelOnlyEndpoint;
+ const std::optional<PermitVpnTunnel::Endpoints> m_potentialEndpoints;
};
}
diff --git a/windows/winfw/src/winfw/winfw.h b/windows/winfw/src/winfw/winfw.h
index 7a7a1ca9e2..5d61f1029d 100644
--- a/windows/winfw/src/winfw/winfw.h
+++ b/windows/winfw/src/winfw/winfw.h
@@ -59,13 +59,15 @@ enum WinFwAllowedTunnelTrafficType : uint8_t
{
None,
All,
- Only
+ One,
+ Two
};
typedef struct tag_WinFwAllowedTunnelTraffic
{
WinFwAllowedTunnelTrafficType type;
- WinFwEndpoint *endpoint;
+ WinFwEndpoint *endpoint1;
+ WinFwEndpoint *endpoint2;
}
WinFwAllowedTunnelTraffic;
@@ -181,9 +183,9 @@ WinFw_ApplyPolicyConnecting(
// Parameters:
//
// tunnelInterfaceAlias:
-// Friendly name of VPN tunnel interface
+// Friendly name of VPN tunnel interface
// dnsServers:
-// Array of string-encoded IP addresses of DNS servers to use
+// Array of string-encoded IP addresses of DNS servers to use
//
extern "C"
WINFW_LINKAGE