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