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