diff options
| author | Odd Stranne <odd@mullvad.net> | 2018-05-25 11:22:01 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2018-05-25 11:22:01 +0200 |
| commit | 32e1bd67dd67652c89762a327e44ba3d47780c48 (patch) | |
| tree | f0ae4de1b7d5703a500209f876d35530f3dcb9fc /windows/winfw/src | |
| parent | 835342bcd9ccffc092825b1c96077f6af1eb9878 (diff) | |
| download | mullvadvpn-32e1bd67dd67652c89762a327e44ba3d47780c48.tar.xz mullvadvpn-32e1bd67dd67652c89762a327e44ba3d47780c48.zip | |
Move wfpctl -> windows/winfw
Diffstat (limited to 'windows/winfw/src')
84 files changed, 5619 insertions, 0 deletions
diff --git a/windows/winfw/src/extras/cli/cli.cpp b/windows/winfw/src/extras/cli/cli.cpp new file mode 100644 index 0000000000..a1758df8ee --- /dev/null +++ b/windows/winfw/src/extras/cli/cli.cpp @@ -0,0 +1,173 @@ +// cli.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include "util.h" +#include "filterengineprovider.h" +#include "modules/imodule.h" +#include "modules/list.h" +#include "modules/monitor.h" +#include "modules/winfw.h" +#include "libcommon/string.h" +#include <iostream> +#include <conio.h> + +// Should set key comparison operator to compare lowercase strings but meh. +std::map<std::wstring, std::unique_ptr<modules::IModule> > g_modules; + +void OutputConsole(const std::wstring &str) +{ + std::wcout << str.c_str() << std::endl; +} + +void InitializeFilterEngine() +{ + FilterEngineProvider::Instance().set(std::move(wfp::FilterEngine::DynamicSession())); +} + +void InitializeModules() +{ + auto list = std::make_unique<modules::List>(OutputConsole); + g_modules.insert(std::make_pair(common::string::Lower(list->name()), std::move(list))); + + auto monitor = std::make_unique<modules::Monitor>(OutputConsole); + g_modules.insert(std::make_pair(common::string::Lower(monitor->name()), std::move(monitor))); + + auto winfw = std::make_unique<modules::WinFw>(OutputConsole); + g_modules.insert(std::make_pair(common::string::Lower(winfw->name()), std::move(winfw))); +} + +void ProcessHelp(const std::wstring &request) +{ + auto tokens = common::string::Tokenize(request, L" "); + + if (tokens.empty()) + { + std::wcout << L"Unable to interpret request." << std::endl; + return; + } + + if (tokens.size() == 1) + { + PropertyList list; + + for (auto &module : g_modules) + { + list.add(common::string::Lower(module.second->name()), module.second->description()); + } + + list.add(L"help", L"List available modules."); + list.add(L"help /module/", L"Show module specific help."); + list.add(L"reset", L"Reset the Filter Engine session."); + list.add(L"quit", L"Exit the application."); + + PrettyPrintOptions options; + + options.indent = 0; + options.useSeparator = false; + + PrettyPrintProperties(OutputConsole, options, list); + + return; + } + + if (tokens.size() != 2) + { + std::wcout << L"Unable to interpret request." << std::endl; + return; + } + + auto wanted = common::string::Lower(tokens[1]); + auto found = g_modules.find(wanted); + + if (found == g_modules.end()) + { + std::wcout << L"No such module: " << wanted << L"." << std::endl; + return; + } + + auto moduleCommands = found->second->commands(); + + PrettyPrintOptions options; + + options.indent = 0; + options.useSeparator = false; + + PrettyPrintProperties(OutputConsole, options, moduleCommands); +} + +void ProcessRequest(const std::wstring &request) +{ + auto tokens = common::string::Tokenize(request, L" "); + + if (tokens.empty()) + { + std::wcout << L"Unable to interpret request." << std::endl; + return; + } + + auto wanted = common::string::Lower(tokens[0]); + auto found = g_modules.find(wanted); + + if (found == g_modules.end()) + { + std::wcout << L"No such module: " << wanted << L"." << std::endl; + return; + } + + tokens.erase(tokens.begin()); + + found->second->handleRequest(tokens); +} + +int main(int, wchar_t **) +{ + InitializeFilterEngine(); + InitializeModules(); + + for (;;) + { + std::wcout << L"wfp> "; + + std::wstring request; + std::getline(std::wcin, request); + + if (0 == _wcsicmp(request.c_str(), L"quit")) + { + break; + } + + if (0 == _wcsicmp(request.c_str(), L"reset")) + { + InitializeFilterEngine(); + std::wcout << std::endl; + + continue; + } + + if (0 == _wcsnicmp(request.c_str(), L"help", 4)) + { + ProcessHelp(request); + std::wcout << std::endl; + + continue; + } + + try + { + ProcessRequest(request); + } + catch (std::exception &error) + { + std::cout << "Error: " << error.what() << std::endl; + } + catch (...) + { + std::cout << "Unknown error, caught exception!" << std::endl; + } + + std::wcout << std::endl; + } + + return 0; +} diff --git a/windows/winfw/src/extras/cli/cli.vcxproj b/windows/winfw/src/extras/cli/cli.vcxproj new file mode 100644 index 0000000000..93ade5ea56 --- /dev/null +++ b/windows/winfw/src/extras/cli/cli.vcxproj @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>15.0</VCProjectVersion> + <ProjectGuid>{242A58CA-8E54-4170-A8C5-6F91CC4F7DB2}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>cli</RootNamespace> + <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>Disabled</Optimization> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>Disabled</Optimization> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\;$(ProjectDir)..\..\..\..\libwfp\src\;$(ProjectDir)..\..\..\..\windows-libraries\src\</AdditionalIncludeDirectories> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + <AdditionalDependencies>winfw.lib;libcommon.lib;libwfp.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="commands\icommand.h" /> + <ClInclude Include="commands\list\events.h" /> + <ClInclude Include="commands\list\filters.h" /> + <ClInclude Include="commands\list\layers.h" /> + <ClInclude Include="commands\list\providercontexts.h" /> + <ClInclude Include="commands\list\providers.h" /> + <ClInclude Include="commands\list\sessions.h" /> + <ClInclude Include="commands\list\sublayers.h" /> + <ClInclude Include="commands\monitor\m_events.h" /> + <ClInclude Include="commands\winfw\deinit.h" /> + <ClInclude Include="commands\winfw\init.h" /> + <ClInclude Include="commands\winfw\policy.h" /> + <ClInclude Include="filterengineprovider.h" /> + <ClInclude Include="inlineformatter.h" /> + <ClInclude Include="ipropertydecorator.h" /> + <ClInclude Include="modules\imodule.h" /> + <ClInclude Include="modules\list.h" /> + <ClInclude Include="modules\module.h" /> + <ClInclude Include="modules\monitor.h" /> + <ClInclude Include="modules\winfw.h" /> + <ClInclude Include="objectproperties.h" /> + <ClInclude Include="propertydecorator.h" /> + <ClInclude Include="propertylist.h" /> + <ClInclude Include="stdafx.h" /> + <ClInclude Include="subcommanddispatcher.h" /> + <ClInclude Include="targetver.h" /> + <ClInclude Include="util.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="cli.cpp" /> + <ClCompile Include="commands\list\events.cpp" /> + <ClCompile Include="commands\list\filters.cpp" /> + <ClCompile Include="commands\list\layers.cpp" /> + <ClCompile Include="commands\list\providercontexts.cpp" /> + <ClCompile Include="commands\list\providers.cpp" /> + <ClCompile Include="commands\list\sessions.cpp" /> + <ClCompile Include="commands\list\sublayers.cpp" /> + <ClCompile Include="commands\monitor\m_events.cpp" /> + <ClCompile Include="commands\winfw\deinit.cpp" /> + <ClCompile Include="commands\winfw\init.cpp" /> + <ClCompile Include="commands\winfw\policy.cpp" /> + <ClCompile Include="modules\module.cpp" /> + <ClCompile Include="objectproperties.cpp" /> + <ClCompile Include="propertydecorator.cpp" /> + <ClCompile Include="stdafx.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="subcommanddispatcher.cpp" /> + <ClCompile Include="util.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/windows/winfw/src/extras/cli/cli.vcxproj.filters b/windows/winfw/src/extras/cli/cli.vcxproj.filters new file mode 100644 index 0000000000..4f737b5a67 --- /dev/null +++ b/windows/winfw/src/extras/cli/cli.vcxproj.filters @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="commands"> + <UniqueIdentifier>{da097053-def0-4a93-a7a3-b62cc761ac4c}</UniqueIdentifier> + </Filter> + <Filter Include="commands\list"> + <UniqueIdentifier>{25335122-c3ac-492b-bec2-827f73a5269f}</UniqueIdentifier> + </Filter> + <Filter Include="modules"> + <UniqueIdentifier>{e88bc8bf-763d-4c21-b558-18c18c3d01aa}</UniqueIdentifier> + </Filter> + <Filter Include="commands\monitor"> + <UniqueIdentifier>{ff50eaf3-6e11-41ad-bae9-24427a025d33}</UniqueIdentifier> + </Filter> + <Filter Include="commands\winfw"> + <UniqueIdentifier>{571487f4-437d-4ad1-a409-f2b143f6803e}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="modules\imodule.h"> + <Filter>modules</Filter> + </ClInclude> + <ClInclude Include="modules\list.h"> + <Filter>modules</Filter> + </ClInclude> + <ClInclude Include="commands\icommand.h"> + <Filter>commands</Filter> + </ClInclude> + <ClInclude Include="commands\list\sessions.h"> + <Filter>commands\list</Filter> + </ClInclude> + <ClInclude Include="modules\module.h"> + <Filter>modules</Filter> + </ClInclude> + <ClInclude Include="commands\list\providers.h"> + <Filter>commands\list</Filter> + </ClInclude> + <ClInclude Include="commands\list\events.h"> + <Filter>commands\list</Filter> + </ClInclude> + <ClInclude Include="commands\list\filters.h"> + <Filter>commands\list</Filter> + </ClInclude> + <ClInclude Include="commands\list\layers.h"> + <Filter>commands\list</Filter> + </ClInclude> + <ClInclude Include="commands\list\providercontexts.h"> + <Filter>commands\list</Filter> + </ClInclude> + <ClInclude Include="commands\list\sublayers.h"> + <Filter>commands\list</Filter> + </ClInclude> + <ClInclude Include="filterengineprovider.h" /> + <ClInclude Include="inlineformatter.h" /> + <ClInclude Include="objectproperties.h" /> + <ClInclude Include="propertylist.h" /> + <ClInclude Include="stdafx.h" /> + <ClInclude Include="targetver.h" /> + <ClInclude Include="util.h" /> + <ClInclude Include="modules\monitor.h"> + <Filter>modules</Filter> + </ClInclude> + <ClInclude Include="commands\monitor\m_events.h"> + <Filter>commands\monitor</Filter> + </ClInclude> + <ClInclude Include="ipropertydecorator.h" /> + <ClInclude Include="propertydecorator.h" /> + <ClInclude Include="commands\winfw\policy.h"> + <Filter>commands\winfw</Filter> + </ClInclude> + <ClInclude Include="subcommanddispatcher.h" /> + <ClInclude Include="modules\winfw.h"> + <Filter>modules</Filter> + </ClInclude> + <ClInclude Include="commands\winfw\init.h"> + <Filter>commands\winfw</Filter> + </ClInclude> + <ClInclude Include="commands\winfw\deinit.h"> + <Filter>commands\winfw</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="commands\list\sessions.cpp"> + <Filter>commands\list</Filter> + </ClCompile> + <ClCompile Include="modules\module.cpp"> + <Filter>modules</Filter> + </ClCompile> + <ClCompile Include="commands\list\providers.cpp"> + <Filter>commands\list</Filter> + </ClCompile> + <ClCompile Include="commands\list\events.cpp"> + <Filter>commands\list</Filter> + </ClCompile> + <ClCompile Include="commands\list\filters.cpp"> + <Filter>commands\list</Filter> + </ClCompile> + <ClCompile Include="commands\list\layers.cpp"> + <Filter>commands\list</Filter> + </ClCompile> + <ClCompile Include="commands\list\providercontexts.cpp"> + <Filter>commands\list</Filter> + </ClCompile> + <ClCompile Include="commands\list\sublayers.cpp"> + <Filter>commands\list</Filter> + </ClCompile> + <ClCompile Include="cli.cpp" /> + <ClCompile Include="objectproperties.cpp" /> + <ClCompile Include="stdafx.cpp" /> + <ClCompile Include="util.cpp" /> + <ClCompile Include="commands\monitor\m_events.cpp"> + <Filter>commands\monitor</Filter> + </ClCompile> + <ClCompile Include="propertydecorator.cpp" /> + <ClCompile Include="commands\winfw\policy.cpp"> + <Filter>commands\winfw</Filter> + </ClCompile> + <ClCompile Include="subcommanddispatcher.cpp" /> + <ClCompile Include="commands\winfw\init.cpp"> + <Filter>commands\winfw</Filter> + </ClCompile> + <ClCompile Include="commands\winfw\deinit.cpp"> + <Filter>commands\winfw</Filter> + </ClCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/windows/winfw/src/extras/cli/commands/icommand.h b/windows/winfw/src/extras/cli/commands/icommand.h new file mode 100644 index 0000000000..a9503a205b --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/icommand.h @@ -0,0 +1,17 @@ +#pragma once + +#include <string> +#include <vector> + +namespace commands +{ + +struct ICommand +{ + virtual std::wstring name() = 0; + virtual std::wstring description() = 0; + + virtual void handleRequest(const std::vector<std::wstring> &arguments) = 0; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/list/events.cpp b/windows/winfw/src/extras/cli/commands/list/events.cpp new file mode 100644 index 0000000000..4396eff980 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/events.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "events.h" +#include "cli/objectproperties.h" +#include "cli/filterengineprovider.h" +#include "cli/propertydecorator.h" +#include "libwfp/objectenumerator.h" + +namespace commands::list +{ + +Events::Events(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Events::name() +{ + return L"events"; + +} + +std::wstring Events::description() +{ + return L"Provides a listing of all recent events in the system."; +} + +void Events::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + PropertyDecorator decorator(FilterEngineProvider::Instance().get()); + + wfp::ObjectEnumerator::Events(*FilterEngineProvider::Instance().get(), [&](const FWPM_NET_EVENT0 &event) + { + m_messageSink(L"Event"); + + PrettyPrintProperties(m_messageSink, options, EventProperties(event, &decorator)); + + return true; + }); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/list/events.h b/windows/winfw/src/extras/cli/commands/list/events.h new file mode 100644 index 0000000000..cc727712f0 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/events.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::list +{ + +class Events : public ICommand +{ +public: + + Events(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/list/filters.cpp b/windows/winfw/src/extras/cli/commands/list/filters.cpp new file mode 100644 index 0000000000..9c7917bf66 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/filters.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "filters.h" +#include "cli/objectproperties.h" +#include "cli/filterengineprovider.h" +#include "cli/propertydecorator.h" +#include "libwfp/objectenumerator.h" + +namespace commands::list +{ + +Filters::Filters(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Filters::name() +{ + return L"filters"; + +} + +std::wstring Filters::description() +{ + return L"Provides a listing of all filters in the system."; +} + +void Filters::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + PropertyDecorator decorator(FilterEngineProvider::Instance().get()); + + wfp::ObjectEnumerator::Filters(*FilterEngineProvider::Instance().get(), [&](const FWPM_FILTER0 &filter) + { + m_messageSink(L"Filter"); + + PrettyPrintProperties(m_messageSink, options, FilterProperties(filter, &decorator)); + + return true; + }); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/list/filters.h b/windows/winfw/src/extras/cli/commands/list/filters.h new file mode 100644 index 0000000000..4c0345f3b3 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/filters.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::list +{ + +class Filters : public ICommand +{ +public: + + Filters(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/list/layers.cpp b/windows/winfw/src/extras/cli/commands/list/layers.cpp new file mode 100644 index 0000000000..3dc4368894 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/layers.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "layers.h" +#include "cli/objectproperties.h" +#include "cli/filterengineprovider.h" +#include "cli/propertydecorator.h" +#include "libwfp/objectenumerator.h" + +namespace commands::list +{ + +Layers::Layers(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Layers::name() +{ + return L"layers"; + +} + +std::wstring Layers::description() +{ + return L"Provides a listing of all layers in the system."; +} + +void Layers::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + PropertyDecorator decorator(FilterEngineProvider::Instance().get()); + + wfp::ObjectEnumerator::Layers(*FilterEngineProvider::Instance().get(), [&](const FWPM_LAYER0 &layer) + { + m_messageSink(L"Layer"); + + PrettyPrintProperties(m_messageSink, options, LayerProperties(layer, &decorator)); + + return true; + }); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/list/layers.h b/windows/winfw/src/extras/cli/commands/list/layers.h new file mode 100644 index 0000000000..92a62c58e0 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/layers.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::list +{ + +class Layers : public ICommand +{ +public: + + Layers(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/list/providercontexts.cpp b/windows/winfw/src/extras/cli/commands/list/providercontexts.cpp new file mode 100644 index 0000000000..6612ce03dc --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/providercontexts.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "providercontexts.h" +#include "cli/objectproperties.h" +#include "cli/filterengineprovider.h" +#include "cli/propertydecorator.h" +#include "libwfp/objectenumerator.h" + +namespace commands::list +{ + +ProviderContexts::ProviderContexts(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring ProviderContexts::name() +{ + return L"providercontexts"; + +} + +std::wstring ProviderContexts::description() +{ + return L"Provides a listing of all provider contexts in the system."; +} + +void ProviderContexts::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + PropertyDecorator decorator(FilterEngineProvider::Instance().get()); + + wfp::ObjectEnumerator::ProviderContexts(*FilterEngineProvider::Instance().get(), [&](const FWPM_PROVIDER_CONTEXT0 &context) + { + m_messageSink(L"Provider context"); + + PrettyPrintProperties(m_messageSink, options, ProviderContextProperties(context, &decorator)); + + return true; + }); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/list/providercontexts.h b/windows/winfw/src/extras/cli/commands/list/providercontexts.h new file mode 100644 index 0000000000..b87886eac7 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/providercontexts.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::list +{ + +class ProviderContexts : public ICommand +{ +public: + + ProviderContexts(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/list/providers.cpp b/windows/winfw/src/extras/cli/commands/list/providers.cpp new file mode 100644 index 0000000000..a06318d74b --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/providers.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include "providers.h" +#include "cli/objectproperties.h" +#include "cli/filterengineprovider.h" +#include "libwfp/objectenumerator.h" + +namespace commands::list +{ + +Providers::Providers(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Providers::name() +{ + return L"providers"; + +} + +std::wstring Providers::description() +{ + return L"Provides a listing of all providers in the system."; +} + +void Providers::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + wfp::ObjectEnumerator::Providers(*FilterEngineProvider::Instance().get(), [&](const FWPM_PROVIDER0 &provider) + { + m_messageSink(L"Provider"); + + PrettyPrintProperties(m_messageSink, options, ProviderProperties(provider)); + + return true; + }); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/list/providers.h b/windows/winfw/src/extras/cli/commands/list/providers.h new file mode 100644 index 0000000000..2a09a88b67 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/providers.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::list +{ + +class Providers : public ICommand +{ +public: + + Providers(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/list/sessions.cpp b/windows/winfw/src/extras/cli/commands/list/sessions.cpp new file mode 100644 index 0000000000..27000c903d --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/sessions.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include "sessions.h" +#include "cli/objectproperties.h" +#include "cli/filterengineprovider.h" +#include "libwfp/objectenumerator.h" + +namespace commands::list +{ + +Sessions::Sessions(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Sessions::name() +{ + return L"sessions"; + +} + +std::wstring Sessions::description() +{ + return L"Provides a listing of all active sessions in the system."; +} + +void Sessions::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + wfp::ObjectEnumerator::Sessions(*FilterEngineProvider::Instance().get(), [&](const FWPM_SESSION0 &session) + { + m_messageSink(L"Session"); + + PrettyPrintProperties(m_messageSink, options, SessionProperties(session)); + + return true; + }); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/list/sessions.h b/windows/winfw/src/extras/cli/commands/list/sessions.h new file mode 100644 index 0000000000..b9d8f04ea2 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/sessions.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::list +{ + +class Sessions : public ICommand +{ +public: + + Sessions(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/list/sublayers.cpp b/windows/winfw/src/extras/cli/commands/list/sublayers.cpp new file mode 100644 index 0000000000..0c8a5f2f58 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/sublayers.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "sublayers.h" +#include "cli/objectproperties.h" +#include "cli/filterengineprovider.h" +#include "cli/propertydecorator.h" +#include "libwfp/objectenumerator.h" + +namespace commands::list +{ + +Sublayers::Sublayers(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Sublayers::name() +{ + return L"sublayers"; + +} + +std::wstring Sublayers::description() +{ + return L"Provides a listing of all sublayers in the system."; +} + +void Sublayers::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + PropertyDecorator decorator(FilterEngineProvider::Instance().get()); + + wfp::ObjectEnumerator::Sublayers(*FilterEngineProvider::Instance().get(), [&](const FWPM_SUBLAYER0 &sublayer) + { + m_messageSink(L"Sublayer"); + + PrettyPrintProperties(m_messageSink, options, SublayerProperties(sublayer, &decorator)); + + return true; + }); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/list/sublayers.h b/windows/winfw/src/extras/cli/commands/list/sublayers.h new file mode 100644 index 0000000000..e9b34bbd52 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/list/sublayers.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::list +{ + +class Sublayers : public ICommand +{ +public: + + Sublayers(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/monitor/m_events.cpp b/windows/winfw/src/extras/cli/commands/monitor/m_events.cpp new file mode 100644 index 0000000000..294ae38e9c --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/monitor/m_events.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "m_events.h" +#include "cli/objectproperties.h" +#include "cli/propertydecorator.h" +#include "cli/filterengineprovider.h" +#include "libwfp/objectmonitor.h" +#include <conio.h> + +namespace commands::monitor +{ + +Events::Events(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Events::name() +{ + return L"events"; + +} + +std::wstring Events::description() +{ + return L"Provides monitoring of drop/allow events."; +} + +void Events::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Unsupported argument(s). Cannot complete request."); + } + + wfp::ObjectMonitor objectMonitor(FilterEngineProvider::Instance().get()); + + objectMonitor.monitorEvents(std::bind(&Events::eventCallback, this, std::placeholders::_1)); + + m_messageSink(L"Successfully enabled monitor. Press any key to abort monitoring."); + + // + // This assumes we're in a console environment, but the alternative is to have + // the command (this class) communicate to the outside world that we're in + // a monitoring state. + // + // Hence, this is sufficient for now. + // + + _getwch(); + + objectMonitor.monitorEventsStop(); +} + +void Events::eventCallback(const FWPM_NET_EVENT1 &event) +{ + m_messageSink(L"Event"); + + PrettyPrintOptions options; + + options.indent = 2; + options.useSeparator = true; + + PropertyDecorator decorator(FilterEngineProvider::Instance().get()); + + PrettyPrintProperties(m_messageSink, options, EventProperties(event, &decorator)); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/monitor/m_events.h b/windows/winfw/src/extras/cli/commands/monitor/m_events.h new file mode 100644 index 0000000000..e8724129e8 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/monitor/m_events.h @@ -0,0 +1,28 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" +#include <memory> + +namespace commands::monitor +{ + +class Events : public ICommand +{ +public: + + Events(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; + + void eventCallback(const FWPM_NET_EVENT1 &event); +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/winfw/deinit.cpp b/windows/winfw/src/extras/cli/commands/winfw/deinit.cpp new file mode 100644 index 0000000000..d0fc5cfffb --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/winfw/deinit.cpp @@ -0,0 +1,35 @@ +#include "stdafx.h" +#include "deinit.h" +#include "winfw/winfw.h" + +namespace commands::winfw +{ + +Deinit::Deinit(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Deinit::name() +{ + return L"deinit"; +} + +std::wstring Deinit::description() +{ + return L"Deinitialize winfw; Destroy session and all associated objects"; +} + +void Deinit::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (false == arguments.empty()) + { + throw std::runtime_error("Invalid argument(s). Cannot complete request."); + } + + m_messageSink((WinFw_Deinitialize() + ? L"Deinitialization completed successfully." + : L"Deinitialization failed. See above for details, if any.")); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/winfw/deinit.h b/windows/winfw/src/extras/cli/commands/winfw/deinit.h new file mode 100644 index 0000000000..2395f381de --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/winfw/deinit.h @@ -0,0 +1,25 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" + +namespace commands::winfw +{ + +class Deinit : public ICommand +{ +public: + + Deinit(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/winfw/init.cpp b/windows/winfw/src/extras/cli/commands/winfw/init.cpp new file mode 100644 index 0000000000..6236027725 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/winfw/init.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "init.h" +#include "libcommon/string.h" + +namespace commands::winfw +{ + +Init::Init(MessageSink messageSink) + : m_messageSink(messageSink) +{ +} + +std::wstring Init::name() +{ + return L"init"; +} + +std::wstring Init::description() +{ + return L"Initialize winfw; Create session and fundamental objects"; +} + +void Init::handleRequest(const std::vector<std::wstring> &arguments) +{ + uint32_t timeout = 0; + + if (false == arguments.empty()) + { + auto keyvalue = common::string::SplitKeyValuePairs(arguments); + + if (keyvalue.empty() || 0 != keyvalue.begin()->first.compare(L"timeout")) + { + throw std::runtime_error("Invalid argument. Cannot complete request."); + } + + timeout = wcstoul(keyvalue.begin()->second.c_str(), nullptr, 10); + } + + auto success = WinFw_Initialize(timeout, &Init::ErrorForwarder, this); + + m_messageSink((success + ? L"Initialization completed successfully." + : L"Initialization failed. See above for details, if any.")); +} + +//static +void WINFW_API Init::ErrorForwarder(const char *errorMessage, void *context) +{ + auto thiz = reinterpret_cast<Init *>(context); + + thiz->m_messageSink(common::string::ToWide(errorMessage)); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/winfw/init.h b/windows/winfw/src/extras/cli/commands/winfw/init.h new file mode 100644 index 0000000000..f3589535bf --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/winfw/init.h @@ -0,0 +1,28 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" +#include "winfw/winfw.h" + +namespace commands::winfw +{ + +class Init : public ICommand +{ +public: + + Init(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; + + static void WINFW_API ErrorForwarder(const char *errorMessage, void *context); +}; + +} diff --git a/windows/winfw/src/extras/cli/commands/winfw/policy.cpp b/windows/winfw/src/extras/cli/commands/winfw/policy.cpp new file mode 100644 index 0000000000..04906332aa --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/winfw/policy.cpp @@ -0,0 +1,170 @@ +#include "stdafx.h" +#include "policy.h" +#include "libcommon/string.h" +#include "winfw/winfw.h" +#include <functional> + +namespace commands::winfw +{ + +namespace detail +{ + +WinFwSettings CreateSettings(const std::wstring &dhcp, const std::wstring &lan) +{ + WinFwSettings s; + + s.permitDhcp = (0 == _wcsicmp(dhcp.c_str(), L"yes")); + s.permitLan = (0 == _wcsicmp(lan.c_str(), L"yes")); + + return s; +} + +WinFwProtocol TranslateProtocol(const std::wstring &protocol) +{ + return (0 == _wcsicmp(protocol.c_str(), L"tcp") ? WinFwProtocol::Tcp : WinFwProtocol::Udp); +} + +WinFwRelay CreateRelay(const wchar_t *ip, const std::wstring &port, const std::wstring &protocol) +{ + WinFwRelay r; + + r.ip = ip; + r.port = common::string::LexicalCast<uint16_t>(port); + r.protocol = TranslateProtocol(protocol); + + return r; +} + +} // namespace detail + +Policy::Policy(MessageSink messageSink) + : m_messageSink(messageSink) +{ + m_dispatcher.addSubcommand + ( + L"connecting", + std::bind(&Policy::processConnecting, this, std::placeholders::_1) + ); + + m_dispatcher.addSubcommand + ( + L"connected", + std::bind(&Policy::processConnected, this, std::placeholders::_1) + ); + + m_dispatcher.addSubcommand + ( + L"netblocked", + std::bind(&Policy::processNetBlocked, this) + ); + + m_dispatcher.addSubcommand + ( + L"reset", + std::bind(&Policy::processReset, this) + ); +} + +std::wstring Policy::name() +{ + return L"policy"; +} + +std::wstring Policy::description() +{ + return L"Activate and reset policies."; +} + +void Policy::handleRequest(const std::vector<std::wstring> &arguments) +{ + if (arguments.empty()) + { + throw std::runtime_error("Missing subcommand. Cannot complete request."); + } + + auto subcommand = arguments[0]; + + auto actualArguments(arguments); + actualArguments.erase(actualArguments.begin()); + + m_dispatcher.dispatch(subcommand, actualArguments); +} + +void Policy::processConnecting(const KeyValuePairs &arguments) +{ + auto settings = detail::CreateSettings + ( + GetArgumentValue(arguments, L"dhcp"), + GetArgumentValue(arguments, L"lan") + ); + + auto r = GetArgumentValue(arguments, L"relay"); + + auto relay = detail::CreateRelay + ( + r.c_str(), + GetArgumentValue(arguments, L"port"), + GetArgumentValue(arguments, L"protocol") + ); + + auto success = WinFw_ApplyPolicyConnecting + ( + settings, + relay + ); + + m_messageSink((success + ? L"Successfully applied policy." + : L"Failed to apply policy.")); +} + +void Policy::processConnected(const KeyValuePairs &arguments) +{ + auto settings = detail::CreateSettings + ( + GetArgumentValue(arguments, L"dhcp"), + GetArgumentValue(arguments, L"lan") + ); + + auto r = GetArgumentValue(arguments, L"relay"); + + auto relay = detail::CreateRelay + ( + r.c_str(), + GetArgumentValue(arguments, L"port"), + GetArgumentValue(arguments, L"protocol") + ); + + auto success = WinFw_ApplyPolicyConnected + ( + settings, + relay, + GetArgumentValue(arguments, L"tunnel").c_str(), + GetArgumentValue(arguments, L"dns").c_str() + ); + + m_messageSink((success + ? L"Successfully applied policy." + : L"Failed to apply policy.")); +} + +void Policy::processNetBlocked() +{ + auto success = WinFw_ApplyPolicyNetBlocked(); + + m_messageSink((success + ? L"Successfully applied policy." + : L"Failed to apply policy.")); +} + +void Policy::processReset() +{ + auto success = WinFw_Reset(); + + m_messageSink((success + ? L"Successfully reset policy." + : L"Failed to reset policy.")); +} + +} diff --git a/windows/winfw/src/extras/cli/commands/winfw/policy.h b/windows/winfw/src/extras/cli/commands/winfw/policy.h new file mode 100644 index 0000000000..5d4d15e6b1 --- /dev/null +++ b/windows/winfw/src/extras/cli/commands/winfw/policy.h @@ -0,0 +1,35 @@ +#pragma once + +#include "cli/commands/icommand.h" +#include "cli/util.h" +#include "cli/subcommanddispatcher.h" +#include "libcommon/string.h" + +namespace commands::winfw +{ + +class Policy : public ICommand +{ +public: + + Policy(MessageSink messageSink); + + std::wstring name() override; + std::wstring description() override; + + void handleRequest(const std::vector<std::wstring> &arguments) override; + +private: + + MessageSink m_messageSink; + SubcommandDispatcher m_dispatcher; + + using KeyValuePairs = common::string::KeyValuePairs; + + void processConnecting(const KeyValuePairs &arguments); + void processConnected(const KeyValuePairs &arguments); + void processNetBlocked(); + void processReset(); +}; + +} diff --git a/windows/winfw/src/extras/cli/filterengineprovider.h b/windows/winfw/src/extras/cli/filterengineprovider.h new file mode 100644 index 0000000000..50382c7a6c --- /dev/null +++ b/windows/winfw/src/extras/cli/filterengineprovider.h @@ -0,0 +1,39 @@ +#pragma once + +#include "libwfp/filterengine.h" +#include <memory> + +class FilterEngineProvider +{ + FilterEngineProvider() + { + } + +public: + + static FilterEngineProvider &Instance() + { + static FilterEngineProvider provider; + return provider; + } + + std::shared_ptr<wfp::FilterEngine> get() + { + return m_engine; + } + + // + // naive set(), good for now + // + void set(std::shared_ptr<wfp::FilterEngine> engine) + { + m_engine = engine; + } + +private: + + FilterEngineProvider(const FilterEngineProvider &); + FilterEngineProvider &operator=(const FilterEngineProvider &); + + std::shared_ptr<wfp::FilterEngine> m_engine; +}; diff --git a/windows/winfw/src/extras/cli/inlineformatter.h b/windows/winfw/src/extras/cli/inlineformatter.h new file mode 100644 index 0000000000..f00aa6d504 --- /dev/null +++ b/windows/winfw/src/extras/cli/inlineformatter.h @@ -0,0 +1,30 @@ +#pragma once + +#include <sstream> +#include <utility> + +class InlineFormatter +{ +public: + + template<typename T> + InlineFormatter &operator<<(const T &t) + { + m_ss << t; + return *this; + } + + std::wstring str() + { + auto s = m_ss.str(); + + m_ss.str(L""); + m_ss.clear(); + + return s; + } + +private: + + std::wstringstream m_ss; +}; diff --git a/windows/winfw/src/extras/cli/ipropertydecorator.h b/windows/winfw/src/extras/cli/ipropertydecorator.h new file mode 100644 index 0000000000..01a6021ba1 --- /dev/null +++ b/windows/winfw/src/extras/cli/ipropertydecorator.h @@ -0,0 +1,32 @@ +#pragma once + +// +// These methods should be used during property extraction in order to translate +// identifiers (references) into something more meaningful. +// +// Ideally we would nest a PropertyList inside a PropertyList, but that's a rather +// large update since each element in the PropertyList would need to be wrapped. +// If such an update is made one day, be sure to also add a "group" element so +// we can have better structuring within a PropertyList +// + +#include <windows.h> +#include <string> + +struct IPropertyDecorator +{ + // + // These methods should return a short string that adds + // value for human operators/analysts. + // + + virtual std::wstring FilterDecoration(UINT64 id) = 0; + virtual std::wstring LayerDecoration(UINT16 id) = 0; + virtual std::wstring LayerDecoration(const GUID &key) = 0; + virtual std::wstring ProviderDecoration(const GUID &key) = 0; + virtual std::wstring SublayerDecoration(const GUID &key) = 0; + + virtual ~IPropertyDecorator() = 0 + { + } +}; diff --git a/windows/winfw/src/extras/cli/modules/imodule.h b/windows/winfw/src/extras/cli/modules/imodule.h new file mode 100644 index 0000000000..82221f284f --- /dev/null +++ b/windows/winfw/src/extras/cli/modules/imodule.h @@ -0,0 +1,19 @@ +#pragma once + +#include "cli/propertylist.h" +#include <string> +#include <vector> + +namespace modules +{ + +struct IModule +{ + virtual std::wstring name() = 0; + virtual std::wstring description() = 0; + virtual PropertyList commands() = 0; + + virtual void handleRequest(const std::vector<std::wstring> &request) = 0; +}; + +} diff --git a/windows/winfw/src/extras/cli/modules/list.h b/windows/winfw/src/extras/cli/modules/list.h new file mode 100644 index 0000000000..23751c2a26 --- /dev/null +++ b/windows/winfw/src/extras/cli/modules/list.h @@ -0,0 +1,33 @@ +#pragma once + +#include "module.h" +#include "cli/util.h" +#include "cli/commands/list/sessions.h" +#include "cli/commands/list/providers.h" +#include "cli/commands/list/events.h" +#include "cli/commands/list/filters.h" +#include "cli/commands/list/layers.h" +#include "cli/commands/list/providercontexts.h" +#include "cli/commands/list/sublayers.h" + +namespace modules +{ + +class List : public Module +{ +public: + + List(MessageSink messageSink) + : Module(L"list", L"List various objects in the WFP universe.") + { + addCommand(std::make_unique<commands::list::Sessions>(messageSink)); + addCommand(std::make_unique<commands::list::Providers>(messageSink)); + addCommand(std::make_unique<commands::list::Events>(messageSink)); + addCommand(std::make_unique<commands::list::Filters>(messageSink)); + addCommand(std::make_unique<commands::list::Layers>(messageSink)); + addCommand(std::make_unique<commands::list::ProviderContexts>(messageSink)); + addCommand(std::make_unique<commands::list::Sublayers>(messageSink)); + } +}; + +} diff --git a/windows/winfw/src/extras/cli/modules/module.cpp b/windows/winfw/src/extras/cli/modules/module.cpp new file mode 100644 index 0000000000..f66ae5a084 --- /dev/null +++ b/windows/winfw/src/extras/cli/modules/module.cpp @@ -0,0 +1,70 @@ +#include "stdafx.h" +#include "module.h" +#include "cli/util.h" +#include "libcommon/string.h" +#include <sstream> +#include <utility> + +namespace modules +{ + +PropertyList Module::commands() +{ + PropertyList c; + + for (auto &command : m_commands) + { + c.add(common::string::Lower(command.second->name()), command.second->description()); + } + + return c; +} + +void Module::handleRequest(const std::vector<std::wstring> &request) +{ + // + // The request has the form of: + // + // [0] command + // [1] arg1 + // [2] arg2 + // ... + // + + if (request.empty()) + { + std::wstringstream ss; + + ss << L"Command missing. Try 'help " << m_name << "'."; + + throw std::runtime_error(common::string::ToAnsi(ss.str())); + } + + auto wanted = common::string::Lower(request[0]); + auto found = m_commands.find(wanted); + + if (found == m_commands.end()) + { + std::wstringstream ss; + + ss << L"Module '" << m_name << "' doesn't support the command '" << request[0] << "'."; + + throw std::runtime_error(common::string::ToAnsi(ss.str())); + } + + auto args = request; + + args.erase(args.begin()); + + found->second->handleRequest(args); +} + +void Module::addCommand(std::unique_ptr<commands::ICommand> command) +{ + m_commands.insert(std::make_pair( + common::string::Lower(command->name()), + std::move(command) + )); +} + +} diff --git a/windows/winfw/src/extras/cli/modules/module.h b/windows/winfw/src/extras/cli/modules/module.h new file mode 100644 index 0000000000..45587ab7e5 --- /dev/null +++ b/windows/winfw/src/extras/cli/modules/module.h @@ -0,0 +1,50 @@ +#pragma once + +#include "imodule.h" +#include "cli/commands/icommand.h" +#include <map> +#include <memory> + +namespace modules +{ + +class Module : public IModule +{ +public: + + Module(const std::wstring &name, const std::wstring &description) + : m_name(name) + , m_description(description) + { + } + + std::wstring name() override + { + return m_name; + } + + std::wstring description() override + { + return m_description; + } + + // Collect name and description from commands. + PropertyList commands() override; + + // Identify requested command and dispatch to it. + void handleRequest(const std::vector<std::wstring> &request) override; + + void addCommand(std::unique_ptr<commands::ICommand> command); + +private: + + Module(const Module &); + Module &operator=(const Module &); + + std::wstring m_name; + std::wstring m_description; + + std::map<std::wstring, std::unique_ptr<commands::ICommand> > m_commands; +}; + +} diff --git a/windows/winfw/src/extras/cli/modules/monitor.h b/windows/winfw/src/extras/cli/modules/monitor.h new file mode 100644 index 0000000000..e227192e82 --- /dev/null +++ b/windows/winfw/src/extras/cli/modules/monitor.h @@ -0,0 +1,21 @@ +#pragma once + +#include "module.h" +#include "cli/util.h" +#include "cli/commands/monitor/m_events.h" + +namespace modules +{ + +class Monitor : public Module +{ +public: + + Monitor(MessageSink messageSink) + : Module(L"monitor", L"Real-time monitoring of events and object creation/deletion in WFP.") + { + addCommand(std::make_unique<commands::monitor::Events>(messageSink)); + } +}; + +} diff --git a/windows/winfw/src/extras/cli/modules/winfw.h b/windows/winfw/src/extras/cli/modules/winfw.h new file mode 100644 index 0000000000..33d5621fe9 --- /dev/null +++ b/windows/winfw/src/extras/cli/modules/winfw.h @@ -0,0 +1,25 @@ +#pragma once + +#include "module.h" +#include "cli/util.h" +#include "cli/commands/winfw/init.h" +#include "cli/commands/winfw/deinit.h" +#include "cli/commands/winfw/policy.h" + +namespace modules +{ + +class WinFw : public Module +{ +public: + + WinFw(MessageSink messageSink) + : Module(L"winfw", L"Exercise functionality provided by \"winfw.dll\".") + { + addCommand(std::make_unique<commands::winfw::Init>(messageSink)); + addCommand(std::make_unique<commands::winfw::Deinit>(messageSink)); + addCommand(std::make_unique<commands::winfw::Policy>(messageSink)); + } +}; + +} diff --git a/windows/winfw/src/extras/cli/objectproperties.cpp b/windows/winfw/src/extras/cli/objectproperties.cpp new file mode 100644 index 0000000000..8b7b5a9c04 --- /dev/null +++ b/windows/winfw/src/extras/cli/objectproperties.cpp @@ -0,0 +1,709 @@ +#include "stdafx.h" +#include "objectproperties.h" +#include "inlineformatter.h" +#include "propertylist.h" +#include "libcommon/string.h" +#include <string> +#include <utility> +#include <vector> +#include <cwchar> + +// Add missing constants +// These are documented in MSDN but not defined in any header? +#define FWP_DIRECTION_IN 0x00003900L +#define FWP_DIRECTION_OUT 0x00003901L +#define FWP_DIRECTION_FORWARD 0x00003902L + +namespace detail +{ + +std::wstring SessionFlags(UINT32 flags) +{ + std::vector<std::pair<UINT32, std::wstring> > definitions = + { + std::make_pair(FWPM_SESSION_FLAG_DYNAMIC, L"FWPM_SESSION_FLAG_DYNAMIC"), + std::make_pair(FWPM_SESSION_FLAG_RESERVED, L"FWPM_SESSION_FLAG_RESERVED") + }; + + return common::string::FormatFlags(definitions, flags); +} + +std::wstring ProviderFlags(UINT32 flags) +{ + std::vector<std::pair<UINT32, std::wstring> > definitions = + { + std::make_pair(FWPM_PROVIDER_FLAG_PERSISTENT, L"FWPM_PROVIDER_FLAG_PERSISTENT"), + std::make_pair(FWPM_PROVIDER_FLAG_DISABLED, L"FWPM_PROVIDER_FLAG_DISABLED") + }; + + return common::string::FormatFlags(definitions, flags); +} + +std::wstring FormatIpProtocol(UINT8 protocol) +{ + switch (protocol) + { + case 0: return L"IPPROTO_HOPOPTS"; + case 1: return L"IPPROTO_ICMP"; + case 2: return L"IPPROTO_IGMP"; + case 3: return L"IPPROTO_GGP"; + case 4: return L"IPPROTO_IPV4"; + case 5: return L"IPPROTO_ST"; + case 6: return L"IPPROTO_TCP"; + case 7: return L"IPPROTO_CBT"; + case 8: return L"IPPROTO_EGP"; + case 9: return L"IPPROTO_IGP"; + case 12: return L"IPPROTO_PUP"; + case 17: return L"IPPROTO_UDP"; + case 22: return L"IPPROTO_IDP"; + case 27: return L"IPPROTO_RDP"; + case 41: return L"IPPROTO_IPV6"; + case 43: return L"IPPROTO_ROUTING"; + case 44: return L"IPPROTO_FRAGMENT"; + case 50: return L"IPPROTO_ESP"; + case 51: return L"IPPROTO_AH"; + case 58: return L"IPPROTO_ICMPV6"; + case 59: return L"IPPROTO_NONE"; + case 60: return L"IPPROTO_DSTOPTS"; + case 77: return L"IPPROTO_ND"; + case 78: return L"IPPROTO_ICLFXBM"; + case 103: return L"IPPROTO_PIM"; + case 113: return L"IPPROTO_PGM"; + case 115: return L"IPPROTO_L2TP"; + case 132: return L"IPPROTO_SCTP"; + case 255: return L"IPPROTO_RAW"; + default: return L"Unknown"; + }; +} + +std::wstring FilterFlags(UINT32 flags) +{ + std::vector<std::pair<UINT32, std::wstring> > definitions = + { + std::make_pair(FWPM_FILTER_FLAG_PERSISTENT, L"FWPM_FILTER_FLAG_PERSISTENT"), + std::make_pair(FWPM_FILTER_FLAG_BOOTTIME, L"FWPM_FILTER_FLAG_BOOTTIME"), + std::make_pair(FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT, L"FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT"), + std::make_pair(FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED, L"FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED"), + std::make_pair(FWPM_FILTER_FLAG_DISABLED, L"FWPM_FILTER_FLAG_DISABLED"), + std::make_pair(FWPM_FILTER_FLAG_INDEXED, L"FWPM_FILTER_FLAG_INDEXED") + }; + + return common::string::FormatFlags(definitions, flags); +} + +std::wstring LayerFlags(UINT32 flags) +{ + std::vector<std::pair<UINT32, std::wstring> > definitions = + { + std::make_pair(FWPM_LAYER_FLAG_KERNEL, L"FWPM_LAYER_FLAG_KERNEL"), + std::make_pair(FWPM_LAYER_FLAG_BUILTIN, L"FWPM_LAYER_FLAG_BUILTIN"), + std::make_pair(FWPM_LAYER_FLAG_CLASSIFY_MOSTLY, L"FWPM_LAYER_FLAG_CLASSIFY_MOSTLY"), + std::make_pair(FWPM_LAYER_FLAG_BUFFERED, L"FWPM_LAYER_FLAG_BUFFERED") + }; + + return common::string::FormatFlags(definitions, flags); +} + +std::wstring ProviderContextType(FWPM_PROVIDER_CONTEXT_TYPE type) +{ + switch (type) + { + case FWPM_IPSEC_KEYING_CONTEXT: return L"FWPM_IPSEC_KEYING_CONTEXT"; + case FWPM_IPSEC_IKE_QM_TRANSPORT_CONTEXT: return L"FWPM_IPSEC_IKE_QM_TRANSPORT_CONTEXT"; + case FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT: return L"FWPM_IPSEC_IKE_QM_TUNNEL_CONTEXT"; + case FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT: return L"FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT"; + case FWPM_IPSEC_AUTHIP_QM_TUNNEL_CONTEXT: return L"FWPM_IPSEC_AUTHIP_QM_TUNNEL_CONTEXT"; + case FWPM_IPSEC_IKE_MM_CONTEXT: return L"FWPM_IPSEC_IKE_MM_CONTEXT"; + case FWPM_IPSEC_AUTHIP_MM_CONTEXT: return L"FWPM_IPSEC_AUTHIP_MM_CONTEXT"; + case FWPM_CLASSIFY_OPTIONS_CONTEXT: return L"FWPM_CLASSIFY_OPTIONS_CONTEXT"; + case FWPM_GENERAL_CONTEXT: return L"FWPM_GENERAL_CONTEXT"; + case FWPM_IPSEC_IKEV2_QM_TUNNEL_CONTEXT: return L"FWPM_IPSEC_IKEV2_QM_TUNNEL_CONTEXT"; + case FWPM_IPSEC_IKEV2_MM_CONTEXT: return L"FWPM_IPSEC_IKEV2_MM_CONTEXT"; + case FWPM_IPSEC_DOSP_CONTEXT: return L"FWPM_IPSEC_DOSP_CONTEXT"; + case FWPM_IPSEC_IKEV2_QM_TRANSPORT_CONTEXT: return L"FWPM_IPSEC_IKEV2_QM_TRANSPORT_CONTEXT"; + default: return L"[Unknown]"; + } +} + +std::wstring Direction(UINT32 direction) +{ + switch (direction) + { + case FWP_DIRECTION_IN: return L"In"; + case FWP_DIRECTION_OUT: return L"Out"; + case FWP_DIRECTION_FORWARD: return L"Forward"; + default: return L"[Unknown]"; + } +} + +std::wstring FilterDecoration(IPropertyDecorator *decorator, UINT64 id) +{ + if (nullptr == decorator) + { + return L""; + } + + return (InlineFormatter() << L" " << decorator->FilterDecoration(id)).str(); +} + +std::wstring LayerDecoration(IPropertyDecorator *decorator, UINT16 id) +{ + if (nullptr == decorator) + { + return L""; + } + + return (InlineFormatter() << L" " << decorator->LayerDecoration(id)).str(); +} + +std::wstring LayerDecoration(IPropertyDecorator *decorator, const GUID &key) +{ + if (nullptr == decorator) + { + return L""; + } + + return (InlineFormatter() << L" " << decorator->LayerDecoration(key)).str(); +} + +std::wstring ProviderDecoration(IPropertyDecorator *decorator, const GUID &key) +{ + if (nullptr == decorator) + { + return L""; + } + + return (InlineFormatter() << L" " << decorator->ProviderDecoration(key)).str(); +} + +std::wstring SublayerDecoration(IPropertyDecorator *decorator, const GUID &key) +{ + if (nullptr == decorator) + { + return L""; + } + + return (InlineFormatter() << L" " << decorator->SublayerDecoration(key)).str(); +} + +void AddStringProperty(PropertyList &props, const wchar_t *name, const wchar_t *value) +{ + if (nullptr == value || 0 == wcslen(value)) + { + return; + } + + props.add(name, value); +} + +// This won't work because sometimes 0 is a valid flag value +//template<typename T> +//void AddFlagProperty(PropertyList &props, const std::wstring &name, T value, std::function<std::wstring(T)> formatter> + +} // namespace detail + +PropertyList SessionProperties(const FWPM_SESSION0 &session) +{ + PropertyList props; + InlineFormatter f; + + props.add(L"key", common::string::FormatGuid(session.sessionKey)); + detail::AddStringProperty(props, L"name", session.displayData.name); + detail::AddStringProperty(props, L"description", session.displayData.description); + + props.add(L"flags", (f << session.flags << L" = " << detail::SessionFlags(session.flags)).str()); + props.add(L"wait timeout", (f << session.txnWaitTimeoutInMSec).str()); + props.add(L"sid", common::string::FormatSid(*session.sid)); + props.add(L"username", session.username); + props.add(L"kernel", (session.kernelMode ? L"true" : L"false")); + + return props; +} + +PropertyList ProviderProperties(const FWPM_PROVIDER0 &provider) +{ + PropertyList props; + InlineFormatter f; + + props.add(L"key", common::string::FormatGuid(provider.providerKey)); + detail::AddStringProperty(props, L"name", provider.displayData.name); + detail::AddStringProperty(props, L"description", provider.displayData.description); + + props.add(L"flags", (f << provider.flags << L" = " << detail::ProviderFlags(provider.flags)).str()); + + if (0 != provider.providerData.size) + { + props.add(L"provider data", (f << L"Present (" << provider.providerData.size << L" bytes)").str()); + } + + detail::AddStringProperty(props, L"service name", provider.serviceName); + + return props; +} + +PropertyList EventProperties(const FWPM_NET_EVENT0 &event, IPropertyDecorator *decorator) +{ + PropertyList props; + InlineFormatter f; + + props.add(L"timestamp", common::string::FormatTime(event.header.timeStamp)); + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET)) + { + props.add(L"protocol", detail::FormatIpProtocol(event.header.ipProtocol)); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET) + && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET)) + { + if (event.header.ipVersion == FWP_IP_VERSION_V4) + { + props.add(L"local addr", common::string::FormatIpv4(event.header.localAddrV4)); + } + else + { + props.add(L"local addr", common::string::FormatIpv6(event.header.localAddrV6.byteArray16)); + } + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET) + && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET)) + { + if (event.header.ipVersion == FWP_IP_VERSION_V4) + { + props.add(L"remote addr", common::string::FormatIpv4(event.header.remoteAddrV4)); + } + else + { + props.add(L"remote addr", common::string::FormatIpv6(event.header.remoteAddrV6.byteArray16)); + } + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)) + { + props.add(L"local port", (f << event.header.localPort).str()); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET)) + { + props.add(L"remote port", (f << event.header.remotePort).str()); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_APP_ID_SET)) + { + auto begin = reinterpret_cast<wchar_t *>(event.header.appId.data); + auto end = begin + (event.header.appId.size / sizeof(wchar_t)); + + props.add(L"app id", std::wstring(begin, end)); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_USER_ID_SET)) + { + props.add(L"user id", common::string::FormatSid(*event.header.userId)); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_SCOPE_ID_SET)) + { + props.add(L"IPv6 scope id", (f << event.header.scopeId).str()); + } + + switch (event.type) + { + case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE: + { + props.add(L"type", L"IKEEXT_MM_FAILURE"); + break; + } + case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE: + { + props.add(L"type", L"IKEEXT_QM_FAILURE"); + break; + } + case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE: + { + props.add(L"type", L"IKEEXT_EM_FAILURE"); + break; + } + case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP: + { + props.add(L"type", L"CLASSIFY_DROP"); + + props.add(L"filter id", (f << event.classifyDrop->filterId + << detail::FilterDecoration(decorator, event.classifyDrop->filterId)).str()); + + props.add(L"layer id", (f << event.classifyDrop->layerId + << detail::LayerDecoration(decorator, event.classifyDrop->layerId)).str()); + + break; + } + case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP: + { + props.add(L"type", L"IPSEC_KERNEL_DROP"); + break; + } + case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP: + { + props.add(L"type", L"IPSEC_DOSP_DROP"); + break; + } + case FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW: + { + props.add(L"type", L"CLASSIFY_ALLOW"); + break; + } + case FWPM_NET_EVENT_TYPE_CAPABILITY_DROP: + { + props.add(L"type", L"CAPABILITY_DROP"); + break; + } + case FWPM_NET_EVENT_TYPE_CAPABILITY_ALLOW: + { + props.add(L"type", L"CAPABILITY_ALLOW"); + break; + } + case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC: + { + props.add(L"type", L"CLASSIFY_DROP_MAC"); + break; + } + default: + { + props.add(L"type", L"Unknown"); + } + }; + + return props; +} + +PropertyList EventProperties(const FWPM_NET_EVENT1 &event, IPropertyDecorator *decorator) +{ + // + // TODO-MAYBE: Restructure code to operate on individual elements of the structure + // then use upcasting and a single implementation for extracting the basic information. + // + + PropertyList props; + InlineFormatter f; + + props.add(L"timestamp", common::string::FormatTime(event.header.timeStamp)); + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_PROTOCOL_SET)) + { + props.add(L"protocol", detail::FormatIpProtocol(event.header.ipProtocol)); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET) + && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_ADDR_SET)) + { + if (event.header.ipVersion == FWP_IP_VERSION_V4) + { + props.add(L"local addr", common::string::FormatIpv4(event.header.localAddrV4)); + } + else + { + props.add(L"local addr", common::string::FormatIpv6(event.header.localAddrV6.byteArray16)); + } + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_IP_VERSION_SET) + && 0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_ADDR_SET)) + { + if (event.header.ipVersion == FWP_IP_VERSION_V4) + { + props.add(L"remote addr", common::string::FormatIpv4(event.header.remoteAddrV4)); + } + else + { + props.add(L"remote addr", common::string::FormatIpv6(event.header.remoteAddrV6.byteArray16)); + } + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_LOCAL_PORT_SET)) + { + props.add(L"local port", (f << event.header.localPort).str()); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_REMOTE_PORT_SET)) + { + props.add(L"remote port", (f << event.header.remotePort).str()); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_APP_ID_SET)) + { + auto begin = reinterpret_cast<wchar_t *>(event.header.appId.data); + auto end = begin + (event.header.appId.size / sizeof(wchar_t)); + + props.add(L"app id", std::wstring(begin, end)); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_USER_ID_SET)) + { + props.add(L"user id", common::string::FormatSid(*event.header.userId)); + } + + if (0 != (event.header.flags & FWPM_NET_EVENT_FLAG_SCOPE_ID_SET)) + { + props.add(L"IPv6 scope id", (f << event.header.scopeId).str()); + } + + switch (event.type) + { + case FWPM_NET_EVENT_TYPE_IKEEXT_MM_FAILURE: + { + props.add(L"type", L"IKEEXT_MM_FAILURE"); + break; + } + case FWPM_NET_EVENT_TYPE_IKEEXT_QM_FAILURE: + { + props.add(L"type", L"IKEEXT_QM_FAILURE"); + break; + } + case FWPM_NET_EVENT_TYPE_IKEEXT_EM_FAILURE: + { + props.add(L"type", L"IKEEXT_EM_FAILURE"); + break; + } + case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP: + { + props.add(L"type", L"CLASSIFY_DROP"); + + props.add(L"filter id", (f << event.classifyDrop->filterId + << detail::FilterDecoration(decorator, event.classifyDrop->filterId)).str()); + + props.add(L"layer id", (f << event.classifyDrop->layerId + << detail::LayerDecoration(decorator, event.classifyDrop->layerId)).str()); + + props.add(L"direction", detail::Direction(event.classifyDrop->msFwpDirection)); + + if (1 == event.classifyDrop->isLoopback) + { + props.add(L"loopback", L"True"); + } + + break; + } + case FWPM_NET_EVENT_TYPE_IPSEC_KERNEL_DROP: + { + props.add(L"type", L"IPSEC_KERNEL_DROP"); + break; + } + case FWPM_NET_EVENT_TYPE_IPSEC_DOSP_DROP: + { + props.add(L"type", L"IPSEC_DOSP_DROP"); + break; + } + case FWPM_NET_EVENT_TYPE_CLASSIFY_ALLOW: + { + props.add(L"type", L"CLASSIFY_ALLOW"); + break; + } + case FWPM_NET_EVENT_TYPE_CAPABILITY_DROP: + { + props.add(L"type", L"CAPABILITY_DROP"); + break; + } + case FWPM_NET_EVENT_TYPE_CAPABILITY_ALLOW: + { + props.add(L"type", L"CAPABILITY_ALLOW"); + break; + } + case FWPM_NET_EVENT_TYPE_CLASSIFY_DROP_MAC: + { + props.add(L"type", L"CLASSIFY_DROP_MAC"); + break; + } + default: + { + props.add(L"type", L"Unknown"); + } + }; + + return props; +} + +PropertyList FilterProperties(const FWPM_FILTER0 &filter, IPropertyDecorator *decorator) +{ + PropertyList props; + InlineFormatter f; + + props.add(L"key", common::string::FormatGuid(filter.filterKey)); + detail::AddStringProperty(props, L"name", filter.displayData.name); + detail::AddStringProperty(props, L"description", filter.displayData.description); + + props.add(L"flags", (f << filter.flags << L" = " << detail::FilterFlags(filter.flags)).str()); + + if (nullptr != filter.providerKey) + { + props.add(L"provider key", (f << common::string::FormatGuid(*filter.providerKey) + << detail::ProviderDecoration(decorator, *filter.providerKey)).str()); + } + + if (0 != filter.providerData.size) + { + props.add(L"provider data", (f << L"Present (" << filter.providerData.size << L" bytes)").str()); + } + + props.add(L"layer key", (f << common::string::FormatGuid(filter.layerKey) + << detail::LayerDecoration(decorator, filter.layerKey)).str()); + + props.add(L"sublayer key", (f << common::string::FormatGuid(filter.subLayerKey) + << detail::SublayerDecoration(decorator, filter.subLayerKey)).str()); + + if (FWP_UINT64 == filter.weight.type) + { + props.add(L"weight", (f << *filter.weight.uint64 << L" (exact)").str()); + } + else if (FWP_UINT8 == filter.weight.type) + { + props.add(L"weight", (f << filter.weight.uint8 << L" (relative 0-15)").str()); + } + else + { + props.add(L"weight", L"Automatic"); + } + + props.add(L"num conditions", (f << filter.numFilterConditions).str()); + props.add(L"conditions", L"TODO"); + + if (FWP_ACTION_BLOCK == filter.action.type) + { + props.add(L"action", L"Block"); + props.add(L"filter type", common::string::FormatGuid(filter.action.filterType)); + } + else if (FWP_ACTION_PERMIT == filter.action.type) + { + props.add(L"action", L"Permit"); + props.add(L"filter type", common::string::FormatGuid(filter.action.filterType)); + } + else if (FWP_ACTION_CALLOUT_TERMINATING == filter.action.type) + { + props.add(L"action", L"Callout terminating"); + props.add(L"callout key", common::string::FormatGuid(filter.action.calloutKey)); + } + else if (FWP_ACTION_CALLOUT_INSPECTION == filter.action.type) + { + props.add(L"action", L"Callout inspection"); + props.add(L"callout key", common::string::FormatGuid(filter.action.calloutKey)); + } + else if (FWP_ACTION_CALLOUT_UNKNOWN == filter.action.type) + { + props.add(L"action", L"Callout unknown"); + props.add(L"callout key", common::string::FormatGuid(filter.action.calloutKey)); + } + + if (0 != (filter.flags & FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT)) + { + props.add(L"context", L"Provider context"); + } + else + { + props.add(L"context", L"Raw context"); + } + + props.add(L"filter id", (f << filter.filterId).str()); + + if (FWP_UINT64 == filter.effectiveWeight.type) + { + props.add(L"effective weight", (f << *filter.effectiveWeight.uint64 << L" (exact)").str()); + } + else if (FWP_UINT8 == filter.effectiveWeight.type) + { + props.add(L"effective weight", (f << filter.effectiveWeight.uint8 << L" (relative 0-15)").str()); + } + else + { + props.add(L"effective weight", L"Automatic"); + } + + return props; +} + +PropertyList LayerProperties(const FWPM_LAYER0 &layer, IPropertyDecorator *decorator) +{ + PropertyList props; + InlineFormatter f; + + props.add(L"key", common::string::FormatGuid(layer.layerKey)); + detail::AddStringProperty(props, L"name", layer.displayData.name); + detail::AddStringProperty(props, L"description", layer.displayData.description); + + props.add(L"flags", (f << layer.flags << L" = " << detail::LayerFlags(layer.flags)).str()); + props.add(L"num fields", (f << layer.numFields).str()); + props.add(L"field array", L"TODO"); + + props.add(L"default sublayer", (f << common::string::FormatGuid(layer.defaultSubLayerKey) + << detail::SublayerDecoration(decorator, layer.defaultSubLayerKey)).str()); + + props.add(L"layer id", (f << layer.layerId).str()); + + return props; +} + +PropertyList ProviderContextProperties(const FWPM_PROVIDER_CONTEXT0 &context, IPropertyDecorator *decorator) +{ + PropertyList props; + InlineFormatter f; + + props.add(L"key", common::string::FormatGuid(context.providerContextKey)); + detail::AddStringProperty(props, L"name", context.displayData.name); + detail::AddStringProperty(props, L"description", context.displayData.description); + + if (0 != (context.flags & FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT)) + { + props.add(L"flags", L"FWPM_PROVIDER_CONTEXT_FLAG_PERSISTENT"); + } + else + { + props.add(L"flags", (f << context.flags).str()); + } + + if (nullptr != context.providerKey) + { + props.add(L"provider key", (f << common::string::FormatGuid(*context.providerKey) + << detail::ProviderDecoration(decorator, *context.providerKey)).str()); + } + + if (0 != context.providerData.size) + { + props.add(L"provider data", (f << L"Present (" << context.providerData.size << L" bytes)").str()); + } + + props.add(L"context type", detail::ProviderContextType(context.type)); + props.add(L"id", (f << context.providerContextId).str()); + + return props; +} + +PropertyList SublayerProperties(const FWPM_SUBLAYER0 &sublayer, IPropertyDecorator *decorator) +{ + PropertyList props; + InlineFormatter f; + + props.add(L"key", common::string::FormatGuid(sublayer.subLayerKey)); + detail::AddStringProperty(props, L"name", sublayer.displayData.name); + detail::AddStringProperty(props, L"description", sublayer.displayData.description); + + if (0 != (sublayer.flags & FWPM_SUBLAYER_FLAG_PERSISTENT)) + { + props.add(L"flags", L"FWPM_SUBLAYER_FLAG_PERSISTENT"); + } + else + { + props.add(L"flags", (f << sublayer.flags).str()); + } + + if (nullptr != sublayer.providerKey) + { + props.add(L"provider key", (f << common::string::FormatGuid(*sublayer.providerKey) + << detail::ProviderDecoration(decorator, *sublayer.providerKey)).str()); + } + + if (0 != sublayer.providerData.size) + { + props.add(L"provider data", (f << L"Present (" << sublayer.providerData.size << L" bytes)").str()); + } + + props.add(L"weight", (f << sublayer.weight).str()); + + return props; +} diff --git a/windows/winfw/src/extras/cli/objectproperties.h b/windows/winfw/src/extras/cli/objectproperties.h new file mode 100644 index 0000000000..a4f349f828 --- /dev/null +++ b/windows/winfw/src/extras/cli/objectproperties.h @@ -0,0 +1,15 @@ +#pragma once + +#include "propertylist.h" +#include "ipropertydecorator.h" +#include <windows.h> +#include <fwpmu.h> + +PropertyList SessionProperties(const FWPM_SESSION0 &session); +PropertyList ProviderProperties(const FWPM_PROVIDER0 &provider); +PropertyList EventProperties(const FWPM_NET_EVENT0 &event, IPropertyDecorator *decorator = nullptr); +PropertyList EventProperties(const FWPM_NET_EVENT1 &event, IPropertyDecorator *decorator = nullptr); +PropertyList FilterProperties(const FWPM_FILTER0 &filter, IPropertyDecorator *decorator = nullptr); +PropertyList LayerProperties(const FWPM_LAYER0 &layer, IPropertyDecorator *decorator = nullptr); +PropertyList ProviderContextProperties(const FWPM_PROVIDER_CONTEXT0 &context, IPropertyDecorator *decorator = nullptr); +PropertyList SublayerProperties(const FWPM_SUBLAYER0 &sublayer, IPropertyDecorator *decorator = nullptr); diff --git a/windows/winfw/src/extras/cli/propertydecorator.cpp b/windows/winfw/src/extras/cli/propertydecorator.cpp new file mode 100644 index 0000000000..115c628c27 --- /dev/null +++ b/windows/winfw/src/extras/cli/propertydecorator.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include "propertydecorator.h" +#include "libwfp/objectexplorer.h" +#include "libcommon/string.h" +#include "inlineformatter.h" +#include <cwchar> + +namespace detail +{ + +std::wstring Format(const wchar_t *in_name, const wchar_t *in_desc) +{ + auto haveName = (nullptr != in_name && 0 != wcslen(in_name)); + auto haveDesc = (nullptr != in_desc && 0 != wcslen(in_desc)); + + std::wstring name = (haveName ? in_name : L"n/a"); + std::wstring desc = (haveDesc ? common::string::Summary(in_desc, 50) : L"n/a"); + + return (InlineFormatter() << L"[" << name << L", " << desc << L"]").str(); +} + +} // namespace detail + +PropertyDecorator::PropertyDecorator(std::shared_ptr<wfp::FilterEngine> engine) + : m_engine(engine) +{ +} + +std::wstring PropertyDecorator::FilterDecoration(UINT64 id) +{ + std::wstring brief = L"[n/a]"; + + wfp::ObjectExplorer::GetFilter(*m_engine, id, [&brief](const FWPM_FILTER0 &filter) + { + brief = detail::Format(filter.displayData.name, filter.displayData.description); + return true; + }); + + return brief; +} + +std::wstring PropertyDecorator::LayerDecoration(UINT16 id) +{ + std::wstring brief = L"[n/a]"; + + wfp::ObjectExplorer::GetLayer(*m_engine, id, [&brief](const FWPM_LAYER0 &layer) + { + brief = detail::Format(layer.displayData.name, layer.displayData.description); + return true; + }); + + return brief; +} + +std::wstring PropertyDecorator::LayerDecoration(const GUID &key) +{ + std::wstring brief = L"[n/a]"; + + wfp::ObjectExplorer::GetLayer(*m_engine, key, [&brief](const FWPM_LAYER0 &layer) + { + brief = detail::Format(layer.displayData.name, layer.displayData.description); + return true; + }); + + return brief; +} + +std::wstring PropertyDecorator::ProviderDecoration(const GUID &key) +{ + std::wstring brief = L"[n/a]"; + + wfp::ObjectExplorer::GetProvider(*m_engine, key, [&brief](const FWPM_PROVIDER0 &provider) + { + brief = detail::Format(provider.displayData.name, provider.displayData.description); + return true; + }); + + return brief; +} + +std::wstring PropertyDecorator::SublayerDecoration(const GUID &key) +{ + std::wstring brief = L"[n/a]"; + + wfp::ObjectExplorer::GetSublayer(*m_engine, key, [&brief](const FWPM_SUBLAYER0 &sublayer) + { + brief = detail::Format(sublayer.displayData.name, sublayer.displayData.description); + return true; + }); + + return brief; +} diff --git a/windows/winfw/src/extras/cli/propertydecorator.h b/windows/winfw/src/extras/cli/propertydecorator.h new file mode 100644 index 0000000000..b021d275e1 --- /dev/null +++ b/windows/winfw/src/extras/cli/propertydecorator.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ipropertydecorator.h" +#include "libwfp/filterengine.h" +#include <memory> + +class PropertyDecorator : public IPropertyDecorator +{ +public: + + PropertyDecorator(std::shared_ptr<wfp::FilterEngine> engine); + + // + // The format of the returned string: + // [name, first 50 chars of description] + // + + std::wstring FilterDecoration(UINT64 id) override; + std::wstring LayerDecoration(UINT16 id) override; + std::wstring LayerDecoration(const GUID &key) override; + std::wstring ProviderDecoration(const GUID &key) override; + std::wstring SublayerDecoration(const GUID &key) override; + +private: + + std::shared_ptr<wfp::FilterEngine> m_engine; +}; diff --git a/windows/winfw/src/extras/cli/propertylist.h b/windows/winfw/src/extras/cli/propertylist.h new file mode 100644 index 0000000000..b5d9d71499 --- /dev/null +++ b/windows/winfw/src/extras/cli/propertylist.h @@ -0,0 +1,55 @@ +#pragma once + +#include <string> +#include <utility> +#include <vector> + +class PropertyList +{ +public: + + struct Property + { + Property(const std::wstring &n, const std::wstring &v) + : name(n), value(v) + { + } + + Property(std::wstring &&n, std::wstring &&v) + : name(n), value(v) + { + } + + std::wstring name; + std::wstring value; + }; + + void add(const std::wstring &name, const std::wstring &value) + { + m_properties.emplace_back(name, value); + } + + void add(std::wstring &&name, std::wstring &&value) + { + m_properties.emplace_back(name, value); + } + + const std::vector<Property> &list() + { + return m_properties; + } + + std::vector<Property>::const_iterator begin() const + { + return m_properties.begin(); + } + + std::vector<Property>::const_iterator end() const + { + return m_properties.end(); + } + +private: + + std::vector<Property> m_properties; +}; diff --git a/windows/winfw/src/extras/cli/stdafx.cpp b/windows/winfw/src/extras/cli/stdafx.cpp new file mode 100644 index 0000000000..62355160c9 --- /dev/null +++ b/windows/winfw/src/extras/cli/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// cli.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/windows/winfw/src/extras/cli/stdafx.h b/windows/winfw/src/extras/cli/stdafx.h new file mode 100644 index 0000000000..b005a839de --- /dev/null +++ b/windows/winfw/src/extras/cli/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include <stdio.h> +#include <tchar.h> + + + +// TODO: reference additional headers your program requires here diff --git a/windows/winfw/src/extras/cli/subcommanddispatcher.cpp b/windows/winfw/src/extras/cli/subcommanddispatcher.cpp new file mode 100644 index 0000000000..4bb7868cd7 --- /dev/null +++ b/windows/winfw/src/extras/cli/subcommanddispatcher.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" +#include "subcommanddispatcher.h" +#include "libcommon/string.h" +#include <sstream> +#include <stdexcept> +#include <utility> + +void SubcommandDispatcher::addSubcommand(const std::wstring &command, Handler handler) +{ + m_commands.insert(std::make_pair(command, handler)); +} + +void SubcommandDispatcher::dispatch(const std::wstring &command, const std::vector<std::wstring> &arguments) +{ + auto selectedCommand = m_commands.find(command); + + if (m_commands.end() == selectedCommand) + { + std::wstringstream ss; + + ss << L"Unsupported subcommand '" << command << "'. Cannot complete request."; + + throw std::runtime_error(common::string::ToAnsi(ss.str()).c_str()); + } + + selectedCommand->second(common::string::SplitKeyValuePairs(arguments)); +} diff --git a/windows/winfw/src/extras/cli/subcommanddispatcher.h b/windows/winfw/src/extras/cli/subcommanddispatcher.h new file mode 100644 index 0000000000..a480ec8104 --- /dev/null +++ b/windows/winfw/src/extras/cli/subcommanddispatcher.h @@ -0,0 +1,20 @@ +#pragma once + +#include "libcommon/string.h" +#include <functional> +#include <string> +#include <unordered_map> + +class SubcommandDispatcher +{ + typedef std::function<void(const common::string::KeyValuePairs &)> Handler; + +public: + + void addSubcommand(const std::wstring &command, Handler handler); + void dispatch(const std::wstring &command, const std::vector<std::wstring> &arguments); + +private: + + std::unordered_map<std::wstring, Handler> m_commands; +}; diff --git a/windows/winfw/src/extras/cli/targetver.h b/windows/winfw/src/extras/cli/targetver.h new file mode 100644 index 0000000000..ae4a5c032c --- /dev/null +++ b/windows/winfw/src/extras/cli/targetver.h @@ -0,0 +1,12 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include <WinSDKVer.h> + +#define _WIN32_WINNT _WIN32_WINNT_WIN7 + +#include <SDKDDKVer.h> diff --git a/windows/winfw/src/extras/cli/util.cpp b/windows/winfw/src/extras/cli/util.cpp new file mode 100644 index 0000000000..6181e68639 --- /dev/null +++ b/windows/winfw/src/extras/cli/util.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "util.h" +#include "inlineformatter.h" +#include "libcommon/string.h" +#include <string> + +void PrettyPrintProperties(MessageSink messageSink, PrettyPrintOptions options, const PropertyList &properties) +{ + size_t longestName = 0; + + for (auto &property : properties) + { + longestName = max(longestName, property.name.size()); + } + + // + // Format is: + // indent, name, [separator], tab, [tab, ...] + // + + const auto separator = std::wstring(options.useSeparator ? L":" : L""); + + auto insert = options.indent + longestName + separator.size(); + + if (0 == (insert % 8)) + { + insert += 8; + } + else + { + insert = ((insert / 8) + 1) * 8; + } + + std::wstring indenter(options.indent, L' '); + InlineFormatter f; + + for (auto &property : properties) + { + auto at = options.indent + property.name.size() + separator.size(); + auto distance = insert - at; + auto tabs = (0 == (distance % 8) ? distance / 8 : (distance / 8) + 1); + + messageSink((f << indenter << property.name << separator + << std::wstring(tabs, L'\t') << property.value).str()); + } +} + +std::wstring GetArgumentValue(const common::string::KeyValuePairs &arguments, const std::wstring &key) +{ + auto arg = arguments.find(key); + + if (arguments.end() == arg) + { + std::wstringstream ss; + + ss << L"Missing argument: '" << key << L"'"; + + throw std::runtime_error(common::string::ToAnsi(ss.str())); + } + + return arg->second; +} diff --git a/windows/winfw/src/extras/cli/util.h b/windows/winfw/src/extras/cli/util.h new file mode 100644 index 0000000000..3919e03422 --- /dev/null +++ b/windows/winfw/src/extras/cli/util.h @@ -0,0 +1,20 @@ +#pragma once + +#include <functional> +#include <memory> +#include <string> +#include "propertylist.h" +#include "libwfp/filterengine.h" +#include "libcommon/string.h" + +typedef std::function<void(const std::wstring &)> MessageSink; + +struct PrettyPrintOptions +{ + size_t indent; + bool useSeparator; +}; + +void PrettyPrintProperties(MessageSink, PrettyPrintOptions options, const PropertyList &properties); + +std::wstring GetArgumentValue(const common::string::KeyValuePairs &arguments, const std::wstring &key); diff --git a/windows/winfw/src/winfw/dllmain.cpp b/windows/winfw/src/winfw/dllmain.cpp new file mode 100644 index 0000000000..21717c7d4a --- /dev/null +++ b/windows/winfw/src/winfw/dllmain.cpp @@ -0,0 +1,11 @@ +#include "stdafx.h" +#include <windows.h> + +BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID) +{ + // + // Avoid doing work in DllMain since the loader lock is held + // + + return TRUE; +} diff --git a/windows/winfw/src/winfw/fwcontext.cpp b/windows/winfw/src/winfw/fwcontext.cpp new file mode 100644 index 0000000000..b97f350ec7 --- /dev/null +++ b/windows/winfw/src/winfw/fwcontext.cpp @@ -0,0 +1,164 @@ +#include "stdafx.h" +#include "fwcontext.h" +#include "mullvadobjects.h" +#include "rules/blockall.h" +#include "rules/ifirewallrule.h" +#include "rules/permitdhcp.h" +#include "rules/permitlan.h" +#include "rules/permitlanservice.h" +#include "rules/permitloopback.h" +#include "rules/permitvpnrelay.h" +#include "rules/permitvpntunnel.h" +#include "rules/restrictdns.h" +#include "libwfp/transaction.h" +#include "libwfp/filterengine.h" +#include <functional> +#include <stdexcept> +#include <utility> + +namespace +{ + +rules::PermitVpnRelay::Protocol TranslateProtocol(WinFwProtocol protocol) +{ + switch (protocol) + { + case Tcp: return rules::PermitVpnRelay::Protocol::Tcp; + case Udp: return rules::PermitVpnRelay::Protocol::Udp; + default: + { + throw std::logic_error("Missing case handler in switch clause"); + } + }; +} + +void AppendSettingsRules(FwContext::Ruleset &ruleset, const WinFwSettings &settings) +{ + if (settings.permitDhcp) + { + ruleset.emplace_back(std::make_unique<rules::PermitDhcp>()); + } + + if (settings.permitLan) + { + ruleset.emplace_back(std::make_unique<rules::PermitLan>()); + ruleset.emplace_back(std::make_unique<rules::PermitLanService>()); + } +} + +void AppendNetBlockedRules(FwContext::Ruleset &ruleset) +{ + ruleset.emplace_back(std::make_unique<rules::BlockAll>()); + ruleset.emplace_back(std::make_unique<rules::PermitLoopback>()); +} + +} // anonymous namespace + +FwContext::FwContext(uint32_t timeout) + : m_baseline(0) +{ + auto engine = wfp::FilterEngine::DynamicSession(timeout); + + // + // Pass engine ownership to "session controller" + // + m_sessionController = std::make_unique<SessionController>(std::move(engine)); + + if (false == applyBaseConfiguration()) + { + throw std::runtime_error("Failed to apply base configuration in BFE."); + } + + m_baseline = m_sessionController->checkpoint(); +} + +bool FwContext::applyPolicyConnecting(const WinFwSettings &settings, const WinFwRelay &relay) +{ + Ruleset ruleset; + + AppendNetBlockedRules(ruleset); + AppendSettingsRules(ruleset, settings); + + ruleset.emplace_back(std::make_unique<rules::PermitVpnRelay>( + wfp::IpAddress(relay.ip), + relay.port, + TranslateProtocol(relay.protocol) + )); + + return applyRuleset(ruleset); +} + +bool FwContext::applyPolicyConnected(const WinFwSettings &settings, const WinFwRelay &relay, const wchar_t *tunnelInterfaceAlias, const wchar_t *primaryDns) +{ + Ruleset ruleset; + + AppendNetBlockedRules(ruleset); + AppendSettingsRules(ruleset, settings); + + ruleset.emplace_back(std::make_unique<rules::PermitVpnRelay>( + wfp::IpAddress(relay.ip), + relay.port, + TranslateProtocol(relay.protocol) + )); + + ruleset.emplace_back(std::make_unique<rules::PermitVpnTunnel>( + tunnelInterfaceAlias + )); + + ruleset.emplace_back(std::make_unique<rules::RestrictDns>( + tunnelInterfaceAlias, + wfp::IpAddress(primaryDns) + )); + + return applyRuleset(ruleset); +} + +bool FwContext::applyPolicyNetBlocked() +{ + Ruleset ruleset; + + AppendNetBlockedRules(ruleset); + + return applyRuleset(ruleset); +} + +bool FwContext::reset() +{ + return m_sessionController->executeTransaction([this]() + { + m_sessionController->revert(m_baseline); + return true; + }); +} + +bool FwContext::applyRuleset(const Ruleset &ruleset) +{ + return m_sessionController->executeTransaction([&]() + { + m_sessionController->revert(m_baseline); + + for (const auto &rule : ruleset) + { + if (false == rule->apply(*m_sessionController)) + { + return false; + } + } + + return true; + }); +} + +bool FwContext::applyBaseConfiguration() +{ + return m_sessionController->executeTransaction([&]() + { + // + // Install structural objects + // + + return m_sessionController->addProvider(*MullvadObjects::Provider()) + && m_sessionController->addSublayer(*MullvadObjects::SublayerWhitelist()) + && m_sessionController->addSublayer(*MullvadObjects::SublayerBlacklist()); + }); +} diff --git a/windows/winfw/src/winfw/fwcontext.h b/windows/winfw/src/winfw/fwcontext.h new file mode 100644 index 0000000000..00f952441e --- /dev/null +++ b/windows/winfw/src/winfw/fwcontext.h @@ -0,0 +1,35 @@ +#pragma once + +#include "winfw.h" +#include "sessioncontroller.h" +#include "rules/ifirewallrule.h" +#include <cstdint> +#include <memory> +#include <vector> + +class FwContext +{ +public: + + FwContext(uint32_t timeout); + + bool applyPolicyConnecting(const WinFwSettings &settings, const WinFwRelay &relay); + bool applyPolicyConnected(const WinFwSettings &settings, const WinFwRelay &relay, const wchar_t *tunnelInterfaceAlias, const wchar_t *primaryDns); + bool applyPolicyNetBlocked(); + + bool reset(); + + using Ruleset = std::vector<std::unique_ptr<rules::IFirewallRule> >; + +private: + + FwContext(const FwContext &) = delete; + FwContext &operator=(const FwContext &) = delete; + + bool applyBaseConfiguration(); + bool applyRuleset(const Ruleset &ruleset); + + std::unique_ptr<SessionController> m_sessionController; + + uint32_t m_baseline; +}; diff --git a/windows/winfw/src/winfw/iobjectinstaller.h b/windows/winfw/src/winfw/iobjectinstaller.h new file mode 100644 index 0000000000..1d38d427c3 --- /dev/null +++ b/windows/winfw/src/winfw/iobjectinstaller.h @@ -0,0 +1,17 @@ +#pragma once + +#include "libwfp/iconditionbuilder.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/providerbuilder.h" +#include "libwfp/sublayerbuilder.h" + +struct IObjectInstaller +{ + virtual ~IObjectInstaller() = 0 + { + } + + virtual bool addProvider(wfp::ProviderBuilder &providerBuilder) = 0; + virtual bool addSublayer(wfp::SublayerBuilder &sublayerBuilder) = 0; + virtual bool addFilter(wfp::FilterBuilder &filterBuilder, const wfp::IConditionBuilder &conditionBuilder) = 0; +}; diff --git a/windows/winfw/src/winfw/mullvadguids.cpp b/windows/winfw/src/winfw/mullvadguids.cpp new file mode 100644 index 0000000000..dbe00b6538 --- /dev/null +++ b/windows/winfw/src/winfw/mullvadguids.cpp @@ -0,0 +1,380 @@ +#include "stdafx.h" +#include "mullvadguids.h" + +//static +const GUID &MullvadGuids::Provider() +{ + static const GUID g = + { + 0x21e1dab8, + 0xb9db, + 0x43c0, + { 0xb3, 0x43, 0xeb, 0x93, 0x65, 0xc7, 0xbd, 0xd2 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::SublayerWhitelist() +{ + static const GUID g = + { + 0x11d1a31a, + 0xd7fa, + 0x469b, + { 0xbc, 0x21, 0xcc, 0xe9, 0x2e, 0x35, 0xfe, 0x90 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::SublayerBlacklist() +{ + static const GUID g = + { + 0x843b74f0, + 0xb499, + 0x499a, + { 0xac, 0xe3, 0xf9, 0xee, 0xa2, 0x4, 0x89, 0xc1 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterBlockAll_Outbound_Ipv4() +{ + static const GUID g = + { + 0xa81c5411, + 0xfd0, + 0x43a9, + { 0xa9, 0xbe, 0x31, 0x3f, 0x29, 0x9d, 0xe6, 0x4f } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterBlockAll_Outbound_Ipv6() +{ + static const GUID g = + { + 0x8ae5c389, + 0xd604, + 0x43df, + { 0x87, 0x4a, 0x5c, 0x86, 0x76, 0xc9, 0xc2, 0xb8 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterBlockAll_Inbound_Ipv4() +{ + static const GUID g = + { + 0x86d07155, + 0x885f, + 0x409a, + { 0x8f, 0x22, 0x1, 0x9f, 0x87, 0x7a, 0xe4, 0x9 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterBlockAll_Inbound_Ipv6() +{ + static const GUID g = + { + 0x18b8c1d2, + 0x5910, + 0x4b51, + { 0xa5, 0x48, 0x1e, 0xfc, 0xd5, 0x4b, 0x63, 0xe9 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLan_10_8() +{ + static const GUID g = + { + 0x73fe6348, + 0x62f4, + 0x4686, + { 0x95, 0x47, 0x51, 0xa8, 0x21, 0xb, 0xa3, 0x8f } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLan_172_16_12() +{ + static const GUID g = + { + 0x7a38dae, + 0x150f, + 0x47f1, + { 0xa6, 0xac, 0x99, 0x3, 0x48, 0x53, 0x83, 0x26 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLan_192_168_16() +{ + static const GUID g = + { + 0x518bfc38, + 0xa7c5, + 0x42fe, + { 0xa3, 0xf2, 0xe1, 0x56, 0x24, 0xd7, 0x86, 0x1c } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLan_Multicast() +{ + static const GUID g = + { + 0xea5e136b, + 0xd951, + 0x4263, + { 0x99, 0xd8, 0x85, 0xc3, 0xf6, 0x4b, 0xda, 0xe9 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLanService_10_8() +{ + static const GUID g = + { + 0x24ed3b23, + 0x5d5a, + 0x4f1e, + { 0x8c, 0xfa, 0xfd, 0x68, 0x79, 0x6a, 0x83, 0x8a } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLanService_172_16_12() +{ + static const GUID g = + { + 0xa925dc62, + 0x54ea, + 0x46f5, + { 0x9d, 0x37, 0xa9, 0x5a, 0xf2, 0x84, 0xc3, 0x6f } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLanService_192_168_16() +{ + static const GUID g = + { + 0x97fd73cb, + 0x9bf0, + 0x47f2, + { 0x98, 0x69, 0xd1, 0x5e, 0xf3, 0x5c, 0x3a, 0x8 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLoopback_Outbound_Ipv4() +{ + static const GUID g = + { + 0xd9ff592d, + 0xbe46, + 0x49fb, + { 0x97, 0xec, 0x71, 0x1, 0x3c, 0x12, 0xb8, 0x30 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLoopback_Outbound_Ipv6() +{ + static const GUID g = + { + 0x764d4944, + 0x8a1e, + 0x4d96, + { 0xbf, 0xf0, 0x8d, 0xa6, 0x4f, 0x31, 0x44, 0xa2 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLoopback_Inbound_Ipv4() +{ + static const GUID g = + { + 0xb8efb500, + 0xc51, + 0x4550, + { 0xbf, 0x5c, 0x48, 0x54, 0xa6, 0xc8, 0x48, 0xb9 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitLoopback_Inbound_Ipv6() +{ + static const GUID g = + { + 0xbad325b0, + 0x736c, + 0x4e67, + { 0x8b, 0x37, 0x62, 0xb2, 0xdb, 0xe7, 0xd6, 0xeb } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitDhcp_Outbound_Request() +{ + static const GUID g = + { + 0x6cf1687b, + 0x35e9, + 0x4d18, + { 0xa2, 0x3, 0xb2, 0x6b, 0x71, 0xa9, 0x5f, 0x8d } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitDhcp_Inbound_Response() +{ + static const GUID g = + { + 0x2db298d7, + 0x4108, + 0x47ff, + { 0x85, 0x99, 0xaf, 0xa5, 0xcb, 0x95, 0x9c, 0x25 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitVpnRelay() +{ + static const GUID g = + { + 0x160c205d, + 0xdb40, + 0x4f79, + { 0x90, 0x6d, 0xfd, 0xa1, 0xe1, 0xc1, 0x8a, 0x70 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv4() +{ + static const GUID g = + { + 0xdfdcbb76, + 0x2284, + 0x4b03, + { 0x93, 0x4e, 0x93, 0xe5, 0xd3, 0x84, 0x8c, 0xf1 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv6() +{ + static const GUID g = + { + 0x9b1fa7d, + 0x843b, + 0x4946, + { 0xa6, 0x2, 0x90, 0x4, 0x26, 0x2a, 0xb8, 0x6b } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterRestrictDns_Outbound_Ipv4() +{ + static const GUID g = + { + 0xc0792b44, + 0xfc3c, + 0x42e8, + { 0xa6, 0x60, 0x25, 0x4b, 0xd0, 0x4, 0xb1, 0x9d } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterRestrictDns_Outbound_Ipv6() +{ + static const GUID g = + { + 0xcde477eb, + 0x2d8a, + 0x45b8, + { 0x9a, 0x3e, 0x9a, 0xa3, 0xbe, 0x4d, 0xe2, 0xb4 } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv4() +{ + static const GUID g = + { + 0x790445dc, + 0xb23e, + 0x4ab4, + { 0x8e, 0x2f, 0xc7, 0x6, 0x55, 0x5f, 0x94, 0xff } + }; + + return g; +} + +//static +const GUID &MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv6() +{ + static const GUID g = + { + 0xacc90d87, + 0xab77, + 0x4cf4, + { 0x84, 0xee, 0x1d, 0x68, 0x95, 0xf0, 0x66, 0xc2 } + }; + + return g; +} diff --git a/windows/winfw/src/winfw/mullvadguids.h b/windows/winfw/src/winfw/mullvadguids.h new file mode 100644 index 0000000000..788b8b8b30 --- /dev/null +++ b/windows/winfw/src/winfw/mullvadguids.h @@ -0,0 +1,45 @@ +#pragma once +#include <guiddef.h> + +class MullvadGuids +{ +public: + + MullvadGuids() = delete; + + static const GUID &Provider(); + static const GUID &SublayerWhitelist(); + static const GUID &SublayerBlacklist(); + + static const GUID &FilterBlockAll_Outbound_Ipv4(); + static const GUID &FilterBlockAll_Outbound_Ipv6(); + static const GUID &FilterBlockAll_Inbound_Ipv4(); + static const GUID &FilterBlockAll_Inbound_Ipv6(); + + static const GUID &FilterPermitLan_10_8(); + static const GUID &FilterPermitLan_172_16_12(); + static const GUID &FilterPermitLan_192_168_16(); + static const GUID &FilterPermitLan_Multicast(); + + static const GUID &FilterPermitLanService_10_8(); + static const GUID &FilterPermitLanService_172_16_12(); + static const GUID &FilterPermitLanService_192_168_16(); + + static const GUID &FilterPermitLoopback_Outbound_Ipv4(); + static const GUID &FilterPermitLoopback_Outbound_Ipv6(); + static const GUID &FilterPermitLoopback_Inbound_Ipv4(); + static const GUID &FilterPermitLoopback_Inbound_Ipv6(); + + static const GUID &FilterPermitDhcp_Outbound_Request(); + static const GUID &FilterPermitDhcp_Inbound_Response(); + + static const GUID &FilterPermitVpnRelay(); + + static const GUID &FilterPermitVpnTunnel_Outbound_Ipv4(); + static const GUID &FilterPermitVpnTunnel_Outbound_Ipv6(); + + static const GUID &FilterRestrictDns_Outbound_Ipv4(); + static const GUID &FilterRestrictDns_Outbound_Ipv6(); + static const GUID &FilterRestrictDns_Outbound_Tunnel_Ipv4(); + static const GUID &FilterRestrictDns_Outbound_Tunnel_Ipv6(); +}; diff --git a/windows/winfw/src/winfw/mullvadobjects.cpp b/windows/winfw/src/winfw/mullvadobjects.cpp new file mode 100644 index 0000000000..43618b51b3 --- /dev/null +++ b/windows/winfw/src/winfw/mullvadobjects.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include "mullvadobjects.h" +#include "mullvadguids.h" + +//static +std::unique_ptr<wfp::ProviderBuilder> MullvadObjects::Provider() +{ + auto builder = std::make_unique<wfp::ProviderBuilder>(); + + (*builder) + .name(L"Mullvad VPN") + .description(L"Mullvad VPN firewall integration") + .key(MullvadGuids::Provider()); + + return builder; +} + +//static +std::unique_ptr<wfp::SublayerBuilder> MullvadObjects::SublayerWhitelist() +{ + auto builder = std::make_unique<wfp::SublayerBuilder>(); + + (*builder) + .name(L"Mullvad VPN whitelist") + .description(L"Filters that permit traffic") + .key(MullvadGuids::SublayerWhitelist()) + .provider(MullvadGuids::Provider()) + .weight(MAXUINT16); + + return builder; +} + +//static +std::unique_ptr<wfp::SublayerBuilder> MullvadObjects::SublayerBlacklist() +{ + auto builder = std::make_unique<wfp::SublayerBuilder>(); + + (*builder) + .name(L"Mullvad VPN blacklist") + .description(L"Filters that block traffic") + .key(MullvadGuids::SublayerBlacklist()) + .provider(MullvadGuids::Provider()) + .weight(MAXUINT16 - 1); + + return builder; +} diff --git a/windows/winfw/src/winfw/mullvadobjects.h b/windows/winfw/src/winfw/mullvadobjects.h new file mode 100644 index 0000000000..9a056c9186 --- /dev/null +++ b/windows/winfw/src/winfw/mullvadobjects.h @@ -0,0 +1,18 @@ +#pragma once + +#include "libwfp/providerbuilder.h" +#include "libwfp/sublayerbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/filterbuilder.h" +#include <memory> + +class MullvadObjects +{ +public: + + MullvadObjects() = delete; + + static std::unique_ptr<wfp::ProviderBuilder> Provider(); + static std::unique_ptr<wfp::SublayerBuilder> SublayerWhitelist(); + static std::unique_ptr<wfp::SublayerBuilder> SublayerBlacklist(); +}; diff --git a/windows/winfw/src/winfw/rules/blockall.cpp b/windows/winfw/src/winfw/rules/blockall.cpp new file mode 100644 index 0000000000..ff8ba5a065 --- /dev/null +++ b/windows/winfw/src/winfw/rules/blockall.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#include "blockall.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/nullconditionbuilder.h" + +namespace rules +{ + +bool BlockAll::apply(IObjectInstaller &objectInstaller) +{ + wfp::FilterBuilder filterBuilder; + + // + // #1 block outbound connections, ipv4 + // + + filterBuilder + .key(MullvadGuids::FilterBlockAll_Outbound_Ipv4()) + .name(L"Block all outbound connections") + .description(L"This filter is part of a rule that restricts inbound and outbound traffic") + .provider(MullvadGuids::Provider()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4) + .sublayer(MullvadGuids::SublayerWhitelist()) + .weight(wfp::FilterBuilder::WeightClass::Min) + .block(); + + wfp::NullConditionBuilder nullConditionBuilder; + + if (false == objectInstaller.addFilter(filterBuilder, nullConditionBuilder)) + { + return false; + } + + // + // #2 block outbound connections, ipv6 + // + + filterBuilder + .key(MullvadGuids::FilterBlockAll_Outbound_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + if (false == objectInstaller.addFilter(filterBuilder, nullConditionBuilder)) + { + return false; + } + + // + // #3 block inbound connections, ipv4 + // + + filterBuilder + .key(MullvadGuids::FilterBlockAll_Inbound_Ipv4()) + .name(L"Block all inbound connections") + .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4); + + if (false == objectInstaller.addFilter(filterBuilder, nullConditionBuilder)) + { + return false; + } + + // + // #4 block inbound connections, ipv6 + // + + filterBuilder + .key(MullvadGuids::FilterBlockAll_Inbound_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6); + + return objectInstaller.addFilter(filterBuilder, nullConditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/blockall.h b/windows/winfw/src/winfw/rules/blockall.h new file mode 100644 index 0000000000..c60c43fa1d --- /dev/null +++ b/windows/winfw/src/winfw/rules/blockall.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ifirewallrule.h" + +namespace rules +{ + +class BlockAll : public IFirewallRule +{ +public: + + BlockAll() = default; + ~BlockAll() = default; + + bool apply(IObjectInstaller &objectInstaller) override; +}; + +} diff --git a/windows/winfw/src/winfw/rules/ifirewallrule.h b/windows/winfw/src/winfw/rules/ifirewallrule.h new file mode 100644 index 0000000000..e0c8755b20 --- /dev/null +++ b/windows/winfw/src/winfw/rules/ifirewallrule.h @@ -0,0 +1,25 @@ +#pragma once + +#include "winfw/iobjectinstaller.h" + +// +// A firewall rule uses one of more filters to implement a concept +// E.g. "allow lan traffic" +// + +namespace rules +{ + +struct IFirewallRule +{ + virtual ~IFirewallRule() = 0 + { + } + + //virtual std::wstring name() = 0; + //virtual std::vector<std::wstring> details() = 0; // doesn't work? because there can be multiple filters each with multiple conditions + + virtual bool apply(IObjectInstaller &objectInstaller) = 0; +}; + +} diff --git a/windows/winfw/src/winfw/rules/permitdhcp.cpp b/windows/winfw/src/winfw/rules/permitdhcp.cpp new file mode 100644 index 0000000000..e92b88056e --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitdhcp.cpp @@ -0,0 +1,74 @@ +#include "stdafx.h" +#include "permitdhcp.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/ipaddress.h" +#include "libwfp/conditions/conditionprotocol.h" +#include "libwfp/conditions/conditionport.h" +#include "libwfp/conditions/conditionip.h" +#include "libwfp/conditions/conditionport.h" + +using namespace wfp::conditions; + +namespace rules +{ + +bool PermitDhcp::apply(IObjectInstaller &objectInstaller) +{ + // + // First UDP packet for a unique [remote address, port] tuple is mapped into: + // + // outbound: FWPM_LAYER_ALE_AUTH_CONNECT_V{4|6} + // inbound: FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4|6} + // + + wfp::FilterBuilder filterBuilder; + + // + // #1 permit outbound DHCP request + // + + filterBuilder + .key(MullvadGuids::FilterPermitDhcp_Outbound_Request()) + .name(L"Permit outbound DHCP request") + .description(L"This filter is part of a rule that permits DHCP client traffic") + .provider(MullvadGuids::Provider()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4) + .sublayer(MullvadGuids::SublayerWhitelist()) + .weight(wfp::FilterBuilder::WeightClass::Max) + .permit(); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + conditionBuilder.add_condition(ConditionProtocol::Udp()); + conditionBuilder.add_condition(ConditionPort::Local(68)); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 255, 255, 255, 255 }))); + conditionBuilder.add_condition(ConditionPort::Remote(67)); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + // + // #2 permit inbound DHCP response + // + + filterBuilder + .key(MullvadGuids::FilterPermitDhcp_Inbound_Response()) + .name(L"Permit inbound DHCP response") + .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4); + + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4); + + conditionBuilder.add_condition(ConditionProtocol::Udp()); + conditionBuilder.add_condition(ConditionPort::Remote(67)); + conditionBuilder.add_condition(ConditionPort::Local(68)); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/permitdhcp.h b/windows/winfw/src/winfw/rules/permitdhcp.h new file mode 100644 index 0000000000..58bd90bfa5 --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitdhcp.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ifirewallrule.h" + +namespace rules +{ + +class PermitDhcp : public IFirewallRule +{ +public: + + PermitDhcp() = default; + ~PermitDhcp() = default; + + bool apply(IObjectInstaller &objectInstaller) override; +}; + +} diff --git a/windows/winfw/src/winfw/rules/permitlan.cpp b/windows/winfw/src/winfw/rules/permitlan.cpp new file mode 100644 index 0000000000..cf2e753d05 --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitlan.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "permitlan.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/ipaddress.h" +#include "libwfp/conditions/conditionip.h" + +using namespace wfp::conditions; + +namespace rules +{ + +bool PermitLan::apply(IObjectInstaller &objectInstaller) +{ + wfp::FilterBuilder filterBuilder; + + // + // #1 locally-initiated on 10/8 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLan_10_8()) + .name(L"Permit locally-initiated traffic on 10/8") + .description(L"This filter is part of a rule that permits LAN traffic") + .provider(MullvadGuids::Provider()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4) + .sublayer(MullvadGuids::SublayerWhitelist()) + .weight(wfp::FilterBuilder::WeightClass::Max) + .permit(); + + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8))); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8))); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + + // + // #2 locally-initiated on 172.16/12 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLan_172_16_12()) + .name(L"Permit locally-initiated traffic on 172.16/12"); + + conditionBuilder.reset(); + + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12))); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12))); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + + // + // #3 locally-initiated on 192.168/16 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLan_192_168_16()) + .name(L"Permit locally-initiated traffic on 192.168/16"); + + conditionBuilder.reset(); + + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16))); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16))); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + + // + // #4 LAN to multicast + // + + filterBuilder + .key(MullvadGuids::FilterPermitLan_Multicast()) + .name(L"Permit locally-initiated multicast traffic"); + + conditionBuilder.reset(); + + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8))); + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12))); + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16))); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 224, 0, 0, 0 }), uint8_t(24))); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/permitlan.h b/windows/winfw/src/winfw/rules/permitlan.h new file mode 100644 index 0000000000..9194b0bcd7 --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitlan.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ifirewallrule.h" + +namespace rules +{ + +class PermitLan : public IFirewallRule +{ +public: + + PermitLan() = default; + ~PermitLan() = default; + + bool apply(IObjectInstaller &objectInstaller) override; +}; + +} diff --git a/windows/winfw/src/winfw/rules/permitlanservice.cpp b/windows/winfw/src/winfw/rules/permitlanservice.cpp new file mode 100644 index 0000000000..4e52f07047 --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitlanservice.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "permitlanservice.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/ipaddress.h" +#include "libwfp/conditions/conditionip.h" + +using namespace wfp::conditions; + +namespace rules +{ + +bool PermitLanService::apply(IObjectInstaller &objectInstaller) +{ + wfp::FilterBuilder filterBuilder; + + // + // #1 incoming request on 10/8 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLanService_10_8()) + .name(L"Permit incoming requests on 10/8") + .description(L"This filter is part of a rule that permits hosting services in a LAN environment") + .provider(MullvadGuids::Provider()) + .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4) + .sublayer(MullvadGuids::SublayerWhitelist()) + .weight(wfp::FilterBuilder::WeightClass::Max) + .permit(); + + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4); + + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8))); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 10, 0, 0, 0 }), uint8_t(8))); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + + // + // #2 incoming request on 172.16/12 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLanService_172_16_12()) + .name(L"Permit incoming requests on 172.16/12"); + + conditionBuilder.reset(); + + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12))); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 172, 16, 0, 0 }), uint8_t(12))); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + + // + // #3 incoming request on 192.168/16 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLanService_192_168_16()) + .name(L"Permit incoming requests on 192.168/16"); + + conditionBuilder.reset(); + + conditionBuilder.add_condition(ConditionIp::Local(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16))); + conditionBuilder.add_condition(ConditionIp::Remote(wfp::IpAddress::Literal({ 192, 168, 0, 0 }), uint8_t(16))); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/permitlanservice.h b/windows/winfw/src/winfw/rules/permitlanservice.h new file mode 100644 index 0000000000..be482386a3 --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitlanservice.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ifirewallrule.h" + +namespace rules +{ + +class PermitLanService : public IFirewallRule +{ +public: + + PermitLanService() = default; + ~PermitLanService() = default; + + bool apply(IObjectInstaller &objectInstaller) override; +}; + +} diff --git a/windows/winfw/src/winfw/rules/permitloopback.cpp b/windows/winfw/src/winfw/rules/permitloopback.cpp new file mode 100644 index 0000000000..f98fe4f756 --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitloopback.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "permitloopback.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/conditions/conditionloopback.h" + +using namespace wfp::conditions; + +namespace rules +{ + +bool PermitLoopback::apply(IObjectInstaller &objectInstaller) +{ + wfp::FilterBuilder filterBuilder; + + // + // #1 permit outbound connections, ipv4 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLoopback_Outbound_Ipv4()) + .name(L"Permit outbound connections on loopback") + .description(L"This filter is part of a rule that permits all loopback traffic") + .provider(MullvadGuids::Provider()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4) + .sublayer(MullvadGuids::SublayerWhitelist()) + .weight(wfp::FilterBuilder::WeightClass::Max) + .permit(); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + conditionBuilder.add_condition(std::make_unique<ConditionLoopback>()); + + if (false == objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + // + // #2 permit outbound connections, ipv6 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLoopback_Outbound_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + conditionBuilder.add_condition(std::make_unique<ConditionLoopback>()); + + if (false == objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + // + // #3 permit inbound connections, ipv4 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLoopback_Inbound_Ipv4()) + .name(L"Permit inbound connections on loopback") + .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4); + + conditionBuilder.add_condition(std::make_unique<ConditionLoopback>()); + + if (false == objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + // + // #4 permit inbound connections, ipv6 + // + + filterBuilder + .key(MullvadGuids::FilterPermitLoopback_Inbound_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6); + + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6); + + conditionBuilder.add_condition(std::make_unique<ConditionLoopback>()); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/permitloopback.h b/windows/winfw/src/winfw/rules/permitloopback.h new file mode 100644 index 0000000000..71694c353d --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitloopback.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ifirewallrule.h" + +namespace rules +{ + +class PermitLoopback : public IFirewallRule +{ +public: + + PermitLoopback() = default; + ~PermitLoopback() = default; + + bool apply(IObjectInstaller &objectInstaller) override; +}; + +} diff --git a/windows/winfw/src/winfw/rules/permitvpnrelay.cpp b/windows/winfw/src/winfw/rules/permitvpnrelay.cpp new file mode 100644 index 0000000000..e6a2f9b918 --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitvpnrelay.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" +#include "permitvpnrelay.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/conditions/conditionprotocol.h" +#include "libwfp/conditions/conditionip.h" +#include "libwfp/conditions/conditionport.h" + +using namespace wfp::conditions; + +namespace rules +{ + +namespace +{ + +const GUID &LayerFromIp(const wfp::IpAddress &ip) +{ + switch (ip.type()) + { + case wfp::IpAddress::Type::Ipv4: return FWPM_LAYER_ALE_AUTH_CONNECT_V4; + case wfp::IpAddress::Type::Ipv6: return FWPM_LAYER_ALE_AUTH_CONNECT_V6; + default: + { + throw std::logic_error("Missing case handler in switch clause"); + } + }; +} + +std::unique_ptr<ConditionProtocol> CreateProtocolCondition(PermitVpnRelay::Protocol protocol) +{ + switch (protocol) + { + case PermitVpnRelay::Protocol::Tcp: return ConditionProtocol::Tcp(); + case PermitVpnRelay::Protocol::Udp: return ConditionProtocol::Udp(); + default: + { + throw std::logic_error("Missing case handler in switch clause"); + } + }; +} + +} // anonymous namespace + +PermitVpnRelay::PermitVpnRelay(const wfp::IpAddress &relay, uint16_t relayPort, Protocol protocol) + : m_relay(relay) + , m_relayPort(relayPort) + , m_protocol(protocol) +{ +} + +bool PermitVpnRelay::apply(IObjectInstaller &objectInstaller) +{ + wfp::FilterBuilder filterBuilder; + + // + // #1 permit connecting to relay + // + + filterBuilder + .key(MullvadGuids::FilterPermitVpnRelay()) + .name(L"Permit outbound connections to VPN relay") + .description(L"This filter is part of a rule that permits communication with a VPN relay") + .provider(MullvadGuids::Provider()) + .layer(LayerFromIp(m_relay)) + .sublayer(MullvadGuids::SublayerWhitelist()) + .weight(wfp::FilterBuilder::WeightClass::Max) + .permit(); + + wfp::ConditionBuilder conditionBuilder(LayerFromIp(m_relay)); + + conditionBuilder.add_condition(ConditionIp::Remote(m_relay)); + conditionBuilder.add_condition(ConditionPort::Remote(m_relayPort)); + conditionBuilder.add_condition(CreateProtocolCondition(m_protocol)); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/permitvpnrelay.h b/windows/winfw/src/winfw/rules/permitvpnrelay.h new file mode 100644 index 0000000000..c5ae8b024a --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitvpnrelay.h @@ -0,0 +1,30 @@ +#pragma once + +#include "ifirewallrule.h" +#include "libwfp/ipaddress.h" + +namespace rules +{ + +class PermitVpnRelay : public IFirewallRule +{ +public: + + enum class Protocol + { + Tcp, + Udp + }; + + PermitVpnRelay(const wfp::IpAddress &relay, uint16_t relayPort, Protocol protocol); + + bool apply(IObjectInstaller &objectInstaller) override; + +private: + + const wfp::IpAddress m_relay; + const uint16_t m_relayPort; + const Protocol m_protocol; +}; + +} diff --git a/windows/winfw/src/winfw/rules/permitvpntunnel.cpp b/windows/winfw/src/winfw/rules/permitvpntunnel.cpp new file mode 100644 index 0000000000..39830e70ec --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitvpntunnel.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "permitvpntunnel.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/conditions/conditioninterface.h" + +using namespace wfp::conditions; + +namespace rules +{ + +PermitVpnTunnel::PermitVpnTunnel(const std::wstring &tunnelInterfaceAlias) + : m_tunnelInterfaceAlias(tunnelInterfaceAlias) +{ +} + +bool PermitVpnTunnel::apply(IObjectInstaller &objectInstaller) +{ + wfp::FilterBuilder filterBuilder; + + // + // #1 permit locally-initiated traffic on tunnel interface, ipv4 + // + + filterBuilder + .key(MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv4()) + .name(L"Permit locally-initiated traffic on tunnel interface") + .description(L"This filter is part of a rule that permits communications inside the VPN tunnel") + .provider(MullvadGuids::Provider()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4) + .sublayer(MullvadGuids::SublayerWhitelist()) + .weight(wfp::FilterBuilder::WeightClass::Max) + .permit(); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias)); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + // + // #2 permit locally-initiated traffic on tunnel interface, ipv6 + // + + filterBuilder + .key(MullvadGuids::FilterPermitVpnTunnel_Outbound_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias)); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/permitvpntunnel.h b/windows/winfw/src/winfw/rules/permitvpntunnel.h new file mode 100644 index 0000000000..49c1b028ab --- /dev/null +++ b/windows/winfw/src/winfw/rules/permitvpntunnel.h @@ -0,0 +1,22 @@ +#pragma once + +#include "ifirewallrule.h" +#include <string> + +namespace rules +{ + +class PermitVpnTunnel : public IFirewallRule +{ +public: + + PermitVpnTunnel(const std::wstring &tunnelInterfaceAlias); + + bool apply(IObjectInstaller &objectInstaller) override; + +private: + + const std::wstring m_tunnelInterfaceAlias; +}; + +} diff --git a/windows/winfw/src/winfw/rules/restrictdns.cpp b/windows/winfw/src/winfw/rules/restrictdns.cpp new file mode 100644 index 0000000000..d8a953fb3a --- /dev/null +++ b/windows/winfw/src/winfw/rules/restrictdns.cpp @@ -0,0 +1,148 @@ +#include "stdafx.h" +#include "restrictdns.h" +#include "winfw/mullvadguids.h" +#include "libwfp/filterbuilder.h" +#include "libwfp/conditionbuilder.h" +#include "libwfp/conditions/conditioninterface.h" +#include "libwfp/conditions/conditionip.h" +#include "libwfp/conditions/conditionport.h" + +using namespace wfp::conditions; + +namespace rules +{ + +RestrictDns::RestrictDns(const std::wstring &tunnelInterfaceAlias, const wfp::IpAddress &dns) + : m_tunnelInterfaceAlias(tunnelInterfaceAlias) + , m_dns(dns) +{ +} + +bool RestrictDns::apply(IObjectInstaller &objectInstaller) +{ + wfp::FilterBuilder filterBuilder; + + // + // Requires that the following rules are in effect: + // + // BlockAll + // PermitVpnTunnel + // + // TODO: Have each rule specify requirements? + // + + filterBuilder + .key(MullvadGuids::FilterRestrictDns_Outbound_Ipv4()) + .name(L"Block DNS requests outside the VPN tunnel") + .description(L"This filter is part of a rule that restricts DNS traffic") + .provider(MullvadGuids::Provider()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4) + .sublayer(MullvadGuids::SublayerBlacklist()) + .weight(wfp::FilterBuilder::WeightClass::Max) + .block(); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + conditionBuilder.add_condition(ConditionPort::Remote(53)); + conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias, CompareNeq())); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + // + // IPv6 also + // + + filterBuilder + .key(MullvadGuids::FilterRestrictDns_Outbound_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + conditionBuilder.add_condition(ConditionPort::Remote(53)); + conditionBuilder.add_condition(ConditionInterface::Alias(m_tunnelInterfaceAlias, CompareNeq())); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + // + // This next part is a little redundant since the entire rule could be defined + // using three filters. Let's use four filters to maintain some kind of readability. + // + // The reason it would be possible to use three filters is because the single DNS + // is going to be either v4 or v6, so all requests that cannot be sent to the DNS + // will have to be blocked (thereby shadowing one of the filters above). + // + + filterBuilder + .name(L"Restrict DNS requests inside the VPN tunnel"); + + if (m_dns.type() == wfp::IpAddress::Type::Ipv4) + { + filterBuilder + .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv4()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + conditionBuilder.add_condition(ConditionPort::Remote(53)); + conditionBuilder.add_condition(ConditionIp::Remote(m_dns, CompareNeq())); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + filterBuilder + .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + conditionBuilder.add_condition(ConditionPort::Remote(53)); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); + } + + // + // Specified DNS is IPv6 + // + + filterBuilder + .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv6()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + { + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V6); + + conditionBuilder.add_condition(ConditionPort::Remote(53)); + conditionBuilder.add_condition(ConditionIp::Remote(m_dns, CompareNeq())); + + if (!objectInstaller.addFilter(filterBuilder, conditionBuilder)) + { + return false; + } + } + + filterBuilder + .key(MullvadGuids::FilterRestrictDns_Outbound_Tunnel_Ipv4()) + .layer(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + wfp::ConditionBuilder conditionBuilder(FWPM_LAYER_ALE_AUTH_CONNECT_V4); + + conditionBuilder.add_condition(ConditionPort::Remote(53)); + + return objectInstaller.addFilter(filterBuilder, conditionBuilder); +} + +} diff --git a/windows/winfw/src/winfw/rules/restrictdns.h b/windows/winfw/src/winfw/rules/restrictdns.h new file mode 100644 index 0000000000..da09e970d3 --- /dev/null +++ b/windows/winfw/src/winfw/rules/restrictdns.h @@ -0,0 +1,24 @@ +#pragma once + +#include "ifirewallrule.h" +#include "libwfp/ipaddress.h" +#include <string> + +namespace rules +{ + +class RestrictDns : public IFirewallRule +{ +public: + + RestrictDns(const std::wstring &tunnelInterfaceAlias, const wfp::IpAddress &dns); + + bool apply(IObjectInstaller &objectInstaller) override; + +private: + + const std::wstring m_tunnelInterfaceAlias; + const wfp::IpAddress m_dns; +}; + +} diff --git a/windows/winfw/src/winfw/sessioncontroller.cpp b/windows/winfw/src/winfw/sessioncontroller.cpp new file mode 100644 index 0000000000..2965233600 --- /dev/null +++ b/windows/winfw/src/winfw/sessioncontroller.cpp @@ -0,0 +1,248 @@ +#include "stdafx.h" +#include "sessioncontroller.h" +#include "libwfp/objectinstaller.h" +#include "libwfp/objectdeleter.h" +#include "libwfp/transaction.h" +#include "libcommon/memory.h" +#include <utility> + +namespace +{ + +template<typename T> +void EraseBack(T &container, size_t elements) +{ + if (elements >= container.size()) + { + container.clear(); + } + else + { + container.erase + ( + std::next(container.begin(), container.size() - elements), + container.end() + ); + } +} + +template<typename T> +void ProcessReverse(T &container, size_t elements, std::function<void(typename T::value_type &)> f) +{ + auto it = container.rbegin(); + auto end = std::next(it, elements); + + while (it != end) + { + f(*it++); + } +} + +bool CheckpointKeyToIndex(const std::vector<SessionRecord> &container, uint32_t key, size_t &elementIndex) +{ + auto index = 0; + + for (auto it = container.begin(); it != container.end(); ++it, ++index) + { + if (it->key() == key) + { + elementIndex = index; + return true; + } + } + + return false; +} + +} // anonymous namespace + +SessionController::SessionController(std::unique_ptr<wfp::FilterEngine> &&engine) + : m_engine(std::move(engine)) + , m_activeTransaction(false) +{ +} + +SessionController::~SessionController() +{ + // + // TODO: Review destruction of this instance and its owner. + // + + try + { + executeTransaction([this]() + { + reset(); + return true; + }); + } + catch (...) + { + return; + } +} + +bool SessionController::addProvider(wfp::ProviderBuilder &providerBuilder) +{ + if (false == m_activeTransaction) + { + throw std::runtime_error("Cannot add provider outside transaction"); + } + + GUID key; + + auto status = wfp::ObjectInstaller::AddProvider(*m_engine, providerBuilder, &key); + + if (status) + { + m_transactionRecords.emplace_back(SessionRecord(key, SessionRecord::ObjectType::Provider)); + } + + return status; +} + +bool SessionController::addSublayer(wfp::SublayerBuilder &sublayerBuilder) +{ + if (false == m_activeTransaction) + { + throw std::runtime_error("Cannot add sublayer outside transaction"); + } + + GUID key; + + auto status = wfp::ObjectInstaller::AddSublayer(*m_engine, sublayerBuilder, &key); + + if (status) + { + m_transactionRecords.emplace_back(SessionRecord(key, SessionRecord::ObjectType::Sublayer)); + } + + return status; +} + +bool SessionController::addFilter(wfp::FilterBuilder &filterBuilder, const wfp::IConditionBuilder &conditionBuilder) +{ + if (false == m_activeTransaction) + { + throw std::runtime_error("Cannot add filter outside transaction"); + } + + UINT64 id; + + auto status = wfp::ObjectInstaller::AddFilter(*m_engine, filterBuilder, conditionBuilder, &id); + + if (status) + { + m_transactionRecords.emplace_back(SessionRecord(id)); + } + + return status; +} + +bool SessionController::executeTransaction(std::function<bool()> operation) +{ + if (m_activeTransaction.exchange(true)) + { + throw std::runtime_error("Recursive/concurrent transactions are not supported"); + } + + common::memory::ScopeDestructor scopeDestructor; + + scopeDestructor += [this]() + { + m_activeTransaction.store(false); + }; + + m_transactionRecords = m_records; + + auto status = wfp::Transaction::Execute(*m_engine, operation); + + if (status) + { + m_records.swap(m_transactionRecords); + } + + return status; +} + +bool SessionController::executeReadOnlyTransaction(std::function<bool()> operation) +{ + if (m_activeTransaction.exchange(true)) + { + throw std::runtime_error("Recursive/concurrent transactions are not supported"); + } + + common::memory::ScopeDestructor scopeDestructor; + + scopeDestructor += [this]() + { + m_activeTransaction.store(false); + }; + + return wfp::Transaction::ExecuteReadOnly(*m_engine, operation); +} + +uint32_t SessionController::checkpoint() +{ + if (m_activeTransaction) + { + throw std::runtime_error("Cannot read checkpoint key while in transaction"); + } + + if (m_records.empty()) + { + return 0; + } + + return m_records.back().key(); +} + +void SessionController::revert(uint32_t key) +{ + if (false == m_activeTransaction) + { + throw std::runtime_error("Cannot revert session state outside transaction"); + } + + size_t elementIndex = 0; + + if (false == CheckpointKeyToIndex(m_transactionRecords, key, elementIndex)) + { + throw std::runtime_error("Invalid checkpoint key (checkpoint may have been overwritten?)"); + } + + const size_t numRemove = m_transactionRecords.size() - (elementIndex + 1); + + rewindState(numRemove); +} + +void SessionController::reset() +{ + if (false == m_activeTransaction) + { + throw std::runtime_error("Cannot reset session state outside transaction"); + } + + rewindState(m_transactionRecords.size()); +} + +void SessionController::rewindState(size_t steps) +{ + auto purged = 0; + + try + { + ProcessReverse(m_transactionRecords, steps, [this, &purged](SessionRecord &record) + { + record.purge(*m_engine); + ++purged; + }); + } + catch (...) + { + EraseBack(m_transactionRecords, purged); + throw; + } + + EraseBack(m_transactionRecords, steps); +} diff --git a/windows/winfw/src/winfw/sessioncontroller.h b/windows/winfw/src/winfw/sessioncontroller.h new file mode 100644 index 0000000000..61163533c4 --- /dev/null +++ b/windows/winfw/src/winfw/sessioncontroller.h @@ -0,0 +1,55 @@ +#pragma once + +#include "iobjectinstaller.h" +#include "sessionrecord.h" +#include "libwfp/filterengine.h" +#include <atomic> +#include <memory> +#include <vector> + +class SessionController : public IObjectInstaller +{ +public: + + SessionController(std::unique_ptr<wfp::FilterEngine> &&engine); + ~SessionController(); + + bool addProvider(wfp::ProviderBuilder &providerBuilder) override; + 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); + + // + // Retrieve checkpoint key that can be used to restore the current session state + // This should be done outside of an active transaction + // + uint32_t checkpoint(); + + // + // Purge objects in the stack and return to an earlier state + // Use only inside active transaction + // + void revert(uint32_t key); + + // + // Purge all objects in the stack + // Use only inside active transaction + // + void reset(); + +private: + + SessionController(const SessionController &) = delete; + SessionController &operator=(const SessionController &) = delete; + + void rewindState(size_t steps); + + std::unique_ptr<wfp::FilterEngine> m_engine; + + std::vector<SessionRecord> m_records; + std::vector<SessionRecord> m_transactionRecords; + + std::atomic_bool m_activeTransaction; +}; diff --git a/windows/winfw/src/winfw/sessionrecord.cpp b/windows/winfw/src/winfw/sessionrecord.cpp new file mode 100644 index 0000000000..ad11f4c013 --- /dev/null +++ b/windows/winfw/src/winfw/sessionrecord.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include "sessionrecord.h" +#include "libwfp/objectdeleter.h" +#include <atomic> +#include <cstdint> +#include <stdexcept> + +namespace +{ + +std::atomic<uint32_t> g_keybase = 0; + +} // anonymous namespace + +SessionRecord::SessionRecord(const GUID &id, ObjectType type) + : m_type(type) + , m_id(id) + , m_key(g_keybase++) +{ +} + +SessionRecord::SessionRecord(UINT64 id) + : m_type(ObjectType::Filter) + , m_filterId(id) + , m_key(g_keybase++) +{ +} + +void SessionRecord::purge(wfp::FilterEngine &engine) +{ + switch (m_type) + { + case ObjectType::Provider: + { + wfp::ObjectDeleter::DeleteProvider(engine, m_id); + break; + } + case ObjectType::Sublayer: + { + wfp::ObjectDeleter::DeleteSublayer(engine, m_id); + break; + } + case ObjectType::Filter: + { + wfp::ObjectDeleter::DeleteFilter(engine, m_filterId); + break; + } + default: + { + throw std::logic_error("Missing case handler in switch clause"); + } + }; +} + +uint32_t SessionRecord::key() const +{ + return m_key; +} diff --git a/windows/winfw/src/winfw/sessionrecord.h b/windows/winfw/src/winfw/sessionrecord.h new file mode 100644 index 0000000000..8ad3be8c86 --- /dev/null +++ b/windows/winfw/src/winfw/sessionrecord.h @@ -0,0 +1,37 @@ +#pragma once + +#include "libwfp/filterengine.h" +#include <guiddef.h> +#include <windows.h> + +class SessionRecord +{ +public: + + enum class ObjectType + { + Provider, + Sublayer, + Filter + }; + + SessionRecord(const GUID &id, ObjectType type); + SessionRecord(UINT64 id); + + SessionRecord(const SessionRecord &) = default; + SessionRecord(SessionRecord &&) = default; + SessionRecord &operator=(const SessionRecord &) = default; + + void purge(wfp::FilterEngine &engine); + + uint32_t key() const; + +private: + + ObjectType m_type; + + GUID m_id; + UINT64 m_filterId; + + uint32_t m_key; +}; diff --git a/windows/winfw/src/winfw/stdafx.cpp b/windows/winfw/src/winfw/stdafx.cpp new file mode 100644 index 0000000000..af96757b53 --- /dev/null +++ b/windows/winfw/src/winfw/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// winfw.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/windows/winfw/src/winfw/stdafx.h b/windows/winfw/src/winfw/stdafx.h new file mode 100644 index 0000000000..f3a07375c7 --- /dev/null +++ b/windows/winfw/src/winfw/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include <windows.h> + + + +// TODO: reference additional headers your program requires here diff --git a/windows/winfw/src/winfw/targetver.h b/windows/winfw/src/winfw/targetver.h new file mode 100644 index 0000000000..ae4a5c032c --- /dev/null +++ b/windows/winfw/src/winfw/targetver.h @@ -0,0 +1,12 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include <WinSDKVer.h> + +#define _WIN32_WINNT _WIN32_WINNT_WIN7 + +#include <SDKDDKVer.h> diff --git a/windows/winfw/src/winfw/winfw.cpp b/windows/winfw/src/winfw/winfw.cpp new file mode 100644 index 0000000000..c7e171a664 --- /dev/null +++ b/windows/winfw/src/winfw/winfw.cpp @@ -0,0 +1,208 @@ +#include "stdafx.h" +#include "winfw.h" +#include "fwcontext.h" +#include "libwfp/ipaddress.h" +#include <windows.h> +#include <stdexcept> + +namespace +{ + +uint32_t g_timeout = 0; + +WinFwErrorSink g_ErrorSink = nullptr; +void * g_ErrorContext = nullptr; + +FwContext *g_fwContext = nullptr; + +} // anonymous namespace + +WINFW_LINKAGE +bool +WINFW_API +WinFw_Initialize( + uint32_t timeout, + 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); + } + catch (std::exception &err) + { + if (nullptr != g_ErrorSink) + { + g_ErrorSink(err.what(), g_ErrorContext); + } + + return false; + } + catch (...) + { + return false; + } + + return true; +} + +WINFW_LINKAGE +bool +WINFW_API +WinFw_Deinitialize() +{ + if (nullptr == g_fwContext) + { + return true; + } + + delete g_fwContext; + g_fwContext = nullptr; + + return true; +} + +WINFW_LINKAGE +bool +WINFW_API +WinFw_ApplyPolicyConnecting( + const WinFwSettings &settings, + const WinFwRelay &relay +) +{ + if (nullptr == g_fwContext) + { + return false; + } + + try + { + return g_fwContext->applyPolicyConnecting(settings, relay); + } + catch (std::exception &err) + { + if (nullptr != g_ErrorSink) + { + g_ErrorSink(err.what(), g_ErrorContext); + } + + return false; + } + catch (...) + { + return false; + } +} + +WINFW_LINKAGE +bool +WINFW_API +WinFw_ApplyPolicyConnected( + const WinFwSettings &settings, + const WinFwRelay &relay, + const wchar_t *tunnelInterfaceAlias, + const wchar_t *primaryDns +) +{ + if (nullptr == g_fwContext) + { + return false; + } + + try + { + return g_fwContext->applyPolicyConnected(settings, relay, tunnelInterfaceAlias, primaryDns); + } + catch (std::exception &err) + { + if (nullptr != g_ErrorSink) + { + g_ErrorSink(err.what(), g_ErrorContext); + } + + return false; + } + catch (...) + { + return false; + } +} + +WINFW_LINKAGE +bool +WINFW_API +WinFw_ApplyPolicyNetBlocked( +) +{ + if (nullptr == g_fwContext) + { + return false; + } + + try + { + return g_fwContext->applyPolicyNetBlocked(); + } + catch (std::exception &err) + { + if (nullptr != g_ErrorSink) + { + g_ErrorSink(err.what(), g_ErrorContext); + } + + return false; + } + catch (...) + { + return false; + } +} + +WINFW_LINKAGE +bool +WINFW_API +WinFw_Reset() +{ + if (nullptr == g_fwContext) + { + // + // This is an error. + // The reset instance enforces the block-all rule. + // + return false; + } + + try + { + return g_fwContext->reset(); + } + catch (std::exception &err) + { + if (nullptr != g_ErrorSink) + { + g_ErrorSink(err.what(), g_ErrorContext); + } + + return false; + } + catch (...) + { + return false; + } +} diff --git a/windows/winfw/src/winfw/winfw.def b/windows/winfw/src/winfw/winfw.def new file mode 100644 index 0000000000..ff028b76d0 --- /dev/null +++ b/windows/winfw/src/winfw/winfw.def @@ -0,0 +1,9 @@ +LIBRARY winfw +EXPORTS + +WinFw_Initialize +WinFw_Deinitialize +WinFw_ApplyPolicyConnecting +WinFw_ApplyPolicyConnected +WinFw_ApplyPolicyNetBlocked +WinFw_Reset diff --git a/windows/winfw/src/winfw/winfw.h b/windows/winfw/src/winfw/winfw.h new file mode 100644 index 0000000000..a575b4d436 --- /dev/null +++ b/windows/winfw/src/winfw/winfw.h @@ -0,0 +1,150 @@ +#pragma once +#include <cstdint> + +// +// WINFW public API +// + +#ifdef WINFW_EXPORTS +#define WINFW_LINKAGE __declspec(dllexport) +#else +#define WINFW_LINKAGE __declspec(dllimport) +#endif + +#define WINFW_API __stdcall + +/////////////////////////////////////////////////////////////////////////////// +// Structures +/////////////////////////////////////////////////////////////////////////////// + +#pragma pack(push, 1) + +typedef struct tag_WinFwSettings +{ + // Permit outbound DHCP requests and inbound DHCP responses on all interfaces. + bool permitDhcp; + + // Permit all traffic to and from private address ranges. + bool permitLan; +} +WinFwSettings; + +enum WinFwProtocol : uint8_t +{ + Tcp = 0, + Udp = 1 +}; + +typedef struct tag_WinFwRelay +{ + const wchar_t *ip; + uint16_t port; + WinFwProtocol protocol; +} +WinFwRelay; + +#pragma pack(pop) + +/////////////////////////////////////////////////////////////////////////////// +// Functions +/////////////////////////////////////////////////////////////////////////////// + +// +// Initialize: +// +// Call this function once at startup, to register the provider etc. +// +// The timeout, in seconds, specifies how long to wait for the +// transaction lock to become available. Specify 0 to use a default timeout +// determined by Windows. +// +// Optionally provide a callback if you are interested in logging exceptions. +// +typedef void (WINFW_API *WinFwErrorSink)(const char *errorMessage, void *context); + +extern "C" +WINFW_LINKAGE +bool +WINFW_API +WinFw_Initialize( + uint32_t timeout, + WinFwErrorSink errorSink, + void *errorContext +); + +// +// Deinitialize: +// +// Call this function once before unloading WFPCTL or exiting the process. +// +extern "C" +WINFW_LINKAGE +bool +WINFW_API +WinFw_Deinitialize(); + +// +// ApplyPolicyConnecting: +// +// Apply restrictions in the firewall that block all traffic, except: +// - What is specified by settings +// - Communication with the relay server +// +extern "C" +WINFW_LINKAGE +bool +WINFW_API +WinFw_ApplyPolicyConnecting( + const WinFwSettings &settings, + const WinFwRelay &relay +); + +// +// ApplyPolicyConnected: +// +// Apply restrictions in the firewall that block all traffic, except: +// - What is specified by settings +// - Communication with the relay server +// - Non-DNS traffic inside the VPN tunnel +// - DNS requests inside the VPN tunnel, to the specified DNS server +// +// Parameters: +// +// tunnelInterfaceAlias: +// Friendly name of VPN tunnel interface +// primaryDns: +// String encoded IP address of DNS to use inside tunnel +// +extern "C" +WINFW_LINKAGE +bool +WINFW_API +WinFw_ApplyPolicyConnected( + const WinFwSettings &settings, + const WinFwRelay &relay, + const wchar_t *tunnelInterfaceAlias, + const wchar_t *primaryDns +); + +// +// ApplyPolicyNetBlocked: +// +// Apply restrictions in the firewall that block all traffic. +// +extern "C" +WINFW_LINKAGE +bool +WINFW_API +WinFw_ApplyPolicyNetBlocked( +); + +// +// Reset: +// +// Clear the policy in effect, if any. +// +extern "C" +WINFW_LINKAGE +bool +WINFW_API +WinFw_Reset(); diff --git a/windows/winfw/src/winfw/winfw.vcxproj b/windows/winfw/src/winfw/winfw.vcxproj new file mode 100644 index 0000000000..f6c59f9486 --- /dev/null +++ b/windows/winfw/src/winfw/winfw.vcxproj @@ -0,0 +1,226 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="mullvadguids.cpp" /> + <ClCompile Include="mullvadobjects.cpp" /> + <ClCompile Include="rules\blockall.cpp" /> + <ClCompile Include="rules\permitdhcp.cpp" /> + <ClCompile Include="rules\permitlan.cpp" /> + <ClCompile Include="rules\permitlanservice.cpp" /> + <ClCompile Include="rules\permitloopback.cpp" /> + <ClCompile Include="rules\permitvpnrelay.cpp" /> + <ClCompile Include="rules\permitvpntunnel.cpp" /> + <ClCompile Include="rules\restrictdns.cpp" /> + <ClCompile Include="sessioncontroller.cpp" /> + <ClCompile Include="sessionrecord.cpp" /> + <ClCompile Include="stdafx.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="fwcontext.cpp" /> + <ClCompile Include="winfw.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="iobjectinstaller.h" /> + <ClInclude Include="mullvadguids.h" /> + <ClInclude Include="mullvadobjects.h" /> + <ClInclude Include="rules\blockall.h" /> + <ClInclude Include="rules\ifirewallrule.h" /> + <ClInclude Include="rules\permitdhcp.h" /> + <ClInclude Include="rules\permitlan.h" /> + <ClInclude Include="rules\permitlanservice.h" /> + <ClInclude Include="rules\permitloopback.h" /> + <ClInclude Include="rules\permitvpnrelay.h" /> + <ClInclude Include="rules\permitvpntunnel.h" /> + <ClInclude Include="rules\restrictdns.h" /> + <ClInclude Include="sessioncontroller.h" /> + <ClInclude Include="sessionrecord.h" /> + <ClInclude Include="stdafx.h" /> + <ClInclude Include="targetver.h" /> + <ClInclude Include="fwcontext.h" /> + <ClInclude Include="winfw.h" /> + </ItemGroup> + <ItemGroup> + <None Include="winfw.def" /> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>15.0</VCProjectVersion> + <ProjectGuid>{801E7DEB-2BD0-4E60-9E4B-74A5CA12ADCB}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>winfw</RootNamespace> + <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v141</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(SolutionDir)\bin\$(Platform)-$(Configuration)\</OutDir> + <IntDir>$(SolutionDir)\bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>Disabled</Optimization> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;_DEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>Disabled</Optimization> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>WIN32;NDEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;WINFW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <LanguageStandard>stdcpplatest</LanguageStandard> + <AdditionalIncludeDirectories>$(ProjectDir)..\;$(ProjectDir)..\..\..\windows-libraries\src\;$(ProjectDir)..\..\..\libwfp\src\</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>libwfp.lib;libcommon.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(SolutionDir)/bin/$(Platform)-$(Configuration)</AdditionalLibraryDirectories> + <ModuleDefinitionFile>winfw.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/windows/winfw/src/winfw/winfw.vcxproj.filters b/windows/winfw/src/winfw/winfw.vcxproj.filters new file mode 100644 index 0000000000..1b7c0711f6 --- /dev/null +++ b/windows/winfw/src/winfw/winfw.vcxproj.filters @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="dllmain.cpp" /> + <ClCompile Include="stdafx.cpp" /> + <ClCompile Include="winfw.cpp" /> + <ClCompile Include="fwcontext.cpp" /> + <ClCompile Include="sessioncontroller.cpp" /> + <ClCompile Include="mullvadguids.cpp" /> + <ClCompile Include="mullvadobjects.cpp" /> + <ClCompile Include="rules\permitlan.cpp"> + <Filter>rules</Filter> + </ClCompile> + <ClCompile Include="rules\blockall.cpp"> + <Filter>rules</Filter> + </ClCompile> + <ClCompile Include="rules\permitlanservice.cpp"> + <Filter>rules</Filter> + </ClCompile> + <ClCompile Include="rules\permitloopback.cpp"> + <Filter>rules</Filter> + </ClCompile> + <ClCompile Include="rules\permitdhcp.cpp"> + <Filter>rules</Filter> + </ClCompile> + <ClCompile Include="rules\permitvpnrelay.cpp"> + <Filter>rules</Filter> + </ClCompile> + <ClCompile Include="rules\permitvpntunnel.cpp"> + <Filter>rules</Filter> + </ClCompile> + <ClCompile Include="sessionrecord.cpp" /> + <ClCompile Include="rules\restrictdns.cpp"> + <Filter>rules</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="stdafx.h" /> + <ClInclude Include="targetver.h" /> + <ClInclude Include="winfw.h" /> + <ClInclude Include="fwcontext.h" /> + <ClInclude Include="sessioncontroller.h" /> + <ClInclude Include="mullvadguids.h" /> + <ClInclude Include="mullvadobjects.h" /> + <ClInclude Include="rules\ifirewallrule.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="rules\permitlan.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="iobjectinstaller.h" /> + <ClInclude Include="rules\blockall.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="rules\permitlanservice.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="rules\permitloopback.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="rules\permitdhcp.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="rules\permitvpnrelay.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="rules\permitvpntunnel.h"> + <Filter>rules</Filter> + </ClInclude> + <ClInclude Include="sessionrecord.h" /> + <ClInclude Include="rules\restrictdns.h"> + <Filter>rules</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Filter Include="rules"> + <UniqueIdentifier>{3639a3ba-599e-400c-918b-8869654cdc1f}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <None Include="winfw.def" /> + </ItemGroup> +</Project>
\ No newline at end of file |
