diff options
| author | Jonathan <jonathan@mullvad.net> | 2023-02-16 15:41:54 +0100 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-02-28 10:07:52 +0100 |
| commit | f2f7fa7109830a6c5cb695c8ca60bf3f84ab9c10 (patch) | |
| tree | 17c77f7e99979323f9b4a7e92cda88230ea25832 | |
| parent | d1eb83161d45f7f98b7f4f705a9550d02e85a030 (diff) | |
| download | mullvadvpn-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.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 53 | ||||
| -rw-r--r-- | talpid-core/src/firewall/windows.rs | 71 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 13 | ||||
| -rw-r--r-- | talpid-tunnel-config-client/examples/psk-exchange.rs | 13 | ||||
| -rw-r--r-- | talpid-tunnel-config-client/src/lib.rs | 10 | ||||
| -rw-r--r-- | talpid-types/src/net/mod.rs | 12 | ||||
| -rw-r--r-- | talpid-wireguard/src/config.rs | 2 | ||||
| -rw-r--r-- | talpid-wireguard/src/lib.rs | 257 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/fwcontext.cpp | 37 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/mullvadguids.cpp | 77 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/mullvadguids.h | 12 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/rules/baseline/permitvpntunnel.cpp | 101 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/rules/baseline/permitvpntunnel.h | 10 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.cpp | 90 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/rules/baseline/permitvpntunnelservice.h | 5 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.h | 10 |
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 |
