libyuv r905 with yuv off by 1 fix for valgrind overread
[chromium-blink-merge.git] / net / ssl / client_cert_store_impl_mac.cc
blob25def0f7c313aaa7a347ae928665467e50288e77
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.
5 #include "net/ssl/client_cert_store_impl.h"
7 #include <CommonCrypto/CommonDigest.h>
8 #include <CoreFoundation/CFArray.h>
9 #include <CoreServices/CoreServices.h>
10 #include <Security/SecBase.h>
11 #include <Security/Security.h>
13 #include <algorithm>
14 #include <string>
16 #include "base/callback.h"
17 #include "base/logging.h"
18 #include "base/mac/mac_logging.h"
19 #include "base/mac/scoped_cftyperef.h"
20 #include "base/strings/sys_string_conversions.h"
21 #include "base/synchronization/lock.h"
22 #include "crypto/mac_security_services_lock.h"
23 #include "net/base/host_port_pair.h"
24 #include "net/cert/x509_util.h"
25 #include "net/cert/x509_util_mac.h"
27 using base::ScopedCFTypeRef;
29 namespace net {
31 namespace {
33 // Gets the issuer for a given cert, starting with the cert itself and
34 // including the intermediate and finally root certificates (if any).
35 // This function calls SecTrust but doesn't actually pay attention to the trust
36 // result: it shouldn't be used to determine trust, just to traverse the chain.
37 // Caller is responsible for releasing the value stored into *out_cert_chain.
38 OSStatus CopyCertChain(SecCertificateRef cert_handle,
39 CFArrayRef* out_cert_chain) {
40 DCHECK(cert_handle);
41 DCHECK(out_cert_chain);
43 // Create an SSL policy ref configured for client cert evaluation.
44 SecPolicyRef ssl_policy;
45 OSStatus result = x509_util::CreateSSLClientPolicy(&ssl_policy);
46 if (result)
47 return result;
48 ScopedCFTypeRef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
50 // Create a SecTrustRef.
51 ScopedCFTypeRef<CFArrayRef> input_certs(CFArrayCreate(
52 NULL, const_cast<const void**>(reinterpret_cast<void**>(&cert_handle)),
53 1, &kCFTypeArrayCallBacks));
54 SecTrustRef trust_ref = NULL;
56 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
57 result = SecTrustCreateWithCertificates(input_certs, ssl_policy,
58 &trust_ref);
60 if (result)
61 return result;
62 ScopedCFTypeRef<SecTrustRef> trust(trust_ref);
64 // Evaluate trust, which creates the cert chain.
65 SecTrustResultType status;
66 CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
68 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
69 result = SecTrustEvaluate(trust, &status);
71 if (result)
72 return result;
74 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
75 result = SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
77 return result;
80 // Returns true if |*cert| is issued by an authority in |valid_issuers|
81 // according to Keychain Services, rather than using |cert|'s intermediate
82 // certificates. If it is, |*cert| is updated to point to the completed
83 // certificate
84 bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
85 scoped_refptr<X509Certificate>* cert) {
86 DCHECK(cert);
87 DCHECK(cert->get());
89 X509Certificate::OSCertHandle cert_handle = (*cert)->os_cert_handle();
90 CFArrayRef cert_chain = NULL;
91 OSStatus result = CopyCertChain(cert_handle, &cert_chain);
92 if (result) {
93 OSSTATUS_LOG(ERROR, result) << "CopyCertChain error";
94 return false;
97 if (!cert_chain)
98 return false;
100 X509Certificate::OSCertHandles intermediates;
101 for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
102 i < chain_count; ++i) {
103 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
104 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
105 intermediates.push_back(cert);
108 scoped_refptr<X509Certificate> new_cert(X509Certificate::CreateFromHandle(
109 cert_handle, intermediates));
110 CFRelease(cert_chain); // Also frees |intermediates|.
112 if (!new_cert->IsIssuedByEncoded(valid_issuers))
113 return false;
115 cert->swap(new_cert);
116 return true;
119 // Examines the certificates in |preferred_cert| and |regular_certs| to find
120 // all certificates that match the client certificate request in |request|,
121 // storing the matching certificates in |selected_certs|.
122 // If |query_keychain| is true, Keychain Services will be queried to construct
123 // full certificate chains. If it is false, only the the certificates and their
124 // intermediates (available via X509Certificate::GetIntermediateCertificates())
125 // will be considered.
126 void GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert,
127 const CertificateList& regular_certs,
128 const SSLCertRequestInfo& request,
129 bool query_keychain,
130 CertificateList* selected_certs) {
131 CertificateList preliminary_list;
132 if (preferred_cert.get())
133 preliminary_list.push_back(preferred_cert);
134 preliminary_list.insert(preliminary_list.end(), regular_certs.begin(),
135 regular_certs.end());
137 selected_certs->clear();
138 for (size_t i = 0; i < preliminary_list.size(); ++i) {
139 scoped_refptr<X509Certificate>& cert = preliminary_list[i];
140 if (cert->HasExpired() || !cert->SupportsSSLClientAuth())
141 continue;
143 // Skip duplicates (a cert may be in multiple keychains).
144 const SHA1HashValue& fingerprint = cert->fingerprint();
145 size_t pos;
146 for (pos = 0; pos < selected_certs->size(); ++pos) {
147 if ((*selected_certs)[pos]->fingerprint().Equals(fingerprint))
148 break;
150 if (pos < selected_certs->size())
151 continue;
153 // Check if the certificate issuer is allowed by the server.
154 if (request.cert_authorities.empty() ||
155 cert->IsIssuedByEncoded(request.cert_authorities) ||
156 (query_keychain &&
157 IsIssuedByInKeychain(request.cert_authorities, &cert))) {
158 selected_certs->push_back(cert);
162 // Preferred cert should appear first in the ui, so exclude it from the
163 // sorting.
164 CertificateList::iterator sort_begin = selected_certs->begin();
165 CertificateList::iterator sort_end = selected_certs->end();
166 if (preferred_cert.get() && sort_begin != sort_end &&
167 sort_begin->get() == preferred_cert.get()) {
168 ++sort_begin;
170 sort(sort_begin, sort_end, x509_util::ClientCertSorter());
173 } // namespace
175 ClientCertStoreImpl::ClientCertStoreImpl() {}
177 ClientCertStoreImpl::~ClientCertStoreImpl() {}
179 void ClientCertStoreImpl::GetClientCerts(const SSLCertRequestInfo& request,
180 CertificateList* selected_certs,
181 const base::Closure& callback) {
182 std::string server_domain =
183 HostPortPair::FromString(request.host_and_port).host();
185 ScopedCFTypeRef<SecIdentityRef> preferred_identity;
186 if (!server_domain.empty()) {
187 // See if there's an identity preference for this domain:
188 ScopedCFTypeRef<CFStringRef> domain_str(
189 base::SysUTF8ToCFStringRef("https://" + server_domain));
190 SecIdentityRef identity = NULL;
191 // While SecIdentityCopyPreferences appears to take a list of CA issuers
192 // to restrict the identity search to, within Security.framework the
193 // argument is ignored and filtering unimplemented. See
194 // SecIdentity.cpp in libsecurity_keychain, specifically
195 // _SecIdentityCopyPreferenceMatchingName().
197 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
198 if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr)
199 preferred_identity.reset(identity);
203 // Now enumerate the identities in the available keychains.
204 scoped_refptr<X509Certificate> preferred_cert = NULL;
205 CertificateList regular_certs;
207 SecIdentitySearchRef search = NULL;
208 OSStatus err;
210 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
211 err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
213 if (err) {
214 selected_certs->clear();
215 callback.Run();
216 return;
218 ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
219 while (!err) {
220 SecIdentityRef identity = NULL;
222 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
223 err = SecIdentitySearchCopyNext(search, &identity);
225 if (err)
226 break;
227 ScopedCFTypeRef<SecIdentityRef> scoped_identity(identity);
229 SecCertificateRef cert_handle;
230 err = SecIdentityCopyCertificate(identity, &cert_handle);
231 if (err != noErr)
232 continue;
233 ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);
235 scoped_refptr<X509Certificate> cert(
236 X509Certificate::CreateFromHandle(cert_handle,
237 X509Certificate::OSCertHandles()));
239 if (preferred_identity && CFEqual(preferred_identity, identity)) {
240 // Only one certificate should match.
241 DCHECK(!preferred_cert.get());
242 preferred_cert = cert;
243 } else {
244 regular_certs.push_back(cert);
248 if (err != errSecItemNotFound) {
249 OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error";
250 selected_certs->clear();
251 callback.Run();
252 return;
255 GetClientCertsImpl(preferred_cert, regular_certs, request, true,
256 selected_certs);
257 callback.Run();
260 bool ClientCertStoreImpl::SelectClientCertsForTesting(
261 const CertificateList& input_certs,
262 const SSLCertRequestInfo& request,
263 CertificateList* selected_certs) {
264 GetClientCertsImpl(NULL, input_certs, request, false, selected_certs);
265 return true;
268 #if !defined(OS_IOS)
269 bool ClientCertStoreImpl::SelectClientCertsGivenPreferredForTesting(
270 const scoped_refptr<X509Certificate>& preferred_cert,
271 const CertificateList& regular_certs,
272 const SSLCertRequestInfo& request,
273 CertificateList* selected_certs) {
274 GetClientCertsImpl(
275 preferred_cert, regular_certs, request, false, selected_certs);
276 return true;
278 #endif
280 } // namespace net