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