Chromium Code Reviews| 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 |