|  | // Copyright 2015 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include "certificate_policies.h" | 
|  |  | 
|  | #include "cert_error_params.h" | 
|  | #include "cert_errors.h" | 
|  | #include "input.h" | 
|  | #include "parse_values.h" | 
|  | #include "parser.h" | 
|  | #include "tag.h" | 
|  |  | 
|  | namespace bssl { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // --------------------------------------------------------------- | 
|  | // Errors | 
|  | // --------------------------------------------------------------- | 
|  |  | 
|  | DEFINE_CERT_ERROR_ID(kPolicyQualifiersEmptySequence, | 
|  | "The policy qualifiers SEQUENCE is empty"); | 
|  | DEFINE_CERT_ERROR_ID(kUnknownPolicyQualifierOid, | 
|  | "Unknown policy qualifier OID (not CPS or User Notice)"); | 
|  | DEFINE_CERT_ERROR_ID(kPoliciesEmptySequence, "Policies is an empty SEQUENCE"); | 
|  | DEFINE_CERT_ERROR_ID(kPoliciesDuplicateOid, "Policies contains duplicate OIDs"); | 
|  | DEFINE_CERT_ERROR_ID(kPolicyInformationTrailingData, | 
|  | "PolicyInformation has trailing data"); | 
|  | DEFINE_CERT_ERROR_ID(kFailedParsingPolicyQualifiers, | 
|  | "Failed parsing policy qualifiers"); | 
|  | DEFINE_CERT_ERROR_ID(kMissingQualifier, | 
|  | "PolicyQualifierInfo is missing qualifier"); | 
|  | DEFINE_CERT_ERROR_ID(kPolicyQualifierInfoTrailingData, | 
|  | "PolicyQualifierInfo has trailing data"); | 
|  |  | 
|  | // Minimally parse policyQualifiers, storing in |policy_qualifiers| if non-null. | 
|  | // If a policy qualifier other than User Notice/CPS is present, parsing | 
|  | // will fail if |restrict_to_known_qualifiers| was set to true. | 
|  | bool ParsePolicyQualifiers(bool restrict_to_known_qualifiers, | 
|  | der::Parser* policy_qualifiers_sequence_parser, | 
|  | std::vector<PolicyQualifierInfo>* policy_qualifiers, | 
|  | CertErrors* errors) { | 
|  | DCHECK(errors); | 
|  |  | 
|  | // If it is present, the policyQualifiers sequence should have at least 1 | 
|  | // element. | 
|  | // | 
|  | //      policyQualifiers   SEQUENCE SIZE (1..MAX) OF | 
|  | //                              PolicyQualifierInfo OPTIONAL } | 
|  | if (!policy_qualifiers_sequence_parser->HasMore()) { | 
|  | errors->AddError(kPolicyQualifiersEmptySequence); | 
|  | return false; | 
|  | } | 
|  | while (policy_qualifiers_sequence_parser->HasMore()) { | 
|  | // PolicyQualifierInfo ::= SEQUENCE { | 
|  | der::Parser policy_information_parser; | 
|  | if (!policy_qualifiers_sequence_parser->ReadSequence( | 
|  | &policy_information_parser)) { | 
|  | return false; | 
|  | } | 
|  | //      policyQualifierId  PolicyQualifierId, | 
|  | der::Input qualifier_oid; | 
|  | if (!policy_information_parser.ReadTag(der::kOid, &qualifier_oid)) | 
|  | return false; | 
|  | if (restrict_to_known_qualifiers && | 
|  | qualifier_oid != der::Input(kCpsPointerId) && | 
|  | qualifier_oid != der::Input(kUserNoticeId)) { | 
|  | errors->AddError(kUnknownPolicyQualifierOid, | 
|  | CreateCertErrorParams1Der("oid", qualifier_oid)); | 
|  | return false; | 
|  | } | 
|  | //      qualifier          ANY DEFINED BY policyQualifierId } | 
|  | der::Input qualifier_tlv; | 
|  | if (!policy_information_parser.ReadRawTLV(&qualifier_tlv)) { | 
|  | errors->AddError(kMissingQualifier); | 
|  | return false; | 
|  | } | 
|  | // Should not have trailing data after qualifier. | 
|  | if (policy_information_parser.HasMore()) { | 
|  | errors->AddError(kPolicyQualifierInfoTrailingData); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (policy_qualifiers) | 
|  | policy_qualifiers->push_back({qualifier_oid, qualifier_tlv}); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // RFC 5280 section 4.2.1.4.  Certificate Policies: | 
|  | // | 
|  | // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation | 
|  | // | 
|  | // PolicyInformation ::= SEQUENCE { | 
|  | //      policyIdentifier   CertPolicyId, | 
|  | //      policyQualifiers   SEQUENCE SIZE (1..MAX) OF | 
|  | //                              PolicyQualifierInfo OPTIONAL } | 
|  | // | 
|  | // CertPolicyId ::= OBJECT IDENTIFIER | 
|  | // | 
|  | // PolicyQualifierInfo ::= SEQUENCE { | 
|  | //      policyQualifierId  PolicyQualifierId, | 
|  | //      qualifier          ANY DEFINED BY policyQualifierId } | 
|  | // | 
|  | // PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) | 
|  | // | 
|  | // Qualifier ::= CHOICE { | 
|  | //      cPSuri           CPSuri, | 
|  | //      userNotice       UserNotice } | 
|  | // | 
|  | // CPSuri ::= IA5String | 
|  | // | 
|  | // UserNotice ::= SEQUENCE { | 
|  | //      noticeRef        NoticeReference OPTIONAL, | 
|  | //      explicitText     DisplayText OPTIONAL } | 
|  | // | 
|  | // NoticeReference ::= SEQUENCE { | 
|  | //      organization     DisplayText, | 
|  | //      noticeNumbers    SEQUENCE OF INTEGER } | 
|  | // | 
|  | // DisplayText ::= CHOICE { | 
|  | //      ia5String        IA5String      (SIZE (1..200)), | 
|  | //      visibleString    VisibleString  (SIZE (1..200)), | 
|  | //      bmpString        BMPString      (SIZE (1..200)), | 
|  | //      utf8String       UTF8String     (SIZE (1..200)) } | 
|  | bool ParseCertificatePoliciesExtensionImpl( | 
|  | const der::Input& extension_value, | 
|  | bool fail_parsing_unknown_qualifier_oids, | 
|  | std::vector<der::Input>* policy_oids, | 
|  | std::vector<PolicyInformation>* policy_informations, | 
|  | CertErrors* errors) { | 
|  | DCHECK(policy_oids); | 
|  | DCHECK(errors); | 
|  | // certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation | 
|  | der::Parser extension_parser(extension_value); | 
|  | der::Parser policies_sequence_parser; | 
|  | if (!extension_parser.ReadSequence(&policies_sequence_parser)) | 
|  | return false; | 
|  | // Should not have trailing data after certificatePolicies sequence. | 
|  | if (extension_parser.HasMore()) | 
|  | return false; | 
|  | // The certificatePolicies sequence should have at least 1 element. | 
|  | if (!policies_sequence_parser.HasMore()) { | 
|  | errors->AddError(kPoliciesEmptySequence); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | policy_oids->clear(); | 
|  | if (policy_informations) | 
|  | policy_informations->clear(); | 
|  |  | 
|  | while (policies_sequence_parser.HasMore()) { | 
|  | // PolicyInformation ::= SEQUENCE { | 
|  | der::Parser policy_information_parser; | 
|  | if (!policies_sequence_parser.ReadSequence(&policy_information_parser)) | 
|  | return false; | 
|  | //      policyIdentifier   CertPolicyId, | 
|  | der::Input policy_oid; | 
|  | if (!policy_information_parser.ReadTag(der::kOid, &policy_oid)) | 
|  | return false; | 
|  |  | 
|  | policy_oids->push_back(policy_oid); | 
|  |  | 
|  | std::vector<PolicyQualifierInfo>* policy_qualifiers = nullptr; | 
|  | if (policy_informations) { | 
|  | policy_informations->emplace_back(); | 
|  | policy_informations->back().policy_oid = policy_oid; | 
|  | policy_qualifiers = &policy_informations->back().policy_qualifiers; | 
|  | } | 
|  |  | 
|  | if (!policy_information_parser.HasMore()) | 
|  | continue; | 
|  |  | 
|  | //      policyQualifiers   SEQUENCE SIZE (1..MAX) OF | 
|  | //                              PolicyQualifierInfo OPTIONAL } | 
|  | der::Parser policy_qualifiers_sequence_parser; | 
|  | if (!policy_information_parser.ReadSequence( | 
|  | &policy_qualifiers_sequence_parser)) { | 
|  | return false; | 
|  | } | 
|  | // Should not have trailing data after policyQualifiers sequence. | 
|  | if (policy_information_parser.HasMore()) { | 
|  | errors->AddError(kPolicyInformationTrailingData); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // RFC 5280 section 4.2.1.4: When qualifiers are used with the special | 
|  | // policy anyPolicy, they MUST be limited to the qualifiers identified in | 
|  | // this section. | 
|  | if (!ParsePolicyQualifiers(fail_parsing_unknown_qualifier_oids || | 
|  | policy_oid == der::Input(kAnyPolicyOid), | 
|  | &policy_qualifiers_sequence_parser, | 
|  | policy_qualifiers, errors)) { | 
|  | errors->AddError(kFailedParsingPolicyQualifiers); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // RFC 5280 section 4.2.1.4: A certificate policy OID MUST NOT appear more | 
|  | // than once in a certificate policies extension. | 
|  | std::sort(policy_oids->begin(), policy_oids->end()); | 
|  | auto dupe_policy_iter = | 
|  | std::adjacent_find(policy_oids->begin(), policy_oids->end()); | 
|  | if (dupe_policy_iter != policy_oids->end()) { | 
|  | errors->AddError(kPoliciesDuplicateOid, | 
|  | CreateCertErrorParams1Der("oid", *dupe_policy_iter)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | PolicyInformation::PolicyInformation() = default; | 
|  | PolicyInformation::~PolicyInformation() = default; | 
|  | PolicyInformation::PolicyInformation(const PolicyInformation&) = default; | 
|  | PolicyInformation::PolicyInformation(PolicyInformation&&) = default; | 
|  |  | 
|  | bool ParseCertificatePoliciesExtension(const der::Input& extension_value, | 
|  | std::vector<PolicyInformation>* policies, | 
|  | CertErrors* errors) { | 
|  | std::vector<der::Input> unused_policy_oids; | 
|  | return ParseCertificatePoliciesExtensionImpl( | 
|  | extension_value, /*fail_parsing_unknown_qualifier_oids=*/false, | 
|  | &unused_policy_oids, policies, errors); | 
|  | } | 
|  |  | 
|  | bool ParseCertificatePoliciesExtensionOids( | 
|  | const der::Input& extension_value, | 
|  | bool fail_parsing_unknown_qualifier_oids, | 
|  | std::vector<der::Input>* policy_oids, | 
|  | CertErrors* errors) { | 
|  | return ParseCertificatePoliciesExtensionImpl( | 
|  | extension_value, fail_parsing_unknown_qualifier_oids, policy_oids, | 
|  | nullptr, errors); | 
|  | } | 
|  |  | 
|  | // From RFC 5280: | 
|  | // | 
|  | //   PolicyConstraints ::= SEQUENCE { | 
|  | //        requireExplicitPolicy           [0] SkipCerts OPTIONAL, | 
|  | //        inhibitPolicyMapping            [1] SkipCerts OPTIONAL } | 
|  | // | 
|  | //   SkipCerts ::= INTEGER (0..MAX) | 
|  | bool ParsePolicyConstraints(const der::Input& policy_constraints_tlv, | 
|  | ParsedPolicyConstraints* out) { | 
|  | der::Parser parser(policy_constraints_tlv); | 
|  |  | 
|  | //   PolicyConstraints ::= SEQUENCE { | 
|  | der::Parser sequence_parser; | 
|  | if (!parser.ReadSequence(&sequence_parser)) | 
|  | return false; | 
|  |  | 
|  | // RFC 5280 prohibits CAs from issuing PolicyConstraints as an empty sequence: | 
|  | // | 
|  | //   Conforming CAs MUST NOT issue certificates where policy constraints | 
|  | //   is an empty sequence.  That is, either the inhibitPolicyMapping field | 
|  | //   or the requireExplicitPolicy field MUST be present.  The behavior of | 
|  | //   clients that encounter an empty policy constraints field is not | 
|  | //   addressed in this profile. | 
|  | if (!sequence_parser.HasMore()) | 
|  | return false; | 
|  |  | 
|  | std::optional<der::Input> require_value; | 
|  | if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(0), | 
|  | &require_value)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (require_value) { | 
|  | uint8_t require_explicit_policy; | 
|  | if (!ParseUint8(require_value.value(), &require_explicit_policy)) { | 
|  | // TODO(eroman): Surface reason for failure if length was longer than | 
|  | // uint8. | 
|  | return false; | 
|  | } | 
|  | out->require_explicit_policy = require_explicit_policy; | 
|  | } | 
|  |  | 
|  | std::optional<der::Input> inhibit_value; | 
|  | if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1), | 
|  | &inhibit_value)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (inhibit_value) { | 
|  | uint8_t inhibit_policy_mapping; | 
|  | if (!ParseUint8(inhibit_value.value(), &inhibit_policy_mapping)) { | 
|  | // TODO(eroman): Surface reason for failure if length was longer than | 
|  | // uint8. | 
|  | return false; | 
|  | } | 
|  | out->inhibit_policy_mapping = inhibit_policy_mapping; | 
|  | } | 
|  |  | 
|  | // There should be no remaining data. | 
|  | if (sequence_parser.HasMore() || parser.HasMore()) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // From RFC 5280: | 
|  | // | 
|  | //   InhibitAnyPolicy ::= SkipCerts | 
|  | // | 
|  | //   SkipCerts ::= INTEGER (0..MAX) | 
|  | bool ParseInhibitAnyPolicy(const der::Input& inhibit_any_policy_tlv, | 
|  | uint8_t* num_certs) { | 
|  | der::Parser parser(inhibit_any_policy_tlv); | 
|  |  | 
|  | // TODO(eroman): Surface reason for failure if length was longer than uint8. | 
|  | if (!parser.ReadUint8(num_certs)) | 
|  | return false; | 
|  |  | 
|  | // There should be no remaining data. | 
|  | if (parser.HasMore()) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // From RFC 5280: | 
|  | // | 
|  | //   PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { | 
|  | //        issuerDomainPolicy      CertPolicyId, | 
|  | //        subjectDomainPolicy     CertPolicyId } | 
|  | bool ParsePolicyMappings(const der::Input& policy_mappings_tlv, | 
|  | std::vector<ParsedPolicyMapping>* mappings) { | 
|  | mappings->clear(); | 
|  |  | 
|  | der::Parser parser(policy_mappings_tlv); | 
|  |  | 
|  | //   PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { | 
|  | der::Parser sequence_parser; | 
|  | if (!parser.ReadSequence(&sequence_parser)) | 
|  | return false; | 
|  |  | 
|  | // Must be at least 1 mapping. | 
|  | if (!sequence_parser.HasMore()) | 
|  | return false; | 
|  |  | 
|  | while (sequence_parser.HasMore()) { | 
|  | der::Parser mapping_parser; | 
|  | if (!sequence_parser.ReadSequence(&mapping_parser)) | 
|  | return false; | 
|  |  | 
|  | ParsedPolicyMapping mapping; | 
|  | if (!mapping_parser.ReadTag(der::kOid, &mapping.issuer_domain_policy)) | 
|  | return false; | 
|  | if (!mapping_parser.ReadTag(der::kOid, &mapping.subject_domain_policy)) | 
|  | return false; | 
|  |  | 
|  | // There shouldn't be extra unconsumed data. | 
|  | if (mapping_parser.HasMore()) | 
|  | return false; | 
|  |  | 
|  | mappings->push_back(mapping); | 
|  | } | 
|  |  | 
|  | // There shouldn't be extra unconsumed data. | 
|  | if (parser.HasMore()) | 
|  | return false; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace net |