|  | // Copyright 2016 The Chromium Authors | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     https://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "cert_errors.h" | 
|  |  | 
|  | #include "cert_error_params.h" | 
|  | #include "parse_name.h" | 
|  | #include "parsed_certificate.h" | 
|  |  | 
|  | #include <sstream> | 
|  |  | 
|  | BSSL_NAMESPACE_BEGIN | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void AppendLinesWithIndentation(const std::string &text, | 
|  | const std::string &indentation, | 
|  | std::string *out) { | 
|  | std::istringstream stream(text); | 
|  | for (std::string line; std::getline(stream, line, '\n');) { | 
|  | out->append(indentation); | 
|  | out->append(line); | 
|  | out->append("\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | CertError::CertError() = default; | 
|  |  | 
|  | CertError::CertError(Severity in_severity, CertErrorId in_id, | 
|  | std::unique_ptr<CertErrorParams> in_params) | 
|  | : severity(in_severity), id(in_id), params(std::move(in_params)) {} | 
|  |  | 
|  | CertError::CertError(CertError &&other) = default; | 
|  |  | 
|  | CertError &CertError::operator=(CertError &&) = default; | 
|  |  | 
|  | CertError::~CertError() = default; | 
|  |  | 
|  | std::string CertError::ToDebugString() const { | 
|  | std::string result; | 
|  | switch (severity) { | 
|  | case SEVERITY_WARNING: | 
|  | result += "WARNING: "; | 
|  | break; | 
|  | case SEVERITY_HIGH: | 
|  | result += "ERROR: "; | 
|  | break; | 
|  | } | 
|  | result += CertErrorIdToDebugString(id); | 
|  | result += +"\n"; | 
|  |  | 
|  | if (params) { | 
|  | AppendLinesWithIndentation(params->ToDebugString(), "  ", &result); | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | CertErrors::CertErrors() = default; | 
|  | CertErrors::CertErrors(CertErrors &&other) = default; | 
|  | CertErrors &CertErrors::operator=(CertErrors &&) = default; | 
|  | CertErrors::~CertErrors() = default; | 
|  |  | 
|  | void CertErrors::Add(CertError::Severity severity, CertErrorId id, | 
|  | std::unique_ptr<CertErrorParams> params) { | 
|  | nodes_.emplace_back(severity, id, std::move(params)); | 
|  | } | 
|  |  | 
|  | void CertErrors::AddError(CertErrorId id, | 
|  | std::unique_ptr<CertErrorParams> params) { | 
|  | Add(CertError::SEVERITY_HIGH, id, std::move(params)); | 
|  | } | 
|  |  | 
|  | void CertErrors::AddError(CertErrorId id) { AddError(id, nullptr); } | 
|  |  | 
|  | void CertErrors::AddWarning(CertErrorId id, | 
|  | std::unique_ptr<CertErrorParams> params) { | 
|  | Add(CertError::SEVERITY_WARNING, id, std::move(params)); | 
|  | } | 
|  |  | 
|  | void CertErrors::AddWarning(CertErrorId id) { AddWarning(id, nullptr); } | 
|  |  | 
|  | std::string CertErrors::ToDebugString() const { | 
|  | std::string result; | 
|  | for (const CertError &node : nodes_) { | 
|  | result += node.ToDebugString(); | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool CertErrors::ContainsErrorWithSeverity(CertErrorId id, | 
|  | CertError::Severity severity) const { | 
|  | for (const CertError &node : nodes_) { | 
|  | if (node.id == id && node.severity == severity) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CertErrors::ContainsError(CertErrorId id) const { | 
|  | return ContainsErrorWithSeverity(id, CertError::SEVERITY_HIGH); | 
|  | } | 
|  |  | 
|  | bool CertErrors::ContainsAnyErrorWithSeverity( | 
|  | CertError::Severity severity) const { | 
|  | for (const CertError &node : nodes_) { | 
|  | if (node.severity == severity) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CertPathErrors::CertPathErrors() = default; | 
|  |  | 
|  | CertPathErrors::CertPathErrors(CertPathErrors &&other) = default; | 
|  | CertPathErrors &CertPathErrors::operator=(CertPathErrors &&) = default; | 
|  |  | 
|  | CertPathErrors::~CertPathErrors() = default; | 
|  |  | 
|  | CertErrors *CertPathErrors::GetErrorsForCert(size_t cert_index) { | 
|  | if (cert_index >= cert_errors_.size()) { | 
|  | cert_errors_.resize(cert_index + 1); | 
|  | } | 
|  | return &cert_errors_[cert_index]; | 
|  | } | 
|  |  | 
|  | const CertErrors *CertPathErrors::GetErrorsForCert(size_t cert_index) const { | 
|  | if (cert_index >= cert_errors_.size()) { | 
|  | return nullptr; | 
|  | } | 
|  | return &cert_errors_[cert_index]; | 
|  | } | 
|  |  | 
|  | CertErrors *CertPathErrors::GetOtherErrors() { return &other_errors_; } | 
|  |  | 
|  | const CertErrors *CertPathErrors::GetOtherErrors() const { | 
|  | return &other_errors_; | 
|  | } | 
|  |  | 
|  | bool CertPathErrors::ContainsError(CertErrorId id) const { | 
|  | for (const CertErrors &errors : cert_errors_) { | 
|  | if (errors.ContainsError(id)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (other_errors_.ContainsError(id)) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CertPathErrors::ContainsAnyErrorWithSeverity( | 
|  | CertError::Severity severity) const { | 
|  | for (const CertErrors &errors : cert_errors_) { | 
|  | if (errors.ContainsAnyErrorWithSeverity(severity)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (other_errors_.ContainsAnyErrorWithSeverity(severity)) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::optional<CertErrorId> CertPathErrors::FindSingleHighSeverityError( | 
|  | ptrdiff_t &out_depth) const { | 
|  | std::optional<CertErrorId> id_seen; | 
|  | for (ptrdiff_t i = -1; i < (ptrdiff_t)cert_errors_.size(); ++i) { | 
|  | const CertErrors *errors = | 
|  | (i < 0) ? GetOtherErrors() : GetErrorsForCert(i); | 
|  | for (const CertError &node : errors->nodes_) { | 
|  | if (node.severity == CertError::SEVERITY_HIGH) { | 
|  | if (!id_seen.has_value()) { | 
|  | id_seen = node.id; | 
|  | out_depth = i; | 
|  | } else { | 
|  | if (id_seen.value() != node.id) { | 
|  | return {}; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return id_seen; | 
|  | } | 
|  |  | 
|  | std::string CertPathErrors::ToDebugString( | 
|  | const ParsedCertificateList &certs) const { | 
|  | std::ostringstream result; | 
|  |  | 
|  | for (size_t i = 0; i < cert_errors_.size(); ++i) { | 
|  | // Pretty print the current CertErrors. If there were no errors/warnings, | 
|  | // then continue. | 
|  | const CertErrors &errors = cert_errors_[i]; | 
|  | std::string cert_errors_string = errors.ToDebugString(); | 
|  | if (cert_errors_string.empty()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Add a header that identifies which certificate this CertErrors pertains | 
|  | // to. | 
|  | std::string cert_name_debug_str; | 
|  | if (i < certs.size() && certs[i]) { | 
|  | RDNSequence subject; | 
|  | if (ParseName(certs[i]->tbs().subject_tlv, &subject) && | 
|  | ConvertToRFC2253(subject, &cert_name_debug_str)) { | 
|  | cert_name_debug_str = " (" + cert_name_debug_str + ")"; | 
|  | } | 
|  | } | 
|  | result << "----- Certificate i=" << i << cert_name_debug_str << " -----\n"; | 
|  | result << cert_errors_string << "\n"; | 
|  | } | 
|  |  | 
|  | // Print any other errors that aren't associated with a particular certificate | 
|  | // in the chain. | 
|  | std::string other_errors = other_errors_.ToDebugString(); | 
|  | if (!other_errors.empty()) { | 
|  | result << "----- Other errors (not certificate specific) -----\n"; | 
|  | result << other_errors << "\n"; | 
|  | } | 
|  |  | 
|  | return result.str(); | 
|  | } | 
|  |  | 
|  | BSSL_NAMESPACE_END |