summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2018-04-03 18:17:05 +0100
committerEmīls Piņķis <emils@mullvad.net>2018-04-13 11:18:46 +0100
commitddac7bb327d7109e6922a545a838a37f14dd6701 (patch)
tree7f6f470f2e954854673857acad2dcf93d6d0705b
parent2397bab569521dcd6d4b9670cd546e29f461a85b (diff)
downloadmullvadvpn-ddac7bb327d7109e6922a545a838a37f14dd6701.tar.xz
mullvadvpn-ddac7bb327d7109e6922a545a838a37f14dd6701.zip
Add windows firewall implementation
-rw-r--r--CHANGELOG.md1
-rw-r--r--Cargo.lock7
-rw-r--r--talpid-core/Cargo.toml4
-rw-r--r--talpid-core/src/firewall/windows.rs21
-rw-r--r--talpid-core/src/firewall/windows/mod.rs241
5 files changed, 253 insertions, 21 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a5a61ddbbf..d469e341bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,7 @@ Line wrap the file at 100 chars. Th
paths and window captions etc.
- Bundle an IP address with the app and introduce a disk cache fallback method for when the DNS
resolution of the Mullvad API server hostname fails.
+- Manage firewall rules on windows depending on connection state.
### Fixed
- Fix a bug in account input field that advanced the cursor to the end regardless its prior
diff --git a/Cargo.lock b/Cargo.lock
index 0152eb979c..c0043253e7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1166,6 +1166,7 @@ dependencies = [
"talpid-types 0.1.0",
"tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "widestring 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1411,6 +1412,11 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "widestring"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1629,6 +1635,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+"checksum widestring 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a212922ea58fbf5044f83663aa4fc6281ff890f1fd7546c0c3f52f5290831781"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index b49a7bf5d8..a95d46eb9e 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -27,3 +27,7 @@ pfctl = "0.1"
system-configuration = "0.1"
core-foundation = "0.5"
tokio-core = "0.1"
+
+[target.'cfg(windows)'.dependencies]
+libc = "0.2.20"
+widestring = "0.3"
diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs
deleted file mode 100644
index 4df9d87e6a..0000000000
--- a/talpid-core/src/firewall/windows.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use super::{Firewall, SecurityPolicy};
-
-error_chain!{}
-
-/// The Windows implementation for the `Firewall` trait.
-pub struct WindowsFirewall;
-impl Firewall for WindowsFirewall {
- type Error = Error;
-
- 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/firewall/windows/mod.rs b/talpid-core/src/firewall/windows/mod.rs
new file mode 100644
index 0000000000..80251d7638
--- /dev/null
+++ b/talpid-core/src/firewall/windows/mod.rs
@@ -0,0 +1,241 @@
+extern crate libc;
+extern crate widestring;
+
+use super::{Firewall, SecurityPolicy};
+use std::net::IpAddr;
+use std::ptr;
+
+use self::ffi::*;
+use talpid_types::net::Endpoint;
+
+use self::widestring::WideCString;
+
+error_chain!{
+ errors{
+ #[doc = "Windows firewall module error"]
+ WfpctlFailure(desc: &'static str){
+ description("Opaque Wfpctl failure")
+ display("Wfpctl failed when {}", desc)
+ }
+ }
+}
+
+const WFPCTL_TIMEOUT_SECONDS: u32 = 2;
+
+/// The Windows implementation for the `Firewall` trait.
+pub struct WindowsFirewall {
+ _unused: [u8; 0],
+}
+
+impl Firewall for WindowsFirewall {
+ type Error = Error;
+
+ fn new() -> Result<Self> {
+ let ok =
+ unsafe { Wfpctl_Initialize(WFPCTL_TIMEOUT_SECONDS, Some(error_sink), ptr::null_mut()) };
+ ok.into_result("initialise wfpctl").map(|_| {
+ trace!("Successfully initialized wfpctl");
+ WindowsFirewall{_unused: []}
+ })
+ }
+
+ fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> {
+ match policy {
+ SecurityPolicy::Connecting {
+ relay_endpoint,
+ allow_lan,
+ } => {
+ let cfg = &WfpCtlSettings::new(allow_lan);
+ self.set_connecting_state(&relay_endpoint, &cfg)
+ }
+ SecurityPolicy::Connected {
+ relay_endpoint,
+ tunnel,
+ allow_lan,
+ } => {
+ let cfg = &WfpCtlSettings::new(allow_lan);
+ self.set_connected_state(&relay_endpoint, &cfg, &tunnel)
+ }
+ }
+ }
+
+ fn reset_policy(&mut self) -> Result<()> {
+ trace!("Resetting firewall policy");
+ let ok = unsafe { Wfpctl_Reset() };
+ ok.into_result("resetting firewall")
+ }
+}
+
+impl Drop for WindowsFirewall {
+ fn drop(&mut self) {
+ if unsafe { Wfpctl_Deinitialize().is_ok() } {
+ trace!("Successfully deinitialized wfpctl");
+ } else {
+ error!("Failed to deinitialize wfpctl");
+ };
+ }
+}
+
+impl WindowsFirewall {
+ fn set_connecting_state(
+ &mut self,
+ endpoint: &Endpoint,
+ wfp_settings: &WfpCtlSettings,
+ ) -> Result<()> {
+ trace!("Applying 'connecting' firewall policy");
+ let ip_str = Self::widestring_ip(&endpoint.address.ip());
+
+ // ip_str has to outlive wfp_relay
+ let wfp_relay = WfpCtlRelay {
+ ip: ip_str.as_wide_c_str().as_ptr(),
+ port: endpoint.address.port(),
+ protocol: WfpCtlProt::from(endpoint.protocol),
+ };
+
+ let ok = unsafe { Wfpctl_ApplyPolicyConnecting(wfp_settings, &wfp_relay) };
+ ok.into_result("applying 'connecting' policy")
+ }
+
+ fn widestring_ip(ip: &IpAddr) -> WideCString {
+ let buf = ip.to_string().encode_utf16().collect::<Vec<_>>();
+ WideCString::new(buf).unwrap()
+ }
+
+ fn set_connected_state(
+ &mut self,
+ endpoint: &Endpoint,
+ wfp_settings: &WfpCtlSettings,
+ tunnel_metadata: &::tunnel::TunnelMetadata,
+ ) -> Result<()> {
+ trace!("Applying 'connected' firewall policy");
+ let ip_str = Self::widestring_ip(&endpoint.address.ip());
+ let gateway_str = Self::widestring_ip(&tunnel_metadata.gateway.into());
+
+ let tunnel_alias =
+ WideCString::new(tunnel_metadata.interface.encode_utf16().collect::<Vec<_>>()).unwrap();
+
+ // ip_str, gateway_str and tunnel_alias have to outlive wfp_relay
+ let wfp_relay = WfpCtlRelay {
+ ip: ip_str.as_wide_c_str().as_ptr(),
+ port: endpoint.address.port(),
+ protocol: WfpCtlProt::from(endpoint.protocol),
+ };
+
+ let ok = unsafe {
+ Wfpctl_ApplyPolicyConnected(
+ wfp_settings,
+ &wfp_relay,
+ tunnel_alias.as_wide_c_str().as_ptr(),
+ gateway_str.as_wide_c_str().as_ptr(),
+ )
+ };
+ ok.into_result("applying 'connected' policy")
+ }
+}
+
+
+#[allow(non_snake_case)]
+mod ffi {
+
+ use super::{ErrorKind, Result};
+ use super::libc;
+ use std::ffi::CStr;
+ use std::os::raw::c_char;
+ use talpid_types::net::TransportProtocol;
+ use std::ptr;
+
+ #[repr(C)]
+ pub struct WfpCtlResult {
+ ok: bool,
+ }
+
+ impl WfpCtlResult {
+ pub fn into_result(self, description: &'static str) -> Result<()> {
+ match self.ok {
+ true => Ok(()),
+ false => Err(ErrorKind::WfpctlFailure(description).into()),
+ }
+ }
+
+ pub fn is_ok(&self) -> bool {
+ self.ok
+ }
+ }
+
+ pub type ErrorSink = extern "system" fn(msg: *const c_char, ctx: *mut libc::c_void);
+
+ pub extern "system" fn error_sink(msg: *const c_char, _ctx: *mut libc::c_void) {
+ if msg == ptr::null() {
+ error!("log message from wfpctl is NULL");
+ } else {
+ error!("{}", unsafe { CStr::from_ptr(msg).to_string_lossy() });
+ }
+ }
+
+ #[repr(C)]
+ pub struct WfpCtlRelay {
+ pub ip: *const libc::wchar_t,
+ pub port: u16,
+ pub protocol: WfpCtlProt,
+ }
+
+ #[repr(u8)]
+ #[derive(Clone, Copy)]
+ pub enum WfpCtlProt {
+ Tcp = 0u8,
+ Udp = 1u8,
+ }
+
+ impl From<TransportProtocol> for WfpCtlProt {
+ fn from(prot: TransportProtocol) -> WfpCtlProt {
+ match prot {
+ TransportProtocol::Tcp => WfpCtlProt::Tcp,
+ TransportProtocol::Udp => WfpCtlProt::Udp,
+ }
+ }
+ }
+
+ #[repr(C)]
+ pub struct WfpCtlSettings {
+ permitDhcp: bool,
+ permitLan: bool,
+ }
+
+ impl WfpCtlSettings {
+ pub fn new(permit_lan: bool) -> WfpCtlSettings {
+ WfpCtlSettings {
+ permitDhcp: true,
+ permitLan: permit_lan,
+ }
+ }
+ }
+
+ extern "system" {
+ #[link_name(Wfpctl_Initialize)]
+ pub fn Wfpctl_Initialize(
+ timeout: libc::c_uint,
+ sink: Option<ErrorSink>,
+ sink_context: *mut libc::c_void,
+ ) -> WfpCtlResult;
+
+ #[link_name(Wfpctl_Deinitialize)]
+ pub fn Wfpctl_Deinitialize() -> WfpCtlResult;
+
+ #[link_name(Wfpctl_ApplyPolicyConnecting)]
+ pub fn Wfpctl_ApplyPolicyConnecting(
+ settings: &WfpCtlSettings,
+ relay: &WfpCtlRelay,
+ ) -> WfpCtlResult;
+
+ #[link_name(Wfpctl_ApplyPolicyConnected)]
+ pub fn Wfpctl_ApplyPolicyConnected(
+ settings: &WfpCtlSettings,
+ relay: &WfpCtlRelay,
+ tunnelIfaceAlias: *const libc::wchar_t,
+ primaryDns: *const libc::wchar_t,
+ ) -> WfpCtlResult;
+
+ #[link_name(Wfpctl_Reset)]
+ pub fn Wfpctl_Reset() -> WfpCtlResult;
+ }
+}