| // Copyright 2017 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 "services/network/ignore_errors_cert_verifier.h" |
| |
| #include <iterator> |
| #include <utility> |
| |
| #include "base/base64.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_split.h" |
| #include "crypto/sha2.h" |
| #include "net/base/hash_value.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/net_export.h" |
| #include "net/cert/asn1_util.h" |
| #include "net/cert/cert_verify_result.h" |
| #include "net/cert/x509_certificate.h" |
| #include "net/cert/x509_util.h" |
| #include "services/network/public/cpp/network_switches.h" |
| |
| using ::net::CertVerifier; |
| using ::net::HashValue; |
| using ::net::SHA256HashValue; |
| using ::net::X509Certificate; |
| |
| namespace network { |
| |
| // static |
| std::unique_ptr<CertVerifier> IgnoreErrorsCertVerifier::MaybeWrapCertVerifier( |
| const base::CommandLine& command_line, |
| const char* user_data_dir_switch, |
| std::unique_ptr<CertVerifier> verifier) { |
| if ((user_data_dir_switch && !command_line.HasSwitch(user_data_dir_switch)) || |
| !command_line.HasSwitch(switches::kIgnoreCertificateErrorsSPKIList)) { |
| return verifier; |
| } |
| auto spki_list = |
| base::SplitString(command_line.GetSwitchValueASCII( |
| switches::kIgnoreCertificateErrorsSPKIList), |
| ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| return std::make_unique<IgnoreErrorsCertVerifier>( |
| std::move(verifier), IgnoreErrorsCertVerifier::MakeWhitelist(spki_list)); |
| } |
| |
| // static |
| IgnoreErrorsCertVerifier::SPKIHashSet IgnoreErrorsCertVerifier::MakeWhitelist( |
| const std::vector<std::string>& fingerprints) { |
| IgnoreErrorsCertVerifier::SPKIHashSet whitelist; |
| for (const std::string& fingerprint : fingerprints) { |
| HashValue hash; |
| if (!hash.FromString("sha256/" + fingerprint)) { |
| LOG(ERROR) << "Invalid SPKI: " << fingerprint; |
| continue; |
| } |
| SHA256HashValue sha256; |
| DCHECK_EQ(hash.size(), sizeof(sha256)); |
| memcpy(&sha256, hash.data(), sizeof(sha256)); |
| whitelist.insert(sha256); |
| } |
| return whitelist; |
| } |
| |
| IgnoreErrorsCertVerifier::IgnoreErrorsCertVerifier( |
| std::unique_ptr<CertVerifier> verifier, |
| IgnoreErrorsCertVerifier::SPKIHashSet whitelist) |
| : verifier_(std::move(verifier)), whitelist_(std::move(whitelist)) {} |
| |
| IgnoreErrorsCertVerifier::~IgnoreErrorsCertVerifier() {} |
| |
| int IgnoreErrorsCertVerifier::Verify(const RequestParams& params, |
| net::CertVerifyResult* verify_result, |
| net::CompletionOnceCallback callback, |
| std::unique_ptr<Request>* out_req, |
| const net::NetLogWithSource& net_log) { |
| SPKIHashSet spki_fingerprints; |
| base::StringPiece cert_spki; |
| SHA256HashValue hash; |
| if (net::asn1::ExtractSPKIFromDERCert( |
| net::x509_util::CryptoBufferAsStringPiece( |
| params.certificate()->cert_buffer()), |
| &cert_spki)) { |
| crypto::SHA256HashString(cert_spki, &hash, sizeof(SHA256HashValue)); |
| spki_fingerprints.insert(hash); |
| } |
| for (const auto& intermediate : |
| params.certificate()->intermediate_buffers()) { |
| if (net::asn1::ExtractSPKIFromDERCert( |
| net::x509_util::CryptoBufferAsStringPiece(intermediate.get()), |
| &cert_spki)) { |
| crypto::SHA256HashString(cert_spki, &hash, sizeof(SHA256HashValue)); |
| spki_fingerprints.insert(hash); |
| } |
| } |
| |
| // Intersect SPKI hashes from the chain with the whitelist. |
| auto whitelist_begin = whitelist_.begin(); |
| auto whitelist_end = whitelist_.end(); |
| auto fingerprints_begin = spki_fingerprints.begin(); |
| auto fingerprints_end = spki_fingerprints.end(); |
| bool ignore_errors = false; |
| while (whitelist_begin != whitelist_end && |
| fingerprints_begin != fingerprints_end) { |
| if (*whitelist_begin < *fingerprints_begin) { |
| ++whitelist_begin; |
| } else if (*fingerprints_begin < *whitelist_begin) { |
| ++fingerprints_begin; |
| } else { |
| ignore_errors = true; |
| break; |
| } |
| } |
| |
| if (ignore_errors) { |
| verify_result->Reset(); |
| verify_result->verified_cert = params.certificate(); |
| std::transform(spki_fingerprints.begin(), spki_fingerprints.end(), |
| std::back_inserter(verify_result->public_key_hashes), |
| [](const SHA256HashValue& v) { return HashValue(v); }); |
| if (!params.ocsp_response().empty()) { |
| verify_result->ocsp_result.response_status = |
| net::OCSPVerifyResult::PROVIDED; |
| verify_result->ocsp_result.revocation_status = |
| net::OCSPRevocationStatus::GOOD; |
| } |
| return net::OK; |
| } |
| |
| return verifier_->Verify(params, verify_result, std::move(callback), out_req, |
| net_log); |
| } |
| |
| void IgnoreErrorsCertVerifier::SetConfig(const Config& config) { |
| verifier_->SetConfig(config); |
| } |
| |
| void IgnoreErrorsCertVerifier::set_whitelist(const SPKIHashSet& whitelist) { |
| whitelist_ = whitelist; |
| } |
| |
| } // namespace network |