Home | History | Annotate | Download | only in cert
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/cert/x509_cert_types.h"
      6 
      7 #include <CoreServices/CoreServices.h>
      8 #include <Security/SecAsn1Coder.h>
      9 #include <Security/Security.h>
     10 
     11 #include "base/i18n/icu_string_conversions.h"
     12 #include "base/logging.h"
     13 #include "base/mac/mac_logging.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 
     16 namespace net {
     17 
     18 namespace {
     19 
     20 // The BER encoding of 0.9.2342.19200300.100.1.25.
     21 // On 10.6 and later this is available as CSSMOID_DomainComponent, which is an
     22 // external symbol from Security.framework. However, it appears that Apple's
     23 // implementation improperly encoded this on 10.6+, and even still is
     24 // unavailable on 10.5, so simply including the raw BER here.
     25 //
     26 // Note: CSSM is allowed to store CSSM_OIDs in any arbitrary format desired,
     27 // as long as the symbols are properly exposed. The fact that Apple's
     28 // implementation stores it in BER is an internal implementation detail
     29 // observed by studying libsecurity_cssm.
     30 const uint8 kDomainComponentData[] = {
     31   0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19
     32 };
     33 
     34 const CSSM_OID kDomainComponentOID = {
     35     arraysize(kDomainComponentData),
     36     const_cast<uint8*>(kDomainComponentData)
     37 };
     38 
     39 const CSSM_OID* kOIDs[] = {
     40     &CSSMOID_CommonName,
     41     &CSSMOID_LocalityName,
     42     &CSSMOID_StateProvinceName,
     43     &CSSMOID_CountryName,
     44     &CSSMOID_StreetAddress,
     45     &CSSMOID_OrganizationName,
     46     &CSSMOID_OrganizationalUnitName,
     47     &kDomainComponentOID,
     48 };
     49 
     50 // The following structs and templates work with Apple's very arcane and under-
     51 // documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1
     52 // decoder:
     53 // http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html
     54 
     55 // These are used to parse the contents of a raw
     56 // BER DistinguishedName structure.
     57 
     58 const SecAsn1Template kStringValueTemplate[] = {
     59   { SEC_ASN1_CHOICE, offsetof(CSSM_X509_TYPE_VALUE_PAIR, valueType), },
     60   { SEC_ASN1_PRINTABLE_STRING,
     61     offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
     62     BER_TAG_PRINTABLE_STRING },
     63   { SEC_ASN1_IA5_STRING,
     64     offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
     65     BER_TAG_IA5_STRING },
     66   { SEC_ASN1_T61_STRING,
     67     offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
     68     BER_TAG_T61_STRING },
     69   { SEC_ASN1_UTF8_STRING,
     70     offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
     71     BER_TAG_PKIX_UTF8_STRING },
     72   { SEC_ASN1_BMP_STRING,
     73     offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
     74     BER_TAG_PKIX_BMP_STRING },
     75   { SEC_ASN1_UNIVERSAL_STRING,
     76     offsetof(CSSM_X509_TYPE_VALUE_PAIR, value), 0,
     77     BER_TAG_PKIX_UNIVERSAL_STRING },
     78   { 0, }
     79 };
     80 
     81 const SecAsn1Template kKeyValuePairTemplate[] = {
     82   { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CSSM_X509_TYPE_VALUE_PAIR) },
     83   { SEC_ASN1_OBJECT_ID, offsetof(CSSM_X509_TYPE_VALUE_PAIR, type), },
     84   { SEC_ASN1_INLINE, 0, &kStringValueTemplate, },
     85   { 0, }
     86 };
     87 
     88 struct KeyValuePairs {
     89   CSSM_X509_TYPE_VALUE_PAIR* pairs;
     90 };
     91 
     92 const SecAsn1Template kKeyValuePairSetTemplate[] = {
     93   { SEC_ASN1_SET_OF, offsetof(KeyValuePairs, pairs),
     94       kKeyValuePairTemplate, sizeof(KeyValuePairs) }
     95 };
     96 
     97 struct X509Name {
     98   KeyValuePairs** pairs_list;
     99 };
    100 
    101 const SecAsn1Template kNameTemplate[] = {
    102   { SEC_ASN1_SEQUENCE_OF, offsetof(X509Name, pairs_list),
    103       kKeyValuePairSetTemplate, sizeof(X509Name) }
    104 };
    105 
    106 // Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.)
    107 std::string DataToString(CSSM_DATA data) {
    108   return std::string(
    109       reinterpret_cast<std::string::value_type*>(data.Data),
    110       data.Length);
    111 }
    112 
    113 // Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8.
    114 std::string Latin1DataToUTF8String(CSSM_DATA data) {
    115   base::string16 utf16;
    116   if (!CodepageToUTF16(DataToString(data), base::kCodepageLatin1,
    117                        base::OnStringConversionError::FAIL, &utf16))
    118     return "";
    119   return UTF16ToUTF8(utf16);
    120 }
    121 
    122 // Converts big-endian UTF-16 to UTF-8 in a std::string.
    123 // Note: The byte-order flipping is done in place on the input buffer!
    124 bool UTF16BigEndianToUTF8(base::char16* chars, size_t length,
    125                           std::string* out_string) {
    126   for (size_t i = 0; i < length; i++)
    127     chars[i] = EndianU16_BtoN(chars[i]);
    128   return UTF16ToUTF8(chars, length, out_string);
    129 }
    130 
    131 // Converts big-endian UTF-32 to UTF-8 in a std::string.
    132 // Note: The byte-order flipping is done in place on the input buffer!
    133 bool UTF32BigEndianToUTF8(char32* chars, size_t length,
    134                           std::string* out_string) {
    135   for (size_t i = 0; i < length; ++i)
    136     chars[i] = EndianS32_BtoN(chars[i]);
    137 #if defined(WCHAR_T_IS_UTF32)
    138   return WideToUTF8(reinterpret_cast<const wchar_t*>(chars),
    139                     length, out_string);
    140 #else
    141 #error This code doesn't handle 16-bit wchar_t.
    142 #endif
    143 }
    144 
    145 // Adds a type+value pair to the appropriate vector from a C array.
    146 // The array is keyed by the matching OIDs from kOIDS[].
    147 void AddTypeValuePair(const CSSM_OID type,
    148                       const std::string& value,
    149                       std::vector<std::string>* values[]) {
    150   for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) {
    151     if (CSSMOIDEqual(&type, kOIDs[oid])) {
    152       values[oid]->push_back(value);
    153       break;
    154     }
    155   }
    156 }
    157 
    158 // Stores the first string of the vector, if any, to *single_value.
    159 void SetSingle(const std::vector<std::string>& values,
    160                std::string* single_value) {
    161   // We don't expect to have more than one CN, L, S, and C.
    162   LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values";
    163   if (!values.empty())
    164     *single_value = values[0];
    165 }
    166 
    167 bool match(const std::string& str, const std::string& against) {
    168   // TODO(snej): Use the full matching rules specified in RFC 5280 sec. 7.1
    169   // including trimming and case-folding: <http://www.ietf.org/rfc/rfc5280.txt>.
    170   return against == str;
    171 }
    172 
    173 bool match(const std::vector<std::string>& rdn1,
    174            const std::vector<std::string>& rdn2) {
    175   // "Two relative distinguished names RDN1 and RDN2 match if they have the
    176   // same number of naming attributes and for each naming attribute in RDN1
    177   // there is a matching naming attribute in RDN2." --RFC 5280 sec. 7.1.
    178   if (rdn1.size() != rdn2.size())
    179     return false;
    180   for (unsigned i1 = 0; i1 < rdn1.size(); ++i1) {
    181     unsigned i2;
    182     for (i2 = 0; i2 < rdn2.size(); ++i2) {
    183       if (match(rdn1[i1], rdn2[i2]))
    184           break;
    185     }
    186     if (i2 == rdn2.size())
    187       return false;
    188   }
    189   return true;
    190 }
    191 
    192 }  // namespace
    193 
    194 bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data,
    195                                            size_t length) {
    196   DCHECK(ber_name_data);
    197 
    198   // First parse the BER |name_data| into the above structs.
    199   SecAsn1CoderRef coder = NULL;
    200   SecAsn1CoderCreate(&coder);
    201   DCHECK(coder);
    202   X509Name* name = NULL;
    203   OSStatus err = SecAsn1Decode(coder, ber_name_data, length, kNameTemplate,
    204                                &name);
    205   if (err) {
    206     OSSTATUS_LOG(ERROR, err) << "SecAsn1Decode";
    207     SecAsn1CoderRelease(coder);
    208     return false;
    209   }
    210 
    211   // Now scan the structs and add the values to my string vectors.
    212   // I don't store multiple common/locality/state/country names, so use
    213   // temporary vectors for those.
    214   std::vector<std::string> common_names, locality_names, state_names,
    215       country_names;
    216   std::vector<std::string>* values[] = {
    217       &common_names, &locality_names,
    218       &state_names, &country_names,
    219       &this->street_addresses,
    220       &this->organization_names,
    221       &this->organization_unit_names,
    222       &this->domain_components
    223   };
    224   DCHECK(arraysize(kOIDs) == arraysize(values));
    225 
    226   for (int rdn = 0; name[rdn].pairs_list; ++rdn) {
    227     CSSM_X509_TYPE_VALUE_PAIR* pair;
    228     for (int pair_index = 0;
    229          NULL != (pair = name[rdn].pairs_list[0][pair_index].pairs);
    230          ++pair_index) {
    231       switch (pair->valueType) {
    232         case BER_TAG_IA5_STRING:          // ASCII (that means 7-bit!)
    233         case BER_TAG_PRINTABLE_STRING:    // a subset of ASCII
    234         case BER_TAG_PKIX_UTF8_STRING:    // UTF-8
    235           AddTypeValuePair(pair->type, DataToString(pair->value), values);
    236           break;
    237         case BER_TAG_T61_STRING:          // T61, pretend it's Latin-1
    238           AddTypeValuePair(pair->type,
    239                            Latin1DataToUTF8String(pair->value),
    240                            values);
    241           break;
    242         case BER_TAG_PKIX_BMP_STRING: {        // UTF-16, big-endian
    243           std::string value;
    244           UTF16BigEndianToUTF8(
    245               reinterpret_cast<base::char16*>(pair->value.Data),
    246               pair->value.Length / sizeof(base::char16),
    247               &value);
    248           AddTypeValuePair(pair->type, value, values);
    249           break;
    250         }
    251         case BER_TAG_PKIX_UNIVERSAL_STRING: {  // UTF-32, big-endian
    252           std::string value;
    253           UTF32BigEndianToUTF8(reinterpret_cast<char32*>(pair->value.Data),
    254                                pair->value.Length / sizeof(char32),
    255                                &value);
    256           AddTypeValuePair(pair->type, value, values);
    257           break;
    258         }
    259         default:
    260           DCHECK_EQ(pair->valueType, BER_TAG_UNKNOWN);
    261           // We don't know what data type this is, but we'll store it as a blob.
    262           // Displaying the string may not work, but at least it can be compared
    263           // byte-for-byte by a Matches() call.
    264           AddTypeValuePair(pair->type, DataToString(pair->value), values);
    265           break;
    266       }
    267     }
    268   }
    269 
    270   SetSingle(common_names, &this->common_name);
    271   SetSingle(locality_names, &this->locality_name);
    272   SetSingle(state_names, &this->state_or_province_name);
    273   SetSingle(country_names, &this->country_name);
    274 
    275   // Releasing |coder| frees all the memory pointed to via |name|.
    276   SecAsn1CoderRelease(coder);
    277   return true;
    278 }
    279 
    280 bool CertPrincipal::Matches(const CertPrincipal& against) const {
    281   return match(common_name, against.common_name) &&
    282       match(locality_name, against.locality_name) &&
    283       match(state_or_province_name, against.state_or_province_name) &&
    284       match(country_name, against.country_name) &&
    285       match(street_addresses, against.street_addresses) &&
    286       match(organization_names, against.organization_names) &&
    287       match(organization_unit_names, against.organization_unit_names) &&
    288       match(domain_components, against.domain_components);
    289 }
    290 
    291 }  // namespace net
    292