| 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 <pk11pub.h> | 8 #include <pk11pub.h> |
| 9 #include <prerror.h> | 9 #include <prerror.h> |
| 10 #include <prtime.h> | 10 #include <prtime.h> |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 131 case SEC_ERROR_CA_CERT_INVALID: | 131 case SEC_ERROR_CA_CERT_INVALID: |
| 132 case SEC_ERROR_UNTRUSTED_CERT: | 132 case SEC_ERROR_UNTRUSTED_CERT: |
| 133 return ERR_CERT_AUTHORITY_INVALID; | 133 return ERR_CERT_AUTHORITY_INVALID; |
| 134 case SEC_ERROR_REVOKED_CERTIFICATE: | 134 case SEC_ERROR_REVOKED_CERTIFICATE: |
| 135 return ERR_CERT_REVOKED; | 135 return ERR_CERT_REVOKED; |
| 136 case SEC_ERROR_BAD_DER: | 136 case SEC_ERROR_BAD_DER: |
| 137 case SEC_ERROR_BAD_SIGNATURE: | 137 case SEC_ERROR_BAD_SIGNATURE: |
| 138 case SEC_ERROR_CERT_NOT_VALID: | 138 case SEC_ERROR_CERT_NOT_VALID: |
| 139 // TODO(port): add an ERR_CERT_WRONG_USAGE error code. | 139 // TODO(port): add an ERR_CERT_WRONG_USAGE error code. |
| 140 case SEC_ERROR_CERT_USAGES_INVALID: | 140 case SEC_ERROR_CERT_USAGES_INVALID: |
| 141 case SEC_ERROR_POLICY_VALIDATION_FAILED: |
| 141 return ERR_CERT_INVALID; | 142 return ERR_CERT_INVALID; |
| 142 default: | 143 default: |
| 143 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; | 144 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; |
| 144 return ERR_FAILED; | 145 return ERR_FAILED; |
| 145 } | 146 } |
| 146 } | 147 } |
| 147 | 148 |
| 148 // Map PORT_GetError() return values to our cert status flags. | 149 // Map PORT_GetError() return values to our cert status flags. |
| 149 int MapCertErrorToCertStatus(int err) { | 150 int MapCertErrorToCertStatus(int err) { |
| 150 switch (err) { | 151 switch (err) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 162 case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE: | 163 case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE: |
| 163 case SEC_ERROR_OCSP_SERVER_ERROR: | 164 case SEC_ERROR_OCSP_SERVER_ERROR: |
| 164 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; | 165 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; |
| 165 case SEC_ERROR_REVOKED_CERTIFICATE: | 166 case SEC_ERROR_REVOKED_CERTIFICATE: |
| 166 return CERT_STATUS_REVOKED; | 167 return CERT_STATUS_REVOKED; |
| 167 case SEC_ERROR_BAD_DER: | 168 case SEC_ERROR_BAD_DER: |
| 168 case SEC_ERROR_BAD_SIGNATURE: | 169 case SEC_ERROR_BAD_SIGNATURE: |
| 169 case SEC_ERROR_CERT_NOT_VALID: | 170 case SEC_ERROR_CERT_NOT_VALID: |
| 170 // TODO(port): add a CERT_STATUS_WRONG_USAGE error code. | 171 // TODO(port): add a CERT_STATUS_WRONG_USAGE error code. |
| 171 case SEC_ERROR_CERT_USAGES_INVALID: | 172 case SEC_ERROR_CERT_USAGES_INVALID: |
| 173 case SEC_ERROR_POLICY_VALIDATION_FAILED: |
| 172 return CERT_STATUS_INVALID; | 174 return CERT_STATUS_INVALID; |
| 173 default: | 175 default: |
| 174 return 0; | 176 return 0; |
| 175 } | 177 } |
| 176 } | 178 } |
| 177 | 179 |
| 178 // Saves some information about the certificate chain cert_list in | 180 // Saves some information about the certificate chain cert_list in |
| 179 // *verify_result. The caller MUST initialize *verify_result before calling | 181 // *verify_result. The caller MUST initialize *verify_result before calling |
| 180 // this function. | 182 // this function. |
| 181 // Note that cert_list[0] is the end entity certificate and cert_list doesn't | 183 // Note that cert_list[0] is the end entity certificate and cert_list doesn't |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 std::string value = std::string(reinterpret_cast<char*>(p), len); | 314 std::string value = std::string(reinterpret_cast<char*>(p), len); |
| 313 result->push_back(value); | 315 result->push_back(value); |
| 314 } | 316 } |
| 315 name = CERT_GetNextGeneralName(name); | 317 name = CERT_GetNextGeneralName(name); |
| 316 if (name == alt_name_list) | 318 if (name == alt_name_list) |
| 317 break; | 319 break; |
| 318 } | 320 } |
| 319 PORT_FreeArena(arena, PR_FALSE); | 321 PORT_FreeArena(arena, PR_FALSE); |
| 320 } | 322 } |
| 321 | 323 |
| 324 // Forward declarations. |
| 325 SECStatus RetryPKIXVerifyCertWithWorkarounds( |
| 326 X509Certificate::OSCertHandle cert_handle, int num_policy_oids, |
| 327 std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout); |
| 328 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle); |
| 329 |
| 322 // Call CERT_PKIXVerifyCert for the cert_handle. | 330 // Call CERT_PKIXVerifyCert for the cert_handle. |
| 323 // Verification results are stored in an array of CERTValOutParam. | 331 // Verification results are stored in an array of CERTValOutParam. |
| 324 // If policy_oids is not NULL and num_policy_oids is positive, policies | 332 // If policy_oids is not NULL and num_policy_oids is positive, policies |
| 325 // are also checked. | 333 // are also checked. |
| 326 // Caller must initialize cvout before calling this function. | 334 // Caller must initialize cvout before calling this function. |
| 327 SECStatus PKIXVerifyCert(X509Certificate::OSCertHandle cert_handle, | 335 SECStatus PKIXVerifyCert(X509Certificate::OSCertHandle cert_handle, |
| 328 bool check_revocation, | 336 bool check_revocation, |
| 329 const SECOidTag* policy_oids, | 337 const SECOidTag* policy_oids, |
| 330 int num_policy_oids, | 338 int num_policy_oids, |
| 331 CERTValOutParam* cvout) { | 339 CERTValOutParam* cvout) { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 385 | 393 |
| 386 revocation_flags.chainTests.number_of_defined_methods = | 394 revocation_flags.chainTests.number_of_defined_methods = |
| 387 arraysize(method_flags); | 395 arraysize(method_flags); |
| 388 revocation_flags.chainTests.cert_rev_flags_per_method = method_flags; | 396 revocation_flags.chainTests.cert_rev_flags_per_method = method_flags; |
| 389 revocation_flags.chainTests.number_of_preferred_methods = | 397 revocation_flags.chainTests.number_of_preferred_methods = |
| 390 arraysize(preferred_revocation_methods); | 398 arraysize(preferred_revocation_methods); |
| 391 revocation_flags.chainTests.preferred_methods = preferred_revocation_methods; | 399 revocation_flags.chainTests.preferred_methods = preferred_revocation_methods; |
| 392 revocation_flags.chainTests.cert_rev_method_independent_flags = | 400 revocation_flags.chainTests.cert_rev_method_independent_flags = |
| 393 revocation_method_independent_flags; | 401 revocation_method_independent_flags; |
| 394 | 402 |
| 395 CERTValInParam cvin[4]; | 403 std::vector<CERTValInParam> cvin; |
| 396 int cvin_index = 0; | 404 cvin.reserve(5); |
| 405 CERTValInParam in_param; |
| 397 // No need to set cert_pi_trustAnchors here. | 406 // No need to set cert_pi_trustAnchors here. |
| 398 cvin[cvin_index].type = cert_pi_revocationFlags; | 407 in_param.type = cert_pi_revocationFlags; |
| 399 cvin[cvin_index].value.pointer.revocation = &revocation_flags; | 408 in_param.value.pointer.revocation = &revocation_flags; |
| 400 cvin_index++; | 409 cvin.push_back(in_param); |
| 401 std::vector<SECOidTag> policies; | |
| 402 if (policy_oids && num_policy_oids > 0) { | 410 if (policy_oids && num_policy_oids > 0) { |
| 403 cvin[cvin_index].type = cert_pi_policyOID; | 411 in_param.type = cert_pi_policyOID; |
| 404 cvin[cvin_index].value.arraySize = num_policy_oids; | 412 in_param.value.arraySize = num_policy_oids; |
| 405 cvin[cvin_index].value.array.oids = policy_oids; | 413 in_param.value.array.oids = policy_oids; |
| 406 cvin_index++; | 414 cvin.push_back(in_param); |
| 407 } | 415 } |
| 408 // Add cert_pi_useAIACertFetch last so we can easily remove it from the | 416 in_param.type = cert_pi_end; |
| 409 // cvin array in the workaround below. | 417 cvin.push_back(in_param); |
| 410 cvin[cvin_index].type = cert_pi_useAIACertFetch; | |
| 411 cvin[cvin_index].value.scalar.b = PR_TRUE; | |
| 412 cvin_index++; | |
| 413 cvin[cvin_index].type = cert_pi_end; | |
| 414 | 418 |
| 415 SECStatus rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | 419 SECStatus rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, |
| 416 cvin, cvout, NULL); | 420 &cvin[0], cvout, NULL); |
| 417 if (rv != SECSuccess) { | 421 if (rv != SECSuccess) { |
| 418 // cert_pi_useAIACertFetch can't handle a CA issuers access location that | 422 rv = RetryPKIXVerifyCertWithWorkarounds(cert_handle, num_policy_oids, |
| 419 // is an LDAP URL with an empty host name (NSS bug 528741). If cert fetch | 423 &cvin, cvout); |
| 420 // fails because of a network error, it also causes CERT_PKIXVerifyCert | |
| 421 // to report the network error rather than SEC_ERROR_UNKNOWN_ISSUER. To | |
| 422 // work around these NSS bugs, we retry without cert_pi_useAIACertFetch. | |
| 423 int nss_error = PORT_GetError(); | |
| 424 if (nss_error == SEC_ERROR_INVALID_ARGS || !IS_SEC_ERROR(nss_error)) { | |
| 425 cvin_index--; | |
| 426 DCHECK_EQ(cvin[cvin_index].type, cert_pi_useAIACertFetch); | |
| 427 cvin[cvin_index].type = cert_pi_end; | |
| 428 rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
| 429 cvin, cvout, NULL); | |
| 430 } | |
| 431 } | 424 } |
| 432 return rv; | 425 return rv; |
| 433 } | 426 } |
| 434 | 427 |
| 435 bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, | 428 // PKIXVerifyCert calls this function to work around some bugs in |
| 436 SECOidTag ev_policy_tag) { | 429 // CERT_PKIXVerifyCert. All the arguments of this function are either the |
| 430 // arguments or local variables of PKIXVerifyCert. |
| 431 SECStatus RetryPKIXVerifyCertWithWorkarounds( |
| 432 X509Certificate::OSCertHandle cert_handle, int num_policy_oids, |
| 433 std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout) { |
| 434 // We call this function when the first CERT_PKIXVerifyCert call in |
| 435 // PKIXVerifyCert failed, so we initialize |rv| to SECFailure. |
| 436 SECStatus rv = SECFailure; |
| 437 int nss_error = PORT_GetError(); |
| 438 CERTValInParam in_param; |
| 439 |
| 440 // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate |
| 441 // CA certificate, so we retry with cert_pi_useAIACertFetch. |
| 442 // cert_pi_useAIACertFetch has several bugs in its error handling and |
| 443 // error reporting (NSS bug 528743), so we don't use it by default. |
| 444 // Note: When building a certificate chain, CERT_PKIXVerifyCert may |
| 445 // incorrectly pick a CA certificate with the same subject name as the |
| 446 // missing intermediate CA certificate, and fail with the |
| 447 // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with |
| 448 // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE. |
| 449 if (nss_error == SEC_ERROR_UNKNOWN_ISSUER || |
| 450 nss_error == SEC_ERROR_BAD_SIGNATURE) { |
| 451 DCHECK_EQ(cvin->back().type, cert_pi_end); |
| 452 cvin->pop_back(); |
| 453 in_param.type = cert_pi_useAIACertFetch; |
| 454 in_param.value.scalar.b = PR_TRUE; |
| 455 cvin->push_back(in_param); |
| 456 in_param.type = cert_pi_end; |
| 457 cvin->push_back(in_param); |
| 458 rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, |
| 459 &(*cvin)[0], cvout, NULL); |
| 460 if (rv == SECSuccess) |
| 461 return rv; |
| 462 int new_nss_error = PORT_GetError(); |
| 463 if (new_nss_error == SEC_ERROR_INVALID_ARGS || |
| 464 new_nss_error == SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE || |
| 465 !IS_SEC_ERROR(new_nss_error)) { |
| 466 // Use the original error code because of cert_pi_useAIACertFetch's |
| 467 // bad error reporting. |
| 468 PORT_SetError(nss_error); |
| 469 return rv; |
| 470 } |
| 471 nss_error = new_nss_error; |
| 472 } |
| 473 |
| 474 // If an intermediate CA certificate has requireExplicitPolicy in its |
| 475 // policyConstraints extension, CERT_PKIXVerifyCert fails with |
| 476 // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any |
| 477 // certificate policy (NSS bug 552775). So we retry with the certificate |
| 478 // policy found in the server certificate. |
| 479 if (nss_error == SEC_ERROR_POLICY_VALIDATION_FAILED && |
| 480 num_policy_oids == 0) { |
| 481 SECOidTag policy = GetFirstCertPolicy(cert_handle); |
| 482 if (policy != SEC_OID_UNKNOWN) { |
| 483 DCHECK_EQ(cvin->back().type, cert_pi_end); |
| 484 cvin->pop_back(); |
| 485 in_param.type = cert_pi_policyOID; |
| 486 in_param.value.arraySize = 1; |
| 487 in_param.value.array.oids = &policy; |
| 488 cvin->push_back(in_param); |
| 489 in_param.type = cert_pi_end; |
| 490 cvin->push_back(in_param); |
| 491 rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, |
| 492 &(*cvin)[0], cvout, NULL); |
| 493 if (rv != SECSuccess) { |
| 494 // Use the original error code. |
| 495 PORT_SetError(nss_error); |
| 496 } |
| 497 } |
| 498 } |
| 499 |
| 500 return rv; |
| 501 } |
| 502 |
| 503 // Decodes the certificatePolicies extension of the certificate. Returns |
| 504 // NULL if the certificate doesn't have the extension or the extension can't |
| 505 // be decoded. The returned value must be freed with a |
| 506 // CERT_DestroyCertificatePoliciesExtension call. |
| 507 CERTCertificatePolicies* DecodeCertPolicies( |
| 508 X509Certificate::OSCertHandle cert_handle) { |
| 437 SECItem policy_ext; | 509 SECItem policy_ext; |
| 438 SECStatus rv = CERT_FindCertExtension( | 510 SECStatus rv = CERT_FindCertExtension( |
| 439 cert_handle, SEC_OID_X509_CERTIFICATE_POLICIES, &policy_ext); | 511 cert_handle, SEC_OID_X509_CERTIFICATE_POLICIES, &policy_ext); |
| 440 if (rv != SECSuccess) { | 512 if (rv != SECSuccess) |
| 441 LOG(ERROR) << "Cert has no policies extension."; | 513 return NULL; |
| 442 return false; | |
| 443 } | |
| 444 CERTCertificatePolicies* policies = | 514 CERTCertificatePolicies* policies = |
| 445 CERT_DecodeCertificatePoliciesExtension(&policy_ext); | 515 CERT_DecodeCertificatePoliciesExtension(&policy_ext); |
| 446 SECITEM_FreeItem(&policy_ext, PR_FALSE); | 516 SECITEM_FreeItem(&policy_ext, PR_FALSE); |
| 517 return policies; |
| 518 } |
| 519 |
| 520 // Returns the OID tag for the first certificate policy in the certificate's |
| 521 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate |
| 522 // has no certificate policy. |
| 523 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) { |
| 524 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); |
| 525 if (!policies) |
| 526 return SEC_OID_UNKNOWN; |
| 527 ScopedCERTCertificatePolicies scoped_policies(policies); |
| 528 CERTPolicyInfo* policy_info = policies->policyInfos[0]; |
| 529 if (!policy_info) |
| 530 return SEC_OID_UNKNOWN; |
| 531 if (policy_info->oid != SEC_OID_UNKNOWN) |
| 532 return policy_info->oid; |
| 533 |
| 534 // The certificate policy is unknown to NSS. We need to create a dynamic |
| 535 // OID tag for the policy. |
| 536 SECOidData od; |
| 537 od.oid.len = policy_info->policyID.len; |
| 538 od.oid.data = policy_info->policyID.data; |
| 539 od.offset = SEC_OID_UNKNOWN; |
| 540 // NSS doesn't allow us to pass an empty description, so I use a hardcoded, |
| 541 // default description here. The description doesn't need to be unique for |
| 542 // each OID. |
| 543 od.desc = "a certificate policy"; |
| 544 od.mechanism = CKM_INVALID_MECHANISM; |
| 545 od.supportedExtension = INVALID_CERT_EXTENSION; |
| 546 return SECOID_AddEntry(&od); |
| 547 } |
| 548 |
| 549 bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, |
| 550 SECOidTag ev_policy_tag) { |
| 551 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); |
| 447 if (!policies) { | 552 if (!policies) { |
| 448 LOG(ERROR) << "Failed to decode certificate policy."; | 553 LOG(ERROR) << "Cert has no policies extension or extension couldn't be " |
| 554 "decoded."; |
| 449 return false; | 555 return false; |
| 450 } | 556 } |
| 451 ScopedCERTCertificatePolicies scoped_policies(policies); | 557 ScopedCERTCertificatePolicies scoped_policies(policies); |
| 452 CERTPolicyInfo** policy_infos = policies->policyInfos; | 558 CERTPolicyInfo** policy_infos = policies->policyInfos; |
| 453 while (*policy_infos != NULL) { | 559 while (*policy_infos != NULL) { |
| 454 CERTPolicyInfo* policy_info = *policy_infos++; | 560 CERTPolicyInfo* policy_info = *policy_infos++; |
| 455 SECOidTag oid_tag = policy_info->oid; | 561 SECOidTag oid_tag = policy_info->oid; |
| 456 if (oid_tag == SEC_OID_UNKNOWN) | 562 if (oid_tag == SEC_OID_UNKNOWN) |
| 457 continue; | 563 continue; |
| 458 if (oid_tag == ev_policy_tag) | 564 if (oid_tag == ev_policy_tag) |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 647 DCHECK(0 != cert->derCert.len); | 753 DCHECK(0 != cert->derCert.len); |
| 648 | 754 |
| 649 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data, | 755 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data, |
| 650 cert->derCert.data, cert->derCert.len); | 756 cert->derCert.data, cert->derCert.len); |
| 651 DCHECK(rv == SECSuccess); | 757 DCHECK(rv == SECSuccess); |
| 652 | 758 |
| 653 return sha1; | 759 return sha1; |
| 654 } | 760 } |
| 655 | 761 |
| 656 } // namespace net | 762 } // namespace net |
| OLD | NEW |