// Copyright 2016 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "base/at_exit.h" #include "base/command_line.h" #include "base/functional/bind.h" #include "base/logging.h" #include "base/message_loop/message_pump_type.h" #include "base/strings/string_split.h" #include "base/synchronization/waitable_event.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/threading/thread.h" #include "base/time/time.h" #include "build/build_config.h" #include "net/cert/cert_net_fetcher.h" #include "net/cert/cert_verify_proc.h" #include "net/cert/cert_verify_proc_builtin.h" #include "net/cert/crl_set.h" #include "net/cert/do_nothing_ct_verifier.h" #include "net/cert/internal/platform_trust_store.h" #include "net/cert/internal/system_trust_store.h" #include "net/cert/x509_util.h" #include "net/cert_net/cert_net_fetcher_url_request.h" #include "net/tools/cert_verify_tool/cert_verify_tool_util.h" #include "net/tools/cert_verify_tool/verify_using_cert_verify_proc.h" #include "net/tools/cert_verify_tool/verify_using_path_builder.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_context_getter.h" #include "third_party/abseil-cpp/absl/cleanup/cleanup.h" #include "third_party/boringssl/src/pki/trust_store.h" #include "third_party/boringssl/src/pki/trust_store_collection.h" #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #include "net/proxy_resolution/proxy_config.h" #include "net/proxy_resolution/proxy_config_service_fixed.h" #endif #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) #include "net/cert/internal/trust_store_chrome.h" #endif namespace { enum class RootStoreType { // No roots other than those explicitly passed in on the command line. kEmpty, #if !BUILDFLAG(CHROME_ROOT_STORE_ONLY) // Use the system root store. kSystem, #endif // Use the Chrome Root Store. kChrome }; std::string GetUserAgent() { return "cert_verify_tool/0.1"; } void SetUpOnNetworkThread( std::unique_ptr* context, scoped_refptr* cert_net_fetcher, base::WaitableEvent* initialization_complete_event) { net::URLRequestContextBuilder url_request_context_builder; url_request_context_builder.set_user_agent(GetUserAgent()); #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // On Linux, use a fixed ProxyConfigService, since the default one // depends on glib. // // TODO(akalin): Remove this once http://crbug.com/146421 is fixed. url_request_context_builder.set_proxy_config_service( std::make_unique( net::ProxyConfigWithAnnotation())); #endif *context = url_request_context_builder.Build(); // TODO(mattm): add command line flag to configure using // CertNetFetcher *cert_net_fetcher = base::MakeRefCounted(); (*cert_net_fetcher)->SetURLRequestContext(context->get()); initialization_complete_event->Signal(); } void ShutdownOnNetworkThread( std::unique_ptr* context, scoped_refptr* cert_net_fetcher) { (*cert_net_fetcher)->Shutdown(); cert_net_fetcher->reset(); context->reset(); } // Base class to abstract running a particular implementation of certificate // verification. class CertVerifyImpl { public: virtual ~CertVerifyImpl() = default; virtual std::string GetName() const = 0; // Does certificate verification. // // Note that |hostname| may be empty to indicate that no name validation is // requested, and a null value of |verify_time| means to use the current time. virtual bool VerifyCert(const CertInput& target_der_cert, const std::string& hostname, const std::vector& intermediate_der_certs, const std::vector& der_certs_with_trust_settings, base::Time verify_time, net::CRLSet* crl_set, const base::FilePath& dump_prefix_path) = 0; }; // Runs certificate verification using a particular CertVerifyProc. class CertVerifyImplUsingProc : public CertVerifyImpl { public: CertVerifyImplUsingProc(const std::string& name, scoped_refptr proc) : name_(name), proc_(std::move(proc)) {} std::string GetName() const override { return name_; } bool VerifyCert(const CertInput& target_der_cert, const std::string& hostname, const std::vector& intermediate_der_certs, const std::vector& der_certs_with_trust_settings, base::Time verify_time, net::CRLSet* crl_set, const base::FilePath& dump_prefix_path) override { if (!verify_time.is_null()) { std::cerr << "WARNING: --time is not supported by " << GetName() << ", will use current time.\n"; } if (hostname.empty()) { std::cerr << "ERROR: --hostname is required for " << GetName() << ", skipping\n"; return true; // "skipping" is considered a successful return. } base::FilePath dump_path; if (!dump_prefix_path.empty()) { dump_path = dump_prefix_path.AddExtension(FILE_PATH_LITERAL(".pem")) .InsertBeforeExtensionASCII("." + GetName()); } return VerifyUsingCertVerifyProc(proc_.get(), target_der_cert, hostname, intermediate_der_certs, der_certs_with_trust_settings, dump_path); } private: const std::string name_; scoped_refptr proc_; }; // Runs certificate verification using bssl::CertPathBuilder. class CertVerifyImplUsingPathBuilder : public CertVerifyImpl { public: explicit CertVerifyImplUsingPathBuilder( scoped_refptr cert_net_fetcher, std::unique_ptr system_trust_store) : cert_net_fetcher_(std::move(cert_net_fetcher)), system_trust_store_(std::move(system_trust_store)) {} std::string GetName() const override { return "CertPathBuilder"; } bool VerifyCert(const CertInput& target_der_cert, const std::string& hostname, const std::vector& intermediate_der_certs, const std::vector& der_certs_with_trust_settings, base::Time verify_time, net::CRLSet* crl_set, const base::FilePath& dump_prefix_path) override { if (!hostname.empty()) { std::cerr << "WARNING: --hostname is not verified with CertPathBuilder\n"; } if (verify_time.is_null()) { verify_time = base::Time::Now(); } return VerifyUsingPathBuilder(target_der_cert, intermediate_der_certs, der_certs_with_trust_settings, verify_time, dump_prefix_path, cert_net_fetcher_, system_trust_store_.get()); } private: scoped_refptr cert_net_fetcher_; std::unique_ptr system_trust_store_; }; class DummySystemTrustStore : public net::SystemTrustStore { public: bssl::TrustStore* GetTrustStore() override { return &trust_store_; } bool IsKnownRoot(const bssl::ParsedCertificate* trust_anchor) const override { return false; } #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) net::PlatformTrustStore* GetPlatformTrustStore() override { return nullptr; } bool IsLocallyTrustedRoot( const bssl::ParsedCertificate* trust_anchor) override { return false; } int64_t chrome_root_store_version() const override { return 0; } base::span GetChromeRootConstraints( const bssl::ParsedCertificate* cert) const override { return {}; } #endif private: bssl::TrustStoreCollection trust_store_; }; std::unique_ptr CreateSystemTrustStore( std::string_view impl_name, RootStoreType root_store_type) { switch (root_store_type) { #if BUILDFLAG(IS_FUCHSIA) case RootStoreType::kSystem: std::cerr << impl_name << ": using system roots (--roots are in addition).\n"; return net::CreateSslSystemTrustStore(); #endif case RootStoreType::kChrome: #if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED) std::cerr << impl_name << ": using Chrome Root Store (--roots are in addition).\n"; return net::CreateSslSystemTrustStoreChromeRoot( std::make_unique()); #else std::cerr << impl_name << ": not supported.\n"; [[fallthrough]]; #endif case RootStoreType::kEmpty: default: std::cerr << impl_name << ": only using --roots specified.\n"; return std::make_unique(); } } // Creates an subclass of CertVerifyImpl based on its name, or returns nullptr. std::unique_ptr CreateCertVerifyImplFromName( std::string_view impl_name, scoped_refptr cert_net_fetcher, scoped_refptr crl_set, RootStoreType root_store_type) { #if !(BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(CHROME_ROOT_STORE_ONLY)) if (impl_name == "platform") { if (root_store_type != RootStoreType::kSystem) { std::cerr << "WARNING: platform verifier not supported with " "--no-system-roots and --use-chrome-root-store, using " "system roots (--roots are in addition).\n"; } return std::make_unique( "CertVerifyProc (system)", net::CertVerifyProc::CreateSystemVerifyProc(std::move(cert_net_fetcher), std::move(crl_set))); } #endif if (impl_name == "builtin") { return std::make_unique( "CertVerifyProcBuiltin", net::CreateCertVerifyProcBuiltin( std::move(cert_net_fetcher), std::move(crl_set), // TODO(crbug.com/41392053): support CT. std::make_unique(), base::MakeRefCounted(), CreateSystemTrustStore(impl_name, root_store_type), {}, std::nullopt)); } if (impl_name == "pathbuilder") { return std::make_unique( std::move(cert_net_fetcher), CreateSystemTrustStore(impl_name, root_store_type)); } std::cerr << "WARNING: Unrecognized impl: " << impl_name << "\n"; return nullptr; } void PrintCertHashAndSubject(CRYPTO_BUFFER* cert) { std::cout << " " << FingerPrintCryptoBuffer(cert) << " " << SubjectFromCryptoBuffer(cert) << "\n"; } void PrintInputChain(const CertInput& target, const std::vector& intermediates) { std::cout << "Input chain:\n"; PrintCertHashAndSubject( net::x509_util::CreateCryptoBuffer(target.der_cert).get()); for (const auto& intermediate : intermediates) { PrintCertHashAndSubject( net::x509_util::CreateCryptoBuffer(intermediate.der_cert).get()); } std::cout << "\n"; } void PrintAdditionalRoots(const std::vector& der_certs_with_trust_settings) { std::cout << "Additional roots:\n"; for (const auto& cert : der_certs_with_trust_settings) { std::cout << " " << cert.trust.ToDebugString() << ":\n "; PrintCertHashAndSubject( net::x509_util::CreateCryptoBuffer(cert.cert_input.der_cert).get()); } std::cout << "\n"; } const char kUsage[] = " [flags] \n" "\n" " is a file containing certificates [1]. Minimally it\n" " contains the target certificate. Optionally it may subsequently list\n" " additional certificates needed to build a chain (this is equivalent to\n" " specifying them through --intermediates)\n" "\n" "Flags:\n" "\n" " --hostname=\n" " The hostname required to match the end-entity certificate.\n" " Required for the CertVerifyProc implementation.\n" "\n" " --roots=\n" " is a file containing certificates [1] to interpret as\n" " trust anchors (without any anchor constraints).\n" "\n" " --no-system-roots\n" " Do not use system provided trust roots, only trust roots specified\n" " by --roots or --trust-last-cert will be used. Only supported by\n" " the builtin and pathbuilter impls.\n" "\n" " --use-chrome-root-store\n" " Use the Chrome Root Store. Only supported by the builtin and \n" " pathbuilder impls; if set will override the --no-system-roots \n" " flag.\n" "\n" " --intermediates=\n" " is a file containing certificates [1] for use when\n" " path building is looking for intermediates.\n" "\n" " --impls=\n" " Ordered list of the verifier implementations to run. If omitted,\n" " will default to: \"platform,builtin,pathbuilder\".\n" " Changing this can lead to different results in cases where the\n" " platform verifier affects global caches (as in the case of NSS).\n" "\n" " --trust-last-cert\n" " Removes the final intermediate from the chain and instead adds it\n" " as a root. This is useful when providing a \n" " parameter whose final certificate is a trust anchor.\n" "\n" " --root-trust=\n" " Roots trusted by --roots and --trust-last-cert will be trusted\n" " with the specified trust [2].\n" "\n" " --trust-leaf-cert=[trust string]\n" " The leaf cert will be considered trusted with the specified\n" " trust [2]. If [trust string] is omitted, defaults to TRUSTED_LEAF.\n" "\n" " --time=