MsixInfo.cpp (32194B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "Public/AppInstallerMsixInfo.h" 5 #include "HttpStream/HttpRandomAccessStream.h" 6 #include "Public/AppInstallerDownloader.h" 7 #include "Public/AppInstallerLogging.h" 8 #include "Public/AppInstallerStrings.h" 9 #include "Public/AppInstallerDownloader.h" 10 #include "Public/AppInstallerRuntime.h" 11 12 using namespace winrt::Windows::Storage::Streams; 13 using namespace Microsoft::WRL; 14 using namespace AppInstaller::Utility::HttpStream; 15 using namespace winrt::Windows::Management::Deployment; 16 17 namespace AppInstaller::Msix 18 { 19 namespace 20 { 21 // MSIX-specific header placed in the P7X file, before the actual signature 22 const byte P7xFileId[] = { 0x50, 0x4b, 0x43, 0x58 }; 23 const DWORD P7xFileIdSize = sizeof(P7xFileId); 24 25 // Gets the version from the manifest reader. 26 UINT64 GetVersionFromManifestReader(IAppxManifestReader* reader) 27 { 28 ComPtr<IAppxManifestPackageId> packageId; 29 THROW_IF_FAILED(reader->GetPackageId(&packageId)); 30 31 UINT64 result = 0; 32 THROW_IF_FAILED(packageId->GetVersion(&result)); 33 34 return result; 35 } 36 37 // Gets the UINT64 version from the version struct. 38 UINT64 GetVersionFromVersion(const winrt::Windows::ApplicationModel::PackageVersion& version) 39 { 40 UINT64 result = version.Major; 41 result = (result << 16) | version.Minor; 42 result = (result << 16) | version.Build; 43 result = (result << 16) | version.Revision; 44 45 return result; 46 } 47 48 // Writes the stream (from current location) to the given file. 49 void WriteStreamToFile(IStream* stream, UINT64 expectedSize, const std::filesystem::path& target, IProgressCallback& progress) 50 { 51 std::filesystem::path tempFile = target; 52 tempFile += ".dnld"; 53 54 { 55 std::ofstream file(tempFile, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); 56 57 constexpr ULONG bufferSize = 1 << 20; 58 std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bufferSize); 59 60 UINT64 totalBytesRead = 0; 61 62 while (!progress.IsCancelledBy(CancelReason::Any)) 63 { 64 ULONG bytesRead = 0; 65 HRESULT hr = stream->Read(buffer.get(), bufferSize, &bytesRead); 66 67 if (bytesRead) 68 { 69 // If we got bytes, just accept them and keep going. 70 LOG_IF_FAILED(hr); 71 72 THROW_HR_IF_MSG(E_UNEXPECTED, expectedSize && totalBytesRead + bytesRead > expectedSize, "Read more bytes than expected size"); 73 74 file.write(buffer.get(), bytesRead); 75 totalBytesRead += bytesRead; 76 progress.OnProgress(totalBytesRead, expectedSize, ProgressType::Bytes); 77 } 78 else 79 { 80 // If given a size, and we have read it all, quit 81 if (expectedSize && totalBytesRead == expectedSize) 82 { 83 break; 84 } 85 86 // If the stream returned an error, throw it 87 THROW_IF_FAILED(hr); 88 89 // If we were given a size and didn't reach it, throw our own error; 90 // otherwise assume that this is just normal EOF. 91 if (expectedSize) 92 { 93 THROW_WIN32(ERROR_HANDLE_EOF); 94 } 95 else 96 { 97 break; 98 } 99 } 100 } 101 } 102 103 std::filesystem::path backupFile = target; 104 backupFile += ".bkup"; 105 if (std::filesystem::exists(target)) 106 { 107 if (std::filesystem::exists(backupFile)) 108 { 109 std::filesystem::remove(backupFile); 110 } 111 std::filesystem::rename(target, backupFile); 112 } 113 114 std::filesystem::rename(tempFile, target); 115 } 116 117 // Writes the appx file to the given file. 118 void WriteAppxFileToFile(IAppxFile* appxFile, const std::filesystem::path& target, IProgressCallback& progress) 119 { 120 UINT64 size = 0; 121 THROW_IF_FAILED(appxFile->GetSize(&size)); 122 123 ComPtr<IStream> stream; 124 THROW_IF_FAILED(appxFile->GetStream(&stream)); 125 126 WriteStreamToFile(stream.Get(), size, target, progress); 127 } 128 129 // Writes the stream (from current location) to the given file handle. 130 void WriteStreamToFileHandle(IStream* stream, UINT64 expectedSize, HANDLE target, IProgressCallback& progress) 131 { 132 constexpr ULONG bufferSize = 1 << 20; 133 std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bufferSize); 134 135 UINT64 totalBytesRead = 0; 136 137 while (!progress.IsCancelledBy(CancelReason::Any)) 138 { 139 ULONG bytesRead = 0; 140 HRESULT hr = stream->Read(buffer.get(), bufferSize, &bytesRead); 141 142 if (bytesRead) 143 { 144 // If we got bytes, just accept them and keep going. 145 LOG_IF_FAILED(hr); 146 147 THROW_HR_IF_MSG(E_UNEXPECTED, expectedSize && totalBytesRead + bytesRead > expectedSize, "Read more bytes than expected size"); 148 149 DWORD bytesWritten = 0; 150 THROW_LAST_ERROR_IF(!WriteFile(target, buffer.get(), bytesRead, &bytesWritten, nullptr)); 151 THROW_HR_IF(E_UNEXPECTED, bytesRead != bytesWritten); 152 totalBytesRead += bytesRead; 153 progress.OnProgress(totalBytesRead, expectedSize, ProgressType::Bytes); 154 } 155 else 156 { 157 // If given a size, and we have read it all, quit 158 if (expectedSize && totalBytesRead == expectedSize) 159 { 160 break; 161 } 162 163 // If the stream returned an error, throw it 164 THROW_IF_FAILED(hr); 165 166 // If we were given a size and didn't reach it, throw our own error; 167 // otherwise assume that this is just normal EOF. 168 if (expectedSize) 169 { 170 THROW_WIN32(ERROR_HANDLE_EOF); 171 } 172 else 173 { 174 break; 175 } 176 } 177 } 178 } 179 180 // Writes the appx file to the given file handle. 181 void WriteAppxFileToFileHandle(IAppxFile* appxFile, HANDLE target, IProgressCallback& progress) 182 { 183 UINT64 size = 0; 184 THROW_IF_FAILED(appxFile->GetSize(&size)); 185 186 ComPtr<IStream> stream; 187 THROW_IF_FAILED(appxFile->GetStream(&stream)); 188 189 WriteStreamToFileHandle(stream.Get(), size, target, progress); 190 } 191 192 bool ValidateMsixTrustInfo(const std::filesystem::path& msixPath, bool verifyMicrosoftOrigin) 193 { 194 bool result = false; 195 AICLI_LOG(Core, Info, << "Started trust validation of msix at: " << msixPath); 196 197 try 198 { 199 bool verifyChainResult = false; 200 201 // First verify certificate chain if requested. 202 if (verifyMicrosoftOrigin) 203 { 204 auto [certContext, certStore] = GetCertContextFromMsix(msixPath); 205 206 // Get certificate chain context for validation 207 CERT_CHAIN_PARA certChainParameters = { 0 }; 208 certChainParameters.cbSize = sizeof(CERT_CHAIN_PARA); 209 certChainParameters.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; 210 DWORD certChainFlags = CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL; 211 212 wil::unique_cert_chain_context certChainContext; 213 THROW_LAST_ERROR_IF(!CertGetCertificateChain( 214 HCCE_LOCAL_MACHINE, 215 certContext.get(), 216 NULL, // Use the current system time for CRL validation 217 certStore.get(), 218 &certChainParameters, 219 certChainFlags, 220 NULL, // Reserved parameter; must be NULL 221 &certChainContext)); 222 223 // Validate that the certificate chain is rooted in one of the well-known Microsoft root certs 224 CERT_CHAIN_POLICY_PARA policyParameters = { 0 }; 225 policyParameters.cbSize = sizeof(CERT_CHAIN_POLICY_PARA); 226 policyParameters.dwFlags = MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG; 227 CERT_CHAIN_POLICY_STATUS policyStatus = { 0 }; 228 policyStatus.cbSize = sizeof(CERT_CHAIN_POLICY_STATUS); 229 LPCSTR policyOid = CERT_CHAIN_POLICY_MICROSOFT_ROOT; 230 BOOL certChainVerifySucceeded = CertVerifyCertificateChainPolicy( 231 policyOid, 232 certChainContext.get(), 233 &policyParameters, 234 &policyStatus); 235 236 AICLI_LOG(Core, Info, << "Result for certificate chain validation of Microsoft origin: " << policyStatus.dwError); 237 238 verifyChainResult = certChainVerifySucceeded && policyStatus.dwError == ERROR_SUCCESS; 239 } 240 else 241 { 242 verifyChainResult = true; 243 } 244 245 // If certificate chain origin validation is success or not requested, then validate the trust info of the file. 246 if (verifyChainResult) 247 { 248 // Set up the structures needed for the WinVerifyTrust call 249 WINTRUST_FILE_INFO fileInfo = { 0 }; 250 fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO); 251 fileInfo.pcwszFilePath = msixPath.c_str(); 252 253 WINTRUST_DATA trustData = { 0 }; 254 trustData.cbStruct = sizeof(WINTRUST_DATA); 255 trustData.dwUIChoice = WTD_UI_NONE; 256 trustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; 257 trustData.dwUnionChoice = WTD_CHOICE_FILE; 258 trustData.dwStateAction = WTD_STATEACTION_VERIFY; 259 trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL; 260 trustData.pFile = &fileInfo; 261 262 GUID verifyActionId = WINTRUST_ACTION_GENERIC_VERIFY_V2; 263 264 HRESULT verifyTrustResult = static_cast<HRESULT>(WinVerifyTrust(static_cast<HWND>(INVALID_HANDLE_VALUE), &verifyActionId, &trustData)); 265 AICLI_LOG(Core, Info, << "Result for trust info validation of the msix: " << verifyTrustResult); 266 267 result = verifyTrustResult == S_OK; 268 } 269 } 270 catch (const wil::ResultException& re) 271 { 272 AICLI_LOG(Core, Error, << "Failed during msix trust validation. Error: " << re.GetErrorCode()); 273 result = false; 274 } 275 catch (...) 276 { 277 AICLI_LOG(Core, Error, << "Failed during msix trust validation."); 278 result = false; 279 } 280 281 return result; 282 } 283 } 284 285 bool GetBundleReader( 286 IStream* inputStream, 287 IAppxBundleReader** reader) 288 { 289 ComPtr<IAppxBundleFactory> bundleFactory; 290 291 // Create a new Appxbundle factory 292 THROW_IF_FAILED(CoCreateInstance( 293 __uuidof(AppxBundleFactory), 294 nullptr, 295 CLSCTX_INPROC_SERVER, 296 __uuidof(IAppxBundleFactory), 297 (LPVOID*)(&bundleFactory))); 298 299 HRESULT hr = bundleFactory->CreateBundleReader(inputStream, reader); 300 301 if (SUCCEEDED(hr)) 302 { 303 return true; 304 } 305 else if (hr == APPX_E_MISSING_REQUIRED_FILE) 306 { 307 // APPX_E_MISSING_REQUIRED_FILE returned when trying to open 308 // an *.msix as an *.msixbundle or vice-versa. 309 return false; 310 } 311 else 312 { 313 THROW_HR(hr); 314 } 315 } 316 317 bool GetPackageReader( 318 IStream* inputStream, 319 IAppxPackageReader** reader) 320 { 321 ComPtr<IAppxFactory> appxFactory; 322 323 // Create a new Appx factory 324 THROW_IF_FAILED(CoCreateInstance( 325 __uuidof(AppxFactory), 326 nullptr, 327 CLSCTX_INPROC_SERVER, 328 __uuidof(IAppxFactory), 329 (LPVOID*)(&appxFactory))); 330 331 // Create a new package reader using the factory. 332 HRESULT hr = appxFactory->CreatePackageReader(inputStream, reader); 333 334 if (SUCCEEDED(hr)) 335 { 336 return true; 337 } 338 else if (hr == APPX_E_MISSING_REQUIRED_FILE) 339 { 340 // APPX_E_MISSING_REQUIRED_FILE returned when trying to open 341 // an *.msix as an *.msixbundle or vice-versa. 342 return false; 343 } 344 else 345 { 346 THROW_HR(hr); 347 } 348 } 349 350 void GetManifestReader( 351 IStream* inputStream, 352 IAppxManifestReader** reader) 353 { 354 ComPtr<IAppxFactory> appxFactory; 355 356 THROW_IF_FAILED(CoCreateInstance( 357 __uuidof(AppxFactory), 358 nullptr, 359 CLSCTX_INPROC_SERVER, 360 __uuidof(IAppxFactory), 361 (LPVOID*)(&appxFactory))); 362 363 THROW_IF_FAILED(appxFactory->CreateManifestReader(inputStream, reader)); 364 } 365 366 std::optional<std::string> GetPackageFullNameFromFamilyName(std::string_view familyName) 367 { 368 PackageManager packageManager; 369 370 std::wstring pfn = Utility::ConvertToUTF16(familyName); 371 372 // PackageManager.FindPackages() can find all packages (including provisioned ones) but requires admin. 373 // For non admin callers, use FindPackagesByPackageFamily where only packages registered to current user will be found. 374 if (Runtime::IsRunningAsAdmin()) 375 { 376 auto packages = packageManager.FindPackages(pfn); 377 378 std::optional<std::string> result; 379 for (const auto& package : packages) 380 { 381 if (result.has_value()) 382 { 383 // More than 1 package found. Don't directly error, let caller deal with it. 384 AICLI_LOG(Core, Error, << "Multiple packages found for family name: " << familyName); 385 return {}; 386 } 387 388 result = Utility::ConvertToUTF8(package.Id().FullName()); 389 } 390 391 return result; 392 } 393 else 394 { 395 UINT32 fullNameCount = 0; 396 UINT32 bufferLength = 0; 397 UINT32 properties = 0; 398 LONG findResult = FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &fullNameCount, nullptr, &bufferLength, nullptr, &properties); 399 if (findResult == ERROR_SUCCESS || fullNameCount == 0) 400 { 401 // No package found 402 return {}; 403 } 404 else if (findResult != ERROR_INSUFFICIENT_BUFFER) 405 { 406 THROW_WIN32(findResult); 407 } 408 else if (fullNameCount != 1) 409 { 410 // Don't directly error, let caller deal with it 411 AICLI_LOG(Core, Error, << "Multiple packages found for family name: " << fullNameCount); 412 return {}; 413 } 414 415 // fullNameCount == 1 at this point 416 PWSTR fullNamePtr; 417 std::wstring buffer(static_cast<size_t>(bufferLength) + 1, '\0'); 418 THROW_IF_WIN32_ERROR(FindPackagesByPackageFamily(pfn.c_str(), PACKAGE_FILTER_HEAD, &fullNameCount, &fullNamePtr, &bufferLength, &buffer[0], &properties)); 419 if (fullNameCount != 1 || bufferLength == 0) 420 { 421 // Something changed in between, abandon 422 AICLI_LOG(Core, Error, << "Packages found for family name: " << fullNameCount); 423 return {}; 424 } 425 buffer.resize(bufferLength - 1); 426 return Utility::ConvertToUTF8(buffer); 427 } 428 } 429 430 std::string GetPackageFamilyNameFromFullName(std::string_view fullName) 431 { 432 std::wstring result; 433 result.resize(PACKAGE_FAMILY_NAME_MAX_LENGTH + 1); 434 UINT32 size = static_cast<UINT32>(result.size()); 435 THROW_IF_WIN32_ERROR(PackageFamilyNameFromFullName(Utility::ConvertToUTF16(fullName).c_str(), &size, &result[0])); 436 result.resize(size - 1); 437 return Utility::ConvertToUTF8(result); 438 } 439 440 std::optional<std::filesystem::path> GetPackageLocationFromFullName(std::string_view fullName) 441 { 442 std::wstring fn = Utility::ConvertToUTF16(fullName); 443 444 UINT32 length = 0; 445 LONG returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, nullptr); 446 if (returnVal != ERROR_INSUFFICIENT_BUFFER) 447 { 448 LOG_WIN32(returnVal); 449 return {}; 450 } 451 452 THROW_HR_IF(E_UNEXPECTED, length == 0); 453 454 std::wstring result; 455 result.resize(length); 456 457 returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, &result[0]); 458 if (returnVal != ERROR_SUCCESS) 459 { 460 LOG_WIN32(returnVal); 461 return {}; 462 } 463 464 result.resize(length - 1); 465 return { result }; 466 } 467 468 Msix::PackageIdInfo GetPackageIdInfoFromFullName(std::string_view fullName) 469 { 470 std::wstring fullNameWide = Utility::ConvertToUTF16(fullName); 471 472 UINT32 length = 0; 473 LONG returnVal = PackageIdFromFullName(fullNameWide.c_str(), PACKAGE_INFORMATION_BASIC, &length, nullptr); 474 if (returnVal != ERROR_INSUFFICIENT_BUFFER) 475 { 476 LOG_WIN32(returnVal); 477 return {}; 478 } 479 480 THROW_HR_IF(E_UNEXPECTED, length == 0); 481 482 std::unique_ptr<BYTE[]> packageIdContent = std::make_unique<BYTE[]>(length); 483 484 returnVal = PackageIdFromFullName(fullNameWide.c_str(), PACKAGE_INFORMATION_BASIC, &length, packageIdContent.get()); 485 if (returnVal != ERROR_SUCCESS) 486 { 487 LOG_WIN32(returnVal); 488 return {}; 489 } 490 491 PACKAGE_ID* packageId = (PACKAGE_ID*)packageIdContent.get(); 492 493 return { Utility::ConvertToUTF8(packageId->name), packageId->version.Version }; 494 } 495 496 GetCertContextResult GetCertContextFromMsix(const std::filesystem::path& msixPath) 497 { 498 // Retrieve raw signature from msix 499 MsixInfo msixInfo{ msixPath }; 500 auto signature = msixInfo.GetSignature(true); 501 502 // Get the cert content 503 wil::unique_any<HCRYPTMSG, decltype(&::CryptMsgClose), ::CryptMsgClose> signedMessage; 504 wil::unique_hcertstore certStore; 505 CRYPT_DATA_BLOB signatureBlob = { 0 }; 506 signatureBlob.cbData = static_cast<DWORD>(signature.size()); 507 signatureBlob.pbData = signature.data(); 508 THROW_LAST_ERROR_IF(!CryptQueryObject( 509 CERT_QUERY_OBJECT_BLOB, 510 &signatureBlob, 511 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED, 512 CERT_QUERY_FORMAT_FLAG_BINARY, 513 0, // Reserved parameter 514 NULL, // No encoding info needed 515 NULL, 516 NULL, 517 &certStore, 518 &signedMessage, 519 NULL)); 520 521 // Get the signer size and information from the signed data message 522 // The properties of the signer info will be used to uniquely identify the signing certificate in the certificate store 523 DWORD signerInfoSize = 0; 524 THROW_LAST_ERROR_IF(!CryptMsgGetParam( 525 signedMessage.get(), 526 CMSG_SIGNER_INFO_PARAM, 527 0, 528 NULL, 529 &signerInfoSize)); 530 531 // Check that the signer info size is within reasonable bounds; under the max length of a string for the issuer field 532 THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), !(signerInfoSize > 0 && signerInfoSize < STRSAFE_MAX_CCH)); 533 534 std::vector<byte> signerInfoBuffer; 535 signerInfoBuffer.resize(signerInfoSize); 536 THROW_LAST_ERROR_IF(!CryptMsgGetParam( 537 signedMessage.get(), 538 CMSG_SIGNER_INFO_PARAM, 539 0, 540 signerInfoBuffer.data(), 541 &signerInfoSize)); 542 543 // Get the signing certificate from the certificate store based on the issuer and serial number of the signer info 544 CMSG_SIGNER_INFO* signerInfo = reinterpret_cast<CMSG_SIGNER_INFO*>(signerInfoBuffer.data()); 545 CERT_INFO certInfo; 546 certInfo.Issuer = signerInfo->Issuer; 547 certInfo.SerialNumber = signerInfo->SerialNumber; 548 549 wil::unique_cert_context certContext; 550 certContext.reset(CertGetSubjectCertificateFromStore( 551 certStore.get(), 552 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 553 &certInfo)); 554 THROW_LAST_ERROR_IF(!certContext.get()); 555 556 return { std::move(certContext), std::move(certStore) }; 557 } 558 559 MsixInfo::MsixInfo(std::string_view uriStr) 560 { 561 m_stream = Utility::GetReadOnlyStreamFromURI(uriStr); 562 563 if (GetBundleReader(m_stream.Get(), &m_bundleReader)) 564 { 565 m_isBundle = true; 566 } 567 else if (GetPackageReader(m_stream.Get(), &m_packageReader)) 568 { 569 m_isBundle = false; 570 } 571 else 572 { 573 THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_INSTALL_OPEN_PACKAGE_FAILED), 574 "Failed to open uri as msix package or bundle. Uri: %hs", uriStr.data()); 575 } 576 } 577 578 std::vector<byte> MsixInfo::GetSignature(bool skipP7xFileId) 579 { 580 ComPtr<IAppxFile> signatureFile; 581 if (m_isBundle) 582 { 583 THROW_IF_FAILED(m_bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_SIGNATURE, &signatureFile)); 584 } 585 else 586 { 587 THROW_IF_FAILED(m_packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, &signatureFile)); 588 } 589 590 std::vector<byte> signatureContent; 591 DWORD signatureSize; 592 593 ComPtr<IStream> signatureStream; 594 THROW_IF_FAILED(signatureFile->GetStream(&signatureStream)); 595 596 STATSTG stat = { 0 }; 597 THROW_IF_FAILED(signatureStream->Stat(&stat, STATFLAG_NONAME)); 598 THROW_HR_IF(E_UNEXPECTED, stat.cbSize.HighPart != 0); // Signature size should be small 599 signatureSize = stat.cbSize.LowPart; 600 THROW_HR_IF(E_UNEXPECTED, signatureSize <= P7xFileIdSize); 601 602 if (skipP7xFileId) 603 { 604 // Validate msix signature header 605 byte headerBuffer[P7xFileIdSize]; 606 DWORD headerRead; 607 THROW_IF_FAILED(signatureStream->Read(headerBuffer, P7xFileIdSize, &headerRead)); 608 THROW_HR_IF_MSG(E_UNEXPECTED, headerRead != P7xFileIdSize, "Failed to read signature header"); 609 THROW_HR_IF_MSG(E_UNEXPECTED, !std::equal(P7xFileId, P7xFileId + P7xFileIdSize, headerBuffer), "Unexpected msix signature header"); 610 signatureSize -= P7xFileIdSize; 611 } 612 613 signatureContent.resize(signatureSize); 614 615 DWORD signatureRead; 616 THROW_IF_FAILED(signatureStream->Read(signatureContent.data(), signatureSize, &signatureRead)); 617 THROW_HR_IF_MSG(E_UNEXPECTED, signatureRead != signatureSize, "Failed to read the whole signature stream"); 618 619 return signatureContent; 620 } 621 622 Utility::SHA256::HashBuffer MsixInfo::GetSignatureHash() 623 { 624 auto signature = GetSignature(); 625 return Utility::SHA256::ComputeHash(signature.data(), static_cast<uint32_t>(signature.size())); 626 } 627 628 std::wstring MsixInfo::GetDigest() 629 { 630 ComPtr<IAppxDigestProvider> digestProvider; 631 if (m_isBundle) 632 { 633 THROW_IF_FAILED(m_bundleReader.As(&digestProvider)); 634 } 635 else 636 { 637 THROW_IF_FAILED(m_packageReader.As(&digestProvider)); 638 } 639 640 wil::unique_cotaskmem_string result; 641 THROW_IF_FAILED(digestProvider->GetDigest(&result)); 642 643 return result.get(); 644 } 645 646 std::wstring MsixInfo::GetPackageFullNameWide() 647 { 648 ComPtr<IAppxManifestPackageId> packageId; 649 if (m_isBundle) 650 { 651 ComPtr<IAppxBundleManifestReader> manifestReader; 652 THROW_IF_FAILED(m_bundleReader->GetManifest(&manifestReader)); 653 THROW_IF_FAILED(manifestReader->GetPackageId(&packageId)); 654 } 655 else 656 { 657 ComPtr<IAppxManifestReader> manifestReader; 658 THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader)); 659 THROW_IF_FAILED(manifestReader->GetPackageId(&packageId)); 660 } 661 662 wil::unique_cotaskmem_string fullName; 663 THROW_IF_FAILED(packageId->GetPackageFullName(&fullName)); 664 665 return { fullName.get() }; 666 } 667 668 std::string MsixInfo::GetPackageFullName() 669 { 670 return Utility::ConvertToUTF8(GetPackageFullNameWide()); 671 } 672 673 std::vector<ComPtr<IAppxPackageReader>> MsixInfo::GetAppPackages(bool includeStub) const 674 { 675 if (!m_isBundle) 676 { 677 return { m_packageReader }; 678 } 679 680 std::vector<ComPtr<IAppxPackageReader>> packages; 681 682 ComPtr<IAppxBundleManifestReader> manifestReader; 683 THROW_IF_FAILED(m_bundleReader->GetManifest(&manifestReader)); 684 685 ComPtr<IAppxBundleManifestPackageInfoEnumerator> packageInfoItems; 686 THROW_IF_FAILED(manifestReader->GetPackageInfoItems(&packageInfoItems)); 687 688 BOOL hasCurrent = FALSE; 689 THROW_IF_FAILED(packageInfoItems->GetHasCurrent(&hasCurrent)); 690 while (hasCurrent) 691 { 692 ComPtr<IAppxBundleManifestPackageInfo> packageInfo; 693 THROW_IF_FAILED(packageInfoItems->GetCurrent(&packageInfo)); 694 695 APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE packageType; 696 THROW_IF_FAILED(packageInfo->GetPackageType(&packageType)); 697 698 // Check flat bundle case. 699 UINT64 offset; 700 THROW_IF_FAILED(packageInfo->GetOffset(&offset)); 701 bool isContained = offset != 0; 702 703 // Check stub package case. 704 ComPtr<IAppxBundleManifestPackageInfo4> packageInfo4; 705 THROW_IF_FAILED(packageInfo.As(&packageInfo4)); 706 BOOL isStub = FALSE; 707 THROW_IF_FAILED(packageInfo4->GetIsStub(&isStub)); 708 709 if (isContained && (includeStub || !isStub) && 710 packageType == APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE::APPX_BUNDLE_PAYLOAD_PACKAGE_TYPE_APPLICATION) 711 { 712 wil::unique_cotaskmem_string fileName; 713 THROW_IF_FAILED(packageInfo->GetFileName(&fileName)); 714 715 ComPtr<IAppxFile> packageFile; 716 THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileName.get(), &packageFile)); 717 718 ComPtr<IStream> stream; 719 THROW_IF_FAILED(packageFile->GetStream(&stream)); 720 721 ComPtr<IAppxPackageReader> packageReader; 722 if (GetPackageReader(stream.Get(), &packageReader)) 723 { 724 packages.emplace_back(std::move(packageReader)); 725 } 726 else 727 { 728 AICLI_LOG(Core, Warning, << "Could not get package reader for bundle payload."); 729 } 730 } 731 732 THROW_IF_FAILED(packageInfoItems->MoveNext(&hasCurrent)); 733 } 734 735 return packages; 736 } 737 738 std::vector<MsixPackageManifest> MsixInfo::GetAppPackageManifests(bool includeStub) const 739 { 740 std::vector<MsixPackageManifest> manifests; 741 auto packages = GetAppPackages(includeStub); 742 for (const auto& package : packages) 743 { 744 ComPtr<IAppxManifestReader> manifestReader; 745 THROW_IF_FAILED(package->GetManifest(&manifestReader)); 746 manifests.emplace_back(std::move(manifestReader)); 747 } 748 749 return manifests; 750 } 751 752 bool MsixInfo::IsNewerThan(const std::filesystem::path& otherPackage) 753 { 754 THROW_HR_IF(E_NOT_VALID_STATE, m_isBundle); 755 756 MsixInfo other{ otherPackage }; 757 758 THROW_HR_IF(E_INVALIDARG, other.m_isBundle); 759 760 ComPtr<IAppxManifestReader> otherReader; 761 THROW_IF_FAILED(other.m_packageReader->GetManifest(&otherReader)); 762 763 ComPtr<IAppxManifestReader> manifestReader; 764 THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader)); 765 766 return (GetVersionFromManifestReader(manifestReader.Get()) > GetVersionFromManifestReader(otherReader.Get())); 767 } 768 769 bool MsixInfo::IsNewerThan(const winrt::Windows::ApplicationModel::PackageVersion& otherVersion) 770 { 771 THROW_HR_IF(E_NOT_VALID_STATE, m_isBundle); 772 773 ComPtr<IAppxManifestReader> manifestReader; 774 THROW_IF_FAILED(m_packageReader->GetManifest(&manifestReader)); 775 776 return (GetVersionFromManifestReader(manifestReader.Get()) > GetVersionFromVersion(otherVersion)); 777 } 778 779 void MsixInfo::WriteToFile(std::string_view packageFile, const std::filesystem::path& target, IProgressCallback& progress) 780 { 781 std::wstring fileUTF16 = Utility::ConvertToUTF16(packageFile); 782 783 ComPtr<IAppxFile> appxFile; 784 if (m_isBundle) 785 { 786 THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileUTF16.c_str(), &appxFile)); 787 } 788 else 789 { 790 THROW_IF_FAILED(m_packageReader->GetPayloadFile(fileUTF16.c_str(), &appxFile)); 791 } 792 793 WriteAppxFileToFile(appxFile.Get(), target, progress); 794 } 795 796 void MsixInfo::WriteManifestToFile(const std::filesystem::path& target, IProgressCallback& progress) 797 { 798 ComPtr<IAppxFile> appxFile; 799 if (m_isBundle) 800 { 801 THROW_IF_FAILED(m_bundleReader->GetFootprintFile(APPX_BUNDLE_FOOTPRINT_FILE_TYPE_MANIFEST, &appxFile)); 802 } 803 else 804 { 805 THROW_IF_FAILED(m_packageReader->GetFootprintFile(APPX_FOOTPRINT_FILE_TYPE_MANIFEST, &appxFile)); 806 } 807 808 WriteAppxFileToFile(appxFile.Get(), target, progress); 809 } 810 811 void MsixInfo::WriteToFileHandle(std::string_view packageFile, HANDLE target, IProgressCallback& progress) 812 { 813 std::wstring fileUTF16 = Utility::ConvertToUTF16(packageFile); 814 815 ComPtr<IAppxFile> appxFile; 816 if (m_isBundle) 817 { 818 THROW_IF_FAILED(m_bundleReader->GetPayloadPackage(fileUTF16.c_str(), &appxFile)); 819 } 820 else 821 { 822 THROW_IF_FAILED(m_packageReader->GetPayloadFile(fileUTF16.c_str(), &appxFile)); 823 } 824 825 WriteAppxFileToFileHandle(appxFile.Get(), target, progress); 826 } 827 828 WriteLockedMsixFile::WriteLockedMsixFile(const std::filesystem::path& path) 829 { 830 m_file = Utility::ManagedFile::OpenWriteLockedFile(path, 0); 831 } 832 833 bool WriteLockedMsixFile::ValidateTrustInfo(bool checkMicrosoftOrigin) const 834 { 835 return ValidateMsixTrustInfo(m_file.GetFilePath(), checkMicrosoftOrigin); 836 } 837 }