diff options
author | snej@chromium.org <snej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-26 21:37:54 +0000 |
---|---|---|
committer | snej@chromium.org <snej@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-26 21:37:54 +0000 |
commit | d3002098f8982ac54901ce9fdc8e81cdb725d8e6 (patch) | |
tree | 4cd95eb31701c7b27e0518d46f1ae0d6a62d05ab /net | |
parent | af3719864762ddf3e061ba24129d054e3a722930 (diff) | |
download | chromium_src-d3002098f8982ac54901ce9fdc8e81cdb725d8e6.zip chromium_src-d3002098f8982ac54901ce9fdc8e81cdb725d8e6.tar.gz chromium_src-d3002098f8982ac54901ce9fdc8e81cdb725d8e6.tar.bz2 |
Mac: Make client-cert picker only show certs the server will accept.
BUG=38691
TEST=manual testing with various sites
Review URL: http://codereview.chromium.org/1128008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42822 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/base/x509_cert_types.cc | 112 | ||||
-rw-r--r-- | net/base/x509_cert_types.h | 133 | ||||
-rw-r--r-- | net/base/x509_cert_types_mac.cc | 293 | ||||
-rw-r--r-- | net/base/x509_cert_types_unittest.cc | 343 | ||||
-rw-r--r-- | net/base/x509_certificate.cc | 49 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 96 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 198 | ||||
-rw-r--r-- | net/net.gyp | 4 | ||||
-rw-r--r-- | net/socket/ssl_client_socket_mac.cc | 29 |
9 files changed, 1022 insertions, 235 deletions
diff --git a/net/base/x509_cert_types.cc b/net/base/x509_cert_types.cc new file mode 100644 index 0000000..8329216 --- /dev/null +++ b/net/base/x509_cert_types.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2006-2010 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 "net/base/x509_cert_types.h" + +#include "net/base/x509_certificate.h" +#include "base/logging.h" + +namespace net { + +bool match(const std::string &str, const std::string &against) { + // TODO(snej): Use the full matching rules specified in RFC 5280 sec. 7.1 + // including trimming and case-folding: <http://www.ietf.org/rfc/rfc5280.txt>. + return against == str; +} + +bool match(const std::vector<std::string> &rdn1, + const std::vector<std::string> &rdn2) { + // "Two relative distinguished names RDN1 and RDN2 match if they have the + // same number of naming attributes and for each naming attribute in RDN1 + // there is a matching naming attribute in RDN2." --RFC 5280 sec. 7.1. + if (rdn1.size() != rdn2.size()) + return false; + for (unsigned i1 = 0; i1 < rdn1.size(); ++i1) { + unsigned i2; + for (i2 = 0; i2 < rdn2.size(); ++i2) { + if (match(rdn1[i1], rdn2[i2])) + break; + } + if (i2 == rdn2.size()) + return false; + } + return true; +} + + +bool CertPrincipal::Matches(const CertPrincipal& against) const { + return match(common_name, against.common_name) && + match(common_name, against.common_name) && + match(locality_name, against.locality_name) && + match(state_or_province_name, against.state_or_province_name) && + match(country_name, against.country_name) && + match(street_addresses, against.street_addresses) && + match(organization_names, against.organization_names) && + match(organization_unit_names, against.organization_unit_names) && + match(domain_components, against.domain_components); +} + +std::ostream& operator<<(std::ostream& s, const CertPrincipal& p) { + s << "CertPrincipal["; + if (!p.common_name.empty()) + s << "cn=\"" << p.common_name << "\" "; + for (unsigned i = 0; i < p.street_addresses.size(); ++i) + s << "street=\"" << p.street_addresses[i] << "\" "; + if (!p.locality_name.empty()) + s << "l=\"" << p.locality_name << "\" "; + for (unsigned i = 0; i < p.organization_names.size(); ++i) + s << "o=\"" << p.organization_names[i] << "\" "; + for (unsigned i = 0; i < p.organization_unit_names.size(); ++i) + s << "ou=\"" << p.organization_unit_names[i] << "\" "; + if (!p.state_or_province_name.empty()) + s << "st=\"" << p.state_or_province_name << "\" "; + if (!p.country_name.empty()) + s << "c=\"" << p.country_name << "\" "; + for (unsigned i = 0; i < p.domain_components.size(); ++i) + s << "dc=\"" << p.domain_components[i] << "\" "; + return s << "]"; +} + +CertPolicy::Judgment CertPolicy::Check( + X509Certificate* cert) const { + // It shouldn't matter which set we check first, but we check denied first + // in case something strange has happened. + + if (denied_.find(cert->fingerprint()) != denied_.end()) { + // DCHECK that the order didn't matter. + DCHECK(allowed_.find(cert->fingerprint()) == allowed_.end()); + return DENIED; + } + + if (allowed_.find(cert->fingerprint()) != allowed_.end()) { + // DCHECK that the order didn't matter. + DCHECK(denied_.find(cert->fingerprint()) == denied_.end()); + return ALLOWED; + } + + // We don't have a policy for this cert. + return UNKNOWN; +} + +void CertPolicy::Allow(X509Certificate* cert) { + // Put the cert in the allowed set and (maybe) remove it from the denied set. + denied_.erase(cert->fingerprint()); + allowed_.insert(cert->fingerprint()); +} + +void CertPolicy::Deny(X509Certificate* cert) { + // Put the cert in the denied set and (maybe) remove it from the allowed set. + allowed_.erase(cert->fingerprint()); + denied_.insert(cert->fingerprint()); +} + +bool CertPolicy::HasAllowedCert() const { + return !allowed_.empty(); +} + +bool CertPolicy::HasDeniedCert() const { + return !denied_.empty(); +} + +} // namespace net diff --git a/net/base/x509_cert_types.h b/net/base/x509_cert_types.h new file mode 100644 index 0000000..cf23f58 --- /dev/null +++ b/net/base/x509_cert_types.h @@ -0,0 +1,133 @@ +// Copyright (c) 2007-2010 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. + +#ifndef NET_BASE_X509_TYPES_H_ +#define NET_BASE_X509_TYPES_H_ + +#include <string.h> + +#include <iostream> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/ref_counted.h" +#include "base/singleton.h" +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +#if defined(OS_WIN) +#include <windows.h> +#include <wincrypt.h> +#elif defined(OS_MACOSX) +#include <Security/x509defs.h> +#elif defined(USE_NSS) +// Forward declaration; real one in <cert.h> +struct CERTCertificateStr; +#endif + +namespace net { + +class X509Certificate; + +// SHA-1 fingerprint (160 bits) of a certificate. +struct SHA1Fingerprint { + bool Equals(const SHA1Fingerprint& other) const { + return memcmp(data, other.data, sizeof(data)) == 0; + } + + unsigned char data[20]; +}; + +class SHA1FingerprintLessThan + : public std::binary_function<SHA1Fingerprint, SHA1Fingerprint, bool> { + public: + bool operator() (const SHA1Fingerprint& lhs, const SHA1Fingerprint& rhs) const; +}; + +// CertPrincipal represents the issuer or subject field of an X.509 certificate. +struct CertPrincipal { + CertPrincipal() { } + explicit CertPrincipal(const std::string& name) : common_name(name) { } + + // Parses a BER-format DistinguishedName. + bool ParseDistinguishedName(const void* ber_name_data, size_t length); + +#if defined(OS_MACOSX) + // Parses a CSSM_X509_NAME struct. + void Parse(const CSSM_X509_NAME* name); +#endif + + // Returns true if all attributes of the two objects match, + // where "match" is defined in RFC 5280 sec. 7.1. + bool Matches(const CertPrincipal& against) const; + + // The different attributes for a principal. They may be "". + // Note that some of them can have several values. + + std::string common_name; + std::string locality_name; + std::string state_or_province_name; + std::string country_name; + + std::vector<std::string> street_addresses; + std::vector<std::string> organization_names; + std::vector<std::string> organization_unit_names; + std::vector<std::string> domain_components; +}; + +// Writes a human-readable description of a CertPrincipal, for debugging. +std::ostream& operator<<(std::ostream& s, const CertPrincipal& p); + +// This class is useful for maintaining policies about which certificates are +// permitted or forbidden for a particular purpose. +class CertPolicy { + public: + // The judgments this policy can reach. + enum Judgment { + // We don't have policy information for this certificate. + UNKNOWN, + + // This certificate is allowed. + ALLOWED, + + // This certificate is denied. + DENIED, + }; + + // Returns the judgment this policy makes about this certificate. + Judgment Check(X509Certificate* cert) const; + + // Causes the policy to allow this certificate. + void Allow(X509Certificate* cert); + + // Causes the policy to deny this certificate. + void Deny(X509Certificate* cert); + + // Returns true if this policy has allowed at least one certificate. + bool HasAllowedCert() const; + + // Returns true if this policy has denied at least one certificate. + bool HasDeniedCert() const; + + private: + // The set of fingerprints of allowed certificates. + std::set<SHA1Fingerprint, SHA1FingerprintLessThan> allowed_; + + // The set of fingerprints of denied certificates. + std::set<SHA1Fingerprint, SHA1FingerprintLessThan> denied_; +}; + +#if defined(OS_MACOSX) +// Compares two OIDs by value. +inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) { + return oid1->Length == oid2->Length && + (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0); +} +#endif + +} // namespace net + +#endif // NET_BASE_X509_TYPES_H_ diff --git a/net/base/x509_cert_types_mac.cc b/net/base/x509_cert_types_mac.cc new file mode 100644 index 0000000..504dde5 --- /dev/null +++ b/net/base/x509_cert_types_mac.cc @@ -0,0 +1,293 @@ +// Copyright (c) 2006-2010 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 "net/base/x509_cert_types.h" + +#include <CoreServices/CoreServices.h> +#include <Security/Security.h> +#include <Security/SecAsn1Coder.h> + +#include "base/logging.h" +#include "base/i18n/icu_string_conversions.h" +#include "base/utf_string_conversions.h" + +namespace net { + +static const CSSM_OID* kOIDs[] = { + &CSSMOID_CommonName, + &CSSMOID_LocalityName, + &CSSMOID_StateProvinceName, + &CSSMOID_CountryName, + &CSSMOID_StreetAddress, + &CSSMOID_OrganizationName, + &CSSMOID_OrganizationalUnitName, + &CSSMOID_DNQualifier // This should be "DC" but is undoubtedly wrong. +}; // TODO(avi): Find the right OID. + +// Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.) +static std::string DataToString(CSSM_DATA data); + +// Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8. +static std::string Latin1DataToUTF8String(CSSM_DATA data); + +// Converts big-endian UTF-16 to UTF-8 in a std::string. +// Note: The byte-order flipping is done in place on the input buffer! +static bool UTF16BigEndianToUTF8(char16* chars, size_t length, + std::string* out_string); + +// Converts big-endian UTF-32 to UTF-8 in a std::string. +// Note: The byte-order flipping is done in place on the input buffer! +static bool UTF32BigEndianToUTF8(char32* chars, size_t length, + std::string* out_string); + +// Adds a type+value pair to the appropriate vector from a C array. +// The array is keyed by the matching OIDs from kOIDS[]. + static void AddTypeValuePair(const CSSM_OID type, + const std::string& value, + std::vector<std::string>* values[]); + +// Stores the first string of the vector, if any, to *single_value. +static void SetSingle(const std::vector<std::string> &values, + std::string* single_value); + + +void CertPrincipal::Parse(const CSSM_X509_NAME* name) { + std::vector<std::string> common_names, locality_names, state_names, + country_names; + + std::vector<std::string>* values[] = { + &common_names, &locality_names, + &state_names, &country_names, + &(this->street_addresses), + &(this->organization_names), + &(this->organization_unit_names), + &(this->domain_components) + }; + DCHECK(arraysize(kOIDs) == arraysize(values)); + + for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) { + CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn]; + for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) { + CSSM_X509_TYPE_VALUE_PAIR pair_struct = + rdn_struct.AttributeTypeAndValue[pair]; + AddTypeValuePair(pair_struct.type, + DataToString(pair_struct.value), + values); + } + } + + SetSingle(common_names, &this->common_name); + SetSingle(locality_names, &this->locality_name); + SetSingle(state_names, &this->state_or_province_name); + SetSingle(country_names, &this->country_name); +} + + +// The following structs and templates work with Apple's very arcane and under- +// documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1 +// decoder: +// http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html + +// These are used to parse the contents of a raw +// BER DistinguishedName structure. + +struct KeyValuePair { + CSSM_OID key; + int value_type; + CSSM_DATA value; + + enum { + kTypeOther = 0, + kTypePrintableString, + kTypeIA5String, + kTypeT61String, + kTypeUTF8String, + kTypeBMPString, + kTypeUniversalString, + }; +}; + +static const SecAsn1Template kStringValueTemplate[] = { + { SEC_ASN1_CHOICE, offsetof(KeyValuePair, value_type), }, + { SEC_ASN1_PRINTABLE_STRING, + offsetof(KeyValuePair, value), 0, KeyValuePair::kTypePrintableString }, + { SEC_ASN1_IA5_STRING, + offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeIA5String }, + { SEC_ASN1_T61_STRING, + offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeT61String }, + { SEC_ASN1_UTF8_STRING, + offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUTF8String }, + { SEC_ASN1_BMP_STRING, + offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeBMPString }, + { SEC_ASN1_UNIVERSAL_STRING, + offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUniversalString }, + { 0, } +}; + +static const SecAsn1Template kKeyValuePairTemplate[] = { + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(KeyValuePair) }, + { SEC_ASN1_OBJECT_ID, offsetof(KeyValuePair, key), }, + { SEC_ASN1_INLINE, 0, &kStringValueTemplate, }, + { 0, } +}; + +struct KeyValuePairs { + KeyValuePair* pairs; +}; + +static const SecAsn1Template kKeyValuePairSetTemplate[] = { + { SEC_ASN1_SET_OF, offsetof(KeyValuePairs,pairs), + kKeyValuePairTemplate, sizeof(KeyValuePairs) } +}; + +struct X509Name { + KeyValuePairs** pairs_list; +}; + +static const SecAsn1Template kNameTemplate[] = { + { SEC_ASN1_SEQUENCE_OF, offsetof(X509Name,pairs_list), + kKeyValuePairSetTemplate, sizeof(X509Name) } +}; + +bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data, + size_t length) { + DCHECK(ber_name_data); + + // First parse the BER |name_data| into the above structs. + SecAsn1CoderRef coder = NULL; + SecAsn1CoderCreate(&coder); + DCHECK(coder); + X509Name* name = NULL; + OSStatus err = SecAsn1Decode(coder, ber_name_data, length, kNameTemplate, + &name); + if (err) { + LOG(ERROR) << "SecAsn1Decode returned " << err << "; name=" << name; + SecAsn1CoderRelease(coder); + return false; + } + + // Now scan the structs and add the values to my string vectors. + // I don't store multiple common/locality/state/country names, so use + // temporary vectors for those. + std::vector<std::string> common_names, locality_names, state_names, + country_names; + std::vector<std::string>* values[] = { + &common_names, &locality_names, + &state_names, &country_names, + &this->street_addresses, + &this->organization_names, + &this->organization_unit_names, + &this->domain_components + }; + DCHECK(arraysize(kOIDs) == arraysize(values)); + + for (int rdn=0; name[rdn].pairs_list; ++rdn) { + KeyValuePair *pair; + for (int pair_index = 0; + NULL != (pair = name[rdn].pairs_list[0][pair_index].pairs); + ++pair_index) { + switch (pair->value_type) { + case KeyValuePair::kTypeIA5String: // ASCII (that means 7-bit!) + case KeyValuePair::kTypePrintableString: // a subset of ASCII + case KeyValuePair::kTypeUTF8String: // UTF-8 + AddTypeValuePair(pair->key, DataToString(pair->value), values); + break; + case KeyValuePair::kTypeT61String: // T61, pretend it's Latin-1 + AddTypeValuePair(pair->key, + Latin1DataToUTF8String(pair->value), + values); + break; + case KeyValuePair::kTypeBMPString: { // UTF-16, big-endian + std::string value; + UTF16BigEndianToUTF8(reinterpret_cast<char16*>(pair->value.Data), + pair->value.Length / sizeof(char16), + &value); + AddTypeValuePair(pair->key, value, values); + break; + } + case KeyValuePair::kTypeUniversalString: { // UTF-32, big-endian + std::string value; + UTF32BigEndianToUTF8(reinterpret_cast<char32*>(pair->value.Data), + pair->value.Length / sizeof(char32), + &value); + AddTypeValuePair(pair->key, value, values); + break; + } + default: + DCHECK_EQ(pair->value_type, KeyValuePair::kTypeOther); + // We don't know what data type this is, but we'll store it as a blob. + // Displaying the string may not work, but at least it can be compared + // byte-for-byte by a Matches() call. + AddTypeValuePair(pair->key, DataToString(pair->value), values); + break; + } + } + } + + SetSingle(common_names, &this->common_name); + SetSingle(locality_names, &this->locality_name); + SetSingle(state_names, &this->state_or_province_name); + SetSingle(country_names, &this->country_name); + + // Releasing |coder| frees all the memory pointed to via |name|. + SecAsn1CoderRelease(coder); + return true; +} + + +// SUBROUTINES: + +static std::string DataToString(CSSM_DATA data) { + return std::string( + reinterpret_cast<std::string::value_type*>(data.Data), + data.Length); +} + +static std::string Latin1DataToUTF8String(CSSM_DATA data) { + string16 utf16; + if (!CodepageToUTF16(DataToString(data), base::kCodepageLatin1, + base::OnStringConversionError::FAIL, &utf16)) + return ""; + return UTF16ToUTF8(utf16); +} + +bool UTF16BigEndianToUTF8(char16* chars, size_t length, + std::string* out_string) { + for (size_t i = 0; i < length; i++) + chars[i] = EndianU16_BtoN(chars[i]); + return UTF16ToUTF8(chars, length, out_string); +} + +bool UTF32BigEndianToUTF8(char32* chars, size_t length, + std::string* out_string) { + for (size_t i = 0; i < length; i++) + chars[i] = EndianS32_BtoN(chars[i]); +#if defined(WCHAR_T_IS_UTF32) + return WideToUTF8(reinterpret_cast<const wchar_t*>(chars), + length, out_string); +#else +#error This code doesn't handle 16-bit wchar_t. +#endif +} + + static void AddTypeValuePair(const CSSM_OID type, + const std::string& value, + std::vector<std::string>* values[]) { + for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { + if (CSSMOIDEqual(&type, kOIDs[oid])) { + values[oid]->push_back(value); + break; + } + } +} + +static void SetSingle(const std::vector<std::string> &values, + std::string* single_value) { + // We don't expect to have more than one CN, L, S, and C. + LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values"; + if (values.size() > 0) + *single_value = values[0]; +} + +} // namespace net diff --git a/net/base/x509_cert_types_unittest.cc b/net/base/x509_cert_types_unittest.cc new file mode 100644 index 0000000..27104cc --- /dev/null +++ b/net/base/x509_cert_types_unittest.cc @@ -0,0 +1,343 @@ +// Copyright (c) 2010 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 "net/base/x509_cert_types.h" +#include "testing/gtest/include/gtest/gtest.h" + +// 0:d=0 hl=2 l= 95 cons: SEQUENCE +// 2:d=1 hl=2 l= 11 cons: SET +// 4:d=2 hl=2 l= 9 cons: SEQUENCE +// 6:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 11:d=3 hl=2 l= 2 prim: PRINTABLESTRING :US +// 15:d=1 hl=2 l= 23 cons: SET +// 17:d=2 hl=2 l= 21 cons: SEQUENCE +// 19:d=3 hl=2 l= 3 prim: OBJECT :organizationName +// 24:d=3 hl=2 l= 14 prim: PRINTABLESTRING :VeriSign, Inc. +// 40:d=1 hl=2 l= 55 cons: SET +// 42:d=2 hl=2 l= 53 cons: SEQUENCE +// 44:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 49:d=3 hl=2 l= 46 prim: PRINTABLESTRING :Class 1 Public Primary Certification Authority +static const uint8_t VerisignDN[] = { + 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, + 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, + 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2e, 0x43, + 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79 +}; + +// 0:d=0 hl=2 l= 125 cons: SEQUENCE +// 2:d=1 hl=2 l= 11 cons: SET +// 4:d=2 hl=2 l= 9 cons: SEQUENCE +// 6:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 11:d=3 hl=2 l= 2 prim: PRINTABLESTRING :IL +// 15:d=1 hl=2 l= 22 cons: SET +// 17:d=2 hl=2 l= 20 cons: SEQUENCE +// 19:d=3 hl=2 l= 3 prim: OBJECT :organizationName +// 24:d=3 hl=2 l= 13 prim: PRINTABLESTRING :StartCom Ltd. +// 39:d=1 hl=2 l= 43 cons: SET +// 41:d=2 hl=2 l= 41 cons: SEQUENCE +// 43:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 48:d=3 hl=2 l= 34 prim: PRINTABLESTRING :Secure Digital Certificate Signing +// 84:d=1 hl=2 l= 41 cons: SET +// 86:d=2 hl=2 l= 39 cons: SEQUENCE +// 88:d=3 hl=2 l= 3 prim: OBJECT :commonName +// 93:d=3 hl=2 l= 32 prim: PRINTABLESTRING :StartCom Certification Authority +static const uint8_t StartComDN[] = { + 0x30, 0x7d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, + 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, + 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, + 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79 +}; + +// 0:d=0 hl=3 l= 174 cons: SEQUENCE +// 3:d=1 hl=2 l= 11 cons: SET +// 5:d=2 hl=2 l= 9 cons: SEQUENCE +// 7:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 12:d=3 hl=2 l= 2 prim: PRINTABLESTRING :US +// 16:d=1 hl=2 l= 11 cons: SET +// 18:d=2 hl=2 l= 9 cons: SEQUENCE +// 20:d=3 hl=2 l= 3 prim: OBJECT :stateOrProvinceName +// 25:d=3 hl=2 l= 2 prim: PRINTABLESTRING :UT +// 29:d=1 hl=2 l= 23 cons: SET +// 31:d=2 hl=2 l= 21 cons: SEQUENCE +// 33:d=3 hl=2 l= 3 prim: OBJECT :localityName +// 38:d=3 hl=2 l= 14 prim: PRINTABLESTRING :Salt Lake City +// 54:d=1 hl=2 l= 30 cons: SET +// 56:d=2 hl=2 l= 28 cons: SEQUENCE +// 58:d=3 hl=2 l= 3 prim: OBJECT :organizationName +// 63:d=3 hl=2 l= 21 prim: PRINTABLESTRING :The USERTRUST Network +// 86:d=1 hl=2 l= 33 cons: SET +// 88:d=2 hl=2 l= 31 cons: SEQUENCE +// 90:d=3 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 95:d=3 hl=2 l= 24 prim: PRINTABLESTRING :http://www.usertrust.com +//121:d=1 hl=2 l= 54 cons: SET +//123:d=2 hl=2 l= 52 cons: SEQUENCE +//125:d=3 hl=2 l= 3 prim: OBJECT :commonName +//130:d=3 hl=2 l= 45 prim: PRINTABLESTRING :UTN-USERFirst-Client Authentication and Email +static const uint8_t UserTrustDN[] = { + 0x30, 0x81, 0xae, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, + 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20, 0x43, 0x69, + 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, + 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, + 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x2d, 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, + 0x73, 0x74, 0x2d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x45, 0x6d, 0x61, 0x69, 0x6c +}; + +// 0:d=0 hl=3 l= 190 cons: SEQUENCE +// 3:d=1 hl=2 l= 63 cons: SET +// 5:d=2 hl=2 l= 61 cons: SEQUENCE +// 7:d=3 hl=2 l= 3 prim: OBJECT :commonName +// 12:d=3 hl=2 l= 54 prim: UTF8STRING :TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı +// 68:d=1 hl=2 l= 11 cons: SET +// 70:d=2 hl=2 l= 9 cons: SEQUENCE +// 72:d=3 hl=2 l= 3 prim: OBJECT :countryName +// 77:d=3 hl=2 l= 2 prim: PRINTABLESTRING :TR +// 81:d=1 hl=2 l= 15 cons: SET +// 83:d=2 hl=2 l= 13 cons: SEQUENCE +// 85:d=3 hl=2 l= 3 prim: OBJECT :localityName +// 90:d=3 hl=2 l= 6 prim: UTF8STRING :Ankara +// 98:d=1 hl=2 l= 93 cons: SET +//100:d=2 hl=2 l= 91 cons: SEQUENCE +//102:d=3 hl=2 l= 3 prim: OBJECT :organizationName +//107:d=3 hl=2 l= 84 prim: UTF8STRING :TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005 +static const uint8_t TurkTrustDN[] = { + 0x30, 0x81, 0xbe, 0x31, 0x3f, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x36, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x45, + 0x6c, 0x65, 0x6b, 0x74, 0x72, 0x6f, 0x6e, 0x69, 0x6b, 0x20, 0x53, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x6b, 0x61, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74, + 0x20, 0x53, 0x61, 0xc4, 0x9f, 0x6c, 0x61, 0x79, 0xc4, 0xb1, 0x63, 0xc4, 0xb1, + 0x73, 0xc4, 0xb1, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x54, 0x52, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x06, 0x41, 0x6e, 0x6b, 0x61, 0x72, 0x61, 0x31, 0x5d, 0x30, 0x5b, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x54, 0x54, 0xc3, 0x9c, 0x52, 0x4b, 0x54, 0x52, 0x55, + 0x53, 0x54, 0x20, 0x42, 0x69, 0x6c, 0x67, 0x69, 0x20, 0xc4, 0xb0, 0x6c, 0x65, + 0x74, 0x69, 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x76, 0x65, 0x20, 0x42, 0x69, 0x6c, + 0x69, 0xc5, 0x9f, 0x69, 0x6d, 0x20, 0x47, 0xc3, 0xbc, 0x76, 0x65, 0x6e, 0x6c, + 0x69, 0xc4, 0x9f, 0x69, 0x20, 0x48, 0x69, 0x7a, 0x6d, 0x65, 0x74, 0x6c, 0x65, + 0x72, 0x69, 0x20, 0x41, 0x2e, 0xc5, 0x9e, 0x2e, 0x20, 0x28, 0x63, 0x29, 0x20, + 0x4b, 0x61, 0x73, 0xc4, 0xb1, 0x6d, 0x20, 0x32, 0x30, 0x30, 0x35, 0x30, 0x1e, + 0x17, 0x0d, 0x30, 0x35, 0x31, 0x31, 0x30, 0x37, 0x31, 0x30, 0x30, 0x37, 0x35, + 0x37 +}; + +// 33:d=2 hl=3 l= 207 cons: SEQUENCE +// 36:d=3 hl=2 l= 11 cons: SET +// 38:d=4 hl=2 l= 9 cons: SEQUENCE +// 40:d=5 hl=2 l= 3 prim: OBJECT :countryName +// 45:d=5 hl=2 l= 2 prim: PRINTABLESTRING :AT +// 49:d=3 hl=3 l= 139 cons: SET +// 52:d=4 hl=3 l= 136 cons: SEQUENCE +// 55:d=5 hl=2 l= 3 prim: OBJECT :organizationName +// 60:d=5 hl=3 l= 128 prim: BMPSTRING :A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH +//191:d=3 hl=2 l= 24 cons: SET +//193:d=4 hl=2 l= 22 cons: SEQUENCE +//195:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName +//200:d=5 hl=2 l= 15 prim: PRINTABLESTRING :A-Trust-Qual-01 +//217:d=3 hl=2 l= 24 cons: SET +//219:d=4 hl=2 l= 22 cons: SEQUENCE +//221:d=5 hl=2 l= 3 prim: OBJECT :commonName +//226:d=5 hl=2 l= 15 prim: PRINTABLESTRING :A-Trust-Qual-01 +static const uint8_t ATrustQual01DN[] = { + 0x30, 0x81, 0xcf, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x54, 0x31, 0x81, 0x8b, 0x30, 0x81, 0x88, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x1e, 0x81, 0x80, 0x00, 0x41, 0x00, 0x2d, 0x00, 0x54, 0x00, 0x72, 0x00, + 0x75, 0x00, 0x73, 0x00, 0x74, 0x00, 0x20, 0x00, 0x47, 0x00, 0x65, 0x00, 0x73, + 0x00, 0x2e, 0x00, 0x20, 0x00, 0x66, 0x00, 0xfc, 0x00, 0x72, 0x00, 0x20, 0x00, + 0x53, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x68, + 0x00, 0x65, 0x00, 0x69, 0x00, 0x74, 0x00, 0x73, 0x00, 0x73, 0x00, 0x79, 0x00, + 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, + 0x00, 0x6d, 0x00, 0x20, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x6b, 0x00, + 0x74, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x44, 0x00, 0x61, 0x00, 0x74, + 0x00, 0x65, 0x00, 0x6e, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6b, 0x00, + 0x65, 0x00, 0x68, 0x00, 0x72, 0x00, 0x20, 0x00, 0x47, 0x00, 0x6d, 0x00, 0x62, + 0x00, 0x48, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, + 0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, 0x75, 0x61, 0x6c, 0x2d, + 0x30, 0x31, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f, + 0x41, 0x2d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x51, 0x75, 0x61, 0x6c, 0x2d, + 0x30, 0x31, 0x30, 0x1e, 0x17 +}; + +// 34:d=2 hl=3 l= 180 cons: SEQUENCE +// 37:d=3 hl=2 l= 20 cons: SET +// 39:d=4 hl=2 l= 18 cons: SEQUENCE +// 41:d=5 hl=2 l= 3 prim: OBJECT :organizationName +// 46:d=5 hl=2 l= 11 prim: PRINTABLESTRING :Entrust.net +// 59:d=3 hl=2 l= 64 cons: SET +// 61:d=4 hl=2 l= 62 cons: SEQUENCE +// 63:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName +// 68:d=5 hl=2 l= 55 prim: T61STRING :www.entrust.net/CPS_2048 incorp. by ref. (limits liab.) +//125:d=3 hl=2 l= 37 cons: SET +//127:d=4 hl=2 l= 35 cons: SEQUENCE +//129:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName +//134:d=5 hl=2 l= 28 prim: PRINTABLESTRING :(c) 1999 Entrust.net Limited +//164:d=3 hl=2 l= 51 cons: SET +//166:d=4 hl=2 l= 49 cons: SEQUENCE +//168:d=5 hl=2 l= 3 prim: OBJECT :commonName +//173:d=5 hl=2 l= 42 prim: PRINTABLESTRING :Entrust.net Certification Authority (2048) +static const uint8_t EntrustDN[] = { + 0x30, 0x81, 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31, + 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77, 0x77, + 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, + 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69, 0x6e, 0x63, 0x6f, + 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e, 0x29, + 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, + 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, + 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, + 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38, + 0x29 +}; + +namespace net { + +TEST(X509TypesTest, Matching) { + CertPrincipal spamco; + spamco.common_name = "SpamCo Dept. Of Certificization"; + spamco.country_name = "EB"; + spamco.organization_names.push_back("SpamCo Holding Company, LLC"); + spamco.organization_names.push_back("SpamCo Evil Masterminds"); + spamco.organization_unit_names.push_back("Class Z Obfuscation Authority"); + ASSERT_TRUE(spamco.Matches(spamco)); + + CertPrincipal bogus; + EXPECT_FALSE(bogus.Matches(spamco)); + EXPECT_FALSE(spamco.Matches(bogus)); + + bogus = spamco; + EXPECT_TRUE(bogus.Matches(spamco)); + EXPECT_TRUE(spamco.Matches(bogus)); + + bogus.organization_names.erase(bogus.organization_names.begin(), + bogus.organization_names.end()); + EXPECT_FALSE(bogus.Matches(spamco)); + EXPECT_FALSE(spamco.Matches(bogus)); + + bogus.organization_names.push_back("SpamCo Holding Company, LLC"); + bogus.organization_names.push_back("SpamCo Evil Masterminds"); + EXPECT_TRUE(bogus.Matches(spamco)); + EXPECT_TRUE(spamco.Matches(bogus)); + + bogus.locality_name = "Elbosdorf"; + EXPECT_FALSE(bogus.Matches(spamco)); + EXPECT_FALSE(spamco.Matches(bogus)); + + bogus.locality_name = ""; + bogus.organization_unit_names.push_back("Q Division"); + EXPECT_FALSE(bogus.Matches(spamco)); + EXPECT_FALSE(spamco.Matches(bogus)); +} + +#if defined(OS_MACOSX) // ParseDistinguishedName not implemented for Win/Linux + +TEST(X509TypesTest, ParseDNVerisign) { + CertPrincipal verisign; + EXPECT_TRUE(verisign.ParseDistinguishedName(VerisignDN, sizeof(VerisignDN))); + EXPECT_EQ("", verisign.common_name); + EXPECT_EQ("US", verisign.country_name); + ASSERT_EQ(1U, verisign.organization_names.size()); + EXPECT_EQ("VeriSign, Inc.", verisign.organization_names[0]); + ASSERT_EQ(1U, verisign.organization_unit_names.size()); + EXPECT_EQ("Class 1 Public Primary Certification Authority", + verisign.organization_unit_names[0]); +} + +TEST(X509TypesTest, ParseDNStartcom) { + CertPrincipal startcom; + EXPECT_TRUE(startcom.ParseDistinguishedName(StartComDN, sizeof(StartComDN))); + EXPECT_EQ("StartCom Certification Authority", startcom.common_name); + EXPECT_EQ("IL", startcom.country_name); + ASSERT_EQ(1U, startcom.organization_names.size()); + EXPECT_EQ("StartCom Ltd.", startcom.organization_names[0]); + ASSERT_EQ(1U, startcom.organization_unit_names.size()); + EXPECT_EQ("Secure Digital Certificate Signing", + startcom.organization_unit_names[0]); +} + +TEST(X509TypesTest, ParseDNUserTrust) { + CertPrincipal usertrust; + EXPECT_TRUE(usertrust.ParseDistinguishedName(UserTrustDN, + sizeof(UserTrustDN))); + EXPECT_EQ("UTN-USERFirst-Client Authentication and Email", + usertrust.common_name); + EXPECT_EQ("US", usertrust.country_name); + EXPECT_EQ("UT", usertrust.state_or_province_name); + EXPECT_EQ("Salt Lake City", usertrust.locality_name); + ASSERT_EQ(1U, usertrust.organization_names.size()); + EXPECT_EQ("The USERTRUST Network", usertrust.organization_names[0]); + ASSERT_EQ(1U, usertrust.organization_unit_names.size()); + EXPECT_EQ("http://www.usertrust.com", + usertrust.organization_unit_names[0]); +} + +TEST(X509TypesTest, ParseDNTurkTrust) { + // Note: This tests parsing UTF8STRINGs. + CertPrincipal turktrust; + EXPECT_TRUE(turktrust.ParseDistinguishedName(TurkTrustDN, + sizeof(TurkTrustDN))); + EXPECT_EQ("TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı", + turktrust.common_name); + EXPECT_EQ("TR", turktrust.country_name); + EXPECT_EQ("Ankara", turktrust.locality_name); + ASSERT_EQ(1U, turktrust.organization_names.size()); + EXPECT_EQ("TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A.Ş. (c) Kasım 2005", + turktrust.organization_names[0]); +} + +TEST(X509TypesTest, ParseDNATrust) { + // Note: This tests parsing 16-bit BMPSTRINGs. + CertPrincipal atrust; + EXPECT_TRUE(atrust.ParseDistinguishedName(ATrustQual01DN, + sizeof(ATrustQual01DN))); + EXPECT_EQ("A-Trust-Qual-01", + atrust.common_name); + EXPECT_EQ("AT", atrust.country_name); + ASSERT_EQ(1U, atrust.organization_names.size()); + EXPECT_EQ("A-Trust Ges. für Sicherheitssysteme im elektr. Datenverkehr GmbH", + atrust.organization_names[0]); + ASSERT_EQ(1U, atrust.organization_unit_names.size()); + EXPECT_EQ("A-Trust-Qual-01", + atrust.organization_unit_names[0]); +} + +TEST(X509TypesTest, ParseDNEntrust) { + // Note: This tests parsing T61STRINGs and fields with multiple values. + CertPrincipal entrust; + EXPECT_TRUE(entrust.ParseDistinguishedName(EntrustDN, + sizeof(EntrustDN))); + EXPECT_EQ("Entrust.net Certification Authority (2048)", + entrust.common_name); + EXPECT_EQ("", entrust.country_name); + ASSERT_EQ(1U, entrust.organization_names.size()); + EXPECT_EQ("Entrust.net", + entrust.organization_names[0]); + ASSERT_EQ(2U, entrust.organization_unit_names.size()); + EXPECT_EQ("www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)", + entrust.organization_unit_names[0]); + EXPECT_EQ("(c) 1999 Entrust.net Limited", + entrust.organization_unit_names[1]); +} +#endif + +} diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc index adf73b9..367afda 100644 --- a/net/base/x509_certificate.cc +++ b/net/base/x509_certificate.cc @@ -4,7 +4,9 @@ #include "net/base/x509_certificate.h" -#if defined(USE_NSS) +#if defined(OS_MACOSX) +#include <Security/Security.h> +#elif defined(USE_NSS) #include <cert.h> #endif @@ -56,8 +58,8 @@ bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, } bool X509Certificate::FingerprintLessThan::operator()( - const Fingerprint& lhs, - const Fingerprint& rhs) const { + const SHA1Fingerprint& lhs, + const SHA1Fingerprint& rhs) const { for (size_t i = 0; i < sizeof(lhs.data); ++i) { if (lhs.data[i] < rhs.data[i]) return true; @@ -121,47 +123,6 @@ X509Certificate* X509Certificate::Cache::Find(const Fingerprint& fingerprint) { return pos->second; }; -X509Certificate::Policy::Judgment X509Certificate::Policy::Check( - X509Certificate* cert) const { - // It shouldn't matter which set we check first, but we check denied first - // in case something strange has happened. - - if (denied_.find(cert->fingerprint()) != denied_.end()) { - // DCHECK that the order didn't matter. - DCHECK(allowed_.find(cert->fingerprint()) == allowed_.end()); - return DENIED; - } - - if (allowed_.find(cert->fingerprint()) != allowed_.end()) { - // DCHECK that the order didn't matter. - DCHECK(denied_.find(cert->fingerprint()) == denied_.end()); - return ALLOWED; - } - - // We don't have a policy for this cert. - return UNKNOWN; -} - -void X509Certificate::Policy::Allow(X509Certificate* cert) { - // Put the cert in the allowed set and (maybe) remove it from the denied set. - denied_.erase(cert->fingerprint()); - allowed_.insert(cert->fingerprint()); -} - -void X509Certificate::Policy::Deny(X509Certificate* cert) { - // Put the cert in the denied set and (maybe) remove it from the allowed set. - allowed_.erase(cert->fingerprint()); - denied_.insert(cert->fingerprint()); -} - -bool X509Certificate::Policy::HasAllowedCert() const { - return !allowed_.empty(); -} - -bool X509Certificate::Policy::HasDeniedCert() const { - return !denied_.empty(); -} - // static X509Certificate* X509Certificate::CreateFromHandle( OSCertHandle cert_handle, diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index ec287ce..32c16f1 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -15,13 +15,15 @@ #include "base/ref_counted.h" #include "base/singleton.h" #include "base/time.h" +#include "net/base/x509_cert_types.h" #include "testing/gtest/include/gtest/gtest_prod.h" #if defined(OS_WIN) #include <windows.h> #include <wincrypt.h> #elif defined(OS_MACOSX) -#include <Security/Security.h> +#include <CoreFoundation/CFArray.h> +#include <Security/SecBase.h> #elif defined(USE_NSS) // Forward declaration; real one in <cert.h> struct CERTCertificateStr; @@ -36,28 +38,6 @@ class CertVerifyResult; // X509Certificate represents an X.509 certificate used by SSL. class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { public: - // SHA-1 fingerprint (160 bits) of a certificate. - struct Fingerprint { - bool Equals(const Fingerprint& other) const { - return memcmp(data, other.data, sizeof(data)) == 0; - } - - unsigned char data[20]; - }; - - class FingerprintLessThan - : public std::binary_function<Fingerprint, Fingerprint, bool> { - public: - bool operator() (const Fingerprint& lhs, const Fingerprint& rhs) const; - }; - - // Predicate functor used in maps when X509Certificate is used as the key. - class LessThan - : public std::binary_function<X509Certificate*, X509Certificate*, bool> { - public: - bool operator() (X509Certificate* lhs, X509Certificate* rhs) const; - }; - // A handle to the certificate object in the underlying crypto library. // We assume that OSCertHandle is a pointer type on all platforms and // NULL is an invalid OSCertHandle. @@ -74,62 +54,18 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { typedef std::vector<OSCertHandle> OSCertHandles; - // Principal represent an X.509 principal. - struct Principal { - Principal() { } - explicit Principal(const std::string& name) : common_name(name) { } - - // The different attributes for a principal. They may be "". - // Note that some of them can have several values. - - std::string common_name; - std::string locality_name; - std::string state_or_province_name; - std::string country_name; - - std::vector<std::string> street_addresses; - std::vector<std::string> organization_names; - std::vector<std::string> organization_unit_names; - std::vector<std::string> domain_components; - }; + // Legacy names for types now defined in x509_cert_types.h. + // TODO(snej): Clean up existing code using these names to use the new names. + typedef CertPrincipal Principal; + typedef CertPolicy Policy; + typedef SHA1Fingerprint Fingerprint; + typedef SHA1FingerprintLessThan FingerprintLessThan; - // This class is useful for maintaining policies about which certificates are - // permitted or forbidden for a particular purpose. - class Policy { + // Predicate functor used in maps when X509Certificate is used as the key. + class LessThan + : public std::binary_function<X509Certificate*, X509Certificate*, bool> { public: - // The judgments this policy can reach. - enum Judgment { - // We don't have policy information for this certificate. - UNKNOWN, - - // This certificate is allowed. - ALLOWED, - - // This certificate is denied. - DENIED, - }; - - // Returns the judgment this policy makes about this certificate. - Judgment Check(X509Certificate* cert) const; - - // Causes the policy to allow this certificate. - void Allow(X509Certificate* cert); - - // Causes the policy to deny this certificate. - void Deny(X509Certificate* cert); - - // Returns true if this policy has allowed at least one certificate. - bool HasAllowedCert() const; - - // Returns true if this policy has denied at least one certificate. - bool HasDeniedCert() const; - - private: - // The set of fingerprints of allowed certificates. - std::set<Fingerprint, FingerprintLessThan> allowed_; - - // The set of fingerprints of denied certificates. - std::set<Fingerprint, FingerprintLessThan> denied_; + bool operator() (X509Certificate* lhs, X509Certificate* rhs) const; }; // Where the certificate comes from. The enumeration constants are @@ -231,6 +167,9 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { // Does this certificate's usage allow SSL client authentication? bool SupportsSSLClientAuth() const; + // Do any of the given issuer names appear in this cert's chain of trust? + bool IsIssuedBy(const std::vector<CertPrincipal>& valid_issuers); + // Creates a security policy for SSL client certificates. static OSStatus CreateSSLClientPolicy(SecPolicyRef* outPolicy); @@ -238,8 +177,11 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { // |server_domain| is a hint for which domain the cert is to be sent to // (a cert previously specified as the default for that domain will be given // precedence and returned first in the output vector.) + // If valid_issuers is non-empty, only certs that were transitively issued by + // one of the given names will be included in the list. static bool GetSSLClientCertificates( const std::string& server_domain, + const std::vector<CertPrincipal>& valid_issuers, std::vector<scoped_refptr<X509Certificate> >* certs); // Creates the chain of certs to use for this client identity cert. diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index 16e7604..a4c37ba 100644 --- a/net/base/x509_certificate_mac.cc +++ b/net/base/x509_certificate_mac.cc @@ -5,6 +5,7 @@ #include "net/base/x509_certificate.h" #include <CommonCrypto/CommonDigest.h> +#include <Security/Security.h> #include <time.h> #include "base/scoped_cftyperef.h" @@ -80,11 +81,6 @@ namespace { typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, CFDictionaryRef*); -inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) { - return oid1->Length == oid2->Length && - (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0); -} - int NetErrorFromOSStatus(OSStatus status) { switch (status) { case noErr: @@ -173,64 +169,6 @@ bool OverrideHostnameMismatch(const std::string& hostname, return override_hostname_mismatch; } -void ParsePrincipal(const CSSM_X509_NAME* name, - X509Certificate::Principal* principal) { - std::vector<std::string> common_names, locality_names, state_names, - country_names; - - // TODO(jcampan): add business_category and serial_number. - const CSSM_OID* kOIDs[] = { &CSSMOID_CommonName, - &CSSMOID_LocalityName, - &CSSMOID_StateProvinceName, - &CSSMOID_CountryName, - &CSSMOID_StreetAddress, - &CSSMOID_OrganizationName, - &CSSMOID_OrganizationalUnitName, - &CSSMOID_DNQualifier }; // This should be "DC" - // but is undoubtedly - // wrong. TODO(avi): - // Find the right OID. - - std::vector<std::string>* values[] = { - &common_names, &locality_names, - &state_names, &country_names, - &(principal->street_addresses), - &(principal->organization_names), - &(principal->organization_unit_names), - &(principal->domain_components) }; - DCHECK(arraysize(kOIDs) == arraysize(values)); - - for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) { - CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn]; - for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) { - CSSM_X509_TYPE_VALUE_PAIR pair_struct = - rdn_struct.AttributeTypeAndValue[pair]; - for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { - if (CSSMOIDEqual(&pair_struct.type, kOIDs[oid])) { - std::string value = - std::string(reinterpret_cast<std::string::value_type*> - (pair_struct.value.Data), - pair_struct.value.Length); - values[oid]->push_back(value); - break; - } - } - } - } - - // We don't expect to have more than one CN, L, S, and C. - std::vector<std::string>* single_value_lists[4] = { - &common_names, &locality_names, &state_names, &country_names }; - std::string* single_values[4] = { - &principal->common_name, &principal->locality_name, - &principal->state_or_province_name, &principal->country_name }; - for (size_t i = 0; i < arraysize(single_value_lists); ++i) { - DCHECK(single_value_lists[i]->size() <= 1); - if (single_value_lists[i]->size() > 0) - *(single_values[i]) = (*(single_value_lists[i]))[0]; - } -} - struct CSSMFields { CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {} ~CSSMFields() { @@ -386,17 +324,51 @@ OSStatus CreatePolicy(const CSSM_OID* policy_OID, return noErr; } +// Gets the issuer for a given cert, starting with the cert itself and +// including the intermediate and finally root certificates (if any). +// This function calls SecTrust but doesn't actually pay attention to the trust +// result: it shouldn't be used to determine trust, just to traverse the chain. +// Caller is responsible for releasing the value stored into *out_cert_chain. +OSStatus CopyCertChain(SecCertificateRef cert_handle, + CFArrayRef* out_cert_chain) { + DCHECK(cert_handle && out_cert_chain); + // Create an SSL policy ref configured for client cert evaluation. + SecPolicyRef ssl_policy; + OSStatus result = X509Certificate::CreateSSLClientPolicy(&ssl_policy); + if (result) + return result; + scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy); + + // Create a SecTrustRef. + scoped_cftyperef<CFArrayRef> input_certs( + CFArrayCreate(NULL, (const void**)&cert_handle, 1, + &kCFTypeArrayCallBacks)); + SecTrustRef trust_ref = NULL; + result = SecTrustCreateWithCertificates(input_certs, ssl_policy, &trust_ref); + if (result) + return result; + scoped_cftyperef<SecTrustRef> trust(trust_ref); + + // Evaluate trust, which creates the cert chain. + SecTrustResultType status; + CSSM_TP_APPLE_EVIDENCE_INFO* status_chain; + result = SecTrustEvaluate(trust, &status); + if (result) + return result; + return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain); +} + } // namespace void X509Certificate::Initialize() { const CSSM_X509_NAME* name; OSStatus status = SecCertificateGetSubject(cert_handle_, &name); if (!status) { - ParsePrincipal(name, &subject_); + subject_.Parse(name); } status = SecCertificateGetIssuer(cert_handle_, &name); if (!status) { - ParsePrincipal(name, &issuer_); + issuer_.Parse(name); } GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore, @@ -742,6 +714,34 @@ bool X509Certificate::SupportsSSLClientAuth() const { return false; } +bool X509Certificate::IsIssuedBy( + const std::vector<CertPrincipal>& valid_issuers) { + // Get the cert's issuer chain. + CFArrayRef cert_chain = NULL; + OSStatus result; + result = CopyCertChain(os_cert_handle(), &cert_chain); + if (result != noErr) + return false; + scoped_cftyperef<CFArrayRef> scoped_cert_chain(cert_chain); + + // Check all the certs in the chain for a match. + int n = CFArrayGetCount(cert_chain); + for (int i = 0; i < n; ++i) { + SecCertificateRef cert_handle = reinterpret_cast<SecCertificateRef>( + const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i))); + CFRetain(cert_handle); + scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( + cert_handle, + X509Certificate::SOURCE_LONE_CERT_IMPORT, + X509Certificate::OSCertHandles()); + for (unsigned j = 0; j < valid_issuers.size(); j++) { + if (cert->subject().Matches(valid_issuers[j])) + return true; + } + } + return false; +} + // static OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = { @@ -759,6 +759,7 @@ OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { // static bool X509Certificate::GetSSLClientCertificates ( const std::string& server_domain, + const std::vector<Principal>& valid_issuers, std::vector<scoped_refptr<X509Certificate> >* certs) { scoped_cftyperef<SecIdentityRef> preferred_identity; if (!server_domain.empty()) { @@ -768,11 +769,12 @@ bool X509Certificate::GetSSLClientCertificates ( SecIdentityRef identity = NULL; if (SecIdentityCopyPreference(domain_str, 0, - NULL, + NULL, // validIssuers argument is ignored :( &identity) == noErr) preferred_identity.reset(identity); } + // Now enumerate the identities in the available keychains. SecIdentitySearchRef search = nil; OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); scoped_cftyperef<SecIdentitySearchRef> scoped_search(search); @@ -805,10 +807,20 @@ bool X509Certificate::GetSSLClientCertificates ( if (i < certs->size()) continue; + bool is_preferred = preferred_identity && + CFEqual(preferred_identity, identity); + + // Make sure the issuer matches valid_issuers, if given. + // But an explicit cert preference overrides this. + if (!is_preferred && + valid_issuers.size() > 0 && + !cert->IsIssuedBy(valid_issuers)) + continue; + // The cert passes, so add it to the vector. // If it's the preferred identity, add it at the start (so it'll be // selected by default in the UI.) - if (preferred_identity && CFEqual(preferred_identity, identity)) + if (is_preferred) certs->insert(certs->begin(), cert); else certs->push_back(cert); @@ -834,46 +846,20 @@ CFArrayRef X509Certificate::CreateClientCertificateChain() const { CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); CFArrayAppendValue(chain, identity); - { - // Create an SSL policy ref configured for client cert evaluation. - SecPolicyRef ssl_policy; - result = CreateSSLClientPolicy(&ssl_policy); - if (result) - goto exit; - scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy); - - // Use a SecTrust object to find the intermediate certs in the trust chain. - scoped_cftyperef<CFArrayRef> input_certs( - CFArrayCreate(NULL, (const void**)&cert_handle_, 1, - &kCFTypeArrayCallBacks)); - SecTrustRef trust_ref = NULL; - result = SecTrustCreateWithCertificates(input_certs, - ssl_policy, - &trust_ref); - if (result) - goto exit; - scoped_cftyperef<SecTrustRef> trust(trust_ref); - - SecTrustResultType status; - CFArrayRef trust_chain = NULL; - CSSM_TP_APPLE_EVIDENCE_INFO* status_chain; - result = SecTrustEvaluate(trust, &status); - if (result) - goto exit; - result = SecTrustGetResult(trust, &status, &trust_chain, &status_chain); - if (result) - goto exit; - - // Append the intermediate certs from SecTrust to the result array: - if (trust_chain) { - int chain_count = CFArrayGetCount(trust_chain); - if (chain_count > 1) { - CFArrayAppendArray(chain, - trust_chain, - CFRangeMake(1, chain_count - 1)); - } - CFRelease(trust_chain); + CFArrayRef cert_chain = NULL; + result = CopyCertChain(cert_handle_, &cert_chain); + if (result) + goto exit; + + // Append the intermediate certs from SecTrust to the result array: + if (cert_chain) { + int chain_count = CFArrayGetCount(cert_chain); + if (chain_count > 1) { + CFArrayAppendArray(chain, + cert_chain, + CFRangeMake(1, chain_count - 1)); } + CFRelease(cert_chain); } exit: if (result) diff --git a/net/net.gyp b/net/net.gyp index a6ed78d..34e77fc 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -161,6 +161,9 @@ 'base/x509_certificate_mac.cc', 'base/x509_certificate_nss.cc', 'base/x509_certificate_win.cc', + 'base/x509_cert_types.cc', + 'base/x509_cert_types.h', + 'base/x509_cert_types_mac.cc', ], 'export_dependent_settings': [ '../base/base.gyp:base', @@ -633,6 +636,7 @@ 'base/test_completion_callback_unittest.cc', 'base/upload_data_stream_unittest.cc', 'base/x509_certificate_unittest.cc', + 'base/x509_cert_types_unittest.cc', 'disk_cache/addr_unittest.cc', 'disk_cache/backend_unittest.cc', 'disk_cache/bitmap_unittest.cc', diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc index 4cf7722..ad3c747 100644 --- a/net/socket/ssl_client_socket_mac.cc +++ b/net/socket/ssl_client_socket_mac.cc @@ -655,19 +655,32 @@ void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) { void SSLClientSocketMac::GetSSLCertRequestInfo( SSLCertRequestInfo* cert_request_info) { // I'm being asked for available client certs (identities). - - CFArrayRef allowed_issuer_names = NULL; - if (SSLCopyDistinguishedNames(ssl_context_, &allowed_issuer_names) == noErr && - allowed_issuer_names != NULL) { - SSL_LOG << "Server has " << CFArrayGetCount(allowed_issuer_names) - << " allowed issuer names"; - CFRelease(allowed_issuer_names); - // TODO(snej): Filter GetSSLClientCertificates using this array. + // First, get the cert issuer names allowed by the server. + std::vector<CertPrincipal> valid_issuers; + CFArrayRef valid_issuer_names = NULL; + if (SSLCopyDistinguishedNames(ssl_context_, &valid_issuer_names) == noErr && + valid_issuer_names != NULL) { + SSL_LOG << "Server has " << CFArrayGetCount(valid_issuer_names) + << " valid issuer names"; + int n = CFArrayGetCount(valid_issuer_names); + for (int i = 0; i < n; i++) { + // Parse each name into a CertPrincipal object. + CFDataRef issuer = reinterpret_cast<CFDataRef>( + CFArrayGetValueAtIndex(valid_issuer_names, i)); + CertPrincipal p; + if (p.ParseDistinguishedName(CFDataGetBytePtr(issuer), + CFDataGetLength(issuer))) { + valid_issuers.push_back(p); + } + } + CFRelease(valid_issuer_names); } + // Now get the available client certs whose issuers are allowed by the server. cert_request_info->host_and_port = hostname_; cert_request_info->client_certs.clear(); X509Certificate::GetSSLClientCertificates(hostname_, + valid_issuers, &cert_request_info->client_certs); SSL_LOG << "Asking user to choose between " << cert_request_info->client_certs.size() << " client certs..."; |