From 97b80191048b23f2e870c9d6a27e368ebaaf594d Mon Sep 17 00:00:00 2001 From: Rob Mensching <rob@firegiant.com> Date: Tue, 2 Jan 2018 23:14:58 -0800 Subject: Initial code commit --- src/Cpp.Build.props | 101 ++ src/Directory.Build.props | 22 + src/FindLocalWix.props | 8 + src/ca/CustomMsiErrors.h | 130 +++ src/ca/cost.h | 5 + src/ca/dllmain.cpp | 26 + src/ca/firewall.cpp | 1067 ++++++++++++++++++++ src/ca/fwca.def | 9 + src/ca/fwca.vcxproj | 72 ++ src/ca/packages.config | 6 + src/ca/precomp.h | 17 + .../FirewallExtensionFixture.cs | 33 + .../TestData/UsingFirewall/Package.en-us.wxl | 11 + .../TestData/UsingFirewall/Package.wxs | 22 + .../TestData/UsingFirewall/PackageComponents.wxs | 14 + .../TestData/UsingFirewall/example.txt | 1 + .../WixToolsetTest.Firewall.csproj | 40 + src/wixext/FirewallCompiler.cs | 356 +++++++ src/wixext/FirewallConstants.cs | 21 + src/wixext/FirewallDecompiler.cs | 169 ++++ src/wixext/FirewallErrors.cs | 42 + src/wixext/FirewallExtensionData.cs | 24 + src/wixext/FirewallExtensionFactory.cs | 18 + .../FirewallWindowsInstallerBackendExtension.cs | 26 + src/wixext/Tuples/FirewallTupleDefinitions.cs | 31 + src/wixext/Tuples/WixFirewallExceptionTuple.cs | 93 ++ src/wixext/WixToolset.Firewall.wixext.csproj | 36 + src/wixext/WixToolset.Firewall.wixext.targets | 11 + src/wixext/firewall.xsd | 211 ++++ src/wixext/tables.xml | 28 + src/wixlib/FirewallExtension.wxs | 12 + src/wixlib/FirewallExtension_Platform.wxi | 41 + src/wixlib/FirewallExtension_x86.wxs | 8 + src/wixlib/caSuffix.wxi | 28 + src/wixlib/caerr.wxi | 96 ++ src/wixlib/en-us.wxl | 14 + src/wixlib/es-es.wxl | 13 + src/wixlib/firewall.wixproj | 44 + src/wixlib/ja-jp.wxl | 14 + src/wixlib/packages.config | 5 + src/wixlib/pl-pl.wxl | 14 + 41 files changed, 2939 insertions(+) create mode 100644 src/Cpp.Build.props create mode 100644 src/Directory.Build.props create mode 100644 src/FindLocalWix.props create mode 100644 src/ca/CustomMsiErrors.h create mode 100644 src/ca/cost.h create mode 100644 src/ca/dllmain.cpp create mode 100644 src/ca/firewall.cpp create mode 100644 src/ca/fwca.def create mode 100644 src/ca/fwca.vcxproj create mode 100644 src/ca/packages.config create mode 100644 src/ca/precomp.h create mode 100644 src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.en-us.wxl create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.wxs create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/PackageComponents.wxs create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/example.txt create mode 100644 src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj create mode 100644 src/wixext/FirewallCompiler.cs create mode 100644 src/wixext/FirewallConstants.cs create mode 100644 src/wixext/FirewallDecompiler.cs create mode 100644 src/wixext/FirewallErrors.cs create mode 100644 src/wixext/FirewallExtensionData.cs create mode 100644 src/wixext/FirewallExtensionFactory.cs create mode 100644 src/wixext/FirewallWindowsInstallerBackendExtension.cs create mode 100644 src/wixext/Tuples/FirewallTupleDefinitions.cs create mode 100644 src/wixext/Tuples/WixFirewallExceptionTuple.cs create mode 100644 src/wixext/WixToolset.Firewall.wixext.csproj create mode 100644 src/wixext/WixToolset.Firewall.wixext.targets create mode 100644 src/wixext/firewall.xsd create mode 100644 src/wixext/tables.xml create mode 100644 src/wixlib/FirewallExtension.wxs create mode 100644 src/wixlib/FirewallExtension_Platform.wxi create mode 100644 src/wixlib/FirewallExtension_x86.wxs create mode 100644 src/wixlib/caSuffix.wxi create mode 100644 src/wixlib/caerr.wxi create mode 100644 src/wixlib/en-us.wxl create mode 100644 src/wixlib/es-es.wxl create mode 100644 src/wixlib/firewall.wixproj create mode 100644 src/wixlib/ja-jp.wxl create mode 100644 src/wixlib/packages.config create mode 100644 src/wixlib/pl-pl.wxl (limited to 'src') diff --git a/src/Cpp.Build.props b/src/Cpp.Build.props new file mode 100644 index 00000000..453aa442 --- /dev/null +++ b/src/Cpp.Build.props @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Project> + <PropertyGroup> + <Platform Condition=" '$(Platform)' == 'AnyCPU' ">Win32</Platform> + <BaseOutputPath>$(OutputPath)</BaseOutputPath> + <IntDir>$(BaseIntermediateOutputPath)$(Platform)\</IntDir> + <OutDir>$(OutputPath)$(Platform)\</OutDir> + </PropertyGroup> + + <ItemDefinitionGroup> + <ClCompile> + <DisableSpecificWarnings>$(DisableSpecificCompilerWarnings)</DisableSpecificWarnings> + <WarningLevel>Level4</WarningLevel> + <AdditionalIncludeDirectories>$(ProjectDir)inc;$(MSBuildProjectDirectory);$(IntDir);$(SqlCESdkIncludePath);$(ProjectAdditionalIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>WIN32;_WINDOWS;_WIN32_MSI=500;_WIN32_WINNT=0x0501;$(ArmPreprocessorDefinitions);$(UnicodePreprocessorDefinitions);_CRT_STDIO_LEGACY_WIDE_SPECIFIERS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile> + <CallingConvention>StdCall</CallingConvention> + <TreatWarningAsError>true</TreatWarningAsError> + <ExceptionHandling>false</ExceptionHandling> + <AdditionalOptions>-YlprecompDefine</AdditionalOptions> + <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/Zc:threadSafeInit- %(AdditionalOptions)</AdditionalOptions> + <MultiProcessorCompilation Condition=" $(NUMBER_OF_PROCESSORS) > 4 ">true</MultiProcessorCompilation> + </ClCompile> + <ResourceCompile> + <PreprocessorDefinitions>$(ArmPreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(ProjectAdditionalResourceIncludeDirectories);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ResourceCompile> + <Lib> + <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ProjectAdditionalLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Lib> + <Link> + <SubSystem>$(ProjectSubSystem)</SubSystem> + <ModuleDefinitionFile>$(ProjectModuleDefinitionFile)</ModuleDefinitionFile> + <NoEntryPoint>$(ResourceOnlyDll)</NoEntryPoint> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalDependencies>$(ProjectAdditionalLinkLibraries);advapi32.lib;comdlg32.lib;user32.lib;oleaut32.lib;gdi32.lib;shell32.lib;ole32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories>$(OutDir);$(AdditionalMultiTargetLibraryPath);$(ArmLibraryDirectories);$(ProjectAdditionalLinkLibraryDirectories);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalOptions Condition=" $(PlatformToolset.StartsWith('v14')) ">/IGNORE:4099 %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + + <ItemDefinitionGroup Condition=" '$(Platform)'=='Win32' and '$(PlatformToolset)'!='v100'"> + <ClCompile> + <EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition=" '$(Platform)'=='arm' "> + <ClCompile> + <CallingConvention>CDecl</CallingConvention> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition=" '$(ConfigurationType)'=='StaticLibrary' "> + <ClCompile> + <DebugInformationFormat>OldStyle</DebugInformationFormat> + <OmitDefaultLibName>true</OmitDefaultLibName> + <IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' "> + <ClCompile> + <Optimization>Disabled</Optimization> + <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> + <PreprocessorDefinitions>_DEBUG;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Debug' and '$(CLRSupport)'=='true' "> + <ClCompile> + <BasicRuntimeChecks></BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDebugDll</RuntimeLibrary> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' "> + <ClCompile> + <Optimization>MinSpace</Optimization> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition=" '$(Configuration)'=='Release' and '$(CLRSupport)'=='true' "> + <ClCompile> + <BasicRuntimeChecks></BasicRuntimeChecks> + <RuntimeLibrary>MultiThreadedDll</RuntimeLibrary> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition=" '$(CLRSupport)'=='true' "> + <Link> + <KeyFile>$(LinkKeyFile)</KeyFile> + <DelaySign>$(LinkDelaySign)</DelaySign> + </Link> + </ItemDefinitionGroup> +</Project> diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..63ad5d6e --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Project> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)..\build\obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath> + <OutputPath>$(MSBuildThisFileDirectory)..\build\$(Configuration)\</OutputPath> + + <Authors>WiX Toolset Team</Authors> + <Company>WiX Toolset</Company> + <Copyright>Copyright (c) .NET Foundation and contributors. All rights reserved.</Copyright> + </PropertyGroup> + + <PropertyGroup> + <WixToolsetRootFolder>$(MSBuildThisFileDirectory)..\..\</WixToolsetRootFolder> + </PropertyGroup> + + <Import Project="Cpp.Build.props" Condition=" '$(MSBuildProjectExtension)'=='.vcxproj' " /> + <Import Project="Custom.Build.props" Condition=" Exists('Custom.Build.props') " /> +</Project> diff --git a/src/FindLocalWix.props b/src/FindLocalWix.props new file mode 100644 index 00000000..1301b0e5 --- /dev/null +++ b/src/FindLocalWix.props @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <WixTargetsPath Condition=" '$(Configuration)' != 'Release' And Exists('$(MSBuildThisFileDirectory)..\..\Core\README.md') ">$(MSBuildThisFileDirectory)..\..\Core\build\Debug\net461\wix.targets</WixTargetsPath> + </PropertyGroup> +</Project> diff --git a/src/ca/CustomMsiErrors.h b/src/ca/CustomMsiErrors.h new file mode 100644 index 00000000..f149fb31 --- /dev/null +++ b/src/ca/CustomMsiErrors.h @@ -0,0 +1,130 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#define GLOBAL_ERROR_BASE 25501 + +#define msierrSecureObjectsFailedCreateSD 25520 +#define msierrSecureObjectsFailedSet 25521 +#define msierrSecureObjectsUnknownType 25522 + +#define msierrXmlFileFailedRead 25530 +#define msierrXmlFileFailedOpen 25531 +#define msierrXmlFileFailedSelect 25532 +#define msierrXmlFileFailedSave 25533 + +#define msierrXmlConfigFailedRead 25540 +#define msierrXmlConfigFailedOpen 25541 +#define msierrXmlConfigFailedSelect 25542 +#define msierrXmlConfigFailedSave 25543 + +#define msierrFirewallCannotConnect 25580 + +//--------------------------------------------------------------------------- +// Server CustomAction Errors +// SERVER range: 26001-26100 +#define SERVER_ERROR_BASE 26000 + +#define msierrIISCannotConnect 26001 +#define msierrIISFailedReadWebSite 26002 +#define msierrIISFailedReadWebDirs 26003 +#define msierrIISFailedReadVDirs 26004 +#define msierrIISFailedReadFilters 26005 +#define msierrIISFailedReadAppPool 26006 +#define msierrIISFailedReadMimeMap 26007 +#define msierrIISFailedReadProp 26008 +#define msierrIISFailedReadWebSvcExt 26009 +#define msierrIISFailedReadWebError 26010 +#define msierrIISFailedReadHttpHeader 26011 + +#define msierrIISFailedSchedTransaction 26031 +#define msierrIISFailedSchedInstallWebs 26032 +#define msierrIISFailedSchedInstallWebDirs 26033 +#define msierrIISFailedSchedInstallVDirs 26034 +#define msierrIISFailedSchedInstallFilters 26035 +#define msierrIISFailedSchedInstallAppPool 26036 +#define msierrIISFailedSchedInstallProp 26037 +#define msierrIISFailedSchedInstallWebSvcExt 26038 + +#define msierrIISFailedSchedUninstallWebs 26051 +#define msierrIISFailedSchedUninstallWebDirs 26052 +#define msierrIISFailedSchedUninstallVDirs 26053 +#define msierrIISFailedSchedUninstallFilters 26054 +#define msierrIISFailedSchedUninstallAppPool 26055 +#define msierrIISFailedSchedUninstallProp 26056 +#define msierrIISFailedSchedUninstallWebSvcExt 26057 + +#define msierrIISFailedStartTransaction 26101 +#define msierrIISFailedOpenKey 26102 +#define msierrIISFailedCreateKey 26103 +#define msierrIISFailedWriteData 26104 +#define msierrIISFailedCreateApp 26105 +#define msierrIISFailedDeleteKey 26106 +#define msierrIISFailedDeleteApp 26107 +#define msierrIISFailedDeleteValue 26108 +#define msierrIISFailedCommitInUse 26109 + +#define msierrSQLFailedCreateDatabase 26201 +#define msierrSQLFailedDropDatabase 26202 +#define msierrSQLFailedConnectDatabase 26203 +#define msierrSQLFailedExecString 26204 +#define msierrSQLDatabaseAlreadyExists 26205 + +#define msierrPERFMONFailedRegisterDLL 26251 +#define msierrPERFMONFailedUnregisterDLL 26252 +#define msierrInstallPerfCounterData 26253 +#define msierrUninstallPerfCounterData 26254 + +#define msierrSMBFailedCreate 26301 +#define msierrSMBFailedDrop 26302 + +#define msierrCERTFailedOpen 26351 +#define msierrCERTFailedAdd 26352 + +#define msierrUSRFailedUserCreate 26401 +#define msierrUSRFailedUserCreatePswd 26402 +#define msierrUSRFailedUserGroupAdd 26403 +#define msierrUSRFailedUserCreateExists 26404 +#define msierrUSRFailedGrantLogonAsService 26405 + +#define msierrDependencyMissingDependencies 26451 +#define msierrDependencyHasDependents 26452 + +//-------------------------------------------------------------------------- +// Managed code CustomAction Errors +// MANAGED range: 27000-27100 +#define MANAGED_ERROR_BASE 27000 + +#define msierrDotNetRuntimeRequired 27000 +//--------------------------------------------------------------------------- +// Public CustomAction Errors +// PUBLIC range: 28001-28100 +#define PUBLIC_ERROR_BASE 28000 + +#define msierrComPlusCannotConnect 28001 +#define msierrComPlusPartitionReadFailed 28002 +#define msierrComPlusPartitionRoleReadFailed 28003 +#define msierrComPlusUserInPartitionRoleReadFailed 28004 +#define msierrComPlusPartitionUserReadFailed 28005 +#define msierrComPlusApplicationReadFailed 28006 +#define msierrComPlusApplicationRoleReadFailed 28007 +#define msierrComPlusUserInApplicationRoleReadFailed 28008 +#define msierrComPlusAssembliesReadFailed 28009 +#define msierrComPlusSubscriptionReadFailed 28010 +#define msierrComPlusPartitionDependency 28011 +#define msierrComPlusPartitionNotFound 28012 +#define msierrComPlusPartitionIdConflict 28013 +#define msierrComPlusPartitionNameConflict 28014 +#define msierrComPlusApplicationDependency 28015 +#define msierrComPlusApplicationNotFound 28016 +#define msierrComPlusApplicationIdConflict 28017 +#define msierrComPlusApplicationNameConflict 28018 +#define msierrComPlusApplicationRoleDependency 28019 +#define msierrComPlusApplicationRoleNotFound 28020 +#define msierrComPlusApplicationRoleConflict 28021 +#define msierrComPlusAssemblyDependency 28022 +#define msierrComPlusSubscriptionIdConflict 28023 +#define msierrComPlusSubscriptionNameConflict 28024 +#define msierrComPlusFailedLookupNames 28025 + +#define msierrMsmqCannotConnect 28101 diff --git a/src/ca/cost.h b/src/ca/cost.h new file mode 100644 index 00000000..da68c667 --- /dev/null +++ b/src/ca/cost.h @@ -0,0 +1,5 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +const UINT COST_FIREWALL_EXCEPTION = 2000; diff --git a/src/ca/dllmain.cpp b/src/ca/dllmain.cpp new file mode 100644 index 00000000..df53f872 --- /dev/null +++ b/src/ca/dllmain.cpp @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +/******************************************************************** +DllMain - standard entry point for all WiX CustomActions + +********************************************************************/ +extern "C" BOOL WINAPI DllMain( + IN HINSTANCE hInst, + IN ULONG ulReason, + IN LPVOID) +{ + switch(ulReason) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hInst); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + } + + return TRUE; +} diff --git a/src/ca/firewall.cpp b/src/ca/firewall.cpp new file mode 100644 index 00000000..62a5b454 --- /dev/null +++ b/src/ca/firewall.cpp @@ -0,0 +1,1067 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +LPCWSTR vcsFirewallExceptionQuery = + L"SELECT `Name`, `RemoteAddresses`, `Port`, `Protocol`, `Program`, `Attributes`, `Profile`, `Component_`, `Description` FROM `WixFirewallException`"; +enum eFirewallExceptionQuery { feqName = 1, feqRemoteAddresses, feqPort, feqProtocol, feqProgram, feqAttributes, feqProfile, feqComponent, feqDescription }; +enum eFirewallExceptionTarget { fetPort = 1, fetApplication, fetUnknown }; +enum eFirewallExceptionAttributes { feaIgnoreFailures = 1 }; + +/****************************************************************** + SchedFirewallExceptions - immediate custom action worker to + register and remove firewall exceptions. + +********************************************************************/ +static UINT SchedFirewallExceptions( + __in MSIHANDLE hInstall, + WCA_TODO todoSched + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + int cFirewallExceptions = 0; + + PMSIHANDLE hView = NULL; + PMSIHANDLE hRec = NULL; + + LPWSTR pwzCustomActionData = NULL; + LPWSTR pwzName = NULL; + LPWSTR pwzRemoteAddresses = NULL; + LPWSTR pwzPort = NULL; + int iProtocol = 0; + int iAttributes = 0; + int iProfile = 0; + LPWSTR pwzProgram = NULL; + LPWSTR pwzComponent = NULL; + LPWSTR pwzFormattedFile = NULL; + LPWSTR pwzDescription = NULL; + + // initialize + hr = WcaInitialize(hInstall, "SchedFirewallExceptions"); + ExitOnFailure(hr, "failed to initialize"); + + // anything to do? + if (S_OK != WcaTableExists(L"WixFirewallException")) + { + WcaLog(LOGMSG_STANDARD, "WixFirewallException table doesn't exist, so there are no firewall exceptions to configure."); + ExitFunction(); + } + + // query and loop through all the firewall exceptions + hr = WcaOpenExecuteView(vcsFirewallExceptionQuery, &hView); + ExitOnFailure(hr, "failed to open view on WixFirewallException table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + hr = WcaGetRecordFormattedString(hRec, feqName, &pwzName); + ExitOnFailure(hr, "failed to get firewall exception name"); + + hr = WcaGetRecordFormattedString(hRec, feqRemoteAddresses, &pwzRemoteAddresses); + ExitOnFailure(hr, "failed to get firewall exception remote addresses"); + + hr = WcaGetRecordFormattedString(hRec, feqPort, &pwzPort); + ExitOnFailure(hr, "failed to get firewall exception port"); + + hr = WcaGetRecordInteger(hRec, feqProtocol, &iProtocol); + ExitOnFailure(hr, "failed to get firewall exception protocol"); + + hr = WcaGetRecordFormattedString(hRec, feqProgram, &pwzProgram); + ExitOnFailure(hr, "failed to get firewall exception program"); + + hr = WcaGetRecordInteger(hRec, feqAttributes, &iAttributes); + ExitOnFailure(hr, "failed to get firewall exception attributes"); + + hr = WcaGetRecordInteger(hRec, feqProfile, &iProfile); + ExitOnFailure(hr, "failed to get firewall exception profile"); + + hr = WcaGetRecordString(hRec, feqComponent, &pwzComponent); + ExitOnFailure(hr, "failed to get firewall exception component"); + + hr = WcaGetRecordString(hRec, feqDescription, &pwzDescription); + ExitOnFailure(hr, "failed to get firewall description"); + + // figure out what we're doing for this exception, treating reinstall the same as install + WCA_TODO todoComponent = WcaGetComponentToDo(pwzComponent); + if ((WCA_TODO_REINSTALL == todoComponent ? WCA_TODO_INSTALL : todoComponent) != todoSched) + { + WcaLog(LOGMSG_STANDARD, "Component '%ls' action state (%d) doesn't match request (%d)", pwzComponent, todoComponent, todoSched); + continue; + } + + // action :: name :: profile :: remoteaddresses :: attributes :: target :: {port::protocol | path} + ++cFirewallExceptions; + hr = WcaWriteIntegerToCaData(todoComponent, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception action to custom action data"); + + hr = WcaWriteStringToCaData(pwzName, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception name to custom action data"); + + hr = WcaWriteIntegerToCaData(iProfile, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception profile to custom action data"); + + hr = WcaWriteStringToCaData(pwzRemoteAddresses, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception remote addresses to custom action data"); + + hr = WcaWriteIntegerToCaData(iAttributes, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception attributes to custom action data"); + + if (*pwzProgram) + { + // If program is defined, we have an application exception. + hr = WcaWriteIntegerToCaData(fetApplication, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception target (application) to custom action data"); + + hr = WcaWriteStringToCaData(pwzProgram, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write application path to custom action data"); + } + else + { + // we have a port-only exception + hr = WcaWriteIntegerToCaData(fetPort, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception target (port) to custom action data"); + } + + hr = WcaWriteStringToCaData(pwzPort, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write application path to custom action data"); + + hr = WcaWriteIntegerToCaData(iProtocol, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write exception protocol to custom action data"); + + hr = WcaWriteStringToCaData(pwzDescription, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write firewall rule description to custom action data"); + } + + // reaching the end of the list is actually a good thing, not an error + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "failure occured while processing WixFirewallException table"); + + // schedule ExecFirewallExceptions if there's anything to do + if (pwzCustomActionData && *pwzCustomActionData) + { + WcaLog(LOGMSG_STANDARD, "Scheduling firewall exception (%ls)", pwzCustomActionData); + + if (WCA_TODO_INSTALL == todoSched) + { + hr = WcaDoDeferredAction(L"WixRollbackFirewallExceptionsInstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); + ExitOnFailure(hr, "failed to schedule firewall install exceptions rollback"); + hr = WcaDoDeferredAction(L"WixExecFirewallExceptionsInstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); + ExitOnFailure(hr, "failed to schedule firewall install exceptions execution"); + } + else + { + hr = WcaDoDeferredAction(L"WixRollbackFirewallExceptionsUninstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); + ExitOnFailure(hr, "failed to schedule firewall uninstall exceptions rollback"); + hr = WcaDoDeferredAction(L"WixExecFirewallExceptionsUninstall", pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION); + ExitOnFailure(hr, "failed to schedule firewall uninstall exceptions execution"); + } + } + else + { + WcaLog(LOGMSG_STANDARD, "No firewall exceptions scheduled"); + } + +LExit: + ReleaseStr(pwzCustomActionData); + ReleaseStr(pwzName); + ReleaseStr(pwzRemoteAddresses); + ReleaseStr(pwzPort); + ReleaseStr(pwzProgram); + ReleaseStr(pwzComponent); + ReleaseStr(pwzDescription); + ReleaseStr(pwzFormattedFile); + + return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er); +} + +/****************************************************************** + SchedFirewallExceptionsInstall - immediate custom action entry + point to register firewall exceptions. + +********************************************************************/ +extern "C" UINT __stdcall SchedFirewallExceptionsInstall( + __in MSIHANDLE hInstall + ) +{ + return SchedFirewallExceptions(hInstall, WCA_TODO_INSTALL); +} + +/****************************************************************** + SchedFirewallExceptionsUninstall - immediate custom action entry + point to remove firewall exceptions. + +********************************************************************/ +extern "C" UINT __stdcall SchedFirewallExceptionsUninstall( + __in MSIHANDLE hInstall + ) +{ + return SchedFirewallExceptions(hInstall, WCA_TODO_UNINSTALL); +} + +/****************************************************************** + GetFirewallRules - Get the collection of firewall rules. + +********************************************************************/ +static HRESULT GetFirewallRules( + __in BOOL fIgnoreFailures, + __out INetFwRules** ppNetFwRules + ) +{ + HRESULT hr = S_OK; + INetFwPolicy2* pNetFwPolicy2 = NULL; + INetFwRules* pNetFwRules = NULL; + *ppNetFwRules = NULL; + + do + { + ReleaseNullObject(pNetFwPolicy2); + ReleaseNullObject(pNetFwRules); + + if (SUCCEEDED(hr = ::CoCreateInstance(__uuidof(NetFwPolicy2), NULL, CLSCTX_ALL, __uuidof(INetFwPolicy2), (void**)&pNetFwPolicy2)) && + SUCCEEDED(hr = pNetFwPolicy2->get_Rules(&pNetFwRules))) + { + break; + } + else if (fIgnoreFailures) + { + ExitFunction1(hr = S_FALSE); + } + else + { + WcaLog(LOGMSG_STANDARD, "Failed to connect to Windows Firewall"); + UINT er = WcaErrorMessage(msierrFirewallCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: // exit with the current HRESULT + ExitFunction(); + case IDRETRY: // clean up and retry the loop + hr = S_FALSE; + break; + case IDIGNORE: // pass S_FALSE back to the caller, who knows how to ignore the failure + ExitFunction1(hr = S_FALSE); + default: // No UI, so default is to fail. + ExitFunction(); + } + } + } while (S_FALSE == hr); + + *ppNetFwRules = pNetFwRules; + pNetFwRules = NULL; + +LExit: + ReleaseObject(pNetFwPolicy2); + ReleaseObject(pNetFwRules); + + return hr; +} + +/****************************************************************** + CreateFwRuleObject - CoCreate a firewall rule, and set the common set of properties which are shared + between port and application firewall rules + +********************************************************************/ +static HRESULT CreateFwRuleObject( + __in BSTR bstrName, + __in int iProfile, + __in_opt LPCWSTR wzRemoteAddresses, + __in LPCWSTR wzPort, + __in int iProtocol, + __in LPCWSTR wzDescription, + __out INetFwRule** ppNetFwRule + ) +{ + HRESULT hr = S_OK; + BSTR bstrRemoteAddresses = NULL; + BSTR bstrPort = NULL; + BSTR bstrDescription = NULL; + INetFwRule* pNetFwRule = NULL; + *ppNetFwRule = NULL; + + // convert to BSTRs to make COM happy + bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses); + ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses"); + bstrPort = ::SysAllocString(wzPort); + ExitOnNull(bstrPort, hr, E_OUTOFMEMORY, "failed SysAllocString for port"); + bstrDescription = ::SysAllocString(wzDescription); + ExitOnNull(bstrDescription, hr, E_OUTOFMEMORY, "failed SysAllocString for description"); + + hr = ::CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_ALL, __uuidof(INetFwRule), (void**)&pNetFwRule); + ExitOnFailure(hr, "failed to create NetFwRule object"); + + hr = pNetFwRule->put_Name(bstrName); + ExitOnFailure(hr, "failed to set exception name"); + + hr = pNetFwRule->put_Profiles(static_cast<NET_FW_PROFILE_TYPE2>(iProfile)); + ExitOnFailure(hr, "failed to set exception profile"); + + if (MSI_NULL_INTEGER != iProtocol) + { + hr = pNetFwRule->put_Protocol(static_cast<NET_FW_IP_PROTOCOL>(iProtocol)); + ExitOnFailure(hr, "failed to set exception protocol"); + } + + if (bstrPort && *bstrPort) + { + hr = pNetFwRule->put_LocalPorts(bstrPort); + ExitOnFailure(hr, "failed to set exception port"); + } + + if (bstrRemoteAddresses && *bstrRemoteAddresses) + { + hr = pNetFwRule->put_RemoteAddresses(bstrRemoteAddresses); + ExitOnFailure(hr, "failed to set exception remote addresses '%ls'", bstrRemoteAddresses); + } + + if (bstrDescription && *bstrDescription) + { + hr = pNetFwRule->put_Description(bstrDescription); + ExitOnFailure(hr, "failed to set exception description '%ls'", bstrDescription); + } + + *ppNetFwRule = pNetFwRule; + pNetFwRule = NULL; + +LExit: + ReleaseBSTR(bstrRemoteAddresses); + ReleaseBSTR(bstrPort); + ReleaseBSTR(bstrDescription); + ReleaseObject(pNetFwRule); + + return hr; +} + +/****************************************************************** + FSupportProfiles - Returns true if we support profiles on this machine. + (Only on Vista or later) + +********************************************************************/ +static BOOL FSupportProfiles() +{ + BOOL fSupportProfiles = FALSE; + INetFwRules* pNetFwRules = NULL; + + // We only support profiles if we can co-create an instance of NetFwPolicy2. + // This will not work on pre-vista machines. + if (SUCCEEDED(GetFirewallRules(TRUE, &pNetFwRules)) && pNetFwRules != NULL) + { + fSupportProfiles = TRUE; + ReleaseObject(pNetFwRules); + } + + return fSupportProfiles; +} + +/****************************************************************** + GetCurrentFirewallProfile - get the active firewall profile as an + INetFwProfile, which owns the lists of exceptions we're + updating. + +********************************************************************/ +static HRESULT GetCurrentFirewallProfile( + __in BOOL fIgnoreFailures, + __out INetFwProfile** ppfwProfile + ) +{ + HRESULT hr = S_OK; + INetFwMgr* pfwMgr = NULL; + INetFwPolicy* pfwPolicy = NULL; + INetFwProfile* pfwProfile = NULL; + *ppfwProfile = NULL; + + do + { + ReleaseNullObject(pfwPolicy); + ReleaseNullObject(pfwMgr); + ReleaseNullObject(pfwProfile); + + if (SUCCEEDED(hr = ::CoCreateInstance(__uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&pfwMgr)) && + SUCCEEDED(hr = pfwMgr->get_LocalPolicy(&pfwPolicy)) && + SUCCEEDED(hr = pfwPolicy->get_CurrentProfile(&pfwProfile))) + { + break; + } + else if (fIgnoreFailures) + { + ExitFunction1(hr = S_FALSE); + } + else + { + WcaLog(LOGMSG_STANDARD, "Failed to connect to Windows Firewall"); + UINT er = WcaErrorMessage(msierrFirewallCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0); + switch (er) + { + case IDABORT: // exit with the current HRESULT + ExitFunction(); + case IDRETRY: // clean up and retry the loop + hr = S_FALSE; + break; + case IDIGNORE: // pass S_FALSE back to the caller, who knows how to ignore the failure + ExitFunction1(hr = S_FALSE); + default: // No UI, so default is to fail. + ExitFunction(); + } + } + } while (S_FALSE == hr); + + *ppfwProfile = pfwProfile; + pfwProfile = NULL; + +LExit: + ReleaseObject(pfwPolicy); + ReleaseObject(pfwMgr); + ReleaseObject(pfwProfile); + + return hr; +} + +/****************************************************************** + AddApplicationException + +********************************************************************/ +static HRESULT AddApplicationException( + __in LPCWSTR wzFile, + __in LPCWSTR wzName, + __in int iProfile, + __in_opt LPCWSTR wzRemoteAddresses, + __in BOOL fIgnoreFailures, + __in LPCWSTR wzPort, + __in int iProtocol, + __in LPCWSTR wzDescription + ) +{ + HRESULT hr = S_OK; + BSTR bstrFile = NULL; + BSTR bstrName = NULL; + INetFwRules* pNetFwRules = NULL; + INetFwRule* pNetFwRule = NULL; + + // convert to BSTRs to make COM happy + bstrFile = ::SysAllocString(wzFile); + ExitOnNull(bstrFile, hr, E_OUTOFMEMORY, "failed SysAllocString for path"); + bstrName = ::SysAllocString(wzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name"); + + // get the collection of firewall rules + hr = GetFirewallRules(fIgnoreFailures, &pNetFwRules); + ExitOnFailure(hr, "failed to get firewall rules object"); + if (S_FALSE == hr) // user or package author chose to ignore missing firewall + { + ExitFunction(); + } + + // try to find it (i.e., support reinstall) + hr = pNetFwRules->Item(bstrName, &pNetFwRule); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, &pNetFwRule); + ExitOnFailure(hr, "failed to create FwRule object"); + + // set edge traversal to true + hr = pNetFwRule->put_EdgeTraversal(VARIANT_TRUE); + ExitOnFailure(hr, "failed to set application exception edgetraversal property"); + + // set path + hr = pNetFwRule->put_ApplicationName(bstrFile); + ExitOnFailure(hr, "failed to set application name"); + + // enable it + hr = pNetFwRule->put_Enabled(VARIANT_TRUE); + ExitOnFailure(hr, "failed to to enable application exception"); + + // add it to the list of authorized apps + hr = pNetFwRules->Add(pNetFwRule); + ExitOnFailure(hr, "failed to add app to the authorized apps list"); + } + else + { + // we found an existing app exception (if we succeeded, that is) + ExitOnFailure(hr, "failed trying to find existing app"); + + // enable it (just in case it was disabled) + pNetFwRule->put_Enabled(VARIANT_TRUE); + } + +LExit: + ReleaseBSTR(bstrName); + ReleaseBSTR(bstrFile); + ReleaseObject(pNetFwRules); + ReleaseObject(pNetFwRule); + + return fIgnoreFailures ? S_OK : hr; +} + +/****************************************************************** + AddApplicationExceptionOnCurrentProfile + +********************************************************************/ +static HRESULT AddApplicationExceptionOnCurrentProfile( + __in LPCWSTR wzFile, + __in LPCWSTR wzName, + __in_opt LPCWSTR wzRemoteAddresses, + __in BOOL fIgnoreFailures + ) +{ + HRESULT hr = S_OK; + BSTR bstrFile = NULL; + BSTR bstrName = NULL; + BSTR bstrRemoteAddresses = NULL; + INetFwProfile* pfwProfile = NULL; + INetFwAuthorizedApplications* pfwApps = NULL; + INetFwAuthorizedApplication* pfwApp = NULL; + + // convert to BSTRs to make COM happy + bstrFile = ::SysAllocString(wzFile); + ExitOnNull(bstrFile, hr, E_OUTOFMEMORY, "failed SysAllocString for path"); + bstrName = ::SysAllocString(wzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name"); + bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses); + ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses"); + + // get the firewall profile, which is our entry point for adding exceptions + hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile); + ExitOnFailure(hr, "failed to get firewall profile"); + if (S_FALSE == hr) // user or package author chose to ignore missing firewall + { + ExitFunction(); + } + + // first, let's see if the app is already on the exception list + hr = pfwProfile->get_AuthorizedApplications(&pfwApps); + ExitOnFailure(hr, "failed to get list of authorized apps"); + + // try to find it (i.e., support reinstall) + hr = pfwApps->Item(bstrFile, &pfwApp); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + // not found, so we get to add it + hr = ::CoCreateInstance(__uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), reinterpret_cast<void**>(&pfwApp)); + ExitOnFailure(hr, "failed to create authorized app"); + + // set the display name + hr = pfwApp->put_Name(bstrName); + ExitOnFailure(hr, "failed to set authorized app name"); + + // set path + hr = pfwApp->put_ProcessImageFileName(bstrFile); + ExitOnFailure(hr, "failed to set authorized app path"); + + // set the allowed remote addresses + if (bstrRemoteAddresses && *bstrRemoteAddresses) + { + hr = pfwApp->put_RemoteAddresses(bstrRemoteAddresses); + ExitOnFailure(hr, "failed to set authorized app remote addresses"); + } + + // add it to the list of authorized apps + hr = pfwApps->Add(pfwApp); + ExitOnFailure(hr, "failed to add app to the authorized apps list"); + } + else + { + // we found an existing app exception (if we succeeded, that is) + ExitOnFailure(hr, "failed trying to find existing app"); + + // enable it (just in case it was disabled) + pfwApp->put_Enabled(VARIANT_TRUE); + } + +LExit: + ReleaseBSTR(bstrRemoteAddresses); + ReleaseBSTR(bstrName); + ReleaseBSTR(bstrFile); + ReleaseObject(pfwApp); + ReleaseObject(pfwApps); + ReleaseObject(pfwProfile); + + return fIgnoreFailures ? S_OK : hr; +} + +/****************************************************************** + AddPortException + +********************************************************************/ +static HRESULT AddPortException( + __in LPCWSTR wzName, + __in int iProfile, + __in_opt LPCWSTR wzRemoteAddresses, + __in BOOL fIgnoreFailures, + __in LPCWSTR wzPort, + __in int iProtocol, + __in LPCWSTR wzDescription + ) +{ + HRESULT hr = S_OK; + BSTR bstrName = NULL; + INetFwRules* pNetFwRules = NULL; + INetFwRule* pNetFwRule = NULL; + + // convert to BSTRs to make COM happy + bstrName = ::SysAllocString(wzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name"); + + // get the collection of firewall rules + hr = GetFirewallRules(fIgnoreFailures, &pNetFwRules); + ExitOnFailure(hr, "failed to get firewall rules object"); + if (S_FALSE == hr) // user or package author chose to ignore missing firewall + { + ExitFunction(); + } + + // try to find it (i.e., support reinstall) + hr = pNetFwRules->Item(bstrName, &pNetFwRule); + if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) + { + hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, &pNetFwRule); + ExitOnFailure(hr, "failed to create FwRule object"); + + // enable it + hr = pNetFwRule->put_Enabled(VARIANT_TRUE); + ExitOnFailure(hr, "failed to to enable port exception"); + + // add it to the list of authorized ports + hr = pNetFwRules->Add(pNetFwRule); + ExitOnFailure(hr, "failed to add app to the authorized ports list"); + } + else + { + // we found an existing port exception (if we succeeded, that is) + ExitOnFailure(hr, "failed trying to find existing port rule"); + + // enable it (just in case it was disabled) + pNetFwRule->put_Enabled(VARIANT_TRUE); + } + +LExit: + ReleaseBSTR(bstrName); + ReleaseObject(pNetFwRules); + ReleaseObject(pNetFwRule); + + return fIgnoreFailures ? S_OK : hr; +} + +/****************************************************************** + AddPortExceptionOnCurrentProfile + +********************************************************************/ +static HRESULT AddPortExceptionOnCurrentProfile( + __in LPCWSTR wzName, + __in_opt LPCWSTR wzRemoteAddresses, + __in BOOL fIgnoreFailures, + __in int iPort, + __in int iProtocol + ) +{ + HRESULT hr = S_OK; + BSTR bstrName = NULL; + BSTR bstrRemoteAddresses = NULL; + INetFwProfile* pfwProfile = NULL; + INetFwOpenPorts* pfwPorts = NULL; + INetFwOpenPort* pfwPort = NULL; + + // convert to BSTRs to make COM happy + bstrName = ::SysAllocString(wzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name"); + bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses); + ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses"); + + // create and initialize a new open port object + hr = ::CoCreateInstance(__uuidof(NetFwOpenPort), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwOpenPort), reinterpret_cast<void**>(&pfwPort)); + ExitOnFailure(hr, "failed to create new open port"); + + hr = pfwPort->put_Port(iPort); + ExitOnFailure(hr, "failed to set exception port"); + + hr = pfwPort->put_Protocol(static_cast<NET_FW_IP_PROTOCOL>(iProtocol)); + ExitOnFailure(hr, "failed to set exception protocol"); + + if (bstrRemoteAddresses && *bstrRemoteAddresses) + { + hr = pfwPort->put_RemoteAddresses(bstrRemoteAddresses); + ExitOnFailure(hr, "failed to set exception remote addresses '%ls'", bstrRemoteAddresses); + } + + hr = pfwPort->put_Name(bstrName); + ExitOnFailure(hr, "failed to set exception name"); + + // get the firewall profile, its current list of open ports, and add ours + hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile); + ExitOnFailure(hr, "failed to get firewall profile"); + if (S_FALSE == hr) // user or package author chose to ignore missing firewall + { + ExitFunction(); + } + + hr = pfwProfile->get_GloballyOpenPorts(&pfwPorts); + ExitOnFailure(hr, "failed to get open ports"); + + hr = pfwPorts->Add(pfwPort); + ExitOnFailure(hr, "failed to add exception to global list"); + +LExit: + ReleaseBSTR(bstrRemoteAddresses); + ReleaseBSTR(bstrName); + ReleaseObject(pfwProfile); + ReleaseObject(pfwPorts); + ReleaseObject(pfwPort); + + return fIgnoreFailures ? S_OK : hr; +} + +/****************************************************************** + RemoveException - Removes the exception rule with the given name. + +********************************************************************/ +static HRESULT RemoveException( + __in LPCWSTR wzName, + __in BOOL fIgnoreFailures + ) +{ + HRESULT hr = S_OK;; + INetFwRules* pNetFwRules = NULL; + + // convert to BSTRs to make COM happy + BSTR bstrName = ::SysAllocString(wzName); + ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for path"); + + // get the collection of firewall rules + hr = GetFirewallRules(fIgnoreFailures, &pNetFwRules); + ExitOnFailure(hr, "failed to get firewall rules object"); + if (S_FALSE == hr) // user or package author chose to ignore missing firewall + { + ExitFunction(); + } + + hr = pNetFwRules->Remove(bstrName); + ExitOnFailure(hr, "failed to remove authorized app"); + +LExit: + ReleaseBSTR(bstrName); + ReleaseObject(pNetFwRules); + + return fIgnoreFailures ? S_OK : hr; +} + +/****************************************************************** + RemoveApplicationExceptionFromCurrentProfile + +********************************************************************/ +static HRESULT RemoveApplicationExceptionFromCurrentProfile( + __in LPCWSTR wzFile, + __in BOOL fIgnoreFailures + ) +{ + HRESULT hr = S_OK; + INetFwProfile* pfwProfile = NULL; + INetFwAuthorizedApplications* pfwApps = NULL; + + // convert to BSTRs to make COM happy + BSTR bstrFile = ::SysAllocString(wzFile); + ExitOnNull(bstrFile, hr, E_OUTOFMEMORY, "failed SysAllocString for path"); + + // get the firewall profile, which is our entry point for removing exceptions + hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile); + ExitOnFailure(hr, "failed to get firewall profile"); + if (S_FALSE == hr) // user or package author chose to ignore missing firewall + { + ExitFunction(); + } + + // now get the list of app exceptions and remove the one + hr = pfwProfile->get_AuthorizedApplications(&pfwApps); + ExitOnFailure(hr, "failed to get list of authorized apps"); + + hr = pfwApps->Remove(bstrFile); + ExitOnFailure(hr, "failed to remove authorized app"); + +LExit: + ReleaseBSTR(bstrFile); + ReleaseObject(pfwApps); + ReleaseObject(pfwProfile); + + return fIgnoreFailures ? S_OK : hr; +} + +/****************************************************************** + RemovePortExceptionFromCurrentProfile + +********************************************************************/ +static HRESULT RemovePortExceptionFromCurrentProfile( + __in int iPort, + __in int iProtocol, + __in BOOL fIgnoreFailures + ) +{ + HRESULT hr = S_OK; + INetFwProfile* pfwProfile = NULL; + INetFwOpenPorts* pfwPorts = NULL; + + // get the firewall profile, which is our entry point for adding exceptions + hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile); + ExitOnFailure(hr, "failed to get firewall profile"); + if (S_FALSE == hr) // user or package author chose to ignore missing firewall + { + ExitFunction(); + } + + hr = pfwProfile->get_GloballyOpenPorts(&pfwPorts); + ExitOnFailure(hr, "failed to get open ports"); + + hr = pfwPorts->Remove(iPort, static_cast<NET_FW_IP_PROTOCOL>(iProtocol)); + ExitOnFailure(hr, "failed to remove open port %d, protocol %d", iPort, iProtocol); + +LExit: + return fIgnoreFailures ? S_OK : hr; +} + +static HRESULT AddApplicationException( + __in BOOL fSupportProfiles, + __in LPCWSTR wzFile, + __in LPCWSTR wzName, + __in int iProfile, + __in_opt LPCWSTR wzRemoteAddresses, + __in BOOL fIgnoreFailures, + __in LPCWSTR wzPort, + __in int iProtocol, + __in LPCWSTR wzDescription + ) +{ + HRESULT hr = S_OK; + + if (fSupportProfiles) + { + hr = AddApplicationException(wzFile, wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription); + } + else + { + if (0 != *wzPort || MSI_NULL_INTEGER != iProtocol) + { + // NOTE: This is treated as an error rather than either creating a rule based on just the application (no port), or + // just the port because it is unclear what is the proper fall back. For example, suppose that you have code that + // runs in dllhost.exe. Clearly falling back to opening all of dllhost is wrong. Because the firewall is a security + // feature, it seems better to require the MSI author to indicate the behavior that they want. + WcaLog(LOGMSG_STANDARD, "FirewallExtension: Cannot add firewall rule '%ls', which defines both an application and a port or protocol. Such a rule requires Microsoft Windows Vista or later.", wzName); + return fIgnoreFailures ? S_OK : E_NOTIMPL; + } + + hr = AddApplicationExceptionOnCurrentProfile(wzFile, wzName, wzRemoteAddresses, fIgnoreFailures); + } + + return hr; +} + +static HRESULT AddPortException( + __in BOOL fSupportProfiles, + __in LPCWSTR wzName, + __in int iProfile, + __in_opt LPCWSTR wzRemoteAddresses, + __in BOOL fIgnoreFailures, + __in LPCWSTR wzPort, + __in int iProtocol, + __in LPCWSTR wzDescription + ) +{ + HRESULT hr = S_OK; + + if (fSupportProfiles) + { + hr = AddPortException(wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription); + } + else + { + hr = AddPortExceptionOnCurrentProfile(wzName, wzRemoteAddresses, fIgnoreFailures, wcstol(wzPort, NULL, 10), iProtocol); + } + + return hr; +} + +static HRESULT RemoveApplicationException( + __in BOOL fSupportProfiles, + __in LPCWSTR wzName, + __in LPCWSTR wzFile, + __in BOOL fIgnoreFailures, + __in LPCWSTR wzPort, + __in int iProtocol + ) +{ + HRESULT hr = S_OK; + + if (fSupportProfiles) + { + hr = RemoveException(wzName, fIgnoreFailures); + } + else + { + if (0 != *wzPort || MSI_NULL_INTEGER != iProtocol) + { + WcaLog(LOGMSG_STANDARD, "FirewallExtension: Cannot remove firewall rule '%ls', which defines both an application and a port or protocol. Such a rule requires Microsoft Windows Vista or later.", wzName); + return S_OK; + } + + hr = RemoveApplicationExceptionFromCurrentProfile(wzFile, fIgnoreFailures); + } + + return hr; +} + +static HRESULT RemovePortException( + __in BOOL fSupportProfiles, + __in LPCWSTR wzName, + __in LPCWSTR wzPort, + __in int iProtocol, + __in BOOL fIgnoreFailures + ) +{ + HRESULT hr = S_OK; + + if (fSupportProfiles) + { + hr = RemoveException(wzName, fIgnoreFailures); + } + else + { + hr = RemovePortExceptionFromCurrentProfile(wcstol(wzPort, NULL, 10), iProtocol, fIgnoreFailures); + } + + return hr; +} + +/****************************************************************** + ExecFirewallExceptions - deferred custom action entry point to + register and remove firewall exceptions. + +********************************************************************/ +extern "C" UINT __stdcall ExecFirewallExceptions( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + BOOL fSupportProfiles = FALSE; + LPWSTR pwz = NULL; + LPWSTR pwzCustomActionData = NULL; + int iTodo = WCA_TODO_UNKNOWN; + LPWSTR pwzName = NULL; + LPWSTR pwzRemoteAddresses = NULL; + int iAttributes = 0; + int iTarget = fetUnknown; + LPWSTR pwzFile = NULL; + LPWSTR pwzPort = NULL; + LPWSTR pwzDescription = NULL; + int iProtocol = 0; + int iProfile = 0; + + // initialize + hr = WcaInitialize(hInstall, "ExecFirewallExceptions"); + ExitOnFailure(hr, "failed to initialize"); + + hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData); + ExitOnFailure(hr, "failed to get CustomActionData"); + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData); + + hr = ::CoInitialize(NULL); + ExitOnFailure(hr, "failed to initialize COM"); + + // Find out if we support profiles (only on Vista or later) + fSupportProfiles = FSupportProfiles(); + + // loop through all the passed in data + pwz = pwzCustomActionData; + while (pwz && *pwz) + { + // extract the custom action data and if rolling back, swap INSTALL and UNINSTALL + hr = WcaReadIntegerFromCaData(&pwz, &iTodo); + ExitOnFailure(hr, "failed to read todo from custom action data"); + if (::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK)) + { + if (WCA_TODO_INSTALL == iTodo) + { + iTodo = WCA_TODO_UNINSTALL; + } + else if (WCA_TODO_UNINSTALL == iTodo) + { + iTodo = WCA_TODO_INSTALL; + } + } + + hr = WcaReadStringFromCaData(&pwz, &pwzName); + ExitOnFailure(hr, "failed to read name from custom action data"); + + hr = WcaReadIntegerFromCaData(&pwz, &iProfile); + ExitOnFailure(hr, "failed to read profile from custom action data"); + + hr = WcaReadStringFromCaData(&pwz, &pwzRemoteAddresses); + ExitOnFailure(hr, "failed to read remote addresses from custom action data"); + + hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); + ExitOnFailure(hr, "failed to read attributes from custom action data"); + BOOL fIgnoreFailures = feaIgnoreFailures == (iAttributes & feaIgnoreFailures); + + hr = WcaReadIntegerFromCaData(&pwz, &iTarget); + ExitOnFailure(hr, "failed to read target from custom action data"); + + if (iTarget == fetApplication) + { + hr = WcaReadStringFromCaData(&pwz, &pwzFile); + ExitOnFailure(hr, "failed to read file path from custom action data"); + } + + hr = WcaReadStringFromCaData(&pwz, &pwzPort); + ExitOnFailure(hr, "failed to read port from custom action data"); + hr = WcaReadIntegerFromCaData(&pwz, &iProtocol); + ExitOnFailure(hr, "failed to read protocol from custom action data"); + hr = WcaReadStringFromCaData(&pwz, &pwzDescription); + ExitOnFailure(hr, "failed to read protocol from custom action data"); + + switch (iTarget) + { + case fetPort: + switch (iTodo) + { + case WCA_TODO_INSTALL: + case WCA_TODO_REINSTALL: + WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls on port %ls, protocol %d", pwzName, pwzPort, iProtocol); + hr = AddPortException(fSupportProfiles, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription); + ExitOnFailure(hr, "failed to add/update port exception for name '%ls' on port %ls, protocol %d", pwzName, pwzPort, iProtocol); + break; + + case WCA_TODO_UNINSTALL: + WcaLog(LOGMSG_STANDARD, "Uninstalling firewall exception2 %ls on port %ls, protocol %d", pwzName, pwzPort, iProtocol); + hr = RemovePortException(fSupportProfiles, pwzName, pwzPort, iProtocol, fIgnoreFailures); + ExitOnFailure(hr, "failed to remove port exception for name '%ls' on port %ls, protocol %d", pwzName, pwzPort, iProtocol); + break; + } + break; + + case fetApplication: + switch (iTodo) + { + case WCA_TODO_INSTALL: + case WCA_TODO_REINSTALL: + WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls (%ls)", pwzName, pwzFile); + hr = AddApplicationException(fSupportProfiles, pwzFile, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription); + ExitOnFailure(hr, "failed to add/update application exception for name '%ls', file '%ls'", pwzName, pwzFile); + break; + + case WCA_TODO_UNINSTALL: + WcaLog(LOGMSG_STANDARD, "Uninstalling firewall exception2 %ls (%ls)", pwzName, pwzFile); + hr = RemoveApplicationException(fSupportProfiles, pwzName, pwzFile, fIgnoreFailures, pwzPort, iProtocol); + ExitOnFailure(hr, "failed to remove application exception for name '%ls', file '%ls'", pwzName, pwzFile); + break; + } + break; + } + } + +LExit: + ReleaseStr(pwzCustomActionData); + ReleaseStr(pwzName); + ReleaseStr(pwzRemoteAddresses); + ReleaseStr(pwzFile); + ReleaseStr(pwzPort); + ReleaseStr(pwzDescription); + ::CoUninitialize(); + + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); +} diff --git a/src/ca/fwca.def b/src/ca/fwca.def new file mode 100644 index 00000000..d32c5379 --- /dev/null +++ b/src/ca/fwca.def @@ -0,0 +1,9 @@ +; Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +LIBRARY "fwca" + +EXPORTS + SchedFirewallExceptionsInstall + SchedFirewallExceptionsUninstall + ExecFirewallExceptions diff --git a/src/ca/fwca.vcxproj b/src/ca/fwca.vcxproj new file mode 100644 index 00000000..b6075790 --- /dev/null +++ b/src/ca/fwca.vcxproj @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props')" /> + <Import Project="..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props" Condition="Exists('..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props')" /> + + <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> + </ItemGroup> + + <PropertyGroup Label="Globals"> + <ProjectGuid>{F72D34CA-48DA-4DFD-91A9-A0C78BEF6981}</ProjectGuid> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <TargetName>fwca</TargetName> + <PlatformToolset>v141</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + <ProjectModuleDefinitionFile>fwca.def</ProjectModuleDefinitionFile> + <Description>WiX Toolset Firewall CustomAction</Description> + </PropertyGroup> + + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + + <ImportGroup Label="Shared"> + <Import Project="..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets" Condition="Exists('..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" /> + </ImportGroup> + + <PropertyGroup> + <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries> + </PropertyGroup> + + <ItemGroup> + <ClCompile Include="dllmain.cpp"> + <PrecompiledHeader>Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="firewall.cpp" /> + </ItemGroup> + + <ItemGroup> + <ClInclude Include="cost.h" /> + <ClInclude Include="CustomMsiErrors.h" /> + <ClInclude Include="precomp.h" /> + </ItemGroup> + + <ItemGroup> + <None Include="packages.config" /> + <None Include="fwca.def" /> + </ItemGroup> + + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.VisualStudio.Setup.Configuration.Native.1.14.114\build\native\Microsoft.VisualStudio.Setup.Configuration.Native.targets'))" /> + <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.6\build\WixToolset.DUtil.props'))" /> + <Error Condition="!Exists('..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.WcaUtil.4.0.2\build\WixToolset.WcaUtil.props'))" /> + </Target> +</Project> + diff --git a/src/ca/packages.config b/src/ca/packages.config new file mode 100644 index 00000000..b74ff5d0 --- /dev/null +++ b/src/ca/packages.config @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.VisualStudio.Setup.Configuration.Native" version="1.14.114" targetFramework="native" developmentDependency="true" /> + <package id="WixToolset.DUtil" version="4.0.6" targetFramework="native" /> + <package id="WixToolset.WcaUtil" version="4.0.2" targetFramework="native" /> +</packages> \ No newline at end of file diff --git a/src/ca/precomp.h b/src/ca/precomp.h new file mode 100644 index 00000000..a84bacff --- /dev/null +++ b/src/ca/precomp.h @@ -0,0 +1,17 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#include <windows.h> +#include <msidefs.h> +#include <msiquery.h> +#include <strsafe.h> +#include <netfw.h> + +#include "wcautil.h" +#include "fileutil.h" +#include "pathutil.h" +#include "strutil.h" + +#include "CustomMsiErrors.h" +#include "cost.h" diff --git a/src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs b/src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs new file mode 100644 index 00000000..0d7eb6ad --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolsetTest.Firewall +{ + using System.Linq; + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using WixToolset.Firewall; + using Xunit; + + public class FirewallExtensionFixture + { + [Fact] + public void CanBuildUsingFileShare() + { + var folder = TestData.Get(@"TestData\UsingFirewall"); + var build = new Builder(folder, typeof(FirewallExtensionFactory), new[] { folder }); + + var results = build.BuildAndQuery(Build, "WixFirewallException"); + Assert.Equal(new[] + { + "WixFirewallException:ExampleFirewall\texample\t*\t42\t6\t\t0\t2147483647\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example firewall", + }, results.OrderBy(s => s).ToArray()); + } + + private static void Build(string[] args) + { + var result = WixRunner.Execute(args, out var messages); + Assert.Equal(0, result); + Assert.Empty(messages); + } + } +} diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.en-us.wxl b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.en-us.wxl new file mode 100644 index 00000000..38c12ac1 --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.en-us.wxl @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- +This file contains the declaration of all the localizable strings. +--> +<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US"> + + <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String> + <String Id="FeatureTitle">MsiPackage</String> + +</WixLocalization> diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.wxs b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.wxs new file mode 100644 index 00000000..68ff98fd --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/Package.wxs @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <Product Id="*" Name="MsiPackage" Language="1033" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a"> + <Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" /> + + <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" /> + <MediaTemplate /> + + <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)"> + <ComponentGroupRef Id="ProductComponents" /> + </Feature> + + </Product> + + <Fragment> + <Directory Id="TARGETDIR" Name="SourceDir"> + <Directory Id="ProgramFilesFolder"> + <Directory Id="INSTALLFOLDER" Name="MsiPackage" /> + </Directory> + </Directory> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/PackageComponents.wxs b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/PackageComponents.wxs new file mode 100644 index 00000000..910cca2d --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/PackageComponents.wxs @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" + xmlns:fw="http://wixtoolset.org/schemas/v4/wxs/firewall"> + <Fragment> + <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER"> + <Component> + <File Source="example.txt" /> + <fw:FirewallException Id="ExampleFirewall" Description="An example firewall" Name="example" Port="42"> + <fw:RemoteAddress>*</fw:RemoteAddress> + </fw:FirewallException> + </Component> + </ComponentGroup> + </Fragment> +</Wix> diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/example.txt b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/example.txt new file mode 100644 index 00000000..1b4ffe8a --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingFirewall/example.txt @@ -0,0 +1 @@ +This is example.txt. \ No newline at end of file diff --git a/src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj b/src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj new file mode 100644 index 00000000..8925cbc7 --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>netcoreapp2.0</TargetFramework> + <IsPackable>false</IsPackable> + </PropertyGroup> + + <PropertyGroup> + <NoWarn>NU1701</NoWarn> + </PropertyGroup> + + <ItemGroup> + <Content Include="TestData\UsingFirewall\example.txt" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\UsingFirewall\Package.en-us.wxl" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\UsingFirewall\Package.wxs" CopyToOutputDirectory="PreserveNewest" /> + <Content Include="TestData\UsingFirewall\PackageComponents.wxs" CopyToOutputDirectory="PreserveNewest" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\wixext\WixToolset.Firewall.wixext.csproj" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\WixToolset.Core.TestPackage\WixToolset.Core.TestPackage.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> + <ProjectReference Include="$(WixToolsetRootFolder)\Core\src\wix\wix.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Core\README.md') " /> + <PackageReference Include="WixToolset.Core.TestPackage" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Core\README.md') " PrivateAssets="all" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="WixBuildTools.TestSupport" Version="4.0.*" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" /> + <PackageReference Include="xunit" Version="2.2.0" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" /> + </ItemGroup> +</Project> diff --git a/src/wixext/FirewallCompiler.cs b/src/wixext/FirewallCompiler.cs new file mode 100644 index 00000000..0696b4b1 --- /dev/null +++ b/src/wixext/FirewallCompiler.cs @@ -0,0 +1,356 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Extensibility; + + /// <summary> + /// The compiler for the WiX Toolset Firewall Extension. + /// </summary> + public sealed class FirewallCompiler : BaseCompilerExtension + { + public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/firewall"; + + /// <summary> + /// Processes an element for the Compiler. + /// </summary> + /// <param name="sourceLineNumbers">Source line number for the parent element.</param> + /// <param name="parentElement">Parent element of element to process.</param> + /// <param name="element">Element to process.</param> + /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param> + public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context) + { + switch (parentElement.Name.LocalName) + { + case "File": + string fileId = context["FileId"]; + string fileComponentId = context["ComponentId"]; + + switch (element.Name.LocalName) + { + case "FirewallException": + this.ParseFirewallExceptionElement(intermediate, section, element, fileComponentId, fileId); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + case "Component": + string componentId = context["ComponentId"]; + + switch (element.Name.LocalName) + { + case "FirewallException": + this.ParseFirewallExceptionElement(intermediate, section, element, componentId, null); + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + break; + default: + this.ParseHelper.UnexpectedElement(parentElement, element); + break; + } + } + + /// <summary> + /// Parses a FirewallException element. + /// </summary> + /// <param name="element">The element to parse.</param> + /// <param name="componentId">Identifier of the component that owns this firewall exception.</param> + /// <param name="fileId">The file identifier of the parent element (null if nested under Component).</param> + private void ParseFirewallExceptionElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId) + { + SourceLineNumber sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + string name = null; + int attributes = 0; + string file = null; + string program = null; + string port = null; + string protocolValue = null; + int? protocol = null; + string profileValue = null; + int? profile = null; + string scope = null; + string remoteAddresses = null; + string description = null; + + foreach (XAttribute attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "File": + if (null != fileId) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, "File", "File")); + } + else + { + file = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + } + break; + case "IgnoreFailure": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 0x1; // feaIgnoreFailures + } + break; + case "Program": + if (null != fileId) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, "Program", "File")); + } + else + { + program = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + } + break; + case "Port": + port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Protocol": + protocolValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + switch (protocolValue) + { + case "tcp": + protocol = FirewallConstants.NET_FW_IP_PROTOCOL_TCP; + break; + case "udp": + protocol = FirewallConstants.NET_FW_IP_PROTOCOL_UDP; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Protocol", protocolValue, "tcp", "udp")); + break; + } + break; + case "Scope": + scope = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + switch (scope) + { + case "any": + remoteAddresses = "*"; + break; + case "localSubnet": + remoteAddresses = "LocalSubnet"; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Scope", scope, "any", "localSubnet")); + break; + } + break; + case "Profile": + profileValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + switch (profileValue) + { + case "domain": + profile = FirewallConstants.NET_FW_PROFILE2_DOMAIN; + break; + case "private": + profile = FirewallConstants.NET_FW_PROFILE2_PRIVATE; + break; + case "public": + profile = FirewallConstants.NET_FW_PROFILE2_PUBLIC; + break; + case "all": + profile = FirewallConstants.NET_FW_PROFILE2_ALL; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Profile", profileValue, "domain", "private", "public", "all")); + break; + } + break; + case "Description": + description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + // parse RemoteAddress children + foreach (XElement child in element.Elements()) + { + if (this.Namespace == child.Name.Namespace) + { + SourceLineNumber childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "RemoteAddress": + if (null != scope) + { + this.Messaging.Write(FirewallErrors.IllegalRemoteAddressWithScopeAttribute(sourceLineNumbers)); + } + else + { + this.ParseRemoteAddressElement(intermediate, section, child, ref remoteAddresses); + } + break; + default: + this.ParseHelper.UnexpectedElement(element, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); + } + } + + // Id and Name are required + if (null == id) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + // Scope or child RemoteAddress(es) are required + if (null == remoteAddresses) + { + this.Messaging.Write(ErrorMessages.ExpectedAttributeOrElement(sourceLineNumbers, element.Name.LocalName, "Scope", "RemoteAddress")); + } + + // can't have both Program and File + if (null != program && null != file) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "File", "Program")); + } + + // must be nested under File, have File or Program attributes, or have Port attribute + if (String.IsNullOrEmpty(fileId) && String.IsNullOrEmpty(file) && String.IsNullOrEmpty(program) && String.IsNullOrEmpty(port)) + { + this.Messaging.Write(FirewallErrors.NoExceptionSpecified(sourceLineNumbers)); + } + + if (!this.Messaging.EncounteredError) + { + // at this point, File attribute and File parent element are treated the same + if (null != file) + { + fileId = file; + } + + var row = this.ParseHelper.CreateRow(section, sourceLineNumbers, "WixFirewallException", id); + row.Set(1, name); + row.Set(2, remoteAddresses); + + if (!String.IsNullOrEmpty(port)) + { + row.Set(3, port); + + if (!protocol.HasValue) + { + // default protocol is "TCP" + protocol = FirewallConstants.NET_FW_IP_PROTOCOL_TCP; + } + } + + if (protocol.HasValue) + { + row.Set(4, protocol); + } + + if (!String.IsNullOrEmpty(fileId)) + { + row.Set(5, $"[#{fileId}]"); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "File", fileId); + } + else if (!String.IsNullOrEmpty(program)) + { + row.Set(5, program); + } + + if (CompilerConstants.IntegerNotSet != attributes) + { + row.Set(6, attributes); + } + + // Default is "all" + row.Set(7, profile ?? FirewallConstants.NET_FW_PROFILE2_ALL); + + row.Set(8, componentId); + + row.Set(9, description); + + if (this.Context.Platform == Platform.ARM) + { + // Ensure ARM version of the CA is referenced + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsInstall_ARM"); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsUninstall_ARM"); + } + else + { + // All other supported platforms use x86 + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsInstall"); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsUninstall"); + } + } + } + + /// <summary> + /// Parses a RemoteAddress element + /// </summary> + /// <param name="element">The element to parse.</param> + private void ParseRemoteAddressElement(Intermediate intermediate, IntermediateSection section, XElement element, ref string remoteAddresses) + { + SourceLineNumber sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + + // no attributes + foreach (XAttribute attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + this.ParseHelper.UnexpectedAttribute(element, attrib); + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + string address = this.ParseHelper.GetTrimmedInnerText(element); + if (String.IsNullOrEmpty(address)) + { + this.Messaging.Write(FirewallErrors.IllegalEmptyRemoteAddress(sourceLineNumbers)); + } + else + { + if (String.IsNullOrEmpty(remoteAddresses)) + { + remoteAddresses = address; + } + else + { + remoteAddresses = String.Concat(remoteAddresses, ",", address); + } + } + } + } +} diff --git a/src/wixext/FirewallConstants.cs b/src/wixext/FirewallConstants.cs new file mode 100644 index 00000000..16caa5b4 --- /dev/null +++ b/src/wixext/FirewallConstants.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall +{ + using System; + using System.Collections.Generic; + using System.Text; + + static class FirewallConstants + { + // from icftypes.h + public const int NET_FW_IP_PROTOCOL_TCP = 6; + public const int NET_FW_IP_PROTOCOL_UDP = 17; + + // from icftypes.h + public const int NET_FW_PROFILE2_DOMAIN = 0x0001; + public const int NET_FW_PROFILE2_PRIVATE = 0x0002; + public const int NET_FW_PROFILE2_PUBLIC = 0x0004; + public const int NET_FW_PROFILE2_ALL = 0x7FFFFFFF; + } +} diff --git a/src/wixext/FirewallDecompiler.cs b/src/wixext/FirewallDecompiler.cs new file mode 100644 index 00000000..b060f8e2 --- /dev/null +++ b/src/wixext/FirewallDecompiler.cs @@ -0,0 +1,169 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall +{ +#if TODO_CONSIDER_DECOMPILER + using System; + using System.Collections; + using System.Diagnostics; + using System.Globalization; + using WixToolset.Data; + using WixToolset.Extensibility; + using Firewall = WixToolset.Extensions.Serialize.Firewall; + using Wix = WixToolset.Data.Serialize; + + /// <summary> + /// The decompiler for the WiX Toolset Firewall Extension. + /// </summary> + public sealed class FirewallDecompiler : DecompilerExtension + { + /// <summary> + /// Creates a decompiler for Firewall Extension. + /// </summary> + public FirewallDecompiler() + { + this.TableDefinitions = FirewallExtensionData.GetExtensionTableDefinitions(); + } + + /// <summary> + /// Get the extensions library to be removed. + /// </summary> + /// <param name="tableDefinitions">Table definitions for library.</param> + /// <returns>Library to remove from decompiled output.</returns> + public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) + { + return FirewallExtensionData.GetExtensionLibrary(tableDefinitions); + } + + /// <summary> + /// Decompiles an extension table. + /// </summary> + /// <param name="table">The table to decompile.</param> + public override void DecompileTable(Table table) + { + switch (table.Name) + { + case "WixFirewallException": + this.DecompileWixFirewallExceptionTable(table); + break; + default: + base.DecompileTable(table); + break; + } + } + + /// <summary> + /// Decompile the WixFirewallException table. + /// </summary> + /// <param name="table">The table to decompile.</param> + private void DecompileWixFirewallExceptionTable(Table table) + { + foreach (Row row in table.Rows) + { + Firewall.FirewallException fire = new Firewall.FirewallException(); + fire.Id = (string)row[0]; + fire.Name = (string)row[1]; + + string[] addresses = ((string)row[2]).Split(','); + if (1 == addresses.Length) + { + // special-case the Scope attribute values + if ("*" == addresses[0]) + { + fire.Scope = Firewall.FirewallException.ScopeType.any; + } + else if ("LocalSubnet" == addresses[0]) + { + fire.Scope = Firewall.FirewallException.ScopeType.localSubnet; + } + else + { + FirewallDecompiler.AddRemoteAddress(fire, addresses[0]); + } + } + else + { + foreach (string address in addresses) + { + FirewallDecompiler.AddRemoteAddress(fire, address); + } + } + + if (!row.IsColumnEmpty(3)) + { + fire.Port = (string)row[3]; + } + + if (!row.IsColumnEmpty(4)) + { + switch (Convert.ToInt32(row[4])) + { + case FirewallConstants.NET_FW_IP_PROTOCOL_TCP: + fire.Protocol = Firewall.FirewallException.ProtocolType.tcp; + break; + case FirewallConstants.NET_FW_IP_PROTOCOL_UDP: + fire.Protocol = Firewall.FirewallException.ProtocolType.udp; + break; + } + } + + if (!row.IsColumnEmpty(5)) + { + fire.Program = (string)row[5]; + } + + if (!row.IsColumnEmpty(6)) + { + int attr = Convert.ToInt32(row[6]); + if (0x1 == (attr & 0x1)) // feaIgnoreFailures + { + fire.IgnoreFailure = Firewall.YesNoType.yes; + } + } + + if (!row.IsColumnEmpty(7)) + { + switch (Convert.ToInt32(row[7])) + { + case FirewallConstants.NET_FW_PROFILE2_DOMAIN: + fire.Profile = Firewall.FirewallException.ProfileType.domain; + break; + case FirewallConstants.NET_FW_PROFILE2_PRIVATE: + fire.Profile = Firewall.FirewallException.ProfileType.@private; + break; + case FirewallConstants.NET_FW_PROFILE2_PUBLIC: + fire.Profile = Firewall.FirewallException.ProfileType.@public; + break; + case FirewallConstants.NET_FW_PROFILE2_ALL: + fire.Profile = Firewall.FirewallException.ProfileType.all; + break; + } + } + + // Description column is new in v3.6 + if (9 < row.Fields.Length && !row.IsColumnEmpty(9)) + { + fire.Description = (string)row[9]; + } + + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[8]); + if (null != component) + { + component.AddChild(fire); + } + else + { + this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[6], "Component")); + } + } + } + + private static void AddRemoteAddress(Firewall.FirewallException fire, string address) + { + Firewall.RemoteAddress remote = new Firewall.RemoteAddress(); + remote.Content = address; + fire.AddChild(remote); + } + } +#endif +} diff --git a/src/wixext/FirewallErrors.cs b/src/wixext/FirewallErrors.cs new file mode 100644 index 00000000..3fff8c8d --- /dev/null +++ b/src/wixext/FirewallErrors.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall +{ + using System.Resources; + using WixToolset.Data; + + public static class FirewallErrors + { + public static Message IllegalRemoteAddressWithScopeAttribute(SourceLineNumber sourceLineNumbers) + { + return Message(sourceLineNumbers, Ids.IllegalRemoteAddressWithScopeAttribute, "The RemoteAddress element cannot be specified because its parent FirewallException already specified the Scope attribute. To use RemoteAddress elements, omit the Scope attribute."); + } + + public static Message IllegalEmptyRemoteAddress(SourceLineNumber sourceLineNumbers) + { + return Message(sourceLineNumbers, Ids.IllegalEmptyRemoteAddress, "The RemoteAddress element's inner text cannot be an empty string or completely whitespace."); + } + + public static Message NoExceptionSpecified(SourceLineNumber sourceLineNumbers) + { + return Message(sourceLineNumbers, Ids.NoExceptionSpecified, "The FirewallException element doesn't identify the target of the firewall exception. To create an application exception, nest the FirewallException element under a File element or provide a value for the File or Program attributes. To create a port exception, provide a value for the Port attribute."); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, format, args); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, ResourceManager resourceManager, string resourceName, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Error, (int)id, resourceManager, resourceName, args); + } + + public enum Ids + { + IllegalRemoteAddressWithScopeAttribute = 6401, + IllegalEmptyRemoteAddress = 6402, + NoExceptionSpecified = 6403, + } + } +} diff --git a/src/wixext/FirewallExtensionData.cs b/src/wixext/FirewallExtensionData.cs new file mode 100644 index 00000000..78939c4e --- /dev/null +++ b/src/wixext/FirewallExtensionData.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall +{ + using WixToolset.Data; + using WixToolset.Extensibility; + using WixToolset.Firewall.Tuples; + + public sealed class FirewallExtensionData : BaseExtensionData + { + public override string DefaultCulture => "en-US"; + + public override bool TryGetTupleDefinitionByName(string name, out IntermediateTupleDefinition tupleDefinition) + { + tupleDefinition = (name == FirewallTupleDefinitionNames.WixFirewallException) ? FirewallTupleDefinitions.WixFirewallException : null; + return tupleDefinition != null; + } + + public override Intermediate GetLibrary(ITupleDefinitionCreator tupleDefinitions) + { + return Intermediate.Load(typeof(FirewallExtensionData).Assembly, "WixToolset.Firewall.firewall.wixlib", tupleDefinitions); + } + } +} diff --git a/src/wixext/FirewallExtensionFactory.cs b/src/wixext/FirewallExtensionFactory.cs new file mode 100644 index 00000000..4594419d --- /dev/null +++ b/src/wixext/FirewallExtensionFactory.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall +{ + using System; + using System.Collections.Generic; + using WixToolset.Extensibility; + + public class FirewallExtensionFactory : BaseExtensionFactory + { + protected override IEnumerable<Type> ExtensionTypes => new[] + { + typeof(FirewallCompiler), + typeof(FirewallExtensionData), + typeof(FirewallWindowsInstallerBackendExtension), + }; + } +} diff --git a/src/wixext/FirewallWindowsInstallerBackendExtension.cs b/src/wixext/FirewallWindowsInstallerBackendExtension.cs new file mode 100644 index 00000000..a1c78f04 --- /dev/null +++ b/src/wixext/FirewallWindowsInstallerBackendExtension.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall +{ + using System.Linq; + using System.Xml; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility; + + public class FirewallWindowsInstallerBackendExtension : BaseWindowsInstallerBackendExtension + { + private static readonly TableDefinition[] Tables = LoadTables(); + + protected override TableDefinition[] TableDefinitionsForTuples => Tables; + + private static TableDefinition[] LoadTables() + { + using (var resourceStream = typeof(FirewallWindowsInstallerBackendExtension).Assembly.GetManifestResourceStream("WixToolset.Firewall.tables.xml")) + using (var reader = XmlReader.Create(resourceStream)) + { + var tables = TableDefinitionCollection.Load(reader); + return tables.ToArray(); + } + } + } +} diff --git a/src/wixext/Tuples/FirewallTupleDefinitions.cs b/src/wixext/Tuples/FirewallTupleDefinitions.cs new file mode 100644 index 00000000..79fc28cf --- /dev/null +++ b/src/wixext/Tuples/FirewallTupleDefinitions.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall.Tuples +{ + using WixToolset.Data; + + public static class FirewallTupleDefinitionNames + { + public static string WixFirewallException { get; } = "WixFirewallException"; + } + + public static partial class FirewallTupleDefinitions + { + public static readonly IntermediateTupleDefinition WixFirewallException = new IntermediateTupleDefinition( + FirewallTupleDefinitionNames.WixFirewallException, + new[] + { + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.WixFirewallException), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Name), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.RemoteAddresses), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Port), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Protocol), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Program), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Profile), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Component_), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Description), IntermediateFieldType.String), + }, + typeof(WixFirewallExceptionTuple)); + } +} diff --git a/src/wixext/Tuples/WixFirewallExceptionTuple.cs b/src/wixext/Tuples/WixFirewallExceptionTuple.cs new file mode 100644 index 00000000..715a4b9b --- /dev/null +++ b/src/wixext/Tuples/WixFirewallExceptionTuple.cs @@ -0,0 +1,93 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Firewall.Tuples +{ + using WixToolset.Data; + + public enum WixFirewallExceptionTupleFields + { + WixFirewallException, + Name, + RemoteAddresses, + Port, + Protocol, + Program, + Attributes, + Profile, + Component_, + Description, + } + + public class WixFirewallExceptionTuple : IntermediateTuple + { + public WixFirewallExceptionTuple() : base(FirewallTupleDefinitions.WixFirewallException, null, null) + { + } + + public WixFirewallExceptionTuple(SourceLineNumber sourceLineNumber, Identifier id = null) : base(FirewallTupleDefinitions.WixFirewallException, sourceLineNumber, id) + { + } + + public IntermediateField this[WixFirewallExceptionTupleFields index] => this.Fields[(int)index]; + + public string WixFirewallException + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.WixFirewallException].AsString(); + set => this.Set((int)WixFirewallExceptionTupleFields.WixFirewallException, value); + } + + public string Name + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Name].AsString(); + set => this.Set((int)WixFirewallExceptionTupleFields.Name, value); + } + + public string RemoteAddresses + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.RemoteAddresses].AsString(); + set => this.Set((int)WixFirewallExceptionTupleFields.RemoteAddresses, value); + } + + public string Port + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Port].AsString(); + set => this.Set((int)WixFirewallExceptionTupleFields.Port, value); + } + + public int Protocol + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Protocol].AsNumber(); + set => this.Set((int)WixFirewallExceptionTupleFields.Protocol, value); + } + + public string Program + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Program].AsString(); + set => this.Set((int)WixFirewallExceptionTupleFields.Program, value); + } + + public int Attributes + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Attributes].AsNumber(); + set => this.Set((int)WixFirewallExceptionTupleFields.Attributes, value); + } + + public int Profile + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Profile].AsNumber(); + set => this.Set((int)WixFirewallExceptionTupleFields.Profile, value); + } + + public string Component_ + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Component_].AsString(); + set => this.Set((int)WixFirewallExceptionTupleFields.Component_, value); + } + + public string Description + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Description].AsString(); + set => this.Set((int)WixFirewallExceptionTupleFields.Description, value); + } + } +} \ No newline at end of file diff --git a/src/wixext/WixToolset.Firewall.wixext.csproj b/src/wixext/WixToolset.Firewall.wixext.csproj new file mode 100644 index 00000000..2d89911c --- /dev/null +++ b/src/wixext/WixToolset.Firewall.wixext.csproj @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <RootNamespace>WixToolset.Firewall</RootNamespace> + <Description>WiX Toolset Firewallity Extension</Description> + <Title>WiX Toolset Firewall Extension</Title> + <IsTool>true</IsTool> + <ContentTargetFolders>build</ContentTargetFolders> + </PropertyGroup> + + <ItemGroup> + <Content Include="$(MSBuildThisFileName).targets" /> + <Content Include="firewall.xsd" PackagePath="tools" /> + <EmbeddedResource Include="tables.xml" /> + <EmbeddedResource Include="$(OutputPath)..\firewall.wixlib" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="$(WixToolsetRootFolder)\Data\src\WixToolset.Data\WixToolset.Data.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Data\README.md') " /> + <PackageReference Include="WixToolset.Data" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Data\README.md') " PrivateAssets="all" /> + + <ProjectReference Include="$(WixToolsetRootFolder)\Extensibility\src\WixToolset.Extensibility\WixToolset.Extensibility.csproj" Condition=" '$(Configuration)' == 'Debug' And Exists('$(WixToolsetRootFolder)\Extensibility\README.md') " /> + <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" Condition=" '$(Configuration)' == 'Release' Or !Exists('$(WixToolsetRootFolder)\Extensibility\README.md') " PrivateAssets="all" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\wixlib\firewall.wixproj" ReferenceOutputAssembly="false" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Nerdbank.GitVersioning" Version="2.1.7" PrivateAssets="all" /> + </ItemGroup> +</Project> diff --git a/src/wixext/WixToolset.Firewall.wixext.targets b/src/wixext/WixToolset.Firewall.wixext.targets new file mode 100644 index 00000000..c717450f --- /dev/null +++ b/src/wixext/WixToolset.Firewall.wixext.targets @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> + <PropertyGroup> + <WixToolsetFirewallWixextPath Condition=" '$(WixToolsetFirewallWixextPath)' == '' ">$(MSBuildThisFileDirectory)..\tools\WixToolset.Firewall.wixext.dll</WixToolsetFirewallWixextPath> + </PropertyGroup> + <ItemGroup> + <WixExtension Include="$(WixToolsetFirewallWixextPath)" /> + </ItemGroup> +</Project> diff --git a/src/wixext/firewall.xsd b/src/wixext/firewall.xsd new file mode 100644 index 00000000..d64aafef --- /dev/null +++ b/src/wixext/firewall.xsd @@ -0,0 +1,211 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + + +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:xse=" http://wixtoolset.org/schemas/XmlSchemaExtension" + xmlns:html="http://www.w3.org/1999/xhtml" + targetNamespace="http://wixtoolset.org/schemas/v4/wxs/firewall" + xmlns="http://wixtoolset.org/schemas/v4/wxs/firewall"> + <xs:annotation> + <xs:documentation> + The source code schema for the WiX Toolset Firewall Extension. + </xs:documentation> + </xs:annotation> + + <xs:import namespace="http://wixtoolset.org/schemas/v4/wxs" /> + + <xs:element name="FirewallException"> + <xs:annotation> + <xs:documentation> + Registers an exception for a program or a specific port and protocol in the Windows Firewall + on Windows XP SP2, Windows Server 2003 SP1, and later. For more information about the Windows + Firewall, see <html:a href="http://msdn.microsoft.com/en-us/library/aa364679.aspx"> + About Windows Firewall API</html:a>. + </xs:documentation> + <xs:appinfo> + <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="Component" /> + <xse:parent namespace="http://wixtoolset.org/schemas/v4/wxs" ref="File" /> + </xs:appinfo> + </xs:annotation> + + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:annotation> + <xs:documentation> + Explicitly-listed remote addresses that this exception allows through the + firewall. + </xs:documentation> + </xs:annotation> + <xs:element ref="RemoteAddress" /> + </xs:choice> + + <xs:attribute name="Id" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + Unique ID of this firewall exception. + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attribute name="Name" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + Name of this firewall exception, visible to the user in the firewall + control panel. + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attribute name="Scope"> + <xs:annotation> + <xs:documentation> + The scope of this firewall exception, which indicates whether incoming + connections can come from any computer including those on the Internet + or only those on the local network subnet. To more precisely specify + allowed remote address, specify a custom scope using RemoteAddress + child elements. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:NMTOKEN"> + <xs:enumeration value="any" /> + <xs:enumeration value="localSubnet" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + + <xs:attribute name="Port" type="xs:string"> + <xs:annotation> + <xs:documentation> + Port to allow through the firewall for this exception. + + If you use Port and also File or Program in the same + FirewallException element, the exception will fail to install on + Windows XP and Windows Server 2003. IgnoreFailure="yes" can be used to + ignore the resulting failure, but the exception will not be added. + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attribute name="Protocol"> + <xs:annotation> + <xs:documentation> + IP protocol used for this firewall exception. If Port is defined, + "tcp" is assumed if the protocol is not specified. + + If you use Protocol and also File or Program in the same + FirewallException element, the exception will fail to install on + Windows XP and Windows Server 2003. IgnoreFailure="yes" can be used to + ignore the resulting failure, but the exception will not be added. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:NMTOKEN"> + <xs:enumeration value="tcp" /> + <xs:enumeration value="udp" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + + <xs:attribute name="File" type="xs:string"> + <xs:annotation> + <xs:documentation> + Identifier of a file to be granted access to all incoming ports and + protocols. If you use File, you cannot also use Program. + + If you use File and also Port or Protocol in the same + FirewallException element, the exception will fail to install on + Windows XP and Windows Server 2003. IgnoreFailure="yes" can be used to + ignore the resulting failure, but the exception will not be added. + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attribute name="Program" type="xs:string"> + <xs:annotation> + <xs:documentation> + Path to a target program to be granted access to all incoming ports and + protocols. Note that this is a formatted field, so you can use [#fileId] + syntax to refer to a file being installed. If you use Program, you cannot + also use File. + + If you use Program and also Port or Protocol in the same + FirewallException element, the exception will fail to install on + Windows XP and Windows Server 2003. IgnoreFailure="yes" can be used to + ignore the resulting failure, but the exception will not be added. + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attribute name="IgnoreFailure" type="YesNoType"> + <xs:annotation> + <xs:documentation> + If "yes," failures to register this firewall exception will be silently + ignored. If "no" (the default), failures will cause rollback. + </xs:documentation> + </xs:annotation> + </xs:attribute> + + <xs:attribute name="Profile"> + <xs:annotation> + <xs:documentation> + Profile type for this firewall exception. Default is "all". + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:NMTOKEN"> + <xs:enumeration value="domain" /> + <xs:enumeration value="private" /> + <xs:enumeration value="public" /> + <xs:enumeration value="all" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute name="Description" type="xs:string"> + <xs:annotation> + <xs:documentation> + Description for this firewall rule displayed in Windows Firewall manager in + Windows Vista and later. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + + <xs:element name="RemoteAddress"> + <xs:annotation> + <xs:documentation> + A remote address to which the port or program can listen. Address formats vary + based on the version of Windows and Windows Firewall the program is being installed + on. For Windows XP SP2 and Windows Server 2003 SP1, see + <html:a href="http://msdn.microsoft.com/en-us/library/aa365270.aspx"> + RemoteAddresses Property</html:a>. + For Windows Vista and Windows Server 2008, see + <html:a href="http://msdn.microsoft.com/en-us/library/aa365366.aspx"> + RemoteAddresses Property</html:a>. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:annotation> + <xs:documentation> + A remote address. + </xs:documentation> + </xs:annotation> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + </xs:element> + + <xs:simpleType name="YesNoType"> + <xs:annotation> + <xs:documentation>Values of this type will either be "yes" or "no".</xs:documentation> + </xs:annotation> + <xs:restriction base='xs:NMTOKEN'> + <xs:enumeration value="no"/> + <xs:enumeration value="yes"/> + </xs:restriction> + </xs:simpleType> +</xs:schema> diff --git a/src/wixext/tables.xml b/src/wixext/tables.xml new file mode 100644 index 00000000..5b408b96 --- /dev/null +++ b/src/wixext/tables.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + + +<tableDefinitions xmlns="http://wixtoolset.org/schemas/v4/wi/tables"> + <tableDefinition name="WixFirewallException"> + <columnDefinition name="WixFirewallException" type="string" length="72" primaryKey="yes" modularize="column" + category="identifier" description="The primary key, a non-localized token." /> + <columnDefinition name="Name" type="localized" length="255" nullable="yes" modularize="property" + category="formatted" description="Localizable display name." /> + <columnDefinition name="RemoteAddresses" type="string" length="0" modularize="property" + category="formatted" description="Remote address to accept incoming connections from." /> + <columnDefinition name="Port" type="string" length="0" modularize="property" nullable="yes" + category="formatted" minValue="1" description="Port number." /> + <columnDefinition name="Protocol" type="number" length="1" nullable="yes" + category="integer" minValue="6" maxValue="17" description="Protocol (6=TCP; 17=UDP)." /> + <columnDefinition name="Program" type="string" length="255" nullable="yes" modularize="property" + category="formatted" description="Exception for a program (formatted path name)." /> + <columnDefinition name="Attributes" type="number" length="4" nullable="yes" + minValue="0" maxValue="65536" description="Vital=1" /> + <columnDefinition name="Profile" type="number" length="4" nullable="no" + category="integer" minValue="1" maxValue="2147483647" description="Profile (1=domain; 2=private; 4=public; 2147483647=all)." /> + <columnDefinition name="Component_" type="string" length="72" modularize="column" + keyTable="Component" keyColumn="1" category="identifier" description="Foreign key into the Component table referencing component that controls the firewall configuration."/> + <columnDefinition name="Description" type="string" length="255" nullable="yes" + category="formatted" description="Description displayed in Windows Firewall manager for this firewall rule."/> + </tableDefinition> +</tableDefinitions> diff --git a/src/wixlib/FirewallExtension.wxs b/src/wixlib/FirewallExtension.wxs new file mode 100644 index 00000000..271469e4 --- /dev/null +++ b/src/wixlib/FirewallExtension.wxs @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + + +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <?include caerr.wxi ?> + <Fragment> + <UI Id="WixFirewallErrors"> + <Error Id="$(var.msierrFirewallCannotConnect)">!(loc.msierrFirewallCannotConnect)</Error> + </UI> + </Fragment> +</Wix> diff --git a/src/wixlib/FirewallExtension_Platform.wxi b/src/wixlib/FirewallExtension_Platform.wxi new file mode 100644 index 00000000..263651d2 --- /dev/null +++ b/src/wixlib/FirewallExtension_Platform.wxi @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Include xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <?include caSuffix.wxi ?> + <Fragment> + <UIRef Id="WixFirewallErrors" /> + <UI> + <ProgressText Action="WixSchedFirewallExceptionsInstall$(var.Suffix)">!(loc.WixSchedFirewallExceptionsInstall)</ProgressText> + <ProgressText Action="WixSchedFirewallExceptionsUninstall$(var.Suffix)">!(loc.WixSchedFirewallExceptionsUninstall)</ProgressText> + <ProgressText Action="WixRollbackFirewallExceptionsInstall$(var.DeferredSuffix)">!(loc.WixRollbackFirewallExceptionsInstall)</ProgressText> + <ProgressText Action="WixExecFirewallExceptionsInstall$(var.DeferredSuffix)">!(loc.WixExecFirewallExceptionsInstall)</ProgressText> + <ProgressText Action="WixRollbackFirewallExceptionsUninstall$(var.DeferredSuffix)">!(loc.WixRollbackFirewallExceptionsUninstall)</ProgressText> + <ProgressText Action="WixExecFirewallExceptionsUninstall$(var.DeferredSuffix)">!(loc.WixExecFirewallExceptionsUninstall)</ProgressText> + </UI> + + <CustomAction Id="WixSchedFirewallExceptionsInstall$(var.Suffix)" BinaryKey="WixFirewallCA$(var.Suffix)" DllEntry="SchedFirewallExceptionsInstall" Execute="immediate" Return="check" SuppressModularization="yes" /> + <CustomAction Id="WixSchedFirewallExceptionsUninstall$(var.Suffix)" BinaryKey="WixFirewallCA$(var.Suffix)" DllEntry="SchedFirewallExceptionsUninstall" Execute="immediate" Return="check" SuppressModularization="yes" /> + <CustomAction Id="WixRollbackFirewallExceptionsInstall$(var.DeferredSuffix)" BinaryKey="WixFirewallCA$(var.Suffix)" DllEntry="ExecFirewallExceptions" Execute="rollback" Impersonate="no" Return="check" SuppressModularization="yes" /> + <CustomAction Id="WixExecFirewallExceptionsInstall$(var.DeferredSuffix)" BinaryKey="WixFirewallCA$(var.Suffix)" DllEntry="ExecFirewallExceptions" Execute="deferred" Impersonate="no" Return="check" SuppressModularization="yes" /> + <CustomAction Id="WixRollbackFirewallExceptionsUninstall$(var.DeferredSuffix)" BinaryKey="WixFirewallCA$(var.Suffix)" DllEntry="ExecFirewallExceptions" Execute="rollback" Impersonate="no" Return="check" SuppressModularization="yes" /> + <CustomAction Id="WixExecFirewallExceptionsUninstall$(var.DeferredSuffix)" BinaryKey="WixFirewallCA$(var.Suffix)" DllEntry="ExecFirewallExceptions" Execute="deferred" Impersonate="no" Return="check" SuppressModularization="yes" /> + + <!-- + We need the firewall on Windows XP SP2 or later. + --> + <InstallExecuteSequence> + <Custom Action="WixSchedFirewallExceptionsUninstall$(var.Suffix)" Before="RemoveFiles" Overridable="yes"> + <![CDATA[ VersionNT >= 600 OR (VersionNT >= 501 AND ((MsiNTProductType = 1 AND ServicePackLevel >= 2) OR (MsiNTProductType > 1 AND ServicePackLevel >= 1))) ]]> + </Custom> + <Custom Action="WixSchedFirewallExceptionsInstall$(var.Suffix)" After="InstallFiles" Overridable="yes"> + <![CDATA[ VersionNT >= 600 OR (VersionNT >= 501 AND ((MsiNTProductType = 1 AND ServicePackLevel >= 2) OR (MsiNTProductType > 1 AND ServicePackLevel >= 1))) ]]> + </Custom> + </InstallExecuteSequence> + </Fragment> + + <!-- Firewall Custom Action DLL Definitions --> + <Fragment> + <Binary Id="WixFirewallCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))fwca.dll" /> + </Fragment> +</Include> diff --git a/src/wixlib/FirewallExtension_x86.wxs b/src/wixlib/FirewallExtension_x86.wxs new file mode 100644 index 00000000..b43a4c5f --- /dev/null +++ b/src/wixlib/FirewallExtension_x86.wxs @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + + +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <?define platform=x86 ?> + <?include FirewallExtension_Platform.wxi ?> +</Wix> diff --git a/src/wixlib/caSuffix.wxi b/src/wixlib/caSuffix.wxi new file mode 100644 index 00000000..a56a2393 --- /dev/null +++ b/src/wixlib/caSuffix.wxi @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<Include xmlns="http://wixtoolset.org/schemas/v4/wxs"> + <?ifndef platform ?> + <?error Required value "platform" not defined in include caSuffix.wxi ?> + <?endif ?> + + <?ifdef Suffix ?> + <?undef Suffix ?> + <?undef DeferredSuffix ?> + <?endif ?> + + <?if $(var.platform)="x86" ?> + <?define Suffix="" ?> + <?define DeferredSuffix="" ?> + <?endif ?> + + <?if $(var.platform)="x64" ?> + <?define Suffix="_x64" ?> + <?define DeferredSuffix="_64" ?> + <?endif ?> + + <?if $(var.platform)="arm" ?> + <?define Suffix="_ARM" ?> + <?define DeferredSuffix="_ARM" ?> + <?endif ?> +</Include> diff --git a/src/wixlib/caerr.wxi b/src/wixlib/caerr.wxi new file mode 100644 index 00000000..141942f2 --- /dev/null +++ b/src/wixlib/caerr.wxi @@ -0,0 +1,96 @@ +<Include> + <?define msierrSecureObjectsFailedCreateSD = 25520?> + <?define msierrSecureObjectsFailedSet = 25521?> + <?define msierrSecureObjectsUnknownType = 25522?> + <?define msierrXmlFileFailedRead = 25530?> + <?define msierrXmlFileFailedOpen = 25531?> + <?define msierrXmlFileFailedSelect = 25532?> + <?define msierrXmlFileFailedSave = 25533?> + <?define msierrXmlConfigFailedRead = 25540?> + <?define msierrXmlConfigFailedOpen = 25541?> + <?define msierrXmlConfigFailedSelect = 25542?> + <?define msierrXmlConfigFailedSave = 25543?> + <?define msierrFirewallCannotConnect = 25580?> + <?define msierrIISCannotConnect = 26001?> + <?define msierrIISFailedReadWebSite = 26002?> + <?define msierrIISFailedReadWebDirs = 26003?> + <?define msierrIISFailedReadVDirs = 26004?> + <?define msierrIISFailedReadFilters = 26005?> + <?define msierrIISFailedReadAppPool = 26006?> + <?define msierrIISFailedReadMimeMap = 26007?> + <?define msierrIISFailedReadProp = 26008?> + <?define msierrIISFailedReadWebSvcExt = 26009?> + <?define msierrIISFailedReadWebError = 26010?> + <?define msierrIISFailedReadHttpHeader = 26011?> + <?define msierrIISFailedSchedTransaction = 26031?> + <?define msierrIISFailedSchedInstallWebs = 26032?> + <?define msierrIISFailedSchedInstallWebDirs = 26033?> + <?define msierrIISFailedSchedInstallVDirs = 26034?> + <?define msierrIISFailedSchedInstallFilters = 26035?> + <?define msierrIISFailedSchedInstallAppPool = 26036?> + <?define msierrIISFailedSchedInstallProp = 26037?> + <?define msierrIISFailedSchedInstallWebSvcExt = 26038?> + <?define msierrIISFailedSchedUninstallWebs = 26051?> + <?define msierrIISFailedSchedUninstallWebDirs = 26052?> + <?define msierrIISFailedSchedUninstallVDirs = 26053?> + <?define msierrIISFailedSchedUninstallFilters = 26054?> + <?define msierrIISFailedSchedUninstallAppPool = 26055?> + <?define msierrIISFailedSchedUninstallProp = 26056?> + <?define msierrIISFailedSchedUninstallWebSvcExt = 26057?> + <?define msierrIISFailedStartTransaction = 26101?> + <?define msierrIISFailedOpenKey = 26102?> + <?define msierrIISFailedCreateKey = 26103?> + <?define msierrIISFailedWriteData = 26104?> + <?define msierrIISFailedCreateApp = 26105?> + <?define msierrIISFailedDeleteKey = 26106?> + <?define msierrIISFailedDeleteApp = 26107?> + <?define msierrIISFailedDeleteValue = 26108?> + <?define msierrIISFailedCommitInUse = 26109?> + <?define msierrSQLFailedCreateDatabase = 26201?> + <?define msierrSQLFailedDropDatabase = 26202?> + <?define msierrSQLFailedConnectDatabase = 26203?> + <?define msierrSQLFailedExecString = 26204?> + <?define msierrSQLDatabaseAlreadyExists = 26205?> + <?define msierrPERFMONFailedRegisterDLL = 26251?> + <?define msierrPERFMONFailedUnregisterDLL = 26252?> + <?define msierrInstallPerfCounterData = 26253?> + <?define msierrUninstallPerfCounterData = 26254?> + <?define msierrSMBFailedCreate = 26301?> + <?define msierrSMBFailedDrop = 26302?> + <?define msierrCERTFailedOpen = 26351?> + <?define msierrCERTFailedAdd = 26352?> + <?define msierrUSRFailedUserCreate = 26401?> + <?define msierrUSRFailedUserCreatePswd = 26402?> + <?define msierrUSRFailedUserGroupAdd = 26403?> + <?define msierrUSRFailedUserCreateExists = 26404?> + <?define msierrUSRFailedGrantLogonAsService = 26405?> + <?define msierrDependencyMissingDependencies = 26451?> + <?define msierrDependencyHasDependents = 26452?> + <?define msierrDotNetRuntimeRequired = 27000?> + <?define msierrComPlusCannotConnect = 28001?> + <?define msierrComPlusPartitionReadFailed = 28002?> + <?define msierrComPlusPartitionRoleReadFailed = 28003?> + <?define msierrComPlusUserInPartitionRoleReadFailed = 28004?> + <?define msierrComPlusPartitionUserReadFailed = 28005?> + <?define msierrComPlusApplicationReadFailed = 28006?> + <?define msierrComPlusApplicationRoleReadFailed = 28007?> + <?define msierrComPlusUserInApplicationRoleReadFailed = 28008?> + <?define msierrComPlusAssembliesReadFailed = 28009?> + <?define msierrComPlusSubscriptionReadFailed = 28010?> + <?define msierrComPlusPartitionDependency = 28011?> + <?define msierrComPlusPartitionNotFound = 28012?> + <?define msierrComPlusPartitionIdConflict = 28013?> + <?define msierrComPlusPartitionNameConflict = 28014?> + <?define msierrComPlusApplicationDependency = 28015?> + <?define msierrComPlusApplicationNotFound = 28016?> + <?define msierrComPlusApplicationIdConflict = 28017?> + <?define msierrComPlusApplicationNameConflict = 28018?> + <?define msierrComPlusApplicationRoleDependency = 28019?> + <?define msierrComPlusApplicationRoleNotFound = 28020?> + <?define msierrComPlusApplicationRoleConflict = 28021?> + <?define msierrComPlusAssemblyDependency = 28022?> + <?define msierrComPlusSubscriptionIdConflict = 28023?> + <?define msierrComPlusSubscriptionNameConflict = 28024?> + <?define msierrComPlusFailedLookupNames = 28025?> + <?define msierrMsmqCannotConnect = 28101?> +</Include> \ No newline at end of file diff --git a/src/wixlib/en-us.wxl b/src/wixlib/en-us.wxl new file mode 100644 index 00000000..7db8a9c2 --- /dev/null +++ b/src/wixlib/en-us.wxl @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + + +<WixLocalization Culture="en-us" xmlns="http://wixtoolset.org/schemas/v4/wxl"> + <String Id="msierrFirewallCannotConnect" Overridable="yes">Cannot connect to Windows Firewall. ([2] [3] [4] [5])</String> + + <String Id="WixSchedFirewallExceptionsInstall" Overridable="yes">Configuring Windows Firewall</String> + <String Id="WixSchedFirewallExceptionsUninstall" Overridable="yes">Configuring Windows Firewall</String> + <String Id="WixRollbackFirewallExceptionsInstall" Overridable="yes">Rolling back Windows Firewall configuration</String> + <String Id="WixExecFirewallExceptionsInstall" Overridable="yes">Installing Windows Firewall configuration</String> + <String Id="WixRollbackFirewallExceptionsUninstall" Overridable="yes">Rolling back Windows Firewall configuration</String> + <String Id="WixExecFirewallExceptionsUninstall" Overridable="yes">Uninstalling Windows Firewall configuration</String> +</WixLocalization> diff --git a/src/wixlib/es-es.wxl b/src/wixlib/es-es.wxl new file mode 100644 index 00000000..45d5fd0e --- /dev/null +++ b/src/wixlib/es-es.wxl @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + +<WixLocalization Culture="es-es" xmlns="http://wixtoolset.org/schemas/v4/wxl"> + <String Id="msierrFirewallCannotConnect" Overridable="yes">No se puede conectar al Firewall de Windows. ([2] [3] [4] [5])</String> + + <String Id="WixSchedFirewallExceptionsInstall" Overridable="yes">Configurando el Firewall de Windows</String> + <String Id="WixSchedFirewallExceptionsUninstall" Overridable="yes">Configurando el Firewall de Windows</String> + <String Id="WixRollbackFirewallExceptionsInstall" Overridable="yes">Regresando la configuración del Firewall de Windows</String> + <String Id="WixExecFirewallExceptionsInstall" Overridable="yes">Instalando la configuración del Firewall de Windows</String> + <String Id="WixRollbackFirewallExceptionsUninstall" Overridable="yes">Regresando la configuración del Firewall de Windows</String> + <String Id="WixExecFirewallExceptionsUninstall" Overridable="yes">Desinstalando la configuración del Firewall de Windows</String> +</WixLocalization> diff --git a/src/wixlib/firewall.wixproj b/src/wixlib/firewall.wixproj new file mode 100644 index 00000000..fc700d61 --- /dev/null +++ b/src/wixlib/firewall.wixproj @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="15.0"> + <Import Project="..\..\packages\WixToolset.Core.InternalPackage.4.0.64\build\WixToolset.Core.InternalPackage.props" Condition="Exists('..\..\packages\WixToolset.Core.InternalPackage.4.0.64\build\WixToolset.Core.InternalPackage.props')" /> + <Import Project="..\FindLocalWix.props" /> + <PropertyGroup> + <ProjectGuid>{1acffefd-505a-41a5-acbf-a02b7b473aa2}</ProjectGuid> + <OutputName>firewall</OutputName> + <OutputType>Library</OutputType> + <BindFiles>true</BindFiles> + <Pedantic>true</Pedantic> + <Cultures>en-us</Cultures> + </PropertyGroup> + <ItemGroup> + <Compile Include="FirewallExtension.wxs" /> + <Compile Include="FirewallExtension_x86.wxs" /> + <EmbeddedResource Include="en-us.wxl" /> + <EmbeddedResource Include="es-es.wxl" /> + <EmbeddedResource Include="ja-jp.wxl" /> + <EmbeddedResource Include="pl-pl.wxl" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\ca\fwca.vcxproj"> + <Name>fwca</Name> + <Project>{F72D34CA-48DA-4DFD-91A9-A0C78BEF6981}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' AND Exists('$(WixTargetsPath)') " /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\wix.targets') " /> + <Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' "> + <Error Text="FG-WiX or WiX Toolset build tools (v3.11 or later) must be installed to build this project. To download FG-WiX, go to https://www.firegiant.com/downloads/. To download the WiX Toolset, go to http://wixtoolset.org/releases/." /> + </Target> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.1.7\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.1.7\build\Nerdbank.GitVersioning.targets'))" /> + <Error Condition="!Exists('..\..\packages\WixToolset.Core.InternalPackage.4.0.64\build\WixToolset.Core.InternalPackage.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.Core.InternalPackage.4.0.64\build\WixToolset.Core.InternalPackage.props'))" /> + </Target> + <Import Project="..\..\packages\Nerdbank.GitVersioning.2.1.7\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.1.7\build\Nerdbank.GitVersioning.targets')" /> +</Project> \ No newline at end of file diff --git a/src/wixlib/ja-jp.wxl b/src/wixlib/ja-jp.wxl new file mode 100644 index 00000000..b6c0b96e --- /dev/null +++ b/src/wixlib/ja-jp.wxl @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + + +<WixLocalization Culture="ja-jp" xmlns="http://wixtoolset.org/schemas/v4/wxl"> + <String Id="msierrFirewallCannotConnect" Overridable="yes">Windows ファイアウォールへ接続できません。 ([2] [3] [4] [5])</String> + + <String Id="WixSchedFirewallExceptionsInstall" Overridable="yes">Windows ファイアウォールを構成しています</String> + <String Id="WixSchedFirewallExceptionsUninstall" Overridable="yes">Windows ファイアウォールを構成しています</String> + <String Id="WixRollbackFirewallExceptionsInstall" Overridable="yes">Windows ファイアウォール構成をロールバックしています</String> + <String Id="WixExecFirewallExceptionsInstall" Overridable="yes">Windows ファイアウォール構成をインストールしています</String> + <String Id="WixRollbackFirewallExceptionsUninstall" Overridable="yes">Windows ファイアウォール構成をロールバックしています</String> + <String Id="WixExecFirewallExceptionsUninstall" Overridable="yes">Windows ファイアウォール構成をアンインストールしています</String> +</WixLocalization> diff --git a/src/wixlib/packages.config b/src/wixlib/packages.config new file mode 100644 index 00000000..4250845c --- /dev/null +++ b/src/wixlib/packages.config @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Nerdbank.GitVersioning" version="2.1.7" developmentDependency="true" targetFramework="net40" /> + <package id="WixToolset.Core.InternalPackage" version="4.0.64" targetFramework="net40" developmentDependency="true" /> +</packages> \ No newline at end of file diff --git a/src/wixlib/pl-pl.wxl b/src/wixlib/pl-pl.wxl new file mode 100644 index 00000000..a685917c --- /dev/null +++ b/src/wixlib/pl-pl.wxl @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> + + +<WixLocalization Culture="pl-pl" xmlns="http://wixtoolset.org/schemas/v4/wxl"> + <String Id="msierrFirewallCannotConnect" Overridable="yes">Nie udało się połączyć z Zaporą systemu Windows. ([2] [3] [4] [5])</String> + + <String Id="WixSchedFirewallExceptionsInstall" Overridable="yes">Dodawanie wyjątków do Zapory systemu Windows</String> + <String Id="WixSchedFirewallExceptionsUninstall" Overridable="yes">Usuwanie wyjątków z Zapory systemu Windows</String> + <String Id="WixRollbackFirewallExceptionsInstall" Overridable="yes">Cofanie zmian konfiguracji Zapory systemu Windows</String> + <String Id="WixExecFirewallExceptionsInstall" Overridable="yes">Konfigurowywanie Zapory systemu Windows</String> + <String Id="WixRollbackFirewallExceptionsUninstall" Overridable="yes">Cofanie zmian konfiguracji Zapory systemu Windows</String> + <String Id="WixExecFirewallExceptionsUninstall" Overridable="yes">Konfigurowywanie Zapory systemu Windows</String> +</WixLocalization> -- cgit v1.2.3-55-g6feb