blob: 07a49a234b0103594aa4673eb3a7506c0e9eaa53 [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"
18#include "base/mac/scoped_cftyperef.h"
19#include "base/sha1.h"
[email protected]d069c11a2013-04-13 00:01:5520#include "base/strings/string_piece.h"
[email protected]d6e8fe62012-10-03 05:46:4521#include "base/synchronization/lock.h"
22#include "crypto/mac_security_services_lock.h"
[email protected]62b23c22012-03-22 04:50:2423#include "crypto/sha2.h"
rsleevife8953712015-10-29 02:06:5324#include "net/base/hash_value.h"
[email protected]62b23c22012-03-22 04:50:2425#include "net/base/net_errors.h"
[email protected]6e7845ae2013-03-29 21:48:1126#include "net/cert/asn1_util.h"
27#include "net/cert/cert_status_flags.h"
28#include "net/cert/cert_verifier.h"
29#include "net/cert/cert_verify_result.h"
30#include "net/cert/crl_set.h"
31#include "net/cert/test_root_certs.h"
32#include "net/cert/x509_certificate.h"
[email protected]6e7845ae2013-03-29 21:48:1133#include "net/cert/x509_util_mac.h"
[email protected]62b23c22012-03-22 04:50:2434
erikchenbedc2612016-03-02 02:52:0835// CSSM functions are deprecated as of OSX 10.7, but have no replacement.
36// https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1
37#pragma clang diagnostic push
38#pragma clang diagnostic ignored "-Wdeprecated-declarations"
39
[email protected]62b23c22012-03-22 04:50:2440// From 10.7.2 libsecurity_keychain-55035/lib/SecTrustPriv.h, for use with
41// SecTrustCopyExtendedResult.
42#ifndef kSecEVOrganizationName
43#define kSecEVOrganizationName CFSTR("Organization")
44#endif
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
151// validate a certificate for an SSL server. |hostname| contains the name of
152// the SSL server that the certificate should be verified against. |flags| is
153// a bitwise-OR of VerifyFlags that can further alter how trust is validated,
154// such as how revocation is checked. If successful, returns noErr, and
155// stores the resultant array of SecPolicyRefs in |policies|.
156OSStatus CreateTrustPolicies(const std::string& hostname,
157 int flags,
158 ScopedCFTypeRef<CFArrayRef>* policies) {
159 ScopedCFTypeRef<CFMutableArrayRef> local_policies(
160 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
161 if (!local_policies)
162 return memFullErr;
163
164 SecPolicyRef ssl_policy;
165 OSStatus status = x509_util::CreateSSLServerPolicy(hostname, &ssl_policy);
166 if (status)
167 return status;
168 CFArrayAppendValue(local_policies, ssl_policy);
169 CFRelease(ssl_policy);
170
171 // Explicitly add revocation policies, in order to override system
172 // revocation checking policies and instead respect the application-level
173 // revocation preference.
174 status = x509_util::CreateRevocationPolicies(
[email protected]8738e0d72012-08-23 02:00:47175 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED),
176 (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY),
[email protected]62b23c22012-03-22 04:50:24177 local_policies);
178 if (status)
179 return status;
180
181 policies->reset(local_policies.release());
182 return noErr;
183}
184
rsleevia3fa5412015-02-04 21:37:15185// Stores the constructed certificate chain |cert_chain| and information about
186// the signature algorithms used into |*verify_result|. If the leaf cert in
187// |cert_chain| contains a weak (MD2, MD4, MD5, SHA-1) signature, stores that
davidbena6173cd82015-04-23 22:55:09188// in |*leaf_is_weak|. |cert_chain| must not be empty.
[email protected]62b23c22012-03-22 04:50:24189void GetCertChainInfo(CFArrayRef cert_chain,
190 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info,
rsleevia3fa5412015-02-04 21:37:15191 CertVerifyResult* verify_result,
192 bool* leaf_is_weak) {
davidbena6173cd82015-04-23 22:55:09193 DCHECK_LT(0, CFArrayGetCount(cert_chain));
194
rsleevia3fa5412015-02-04 21:37:15195 *leaf_is_weak = false;
rsleevia3fa5412015-02-04 21:37:15196 verify_result->has_md2 = false;
197 verify_result->has_md4 = false;
198 verify_result->has_md5 = false;
199 verify_result->has_sha1 = false;
rsleevi22241d9e2015-11-13 21:14:37200 verify_result->has_sha1_leaf = false;
rsleevia3fa5412015-02-04 21:37:15201
[email protected]62b23c22012-03-22 04:50:24202 SecCertificateRef verified_cert = NULL;
203 std::vector<SecCertificateRef> verified_chain;
204 for (CFIndex i = 0, count = CFArrayGetCount(cert_chain); i < count; ++i) {
205 SecCertificateRef chain_cert = reinterpret_cast<SecCertificateRef>(
206 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
207 if (i == 0) {
208 verified_cert = chain_cert;
209 } else {
210 verified_chain.push_back(chain_cert);
211 }
212
213 if ((chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_IN_ANCHORS) ||
214 (chain_info[i].StatusBits & CSSM_CERT_STATUS_IS_ROOT)) {
215 // The current certificate is either in the user's trusted store or is
216 // a root (self-signed) certificate. Ignore the signature algorithm for
217 // these certificates, as it is meaningless for security. We allow
218 // self-signed certificates (i == 0 & IS_ROOT), since we accept that
219 // any security assertions by such a cert are inherently meaningless.
220 continue;
221 }
222
223 x509_util::CSSMCachedCertificate cached_cert;
224 OSStatus status = cached_cert.Init(chain_cert);
225 if (status)
226 continue;
227 x509_util::CSSMFieldValue signature_field;
228 status = cached_cert.GetField(&CSSMOID_X509V1SignatureAlgorithm,
229 &signature_field);
230 if (status || !signature_field.field())
231 continue;
232 // Match the behaviour of OS X system tools and defensively check that
233 // sizes are appropriate. This would indicate a critical failure of the
234 // OS X certificate library, but based on history, it is best to play it
235 // safe.
236 const CSSM_X509_ALGORITHM_IDENTIFIER* sig_algorithm =
237 signature_field.GetAs<CSSM_X509_ALGORITHM_IDENTIFIER>();
238 if (!sig_algorithm)
239 continue;
240
241 const CSSM_OID* alg_oid = &sig_algorithm->algorithm;
242 if (CSSMOIDEqual(alg_oid, &CSSMOID_MD2WithRSA)) {
243 verify_result->has_md2 = true;
rsleevia3fa5412015-02-04 21:37:15244 if (i == 0)
245 *leaf_is_weak = true;
[email protected]62b23c22012-03-22 04:50:24246 } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD4WithRSA)) {
247 verify_result->has_md4 = true;
rsleevia3fa5412015-02-04 21:37:15248 if (i == 0)
249 *leaf_is_weak = true;
[email protected]62b23c22012-03-22 04:50:24250 } else if (CSSMOIDEqual(alg_oid, &CSSMOID_MD5WithRSA)) {
251 verify_result->has_md5 = true;
rsleevia3fa5412015-02-04 21:37:15252 if (i == 0)
253 *leaf_is_weak = true;
rsleevib92e6f52014-09-29 23:48:04254 } else if (CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithRSA) ||
255 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithRSA_OIW) ||
256 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA) ||
257 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA_CMS) ||
258 CSSMOIDEqual(alg_oid, &CSSMOID_SHA1WithDSA_JDK) ||
259 CSSMOIDEqual(alg_oid, &CSSMOID_ECDSA_WithSHA1)) {
260 verify_result->has_sha1 = true;
rsleevi22241d9e2015-11-13 21:14:37261 if (i == 0) {
262 verify_result->has_sha1_leaf = true;
rsleevia3fa5412015-02-04 21:37:15263 *leaf_is_weak = true;
rsleevi22241d9e2015-11-13 21:14:37264 }
[email protected]62b23c22012-03-22 04:50:24265 }
266 }
davidbena6173cd82015-04-23 22:55:09267 if (!verified_cert) {
268 NOTREACHED();
[email protected]62b23c22012-03-22 04:50:24269 return;
davidbena6173cd82015-04-23 22:55:09270 }
[email protected]62b23c22012-03-22 04:50:24271
272 verify_result->verified_cert =
273 X509Certificate::CreateFromHandle(verified_cert, verified_chain);
274}
275
276void AppendPublicKeyHashes(CFArrayRef chain,
[email protected]ede03212012-09-07 12:52:26277 HashValueVector* hashes) {
[email protected]62b23c22012-03-22 04:50:24278 const CFIndex n = CFArrayGetCount(chain);
279 for (CFIndex i = 0; i < n; i++) {
280 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
281 const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
282
283 CSSM_DATA cert_data;
284 OSStatus err = SecCertificateGetData(cert, &cert_data);
285 DCHECK_EQ(err, noErr);
286 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
287 cert_data.Length);
288 base::StringPiece spki_bytes;
289 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki_bytes))
290 continue;
291
[email protected]ede03212012-09-07 12:52:26292 HashValue sha1(HASH_VALUE_SHA1);
293 CC_SHA1(spki_bytes.data(), spki_bytes.size(), sha1.data());
294 hashes->push_back(sha1);
295
296 HashValue sha256(HASH_VALUE_SHA256);
297 CC_SHA256(spki_bytes.data(), spki_bytes.size(), sha256.data());
298 hashes->push_back(sha256);
[email protected]62b23c22012-03-22 04:50:24299 }
300}
301
302bool CheckRevocationWithCRLSet(CFArrayRef chain, CRLSet* crl_set) {
303 if (CFArrayGetCount(chain) == 0)
304 return true;
305
306 // We iterate from the root certificate down to the leaf, keeping track of
307 // the issuer's SPKI at each step.
308 std::string issuer_spki_hash;
309 for (CFIndex i = CFArrayGetCount(chain) - 1; i >= 0; i--) {
310 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
311 const_cast<void*>(CFArrayGetValueAtIndex(chain, i)));
312
313 CSSM_DATA cert_data;
314 OSStatus err = SecCertificateGetData(cert, &cert_data);
315 if (err != noErr) {
316 NOTREACHED();
317 continue;
318 }
319 base::StringPiece der_bytes(reinterpret_cast<const char*>(cert_data.Data),
320 cert_data.Length);
321 base::StringPiece spki;
322 if (!asn1::ExtractSPKIFromDERCert(der_bytes, &spki)) {
323 NOTREACHED();
324 continue;
325 }
326
327 const std::string spki_hash = crypto::SHA256HashString(spki);
328 x509_util::CSSMCachedCertificate cached_cert;
329 if (cached_cert.Init(cert) != CSSM_OK) {
330 NOTREACHED();
331 continue;
332 }
333 x509_util::CSSMFieldValue serial_number;
334 err = cached_cert.GetField(&CSSMOID_X509V1SerialNumber, &serial_number);
335 if (err || !serial_number.field()) {
336 NOTREACHED();
337 continue;
338 }
339
340 base::StringPiece serial(
341 reinterpret_cast<const char*>(serial_number.field()->Data),
342 serial_number.field()->Length);
343
344 CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
345
346 if (result != CRLSet::REVOKED && !issuer_spki_hash.empty())
347 result = crl_set->CheckSerial(serial, issuer_spki_hash);
348
349 issuer_spki_hash = spki_hash;
350
351 switch (result) {
352 case CRLSet::REVOKED:
353 return false;
354 case CRLSet::UNKNOWN:
355 case CRLSet::GOOD:
356 continue;
357 default:
358 NOTREACHED();
359 return false;
360 }
361 }
362
363 return true;
364}
365
[email protected]339e17e2013-06-14 02:48:29366// Builds and evaluates a SecTrustRef for the certificate chain contained
367// in |cert_array|, using the verification policies in |trust_policies|. On
368// success, returns OK, and updates |trust_ref|, |trust_result|,
369// |verified_chain|, and |chain_info| with the verification results. On
370// failure, no output parameters are modified.
371//
372// Note: An OK return does not mean that |cert_array| is trusted, merely that
373// verification was performed successfully.
374//
375// This function should only be called while the Mac Security Services lock is
376// held.
377int BuildAndEvaluateSecTrustRef(CFArrayRef cert_array,
378 CFArrayRef trust_policies,
379 int flags,
380 ScopedCFTypeRef<SecTrustRef>* trust_ref,
381 SecTrustResultType* trust_result,
382 ScopedCFTypeRef<CFArrayRef>* verified_chain,
383 CSSM_TP_APPLE_EVIDENCE_INFO** chain_info) {
384 SecTrustRef tmp_trust = NULL;
385 OSStatus status = SecTrustCreateWithCertificates(cert_array, trust_policies,
386 &tmp_trust);
387 if (status)
388 return NetErrorFromOSStatus(status);
389 ScopedCFTypeRef<SecTrustRef> scoped_tmp_trust(tmp_trust);
390
391 if (TestRootCerts::HasInstance()) {
392 status = TestRootCerts::GetInstance()->FixupSecTrustRef(tmp_trust);
393 if (status)
394 return NetErrorFromOSStatus(status);
395 }
396
397 CSSM_APPLE_TP_ACTION_DATA tp_action_data;
398 memset(&tp_action_data, 0, sizeof(tp_action_data));
399 tp_action_data.Version = CSSM_APPLE_TP_ACTION_VERSION;
400 // Allow CSSM to download any missing intermediate certificates if an
401 // authorityInfoAccess extension or issuerAltName extension is present.
402 tp_action_data.ActionFlags = CSSM_TP_ACTION_FETCH_CERT_FROM_NET |
403 CSSM_TP_ACTION_TRUST_SETTINGS;
404
405 // Note: For EV certificates, the Apple TP will handle setting these flags
406 // as part of EV evaluation.
407 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED) {
408 // Require a positive result from an OCSP responder or a CRL (or both)
409 // for every certificate in the chain. The Apple TP automatically
410 // excludes the self-signed root from this requirement. If a certificate
411 // is missing both a crlDistributionPoints extension and an
412 // authorityInfoAccess extension with an OCSP responder URL, then we
413 // will get a kSecTrustResultRecoverableTrustFailure back from
414 // SecTrustEvaluate(), with a
415 // CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK error code. In that case,
416 // we'll set our own result to include
417 // CERT_STATUS_NO_REVOCATION_MECHANISM. If one or both extensions are
418 // present, and a check fails (server unavailable, OCSP retry later,
419 // signature mismatch), then we'll set our own result to include
420 // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION.
421 tp_action_data.ActionFlags |= CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
422
423 // Note, even if revocation checking is disabled, SecTrustEvaluate() will
424 // modify the OCSP options so as to attempt OCSP checking if it believes a
425 // certificate may chain to an EV root. However, because network fetches
426 // are disabled in CreateTrustPolicies() when revocation checking is
427 // disabled, these will only go against the local cache.
428 }
429
430 CFDataRef action_data_ref =
431 CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
432 reinterpret_cast<UInt8*>(&tp_action_data),
433 sizeof(tp_action_data), kCFAllocatorNull);
434 if (!action_data_ref)
435 return ERR_OUT_OF_MEMORY;
436 ScopedCFTypeRef<CFDataRef> scoped_action_data_ref(action_data_ref);
437 status = SecTrustSetParameters(tmp_trust, CSSM_TP_ACTION_DEFAULT,
438 action_data_ref);
439 if (status)
440 return NetErrorFromOSStatus(status);
441
442 // Verify the certificate. A non-zero result from SecTrustGetResult()
443 // indicates that some fatal error occurred and the chain couldn't be
444 // processed, not that the chain contains no errors. We need to examine the
445 // output of SecTrustGetResult() to determine that.
446 SecTrustResultType tmp_trust_result;
447 status = SecTrustEvaluate(tmp_trust, &tmp_trust_result);
448 if (status)
449 return NetErrorFromOSStatus(status);
450 CFArrayRef tmp_verified_chain = NULL;
451 CSSM_TP_APPLE_EVIDENCE_INFO* tmp_chain_info;
452 status = SecTrustGetResult(tmp_trust, &tmp_trust_result, &tmp_verified_chain,
453 &tmp_chain_info);
454 if (status)
455 return NetErrorFromOSStatus(status);
456
457 trust_ref->swap(scoped_tmp_trust);
458 *trust_result = tmp_trust_result;
459 verified_chain->reset(tmp_verified_chain);
460 *chain_info = tmp_chain_info;
461
462 return OK;
463}
464
rsleevife8953712015-10-29 02:06:53465// Helper class for managing the set of OS X Known Roots. This is only safe
466// to initialize while the crypto::GetMacSecurityServicesLock() is held, due
467// to calling into Security.framework functions; however, once initialized,
468// it can be called at any time.
469// In practice, due to lazy initialization, it's best to just always guard
470// accesses with the lock.
471class OSXKnownRootHelper {
472 public:
473 // IsIssuedByKnownRoot returns true if the given chain is rooted at a root CA
474 // that we recognise as a standard root.
475 bool IsIssuedByKnownRoot(CFArrayRef chain) {
476 // If there are no known roots, then an API failure occurred. For safety,
477 // assume that all certificates are issued by known roots.
478 if (known_roots_.empty())
479 return true;
480
481 CFIndex n = CFArrayGetCount(chain);
482 if (n < 1)
483 return false;
484 SecCertificateRef root_ref = reinterpret_cast<SecCertificateRef>(
485 const_cast<void*>(CFArrayGetValueAtIndex(chain, n - 1)));
486 SHA256HashValue hash = X509Certificate::CalculateFingerprint256(root_ref);
487 return known_roots_.find(hash) != known_roots_.end();
488 }
489
490 private:
491 friend struct base::DefaultLazyInstanceTraits<OSXKnownRootHelper>;
492
493 OSXKnownRootHelper() {
494 CFArrayRef cert_array = NULL;
495 OSStatus rv = SecTrustSettingsCopyCertificates(
496 kSecTrustSettingsDomainSystem, &cert_array);
497 if (rv != noErr) {
498 LOG(ERROR) << "Unable to determine trusted roots; assuming all roots are "
499 << "trusted! Error " << rv;
500 return;
501 }
502 base::ScopedCFTypeRef<CFArrayRef> scoped_array(cert_array);
503 for (CFIndex i = 0, size = CFArrayGetCount(cert_array); i < size; ++i) {
504 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
505 const_cast<void*>(CFArrayGetValueAtIndex(cert_array, i)));
506 known_roots_.insert(X509Certificate::CalculateFingerprint256(cert));
507 }
508 }
509
510 ~OSXKnownRootHelper() {}
511
512 std::set<SHA256HashValue, SHA256HashValueLessThan> known_roots_;
513};
514
515base::LazyInstance<OSXKnownRootHelper>::Leaky g_known_roots =
516 LAZY_INSTANCE_INITIALIZER;
517
[email protected]62b23c22012-03-22 04:50:24518} // namespace
519
520CertVerifyProcMac::CertVerifyProcMac() {}
521
522CertVerifyProcMac::~CertVerifyProcMac() {}
523
[email protected]ef155122013-03-23 19:11:24524bool CertVerifyProcMac::SupportsAdditionalTrustAnchors() const {
525 return false;
526}
527
davidben15f57132015-04-27 18:08:36528bool CertVerifyProcMac::SupportsOCSPStapling() const {
rsleevib4bbc582015-10-22 00:08:12529 // TODO(rsleevi): Plumb an OCSP response into the Mac system library.
530 // https://crbug.com/430714
531 return false;
davidben15f57132015-04-27 18:08:36532}
533
[email protected]ef155122013-03-23 19:11:24534int CertVerifyProcMac::VerifyInternal(
535 X509Certificate* cert,
536 const std::string& hostname,
davidben15f57132015-04-27 18:08:36537 const std::string& ocsp_response,
[email protected]ef155122013-03-23 19:11:24538 int flags,
539 CRLSet* crl_set,
540 const CertificateList& additional_trust_anchors,
541 CertVerifyResult* verify_result) {
[email protected]62b23c22012-03-22 04:50:24542 ScopedCFTypeRef<CFArrayRef> trust_policies;
543 OSStatus status = CreateTrustPolicies(hostname, flags, &trust_policies);
544 if (status)
545 return NetErrorFromOSStatus(status);
546
547 // Create and configure a SecTrustRef, which takes our certificate(s)
548 // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an
549 // array of certificates, the first of which is the certificate we're
550 // verifying, and the subsequent (optional) certificates are used for
551 // chain building.
rsleevia52857b52015-02-06 00:59:32552 ScopedCFTypeRef<CFMutableArrayRef> cert_array(
553 cert->CreateOSCertChainForCert());
[email protected]62b23c22012-03-22 04:50:24554
[email protected]d6e8fe62012-10-03 05:46:45555 // Serialize all calls that may use the Keychain, to work around various
556 // issues in OS X 10.6+ with multi-threaded access to Security.framework.
557 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
[email protected]62b23c22012-03-22 04:50:24558
[email protected]339e17e2013-06-14 02:48:29559 ScopedCFTypeRef<SecTrustRef> trust_ref;
560 SecTrustResultType trust_result = kSecTrustResultDeny;
561 ScopedCFTypeRef<CFArrayRef> completed_chain;
562 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info = NULL;
rsleevia3fa5412015-02-04 21:37:15563 bool candidate_untrusted = true;
564 bool candidate_weak = false;
[email protected]62b23c22012-03-22 04:50:24565
rsleevia3fa5412015-02-04 21:37:15566 // OS X lacks proper path discovery; it will take the input certs and never
567 // backtrack the graph attempting to discover valid paths.
568 // This can create issues in some situations:
569 // - When OS X changes the trust store, there may be a chain
570 // A -> B -> C -> D
571 // where OS X trusts D (on some versions) and trusts C (on some versions).
572 // If a server supplies a chain A, B, C (cross-signed by D), then this chain
573 // will successfully validate on systems that trust D, but fail for systems
574 // that trust C. If the server supplies a chain of A -> B, then it forces
575 // all clients to fetch C (via AIA) if they trust D, and not all clients
576 // (notably, Firefox and Android) will do this, thus breaking them.
577 // An example of this is the Verizon Business Services root - GTE CyberTrust
578 // and Baltimore CyberTrust roots represent old and new roots that cause
579 // issues depending on which version of OS X being used.
580 //
581 // - A server may be (misconfigured) to send an expired intermediate
582 // certificate. On platforms with path discovery, the graph traversal
583 // will back up to immediately before this intermediate, and then
584 // attempt an AIA fetch or retrieval from local store. However, OS X
585 // does not do this, and thus prevents access. While this is ostensibly
586 // a server misconfiguration issue, the fact that it works on other
587 // platforms is a jarring inconsistency for users.
588 //
589 // - When OS X trusts both C and D (simultaneously), it's possible that the
590 // version of C signed by D is signed using a weak algorithm (e.g. SHA-1),
591 // while the version of C in the trust store's signature doesn't matter.
592 // Since a 'strong' chain exists, it would be desirable to prefer this
593 // chain.
594 //
595 // - A variant of the above example, it may be that the version of B sent by
596 // the server is signed using a weak algorithm, but the version of B
597 // present in the AIA of A is signed using a strong algorithm. Since a
598 // 'strong' chain exists, it would be desirable to prefer this chain.
599 //
600 // Because of this, the code below first attempts to validate the peer's
601 // identity using the supplied chain. If it is not trusted (e.g. the OS only
602 // trusts C, but the version of C signed by D was sent, and D is not trusted),
603 // or if it contains a weak chain, it will begin lopping off certificates
604 // from the end of the chain and attempting to verify. If a stronger, trusted
605 // chain is found, it is used, otherwise, the algorithm continues until only
606 // the peer's certificate remains.
607 //
608 // This does cause a performance hit for these users, but only in cases where
609 // OS X is building weaker chains than desired, or when it would otherwise
610 // fail the connection.
611 while (CFArrayGetCount(cert_array) > 0) {
612 ScopedCFTypeRef<SecTrustRef> temp_ref;
613 SecTrustResultType temp_trust_result = kSecTrustResultDeny;
614 ScopedCFTypeRef<CFArrayRef> temp_chain;
615 CSSM_TP_APPLE_EVIDENCE_INFO* temp_chain_info = NULL;
616
rsleevib4bbc582015-10-22 00:08:12617 int rv = BuildAndEvaluateSecTrustRef(cert_array, trust_policies, flags,
618 &temp_ref, &temp_trust_result,
619 &temp_chain, &temp_chain_info);
rsleevia3fa5412015-02-04 21:37:15620 if (rv != OK)
621 return rv;
622
rsleevia3fa5412015-02-04 21:37:15623 bool untrusted = (temp_trust_result != kSecTrustResultUnspecified &&
624 temp_trust_result != kSecTrustResultProceed);
davidbena6173cd82015-04-23 22:55:09625 bool weak_chain = false;
626 if (CFArrayGetCount(temp_chain) == 0) {
627 // If the chain is empty, it cannot be trusted or have recoverable
628 // errors.
629 DCHECK(untrusted);
630 DCHECK_NE(kSecTrustResultRecoverableTrustFailure, temp_trust_result);
631 } else {
632 CertVerifyResult temp_verify_result;
633 bool leaf_is_weak = false;
634 GetCertChainInfo(temp_chain, temp_chain_info, &temp_verify_result,
635 &leaf_is_weak);
636 weak_chain = !leaf_is_weak &&
637 (temp_verify_result.has_md2 || temp_verify_result.has_md4 ||
638 temp_verify_result.has_md5 || temp_verify_result.has_sha1);
639 }
rsleevia3fa5412015-02-04 21:37:15640 // Set the result to the current chain if:
641 // - This is the first verification attempt. This ensures that if
642 // everything is awful (e.g. it may just be an untrusted cert), that
643 // what is reported is exactly what was sent by the server
644 // - If the current chain is trusted, and the old chain was not trusted,
645 // then prefer this chain. This ensures that if there is at least a
646 // valid path to a trust anchor, it's preferred over reporting an error.
647 // - If the current chain is trusted, and the old chain is trusted, but
648 // the old chain contained weak algorithms while the current chain only
649 // contains strong algorithms, then prefer the current chain over the
650 // old chain.
651 //
652 // Note: If the leaf certificate itself is weak, then the only
653 // consideration is whether or not there is a trusted chain. That's
654 // because no amount of path discovery will fix a weak leaf.
655 if (!trust_ref || (!untrusted && (candidate_untrusted ||
656 (candidate_weak && !weak_chain)))) {
657 trust_ref = temp_ref;
658 trust_result = temp_trust_result;
659 completed_chain = temp_chain;
660 chain_info = temp_chain_info;
661
662 candidate_untrusted = untrusted;
663 candidate_weak = weak_chain;
664 }
665 // Short-circuit when a current, trusted chain is found.
666 if (!untrusted && !weak_chain)
667 break;
668 CFArrayRemoveValueAtIndex(cert_array, CFArrayGetCount(cert_array) - 1);
[email protected]62b23c22012-03-22 04:50:24669 }
670
[email protected]339e17e2013-06-14 02:48:29671 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED)
[email protected]62b23c22012-03-22 04:50:24672 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
673
[email protected]62b23c22012-03-22 04:50:24674 if (crl_set && !CheckRevocationWithCRLSet(completed_chain, crl_set))
675 verify_result->cert_status |= CERT_STATUS_REVOKED;
676
davidbena6173cd82015-04-23 22:55:09677 if (CFArrayGetCount(completed_chain) > 0) {
678 bool leaf_is_weak_unused = false;
679 GetCertChainInfo(completed_chain, chain_info, verify_result,
680 &leaf_is_weak_unused);
681 }
[email protected]62b23c22012-03-22 04:50:24682
[email protected]58484ca2012-05-29 21:56:34683 // As of Security Update 2012-002/OS X 10.7.4, when an RSA key < 1024 bits
684 // is encountered, CSSM returns CSSMERR_TP_VERIFY_ACTION_FAILED and adds
685 // CSSMERR_CSP_UNSUPPORTED_KEY_SIZE as a certificate status. Avoid mapping
686 // the CSSMERR_TP_VERIFY_ACTION_FAILED to CERT_STATUS_INVALID if the only
687 // error was due to an unsupported key size.
688 bool policy_failed = false;
[email protected]29444412014-01-04 02:02:09689 bool weak_key_or_signature_algorithm = false;
[email protected]58484ca2012-05-29 21:56:34690
[email protected]62b23c22012-03-22 04:50:24691 // Evaluate the results
692 OSStatus cssm_result;
693 switch (trust_result) {
694 case kSecTrustResultUnspecified:
695 case kSecTrustResultProceed:
696 // Certificate chain is valid and trusted ("unspecified" indicates that
697 // the user has not explicitly set a trust setting)
698 break;
699
[email protected]4abf5b4c2013-06-20 22:20:48700 // According to SecTrust.h, kSecTrustResultConfirm isn't returned on 10.5+,
701 // and it is marked deprecated in the 10.9 SDK.
[email protected]62b23c22012-03-22 04:50:24702 case kSecTrustResultDeny:
[email protected]4abf5b4c2013-06-20 22:20:48703 // Certificate chain is explicitly untrusted.
[email protected]62b23c22012-03-22 04:50:24704 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
705 break;
706
707 case kSecTrustResultRecoverableTrustFailure:
708 // Certificate chain has a failure that can be overridden by the user.
709 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
710 if (status)
711 return NetErrorFromOSStatus(status);
[email protected]58484ca2012-05-29 21:56:34712 if (cssm_result == CSSMERR_TP_VERIFY_ACTION_FAILED) {
713 policy_failed = true;
714 } else {
715 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
716 }
[email protected]62b23c22012-03-22 04:50:24717 // Walk the chain of error codes in the CSSM_TP_APPLE_EVIDENCE_INFO
718 // structure which can catch multiple errors from each certificate.
719 for (CFIndex index = 0, chain_count = CFArrayGetCount(completed_chain);
720 index < chain_count; ++index) {
721 if (chain_info[index].StatusBits & CSSM_CERT_STATUS_EXPIRED ||
722 chain_info[index].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET)
723 verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
724 if (!IsCertStatusError(verify_result->cert_status) &&
725 chain_info[index].NumStatusCodes == 0) {
726 LOG(WARNING) << "chain_info[" << index << "].NumStatusCodes is 0"
727 ", chain_info[" << index << "].StatusBits is "
728 << chain_info[index].StatusBits;
729 }
wtc69f8ea82015-06-04 00:08:13730 for (uint32_t status_code_index = 0;
[email protected]62b23c22012-03-22 04:50:24731 status_code_index < chain_info[index].NumStatusCodes;
732 ++status_code_index) {
[email protected]29444412014-01-04 02:02:09733 // As of OS X 10.9, attempting to verify a certificate chain that
734 // contains a weak signature algorithm (MD2, MD5) in an intermediate
735 // or leaf cert will be treated as a (recoverable) policy validation
736 // failure, with the status code CSSMERR_TP_INVALID_CERTIFICATE
737 // added to the Status Codes. Don't treat this code as an invalid
738 // certificate; instead, map it to a weak key. Any truly invalid
739 // certificates will have the major error (cssm_result) set to
740 // CSSMERR_TP_INVALID_CERTIFICATE, rather than
741 // CSSMERR_TP_VERIFY_ACTION_FAILED.
742 CertStatus mapped_status = 0;
743 if (policy_failed &&
744 chain_info[index].StatusCodes[status_code_index] ==
745 CSSMERR_TP_INVALID_CERTIFICATE) {
746 mapped_status = CERT_STATUS_WEAK_SIGNATURE_ALGORITHM;
747 weak_key_or_signature_algorithm = true;
748 } else {
749 mapped_status = CertStatusFromOSStatus(
750 chain_info[index].StatusCodes[status_code_index]);
751 if (mapped_status == CERT_STATUS_WEAK_KEY)
752 weak_key_or_signature_algorithm = true;
753 }
[email protected]58484ca2012-05-29 21:56:34754 verify_result->cert_status |= mapped_status;
[email protected]62b23c22012-03-22 04:50:24755 }
756 }
[email protected]29444412014-01-04 02:02:09757 if (policy_failed && !weak_key_or_signature_algorithm) {
[email protected]58484ca2012-05-29 21:56:34758 // If CSSMERR_TP_VERIFY_ACTION_FAILED wasn't returned due to a weak
759 // key, map it back to an appropriate error code.
760 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
761 }
[email protected]62b23c22012-03-22 04:50:24762 if (!IsCertStatusError(verify_result->cert_status)) {
763 LOG(ERROR) << "cssm_result=" << cssm_result;
764 verify_result->cert_status |= CERT_STATUS_INVALID;
765 NOTREACHED();
766 }
767 break;
768
769 default:
770 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
771 if (status)
772 return NetErrorFromOSStatus(status);
773 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
774 if (!IsCertStatusError(verify_result->cert_status)) {
775 LOG(WARNING) << "trust_result=" << trust_result;
776 verify_result->cert_status |= CERT_STATUS_INVALID;
777 }
778 break;
779 }
780
781 // Perform hostname verification independent of SecTrustEvaluate. In order to
782 // do so, mask off any reported name errors first.
783 verify_result->cert_status &= ~CERT_STATUS_COMMON_NAME_INVALID;
[email protected]cc33eb72013-10-24 08:14:15784 if (!cert->VerifyNameMatch(hostname,
785 &verify_result->common_name_fallback_used)) {
[email protected]62b23c22012-03-22 04:50:24786 verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID;
[email protected]cc33eb72013-10-24 08:14:15787 }
[email protected]62b23c22012-03-22 04:50:24788
789 // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
790 // compatible with Windows, which in turn implements this behavior to be
791 // compatible with WinHTTP, which doesn't report this error (bug 3004).
792 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
793
[email protected]8b4a61a2012-11-28 22:57:20794 AppendPublicKeyHashes(completed_chain, &verify_result->public_key_hashes);
rsleevife8953712015-10-29 02:06:53795 verify_result->is_issued_by_known_root =
796 g_known_roots.Get().IsIssuedByKnownRoot(completed_chain);
[email protected]8b4a61a2012-11-28 22:57:20797
[email protected]62b23c22012-03-22 04:50:24798 if (IsCertStatusError(verify_result->cert_status))
799 return MapCertStatusToNetError(verify_result->cert_status);
800
[email protected]8738e0d72012-08-23 02:00:47801 if (flags & CertVerifier::VERIFY_EV_CERT) {
[email protected]62b23c22012-03-22 04:50:24802 // Determine the certificate's EV status using SecTrustCopyExtendedResult(),
[email protected]c97ac4f2013-04-30 02:31:36803 // which is an internal/private API function added in OS X 10.5.7.
[email protected]62b23c22012-03-22 04:50:24804 // Note: "ExtendedResult" means extended validation results.
805 CFBundleRef bundle =
806 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
807 if (bundle) {
808 SecTrustCopyExtendedResultFuncPtr copy_extended_result =
809 reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>(
810 CFBundleGetFunctionPointerForName(bundle,
811 CFSTR("SecTrustCopyExtendedResult")));
812 if (copy_extended_result) {
813 CFDictionaryRef ev_dict_temp = NULL;
814 status = copy_extended_result(trust_ref, &ev_dict_temp);
815 ScopedCFTypeRef<CFDictionaryRef> ev_dict(ev_dict_temp);
816 ev_dict_temp = NULL;
817 if (status == noErr && ev_dict) {
818 // In 10.7.3, SecTrustCopyExtendedResult returns noErr and populates
819 // ev_dict even for non-EV certificates, but only EV certificates
820 // will cause ev_dict to contain kSecEVOrganizationName. In previous
821 // releases, SecTrustCopyExtendedResult would only return noErr and
822 // populate ev_dict for EV certificates, but would always include
823 // kSecEVOrganizationName in that case, so checking for this key is
824 // appropriate for all known versions of SecTrustCopyExtendedResult.
825 // The actual organization name is unneeded here and can be accessed
826 // through other means. All that matters here is the OS' conception
827 // of whether or not the certificate is EV.
828 if (CFDictionaryContainsKey(ev_dict,
829 kSecEVOrganizationName)) {
830 verify_result->cert_status |= CERT_STATUS_IS_EV;
[email protected]8738e0d72012-08-23 02:00:47831 if (flags & CertVerifier::VERIFY_REV_CHECKING_ENABLED_EV_ONLY)
[email protected]b6f2de32012-08-17 04:35:08832 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
[email protected]62b23c22012-03-22 04:50:24833 }
834 }
835 }
836 }
837 }
838
[email protected]62b23c22012-03-22 04:50:24839 return OK;
840}
841
842} // namespace net
erikchenbedc2612016-03-02 02:52:08843
844#pragma clang diagnostic pop // "-Wdeprecated-declarations"