| 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 |