From 0afd76e4c5d46f237591d860e7d445e267522187 Mon Sep 17 00:00:00 2001
From: Sean Hall <r.sean.hall@gmail.com>
Date: Mon, 30 Mar 2020 19:46:56 +1000
Subject: Add DetectSHA2Support "search".

---
 src/be/UtilBundleExtension.cpp |  87 ++++++++++++++++++++++++++
 src/be/UtilBundleExtension.h   |  16 +++++
 src/be/detectsha2support.cpp   |  58 +++++++++++++++++
 src/be/detectsha2support.h     |   8 +++
 src/be/packages.config         |   1 +
 src/be/precomp.h               |  23 +++++--
 src/be/utilbe.cpp              |  28 ++++++++-
 src/be/utilbe.vcxproj          |  20 +++---
 src/be/utilsearch.cpp          | 137 +++++++++++++++++++++++++++++++++++++++++
 src/be/utilsearch.h            |  52 ++++++++++++++++
 10 files changed, 410 insertions(+), 20 deletions(-)
 create mode 100644 src/be/UtilBundleExtension.cpp
 create mode 100644 src/be/UtilBundleExtension.h
 create mode 100644 src/be/detectsha2support.cpp
 create mode 100644 src/be/detectsha2support.h
 create mode 100644 src/be/utilsearch.cpp
 create mode 100644 src/be/utilsearch.h

(limited to 'src/be')

diff --git a/src/be/UtilBundleExtension.cpp b/src/be/UtilBundleExtension.cpp
new file mode 100644
index 00000000..2ac842a5
--- /dev/null
+++ b/src/be/UtilBundleExtension.cpp
@@ -0,0 +1,87 @@
+// 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"
+#include "BextBaseBundleExtension.h"
+
+class CWixUtilBundleExtension : public CBextBaseBundleExtension
+{
+public: // IBundleExtension
+    virtual STDMETHODIMP Search(
+        __in LPCWSTR wzId,
+        __in LPCWSTR wzVariable
+        )
+    {
+        HRESULT hr = S_OK;
+
+        hr = UtilSearchExecute(&m_searches, wzId, wzVariable, m_pEngine);
+
+        return hr;
+    }
+
+public: //CBextBaseBundleExtension
+    virtual STDMETHODIMP Initialize(
+        __in const BUNDLE_EXTENSION_CREATE_ARGS* pCreateArgs
+        )
+    {
+        HRESULT hr = S_OK;
+        IXMLDOMDocument* pixdManifest = NULL;
+        IXMLDOMNode* pixnBundleExtension = NULL;
+
+        hr = CBextBaseBundleExtension::Initialize(pCreateArgs);
+        ExitOnFailure(hr, "CBextBaseBundleExtension initialization failed.");
+
+        hr = XmlLoadDocumentFromFile(m_sczBundleExtensionDataPath, &pixdManifest);
+        ExitOnFailure(hr, "Failed to load bundle extension manifest from path: %ls", m_sczBundleExtensionDataPath);
+
+        hr = BextGetBundleExtensionDataNode(pixdManifest, UTIL_BUNDLE_EXTENSION_ID, &pixnBundleExtension);
+        ExitOnFailure(hr, "Failed to get BundleExtension '%ls'", UTIL_BUNDLE_EXTENSION_ID);
+
+        hr = UtilSearchParseFromXml(&m_searches, pixnBundleExtension);
+        ExitOnFailure(hr, "Failed to parse searches from bundle extension manifest.");
+
+    LExit:
+        ReleaseObject(pixnBundleExtension);
+        ReleaseObject(pixdManifest);
+
+        return hr;
+    }
+
+public:
+    CWixUtilBundleExtension(
+        __in IBundleExtensionEngine* pEngine
+        ) : CBextBaseBundleExtension(pEngine)
+    {
+        m_searches = { };
+    }
+
+    ~CWixUtilBundleExtension()
+    {
+        UtilSearchUninitialize(&m_searches);
+    }
+
+private:
+    UTIL_SEARCHES m_searches;
+};
+
+HRESULT UtilBundleExtensionCreate(
+    __in IBundleExtensionEngine* pEngine,
+    __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
+    __out IBundleExtension** ppBundleExtension
+    )
+{
+    HRESULT hr = S_OK;
+    CWixUtilBundleExtension* pExtension = NULL;
+
+    pExtension = new CWixUtilBundleExtension(pEngine);
+    ExitOnNull(pExtension, hr, E_OUTOFMEMORY, "Failed to create new CWixUtilBundleExtension.");
+
+    hr = pExtension->Initialize(pArgs);
+    ExitOnFailure(hr, "CWixUtilBundleExtension initialization failed");
+
+    *ppBundleExtension = pExtension;
+    pExtension = NULL;
+
+LExit:
+    ReleaseObject(pExtension);
+    return hr;
+}
diff --git a/src/be/UtilBundleExtension.h b/src/be/UtilBundleExtension.h
new file mode 100644
index 00000000..16c5b346
--- /dev/null
+++ b/src/be/UtilBundleExtension.h
@@ -0,0 +1,16 @@
+#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.
+
+
+// constants
+
+#define UTIL_BUNDLE_EXTENSION_ID L"WixUtilBundleExtension"
+
+
+// function declarations
+
+HRESULT UtilBundleExtensionCreate(
+    __in IBundleExtensionEngine* pEngine,
+    __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
+    __out IBundleExtension** ppBundleExtension
+    );
diff --git a/src/be/detectsha2support.cpp b/src/be/detectsha2support.cpp
new file mode 100644
index 00000000..f1f3637e
--- /dev/null
+++ b/src/be/detectsha2support.cpp
@@ -0,0 +1,58 @@
+// 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"
+
+// https://gist.github.com/navossoc/7572c7d82243e9f818989e2765e7793a
+HRESULT DetectSHA2Support(
+    __out BOOL* pfSupported
+    )
+{
+    HRESULT hr = S_OK;
+    HMODULE hModule = NULL;
+    FARPROC pfn = NULL;
+    DWORD er = ERROR_SUCCESS;
+
+    hr = LoadSystemLibrary(L"wintrust.dll", &hModule);
+    ExitOnFailure(hr, "Failed to load wintrust.dll");
+
+    pfn = ::GetProcAddress(hModule, "CryptCATAdminAcquireContext2");
+    if (pfn)
+    {
+        *pfSupported = TRUE;
+        ExitFunction1(hr = S_OK);
+    }
+
+    er = ::GetLastError();
+    if (er == ERROR_PROC_NOT_FOUND)
+    {
+        *pfSupported = FALSE;
+        ExitFunction1(hr = S_OK);
+    }
+
+    hr = HRESULT_FROM_WIN32(er);
+    ExitOnFailure(hr, "Failed to probe for CryptCATAdminAcquireContext2 in wintrust.dll");
+
+LExit:
+    ::FreeLibrary(hModule);
+
+    return hr;
+}
+
+HRESULT UtilPerformDetectSHA2Support(
+    __in LPCWSTR wzVariable,
+    __in UTIL_SEARCH* /*pSearch*/,
+    __in IBundleExtensionEngine* pEngine
+    )
+{
+    HRESULT hr = S_OK;
+    BOOL fSupported = FALSE;
+
+    hr = DetectSHA2Support(&fSupported);
+    ExitOnFailure(hr, "DetectSHA2Support failed.");
+
+    hr = pEngine->SetVariableNumeric(wzVariable, fSupported ? 1 : 0);
+    ExitOnFailure(hr, "Failed to set variable '%ls'", wzVariable);
+
+LExit:
+    return hr;
+}
diff --git a/src/be/detectsha2support.h b/src/be/detectsha2support.h
new file mode 100644
index 00000000..7f1f6031
--- /dev/null
+++ b/src/be/detectsha2support.h
@@ -0,0 +1,8 @@
+#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.
+
+HRESULT UtilPerformDetectSHA2Support(
+    __in LPCWSTR wzVariable,
+    __in UTIL_SEARCH* pSearch,
+    __in IBundleExtensionEngine* pEngine
+    );
\ No newline at end of file
diff --git a/src/be/packages.config b/src/be/packages.config
index f05546c5..ca23641f 100644
--- a/src/be/packages.config
+++ b/src/be/packages.config
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
+  <package id="WixToolset.BextUtil" version="4.0.16" targetFramework="native" />
   <package id="WixToolset.BootstrapperCore.Native" version="4.0.13" targetFramework="native" />
   <package id="WixToolset.DUtil" version="4.0.18" targetFramework="native" />
 </packages>
\ No newline at end of file
diff --git a/src/be/precomp.h b/src/be/precomp.h
index 9dc3c468..a4ab7abf 100644
--- a/src/be/precomp.h
+++ b/src/be/precomp.h
@@ -17,9 +17,20 @@
 
 #define MAXUINT USHRT_MAX
 
-#include "dutil.h"
-#include "memutil.h"
-
-#include "BootstrapperEngine.h"
-#include "BundleExtensionEngine.h"
-#include "BundleExtension.h"
+#include <dutil.h>
+#include <memutil.h>
+#include <strutil.h>
+#include <pathutil.h>
+#include <xmlutil.h>
+
+#include <BundleExtensionEngine.h>
+#include <BundleExtension.h>
+
+#include <IBundleExtensionEngine.h>
+#include <IBundleExtension.h>
+#include <bextutil.h>
+#include <BextBundleExtensionEngine.h>
+
+#include "utilsearch.h"
+#include "detectsha2support.h"
+#include "UtilBundleExtension.h"
diff --git a/src/be/utilbe.cpp b/src/be/utilbe.cpp
index 370669dd..d9816dc7 100644
--- a/src/be/utilbe.cpp
+++ b/src/be/utilbe.cpp
@@ -1,19 +1,41 @@
 // 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"
+#include "BextBaseBundleExtensionProc.h"
+
+static IBundleExtension* vpBundleExtension = NULL;
 
 // function definitions
 
 extern "C" HRESULT WINAPI BundleExtensionCreate(
-    __in const BUNDLE_EXTENSION_CREATE_ARGS* /*pArgs*/,
-    __inout BUNDLE_EXTENSION_CREATE_RESULTS* /*pResults*/
+    __in const BUNDLE_EXTENSION_CREATE_ARGS* pArgs,
+    __inout BUNDLE_EXTENSION_CREATE_RESULTS* pResults
     )
 {
-    HRESULT hr = S_OK; 
+    HRESULT hr = S_OK;
+    IBundleExtensionEngine* pEngine = NULL;
+
+    hr = XmlInitialize();
+    ExitOnFailure(hr, "Failed to initialize XML.");
+
+    hr = BextInitializeFromCreateArgs(pArgs, &pEngine);
+    ExitOnFailure(hr, "Failed to initialize bext");
+
+    hr = UtilBundleExtensionCreate(pEngine, pArgs, &vpBundleExtension);
+    BextExitOnFailure(hr, "Failed to create WixUtilBundleExtension");
+
+    pResults->pfnBundleExtensionProc = BextBaseBundleExtensionProc;
+    pResults->pvBundleExtensionProcContext = vpBundleExtension;
+
+LExit:
+    ReleaseObject(pEngine);
 
     return hr;
 }
 
 extern "C" void WINAPI BundleExtensionDestroy()
 {
+    BextUninitialize();
+    ReleaseNullObject(vpBundleExtension);
+    XmlUninitialize();
 }
\ No newline at end of file
diff --git a/src/be/utilbe.vcxproj b/src/be/utilbe.vcxproj
index 963eac7f..e64d113e 100644
--- a/src/be/utilbe.vcxproj
+++ b/src/be/utilbe.vcxproj
@@ -1,10 +1,9 @@
 <?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.BextUtil.4.0.16\build\WixToolset.BextUtil.props" Condition="Exists('..\..\packages\WixToolset.BextUtil.4.0.16\build\WixToolset.BextUtil.props')" />
   <Import Project="..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props" Condition="Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" />
   <Import Project="..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" />
-
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="Debug|Win32">
       <Configuration>Debug</Configuration>
@@ -15,7 +14,6 @@
       <Platform>Win32</Platform>
     </ProjectConfiguration>
   </ItemGroup>
-
   <PropertyGroup Label="Globals">
     <ProjectGuid>{630C1EE7-2517-4A8C-83E3-DA1150308B58}</ProjectGuid>
     <ConfigurationType>DynamicLibrary</ConfigurationType>
@@ -25,37 +23,37 @@
     <ProjectModuleDefinitionFile>utilbe.def</ProjectModuleDefinitionFile>
     <Description>WiX Toolset Util BundleExtension</Description>
   </PropertyGroup>
-
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-
   <PropertyGroup>
     <ProjectAdditionalLinkLibraries>msi.lib</ProjectAdditionalLinkLibraries>
   </PropertyGroup>
-
   <ItemGroup>
+    <ClCompile Include="detectsha2support.cpp" />
     <ClCompile Include="precomp.cpp">
       <PrecompiledHeader>Create</PrecompiledHeader>
     </ClCompile>
     <ClCompile Include="utilbe.cpp" />
+    <ClCompile Include="UtilBundleExtension.cpp" />
+    <ClCompile Include="utilsearch.cpp" />
   </ItemGroup>
-
   <ItemGroup>
+    <ClInclude Include="detectsha2support.h" />
     <ClInclude Include="precomp.h" />
+    <ClInclude Include="UtilBundleExtension.h" />
+    <ClInclude Include="utilsearch.h" />
   </ItemGroup>
-
   <ItemGroup>
     <None Include="packages.config" />
     <None Include="utilbe.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\WixToolset.BextUtil.4.0.16\build\WixToolset.BextUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.BextUtil.4.0.16\build\WixToolset.BextUtil.props'))" />
     <Error Condition="!Exists('..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.BootstrapperCore.Native.4.0.13\build\WixToolset.BootstrapperCore.Native.props'))" />
     <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.18\build\WixToolset.DUtil.props'))" />
   </Target>
-</Project>
+</Project>
\ No newline at end of file
diff --git a/src/be/utilsearch.cpp b/src/be/utilsearch.cpp
new file mode 100644
index 00000000..4e9d86a1
--- /dev/null
+++ b/src/be/utilsearch.cpp
@@ -0,0 +1,137 @@
+// 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"
+
+
+STDMETHODIMP UtilSearchParseFromXml(
+    __in UTIL_SEARCHES* pSearches,
+    __in IXMLDOMNode* pixnBundleExtension
+    )
+{
+    HRESULT hr = S_OK;
+    IXMLDOMNodeList* pixnNodes = NULL;
+    IXMLDOMNode* pixnNode = NULL;
+    DWORD cNodes = 0;
+    BSTR bstrNodeName = NULL;
+
+    // Select Util search nodes.
+    hr = XmlSelectNodes(pixnBundleExtension, L"WixDetectSHA2Support", &pixnNodes);
+    ExitOnFailure(hr, "Failed to select Util search nodes.");
+
+    // Get Util search node count.
+    hr = pixnNodes->get_length((long*)&cNodes);
+    ExitOnFailure(hr, "Failed to get Util search node count.");
+
+    if (!cNodes)
+    {
+        ExitFunction();
+    }
+
+    // Allocate memory for searches.
+    pSearches->rgSearches = (UTIL_SEARCH*)MemAlloc(sizeof(UTIL_SEARCH) * cNodes, TRUE);
+    ExitOnNull(pSearches->rgSearches, hr, E_OUTOFMEMORY, "Failed to allocate memory for search structs.");
+
+    pSearches->cSearches = cNodes;
+
+    // Parse search elements.
+    for (DWORD i = 0; i < cNodes; ++i)
+    {
+        UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
+
+        hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName);
+        ExitOnFailure(hr, "Failed to get next node.");
+
+        // @Id
+        hr = XmlGetAttributeEx(pixnNode, L"Id", &pSearch->sczId);
+        ExitOnFailure(hr, "Failed to get @Id.");
+
+        // Read type specific attributes.
+        if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"WixDetectSHA2Support", -1))
+        {
+            pSearch->Type = UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT;
+        }
+        else
+        {
+            hr = E_UNEXPECTED;
+            ExitOnFailure(hr, "Unexpected element name: %ls", bstrNodeName);
+        }
+
+        // prepare next iteration
+        ReleaseNullObject(pixnNode);
+        ReleaseNullBSTR(bstrNodeName);
+    }
+
+LExit:
+    ReleaseBSTR(bstrNodeName);
+    ReleaseObject(pixnNode);
+    ReleaseObject(pixnNodes);
+
+    return hr;
+}
+
+void UtilSearchUninitialize(
+    __in UTIL_SEARCHES* pSearches
+    )
+{
+    if (pSearches->rgSearches)
+    {
+        for (DWORD i = 0; i < pSearches->cSearches; ++i)
+        {
+            UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
+
+            ReleaseStr(pSearch->sczId);
+        }
+        MemFree(pSearches->rgSearches);
+    }
+}
+
+STDMETHODIMP UtilSearchExecute(
+    __in UTIL_SEARCHES* pSearches,
+    __in LPCWSTR wzSearchId,
+    __in LPCWSTR wzVariable,
+    __in IBundleExtensionEngine* pEngine
+    )
+{
+    HRESULT hr = S_OK;
+    UTIL_SEARCH* pSearch = NULL;
+
+    hr = UtilSearchFindById(pSearches, wzSearchId, &pSearch);
+    ExitOnFailure(hr, "Search id '%ls' is unknown to the util extension.");
+
+    switch (pSearch->Type)
+    {
+    case UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT:
+        hr = UtilPerformDetectSHA2Support(wzVariable, pSearch, pEngine);
+        break;
+    default:
+        hr = E_UNEXPECTED;
+    }
+
+LExit:
+    return hr;
+}
+
+STDMETHODIMP UtilSearchFindById(
+    __in UTIL_SEARCHES* pSearches,
+    __in LPCWSTR wzId,
+    __out UTIL_SEARCH** ppSearch
+    )
+{
+    HRESULT hr = S_OK;
+
+    for (DWORD i = 0; i < pSearches->cSearches; ++i)
+    {
+        UTIL_SEARCH* pSearch = &pSearches->rgSearches[i];
+
+        if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pSearch->sczId, -1, wzId, -1))
+        {
+            *ppSearch = pSearch;
+            ExitFunction1(hr = S_OK);
+        }
+    }
+
+    hr = E_NOTFOUND;
+
+LExit:
+    return hr;
+}
diff --git a/src/be/utilsearch.h b/src/be/utilsearch.h
new file mode 100644
index 00000000..1e0ca96d
--- /dev/null
+++ b/src/be/utilsearch.h
@@ -0,0 +1,52 @@
+#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.
+
+
+// constants
+
+enum UTIL_SEARCH_TYPE
+{
+    UTIL_SEARCH_TYPE_NONE,
+    UTIL_SEARCH_TYPE_DETECT_SHA2_SUPPORT,
+};
+
+
+// structs
+
+typedef struct _UTIL_SEARCH
+{
+    LPWSTR sczId;
+
+    UTIL_SEARCH_TYPE Type;
+} UTIL_SEARCH;
+
+typedef struct _UTIL_SEARCHES
+{
+    UTIL_SEARCH* rgSearches;
+    DWORD cSearches;
+} UTIL_SEARCHES;
+
+
+// function declarations
+
+STDMETHODIMP UtilSearchParseFromXml(
+    __in UTIL_SEARCHES* pSearches,
+    __in IXMLDOMNode* pixnBundleExtension
+    );
+
+void UtilSearchUninitialize(
+    __in UTIL_SEARCHES* pSearches
+    );
+
+STDMETHODIMP UtilSearchExecute(
+    __in UTIL_SEARCHES* pSearches,
+    __in LPCWSTR wzSearchId,
+    __in LPCWSTR wzVariable,
+    __in IBundleExtensionEngine* pEngine
+    );
+
+STDMETHODIMP UtilSearchFindById(
+    __in UTIL_SEARCHES* pSearches,
+    __in LPCWSTR wzId,
+    __out UTIL_SEARCH** ppSearch
+    );
-- 
cgit v1.2.3-55-g6feb