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
15 namespace net {
16
17 namespace {
18
19 const 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
38 struct 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
54 const 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
71 const 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
78 struct KeyValuePairs {
79 KeyValuePair* pairs;
80 };
81
82 const SecAsn1Template kKeyValuePairSetTemplate[] = {
83 { SEC_ASN1_SET_OF, offsetof(KeyValuePairs, pairs),
84 kKeyValuePairTemplate, sizeof(KeyValuePairs) }
85 };
86
87 struct X509Name {
88 KeyValuePairs** pairs_list;
89 };
90
91 const 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.)
DataToString(CSSM_DATA data)97 std::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.
Latin1DataToUTF8String(CSSM_DATA data)104 std::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!
UTF16BigEndianToUTF8(char16 * chars,size_t length,std::string * out_string)114 bool 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!
UTF32BigEndianToUTF8(char32 * chars,size_t length,std::string * out_string)123 bool 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[].
137 void 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.
149 void 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
match(const std::string & str,const std::string & against)157 bool 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
match(const std::vector<std::string> & rdn1,const std::vector<std::string> & rdn2)163 bool 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
ParseDistinguishedName(const void * ber_name_data,size_t length)184 bool 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
Parse(const CSSM_X509_NAME * name)269 void 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
Matches(const CertPrincipal & against) const300 bool 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