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) { | |
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; |
97 int value_type; | 40 int value_type; |
98 CSSM_DATA value; | 41 CSSM_DATA value; |
99 | 42 |
100 enum { | 43 enum { |
101 kTypeOther = 0, | 44 kTypeOther = 0, |
102 kTypePrintableString, | 45 kTypePrintableString, |
103 kTypeIA5String, | 46 kTypeIA5String, |
104 kTypeT61String, | 47 kTypeT61String, |
105 kTypeUTF8String, | 48 kTypeUTF8String, |
106 kTypeBMPString, | 49 kTypeBMPString, |
107 kTypeUniversalString, | 50 kTypeUniversalString, |
108 }; | 51 }; |
109 }; | 52 }; |
110 | 53 |
111 static const SecAsn1Template kStringValueTemplate[] = { | 54 const SecAsn1Template kStringValueTemplate[] = { |
112 { SEC_ASN1_CHOICE, offsetof(KeyValuePair, value_type), }, | 55 { SEC_ASN1_CHOICE, offsetof(KeyValuePair, value_type), }, |
113 { SEC_ASN1_PRINTABLE_STRING, | 56 { SEC_ASN1_PRINTABLE_STRING, |
114 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypePrintableString }, | 57 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypePrintableString }, |
115 { SEC_ASN1_IA5_STRING, | 58 { SEC_ASN1_IA5_STRING, |
116 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeIA5String }, | 59 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeIA5String }, |
117 { SEC_ASN1_T61_STRING, | 60 { SEC_ASN1_T61_STRING, |
118 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeT61String }, | 61 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeT61String }, |
119 { SEC_ASN1_UTF8_STRING, | 62 { SEC_ASN1_UTF8_STRING, |
120 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUTF8String }, | 63 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUTF8String }, |
121 { SEC_ASN1_BMP_STRING, | 64 { SEC_ASN1_BMP_STRING, |
122 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeBMPString }, | 65 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeBMPString }, |
123 { SEC_ASN1_UNIVERSAL_STRING, | 66 { SEC_ASN1_UNIVERSAL_STRING, |
124 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUniversalString }, | 67 offsetof(KeyValuePair, value), 0, KeyValuePair::kTypeUniversalString }, |
125 { 0, } | 68 { 0, } |
126 }; | 69 }; |
127 | 70 |
128 static const SecAsn1Template kKeyValuePairTemplate[] = { | 71 const SecAsn1Template kKeyValuePairTemplate[] = { |
129 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(KeyValuePair) }, | 72 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(KeyValuePair) }, |
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 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 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) |
| 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.size() > 0) |
| 154 *single_value = values[0]; |
| 155 } |
| 156 |
| 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 |
| 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 |
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(locality_name, against.locality_name) && |
250 base::OnStringConversionError::FAIL, &utf16)) | 303 match(state_or_province_name, against.state_or_province_name) && |
251 return ""; | 304 match(country_name, against.country_name) && |
252 return UTF16ToUTF8(utf16); | 305 match(street_addresses, against.street_addresses) && |
253 } | 306 match(organization_names, against.organization_names) && |
254 | 307 match(organization_unit_names, against.organization_unit_names) && |
255 bool UTF16BigEndianToUTF8(char16* chars, size_t length, | 308 match(domain_components, against.domain_components); |
256 std::string* out_string) { | |
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 } | 309 } |
292 | 310 |
293 } // namespace net | 311 } // namespace net |
OLD | NEW |