From ea3d554e3444a80c822ab2470d79748cf598dd70 Mon Sep 17 00:00:00 2001 From: klzgrad Date: Sun, 16 May 2021 00:41:07 +0800 Subject: [PATCH] cert: Add SystemTrustStoreStaticUnix It reads CA certificates from: * The file in environment variable SSL_CERT_FILE * The first available file of /etc/ssl/certs/ca-certificates.crt (Debian/Ubuntu/Gentoo etc.) /etc/pki/tls/certs/ca-bundle.crt (Fedora/RHEL 6) /etc/ssl/ca-bundle.pem (OpenSUSE) /etc/pki/tls/cacert.pem (OpenELEC) /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem (CentOS/RHEL 7) /etc/ssl/cert.pem (Alpine Linux) * Files in the directory of environment variable SSL_CERT_DIR * Files in the first available directory of /etc/ssl/certs (SLES10/SLES11, https://golang.org/issue/12139) /etc/pki/tls/certs (Fedora/RHEL) /system/etc/security/cacerts (Android) --- src/net/cert/internal/system_trust_store.cc | 182 +++++++++++++++++++- 1 file changed, 180 insertions(+), 2 deletions(-) diff --git a/src/net/cert/internal/system_trust_store.cc b/src/net/cert/internal/system_trust_store.cc index 0722863d49..cd7b09f1bd 100644 --- a/src/net/cert/internal/system_trust_store.cc +++ b/src/net/cert/internal/system_trust_store.cc @@ -16,12 +16,17 @@ #include #endif +#include #include +#include +#include "base/environment.h" +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/no_destructor.h" +#include "base/strings/string_split.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "build/build_config.h" @@ -43,8 +48,8 @@ #include "third_party/boringssl/src/include/openssl/pool.h" #elif BUILDFLAG(IS_WIN) #include "net/cert/internal/trust_store_win.h" -#elif BUILDFLAG(IS_ANDROID) -#include "net/cert/internal/trust_store_android.h" +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) +#include "base/lazy_instance.h" #endif #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) #include "net/cert/internal/trust_store_chrome.h" @@ -261,6 +266,179 @@ std::unique_ptr CreateSslSystemTrustStore() { return std::make_unique(); } +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) + +namespace { + +// Copied from https://golang.org/src/crypto/x509/root_linux.go +// Possible certificate files; stop after finding one. +constexpr std::array kStaticRootCertFiles = { + "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. + "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6 + "/etc/ssl/ca-bundle.pem", // OpenSUSE + "/etc/pki/tls/cacert.pem", // OpenELEC + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7 + "/etc/ssl/cert.pem", // Alpine Linux +}; + +// Possible directories with certificate files; stop after successfully +// reading at least one file from a directory. +constexpr std::array kStaticRootCertDirs = { + "/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139 + "/etc/pki/tls/certs", // Fedora/RHEL + "/system/etc/security/cacerts", // Android +}; + +// The environment variable which identifies where to locate the SSL +// certificate file. If set this overrides the system default. +constexpr char kStaticCertFileEnv[] = "SSL_CERT_FILE"; + +// The environment variable which identifies which directory to check for SSL +// certificate files. If set this overrides the system default. It is a colon +// separated list of directories. +// See https://www.openssl.org/docs/man1.0.2/man1/c_rehash.html. +constexpr char kStaticCertDirsEnv[] = "SSL_CERT_DIR"; + +class StaticUnixSystemCerts { + public: + StaticUnixSystemCerts() : system_trust_store_(Create()) {} + + bssl::TrustStoreInMemory* system_trust_store() { + return system_trust_store_.get(); + } + + static std::unique_ptr Create() { + auto ptr = std::make_unique(); + auto env = base::Environment::Create(); + std::string env_value; + + std::vector cert_filenames(kStaticRootCertFiles.begin(), + kStaticRootCertFiles.end()); + if (env->GetVar(kStaticCertFileEnv, &env_value) && !env_value.empty()) { + cert_filenames = {env_value}; + } + + bool cert_file_ok = false; + for (const auto& filename : cert_filenames) { + std::string file; + if (!base::ReadFileToString(base::FilePath(filename), &file)) + continue; + if (AddCertificatesFromBytes(file.data(), file.size(), ptr.get())) { + cert_file_ok = true; + break; + } + } + + std::vector cert_dirnames(kStaticRootCertDirs.begin(), + kStaticRootCertDirs.end()); + if (env->GetVar(kStaticCertDirsEnv, &env_value) && !env_value.empty()) { + cert_dirnames = base::SplitString(env_value, ":", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + } + + bool cert_dir_ok = false; + for (const auto& dir : cert_dirnames) { + base::FileEnumerator e(base::FilePath(dir), + /*recursive=*/true, base::FileEnumerator::FILES); + for (auto filename = e.Next(); !filename.empty(); filename = e.Next()) { + std::string file; + if (!base::ReadFileToString(filename, &file)) { + continue; + } + if (AddCertificatesFromBytes(file.data(), file.size(), ptr.get())) { + cert_dir_ok = true; + } + } + if (cert_dir_ok) + break; + } + + if (!cert_file_ok && !cert_dir_ok) { + LOG(ERROR) << "No CA certificates were found. Try using environment " + "variable SSL_CERT_FILE or SSL_CERT_DIR"; + } + + return ptr; + } + + private: + static bool AddCertificatesFromBytes(const char* data, + size_t length, + bssl::TrustStoreInMemory* store) { + auto certs = X509Certificate::CreateCertificateListFromBytes( + {reinterpret_cast(data), length}, + X509Certificate::FORMAT_AUTO); + bool certs_ok = false; + for (const auto& cert : certs) { + bssl::CertErrors errors; + auto parsed = bssl::ParsedCertificate::Create( + bssl::UpRef(cert->cert_buffer()), + x509_util::DefaultParseCertificateOptions(), &errors); + if (parsed) { + if (!store->Contains(parsed.get())) { + store->AddTrustAnchor(parsed); + } + certs_ok = true; + } else { + LOG(ERROR) << errors.ToDebugString(); + } + } + return certs_ok; + } + + std::unique_ptr system_trust_store_; +}; + +base::LazyInstance::Leaky g_root_certs_static_unix = + LAZY_INSTANCE_INITIALIZER; + +} // namespace + +class SystemTrustStoreStaticUnix : public SystemTrustStore { + public: + SystemTrustStoreStaticUnix() = default; + + bssl::TrustStore* GetTrustStore() override { + return g_root_certs_static_unix.Get().system_trust_store(); + } + + bool IsKnownRoot(const bssl::ParsedCertificate* trust_anchor) const override { + return g_root_certs_static_unix.Get().system_trust_store()->Contains( + trust_anchor); + } + +#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) + int64_t chrome_root_store_version() const override { + return 0; + } + + base::span GetChromeRootConstraints( + const bssl::ParsedCertificate* /*cert*/) const override { + return {}; + } +#endif +}; + +std::unique_ptr CreateSslSystemTrustStore() { + return std::make_unique(); +} + +#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) + +std::unique_ptr CreateSslSystemTrustStoreChromeRoot( + std::unique_ptr chrome_root) { + return std::make_unique( + std::move(chrome_root), StaticUnixSystemCerts::Create()); +} + +#else + +std::unique_ptr CreateSslSystemTrustStoreChromeRoot() { + return std::make_unique(); +} + +#endif // CHROME_ROOT_STORE_SUPPORTED + #elif BUILDFLAG(IS_WIN) namespace {