#include "net/cert/internal/trust_store_win.h"
#include <algorithm>
#include <string_view>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/containers/to_vector.h"
#include "base/hash/sha1.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "net/base/features.h"
#include "net/cert/x509_util.h"
#include "net/cert/x509_util_win.h"
#include "net/third_party/mozilla_win/cert/win_util.h"
#include "third_party/boringssl/src/pki/cert_errors.h"
#include "third_party/boringssl/src/pki/parsed_certificate.h"
namespace net {
namespace {
constexpr bssl::CertificateTrust kRootCertTrust =
bssl::CertificateTrust::ForTrustAnchorOrLeaf()
.WithEnforceAnchorExpiry()
.WithEnforceAnchorConstraints()
.WithRequireLeafSelfSigned();
constexpr bssl::CertificateTrust kTrustedPeopleTrust =
bssl::CertificateTrust::ForTrustedLeaf().WithRequireLeafSelfSigned();
bool IsCertTrustedForServerAuth(PCCERT_CONTEXT cert) {
DWORD usage_size = 0;
if (!CertGetEnhancedKeyUsage(cert, 0, nullptr, &usage_size)) {
return false;
}
std::vector<BYTE> usage_bytes(usage_size);
CERT_ENHKEY_USAGE* usage =
UNSAFE_TODO(reinterpret_cast<CERT_ENHKEY_USAGE*>(usage_bytes.data()));
if (!CertGetEnhancedKeyUsage(cert, 0, usage, &usage_size)) {
return false;
}
if (usage->cUsageIdentifier == 0) {
HRESULT error_code = GetLastError();
switch (error_code) {
case CRYPT_E_NOT_FOUND:
return true;
case S_OK:
return false;
default:
return false;
}
}
base::span<LPSTR> usage_identifiers = UNSAFE_BUFFERS(
base::span(usage->rgpszUsageIdentifier, usage->cUsageIdentifier));
for (std::string_view eku : usage_identifiers) {
if ((eku == szOID_PKIX_KP_SERVER_AUTH) ||
(eku == szOID_ANY_ENHANCED_KEY_USAGE)) {
return true;
}
}
return false;
}
void AddCertWithTrust(
PCCERT_CONTEXT cert,
const bssl::CertificateTrust trust,
std::vector<net::PlatformTrustStore::CertWithTrust>* certs) {
certs->push_back(net::PlatformTrustStore::CertWithTrust(
base::ToVector(x509_util::CertContextAsSpan(cert)), trust));
}
}
TrustStoreWin::CertStores::CertStores() = default;
TrustStoreWin::CertStores::~CertStores() = default;
TrustStoreWin::CertStores::CertStores(CertStores&& other) = default;
TrustStoreWin::CertStores& TrustStoreWin::CertStores::operator=(
CertStores&& other) = default;
TrustStoreWin::CertStores
TrustStoreWin::CertStores::CreateInMemoryStoresForTesting() {
TrustStoreWin::CertStores stores;
stores.roots = crypto::ScopedHCERTSTORE(CertOpenStore(
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
stores.intermediates = crypto::ScopedHCERTSTORE(CertOpenStore(
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
stores.trusted_people = crypto::ScopedHCERTSTORE(CertOpenStore(
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
stores.disallowed = crypto::ScopedHCERTSTORE(CertOpenStore(
CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, nullptr));
stores.InitializeAllCertsStore();
return stores;
}
TrustStoreWin::CertStores
TrustStoreWin::CertStores::CreateNullStoresForTesting() {
return TrustStoreWin::CertStores();
}
TrustStoreWin::CertStores TrustStoreWin::CertStores::CreateWithCollections() {
TrustStoreWin::CertStores stores;
stores.roots = crypto::ScopedHCERTSTORE(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
stores.intermediates = crypto::ScopedHCERTSTORE(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
stores.trusted_people = crypto::ScopedHCERTSTORE(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
stores.disallowed = crypto::ScopedHCERTSTORE(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
stores.InitializeAllCertsStore();
return stores;
}
void TrustStoreWin::CertStores::InitializeAllCertsStore() {
all = crypto::ScopedHCERTSTORE(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
if (is_null()) {
return;
}
if (!CertAddStoreToCollection(all.get(), intermediates.get(),
0, 0)) {
return;
}
if (!CertAddStoreToCollection(all.get(), roots.get(),
0, 0)) {
return;
}
}
class TrustStoreWin::Impl {
public:
Impl() {
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
CertStores stores = CertStores::CreateWithCollections();
if (stores.is_null()) {
return;
}
GatherEnterpriseCertsForLocation(stores.roots.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE, L"ROOT");
GatherEnterpriseCertsForLocation(
stores.roots.get(), CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
L"ROOT");
GatherEnterpriseCertsForLocation(stores.roots.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
L"ROOT");
GatherEnterpriseCertsForLocation(stores.roots.get(),
CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT");
GatherEnterpriseCertsForLocation(
stores.roots.get(), CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
L"ROOT");
GatherEnterpriseCertsForLocation(stores.intermediates.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE, L"CA");
GatherEnterpriseCertsForLocation(
stores.intermediates.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY, L"CA");
GatherEnterpriseCertsForLocation(stores.intermediates.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
L"CA");
GatherEnterpriseCertsForLocation(stores.intermediates.get(),
CERT_SYSTEM_STORE_CURRENT_USER, L"CA");
GatherEnterpriseCertsForLocation(
stores.intermediates.get(), CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
L"CA");
GatherEnterpriseCertsForLocation(stores.trusted_people.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"TrustedPeople");
GatherEnterpriseCertsForLocation(
stores.trusted_people.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY, L"TrustedPeople");
GatherEnterpriseCertsForLocation(stores.trusted_people.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
L"TrustedPeople");
GatherEnterpriseCertsForLocation(stores.disallowed.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE,
L"Disallowed");
GatherEnterpriseCertsForLocation(
stores.disallowed.get(), CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
L"Disallowed");
GatherEnterpriseCertsForLocation(stores.disallowed.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
L"Disallowed");
GatherEnterpriseCertsForLocation(
stores.disallowed.get(), CERT_SYSTEM_STORE_CURRENT_USER, L"Disallowed");
GatherEnterpriseCertsForLocation(
stores.disallowed.get(), CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
L"Disallowed");
if (!CertControlStore(stores.roots.get(), 0, CERT_STORE_CTRL_AUTO_RESYNC,
0) ||
!CertControlStore(stores.trusted_people.get(), 0,
CERT_STORE_CTRL_AUTO_RESYNC, 0) ||
!CertControlStore(stores.intermediates.get(), 0,
CERT_STORE_CTRL_AUTO_RESYNC, 0) ||
!CertControlStore(stores.disallowed.get(), 0,
CERT_STORE_CTRL_AUTO_RESYNC, 0)) {
PLOG(ERROR) << "Error enabling CERT_STORE_CTRL_AUTO_RESYNC";
}
root_cert_store_ = std::move(stores.roots);
intermediate_cert_store_ = std::move(stores.intermediates);
trusted_people_cert_store_ = std::move(stores.trusted_people);
disallowed_cert_store_ = std::move(stores.disallowed);
all_certs_store_ = std::move(stores.all);
}
Impl(CertStores stores)
: root_cert_store_(std::move(stores.roots)),
intermediate_cert_store_(std::move(stores.intermediates)),
all_certs_store_(std::move(stores.all)),
trusted_people_cert_store_(std::move(stores.trusted_people)),
disallowed_cert_store_(std::move(stores.disallowed)) {}
~Impl() = default;
Impl(const Impl& other) = delete;
Impl& operator=(const Impl& other) = delete;
void SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
bssl::ParsedCertificateList* issuers) {
if (!root_cert_store_.get() || !intermediate_cert_store_.get() ||
!trusted_people_cert_store_.get() || !all_certs_store_.get() ||
!disallowed_cert_store_.get()) {
return;
}
base::span<const uint8_t> issuer_span = cert->issuer_tlv();
CERT_NAME_BLOB cert_issuer_blob;
cert_issuer_blob.cbData = static_cast<DWORD>(issuer_span.size());
cert_issuer_blob.pbData = const_cast<uint8_t*>(issuer_span.data());
PCCERT_CONTEXT cert_from_store = nullptr;
while ((cert_from_store = CertFindCertificateInStore(
all_certs_store_.get(), X509_ASN_ENCODING, 0,
CERT_FIND_SUBJECT_NAME, &cert_issuer_blob, cert_from_store))) {
bssl::UniquePtr<CRYPTO_BUFFER> der_crypto = x509_util::CreateCryptoBuffer(
x509_util::CertContextAsSpan(cert_from_store));
bssl::CertErrors errors;
bssl::ParsedCertificate::CreateAndAddToVector(
std::move(der_crypto), x509_util::DefaultParseCertificateOptions(),
issuers, &errors);
}
}
bssl::CertificateTrust GetTrust(const bssl::ParsedCertificate* cert) {
if (!root_cert_store_.get() || !intermediate_cert_store_.get() ||
!trusted_people_cert_store_.get() || !all_certs_store_.get() ||
!disallowed_cert_store_.get()) {
return bssl::CertificateTrust::ForUnspecified();
}
base::span<const uint8_t> cert_span = cert->der_cert();
base::SHA1Digest cert_hash = base::SHA1Hash(cert_span);
CRYPT_HASH_BLOB cert_hash_blob;
cert_hash_blob.cbData = static_cast<DWORD>(cert_hash.size());
cert_hash_blob.pbData = cert_hash.data();
PCCERT_CONTEXT cert_from_store = nullptr;
while ((cert_from_store = CertFindCertificateInStore(
disallowed_cert_store_.get(), X509_ASN_ENCODING, 0,
CERT_FIND_SHA1_HASH, &cert_hash_blob, cert_from_store))) {
base::span<const uint8_t> cert_from_store_span =
x509_util::CertContextAsSpan(cert_from_store);
if (std::ranges::equal(cert_span, cert_from_store_span)) {
return bssl::CertificateTrust::ForDistrusted();
}
}
while ((cert_from_store = CertFindCertificateInStore(
root_cert_store_.get(), X509_ASN_ENCODING, 0,
CERT_FIND_SHA1_HASH, &cert_hash_blob, cert_from_store))) {
base::span<const uint8_t> cert_from_store_span =
x509_util::CertContextAsSpan(cert_from_store);
if (std::ranges::equal(cert_span, cert_from_store_span)) {
if (IsCertTrustedForServerAuth(cert_from_store)) {
return kRootCertTrust;
}
}
}
while ((cert_from_store = CertFindCertificateInStore(
trusted_people_cert_store_.get(), X509_ASN_ENCODING, 0,
CERT_FIND_SHA1_HASH, &cert_hash_blob, cert_from_store))) {
base::span<const uint8_t> cert_from_store_span =
x509_util::CertContextAsSpan(cert_from_store);
if (std::ranges::equal(cert_span, cert_from_store_span)) {
if (IsCertTrustedForServerAuth(cert_from_store)) {
return kTrustedPeopleTrust;
}
}
}
return bssl::CertificateTrust::ForUnspecified();
}
std::vector<net::PlatformTrustStore::CertWithTrust> GetAllUserAddedCerts() {
std::vector<net::PlatformTrustStore::CertWithTrust> certs;
if (!root_cert_store_.get() || !intermediate_cert_store_.get() ||
!trusted_people_cert_store_.get() || !all_certs_store_.get() ||
!disallowed_cert_store_.get()) {
return certs;
}
PCCERT_CONTEXT cert_from_store = nullptr;
while ((cert_from_store = CertEnumCertificatesInStore(
disallowed_cert_store_.get(), cert_from_store))) {
AddCertWithTrust(cert_from_store, bssl::CertificateTrust::ForDistrusted(),
&certs);
}
while ((cert_from_store = CertEnumCertificatesInStore(
trusted_people_cert_store_.get(), cert_from_store))) {
if (IsCertTrustedForServerAuth(cert_from_store)) {
AddCertWithTrust(cert_from_store, kTrustedPeopleTrust, &certs);
}
}
while ((cert_from_store = CertEnumCertificatesInStore(
root_cert_store_.get(), cert_from_store))) {
if (IsCertTrustedForServerAuth(cert_from_store)) {
AddCertWithTrust(cert_from_store, kRootCertTrust, &certs);
}
}
while ((cert_from_store = CertEnumCertificatesInStore(
intermediate_cert_store_.get(), cert_from_store))) {
AddCertWithTrust(cert_from_store,
bssl::CertificateTrust::ForUnspecified(), &certs);
}
return certs;
}
private:
crypto::ScopedHCERTSTORE root_cert_store_;
crypto::ScopedHCERTSTORE intermediate_cert_store_;
crypto::ScopedHCERTSTORE all_certs_store_;
crypto::ScopedHCERTSTORE trusted_people_cert_store_;
crypto::ScopedHCERTSTORE disallowed_cert_store_;
};
TrustStoreWin::TrustStoreWin() = default;
void TrustStoreWin::InitializeStores() {
MaybeInitializeAndGetImpl();
}
TrustStoreWin::Impl* TrustStoreWin::MaybeInitializeAndGetImpl() {
base::AutoLock lock(init_lock_);
if (!impl_) {
impl_ = std::make_unique<TrustStoreWin::Impl>();
}
return impl_.get();
}
std::unique_ptr<TrustStoreWin> TrustStoreWin::CreateForTesting(
CertStores stores) {
return base::WrapUnique(new TrustStoreWin(
std::make_unique<TrustStoreWin::Impl>(std::move(stores))));
}
TrustStoreWin::TrustStoreWin(std::unique_ptr<Impl> impl)
: impl_(std::move(impl)) {}
TrustStoreWin::~TrustStoreWin() = default;
void TrustStoreWin::SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
bssl::ParsedCertificateList* issuers) {
MaybeInitializeAndGetImpl()->SyncGetIssuersOf(cert, issuers);
}
bssl::CertificateTrust TrustStoreWin::GetTrust(
const bssl::ParsedCertificate* cert) {
return MaybeInitializeAndGetImpl()->GetTrust(cert);
}
std::vector<net::PlatformTrustStore::CertWithTrust>
TrustStoreWin::GetAllUserAddedCerts() {
return MaybeInitializeAndGetImpl()->GetAllUserAddedCerts();
}
}