Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: net/base/x509_certificate_nss.cc

Issue 545103: Work around the SEC_ERROR_POLICY_VALIDATION_FAILED error from... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: New workaround doesn't work in some cases. Revert to Patch Set 7. Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/test_certificate_data.h ('k') | net/base/x509_certificate_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « net/base/test_certificate_data.h ('k') | net/base/x509_certificate_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698