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