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

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

Issue 113578: Implement X509Certificate::Verify for Linux.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 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 | « base/worker_pool_linux.cc ('k') | no next file » | 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 // 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
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
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
OLDNEW
« no previous file with comments | « base/worker_pool_linux.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698