summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJonathan <jonathan@mullvad.net>2023-02-16 15:41:54 +0100
committerDavid Lönnhager <david.l@mullvad.net>2023-02-28 10:07:52 +0100
commitf2f7fa7109830a6c5cb695c8ca60bf3f84ab9c10 (patch)
tree17c77f7e99979323f9b4a7e92cda88230ea25832
parentd1eb83161d45f7f98b7f4f705a9550d02e85a030 (diff)
downloadmullvadvpn-f2f7fa7109830a6c5cb695c8ca60bf3f84ab9c10.tar.xz
mullvadvpn-f2f7fa7109830a6c5cb695c8ca60bf3f84ab9c10.zip
Add PQ support for multihop, and allow listing two endpoints in the
tunnel
-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.rs71
-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.rs257
-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
17 files changed, 549 insertions, 230 deletions
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..b015db2385 100644
--- a/talpid-core/src/firewall/windows.rs
+++ b/talpid-core/src/firewall/windows.rs
@@ -168,27 +168,52 @@ 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: `allowed_tun_ips`, `entry_endpoint` and `exit_endpoint` must not be dropped until
+ // `WinFw_ApplyPolicyConnecting` has returned.
+ let mut allowed_tun_ips = [WideCString::new(), WideCString::new()];
+ let (entry_endpoint, exit_endpoint) = match allowed_tunnel_traffic {
+ AllowedTunnelTraffic::One(endpoint) => {
+ allowed_tun_ips[0] = widestring_ip(endpoint.address.ip());
+ (
+ Some(WinFwEndpoint {
+ ip: allowed_tun_ips[0].as_ptr(),
+ port: endpoint.address.port(),
+ protocol: WinFwProt::from(endpoint.protocol),
+ }),
+ None,
+ )
+ }
+ AllowedTunnelTraffic::Two(endpoint1, endpoint2) => {
+ allowed_tun_ips[0] = widestring_ip(endpoint1.address.ip());
+ let entry_endpoint = Some(WinFwEndpoint {
+ ip: allowed_tun_ips[0].as_ptr(),
+ port: endpoint1.address.port(),
+ protocol: WinFwProt::from(endpoint1.protocol),
+ });
+ allowed_tun_ips[1] = widestring_ip(endpoint2.address.ip());
+ let exit_endpoint = Some(WinFwEndpoint {
+ ip: allowed_tun_ips[1].as_ptr(),
+ port: endpoint2.address.port(),
+ protocol: WinFwProt::from(endpoint2.protocol),
+ });
+ (entry_endpoint, exit_endpoint)
+ }
+ AllowedTunnelTraffic::None | AllowedTunnelTraffic::All => (None, None),
+ };
+
let allowed_tunnel_traffic = WinFwAllowedTunnelTraffic {
type_: WinFwAllowedTunnelTrafficType::from(allowed_tunnel_traffic),
- endpoint: allowed_tunnel_endpoint
+ entry_endpoint: entry_endpoint
+ .as_ref()
+ .map(|ep| ep as *const _)
+ .unwrap_or(ptr::null()),
+ exit_endpoint: exit_endpoint
.as_ref()
.map(|ep| ep as *const _)
.unwrap_or(ptr::null()),
};
- unsafe {
+ let res = unsafe {
WinFw_ApplyPolicyConnecting(
winfw_settings,
&winfw_relay,
@@ -199,7 +224,14 @@ 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(allowed_tun_ips);
+ drop(entry_endpoint);
+ drop(exit_endpoint);
+ res
}
fn set_connected_state(
@@ -439,7 +471,8 @@ mod winfw {
#[repr(C)]
pub struct WinFwAllowedTunnelTraffic {
pub type_: WinFwAllowedTunnelTrafficType,
- pub endpoint: *const WinFwEndpoint,
+ pub entry_endpoint: *const WinFwEndpoint,
+ pub exit_endpoint: *const WinFwEndpoint,
}
#[repr(u8)]
@@ -447,7 +480,8 @@ mod winfw {
pub enum WinFwAllowedTunnelTrafficType {
None,
All,
- Only,
+ One,
+ Two,
}
impl From<&AllowedTunnelTraffic> for WinFwAllowedTunnelTrafficType {
@@ -455,7 +489,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..1dffe191fd 100644
--- a/talpid-wireguard/src/lib.rs
+++ b/talpid-wireguard/src/lib.rs
@@ -28,12 +28,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 +93,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 +236,20 @@ 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 obfuscator = args.runtime.block_on(maybe_create_obfuscator(
- &mut config,
- close_msg_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()),
+ &Self::patch_allowed_ips(&config, psk_negotiation),
log_path,
args.resource_dir,
args.tun_provider.clone(),
@@ -261,6 +260,12 @@ impl WireguardMonitor {
)?;
let iface_name = tunnel.get_interface_name();
+ let (close_obfs_sender, close_obfs_listener) = sync_mpsc::channel();
+
+ let obfuscator = Arc::new(AsyncMutex::new(args.runtime.block_on(
+ maybe_create_obfuscator(&mut config, close_obfs_sender.clone()),
+ )?));
+
#[cfg(target_os = "android")]
if let Some(remote_socket_fd) = obfuscator.as_ref().map(|obfs| obfs.remote_socket_fd()) {
// Exclude remote obfuscation socket or bridge
@@ -276,9 +281,9 @@ impl 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 +296,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 +336,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 +376,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 +393,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,13 +403,144 @@ 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.
fn patch_allowed_ips(config: &Config, gateway_only: bool) -> Cow<'_, Config> {
@@ -473,13 +609,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 +621,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 +637,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 +726,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 +880,7 @@ impl WireguardMonitor {
}
}
+#[derive(Debug)]
enum CloseMsg {
Stop,
PskNegotiationTimeout,
@@ -786,6 +888,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..ac5e367587 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.entryEndpoint->ip),
+ allowedTunnelTraffic.entryEndpoint->port,
+ allowedTunnelTraffic.entryEndpoint->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.entryEndpoint->ip),
+ allowedTunnelTraffic.entryEndpoint->port,
+ allowedTunnelTraffic.entryEndpoint->protocol
+ },
+ std::make_optional<baseline::PermitVpnTunnel::Endpoint>({
+ wfp::IpAddress(allowedTunnelTraffic.exitEndpoint->ip),
+ allowedTunnelTraffic.exitEndpoint->port,
+ allowedTunnelTraffic.exitEndpoint->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..49e80107db 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_Entry()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_Entry()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_Exit()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_Exit()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv4_Entry()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv6_Entry()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv4_Exit()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, Filter_Baseline_PermitVpnTunnelService_Ipv6_Exit()));
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_Entry()
{
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_Entry()
{
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_Exit()
+{
+ 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_Exit()
+{
+ 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_Entry()
{
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_Entry()
{
static const GUID g =
{
@@ -719,6 +751,35 @@ const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6()
}
//static
+const GUID &MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_Exit()
+{
+ 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_Exit()
+{
+
+ 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..57d4cc4c91 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_Entry();
+ static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_Entry();
+ static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_Exit();
+ static const GUID &Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_Exit();
- static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv4();
- static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv6();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv4_Entry();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv6_Entry();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv4_Exit();
+ static const GUID &Filter_Baseline_PermitVpnTunnelService_Ipv6_Exit();
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..b5ea28aeeb 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_Entry(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_Entry(),
+ objectInstaller
+ );
+ }
+ AddEndpointFilter(
+ std::make_optional<Endpoint>(m_potentialEndpoints.value().entryEndpoint),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_Entry(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_Entry(),
+ objectInstaller
+ );
+ if (m_potentialEndpoints.value().exitEndpoint.has_value())
+ {
+ AddEndpointFilter(
+ m_potentialEndpoints.value().exitEndpoint.value(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv4_Exit(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnel_Outbound_Ipv6_Exit(),
+ 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..0cb773725b 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_Entry(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_Entry(),
+ objectInstaller
+ );
+ }
+ AddEndpointFilter(
+ std::make_optional<Endpoint>(m_potentialEndpoints.value().entryEndpoint),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_Entry(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_Entry(),
+ objectInstaller
+ );
+ if (m_potentialEndpoints.value().exitEndpoint.has_value())
+ {
+ AddEndpointFilter(
+ m_potentialEndpoints.value().exitEndpoint.value(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv4_Exit(),
+ MullvadGuids::Filter_Baseline_PermitVpnTunnelService_Ipv6_Exit(),
+ 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..c5498d3969 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 *entryEndpoint;
+ WinFwEndpoint *exitEndpoint;
}
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