1 // Copyright 2016 The Chromium Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "cert_errors.h"
16 
17 #include "cert_error_params.h"
18 #include "parse_name.h"
19 #include "parsed_certificate.h"
20 
21 #include <sstream>
22 
23 BSSL_NAMESPACE_BEGIN
24 
25 namespace {
26 
AppendLinesWithIndentation(const std::string & text,const std::string & indentation,std::string * out)27 void AppendLinesWithIndentation(const std::string &text,
28                                 const std::string &indentation,
29                                 std::string *out) {
30   std::istringstream stream(text);
31   for (std::string line; std::getline(stream, line, '\n');) {
32     out->append(indentation);
33     out->append(line);
34     out->append("\n");
35   }
36 }
37 
38 }  // namespace
39 
40 CertError::CertError() = default;
41 
CertError(Severity in_severity,CertErrorId in_id,std::unique_ptr<CertErrorParams> in_params)42 CertError::CertError(Severity in_severity, CertErrorId in_id,
43                      std::unique_ptr<CertErrorParams> in_params)
44     : severity(in_severity), id(in_id), params(std::move(in_params)) {}
45 
46 CertError::CertError(CertError &&other) = default;
47 
48 CertError &CertError::operator=(CertError &&) = default;
49 
50 CertError::~CertError() = default;
51 
ToDebugString() const52 std::string CertError::ToDebugString() const {
53   std::string result;
54   switch (severity) {
55     case SEVERITY_WARNING:
56       result += "WARNING: ";
57       break;
58     case SEVERITY_HIGH:
59       result += "ERROR: ";
60       break;
61   }
62   result += CertErrorIdToDebugString(id);
63   result += +"\n";
64 
65   if (params) {
66     AppendLinesWithIndentation(params->ToDebugString(), "  ", &result);
67   }
68 
69   return result;
70 }
71 
72 CertErrors::CertErrors() = default;
73 CertErrors::CertErrors(CertErrors &&other) = default;
74 CertErrors &CertErrors::operator=(CertErrors &&) = default;
75 CertErrors::~CertErrors() = default;
76 
Add(CertError::Severity severity,CertErrorId id,std::unique_ptr<CertErrorParams> params)77 void CertErrors::Add(CertError::Severity severity, CertErrorId id,
78                      std::unique_ptr<CertErrorParams> params) {
79   nodes_.emplace_back(severity, id, std::move(params));
80 }
81 
AddError(CertErrorId id,std::unique_ptr<CertErrorParams> params)82 void CertErrors::AddError(CertErrorId id,
83                           std::unique_ptr<CertErrorParams> params) {
84   Add(CertError::SEVERITY_HIGH, id, std::move(params));
85 }
86 
AddError(CertErrorId id)87 void CertErrors::AddError(CertErrorId id) { AddError(id, nullptr); }
88 
AddWarning(CertErrorId id,std::unique_ptr<CertErrorParams> params)89 void CertErrors::AddWarning(CertErrorId id,
90                             std::unique_ptr<CertErrorParams> params) {
91   Add(CertError::SEVERITY_WARNING, id, std::move(params));
92 }
93 
AddWarning(CertErrorId id)94 void CertErrors::AddWarning(CertErrorId id) { AddWarning(id, nullptr); }
95 
ToDebugString() const96 std::string CertErrors::ToDebugString() const {
97   std::string result;
98   for (const CertError &node : nodes_) {
99     result += node.ToDebugString();
100   }
101 
102   return result;
103 }
104 
ContainsErrorWithSeverity(CertErrorId id,CertError::Severity severity) const105 bool CertErrors::ContainsErrorWithSeverity(CertErrorId id,
106                                            CertError::Severity severity) const {
107   for (const CertError &node : nodes_) {
108     if (node.id == id && node.severity == severity) {
109       return true;
110     }
111   }
112   return false;
113 }
114 
ContainsError(CertErrorId id) const115 bool CertErrors::ContainsError(CertErrorId id) const {
116   return ContainsErrorWithSeverity(id, CertError::SEVERITY_HIGH);
117 }
118 
ContainsAnyErrorWithSeverity(CertError::Severity severity) const119 bool CertErrors::ContainsAnyErrorWithSeverity(
120     CertError::Severity severity) const {
121   for (const CertError &node : nodes_) {
122     if (node.severity == severity) {
123       return true;
124     }
125   }
126   return false;
127 }
128 
129 CertPathErrors::CertPathErrors() = default;
130 
131 CertPathErrors::CertPathErrors(CertPathErrors &&other) = default;
132 CertPathErrors &CertPathErrors::operator=(CertPathErrors &&) = default;
133 
134 CertPathErrors::~CertPathErrors() = default;
135 
GetErrorsForCert(size_t cert_index)136 CertErrors *CertPathErrors::GetErrorsForCert(size_t cert_index) {
137   if (cert_index >= cert_errors_.size()) {
138     cert_errors_.resize(cert_index + 1);
139   }
140   return &cert_errors_[cert_index];
141 }
142 
GetErrorsForCert(size_t cert_index) const143 const CertErrors *CertPathErrors::GetErrorsForCert(size_t cert_index) const {
144   if (cert_index >= cert_errors_.size()) {
145     return nullptr;
146   }
147   return &cert_errors_[cert_index];
148 }
149 
GetOtherErrors()150 CertErrors *CertPathErrors::GetOtherErrors() { return &other_errors_; }
151 
GetOtherErrors() const152 const CertErrors *CertPathErrors::GetOtherErrors() const {
153   return &other_errors_;
154 }
155 
ContainsError(CertErrorId id) const156 bool CertPathErrors::ContainsError(CertErrorId id) const {
157   for (const CertErrors &errors : cert_errors_) {
158     if (errors.ContainsError(id)) {
159       return true;
160     }
161   }
162 
163   if (other_errors_.ContainsError(id)) {
164     return true;
165   }
166 
167   return false;
168 }
169 
ContainsAnyErrorWithSeverity(CertError::Severity severity) const170 bool CertPathErrors::ContainsAnyErrorWithSeverity(
171     CertError::Severity severity) const {
172   for (const CertErrors &errors : cert_errors_) {
173     if (errors.ContainsAnyErrorWithSeverity(severity)) {
174       return true;
175     }
176   }
177 
178   if (other_errors_.ContainsAnyErrorWithSeverity(severity)) {
179     return true;
180   }
181 
182   return false;
183 }
184 
FindSingleHighSeverityError(ptrdiff_t & out_depth) const185 std::optional<CertErrorId> CertPathErrors::FindSingleHighSeverityError(
186     ptrdiff_t &out_depth) const {
187   std::optional<CertErrorId> id_seen;
188   for (ptrdiff_t i = -1; i < (ptrdiff_t)cert_errors_.size(); ++i) {
189     const CertErrors *errors =
190         (i < 0) ? GetOtherErrors() : GetErrorsForCert(i);
191     for (const CertError &node : errors->nodes_) {
192       if (node.severity == CertError::SEVERITY_HIGH) {
193         if (!id_seen.has_value()) {
194           id_seen = node.id;
195           out_depth = i;
196         } else {
197           if (id_seen.value() != node.id) {
198             return {};
199           }
200         }
201       }
202     }
203   }
204   return id_seen;
205 }
206 
ToDebugString(const ParsedCertificateList & certs) const207 std::string CertPathErrors::ToDebugString(
208     const ParsedCertificateList &certs) const {
209   std::ostringstream result;
210 
211   for (size_t i = 0; i < cert_errors_.size(); ++i) {
212     // Pretty print the current CertErrors. If there were no errors/warnings,
213     // then continue.
214     const CertErrors &errors = cert_errors_[i];
215     std::string cert_errors_string = errors.ToDebugString();
216     if (cert_errors_string.empty()) {
217       continue;
218     }
219 
220     // Add a header that identifies which certificate this CertErrors pertains
221     // to.
222     std::string cert_name_debug_str;
223     if (i < certs.size() && certs[i]) {
224       RDNSequence subject;
225       if (ParseName(certs[i]->tbs().subject_tlv, &subject) &&
226           ConvertToRFC2253(subject, &cert_name_debug_str)) {
227         cert_name_debug_str = " (" + cert_name_debug_str + ")";
228       }
229     }
230     result << "----- Certificate i=" << i << cert_name_debug_str << " -----\n";
231     result << cert_errors_string << "\n";
232   }
233 
234   // Print any other errors that aren't associated with a particular certificate
235   // in the chain.
236   std::string other_errors = other_errors_.ToDebugString();
237   if (!other_errors.empty()) {
238     result << "----- Other errors (not certificate specific) -----\n";
239     result << other_errors << "\n";
240   }
241 
242   return result.str();
243 }
244 
245 BSSL_NAMESPACE_END
246