diff options
| -rw-r--r-- | CHANGELOG.md | 6 | ||||
| -rw-r--r-- | docs/security.md | 8 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 34 | ||||
| -rw-r--r-- | talpid-core/src/firewall/linux.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/firewall/mod.rs | 13 | ||||
| -rw-r--r-- | talpid-core/src/firewall/windows.rs | 111 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/mod.rs | 22 | ||||
| -rw-r--r-- | talpid-types/src/net/mod.rs | 34 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/fwcontext.cpp | 25 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/fwcontext.h | 10 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/rules/baseline/permitendpoint.cpp | 6 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/rules/baseline/permitendpoint.h | 3 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.cpp | 6 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.h | 20 |
15 files changed, 218 insertions, 88 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 3123022759..6df7d9ca18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,12 @@ Line wrap the file at 100 chars. Th - Fix banner sometimes incorrectly showing (e.g. "BLOCKING INTERNET"). - Fix issue with the user getting kicked out of certain views in settings when the app is brought to the foreground. +### Security +#### Windows +- Restrict which applications are allowed to communicate with the API while in a blocking state. + This prevents malicious scripts on websites from trying to do so. + + ## [2021.6] - 2021-11-17 ### Fixed - Fix the font for Russian. Issue introduced in 2021.6-beta1. diff --git a/docs/security.md b/docs/security.md index 2f0b09e31c..67d4f3dcdf 100644 --- a/docs/security.md +++ b/docs/security.md @@ -99,6 +99,13 @@ The following network traffic is allowed or blocked independent of state: On Linux, any situation that permits incoming or outgoing traffic also allows that traffic to be forwarded. All other forward traffic is rejected. +#### Mullvad API + +The firewall allows traffic for the API regardless of tunnel state, to allow for updating keys, +fetching account data, etc. In the [Connected] state, this is only allowed inside the tunnel. +For the other states, it is allowed regardless. On Windows, only the Mullvad service and problem +report tool are able to communicate with the API in any of the blocking states. + ### Disconnected This is the default state that the `mullvad-daemon` starts in when the device boots, unless @@ -184,7 +191,6 @@ disconnect/quit is explicitly requested by the user. At the same time there migh when the app can't establish a tunnel for the device. This includes, but is not limited to: * Account runs out of time * The computer is offline -* the TAP adapter driver has an error or the adapter can't be found (Windows) * Some internal error parsing or modifying system routing table, DNS settings etc. In the above cases the app gives up trying to create a tunnel, but it can't go to the diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index b0292b4a8b..5557590b09 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -64,7 +64,10 @@ use talpid_core::{ #[cfg(target_os = "android")] use talpid_types::android::AndroidContext; use talpid_types::{ - net::{openvpn, Endpoint, TransportProtocol, TunnelEndpoint, TunnelParameters, TunnelType}, + net::{ + openvpn, AllowedEndpoint, Endpoint, TransportProtocol, TunnelEndpoint, TunnelParameters, + TunnelType, + }, tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition}, ErrorExt, }; @@ -640,10 +643,8 @@ where let api_availability = rpc_runtime.availability_handle(); api_availability.suspend(); - let initial_api_endpoint = Endpoint::from_socket_address( - rpc_runtime.address_cache.peek_address(), - TransportProtocol::Tcp, - ); + let initial_api_endpoint = + Self::get_allowed_endpoint(rpc_runtime.address_cache.peek_address()); let (offline_state_tx, offline_state_rx) = mpsc::unbounded(); let tunnel_command_tx = tunnel_state_machine::spawn( @@ -676,7 +677,7 @@ where address_change_runtime.block_on(async move { if let Some(tx) = tx.upgrade() { let _ = tx.unbounded_send(TunnelCommand::AllowEndpoint( - Endpoint::from_socket_address(address, TransportProtocol::Tcp), + Self::get_allowed_endpoint(address), result_tx, )); result_rx.await.map_err(|_| ()) @@ -770,6 +771,27 @@ where Ok(daemon) } + fn get_allowed_endpoint(api_address: std::net::SocketAddr) -> AllowedEndpoint { + let endpoint = Endpoint::from_socket_address(api_address, TransportProtocol::Tcp); + + #[cfg(windows)] + let daemon_exe = std::env::current_exe().expect("failed to obtain executable path"); + #[cfg(windows)] + let clients = vec![ + daemon_exe + .parent() + .expect("missing executable parent directory") + .join("mullvad-problem-report.exe"), + daemon_exe, + ]; + + AllowedEndpoint { + #[cfg(windows)] + clients, + endpoint, + } + } + fn get_dns_resolvers(options: &DnsOptions) -> Option<Vec<IpAddr>> { match options.state { DnsState::Default => { diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs index c1619f0f46..c10aba6ba8 100644 --- a/talpid-core/src/firewall/linux.rs +++ b/talpid-core/src/firewall/linux.rs @@ -560,7 +560,7 @@ impl<'a> PolicyBatch<'a> { allowed_endpoint, } => { self.add_allow_tunnel_endpoint_rules(peer_endpoint); - self.add_allow_endpoint_rules(allowed_endpoint); + self.add_allow_endpoint_rules(&allowed_endpoint.endpoint); // Important to block DNS after allow relay rule (so the relay can operate // over port 53) but before allow LAN (so DNS does not leak to the LAN) @@ -596,7 +596,7 @@ impl<'a> PolicyBatch<'a> { allow_lan, allowed_endpoint, } => { - self.add_allow_endpoint_rules(allowed_endpoint); + self.add_allow_endpoint_rules(&allowed_endpoint.endpoint); // Important to drop DNS before allowing LAN (to stop DNS leaking to the LAN) self.add_drop_dns_rule(); diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs index 3cb63e485e..6cbdbbf7ff 100644 --- a/talpid-core/src/firewall/macos.rs +++ b/talpid-core/src/firewall/macos.rs @@ -104,7 +104,7 @@ impl Firewall { allowed_endpoint, } => { let mut rules = vec![self.get_allow_relay_rule(peer_endpoint)?]; - rules.push(self.get_allowed_endpoint_rule(allowed_endpoint)?); + rules.push(self.get_allowed_endpoint_rule(allowed_endpoint.endpoint)?); // Important to block DNS after allow relay rule (so the relay can operate // over port 53) but before allow LAN (so DNS does not leak to the LAN) @@ -150,7 +150,7 @@ impl Firewall { allowed_endpoint, } => { let mut rules = Vec::new(); - rules.push(self.get_allowed_endpoint_rule(allowed_endpoint)?); + rules.push(self.get_allowed_endpoint_rule(allowed_endpoint.endpoint)?); if allow_lan { // Important to block DNS before allow LAN (so DNS does not leak to the LAN) rules.append(&mut self.get_block_dns_rules()?); diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs index 380953f7c5..46bcf27745 100644 --- a/talpid-core/src/firewall/mod.rs +++ b/talpid-core/src/firewall/mod.rs @@ -9,7 +9,7 @@ use std::net::IpAddr; use std::net::{Ipv4Addr, Ipv6Addr}; #[cfg(windows)] use std::path::PathBuf; -use talpid_types::net::Endpoint; +use talpid_types::net::{AllowedEndpoint, Endpoint}; #[cfg(target_os = "macos")] #[path = "macos.rs"] @@ -107,8 +107,8 @@ pub enum FirewallPolicy { tunnel: Option<crate::tunnel::TunnelMetadata>, /// Flag setting if communication with LAN networks should be possible. allow_lan: bool, - /// Host that should be reachable by the tunnel client while connecting. - allowed_endpoint: Endpoint, + /// Host that should be reachable while connecting. + allowed_endpoint: AllowedEndpoint, /// A process that is allowed to send packets to the relay. #[cfg(windows)] relay_client: PathBuf, @@ -135,7 +135,7 @@ pub enum FirewallPolicy { /// Flag setting if communication with LAN networks should be possible. allow_lan: bool, /// Host that should be reachable while in the blocked state. - allowed_endpoint: Endpoint, + allowed_endpoint: AllowedEndpoint, }, } @@ -225,10 +225,7 @@ pub enum InitialFirewallState { /// Do not set any policy. None, /// Atomically enter the blocked state. - Blocked { - /// Host that should be reachable while in the blocked state. - allowed_endpoint: Endpoint, - }, + Blocked(AllowedEndpoint), } impl Firewall { diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs index 3bb201507c..989e453ff7 100644 --- a/talpid-core/src/firewall/windows.rs +++ b/talpid-core/src/firewall/windows.rs @@ -6,7 +6,10 @@ use self::winfw::*; use super::{FirewallArguments, FirewallPolicy, FirewallT, InitialFirewallState}; use crate::winnet; use log::{debug, error, trace}; -use talpid_types::{net::Endpoint, tunnel::FirewallPolicyError}; +use talpid_types::{ + net::{AllowedEndpoint, Endpoint}, + tunnel::FirewallPolicyError, +}; use widestring::WideCString; /// Errors that can happen when configuring the Windows firewall. @@ -53,19 +56,14 @@ impl FirewallT for Firewall { fn new(args: FirewallArguments) -> Result<Self, Self::Error> { let logging_context = b"WinFw\0".as_ptr(); - if let InitialFirewallState::Blocked { allowed_endpoint } = args.initial_state { + if let InitialFirewallState::Blocked(allowed_endpoint) = args.initial_state { let cfg = &WinFwSettings::new(args.allow_lan); - let allowed_endpoint_ip = widestring_ip(allowed_endpoint.address.ip()); - let winfw_allowed_endpoint = WinFwEndpoint { - ip: allowed_endpoint_ip.as_ptr(), - port: allowed_endpoint.address.port(), - protocol: WinFwProt::from(allowed_endpoint.protocol), - }; + let allowed_endpoint = WinFwAllowedEndpointContainer::from(allowed_endpoint); unsafe { WinFw_InitializeBlocked( WINFW_TIMEOUT_SECONDS, &cfg, - &winfw_allowed_endpoint, + &allowed_endpoint.as_endpoint(), Some(log_sink), logging_context, ) @@ -92,11 +90,12 @@ impl FirewallT for Firewall { relay_client, } => { let cfg = &WinFwSettings::new(allow_lan); + self.set_connecting_state( &peer_endpoint, &cfg, &tunnel, - &allowed_endpoint, + &WinFwAllowedEndpointContainer::from(allowed_endpoint).as_endpoint(), &relay_client, ) } @@ -115,7 +114,10 @@ impl FirewallT for Firewall { allowed_endpoint, } => { let cfg = &WinFwSettings::new(allow_lan); - self.set_blocked_state(&cfg, &allowed_endpoint) + self.set_blocked_state( + &cfg, + &WinFwAllowedEndpointContainer::from(allowed_endpoint).as_endpoint(), + ) } } } @@ -146,7 +148,7 @@ impl Firewall { endpoint: &Endpoint, winfw_settings: &WinFwSettings, tunnel_metadata: &Option<TunnelMetadata>, - allowed_endpoint: &Endpoint, + allowed_endpoint: &WinFwAllowedEndpoint<'_>, relay_client: &Path, ) -> Result<(), Error> { trace!("Applying 'connecting' firewall policy"); @@ -159,13 +161,6 @@ impl Firewall { let relay_client = WideCString::from_os_str_truncate(relay_client); - let allowed_endpoint_ip = widestring_ip(allowed_endpoint.address.ip()); - let winfw_allowed_endpoint = WinFwEndpoint { - ip: allowed_endpoint_ip.as_ptr(), - port: allowed_endpoint.address.port(), - protocol: WinFwProt::from(allowed_endpoint.protocol), - }; - let interface_wstr = tunnel_metadata .as_ref() .map(|metadata| WideCString::from_str_truncate(&metadata.interface)); @@ -181,7 +176,7 @@ impl Firewall { &winfw_relay, relay_client.as_ptr(), interface_wstr_ptr, - &winfw_allowed_endpoint, + allowed_endpoint, ) .into_result() .map_err(Error::ApplyingConnectingPolicy) @@ -251,19 +246,11 @@ impl Firewall { fn set_blocked_state( &mut self, winfw_settings: &WinFwSettings, - allowed_endpoint: &Endpoint, + allowed_endpoint: &WinFwAllowedEndpoint<'_>, ) -> Result<(), Error> { trace!("Applying 'blocked' firewall policy"); - - let allowed_endpoint_ip = widestring_ip(allowed_endpoint.address.ip()); - let winfw_allowed_endpoint = WinFwEndpoint { - ip: allowed_endpoint_ip.as_ptr(), - port: allowed_endpoint.address.port(), - protocol: WinFwProt::from(allowed_endpoint.protocol), - }; - unsafe { - WinFw_ApplyPolicyBlocked(winfw_settings, &winfw_allowed_endpoint) + WinFw_ApplyPolicyBlocked(winfw_settings, allowed_endpoint) .into_result() .map_err(Error::ApplyingBlockedPolicy) } @@ -276,11 +263,67 @@ fn widestring_ip(ip: IpAddr) -> WideCString { #[allow(non_snake_case)] mod winfw { - use super::Error; + use super::{widestring_ip, AllowedEndpoint, Error, WideCString}; use crate::logging::windows::LogSink; use libc; use talpid_types::net::TransportProtocol; + pub struct WinFwAllowedEndpointContainer { + _clients: Box<[WideCString]>, + clients_ptrs: Box<[*const u16]>, + ip: WideCString, + port: u16, + protocol: WinFwProt, + } + + impl From<AllowedEndpoint> for WinFwAllowedEndpointContainer { + fn from(endpoint: AllowedEndpoint) -> Self { + let clients = endpoint + .clients + .iter() + .map(|client| WideCString::from_os_str_truncate(client)) + .collect::<Box<_>>(); + let clients_ptrs = clients + .iter() + .map(|client| client.as_ptr()) + .collect::<Box<_>>(); + let ip = widestring_ip(endpoint.endpoint.address.ip()); + + WinFwAllowedEndpointContainer { + _clients: clients, + clients_ptrs, + ip, + port: endpoint.endpoint.address.port(), + protocol: WinFwProt::from(endpoint.endpoint.protocol), + } + } + } + + impl WinFwAllowedEndpointContainer { + pub fn as_endpoint(&self) -> WinFwAllowedEndpoint<'_> { + WinFwAllowedEndpoint { + num_clients: self.clients_ptrs.len() as u32, + clients: self.clients_ptrs.as_ptr(), + endpoint: WinFwEndpoint { + ip: self.ip.as_ptr(), + port: self.port, + protocol: self.protocol, + }, + + _phantom: std::marker::PhantomData, + } + } + } + + #[repr(C)] + pub struct WinFwAllowedEndpoint<'a> { + num_clients: u32, + clients: *const *const libc::wchar_t, + endpoint: WinFwEndpoint, + + _phantom: std::marker::PhantomData<&'a WinFwAllowedEndpointContainer>, + } + #[repr(C)] pub struct WinFwEndpoint { pub ip: *const libc::wchar_t, @@ -370,7 +413,7 @@ mod winfw { pub fn WinFw_InitializeBlocked( timeout: libc::c_uint, settings: &WinFwSettings, - allowed_endpoint: *const WinFwEndpoint, + allowed_endpoint: *const WinFwAllowedEndpoint<'_>, sink: Option<LogSink>, sink_context: *const u8, ) -> InitializationResult; @@ -384,7 +427,7 @@ mod winfw { relay: &WinFwEndpoint, relayClient: *const libc::wchar_t, tunnelIfaceAlias: *const libc::wchar_t, - allowed_endpoint: *const WinFwEndpoint, + allowed_endpoint: *const WinFwAllowedEndpoint<'_>, ) -> WinFwPolicyStatus; #[link_name = "WinFw_ApplyPolicyConnected"] @@ -402,7 +445,7 @@ mod winfw { #[link_name = "WinFw_ApplyPolicyBlocked"] pub fn WinFw_ApplyPolicyBlocked( settings: &WinFwSettings, - allowed_endpoint: *const WinFwEndpoint, + allowed_endpoint: *const WinFwAllowedEndpoint<'_>, ) -> WinFwPolicyStatus; #[link_name = "WinFw_Reset"] diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index 671148fde0..5f33d264ca 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -34,7 +34,7 @@ use std::{collections::HashSet, io, net::IpAddr, path::PathBuf, sync::Arc}; #[cfg(target_os = "android")] use talpid_types::{android::AndroidContext, ErrorExt}; use talpid_types::{ - net::{Endpoint, TunnelParameters}, + net::{AllowedEndpoint, TunnelParameters}, tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition}, }; @@ -81,7 +81,7 @@ pub struct InitialTunnelState { pub dns_servers: Option<Vec<IpAddr>>, /// A single endpoint that is allowed to communicate outside the tunnel, i.e. /// in any of the blocking states. - pub allowed_endpoint: Endpoint, + pub allowed_endpoint: AllowedEndpoint, /// Whether to reset any existing firewall rules when initializing the disconnected state. pub reset_firewall: bool, /// Programs to exclude from the tunnel using the split tunnel driver. @@ -109,7 +109,7 @@ pub async fn spawn( #[cfg(target_os = "android")] initial_settings.allow_lan, #[cfg(target_os = "android")] - initial_settings.allowed_endpoint.address.ip(), + initial_settings.allowed_endpoint.endpoint.address.ip(), #[cfg(target_os = "android")] initial_settings.dns_servers.clone(), ); @@ -145,7 +145,7 @@ pub enum TunnelCommand { AllowLan(bool), /// Endpoint that should never be blocked. /// If an error occurs, the sender is dropped. - AllowEndpoint(Endpoint, oneshot::Sender<()>), + AllowEndpoint(AllowedEndpoint, oneshot::Sender<()>), /// Set DNS servers to use. Dns(Option<Vec<IpAddr>>), /// Enable or disable the block_when_disconnected feature. @@ -209,9 +209,7 @@ impl TunnelStateMachine { let args = FirewallArguments { initial_state: if settings.block_when_disconnected || !settings.reset_firewall { - InitialFirewallState::Blocked { - allowed_endpoint: settings.allowed_endpoint, - } + InitialFirewallState::Blocked(settings.allowed_endpoint.clone()) } else { InitialFirewallState::None }, @@ -356,7 +354,7 @@ struct SharedTunnelStateValues { /// DNS servers to use (overriding default). dns_servers: Option<Vec<IpAddr>>, /// Endpoint that should not be blocked by the firewall. - allowed_endpoint: Endpoint, + allowed_endpoint: AllowedEndpoint, /// The generator of new `TunnelParameter`s tunnel_parameters_generator: Box<dyn TunnelParametersGenerator>, /// The provider of tunnel devices. @@ -394,13 +392,13 @@ impl SharedTunnelStateValues { Ok(()) } - pub fn set_allowed_endpoint(&mut self, endpoint: Endpoint) -> bool { + pub fn set_allowed_endpoint(&mut self, endpoint: AllowedEndpoint) -> bool { if self.allowed_endpoint != endpoint { - self.allowed_endpoint = endpoint; - #[cfg(target_os = "android")] self.tun_provider - .set_allowed_endpoint(endpoint.address.ip()); + .set_allowed_endpoint(endpoint.endpoint.address.ip()); + + self.allowed_endpoint = endpoint; true } else { diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs index c03aac3d58..e758d0e02f 100644 --- a/talpid-types/src/net/mod.rs +++ b/talpid-types/src/net/mod.rs @@ -1,6 +1,8 @@ #[cfg(target_os = "android")] use jnix::IntoJava; use serde::{Deserialize, Serialize}; +#[cfg(windows)] +use std::path::PathBuf; use std::{ fmt, net::{IpAddr, SocketAddr}, @@ -174,6 +176,38 @@ impl fmt::Display for Endpoint { } } +/// Host that should be reachable in any tunnel state. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AllowedEndpoint { + /// Paths that should be allowed to communicate with `endpoint`. + #[cfg(windows)] + pub clients: Vec<PathBuf>, + pub endpoint: Endpoint, +} + +impl fmt::Display for AllowedEndpoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + #[cfg(not(windows))] + write!(f, "{}", self.endpoint)?; + #[cfg(windows)] + { + write!(f, "{} for", self.endpoint)?; + #[cfg(windows)] + for client in &self.clients { + write!( + f, + " {}", + client + .file_name() + .map(|s| s.to_string_lossy()) + .unwrap_or(std::borrow::Cow::Borrowed("<UNKNOWN>")) + )?; + } + } + Ok(()) + } +} + /// IP protocol version. #[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] diff --git a/windows/winfw/src/winfw/fwcontext.cpp b/windows/winfw/src/winfw/fwcontext.cpp index 6f2667c429..fe6c227e7f 100644 --- a/windows/winfw/src/winfw/fwcontext.cpp +++ b/windows/winfw/src/winfw/fwcontext.cpp @@ -104,13 +104,20 @@ void AppendRelayRules void AppendAllowedEndpointRules ( FwContext::Ruleset &ruleset, - const WinFwEndpoint &endpoint + const WinFwAllowedEndpoint &endpoint ) { + std::vector<std::wstring> clients; + clients.reserve(endpoint.numClients); + for (uint32_t i = 0; i < endpoint.numClients; i++) { + clients.push_back(endpoint.clients[i]); + } + ruleset.emplace_back(std::make_unique<baseline::PermitEndpoint>( - wfp::IpAddress(endpoint.ip), - endpoint.port, - endpoint.protocol + wfp::IpAddress(endpoint.endpoint.ip), + clients, + endpoint.endpoint.port, + endpoint.endpoint.protocol )); } @@ -149,7 +156,7 @@ FwContext::FwContext ( uint32_t timeout, const WinFwSettings &settings, - const std::optional<WinFwEndpoint> &allowedEndpoint + const std::optional<WinFwAllowedEndpoint> &allowedEndpoint ) : m_baseline(0) , m_activePolicy(Policy::None) @@ -178,7 +185,7 @@ bool FwContext::applyPolicyConnecting const WinFwEndpoint &relay, const std::wstring &relayClient, const std::optional<std::wstring> &tunnelInterfaceAlias, - const std::optional<WinFwEndpoint> &allowedEndpoint + const std::optional<WinFwAllowedEndpoint> &allowedEndpoint ) { Ruleset ruleset; @@ -260,7 +267,7 @@ bool FwContext::applyPolicyConnected return status; } -bool FwContext::applyPolicyBlocked(const WinFwSettings &settings, const std::optional<WinFwEndpoint> &allowedEndpoint) +bool FwContext::applyPolicyBlocked(const WinFwSettings &settings, const std::optional<WinFwAllowedEndpoint> &allowedEndpoint) { const auto status = applyRuleset(composePolicyBlocked(settings, allowedEndpoint)); @@ -292,7 +299,7 @@ FwContext::Policy FwContext::activePolicy() const return m_activePolicy; } -FwContext::Ruleset FwContext::composePolicyBlocked(const WinFwSettings &settings, const std::optional<WinFwEndpoint> &allowedEndpoint) +FwContext::Ruleset FwContext::composePolicyBlocked(const WinFwSettings &settings, const std::optional<WinFwAllowedEndpoint> &allowedEndpoint) { Ruleset ruleset; @@ -315,7 +322,7 @@ bool FwContext::applyBaseConfiguration() }); } -bool FwContext::applyBlockedBaseConfiguration(const WinFwSettings &settings, const std::optional<WinFwEndpoint> &allowedEndpoint, uint32_t &checkpoint) +bool FwContext::applyBlockedBaseConfiguration(const WinFwSettings &settings, const std::optional<WinFwAllowedEndpoint> &allowedEndpoint, uint32_t &checkpoint) { return m_sessionController->executeTransaction([&](SessionController &controller, wfp::FilterEngine &engine) { diff --git a/windows/winfw/src/winfw/fwcontext.h b/windows/winfw/src/winfw/fwcontext.h index a3b23f2c8b..bf67565993 100644 --- a/windows/winfw/src/winfw/fwcontext.h +++ b/windows/winfw/src/winfw/fwcontext.h @@ -21,7 +21,7 @@ public: ( uint32_t timeout, const WinFwSettings &settings, - const std::optional<WinFwEndpoint> &allowedEndpoint + const std::optional<WinFwAllowedEndpoint> &allowedEndpoint ); bool applyPolicyConnecting @@ -30,7 +30,7 @@ public: const WinFwEndpoint &relay, const std::wstring &relayClient, const std::optional<std::wstring> &tunnelInterfaceAlias, - const std::optional<WinFwEndpoint> &allowedEndpoint + const std::optional<WinFwAllowedEndpoint> &allowedEndpoint ); bool applyPolicyConnected @@ -45,7 +45,7 @@ public: bool applyPolicyBlocked( const WinFwSettings &settings, - const std::optional<WinFwEndpoint> &allowedEndpoint + const std::optional<WinFwAllowedEndpoint> &allowedEndpoint ); bool reset(); @@ -67,10 +67,10 @@ private: FwContext(const FwContext &) = delete; FwContext &operator=(const FwContext &) = delete; - Ruleset composePolicyBlocked(const WinFwSettings &settings, const std::optional<WinFwEndpoint> &allowedEndpoint); + Ruleset composePolicyBlocked(const WinFwSettings &settings, const std::optional<WinFwAllowedEndpoint> &allowedEndpoint); bool applyBaseConfiguration(); - bool applyBlockedBaseConfiguration(const WinFwSettings &settings, const std::optional<WinFwEndpoint> &allowedEndpoint, uint32_t &checkpoint); + bool applyBlockedBaseConfiguration(const WinFwSettings &settings, const std::optional<WinFwAllowedEndpoint> &allowedEndpoint, uint32_t &checkpoint); bool applyCommonBaseConfiguration(SessionController &controller, wfp::FilterEngine &engine); bool applyRuleset(const Ruleset &ruleset); diff --git a/windows/winfw/src/winfw/rules/baseline/permitendpoint.cpp b/windows/winfw/src/winfw/rules/baseline/permitendpoint.cpp index 5b79d64ceb..09d8937535 100644 --- a/windows/winfw/src/winfw/rules/baseline/permitendpoint.cpp +++ b/windows/winfw/src/winfw/rules/baseline/permitendpoint.cpp @@ -48,10 +48,12 @@ std::unique_ptr<ConditionProtocol> CreateProtocolCondition(WinFwProtocol protoco PermitEndpoint::PermitEndpoint ( const wfp::IpAddress &address, + const std::vector<std::wstring> &clients, uint16_t port, WinFwProtocol protocol ) : m_address(address) + , m_clients(clients) , m_port(port) , m_protocol(protocol) { @@ -81,6 +83,10 @@ bool PermitEndpoint::apply(IObjectInstaller &objectInstaller) conditionBuilder.add_condition(ConditionPort::Remote(m_port)); conditionBuilder.add_condition(CreateProtocolCondition(m_protocol)); + for (const auto client : m_clients) { + conditionBuilder.add_condition(std::make_unique<ConditionApplication>(client)); + } + return objectInstaller.addFilter(filterBuilder, conditionBuilder); } diff --git a/windows/winfw/src/winfw/rules/baseline/permitendpoint.h b/windows/winfw/src/winfw/rules/baseline/permitendpoint.h index 93564dbd1e..9e5e2fc923 100644 --- a/windows/winfw/src/winfw/rules/baseline/permitendpoint.h +++ b/windows/winfw/src/winfw/rules/baseline/permitendpoint.h @@ -3,6 +3,7 @@ #include <winfw/rules/ifirewallrule.h> #include <winfw/winfw.h> #include <libwfp/ipaddress.h> +#include <vector> #include <string> namespace rules::baseline @@ -15,6 +16,7 @@ public: PermitEndpoint ( const wfp::IpAddress &address, + const std::vector<std::wstring> &clients, uint16_t port, WinFwProtocol protocol ); @@ -24,6 +26,7 @@ public: private: const wfp::IpAddress m_address; + const std::vector<std::wstring> m_clients; const uint16_t m_port; const WinFwProtocol m_protocol; }; diff --git a/windows/winfw/src/winfw/winfw.cpp b/windows/winfw/src/winfw/winfw.cpp index 57610409c4..ae0f0791de 100644 --- a/windows/winfw/src/winfw/winfw.cpp +++ b/windows/winfw/src/winfw/winfw.cpp @@ -118,7 +118,7 @@ WINFW_API WinFw_InitializeBlocked( uint32_t timeout, const WinFwSettings *settings, - const WinFwEndpoint *allowedEndpoint, + const WinFwAllowedEndpoint *allowedEndpoint, MullvadLogSink logSink, void *logSinkContext ) @@ -233,7 +233,7 @@ WinFw_ApplyPolicyConnecting( const WinFwEndpoint *relay, const wchar_t *relayClient, const wchar_t *tunnelInterfaceAlias, - const WinFwEndpoint *allowedEndpoint + const WinFwAllowedEndpoint *allowedEndpoint ) { if (nullptr == g_fwContext) @@ -433,7 +433,7 @@ WINFW_POLICY_STATUS WINFW_API WinFw_ApplyPolicyBlocked( const WinFwSettings *settings, - const WinFwEndpoint *allowedEndpoint + const WinFwAllowedEndpoint *allowedEndpoint ) { if (nullptr == g_fwContext) diff --git a/windows/winfw/src/winfw/winfw.h b/windows/winfw/src/winfw/winfw.h index 5a34b7784b..4913ae7baf 100644 --- a/windows/winfw/src/winfw/winfw.h +++ b/windows/winfw/src/winfw/winfw.h @@ -19,8 +19,6 @@ // Structures /////////////////////////////////////////////////////////////////////////////// -#pragma pack(push, 1) - typedef struct tag_WinFwSettings { // Permit outbound DHCP requests and inbound DHCP responses on all interfaces. @@ -45,7 +43,17 @@ typedef struct tag_WinFwEndpoint } WinFwEndpoint; -#pragma pack(pop) +typedef struct tag_WinFwAllowedEndpoint +{ + uint32_t numClients; + + // A list of paths that are allowed to reach the given endpoint, + // even when traffic would otherwise be blocked. + const wchar_t **clients; + + WinFwEndpoint endpoint; +} +WinFwAllowedEndpoint; /////////////////////////////////////////////////////////////////////////////// // Functions @@ -88,7 +96,7 @@ WINFW_API WinFw_InitializeBlocked( uint32_t timeout, const WinFwSettings *settings, - const WinFwEndpoint *allowedEndpoint, + const WinFwAllowedEndpoint *allowedEndpoint, MullvadLogSink logSink, void *logSinkContext ); @@ -142,7 +150,7 @@ WinFw_ApplyPolicyConnecting( const WinFwEndpoint *relay, const wchar_t *relayClient, const wchar_t *tunnelInterfaceAlias, - const WinFwEndpoint *allowedEndpoint + const WinFwAllowedEndpoint *allowedEndpoint ); // @@ -189,7 +197,7 @@ WINFW_POLICY_STATUS WINFW_API WinFw_ApplyPolicyBlocked( const WinFwSettings *settings, - const WinFwEndpoint *allowedEndpoint + const WinFwAllowedEndpoint *allowedEndpoint ); // |
