Chromium Code Reviews| 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 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 | 7 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 |
| 8 // until NSS 3.12.2 comes out and we update to it. | 8 // until NSS 3.12.2 comes out and we update to it. |
| 9 #define Lock FOO_NSS_Lock | 9 #define Lock FOO_NSS_Lock |
| 10 #include <cert.h> | 10 #include <cert.h> |
| 11 #include <pk11pub.h> | 11 #include <pk11pub.h> |
| 12 #include <prtime.h> | 12 #include <prtime.h> |
| 13 #include <secder.h> | 13 #include <secder.h> |
| 14 #include <secerr.h> | 14 #include <secerr.h> |
| 15 #include <sechash.h> | 15 #include <sechash.h> |
| 16 #undef Lock | 16 #undef Lock |
| 17 | 17 |
| 18 #include "base/logging.h" | 18 #include "base/logging.h" |
| 19 #include "base/pickle.h" | 19 #include "base/pickle.h" |
| 20 #include "base/time.h" | 20 #include "base/time.h" |
| 21 #include "base/nss_init.h" | 21 #include "base/nss_init.h" |
| 22 #include "net/base/cert_status_flags.h" | 22 #include "net/base/cert_status_flags.h" |
| 23 #include "net/base/cert_verify_result.h" | 23 #include "net/base/cert_verify_result.h" |
| 24 #include "net/base/ev_root_ca_metadata.h" | |
| 24 #include "net/base/net_errors.h" | 25 #include "net/base/net_errors.h" |
| 25 | 26 |
| 26 namespace net { | 27 namespace net { |
| 27 | 28 |
| 28 namespace { | 29 namespace { |
| 29 | 30 |
| 30 class ScopedCERTCertificate { | 31 class ScopedCERTCertificate { |
| 31 public: | 32 public: |
| 32 explicit ScopedCERTCertificate(CERTCertificate* cert) | 33 explicit ScopedCERTCertificate(CERTCertificate* cert) |
| 33 : cert_(cert) {} | 34 : cert_(cert) {} |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 194 exploded.hour = prxtime.tm_hour; | 195 exploded.hour = prxtime.tm_hour; |
| 195 exploded.minute = prxtime.tm_min; | 196 exploded.minute = prxtime.tm_min; |
| 196 exploded.second = prxtime.tm_sec; | 197 exploded.second = prxtime.tm_sec; |
| 197 exploded.millisecond = prxtime.tm_usec / 1000; | 198 exploded.millisecond = prxtime.tm_usec / 1000; |
| 198 | 199 |
| 199 return base::Time::FromUTCExploded(exploded); | 200 return base::Time::FromUTCExploded(exploded); |
| 200 } | 201 } |
| 201 | 202 |
| 202 void ParsePrincipal(SECItem* der_name, | 203 void ParsePrincipal(SECItem* der_name, |
| 203 X509Certificate::Principal* principal) { | 204 X509Certificate::Principal* principal) { |
| 204 | |
| 205 CERTName name; | 205 CERTName name; |
| 206 PRArenaPool* arena = NULL; | 206 PRArenaPool* arena = NULL; |
| 207 | 207 |
| 208 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); | 208 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| 209 DCHECK(arena != NULL); | 209 DCHECK(arena != NULL); |
| 210 if (arena == NULL) | 210 if (arena == NULL) |
| 211 return; | 211 return; |
| 212 | 212 |
| 213 // TODO(dkegel): is CERT_NameTemplate what we always want here? | 213 // TODO(dkegel): is CERT_NameTemplate what we always want here? |
| 214 SECStatus rv; | 214 SECStatus rv; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 279 void ParseDate(SECItem* der_date, base::Time* result) { | 279 void ParseDate(SECItem* der_date, base::Time* result) { |
| 280 PRTime prtime; | 280 PRTime prtime; |
| 281 SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date); | 281 SECStatus rv = DER_DecodeTimeChoice(&prtime, der_date); |
| 282 DCHECK(rv == SECSuccess); | 282 DCHECK(rv == SECSuccess); |
| 283 *result = PRTimeToBaseTime(prtime); | 283 *result = PRTimeToBaseTime(prtime); |
| 284 } | 284 } |
| 285 | 285 |
| 286 void GetCertSubjectAltNamesOfType(X509Certificate::OSCertHandle cert_handle, | 286 void GetCertSubjectAltNamesOfType(X509Certificate::OSCertHandle cert_handle, |
| 287 CERTGeneralNameType name_type, | 287 CERTGeneralNameType name_type, |
| 288 std::vector<std::string>* result) { | 288 std::vector<std::string>* result) { |
| 289 | |
| 290 SECItem alt_name; | 289 SECItem alt_name; |
| 291 SECStatus rv = CERT_FindCertExtension(cert_handle, | 290 SECStatus rv = CERT_FindCertExtension(cert_handle, |
| 292 SEC_OID_X509_SUBJECT_ALT_NAME, &alt_name); | 291 SEC_OID_X509_SUBJECT_ALT_NAME, &alt_name); |
| 293 if (rv != SECSuccess) | 292 if (rv != SECSuccess) |
| 294 return; | 293 return; |
| 295 | 294 |
| 296 PRArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); | 295 PRArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| 297 DCHECK(arena != NULL); | 296 DCHECK(arena != NULL); |
| 298 | 297 |
| 299 CERTGeneralName* alt_name_list; | 298 CERTGeneralName* alt_name_list; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 313 result->push_back(value); | 312 result->push_back(value); |
| 314 } | 313 } |
| 315 name = CERT_GetNextGeneralName(name); | 314 name = CERT_GetNextGeneralName(name); |
| 316 if (name == alt_name_list) | 315 if (name == alt_name_list) |
| 317 break; | 316 break; |
| 318 } | 317 } |
| 319 PORT_FreeArena(arena, PR_FALSE); | 318 PORT_FreeArena(arena, PR_FALSE); |
| 320 PORT_Free(alt_name.data); | 319 PORT_Free(alt_name.data); |
| 321 } | 320 } |
| 322 | 321 |
| 323 } // namespace | 322 // TODO(ukai): make a Linux-only method of the EVRootCAMetadata. |
|
wtc
2009/08/07 04:01:20
Nit: make => this should be
Add "class" at the en
| |
| 323 void GetPolicyOidTags(net::EVRootCAMetadata* metadata, | |
| 324 std::vector<SECOidTag>* policies) { | |
| 325 const char* const* policy_oids = metadata->GetPolicyOIDs(); | |
| 326 for (int i = 0; i < metadata->NumPolicyOIDs(); i++) { | |
| 327 PRUint8 buf[1024]; | |
| 328 SECItem oid_item; | |
| 329 oid_item.data = buf; | |
| 330 oid_item.len = sizeof(buf); | |
| 331 SECStatus status = SEC_StringToOID(NULL, &oid_item, policy_oids[i], 0); | |
| 332 if (status != SECSuccess) { | |
| 333 LOG(ERROR) << "Failed to convert to OID: " << policy_oids[i]; | |
| 334 continue; | |
| 335 } | |
| 336 SECOidTag policy = SECOID_FindOIDTag(&oid_item); | |
| 337 if (policy == SEC_OID_UNKNOWN) { | |
| 338 // Register the OID. | |
| 339 SECOidData od; | |
| 340 od.oid.len = oid_item.len; | |
| 341 od.oid.data = oid_item.data; | |
| 342 od.offset = SEC_OID_UNKNOWN; | |
| 343 od.desc = policy_oids[i]; | |
| 344 od.mechanism = CKM_INVALID_MECHANISM; | |
| 345 od.supportedExtension = INVALID_CERT_EXTENSION; | |
| 346 policy = SECOID_AddEntry(&od); | |
| 347 DCHECK(policy != SEC_OID_UNKNOWN); | |
| 348 } | |
| 349 policies->push_back(policy); | |
| 350 } | |
| 351 return; | |
| 352 } | |
| 353 | |
| 354 // Call CERT_PKIXVerifyCert for the cert_handle. | |
| 355 // Verification results are stored in an array of CERTValOutParam. | |
| 356 // If metadata is not NULL, policies are also checked. | |
|
wtc
2009/08/07 04:01:20
This sentence needs to be updated because 'metadat
| |
| 357 // Caller must initialize cvout before calling this function. | |
| 358 SECStatus PKIXVerifyCert(X509Certificate::OSCertHandle cert_handle, | |
| 359 const SECOidTag* policy_oids, | |
| 360 int num_policy_oids, | |
| 361 CERTValOutParam* cvout) { | |
| 362 PRUint64 revocation_method_flags = | |
| 363 CERT_REV_M_TEST_USING_THIS_METHOD | | |
| 364 CERT_REV_M_ALLOW_NETWORK_FETCHING | | |
| 365 CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | | |
|
alv
2009/08/08 00:47:15
if you plan to allow user to use default source th
| |
| 366 CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE | | |
| 367 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; | |
|
alv
2009/08/08 00:47:15
I would also list here the defalt CERT_REV_M_IGNOR
| |
| 368 PRUint64 revocation_method_independent_flags = | |
| 369 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST | | |
| 370 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; | |
| 371 PRUint64 method_flags[2]; | |
| 372 method_flags[cert_revocation_method_crl] = revocation_method_flags; | |
| 373 method_flags[cert_revocation_method_ocsp] = revocation_method_flags; | |
| 374 | |
| 375 // TODO(ukai): need to find out if we need to call OCSP-related NSS functions, | |
| 376 // CERT_EnableOCSPChecking, CERT_DisableOCSPDefaultResponder and | |
| 377 // CERT_SetOCSPFailureMode. | |
| 378 CERTRevocationMethodIndex preferred_revocation_methods[1]; | |
| 379 preferred_revocation_methods[0] = cert_revocation_method_ocsp; | |
| 380 | |
| 381 CERTRevocationFlags revocation_flags; | |
| 382 revocation_flags.leafTests.number_of_defined_methods = | |
| 383 arraysize(method_flags); | |
| 384 revocation_flags.leafTests.cert_rev_flags_per_method = method_flags; | |
| 385 revocation_flags.leafTests.number_of_preferred_methods = | |
| 386 arraysize(preferred_revocation_methods); | |
| 387 revocation_flags.leafTests.preferred_methods = preferred_revocation_methods; | |
| 388 revocation_flags.leafTests.cert_rev_method_independent_flags = | |
| 389 revocation_method_independent_flags; | |
| 390 | |
| 391 revocation_flags.chainTests.number_of_defined_methods = | |
| 392 arraysize(method_flags); | |
| 393 revocation_flags.chainTests.cert_rev_flags_per_method = method_flags; | |
| 394 revocation_flags.chainTests.number_of_preferred_methods = | |
| 395 arraysize(preferred_revocation_methods); | |
| 396 revocation_flags.chainTests.preferred_methods = preferred_revocation_methods; | |
| 397 revocation_flags.chainTests.cert_rev_method_independent_flags = | |
| 398 revocation_method_independent_flags; | |
| 399 | |
| 400 CERTValInParam cvin[3]; | |
| 401 int cvin_index = 0; | |
| 402 // No need to set cert_pi_trustAnchors here. | |
| 403 // TODO(ukai): use cert_pi_useAIACertFetch (new feature in NSS 3.12.1). | |
| 404 cvin[cvin_index].type = cert_pi_revocationFlags; | |
| 405 cvin[cvin_index].value.pointer.revocation = &revocation_flags; | |
| 406 cvin_index++; | |
| 407 std::vector<SECOidTag> policies; | |
| 408 if (policy_oids && num_policy_oids > 0) { | |
| 409 cvin[cvin_index].type = cert_pi_policyOID; | |
| 410 cvin[cvin_index].value.arraySize = num_policy_oids; | |
| 411 cvin[cvin_index].value.array.oids = policy_oids; | |
| 412 cvin_index++; | |
| 413 } | |
| 414 cvin[cvin_index].type = cert_pi_end; | |
| 415 | |
| 416 return CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
| 417 cvin, cvout, NULL); | |
| 418 } | |
| 419 | |
| 420 // TODO(ukai): make a Linux-only method of the EVRootCAMetadata. | |
| 421 bool GetEvPolicyOidTag(net::EVRootCAMetadata* metadata, | |
|
alv
2009/08/08 00:47:15
Seems like this method and GetPolicyOidTags belong
| |
| 422 const X509Certificate::Fingerprint& fingerprint, | |
| 423 SECOidTag* ev_policy_tag) { | |
| 424 std::string ev_policy_oid; | |
| 425 if (!metadata->GetPolicyOID(fingerprint, &ev_policy_oid)) { | |
| 426 LOG(ERROR) << "GetPolicyOID failed"; | |
| 427 return false; | |
| 428 } | |
| 429 DCHECK(!ev_policy_oid.empty()); | |
| 430 | |
| 431 PRUint8 buf[1024]; | |
| 432 SECItem oid_item; | |
| 433 oid_item.data = buf; | |
| 434 oid_item.len = sizeof(buf); | |
| 435 SECStatus status = SEC_StringToOID(NULL, &oid_item, ev_policy_oid.data(), | |
| 436 ev_policy_oid.length()); | |
| 437 if (status != SECSuccess) { | |
| 438 LOG(ERROR) << "Failed to convert OID:" << ev_policy_oid; | |
| 439 return false; | |
| 440 } | |
| 441 *ev_policy_tag = SECOID_FindOIDTag(&oid_item); | |
| 442 return true; | |
| 443 } | |
| 444 | |
| 445 bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, | |
| 446 SECOidTag ev_policy_tag) { | |
| 447 SECItem policy_ext; | |
| 448 SECStatus rv = CERT_FindCertExtension( | |
| 449 cert_handle, SEC_OID_X509_CERTIFICATE_POLICIES, &policy_ext); | |
| 450 if (rv != SECSuccess) { | |
| 451 LOG(ERROR) << "Cert has no policies extension."; | |
| 452 return false; | |
| 453 } | |
| 454 CERTCertificatePolicies* policies = | |
|
alv
2009/08/08 00:47:15
Does this function leak policies?
| |
| 455 CERT_DecodeCertificatePoliciesExtension(&policy_ext); | |
| 456 if (!policies) { | |
| 457 LOG(ERROR) << "Failed to decode certificate policy."; | |
| 458 return false; | |
| 459 } | |
| 460 CERTPolicyInfo** policy_infos = policies->policyInfos; | |
| 461 while (*policy_infos != NULL) { | |
| 462 CERTPolicyInfo* policy_info = *policy_infos++; | |
| 463 SECOidTag oid_tag = SECOID_FindOIDTag(&policy_info->policyID); | |
|
wtc
2009/08/07 04:01:20
Does it work to just use policy_info->oid? Mozill
alv
2009/08/08 00:47:15
You right. Decoder for cert policy extension has a
| |
| 464 if (oid_tag == SEC_OID_UNKNOWN) | |
| 465 continue; | |
| 466 if (oid_tag == ev_policy_tag) | |
| 467 return true; | |
| 468 } | |
| 469 LOG(ERROR) << "No EV Policy Tag"; | |
| 470 return false; | |
| 471 } | |
| 472 | |
| 473 } // namespace | |
| 324 | 474 |
| 325 void X509Certificate::Initialize() { | 475 void X509Certificate::Initialize() { |
| 326 ParsePrincipal(&cert_handle_->derSubject, &subject_); | 476 ParsePrincipal(&cert_handle_->derSubject, &subject_); |
| 327 ParsePrincipal(&cert_handle_->derIssuer, &issuer_); | 477 ParsePrincipal(&cert_handle_->derIssuer, &issuer_); |
| 328 | 478 |
| 329 ParseDate(&cert_handle_->validity.notBefore, &valid_start_); | 479 ParseDate(&cert_handle_->validity.notBefore, &valid_start_); |
| 330 ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_); | 480 ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_); |
| 331 | 481 |
| 332 fingerprint_ = CalculateFingerprint(cert_handle_); | 482 fingerprint_ = CalculateFingerprint(cert_handle_); |
| 333 | 483 |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 358 GetCertSubjectAltNamesOfType(cert_handle_, certDNSName, dns_names); | 508 GetCertSubjectAltNamesOfType(cert_handle_, certDNSName, dns_names); |
| 359 | 509 |
| 360 // TODO(port): suppress nss's support of the obsolete extension | 510 // TODO(port): suppress nss's support of the obsolete extension |
| 361 // SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME | 511 // SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME |
| 362 // by providing our own authCertificate callback. | 512 // by providing our own authCertificate callback. |
| 363 | 513 |
| 364 if (dns_names->empty()) | 514 if (dns_names->empty()) |
| 365 dns_names->push_back(subject_.common_name); | 515 dns_names->push_back(subject_.common_name); |
| 366 } | 516 } |
| 367 | 517 |
| 368 // TODO(ukai): fix to use this method to verify certificate on SSL channel. | |
| 369 // Note that it's not being used yet. We need to fix SSLClientSocketNSS to | |
| 370 // use this method to verify ssl certificate. | |
| 371 // The problem is that we get segfault when unit tests is going to terminate | |
| 372 // if PR_Cleanup is called in NSSInitSingleton destructor. | |
| 373 int X509Certificate::Verify(const std::string& hostname, | 518 int X509Certificate::Verify(const std::string& hostname, |
| 374 int flags, | 519 int flags, |
| 375 CertVerifyResult* verify_result) const { | 520 CertVerifyResult* verify_result) const { |
| 376 verify_result->Reset(); | 521 verify_result->Reset(); |
| 377 | 522 |
| 378 // Make sure that the hostname matches with the common name of the cert. | 523 // Make sure that the hostname matches with the common name of the cert. |
| 379 SECStatus status = CERT_VerifyCertName(cert_handle_, hostname.c_str()); | 524 SECStatus status = CERT_VerifyCertName(cert_handle_, hostname.c_str()); |
| 380 if (status != SECSuccess) | 525 if (status != SECSuccess) |
| 381 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; | 526 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; |
| 382 | 527 |
| 383 // Make sure that the cert is valid now. | 528 // Make sure that the cert is valid now. |
| 384 SECCertTimeValidity validity = CERT_CheckCertValidTimes( | 529 SECCertTimeValidity validity = CERT_CheckCertValidTimes( |
| 385 cert_handle_, PR_Now(), PR_TRUE); | 530 cert_handle_, PR_Now(), PR_TRUE); |
| 386 if (validity != secCertTimeValid) | 531 if (validity != secCertTimeValid) |
| 387 verify_result->cert_status |= CERT_STATUS_DATE_INVALID; | 532 verify_result->cert_status |= CERT_STATUS_DATE_INVALID; |
| 388 | 533 |
| 389 CERTRevocationFlags revocation_flags; | |
| 390 // TODO(ukai): Fix to use OCSP. | |
| 391 // OCSP mode would fail with SEC_ERROR_UNKNOWN_ISSUER. | |
| 392 // We need to set up OCSP and install an HTTP client for NSS. | |
| 393 bool use_ocsp = false; | |
| 394 // EV requires revocation checking. | |
| 395 if (!(flags & VERIFY_REV_CHECKING_ENABLED)) | |
| 396 flags &= ~VERIFY_EV_CERT; | |
| 397 | |
| 398 // TODO(wtc): Use CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE and | |
| 399 // CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE for EV certificate | |
| 400 // verification. | |
| 401 PRUint64 revocation_method_flags = | |
| 402 CERT_REV_M_TEST_USING_THIS_METHOD | | |
| 403 CERT_REV_M_ALLOW_NETWORK_FETCHING | | |
| 404 CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | | |
| 405 CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE | | |
| 406 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; | |
| 407 PRUint64 revocation_method_independent_flags = | |
| 408 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST | | |
| 409 CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; | |
| 410 PRUint64 method_flags[2]; | |
| 411 method_flags[cert_revocation_method_crl] = revocation_method_flags; | |
| 412 method_flags[cert_revocation_method_ocsp] = revocation_method_flags; | |
| 413 | |
| 414 int number_of_defined_methods; | |
| 415 CERTRevocationMethodIndex preferred_revocation_methods[1]; | |
| 416 if (use_ocsp) { | |
| 417 number_of_defined_methods = 2; | |
| 418 preferred_revocation_methods[0] = cert_revocation_method_ocsp; | |
| 419 } else { | |
| 420 number_of_defined_methods = 1; | |
| 421 preferred_revocation_methods[0] = cert_revocation_method_crl; | |
| 422 } | |
| 423 | |
| 424 revocation_flags.leafTests.number_of_defined_methods = | |
| 425 number_of_defined_methods; | |
| 426 revocation_flags.leafTests.cert_rev_flags_per_method = method_flags; | |
| 427 revocation_flags.leafTests.number_of_preferred_methods = | |
| 428 arraysize(preferred_revocation_methods); | |
| 429 revocation_flags.leafTests.preferred_methods = preferred_revocation_methods; | |
| 430 revocation_flags.leafTests.cert_rev_method_independent_flags = | |
| 431 revocation_method_independent_flags; | |
| 432 revocation_flags.chainTests.number_of_defined_methods = | |
| 433 number_of_defined_methods; | |
| 434 revocation_flags.chainTests.cert_rev_flags_per_method = method_flags; | |
| 435 revocation_flags.chainTests.number_of_preferred_methods = | |
| 436 arraysize(preferred_revocation_methods); | |
| 437 revocation_flags.chainTests.preferred_methods = preferred_revocation_methods; | |
| 438 revocation_flags.chainTests.cert_rev_method_independent_flags = | |
| 439 revocation_method_independent_flags; | |
| 440 | |
| 441 CERTValInParam cvin[2]; | |
| 442 int cvin_index = 0; | |
| 443 // We can't use PK11_ListCerts(PK11CertListCA, NULL) for cert_pi_trustAnchors. | |
| 444 // We get SEC_ERROR_UNTRUSTED_ISSUER (-8172) for our test root CA cert with | |
| 445 // it by NSS 3.12.0.3. | |
| 446 // No need to set cert_pi_trustAnchors here. | |
| 447 // TODO(ukai): use cert_pi_useAIACertFetch (new feature in NSS 3.12.1). | |
| 448 cvin[cvin_index].type = cert_pi_revocationFlags; | |
| 449 cvin[cvin_index].value.pointer.revocation = &revocation_flags; | |
| 450 cvin_index++; | |
| 451 cvin[cvin_index].type = cert_pi_end; | |
| 452 | |
| 453 CERTValOutParam cvout[3]; | 534 CERTValOutParam cvout[3]; |
| 454 int cvout_index = 0; | 535 int cvout_index = 0; |
| 455 cvout[cvout_index].type = cert_po_trustAnchor; | 536 // We don't need the trust anchor for the first PKIXVerifyCert call. |
| 456 cvout[cvout_index].value.pointer.cert = NULL; | |
| 457 cvout_index++; | |
| 458 cvout[cvout_index].type = cert_po_certList; | 537 cvout[cvout_index].type = cert_po_certList; |
| 459 cvout[cvout_index].value.pointer.chain = NULL; | 538 cvout[cvout_index].value.pointer.chain = NULL; |
| 460 int cvout_cert_list_index = cvout_index; | 539 int cvout_cert_list_index = cvout_index; |
| 461 cvout_index++; | 540 cvout_index++; |
| 462 cvout[cvout_index].type = cert_po_end; | 541 cvout[cvout_index].type = cert_po_end; |
| 463 ScopedCERTValOutParam scoped_cvout(cvout); | 542 ScopedCERTValOutParam scoped_cvout(cvout); |
| 464 | 543 |
| 465 status = CERT_PKIXVerifyCert(cert_handle_, certificateUsageSSLServer, | 544 verify_result->cert_status |= net::CERT_STATUS_REV_CHECKING_ENABLED; |
| 466 cvin, cvout, NULL); | 545 status = PKIXVerifyCert(cert_handle_, NULL, 0, cvout); |
| 467 if (status != SECSuccess) { | 546 if (status != SECSuccess) { |
| 468 int err = PORT_GetError(); | 547 int err = PORT_GetError(); |
| 469 LOG(ERROR) << "CERT_PKIXVerifyCert failed err=" << err; | 548 LOG(ERROR) << "CERT_PKIXVerifyCert failed err=" << err; |
| 470 // CERT_PKIXVerifyCert rerports the wrong error code for | 549 // CERT_PKIXVerifyCert rerports the wrong error code for |
| 471 // expired certificates (NSS bug 491174) | 550 // expired certificates (NSS bug 491174) |
| 472 if (err == SEC_ERROR_CERT_NOT_VALID && | 551 if (err == SEC_ERROR_CERT_NOT_VALID && |
| 473 (verify_result->cert_status & CERT_STATUS_DATE_INVALID) != 0) | 552 (verify_result->cert_status & CERT_STATUS_DATE_INVALID) != 0) |
| 474 err = SEC_ERROR_EXPIRED_CERTIFICATE; | 553 err = SEC_ERROR_EXPIRED_CERTIFICATE; |
| 475 verify_result->cert_status |= MapCertErrorToCertStatus(err); | 554 verify_result->cert_status |= MapCertErrorToCertStatus(err); |
| 476 return MapCertStatusToNetError(verify_result->cert_status); | 555 return MapCertStatusToNetError(verify_result->cert_status); |
| 477 } | 556 } |
| 478 | 557 |
| 479 GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain, | 558 GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain, |
| 480 verify_result); | 559 verify_result); |
| 481 if (IsCertStatusError(verify_result->cert_status)) | 560 if (IsCertStatusError(verify_result->cert_status)) |
| 482 return MapCertStatusToNetError(verify_result->cert_status); | 561 return MapCertStatusToNetError(verify_result->cert_status); |
| 483 if ((flags & VERIFY_EV_CERT) && VerifyEV()) | 562 |
| 484 verify_result->cert_status |= CERT_STATUS_IS_EV; | 563 if (flags & VERIFY_EV_CERT) { |
|
wtc
2009/08/07 04:01:20
Nit: use the original form:
if ((flags & VERIFY
| |
| 564 if (VerifyEV()) | |
| 565 verify_result->cert_status |= CERT_STATUS_IS_EV; | |
| 566 } | |
| 485 return OK; | 567 return OK; |
| 486 } | 568 } |
| 487 | 569 |
| 488 // TODO(port): Implement properly on Linux. | 570 // Studied Mozilla's code (esp. security/manager/ssl/src/nsNSSCertHelper.cpp) |
|
wtc
2009/08/07 04:01:20
Isn't nsIdentityChecking.cpp the most useful file
| |
| 571 // to learn how to verify EV certificate. | |
| 572 // TODO(wtc): We may be able to request cert_po_policyOID and just | |
| 573 // check if any of the returned policies is the EV policy of the trust anchor. | |
| 574 // Another possible optimization is that we get the trust anchor from | |
| 575 // the first PKIXVerifyCert call. We look up the EV policy for the trust | |
| 576 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV. | |
| 577 // Otherwise, we pass just that EV policy (as opposed to all the EV policies) | |
| 578 // to the second PKIXVerifyCert call. | |
|
alv
2009/08/08 00:47:15
The first option does not seem to be a working sol
wtc
2009/08/13 00:22:06
Alexei, you may have misunderstood me.
What I mea
| |
| 489 bool X509Certificate::VerifyEV() const { | 579 bool X509Certificate::VerifyEV() const { |
| 490 NOTIMPLEMENTED(); | 580 net::EVRootCAMetadata* metadata = net::EVRootCAMetadata::GetInstance(); |
| 491 return false; | 581 |
| 582 CERTValOutParam cvout[3]; | |
| 583 int cvout_index = 0; | |
| 584 cvout[cvout_index].type = cert_po_trustAnchor; | |
| 585 cvout[cvout_index].value.pointer.cert = NULL; | |
| 586 int cvout_trust_anchor_index = cvout_index; | |
| 587 cvout_index++; | |
| 588 cvout[cvout_index].type = cert_po_end; | |
| 589 ScopedCERTValOutParam scoped_cvout(cvout); | |
| 590 | |
| 591 std::vector<SECOidTag> policies; | |
| 592 GetPolicyOidTags(metadata, &policies); | |
| 593 SECStatus status = PKIXVerifyCert(cert_handle_, | |
| 594 &policies[0], policies.size(), cvout); | |
| 595 if (status != SECSuccess) | |
| 596 return false; | |
| 597 | |
| 598 CERTCertificate* root_ca = | |
| 599 cvout[cvout_trust_anchor_index].value.pointer.cert; | |
| 600 if (root_ca == NULL) | |
| 601 return false; | |
| 602 X509Certificate::Fingerprint fingerprint = | |
| 603 X509Certificate::CalculateFingerprint(root_ca); | |
| 604 SECOidTag ev_policy_tag; | |
| 605 if (!GetEvPolicyOidTag(metadata, fingerprint, &ev_policy_tag)) | |
| 606 return false; | |
| 607 | |
| 608 if (!CheckCertPolicies(cert_handle_, ev_policy_tag)) | |
| 609 return false; | |
| 610 | |
| 611 return true; | |
| 492 } | 612 } |
| 493 | 613 |
| 494 // static | 614 // static |
| 495 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( | 615 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( |
| 496 const char* data, int length) { | 616 const char* data, int length) { |
| 497 base::EnsureNSSInit(); | 617 base::EnsureNSSInit(); |
| 498 | 618 |
| 499 SECItem der_cert; | 619 SECItem der_cert; |
| 500 der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data)); | 620 der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data)); |
| 501 der_cert.len = length; | 621 der_cert.len = length; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 518 DCHECK(0 != cert->derCert.len); | 638 DCHECK(0 != cert->derCert.len); |
| 519 | 639 |
| 520 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data, | 640 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data, |
| 521 cert->derCert.data, cert->derCert.len); | 641 cert->derCert.data, cert->derCert.len); |
| 522 DCHECK(rv == SECSuccess); | 642 DCHECK(rv == SECSuccess); |
| 523 | 643 |
| 524 return sha1; | 644 return sha1; |
| 525 } | 645 } |
| 526 | 646 |
| 527 } // namespace net | 647 } // namespace net |
| OLD | NEW |