summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2017-08-15 22:18:06 +0100
committerAndrej Mihajlov <and@mullvad.net>2017-08-17 19:09:02 +0100
commit89b685bde4b30523987986bcaf046d196b7d74a5 (patch)
treef786d92626ab1474a6d80f3b110fab2faf9204a2 /talpid-core/src
parentad59695517f40d4c3c90a222212232d309d37edc (diff)
downloadmullvadvpn-89b685bde4b30523987986bcaf046d196b7d74a5.tar.xz
mullvadvpn-89b685bde4b30523987986bcaf046d196b7d74a5.zip
Add macOS firewall implementation
Diffstat (limited to 'talpid-core/src')
-rw-r--r--talpid-core/src/firewall/macos.rs141
-rw-r--r--talpid-core/src/lib.rs3
2 files changed, 138 insertions, 6 deletions
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs
index d55150a2a1..3bf6bc8db6 100644
--- a/talpid-core/src/firewall/macos.rs
+++ b/talpid-core/src/firewall/macos.rs
@@ -1,21 +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};
-error_chain!{}
+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>,
+}
-pub struct PacketFilter;
impl Firewall<Error> for PacketFilter {
fn new() -> Result<Self> {
- Ok(PacketFilter)
+ Ok(
+ PacketFilter {
+ pf: pfctl::PfCtl::new()?,
+ pf_was_enabled: None,
+ },
+ )
}
- fn apply_policy(&mut self, _policy: SecurityPolicy) -> Result<()> {
- Ok(())
+ fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> {
+ self.enable()?;
+ self.add_anchor()?;
+ self.set_rules(policy)
}
fn reset_policy(&mut self) -> Result<()> {
- Ok(())
+ 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/lib.rs b/talpid-core/src/lib.rs
index 9c78ef02e8..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;