diff --git a/dev/Common/IsWindowsVersion.h b/dev/Common/IsWindowsVersion.h index f7fe5ee17c..efabbdce0c 100644 --- a/dev/Common/IsWindowsVersion.h +++ b/dev/Common/IsWindowsVersion.h @@ -1,10 +1,12 @@ -// Copyright (c) Microsoft Corporation and Contributors. +// Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. #ifndef __ISWINDOWSVERSION_H #define __ISWINDOWSVERSION_H #include +#include +#include namespace WindowsVersion { @@ -62,6 +64,12 @@ inline bool IsWindows11_24H2OrGreater() // MsixIsPackageFeatureSupported() added to in Windows 11 24H2 (aka NTDDI_WIN11_GE) return IsExportPresent(L"appxdeploymentclient.dll", "MsixIsPackageFeatureSupported"); } + +inline bool SupportsIAppxFactory4() +{ + return wil::CoCreateInstanceNoThrow().get() != nullptr; +} + } #endif // __ISWINDOWSVERSION_H diff --git a/dev/Common/TerminalVelocityFeatures-PackageManager.h b/dev/Common/TerminalVelocityFeatures-PackageManager.h index 99edf97bbd..edc43420d2 100644 --- a/dev/Common/TerminalVelocityFeatures-PackageManager.h +++ b/dev/Common/TerminalVelocityFeatures-PackageManager.h @@ -4,17 +4,19 @@ // THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT IT // INPUT FILE: dev\common\TerminalVelocityFeatures-PackageManager.xml -// OPTIONS: -Channel Experimental -Language C++ -Namespace Microsoft.Windows.Management.Deployment -Path dev\common\TerminalVelocityFeatures-PackageManager.xml -Output dev\common\TerminalVelocityFeatures-PackageManager.h +// OPTIONS: -Channel Experimental -Language C++ -Namespace Microsoft::Windows::Management::Deployment -Path dev\common\TerminalVelocityFeatures-PackageManager.xml -Output dev\common #if defined(__midlrt) namespace features { feature_name Feature_PackageManager = { DisabledByDefault, FALSE }; + feature_name Feature_PackageValidator = { DisabledByDefault, FALSE }; } #endif // defined(__midlrt) // Feature constants #define WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEMANAGER_ENABLED 1 +#define WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEVALIDATOR_ENABLED 1 #if defined(__cplusplus) @@ -27,6 +29,12 @@ struct Feature_PackageManager static constexpr bool IsEnabled() { return WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEMANAGER_ENABLED == 1; } }; -} // namespace Microsoft.Windows.Management.Deployment +__pragma(detect_mismatch("ODR_violation_WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEVALIDATOR_ENABLED_mismatch", "AlwaysEnabled")) +struct Feature_PackageValidator +{ + static constexpr bool IsEnabled() { return WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEVALIDATOR_ENABLED == 1; } +}; + +} // namespace Microsoft::Windows::Management::Deployment #endif // defined(__cplusplus) diff --git a/dev/Common/TerminalVelocityFeatures-PackageManager.xml b/dev/Common/TerminalVelocityFeatures-PackageManager.xml index 932aa39394..cd235b8d2e 100644 --- a/dev/Common/TerminalVelocityFeatures-PackageManager.xml +++ b/dev/Common/TerminalVelocityFeatures-PackageManager.xml @@ -17,4 +17,15 @@ Stable + + + Feature_PackageValidator + PackageValidator support in PackageDeploymentManager + AlwaysEnabled + + Preview + Stable + + + diff --git a/dev/PackageManager/API/AppxPackagingObject.h b/dev/PackageManager/API/AppxPackagingObject.h new file mode 100644 index 0000000000..778670f224 --- /dev/null +++ b/dev/PackageManager/API/AppxPackagingObject.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include + +namespace winrt::Microsoft::Windows::Management::Deployment::implementation +{ + struct AppxPackagingObject : winrt::implements + { + AppxPackagingObject(IUnknown* object) + { + m_object.copy_from(object); + } + + int32_t query_interface_tearoff(winrt::guid const& id, void** object) const noexcept override + { + return m_object.as(id, object); + } + + private: + winrt::com_ptr m_object; + }; + +} diff --git a/dev/PackageManager/API/M.W.M.D.AddPackageOptions.cpp b/dev/PackageManager/API/M.W.M.D.AddPackageOptions.cpp index fb95f820a7..37d7df9477 100644 --- a/dev/PackageManager/API/M.W.M.D.AddPackageOptions.cpp +++ b/dev/PackageManager/API/M.W.M.D.AddPackageOptions.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include +#include #include "M.W.M.D.AddPackageOptions.h" #include "Microsoft.Windows.Management.Deployment.AddPackageOptions.g.cpp" @@ -172,4 +173,35 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation { m_limitToExistingPackages = value; } + + bool AddPackageOptions::IsPackageValidatorsSupported() + { + return WindowsVersion::SupportsIAppxFactory4(); + } + winrt::Windows::Foundation::Collections::IMapView > AddPackageOptions::PackageValidators() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + if (!m_packageValidators) + { + // return an empty view + return winrt::single_threaded_map >().GetView(); + } + else + { + return m_packageValidators.GetView(); + } + } + void AddPackageOptions::AddPackageValidator(winrt::Windows::Foundation::Uri const& packageUri, winrt::Microsoft::Windows::Management::Deployment::IPackageValidator const& validator) + { + if (!m_packageValidators) + { + m_packageValidators = winrt::single_threaded_map >(); + } + if (!m_packageValidators.HasKey(packageUri)) + { + m_packageValidators.Insert(packageUri, winrt::single_threaded_vector()); + } + m_packageValidators.Lookup(packageUri).Append(validator); + } } diff --git a/dev/PackageManager/API/M.W.M.D.AddPackageOptions.h b/dev/PackageManager/API/M.W.M.D.AddPackageOptions.h index 7ad3423f6b..11e984b0f5 100644 --- a/dev/PackageManager/API/M.W.M.D.AddPackageOptions.h +++ b/dev/PackageManager/API/M.W.M.D.AddPackageOptions.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation and Contributors. +// Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. #pragma once @@ -46,6 +46,9 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation bool IsLimitToExistingPackagesSupported(); bool LimitToExistingPackages(); void LimitToExistingPackages(bool value); + bool IsPackageValidatorsSupported(); + winrt::Windows::Foundation::Collections::IMapView > PackageValidators(); + void AddPackageValidator(winrt::Windows::Foundation::Uri const&, winrt::Microsoft::Windows::Management::Deployment::IPackageValidator const&); private: winrt::Microsoft::Windows::Management::Deployment::PackageVolume m_targetVolume{ nullptr }; @@ -67,6 +70,7 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation bool m_deferRegistrationWhenPackagesAreInUse{}; winrt::Windows::Foundation::Collections::IMap m_expectedDigests; bool m_limitToExistingPackages{}; + winrt::Windows::Foundation::Collections::IMap > m_packageValidators; }; } namespace winrt::Microsoft::Windows::Management::Deployment::factory_implementation diff --git a/dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.cpp b/dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.cpp new file mode 100644 index 0000000000..e0cf76ce6a --- /dev/null +++ b/dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.cpp @@ -0,0 +1,195 @@ +#include "pch.h" +#include "M.W.M.D.PackageCertificateEkuValidator.h" +#include "Microsoft.Windows.Management.Deployment.PackageCertificateEkuValidator.g.cpp" +#include + +#include + +namespace winrt::Microsoft::Windows::Management::Deployment::implementation +{ + PackageCertificateEkuValidator::PackageCertificateEkuValidator(hstring const& expectedCertificateEku) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + m_expectedEku = expectedCertificateEku; + } + + bool PackageCertificateEkuValidator::IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + winrt::com_ptr packageReader; + winrt::com_ptr bundleReader; + + if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&packageReader)))) + { + winrt::com_ptr signatureFile; + if (SUCCEEDED(packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, signatureFile.put()))) + { + return CheckSignature(signatureFile.get()); + } + else + { + return false; // package is not valid because there is no signature present + } + } + else if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&bundleReader)))) + { + winrt::com_ptr signatureFile; + if (SUCCEEDED(bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_SIGNATURE, signatureFile.put()))) + { + return CheckSignature(signatureFile.get()); + } + else + { + return false; // package is not valid because there is no signature present + } + } + else + { + THROW_WIN32(ERROR_NOT_SUPPORTED); + } + } + + bool PackageCertificateEkuValidator::CheckSignature(IAppxFile* signatureFile) + { + winrt::com_ptr signatureStream; + THROW_IF_FAILED(signatureFile->GetStream(signatureStream.put())); + + // The p7x signature should have a leading 4-byte PKCX header + static const DWORD P7xFileId = 0x58434b50; // PKCX + static const DWORD P7xFileIdSize = sizeof(P7xFileId); + + STATSTG streamStats{}; + THROW_IF_FAILED(signatureStream->Stat(&streamStats, STATFLAG_NONAME)); + if (streamStats.cbSize.HighPart > 0 || streamStats.cbSize.LowPart < P7xFileIdSize) + { + return false; // The signature has unexpected size + } + + DWORD streamSize = streamStats.cbSize.LowPart; + auto streamBuffer{ wil::make_unique_nothrow(streamSize) }; + THROW_IF_NULL_ALLOC(streamBuffer); + + ULONG bytesRead{}; + THROW_IF_FAILED(signatureStream->Read(streamBuffer.get(), streamSize, &bytesRead)); + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT), bytesRead != streamSize); + + if (*reinterpret_cast(streamBuffer.get()) != P7xFileId) + { + return false; // The signature does not have expected header + } + + CRYPT_DATA_BLOB signatureBlob{}; + signatureBlob.cbData = streamSize - P7xFileIdSize; + signatureBlob.pbData = streamBuffer.get() + P7xFileIdSize; + + wil::unique_hcertstore certStore; + wil::unique_hcryptmsg signedMessage; + DWORD queryContentType = 0; + DWORD queryFormatType = 0; + if (!CryptQueryObject( + CERT_QUERY_OBJECT_BLOB, + &signatureBlob, + CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, + CERT_QUERY_FORMAT_FLAG_BINARY, + 0, // Reserved parameter + NULL, // No encoding info needed + &queryContentType, + &queryFormatType, + &certStore, + &signedMessage, + NULL // No additional params for signed message output + )) + { + return false; // CryptQueryObject could not read the signature + } + + if (queryContentType != CERT_QUERY_CONTENT_PKCS7_SIGNED || queryFormatType != CERT_QUERY_FORMAT_BINARY || !certStore || !signedMessage) + { + return false; // CryptQueryObject returned unexpected data + } + + CMSG_SIGNER_INFO* signerInfo = NULL; + DWORD signerInfoSize = 0; + if (!CryptMsgGetParam(signedMessage.get(), CMSG_SIGNER_INFO_PARAM, 0, NULL, &signerInfoSize)) + { + return false; // CryptMsgGetParam could not read the signature + } + + if (signerInfoSize == 0 || signerInfoSize >= STRSAFE_MAX_CCH) + { + return false; // Signer info has unexpected size + } + + auto signerInfoBuffer{ wil::make_unique_nothrow(signerInfoSize) }; + THROW_IF_NULL_ALLOC(signerInfoBuffer); + signerInfo = reinterpret_cast(signerInfoBuffer.get()); + if (!CryptMsgGetParam(signedMessage.get(), CMSG_SIGNER_INFO_PARAM, 0, signerInfo, &signerInfoSize)) + { + return false; // CryptMsgGetParam could not read the signature + } + + // Get the signing certificate from the certificate store based on the issuer and serial number of the signer info + CERT_INFO certInfo; + certInfo.Issuer = signerInfo->Issuer; + certInfo.SerialNumber = signerInfo->SerialNumber; + + wil::unique_cert_context certContext{ + CertGetSubjectCertificateFromStore( + certStore.get(), + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &certInfo) + }; + if (!certContext) + { + return false; // CertGetSubjectCertificateFromStore could not find a cert context + } + + DWORD ekuBufferSize = 0; + if (!CertGetEnhancedKeyUsage( + certContext.get(), + 0, // Accept EKU from EKU extension or EKU extended properties + NULL, + &ekuBufferSize)) + { + return false; // CertGetEnhancedKeyUsage could not read EKUs + } + + if (ekuBufferSize < sizeof(CERT_ENHKEY_USAGE)) + { + return false; // No EKUs are found in the signature + } + + auto ekuBuffer{ wil::make_unique_nothrow(ekuBufferSize)}; + THROW_IF_NULL_ALLOC(ekuBuffer); + PCERT_ENHKEY_USAGE ekusFound = reinterpret_cast(ekuBuffer.get()); + if (!CertGetEnhancedKeyUsage( + certContext.get(), + 0, // Accept EKU from EKU extension or EKU extended properties + ekusFound, + &ekuBufferSize)) + { + return false; // CertGetEnhancedKeyUsage could not read EKUs + } + + for (DWORD i = 0; i < ekusFound->cUsageIdentifier; i++) + { + auto eku{ ekusFound->rgpszUsageIdentifier[i] }; + + int length = MultiByteToWideChar(CP_ACP, 0, eku, -1, nullptr, 0); + auto ekuWcharBuffer{ wil::make_unique_nothrow(length) }; + THROW_IF_NULL_ALLOC(ekuWcharBuffer); + + MultiByteToWideChar(CP_ACP, 0, eku, -1, ekuWcharBuffer.get(), length); + + if (wcscmp(ekuWcharBuffer.get(), m_expectedEku.c_str()) == 0) + { + return true; // Found expected EKU + } + } + + return false; // Did not find expected EKU + } + +} diff --git a/dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.h b/dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.h new file mode 100644 index 0000000000..abf70be04c --- /dev/null +++ b/dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.h @@ -0,0 +1,27 @@ +#pragma once +#include "Microsoft.Windows.Management.Deployment.PackageCertificateEkuValidator.g.h" + +#include + +namespace winrt::Microsoft::Windows::Management::Deployment::implementation +{ + struct PackageCertificateEkuValidator : PackageCertificateEkuValidatorT + { + PackageCertificateEkuValidator() = default; + + PackageCertificateEkuValidator(hstring const& expectedCertificateEku); + bool IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject); + + private: + bool CheckSignature(IAppxFile* signatureFile); + + hstring m_expectedEku{}; + }; +} + +namespace winrt::Microsoft::Windows::Management::Deployment::factory_implementation +{ + struct PackageCertificateEkuValidator : PackageCertificateEkuValidatorT + { + }; +} diff --git a/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.cpp b/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.cpp index 8f233ba6cd..f8d88e2d33 100644 --- a/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.cpp +++ b/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.cpp @@ -32,7 +32,11 @@ namespace ABI::Windows::Management::Deployment #include #include +#include +#include + #include +#include static_assert(static_cast(winrt::Microsoft::Windows::Management::Deployment::StubPackageOption::Default) == static_cast(winrt::Windows::Management::Deployment::StubPackageOption::Default), "winrt::Microsoft::Windows::Management::Deployment::StubPackageOption::Default != winrt::Windows::Management::Deployment::StubPackageOption::Default"); @@ -554,7 +558,6 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation PackageDeploymentProgressStatus::Queued, 0} }; progress(packageDeploymentProgress); - winrt::Windows::Management::Deployment::AddPackageOptions addOptions{ ToOptions(options) }; const double progressMaxPerPackage{ 1.0 }; HRESULT error{}; HRESULT extendedError{}; @@ -562,7 +565,7 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation winrt::guid activityId{}; try { - error = LOG_IF_FAILED_MSG(AddPackage(packageUri, addOptions, packageDeploymentProgress, progress, progressMaxPerPackage, extendedError, errorText, activityId), + error = LOG_IF_FAILED_MSG(AddPackage(packageUri, options, packageDeploymentProgress, progress, progressMaxPerPackage, extendedError, errorText, activityId), "ExtendedError:0x%08X PackageUri:%ls", extendedError, packageUri.ToString().c_str()); } @@ -677,7 +680,6 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation PackageDeploymentProgressStatus::Queued, 0} }; progress(packageDeploymentProgress); - winrt::Windows::Management::Deployment::StagePackageOptions stageOptions{ ToOptions(options) }; const double progressMaxPerPackage{ 1.0 }; HRESULT error{}; HRESULT extendedError{}; @@ -685,7 +687,7 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation winrt::guid activityId{}; try { - error = LOG_IF_FAILED_MSG(StagePackage(packageUri, stageOptions, packageDeploymentProgress, progress, progressMaxPerPackage, extendedError, errorText, activityId), + error = LOG_IF_FAILED_MSG(StagePackage(packageUri, options, packageDeploymentProgress, progress, progressMaxPerPackage, extendedError, errorText, activityId), "ExtendedError:0x%08X PackageUri:%ls", extendedError, packageUri.ToString().c_str()); } @@ -1958,6 +1960,12 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation return S_OK; } + if (::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()) + { + auto expectedDigests{ options.AddPackageOptions().ExpectedDigests() }; + ValidatePackagesAndUpdateExpectedDigests(options.AddPackageOptions().PackageValidators(), expectedDigests); + } + const auto progressBefore{ packageDeploymentProgress.Progress }; winrt::Windows::Management::Deployment::AddPackageOptions addOptions{ ToOptions(options) }; auto deploymentOperation{ m_packageManager.AddPackageByUriAsync(packageUri, addOptions) }; @@ -2016,25 +2024,6 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation winrt::Microsoft::Windows::Management::Deployment::AddPackageOptions const& options, winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentProgress& packageDeploymentProgress, wistd::function progress, - const double progressMaxPerPackageSetItem, - HRESULT& extendedError, - winrt::hstring& errorText, - winrt::guid& activityId) - { - extendedError = S_OK; - errorText.clear(); - activityId = winrt::guid{}; - - winrt::Windows::Management::Deployment::AddPackageOptions addOptions{ ToOptions(options) }; - RETURN_IF_FAILED(AddPackage(packageUri, addOptions, packageDeploymentProgress, progress, progressMaxPerPackageSetItem, extendedError, errorText, activityId)); - return S_OK; - } - - HRESULT PackageDeploymentManager::AddPackage( - winrt::Windows::Foundation::Uri const& packageUri, - winrt::Windows::Management::Deployment::AddPackageOptions const& addOptions, - winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentProgress& packageDeploymentProgress, - wistd::function progress, const double progressMaxPerPackage, HRESULT& extendedError, winrt::hstring& errorText, @@ -2044,6 +2033,13 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation errorText.clear(); activityId = winrt::guid{}; + if (::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()) + { + auto expectedDigests{ options.ExpectedDigests() }; + ValidatePackagesAndUpdateExpectedDigests(options.PackageValidators(), expectedDigests); + } + + winrt::Windows::Management::Deployment::AddPackageOptions addOptions{ ToOptions(options) }; const auto progressBefore{ packageDeploymentProgress.Progress }; auto deploymentOperation{ m_packageManager.AddPackageByUriAsync(packageUri, addOptions) }; deploymentOperation.Progress([&](winrt::Windows::Foundation::IAsyncOperationWithProgress< @@ -2111,25 +2107,6 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation winrt::Microsoft::Windows::Management::Deployment::StagePackageOptions const& options, winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentProgress& packageDeploymentProgress, wistd::function progress, - const double progressMaxPerPackageSetItem, - HRESULT& extendedError, - winrt::hstring& errorText, - winrt::guid& activityId) - { - extendedError = S_OK; - errorText.clear(); - activityId = winrt::guid{}; - - winrt::Windows::Management::Deployment::StagePackageOptions stageOptions{ ToOptions(options) }; - RETURN_IF_FAILED(StagePackage(packageUri, stageOptions, packageDeploymentProgress, progress, progressMaxPerPackageSetItem, extendedError, errorText, activityId)); - return S_OK; - } - - HRESULT PackageDeploymentManager::StagePackage( - winrt::Windows::Foundation::Uri const& packageUri, - winrt::Windows::Management::Deployment::StagePackageOptions const& stageOptions, - winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentProgress& packageDeploymentProgress, - wistd::function progress, const double progressMaxPerPackage, HRESULT& extendedError, winrt::hstring& errorText, @@ -2139,6 +2116,13 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation errorText.clear(); activityId = winrt::guid{}; + if (::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()) + { + auto expectedDigests{ options.ExpectedDigests() }; + ValidatePackagesAndUpdateExpectedDigests(options.PackageValidators(), expectedDigests); + } + + winrt::Windows::Management::Deployment::StagePackageOptions stageOptions{ ToOptions(options) }; const auto progressBefore{ packageDeploymentProgress.Progress }; auto deploymentOperation{ m_packageManager.StagePackageByUriAsync(packageUri, stageOptions) }; deploymentOperation.Progress([&](winrt::Windows::Foundation::IAsyncOperationWithProgress< @@ -3693,4 +3677,68 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation const auto schemeName{ packageUri.SchemeName() }; return CompareStringOrdinal(schemeName.c_str(), -1, L"ms-uup", -1, TRUE) == CSTR_EQUAL; } + + void PackageDeploymentManager::ValidatePackagesAndUpdateExpectedDigests( + winrt::Windows::Foundation::Collections::IMapView > const& packageValidators, + winrt::Windows::Foundation::Collections::IMap& expectedDigests + ) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + auto appxFactory{ wil::CoCreateInstance() }; + auto bundleFactory{ wil::CoCreateInstance() }; + + for (const auto pair : packageValidators) + { + const auto packageUri{ pair.Key() }; + const auto validators{ pair.Value() }; + if (!packageUri || !validators || validators.Size() == 0) + { + continue; + } + + auto expectedDigest{ expectedDigests.TryLookup(packageUri) }; + auto packageUriString{ packageUri.AbsoluteCanonicalUri().c_str() }; + auto expectedDigestString{ expectedDigest.has_value() ? expectedDigest.value().c_str() : nullptr }; + + winrt::com_ptr<::IInspectable> appxObject; + winrt::com_ptr packageReader; + winrt::com_ptr bundleReader; + winrt::com_ptr digestProvider; + + if (SUCCEEDED(appxFactory->CreatePackageReaderFromSourceUri(packageUriString, expectedDigestString, packageReader.put()))) + { + appxObject = winrt::make(packageReader.get()); + digestProvider = packageReader.as(); + } + else if (SUCCEEDED(bundleFactory->CreateBundleReaderFromSourceUri(packageUriString, expectedDigestString, bundleReader.put()))) + { + appxObject = winrt::make(bundleReader.get()); + digestProvider = bundleReader.as(); + } + else + { + THROW_WIN32(ERROR_INSTALL_OPEN_PACKAGE_FAILED); + } + + for (const auto validator : validators) + { + if (!validator) + { + continue; + } + + THROW_HR_IF(APPX_E_DIGEST_MISMATCH, !validator.IsPackageValid(appxObject.as())); + } + + if (!expectedDigestString) + { + wil::unique_cotaskmem_string digest; + THROW_IF_FAILED(digestProvider->GetDigest(&digest)); + + expectedDigests.Insert(packageUri, digest.get()); + } + } + } + } diff --git a/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.h b/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.h index 870dbacc6b..3179a3b949 100644 --- a/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.h +++ b/dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.h @@ -106,15 +106,6 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation HRESULT& extendedError, winrt::hstring& errorText, winrt::guid& activityId); - HRESULT AddPackage( - winrt::Windows::Foundation::Uri const& packageUri, - winrt::Windows::Management::Deployment::AddPackageOptions const& addOptions, - winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentProgress& packageDeploymentProgress, - wistd::function progress, - const double progressMaxPerPackage, - HRESULT& extendedError, - winrt::hstring& errorText, - winrt::guid& activityId); HRESULT StagePackage( winrt::Windows::Foundation::Uri const& packageUri, winrt::Microsoft::Windows::Management::Deployment::StagePackageOptions const& options, @@ -124,15 +115,6 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation HRESULT& extendedError, winrt::hstring& errorText, winrt::guid& activityId); - HRESULT StagePackage( - winrt::Windows::Foundation::Uri const& packageUri, - winrt::Windows::Management::Deployment::StagePackageOptions const& stageOptions, - winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentProgress& packageDeploymentProgress, - wistd::function progress, - const double progressMaxPerPackage, - HRESULT& extendedError, - winrt::hstring& errorText, - winrt::guid& activityId); HRESULT RegisterPackage( winrt::Windows::Foundation::Uri const& packageUri, winrt::Microsoft::Windows::Management::Deployment::RegisterPackageOptions const& options, @@ -299,6 +281,11 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation winrt::Microsoft::Windows::Management::Deployment::PackageSetItem const& packageSetItem); static bool IsUriScheme_MsUup(winrt::Windows::Foundation::Uri const& packageUri); + void ValidatePackagesAndUpdateExpectedDigests( + winrt::Windows::Foundation::Collections::IMapView > const& packageValidators, + winrt::Windows::Foundation::Collections::IMap& expectedDigests + ); + private: static size_t Count(winrt::Windows::Foundation::Collections::IIterable packages) { diff --git a/dev/PackageManager/API/M.W.M.D.PackageFamilyNameValidator.cpp b/dev/PackageManager/API/M.W.M.D.PackageFamilyNameValidator.cpp new file mode 100644 index 0000000000..0d98739bfc --- /dev/null +++ b/dev/PackageManager/API/M.W.M.D.PackageFamilyNameValidator.cpp @@ -0,0 +1,57 @@ +#include "pch.h" +#include "M.W.M.D.PackageFamilyNameValidator.h" +#include "Microsoft.Windows.Management.Deployment.PackageFamilyNameValidator.g.cpp" +#include + +#include + +namespace winrt::Microsoft::Windows::Management::Deployment::implementation +{ + PackageFamilyNameValidator::PackageFamilyNameValidator(hstring const& expectedPackageFamilyName) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + m_packageFamilyName = expectedPackageFamilyName; + } + + bool PackageFamilyNameValidator::IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + winrt::com_ptr packageReader; + winrt::com_ptr bundleReader; + + if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&packageReader)))) + { + winrt::com_ptr manifestReader; + THROW_IF_FAILED(packageReader->GetManifest(manifestReader.put())); + + winrt::com_ptr packageId; + THROW_IF_FAILED(manifestReader->GetPackageId(packageId.put())); + + return CheckIdentity(packageId.get()); + } + else if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&bundleReader)))) + { + winrt::com_ptr manifestReader; + THROW_IF_FAILED(bundleReader->GetManifest(manifestReader.put())); + + winrt::com_ptr packageId; + THROW_IF_FAILED(manifestReader->GetPackageId(packageId.put())); + + return CheckIdentity(packageId.get()); + } + else + { + THROW_WIN32(ERROR_NOT_SUPPORTED); + } + } + + bool PackageFamilyNameValidator::CheckIdentity(IAppxManifestPackageId* packageId) + { + wil::unique_cotaskmem_string familyName; + THROW_IF_FAILED(packageId->GetPackageFamilyName(&familyName)); + + return (CSTR_EQUAL == CompareStringOrdinal(familyName.get(), -1, m_packageFamilyName.c_str(), -1, true)); + } +} diff --git a/dev/PackageManager/API/M.W.M.D.PackageFamilyNameValidator.h b/dev/PackageManager/API/M.W.M.D.PackageFamilyNameValidator.h new file mode 100644 index 0000000000..72e00a1a21 --- /dev/null +++ b/dev/PackageManager/API/M.W.M.D.PackageFamilyNameValidator.h @@ -0,0 +1,27 @@ +#pragma once +#include "Microsoft.Windows.Management.Deployment.PackageFamilyNameValidator.g.h" + +#include + +namespace winrt::Microsoft::Windows::Management::Deployment::implementation +{ + struct PackageFamilyNameValidator : PackageFamilyNameValidatorT + { + PackageFamilyNameValidator() = default; + + PackageFamilyNameValidator(hstring const& expectedPackageFamilyName); + bool IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject); + + private: + bool CheckIdentity(IAppxManifestPackageId* packageId); + + hstring m_packageFamilyName; + }; +} + +namespace winrt::Microsoft::Windows::Management::Deployment::factory_implementation +{ + struct PackageFamilyNameValidator : PackageFamilyNameValidatorT + { + }; +} diff --git a/dev/PackageManager/API/M.W.M.D.PackageMinimumVersionValidator.cpp b/dev/PackageManager/API/M.W.M.D.PackageMinimumVersionValidator.cpp new file mode 100644 index 0000000000..dc1986fdb6 --- /dev/null +++ b/dev/PackageManager/API/M.W.M.D.PackageMinimumVersionValidator.cpp @@ -0,0 +1,55 @@ +#include "pch.h" +#include "M.W.M.D.PackageMinimumVersionValidator.h" +#include "Microsoft.Windows.Management.Deployment.PackageMinimumVersionValidator.g.cpp" +#include + +namespace winrt::Microsoft::Windows::Management::Deployment::implementation +{ + PackageMinimumVersionValidator::PackageMinimumVersionValidator(winrt::Windows::ApplicationModel::PackageVersion const& minimumVersion) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + m_minimumVersion = ::AppModel::Identity::PackageVersion{ minimumVersion }.Version; + } + + bool PackageMinimumVersionValidator::IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject) + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + winrt::com_ptr packageReader; + winrt::com_ptr bundleReader; + + if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&packageReader)))) + { + winrt::com_ptr manifestReader; + THROW_IF_FAILED(packageReader->GetManifest(manifestReader.put())); + + winrt::com_ptr packageId; + THROW_IF_FAILED(manifestReader->GetPackageId(packageId.put())); + + return CheckIdentity(packageId.get()); + } + else if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&bundleReader)))) + { + winrt::com_ptr manifestReader; + THROW_IF_FAILED(bundleReader->GetManifest(manifestReader.put())); + + winrt::com_ptr packageId; + THROW_IF_FAILED(manifestReader->GetPackageId(packageId.put())); + + return CheckIdentity(packageId.get()); + } + else + { + THROW_WIN32(ERROR_NOT_SUPPORTED); + } + } + + bool PackageMinimumVersionValidator::CheckIdentity(IAppxManifestPackageId* packageId) + { + UINT64 version; + THROW_IF_FAILED(packageId->GetVersion(&version)); + + return (version >= m_minimumVersion); + } +} diff --git a/dev/PackageManager/API/M.W.M.D.PackageMinimumVersionValidator.h b/dev/PackageManager/API/M.W.M.D.PackageMinimumVersionValidator.h new file mode 100644 index 0000000000..45ad4f3c2a --- /dev/null +++ b/dev/PackageManager/API/M.W.M.D.PackageMinimumVersionValidator.h @@ -0,0 +1,27 @@ +#pragma once +#include "Microsoft.Windows.Management.Deployment.PackageMinimumVersionValidator.g.h" + +#include + +namespace winrt::Microsoft::Windows::Management::Deployment::implementation +{ + struct PackageMinimumVersionValidator : PackageMinimumVersionValidatorT + { + PackageMinimumVersionValidator() = default; + + PackageMinimumVersionValidator(winrt::Windows::ApplicationModel::PackageVersion const& minimumVersion); + bool IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject); + + private: + bool CheckIdentity(IAppxManifestPackageId* packageId); + + UINT64 m_minimumVersion; + }; +} + +namespace winrt::Microsoft::Windows::Management::Deployment::factory_implementation +{ + struct PackageMinimumVersionValidator : PackageMinimumVersionValidatorT + { + }; +} diff --git a/dev/PackageManager/API/M.W.M.D.StagePackageOptions.cpp b/dev/PackageManager/API/M.W.M.D.StagePackageOptions.cpp index dfd7810dff..afcd22c477 100644 --- a/dev/PackageManager/API/M.W.M.D.StagePackageOptions.cpp +++ b/dev/PackageManager/API/M.W.M.D.StagePackageOptions.cpp @@ -4,6 +4,7 @@ #include "pch.h" #include +#include #include "M.W.M.D.StagePackageOptions.h" #include "Microsoft.Windows.Management.Deployment.StagePackageOptions.g.cpp" @@ -127,4 +128,35 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation } return m_expectedDigests; } + + bool StagePackageOptions::IsPackageValidatorsSupported() + { + return WindowsVersion::SupportsIAppxFactory4(); + } + winrt::Windows::Foundation::Collections::IMapView > StagePackageOptions::PackageValidators() + { + THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled()); + + if (!m_packageValidators) + { + // return an empty view + return winrt::single_threaded_map >().GetView(); + } + else + { + return m_packageValidators.GetView(); + } + } + void StagePackageOptions::AddPackageValidator(winrt::Windows::Foundation::Uri const& packageUri, winrt::Microsoft::Windows::Management::Deployment::IPackageValidator const& validator) + { + if (!m_packageValidators) + { + m_packageValidators = winrt::single_threaded_map >(); + } + if (!m_packageValidators.HasKey(packageUri)) + { + m_packageValidators.Insert(packageUri, winrt::single_threaded_vector()); + } + m_packageValidators.Lookup(packageUri).Append(validator); + } } diff --git a/dev/PackageManager/API/M.W.M.D.StagePackageOptions.h b/dev/PackageManager/API/M.W.M.D.StagePackageOptions.h index 386be0cf3a..309b805595 100644 --- a/dev/PackageManager/API/M.W.M.D.StagePackageOptions.h +++ b/dev/PackageManager/API/M.W.M.D.StagePackageOptions.h @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation and Contributors. +// Copyright (c) Microsoft Corporation and Contributors. // Licensed under the MIT License. #pragma once @@ -35,6 +35,9 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation void AllowUnsigned(bool value); bool IsExpectedDigestsSupported(); winrt::Windows::Foundation::Collections::IMap ExpectedDigests(); + bool IsPackageValidatorsSupported(); + winrt::Windows::Foundation::Collections::IMapView > PackageValidators(); + void AddPackageValidator(winrt::Windows::Foundation::Uri const&, winrt::Microsoft::Windows::Management::Deployment::IPackageValidator const&); private: winrt::Microsoft::Windows::Management::Deployment::PackageVolume m_targetVolume{ nullptr }; @@ -51,6 +54,7 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation bool m_stageInPlace{}; bool m_allowUnsigned{}; winrt::Windows::Foundation::Collections::IMap m_expectedDigests; + winrt::Windows::Foundation::Collections::IMap > m_packageValidators; }; } namespace winrt::Microsoft::Windows::Management::Deployment::factory_implementation diff --git a/dev/PackageManager/API/PackageManager.idl b/dev/PackageManager/API/PackageManager.idl index 1a2a0e27b2..109995d12b 100644 --- a/dev/PackageManager/API/PackageManager.idl +++ b/dev/PackageManager/API/PackageManager.idl @@ -7,7 +7,7 @@ import "M.AM.DynamicDependency.idl"; namespace Microsoft.Windows.Management.Deployment { - [contractversion(2)] + [contractversion(3)] apicontract PackageDeploymentContract{}; /// Features can be queried if currently available/enabled. @@ -171,6 +171,34 @@ namespace Microsoft.Windows.Management.Deployment IVector Items { get; }; } + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + interface IPackageValidator + { + Boolean IsPackageValid(IInspectable appxPackagingObject); + } + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + runtimeclass PackageFamilyNameValidator : [default] IPackageValidator + { + PackageFamilyNameValidator(String expectedPackageFamilyName); + } + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + runtimeclass PackageMinimumVersionValidator : [default] IPackageValidator + { + PackageMinimumVersionValidator(Windows.ApplicationModel.PackageVersion minimumVersion); + } + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + runtimeclass PackageCertificateEkuValidator : [default] IPackageValidator + { + PackageCertificateEkuValidator(String expectedCertificateEku); + } + // Requires Windows >= 10.0.19041.0 (aka 2004 aka 20H1) [contract(PackageDeploymentContract, 1)] runtimeclass AddPackageOptions @@ -200,6 +228,18 @@ namespace Microsoft.Windows.Management.Deployment Boolean IsLimitToExistingPackagesSupported { get; }; // Requires Windows >= 10.0.22621.0 (aka Win11 22H2) Boolean LimitToExistingPackages; + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + Boolean IsPackageValidatorsSupported{ get; }; + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + IMapView > PackageValidators{ get; }; + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + void AddPackageValidator(Windows.Foundation.Uri packageUri, IPackageValidator validator); } // Requires Windows >= 10.0.19041.0 (aka 2004 aka 20H1) @@ -224,6 +264,18 @@ namespace Microsoft.Windows.Management.Deployment Boolean IsExpectedDigestsSupported { get; }; // Requires Windows >= 10.0.22621.0 (aka Win11 22H2) IMap ExpectedDigests{ get; }; + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + Boolean IsPackageValidatorsSupported{ get; }; + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + IMapView > PackageValidators{ get; }; + + [feature(Feature_PackageValidator)] + [contract(PackageDeploymentContract, 3)] + void AddPackageValidator(Windows.Foundation.Uri packageUri, IPackageValidator validator); } // Requires Windows >= 10.0.19041.0 (aka 2004 aka 20H1) diff --git a/dev/PackageManager/API/PackageManager.vcxitems b/dev/PackageManager/API/PackageManager.vcxitems index 7b67a3efa0..9873a2de9a 100644 --- a/dev/PackageManager/API/PackageManager.vcxitems +++ b/dev/PackageManager/API/PackageManager.vcxitems @@ -16,8 +16,11 @@ + + + @@ -31,10 +34,14 @@ + + + + @@ -56,4 +63,4 @@ - + \ No newline at end of file diff --git a/dev/PackageManager/API/PackageManager.vcxitems.filters b/dev/PackageManager/API/PackageManager.vcxitems.filters index e41fd8b76e..7b29981e2c 100644 --- a/dev/PackageManager/API/PackageManager.vcxitems.filters +++ b/dev/PackageManager/API/PackageManager.vcxitems.filters @@ -56,6 +56,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -106,8 +115,20 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + - + \ No newline at end of file