-
Notifications
You must be signed in to change notification settings - Fork 395
API Spec: PackageValidator API in PackageManagement.md #5863
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ but with additional functionality, improved developer experience and performance | |
| - [3.10. PackageVolume Repair](#310-packagevolume-repair) | ||
| - [3.11. Usability](#311-usability) | ||
| - [3.12. Is\*Provisioned()](#312-312-isprovisioned) | ||
| - [3.13. PackageValidator](#313-packagevalidator) | ||
| - [4. Examples](#4-examples) | ||
| - [4.1. AddPackageAsync()](#41-addpackageasync) | ||
| - [4.2. AddPackageByUriAsync()](#42-addpackagebyuriasync) | ||
|
|
@@ -73,6 +74,7 @@ Additional functionality includes: | |
| * IsPackageRegistrationPending -- Is there an update waiting to register? | ||
| * PackageSets -- Batch operations | ||
| * PackageRuntimeManager -- Batch operations for use at runtime via Dynamic Dependencies | ||
| * PackageValidator -- Validate a package has expected identity, signature, etc. before adding/staging | ||
| * Usability -- Quality-of-Life enhancements | ||
|
|
||
| ## 3.1. API Structure | ||
|
|
@@ -398,6 +400,24 @@ Is\*Provisioned\*() methods determine if the target is provisioned. | |
|
|
||
| These methods require administrative privileges. | ||
|
|
||
| ## 3.13. PackageValidator | ||
|
|
||
| This API allows callers to verify that packages being processed by PackageDeploymentManager.AddPackageAsync | ||
| and PackageDeploymentManager.StagePackageAsync match what are expected from their URI. | ||
|
|
||
| When adding or staging a package from an external source such as HTTP URI or uncontrolled file location, | ||
| an attacker can potentially intercept and tamper with the package data being read, causing a malicious | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| package to be installed instead of the expected one. Verifying the identity and signature of target | ||
| packages helps ensure that such tampering has not happened. | ||
|
|
||
| The following PackageValidators are provided and available for use directly: | ||
| * PackageFamilyNameValidator: Validates that the package has the expected package family name. | ||
| * PackageMinimumVersionValidator: Validates that the package has at least the expected minimum version number. | ||
| * PackageCertificateEkuValidator: Validates that the certificate used to sign the package contains the expected Extended Key Usage (EKU) value. | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Custom validators can also be implemented using the IPackageValidator interface, which allows verifying | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| any part of the package’s footprint data (manifest, block map, and digital signature) in any desired manner. | ||
|
|
||
| # 4. Examples | ||
|
|
||
| ## 4.1. AddPackageAsync() | ||
|
|
@@ -761,6 +781,100 @@ PackageVersion ToVersion(uint major, uint minor, uint build, uint revision) => | |
| }; | ||
| ``` | ||
|
|
||
| ## 4.9. PackageValidator | ||
|
|
||
| ### 4.9.1. Using built-in PackageValidators | ||
|
|
||
| This example shows how to use built-in PackageValidators to verify package family name, minimum version, and certificate EKU. | ||
|
|
||
| ```c++/winrt | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| auto packageDeploymentManager{ winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentManager::GetDefault() }; | ||
| const auto packageUri{ L"https://example.com/package.msix"}; | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const winrt::hstring package{ packageUri.c_str() }; | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| IVector<IPackageValidator> validators{ winrt::single_threaded_vector<IPackageValidator>() }; | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| validators.Append(winrt::Microsoft::Windows::Management::Deployment::PackageFamilyNameValidator(L"ExpectedFamilyName_abcdefg123")); | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| validators.Append(winrt::Microsoft::Windows::Management::Deployment::PackageMinimumVersionValidator(2, 0, 0, 0)); | ||
| validators.Append(winrt::Microsoft::Windows::Management::Deployment::PackageCertificateEkuValidator(L"1.3.6.1.4.1.311.2.1.11"); | ||
|
|
||
| winrt::Microsoft::Windows::Management::Deployment::AddPackageOptions addOptions; | ||
| addOptions.PackageValidators.Insert(packageUri, validators); | ||
|
|
||
| auto deploymentOperation{ packageDeploymentManager.AddPackageAsync(package, addOptions) }; | ||
| auto deploymentResult{ WaitForDeploymentOperation(deploymentOperation) }; | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // deploymentOperation will fail with APPX_E_DIGEST_MISMATCH if any packageValidator rejects the package | ||
| ``` | ||
|
|
||
| ### 4.9.2. Using custom PackageValidators | ||
|
|
||
| This example shows how to implement a custom PackageValidator using IPackageValidator interface, and use it to verify a package. | ||
|
|
||
| ```idl | ||
| // MyCustom.idl | ||
| namespace MyCustom | ||
| { | ||
| runtimeclass MyPackageValidator : [default] Microsoft.Windows.Management.Deployment.IPackageValidator | ||
| { | ||
| Boolean IsPackageValid(AppxPackagingObject packagingObject); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ```c++/winrt | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Samples should be written in C# unless there's a need for other language(s) You should be able to do this in C# with C#/WinRT, but I'm not familiar with the syntax. Plus in this case the custom validator needs to interact with the Packaging API (e.g. IAppxPackageReader) which is COM. Can this be done in C#? I suspect so, but the details probably differ significantly from C++. If so that warrants C++ and C#, to illustrate how to do it despite the significant and not well known differences. RECOMMEND: Provide this example in C# and C++ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes the COM stuff should be doable in C# too, with some interop code. I'll add an equivalent C# example for the custom validator case once the API is implemented and I can confirm a working sample. [Pending] |
||
| // MyCustom.cpp | ||
| // (Assume other standard cppwinrt-generated code related to MyCustom.MyPackageValidator are present. | ||
| // This sample only shows the custom hand-written parts.) | ||
| namespace winrt::MyCustom::implementation | ||
| { | ||
| // Implements a custom package validator that checks that the package does not delcare any capabilities. | ||
| struct MyCustomPackageValidator : MyCustomPackageValidatorT<MyCustomPackageValidator> | ||
| { | ||
| MyCustomPackageValidator(); | ||
|
|
||
| bool IsPackageValid(winrt::Microsoft::Windows::Management::Deployment::AppxPackagingObject object) | ||
| { | ||
| winrt::com_ptr<IAppxPackageReader> packageReader; | ||
| if (FAILED(object.as<IAppxPackageReader>(packageReader.put()))) | ||
| { | ||
| // object is not an msix package as expected (i.e. it is a bundle), reject it | ||
| return false; | ||
| } | ||
|
|
||
| winrt::com_ptr<IAppxManifestReader> manifestReader; | ||
| THROW_IF_FAILED(packageReader->GetManifest(manifestReader.put())); | ||
|
|
||
| winrt::com_ptr<IAppxManifestReader3> manifestReader3; | ||
| THROW_IF_FAILED(manifestReader->QueryInterface(manifestReader3.put())); | ||
|
|
||
| winrt::com_ptr<IAppxManifestCapabilitiesEnumerator> capabilitiesEnumerator; | ||
| THROW_IF_FAILED(manifestReader3->GetCapabilitiesByCapabilityClass(APPX_CAPABILITY_CLASS_ALL, capabilitiesEnumerator.put())); | ||
|
|
||
| BOOL hasCapabilities{}; | ||
| THROW_IF_FAILED(capabilitiesEnumerator->GetHasCurrent(&hasCapabilities)); | ||
| return !hasCapabilities; | ||
| } | ||
| }; | ||
| } | ||
| ``` | ||
|
|
||
| ```c++/winrt | ||
| // At the call site to PackageDeploymentManager, using the custom package validator | ||
| auto packageDeploymentManager{ winrt::Microsoft::Windows::Management::Deployment::PackageDeploymentManager::GetDefault() }; | ||
|
|
||
| const auto packageUri{ L"https://example.com/package.msix"}; | ||
| const winrt::hstring package{ packageUri.c_str() }; | ||
|
|
||
| IVector<IPackageValidator> validators{ winrt::single_threaded_vector<IPackageValidator>() }; | ||
| validators.Append(winrt::MyCustom::MyPackageValidator()); | ||
|
|
||
| winrt::Microsoft::Windows::Management::Deployment::AddPackageOptions options; | ||
| options.PackageValidators.Insert(packageUri, validators); | ||
|
|
||
| auto deploymentOperation{ packageDeploymentManager.AddPackageAsync(package, options) }; | ||
| auto deploymentResult{ WaitForDeploymentOperation(deploymentOperation) }; | ||
| // deploymentOperation will fail with APPX_E_DIGEST_MISMATCH if any packageValidator rejects the package | ||
| ``` | ||
|
|
||
| # 5. Remarks | ||
|
|
||
| ## 5.1. Platform Support | ||
|
|
@@ -857,6 +971,41 @@ namespace Microsoft.Windows.Management.Deployment | |
| NewerAvailable = 2, | ||
| }; | ||
|
|
||
| [contract(PackageDeploymentContract, 3)] | ||
| [default_interface] | ||
| runtimeclass AppxPackagingObject{ | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // This is an interop class for COM types defined in AppxPackaging.idl. | ||
| // The WinRT side has no methods or properties, but the object supports QueryInterface into the COM interfaces | ||
| // IAppxPackageReader or IAppxBundleReader, whichever is relevant for the object being read. | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| [contract(PackageDeploymentContract, 3)] | ||
| interface IPackageValidator | ||
| { | ||
| Boolean IsPackageValid(AppxPackagingObject packagingObject); | ||
letao-msft marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| [contract(PackageDeploymentContract, 3)] | ||
| runtimeclass PackageFamilyNameValidator : [default] IPackageValidator | ||
| { | ||
| PackageFamilyNameValidator(String expectedPackageFamilyName); | ||
| Boolean IsPackageValid(AppxPackagingObject packagingObject); | ||
| } | ||
|
|
||
| [contract(PackageDeploymentContract, 3)] | ||
| runtimeclass PackageMinimumVersionValidator : [default] IPackageValidator | ||
| { | ||
| PackageMinimumVersionValidator(UINT major, UINT minor, UINT build, UINT revision); | ||
| Boolean IsPackageValid(AppxPackagingObject packagingObject); | ||
| } | ||
|
|
||
| [contract(PackageDeploymentContract, 3)] | ||
| runtimeclass PackageCertificateEkuValidator : [default] IPackageValidator | ||
| { | ||
| PackageCertificateEkuValidator(String expectedCertificateEku); | ||
| Boolean IsPackageValid(AppxPackagingObject packagingObject); | ||
| } | ||
|
|
||
| /// The progress status of the deployment request. | ||
| /// @see https://learn.microsoft.com/uwp/api/windows.management.deployment.deploymentprogress.state | ||
| [contract(PackageDeploymentContract, 1)] | ||
|
|
@@ -964,6 +1113,9 @@ namespace Microsoft.Windows.Management.Deployment | |
|
|
||
| Boolean IsLimitToExistingPackagesSupported { get; }; // Requires Windows >= 10.0.22621.0 (aka Win11 22H2) | ||
| Boolean LimitToExistingPackages; | ||
|
|
||
| Boolean IsPackageValidatorsSupported{ get; }; | ||
| IMap<Windows.Foundation.Uri, IVector<IPackageValidator>> PackageValidators{ get; }; | ||
| } | ||
|
|
||
| // Requires Windows >= 10.0.19041.0 (aka 2004 aka 20H1) | ||
|
|
@@ -988,6 +1140,9 @@ namespace Microsoft.Windows.Management.Deployment | |
|
|
||
| Boolean IsExpectedDigestsSupported { get; }; // Requires Windows >= 10.0.22621.0 (aka Win11 22H2) | ||
| IMap<Windows.Foundation.Uri, String> ExpectedDigests{ get; }; | ||
|
|
||
| Boolean IsPackageValidatorsSupported{ get; }; | ||
| IMap<Windows.Foundation.Uri, IVector<IPackageValidator>> PackageValidators{ get; }; | ||
| } | ||
|
|
||
| // Requires Windows >= 10.0.19041.0 (aka 2004 aka 20H1) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.