diff options
| -rw-r--r-- | .travis.yml | 16 | ||||
| -rw-r--r-- | CHANGELOG.md | 3 | ||||
| -rw-r--r-- | Cargo.lock | 27 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/lan.rs | 55 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/mod.rs | 4 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 33 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 30 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings.rs | 34 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 2 | ||||
| -rw-r--r-- | talpid-core/src/firewall/macos/mod.rs | 59 | ||||
| -rw-r--r-- | talpid-core/src/firewall/mod.rs | 17 |
11 files changed, 242 insertions, 38 deletions
diff --git a/.travis.yml b/.travis.yml index 2ac2b4aad5..c62e82f632 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,10 +43,10 @@ matrix: script: *node_script - # Backend Linux + # Backend macOS - language: rust rust: nightly - os: linux + os: osx cache: cargo before_script: &rust_before_script @@ -65,36 +65,36 @@ matrix: - language: rust rust: stable - os: linux + os: osx cache: cargo before_script: *rust_before_script script: *rust_script - language: rust rust: beta - os: linux + os: osx cache: cargo before_script: *rust_before_script script: *rust_script - # Backend macOS + # Backend Linux - language: rust rust: stable - os: osx + os: linux cache: cargo before_script: *rust_before_script script: *rust_script - language: rust rust: beta - os: osx + os: linux cache: cargo before_script: *rust_before_script script: *rust_script - language: rust rust: nightly - os: osx + os: linux cache: cargo before_script: *rust_before_script script: *rust_script diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f1b338ff5..dbc53884df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added +- "Allow LAN" setting that configures if the app should allow communication to the LAN (private + networks: 10/8, 192.168/16 and 172.16/12) while the app is in the secured state. ## [2017.1-beta7] - 2017-12-13 diff --git a/Cargo.lock b/Cargo.lock index e420816f03..d12ddfdf55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,6 +98,24 @@ dependencies = [ ] [[package]] +name = "bindgen" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cexpr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clang-sys 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "which 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "bitflags" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -919,9 +937,9 @@ 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?rev=dae436f6ee4e3529fc995c5192b314f1cc8dccec#dae436f6ee4e3529fc995c5192b314f1cc8dccec" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bindgen 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)", "derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1206,7 +1224,7 @@ dependencies = [ "libc 0.2.34 (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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pfctl 0.1.0 (git+https://github.com/mullvad/pfctl-rs?rev=dae436f6ee4e3529fc995c5192b314f1cc8dccec)", + "pfctl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "system-configuration 0.1.0 (git+https://github.com/mullvad/system-configuration-rs)", "talpid-ipc 0.1.0", @@ -1508,6 +1526,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" "checksum base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c4a342b450b268e1be8036311e2c613d7f8a7ed31214dff1cc3b60852a3168d" "checksum bindgen 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57253399c086f4f29e57ffd3b5cdbc23a806a00292619351aa4cfa39cb49d4ea" +"checksum bindgen 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e0e57fd015c86d16b28d6409995045124a07665f36b38ca1992b1caf882fde6" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" @@ -1594,7 +1613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum parking_lot_core 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "12d20aac4f67aa75f681aded784bac91f910ba3f2af1812573cdcf687414e122" "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum pfctl 0.1.0 (git+https://github.com/mullvad/pfctl-rs?rev=dae436f6ee4e3529fc995c5192b314f1cc8dccec)" = "<none>" +"checksum pfctl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "296453377ce6b698986a6015bdf52341a247fe7db8796677d09030fda9a6252d" "checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" diff --git a/mullvad-cli/src/cmds/lan.rs b/mullvad-cli/src/cmds/lan.rs new file mode 100644 index 0000000000..29ab264de2 --- /dev/null +++ b/mullvad-cli/src/cmds/lan.rs @@ -0,0 +1,55 @@ +use {Command, Result}; +use clap; +use rpc; + +pub struct Lan; + +impl Command for Lan { + fn name(&self) -> &'static str { + "lan" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Control the allow local network sharing setting") + .setting(clap::AppSettings::SubcommandRequired) + .subcommand( + clap::SubCommand::with_name("set") + .about("Change allow LAN setting") + .arg( + clap::Arg::with_name("policy") + .required(true) + .possible_values(&["allow", "block"]), + ), + ) + .subcommand( + clap::SubCommand::with_name("get") + .about("Display the current local network sharing setting"), + ) + } + + fn run(&self, matches: &clap::ArgMatches) -> Result<()> { + if let Some(set_matches) = matches.subcommand_matches("set") { + let allow_lan = value_t_or_exit!(set_matches.value_of("policy"), String); + self.set(allow_lan == "allow") + } else if let Some(_matches) = matches.subcommand_matches("get") { + self.get() + } else { + unreachable!("No lan command given"); + } + } +} + +impl Lan { + fn set(&self, allow_lan: bool) -> Result<()> { + rpc::call("set_allow_lan", &[allow_lan]).map(|_: Option<()>| { + println!("Changed local network sharing setting"); + }) + } + + fn get(&self) -> Result<()> { + let allow_lan: bool = rpc::call("get_allow_lan", &[] as &[u8; 0])?; + println!("Local network sharing setting: {}", if allow_lan { "allow" } else { "block" }); + Ok(()) + } +} diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs index 1c10fa81c2..0caa5179e7 100644 --- a/mullvad-cli/src/cmds/mod.rs +++ b/mullvad-cli/src/cmds/mod.rs @@ -19,6 +19,9 @@ pub use self::relay::Relay; mod shutdown; pub use self::shutdown::Shutdown; +mod lan; +pub use self::lan::Lan; + /// Returns a map of all available subcommands with their name as key. pub fn get_commands() -> HashMap<&'static str, Box<Command>> { let commands: Vec<Box<Command>> = vec![ @@ -28,6 +31,7 @@ pub fn get_commands() -> HashMap<&'static str, Box<Command>> { Box::new(Disconnect), Box::new(Shutdown), Box::new(Relay), + Box::new(Lan), ]; let mut map = HashMap::new(); for cmd in commands { diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index f82a668940..59446eb5af 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -356,6 +356,8 @@ impl Daemon { SetAccount(tx, account_token) => self.on_set_account(tx, account_token), GetAccount(tx) => Ok(self.on_get_account(tx)), UpdateRelaySettings(tx, update) => self.on_update_relay_settings(tx, update), + SetAllowLan(tx, allow_lan) => self.on_set_allow_lan(tx, allow_lan), + GetAllowLan(tx) => Ok(self.on_get_allow_lan(tx)), GetRelaySettings(tx) => Ok(self.on_get_relay_settings(tx)), Shutdown => self.handle_trigger_shutdown_event(), } @@ -473,6 +475,24 @@ impl Daemon { Self::oneshot_send(tx, self.settings.get_relay_settings(), "relay settings") } + fn on_set_allow_lan(&mut self, tx: OneshotSender<()>, allow_lan: bool) -> Result<()> { + let save_result = self.settings.set_allow_lan(allow_lan); + match save_result.chain_err(|| "Unable to save settings") { + Ok(settings_changed) => { + if settings_changed && self.target_state == TargetState::Secured { + self.set_security_policy()?; + } + Self::oneshot_send(tx, (), "set_allow_lan response"); + } + Err(e) => error!("{}", e.display_chain()), + } + Ok(()) + } + + fn on_get_allow_lan(&self, tx: OneshotSender<bool>) { + Self::oneshot_send(tx, self.settings.get_allow_lan(), "allow lan") + } + fn oneshot_send<T>(tx: OneshotSender<T>, t: T, msg: &'static str) { if let Err(_) = tx.send(t) { warn!("Unable to send {} to management interface client", msg); @@ -669,10 +689,15 @@ impl Daemon { fn set_security_policy(&mut self) -> Result<()> { let policy = match (self.tunnel_endpoint, self.tunnel_metadata.as_ref()) { - (Some(relay), None) => SecurityPolicy::Connecting(relay.to_endpoint()), - (Some(relay), Some(tunnel_metadata)) => { - SecurityPolicy::Connected(relay.to_endpoint(), tunnel_metadata.clone()) - } + (Some(relay), None) => SecurityPolicy::Connecting { + relay_endpoint: relay.to_endpoint(), + allow_lan: self.settings.get_allow_lan(), + }, + (Some(relay), Some(tunnel_metadata)) => SecurityPolicy::Connected { + relay_endpoint: relay.to_endpoint(), + tunnel: tunnel_metadata.clone(), + allow_lan: self.settings.get_allow_lan(), + }, _ => bail!(ErrorKind::InvalidState), }; debug!("Set security policy: {:?}", policy); diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 58f62b1ede..5bb79e8d14 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -73,6 +73,14 @@ build_rpc_trait! { Self::Metadata ) -> BoxFuture<RelaySettings, Error>; + /// Set if the client should allow communication with the LAN while in secured state. + #[rpc(meta, name = "set_allow_lan")] + fn set_allow_lan(&self, Self::Metadata, bool) -> BoxFuture<(), Error>; + + /// Get if the client should allow communication with the LAN while in secured state. + #[rpc(meta, name = "get_allow_lan")] + fn get_allow_lan(&self, Self::Metadata) -> BoxFuture<bool, Error>; + /// Set if the client should automatically establish a tunnel on start or not. #[rpc(meta, name = "set_autoconnect")] fn set_autoconnect(&self, Self::Metadata, bool) -> BoxFuture<(), Error>; @@ -160,6 +168,10 @@ pub enum TunnelCommand { UpdateRelaySettings(OneshotSender<()>, RelaySettingsUpdate), /// Read the constraints put on the tunnel and relay GetRelaySettings(OneshotSender<RelaySettings>), + /// Setting if communication with LAN networks should be possible. + SetAllowLan(OneshotSender<()>, bool), + /// Request the current allow LAN setting. + GetAllowLan(OneshotSender<bool>), /// Makes the daemon exit the main loop and quit. Shutdown, } @@ -448,6 +460,24 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem Box::new(future) } + fn set_allow_lan(&self, meta: Self::Metadata, allow_lan: bool) -> BoxFuture<(), Error> { + trace!("allow_lan"); + try_future!(self.check_auth(&meta)); + let (tx, rx) = sync::oneshot::channel(); + let future = self.send_command_to_daemon(TunnelCommand::SetAllowLan(tx, allow_lan)) + .and_then(|_| rx.map_err(|_| Error::internal_error())); + Box::new(future) + } + + fn get_allow_lan(&self, meta: Self::Metadata) -> BoxFuture<bool, Error> { + trace!("get_allow_lan"); + try_future!(self.check_auth(&meta)); + let (tx, rx) = sync::oneshot::channel(); + let future = self.send_command_to_daemon(TunnelCommand::GetAllowLan(tx)) + .and_then(|_| rx.map_err(|_| Error::internal_error())); + Box::new(future) + } + fn set_autoconnect(&self, meta: Self::Metadata, _autoconnect: bool) -> BoxFuture<(), Error> { trace!("set_autoconnect"); try_future!(self.check_auth(&meta)); diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs index 9d2c77f991..5735c687c9 100644 --- a/mullvad-daemon/src/settings.rs +++ b/mullvad-daemon/src/settings.rs @@ -35,22 +35,23 @@ static SETTINGS_FILE: &str = "settings.json"; pub struct Settings { account_token: Option<String>, relay_settings: RelaySettings, + /// If the app should allow communication with private (LAN) networks. + allow_lan: bool, } impl Default for Settings { fn default() -> Self { - DEFAULT_SETTINGS + Settings { + account_token: None, + relay_settings: RelaySettings::Normal(RelayConstraints { + location: Constraint::Any, + tunnel: Constraint::Any, + }), + allow_lan: false, + } } } -const DEFAULT_SETTINGS: Settings = Settings { - account_token: None, - relay_settings: RelaySettings::Normal(RelayConstraints { - location: Constraint::Any, - tunnel: Constraint::Any, - }), -}; - impl Settings { /// Loads user settings from file. If no file is present it returns the defaults. pub fn load() -> Result<Settings> { @@ -65,7 +66,7 @@ impl Settings { "No settings file at {}, using defaults", settings_path.to_string_lossy() ); - Ok(DEFAULT_SETTINGS) + Ok(Settings::default()) } Err(e) => Err(e).chain_err(|| ErrorKind::ReadError(settings_path)), } @@ -135,4 +136,17 @@ impl Settings { Ok(false) } } + + pub fn get_allow_lan(&self) -> bool { + self.allow_lan + } + + pub fn set_allow_lan(&mut self, allow_lan: bool) -> Result<bool> { + if allow_lan != self.allow_lan { + self.allow_lan = allow_lan; + self.save().map(|_| true) + } else { + Ok(false) + } + } } diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index c52005a5ff..511647883b 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -23,7 +23,7 @@ talpid-types = { path = "../talpid-types" } libc = "0.2.20" [target.'cfg(target_os = "macos")'.dependencies] -pfctl = { git = "https://github.com/mullvad/pfctl-rs", rev = "dae436f6ee4e3529fc995c5192b314f1cc8dccec" } +pfctl = "0.1" system-configuration = { git = "https://github.com/mullvad/system-configuration-rs", version = "0.1.0" } core-foundation = "0.4.6" tokio-core = "0.1" diff --git a/talpid-core/src/firewall/macos/mod.rs b/talpid-core/src/firewall/macos/mod.rs index 9065a607d6..a763f1c4d8 100644 --- a/talpid-core/src/firewall/macos/mod.rs +++ b/talpid-core/src/firewall/macos/mod.rs @@ -1,6 +1,7 @@ extern crate pfctl; extern crate tokio_core; +use self::pfctl::ipnetwork::{IpNetwork, Ipv4Network}; use super::{Firewall, SecurityPolicy}; use std::net::Ipv4Addr; @@ -64,9 +65,7 @@ impl PacketFilter { new_filter_rules.append(&mut Self::get_allow_loopback_rules()?); new_filter_rules.append(&mut Self::get_allow_dhcp_rules()?); - - let mut policy_filter_rules = self.get_policy_specific_rules(policy)?; - new_filter_rules.append(&mut policy_filter_rules); + new_filter_rules.append(&mut self.get_policy_specific_rules(policy)?); let drop_all_rule = pfctl::FilterRuleBuilder::default() .action(pfctl::FilterRuleAction::Drop) @@ -84,10 +83,21 @@ impl PacketFilter { policy: SecurityPolicy, ) -> Result<Vec<pfctl::FilterRule>> { match policy { - SecurityPolicy::Connecting(relay_endpoint) => { - Ok(vec![Self::get_allow_relay_rule(relay_endpoint)?]) + SecurityPolicy::Connecting { + relay_endpoint, + allow_lan, + } => { + let mut rules = vec![Self::get_allow_relay_rule(relay_endpoint)?]; + if allow_lan { + rules.append(&mut Self::get_allow_lan_rules()?); + } + Ok(rules) } - SecurityPolicy::Connected(relay_endpoint, tunnel) => { + SecurityPolicy::Connected { + relay_endpoint, + tunnel, + allow_lan, + } => { self.dns_monitor.set_dns(vec![tunnel.gateway.to_string()])?; let allow_tcp_dns_to_relay_rule = pfctl::FilterRuleBuilder::default() @@ -121,14 +131,19 @@ impl PacketFilter { .to(pfctl::Port::from(53)) .build()?; - Ok(vec![ + let mut rules = vec![ allow_tcp_dns_to_relay_rule, allow_udp_dns_to_relay_rule, block_tcp_dns_rule, block_udp_dns_rule, Self::get_allow_relay_rule(relay_endpoint)?, Self::get_allow_tunnel_rule(tunnel.interface.as_str())?, - ]) + ]; + + if allow_lan { + rules.append(&mut Self::get_allow_lan_rules()?); + } + Ok(rules) } } } @@ -167,6 +182,34 @@ impl PacketFilter { Ok(vec![lo0_rule]) } + fn get_allow_lan_rules() -> Result<Vec<pfctl::FilterRule>> { + let private_nets = [ + Ipv4Network::new(Ipv4Addr::new(10, 0, 0, 0), 8).unwrap(), + Ipv4Network::new(Ipv4Addr::new(172, 16, 0, 0), 12).unwrap(), + Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 16).unwrap(), + ]; + let multicast_net = Ipv4Network::new(Ipv4Addr::new(224, 0, 0, 0), 24).unwrap(); + let mut rules = vec![]; + for net in &private_nets { + let mut rule_builder = pfctl::FilterRuleBuilder::default(); + rule_builder + .action(pfctl::FilterRuleAction::Pass) + .keep_state(pfctl::StatePolicy::Keep) + .quick(true) + .af(pfctl::AddrFamily::Ipv4) + .from(pfctl::Ip::from(IpNetwork::V4(*net))); + let allow_net = rule_builder + .to(pfctl::Ip::from(IpNetwork::V4(*net))) + .build()?; + let allow_multicast = rule_builder + .to(pfctl::Ip::from(IpNetwork::V4(multicast_net))) + .build()?; + rules.push(allow_net); + rules.push(allow_multicast); + } + Ok(rules) + } + fn get_allow_dhcp_rules() -> Result<Vec<pfctl::FilterRule>> { let broadcast_address = Ipv4Addr::new(255, 255, 255, 255); let server_port = pfctl::Port::from(67); diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs index c3115339f1..c0f839817d 100644 --- a/talpid-core/src/firewall/mod.rs +++ b/talpid-core/src/firewall/mod.rs @@ -18,7 +18,6 @@ pub mod windows; #[cfg(windows)] use self::windows as imp; - error_chain!{ errors { /// Initialization error @@ -36,10 +35,22 @@ error_chain!{ #[derive(Debug, Clone, Eq, PartialEq)] pub enum SecurityPolicy { /// Allow traffic only to relay server - Connecting(Endpoint), + Connecting { + /// The relay endpoint that should be allowed. + relay_endpoint: Endpoint, + /// Flag setting if communication with LAN networks should be possible. + allow_lan: bool, + }, /// Allow traffic only to relay server and over tunnel interface - Connected(Endpoint, ::tunnel::TunnelMetadata), + Connected { + /// The relay endpoint that should be allowed. + relay_endpoint: Endpoint, + /// Metadata about the tunnel and tunnel interface. + tunnel: ::tunnel::TunnelMetadata, + /// Flag setting if communication with LAN networks should be possible. + allow_lan: bool, + }, } /// Abstract firewall interaction trait |
