diff options
| author | Odd Stranne <odd@mullvad.net> | 2019-04-04 22:33:21 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2019-04-04 22:33:21 +0200 |
| commit | 5a710f1291b438ad42eeb447d182fb9c47d88fd2 (patch) | |
| tree | d13f0a81cca5d4db1776b5a3d418f0631f4da0ca /windows | |
| parent | 646ac0eff4253edf31934c36caa1dd1a2c778b27 (diff) | |
| parent | eca93f8828df00e6e34d7de3d50e6f324f42d408 (diff) | |
| download | mullvadvpn-5a710f1291b438ad42eeb447d182fb9c47d88fd2.tar.xz mullvadvpn-5a710f1291b438ad42eeb447d182fb9c47d88fd2.zip | |
Merge branch 'win-sticky-fw-rules'
Diffstat (limited to 'windows')
| m--------- | windows/libwfp | 0 | ||||
| m--------- | windows/windows-libraries | 0 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/fwcontext.cpp | 124 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/fwcontext.h | 18 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/guidhash.h | 25 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/mullvadguids.cpp | 77 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/mullvadguids.h | 14 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/objectpurger.cpp | 68 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/objectpurger.h | 20 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/sessioncontroller.cpp | 54 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/sessioncontroller.h | 13 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/sessionrecord.cpp | 10 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/sessionrecord.h | 12 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/wfpobjecttype.h | 8 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.cpp | 97 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.def | 1 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.h | 27 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.vcxproj | 4 | ||||
| -rw-r--r-- | windows/winfw/src/winfw/winfw.vcxproj.filters | 4 |
19 files changed, 493 insertions, 83 deletions
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"> |
