Skip to content

Commit e29c732

Browse files
committed
impl rebased
1 parent 0cddda0 commit e29c732

26 files changed

+993
-108
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/Common/TerminalVelocityFeatures-PackageManager.h

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

44
// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT IT
55

66
// INPUT FILE: dev\common\TerminalVelocityFeatures-PackageManager.xml
7-
// OPTIONS: -Channel Experimental -Language C++ -Namespace Microsoft.Windows.Management.Deployment -Path dev\common\TerminalVelocityFeatures-PackageManager.xml -Output dev\common\TerminalVelocityFeatures-PackageManager.h
7+
// OPTIONS: -Channel Experimental -Language C++ -Namespace Microsoft::Windows::Management::Deployment -Path dev\common\TerminalVelocityFeatures-PackageManager.xml -Output dev\common
88

99
#if defined(__midlrt)
1010
namespace features
1111
{
1212
feature_name Feature_PackageManager = { DisabledByDefault, FALSE };
13+
feature_name Feature_PackageValidation = { DisabledByDefault, FALSE };
1314
}
1415
#endif // defined(__midlrt)
1516

1617
// Feature constants
1718
#define WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEMANAGER_ENABLED 1
19+
#define WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEVALIDATION_ENABLED 1
1820

1921
#if defined(__cplusplus)
2022

@@ -27,6 +29,12 @@ struct Feature_PackageManager
2729
static constexpr bool IsEnabled() { return WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEMANAGER_ENABLED == 1; }
2830
};
2931

30-
} // namespace Microsoft.Windows.Management.Deployment
32+
__pragma(detect_mismatch("ODR_violation_WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEVALIDATION_ENABLED_mismatch", "AlwaysEnabled"))
33+
struct Feature_PackageValidation
34+
{
35+
static constexpr bool IsEnabled() { return WINDOWSAPPRUNTIME_MICROSOFT_WINDOWS_MANAGEMENT_DEPLOYMENT_FEATURE_PACKAGEVALIDATION_ENABLED == 1; }
36+
};
37+
38+
} // namespace Microsoft::Windows::Management::Deployment
3139

3240
#endif // defined(__cplusplus)

dev/Common/TerminalVelocityFeatures-PackageManager.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,15 @@
1717
<channelToken>Stable</channelToken>
1818
</alwaysDisabledChannelTokens>
1919
</feature>
20+
21+
<feature>
22+
<name>Feature_PackageValidation</name>
23+
<description>PackageValidation support in PackageDeploymentManager</description>
24+
<state>AlwaysEnabled</state>
25+
<alwaysDisabledChannelTokens>
26+
<channelToken>Preview</channelToken>
27+
<channelToken>Stable</channelToken>
28+
</alwaysDisabledChannelTokens>
29+
</feature>
30+
2031
</features>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include <winrt/base.h>
4+
#include <wil/com.h>
5+
#include <AppxPackaging.h>
6+
7+
namespace winrt::Microsoft::Windows::Management::Deployment::implementation
8+
{
9+
struct AppxPackagingObject : winrt::implements<AppxPackagingObject, IInspectable>
10+
{
11+
AppxPackagingObject(IUnknown* object)
12+
{
13+
m_object.copy_from(object);
14+
}
15+
16+
int32_t query_interface_tearoff(winrt::guid const& id, void** object) const noexcept override
17+
{
18+
return m_object.as(id, object);
19+
}
20+
21+
private:
22+
winrt::com_ptr<IUnknown> m_object;
23+
};
24+
25+
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
#include "pch.h"
55

66
#include <IsWindowsVersion.h>
7+
#include <TerminalVelocityFeatures-PackageManager.h>
78

9+
#include "M.W.M.D.PackageValidationEventSource.h"
810
#include "M.W.M.D.AddPackageOptions.h"
911
#include "Microsoft.Windows.Management.Deployment.AddPackageOptions.g.cpp"
1012

@@ -172,4 +174,40 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation
172174
{
173175
m_limitToExistingPackages = value;
174176
}
177+
178+
bool AddPackageOptions::IsPackageValidationSupported()
179+
{
180+
return WindowsVersion::SupportsIAppxFactory4();
181+
}
182+
183+
winrt::Microsoft::Windows::Management::Deployment::PackageValidationEventSource AddPackageOptions::GetValidationEventSourceForUri(winrt::Windows::Foundation::Uri const& uri)
184+
{
185+
THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidation::IsEnabled());
186+
187+
if (!m_packageValidators)
188+
{
189+
m_packageValidators = winrt::single_threaded_map<winrt::Windows::Foundation::Uri, winrt::Microsoft::Windows::Management::Deployment::PackageValidationEventSource>();
190+
}
191+
if (!m_packageValidators.HasKey(uri))
192+
{
193+
m_packageValidators.Insert(uri, winrt::make<winrt::Microsoft::Windows::Management::Deployment::implementation::PackageValidationEventSource>());
194+
}
195+
196+
return m_packageValidators.Lookup(uri);
197+
}
198+
199+
winrt::Windows::Foundation::Collections::IMapView<winrt::Windows::Foundation::Uri, winrt::Microsoft::Windows::Management::Deployment::PackageValidationEventSource> AddPackageOptions::PackageValidators()
200+
{
201+
THROW_HR_IF(E_NOTIMPL, !::Microsoft::Windows::Management::Deployment::Feature_PackageValidation::IsEnabled());
202+
203+
if (!m_packageValidators)
204+
{
205+
// return an empty view
206+
return winrt::single_threaded_map<winrt::Windows::Foundation::Uri, winrt::Microsoft::Windows::Management::Deployment::PackageValidationEventSource>().GetView();
207+
}
208+
else
209+
{
210+
return m_packageValidators.GetView();
211+
}
212+
}
175213
}

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

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

44
#pragma once
@@ -46,6 +46,9 @@ namespace winrt::Microsoft::Windows::Management::Deployment::implementation
4646
bool IsLimitToExistingPackagesSupported();
4747
bool LimitToExistingPackages();
4848
void LimitToExistingPackages(bool value);
49+
bool IsPackageValidationSupported();
50+
winrt::Microsoft::Windows::Management::Deployment::PackageValidationEventSource GetValidationEventSourceForUri(winrt::Windows::Foundation::Uri const& uri);
51+
winrt::Windows::Foundation::Collections::IMapView<winrt::Windows::Foundation::Uri, winrt::Microsoft::Windows::Management::Deployment::PackageValidationEventSource> PackageValidators();
4952

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

0 commit comments

Comments
 (0)