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