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)
This commit is contained in:
klzgrad 2021-05-16 00:41:07 +08:00
parent 51ab30f26f
commit e1c59a6e41

View File

@ -15,12 +15,17 @@
#include <Security/Security.h>
#endif
#include <array>
#include <memory>
#include <vector>
#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,6 +48,8 @@
#elif defined(OS_FUCHSIA)
#include "base/lazy_instance.h"
#include "third_party/boringssl/src/include/openssl/pool.h"
#else
#include "base/lazy_instance.h"
#endif
namespace net {
@ -262,6 +269,144 @@ std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {
return std::make_unique<SystemTrustStoreFuchsia>();
}
#elif defined(OS_LINUX) || defined(OS_ANDROID)
namespace {
// Copied from https://golang.org/src/crypto/x509/root_linux.go
// Possible certificate files; stop after finding one.
constexpr std::array<const char*, 6> 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<const char*, 3> 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() {
auto env = base::Environment::Create();
std::string env_value;
std::vector<std::string> 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())) {
cert_file_ok = true;
break;
}
}
std::vector<std::string> 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())) {
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";
}
}
TrustStoreInMemory* system_trust_store() { return &system_trust_store_; }
private:
bool AddCertificatesFromBytes(const char* data, size_t length) {
auto certs = X509Certificate::CreateCertificateListFromBytes(
data, length, X509Certificate::FORMAT_AUTO);
bool certs_ok = false;
for (const auto& cert : certs) {
CertErrors errors;
auto parsed = ParsedCertificate::Create(
bssl::UpRef(cert->cert_buffer()),
x509_util::DefaultParseCertificateOptions(), &errors);
if (parsed) {
if (!system_trust_store_.Contains(parsed.get())) {
system_trust_store_.AddTrustAnchor(parsed);
}
certs_ok = true;
} else {
LOG(ERROR) << errors.ToDebugString();
}
}
return certs_ok;
}
TrustStoreInMemory system_trust_store_;
};
base::LazyInstance<StaticUnixSystemCerts>::Leaky g_root_certs_static_unix =
LAZY_INSTANCE_INITIALIZER;
} // namespace
class SystemTrustStoreStaticUnix : public SystemTrustStore {
public:
SystemTrustStoreStaticUnix() = default;
TrustStore* GetTrustStore() override {
return g_root_certs_static_unix.Get().system_trust_store();
}
bool UsesSystemTrustStore() const override { return true; }
bool IsKnownRoot(const ParsedCertificate* trust_anchor) const override {
return g_root_certs_static_unix.Get().system_trust_store()->Contains(
trust_anchor);
}
};
std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {
return std::make_unique<SystemTrustStoreStaticUnix>();
}
#else
std::unique_ptr<SystemTrustStore> CreateSslSystemTrustStore() {