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 |