// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "net/cert/internal/certificate_policies.h" #include "net/cert/internal/cert_error_params.h" #include "net/cert/internal/cert_errors.h" #include "net/der/input.h" #include "net/der/parse_values.h" #include "net/der/parser.h" #include "net/der/tag.h" namespace net { 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"); // -- policyQualifierIds for Internet policy qualifiers // // id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } // id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } // // In dotted decimal form: 1.3.6.1.5.5.7.2.1 const der::Input CpsPointerId() { static const uint8_t cps_pointer_id[] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01}; return der::Input(cps_pointer_id); } // id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } // // In dotted decimal form: 1.3.6.1.5.5.7.2.2 const der::Input UserNoticeId() { static const uint8_t user_notice_id[] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x02}; return der::Input(user_notice_id); } // Ignores the policyQualifiers, but does some minimal correctness checking. // TODO(mattm): parse and return the policyQualifiers, since the cert viewer // still needs to display them. bool ParsePolicyQualifiers(bool restrict_to_known_qualifiers, der::Parser* policy_qualifiers_sequence_parser, 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 != CpsPointerId() && qualifier_oid != UserNoticeId()) { errors->AddError(kUnknownPolicyQualifierOid, CreateCertErrorParams1Der("oid", qualifier_oid)); return false; } // qualifier ANY DEFINED BY policyQualifierId } der::Tag tag; der::Input value; if (!policy_information_parser.ReadTagAndValue(&tag, &value)) { errors->AddError(kMissingQualifier); return false; } // Should not have trailing data after qualifier. if (policy_information_parser.HasMore()) { errors->AddError(kPolicyQualifierInfoTrailingData); return false; } } return true; } } // namespace const der::Input AnyPolicy() { // id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} // // id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } // // anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 } // // In dotted decimal form: 2.5.29.32.0 static const uint8_t any_policy[] = {0x55, 0x1D, 0x20, 0x00}; return der::Input(any_policy); } der::Input InhibitAnyPolicyOid() { // From RFC 5280: // // id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } // // In dotted notation: 2.5.29.54 static const uint8_t oid[] = {0x55, 0x1d, 0x36}; return der::Input(oid); } der::Input PolicyMappingsOid() { // From RFC 5280: // // id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } // // In dotted notation: 2.5.29.33 static const uint8_t oid[] = {0x55, 0x1d, 0x21}; return der::Input(oid); } // 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 ParseCertificatePoliciesExtension(const der::Input& extension_value, bool fail_parsing_unknown_qualifier_oids, std::vector* policies, CertErrors* errors) { 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; } policies->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; // Build the |policies| vector in sorted order (sorted on DER encoded policy // OID). Use a binary search to check whether a duplicate policy is present, // and if not, where to insert the policy to maintain the sorted order. std::vector::iterator i = std::lower_bound(policies->begin(), policies->end(), policy_oid); // RFC 5280 section 4.2.1.4: A certificate policy OID MUST NOT appear more // than once in a certificate policies extension. if (i != policies->end() && *i == policy_oid) { errors->AddError(kPoliciesDuplicateOid, CreateCertErrorParams1Der("oid", policy_oid)); return false; } policies->insert(i, policy_oid); 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 == AnyPolicy(), &policy_qualifiers_sequence_parser, errors)) { errors->AddError(kFailedParsingPolicyQualifiers); return false; } } return true; } // 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; der::Input value; if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(0), &value, &out->has_require_explicit_policy)) { return false; } if (out->has_require_explicit_policy) { if (!ParseUint8(value, &out->require_explicit_policy)) { // TODO(eroman): Surface reason for failure if length was longer than // uint8. return false; } } else { out->require_explicit_policy = 0; } if (!sequence_parser.ReadOptionalTag(der::ContextSpecificPrimitive(1), &value, &out->has_inhibit_policy_mapping)) { return false; } if (out->has_inhibit_policy_mapping) { if (!ParseUint8(value, &out->inhibit_policy_mapping)) { // TODO(eroman): Surface reason for failure if length was longer than // uint8. return false; } } else { out->inhibit_policy_mapping = 0; } // 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* 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