/src/boringssl/pki/certificate_policies.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2015 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 <algorithm> |
16 | | |
17 | | #include "certificate_policies.h" |
18 | | |
19 | | #include <openssl/base.h> |
20 | | #include "cert_error_params.h" |
21 | | #include "cert_errors.h" |
22 | | #include "input.h" |
23 | | #include "parse_values.h" |
24 | | #include "parser.h" |
25 | | |
26 | | BSSL_NAMESPACE_BEGIN |
27 | | |
28 | | namespace { |
29 | | |
30 | | // --------------------------------------------------------------- |
31 | | // Errors |
32 | | // --------------------------------------------------------------- |
33 | | |
34 | | DEFINE_CERT_ERROR_ID(kPolicyQualifiersEmptySequence, |
35 | | "The policy qualifiers SEQUENCE is empty"); |
36 | | DEFINE_CERT_ERROR_ID(kUnknownPolicyQualifierOid, |
37 | | "Unknown policy qualifier OID (not CPS or User Notice)"); |
38 | | DEFINE_CERT_ERROR_ID(kPoliciesEmptySequence, "Policies is an empty SEQUENCE"); |
39 | | DEFINE_CERT_ERROR_ID(kPoliciesDuplicateOid, "Policies contains duplicate OIDs"); |
40 | | DEFINE_CERT_ERROR_ID(kPolicyInformationTrailingData, |
41 | | "PolicyInformation has trailing data"); |
42 | | DEFINE_CERT_ERROR_ID(kFailedParsingPolicyQualifiers, |
43 | | "Failed parsing policy qualifiers"); |
44 | | DEFINE_CERT_ERROR_ID(kMissingQualifier, |
45 | | "PolicyQualifierInfo is missing qualifier"); |
46 | | DEFINE_CERT_ERROR_ID(kPolicyQualifierInfoTrailingData, |
47 | | "PolicyQualifierInfo has trailing data"); |
48 | | |
49 | | // Minimally parse policyQualifiers, storing in |policy_qualifiers| if non-null. |
50 | | // If a policy qualifier other than User Notice/CPS is present, parsing |
51 | | // will fail if |restrict_to_known_qualifiers| was set to true. |
52 | | bool ParsePolicyQualifiers(bool restrict_to_known_qualifiers, |
53 | | der::Parser *policy_qualifiers_sequence_parser, |
54 | | std::vector<PolicyQualifierInfo> *policy_qualifiers, |
55 | 330 | CertErrors *errors) { |
56 | 330 | BSSL_CHECK(errors); |
57 | | |
58 | | // If it is present, the policyQualifiers sequence should have at least 1 |
59 | | // element. |
60 | | // |
61 | | // policyQualifiers SEQUENCE SIZE (1..MAX) OF |
62 | | // PolicyQualifierInfo OPTIONAL } |
63 | 330 | if (!policy_qualifiers_sequence_parser->HasMore()) { |
64 | 2 | errors->AddError(kPolicyQualifiersEmptySequence); |
65 | 2 | return false; |
66 | 2 | } |
67 | 749 | while (policy_qualifiers_sequence_parser->HasMore()) { |
68 | | // PolicyQualifierInfo ::= SEQUENCE { |
69 | 449 | der::Parser policy_information_parser; |
70 | 449 | if (!policy_qualifiers_sequence_parser->ReadSequence( |
71 | 449 | &policy_information_parser)) { |
72 | 5 | return false; |
73 | 5 | } |
74 | | // policyQualifierId PolicyQualifierId, |
75 | 444 | der::Input qualifier_oid; |
76 | 444 | if (!policy_information_parser.ReadTag(CBS_ASN1_OBJECT, &qualifier_oid)) { |
77 | 3 | return false; |
78 | 3 | } |
79 | 441 | if (restrict_to_known_qualifiers && |
80 | 441 | qualifier_oid != der::Input(kCpsPointerId) && |
81 | 441 | qualifier_oid != der::Input(kUserNoticeId)) { |
82 | 17 | errors->AddError(kUnknownPolicyQualifierOid, |
83 | 17 | CreateCertErrorParams1Der("oid", qualifier_oid)); |
84 | 17 | return false; |
85 | 17 | } |
86 | | // qualifier ANY DEFINED BY policyQualifierId } |
87 | 424 | der::Input qualifier_tlv; |
88 | 424 | if (!policy_information_parser.ReadRawTLV(&qualifier_tlv)) { |
89 | 2 | errors->AddError(kMissingQualifier); |
90 | 2 | return false; |
91 | 2 | } |
92 | | // Should not have trailing data after qualifier. |
93 | 422 | if (policy_information_parser.HasMore()) { |
94 | 1 | errors->AddError(kPolicyQualifierInfoTrailingData); |
95 | 1 | return false; |
96 | 1 | } |
97 | | |
98 | 421 | if (policy_qualifiers) { |
99 | 0 | policy_qualifiers->push_back({qualifier_oid, qualifier_tlv}); |
100 | 0 | } |
101 | 421 | } |
102 | 300 | return true; |
103 | 328 | } |
104 | | |
105 | | // RFC 5280 section 4.2.1.4. Certificate Policies: |
106 | | // |
107 | | // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation |
108 | | // |
109 | | // PolicyInformation ::= SEQUENCE { |
110 | | // policyIdentifier CertPolicyId, |
111 | | // policyQualifiers SEQUENCE SIZE (1..MAX) OF |
112 | | // PolicyQualifierInfo OPTIONAL } |
113 | | // |
114 | | // CertPolicyId ::= OBJECT IDENTIFIER |
115 | | // |
116 | | // PolicyQualifierInfo ::= SEQUENCE { |
117 | | // policyQualifierId PolicyQualifierId, |
118 | | // qualifier ANY DEFINED BY policyQualifierId } |
119 | | // |
120 | | // PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) |
121 | | // |
122 | | // Qualifier ::= CHOICE { |
123 | | // cPSuri CPSuri, |
124 | | // userNotice UserNotice } |
125 | | // |
126 | | // CPSuri ::= IA5String |
127 | | // |
128 | | // UserNotice ::= SEQUENCE { |
129 | | // noticeRef NoticeReference OPTIONAL, |
130 | | // explicitText DisplayText OPTIONAL } |
131 | | // |
132 | | // NoticeReference ::= SEQUENCE { |
133 | | // organization DisplayText, |
134 | | // noticeNumbers SEQUENCE OF INTEGER } |
135 | | // |
136 | | // DisplayText ::= CHOICE { |
137 | | // ia5String IA5String (SIZE (1..200)), |
138 | | // visibleString VisibleString (SIZE (1..200)), |
139 | | // bmpString BMPString (SIZE (1..200)), |
140 | | // utf8String UTF8String (SIZE (1..200)) } |
141 | | bool ParseCertificatePoliciesExtensionImpl( |
142 | | der::Input extension_value, bool fail_parsing_unknown_qualifier_oids, |
143 | | std::vector<der::Input> *policy_oids, |
144 | 498 | std::vector<PolicyInformation> *policy_informations, CertErrors *errors) { |
145 | 498 | BSSL_CHECK(policy_oids); |
146 | 498 | BSSL_CHECK(errors); |
147 | | // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation |
148 | 498 | der::Parser extension_parser(extension_value); |
149 | 498 | der::Parser policies_sequence_parser; |
150 | 498 | if (!extension_parser.ReadSequence(&policies_sequence_parser)) { |
151 | 1 | return false; |
152 | 1 | } |
153 | | // Should not have trailing data after certificatePolicies sequence. |
154 | 497 | if (extension_parser.HasMore()) { |
155 | 2 | return false; |
156 | 2 | } |
157 | | // The certificatePolicies sequence should have at least 1 element. |
158 | 495 | if (!policies_sequence_parser.HasMore()) { |
159 | 2 | errors->AddError(kPoliciesEmptySequence); |
160 | 2 | return false; |
161 | 2 | } |
162 | | |
163 | 493 | policy_oids->clear(); |
164 | 493 | if (policy_informations) { |
165 | 0 | policy_informations->clear(); |
166 | 0 | } |
167 | | |
168 | 11.3k | while (policies_sequence_parser.HasMore()) { |
169 | | // PolicyInformation ::= SEQUENCE { |
170 | 10.9k | der::Parser policy_information_parser; |
171 | 10.9k | if (!policies_sequence_parser.ReadSequence(&policy_information_parser)) { |
172 | 7 | return false; |
173 | 7 | } |
174 | | // policyIdentifier CertPolicyId, |
175 | 10.9k | der::Input policy_oid; |
176 | 10.9k | if (!policy_information_parser.ReadTag(CBS_ASN1_OBJECT, &policy_oid)) { |
177 | 5 | return false; |
178 | 5 | } |
179 | | |
180 | 10.9k | policy_oids->push_back(policy_oid); |
181 | | |
182 | 10.9k | std::vector<PolicyQualifierInfo> *policy_qualifiers = nullptr; |
183 | 10.9k | if (policy_informations) { |
184 | 0 | policy_informations->emplace_back(); |
185 | 0 | policy_informations->back().policy_oid = policy_oid; |
186 | 0 | policy_qualifiers = &policy_informations->back().policy_qualifiers; |
187 | 0 | } |
188 | | |
189 | 10.9k | if (!policy_information_parser.HasMore()) { |
190 | 10.5k | continue; |
191 | 10.5k | } |
192 | | |
193 | | // policyQualifiers SEQUENCE SIZE (1..MAX) OF |
194 | | // PolicyQualifierInfo OPTIONAL } |
195 | 332 | der::Parser policy_qualifiers_sequence_parser; |
196 | 332 | if (!policy_information_parser.ReadSequence( |
197 | 332 | &policy_qualifiers_sequence_parser)) { |
198 | 1 | return false; |
199 | 1 | } |
200 | | // Should not have trailing data after policyQualifiers sequence. |
201 | 331 | if (policy_information_parser.HasMore()) { |
202 | 1 | errors->AddError(kPolicyInformationTrailingData); |
203 | 1 | return false; |
204 | 1 | } |
205 | | |
206 | | // RFC 5280 section 4.2.1.4: When qualifiers are used with the special |
207 | | // policy anyPolicy, they MUST be limited to the qualifiers identified in |
208 | | // this section. |
209 | 330 | if (!ParsePolicyQualifiers(fail_parsing_unknown_qualifier_oids || |
210 | 330 | policy_oid == der::Input(kAnyPolicyOid), |
211 | 330 | &policy_qualifiers_sequence_parser, |
212 | 330 | policy_qualifiers, errors)) { |
213 | 30 | errors->AddError(kFailedParsingPolicyQualifiers); |
214 | 30 | return false; |
215 | 30 | } |
216 | 330 | } |
217 | | |
218 | | // RFC 5280 section 4.2.1.4: A certificate policy OID MUST NOT appear more |
219 | | // than once in a certificate policies extension. |
220 | 449 | std::sort(policy_oids->begin(), policy_oids->end()); |
221 | 449 | auto dupe_policy_iter = |
222 | 449 | std::adjacent_find(policy_oids->begin(), policy_oids->end()); |
223 | 449 | if (dupe_policy_iter != policy_oids->end()) { |
224 | 250 | errors->AddError(kPoliciesDuplicateOid, |
225 | 250 | CreateCertErrorParams1Der("oid", *dupe_policy_iter)); |
226 | 250 | return false; |
227 | 250 | } |
228 | | |
229 | 199 | return true; |
230 | 449 | } |
231 | | |
232 | | } // namespace |
233 | | |
234 | 0 | PolicyInformation::PolicyInformation() = default; |
235 | 0 | PolicyInformation::~PolicyInformation() = default; |
236 | 0 | PolicyInformation::PolicyInformation(const PolicyInformation &) = default; |
237 | 0 | PolicyInformation::PolicyInformation(PolicyInformation &&) = default; |
238 | | |
239 | | bool ParseCertificatePoliciesExtension(der::Input extension_value, |
240 | | std::vector<PolicyInformation> *policies, |
241 | 0 | CertErrors *errors) { |
242 | 0 | std::vector<der::Input> unused_policy_oids; |
243 | 0 | return ParseCertificatePoliciesExtensionImpl( |
244 | 0 | extension_value, /*fail_parsing_unknown_qualifier_oids=*/false, |
245 | 0 | &unused_policy_oids, policies, errors); |
246 | 0 | } |
247 | | |
248 | | bool ParseCertificatePoliciesExtensionOids( |
249 | | der::Input extension_value, bool fail_parsing_unknown_qualifier_oids, |
250 | 498 | std::vector<der::Input> *policy_oids, CertErrors *errors) { |
251 | 498 | return ParseCertificatePoliciesExtensionImpl( |
252 | 498 | extension_value, fail_parsing_unknown_qualifier_oids, policy_oids, |
253 | 498 | nullptr, errors); |
254 | 498 | } |
255 | | |
256 | | // From RFC 5280: |
257 | | // |
258 | | // PolicyConstraints ::= SEQUENCE { |
259 | | // requireExplicitPolicy [0] SkipCerts OPTIONAL, |
260 | | // inhibitPolicyMapping [1] SkipCerts OPTIONAL } |
261 | | // |
262 | | // SkipCerts ::= INTEGER (0..MAX) |
263 | | bool ParsePolicyConstraints(der::Input policy_constraints_tlv, |
264 | 43 | ParsedPolicyConstraints *out) { |
265 | 43 | der::Parser parser(policy_constraints_tlv); |
266 | | |
267 | | // PolicyConstraints ::= SEQUENCE { |
268 | 43 | der::Parser sequence_parser; |
269 | 43 | if (!parser.ReadSequence(&sequence_parser)) { |
270 | 6 | return false; |
271 | 6 | } |
272 | | |
273 | | // RFC 5280 prohibits CAs from issuing PolicyConstraints as an empty sequence: |
274 | | // |
275 | | // Conforming CAs MUST NOT issue certificates where policy constraints |
276 | | // is an empty sequence. That is, either the inhibitPolicyMapping field |
277 | | // or the requireExplicitPolicy field MUST be present. The behavior of |
278 | | // clients that encounter an empty policy constraints field is not |
279 | | // addressed in this profile. |
280 | 37 | if (!sequence_parser.HasMore()) { |
281 | 1 | return false; |
282 | 1 | } |
283 | | |
284 | 36 | std::optional<der::Input> require_value; |
285 | 36 | if (!sequence_parser.ReadOptionalTag(CBS_ASN1_CONTEXT_SPECIFIC | 0, |
286 | 36 | &require_value)) { |
287 | 1 | return false; |
288 | 1 | } |
289 | | |
290 | 35 | if (require_value) { |
291 | 27 | uint8_t require_explicit_policy; |
292 | 27 | if (!ParseUint8(require_value.value(), &require_explicit_policy)) { |
293 | | // TODO(eroman): Surface reason for failure if length was longer than |
294 | | // uint8. |
295 | 5 | return false; |
296 | 5 | } |
297 | 22 | out->require_explicit_policy = require_explicit_policy; |
298 | 22 | } |
299 | | |
300 | 30 | std::optional<der::Input> inhibit_value; |
301 | 30 | if (!sequence_parser.ReadOptionalTag(CBS_ASN1_CONTEXT_SPECIFIC | 1, |
302 | 30 | &inhibit_value)) { |
303 | 3 | return false; |
304 | 3 | } |
305 | | |
306 | 27 | if (inhibit_value) { |
307 | 26 | uint8_t inhibit_policy_mapping; |
308 | 26 | if (!ParseUint8(inhibit_value.value(), &inhibit_policy_mapping)) { |
309 | | // TODO(eroman): Surface reason for failure if length was longer than |
310 | | // uint8. |
311 | 21 | return false; |
312 | 21 | } |
313 | 5 | out->inhibit_policy_mapping = inhibit_policy_mapping; |
314 | 5 | } |
315 | | |
316 | | // There should be no remaining data. |
317 | 6 | if (sequence_parser.HasMore() || parser.HasMore()) { |
318 | 5 | return false; |
319 | 5 | } |
320 | | |
321 | 1 | return true; |
322 | 6 | } |
323 | | |
324 | | // From RFC 5280: |
325 | | // |
326 | | // InhibitAnyPolicy ::= SkipCerts |
327 | | // |
328 | | // SkipCerts ::= INTEGER (0..MAX) |
329 | | std::optional<uint8_t> ParseInhibitAnyPolicy( |
330 | 87 | der::Input inhibit_any_policy_tlv) { |
331 | 87 | der::Parser parser(inhibit_any_policy_tlv); |
332 | 87 | std::optional<uint8_t> num_certs = std::make_optional<uint8_t>(); |
333 | | |
334 | | // TODO(eroman): Surface reason for failure if length was longer than uint8. |
335 | 87 | if (!parser.ReadUint8(&num_certs.value())) { |
336 | 81 | return std::nullopt; |
337 | 81 | } |
338 | | |
339 | | // There should be no remaining data. |
340 | 6 | if (parser.HasMore()) { |
341 | 5 | return std::nullopt; |
342 | 5 | } |
343 | | |
344 | 1 | return num_certs; |
345 | 6 | } |
346 | | |
347 | | // From RFC 5280: |
348 | | // |
349 | | // PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { |
350 | | // issuerDomainPolicy CertPolicyId, |
351 | | // subjectDomainPolicy CertPolicyId } |
352 | | bool ParsePolicyMappings(der::Input policy_mappings_tlv, |
353 | 24 | std::vector<ParsedPolicyMapping> *mappings) { |
354 | 24 | mappings->clear(); |
355 | | |
356 | 24 | der::Parser parser(policy_mappings_tlv); |
357 | | |
358 | | // PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { |
359 | 24 | der::Parser sequence_parser; |
360 | 24 | if (!parser.ReadSequence(&sequence_parser)) { |
361 | 6 | return false; |
362 | 6 | } |
363 | | |
364 | | // Must be at least 1 mapping. |
365 | 18 | if (!sequence_parser.HasMore()) { |
366 | 5 | return false; |
367 | 5 | } |
368 | | |
369 | 59 | while (sequence_parser.HasMore()) { |
370 | 56 | der::Parser mapping_parser; |
371 | 56 | if (!sequence_parser.ReadSequence(&mapping_parser)) { |
372 | 4 | return false; |
373 | 4 | } |
374 | | |
375 | 52 | ParsedPolicyMapping mapping; |
376 | 52 | if (!mapping_parser.ReadTag(CBS_ASN1_OBJECT, |
377 | 52 | &mapping.issuer_domain_policy)) { |
378 | 3 | return false; |
379 | 3 | } |
380 | 49 | if (!mapping_parser.ReadTag(CBS_ASN1_OBJECT, |
381 | 49 | &mapping.subject_domain_policy)) { |
382 | 2 | return false; |
383 | 2 | } |
384 | | |
385 | | // There shouldn't be extra unconsumed data. |
386 | 47 | if (mapping_parser.HasMore()) { |
387 | 1 | return false; |
388 | 1 | } |
389 | | |
390 | 46 | mappings->push_back(mapping); |
391 | 46 | } |
392 | | |
393 | | // There shouldn't be extra unconsumed data. |
394 | 3 | if (parser.HasMore()) { |
395 | 2 | return false; |
396 | 2 | } |
397 | | |
398 | 1 | return true; |
399 | 3 | } |
400 | | |
401 | | BSSL_NAMESPACE_END |