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 <prtime.h> | 12 #include <prtime.h> |
12 #include <secder.h> | 13 #include <secder.h> |
| 14 #include <secerr.h> |
13 #include <sechash.h> | 15 #include <sechash.h> |
14 #undef Lock | 16 #undef Lock |
15 | 17 |
16 #include "base/logging.h" | 18 #include "base/logging.h" |
17 #include "base/pickle.h" | 19 #include "base/pickle.h" |
18 #include "base/time.h" | 20 #include "base/time.h" |
19 #include "base/nss_init.h" | 21 #include "base/nss_init.h" |
| 22 #include "net/base/cert_status_flags.h" |
| 23 #include "net/base/cert_verify_result.h" |
20 #include "net/base/net_errors.h" | 24 #include "net/base/net_errors.h" |
21 | 25 |
22 namespace net { | 26 namespace net { |
23 | 27 |
24 namespace { | 28 namespace { |
25 | 29 |
| 30 class ScopedCERTCertificate { |
| 31 public: |
| 32 explicit ScopedCERTCertificate(CERTCertificate* cert) |
| 33 : cert_(cert) {} |
| 34 |
| 35 ~ScopedCERTCertificate() { |
| 36 if (cert_) |
| 37 CERT_DestroyCertificate(cert_); |
| 38 } |
| 39 |
| 40 private: |
| 41 CERTCertificate* cert_; |
| 42 |
| 43 DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertificate); |
| 44 }; |
| 45 |
| 46 class ScopedCERTCertList { |
| 47 public: |
| 48 explicit ScopedCERTCertList(CERTCertList* cert_list) |
| 49 : cert_list_(cert_list) {} |
| 50 |
| 51 ~ScopedCERTCertList() { |
| 52 if (cert_list_) |
| 53 CERT_DestroyCertList(cert_list_); |
| 54 } |
| 55 |
| 56 private: |
| 57 CERTCertList* cert_list_; |
| 58 |
| 59 DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertList); |
| 60 }; |
| 61 |
| 62 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam |
| 63 // array that cvout points to. cvout must be initialized as passed to |
| 64 // CERT_PKIXVerifyCert, so that the array must be terminated with |
| 65 // cert_po_end type. |
| 66 // When it goes out of scope, it destroys values of cert_po_trustAnchor |
| 67 // and cert_po_certList types, but doesn't release the array itself. |
| 68 class ScopedCERTValOutParam { |
| 69 public: |
| 70 explicit ScopedCERTValOutParam(CERTValOutParam* cvout) |
| 71 : cvout_(cvout) {} |
| 72 |
| 73 ~ScopedCERTValOutParam() { |
| 74 if (cvout_ == NULL) |
| 75 return; |
| 76 for (CERTValOutParam *p = cvout_; p->type != cert_po_end; p++) { |
| 77 switch (p->type) { |
| 78 case cert_po_trustAnchor: |
| 79 if (p->value.pointer.cert) { |
| 80 CERT_DestroyCertificate(p->value.pointer.cert); |
| 81 p->value.pointer.cert = NULL; |
| 82 } |
| 83 break; |
| 84 case cert_po_certList: |
| 85 if (p->value.pointer.chain) { |
| 86 CERT_DestroyCertList(p->value.pointer.chain); |
| 87 p->value.pointer.chain = NULL; |
| 88 } |
| 89 break; |
| 90 default: |
| 91 break; |
| 92 } |
| 93 } |
| 94 } |
| 95 |
| 96 private: |
| 97 CERTValOutParam* cvout_; |
| 98 |
| 99 DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam); |
| 100 }; |
| 101 |
| 102 // Map PORT_GetError() return values to our network error codes. |
| 103 int MapSecurityError(int err) { |
| 104 switch (err) { |
| 105 case SEC_ERROR_INVALID_TIME: |
| 106 case SEC_ERROR_EXPIRED_CERTIFICATE: |
| 107 return ERR_CERT_DATE_INVALID; |
| 108 case SEC_ERROR_UNKNOWN_ISSUER: |
| 109 case SEC_ERROR_UNTRUSTED_ISSUER: |
| 110 case SEC_ERROR_CA_CERT_INVALID: |
| 111 case SEC_ERROR_UNTRUSTED_CERT: |
| 112 return ERR_CERT_AUTHORITY_INVALID; |
| 113 case SEC_ERROR_REVOKED_CERTIFICATE: |
| 114 return ERR_CERT_REVOKED; |
| 115 case SEC_ERROR_BAD_DER: |
| 116 case SEC_ERROR_BAD_SIGNATURE: |
| 117 case SEC_ERROR_CERT_NOT_VALID: |
| 118 // TODO(port): add an ERR_CERT_WRONG_USAGE error code. |
| 119 case SEC_ERROR_CERT_USAGES_INVALID: |
| 120 return ERR_CERT_INVALID; |
| 121 default: |
| 122 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; |
| 123 return ERR_FAILED; |
| 124 } |
| 125 } |
| 126 |
| 127 // Map PORT_GetError() return values to our cert status flags. |
| 128 int MapCertErrorToCertStatus(int err) { |
| 129 switch (err) { |
| 130 case SEC_ERROR_INVALID_TIME: |
| 131 case SEC_ERROR_EXPIRED_CERTIFICATE: |
| 132 return CERT_STATUS_DATE_INVALID; |
| 133 case SEC_ERROR_UNTRUSTED_CERT: |
| 134 case SEC_ERROR_UNKNOWN_ISSUER: |
| 135 case SEC_ERROR_UNTRUSTED_ISSUER: |
| 136 case SEC_ERROR_CA_CERT_INVALID: |
| 137 return CERT_STATUS_AUTHORITY_INVALID; |
| 138 case SEC_ERROR_REVOKED_CERTIFICATE: |
| 139 return CERT_STATUS_REVOKED; |
| 140 case SEC_ERROR_BAD_DER: |
| 141 case SEC_ERROR_BAD_SIGNATURE: |
| 142 case SEC_ERROR_CERT_NOT_VALID: |
| 143 // TODO(port): add an CERT_STATUS_WRONG_USAGE error code. |
| 144 case SEC_ERROR_CERT_USAGES_INVALID: |
| 145 return CERT_STATUS_INVALID; |
| 146 default: |
| 147 return 0; |
| 148 } |
| 149 } |
| 150 |
| 151 // Saves some information about the certificate chain cert_list in |
| 152 // *verify_result. The caller MUST initialize *verify_result before calling |
| 153 // this function. |
| 154 // Note that cert_list[0] is the end entity certificate and cert_list doesn't |
| 155 // contain the root CA certificate. |
| 156 void GetCertChainInfo(CERTCertList* cert_list, |
| 157 CertVerifyResult* verify_result) { |
| 158 int i = 0; |
| 159 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); |
| 160 !CERT_LIST_END(node, cert_list); |
| 161 node = CERT_LIST_NEXT(node), i++) { |
| 162 SECAlgorithmID& signature = node->cert->signature; |
| 163 SECOidTag oid_tag = SECOID_FindOIDTag(&signature.algorithm); |
| 164 switch (oid_tag) { |
| 165 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: |
| 166 verify_result->has_md5 = true; |
| 167 if (i != 0) |
| 168 verify_result->has_md5_ca = true; |
| 169 break; |
| 170 case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: |
| 171 verify_result->has_md2 = true; |
| 172 if (i != 0) |
| 173 verify_result->has_md2_ca = true; |
| 174 break; |
| 175 case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: |
| 176 verify_result->has_md4 = true; |
| 177 break; |
| 178 default: |
| 179 break; |
| 180 } |
| 181 } |
| 182 } |
| 183 |
26 // TODO(port): Implement this more simply, and put it in the right place | 184 // TODO(port): Implement this more simply, and put it in the right place |
27 base::Time PRTimeToBaseTime(PRTime prtime) { | 185 base::Time PRTimeToBaseTime(PRTime prtime) { |
28 PRExplodedTime prxtime; | 186 PRExplodedTime prxtime; |
29 PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime); | 187 PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime); |
30 | 188 |
31 base::Time::Exploded exploded; | 189 base::Time::Exploded exploded; |
32 exploded.year = prxtime.tm_year; | 190 exploded.year = prxtime.tm_year; |
33 exploded.month = prxtime.tm_month + 1; | 191 exploded.month = prxtime.tm_month + 1; |
34 exploded.day_of_week = prxtime.tm_wday; | 192 exploded.day_of_week = prxtime.tm_wday; |
35 exploded.day_of_month = prxtime.tm_mday; | 193 exploded.day_of_month = prxtime.tm_mday; |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 | 363 |
206 if (dns_names->empty()) | 364 if (dns_names->empty()) |
207 dns_names->push_back(subject_.common_name); | 365 dns_names->push_back(subject_.common_name); |
208 } | 366 } |
209 | 367 |
210 bool X509Certificate::HasExpired() const { | 368 bool X509Certificate::HasExpired() const { |
211 NOTIMPLEMENTED(); | 369 NOTIMPLEMENTED(); |
212 return false; | 370 return false; |
213 } | 371 } |
214 | 372 |
| 373 // TODO(ukai): fix to use this method to verify certificate on SSL channel. |
| 374 // Note that it's not being used yet. We need to fix SSLClientSocketNSS to |
| 375 // use this method to verify ssl certificate. |
| 376 // The problem is that we get segfault when unit tests is going to terminate |
| 377 // if PR_Cleanup is called in NSSInitSingleton destructor. |
215 int X509Certificate::Verify(const std::string& hostname, | 378 int X509Certificate::Verify(const std::string& hostname, |
216 bool rev_checking_enabled, | 379 bool rev_checking_enabled, |
217 CertVerifyResult* verify_result) const { | 380 CertVerifyResult* verify_result) const { |
| 381 verify_result->Reset(); |
| 382 |
| 383 // Make sure that the hostname matches with the common name of the cert. |
| 384 SECStatus status = CERT_VerifyCertName(cert_handle_, hostname.c_str()); |
| 385 if (status != SECSuccess) |
| 386 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; |
| 387 |
| 388 // Make sure that the cert is valid now. |
| 389 SECCertTimeValidity validity = CERT_CheckCertValidTimes( |
| 390 cert_handle_, PR_Now(), PR_TRUE); |
| 391 if (validity != secCertTimeValid) |
| 392 verify_result->cert_status |= CERT_STATUS_DATE_INVALID; |
| 393 |
| 394 CERTRevocationFlags revocation_flags; |
| 395 // TODO(ukai): Fix to use OCSP. |
| 396 // OCSP mode would fail with SEC_ERROR_UNKNOWN_ISSUER. |
| 397 // We need to set up OCSP and install an HTTP client for NSS. |
| 398 bool use_ocsp = false; |
| 399 |
| 400 PRUint64 revocation_method_flags = |
| 401 CERT_REV_M_TEST_USING_THIS_METHOD | |
| 402 CERT_REV_M_ALLOW_NETWORK_FETCHING | |
| 403 CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | |
| 404 CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE | |
| 405 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; |
| 406 PRUint64 revocation_method_independent_flags = |
| 407 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST | |
| 408 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; |
| 409 PRUint64 method_flags[2]; |
| 410 method_flags[cert_revocation_method_crl] = revocation_method_flags; |
| 411 method_flags[cert_revocation_method_ocsp] = revocation_method_flags; |
| 412 |
| 413 int number_of_defined_methods; |
| 414 CERTRevocationMethodIndex preferred_revocation_methods[1]; |
| 415 if (use_ocsp) { |
| 416 number_of_defined_methods = 2; |
| 417 preferred_revocation_methods[0] = cert_revocation_method_ocsp; |
| 418 } else { |
| 419 number_of_defined_methods = 1; |
| 420 preferred_revocation_methods[0] = cert_revocation_method_crl; |
| 421 } |
| 422 |
| 423 revocation_flags.leafTests.number_of_defined_methods = |
| 424 number_of_defined_methods; |
| 425 revocation_flags.leafTests.cert_rev_flags_per_method = method_flags; |
| 426 revocation_flags.leafTests.number_of_preferred_methods = |
| 427 arraysize(preferred_revocation_methods); |
| 428 revocation_flags.leafTests.preferred_methods = preferred_revocation_methods; |
| 429 revocation_flags.leafTests.cert_rev_method_independent_flags = |
| 430 revocation_method_independent_flags; |
| 431 revocation_flags.chainTests.number_of_defined_methods = |
| 432 number_of_defined_methods; |
| 433 revocation_flags.chainTests.cert_rev_flags_per_method = method_flags; |
| 434 revocation_flags.chainTests.number_of_preferred_methods = |
| 435 arraysize(preferred_revocation_methods); |
| 436 revocation_flags.chainTests.preferred_methods = preferred_revocation_methods; |
| 437 revocation_flags.chainTests.cert_rev_method_independent_flags = |
| 438 revocation_method_independent_flags; |
| 439 |
| 440 CERTValInParam cvin[2]; |
| 441 int cvin_index = 0; |
| 442 // We can't use PK11_ListCerts(PK11CertListCA, NULL) for cert_pi_trustAnchors. |
| 443 // We get SEC_ERROR_UNTRUSTED_ISSUER (-8172) for our test root CA cert with |
| 444 // it by NSS 3.12.0.3. |
| 445 // No need to set cert_pi_trustAnchors here. |
| 446 // TODO(ukai): use cert_pi_useAIACertFetch (new feature in NSS 3.12.1). |
| 447 cvin[cvin_index].type = cert_pi_revocationFlags; |
| 448 cvin[cvin_index].value.pointer.revocation = &revocation_flags; |
| 449 cvin_index++; |
| 450 cvin[cvin_index].type = cert_pi_end; |
| 451 |
| 452 CERTValOutParam cvout[3]; |
| 453 int cvout_index = 0; |
| 454 cvout[cvout_index].type = cert_po_trustAnchor; |
| 455 cvout[cvout_index].value.pointer.cert = NULL; |
| 456 cvout_index++; |
| 457 cvout[cvout_index].type = cert_po_certList; |
| 458 cvout[cvout_index].value.pointer.chain = NULL; |
| 459 int cvout_cert_list_index = cvout_index; |
| 460 cvout_index++; |
| 461 cvout[cvout_index].type = cert_po_end; |
| 462 ScopedCERTValOutParam scoped_cvout(cvout); |
| 463 |
| 464 status = CERT_PKIXVerifyCert(cert_handle_, certificateUsageSSLServer, |
| 465 cvin, cvout, NULL); |
| 466 if (status != SECSuccess) { |
| 467 int err = PORT_GetError(); |
| 468 LOG(ERROR) << "CERT_PKIXVerifyCert failed err=" << err; |
| 469 // CERT_PKIXVerifyCert rerports the wrong error code for |
| 470 // expired certificates (NSS bug 491174) |
| 471 if (err == SEC_ERROR_CERT_NOT_VALID && |
| 472 (verify_result->cert_status & CERT_STATUS_DATE_INVALID) != 0) |
| 473 err = SEC_ERROR_EXPIRED_CERTIFICATE; |
| 474 verify_result->cert_status |= MapCertErrorToCertStatus(err); |
| 475 return MapCertStatusToNetError(verify_result->cert_status); |
| 476 } |
| 477 |
| 478 GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain, |
| 479 verify_result); |
| 480 if (IsCertStatusError(verify_result->cert_status)) |
| 481 return MapCertStatusToNetError(verify_result->cert_status); |
| 482 return OK; |
| 483 } |
| 484 |
| 485 // TODO(port): Implement properly on Linux. |
| 486 bool X509Certificate::IsEV(int status) const { |
218 NOTIMPLEMENTED(); | 487 NOTIMPLEMENTED(); |
219 return ERR_NOT_IMPLEMENTED; | 488 return false; |
220 } | 489 } |
221 | 490 |
222 // static | 491 // static |
223 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( | 492 X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( |
224 const char* data, int length) { | 493 const char* data, int length) { |
225 base::EnsureNSSInit(); | 494 base::EnsureNSSInit(); |
226 | 495 |
227 SECItem der_cert; | 496 SECItem der_cert; |
228 der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data)); | 497 der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data)); |
229 der_cert.len = length; | 498 der_cert.len = length; |
(...skipping 15 matching lines...) Expand all Loading... |
245 DCHECK(NULL != cert->derCert.data); | 514 DCHECK(NULL != cert->derCert.data); |
246 DCHECK(0 != cert->derCert.len); | 515 DCHECK(0 != cert->derCert.len); |
247 | 516 |
248 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data, | 517 SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data, |
249 cert->derCert.data, cert->derCert.len); | 518 cert->derCert.data, cert->derCert.len); |
250 DCHECK(rv == SECSuccess); | 519 DCHECK(rv == SECSuccess); |
251 | 520 |
252 return sha1; | 521 return sha1; |
253 } | 522 } |
254 | 523 |
255 // TODO(port): Implement properly on Linux. | |
256 bool X509Certificate::IsEV(int status) const { | |
257 // http://code.google.com/p/chromium/issues/detail?id=10911 | |
258 return false; | |
259 } | |
260 | |
261 } // namespace net | 524 } // namespace net |
OLD | NEW |