1// 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 5#include "net/cert/test_root_certs.h" 6 7#include <Security/Security.h> 8 9#include "base/logging.h" 10#include "base/mac/mac_util.h" 11#include "base/mac/scoped_cftyperef.h" 12#include "net/cert/x509_certificate.h" 13 14namespace net { 15 16namespace { 17 18typedef OSStatus (*SecTrustSetAnchorCertificatesOnlyFuncPtr)(SecTrustRef, 19 Boolean); 20 21Boolean OurSecCertificateEqual(const void* value1, const void* value2) { 22 if (CFGetTypeID(value1) != SecCertificateGetTypeID() || 23 CFGetTypeID(value2) != SecCertificateGetTypeID()) 24 return CFEqual(value1, value2); 25 return X509Certificate::IsSameOSCert( 26 reinterpret_cast<SecCertificateRef>(const_cast<void*>(value1)), 27 reinterpret_cast<SecCertificateRef>(const_cast<void*>(value2))); 28} 29 30const void* RetainWrapper(CFAllocatorRef unused, const void* value) { 31 return CFRetain(value); 32} 33 34void ReleaseWrapper(CFAllocatorRef unused, const void* value) { 35 CFRelease(value); 36} 37 38// CFEqual prior to 10.6 only performed pointer checks on SecCertificateRefs, 39// rather than checking if they were the same (logical) certificate, so a 40// custom structure is used for the array callbacks. 41const CFArrayCallBacks kCertArrayCallbacks = { 42 0, // version 43 RetainWrapper, 44 ReleaseWrapper, 45 CFCopyDescription, 46 OurSecCertificateEqual, 47}; 48 49} // namespace 50 51bool TestRootCerts::Add(X509Certificate* certificate) { 52 if (CFArrayContainsValue(temporary_roots_, 53 CFRangeMake(0, CFArrayGetCount(temporary_roots_)), 54 certificate->os_cert_handle())) 55 return true; 56 CFArrayAppendValue(temporary_roots_, certificate->os_cert_handle()); 57 return true; 58} 59 60void TestRootCerts::Clear() { 61 CFArrayRemoveAllValues(temporary_roots_); 62} 63 64bool TestRootCerts::IsEmpty() const { 65 return CFArrayGetCount(temporary_roots_) == 0; 66} 67 68OSStatus TestRootCerts::FixupSecTrustRef(SecTrustRef trust_ref) const { 69 if (IsEmpty()) 70 return noErr; 71 72 // Despite SecTrustSetAnchorCertificatesOnly existing in OS X 10.6, and 73 // being documented as available, it is not actually implemented. On 10.7+, 74 // however, it always works. 75 if (base::mac::IsOSLionOrLater()) { 76 OSStatus status = SecTrustSetAnchorCertificates(trust_ref, 77 temporary_roots_); 78 if (status) 79 return status; 80 return SecTrustSetAnchorCertificatesOnly(trust_ref, !allow_system_trust_); 81 } 82 83 if (!allow_system_trust_) { 84 // Avoid any copying if system roots are not to be trusted. This acts as 85 // an exclusive list on 10.6, replacing the built-ins. 86 return SecTrustSetAnchorCertificates(trust_ref, temporary_roots_); 87 } 88 89 // Otherwise, both system trust and temporary_roots_ must be trusted. 90 // Emulate the functionality of SecTrustSetAnchorCertificatesOnly by 91 // creating a copy of the system roots and merging with temporary_roots_. 92 CFArrayRef system_roots = NULL; 93 OSStatus status = SecTrustCopyAnchorCertificates(&system_roots); 94 if (status) 95 return status; 96 97 base::ScopedCFTypeRef<CFArrayRef> scoped_system_roots(system_roots); 98 base::ScopedCFTypeRef<CFMutableArrayRef> scoped_roots( 99 CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, scoped_system_roots)); 100 CFArrayAppendArray(scoped_roots, temporary_roots_, 101 CFRangeMake(0, CFArrayGetCount(temporary_roots_))); 102 return SecTrustSetAnchorCertificates(trust_ref, scoped_roots); 103} 104 105void TestRootCerts::SetAllowSystemTrust(bool allow_system_trust) { 106 allow_system_trust_ = allow_system_trust; 107} 108 109TestRootCerts::~TestRootCerts() {} 110 111void TestRootCerts::Init() { 112 temporary_roots_.reset(CFArrayCreateMutable(kCFAllocatorDefault, 0, 113 &kCertArrayCallbacks)); 114 allow_system_trust_ = true; 115} 116 117} // namespace net 118