OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/base/x509_chain.h" | |
6 | |
7 #include <cert.h> | |
8 #include <nss.h> | |
9 #include <prerror.h> | |
10 #include <secerr.h> | |
11 #include <sslerr.h> | |
12 | |
13 #include "base/logging.h" | |
14 #include "net/base/cert_status_flags.h" | |
15 #include "net/base/cert_verify_result.h" | |
16 #include "net/base/ev_root_ca_metadata.h" | |
17 #include "net/base/net_errors.h" | |
18 | |
19 namespace net { | |
20 | |
21 namespace { | |
22 | |
23 class ScopedCERTCertificatePolicies { | |
24 public: | |
25 explicit ScopedCERTCertificatePolicies(CERTCertificatePolicies* policies) | |
26 : policies_(policies) {} | |
27 | |
28 ~ScopedCERTCertificatePolicies() { | |
29 if (policies_) | |
30 CERT_DestroyCertificatePoliciesExtension(policies_); | |
31 } | |
32 | |
33 private: | |
34 CERTCertificatePolicies* policies_; | |
35 | |
36 DISALLOW_COPY_AND_ASSIGN(ScopedCERTCertificatePolicies); | |
37 }; | |
38 | |
39 // ScopedCERTValOutParam manages destruction of values in the CERTValOutParam | |
40 // array that cvout points to. cvout must be initialized as passed to | |
41 // CERT_PKIXVerifyCert, so that the array must be terminated with | |
42 // cert_po_end type. | |
43 // When it goes out of scope, it destroys values of cert_po_trustAnchor | |
44 // and cert_po_certList types, but doesn't release the array itself. | |
45 class ScopedCERTValOutParam { | |
46 public: | |
47 explicit ScopedCERTValOutParam(CERTValOutParam* cvout) | |
48 : cvout_(cvout) {} | |
49 | |
50 ~ScopedCERTValOutParam() { | |
51 if (cvout_ == NULL) | |
52 return; | |
53 for (CERTValOutParam *p = cvout_; p->type != cert_po_end; p++) { | |
bulach
2010/10/21 10:21:33
nits:
s/CERTValOutParam */CERTValOutParam* /
s/p++
| |
54 switch (p->type) { | |
55 case cert_po_trustAnchor: | |
56 if (p->value.pointer.cert) { | |
57 CERT_DestroyCertificate(p->value.pointer.cert); | |
58 p->value.pointer.cert = NULL; | |
59 } | |
60 break; | |
61 case cert_po_certList: | |
62 if (p->value.pointer.chain) { | |
63 CERT_DestroyCertList(p->value.pointer.chain); | |
64 p->value.pointer.chain = NULL; | |
65 } | |
66 break; | |
67 default: | |
68 break; | |
69 } | |
70 } | |
71 } | |
72 | |
73 private: | |
74 CERTValOutParam* cvout_; | |
75 | |
76 DISALLOW_COPY_AND_ASSIGN(ScopedCERTValOutParam); | |
77 }; | |
78 | |
79 // Map PORT_GetError() return values to our network error codes. | |
80 int MapSecurityError(int err) { | |
81 switch (err) { | |
82 case PR_DIRECTORY_LOOKUP_ERROR: // DNS lookup error. | |
83 return ERR_NAME_NOT_RESOLVED; | |
84 case SEC_ERROR_INVALID_ARGS: | |
85 return ERR_INVALID_ARGUMENT; | |
86 case SSL_ERROR_BAD_CERT_DOMAIN: | |
87 return ERR_CERT_COMMON_NAME_INVALID; | |
88 case SEC_ERROR_INVALID_TIME: | |
89 case SEC_ERROR_EXPIRED_CERTIFICATE: | |
90 return ERR_CERT_DATE_INVALID; | |
91 case SEC_ERROR_UNKNOWN_ISSUER: | |
92 case SEC_ERROR_UNTRUSTED_ISSUER: | |
93 case SEC_ERROR_CA_CERT_INVALID: | |
94 case SEC_ERROR_UNTRUSTED_CERT: | |
95 return ERR_CERT_AUTHORITY_INVALID; | |
96 case SEC_ERROR_REVOKED_CERTIFICATE: | |
97 return ERR_CERT_REVOKED; | |
98 case SEC_ERROR_BAD_DER: | |
99 case SEC_ERROR_BAD_SIGNATURE: | |
100 case SEC_ERROR_CERT_NOT_VALID: | |
101 // TODO(port): add an ERR_CERT_WRONG_USAGE error code. | |
102 case SEC_ERROR_CERT_USAGES_INVALID: | |
103 case SEC_ERROR_POLICY_VALIDATION_FAILED: | |
104 return ERR_CERT_INVALID; | |
105 default: | |
106 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; | |
107 return ERR_FAILED; | |
108 } | |
109 } | |
110 | |
111 // Map PORT_GetError() return values to our cert status flags. | |
112 int MapCertErrorToCertStatus(int err) { | |
113 switch (err) { | |
114 case SSL_ERROR_BAD_CERT_DOMAIN: | |
115 return CERT_STATUS_COMMON_NAME_INVALID; | |
116 case SEC_ERROR_INVALID_TIME: | |
117 case SEC_ERROR_EXPIRED_CERTIFICATE: | |
118 return CERT_STATUS_DATE_INVALID; | |
119 case SEC_ERROR_UNTRUSTED_CERT: | |
120 case SEC_ERROR_UNKNOWN_ISSUER: | |
121 case SEC_ERROR_UNTRUSTED_ISSUER: | |
122 case SEC_ERROR_CA_CERT_INVALID: | |
123 return CERT_STATUS_AUTHORITY_INVALID; | |
124 // TODO(port): map CERT_STATUS_NO_REVOCATION_MECHANISM. | |
125 case SEC_ERROR_OCSP_BAD_HTTP_RESPONSE: | |
126 case SEC_ERROR_OCSP_SERVER_ERROR: | |
127 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; | |
128 case SEC_ERROR_REVOKED_CERTIFICATE: | |
129 return CERT_STATUS_REVOKED; | |
130 case SEC_ERROR_BAD_DER: | |
131 case SEC_ERROR_BAD_SIGNATURE: | |
132 case SEC_ERROR_CERT_NOT_VALID: | |
133 // TODO(port): add a CERT_STATUS_WRONG_USAGE error code. | |
134 case SEC_ERROR_CERT_USAGES_INVALID: | |
135 case SEC_ERROR_POLICY_VALIDATION_FAILED: | |
136 return CERT_STATUS_INVALID; | |
137 default: | |
138 return 0; | |
139 } | |
140 } | |
141 | |
142 // Saves some information about the certificate chain cert_list in | |
143 // *verify_result. The caller MUST initialize *verify_result before calling | |
144 // this function. | |
145 // Note that cert_list[0] is the end entity certificate and cert_list doesn't | |
146 // contain the root CA certificate. | |
147 void GetCertChainInfo(CERTCertList* cert_list, | |
148 CertVerifyResult* verify_result) { | |
149 // NOTE: Using a NSS library before 3.12.3.1 will crash below. To see the | |
150 // NSS version currently in use: | |
151 // 1. use ldd on the chrome executable for NSS's location (ie. libnss3.so*) | |
152 // 2. use ident libnss3.so* for the library's version | |
153 DCHECK(cert_list); | |
154 int i = 0; | |
155 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); | |
156 !CERT_LIST_END(node, cert_list); | |
157 node = CERT_LIST_NEXT(node), i++) { | |
bulach
2010/10/21 10:21:33
nit: ++i
| |
158 SECAlgorithmID& signature = node->cert->signature; | |
159 SECOidTag oid_tag = SECOID_FindOIDTag(&signature.algorithm); | |
160 switch (oid_tag) { | |
161 case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: | |
162 verify_result->has_md5 = true; | |
163 if (i != 0) | |
164 verify_result->has_md5_ca = true; | |
165 break; | |
166 case SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION: | |
167 verify_result->has_md2 = true; | |
168 if (i != 0) | |
169 verify_result->has_md2_ca = true; | |
170 break; | |
171 case SEC_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION: | |
172 verify_result->has_md4 = true; | |
173 break; | |
174 default: | |
175 break; | |
176 } | |
177 } | |
178 } | |
179 | |
180 // Forward declarations. | |
181 SECStatus RetryPKIXVerifyCertWithWorkarounds( | |
182 X509Certificate::OSCertHandle cert_handle, int num_policy_oids, | |
183 std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout); | |
184 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle); | |
185 | |
186 // Call CERT_PKIXVerifyCert for the cert_handle. | |
187 // Verification results are stored in an array of CERTValOutParam. | |
188 // If policy_oids is not NULL and num_policy_oids is positive, policies | |
189 // are also checked. | |
190 // Caller must initialize cvout before calling this function. | |
191 SECStatus PKIXVerifyCert(X509Certificate::OSCertHandle cert_handle, | |
192 bool check_revocation, | |
193 const SECOidTag* policy_oids, | |
194 int num_policy_oids, | |
195 CERTValOutParam* cvout) { | |
196 bool use_crl = check_revocation; | |
197 bool use_ocsp = check_revocation; | |
198 | |
199 // These CAs have multiple keys, which trigger two bugs in NSS's CRL code. | |
200 // 1. NSS may use one key to verify a CRL signed with another key, | |
201 // incorrectly concluding that the CRL's signature is invalid. | |
202 // Hopefully this bug will be fixed in NSS 3.12.9. | |
203 // 2. NSS considers all certificates issued by the CA as revoked when it | |
204 // receives a CRL with an invalid signature. This overly strict policy | |
205 // has been relaxed in NSS 3.12.7. See | |
206 // https://bugzilla.mozilla.org/show_bug.cgi?id=562542. | |
207 // So we have to turn off CRL checking for these CAs. See | |
208 // http://crbug.com/55695. | |
209 static const char* const kMultipleKeyCA[] = { | |
210 "CN=Microsoft Secure Server Authority," | |
211 "DC=redmond,DC=corp,DC=microsoft,DC=com", | |
212 "CN=Microsoft Secure Server Authority", | |
213 }; | |
214 | |
215 if (!NSS_VersionCheck("3.12.7")) { | |
216 for (size_t i = 0; i < arraysize(kMultipleKeyCA); ++i) { | |
217 if (strcmp(cert_handle->issuerName, kMultipleKeyCA[i]) == 0) { | |
218 use_crl = false; | |
219 break; | |
220 } | |
221 } | |
222 } | |
223 | |
224 PRUint64 revocation_method_flags = | |
225 CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD | | |
226 CERT_REV_M_ALLOW_NETWORK_FETCHING | | |
227 CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE | | |
228 CERT_REV_M_IGNORE_MISSING_FRESH_INFO | | |
229 CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; | |
230 PRUint64 revocation_method_independent_flags = | |
231 CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST; | |
232 if (policy_oids && num_policy_oids > 0) { | |
233 // EV verification requires revocation checking. Consider the certificate | |
234 // revoked if we don't have revocation info. | |
235 // TODO(wtc): Add a bool parameter to expressly specify we're doing EV | |
236 // verification or we want strict revocation flags. | |
237 revocation_method_flags |= CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE; | |
238 revocation_method_independent_flags |= | |
239 CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; | |
240 } else { | |
241 revocation_method_flags |= CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE; | |
242 revocation_method_independent_flags |= | |
243 CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT; | |
244 } | |
245 PRUint64 method_flags[2]; | |
246 method_flags[cert_revocation_method_crl] = revocation_method_flags; | |
247 method_flags[cert_revocation_method_ocsp] = revocation_method_flags; | |
248 | |
249 if (use_crl) { | |
250 method_flags[cert_revocation_method_crl] |= | |
251 CERT_REV_M_TEST_USING_THIS_METHOD; | |
252 } | |
253 if (use_ocsp) { | |
254 method_flags[cert_revocation_method_ocsp] |= | |
255 CERT_REV_M_TEST_USING_THIS_METHOD; | |
256 } | |
257 | |
258 CERTRevocationMethodIndex preferred_revocation_methods[1]; | |
259 if (use_ocsp) { | |
260 preferred_revocation_methods[0] = cert_revocation_method_ocsp; | |
261 } else { | |
262 preferred_revocation_methods[0] = cert_revocation_method_crl; | |
263 } | |
264 | |
265 CERTRevocationFlags revocation_flags; | |
266 revocation_flags.leafTests.number_of_defined_methods = | |
267 arraysize(method_flags); | |
268 revocation_flags.leafTests.cert_rev_flags_per_method = method_flags; | |
269 revocation_flags.leafTests.number_of_preferred_methods = | |
270 arraysize(preferred_revocation_methods); | |
271 revocation_flags.leafTests.preferred_methods = preferred_revocation_methods; | |
272 revocation_flags.leafTests.cert_rev_method_independent_flags = | |
273 revocation_method_independent_flags; | |
274 | |
275 revocation_flags.chainTests.number_of_defined_methods = | |
276 arraysize(method_flags); | |
277 revocation_flags.chainTests.cert_rev_flags_per_method = method_flags; | |
278 revocation_flags.chainTests.number_of_preferred_methods = | |
279 arraysize(preferred_revocation_methods); | |
280 revocation_flags.chainTests.preferred_methods = preferred_revocation_methods; | |
281 revocation_flags.chainTests.cert_rev_method_independent_flags = | |
282 revocation_method_independent_flags; | |
283 | |
284 std::vector<CERTValInParam> cvin; | |
285 cvin.reserve(5); | |
bulach
2010/10/21 10:21:33
nit: not sure why 5 as there are three push_back b
| |
286 CERTValInParam in_param; | |
287 // No need to set cert_pi_trustAnchors here. | |
288 in_param.type = cert_pi_revocationFlags; | |
289 in_param.value.pointer.revocation = &revocation_flags; | |
290 cvin.push_back(in_param); | |
291 if (policy_oids && num_policy_oids > 0) { | |
292 in_param.type = cert_pi_policyOID; | |
293 in_param.value.arraySize = num_policy_oids; | |
294 in_param.value.array.oids = policy_oids; | |
295 cvin.push_back(in_param); | |
296 } | |
297 in_param.type = cert_pi_end; | |
298 cvin.push_back(in_param); | |
299 | |
300 SECStatus rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
301 &cvin[0], cvout, NULL); | |
302 if (rv != SECSuccess) { | |
303 rv = RetryPKIXVerifyCertWithWorkarounds(cert_handle, num_policy_oids, | |
304 &cvin, cvout); | |
305 } | |
306 return rv; | |
307 } | |
308 | |
309 // PKIXVerifyCert calls this function to work around some bugs in | |
310 // CERT_PKIXVerifyCert. All the arguments of this function are either the | |
311 // arguments or local variables of PKIXVerifyCert. | |
312 SECStatus RetryPKIXVerifyCertWithWorkarounds( | |
313 X509Certificate::OSCertHandle cert_handle, int num_policy_oids, | |
314 std::vector<CERTValInParam>* cvin, CERTValOutParam* cvout) { | |
315 // We call this function when the first CERT_PKIXVerifyCert call in | |
316 // PKIXVerifyCert failed, so we initialize |rv| to SECFailure. | |
317 SECStatus rv = SECFailure; | |
318 int nss_error = PORT_GetError(); | |
319 CERTValInParam in_param; | |
320 | |
321 // If we get SEC_ERROR_UNKNOWN_ISSUER, we may be missing an intermediate | |
322 // CA certificate, so we retry with cert_pi_useAIACertFetch. | |
323 // cert_pi_useAIACertFetch has several bugs in its error handling and | |
324 // error reporting (NSS bug 528743), so we don't use it by default. | |
325 // Note: When building a certificate chain, CERT_PKIXVerifyCert may | |
326 // incorrectly pick a CA certificate with the same subject name as the | |
327 // missing intermediate CA certificate, and fail with the | |
328 // SEC_ERROR_BAD_SIGNATURE error (NSS bug 524013), so we also retry with | |
329 // cert_pi_useAIACertFetch on SEC_ERROR_BAD_SIGNATURE. | |
330 if (nss_error == SEC_ERROR_UNKNOWN_ISSUER || | |
331 nss_error == SEC_ERROR_BAD_SIGNATURE) { | |
332 DCHECK_EQ(cvin->back().type, cert_pi_end); | |
333 cvin->pop_back(); | |
334 in_param.type = cert_pi_useAIACertFetch; | |
335 in_param.value.scalar.b = PR_TRUE; | |
336 cvin->push_back(in_param); | |
337 in_param.type = cert_pi_end; | |
338 cvin->push_back(in_param); | |
339 rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
340 &(*cvin)[0], cvout, NULL); | |
341 if (rv == SECSuccess) | |
342 return rv; | |
343 int new_nss_error = PORT_GetError(); | |
344 if (new_nss_error == SEC_ERROR_INVALID_ARGS || | |
345 new_nss_error == SEC_ERROR_UNKNOWN_AIA_LOCATION_TYPE || | |
346 new_nss_error == SEC_ERROR_BAD_HTTP_RESPONSE || | |
347 new_nss_error == SEC_ERROR_BAD_LDAP_RESPONSE || | |
348 !IS_SEC_ERROR(new_nss_error)) { | |
349 // Use the original error code because of cert_pi_useAIACertFetch's | |
350 // bad error reporting. | |
351 PORT_SetError(nss_error); | |
352 return rv; | |
353 } | |
354 nss_error = new_nss_error; | |
355 } | |
356 | |
357 // If an intermediate CA certificate has requireExplicitPolicy in its | |
358 // policyConstraints extension, CERT_PKIXVerifyCert fails with | |
359 // SEC_ERROR_POLICY_VALIDATION_FAILED because we didn't specify any | |
360 // certificate policy (NSS bug 552775). So we retry with the certificate | |
361 // policy found in the server certificate. | |
362 if (nss_error == SEC_ERROR_POLICY_VALIDATION_FAILED && | |
363 num_policy_oids == 0) { | |
364 SECOidTag policy = GetFirstCertPolicy(cert_handle); | |
365 if (policy != SEC_OID_UNKNOWN) { | |
366 DCHECK_EQ(cvin->back().type, cert_pi_end); | |
367 cvin->pop_back(); | |
368 in_param.type = cert_pi_policyOID; | |
369 in_param.value.arraySize = 1; | |
370 in_param.value.array.oids = &policy; | |
371 cvin->push_back(in_param); | |
372 in_param.type = cert_pi_end; | |
373 cvin->push_back(in_param); | |
374 rv = CERT_PKIXVerifyCert(cert_handle, certificateUsageSSLServer, | |
375 &(*cvin)[0], cvout, NULL); | |
376 if (rv != SECSuccess) { | |
377 // Use the original error code. | |
378 PORT_SetError(nss_error); | |
379 } | |
380 } | |
381 } | |
382 | |
383 return rv; | |
384 } | |
385 | |
386 // Decodes the certificatePolicies extension of the certificate. Returns | |
387 // NULL if the certificate doesn't have the extension or the extension can't | |
388 // be decoded. The returned value must be freed with a | |
389 // CERT_DestroyCertificatePoliciesExtension call. | |
390 CERTCertificatePolicies* DecodeCertPolicies( | |
391 X509Certificate::OSCertHandle cert_handle) { | |
392 SECItem policy_ext; | |
393 SECStatus rv = CERT_FindCertExtension( | |
394 cert_handle, SEC_OID_X509_CERTIFICATE_POLICIES, &policy_ext); | |
395 if (rv != SECSuccess) | |
396 return NULL; | |
397 CERTCertificatePolicies* policies = | |
398 CERT_DecodeCertificatePoliciesExtension(&policy_ext); | |
399 SECITEM_FreeItem(&policy_ext, PR_FALSE); | |
400 return policies; | |
401 } | |
402 | |
403 // Returns the OID tag for the first certificate policy in the certificate's | |
404 // certificatePolicies extension. Returns SEC_OID_UNKNOWN if the certificate | |
405 // has no certificate policy. | |
406 SECOidTag GetFirstCertPolicy(X509Certificate::OSCertHandle cert_handle) { | |
407 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); | |
408 if (!policies) | |
409 return SEC_OID_UNKNOWN; | |
410 ScopedCERTCertificatePolicies scoped_policies(policies); | |
411 CERTPolicyInfo* policy_info = policies->policyInfos[0]; | |
412 if (!policy_info) | |
413 return SEC_OID_UNKNOWN; | |
414 if (policy_info->oid != SEC_OID_UNKNOWN) | |
415 return policy_info->oid; | |
416 | |
417 // The certificate policy is unknown to NSS. We need to create a dynamic | |
418 // OID tag for the policy. | |
419 SECOidData od; | |
420 od.oid.len = policy_info->policyID.len; | |
421 od.oid.data = policy_info->policyID.data; | |
422 od.offset = SEC_OID_UNKNOWN; | |
423 // NSS doesn't allow us to pass an empty description, so I use a hardcoded, | |
424 // default description here. The description doesn't need to be unique for | |
425 // each OID. | |
426 od.desc = "a certificate policy"; | |
427 od.mechanism = CKM_INVALID_MECHANISM; | |
428 od.supportedExtension = INVALID_CERT_EXTENSION; | |
429 return SECOID_AddEntry(&od); | |
430 } | |
431 | |
432 bool CheckCertPolicies(X509Certificate::OSCertHandle cert_handle, | |
433 SECOidTag ev_policy_tag) { | |
434 CERTCertificatePolicies* policies = DecodeCertPolicies(cert_handle); | |
435 if (!policies) { | |
436 LOG(ERROR) << "Cert has no policies extension or extension couldn't be " | |
437 "decoded."; | |
438 return false; | |
439 } | |
440 ScopedCERTCertificatePolicies scoped_policies(policies); | |
441 CERTPolicyInfo** policy_infos = policies->policyInfos; | |
442 while (*policy_infos != NULL) { | |
443 CERTPolicyInfo* policy_info = *policy_infos++; | |
444 SECOidTag oid_tag = policy_info->oid; | |
445 if (oid_tag == SEC_OID_UNKNOWN) | |
446 continue; | |
447 if (oid_tag == ev_policy_tag) | |
448 return true; | |
449 } | |
450 LOG(ERROR) << "No EV Policy Tag"; | |
451 return false; | |
452 } | |
453 | |
454 // Studied Mozilla's code (esp. security/manager/ssl/src/nsIdentityChecking.cpp | |
455 // and nsNSSCertHelper.cpp) to learn how to verify EV certificate. | |
456 // TODO(wtc): A possible optimization is that we get the trust anchor from | |
457 // the first PKIXVerifyCert call. We look up the EV policy for the trust | |
458 // anchor. If the trust anchor has no EV policy, we know the cert isn't EV. | |
459 // Otherwise, we pass just that EV policy (as opposed to all the EV policies) | |
460 // to the second PKIXVerifyCert call. | |
461 bool VerifyEV(X509Certificate* certificate) { | |
462 net::EVRootCAMetadata* metadata = net::EVRootCAMetadata::GetInstance(); | |
463 | |
464 CERTValOutParam cvout[3]; | |
465 int cvout_index = 0; | |
466 cvout[cvout_index].type = cert_po_trustAnchor; | |
467 cvout[cvout_index].value.pointer.cert = NULL; | |
468 int cvout_trust_anchor_index = cvout_index; | |
469 cvout_index++; | |
bulach
2010/10/21 10:21:33
nit: ++cvout_index;
| |
470 cvout[cvout_index].type = cert_po_end; | |
471 ScopedCERTValOutParam scoped_cvout(cvout); | |
472 | |
473 SECStatus status = PKIXVerifyCert(certificate->os_cert_handle(), | |
474 true, | |
475 metadata->GetPolicyOIDs(), | |
476 metadata->NumPolicyOIDs(), | |
477 cvout); | |
478 if (status != SECSuccess) | |
479 return false; | |
480 | |
481 CERTCertificate* root_ca = | |
482 cvout[cvout_trust_anchor_index].value.pointer.cert; | |
483 if (root_ca == NULL) | |
484 return false; | |
485 SHA1Fingerprint fingerprint = | |
486 X509Certificate::CalculateFingerprint(root_ca); | |
487 SECOidTag ev_policy_tag = SEC_OID_UNKNOWN; | |
488 if (!metadata->GetPolicyOID(fingerprint, &ev_policy_tag)) | |
489 return false; | |
490 | |
491 if (!CheckCertPolicies(certificate->os_cert_handle(), ev_policy_tag)) | |
492 return false; | |
493 | |
494 return true; | |
495 } | |
496 | |
497 } // namespace | |
498 | |
499 namespace x509_chain { | |
500 | |
501 int VerifySSLServer(X509Certificate* certificate, const std::string& hostname, | |
502 int flags, CertVerifyResult* verify_result) { | |
503 verify_result->Reset(); | |
504 if (!certificate || !certificate->os_cert_handle()) | |
505 return ERR_UNEXPECTED; | |
506 | |
507 // Make sure that the hostname matches with the common name of the cert. | |
508 SECStatus status = CERT_VerifyCertName(certificate->os_cert_handle(), | |
509 hostname.c_str()); | |
510 if (status != SECSuccess) | |
511 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; | |
512 | |
513 // Make sure that the cert is valid now. | |
514 SECCertTimeValidity validity = CERT_CheckCertValidTimes( | |
515 certificate->os_cert_handle(), PR_Now(), PR_TRUE); | |
516 if (validity != secCertTimeValid) | |
517 verify_result->cert_status |= CERT_STATUS_DATE_INVALID; | |
518 | |
519 CERTValOutParam cvout[3]; | |
520 int cvout_index = 0; | |
521 // We don't need the trust anchor for the first PKIXVerifyCert call. | |
522 cvout[cvout_index].type = cert_po_certList; | |
523 cvout[cvout_index].value.pointer.chain = NULL; | |
524 int cvout_cert_list_index = cvout_index; | |
525 cvout_index++; | |
526 cvout[cvout_index].type = cert_po_end; | |
527 ScopedCERTValOutParam scoped_cvout(cvout); | |
528 | |
529 bool check_revocation = (flags & VERIFY_REV_CHECKING_ENABLED); | |
530 if (check_revocation) { | |
531 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED; | |
532 } else { | |
533 // EV requires revocation checking. | |
534 flags &= ~VERIFY_EV_CERT; | |
535 } | |
536 status = PKIXVerifyCert(certificate->os_cert_handle(), check_revocation, | |
537 NULL, 0, cvout); | |
538 if (status != SECSuccess) { | |
539 int err = PORT_GetError(); | |
540 LOG(ERROR) << "CERT_PKIXVerifyCert for " << hostname | |
541 << " failed err=" << err; | |
542 // CERT_PKIXVerifyCert rerports the wrong error code for | |
543 // expired certificates (NSS bug 491174) | |
544 if (err == SEC_ERROR_CERT_NOT_VALID && | |
545 (verify_result->cert_status & CERT_STATUS_DATE_INVALID) != 0) | |
546 err = SEC_ERROR_EXPIRED_CERTIFICATE; | |
547 int cert_status = MapCertErrorToCertStatus(err); | |
548 if (cert_status) { | |
549 verify_result->cert_status |= cert_status; | |
550 return MapCertStatusToNetError(verify_result->cert_status); | |
551 } | |
552 // |err| is not a certificate error. | |
553 return MapSecurityError(err); | |
554 } | |
555 | |
556 GetCertChainInfo(cvout[cvout_cert_list_index].value.pointer.chain, | |
557 verify_result); | |
558 if (IsCertStatusError(verify_result->cert_status)) | |
559 return MapCertStatusToNetError(verify_result->cert_status); | |
560 | |
561 if ((flags & VERIFY_EV_CERT) && VerifyEV(certificate)) | |
562 verify_result->cert_status |= CERT_STATUS_IS_EV; | |
563 return OK; | |
564 } | |
565 | |
566 } // namespace x509_chain | |
567 | |
568 } // namespace net | |
OLD | NEW |