Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion dev/Common/IsWindowsVersion.h
Original file line number Diff line number Diff line change
@@ -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 <VersionHelpers.h>
#include <wil/com.h>
#include <AppxPackaging.h>

namespace WindowsVersion
{
Expand Down Expand Up @@ -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<AppxFactory, IAppxFactory4>().get() != nullptr;
}

}

#endif // __ISWINDOWSVERSION_H
12 changes: 10 additions & 2 deletions dev/Common/TerminalVelocityFeatures-PackageManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
11 changes: 11 additions & 0 deletions dev/Common/TerminalVelocityFeatures-PackageManager.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,15 @@
<channelToken>Stable</channelToken>
</alwaysDisabledChannelTokens>
</feature>

<feature>
<name>Feature_PackageValidator</name>
<description>PackageValidator support in PackageDeploymentManager</description>
<state>AlwaysEnabled</state>
<alwaysDisabledChannelTokens>
<channelToken>Preview</channelToken>
<channelToken>Stable</channelToken>
</alwaysDisabledChannelTokens>
</feature>

</features>
25 changes: 25 additions & 0 deletions dev/PackageManager/API/AppxPackagingObject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <winrt/base.h>
#include <wil/com.h>
#include <AppxPackaging.h>

namespace winrt::Microsoft::Windows::Management::Deployment::implementation
{
struct AppxPackagingObject : winrt::implements<AppxPackagingObject, IInspectable>
{
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<IUnknown> m_object;
};

}
32 changes: 32 additions & 0 deletions dev/PackageManager/API/M.W.M.D.AddPackageOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "pch.h"

#include <IsWindowsVersion.h>
#include <TerminalVelocityFeatures-PackageManager.h>

#include "M.W.M.D.AddPackageOptions.h"
#include "Microsoft.Windows.Management.Deployment.AddPackageOptions.g.cpp"
Expand Down Expand Up @@ -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<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator> > 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<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator> >().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<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator> >();
}
if (!m_packageValidators.HasKey(packageUri))
{
m_packageValidators.Insert(packageUri, winrt::single_threaded_vector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator>());
}
m_packageValidators.Lookup(packageUri).Append(validator);
}
}
6 changes: 5 additions & 1 deletion dev/PackageManager/API/M.W.M.D.AddPackageOptions.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#pragma once
Expand Down Expand Up @@ -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<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator> > 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 };
Expand All @@ -67,6 +70,7 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation
bool m_deferRegistrationWhenPackagesAreInUse{};
winrt::Windows::Foundation::Collections::IMap<winrt::Windows::Foundation::Uri, hstring> m_expectedDigests;
bool m_limitToExistingPackages{};
winrt::Windows::Foundation::Collections::IMap<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator> > m_packageValidators;
};
}
namespace winrt::Microsoft::Windows::Management::Deployment::factory_implementation
Expand Down
195 changes: 195 additions & 0 deletions dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#include "pch.h"
#include "M.W.M.D.PackageCertificateEkuValidator.h"
#include "Microsoft.Windows.Management.Deployment.PackageCertificateEkuValidator.g.cpp"
#include <TerminalVelocityFeatures-PackageManager.h>

#include <AppxPackaging.h>

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<IAppxPackageReader> packageReader;
winrt::com_ptr<IAppxBundleReader> bundleReader;

if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&packageReader))))
{
winrt::com_ptr<IAppxFile> 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<IAppxFile> 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<IStream> 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<BYTE[]>(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<DWORD*>(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<BYTE[]>(signerInfoSize) };
THROW_IF_NULL_ALLOC(signerInfoBuffer);
signerInfo = reinterpret_cast<CMSG_SIGNER_INFO*>(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<BYTE[]>(ekuBufferSize)};
THROW_IF_NULL_ALLOC(ekuBuffer);
PCERT_ENHKEY_USAGE ekusFound = reinterpret_cast<PCERT_ENHKEY_USAGE>(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<WCHAR[]>(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
}

}
27 changes: 27 additions & 0 deletions dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once
#include "Microsoft.Windows.Management.Deployment.PackageCertificateEkuValidator.g.h"

#include <AppxPackaging.h>

namespace winrt::Microsoft::Windows::Management::Deployment::implementation
{
struct PackageCertificateEkuValidator : PackageCertificateEkuValidatorT<PackageCertificateEkuValidator>
{
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<PackageCertificateEkuValidator, implementation::PackageCertificateEkuValidator>
{
};
}
Loading