Chromium Code Reviews| Index: net/cert/internal/verify_name_match.cc |
| diff --git a/net/cert/internal/verify_name_match.cc b/net/cert/internal/verify_name_match.cc |
| index c34bb2c5fdb3505434d510a6f1281fb878cedb02..57d09c221e50fda3d7e1192d95c5e8f388ee7e7f 100644 |
| --- a/net/cert/internal/verify_name_match.cc |
| +++ b/net/cert/internal/verify_name_match.cc |
| @@ -22,6 +22,26 @@ namespace net { |
| namespace { |
| +// RFC 5280 section A.1: |
| +// |
| +// pkcs-9 OBJECT IDENTIFIER ::= |
| +// { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } |
| +// |
| +// id-emailAddress AttributeType ::= { pkcs-9 1 } |
| +// |
| +// In dotted form: 1.2.840.113549.1.9.1 |
| +const uint8_t kOidEmailAddress[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, |
| + 0x0D, 0x01, 0x09, 0x01}; |
| + |
| +// RFC 5280 section A.1: |
| +// |
| +// id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } |
| +// |
| +// id-at-commonName AttributeType ::= { id-at 3 } |
| +// |
| +// In dotted form: 2.5.4.3 |
| +const uint8_t kOidCommonName[] = {0x55, 0x04, 0x03}; |
| + |
| // Types of character set checking that NormalizeDirectoryString can perform. |
| enum CharsetEnforcement { |
| NO_ENFORCEMENT, |
| @@ -384,8 +404,15 @@ bool VerifyRdnMatch(der::Parser* a_parser, der::Parser* b_parser) { |
| return true; |
| } |
| -} // namespace |
| - |
| +enum NameMatchType { |
| + EXACT_MATCH, |
| + SUBTREE_MATCH, |
| +}; |
|
eroman
2015/08/26 19:56:44
newline
mattm
2015/08/29 01:37:19
Done.
|
| +// Verify that |a| matches |b|. If |match_type| is EXACT_MATCH, returns true if |
| +// they are an exact match as defined by RFC 5280 7.1. If |match_type| is |
| +// SUBTREE_MATCH, returns true if |a| is within the subtree defined by |b| as |
| +// defined by RFC 5280 7.1. |
| +// |
| // |a| and |b| are ASN.1 RDNSequence values (not including the Sequence tag), |
| // defined in RFC 5280 section 4.1.2.4: |
| // |
| @@ -396,7 +423,9 @@ bool VerifyRdnMatch(der::Parser* a_parser, der::Parser* b_parser) { |
| // |
| // RelativeDistinguishedName ::= |
| // SET SIZE (1..MAX) OF AttributeTypeAndValue |
| -bool VerifyNameMatch(const der::Input& a, const der::Input& b) { |
| +bool VerifyNameMatchInternal(const der::Input& a, |
| + const der::Input& b, |
| + NameMatchType match_type) { |
| // Empty Names are allowed. RFC 5280 section 4.1.2.4 requires "The issuer |
| // field MUST contain a non-empty distinguished name (DN)", while section |
| // 4.1.2.6 allows for the Subject to be empty in certain cases. The caller is |
| @@ -416,7 +445,12 @@ bool VerifyNameMatch(const der::Input& a, const der::Input& b) { |
| return false; |
| } |
| } |
| - if (a_rdn_sequence_counter.HasMore() || b_rdn_sequence_counter.HasMore()) |
| + // If doing exact match and either of the sequences has more elements than the |
| + // other, not a match. If doing a subtree match, the first Name may have more |
| + // RDNs than the second. |
| + if (b_rdn_sequence_counter.HasMore()) |
| + return false; |
| + if (match_type == EXACT_MATCH && a_rdn_sequence_counter.HasMore()) |
| return false; |
| // Same number of RDNs, now check if they match. |
| @@ -435,4 +469,68 @@ bool VerifyNameMatch(const der::Input& a, const der::Input& b) { |
| return true; |
| } |
| +} // namespace |
| + |
| +bool VerifyNameMatch(const der::Input& a_rdn_sequence, |
| + const der::Input& b_rdn_sequence) { |
| + return VerifyNameMatchInternal(a_rdn_sequence, b_rdn_sequence, EXACT_MATCH); |
| +} |
| + |
| +bool VerifyNameInSubtree(const der::Input& name_rdn_sequence, |
| + const der::Input& parent_rdn_sequence) { |
| + return VerifyNameMatchInternal(name_rdn_sequence, parent_rdn_sequence, |
| + SUBTREE_MATCH); |
| +} |
| + |
| +bool NameContainsEmailAddress(const der::Input& name_rdn_sequence) { |
| + der::Parser rdn_sequence_parser(name_rdn_sequence); |
| + |
| + while (rdn_sequence_parser.HasMore()) { |
| + der::Parser rdn_parser; |
| + if (!rdn_sequence_parser.ReadConstructed(der::kSet, &rdn_parser)) |
| + return false; |
| + |
| + std::vector<AttributeTypeAndValue> type_and_values; |
| + if (!ReadRdn(&rdn_parser, &type_and_values)) |
| + return false; |
| + |
| + for (const auto& type_and_value : type_and_values) { |
| + if (type_and_value.type.Equals(der::Input(kOidEmailAddress))) |
| + return true; |
| + } |
| + } |
| + |
| + return false; |
| +} |
| + |
| +std::string GetNormalizedCommonNameFromName( |
| + const der::Input& name_rdn_sequence) { |
| + der::Parser rdn_sequence_parser(name_rdn_sequence); |
| + |
| + while (rdn_sequence_parser.HasMore()) { |
| + der::Parser rdn_parser; |
| + if (!rdn_sequence_parser.ReadConstructed(der::kSet, &rdn_parser)) |
| + return std::string(); |
|
eroman
2015/08/26 19:56:44
Why return an empty string rather than return fals
mattm
2015/08/29 01:37:19
It just didn't matter much for this case, but I've
|
| + |
| + std::vector<AttributeTypeAndValue> type_and_values; |
| + if (!ReadRdn(&rdn_parser, &type_and_values)) |
| + return std::string(); |
| + |
| + for (const auto& type_and_value : type_and_values) { |
| + if (type_and_value.type.Equals(der::Input(kOidCommonName))) { |
| + if (!IsNormalizableDirectoryString(type_and_value.value_tag)) |
| + return std::string(); |
| + std::string normalized; |
| + bool ok = NormalizeValue(type_and_value.value_tag, type_and_value.value, |
| + &normalized); |
| + if (!ok) |
| + return std::string(); |
| + return normalized; |
| + } |
| + } |
| + } |
| + |
| + return std::string(); |
| +} |
| + |
| } // namespace net |