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