blob: 60621d6b2079a88b3c02814cd8b0d68815990138 [file] [log] [blame]
[email protected]62b23c22012-03-22 04:50:241// Copyright (c) 2012 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
[email protected]6e7845ae2013-03-29 21:48:115#include "net/cert/cert_verify_proc_mac.h"
[email protected]62b23c22012-03-22 04:50:246
7#include <CommonCrypto/CommonDigest.h>
8#include <CoreServices/CoreServices.h>
9#include <Security/Security.h>
10
rsleevife8953712015-10-29 02:06:5311#include <set>
[email protected]ede03212012-09-07 12:52:2612#include <string>
13#include <vector>
14
rsleevife8953712015-10-29 02:06:5315#include "base/lazy_instance.h"
[email protected]62b23c22012-03-22 04:50:2416#include "base/logging.h"
17#include "base/mac/mac_logging.h"
mattm1a282f52016-11-10 21:49:4218#include "base/mac/mac_util.h"
[email protected]62b23c22012-03-22 04:50:2419#include "base/mac/scoped_cftyperef.h"
20#include "base/sha1.h"
[email protected]d069c11a2013-04-13 00:01:5521#include "base/strings/string_piece.h"
[email protected]d6e8fe62012-10-03 05:46:4522#include "base/synchronization/lock.h"
23#include "crypto/mac_security_services_lock.h"
[email protected]62b23c22012-03-22 04:50:2424#include "crypto/sha2.h"
rsleevife8953712015-10-29 02:06:5325#include "net/base/hash_value.h"
[email protected]62b23c22012-03-22 04:50:2426#include "net/base/net_errors.h"
[email protected]6e7845ae2013-03-29 21:48:1127#include "net/cert/asn1_util.h"
28#include "net/cert/cert_status_flags.h"
29#include "net/cert/cert_verifier.h"
30#include "net/cert/cert_verify_result.h"
31#include "net/cert/crl_set.h"
mattm1a282f52016-11-10 21:49:4232#include "net/cert/ev_root_ca_metadata.h"
33#include "net/cert/internal/certificate_policies.h"
34#include "net/cert/internal/parsed_certificate.h"
mattmaf868e72016-09-23 23:25:2035#include "net/cert/test_keychain_search_list_mac.h"
[email protected]6e7845ae2013-03-29 21:48:1136#include "net/cert/test_root_certs.h"
37#include "net/cert/x509_certificate.h"
mattm8023e8b2017-02-24 19:08:4938#include "net/cert/x509_util.h"
[email protected]6e7845ae2013-03-29 21:48:1139#include "net/cert/x509_util_mac.h"
[email protected]62b23c22012-03-22 04:50:2440
erikchenbedc2612016-03-02 02:52:0841// CSSM functions are deprecated as of OSX 10.7, but have no replacement.
42// https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1
43#pragma clang diagnostic push
44#pragma clang diagnostic ignored "-Wdeprecated-declarations"
45
[email protected]3df79f42013-06-24 18:49:0546using base::ScopedCFTypeRef;
[email protected]62b23c22012-03-22 04:50:2447
48namespace net {
49
50namespace {
51
52typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
53 CFDictionaryRef*);
54
55int NetErrorFromOSStatus(OSStatus status) {
56 switch (status) {
57 case noErr:
58 return OK;
59 case errSecNotAvailable:
60 case errSecNoCertificateModule:
61 case errSecNoPolicyModule:
62 return ERR_NOT_IMPLEMENTED;
63 case errSecAuthFailed:
64 return ERR_ACCESS_DENIED;
65 default: {
66 OSSTATUS_LOG(ERROR, status) << "Unknown error mapped to ERR_FAILED";
67 return ERR_FAILED;
68 }
69 }
70}
71
72CertStatus CertStatusFromOSStatus(OSStatus status) {
73 switch (status) {
74 case noErr:
75 return 0;
76
77 case CSSMERR_TP_INVALID_ANCHOR_CERT:
78 case CSSMERR_TP_NOT_TRUSTED:
79 case CSSMERR_TP_INVALID_CERT_AUTHORITY:
80 return CERT_STATUS_AUTHORITY_INVALID;
81
82 case CSSMERR_TP_CERT_EXPIRED:
83 case CSSMERR_TP_CERT_NOT_VALID_YET:
84 // "Expired" and "not yet valid" collapse into a single status.
85 return CERT_STATUS_DATE_INVALID;
86
87 case CSSMERR_TP_CERT_REVOKED:
88 case CSSMERR_TP_CERT_SUSPENDED:
89 return CERT_STATUS_REVOKED;
90
91 case CSSMERR_APPLETP_HOSTNAME_MISMATCH:
92 return CERT_STATUS_COMMON_NAME_INVALID;
93
94 case CSSMERR_APPLETP_CRL_NOT_FOUND:
95 case CSSMERR_APPLETP_OCSP_UNAVAILABLE:
96 case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK:
97 return CERT_STATUS_NO_REVOCATION_MECHANISM;
98
99 case CSSMERR_APPLETP_CRL_EXPIRED:
100 case CSSMERR_APPLETP_CRL_NOT_VALID_YET:
101 case CSSMERR_APPLETP_CRL_SERVER_DOWN:
102 case CSSMERR_APPLETP_CRL_NOT_TRUSTED:
103 case CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT:
104 case CSSMERR_APPLETP_CRL_POLICY_FAIL:
105 case CSSMERR_APPLETP_OCSP_BAD_RESPONSE:
106 case CSSMERR_APPLETP_OCSP_BAD_REQUEST:
107 case CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED:
108 case CSSMERR_APPLETP_NETWORK_FAILURE:
109 case CSSMERR_APPLETP_OCSP_NOT_TRUSTED:
110 case CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT:
111 case CSSMERR_APPLETP_OCSP_SIG_ERROR:
112 case CSSMERR_APPLETP_OCSP_NO_SIGNER:
113 case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ:
114 case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR:
115 case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER:
116 case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED:
117 case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED:
118 case CSSMERR_APPLETP_OCSP_NONCE_MISMATCH:
119 // We asked for a revocation check, but didn't get it.
120 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
121
[email protected]e9b8ca82013-04-29 20:52:29122 case CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE:
123 // TODO(wtc): Should we add CERT_STATUS_WRONG_USAGE?
124 return CERT_STATUS_INVALID;
125
[email protected]62b23c22012-03-22 04:50:24126 case CSSMERR_APPLETP_CRL_BAD_URI:
127 case CSSMERR_APPLETP_IDP_FAIL:
128 return CERT_STATUS_INVALID;
129
[email protected]58484ca2012-05-29 21:56:34130 case CSSMERR_CSP_UNSUPPORTED_KEY_SIZE:
131 // Mapping UNSUPPORTED_KEY_SIZE to CERT_STATUS_WEAK_KEY is not strictly
132 // accurate, as the error may have been returned due to a key size
133 // that exceeded the maximum supported. However, within
134 // CertVerifyProcMac::VerifyInternal(), this code should only be
135 // encountered as a certificate status code, and only when the key size
136 // is smaller than the minimum required (1024 bits).
137 return CERT_STATUS_WEAK_KEY;
138
[email protected]62b23c22012-03-22 04:50:24139 default: {
140 // Failure was due to something Chromium doesn't define a
141 // specific status for (such as basic constraints violation, or
142 // unknown critical extension)
143 OSSTATUS_LOG(WARNING, status)
144 << "Unknown error mapped to CERT_STATUS_INVALID";
145 return CERT_STATUS_INVALID;
146 }
147 }
148}
149
150// Creates a series of SecPolicyRefs to be added to a SecTrustRef used to
mattmaf868e72016-09-23 23:25:20151// validate a certificate for an SSL server. |flags| is a bitwise-OR of
152// VerifyFlags that can further alter how trust is validated, such as how
153// revocation is checked. If successful, returns noErr, and stores the
154// resultant array of SecPolicyRefs in |policies|.
155OSStatus CreateTrustPolicies(int flags, ScopedCFTypeRef<CFArrayRef>* policies) {
[email protected]62b23c22012-03-22 04:50:24156 ScopedCFTypeRef<CFMutableArrayRef> local_policies(
157 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
158 if (!local_policies)
159 return memFullErr;
160
161 SecPolicyRef ssl_policy;
mattmaf868e72016-09-23 23:25:20162 OSStatus status =
163 x509_util::CreateSSLServerPolicy(std::string(), &ssl_policy);
[email protected]62b23c22012-03-22 04:50:24164 if (status)
165 return status;
166 CFArrayAppendValue(local_policies, ssl_policy);
167 CFRelease(ssl_policy);
168
169 // Explicitly add revocation policies, in order to override system
170 // revocation checking policies and instead respect the application-level
171 // revocation preference.
172 status = x509_util::CreateRevocationPolicies(
mattm1a282f52016-11-10 21:49:42173 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED), local_policies);
[email protected]62b23c22012-03-22 04:50:24174 if (status)
175 return status;
176
177 policies->reset(local_policies.release());
178 return noErr;
179}
180
eromanaccb8132017-01-10 07:13:11181// Stores the constructed certificate chain |cert_chain| into
182// |*verify_result|. |cert_chain| must not be empty.
183void CopyCertChainToVerifyResult(CFArrayRef cert_chain,
184 CertVerifyResult* verify_result) {
davidbena6173cd82015-04-23 22:55:09185 DCHECK_LT(0, CFArrayGetCount(cert_chain));
186
[email protected]62b23c22012-03-22 04:50:24187 SecCertificateRef verified_cert = NULL;
188 std::vector<SecCertificateRef> verified_chain;
189 for (CFIndex i = 0, count = CFArrayGetCount(cert_chain); i < count; ++i) {
190 SecCertificateRef chain_cert = reinterpret_cast<SecCertificateRef>(
191 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
192 if (i == 0) {
193 verified_cert = chain_cert;
194 } else {
195 verified_chain.push_back(chain_cert);
196 }
eromanaccb8132017-01-10 07:13:11197 }
198 if (!verified_cert) {
199 NOTREACHED();
200 return;
201 }
202
203 verify_result->verified_cert =
204 X509Certificate::CreateFromHandle(verified_cert, verified_chain);
205}
206
207// Returns true if the intermediates (excluding trusted certificates) use a
208// weak hashing algorithm, but the target does not use a weak hash.
209bool IsWeakChainBasedOnHashingAlgorithms(
210 CFArrayRef cert_chain,
211 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info) {
212 DCHECK_LT(0, CFArrayGetCount(cert_chain));
213
214 bool intermediates_contain_weak_hash = false;
215 bool leaf_uses_weak_hash = false;
216
217 for (CFIndex i = 0, count = CFArrayGetCount(cert_chain); i < count; ++i) {
218 SecCertificateRef chain_cert = reinterpret_cast<SecCertificateRef>(
219 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
[email protected]62b23c22012-03-22 04:50:24220
221 if ((chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_IN_ANCHORS) ||
222 (chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
223 // The current certificate is either in the user's trusted store or is
224 // a root (self-signed) certificate. Ignore the signature algorithm for
225 // these certificates, as it is meaningless for security. We allow
226 // self-signed certificates (i == 0 & IS_ROOT), since we accept that
227 // any security assertions by such a cert are inherently meaningless.
228 continue;
229 }
230
eromanb28b2b62017-01-06 01:44:02231 X509Certificate::SignatureHashAlgorithm hash_algorithm =
eromanaccb8132017-01-10 07:13:11232 X509Certificate::GetSignatureHashAlgorithm(chain_cert);
233
234 switch (hash_algorithm) {
235 case X509Certificate::kSignatureHashAlgorithmMd2:
236 case X509Certificate::kSignatureHashAlgorithmMd4:
237 case X509Certificate::kSignatureHashAlgorithmMd5:
238 case X509Certificate::kSignatureHashAlgorithmSha1:
239 if (i == 0) {
240 leaf_uses_weak_hash = true;
241 } else {
242 intermediates_contain_weak_hash = true;
243 }
244 break;
245 case X509Certificate::kSignatureHashAlgorithmOther:
246 break;
[email protected]62b23c22012-03-22 04:50:24247 }
248 }
[email protected]62b23c22012-03-22 04:50:24249
eromanaccb8132017-01-10 07:13:11250 return !leaf_uses_weak_hash && intermediates_contain_weak_hash;
[email protected]62b23c22012-03-22 04:50:24251}
252
mattm1a282f52016-11-10 21:49:42253using ExtensionsMap = std::map<net::der::Input, net::ParsedExtension>;
254
255// Helper that looks up an extension by OID given a map of extensions.
256bool GetExtensionValue(const ExtensionsMap& extensions,
257 const net::der::Input& oid,
258 net::der::Input* value) {
259 auto it = extensions.find(oid);
260 if (it == extensions.end())
261 return false;
262 *value = it->second.value;
263 return true;
264}
265
266// Checks if |*cert| has a Certificate Policies extension containing either
267// of |ev_policy_oid| or anyPolicy.
268bool HasPolicyOrAnyPolicy(const ParsedCertificate* cert,
269 const der::Input& ev_policy_oid) {
270 der::Input extension_value;
271 if (!GetExtensionValue(cert->unparsed_extensions(), CertificatePoliciesOid(),
272 &extension_value)) {
273 return false;
274 }
275
276 std::vector<der::Input> policies;
277 if (!ParseCertificatePoliciesExtension(extension_value, &policies))
278 return false;
279
280 for (const der::Input& policy_oid : policies) {
281 if (policy_oid == ev_policy_oid || policy_oid == AnyPolicy())
282 return true;
283 }
284 return false;
285}
286
287// Looks for known EV policy OIDs in |cert_input|, if one is found it will be
288// stored in |*ev_policy_oid| as a DER-encoded OID value (no tag or length).
289void GetCandidateEVPolicy(const X509Certificate* cert_input,
290 std::string* ev_policy_oid) {
291 ev_policy_oid->clear();
292
293 std::string der_cert;
294 if (!X509Certificate::GetDEREncoded(cert_input->os_cert_handle(),
295 &der_cert)) {
296 return;
297 }
298
mattm8023e8b2017-02-24 19:08:49299 scoped_refptr<ParsedCertificate> cert(ParsedCertificate::Create(
300 x509_util::CreateCryptoBuffer(der_cert), {}, nullptr));
mattm1a282f52016-11-10 21:49:42301 if (!cert)
302 return;
303
304 der::Input extension_value;
305 if (!GetExtensionValue(cert->unparsed_extensions(), CertificatePoliciesOid(),
306 &extension_value)) {
307 return;
308 }
309
310 std::vector<der::Input> policies;
311 if (!ParseCertificatePoliciesExtension(extension_value, &policies))
312 return;
313
314 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
315 for (const der::Input& policy_oid : policies) {
316 if (metadata->IsEVPolicyOID(policy_oid)) {
317 *ev_policy_oid = policy_oid.AsString();
318 return;
319 }
320 }
321}
322
323// Checks that the certificate chain of |cert| has policies consistent with
324// |ev_policy_oid_string|. The leaf is not checked, as it is assumed that is
325// where the policy came from.
326bool CheckCertChainEV(const X509Certificate* cert,
327 const std::string& ev_policy_oid_string) {
328 der::Input ev_policy_oid(&ev_policy_oid_string);
329 X509Certificate::OSCertHandles os_cert_chain =
330 cert->GetIntermediateCertificates();
331
332 // Root should have matching policy in EVRootCAMetadata.
333 std::string der_cert;
334 if (!X509Certificate::GetDEREncoded(os_cert_chain.back(), &der_cert))
335 return false;
336 SHA1HashValue weak_fingerprint;
337 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(der_cert.data()),
338 der_cert.size(), weak_fingerprint.data);
339 EVRootCAMetadata* metadata = EVRootCAMetadata::GetInstance();
340 if (!metadata->HasEVPolicyOID(weak_fingerprint, ev_policy_oid))
341 return false;
342
343 // Intermediates should have Certificate Policies extension with the EV policy
344 // or AnyPolicy.
345 for (size_t i = 0; i < os_cert_chain.size() - 1; ++i) {
346 std::string der_cert;
347 if (!X509Certificate::GetDEREncoded(os_cert_chain[i], &der_cert))
348 return false;
349 scoped_refptr<ParsedCertificate> intermediate_cert(
mattm8023e8b2017-02-24 19:08:49350 ParsedCertificate::Create(x509_util::CreateCryptoBuffer(der_cert), {},
351 nullptr));
mattm1a282f52016-11-10 21:49:42352 if (!intermediate_cert)
353 return false;
354 if (!HasPolicyOrAnyPolicy(intermediate_cert.get(), ev_policy_oid))
355 return false;
356 }
357
358 return true;
359}
360
[email protected]62b23c22012-03-22 04:50:24361void AppendPublicKeyHashes(CFArrayRef chain,
[email protected]ede03212012-09-07 12:52:26362 HashValueVector* hashes) {
[email protected]62b23c22012-03-22 04:50:24363 const CFIndex n = CFArrayGetCount(chain);
364 for (CFIndex i = 0; i < n; i++) {
365 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
366 const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
367
368 CSSM_DATA cert_data;
369 OSStatus err = SecCertificateGetData(cert, &cert_data);
370 DCHECK_EQ(err, noErr);
371 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
372 cert_data.Length);
373 base::StringPiece spki_bytes;
374 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
375 continue;
376
[email protected]ede03212012-09-07 12:52:26377 HashValue sha1(HASH_VALUE_SHA1);
378 CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data());
379 hashes->push_back(sha1);
380
381 HashValue sha256(HASH_VALUE_SHA256);
382 CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data());
383 hashes->push_back(sha256);
[email protected]62b23c22012-03-22 04:50:24384 }
385}
386
mattm1a282f52016-11-10 21:49:42387enum CRLSetResult {
388 kCRLSetOk,
389 kCRLSetRevoked,
390 kCRLSetUnknown,
391};
392
393// CheckRevocationWithCRLSet attempts to check each element of |cert_list|
394// against |crl_set|. It returns:
395// kCRLSetRevoked: if any element of the chain is known to have been revoked.
396// kCRLSetUnknown: if there is no fresh information about the leaf
397// certificate in the chain or if the CRLSet has expired.
398//
399// Only the leaf certificate is considered for coverage because some
400// intermediates have CRLs with no revocations (after filtering) and
401// those CRLs are pruned from the CRLSet at generation time. This means
402// that some EV sites would otherwise take the hit of an OCSP lookup for
403// no reason.
404// kCRLSetOk: otherwise.
405CRLSetResult CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) {
[email protected]62b23c22012-03-22 04:50:24406 if (CFArrayGetCount(chain) == 0)
mattm1a282f52016-11-10 21:49:42407 return kCRLSetOk;
408
409 // error is set to true if any errors are found. It causes such chains to be
410 // considered as not covered.
411 bool error = false;
412 // last_covered is set to the coverage state of the previous certificate. The
413 // certificates are iterated over backwards thus, after the iteration,
414 // |last_covered| contains the coverage state of the leaf certificate.
415 bool last_covered = false;
[email protected]62b23c22012-03-22 04:50:24416
417 // We iterate from the root certificate down to the leaf, keeping track of
418 // the issuer's SPKI at each step.
419 std::string issuer_spki_hash;
420 for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) {
421 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
422 const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
423
424 CSSM_DATA cert_data;
425 OSStatus err = SecCertificateGetData(cert, &cert_data);
426 if (err != noErr) {
427 NOTREACHED();
mattm1a282f52016-11-10 21:49:42428 error = true;
[email protected]62b23c22012-03-22 04:50:24429 continue;
430 }
431 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
432 cert_data.Length);
433 base::StringPiece spki;
434 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
435 NOTREACHED();
mattm1a282f52016-11-10 21:49:42436 error = true;
[email protected]62b23c22012-03-22 04:50:24437 continue;
438 }
439
440 const std::string spki_hash = crypto::SHA256HashString(spki);
441 x509_util::CSSMCachedCertificate cached_cert;
442 if (cached_cert.Init(cert) != CSSM_OK) {
443 NOTREACHED();
mattm1a282f52016-11-10 21:49:42444 error = true;
[email protected]62b23c22012-03-22 04:50:24445 continue;
446 }
447 x509_util::CSSMFieldValue serial_number;
448 err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number);
449 if (err || !serial_number.field()) {
450 NOTREACHED();
mattm1a282f52016-11-10 21:49:42451 error = true;
[email protected]62b23c22012-03-22 04:50:24452 continue;
453 }
454
455 base::StringPiece serial(
456 reinterpret_cast<const char*>(serial_number.field()->Data),
457 serial_number.field()->Length);
458
459 CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
460
461 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty())
462 result = crl_set->CheckSerial(serial, issuer_spki_hash);
463
464 issuer_spki_hash = spki_hash;
465
466 switch (result) {
467 case CRLSet::REVOKED:
mattm1a282f52016-11-10 21:49:42468 return kCRLSetRevoked;
[email protected]62b23c22012-03-22 04:50:24469 case CRLSet::UNKNOWN:
mattm1a282f52016-11-10 21:49:42470 last_covered = false;
471 continue;
[email protected]62b23c22012-03-22 04:50:24472 case CRLSet::GOOD:
mattm1a282f52016-11-10 21:49:42473 last_covered = true;
[email protected]62b23c22012-03-22 04:50:24474 continue;
475 default:
476 NOTREACHED();
mattm1a282f52016-11-10 21:49:42477 error = true;
478 continue;
[email protected]62b23c22012-03-22 04:50:24479 }
480 }
481
mattm1a282f52016-11-10 21:49:42482 if (error || !last_covered || crl_set->IsExpired())
483 return kCRLSetUnknown;
484 return kCRLSetOk;
[email protected]62b23c22012-03-22 04:50:24485}
486
[email protected]339e17e2013-06-14 02:48:29487// Builds and evaluates a SecTrustRef for the certificate chain contained
488// in |cert_array|, using the verification policies in |trust_policies|. On
489// success, returns OK, and updates |trust_ref|, |trust_result|,
490// |verified_chain|, and |chain_info| with the verification results. On
491// failure, no output parameters are modified.
492//
493// Note: An OK return does not mean that |cert_array| is trusted, merely that
494// verification was performed successfully.
495//
496// This function should only be called while the Mac Security Services lock is
497// held.
498int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
499 CFArrayRef trust_policies,
500 int flags,
mattmaf868e72016-09-23 23:25:20501 CFArrayRef keychain_search_list,
[email protected]339e17e2013-06-14 02:48:29502 ScopedCFTypeRef<SecTrustRef>* trust_ref,
503 SecTrustResultType* trust_result,
504 ScopedCFTypeRef<CFArrayRef>* verified_chain,
505 CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) {
506 SecTrustRef tmp_trust = NULL;
507 OSStatus status = SecTrustCreateWithCertificates(cert_array, trust_policies,
508 &tmp_trust);
509 if (status)
510 return NetErrorFromOSStatus(status);
511 ScopedCFTypeRef<SecTrustRef> scoped_tmp_trust(tmp_trust);
512
513 if (TestRootCerts::HasInstance()) {
514 status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust);
515 if (status)
516 return NetErrorFromOSStatus(status);
517 }
518
mattmaf868e72016-09-23 23:25:20519 if (keychain_search_list) {
520 status = SecTrustSetKeychains(tmp_trust, keychain_search_list);
521 if (status)
522 return NetErrorFromOSStatus(status);
523 }
524
[email protected]339e17e2013-06-14 02:48:29525 CSSM_APPLE_TP_ACTION_DATA tp_action_data;
526 memset(&tp_action_data, 0, sizeof(tp_action_data));
527 tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION;
528 // Allow CSSM to download any missing intermediate certificates if an
529 // authorityInfoAccess extension or issuerAltName extension is present.
530 tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET |
531 CSSM_TP_ACTION_TRUST_SETTINGS;
532
533 // Note: For EV certificates, the Apple TP will handle setting these flags
534 // as part of EV evaluation.
535 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) {
536 // Require a positive result from an OCSP responder or a CRL (or both)
537 // for every certificate in the chain. The Apple TP automatically
538 // excludes the self-signed root from this requirement. If a certificate
539 // is missing both a crlDistributionPoints extension and an
540 // authorityInfoAccess extension with an OCSP responder URL, then we
541 // will get a kSecTrustResultRecoverableTrustFailure back from
542 // SecTrustEvaluate(), with a
543 // CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK error code. In that case,
544 // we'll set our own result to include
545 // CERT_STATUS_NO_REVOCATION_MECHANISM. If one or both extensions are
546 // present, and a check fails (server unavailable, OCSP retry later,
547 // signature mismatch), then we'll set our own result to include
548 // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION.
549 tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
550
551 // Note, even if revocation checking is disabled, SecTrustEvaluate() will
552 // modify the OCSP options so as to attempt OCSP checking if it believes a
553 // certificate may chain to an EV root. However, because network fetches
554 // are disabled in CreateTrustPolicies() when revocation checking is
555 // disabled, these will only go against the local cache.
556 }
557
558 CFDataRef action_data_ref =
559 CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
560 reinterpret_cast<UInt8*>(&tp_action_data),
561 sizeof(tp_action_data), kCFAllocatorNull);
562 if (!action_data_ref)
563 return ERR_OUT_OF_MEMORY;
564 ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref);
565 status = SecTrustSetParameters(tmp_trust, CSSM_TP_ACTION_DEFAULT,
566 action_data_ref);
567 if (status)
568 return NetErrorFromOSStatus(status);
569
570 // Verify the certificate. A non-zero result from SecTrustGetResult()
571 // indicates that some fatal error occurred and the chain couldn't be
572 // processed, not that the chain contains no errors. We need to examine the
573 // output of SecTrustGetResult() to determine that.
574 SecTrustResultType tmp_trust_result;
575 status = SecTrustEvaluate(tmp_trust, &tmp_trust_result);
576 if (status)
577 return NetErrorFromOSStatus(status);
578 CFArrayRef tmp_verified_chain = NULL;
579 CSSM_TP_APPLE_EVIDENCE_INFO* tmp_chain_info;
580 status = SecTrustGetResult(tmp_trust, &tmp_trust_result, &tmp_verified_chain,
581 &tmp_chain_info);
582 if (status)
583 return NetErrorFromOSStatus(status);
584
585 trust_ref->swap(scoped_tmp_trust);
586 *trust_result = tmp_trust_result;
587 verified_chain->reset(tmp_verified_chain);
588 *chain_info = tmp_chain_info;
589
590 return OK;
591}
592
rsleevife8953712015-10-29 02:06:53593// Helper class for managing the set of OS X Known Roots. This is only safe
594// to initialize while the crypto::GetMacSecurityServicesLock() is held, due
595// to calling into Security.framework functions; however, once initialized,
596// it can be called at any time.
597// In practice, due to lazy initialization, it's best to just always guard
598// accesses with the lock.
599class OSXKnownRootHelper {
600 public:
601 // IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
602 // that we recognise as a standard root.
603 bool IsIssuedByKnownRoot(CFArrayRef chain) {
604 // If there are no known roots, then an API failure occurred. For safety,
605 // assume that all certificates are issued by known roots.
606 if (known_roots_.empty())
607 return true;
608
609 CFIndex n = CFArrayGetCount(chain);
610 if (n < 1)
611 return false;
612 SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>(
613 const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1)));
614 SHA256HashValue hash = X509Certificate::CalculateFingerprint256(root_ref);
615 return known_roots_.find(hash) != known_roots_.end();
616 }
617
618 private:
scottmg5e65e3a2017-03-08 08:48:46619 friend struct base::LazyInstanceTraitsBase<OSXKnownRootHelper>;
rsleevife8953712015-10-29 02:06:53620
621 OSXKnownRootHelper() {
622 CFArrayRef cert_array = NULL;
623 OSStatus rv = SecTrustSettingsCopyCertificates(
624 kSecTrustSettingsDomainSystem, &cert_array);
625 if (rv != noErr) {
626 LOG(ERROR) << "Unable to determine trusted roots; assuming all roots are "
627 << "trusted! Error " << rv;
628 return;
629 }
630 base::ScopedCFTypeRef<CFArrayRef> scoped_array(cert_array);
631 for (CFIndex i = 0, size = CFArrayGetCount(cert_array); i < size; ++i) {
632 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
633 const_cast<void*>(CFArrayGetValueAtIndex(cert_array, i)));
634 known_roots_.insert(X509Certificate::CalculateFingerprint256(cert));
635 }
636 }
637
638 ~OSXKnownRootHelper() {}
639
640 std::set<SHA256HashValue, SHA256HashValueLessThan> known_roots_;
641};
642
643base::LazyInstance<OSXKnownRootHelper>::Leaky g_known_roots =
644 LAZY_INSTANCE_INITIALIZER;
645
mattm1a282f52016-11-10 21:49:42646// Runs path building & verification loop for |cert|, given |flags|. This is
647// split into a separate function so verification can be repeated with different
648// flags. This function does not handle EV.
649int VerifyWithGivenFlags(X509Certificate* cert,
650 const std::string& hostname,
651 const int flags,
652 CRLSet* crl_set,
653 CertVerifyResult* verify_result,
654 CRLSetResult* completed_chain_crl_result) {
[email protected]62b23c22012-03-22 04:50:24655 ScopedCFTypeRef<CFArrayRef> trust_policies;
mattmaf868e72016-09-23 23:25:20656 OSStatus status = CreateTrustPolicies(flags, &trust_policies);
[email protected]62b23c22012-03-22 04:50:24657 if (status)
658 return NetErrorFromOSStatus(status);
659
mattm1a282f52016-11-10 21:49:42660 *completed_chain_crl_result = kCRLSetUnknown;
661
[email protected]d6e8fe62012-10-03 05:46:45662 // Serialize all calls that may use the Keychain, to work around various
663 // issues in OS X 10.6+ with multi-threaded access to Security.framework.
664 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
[email protected]62b23c22012-03-22 04:50:24665
[email protected]339e17e2013-06-14 02:48:29666 ScopedCFTypeRef<SecTrustRef> trust_ref;
667 SecTrustResultType trust_result = kSecTrustResultDeny;
668 ScopedCFTypeRef<CFArrayRef> completed_chain;
669 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL;
rsleevia3fa5412015-02-04 21:37:15670 bool candidate_untrusted = true;
671 bool candidate_weak = false;
[email protected]62b23c22012-03-22 04:50:24672
rsleevia3fa5412015-02-04 21:37:15673 // OS X lacks proper path discovery; it will take the input certs and never
674 // backtrack the graph attempting to discover valid paths.
675 // This can create issues in some situations:
676 // - When OS X changes the trust store, there may be a chain
677 // A -> B -> C -> D
678 // where OS X trusts D (on some versions) and trusts C (on some versions).
679 // If a server supplies a chain A, B, C (cross-signed by D), then this chain
680 // will successfully validate on systems that trust D, but fail for systems
681 // that trust C. If the server supplies a chain of A -> B, then it forces
682 // all clients to fetch C (via AIA) if they trust D, and not all clients
683 // (notably, Firefox and Android) will do this, thus breaking them.
684 // An example of this is the Verizon Business Services root - GTE CyberTrust
685 // and Baltimore CyberTrust roots represent old and new roots that cause
686 // issues depending on which version of OS X being used.
687 //
688 // - A server may be (misconfigured) to send an expired intermediate
689 // certificate. On platforms with path discovery, the graph traversal
690 // will back up to immediately before this intermediate, and then
691 // attempt an AIA fetch or retrieval from local store. However, OS X
692 // does not do this, and thus prevents access. While this is ostensibly
693 // a server misconfiguration issue, the fact that it works on other
694 // platforms is a jarring inconsistency for users.
695 //
696 // - When OS X trusts both C and D (simultaneously), it's possible that the
697 // version of C signed by D is signed using a weak algorithm (e.g. SHA-1),
698 // while the version of C in the trust store's signature doesn't matter.
699 // Since a 'strong' chain exists, it would be desirable to prefer this
700 // chain.
701 //
702 // - A variant of the above example, it may be that the version of B sent by
703 // the server is signed using a weak algorithm, but the version of B
704 // present in the AIA of A is signed using a strong algorithm. Since a
705 // 'strong' chain exists, it would be desirable to prefer this chain.
706 //
mattmaf868e72016-09-23 23:25:20707 // - A user keychain may contain a less desirable intermediate or root.
708 // OS X gives the user keychains higher priority than the system keychain,
709 // so it may build a weak chain.
710 //
rsleevia3fa5412015-02-04 21:37:15711 // Because of this, the code below first attempts to validate the peer's
712 // identity using the supplied chain. If it is not trusted (e.g. the OS only
713 // trusts C, but the version of C signed by D was sent, and D is not trusted),
714 // or if it contains a weak chain, it will begin lopping off certificates
715 // from the end of the chain and attempting to verify. If a stronger, trusted
716 // chain is found, it is used, otherwise, the algorithm continues until only
717 // the peer's certificate remains.
718 //
mattmaf868e72016-09-23 23:25:20719 // If the loop does not find a trusted chain, the loop will be repeated with
720 // the keychain search order altered to give priority to the System Roots
721 // keychain.
722 //
rsleevia3fa5412015-02-04 21:37:15723 // This does cause a performance hit for these users, but only in cases where
724 // OS X is building weaker chains than desired, or when it would otherwise
725 // fail the connection.
mattmaf868e72016-09-23 23:25:20726 for (bool try_reordered_keychain : {false, true}) {
727 ScopedCFTypeRef<CFArrayRef> scoped_alternate_keychain_search_list;
728 if (TestKeychainSearchList::HasInstance()) {
729 // Unit tests need to be able to hermetically simulate situations where a
730 // user has an undesirable certificate in a per-user keychain.
731 // Adding/Removing a Keychain using SecKeychainCreate/SecKeychainDelete
732 // has global side effects, which would break other tests and processes
733 // running on the same machine, so instead tests may load pre-created
734 // keychains using SecKeychainOpen and then inject them through
735 // TestKeychainSearchList.
736 CFArrayRef keychain_search_list;
737 status = TestKeychainSearchList::GetInstance()->CopySearchList(
738 &keychain_search_list);
739 if (status)
740 return NetErrorFromOSStatus(status);
741 scoped_alternate_keychain_search_list.reset(keychain_search_list);
davidbena6173cd82015-04-23 22:55:09742 }
mattmaf868e72016-09-23 23:25:20743 if (try_reordered_keychain) {
744 // If a TestKeychainSearchList is present, it will have already set
745 // |scoped_alternate_keychain_search_list|, which will be used as the
746 // basis for reordering the keychain. Otherwise, get the current keychain
747 // search list and use that.
748 if (!scoped_alternate_keychain_search_list) {
749 CFArrayRef keychain_search_list;
750 status = SecKeychainCopySearchList(&keychain_search_list);
751 if (status)
752 return NetErrorFromOSStatus(status);
753 scoped_alternate_keychain_search_list.reset(keychain_search_list);
754 }
755 CFMutableArrayRef mutable_keychain_search_list = CFArrayCreateMutableCopy(
756 kCFAllocatorDefault,
757 CFArrayGetCount(scoped_alternate_keychain_search_list.get()) + 1,
758 scoped_alternate_keychain_search_list.get());
759 if (!mutable_keychain_search_list)
760 return ERR_OUT_OF_MEMORY;
761 scoped_alternate_keychain_search_list.reset(mutable_keychain_search_list);
rsleevia3fa5412015-02-04 21:37:15762
mattmaf868e72016-09-23 23:25:20763 SecKeychainRef keychain;
764 // Get a reference to the System Roots keychain. The System Roots
765 // keychain is not normally present in the keychain search list, but is
766 // implicitly checked after the keychains in the search list. By
767 // including it directly, force it to be checked first. This is a gross
768 // hack, but the path is known to be valid on OS X 10.9-10.11.
769 status = SecKeychainOpen(
770 "/System/Library/Keychains/SystemRootCertificates.keychain",
771 &keychain);
772 if (status)
773 return NetErrorFromOSStatus(status);
774 ScopedCFTypeRef<SecKeychainRef> scoped_keychain(keychain);
775
776 CFArrayInsertValueAtIndex(mutable_keychain_search_list, 0, keychain);
777 }
778
779 ScopedCFTypeRef<CFMutableArrayRef> cert_array(
780 cert->CreateOSCertChainForCert());
781
782 // Beginning with the certificate chain as supplied by the server, attempt
783 // to verify the chain. If a failure is encountered, trim a certificate
784 // from the end (so long as one remains) and retry, in the hope of forcing
785 // OS X to find a better path.
786 while (CFArrayGetCount(cert_array) > 0) {
787 ScopedCFTypeRef<SecTrustRef> temp_ref;
788 SecTrustResultType temp_trust_result = kSecTrustResultDeny;
789 ScopedCFTypeRef<CFArrayRef> temp_chain;
790 CSSM_TP_APPLE_EVIDENCE_INFO* temp_chain_info = NULL;
791
792 int rv = BuildAndEvaluateSecTrustRef(
793 cert_array, trust_policies, flags,
794 scoped_alternate_keychain_search_list.get(), &temp_ref,
795 &temp_trust_result, &temp_chain, &temp_chain_info);
796 if (rv != OK)
797 return rv;
798
799 // Check to see if the path |temp_chain| has been revoked. This is less
800 // than ideal to perform after path building, rather than during, because
801 // there may be multiple paths to trust anchors, and only some of them
802 // are revoked. Ideally, CRLSets would be part of path building, which
803 // they are when using NSS (Linux) or CryptoAPI (Windows).
804 //
805 // The CRLSet checking is performed inside the loop in the hope that if a
806 // path is revoked, it's an older path, and the only reason it was built
807 // is because the server forced it (by supplying an older or less
808 // desirable intermediate) or because the user had installed a
809 // certificate in their Keychain forcing this path. However, this means
810 // its still possible for a CRLSet block of an intermediate to prevent
811 // access, even when there is a 'good' chain. To fully remedy this, a
812 // solution might be to have CRLSets contain enough knowledge about what
813 // the 'desired' path might be, but for the time being, the
814 // implementation is kept as 'simple' as it can be.
mattm1a282f52016-11-10 21:49:42815 CRLSetResult crl_result = kCRLSetUnknown;
816 if (crl_set)
817 crl_result = CheckRevocationWithCRLSet(temp_chain, crl_set);
mattmaf868e72016-09-23 23:25:20818 bool untrusted = (temp_trust_result != kSecTrustResultUnspecified &&
819 temp_trust_result != kSecTrustResultProceed) ||
mattm1a282f52016-11-10 21:49:42820 crl_result == kCRLSetRevoked;
mattmaf868e72016-09-23 23:25:20821 bool weak_chain = false;
822 if (CFArrayGetCount(temp_chain) == 0) {
823 // If the chain is empty, it cannot be trusted or have recoverable
824 // errors.
825 DCHECK(untrusted);
826 DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result);
827 } else {
mattmaf868e72016-09-23 23:25:20828 weak_chain =
eromanaccb8132017-01-10 07:13:11829 IsWeakChainBasedOnHashingAlgorithms(temp_chain, temp_chain_info);
mattmaf868e72016-09-23 23:25:20830 }
831 // Set the result to the current chain if:
832 // - This is the first verification attempt. This ensures that if
833 // everything is awful (e.g. it may just be an untrusted cert), that
834 // what is reported is exactly what was sent by the server
835 // - If the current chain is trusted, and the old chain was not trusted,
836 // then prefer this chain. This ensures that if there is at least a
837 // valid path to a trust anchor, it's preferred over reporting an error.
838 // - If the current chain is trusted, and the old chain is trusted, but
839 // the old chain contained weak algorithms while the current chain only
840 // contains strong algorithms, then prefer the current chain over the
841 // old chain.
842 //
843 // Note: If the leaf certificate itself is weak, then the only
844 // consideration is whether or not there is a trusted chain. That's
845 // because no amount of path discovery will fix a weak leaf.
846 if (!trust_ref || (!untrusted && (candidate_untrusted ||
847 (candidate_weak && !weak_chain)))) {
848 trust_ref = temp_ref;
849 trust_result = temp_trust_result;
850 completed_chain = temp_chain;
mattm1a282f52016-11-10 21:49:42851 *completed_chain_crl_result = crl_result;
mattmaf868e72016-09-23 23:25:20852 chain_info = temp_chain_info;
853
854 candidate_untrusted = untrusted;
855 candidate_weak = weak_chain;
856 }
857 // Short-circuit when a current, trusted chain is found.
858 if (!untrusted && !weak_chain)
859 break;
860 CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1);
rsleevia3fa5412015-02-04 21:37:15861 }
862 // Short-circuit when a current, trusted chain is found.
mattmaf868e72016-09-23 23:25:20863 if (!candidate_untrusted && !candidate_weak)
rsleevia3fa5412015-02-04 21:37:15864 break;
[email protected]62b23c22012-03-22 04:50:24865 }
866
[email protected]339e17e2013-06-14 02:48:29867 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)
[email protected]62b23c22012-03-22 04:50:24868 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
869
mattm1a282f52016-11-10 21:49:42870 if (*completed_chain_crl_result == kCRLSetRevoked)
[email protected]62b23c22012-03-22 04:50:24871 verify_result->cert_status |= CERT_STATUS_REVOKED;
872
davidbena6173cd82015-04-23 22:55:09873 if (CFArrayGetCount(completed_chain) > 0) {
eromanaccb8132017-01-10 07:13:11874 CopyCertChainToVerifyResult(completed_chain, verify_result);
davidbena6173cd82015-04-23 22:55:09875 }
[email protected]62b23c22012-03-22 04:50:24876
[email protected]58484ca2012-05-29 21:56:34877 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits
878 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds
879 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping
880 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only
881 // error was due to an unsupported key size.
882 bool policy_failed = false;
mattm1a282f52016-11-10 21:49:42883 bool policy_fail_already_mapped = false;
[email protected]29444412014-01-04 02:02:09884 bool weak_key_or_signature_algorithm = false;
[email protected]58484ca2012-05-29 21:56:34885
[email protected]62b23c22012-03-22 04:50:24886 // Evaluate the results
887 OSStatus cssm_result;
888 switch (trust_result) {
889 case kSecTrustResultUnspecified:
890 case kSecTrustResultProceed:
891 // Certificate chain is valid and trusted ("unspecified" indicates that
892 // the user has not explicitly set a trust setting)
893 break;
894
[email protected]4abf5b4c2013-06-20 22:20:48895 // According to SecTrust.h, kSecTrustResultConfirm isn't returned on 10.5+,
896 // and it is marked deprecated in the 10.9 SDK.
[email protected]62b23c22012-03-22 04:50:24897 case kSecTrustResultDeny:
[email protected]4abf5b4c2013-06-20 22:20:48898 // Certificate chain is explicitly untrusted.
[email protected]62b23c22012-03-22 04:50:24899 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
900 break;
901
902 case kSecTrustResultRecoverableTrustFailure:
903 // Certificate chain has a failure that can be overridden by the user.
904 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
905 if (status)
906 return NetErrorFromOSStatus(status);
[email protected]58484ca2012-05-29 21:56:34907 if (cssm_result == CSSMERR_TP_VERIFY_ACTION_FAILED) {
908 policy_failed = true;
909 } else {
910 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
911 }
[email protected]62b23c22012-03-22 04:50:24912 // Walk the chain of error codes in the CSSM_TP_APPLE_EVIDENCE_INFO
913 // structure which can catch multiple errors from each certificate.
914 for (CFIndex index = 0, chain_count = CFArrayGetCount(completed_chain);
915 index < chain_count; ++index) {
916 if (chain_info[index].StatusBits & CSSM_CERT_STATUS_EXPIRED ||
917 chain_info[index].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET)
918 verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
919 if (!IsCertStatusError(verify_result->cert_status) &&
920 chain_info[index].NumStatusCodes == 0) {
921 LOG(WARNING) << "chain_info[" << index << "].NumStatusCodes is 0"
922 ", chain_info[" << index << "].StatusBits is "
923 << chain_info[index].StatusBits;
924 }
wtc69f8ea82015-06-04 00:08:13925 for (uint32_t status_code_index = 0;
[email protected]62b23c22012-03-22 04:50:24926 status_code_index < chain_info[index].NumStatusCodes;
927 ++status_code_index) {
[email protected]29444412014-01-04 02:02:09928 // As of OS X 10.9, attempting to verify a certificate chain that
929 // contains a weak signature algorithm (MD2, MD5) in an intermediate
930 // or leaf cert will be treated as a (recoverable) policy validation
931 // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE
932 // added to the Status Codes. Don't treat this code as an invalid
933 // certificate; instead, map it to a weak key. Any truly invalid
934 // certificates will have the major error (cssm_result) set to
935 // CSSMERR_TP_INVALID_CERTIFICATE, rather than
936 // CSSMERR_TP_VERIFY_ACTION_FAILED.
937 CertStatus mapped_status = 0;
938 if (policy_failed &&
939 chain_info[index].StatusCodes[status_code_index] ==
940 CSSMERR_TP_INVALID_CERTIFICATE) {
mattm1a282f52016-11-10 21:49:42941 mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
942 weak_key_or_signature_algorithm = true;
943 policy_fail_already_mapped = true;
944 } else if (policy_failed &&
945 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) &&
946 chain_info[index].StatusCodes[status_code_index] ==
947 CSSMERR_TP_VERIFY_ACTION_FAILED &&
948 base::mac::IsAtLeastOS10_12()) {
949 // On 10.12, using kSecRevocationRequirePositiveResponse flag
950 // causes a CSSMERR_TP_VERIFY_ACTION_FAILED status if revocation
951 // couldn't be checked. (Note: even if the cert had no
952 // crlDistributionPoints or OCSP AIA.)
953 mapped_status = CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
954 policy_fail_already_mapped = true;
[email protected]29444412014-01-04 02:02:09955 } else {
mattm1a282f52016-11-10 21:49:42956 mapped_status = CertStatusFromOSStatus(
957 chain_info[index].StatusCodes[status_code_index]);
958 if (mapped_status == CERT_STATUS_WEAK_KEY) {
959 weak_key_or_signature_algorithm = true;
960 policy_fail_already_mapped = true;
961 }
[email protected]29444412014-01-04 02:02:09962 }
[email protected]58484ca2012-05-29 21:56:34963 verify_result->cert_status |= mapped_status;
[email protected]62b23c22012-03-22 04:50:24964 }
965 }
mattm1a282f52016-11-10 21:49:42966 if (policy_failed && !policy_fail_already_mapped) {
[email protected]58484ca2012-05-29 21:56:34967 // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak
mattm1a282f52016-11-10 21:49:42968 // key or problem checking revocation, map it back to an appropriate
969 // error code.
[email protected]58484ca2012-05-29 21:56:34970 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
971 }
[email protected]62b23c22012-03-22 04:50:24972 if (!IsCertStatusError(verify_result->cert_status)) {
973 LOG(ERROR) << "cssm_result=" << cssm_result;
974 verify_result->cert_status |= CERT_STATUS_INVALID;
975 NOTREACHED();
976 }
977 break;
978
979 default:
980 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
981 if (status)
982 return NetErrorFromOSStatus(status);
983 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
984 if (!IsCertStatusError(verify_result->cert_status)) {
985 LOG(WARNING) << "trust_result=" << trust_result;
986 verify_result->cert_status |= CERT_STATUS_INVALID;
987 }
988 break;
989 }
990
rsleevi0f9bfb02017-03-04 03:07:20991 // Hostname validation is handled by CertVerifyProc, so mask off any errors
992 // that SecTrustEvaluate may have set, as its results are not used.
[email protected]62b23c22012-03-22 04:50:24993 verify_result->cert_status &= ~CERT_STATUS_COMMON_NAME_INVALID;
[email protected]62b23c22012-03-22 04:50:24994
995 // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
996 // compatible with Windows, which in turn implements this behavior to be
997 // compatible with WinHTTP, which doesn't report this error (bug 3004).
998 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
999
[email protected]8b4a61a2012-11-28 22:57:201000 AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes);
rsleevife8953712015-10-29 02:06:531001 verify_result->is_issued_by_known_root =
1002 g_known_roots.Get().IsIssuedByKnownRoot(completed_chain);
[email protected]8b4a61a2012-11-28 22:57:201003
[email protected]62b23c22012-03-22 04:50:241004 if (IsCertStatusError(verify_result->cert_status))
1005 return MapCertStatusToNetError(verify_result->cert_status);
1006
mattm1a282f52016-11-10 21:49:421007 return OK;
1008}
1009
1010} // namespace
1011
1012CertVerifyProcMac::CertVerifyProcMac() {}
1013
1014CertVerifyProcMac::~CertVerifyProcMac() {}
1015
1016bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const {
1017 return false;
1018}
1019
1020bool CertVerifyProcMac::SupportsOCSPStapling() const {
1021 // TODO(rsleevi): Plumb an OCSP response into the Mac system library.
1022 // https://crbug.com/430714
1023 return false;
1024}
1025
1026int CertVerifyProcMac::VerifyInternal(
1027 X509Certificate* cert,
1028 const std::string& hostname,
1029 const std::string& ocsp_response,
1030 int flags,
1031 CRLSet* crl_set,
1032 const CertificateList& additional_trust_anchors,
1033 CertVerifyResult* verify_result) {
1034 // Save the input state of |*verify_result|, which may be needed to re-do
1035 // verification with different flags.
1036 const CertVerifyResult input_verify_result(*verify_result);
1037
1038 // If EV verification is enabled, check for EV policy in leaf cert.
1039 std::string candidate_ev_policy_oid;
1040 if (flags & CertVerifier::VERIFY_EV_CERT)
1041 GetCandidateEVPolicy(cert, &candidate_ev_policy_oid);
1042
1043 CRLSetResult completed_chain_crl_result;
1044 int rv = VerifyWithGivenFlags(cert, hostname, flags, crl_set, verify_result,
1045 &completed_chain_crl_result);
1046 if (rv != OK)
1047 return rv;
1048
1049 if (!candidate_ev_policy_oid.empty() &&
1050 CheckCertChainEV(verify_result->verified_cert.get(),
1051 candidate_ev_policy_oid)) {
1052 // EV policies check out and the verification succeeded. See if revocation
1053 // checking still needs to be done before it can be marked as EV.
1054 if (completed_chain_crl_result == kCRLSetUnknown &&
1055 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY) &&
1056 !(flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)) {
1057 // If this is an EV cert and it wasn't covered by CRLSets and revocation
1058 // checking wasn't already on, try again with revocation forced on.
1059 //
1060 // Restore the input state of |*verify_result|, so that the
1061 // re-verification starts with a clean slate.
1062 *verify_result = input_verify_result;
1063 int tmp_rv = VerifyWithGivenFlags(
1064 verify_result->verified_cert.get(), hostname,
1065 flags | CertVerifier::VERIFY_REV_CHECKING_ENABLED, crl_set,
1066 verify_result, &completed_chain_crl_result);
1067 // If re-verification failed, return those results without setting EV
1068 // status.
1069 if (tmp_rv != OK)
1070 return tmp_rv;
1071 // Otherwise, fall through and add the EV status flag.
[email protected]62b23c22012-03-22 04:50:241072 }
mattm1a282f52016-11-10 21:49:421073 // EV cert and it was covered by CRLSets or revocation checking passed.
1074 verify_result->cert_status |= CERT_STATUS_IS_EV;
[email protected]62b23c22012-03-22 04:50:241075 }
1076
[email protected]62b23c22012-03-22 04:50:241077 return OK;
1078}
1079
1080} // namespace net
erikchenbedc2612016-03-02 02:52:081081
1082#pragma clang diagnostic pop // "-Wdeprecated-declarations"