Skip to content

Commit c9bc154

Browse files
committed
impl
1 parent c32be11 commit c9bc154

7 files changed

+252
-13
lines changed

dev/Common/IsWindowsVersion.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
// Copyright (c) Microsoft Corporation and Contributors.
1+
// Copyright (c) Microsoft Corporation and Contributors.
22
// Licensed under the MIT License.
33

44
#ifndef __ISWINDOWSVERSION_H
55
#define __ISWINDOWSVERSION_H
66

77
#include <VersionHelpers.h>
8+
#include <wil/com.h>
9+
#include <AppxPackaging.h>
810

911
namespace WindowsVersion
1012
{
@@ -62,6 +64,12 @@ inline bool IsWindows11_24H2OrGreater()
6264
// MsixIsPackageFeatureSupported() added to in Windows 11 24H2 (aka NTDDI_WIN11_GE)
6365
return IsExportPresent(L"appxdeploymentclient.dll", "MsixIsPackageFeatureSupported");
6466
}
67+
68+
inline bool SupportsIAppxFactory4()
69+
{
70+
return wil::CoCreateInstanceNoThrow<AppxFactory, IAppxFactory4>().get() != nullptr;
71+
}
72+
6573
}
6674

6775
#endif // __ISWINDOWSVERSION_H

dev/PackageManager/API/M.W.M.D.AddPackageOptions.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,7 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation
176176

177177
bool AddPackageOptions::IsPackageValidatorsSupported()
178178
{
179-
// TODO - check presence of AppxPackaging streaming reader feature
180-
return true;
179+
return WindowsVersion::SupportsIAppxFactory4();
181180
}
182181
winrt::Windows::Foundation::Collections::IMap<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator> > AddPackageOptions::PackageValidators()
183182
{

dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.cpp

Lines changed: 175 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,193 @@
33
#include "Microsoft.Windows.Management.Deployment.PackageCertificateEkuValidator.g.cpp"
44
#include <TerminalVelocityFeatures-PackageManager.h>
55

6+
#include <AppxPackaging.h>
7+
68
namespace winrt::Microsoft::Windows::Management::Deployment::implementation
79
{
810
PackageCertificateEkuValidator::PackageCertificateEkuValidator(hstring const& expectedCertificateEku)
911
{
1012
THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled());
1113

12-
UNREFERENCED_PARAMETER(expectedCertificateEku);
13-
14-
throw hresult_not_implemented();
14+
m_expectedEku = expectedCertificateEku;
1515
}
1616

1717
bool PackageCertificateEkuValidator::IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject)
1818
{
1919
THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled());
2020

21-
UNREFERENCED_PARAMETER(appxPackagingObject);
21+
winrt::com_ptr<IAppxPackageReader> packageReader;
22+
winrt::com_ptr<IAppxBundleReader> bundleReader;
2223

23-
throw hresult_not_implemented();
24+
if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&packageReader))))
25+
{
26+
winrt::com_ptr<IAppxFile> signatureFile;
27+
if (SUCCEEDED(packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, signatureFile.put())))
28+
{
29+
return CheckSignature(signatureFile.get());
30+
}
31+
else
32+
{
33+
return false; // package is not valid because there is no signature present
34+
}
35+
}
36+
else if (SUCCEEDED(appxPackagingObject.as(IID_PPV_ARGS(&bundleReader))))
37+
{
38+
winrt::com_ptr<IAppxFile> signatureFile;
39+
if (SUCCEEDED(bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_SIGNATURE, signatureFile.put())))
40+
{
41+
return CheckSignature(signatureFile.get());
42+
}
43+
else
44+
{
45+
return false; // package is not valid because there is no signature present
46+
}
47+
}
48+
else
49+
{
50+
THROW_WIN32(ERROR_NOT_SUPPORTED);
51+
}
2452
}
53+
54+
bool PackageCertificateEkuValidator::CheckSignature(IAppxFile* signatureFile)
55+
{
56+
winrt::com_ptr<IStream> signatureStream;
57+
THROW_IF_FAILED(signatureFile->GetStream(signatureStream.put()));
58+
59+
// The p7x signature should have a leading 4-byte PKCX header
60+
static const DWORD P7xFileId = 0x58434b50; // PKCX
61+
static const DWORD P7xFileIdSize = sizeof(P7xFileId);
62+
63+
STATSTG streamStats{};
64+
THROW_IF_FAILED(signatureStream->Stat(&streamStats, STATFLAG_NONAME));
65+
if (streamStats.cbSize.HighPart > 0 || streamStats.cbSize.LowPart < P7xFileIdSize)
66+
{
67+
return false; // The signature has unexpected size
68+
}
69+
70+
DWORD streamSize = streamStats.cbSize.LowPart;
71+
auto streamBuffer{ wil::make_unique_nothrow<BYTE[]>(streamSize) };
72+
THROW_IF_NULL_ALLOC(streamBuffer);
73+
74+
ULONG bytesRead{};
75+
THROW_IF_FAILED(signatureStream->Read(streamBuffer.get(), streamSize, &bytesRead));
76+
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_BAD_FORMAT), bytesRead != streamSize);
77+
78+
if (*reinterpret_cast<DWORD*>(streamBuffer.get()) != P7xFileId)
79+
{
80+
return false; // The signature does not have expected header
81+
}
82+
83+
CRYPT_DATA_BLOB signatureBlob{};
84+
signatureBlob.cbData = streamSize - P7xFileIdSize;
85+
signatureBlob.pbData = streamBuffer.get() + P7xFileIdSize;
86+
87+
wil::unique_hcertstore certStore;
88+
wil::unique_hcryptmsg signedMessage;
89+
DWORD queryContentType = 0;
90+
DWORD queryFormatType = 0;
91+
if (!CryptQueryObject(
92+
CERT_QUERY_OBJECT_BLOB,
93+
&signatureBlob,
94+
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
95+
CERT_QUERY_FORMAT_FLAG_BINARY,
96+
0, // Reserved parameter
97+
NULL, // No encoding info needed
98+
&queryContentType,
99+
&queryFormatType,
100+
&certStore,
101+
&signedMessage,
102+
NULL // No additional params for signed message output
103+
))
104+
{
105+
return false; // CryptQueryObject could not read the signature
106+
}
107+
108+
if (queryContentType != CERT_QUERY_CONTENT_PKCS7_SIGNED || queryFormatType != CERT_QUERY_FORMAT_BINARY || !certStore || !signedMessage)
109+
{
110+
return false; // CryptQueryObject returned unexpected data
111+
}
112+
113+
CMSG_SIGNER_INFO* signerInfo = NULL;
114+
DWORD signerInfoSize = 0;
115+
if (!CryptMsgGetParam(signedMessage.get(), CMSG_SIGNER_INFO_PARAM, 0, NULL, &signerInfoSize))
116+
{
117+
return false; // CryptMsgGetParam could not read the signature
118+
}
119+
120+
if (signerInfoSize == 0 || signerInfoSize >= STRSAFE_MAX_CCH)
121+
{
122+
return false; // Signer info has unexpected size
123+
}
124+
125+
auto signerInfoBuffer{ wil::make_unique_nothrow<BYTE[]>(signerInfoSize) };
126+
THROW_IF_NULL_ALLOC(signerInfoBuffer);
127+
signerInfo = reinterpret_cast<CMSG_SIGNER_INFO*>(signerInfoBuffer.get());
128+
if (!CryptMsgGetParam(signedMessage.get(), CMSG_SIGNER_INFO_PARAM, 0, signerInfo, &signerInfoSize))
129+
{
130+
return false; // CryptMsgGetParam could not read the signature
131+
}
132+
133+
// Get the signing certificate from the certificate store based on the issuer and serial number of the signer info
134+
CERT_INFO certInfo;
135+
certInfo.Issuer = signerInfo->Issuer;
136+
certInfo.SerialNumber = signerInfo->SerialNumber;
137+
138+
wil::unique_cert_context certContext{
139+
CertGetSubjectCertificateFromStore(
140+
certStore.get(),
141+
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
142+
&certInfo)
143+
};
144+
if (!certContext)
145+
{
146+
return false; // CertGetSubjectCertificateFromStore could not find a cert context
147+
}
148+
149+
DWORD ekuBufferSize = 0;
150+
if (!CertGetEnhancedKeyUsage(
151+
certContext.get(),
152+
0, // Accept EKU from EKU extension or EKU extended properties
153+
NULL,
154+
&ekuBufferSize))
155+
{
156+
return false; // CertGetEnhancedKeyUsage could not read EKUs
157+
}
158+
159+
if (ekuBufferSize < sizeof(CERT_ENHKEY_USAGE))
160+
{
161+
return false; // No EKUs are found in the signature
162+
}
163+
164+
auto ekuBuffer{ wil::make_unique_nothrow<BYTE[]>(ekuBufferSize)};
165+
THROW_IF_NULL_ALLOC(ekuBuffer);
166+
PCERT_ENHKEY_USAGE ekusFound = reinterpret_cast<PCERT_ENHKEY_USAGE>(ekuBuffer.get());
167+
if (!CertGetEnhancedKeyUsage(
168+
certContext.get(),
169+
0, // Accept EKU from EKU extension or EKU extended properties
170+
ekusFound,
171+
&ekuBufferSize))
172+
{
173+
return false; // CertGetEnhancedKeyUsage could not read EKUs
174+
}
175+
176+
for (DWORD i = 0; i < ekusFound->cUsageIdentifier; i++)
177+
{
178+
auto eku{ ekusFound->rgpszUsageIdentifier[i] };
179+
180+
int length = MultiByteToWideChar(CP_ACP, 0, eku, -1, nullptr, 0);
181+
auto ekuWcharBuffer{ wil::make_unique_nothrow<WCHAR[]>(length) };
182+
THROW_IF_NULL_ALLOC(ekuWcharBuffer);
183+
184+
MultiByteToWideChar(CP_ACP, 0, eku, -1, ekuWcharBuffer.get(), length);
185+
186+
if (wcscmp(ekuWcharBuffer.get(), m_expectedEku.c_str()) == 0)
187+
{
188+
return true; // Found expected EKU
189+
}
190+
}
191+
192+
return false; // Did not find expected EKU
193+
}
194+
25195
}

dev/PackageManager/API/M.W.M.D.PackageCertificateEkuValidator.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#pragma once
22
#include "Microsoft.Windows.Management.Deployment.PackageCertificateEkuValidator.g.h"
33

4+
#include <AppxPackaging.h>
5+
46
namespace winrt::Microsoft::Windows::Management::Deployment::implementation
57
{
68
struct PackageCertificateEkuValidator : PackageCertificateEkuValidatorT<PackageCertificateEkuValidator>
@@ -9,6 +11,11 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation
911

1012
PackageCertificateEkuValidator(hstring const& expectedCertificateEku);
1113
bool IsPackageValid(winrt::Windows::Foundation::IInspectable const& appxPackagingObject);
14+
15+
private:
16+
bool CheckSignature(IAppxFile* signatureFile);
17+
18+
hstring m_expectedEku{};
1219
};
1320
}
1421

dev/PackageManager/API/M.W.M.D.PackageDeploymentManager.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ namespace ABI::Windows::Management::Deployment
3232
#include <FrameworkUdk/PackageManagement.h>
3333
#include <FrameworkUdk/UupStateRepository.h>
3434

35+
#include <appxpackaging.h>
36+
#include <appxpackagingobject.h>
37+
3538
#include <IsWindowsVersion.h>
3639
#include <TerminalVelocityFeatures-PackageManager.h>
3740

@@ -3682,10 +3685,60 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation
36823685
{
36833686
THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidator::IsEnabled());
36843687

3685-
UNREFERENCED_PARAMETER(packageValidators);
3686-
UNREFERENCED_PARAMETER(expectedDigests);
3688+
auto appxFactory{ wil::CoCreateInstance<AppxFactory, IAppxFactory4, wil::err_exception_policy>() };
3689+
auto bundleFactory{ wil::CoCreateInstance<AppxBundleFactory, IAppxBundleFactory3, wil::err_exception_policy>() };
36873690

3688-
throw hresult_not_implemented();
3691+
for (const auto pair : packageValidators)
3692+
{
3693+
const auto packageUri{ pair.Key() };
3694+
const auto validators{ pair.Value() };
3695+
if (!packageUri || !validators || validators.Size() == 0)
3696+
{
3697+
continue;
3698+
}
3699+
3700+
auto expectedDigest{ expectedDigests.TryLookup(packageUri) };
3701+
auto packageUriString{ packageUri.AbsoluteCanonicalUri().c_str() };
3702+
auto expectedDigestString{ expectedDigest.has_value() ? expectedDigest.value().c_str() : nullptr };
3703+
3704+
winrt::com_ptr<::IInspectable> appxObject;
3705+
winrt::com_ptr<IAppxPackageReader> packageReader;
3706+
winrt::com_ptr<IAppxBundleReader> bundleReader;
3707+
winrt::com_ptr<IAppxDigestProvider> digestProvider;
3708+
3709+
if (SUCCEEDED(appxFactory->CreatePackageReaderFromSourceUri(packageUriString, expectedDigestString, packageReader.put())))
3710+
{
3711+
appxObject = winrt::make<AppxPackagingObject>(packageReader.get());
3712+
digestProvider = packageReader.as<IAppxDigestProvider>();
3713+
}
3714+
else if (SUCCEEDED(bundleFactory->CreateBundleReaderFromSourceUri(packageUriString, expectedDigestString, bundleReader.put())))
3715+
{
3716+
appxObject = winrt::make<AppxPackagingObject>(bundleReader.get());
3717+
digestProvider = bundleReader.as<IAppxDigestProvider>();
3718+
}
3719+
else
3720+
{
3721+
THROW_WIN32(ERROR_INSTALL_OPEN_PACKAGE_FAILED);
3722+
}
3723+
3724+
for (const auto validator : validators)
3725+
{
3726+
if (!validator)
3727+
{
3728+
continue;
3729+
}
3730+
3731+
THROW_HR_IF(APPX_E_DIGEST_MISMATCH, !validator.IsPackageValid(appxObject.as<IInspectable>()));
3732+
}
3733+
3734+
if (!expectedDigestString)
3735+
{
3736+
wil::unique_cotaskmem_string digest;
3737+
THROW_IF_FAILED(digestProvider->GetDigest(&digest));
3738+
3739+
expectedDigests.Insert(packageUri, digest.get());
3740+
}
3741+
}
36893742
}
36903743

36913744
}

dev/PackageManager/API/M.W.M.D.StagePackageOptions.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation
131131

132132
bool StagePackageOptions::IsPackageValidatorsSupported()
133133
{
134-
// TODO - check presence of AppxPackaging streaming reader feature
135-
return true;
134+
return WindowsVersion::SupportsIAppxFactory4();
136135
}
137136
winrt::Windows::Foundation::Collections::IMap<winrt::Windows::Foundation::Uri, winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Windows::Management::Deployment::IPackageValidator> > StagePackageOptions::PackageValidators()
138137
{

dev/PackageManager/API/PackageManager.vcxitems.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@
124124
<ClInclude Include="$(MSBuildThisFileDirectory)M.W.M.D.PackageMinimumVersionValidator.h">
125125
<Filter>Header Files</Filter>
126126
</ClInclude>
127+
<ClInclude Include="$(MSBuildThisFileDirectory)AppxPackagingObject.h">
128+
<Filter>Header Files</Filter>
129+
</ClInclude>
127130
</ItemGroup>
128131
<ItemGroup>
129132
<Midl Include="$(MSBuildThisFileDirectory)PackageManager.idl" />

0 commit comments

Comments
 (0)