1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/base/test_root_certs.h" 6 7 #include <Security/Security.h> 8 9 #include "base/logging.h" 10 #include "base/mac/scoped_cftyperef.h" 11 #include "net/base/x509_certificate.h" 12 13 namespace net { 14 15 namespace { 16 17 #if !defined(MAC_OS_X_VERSION_10_6) || \ 18 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 19 // Declared in <Security/SecBase.h> of the 10.6 SDK. 20 enum { 21 errSecUnimplemented = -4, 22 }; 23 #endif 24 25 typedef OSStatus (*SecTrustSetAnchorCertificatesOnlyFuncPtr)(SecTrustRef, 26 Boolean); 27 28 Boolean OurSecCertificateEqual(const void* value1, const void* value2) { 29 if (CFGetTypeID(value1) != SecCertificateGetTypeID() || 30 CFGetTypeID(value2) != SecCertificateGetTypeID()) 31 return CFEqual(value1, value2); 32 return X509Certificate::IsSameOSCert( 33 reinterpret_cast<SecCertificateRef>(const_cast<void*>(value1)), 34 reinterpret_cast<SecCertificateRef>(const_cast<void*>(value2))); 35 } 36 37 const void* RetainWrapper(CFAllocatorRef unused, const void* value) { 38 return CFRetain(value); 39 } 40 41 void ReleaseWrapper(CFAllocatorRef unused, const void* value) { 42 CFRelease(value); 43 } 44 45 // CFEqual prior to 10.6 only performed pointer checks on SecCertificateRefs, 46 // rather than checking if they were the same (logical) certificate, so a 47 // custom structure is used for the array callbacks. 48 const CFArrayCallBacks kCertArrayCallbacks = { 49 0, // version 50 RetainWrapper, 51 ReleaseWrapper, 52 CFCopyDescription, 53 OurSecCertificateEqual, 54 }; 55 56 } // namespace 57 58 bool TestRootCerts::Add(X509Certificate* certificate) { 59 if (CFArrayContainsValue(temporary_roots_, 60 CFRangeMake(0, CFArrayGetCount(temporary_roots_)), 61 certificate->os_cert_handle())) 62 return true; 63 CFArrayAppendValue(temporary_roots_, certificate->os_cert_handle()); 64 return true; 65 } 66 67 void TestRootCerts::Clear() { 68 CFArrayRemoveAllValues(temporary_roots_); 69 } 70 71 bool TestRootCerts::IsEmpty() const { 72 return CFArrayGetCount(temporary_roots_) == 0; 73 } 74 75 OSStatus TestRootCerts::FixupSecTrustRef(SecTrustRef trust_ref) const { 76 if (IsEmpty()) 77 return noErr; 78 79 CFBundleRef bundle = 80 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); 81 SecTrustSetAnchorCertificatesOnlyFuncPtr set_anchor_certificates_only = NULL; 82 if (bundle) { 83 set_anchor_certificates_only = 84 reinterpret_cast<SecTrustSetAnchorCertificatesOnlyFuncPtr>( 85 CFBundleGetFunctionPointerForName(bundle, 86 CFSTR("SecTrustSetAnchorCertificatesOnly"))); 87 } 88 89 OSStatus status = noErr; 90 if (set_anchor_certificates_only) { 91 // OS X 10.6 includes a function where the system trusts can be 92 // preserved while appending application trusts. This is preferable, 93 // because it preserves any user trust settings (explicit distrust), 94 // which the naive copy in 10.5 does not. Unfortunately, though the 95 // function pointer may be available, it is not always implemented. If it 96 // returns errSecUnimplemented, fall through to the 10.5 behaviour. 97 status = SecTrustSetAnchorCertificates(trust_ref, temporary_roots_); 98 if (status) 99 return status; 100 status = set_anchor_certificates_only(trust_ref, false); 101 if (status != errSecUnimplemented) 102 return status; 103 104 // Restore the original settings before falling back. 105 status = SecTrustSetAnchorCertificates(trust_ref, NULL); 106 if (status) 107 return status; 108 } 109 110 // On 10.5, the system certificates have to be copied and merged into 111 // the application trusts, and may override any user trust settings. 112 CFArrayRef system_roots = NULL; 113 status = SecTrustCopyAnchorCertificates(&system_roots); 114 if (status) 115 return status; 116 117 base::mac::ScopedCFTypeRef<CFArrayRef> scoped_system_roots(system_roots); 118 base::mac::ScopedCFTypeRef<CFMutableArrayRef> scoped_roots( 119 CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, 120 scoped_system_roots)); 121 DCHECK(scoped_roots.get()); 122 123 CFArrayAppendArray(scoped_roots, temporary_roots_, 124 CFRangeMake(0, CFArrayGetCount(temporary_roots_))); 125 return SecTrustSetAnchorCertificates(trust_ref, scoped_roots); 126 } 127 128 TestRootCerts::~TestRootCerts() {} 129 130 void TestRootCerts::Init() { 131 temporary_roots_.reset(CFArrayCreateMutable(kCFAllocatorDefault, 0, 132 &kCertArrayCallbacks)); 133 } 134 135 } // namespace net 136