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
13namespace net {
14
15namespace {
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.
20enum {
21  errSecUnimplemented = -4,
22};
23#endif
24
25typedef OSStatus (*SecTrustSetAnchorCertificatesOnlyFuncPtr)(SecTrustRef,
26                                                             Boolean);
27
28Boolean 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
37const void* RetainWrapper(CFAllocatorRef unused, const void* value) {
38  return CFRetain(value);
39}
40
41void 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.
48const CFArrayCallBacks kCertArrayCallbacks = {
49  0,  // version
50  RetainWrapper,
51  ReleaseWrapper,
52  CFCopyDescription,
53  OurSecCertificateEqual,
54};
55
56}  // namespace
57
58bool 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
67void TestRootCerts::Clear() {
68  CFArrayRemoveAllValues(temporary_roots_);
69}
70
71bool TestRootCerts::IsEmpty() const {
72  return CFArrayGetCount(temporary_roots_) == 0;
73}
74
75OSStatus 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
128TestRootCerts::~TestRootCerts() {}
129
130void TestRootCerts::Init() {
131  temporary_roots_.reset(CFArrayCreateMutable(kCFAllocatorDefault, 0,
132                                              &kCertArrayCallbacks));
133}
134
135}  // namespace net
136