summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2019-04-04 22:33:21 +0200
committerOdd Stranne <odd@mullvad.net>2019-04-04 22:33:21 +0200
commit5a710f1291b438ad42eeb447d182fb9c47d88fd2 (patch)
treed13f0a81cca5d4db1776b5a3d418f0631f4da0ca
parent646ac0eff4253edf31934c36caa1dd1a2c778b27 (diff)
parenteca93f8828df00e6e34d7de3d50e6f324f42d408 (diff)
downloadmullvadvpn-5a710f1291b438ad42eeb447d182fb9c47d88fd2.tar.xz
mullvadvpn-5a710f1291b438ad42eeb447d182fb9c47d88fd2.zip
Merge branch 'win-sticky-fw-rules'
-rw-r--r--CHANGELOG.md4
-rw-r--r--talpid-core/src/firewall/android.rs4
-rw-r--r--talpid-core/src/firewall/linux.rs4
-rw-r--r--talpid-core/src/firewall/macos.rs4
-rw-r--r--talpid-core/src/firewall/mod.rs14
-rw-r--r--talpid-core/src/firewall/windows.rs42
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs20
m---------windows/libwfp0
m---------windows/windows-libraries0
-rw-r--r--windows/winfw/src/winfw/fwcontext.cpp124
-rw-r--r--windows/winfw/src/winfw/fwcontext.h18
-rw-r--r--windows/winfw/src/winfw/guidhash.h25
-rw-r--r--windows/winfw/src/winfw/mullvadguids.cpp77
-rw-r--r--windows/winfw/src/winfw/mullvadguids.h14
-rw-r--r--windows/winfw/src/winfw/objectpurger.cpp68
-rw-r--r--windows/winfw/src/winfw/objectpurger.h20
-rw-r--r--windows/winfw/src/winfw/sessioncontroller.cpp54
-rw-r--r--windows/winfw/src/winfw/sessioncontroller.h13
-rw-r--r--windows/winfw/src/winfw/sessionrecord.cpp10
-rw-r--r--windows/winfw/src/winfw/sessionrecord.h12
-rw-r--r--windows/winfw/src/winfw/wfpobjecttype.h8
-rw-r--r--windows/winfw/src/winfw/winfw.cpp97
-rw-r--r--windows/winfw/src/winfw/winfw.def1
-rw-r--r--windows/winfw/src/winfw/winfw.h27
-rw-r--r--windows/winfw/src/winfw/winfw.vcxproj4
-rw-r--r--windows/winfw/src/winfw/winfw.vcxproj.filters4
26 files changed, 564 insertions, 104 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7911c24e35..042a6b1950 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,10 @@ Line wrap the file at 100 chars. Th
### Fixed
- Reset the tray icon padlock to the unsecured state, when losing connectivity with the daemon.
+### Changed
+#### Windows
+- Make the firewall rules permanent until reboot, or until the daemon removes them.
+
## [2019.3] - 2019-04-02
### Fixed
#### Windows
diff --git a/talpid-core/src/firewall/android.rs b/talpid-core/src/firewall/android.rs
index eb5d8574f9..d1959d7ae5 100644
--- a/talpid-core/src/firewall/android.rs
+++ b/talpid-core/src/firewall/android.rs
@@ -1,4 +1,4 @@
-use super::{FirewallPolicy, FirewallT};
+use super::{FirewallArguments, FirewallPolicy, FirewallT};
/// Stub error type for Firewall errors on Android.
#[derive(Debug, err_derive::Error)]
@@ -11,7 +11,7 @@ pub struct Firewall;
impl FirewallT for Firewall {
type Error = Error;
- fn new() -> Result<Self, Self::Error> {
+ fn new(_args: FirewallArguments) -> Result<Self, Self::Error> {
Ok(Firewall)
}
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs
index 85aa82eafb..5c8d9e777e 100644
--- a/talpid-core/src/firewall/linux.rs
+++ b/talpid-core/src/firewall/linux.rs
@@ -1,4 +1,4 @@
-use super::{FirewallPolicy, FirewallT};
+use super::{FirewallArguments, FirewallPolicy, FirewallT};
use crate::tunnel;
use ipnetwork::IpNetwork;
use lazy_static::lazy_static;
@@ -83,7 +83,7 @@ pub struct Firewall {
impl FirewallT for Firewall {
type Error = Error;
- fn new() -> Result<Self> {
+ fn new(_args: FirewallArguments) -> Result<Self> {
Ok(Firewall {
table_name: TABLE_NAME.clone(),
})
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs
index 673f834d85..ba920d4e79 100644
--- a/talpid-core/src/firewall/macos.rs
+++ b/talpid-core/src/firewall/macos.rs
@@ -1,4 +1,4 @@
-use super::{FirewallPolicy, FirewallT};
+use super::{FirewallArguments, FirewallPolicy, FirewallT};
use pfctl::FilterRuleAction;
use std::{
env,
@@ -24,7 +24,7 @@ pub struct Firewall {
impl FirewallT for Firewall {
type Error = Error;
- fn new() -> Result<Self> {
+ fn new(_args: FirewallArguments) -> Result<Self> {
// Allows controlling whether firewall rules should log to pflog0. Useful for debugging the
// rules.
let firewall_debugging = env::var("TALPID_FIREWALL_DEBUG");
diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs
index 40aaf52040..c86ac65b68 100644
--- a/talpid-core/src/firewall/mod.rs
+++ b/talpid-core/src/firewall/mod.rs
@@ -131,11 +131,19 @@ pub struct Firewall {
inner: imp::Firewall,
}
+/// Arguments required when first initializing the firewall.
+pub struct FirewallArguments {
+ /// Determines whether the firewall should atomically enter the blocked state during init.
+ pub initialize_blocked: bool,
+ /// This argument is required for the blocked state to configure the firewall correctly.
+ pub allow_lan: Option<bool>,
+}
+
impl Firewall {
/// Returns a new `Firewall`, ready to apply policies.
- pub fn new() -> Result<Self, Error> {
+ pub fn new(args: FirewallArguments) -> Result<Self, Error> {
Ok(Firewall {
- inner: imp::Firewall::new()?,
+ inner: imp::Firewall::new(args)?,
})
}
@@ -160,7 +168,7 @@ trait FirewallT: Sized {
type Error: std::error::Error;
/// Create new instance
- fn new() -> Result<Self, Self::Error>;
+ fn new(args: FirewallArguments) -> Result<Self, Self::Error>;
/// Enable the given FirewallPolicy
fn apply_policy(&mut self, policy: FirewallPolicy) -> Result<(), Self::Error>;
diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs
index 2ab290005f..e4af592f38 100644
--- a/talpid-core/src/firewall/windows.rs
+++ b/talpid-core/src/firewall/windows.rs
@@ -1,7 +1,7 @@
use std::{net::IpAddr, ptr};
use self::winfw::*;
-use super::{FirewallPolicy, FirewallT};
+use super::{FirewallArguments, FirewallPolicy, FirewallT};
use crate::winnet;
use log::{debug, error, trace};
use talpid_types::net::Endpoint;
@@ -48,15 +48,29 @@ pub struct Firewall(());
impl FirewallT for Firewall {
type Error = Error;
- fn new() -> Result<Self, Self::Error> {
- unsafe {
- WinFw_Initialize(
- WINFW_TIMEOUT_SECONDS,
- Some(winnet::error_sink),
- ptr::null_mut(),
- )
- .into_result()?
- };
+ fn new(args: FirewallArguments) -> Result<Self, Self::Error> {
+ if args.initialize_blocked {
+ let cfg = &WinFwSettings::new(args.allow_lan.unwrap());
+ unsafe {
+ WinFw_InitializeBlocked(
+ WINFW_TIMEOUT_SECONDS,
+ &cfg,
+ Some(winnet::error_sink),
+ ptr::null_mut(),
+ )
+ .into_result()?
+ };
+ } else {
+ unsafe {
+ WinFw_Initialize(
+ WINFW_TIMEOUT_SECONDS,
+ Some(winnet::error_sink),
+ ptr::null_mut(),
+ )
+ .into_result()?
+ };
+ }
+
trace!("Successfully initialized windows firewall module");
Ok(Firewall(()))
}
@@ -243,6 +257,14 @@ mod winfw {
sink_context: *mut libc::c_void,
) -> InitializationResult;
+ #[link_name = "WinFw_InitializeBlocked"]
+ pub fn WinFw_InitializeBlocked(
+ timeout: libc::c_uint,
+ settings: &WinFwSettings,
+ sink: Option<winnet::ErrorSink>,
+ sink_context: *mut libc::c_void,
+ ) -> InitializationResult;
+
#[link_name = "WinFw_Deinitialize"]
pub fn WinFw_Deinitialize() -> DeinitializationResult;
diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs
index 370aad57bc..715c2d773c 100644
--- a/talpid-core/src/tunnel_state_machine/mod.rs
+++ b/talpid-core/src/tunnel_state_machine/mod.rs
@@ -14,7 +14,12 @@ use self::{
disconnected_state::DisconnectedState,
disconnecting_state::{AfterDisconnect, DisconnectingState},
};
-use crate::{dns::DnsMonitor, firewall::Firewall, mpsc::IntoSender, offline};
+use crate::{
+ dns::DnsMonitor,
+ firewall::{Firewall, FirewallArguments},
+ mpsc::IntoSender,
+ offline,
+};
use futures::{sync::mpsc, Async, Future, Poll, Stream};
use std::{
io,
@@ -186,7 +191,18 @@ impl TunnelStateMachine {
cache_dir: impl AsRef<Path>,
commands: mpsc::UnboundedReceiver<TunnelCommand>,
) -> Result<Self, Error> {
- let firewall = Firewall::new().map_err(Error::InitFirewallError)?;
+ let args = if block_when_disconnected {
+ FirewallArguments {
+ initialize_blocked: true,
+ allow_lan: Some(allow_lan),
+ }
+ } else {
+ FirewallArguments {
+ initialize_blocked: false,
+ allow_lan: None,
+ }
+ };
+ let firewall = Firewall::new(args).map_err(Error::InitFirewallError)?;
let dns_monitor = DnsMonitor::new(cache_dir).map_err(Error::InitDnsMonitorError)?;
let mut shared_values = SharedTunnelStateValues {
firewall,
diff --git a/windows/libwfp b/windows/libwfp
-Subproject d351ea55711894643128713c0d179dd8529e73c
+Subproject 4065b5bdf56668ca09dd29bf83805f363126935
diff --git a/windows/windows-libraries b/windows/windows-libraries
-Subproject d98f74cf41b4a40d7878e7d9686b3feb0d9b537
+Subproject d609c6dea5e97f615f18827d4086949b3e857f8
diff --git a/windows/winfw/src/winfw/fwcontext.cpp b/windows/winfw/src/winfw/fwcontext.cpp
index f971f24b2e..b77ac82cb5 100644
--- a/windows/winfw/src/winfw/fwcontext.cpp
+++ b/windows/winfw/src/winfw/fwcontext.cpp
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "fwcontext.h"
#include "mullvadobjects.h"
+#include "objectpurger.h"
#include "rules/blockall.h"
#include "rules/ifirewallrule.h"
#include "rules/permitdhcp.h"
@@ -13,6 +14,7 @@
#include "rules/restrictdns.h"
#include "libwfp/transaction.h"
#include "libwfp/filterengine.h"
+#include "libwfp/ipaddress.h"
#include <functional>
#include <stdexcept>
#include <utility>
@@ -58,7 +60,7 @@ void AppendNetBlockedRules(FwContext::Ruleset &ruleset)
FwContext::FwContext(uint32_t timeout)
: m_baseline(0)
{
- auto engine = wfp::FilterEngine::DynamicSession(timeout);
+ auto engine = wfp::FilterEngine::StandardSession(timeout);
//
// Pass engine ownership to "session controller"
@@ -73,6 +75,26 @@ FwContext::FwContext(uint32_t timeout)
m_baseline = m_sessionController->checkpoint();
}
+FwContext::FwContext(uint32_t timeout, const WinFwSettings &settings)
+ : m_baseline(0)
+{
+ auto engine = wfp::FilterEngine::StandardSession(timeout);
+
+ //
+ // Pass engine ownership to "session controller"
+ //
+ m_sessionController = std::make_unique<SessionController>(std::move(engine));
+
+ uint32_t checkpoint = 0;
+
+ if (false == applyBlockedBaseConfiguration(settings, checkpoint))
+ {
+ throw std::runtime_error("Failed to apply base configuration in BFE.");
+ }
+
+ m_baseline = checkpoint;
+}
+
bool FwContext::applyPolicyConnecting(const WinFwSettings &settings, const WinFwRelay &relay)
{
Ruleset ruleset;
@@ -89,7 +111,14 @@ bool FwContext::applyPolicyConnecting(const WinFwSettings &settings, const WinFw
return applyRuleset(ruleset);
}
-bool FwContext::applyPolicyConnected(const WinFwSettings &settings, const WinFwRelay &relay, const wchar_t *tunnelInterfaceAlias, const wchar_t *v4Gateway, const wchar_t *v6Gateway)
+bool FwContext::applyPolicyConnected
+(
+ const WinFwSettings &settings,
+ const WinFwRelay &relay,
+ const wchar_t *tunnelInterfaceAlias,
+ const wchar_t *v4DnsHost,
+ const wchar_t *v6DnsHost
+)
{
Ruleset ruleset;
@@ -110,11 +139,10 @@ bool FwContext::applyPolicyConnected(const WinFwSettings &settings, const WinFwR
tunnelInterfaceAlias
));
- /// We currently expect DNS servers to only be ran on the tunnel gateway IPs
ruleset.emplace_back(std::make_unique<rules::RestrictDns>(
tunnelInterfaceAlias,
- wfp::IpAddress(v4Gateway),
- (v6Gateway != nullptr) ? std::make_unique<wfp::IpAddress>(v6Gateway) : nullptr
+ wfp::IpAddress(v4DnsHost),
+ (v6DnsHost != nullptr) ? std::make_unique<wfp::IpAddress>(v6DnsHost) : nullptr
));
return applyRuleset(ruleset);
@@ -122,51 +150,89 @@ bool FwContext::applyPolicyConnected(const WinFwSettings &settings, const WinFwR
bool FwContext::applyPolicyBlocked(const WinFwSettings &settings)
{
+ return applyRuleset(composePolicyBlocked(settings));
+}
+
+bool FwContext::reset()
+{
+ return m_sessionController->executeTransaction([this](SessionController &controller, wfp::FilterEngine &)
+ {
+ return controller.revert(m_baseline), true;
+ });
+}
+
+FwContext::Ruleset FwContext::composePolicyBlocked(const WinFwSettings &settings)
+{
Ruleset ruleset;
AppendNetBlockedRules(ruleset);
AppendSettingsRules(ruleset, settings);
- return applyRuleset(ruleset);
+ return ruleset;
}
-bool FwContext::reset()
+bool FwContext::applyBaseConfiguration()
{
- return m_sessionController->executeTransaction([this]()
+ return m_sessionController->executeTransaction([this](SessionController &controller, wfp::FilterEngine &engine)
{
- m_sessionController->revert(m_baseline);
- return true;
+ return applyCommonBaseConfiguration(controller, engine);
});
}
-bool FwContext::applyRuleset(const Ruleset &ruleset)
+bool FwContext::applyBlockedBaseConfiguration(const WinFwSettings &settings, uint32_t &checkpoint)
{
- return m_sessionController->executeTransaction([&]()
+ return m_sessionController->executeTransaction([&](SessionController &controller, wfp::FilterEngine &engine)
{
- m_sessionController->revert(m_baseline);
-
- for (const auto &rule : ruleset)
+ if (false == applyCommonBaseConfiguration(controller, engine))
{
- if (false == rule->apply(*m_sessionController))
- {
- return false;
- }
+ return false;
}
- return true;
+ //
+ // Record the current session state with only structural objects added.
+ // If we snapshot at a later time we'd accidentally include the blocking policy rules
+ // in the baseline checkpoint.
+ //
+ checkpoint = controller.peekCheckpoint();
+
+ return applyRulesetDirectly(composePolicyBlocked(settings), controller);
});
}
-bool FwContext::applyBaseConfiguration()
+bool FwContext::applyCommonBaseConfiguration(SessionController &controller, wfp::FilterEngine &engine)
{
- return m_sessionController->executeTransaction([&]()
- {
- //
- // Install structural objects
- //
+ //
+ // Since we're using a standard WFP session we can make no assumptions
+ // about which objects are already installed since before.
+ //
+ ObjectPurger::GetRemoveAllFunctor()(engine);
+
+ //
+ // Install structural objects
+ //
+ return controller.addProvider(*MullvadObjects::Provider())
+ && controller.addSublayer(*MullvadObjects::SublayerWhitelist())
+ && controller.addSublayer(*MullvadObjects::SublayerBlacklist());
+}
- return m_sessionController->addProvider(*MullvadObjects::Provider())
- && m_sessionController->addSublayer(*MullvadObjects::SublayerWhitelist())
- && m_sessionController->addSublayer(*MullvadObjects::SublayerBlacklist());
+bool FwContext::applyRuleset(const Ruleset &ruleset)
+{
+ return m_sessionController->executeTransaction([&](SessionController &controller, wfp::FilterEngine &)
+ {
+ controller.revert(m_baseline);
+ return applyRulesetDirectly(ruleset, controller);
});
}
+
+bool FwContext::applyRulesetDirectly(const Ruleset &ruleset, SessionController &controller)
+{
+ for (const auto &rule : ruleset)
+ {
+ if (false == rule->apply(controller))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/windows/winfw/src/winfw/fwcontext.h b/windows/winfw/src/winfw/fwcontext.h
index 65c45ac539..89ef40e1d3 100644
--- a/windows/winfw/src/winfw/fwcontext.h
+++ b/windows/winfw/src/winfw/fwcontext.h
@@ -13,8 +13,18 @@ public:
FwContext(uint32_t timeout);
+ // This ctor applies the "blocked" policy.
+ FwContext(uint32_t timeout, const WinFwSettings &settings);
+
bool applyPolicyConnecting(const WinFwSettings &settings, const WinFwRelay &relay);
- bool applyPolicyConnected(const WinFwSettings &settings, const WinFwRelay &relay, const wchar_t *tunnelInterfaceAlias, const wchar_t *v4DnsHosts, const wchar_t *v6DnsHost);
+ bool applyPolicyConnected
+ (
+ const WinFwSettings &settings,
+ const WinFwRelay &relay,
+ const wchar_t *tunnelInterfaceAlias,
+ const wchar_t *v4DnsHost,
+ const wchar_t *v6DnsHost
+ );
bool applyPolicyBlocked(const WinFwSettings &settings);
bool reset();
@@ -26,8 +36,14 @@ private:
FwContext(const FwContext &) = delete;
FwContext &operator=(const FwContext &) = delete;
+ Ruleset composePolicyBlocked(const WinFwSettings &settings);
+
bool applyBaseConfiguration();
+ bool applyBlockedBaseConfiguration(const WinFwSettings &settings, uint32_t &checkpoint);
+ bool applyCommonBaseConfiguration(SessionController &controller, wfp::FilterEngine &engine);
+
bool applyRuleset(const Ruleset &ruleset);
+ bool applyRulesetDirectly(const Ruleset &ruleset, SessionController &controller);
std::unique_ptr<SessionController> m_sessionController;
diff --git a/windows/winfw/src/winfw/guidhash.h b/windows/winfw/src/winfw/guidhash.h
new file mode 100644
index 0000000000..6d730835a7
--- /dev/null
+++ b/windows/winfw/src/winfw/guidhash.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <cstdint>
+#include <utility>
+#include <guiddef.h>
+
+// Specialize std::hash
+namespace std
+{
+
+template<>
+struct hash<GUID>
+{
+ size_t operator()(const GUID &guid) const noexcept
+ {
+ static_assert(sizeof(GUID) == (2 * sizeof(uint64_t)));
+
+ // MOV on x86 supports non-aligned access.
+ auto data = reinterpret_cast<const uint64_t *>(&guid);
+
+ return hash<uint64_t>()(data[0] ^ data[1]);
+ }
+};
+
+}
diff --git a/windows/winfw/src/winfw/mullvadguids.cpp b/windows/winfw/src/winfw/mullvadguids.cpp
index 8f717d329b..e68312957f 100644
--- a/windows/winfw/src/winfw/mullvadguids.cpp
+++ b/windows/winfw/src/winfw/mullvadguids.cpp
@@ -1,5 +1,82 @@
#include "stdafx.h"
#include "mullvadguids.h"
+#include <algorithm>
+#include <iterator>
+
+//static
+WfpObjectRegistry MullvadGuids::BuildRegistry()
+{
+ const auto detailedRegistry = DetailedRegistry();
+ using ValueType = decltype(detailedRegistry)::const_reference;
+
+ std::unordered_set<GUID> registry;
+
+ std::transform(detailedRegistry.begin(), detailedRegistry.end(), std::inserter(registry, registry.end()), [](ValueType value)
+ {
+ return value.second;
+ });
+
+ return registry;
+}
+
+//static
+DetailedWfpObjectRegistry MullvadGuids::BuildDetailedRegistry()
+{
+ std::multimap<WfpObjectType, GUID> registry;
+
+ registry.insert(std::make_pair(WfpObjectType::Provider, Provider()));
+ registry.insert(std::make_pair(WfpObjectType::Sublayer, SublayerWhitelist()));
+ registry.insert(std::make_pair(WfpObjectType::Sublayer, SublayerBlacklist()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterBlockAll_Outbound_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterBlockAll_Outbound_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterBlockAll_Inbound_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterBlockAll_Inbound_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLan_10_8()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLan_172_16_12()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLan_192_168_16()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLan_169_254_16()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLan_Multicast()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLan_Ipv6_fe80_10()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLan_Ipv6_Multicast()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLanService_10_8()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLanService_172_16_12()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLanService_192_168_16()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLanService_169_254_16()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLanService_Ipv6_fe80_10()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLoopback_Outbound_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLoopback_Outbound_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLoopback_Inbound_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitLoopback_Inbound_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitDhcpV4_Outbound_Request()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitDhcpV6_Outbound_Request()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitDhcpV4_Inbound_Response()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitDhcpV6_Inbound_Response()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitVpnRelay()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitVpnTunnel_Outbound_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitVpnTunnel_Outbound_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterRestrictDns_Outbound_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterRestrictDns_Outbound_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterRestrictDns_Outbound_Tunnel_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterRestrictDns_Outbound_Tunnel_Ipv6()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitVpnTunnelService_Ipv4()));
+ registry.insert(std::make_pair(WfpObjectType::Filter, FilterPermitVpnTunnelService_Ipv6()));
+
+ return registry;
+}
+
+//static
+const WfpObjectRegistry &MullvadGuids::Registry()
+{
+ static auto registry = BuildRegistry(); // TODO: Thread safety.
+ return registry;
+}
+
+//static
+const DetailedWfpObjectRegistry &MullvadGuids::DetailedRegistry()
+{
+ static auto registry = BuildDetailedRegistry(); // TODO: Thread safety.
+ return registry;
+}
//static
const GUID &MullvadGuids::Provider()
diff --git a/windows/winfw/src/winfw/mullvadguids.h b/windows/winfw/src/winfw/mullvadguids.h
index 3b9e9bbecf..04cad0a6c8 100644
--- a/windows/winfw/src/winfw/mullvadguids.h
+++ b/windows/winfw/src/winfw/mullvadguids.h
@@ -1,10 +1,24 @@
#pragma once
+
+#include "wfpobjecttype.h"
+#include "guidhash.h"
#include <guiddef.h>
+#include <unordered_set>
+#include <map>
+
+using WfpObjectRegistry = std::unordered_set<GUID>;
+using DetailedWfpObjectRegistry = std::multimap<WfpObjectType, GUID>;
class MullvadGuids
{
+ static WfpObjectRegistry BuildRegistry();
+ static DetailedWfpObjectRegistry BuildDetailedRegistry();
+
public:
+ static const WfpObjectRegistry &Registry();
+ static const DetailedWfpObjectRegistry &DetailedRegistry();
+
MullvadGuids() = delete;
static const GUID &Provider();
diff --git a/windows/winfw/src/winfw/objectpurger.cpp b/windows/winfw/src/winfw/objectpurger.cpp
new file mode 100644
index 0000000000..60c32cd53e
--- /dev/null
+++ b/windows/winfw/src/winfw/objectpurger.cpp
@@ -0,0 +1,68 @@
+#include "stdafx.h"
+#include "objectpurger.h"
+#include "mullvadguids.h"
+#include "wfpobjecttype.h"
+#include "libwfp/filterengine.h"
+#include "libwfp/objectdeleter.h"
+#include "libwfp/transaction.h"
+#include <algorithm>
+
+namespace
+{
+
+using ObjectDeleter = std::function<void(wfp::FilterEngine &, const GUID &)>;
+
+template<typename TRange>
+void RemoveRange(wfp::FilterEngine &engine, ObjectDeleter deleter, TRange range)
+{
+ std::for_each(range.first, range.second, [&](const auto &record)
+ {
+ const GUID &objectId = record.second;
+ deleter(engine, objectId);
+ });
+}
+
+} // anonymous namespace
+
+//static
+ObjectPurger::RemovalFunctor ObjectPurger::GetRemoveFiltersFunctor()
+{
+ return [](wfp::FilterEngine &engine)
+ {
+ const auto registry = MullvadGuids::DetailedRegistry();
+
+ // Resolve correct overload.
+ void (*deleter)(wfp::FilterEngine &, const GUID &) = wfp::ObjectDeleter::DeleteFilter;
+
+ RemoveRange(engine, deleter, registry.equal_range(WfpObjectType::Filter));
+ };
+}
+
+//static
+ObjectPurger::RemovalFunctor ObjectPurger::GetRemoveAllFunctor()
+{
+ return [](wfp::FilterEngine &engine)
+ {
+ const auto registry = MullvadGuids::DetailedRegistry();
+
+ // Resolve correct overload.
+ void(*deleter)(wfp::FilterEngine &, const GUID &) = wfp::ObjectDeleter::DeleteFilter;
+
+ RemoveRange(engine, deleter, registry.equal_range(WfpObjectType::Filter));
+ RemoveRange(engine, wfp::ObjectDeleter::DeleteSublayer, registry.equal_range(WfpObjectType::Sublayer));
+ RemoveRange(engine, wfp::ObjectDeleter::DeleteProvider, registry.equal_range(WfpObjectType::Provider));
+ };
+}
+
+//static
+bool ObjectPurger::Execute(RemovalFunctor f)
+{
+ auto engine = wfp::FilterEngine::StandardSession();
+
+ auto wrapper = [&]()
+ {
+ return f(*engine), true;
+ };
+
+ return wfp::Transaction::Execute(*engine, wrapper);
+}
diff --git a/windows/winfw/src/winfw/objectpurger.h b/windows/winfw/src/winfw/objectpurger.h
new file mode 100644
index 0000000000..62f7ce2e11
--- /dev/null
+++ b/windows/winfw/src/winfw/objectpurger.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "winfw.h"
+#include "libwfp/filterengine.h"
+#include <cstdint>
+#include <functional>
+
+class ObjectPurger
+{
+public:
+
+ ObjectPurger() = delete;
+
+ using RemovalFunctor = std::function<void(wfp::FilterEngine &engine)>;
+
+ static RemovalFunctor GetRemoveFiltersFunctor();
+ static RemovalFunctor GetRemoveAllFunctor();
+
+ static bool Execute(RemovalFunctor f);
+};
diff --git a/windows/winfw/src/winfw/sessioncontroller.cpp b/windows/winfw/src/winfw/sessioncontroller.cpp
index 2965233600..f3f948cd44 100644
--- a/windows/winfw/src/winfw/sessioncontroller.cpp
+++ b/windows/winfw/src/winfw/sessioncontroller.cpp
@@ -1,10 +1,13 @@
#include "stdafx.h"
#include "sessioncontroller.h"
+#include "wfpobjecttype.h"
+#include "mullvadguids.h"
#include "libwfp/objectinstaller.h"
#include "libwfp/objectdeleter.h"
#include "libwfp/transaction.h"
#include "libcommon/memory.h"
#include <utility>
+#include <stdexcept>
namespace
{
@@ -54,6 +57,17 @@ bool CheckpointKeyToIndex(const std::vector<SessionRecord> &container, uint32_t
return false;
}
+void ValidateObject(const wfp::IIdentifiable &object)
+{
+ const auto registry = MullvadGuids::Registry();
+
+ if (registry.end() == registry.find(object.id()))
+ {
+ throw std::runtime_error("Attempting to install non-registered WFP object");
+ }
+}
+
+
} // anonymous namespace
SessionController::SessionController(std::unique_ptr<wfp::FilterEngine> &&engine)
@@ -70,7 +84,7 @@ SessionController::~SessionController()
try
{
- executeTransaction([this]()
+ executeTransaction([this](SessionController &, wfp::FilterEngine &)
{
reset();
return true;
@@ -89,13 +103,15 @@ bool SessionController::addProvider(wfp::ProviderBuilder &providerBuilder)
throw std::runtime_error("Cannot add provider outside transaction");
}
+ ValidateObject(providerBuilder);
+
GUID key;
auto status = wfp::ObjectInstaller::AddProvider(*m_engine, providerBuilder, &key);
if (status)
{
- m_transactionRecords.emplace_back(SessionRecord(key, SessionRecord::ObjectType::Provider));
+ m_transactionRecords.emplace_back(SessionRecord(key, WfpObjectType::Provider));
}
return status;
@@ -108,13 +124,15 @@ bool SessionController::addSublayer(wfp::SublayerBuilder &sublayerBuilder)
throw std::runtime_error("Cannot add sublayer outside transaction");
}
+ ValidateObject(sublayerBuilder);
+
GUID key;
auto status = wfp::ObjectInstaller::AddSublayer(*m_engine, sublayerBuilder, &key);
if (status)
{
- m_transactionRecords.emplace_back(SessionRecord(key, SessionRecord::ObjectType::Sublayer));
+ m_transactionRecords.emplace_back(SessionRecord(key, WfpObjectType::Sublayer));
}
return status;
@@ -127,6 +145,8 @@ bool SessionController::addFilter(wfp::FilterBuilder &filterBuilder, const wfp::
throw std::runtime_error("Cannot add filter outside transaction");
}
+ ValidateObject(filterBuilder);
+
UINT64 id;
auto status = wfp::ObjectInstaller::AddFilter(*m_engine, filterBuilder, conditionBuilder, &id);
@@ -139,7 +159,7 @@ bool SessionController::addFilter(wfp::FilterBuilder &filterBuilder, const wfp::
return status;
}
-bool SessionController::executeTransaction(std::function<bool()> operation)
+bool SessionController::executeTransaction(TransactionFunctor operation)
{
if (m_activeTransaction.exchange(true))
{
@@ -155,7 +175,12 @@ bool SessionController::executeTransaction(std::function<bool()> operation)
m_transactionRecords = m_records;
- auto status = wfp::Transaction::Execute(*m_engine, operation);
+ auto transactionForwarder = [this, operation]()
+ {
+ return operation(*this, *m_engine);
+ };
+
+ auto status = wfp::Transaction::Execute(*m_engine, transactionForwarder);
if (status)
{
@@ -165,7 +190,7 @@ bool SessionController::executeTransaction(std::function<bool()> operation)
return status;
}
-bool SessionController::executeReadOnlyTransaction(std::function<bool()> operation)
+bool SessionController::executeReadOnlyTransaction(TransactionFunctor operation)
{
if (m_activeTransaction.exchange(true))
{
@@ -179,7 +204,12 @@ bool SessionController::executeReadOnlyTransaction(std::function<bool()> operati
m_activeTransaction.store(false);
};
- return wfp::Transaction::ExecuteReadOnly(*m_engine, operation);
+ auto transactionForwarder = [this, operation]()
+ {
+ return operation(*this, *m_engine);
+ };
+
+ return wfp::Transaction::ExecuteReadOnly(*m_engine, transactionForwarder);
}
uint32_t SessionController::checkpoint()
@@ -197,6 +227,16 @@ uint32_t SessionController::checkpoint()
return m_records.back().key();
}
+uint32_t SessionController::peekCheckpoint()
+{
+ if (m_transactionRecords.empty())
+ {
+ return 0;
+ }
+
+ return m_transactionRecords.back().key();
+}
+
void SessionController::revert(uint32_t key)
{
if (false == m_activeTransaction)
diff --git a/windows/winfw/src/winfw/sessioncontroller.h b/windows/winfw/src/winfw/sessioncontroller.h
index 61163533c4..243a501083 100644
--- a/windows/winfw/src/winfw/sessioncontroller.h
+++ b/windows/winfw/src/winfw/sessioncontroller.h
@@ -3,6 +3,8 @@
#include "iobjectinstaller.h"
#include "sessionrecord.h"
#include "libwfp/filterengine.h"
+#include "libwfp/iidentifiable.h"
+#include <functional>
#include <atomic>
#include <memory>
#include <vector>
@@ -18,8 +20,10 @@ public:
bool addSublayer(wfp::SublayerBuilder &sublayerBuilder) override;
bool addFilter(wfp::FilterBuilder &filterBuilder, const wfp::IConditionBuilder &conditionBuilder) override;
- bool executeTransaction(std::function<bool()> operation);
- bool executeReadOnlyTransaction(std::function<bool()> operation);
+ using TransactionFunctor = std::function<bool(SessionController &, wfp::FilterEngine &)>;
+
+ bool executeTransaction(TransactionFunctor operation);
+ bool executeReadOnlyTransaction(TransactionFunctor operation);
//
// Retrieve checkpoint key that can be used to restore the current session state
@@ -28,6 +32,11 @@ public:
uint32_t checkpoint();
//
+ // Hack. Read checkpoint while currently inside a transaction.
+ //
+ uint32_t peekCheckpoint();
+
+ //
// Purge objects in the stack and return to an earlier state
// Use only inside active transaction
//
diff --git a/windows/winfw/src/winfw/sessionrecord.cpp b/windows/winfw/src/winfw/sessionrecord.cpp
index ad11f4c013..f57b06afc1 100644
--- a/windows/winfw/src/winfw/sessionrecord.cpp
+++ b/windows/winfw/src/winfw/sessionrecord.cpp
@@ -12,7 +12,7 @@ std::atomic<uint32_t> g_keybase = 0;
} // anonymous namespace
-SessionRecord::SessionRecord(const GUID &id, ObjectType type)
+SessionRecord::SessionRecord(const GUID &id, WfpObjectType type)
: m_type(type)
, m_id(id)
, m_key(g_keybase++)
@@ -20,7 +20,7 @@ SessionRecord::SessionRecord(const GUID &id, ObjectType type)
}
SessionRecord::SessionRecord(UINT64 id)
- : m_type(ObjectType::Filter)
+ : m_type(WfpObjectType::Filter)
, m_filterId(id)
, m_key(g_keybase++)
{
@@ -30,17 +30,17 @@ void SessionRecord::purge(wfp::FilterEngine &engine)
{
switch (m_type)
{
- case ObjectType::Provider:
+ case WfpObjectType::Provider:
{
wfp::ObjectDeleter::DeleteProvider(engine, m_id);
break;
}
- case ObjectType::Sublayer:
+ case WfpObjectType::Sublayer:
{
wfp::ObjectDeleter::DeleteSublayer(engine, m_id);
break;
}
- case ObjectType::Filter:
+ case WfpObjectType::Filter:
{
wfp::ObjectDeleter::DeleteFilter(engine, m_filterId);
break;
diff --git a/windows/winfw/src/winfw/sessionrecord.h b/windows/winfw/src/winfw/sessionrecord.h
index 8ad3be8c86..761a4c863f 100644
--- a/windows/winfw/src/winfw/sessionrecord.h
+++ b/windows/winfw/src/winfw/sessionrecord.h
@@ -1,6 +1,7 @@
#pragma once
#include "libwfp/filterengine.h"
+#include "wfpobjecttype.h"
#include <guiddef.h>
#include <windows.h>
@@ -8,14 +9,7 @@ class SessionRecord
{
public:
- enum class ObjectType
- {
- Provider,
- Sublayer,
- Filter
- };
-
- SessionRecord(const GUID &id, ObjectType type);
+ SessionRecord(const GUID &id, WfpObjectType type);
SessionRecord(UINT64 id);
SessionRecord(const SessionRecord &) = default;
@@ -28,7 +22,7 @@ public:
private:
- ObjectType m_type;
+ WfpObjectType m_type;
GUID m_id;
UINT64 m_filterId;
diff --git a/windows/winfw/src/winfw/wfpobjecttype.h b/windows/winfw/src/winfw/wfpobjecttype.h
new file mode 100644
index 0000000000..0e31da2969
--- /dev/null
+++ b/windows/winfw/src/winfw/wfpobjecttype.h
@@ -0,0 +1,8 @@
+#pragma once
+
+enum class WfpObjectType
+{
+ Provider,
+ Sublayer,
+ Filter
+};
diff --git a/windows/winfw/src/winfw/winfw.cpp b/windows/winfw/src/winfw/winfw.cpp
index f391d691b2..7b9ea2dc6b 100644
--- a/windows/winfw/src/winfw/winfw.cpp
+++ b/windows/winfw/src/winfw/winfw.cpp
@@ -1,7 +1,7 @@
#include "stdafx.h"
#include "winfw.h"
#include "fwcontext.h"
-#include "libwfp/ipaddress.h"
+#include "objectpurger.h"
#include <windows.h>
#include <stdexcept>
@@ -10,8 +10,8 @@ namespace
uint32_t g_timeout = 0;
-WinFwErrorSink g_ErrorSink = nullptr;
-void * g_ErrorContext = nullptr;
+WinFwErrorSink g_errorSink = nullptr;
+void * g_errorContext = nullptr;
FwContext *g_fwContext = nullptr;
@@ -38,8 +38,8 @@ WinFw_Initialize(
// Convert seconds to milliseconds.
g_timeout = timeout * 1000;
- g_ErrorSink = errorSink;
- g_ErrorContext = errorContext;
+ g_errorSink = errorSink;
+ g_errorContext = errorContext;
try
{
@@ -47,9 +47,56 @@ WinFw_Initialize(
}
catch (std::exception &err)
{
- if (nullptr != g_ErrorSink)
+ if (nullptr != g_errorSink)
{
- g_ErrorSink(err.what(), g_ErrorContext);
+ g_errorSink(err.what(), g_errorContext);
+ }
+
+ return false;
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_InitializeBlocked(
+ uint32_t timeout,
+ const WinFwSettings &settings,
+ WinFwErrorSink errorSink,
+ void *errorContext
+)
+{
+ if (nullptr != g_fwContext)
+ {
+ //
+ // This is an error.
+ // The existing instance may have a different timeout etc.
+ //
+ return false;
+ }
+
+ // Convert seconds to milliseconds.
+ g_timeout = timeout * 1000;
+
+ g_errorSink = errorSink;
+ g_errorContext = errorContext;
+
+ try
+ {
+ g_fwContext = new FwContext(g_timeout, settings);
+ }
+ catch (std::exception &err)
+ {
+ if (nullptr != g_errorSink)
+ {
+ g_errorSink(err.what(), g_errorContext);
}
return false;
@@ -97,9 +144,9 @@ WinFw_ApplyPolicyConnecting(
}
catch (std::exception &err)
{
- if (nullptr != g_ErrorSink)
+ if (nullptr != g_errorSink)
{
- g_ErrorSink(err.what(), g_ErrorContext);
+ g_errorSink(err.what(), g_errorContext);
}
return false;
@@ -117,8 +164,8 @@ WinFw_ApplyPolicyConnected(
const WinFwSettings &settings,
const WinFwRelay &relay,
const wchar_t *tunnelInterfaceAlias,
- const wchar_t *v4Gateway,
- const wchar_t *v6Gateway
+ const wchar_t *v4DnsHost,
+ const wchar_t *v6DnsHost
)
{
if (nullptr == g_fwContext)
@@ -128,13 +175,13 @@ WinFw_ApplyPolicyConnected(
try
{
- return g_fwContext->applyPolicyConnected(settings, relay, tunnelInterfaceAlias, v4Gateway, v6Gateway);
+ return g_fwContext->applyPolicyConnected(settings, relay, tunnelInterfaceAlias, v4DnsHost, v6DnsHost);
}
catch (std::exception &err)
{
- if (nullptr != g_ErrorSink)
+ if (nullptr != g_errorSink)
{
- g_ErrorSink(err.what(), g_ErrorContext);
+ g_errorSink(err.what(), g_errorContext);
}
return false;
@@ -163,9 +210,9 @@ WinFw_ApplyPolicyBlocked(
}
catch (std::exception &err)
{
- if (nullptr != g_ErrorSink)
+ if (nullptr != g_errorSink)
{
- g_ErrorSink(err.what(), g_ErrorContext);
+ g_errorSink(err.what(), g_errorContext);
}
return false;
@@ -181,24 +228,20 @@ bool
WINFW_API
WinFw_Reset()
{
- if (nullptr == g_fwContext)
- {
- //
- // This is OK because the practical difference between having no instance
- // and having a reset instance is negligible.
- //
- return true;
- }
-
try
{
+ if (nullptr == g_fwContext)
+ {
+ return ObjectPurger::Execute(ObjectPurger::GetRemoveAllFunctor());
+ }
+
return g_fwContext->reset();
}
catch (std::exception &err)
{
- if (nullptr != g_ErrorSink)
+ if (nullptr != g_errorSink)
{
- g_ErrorSink(err.what(), g_ErrorContext);
+ g_errorSink(err.what(), g_errorContext);
}
return false;
diff --git a/windows/winfw/src/winfw/winfw.def b/windows/winfw/src/winfw/winfw.def
index e8ef663dae..20d59bb4c8 100644
--- a/windows/winfw/src/winfw/winfw.def
+++ b/windows/winfw/src/winfw/winfw.def
@@ -2,6 +2,7 @@ LIBRARY winfw
EXPORTS
WinFw_Initialize
+WinFw_InitializeBlocked
WinFw_Deinitialize
WinFw_ApplyPolicyConnecting
WinFw_ApplyPolicyConnected
diff --git a/windows/winfw/src/winfw/winfw.h b/windows/winfw/src/winfw/winfw.h
index f0c1adb2dc..95e66a608f 100644
--- a/windows/winfw/src/winfw/winfw.h
+++ b/windows/winfw/src/winfw/winfw.h
@@ -73,6 +73,27 @@ WinFw_Initialize(
);
//
+// WinFw_InitializeBlocked
+//
+// Same as `WinFw_Initialize` with the addition that the blocked policy is
+// immediately applied, within the same initialization transaction.
+//
+// This function is preferred rather than first initializing and then applying
+// the blocked policy. Using two separate operations leaves a tiny window
+// for traffic to leak out.
+//
+extern "C"
+WINFW_LINKAGE
+bool
+WINFW_API
+WinFw_InitializeBlocked(
+ uint32_t timeout,
+ const WinFwSettings &settings,
+ WinFwErrorSink errorSink,
+ void *errorContext
+);
+
+//
// Deinitialize:
//
// Call this function once before unloading WINFW or exiting the process.
@@ -112,7 +133,7 @@ WinFw_ApplyPolicyConnecting(
//
// tunnelInterfaceAlias:
// Friendly name of VPN tunnel interface
-// primaryDns:
+// v4DnsHost/v6DnsHost:
// String encoded IP address of DNS to use inside tunnel
//
extern "C"
@@ -123,8 +144,8 @@ WinFw_ApplyPolicyConnected(
const WinFwSettings &settings,
const WinFwRelay &relay,
const wchar_t *tunnelInterfaceAlias,
- const wchar_t *v4Gateway,
- const wchar_t *v6Gateway
+ const wchar_t *v4DnsHost,
+ const wchar_t *v6DnsHost
);
//
diff --git a/windows/winfw/src/winfw/winfw.vcxproj b/windows/winfw/src/winfw/winfw.vcxproj
index 7f6a919cd2..e2db2fd432 100644
--- a/windows/winfw/src/winfw/winfw.vcxproj
+++ b/windows/winfw/src/winfw/winfw.vcxproj
@@ -22,6 +22,7 @@
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="mullvadguids.cpp" />
<ClCompile Include="mullvadobjects.cpp" />
+ <ClCompile Include="objectpurger.cpp" />
<ClCompile Include="rules\blockall.cpp" />
<ClCompile Include="rules\permitdhcp.cpp" />
<ClCompile Include="rules\permitlan.cpp" />
@@ -43,9 +44,12 @@
<ClCompile Include="winfw.cpp" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="guidhash.h" />
<ClInclude Include="iobjectinstaller.h" />
<ClInclude Include="mullvadguids.h" />
<ClInclude Include="mullvadobjects.h" />
+ <ClInclude Include="objectpurger.h" />
+ <ClInclude Include="wfpobjecttype.h" />
<ClInclude Include="rules\blockall.h" />
<ClInclude Include="rules\ifirewallrule.h" />
<ClInclude Include="rules\permitdhcp.h" />
diff --git a/windows/winfw/src/winfw/winfw.vcxproj.filters b/windows/winfw/src/winfw/winfw.vcxproj.filters
index a43d966614..8ccdaa4627 100644
--- a/windows/winfw/src/winfw/winfw.vcxproj.filters
+++ b/windows/winfw/src/winfw/winfw.vcxproj.filters
@@ -36,6 +36,7 @@
<ClCompile Include="rules\permitvpntunnelservice.cpp">
<Filter>rules</Filter>
</ClCompile>
+ <ClCompile Include="objectpurger.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@@ -77,6 +78,9 @@
<ClInclude Include="rules\permitvpntunnelservice.h">
<Filter>rules</Filter>
</ClInclude>
+ <ClInclude Include="wfpobjecttype.h" />
+ <ClInclude Include="guidhash.h" />
+ <ClInclude Include="objectpurger.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="rules">