OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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_certificate.h" | 5 #include "net/base/x509_certificate.h" |
6 | 6 |
7 #include <cert.h> | 7 #include <cert.h> |
8 #include <nss.h> | 8 #include <nss.h> |
9 #include <pk11pub.h> | |
10 #include <prerror.h> | |
11 #include <prtime.h> | 9 #include <prtime.h> |
12 #include <secder.h> | 10 #include <secder.h> |
13 #include <secerr.h> | |
14 #include <sechash.h> | 11 #include <sechash.h> |
15 #include <sslerr.h> | |
16 | 12 |
17 #include "base/logging.h" | 13 #include "base/logging.h" |
18 #include "base/pickle.h" | 14 #include "base/pickle.h" |
19 #include "base/scoped_ptr.h" | 15 #include "base/scoped_ptr.h" |
20 #include "base/time.h" | 16 #include "base/time.h" |
21 #include "base/nss_util.h" | 17 #include "base/nss_util.h" |
22 #include "net/base/cert_status_flags.h" | 18 |
23 #include "net/base/cert_verify_result.h" | |
24 #include "net/base/ev_root_ca_metadata.h" | |
25 #include "net/base/net_errors.h" | |
26 | 19 |
27 namespace net { | 20 namespace net { |
28 | 21 |
29 namespace { | 22 namespace { |
30 | 23 |
31 class ScopedCERTCertificatePolicies { | |
32 public: | |
33 explicit ScopedCERTCertificatePolicies(CERTCertificatePolicies* policies) | |
34 : policies_(policies) {} | |
35 | |
36 ~ScopedCERTCertificatePolicies() { | |
37 if (policies_) | |
38 CERT_DestroyCertificatePoliciesExtension(policies_); | |
39 } | |
40 | |
41 private: | |
42 CERTCertificatePolicies* policies_; | |
43 | |
44 DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertificatePolicies); | |
45 }; | |
46 | |
47 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam | |
48 // array that cvout points to. cvout must be initialized as passed to | |
49 // CERT_PKIXVerifyCert, so that the array must be terminated with | |
50 // cert_po_end type. | |
51 // When it goes out of scope, it destroys values of cert_po_trustAnchor | |
52 // and cert_po_certList types, but doesn't release the array itself. | |
53 class ScopedCERTValOutParam { | |
54 public: | |
55 explicit ScopedCERTValOutParam(CERTValOutParam* cvout) | |
56 : cvout_(cvout) {} | |
57 | |
58 ~ScopedCERTValOutParam() { | |
59 if (cvout_ == NULL) | |
60 return; | |
61 for (CERTValOutParam *p = cvout_; p->type != cert_po_end; p++) { | |
62 switch (p->type) { | |
63 case cert_po_trustAnchor: | |
64 if (p->value.pointer.cert) { | |
65 CERT_DestroyCertificate(p->value.pointer.cert); | |
66 p->value.pointer.cert = NULL; | |
67 } | |
68 break; | |
69 case cert_po_certList: | |
70 if (p->value.pointer.chain) { | |
71 CERT_DestroyCertList(p->value.pointer.chain); | |
72 p->value.pointer.chain = NULL; | |
73 } | |
74 break; | |
75 default: | |
76 break; | |
77 } | |
78 } | |
79 } | |
80 | |
81 private: | |
82 CERTValOutParam* cvout_; | |
83 | |
84 DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam); | |
85 }; | |
86 | |
87 // Map PORT_GetError() return values to our network error codes. | |
88 int MapSecurityError(int err) { | |
89 switch (err) { | |
90 case PR_DIRECTORY_LOOKUP_ERROR: // DNS lookup error. | |
91 return ERR_NAME_NOT_RESOLVED; | |
92 case SEC_ERROR_INVALID_ARGS: | |
93 return ERR_INVALID_ARGUMENT; | |
94 case SSL_ERROR_BAD_CERT_DOMAIN: | |
95 return ERR_CERT_COMMON_NAME_INVALID; | |
96 case SEC_ERROR_INVALID_TIME: | |
97 case SEC_ERROR_EXPIRED_CERTIFICATE: | |
98 return ERR_CERT_DATE_INVALID; | |
99 case SEC_ERROR_UNKNOWN_ISSUER: | |
100 case SEC_ERROR_UNTRUSTED_ISSUER: | |
101 case SEC_ERROR_CA_CERT_INVALID: | |
102 case SEC_ERROR_UNTRUSTED_CERT: | |
103 return ERR_CERT_AUTHORITY_INVALID; | |
104 case SEC_ERROR_REVOKED_CERTIFICATE: | |
105 return ERR_CERT_REVOKED; | |
106 case SEC_ERROR_BAD_DER: | |
107 case SEC_ERROR_BAD_SIGNATURE: | |
108 case SEC_ERROR_CERT_NOT_VALID: | |
109 // TODO(port): add an ERR_CERT_WRONG_USAGE error code. | |
110 case SEC_ERROR_CERT_USAGES_INVALID: | |
111 case SEC_ERROR_POLICY_VALIDATION_FAILED: | |
112 return ERR_CERT_INVALID; | |
113 default: | |
114 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; | |
115 return ERR_FAILED; | |
116 } | |
117 } | |
118 | |
119 // Map PORT_GetError() return values to our cert status flags. | |
120 int MapCertErrorToCertStatus(int err) { | |
121 switch (err) { | |
122 case SSL_ERROR_BAD_CERT_DOMAIN: | |
123 return CERT_STATUS_COMMON_NAME_INVALID; | |
124 case SEC_ERROR_INVALID_TIME: | |
125 case SEC_ERROR_EXPIRED_CERTIFICATE: | |
126 return CERT_STATUS_DATE_INVALID; | |
127 case SEC_ERROR_UNTRUSTED_CERT: | |
128 case SEC_ERROR_UNKNOWN_ISSUER: | |
129 case SEC_ERROR_UNTRUSTED_ISSUER: | |
130 case SEC_ERROR_CA_CERT_INVALID: | |
131 return CERT_STATUS_AUTHORITY_INVALID; | |
132 // TODO(port): map CERT_STATUS_NO_REVOCATION_MECHANISM. | |
133 case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE: | |
134 case SEC_ERROR_OCSP_SERVER_ERROR: | |
135 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; | |
136 case SEC_ERROR_REVOKED_CERTIFICATE: | |
137 return CERT_STATUS_REVOKED; | |
138 case SEC_ERROR_BAD_DER: | |
139 case SEC_ERROR_BAD_SIGNATURE: | |
140 case SEC_ERROR_CERT_NOT_VALID: | |
141 // TODO(port): add a CERT_STATUS_WRONG_USAGE error code. | |
142 case SEC_ERROR_CERT_USAGES_INVALID: | |
143 case SEC_ERROR_POLICY_VALIDATION_FAILED: | |
144 return CERT_STATUS_INVALID; | |
145 default: | |
146 return 0; | |
147 } | |
148 } | |
149 | |
150 // Saves some information about the certificate chain cert_list in | |
151 // *verify_result. The caller MUST initialize *verify_result before calling | |
152 // this function. | |
153 // Note that cert_list[0] is the end entity certificate and cert_list doesn't | |
154 // contain the root CA certificate. | |
155 void GetCertChainInfo(CERTCertList* cert_list, | |
156 CertVerifyResult* verify_result) { | |
157 // NOTE: Using a NSS library before 3.12.3.1 will crash below. To see the | |
158 // NSS version currently in use: | |
159 // 1. use ldd on the chrome executable for NSS's location (ie. libnss3.so*) | |
160 // 2. use ident libnss3.so* for the library's version | |
161 DCHECK(cert_list); | |
162 int i = 0; | |
163 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); | |
164 !CERT_LIST_END(node, cert_list); | |
165 node = CERT_LIST_NEXT(node), i++) { | |
166 SECAlgorithmID& signature = node->cert->signature; | |
167 SECOidTag oid_tag = SECOID_FindOIDTag(&signature.algorithm); | |
168 switch (oid_tag) { | |
169 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: | |
170 verify_result->has_md5 = true; | |
171 if (i != 0) | |
172 verify_result->has_md5_ca = true; | |
173 break; | |
174 case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: | |
175 verify_result->has_md2 = true; | |
176 if (i != 0) | |
177 verify_result->has_md2_ca = true; | |
178 break; | |
179 case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: | |
180 verify_result->has_md4 = true; | |
181 break; | |
182 default: | |
183 break; | |
184 } | |
185 } | |
186 } | |
187 | |
188 typedef char* (*CERTGetNameFunc)(CERTName* name); | 24 typedef char* (*CERTGetNameFunc)(CERTName* name); |
189 | 25 |
190 void ParsePrincipal(CERTName* name, | 26 void ParsePrincipal(CERTName* name, |
191 CertPrincipal* principal) { | 27 CertPrincipal* principal) { |
192 // TODO(jcampan): add business_category and serial_number. | 28 // TODO(jcampan): add business_category and serial_number. |
193 // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and | 29 // TODO(wtc): NSS has the CERT_GetOrgName, CERT_GetOrgUnitName, and |
194 // CERT_GetDomainComponentName functions, but they return only the most | 30 // CERT_GetDomainComponentName functions, but they return only the most |
195 // general (the first) RDN. NSS doesn't have a function for the street | 31 // general (the first) RDN. NSS doesn't have a function for the street |
196 // address. | 32 // address. |
197 static const SECOidTag kOIDs[] = { | 33 static const SECOidTag kOIDs[] = { |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 std::string value = std::string(reinterpret_cast<char*>(p), len); | 120 std::string value = std::string(reinterpret_cast<char*>(p), len); |
285 result->push_back(value); | 121 result->push_back(value); |
286 } | 122 } |
287 name = CERT_GetNextGeneralName(name); | 123 name = CERT_GetNextGeneralName(name); |
288 if (name == alt_name_list) | 124 if (name == alt_name_list) |
289 break; | 125 break; |
290 } | 126 } |
291 PORT_FreeArena(arena, PR_FALSE); | 127 PORT_FreeArena(arena, PR_FALSE); |
292 } | 128 } |
293 | 129 |
294 // Forward declarations. | |
295 SECStatus RetryPKIXVerifyCertWithWorkarounds( | |
296 X509Certificate::OSCertHandle cert_handle, int num_policy_oids, | |
297 std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout); | |
298 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle); | |
299 | |
300 // Call CERT_PKIXVerifyCert for the cert_handle. | |
301 // Verification results are stored in an array of CERTValOutParam. | |
302 // If policy_oids is not NULL and num_policy_oids is positive, policies | |
303 // are also checked. | |
304 // Caller must initialize cvout before calling this function. | |
305 SECStatus PKIXVerifyCert(X509Certificate::OSCertHandle cert_handle, | |
306 bool check_revocation, | |
307 const SECOidTag* policy_oids, | |
308 int num_policy_oids, | |
309 CERTValOutParam* cvout) { | |
310 bool use_crl = check_revocation; | |
311 bool use_ocsp = check_revocation; | |
312 | |
313 // These CAs have multiple keys, which trigger two bugs in NSS's CRL code. | |
314 // 1. NSS may use one key to verify a CRL signed with another key, | |
315 // incorrectly concluding that the CRL's signature is invalid. | |
316 // Hopefully this bug will be fixed in NSS 3.12.9. | |
317 // 2. NSS considers all certificates issued by the CA as revoked when it | |
318 // receives a CRL with an invalid signature. This overly strict policy | |
319 // has been relaxed in NSS 3.12.7. See | |
320 // https://bugzilla.mozilla.org/show_bug.cgi?id=562542. | |
321 // So we have to turn off CRL checking for these CAs. See | |
322 // http://crbug.com/55695. | |
323 static const char* const kMultipleKeyCA[] = { | |
324 "CN=Microsoft Secure Server Authority," | |
325 "DC=redmond,DC=corp,DC=microsoft,DC=com", | |
326 "CN=Microsoft Secure Server Authority", | |
327 }; | |
328 | |
329 if (!NSS_VersionCheck("3.12.7")) { | |
330 for (size_t i = 0; i < arraysize(kMultipleKeyCA); ++i) { | |
331 if (strcmp(cert_handle->issuerName, kMultipleKeyCA[i]) == 0) { | |
332 use_crl = false; | |
333 break; | |
334 } | |
335 } | |
336 } | |
337 | |
338 PRUint64 revocation_method_flags = | |
339 CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD | | |
340 CERT_REV_M_ALLOW_NETWORK_FETCHING | | |
341 CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE | | |
342 CERT_REV_M_IGNORE_MISSING_FRESH_INFO | | |
343 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; | |
344 PRUint64 revocation_method_independent_flags = | |
345 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST; | |
346 if (policy_oids && num_policy_oids > 0) { | |
347 // EV verification requires revocation checking. Consider the certificate | |
348 // revoked if we don't have revocation info. | |
349 // TODO(wtc): Add a bool parameter to expressly specify we're doing EV | |
350 // verification or we want strict revocation flags. | |
351 revocation_method_flags |= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE; | |
352 revocation_method_independent_flags |= | |
353 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; | |
354 } else { | |
355 revocation_method_flags |= CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE; | |
356 revocation_method_independent_flags |= | |
357 CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; | |
358 } | |
359 PRUint64 method_flags[2]; | |
360 method_flags[cert_revocation_method_crl] = revocation_method_flags; | |
361 method_flags[cert_revocation_method_ocsp] = revocation_method_flags; | |
362 | |
363 if (use_crl) { | |
364 method_flags[cert_revocation_method_crl] |= | |
365 CERT_REV_M_TEST_USING_THIS_METHOD; | |
366 } | |
367 if (use_ocsp) { | |
368 method_flags[cert_revocation_method_ocsp] |= | |
369 CERT_REV_M_TEST_USING_THIS_METHOD; | |
370 } | |
371 | |
372 CERTRevocationMethodIndex preferred_revocation_methods[1]; | |
373 if (use_ocsp) { | |
374 preferred_revocation_methods[0] = cert_revocation_method_ocsp; | |
375 } else { | |
376 preferred_revocation_methods[0] = cert_revocation_method_crl; | |
377 } | |
378 | |
379 CERTRevocationFlags revocation_flags; | |
380 revocation_flags.leafTests.number_of_defined_methods = | |
381 arraysize(method_flags); | |
382 revocation_flags.leafTests.cert_rev_flags_per_method = method_flags; | |
383 revocation_flags.leafTests.number_of_preferred_methods = | |
384 arraysize(preferred_revocation_methods); | |
385 revocation_flags.leafTests.preferred_methods = preferred_revocation_methods; | |
386 revocation_flags.leafTests.cert_rev_method_independent_flags = | |
387 revocation_method_independent_flags; | |
388 | |
389 revocation_flags.chainTests.number_of_defined_methods = | |
390 arraysize(method_flags); | |
391 revocation_flags.chainTests.cert_rev_flags_per_method = method_flags; | |
392 revocation_flags.chainTests.number_of_preferred_methods = | |
393 arraysize(preferred_revocation_methods); | |
394 revocation_flags.chainTests.preferred_methods = preferred_revocation_methods; | |
395 revocation_flags.chainTests.cert_rev_method_independent_flags = | |
396 revocation_method_independent_flags; | |
397 | |
398 std::vector<CERTValInParam> cvin; | |
399 cvin.reserve(5); | |
400 CERTValInParam in_param; | |
401 // No need to set cert_pi_trustAnchors here. | |
402 in_param.type = cert_pi_revocationFlags; | |
403 in_param.value.pointer.revocation = &revocation_flags; | |
404 cvin.push_back(in_param); | |
405 if (policy_oids && num_policy_oids > 0) { | |
406 in_param.type = cert_pi_policyOID; | |
407 in_param.value.arraySize = num_policy_oids; | |
408 in_param.value.array.oids = policy_oids; | |
409 cvin.push_back(in_param); | |
410 } | |
411 in_param.type = cert_pi_end; | |
412 cvin.push_back(in_param); | |
413 | |
414 SECStatus rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
415 &cvin[0], cvout, NULL); | |
416 if (rv != SECSuccess) { | |
417 rv = RetryPKIXVerifyCertWithWorkarounds(cert_handle, num_policy_oids, | |
418 &cvin, cvout); | |
419 } | |
420 return rv; | |
421 } | |
422 | |
423 // PKIXVerifyCert calls this function to work around some bugs in | |
424 // CERT_PKIXVerifyCert. All the arguments of this function are either the | |
425 // arguments or local variables of PKIXVerifyCert. | |
426 SECStatus RetryPKIXVerifyCertWithWorkarounds( | |
427 X509Certificate::OSCertHandle cert_handle, int num_policy_oids, | |
428 std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout) { | |
429 // We call this function when the first CERT_PKIXVerifyCert call in | |
430 // PKIXVerifyCert failed, so we initialize |rv| to SECFailure. | |
431 SECStatus rv = SECFailure; | |
432 int nss_error = PORT_GetError(); | |
433 CERTValInParam in_param; | |
434 | |
435 // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate | |
436 // CA certificate, so we retry with cert_pi_useAIACertFetch. | |
437 // cert_pi_useAIACertFetch has several bugs in its error handling and | |
438 // error reporting (NSS bug 528743), so we don't use it by default. | |
439 // Note: When building a certificate chain, CERT_PKIXVerifyCert may | |
440 // incorrectly pick a CA certificate with the same subject name as the | |
441 // missing intermediate CA certificate, and fail with the | |
442 // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with | |
443 // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE. | |
444 if (nss_error == SEC_ERROR_UNKNOWN_ISSUER || | |
445 nss_error == SEC_ERROR_BAD_SIGNATURE) { | |
446 DCHECK_EQ(cvin->back().type, cert_pi_end); | |
447 cvin->pop_back(); | |
448 in_param.type = cert_pi_useAIACertFetch; | |
449 in_param.value.scalar.b = PR_TRUE; | |
450 cvin->push_back(in_param); | |
451 in_param.type = cert_pi_end; | |
452 cvin->push_back(in_param); | |
453 rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
454 &(*cvin)[0], cvout, NULL); | |
455 if (rv == SECSuccess) | |
456 return rv; | |
457 int new_nss_error = PORT_GetError(); | |
458 if (new_nss_error == SEC_ERROR_INVALID_ARGS || | |
459 new_nss_error == SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE || | |
460 new_nss_error == SEC_ERROR_BAD_HTTP_RESPONSE || | |
461 new_nss_error == SEC_ERROR_BAD_LDAP_RESPONSE || | |
462 !IS_SEC_ERROR(new_nss_error)) { | |
463 // Use the original error code because of cert_pi_useAIACertFetch's | |
464 // bad error reporting. | |
465 PORT_SetError(nss_error); | |
466 return rv; | |
467 } | |
468 nss_error = new_nss_error; | |
469 } | |
470 | |
471 // If an intermediate CA certificate has requireExplicitPolicy in its | |
472 // policyConstraints extension, CERT_PKIXVerifyCert fails with | |
473 // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any | |
474 // certificate policy (NSS bug 552775). So we retry with the certificate | |
475 // policy found in the server certificate. | |
476 if (nss_error == SEC_ERROR_POLICY_VALIDATION_FAILED && | |
477 num_policy_oids == 0) { | |
478 SECOidTag policy = GetFirstCertPolicy(cert_handle); | |
479 if (policy != SEC_OID_UNKNOWN) { | |
480 DCHECK_EQ(cvin->back().type, cert_pi_end); | |
481 cvin->pop_back(); | |
482 in_param.type = cert_pi_policyOID; | |
483 in_param.value.arraySize = 1; | |
484 in_param.value.array.oids = &policy; | |
485 cvin->push_back(in_param); | |
486 in_param.type = cert_pi_end; | |
487 cvin->push_back(in_param); | |
488 rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
489 &(*cvin)[0], cvout, NULL); | |
490 if (rv != SECSuccess) { | |
491 // Use the original error code. | |
492 PORT_SetError(nss_error); | |
493 } | |
494 } | |
495 } | |
496 | |
497 return rv; | |
498 } | |
499 | |
500 // Decodes the certificatePolicies extension of the certificate. Returns | |
501 // NULL if the certificate doesn't have the extension or the extension can't | |
502 // be decoded. The returned value must be freed with a | |
503 // CERT_DestroyCertificatePoliciesExtension call. | |
504 CERTCertificatePolicies* DecodeCertPolicies( | |
505 X509Certificate::OSCertHandle cert_handle) { | |
506 SECItem policy_ext; | |
507 SECStatus rv = CERT_FindCertExtension( | |
508 cert_handle, SEC_OID_X509_CERTIFICATE_POLICIES, &policy_ext); | |
509 if (rv != SECSuccess) | |
510 return NULL; | |
511 CERTCertificatePolicies* policies = | |
512 CERT_DecodeCertificatePoliciesExtension(&policy_ext); | |
513 SECITEM_FreeItem(&policy_ext, PR_FALSE); | |
514 return policies; | |
515 } | |
516 | |
517 // Returns the OID tag for the first certificate policy in the certificate's | |
518 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate | |
519 // has no certificate policy. | |
520 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) { | |
521 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); | |
522 if (!policies) | |
523 return SEC_OID_UNKNOWN; | |
524 ScopedCERTCertificatePolicies scoped_policies(policies); | |
525 CERTPolicyInfo* policy_info = policies->policyInfos[0]; | |
526 if (!policy_info) | |
527 return SEC_OID_UNKNOWN; | |
528 if (policy_info->oid != SEC_OID_UNKNOWN) | |
529 return policy_info->oid; | |
530 | |
531 // The certificate policy is unknown to NSS. We need to create a dynamic | |
532 // OID tag for the policy. | |
533 SECOidData od; | |
534 od.oid.len = policy_info->policyID.len; | |
535 od.oid.data = policy_info->policyID.data; | |
536 od.offset = SEC_OID_UNKNOWN; | |
537 // NSS doesn't allow us to pass an empty description, so I use a hardcoded, | |
538 // default description here. The description doesn't need to be unique for | |
539 // each OID. | |
540 od.desc = "a certificate policy"; | |
541 od.mechanism = CKM_INVALID_MECHANISM; | |
542 od.supportedExtension = INVALID_CERT_EXTENSION; | |
543 return SECOID_AddEntry(&od); | |
544 } | |
545 | |
546 bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, | |
547 SECOidTag ev_policy_tag) { | |
548 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); | |
549 if (!policies) { | |
550 LOG(ERROR) << "Cert has no policies extension or extension couldn't be " | |
551 "decoded."; | |
552 return false; | |
553 } | |
554 ScopedCERTCertificatePolicies scoped_policies(policies); | |
555 CERTPolicyInfo** policy_infos = policies->policyInfos; | |
556 while (*policy_infos != NULL) { | |
557 CERTPolicyInfo* policy_info = *policy_infos++; | |
558 SECOidTag oid_tag = policy_info->oid; | |
559 if (oid_tag == SEC_OID_UNKNOWN) | |
560 continue; | |
561 if (oid_tag == ev_policy_tag) | |
562 return true; | |
563 } | |
564 LOG(ERROR) << "No EV Policy Tag"; | |
565 return false; | |
566 } | |
567 | |
568 SECStatus PR_CALLBACK | 130 SECStatus PR_CALLBACK |
569 CollectCertsCallback(void* arg, SECItem** certs, int num_certs) { | 131 CollectCertsCallback(void* arg, SECItem** certs, int num_certs) { |
570 X509Certificate::OSCertHandles* results = | 132 X509Certificate::OSCertHandles* results = |
571 reinterpret_cast<X509Certificate::OSCertHandles*>(arg); | 133 reinterpret_cast<X509Certificate::OSCertHandles*>(arg); |
572 | 134 |
573 for (int i = 0; i < num_certs; ++i) { | 135 for (int i = 0; i < num_certs; ++i) { |
574 X509Certificate::OSCertHandle handle = | 136 X509Certificate::OSCertHandle handle = |
575 X509Certificate::CreateOSCertHandleFromBytes( | 137 X509Certificate::CreateOSCertHandleFromBytes( |
576 reinterpret_cast<char*>(certs[i]->data), certs[i]->len); | 138 reinterpret_cast<char*>(certs[i]->data), certs[i]->len); |
577 if (handle) | 139 if (handle) |
(...skipping 23 matching lines...) Expand all Loading... |
601 | 163 |
602 if (dns_names->empty()) | 164 if (dns_names->empty()) |
603 dns_names->push_back(subject_.common_name); | 165 dns_names->push_back(subject_.common_name); |
604 } | 166 } |
605 | 167 |
606 X509Certificate::OSCertListHandle | 168 X509Certificate::OSCertListHandle |
607 X509Certificate::CreateOSCertListHandle() const { | 169 X509Certificate::CreateOSCertListHandle() const { |
608 return CERT_DupCertificate(cert_handle_); | 170 return CERT_DupCertificate(cert_handle_); |
609 } | 171 } |
610 | 172 |
611 int X509Certificate::Verify(const std::string& hostname, | |
612 int flags, | |
613 CertVerifyResult* verify_result) const { | |
614 verify_result->Reset(); | |
615 | |
616 // Make sure that the hostname matches with the common name of the cert. | |
617 SECStatus status = CERT_VerifyCertName(cert_handle_, hostname.c_str()); | |
618 if (status != SECSuccess) | |
619 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; | |
620 | |
621 // Make sure that the cert is valid now. | |
622 SECCertTimeValidity validity = CERT_CheckCertValidTimes( | |
623 cert_handle_, PR_Now(), PR_TRUE); | |
624 if (validity != secCertTimeValid) | |
625 verify_result->cert_status |= CERT_STATUS_DATE_INVALID; | |
626 | |
627 CERTValOutParam cvout[3]; | |
628 int cvout_index = 0; | |
629 // We don't need the trust anchor for the first PKIXVerifyCert call. | |
630 cvout[cvout_index].type = cert_po_certList; | |
631 cvout[cvout_index].value.pointer.chain = NULL; | |
632 int cvout_cert_list_index = cvout_index; | |
633 cvout_index++; | |
634 cvout[cvout_index].type = cert_po_end; | |
635 ScopedCERTValOutParam scoped_cvout(cvout); | |
636 | |
637 bool check_revocation = (flags & VERIFY_REV_CHECKING_ENABLED); | |
638 if (check_revocation) { | |
639 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | |
640 } else { | |
641 // EV requires revocation checking. | |
642 flags &= ~VERIFY_EV_CERT; | |
643 } | |
644 status = PKIXVerifyCert(cert_handle_, check_revocation, NULL, 0, cvout); | |
645 if (status != SECSuccess) { | |
646 int err = PORT_GetError(); | |
647 LOG(ERROR) << "CERT_PKIXVerifyCert for " << hostname | |
648 << " failed err=" << err; | |
649 // CERT_PKIXVerifyCert rerports the wrong error code for | |
650 // expired certificates (NSS bug 491174) | |
651 if (err == SEC_ERROR_CERT_NOT_VALID && | |
652 (verify_result->cert_status & CERT_STATUS_DATE_INVALID) != 0) | |
653 err = SEC_ERROR_EXPIRED_CERTIFICATE; | |
654 int cert_status = MapCertErrorToCertStatus(err); | |
655 if (cert_status) { | |
656 verify_result->cert_status |= cert_status; | |
657 return MapCertStatusToNetError(verify_result->cert_status); | |
658 } | |
659 // |err| is not a certificate error. | |
660 return MapSecurityError(err); | |
661 } | |
662 | |
663 GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain, | |
664 verify_result); | |
665 if (IsCertStatusError(verify_result->cert_status)) | |
666 return MapCertStatusToNetError(verify_result->cert_status); | |
667 | |
668 if ((flags & VERIFY_EV_CERT) && VerifyEV()) | |
669 verify_result->cert_status |= CERT_STATUS_IS_EV; | |
670 return OK; | |
671 } | |
672 | |
673 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp | |
674 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate. | |
675 // TODO(wtc): A possible optimization is that we get the trust anchor from | |
676 // the first PKIXVerifyCert call. We look up the EV policy for the trust | |
677 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV. | |
678 // Otherwise, we pass just that EV policy (as opposed to all the EV policies) | |
679 // to the second PKIXVerifyCert call. | |
680 bool X509Certificate::VerifyEV() const { | |
681 net::EVRootCAMetadata* metadata = net::EVRootCAMetadata::GetInstance(); | |
682 | |
683 CERTValOutParam cvout[3]; | |
684 int cvout_index = 0; | |
685 cvout[cvout_index].type = cert_po_trustAnchor; | |
686 cvout[cvout_index].value.pointer.cert = NULL; | |
687 int cvout_trust_anchor_index = cvout_index; | |
688 cvout_index++; | |
689 cvout[cvout_index].type = cert_po_end; | |
690 ScopedCERTValOutParam scoped_cvout(cvout); | |
691 | |
692 SECStatus status = PKIXVerifyCert(cert_handle_, | |
693 true, | |
694 metadata->GetPolicyOIDs(), | |
695 metadata->NumPolicyOIDs(), | |
696 cvout); | |
697 if (status != SECSuccess) | |
698 return false; | |
699 | |
700 CERTCertificate* root_ca = | |
701 cvout[cvout_trust_anchor_index].value.pointer.cert; | |
702 if (root_ca == NULL) | |
703 return false; | |
704 SHA1Fingerprint fingerprint = | |
705 X509Certificate::CalculateFingerprint(root_ca); | |
706 SECOidTag ev_policy_tag = SEC_OID_UNKNOWN; | |
707 if (!metadata->GetPolicyOID(fingerprint, &ev_policy_tag)) | |
708 return false; | |
709 | |
710 if (!CheckCertPolicies(cert_handle_, ev_policy_tag)) | |
711 return false; | |
712 | |
713 return true; | |
714 } | |
715 | |
716 // static | 173 // static |
717 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, | 174 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, |
718 X509Certificate::OSCertHandle b) { | 175 X509Certificate::OSCertHandle b) { |
719 DCHECK(a && b); | 176 DCHECK(a && b); |
720 if (a == b) | 177 if (a == b) |
721 return true; | 178 return true; |
722 return a->derCert.len == b->derCert.len && | 179 return a->derCert.len == b->derCert.len && |
723 memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0; | 180 memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0; |
724 } | 181 } |
725 | 182 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
827 | 284 |
828 // static | 285 // static |
829 bool X509Certificate::WriteCertHandleToPickle(OSCertHandle cert_handle, | 286 bool X509Certificate::WriteCertHandleToPickle(OSCertHandle cert_handle, |
830 Pickle* pickle) { | 287 Pickle* pickle) { |
831 return pickle->WriteData( | 288 return pickle->WriteData( |
832 reinterpret_cast<const char*>(cert_handle->derCert.data), | 289 reinterpret_cast<const char*>(cert_handle->derCert.data), |
833 cert_handle->derCert.len); | 290 cert_handle->derCert.len); |
834 } | 291 } |
835 | 292 |
836 } // namespace net | 293 } // namespace net |
OLD | NEW |