diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2017-08-17 19:35:00 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2017-08-17 19:35:00 +0100 |
| commit | 3416c0c69d33c3247a7e5687dc6778a18d2a693f (patch) | |
| tree | d8dd11c5947b3db9cb663343025e11e37f48732b /talpid-core/src | |
| parent | d49fb3baa06f482f3e839de7a1cda989b884e8f2 (diff) | |
| parent | ec89d6571d68c07db9b9380aaf4c7bb21ca1b26a (diff) | |
| download | mullvadvpn-3416c0c69d33c3247a7e5687dc6778a18d2a693f.tar.xz mullvadvpn-3416c0c69d33c3247a7e5687dc6778a18d2a693f.zip | |
Merge branch 'spm'
Diffstat (limited to 'talpid-core/src')
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 150 | ||||
| -rw-r--r-- | talpid-core/src/firewall/mod.rs | 67 | ||||
| -rw-r--r-- | talpid-core/src/firewall/unix.rs | 21 | ||||
| -rw-r--r-- | talpid-core/src/firewall/windows.rs | 21 | ||||
| -rw-r--r-- | talpid-core/src/lib.rs | 6 |
5 files changed, 265 insertions, 0 deletions
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs new file mode 100644 index 0000000000..3bf6bc8db6 --- /dev/null +++ b/talpid-core/src/firewall/macos.rs @@ -0,0 +1,150 @@ +use super::{Firewall, SecurityPolicy}; +use net; +use pfctl; + +// alias used to instantiate firewall implementation +pub type ConcreteFirewall = PacketFilter; +pub use pfctl::{Error, ErrorKind, Result}; + +const ANCHOR_NAME: &'static str = "talpid_core"; + +impl From<net::Endpoint> for pfctl::Endpoint { + fn from(endpoint: net::Endpoint) -> Self { + pfctl::Endpoint( + pfctl::Ip::from(endpoint.address.ip()), + pfctl::Port::from(endpoint.address.port()), + ) + } +} + +impl From<net::Endpoint> for pfctl::Proto { + fn from(endpoint: net::Endpoint) -> Self { + match endpoint.protocol { + net::TransportProtocol::Udp => pfctl::Proto::Udp, + net::TransportProtocol::Tcp => pfctl::Proto::Tcp, + } + } +} + +pub struct PacketFilter { + pf: pfctl::PfCtl, + pf_was_enabled: Option<bool>, +} + +impl Firewall<Error> for PacketFilter { + fn new() -> Result<Self> { + Ok( + PacketFilter { + pf: pfctl::PfCtl::new()?, + pf_was_enabled: None, + }, + ) + } + + fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> { + self.enable()?; + self.add_anchor()?; + self.set_rules(policy) + } + + fn reset_policy(&mut self) -> Result<()> { + vec![ + self.remove_rules(), + self.remove_anchor(), + self.restore_state(), + ] + .into_iter() + .collect::<Result<Vec<_>>>() + .map(|_| ()) + } +} + +impl PacketFilter { + fn set_rules(&mut self, policy: SecurityPolicy) -> Result<()> { + let drop_all_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::RuleAction::Drop) + .quick(true) + .build()?; + let allow_dns_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::RuleAction::Pass) + .direction(pfctl::Direction::Out) + .quick(true) + .to(pfctl::Port::One(53, pfctl::PortUnaryModifier::Equal)) + .keep_state(pfctl::StatePolicy::Keep) + .build()?; + let mut new_rules = self.get_loopback_rules()?; + + match policy { + SecurityPolicy::Connecting(relay_endpoint) => { + new_rules.push(Self::get_relay_rule(relay_endpoint)?); + } + SecurityPolicy::Connected(relay_endpoint, tunnel_interface) => { + new_rules.push(Self::get_relay_rule(relay_endpoint)?); + new_rules.push(Self::get_tunnel_rule(tunnel_interface)?); + } + }; + + new_rules.push(allow_dns_rule); + new_rules.push(drop_all_rule); + + self.pf.set_rules(ANCHOR_NAME, &new_rules) + } + + fn get_relay_rule(relay_endpoint: net::Endpoint) -> Result<pfctl::FilterRule> { + pfctl::FilterRuleBuilder::default() + .action(pfctl::RuleAction::Pass) + .direction(pfctl::Direction::Out) + .to(relay_endpoint) + .proto(relay_endpoint) + .keep_state(pfctl::StatePolicy::Keep) + .quick(true) + .build() + } + + fn get_tunnel_rule(tunnel_interface: String) -> Result<pfctl::FilterRule> { + pfctl::FilterRuleBuilder::default() + .action(pfctl::RuleAction::Pass) + .interface(tunnel_interface) + .keep_state(pfctl::StatePolicy::Keep) + .quick(true) + .build() + } + + fn get_loopback_rules(&self) -> Result<Vec<pfctl::FilterRule>> { + let lo0_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::RuleAction::Pass) + .interface("lo0") + .keep_state(pfctl::StatePolicy::Keep) + .quick(true) + .build()?; + Ok(vec![lo0_rule]) + } + + fn remove_rules(&mut self) -> Result<()> { + // remove_anchor() does not deactivate active rules + self.pf.flush_rules(ANCHOR_NAME, pfctl::RulesetKind::Filter) + } + + fn enable(&mut self) -> Result<()> { + if self.pf_was_enabled.is_none() { + self.pf_was_enabled = Some(self.pf.is_enabled()?); + } + self.pf.try_enable() + } + + fn restore_state(&mut self) -> Result<()> { + match self.pf_was_enabled.take() { + Some(true) => self.pf.try_enable(), + Some(false) => self.pf.try_disable(), + None => Ok(()), + } + } + + fn add_anchor(&mut self) -> Result<()> { + self.pf.try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter) + } + + fn remove_anchor(&mut self) -> Result<()> { + self.pf.try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter) + } +} diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs new file mode 100644 index 0000000000..16b8139453 --- /dev/null +++ b/talpid-core/src/firewall/mod.rs @@ -0,0 +1,67 @@ +use net::Endpoint; + +#[cfg(target_os = "macos")] +#[path = "macos.rs"] +mod imp; + +#[cfg(all(unix, not(target_os = "macos")))] +#[path = "unix.rs"] +mod imp; + +#[cfg(windows)] +#[path = "windows.rs"] +mod imp; + +error_chain!{ + errors { + /// Initialization error + FirewallInitError { + description("Failed to initialize firewall") + } + /// Firewall configuration error + FirewallConfigurationError { + description("Failed to configure firewall") + } + } +} + +/// A enum that describes firewall rules strategy +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum SecurityPolicy { + /// Allow traffic only to relay server + Connecting(Endpoint), + + /// Allow traffic only to relay server and over tunnel interface + Connected(Endpoint, String), +} + +/// Abstract firewall interaction trait +pub trait Firewall<E: ::std::error::Error> { + /// Create new instance of Firewall + fn new() -> ::std::result::Result<Self, E> where Self: Sized; + + /// Enable firewall and set firewall rules based on SecurityPolicy + fn apply_policy(&mut self, policy: SecurityPolicy) -> ::std::result::Result<(), E>; + + /// Remove firewall rules applied by active SecurityPolicy and + /// revert firewall to its original state + fn reset_policy(&mut self) -> ::std::result::Result<(), E>; +} + +/// An abstraction around platform specific firewall implementation +pub struct FirewallProxy(Box<Firewall<imp::Error>>); + +impl Firewall<Error> for FirewallProxy { + fn new() -> Result<Self> { + let firewall = imp::ConcreteFirewall::new().chain_err(|| ErrorKind::FirewallInitError)?; + Ok(FirewallProxy(Box::new(firewall) as Box<Firewall<_>>)) + } + + fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> { + self.0.apply_policy(policy).chain_err(|| ErrorKind::FirewallConfigurationError) + } + + fn reset_policy(&mut self) -> Result<()> { + self.0.reset_policy().chain_err(|| ErrorKind::FirewallConfigurationError) + } +} diff --git a/talpid-core/src/firewall/unix.rs b/talpid-core/src/firewall/unix.rs new file mode 100644 index 0000000000..7550c3c051 --- /dev/null +++ b/talpid-core/src/firewall/unix.rs @@ -0,0 +1,21 @@ +use super::{Firewall, SecurityPolicy}; + +// alias used to instantiate firewall implementation +pub type ConcreteFirewall = Netfilter; + +error_chain!{} + +pub struct Netfilter; +impl Firewall<Error> for Netfilter { + fn new() -> Result<Self> { + Ok(Netfilter) + } + + fn apply_policy(&mut self, _policy: SecurityPolicy) -> Result<()> { + Ok(()) + } + + fn reset_policy(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs new file mode 100644 index 0000000000..3405ba12c0 --- /dev/null +++ b/talpid-core/src/firewall/windows.rs @@ -0,0 +1,21 @@ +use super::{Firewall, SecurityPolicy}; + +// alias used to instantiate firewall implementation +pub type ConcreteFirewall = WindowsFirewall; + +error_chain!{} + +pub struct WindowsFirewall; +impl Firewall<Error> for WindowsFirewall { + fn new() -> Result<Self> { + Ok(WindowsFirewall) + } + + fn apply_policy(&mut self, _policy: SecurityPolicy) -> Result<()> { + Ok(()) + } + + fn reset_policy(&mut self) -> Result<()> { + Ok(()) + } +} diff --git a/talpid-core/src/lib.rs b/talpid-core/src/lib.rs index 29ab0377e5..cbdc08886e 100644 --- a/talpid-core/src/lib.rs +++ b/talpid-core/src/lib.rs @@ -23,6 +23,9 @@ extern crate jsonrpc_macros; extern crate talpid_ipc; extern crate openvpn_plugin; +#[cfg(target_os = "macos")] +extern crate pfctl; + /// Working with processes. pub mod process; @@ -34,3 +37,6 @@ pub mod tunnel; /// Abstractions and extra features on `std::mpsc` pub mod mpsc; + +/// Abstractions over different firewalls +pub mod firewall; |
