/src/boringssl/pki/certificate_policies.cc
Line | Count | Source |
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 | 541 | CertErrors *errors) { |
56 | 541 | 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 | 541 | if (!policy_qualifiers_sequence_parser->HasMore()) { |
64 | 3 | errors->AddError(kPolicyQualifiersEmptySequence); |
65 | 3 | return false; |
66 | 3 | } |
67 | 1.15k | while (policy_qualifiers_sequence_parser->HasMore()) { |
68 | | // PolicyQualifierInfo ::= SEQUENCE { |
69 | 657 | der::Parser policy_information_parser; |
70 | 657 | if (!policy_qualifiers_sequence_parser->ReadSequence( |
71 | 657 | &policy_information_parser)) { |
72 | 5 | return false; |
73 | 5 | } |
74 | | // policyQualifierId PolicyQualifierId, |
75 | 652 | der::Input qualifier_oid; |
76 | 652 | if (!policy_information_parser.ReadTag(CBS_ASN1_OBJECT, &qualifier_oid)) { |
77 | 3 | return false; |
78 | 3 | } |
79 | 649 | if (restrict_to_known_qualifiers && |
80 | 64 | qualifier_oid != der::Input(kCpsPointerId) && |
81 | 51 | qualifier_oid != der::Input(kUserNoticeId)) { |
82 | 30 | errors->AddError(kUnknownPolicyQualifierOid, |
83 | 30 | CreateCertErrorParams1Der("oid", qualifier_oid)); |
84 | 30 | return false; |
85 | 30 | } |
86 | | // qualifier ANY DEFINED BY policyQualifierId } |
87 | 619 | der::Input qualifier_tlv; |
88 | 619 | 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 | 617 | if (policy_information_parser.HasMore()) { |
94 | 4 | errors->AddError(kPolicyQualifierInfoTrailingData); |
95 | 4 | return false; |
96 | 4 | } |
97 | | |
98 | 613 | if (policy_qualifiers) { |
99 | 0 | policy_qualifiers->push_back({qualifier_oid, qualifier_tlv}); |
100 | 0 | } |
101 | 613 | } |
102 | 494 | return true; |
103 | 538 | } |
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 | 569 | std::vector<PolicyInformation> *policy_informations, CertErrors *errors) { |
145 | 569 | BSSL_CHECK(policy_oids); |
146 | 569 | BSSL_CHECK(errors); |
147 | | // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation |
148 | 569 | der::Parser extension_parser(extension_value); |
149 | 569 | der::Parser policies_sequence_parser; |
150 | 569 | if (!extension_parser.ReadSequence(&policies_sequence_parser)) { |
151 | 5 | return false; |
152 | 5 | } |
153 | | // Should not have trailing data after certificatePolicies sequence. |
154 | 564 | if (extension_parser.HasMore()) { |
155 | 1 | return false; |
156 | 1 | } |
157 | | // The certificatePolicies sequence should have at least 1 element. |
158 | 563 | if (!policies_sequence_parser.HasMore()) { |
159 | 8 | errors->AddError(kPoliciesEmptySequence); |
160 | 8 | return false; |
161 | 8 | } |
162 | | |
163 | 555 | policy_oids->clear(); |
164 | 555 | if (policy_informations) { |
165 | 0 | policy_informations->clear(); |
166 | 0 | } |
167 | | |
168 | 14.3k | while (policies_sequence_parser.HasMore()) { |
169 | | // PolicyInformation ::= SEQUENCE { |
170 | 13.8k | der::Parser policy_information_parser; |
171 | 13.8k | if (!policies_sequence_parser.ReadSequence(&policy_information_parser)) { |
172 | 7 | return false; |
173 | 7 | } |
174 | | // policyIdentifier CertPolicyId, |
175 | 13.8k | der::Input policy_oid; |
176 | 13.8k | if (!policy_information_parser.ReadTag(CBS_ASN1_OBJECT, &policy_oid)) { |
177 | 2 | return false; |
178 | 2 | } |
179 | | |
180 | 13.8k | policy_oids->push_back(policy_oid); |
181 | | |
182 | 13.8k | std::vector<PolicyQualifierInfo> *policy_qualifiers = nullptr; |
183 | 13.8k | 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 | 13.8k | if (!policy_information_parser.HasMore()) { |
190 | 13.2k | continue; |
191 | 13.2k | } |
192 | | |
193 | | // policyQualifiers SEQUENCE SIZE (1..MAX) OF |
194 | | // PolicyQualifierInfo OPTIONAL } |
195 | 544 | der::Parser policy_qualifiers_sequence_parser; |
196 | 544 | if (!policy_information_parser.ReadSequence( |
197 | 544 | &policy_qualifiers_sequence_parser)) { |
198 | 2 | return false; |
199 | 2 | } |
200 | | // Should not have trailing data after policyQualifiers sequence. |
201 | 542 | 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 | 541 | if (!ParsePolicyQualifiers(fail_parsing_unknown_qualifier_oids || |
210 | 541 | policy_oid == der::Input(kAnyPolicyOid), |
211 | 541 | &policy_qualifiers_sequence_parser, |
212 | 541 | policy_qualifiers, errors)) { |
213 | 47 | errors->AddError(kFailedParsingPolicyQualifiers); |
214 | 47 | return false; |
215 | 47 | } |
216 | 541 | } |
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 | 496 | std::sort(policy_oids->begin(), policy_oids->end()); |
221 | 496 | auto dupe_policy_iter = |
222 | 496 | std::adjacent_find(policy_oids->begin(), policy_oids->end()); |
223 | 496 | if (dupe_policy_iter != policy_oids->end()) { |
224 | 355 | errors->AddError(kPoliciesDuplicateOid, |
225 | 355 | CreateCertErrorParams1Der("oid", *dupe_policy_iter)); |
226 | 355 | return false; |
227 | 355 | } |
228 | | |
229 | 141 | return true; |
230 | 496 | } |
231 | | |
232 | | } // namespace |
233 | | |
234 | | bool ParseCertificatePoliciesExtension(der::Input extension_value, |
235 | | std::vector<PolicyInformation> *policies, |
236 | 0 | CertErrors *errors) { |
237 | 0 | std::vector<der::Input> unused_policy_oids; |
238 | 0 | return ParseCertificatePoliciesExtensionImpl( |
239 | 0 | extension_value, /*fail_parsing_unknown_qualifier_oids=*/false, |
240 | 0 | &unused_policy_oids, policies, errors); |
241 | 0 | } |
242 | | |
243 | | bool ParseCertificatePoliciesExtensionOids( |
244 | | der::Input extension_value, bool fail_parsing_unknown_qualifier_oids, |
245 | 569 | std::vector<der::Input> *policy_oids, CertErrors *errors) { |
246 | 569 | return ParseCertificatePoliciesExtensionImpl( |
247 | 569 | extension_value, fail_parsing_unknown_qualifier_oids, policy_oids, |
248 | 569 | nullptr, errors); |
249 | 569 | } |
250 | | |
251 | | // From RFC 5280: |
252 | | // |
253 | | // PolicyConstraints ::= SEQUENCE { |
254 | | // requireExplicitPolicy [0] SkipCerts OPTIONAL, |
255 | | // inhibitPolicyMapping [1] SkipCerts OPTIONAL } |
256 | | // |
257 | | // SkipCerts ::= INTEGER (0..MAX) |
258 | | bool ParsePolicyConstraints(der::Input policy_constraints_tlv, |
259 | 32 | ParsedPolicyConstraints *out) { |
260 | 32 | der::Parser parser(policy_constraints_tlv); |
261 | | |
262 | | // PolicyConstraints ::= SEQUENCE { |
263 | 32 | der::Parser sequence_parser; |
264 | 32 | if (!parser.ReadSequence(&sequence_parser)) { |
265 | 4 | return false; |
266 | 4 | } |
267 | | |
268 | | // RFC 5280 prohibits CAs from issuing PolicyConstraints as an empty sequence: |
269 | | // |
270 | | // Conforming CAs MUST NOT issue certificates where policy constraints |
271 | | // is an empty sequence. That is, either the inhibitPolicyMapping field |
272 | | // or the requireExplicitPolicy field MUST be present. The behavior of |
273 | | // clients that encounter an empty policy constraints field is not |
274 | | // addressed in this profile. |
275 | 28 | if (!sequence_parser.HasMore()) { |
276 | 3 | return false; |
277 | 3 | } |
278 | | |
279 | 25 | std::optional<der::Input> require_value; |
280 | 25 | if (!sequence_parser.ReadOptionalTag(CBS_ASN1_CONTEXT_SPECIFIC | 0, |
281 | 25 | &require_value)) { |
282 | 3 | return false; |
283 | 3 | } |
284 | | |
285 | 22 | if (require_value) { |
286 | 16 | uint8_t require_explicit_policy; |
287 | 16 | if (!ParseUint8(require_value.value(), &require_explicit_policy)) { |
288 | | // TODO(eroman): Surface reason for failure if length was longer than |
289 | | // uint8. |
290 | 7 | return false; |
291 | 7 | } |
292 | 9 | out->require_explicit_policy = require_explicit_policy; |
293 | 9 | } |
294 | | |
295 | 15 | std::optional<der::Input> inhibit_value; |
296 | 15 | if (!sequence_parser.ReadOptionalTag(CBS_ASN1_CONTEXT_SPECIFIC | 1, |
297 | 15 | &inhibit_value)) { |
298 | 1 | return false; |
299 | 1 | } |
300 | | |
301 | 14 | if (inhibit_value) { |
302 | 12 | uint8_t inhibit_policy_mapping; |
303 | 12 | if (!ParseUint8(inhibit_value.value(), &inhibit_policy_mapping)) { |
304 | | // TODO(eroman): Surface reason for failure if length was longer than |
305 | | // uint8. |
306 | 7 | return false; |
307 | 7 | } |
308 | 5 | out->inhibit_policy_mapping = inhibit_policy_mapping; |
309 | 5 | } |
310 | | |
311 | | // There should be no remaining data. |
312 | 7 | if (sequence_parser.HasMore() || parser.HasMore()) { |
313 | 6 | return false; |
314 | 6 | } |
315 | | |
316 | 1 | return true; |
317 | 7 | } |
318 | | |
319 | | // From RFC 5280: |
320 | | // |
321 | | // InhibitAnyPolicy ::= SkipCerts |
322 | | // |
323 | | // SkipCerts ::= INTEGER (0..MAX) |
324 | | std::optional<uint8_t> ParseInhibitAnyPolicy( |
325 | 59 | der::Input inhibit_any_policy_tlv) { |
326 | 59 | der::Parser parser(inhibit_any_policy_tlv); |
327 | 59 | std::optional<uint8_t> num_certs = std::make_optional<uint8_t>(); |
328 | | |
329 | | // TODO(eroman): Surface reason for failure if length was longer than uint8. |
330 | 59 | if (!parser.ReadUint8(&num_certs.value())) { |
331 | 55 | return std::nullopt; |
332 | 55 | } |
333 | | |
334 | | // There should be no remaining data. |
335 | 4 | if (parser.HasMore()) { |
336 | 2 | return std::nullopt; |
337 | 2 | } |
338 | | |
339 | 2 | return num_certs; |
340 | 4 | } |
341 | | |
342 | | // From RFC 5280: |
343 | | // |
344 | | // PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { |
345 | | // issuerDomainPolicy CertPolicyId, |
346 | | // subjectDomainPolicy CertPolicyId } |
347 | | bool ParsePolicyMappings(der::Input policy_mappings_tlv, |
348 | 20 | std::vector<ParsedPolicyMapping> *mappings) { |
349 | 20 | mappings->clear(); |
350 | | |
351 | 20 | der::Parser parser(policy_mappings_tlv); |
352 | | |
353 | | // PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { |
354 | 20 | der::Parser sequence_parser; |
355 | 20 | if (!parser.ReadSequence(&sequence_parser)) { |
356 | 3 | return false; |
357 | 3 | } |
358 | | |
359 | | // Must be at least 1 mapping. |
360 | 17 | if (!sequence_parser.HasMore()) { |
361 | 5 | return false; |
362 | 5 | } |
363 | | |
364 | 60 | while (sequence_parser.HasMore()) { |
365 | 57 | der::Parser mapping_parser; |
366 | 57 | if (!sequence_parser.ReadSequence(&mapping_parser)) { |
367 | 4 | return false; |
368 | 4 | } |
369 | | |
370 | 53 | ParsedPolicyMapping mapping; |
371 | 53 | if (!mapping_parser.ReadTag(CBS_ASN1_OBJECT, |
372 | 53 | &mapping.issuer_domain_policy)) { |
373 | 2 | return false; |
374 | 2 | } |
375 | 51 | if (!mapping_parser.ReadTag(CBS_ASN1_OBJECT, |
376 | 51 | &mapping.subject_domain_policy)) { |
377 | 2 | return false; |
378 | 2 | } |
379 | | |
380 | | // There shouldn't be extra unconsumed data. |
381 | 49 | if (mapping_parser.HasMore()) { |
382 | 1 | return false; |
383 | 1 | } |
384 | | |
385 | 48 | mappings->push_back(mapping); |
386 | 48 | } |
387 | | |
388 | | // There shouldn't be extra unconsumed data. |
389 | 3 | if (parser.HasMore()) { |
390 | 2 | return false; |
391 | 2 | } |
392 | | |
393 | 1 | return true; |
394 | 3 | } |
395 | | |
396 | | BSSL_NAMESPACE_END |