diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-09-20 22:23:55 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-09-21 18:52:29 +0200 |
| commit | 98ab8d1993c2a2d8c1211996f5ca7ed0002d3e32 (patch) | |
| tree | 82bceaefd28c54a167296332d66cb5c5accce971 | |
| parent | 1c6382dc9cbcf277501d7269f762c0a0c9d344b1 (diff) | |
| download | mullvadvpn-98ab8d1993c2a2d8c1211996f5ca7ed0002d3e32.tar.xz mullvadvpn-98ab8d1993c2a2d8c1211996f5ca7ed0002d3e32.zip | |
Upgrade pfctl and add DNS proxy
| -rw-r--r-- | Cargo.lock | 8 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 6 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 4 | ||||
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 157 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 2 |
5 files changed, 144 insertions, 33 deletions
diff --git a/Cargo.lock b/Cargo.lock index 1a42817892..7dff8bc43a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -866,7 +866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pfctl" version = "0.1.0" -source = "git+https://github.com/mullvad/pfctl-rs.git#17c90bca15244ae0a28fa54339c074ff306206d0" +source = "git+ssh://git@github.com/mullvad/pfctl-rs.git#35ca17308409ffab07e3a2a7c9c9ba3c81f8ee5e" dependencies = [ "bindgen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", "derive_builder 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1196,9 +1196,11 @@ dependencies = [ "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "openvpn-plugin 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pfctl 0.1.0 (git+https://github.com/mullvad/pfctl-rs.git)", + "pfctl 0.1.0 (git+ssh://git@github.com/mullvad/pfctl-rs.git)", + "socket-relay 0.1.0", "talpid-ipc 0.1.0", "talpid-types 0.1.0", + "tokio-core 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1587,7 +1589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum parking_lot_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ad2c4d148942b3560034785bf19df586ebba53351e8c78f84984147d5795eef" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356" -"checksum pfctl 0.1.0 (git+https://github.com/mullvad/pfctl-rs.git)" = "<none>" +"checksum pfctl 0.1.0 (git+ssh://git@github.com/mullvad/pfctl-rs.git)" = "<none>" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum quasi 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18c45c4854d6d1cf5d531db97c75880feb91c958b0720f4ec1057135fec358b3" "checksum quasi_codegen 0.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b9e25fa23c044c1803f43ca59c98dac608976dd04ce799411edd58ece776d4" diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index 2eea1a704b..017c428fc2 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -63,7 +63,7 @@ use std::thread; use talpid_core::firewall::{Firewall, FirewallProxy, SecurityPolicy}; use talpid_core::mpsc::IntoSender; -use talpid_core::tunnel::{self, TunnelEvent, TunnelMonitor, TunnelMetadata}; +use talpid_core::tunnel::{self, TunnelEvent, TunnelMetadata, TunnelMonitor}; use talpid_types::net::{Endpoint, TransportProtocol}; error_chain!{ @@ -569,7 +569,9 @@ impl Daemon { fn set_security_policy(&mut self) -> Result<()> { let policy = match (self.relay_endpoint, self.tunnel_metadata.as_ref()) { (Some(relay), None) => SecurityPolicy::Connecting(relay), - (Some(relay), Some(tunnel_metadata)) => SecurityPolicy::Connected(relay, tunnel_metadata.clone()), + (Some(relay), Some(tunnel_metadata)) => { + SecurityPolicy::Connected(relay, tunnel_metadata.clone()) + } _ => bail!(ErrorKind::InvalidState), }; debug!("Set security policy: {:?}", policy); diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index ba70f49071..bf21f63001 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -22,7 +22,9 @@ talpid-types = { path = "../talpid-types" } libc = "0.2.20" [target.'cfg(target_os = "macos")'.dependencies] -pfctl = { git = "https://github.com/mullvad/pfctl-rs.git" } +pfctl = { git = "ssh://git@github.com/mullvad/pfctl-rs.git" } +socket-relay = { path = "../socket-relay" } +tokio-core = "0.1" [dev-dependencies] assert_matches = "1.0" diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs index 4b9e05f6b0..70bb2cfb20 100644 --- a/talpid-core/src/firewall/macos.rs +++ b/talpid-core/src/firewall/macos.rs @@ -1,17 +1,27 @@ +extern crate socket_relay; +extern crate tokio_core; + use super::{Firewall, SecurityPolicy}; use pfctl; -use std::net::Ipv4Addr; + +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::mpsc; +use std::thread; + +use self::socket_relay::udp::{Relay, RelayCloseHandle}; use talpid_types::net; +use tunnel::TunnelMetadata; // alias used to instantiate firewall implementation pub type ConcreteFirewall = PacketFilter; -pub use pfctl::{Error, ErrorKind, Result}; +pub use pfctl::{Error, ErrorKind, Result, ResultExt}; const ANCHOR_NAME: &'static str = "talpid_core"; pub struct PacketFilter { pf: pfctl::PfCtl, pf_was_enabled: Option<bool>, + dns_proxy_close_handle: Option<RelayCloseHandle>, } impl Firewall<Error> for PacketFilter { @@ -19,6 +29,7 @@ impl Firewall<Error> for PacketFilter { Ok(PacketFilter { pf: pfctl::PfCtl::new()?, pf_was_enabled: None, + dns_proxy_close_handle: None, }) } @@ -29,6 +40,7 @@ impl Firewall<Error> for PacketFilter { } fn reset_policy(&mut self) -> Result<()> { + self.stop_dns_proxy(); vec![ self.remove_rules(), self.remove_anchor(), @@ -41,35 +53,71 @@ impl Firewall<Error> for PacketFilter { impl PacketFilter { fn set_rules(&mut self, policy: SecurityPolicy) -> Result<()> { - let drop_all_rule = pfctl::FilterRuleBuilder::default() - .action(pfctl::FilterRuleAction::Drop) - .quick(true) - .build()?; - let allow_dns_rule = pfctl::FilterRuleBuilder::default() - .action(pfctl::FilterRuleAction::Pass) - .direction(pfctl::Direction::Out) - .quick(true) - .to(pfctl::Port::One(53, pfctl::PortUnaryModifier::Equal)) - .keep_state(pfctl::StatePolicy::Keep) - .tcp_flags(Self::get_tcp_flags()) - .build()?; - let mut new_rules = self.get_loopback_rules()?; + let mut new_filter_rules = self.get_loopback_rules()?; + let mut new_redirect_rules = vec![]; match policy { SecurityPolicy::Connecting(relay_endpoint) => { - new_rules.push(Self::get_relay_rule(relay_endpoint)?); + self.stop_dns_proxy(); + new_filter_rules.push(Self::get_relay_rule(relay_endpoint)?); } - SecurityPolicy::Connected(relay_endpoint, tunnel_metadata) => { - new_rules.push(Self::get_relay_rule(relay_endpoint)?); - new_rules.push(Self::get_tunnel_rule(tunnel_metadata.interface)?); + SecurityPolicy::Connected(relay_endpoint, tunnel) => { + let dns_proxy_listen_addr = self.start_dns_proxy(&tunnel)?; + + let allow_dns_to_relay_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Pass) + .direction(pfctl::Direction::Out) + .quick(true) + .interface(&tunnel.interface) + .proto(pfctl::Proto::Udp) + .to(pfctl::Endpoint::new(tunnel.gateway, 53)) + .build()?; + let reroute_dns_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Pass) + .direction(pfctl::Direction::Out) + .quick(true) + .route(pfctl::Route::route_to(pfctl::Interface::from("lo0"))) + .proto(pfctl::Proto::Udp) + .to(pfctl::Port::from(53)) + .build()?; + let block_all_other_dns_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Drop) + .direction(pfctl::Direction::Out) + .quick(true) + .proto(pfctl::Proto::Tcp) + .to(pfctl::Port::from(53)) + .build()?; + + new_filter_rules.push(allow_dns_to_relay_rule); + new_filter_rules.push(reroute_dns_rule); + new_filter_rules.push(block_all_other_dns_rule); + + let dns_redirect_rule = pfctl::RedirectRuleBuilder::default() + .action(pfctl::RedirectRuleAction::Redirect) + .interface("lo0") + .proto(pfctl::Proto::Udp) + .to(pfctl::Port::from(53)) + .redirect_to(dns_proxy_listen_addr) + .build()?; + new_redirect_rules.push(dns_redirect_rule); + + new_filter_rules.push(Self::get_relay_rule(relay_endpoint)?); + new_filter_rules.push(Self::get_tunnel_rule(tunnel.interface.as_str())?); } }; - new_rules.push(allow_dns_rule); - new_rules.append(&mut Self::get_dhcp_rules()?); - new_rules.push(drop_all_rule); + new_filter_rules.append(&mut Self::get_dhcp_rules()?); + + let drop_all_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Drop) + .quick(true) + .build()?; + new_filter_rules.push(drop_all_rule); - self.pf.set_rules(ANCHOR_NAME, &new_rules) + let mut anchor_change = pfctl::AnchorChange::new(); + anchor_change.set_filter_rules(new_filter_rules); + anchor_change.set_redirect_rules(new_redirect_rules); + self.pf.set_rules(ANCHOR_NAME, anchor_change) } fn get_relay_rule(relay_endpoint: net::Endpoint) -> Result<pfctl::FilterRule> { @@ -86,7 +134,7 @@ impl PacketFilter { .build() } - fn get_tunnel_rule(tunnel_interface: String) -> Result<pfctl::FilterRule> { + fn get_tunnel_rule(tunnel_interface: &str) -> Result<pfctl::FilterRule> { pfctl::FilterRuleBuilder::default() .action(pfctl::FilterRuleAction::Pass) .interface(tunnel_interface) @@ -158,12 +206,29 @@ impl PacketFilter { fn add_anchor(&mut self) -> Result<()> { self.pf - .try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter) + .try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter)?; + self.pf + .try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Redirect) } fn remove_anchor(&mut self) -> Result<()> { self.pf - .try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter) + .try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter)?; + self.pf + .try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Redirect) + } + + fn start_dns_proxy(&mut self, tunnel: &TunnelMetadata) -> Result<SocketAddr> { + self.stop_dns_proxy(); + let (listen_addr, close_handle) = spawn_dns_proxy(tunnel.ip, tunnel.gateway)?; + self.dns_proxy_close_handle = Some(close_handle); + Ok(listen_addr) + } + + fn stop_dns_proxy(&mut self) { + if let Some(close_handle) = self.dns_proxy_close_handle.take() { + close_handle.close(); + } } } @@ -173,3 +238,43 @@ fn as_pfctl_proto(protocol: net::TransportProtocol) -> pfctl::Proto { net::TransportProtocol::Tcp => pfctl::Proto::Tcp, } } + +fn spawn_dns_proxy( + tunnel_ip: Ipv4Addr, + tunnel_gateway: Ipv4Addr, +) -> Result<(SocketAddr, RelayCloseHandle)> { + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + match spawn_dns_proxy_helper(tunnel_ip, tunnel_gateway) { + Ok((mut core, relay)) => { + tx.send(Ok((relay.listen_addr(), relay.close_handle()))) + .unwrap(); + match core.run(relay) { + Err(e) => error!("DNS proxy died with an error: {}", e), + Ok(_) => info!("DNS proxy exiting"), + } + } + Err(e) => { + tx.send(Err(e)).unwrap(); + } + } + }); + rx.recv().unwrap() +} + +fn spawn_dns_proxy_helper( + tunnel_ip: Ipv4Addr, + tunnel_gateway: Ipv4Addr, +) -> Result<(tokio_core::reactor::Core, Relay)> { + let core = tokio_core::reactor::Core::new().chain_err(|| "Unable to init Tokio event loop")?; + + let relay = Relay::new( + "127.0.0.1:0".parse().unwrap(), + IpAddr::V4(tunnel_ip), + SocketAddr::from((tunnel_gateway, 53)), + core.handle(), + ).chain_err(|| "Unable to create DNS proxy socket relay")?; + info!("DNS proxy listening on {}", relay.listen_addr()); + + Ok((core, relay)) +} diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 0ed7e86960..4ee7b24cee 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -9,8 +9,8 @@ use std::env; use std::ffi::{OsStr, OsString}; use std::fs; use std::io::{self, Write}; -use std::path::{Path, PathBuf}; use std::net::Ipv4Addr; +use std::path::{Path, PathBuf}; use talpid_types::net; |
