| 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 "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/pickle.h" | 8 #include "base/pickle.h" |
| 9 #include "base/string_tokenizer.h" | 9 #include "base/string_tokenizer.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 11 #include "net/base/cert_status_flags.h" | 11 #include "net/base/cert_status_flags.h" |
| 12 #include "net/base/cert_verify_result.h" |
| 12 #include "net/base/ev_root_ca_metadata.h" | 13 #include "net/base/ev_root_ca_metadata.h" |
| 14 #include "net/base/net_errors.h" |
| 13 #include "net/base/scoped_cert_chain_context.h" | 15 #include "net/base/scoped_cert_chain_context.h" |
| 14 | 16 |
| 15 #pragma comment(lib, "crypt32.lib") | 17 #pragma comment(lib, "crypt32.lib") |
| 16 | 18 |
| 17 using base::Time; | 19 using base::Time; |
| 18 | 20 |
| 19 namespace net { | 21 namespace net { |
| 20 | 22 |
| 21 namespace { | 23 namespace { |
| 22 | 24 |
| 25 //----------------------------------------------------------------------------- |
| 26 |
| 27 // TODO(wtc): This is a copy of the MapSecurityError function in |
| 28 // ssl_client_socket_win.cc. Another function that maps Windows error codes |
| 29 // to our network error codes is WinInetUtil::OSErrorToNetError. We should |
| 30 // eliminate the code duplication. |
| 31 int MapSecurityError(SECURITY_STATUS err) { |
| 32 // There are numerous security error codes, but these are the ones we thus |
| 33 // far find interesting. |
| 34 switch (err) { |
| 35 case SEC_E_WRONG_PRINCIPAL: // Schannel |
| 36 case CERT_E_CN_NO_MATCH: // CryptoAPI |
| 37 return ERR_CERT_COMMON_NAME_INVALID; |
| 38 case SEC_E_UNTRUSTED_ROOT: // Schannel |
| 39 case CERT_E_UNTRUSTEDROOT: // CryptoAPI |
| 40 return ERR_CERT_AUTHORITY_INVALID; |
| 41 case SEC_E_CERT_EXPIRED: // Schannel |
| 42 case CERT_E_EXPIRED: // CryptoAPI |
| 43 return ERR_CERT_DATE_INVALID; |
| 44 case CRYPT_E_NO_REVOCATION_CHECK: |
| 45 return ERR_CERT_NO_REVOCATION_MECHANISM; |
| 46 case CRYPT_E_REVOCATION_OFFLINE: |
| 47 return ERR_CERT_UNABLE_TO_CHECK_REVOCATION; |
| 48 case CRYPT_E_REVOKED: // Schannel and CryptoAPI |
| 49 return ERR_CERT_REVOKED; |
| 50 case SEC_E_CERT_UNKNOWN: |
| 51 case CERT_E_ROLE: |
| 52 return ERR_CERT_INVALID; |
| 53 // We received an unexpected_message or illegal_parameter alert message |
| 54 // from the server. |
| 55 case SEC_E_ILLEGAL_MESSAGE: |
| 56 return ERR_SSL_PROTOCOL_ERROR; |
| 57 case SEC_E_ALGORITHM_MISMATCH: |
| 58 return ERR_SSL_VERSION_OR_CIPHER_MISMATCH; |
| 59 case SEC_E_INVALID_HANDLE: |
| 60 return ERR_UNEXPECTED; |
| 61 case SEC_E_OK: |
| 62 return OK; |
| 63 default: |
| 64 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; |
| 65 return ERR_FAILED; |
| 66 } |
| 67 } |
| 68 |
| 69 // Map the errors in the chain_context->TrustStatus.dwErrorStatus returned by |
| 70 // CertGetCertificateChain to our certificate status flags. |
| 71 int MapCertChainErrorStatusToCertStatus(DWORD error_status) { |
| 72 int cert_status = 0; |
| 73 |
| 74 // CERT_TRUST_IS_NOT_TIME_NESTED means a subject certificate's time validity |
| 75 // does not nest correctly within its issuer's time validity. |
| 76 const DWORD kDateInvalidErrors = CERT_TRUST_IS_NOT_TIME_VALID | |
| 77 CERT_TRUST_IS_NOT_TIME_NESTED | |
| 78 CERT_TRUST_CTL_IS_NOT_TIME_VALID; |
| 79 if (error_status & kDateInvalidErrors) |
| 80 cert_status |= CERT_STATUS_DATE_INVALID; |
| 81 |
| 82 const DWORD kAuthorityInvalidErrors = CERT_TRUST_IS_UNTRUSTED_ROOT | |
| 83 CERT_TRUST_IS_EXPLICIT_DISTRUST | |
| 84 CERT_TRUST_IS_PARTIAL_CHAIN; |
| 85 if (error_status & kAuthorityInvalidErrors) |
| 86 cert_status |= CERT_STATUS_AUTHORITY_INVALID; |
| 87 |
| 88 if ((error_status & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) && |
| 89 !(error_status & CERT_TRUST_IS_OFFLINE_REVOCATION)) |
| 90 cert_status |= CERT_STATUS_NO_REVOCATION_MECHANISM; |
| 91 |
| 92 if (error_status & CERT_TRUST_IS_OFFLINE_REVOCATION) |
| 93 cert_status |= CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; |
| 94 |
| 95 if (error_status & CERT_TRUST_IS_REVOKED) |
| 96 cert_status |= CERT_STATUS_REVOKED; |
| 97 |
| 98 const DWORD kWrongUsageErrors = CERT_TRUST_IS_NOT_VALID_FOR_USAGE | |
| 99 CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE; |
| 100 if (error_status & kWrongUsageErrors) { |
| 101 // TODO(wtc): Handle these errors. |
| 102 // cert_status = |= CERT_STATUS_WRONG_USAGE; |
| 103 } |
| 104 |
| 105 // The rest of the errors. |
| 106 const DWORD kCertInvalidErrors = |
| 107 CERT_TRUST_IS_NOT_SIGNATURE_VALID | |
| 108 CERT_TRUST_IS_CYCLIC | |
| 109 CERT_TRUST_INVALID_EXTENSION | |
| 110 CERT_TRUST_INVALID_POLICY_CONSTRAINTS | |
| 111 CERT_TRUST_INVALID_BASIC_CONSTRAINTS | |
| 112 CERT_TRUST_INVALID_NAME_CONSTRAINTS | |
| 113 CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID | |
| 114 CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT | |
| 115 CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT | |
| 116 CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT | |
| 117 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT | |
| 118 CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY | |
| 119 CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT; |
| 120 if (error_status & kCertInvalidErrors) |
| 121 cert_status |= CERT_STATUS_INVALID; |
| 122 |
| 123 return cert_status; |
| 124 } |
| 125 |
| 126 //----------------------------------------------------------------------------- |
| 127 |
| 23 // Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the | 128 // Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the |
| 24 // WINAPI calling convention. | 129 // WINAPI calling convention. |
| 25 void* WINAPI MyCryptAlloc(size_t size) { | 130 void* WINAPI MyCryptAlloc(size_t size) { |
| 26 return malloc(size); | 131 return malloc(size); |
| 27 } | 132 } |
| 28 | 133 |
| 29 void WINAPI MyCryptFree(void* p) { | 134 void WINAPI MyCryptFree(void* p) { |
| 30 free(p); | 135 free(p); |
| 31 } | 136 } |
| 32 | 137 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 52 extension->Value.pbData, | 157 extension->Value.pbData, |
| 53 extension->Value.cbData, | 158 extension->Value.cbData, |
| 54 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, | 159 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, |
| 55 &decode_para, | 160 &decode_para, |
| 56 &alt_name_info, | 161 &alt_name_info, |
| 57 &alt_name_info_size); | 162 &alt_name_info_size); |
| 58 if (rv) | 163 if (rv) |
| 59 output->reset(alt_name_info); | 164 output->reset(alt_name_info); |
| 60 } | 165 } |
| 61 | 166 |
| 167 // Saves some information about the certificate chain chain_context in |
| 168 // *verify_result. |
| 169 void GetCertChainInfo(PCCERT_CHAIN_CONTEXT chain_context, |
| 170 CertVerifyResult* verify_result) { |
| 171 PCERT_SIMPLE_CHAIN first_chain = chain_context->rgpChain[0]; |
| 172 int num_elements = first_chain->cElement; |
| 173 PCERT_CHAIN_ELEMENT* element = first_chain->rgpElement; |
| 174 |
| 175 // Each chain starts with the end entity certificate (i = 0) and ends with |
| 176 // the root CA certificate (i = num_elements - 1). Do not inspect the |
| 177 // signature algorithm of the root CA certificate because the signature on |
| 178 // the trust anchor is not important. |
| 179 for (int i = 0; i < num_elements - 1; ++i) { |
| 180 PCCERT_CONTEXT cert = element[i]->pCertContext; |
| 181 const char* algorithm = cert->pCertInfo->SignatureAlgorithm.pszObjId; |
| 182 if (strcmp(algorithm, szOID_RSA_MD5RSA) == 0) { |
| 183 // md5WithRSAEncryption: 1.2.840.113549.1.1.4 |
| 184 verify_result->has_md5 = true; |
| 185 if (i != 0) |
| 186 verify_result->has_md5_ca = true; |
| 187 } else if (strcmp(algorithm, szOID_RSA_MD2RSA) == 0) { |
| 188 // md2WithRSAEncryption: 1.2.840.113549.1.1.2 |
| 189 verify_result->has_md2 = true; |
| 190 if (i != 0) |
| 191 verify_result->has_md2_ca = true; |
| 192 } else if (strcmp(algorithm, szOID_RSA_MD4RSA) == 0) { |
| 193 // md4WithRSAEncryption: 1.2.840.113549.1.1.3 |
| 194 verify_result->has_md4 = true; |
| 195 } |
| 196 } |
| 197 } |
| 198 |
| 62 /////////////////////////////////////////////////////////////////////////// | 199 /////////////////////////////////////////////////////////////////////////// |
| 63 // | 200 // |
| 64 // Functions used by X509Certificate::IsEV | 201 // Functions used by X509Certificate::IsEV |
| 65 // | 202 // |
| 66 /////////////////////////////////////////////////////////////////////////// | 203 /////////////////////////////////////////////////////////////////////////// |
| 67 | 204 |
| 68 // Constructs a certificate chain starting from the end certificate | 205 // Constructs a certificate chain starting from the end certificate |
| 69 // 'cert_context', matching any of the certificate policies. | 206 // 'cert_context', matching any of the certificate policies. |
| 70 // | 207 // |
| 71 // Returns the certificate chain context on success, or NULL on failure. | 208 // Returns the certificate chain context on success, or NULL on failure. |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 } | 423 } |
| 287 } | 424 } |
| 288 if (dns_names->empty()) | 425 if (dns_names->empty()) |
| 289 dns_names->push_back(subject_.common_name); | 426 dns_names->push_back(subject_.common_name); |
| 290 } | 427 } |
| 291 | 428 |
| 292 bool X509Certificate::HasExpired() const { | 429 bool X509Certificate::HasExpired() const { |
| 293 return Time::Now() > valid_expiry(); | 430 return Time::Now() > valid_expiry(); |
| 294 } | 431 } |
| 295 | 432 |
| 433 int X509Certificate::Verify(const std::string& hostname, |
| 434 bool rev_checking_enabled, |
| 435 CertVerifyResult* verify_result) const { |
| 436 verify_result->cert_status = 0; |
| 437 verify_result->has_md5 = false; |
| 438 verify_result->has_md2 = false; |
| 439 verify_result->has_md4 = false; |
| 440 verify_result->has_md5_ca = false; |
| 441 verify_result->has_md2_ca = false; |
| 442 |
| 443 // Build and validate certificate chain. |
| 444 |
| 445 CERT_CHAIN_PARA chain_para; |
| 446 memset(&chain_para, 0, sizeof(chain_para)); |
| 447 chain_para.cbSize = sizeof(chain_para); |
| 448 // TODO(wtc): consider requesting the usage szOID_PKIX_KP_SERVER_AUTH |
| 449 // or szOID_SERVER_GATED_CRYPTO or szOID_SGC_NETSCAPE |
| 450 chain_para.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; |
| 451 chain_para.RequestedUsage.Usage.cUsageIdentifier = 0; |
| 452 chain_para.RequestedUsage.Usage.rgpszUsageIdentifier = NULL; // LPSTR* |
| 453 // We can set CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS to get more chains. |
| 454 DWORD flags = CERT_CHAIN_CACHE_END_CERT; |
| 455 if (rev_checking_enabled) { |
| 456 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; |
| 457 flags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; |
| 458 } else { |
| 459 flags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; |
| 460 } |
| 461 PCCERT_CHAIN_CONTEXT chain_context; |
| 462 if (!CertGetCertificateChain( |
| 463 NULL, // default chain engine, HCCE_CURRENT_USER |
| 464 cert_handle_, |
| 465 NULL, // current system time |
| 466 cert_handle_->hCertStore, // search this store |
| 467 &chain_para, |
| 468 flags, |
| 469 NULL, // reserved |
| 470 &chain_context)) { |
| 471 return MapSecurityError(GetLastError()); |
| 472 } |
| 473 ScopedCertChainContext scoped_chain_context(chain_context); |
| 474 |
| 475 GetCertChainInfo(chain_context, verify_result); |
| 476 |
| 477 verify_result->cert_status |= MapCertChainErrorStatusToCertStatus( |
| 478 chain_context->TrustStatus.dwErrorStatus); |
| 479 |
| 480 std::wstring wstr_hostname = ASCIIToWide(hostname); |
| 481 |
| 482 SSL_EXTRA_CERT_CHAIN_POLICY_PARA extra_policy_para; |
| 483 memset(&extra_policy_para, 0, sizeof(extra_policy_para)); |
| 484 extra_policy_para.cbSize = sizeof(extra_policy_para); |
| 485 extra_policy_para.dwAuthType = AUTHTYPE_SERVER; |
| 486 extra_policy_para.fdwChecks = 0; |
| 487 extra_policy_para.pwszServerName = |
| 488 const_cast<wchar_t*>(wstr_hostname.c_str()); |
| 489 |
| 490 CERT_CHAIN_POLICY_PARA policy_para; |
| 491 memset(&policy_para, 0, sizeof(policy_para)); |
| 492 policy_para.cbSize = sizeof(policy_para); |
| 493 policy_para.dwFlags = 0; |
| 494 policy_para.pvExtraPolicyPara = &extra_policy_para; |
| 495 |
| 496 CERT_CHAIN_POLICY_STATUS policy_status; |
| 497 memset(&policy_status, 0, sizeof(policy_status)); |
| 498 policy_status.cbSize = sizeof(policy_status); |
| 499 |
| 500 if (!CertVerifyCertificateChainPolicy( |
| 501 CERT_CHAIN_POLICY_SSL, |
| 502 chain_context, |
| 503 &policy_para, |
| 504 &policy_status)) { |
| 505 return MapSecurityError(GetLastError()); |
| 506 } |
| 507 |
| 508 if (policy_status.dwError) { |
| 509 verify_result->cert_status |= MapNetErrorToCertStatus( |
| 510 MapSecurityError(policy_status.dwError)); |
| 511 |
| 512 // CertVerifyCertificateChainPolicy reports only one error (in |
| 513 // policy_status.dwError) if the certificate has multiple errors. |
| 514 // CertGetCertificateChain doesn't report certificate name mismatch, so |
| 515 // CertVerifyCertificateChainPolicy is the only function that can report |
| 516 // certificate name mismatch. |
| 517 // |
| 518 // To prevent a potential certificate name mismatch from being hidden by |
| 519 // some other certificate error, if we get any other certificate error, |
| 520 // we call CertVerifyCertificateChainPolicy again, ignoring all other |
| 521 // certificate errors. Both extra_policy_para.fdwChecks and |
| 522 // policy_para.dwFlags allow us to ignore certificate errors, so we set |
| 523 // them both. |
| 524 if (policy_status.dwError != CERT_E_CN_NO_MATCH) { |
| 525 const DWORD extra_ignore_flags = |
| 526 0x00000080 | // SECURITY_FLAG_IGNORE_REVOCATION |
| 527 0x00000100 | // SECURITY_FLAG_IGNORE_UNKNOWN_CA |
| 528 0x00002000 | // SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
| 529 0x00000200; // SECURITY_FLAG_IGNORE_WRONG_USAGE |
| 530 extra_policy_para.fdwChecks = extra_ignore_flags; |
| 531 const DWORD ignore_flags = |
| 532 CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS | |
| 533 CERT_CHAIN_POLICY_IGNORE_INVALID_BASIC_CONSTRAINTS_FLAG | |
| 534 CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG | |
| 535 CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG | |
| 536 CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG | |
| 537 CERT_CHAIN_POLICY_IGNORE_INVALID_POLICY_FLAG | |
| 538 CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS | |
| 539 CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG | |
| 540 CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG | |
| 541 CERT_CHAIN_POLICY_IGNORE_NOT_SUPPORTED_CRITICAL_EXT_FLAG | |
| 542 CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG; |
| 543 policy_para.dwFlags = ignore_flags; |
| 544 if (!CertVerifyCertificateChainPolicy( |
| 545 CERT_CHAIN_POLICY_SSL, |
| 546 chain_context, |
| 547 &policy_para, |
| 548 &policy_status)) { |
| 549 return MapSecurityError(GetLastError()); |
| 550 } |
| 551 if (policy_status.dwError) { |
| 552 verify_result->cert_status |= MapNetErrorToCertStatus( |
| 553 MapSecurityError(policy_status.dwError)); |
| 554 } |
| 555 } |
| 556 } |
| 557 |
| 558 // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be |
| 559 // compatible with WinHTTP, which doesn't report this error (bug 3004). |
| 560 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM; |
| 561 |
| 562 if (IsCertStatusError(verify_result->cert_status)) |
| 563 return MapCertStatusToNetError(verify_result->cert_status); |
| 564 return OK; |
| 565 } |
| 566 |
| 296 // Returns true if the certificate is an extended-validation certificate. | 567 // Returns true if the certificate is an extended-validation certificate. |
| 297 // | 568 // |
| 298 // The certificate has already been verified by the HTTP library. cert_status | 569 // The certificate has already been verified by the HTTP library. cert_status |
| 299 // represents the result of that verification. This function performs | 570 // represents the result of that verification. This function performs |
| 300 // additional checks of the certificatePolicies extensions of the certificates | 571 // additional checks of the certificatePolicies extensions of the certificates |
| 301 // in the certificate chain according to Section 7 (pp. 11-12) of the EV | 572 // in the certificate chain according to Section 7 (pp. 11-12) of the EV |
| 302 // Certificate Guidelines Version 1.0 at | 573 // Certificate Guidelines Version 1.0 at |
| 303 // http://cabforum.org/EV_Certificate_Guidelines.pdf. | 574 // http://cabforum.org/EV_Certificate_Guidelines.pdf. |
| 304 bool X509Certificate::IsEV(int cert_status) const { | 575 bool X509Certificate::IsEV(int cert_status) const { |
| 305 if (net::IsCertStatusError(cert_status) || | 576 if (net::IsCertStatusError(cert_status) || |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 381 rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded, | 652 rv = CryptHashCertificate(NULL, CALG_SHA1, 0, cert->pbCertEncoded, |
| 382 cert->cbCertEncoded, sha1.data, &sha1_size); | 653 cert->cbCertEncoded, sha1.data, &sha1_size); |
| 383 DCHECK(rv && sha1_size == sizeof(sha1.data)); | 654 DCHECK(rv && sha1_size == sizeof(sha1.data)); |
| 384 if (!rv) | 655 if (!rv) |
| 385 memset(sha1.data, 0, sizeof(sha1.data)); | 656 memset(sha1.data, 0, sizeof(sha1.data)); |
| 386 return sha1; | 657 return sha1; |
| 387 } | 658 } |
| 388 | 659 |
| 389 } // namespace net | 660 } // namespace net |
| 390 | 661 |
| OLD | NEW |