Skip to content

Commit

Permalink
Windows implementation of Chrome Root Store
Browse files Browse the repository at this point in the history
This wires up the Chrome Root Store on Windows, including limited
support for interaction with user-added root certificates.

On Windows, the logical "ROOT" store [1] for the local machine
will also open up the AuthRoot store, which is Microsoft's builtin
root store. The goal here is not to import certificates that
Microsoft has trusted, and only import certificates that the user
or enterprise has manually trusted. To achieve this, rather than
opening up the logical "Root" (e.g. CERT_STORE_PROV_SYSTEM), the
individual physical stores are opened (e.g.
CERT_STORE_PROV_SYSTEM_REGISTRY) that make up the logical
store [2], excluding AuthRoot.

This only implements support for positive trust, for the moment,
and only for certificates, not for CTLs. It also does not support
negative trust, nor does it consider the trust purposes a root
is enabled for (e.g. only enabling for S/MIME).

[1] https://docs.microsoft.com/en-us/windows/win32/seccrypto/logical-and-physical-stores
[2] https://docs.microsoft.com/en-us/windows/win32/seccrypto/system-store-locations

Bug: 1233012
Change-Id: I282f453ff57b3f648f31bfb7cac8aa37e2171390
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3052905
Reviewed-by: Ryan Sleevi <rsleevi@chromium.org>
Reviewed-by: Mike Pinkerton <pinkerton@chromium.org>
Commit-Queue: Hubert Chao <hchao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#912276}
  • Loading branch information
Hubert Chao authored and Chromium LUCI CQ committed Aug 16, 2021
1 parent 83a2dae commit 78114c5
Show file tree
Hide file tree
Showing 15 changed files with 925 additions and 14 deletions.
23 changes: 18 additions & 5 deletions net/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1397,6 +1397,14 @@ component("net") {
"cert/internal/trust_store_chrome.h",
]
deps += [ "//net/data/ssl/chrome_root_store:gen_root_store_inc" ]
if (is_win) {
sources += [
"cert/internal/trust_store_win.cc",
"cert/internal/trust_store_win.h",
"third_party/mozilla_win/cert/win_util.cc",
"third_party/mozilla_win/cert/win_util.h",
]
}
}

if (use_nss_certs) {
Expand Down Expand Up @@ -4716,11 +4724,6 @@ test("net_unittests") {
]
}

if (chrome_root_store_supported) {
sources += [ "cert/internal/trust_store_chrome_unittest.cc" ]
deps += [ "//net/data/ssl/chrome_root_store:gen_root_store_test_inc" ]
}

if (!disable_brotli_filter) {
sources += [ "filter/brotli_source_stream_unittest.cc" ]
}
Expand Down Expand Up @@ -4756,6 +4759,16 @@ test("net_unittests") {
]
}

if (chrome_root_store_supported) {
sources += [ "cert/internal/trust_store_chrome_unittest.cc" ]
deps += [ "//net/data/ssl/chrome_root_store:gen_root_store_test_inc" ]

if (is_win) {
sources += [ "cert/internal/trust_store_win_unittest.cc" ]
libs += [ "crypt32.lib" ]
}
}

if (trial_comparison_cert_verifier_supported) {
sources += [ "cert/trial_comparison_cert_verifier_unittest.cc" ]
}
Expand Down
13 changes: 11 additions & 2 deletions net/cert/internal/parsed_certificate.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
Expand Down Expand Up @@ -105,14 +106,22 @@ class NET_EXPORT ParsedCertificate
return *signature_algorithm_;
}

// Returns the DER-encoded raw subject value (including the outer sequence
// tag). This is guaranteed to be valid DER, though the contents of unhandled
// string types are treated as raw bytes.
der::Input subject_tlv() const { return tbs_.subject_tlv; }
// Returns the DER-encoded normalized subject value (not including outer
// Sequence tag). This is gauranteed to be valid DER, though the contents of
// Sequence tag). This is guaranteed to be valid DER, though the contents of
// unhandled string types are treated as raw bytes.
der::Input normalized_subject() const {
return der::Input(&normalized_subject_);
}
// Returns the DER-encoded raw issuer value (including the outer sequence
// tag). This is guaranteed to be valid DER, though the contents of unhandled
// string types are treated as raw bytes.
der::Input issuer_tlv() const { return tbs_.issuer_tlv; }
// Returns the DER-encoded normalized issuer value (not including outer
// Sequence tag). This is gauranteed to be valid DER, though the contents of
// Sequence tag). This is guaranteed to be valid DER, though the contents of
// unhandled string types are treated as raw bytes.
der::Input normalized_issuer() const {
return der::Input(&normalized_issuer_);
Expand Down
25 changes: 25 additions & 0 deletions net/cert/internal/system_trust_store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#elif defined(OS_FUCHSIA)
#include "base/lazy_instance.h"
#include "third_party/boringssl/src/include/openssl/pool.h"
#elif defined(OS_WIN)
#include "net/cert/internal/trust_store_win.h"
#endif
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
#include "net/cert/internal/trust_store_chrome.h"
Expand Down Expand Up @@ -321,6 +323,29 @@ std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStoreChromeRoot() {
return std::make_unique<DummySystemTrustStore>();
}

#elif defined(OS_WIN)

// Using the Builtin Verifier w/o the Chrome Root Store is unsupported on
// Windows.
std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {
return std::make_unique<DummySystemTrustStore>();
}

#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)

std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStoreChromeRoot() {
return std::make_unique<SystemTrustStoreChrome>(
std::make_unique<TrustStoreChrome>(), TrustStoreWin::Create());
}

#else

std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStoreChromeRoot() {
return std::make_unique<DummySystemTrustStore>();
}

#endif // CHROME_ROOT_STORE_SUPPORTED

#else

std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {
Expand Down
142 changes: 142 additions & 0 deletions net/cert/internal/trust_store_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright (c) 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/cert/internal/trust_store_win.h"

#include "base/hash/sha1.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "net/cert/internal/cert_errors.h"
#include "net/cert/internal/parsed_certificate.h"
#include "net/cert/x509_util.h"
#include "net/third_party/mozilla_win/cert/win_util.h"

namespace net {

// TODO(https://crbug.com/1239258): import and use distrust settings.
// TODO(https://crbug.com/1239260): limit certs if they have EKU settings.
// TODO(https://crbug.com/1239268): support CTLs.
std::unique_ptr<TrustStoreWin> TrustStoreWin::Create() {
crypto::ScopedHCERTSTORE root_cert_store(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
crypto::ScopedHCERTSTORE intermediate_cert_store(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
crypto::ScopedHCERTSTORE all_certs_store(
CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, NULL, 0, nullptr));
if (!root_cert_store.get() || !intermediate_cert_store.get() ||
!all_certs_store.get()) {
return nullptr;
}

// Grab the user-added roots.
GatherEnterpriseCertsForLocation(root_cert_store.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE, L"ROOT");
GatherEnterpriseCertsForLocation(root_cert_store.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
L"ROOT");
GatherEnterpriseCertsForLocation(root_cert_store.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
L"ROOT");
GatherEnterpriseCertsForLocation(root_cert_store.get(),
CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT");
GatherEnterpriseCertsForLocation(root_cert_store.get(),
CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
L"ROOT");

// Grab the user-added intermediates.
GatherEnterpriseCertsForLocation(intermediate_cert_store.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE, L"CA");
GatherEnterpriseCertsForLocation(intermediate_cert_store.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
L"CA");
GatherEnterpriseCertsForLocation(intermediate_cert_store.get(),
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
L"CA");
GatherEnterpriseCertsForLocation(intermediate_cert_store.get(),
CERT_SYSTEM_STORE_CURRENT_USER, L"CA");
GatherEnterpriseCertsForLocation(intermediate_cert_store.get(),
CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY,
L"CA");

CertAddStoreToCollection(all_certs_store.get(), intermediate_cert_store.get(),
/*dwUpdateFlags=*/0, /*dwPriority=*/0);

CertAddStoreToCollection(all_certs_store.get(), root_cert_store.get(),
/*dwUpdateFlags=*/0, /*dwPriority=*/0);
return base::WrapUnique(new TrustStoreWin(std::move(root_cert_store),
std::move(all_certs_store)));
}

std::unique_ptr<TrustStoreWin> TrustStoreWin::CreateForTesting(
crypto::ScopedHCERTSTORE root_cert_store,
crypto::ScopedHCERTSTORE all_certs_store) {
return base::WrapUnique(new TrustStoreWin(std::move(root_cert_store),
std::move(all_certs_store)));
}

TrustStoreWin::TrustStoreWin(crypto::ScopedHCERTSTORE root_cert_store,
crypto::ScopedHCERTSTORE all_certs_store)
: root_cert_store_(std::move(root_cert_store)),
all_certs_store_(std::move(all_certs_store)) {}

TrustStoreWin::~TrustStoreWin() = default;

void TrustStoreWin::SyncGetIssuersOf(const ParsedCertificate* cert,
ParsedCertificateList* issuers) {
base::span<const uint8_t> issuer_span = cert->issuer_tlv().AsSpan();

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;
// TODO(https://crbug.com/1239270): figure out if this is thread-safe or if we
// need locking here
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(
cert_from_store->pbCertEncoded, cert_from_store->cbCertEncoded);
CertErrors errors;
ParsedCertificate::CreateAndAddToVector(
std::move(der_crypto), x509_util::DefaultParseCertificateOptions(),
issuers, &errors);
}
}

void TrustStoreWin::GetTrust(const scoped_refptr<ParsedCertificate>& cert,
CertificateTrust* trust,
base::SupportsUserData* debug_data) const {
base::span<const uint8_t> cert_span = cert->der_cert().AsSpan();
base::SHA1Digest cert_hash = base::SHA1HashSpan(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;

// TODO(https://crbug.com/1239270): figure out if this is thread-safe or if we
// need locking here
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 = base::make_span(
cert_from_store->pbCertEncoded, cert_from_store->cbCertEncoded);

if (base::ranges::equal(cert_span, cert_from_store_span)) {
// Found cert, yay!
*trust = CertificateTrust::ForTrustAnchor();
// Free before returning
CertFreeCertificateContext(cert_from_store);
return;
}
}

// Didn't find cert, return Unspecified Trust.
*trust = CertificateTrust::ForUnspecified();
}

} // namespace net
59 changes: 59 additions & 0 deletions net/cert/internal/trust_store_win.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_CERT_INTERNAL_TRUST_STORE_WIN_H_
#define NET_CERT_INTERNAL_TRUST_STORE_WIN_H_

#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/win/wincrypt_shim.h"
#include "crypto/scoped_capi_types.h"
#include "net/base/net_export.h"
#include "net/cert/internal/trust_store.h"

namespace net {

// TrustStoreWin is an implementation of TrustStore which uses the Windows cert
// systems to find user-added trust anchors for path building. It ignores the
// Windows builtin trust anchors. This TrustStore is thread-safe (we think).
// TODO(https://crbug.com/1239270): confirm this is thread safe.
class NET_EXPORT TrustStoreWin : public TrustStore {
public:
~TrustStoreWin() override;
TrustStoreWin(const TrustStoreWin& other) = delete;
TrustStoreWin& operator=(const TrustStoreWin& other) = delete;

// Creates a TrustStoreWin by reading user settings from Windows system
// CertStores. Returns nullptr on failure.
static std::unique_ptr<TrustStoreWin> Create();

// Creates a TrustStoreWin for testing, which will treat `root_cert_store`
// as if it's the source of truth for roots for `GetTrust,
// and `all_certs_store` as the store for locating certificates during
// `SyncGetIssuersOf`.
static std::unique_ptr<TrustStoreWin> CreateForTesting(
crypto::ScopedHCERTSTORE root_cert_store,
crypto::ScopedHCERTSTORE all_certs_store);

void SyncGetIssuersOf(const ParsedCertificate* cert,
ParsedCertificateList* issuers) override;

void GetTrust(const scoped_refptr<ParsedCertificate>& cert,
CertificateTrust* trust,
base::SupportsUserData* debug_data) const override;

private:
TrustStoreWin(crypto::ScopedHCERTSTORE root_cert_store,
crypto::ScopedHCERTSTORE all_certs_store);

// Cert Collection containing all user-added trust anchors.
crypto::ScopedHCERTSTORE root_cert_store_;

// Cert Collection for searching via SyncGetIssuersOf()
crypto::ScopedHCERTSTORE all_certs_store_;
};

} // namespace net

#endif // NET_CERT_INTERNAL_TRUST_STORE_WIN_H_
Loading

0 comments on commit 78114c5

Please sign in to comment.