OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/x509_cert_types.h" | 5 #include "net/base/x509_cert_types.h" |
6 | 6 |
7 #include <CoreServices/CoreServices.h> | 7 #include <CoreServices/CoreServices.h> |
8 #include <Security/Security.h> | 8 #include <Security/Security.h> |
9 #include <Security/SecAsn1Coder.h> | 9 #include <Security/SecAsn1Coder.h> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/i18n/icu_string_conversions.h" | 12 #include "base/i18n/icu_string_conversions.h" |
13 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
14 | 14 |
15 namespace net { | 15 namespace net { |
16 | 16 |
17 static const CSSM_OID* kOIDs[] = { | 17 namespace { |
18 | |
19 const CSSM_OID* kOIDs[] = { | |
18 &CSSMOID_CommonName, | 20 &CSSMOID_CommonName, |
19 &CSSMOID_LocalityName, | 21 &CSSMOID_LocalityName, |
20 &CSSMOID_StateProvinceName, | 22 &CSSMOID_StateProvinceName, |
21 &CSSMOID_CountryName, | 23 &CSSMOID_CountryName, |
22 &CSSMOID_StreetAddress, | 24 &CSSMOID_StreetAddress, |
23 &CSSMOID_OrganizationName, | 25 &CSSMOID_OrganizationName, |
24 &CSSMOID_OrganizationalUnitName, | 26 &CSSMOID_OrganizationalUnitName, |
25 &CSSMOID_DNQualifier // This should be "DC" but is undoubtedly wrong. | 27 &CSSMOID_DNQualifier // This should be "DC" but is undoubtedly wrong. |
26 }; // TODO(avi): Find the right OID. | 28 }; // TODO(avi): Find the right OID. |
27 | 29 |
28 // Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.) | |
29 static std::string DataToString(CSSM_DATA data); | |
30 | |
31 // Converts raw CSSM_DATA in ISO-8859-1 to a std::string in UTF-8. | |
32 static std::string Latin1DataToUTF8String(CSSM_DATA data); | |
33 | |
34 // Converts big-endian UTF-16 to UTF-8 in a std::string. | |
35 // Note: The byte-order flipping is done in place on the input buffer! | |
36 static bool UTF16BigEndianToUTF8(char16* chars, size_t length, | |
37 std::string* out_string); | |
38 | |
39 // Converts big-endian UTF-32 to UTF-8 in a std::string. | |
40 // Note: The byte-order flipping is done in place on the input buffer! | |
41 static bool UTF32BigEndianToUTF8(char32* chars, size_t length, | |
42 std::string* out_string); | |
43 | |
44 // Adds a type+value pair to the appropriate vector from a C array. | |
45 // The array is keyed by the matching OIDs from kOIDS[]. | |
46 static void AddTypeValuePair(const CSSM_OID type, | |
47 const std::string& value, | |
48 std::vector<std::string>* values[]); | |
49 | |
50 // Stores the first string of the vector, if any, to *single_value. | |
51 static void SetSingle(const std::vector<std::string> &values, | |
52 std::string* single_value); | |
53 | |
54 | |
55 void CertPrincipal::Parse(const CSSM_X509_NAME* name) { | |
wtc
2010/11/17 21:31:53
PERSONAL OPINION: when a file becomes stable, it m
Ryan Sleevi
2010/11/18 08:21:36
It was more about pushing this code into the anony
| |
56 std::vector<std::string> common_names, locality_names, state_names, | |
57 country_names; | |
58 | |
59 std::vector<std::string>* values[] = { | |
60 &common_names, &locality_names, | |
61 &state_names, &country_names, | |
62 &(this->street_addresses), | |
63 &(this->organization_names), | |
64 &(this->organization_unit_names), | |
65 &(this->domain_components) | |
66 }; | |
67 DCHECK(arraysize(kOIDs) == arraysize(values)); | |
68 | |
69 for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) { | |
70 CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn]; | |
71 for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) { | |
72 CSSM_X509_TYPE_VALUE_PAIR pair_struct = | |
73 rdn_struct.AttributeTypeAndValue[pair]; | |
74 AddTypeValuePair(pair_struct.type, | |
75 DataToString(pair_struct.value), | |
76 values); | |
77 } | |
78 } | |
79 | |
80 SetSingle(common_names, &this->common_name); | |
81 SetSingle(locality_names, &this->locality_name); | |
82 SetSingle(state_names, &this->state_or_province_name); | |
83 SetSingle(country_names, &this->country_name); | |
84 } | |
85 | |
86 | |
87 // The following structs and templates work with Apple's very arcane and under- | 30 // The following structs and templates work with Apple's very arcane and under- |
88 // documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1 | 31 // documented SecAsn1Parser API, which is apparently the same as NSS's ASN.1 |
89 // decoder: | 32 // decoder: |
90 // http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html | 33 // http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn1.html |
91 | 34 |
92 // These are used to parse the contents of a raw | 35 // These are used to parse the contents of a raw |
93 // BER DistinguishedName structure. | 36 // BER DistinguishedName structure. |
94 | 37 |
95 struct KeyValuePair { | 38 struct KeyValuePair { |
96 CSSM_OID key; | 39 CSSM_OID key; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
130 { SEC_ASN1_OBJECT_ID, offsetof(KeyValuePair, key), }, | 73 { SEC_ASN1_OBJECT_ID, offsetof(KeyValuePair, key), }, |
131 { SEC_ASN1_INLINE, 0, &kStringValueTemplate, }, | 74 { SEC_ASN1_INLINE, 0, &kStringValueTemplate, }, |
132 { 0, } | 75 { 0, } |
133 }; | 76 }; |
134 | 77 |
135 struct KeyValuePairs { | 78 struct KeyValuePairs { |
136 KeyValuePair* pairs; | 79 KeyValuePair* pairs; |
137 }; | 80 }; |
138 | 81 |
139 static const SecAsn1Template kKeyValuePairSetTemplate[] = { | 82 static const SecAsn1Template kKeyValuePairSetTemplate[] = { |
140 { SEC_ASN1_SET_OF, offsetof(KeyValuePairs,pairs), | 83 { SEC_ASN1_SET_OF, offsetof(KeyValuePairs, pairs), |
141 kKeyValuePairTemplate, sizeof(KeyValuePairs) } | 84 kKeyValuePairTemplate, sizeof(KeyValuePairs) } |
142 }; | 85 }; |
143 | 86 |
144 struct X509Name { | 87 struct X509Name { |
145 KeyValuePairs** pairs_list; | 88 KeyValuePairs** pairs_list; |
146 }; | 89 }; |
147 | 90 |
148 static const SecAsn1Template kNameTemplate[] = { | 91 static const SecAsn1Template kNameTemplate[] = { |
149 { SEC_ASN1_SEQUENCE_OF, offsetof(X509Name,pairs_list), | 92 { SEC_ASN1_SEQUENCE_OF, offsetof(X509Name, pairs_list), |
150 kKeyValuePairSetTemplate, sizeof(X509Name) } | 93 kKeyValuePairSetTemplate, sizeof(X509Name) } |
151 }; | 94 }; |
152 | 95 |
96 // Converts raw CSSM_DATA to a std::string. (Char encoding is unaltered.) | |
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. | |
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! | |
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! | |
123 bool UTF32BigEndianToUTF8(char32* chars, size_t length, | |
124 std::string* out_string) { | |
125 for (size_t i = 0; i < length; i++) | |
bulach
2010/11/17 12:16:50
nit: ++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, | |
bulach
2010/11/17 12:16:50
nit: s/> &values/>& values/
| |
150 std::string* single_value) { | |
bulach
2010/11/17 12:16:50
nit:unindent
| |
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.size() > 0) | |
154 *single_value = values[0]; | |
155 } | |
156 | |
157 bool match(const std::string &str, const std::string &against) { | |
bulach
2010/11/17 12:16:50
nit: s/string &/string& / for both params
| |
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 | |
163 bool match(const std::vector<std::string> &rdn1, | |
164 const std::vector<std::string> &rdn2) { | |
bulach
2010/11/17 12:16:50
nit: s/> &rd/>& rd/ for the two above
| |
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 | |
153 bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data, | 184 bool CertPrincipal::ParseDistinguishedName(const void* ber_name_data, |
154 size_t length) { | 185 size_t length) { |
155 DCHECK(ber_name_data); | 186 DCHECK(ber_name_data); |
156 | 187 |
157 // First parse the BER |name_data| into the above structs. | 188 // First parse the BER |name_data| into the above structs. |
158 SecAsn1CoderRef coder = NULL; | 189 SecAsn1CoderRef coder = NULL; |
159 SecAsn1CoderCreate(&coder); | 190 SecAsn1CoderCreate(&coder); |
160 DCHECK(coder); | 191 DCHECK(coder); |
161 X509Name* name = NULL; | 192 X509Name* name = NULL; |
162 OSStatus err = SecAsn1Decode(coder, ber_name_data, length, kNameTemplate, | 193 OSStatus err = SecAsn1Decode(coder, ber_name_data, length, kNameTemplate, |
(...skipping 12 matching lines...) Expand all Loading... | |
175 std::vector<std::string>* values[] = { | 206 std::vector<std::string>* values[] = { |
176 &common_names, &locality_names, | 207 &common_names, &locality_names, |
177 &state_names, &country_names, | 208 &state_names, &country_names, |
178 &this->street_addresses, | 209 &this->street_addresses, |
179 &this->organization_names, | 210 &this->organization_names, |
180 &this->organization_unit_names, | 211 &this->organization_unit_names, |
181 &this->domain_components | 212 &this->domain_components |
182 }; | 213 }; |
183 DCHECK(arraysize(kOIDs) == arraysize(values)); | 214 DCHECK(arraysize(kOIDs) == arraysize(values)); |
184 | 215 |
185 for (int rdn=0; name[rdn].pairs_list; ++rdn) { | 216 for (int rdn = 0; name[rdn].pairs_list; ++rdn) { |
186 KeyValuePair *pair; | 217 KeyValuePair *pair; |
187 for (int pair_index = 0; | 218 for (int pair_index = 0; |
188 NULL != (pair = name[rdn].pairs_list[0][pair_index].pairs); | 219 NULL != (pair = name[rdn].pairs_list[0][pair_index].pairs); |
189 ++pair_index) { | 220 ++pair_index) { |
190 switch (pair->value_type) { | 221 switch (pair->value_type) { |
191 case KeyValuePair::kTypeIA5String: // ASCII (that means 7-bit!) | 222 case KeyValuePair::kTypeIA5String: // ASCII (that means 7-bit!) |
192 case KeyValuePair::kTypePrintableString: // a subset of ASCII | 223 case KeyValuePair::kTypePrintableString: // a subset of ASCII |
193 case KeyValuePair::kTypeUTF8String: // UTF-8 | 224 case KeyValuePair::kTypeUTF8String: // UTF-8 |
194 AddTypeValuePair(pair->key, DataToString(pair->value), values); | 225 AddTypeValuePair(pair->key, DataToString(pair->value), values); |
195 break; | 226 break; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
228 SetSingle(common_names, &this->common_name); | 259 SetSingle(common_names, &this->common_name); |
229 SetSingle(locality_names, &this->locality_name); | 260 SetSingle(locality_names, &this->locality_name); |
230 SetSingle(state_names, &this->state_or_province_name); | 261 SetSingle(state_names, &this->state_or_province_name); |
231 SetSingle(country_names, &this->country_name); | 262 SetSingle(country_names, &this->country_name); |
232 | 263 |
233 // Releasing |coder| frees all the memory pointed to via |name|. | 264 // Releasing |coder| frees all the memory pointed to via |name|. |
234 SecAsn1CoderRelease(coder); | 265 SecAsn1CoderRelease(coder); |
235 return true; | 266 return true; |
236 } | 267 } |
237 | 268 |
269 void CertPrincipal::Parse(const CSSM_X509_NAME* name) { | |
270 std::vector<std::string> common_names, locality_names, state_names, | |
271 country_names; | |
238 | 272 |
239 // SUBROUTINES: | 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)); | |
240 | 282 |
241 static std::string DataToString(CSSM_DATA data) { | 283 for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) { |
242 return std::string( | 284 CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn]; |
243 reinterpret_cast<std::string::value_type*>(data.Data), | 285 for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) { |
244 data.Length); | 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); | |
245 } | 298 } |
246 | 299 |
247 static std::string Latin1DataToUTF8String(CSSM_DATA data) { | 300 bool CertPrincipal::Matches(const CertPrincipal& against) const { |
248 string16 utf16; | 301 return match(common_name, against.common_name) && |
249 if (!CodepageToUTF16(DataToString(data), base::kCodepageLatin1, | 302 match(common_name, against.common_name) && |
bulach
2010/11/17 12:16:50
301 and 302 are redundant
Ryan Sleevi
2010/11/18 08:21:36
Thanks! Good catch!
| |
250 base::OnStringConversionError::FAIL, &utf16)) | 303 match(locality_name, against.locality_name) && |
251 return ""; | 304 match(state_or_province_name, against.state_or_province_name) && |
252 return UTF16ToUTF8(utf16); | 305 match(country_name, against.country_name) && |
253 } | 306 match(street_addresses, against.street_addresses) && |
254 | 307 match(organization_names, against.organization_names) && |
255 bool UTF16BigEndianToUTF8(char16* chars, size_t length, | 308 match(organization_unit_names, against.organization_unit_names) && |
256 std::string* out_string) { | 309 match(domain_components, against.domain_components); |
257 for (size_t i = 0; i < length; i++) | |
258 chars[i] = EndianU16_BtoN(chars[i]); | |
259 return UTF16ToUTF8(chars, length, out_string); | |
260 } | |
261 | |
262 bool UTF32BigEndianToUTF8(char32* chars, size_t length, | |
263 std::string* out_string) { | |
264 for (size_t i = 0; i < length; i++) | |
265 chars[i] = EndianS32_BtoN(chars[i]); | |
266 #if defined(WCHAR_T_IS_UTF32) | |
267 return WideToUTF8(reinterpret_cast<const wchar_t*>(chars), | |
268 length, out_string); | |
269 #else | |
270 #error This code doesn't handle 16-bit wchar_t. | |
271 #endif | |
272 } | |
273 | |
274 static void AddTypeValuePair(const CSSM_OID type, | |
275 const std::string& value, | |
276 std::vector<std::string>* values[]) { | |
277 for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { | |
278 if (CSSMOIDEqual(&type, kOIDs[oid])) { | |
279 values[oid]->push_back(value); | |
280 break; | |
281 } | |
282 } | |
283 } | |
284 | |
285 static void SetSingle(const std::vector<std::string> &values, | |
286 std::string* single_value) { | |
287 // We don't expect to have more than one CN, L, S, and C. | |
288 LOG_IF(WARNING, values.size() > 1) << "Didn't expect multiple values"; | |
289 if (values.size() > 0) | |
290 *single_value = values[0]; | |
291 } | 310 } |
292 | 311 |
293 } // namespace net | 312 } // namespace net |
OLD | NEW |