master
cpp 123 lines 3.62 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 WindowsCertStore.cpp
8
9 Abstract:
10
11 Implementation of helpers for reading the Windows certificate stores.
12
13 --*/
14
15 #include "precomp.h"
16 #include "WindowsCertStore.h"
17 #include <optional>
18 #include <set>
19 #include <wincrypt.h>
20
21 namespace {
22
23 // Converts a single DER-encoded certificate into a PEM block.
24 // Returns nullopt if the certificate cannot be encoded.
25 std::optional<std::string> TryEncodeCertificateAsPem(const CERT_CONTEXT& Cert)
26 {
27 DWORD pemSize = 0;
28 if (!CryptBinaryToStringA(Cert.pbCertEncoded, Cert.cbCertEncoded, CRYPT_STRING_BASE64HEADER, nullptr, &pemSize))
29 {
30 LOG_LAST_ERROR_MSG("CryptBinaryToStringA (size query) failed for a root certificate; skipping it");
31 return std::nullopt;
32 }
33
34 std::string pem(pemSize, '\0');
35 if (!CryptBinaryToStringA(Cert.pbCertEncoded, Cert.cbCertEncoded, CRYPT_STRING_BASE64HEADER, pem.data(), &pemSize))
36 {
37 LOG_LAST_ERROR_MSG("CryptBinaryToStringA (encode) failed for a root certificate; skipping it");
38 return std::nullopt;
39 }
40
41 // The pemSize after the actual write call does not include terminating null,
42 // and is 1 less than the value returned by the earlier query call.
43 pem.resize(pemSize);
44
45 return pem;
46 }
47
48 // Enumerates every certificate in the given "ROOT" system store and appends each
49 // (deduplicated by cert thumbprint) to the output PEM bundle. Returns number of skipped certs.
50 int AppendRootStore(DWORD StoreFlags, std::set<std::string>& Seen, std::string& Pem)
51 {
52 const wil::unique_hcertstore store{CertOpenStore(
53 CERT_STORE_PROV_SYSTEM_W, 0, NULL, StoreFlags | CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, L"ROOT")};
54 if (!store)
55 {
56 LOG_LAST_ERROR_MSG("CertOpenStore failed for ROOT store (flags 0x%x)", StoreFlags);
57 return 0;
58 }
59
60 // N.B. CertEnumCertificatesInStore frees the context passed to it and returns the next one,
61 // so the loop must not free the context itself.
62 int skippedCount = 0;
63 PCCERT_CONTEXT cert = nullptr;
64 while ((cert = CertEnumCertificatesInStore(store.get(), cert)) != nullptr)
65 {
66 if (cert->cbCertEncoded == 0)
67 {
68 continue;
69 }
70
71 // Use cert Thumbprint for dedupe.
72 BYTE hash[20];
73 DWORD hashSize = sizeof(hash);
74 if (!CertGetCertificateContextProperty(cert, CERT_SHA1_HASH_PROP_ID, hash, &hashSize))
75 {
76 LOG_LAST_ERROR_MSG("CertGetCertificateContextProperty(CERT_SHA1_HASH_PROP_ID) failed; skipping a root certificate");
77 skippedCount++;
78 continue;
79 }
80
81 if (!Seen.insert(std::string{reinterpret_cast<const char*>(hash), hashSize}).second)
82 {
83 continue;
84 }
85
86 if (auto pem = TryEncodeCertificateAsPem(*cert))
87 {
88 Pem += *pem;
89 }
90 else
91 {
92 skippedCount++;
93 }
94 }
95
96 return skippedCount;
97 }
98
99 } // namespace
100
101 namespace wsl::windows::service::wslc {
102
103 std::string CollectTrustedRootCertificatesPem()
104 {
105 std::set<std::string> seen;
106 std::string pem;
107
108 auto skippedCount = AppendRootStore(CERT_SYSTEM_STORE_LOCAL_MACHINE, seen, pem);
109 if (skippedCount > 0)
110 {
111 EMIT_USER_WARNING(wsl::shared::Localization::MessageWslcImportCertsSkippedComputer(std::to_wstring(skippedCount)));
112 }
113
114 skippedCount = AppendRootStore(CERT_SYSTEM_STORE_CURRENT_USER, seen, pem);
115 if (skippedCount > 0)
116 {
117 EMIT_USER_WARNING(wsl::shared::Localization::MessageWslcImportCertsSkippedUser(std::to_wstring(skippedCount)));
118 }
119
120 return pem;
121 }
122
123 } // namespace wsl::windows::service::wslc