1a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "net/ssl/client_cert_store_mac.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <CommonCrypto/CommonDigest.h>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <CoreFoundation/CFArray.h>
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <CoreServices/CoreServices.h>
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <Security/SecBase.h>
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <Security/Security.h>
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <algorithm>
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <string>
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "base/callback.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/mac/mac_logging.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/mac/scoped_cftyperef.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/synchronization/lock.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "crypto/mac_security_services_lock.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/host_port_pair.h"
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/cert/x509_util.h"
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/cert/x509_util_mac.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
27eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing base::ScopedCFTypeRef;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace net {
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Gets the issuer for a given cert, starting with the cert itself and
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// including the intermediate and finally root certificates (if any).
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// This function calls SecTrust but doesn't actually pay attention to the trust
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// result: it shouldn't be used to determine trust, just to traverse the chain.
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Caller is responsible for releasing the value stored into *out_cert_chain.
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)OSStatus CopyCertChain(SecCertificateRef cert_handle,
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                       CFArrayRef* out_cert_chain) {
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(cert_handle);
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(out_cert_chain);
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Create an SSL policy ref configured for client cert evaluation.
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SecPolicyRef ssl_policy;
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  OSStatus result = x509_util::CreateSSLClientPolicy(&ssl_policy);
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (result)
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return result;
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ScopedCFTypeRef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Create a SecTrustRef.
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ScopedCFTypeRef<CFArrayRef> input_certs(CFArrayCreate(
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      NULL, const_cast<const void**>(reinterpret_cast<void**>(&cert_handle)),
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      1, &kCFTypeArrayCallBacks));
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SecTrustRef trust_ref = NULL;
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  {
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::AutoLock lock(crypto::GetMacSecurityServicesLock());
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    result = SecTrustCreateWithCertificates(input_certs, ssl_policy,
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                            &trust_ref);
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (result)
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return result;
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ScopedCFTypeRef<SecTrustRef> trust(trust_ref);
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Evaluate trust, which creates the cert chain.
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  SecTrustResultType status;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  {
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::AutoLock lock(crypto::GetMacSecurityServicesLock());
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    result = SecTrustEvaluate(trust, &status);
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (result)
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return result;
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  {
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::AutoLock lock(crypto::GetMacSecurityServicesLock());
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    result = SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return result;
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Returns true if |*cert| is issued by an authority in |valid_issuers|
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// according to Keychain Services, rather than using |cert|'s intermediate
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// certificates. If it is, |*cert| is updated to point to the completed
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// certificate
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                          scoped_refptr<X509Certificate>* cert) {
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(cert);
877d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  DCHECK(cert->get());
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  X509Certificate::OSCertHandle cert_handle = (*cert)->os_cert_handle();
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CFArrayRef cert_chain = NULL;
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  OSStatus result = CopyCertChain(cert_handle, &cert_chain);
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (result) {
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    OSSTATUS_LOG(ERROR, result) << "CopyCertChain error";
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!cert_chain)
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  X509Certificate::OSCertHandles intermediates;
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)       i < chain_count; ++i) {
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    intermediates.push_back(cert);
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_refptr<X509Certificate> new_cert(X509Certificate::CreateFromHandle(
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      cert_handle, intermediates));
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CFRelease(cert_chain);  // Also frees |intermediates|.
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!new_cert->IsIssuedByEncoded(valid_issuers))
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  cert->swap(new_cert);
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return true;
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Examines the certificates in |preferred_cert| and |regular_certs| to find
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// all certificates that match the client certificate request in |request|,
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// storing the matching certificates in |selected_certs|.
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// If |query_keychain| is true, Keychain Services will be queried to construct
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// full certificate chains. If it is false, only the the certificates and their
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// intermediates (available via X509Certificate::GetIntermediateCertificates())
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// will be considered.
1261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)void GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert,
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        const CertificateList& regular_certs,
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        const SSLCertRequestInfo& request,
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        bool query_keychain,
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        CertificateList* selected_certs) {
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CertificateList preliminary_list;
1327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (preferred_cert.get())
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    preliminary_list.push_back(preferred_cert);
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  preliminary_list.insert(preliminary_list.end(), regular_certs.begin(),
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          regular_certs.end());
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  selected_certs->clear();
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < preliminary_list.size(); ++i) {
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_refptr<X509Certificate>& cert = preliminary_list[i];
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (cert->HasExpired() || !cert->SupportsSSLClientAuth())
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Skip duplicates (a cert may be in multiple keychains).
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const SHA1HashValue& fingerprint = cert->fingerprint();
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    size_t pos;
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (pos = 0; pos < selected_certs->size(); ++pos) {
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if ((*selected_certs)[pos]->fingerprint().Equals(fingerprint))
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (pos < selected_certs->size())
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check if the certificate issuer is allowed by the server.
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (request.cert_authorities.empty() ||
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        cert->IsIssuedByEncoded(request.cert_authorities) ||
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        (query_keychain &&
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         IsIssuedByInKeychain(request.cert_authorities, &cert))) {
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      selected_certs->push_back(cert);
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Preferred cert should appear first in the ui, so exclude it from the
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // sorting.
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CertificateList::iterator sort_begin = selected_certs->begin();
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CertificateList::iterator sort_end = selected_certs->end();
1667d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (preferred_cert.get() && sort_begin != sort_end &&
1677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      sort_begin->get() == preferred_cert.get()) {
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ++sort_begin;
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sort(sort_begin, sort_end, x509_util::ClientCertSorter());
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ClientCertStoreMac::ClientCertStoreMac() {}
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)ClientCertStoreMac::~ClientCertStoreMac() {}
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void ClientCertStoreMac::GetClientCerts(const SSLCertRequestInfo& request,
1801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                         CertificateList* selected_certs,
1811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                         const base::Closure& callback) {
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string server_domain = request.host_and_port.host();
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedCFTypeRef<SecIdentityRef> preferred_identity;
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!server_domain.empty()) {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // See if there's an identity preference for this domain:
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ScopedCFTypeRef<CFStringRef> domain_str(
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::SysUTF8ToCFStringRef("https://" + server_domain));
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SecIdentityRef identity = NULL;
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // While SecIdentityCopyPreferences appears to take a list of CA issuers
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // to restrict the identity search to, within Security.framework the
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // argument is ignored and filtering unimplemented. See
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // SecIdentity.cpp in libsecurity_keychain, specifically
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // _SecIdentityCopyPreferenceMatchingName().
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    {
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::AutoLock lock(crypto::GetMacSecurityServicesLock());
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        preferred_identity.reset(identity);
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Now enumerate the identities in the available keychains.
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<X509Certificate> preferred_cert = NULL;
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CertificateList regular_certs;
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SecIdentitySearchRef search = NULL;
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OSStatus err;
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  {
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::AutoLock lock(crypto::GetMacSecurityServicesLock());
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (err) {
2131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    selected_certs->clear();
2141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    callback.Run();
2151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return;
2161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  while (!err) {
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SecIdentityRef identity = NULL;
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    {
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::AutoLock lock(crypto::GetMacSecurityServicesLock());
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      err = SecIdentitySearchCopyNext(search, &identity);
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (err)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ScopedCFTypeRef<SecIdentityRef> scoped_identity(identity);
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SecCertificateRef cert_handle;
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    err = SecIdentityCopyCertificate(identity, &cert_handle);
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (err != noErr)
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_refptr<X509Certificate> cert(
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        X509Certificate::CreateFromHandle(cert_handle,
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                          X509Certificate::OSCertHandles()));
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (preferred_identity && CFEqual(preferred_identity, identity)) {
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Only one certificate should match.
2407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      DCHECK(!preferred_cert.get());
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      preferred_cert = cert;
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      regular_certs.push_back(cert);
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (err != errSecItemNotFound) {
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error";
2491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    selected_certs->clear();
2501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    callback.Run();
2511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return;
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  GetClientCertsImpl(preferred_cert, regular_certs, request, true,
2551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                     selected_certs);
2561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  callback.Run();
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
259a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool ClientCertStoreMac::SelectClientCertsForTesting(
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const CertificateList& input_certs,
261c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const SSLCertRequestInfo& request,
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    CertificateList* selected_certs) {
2631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  GetClientCertsImpl(NULL, input_certs, request, false, selected_certs);
2641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return true;
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
267a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting(
268c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const scoped_refptr<X509Certificate>& preferred_cert,
269c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const CertificateList& regular_certs,
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const SSLCertRequestInfo& request,
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    CertificateList* selected_certs) {
2721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  GetClientCertsImpl(
2731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      preferred_cert, regular_certs, request, false, selected_certs);
2741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return true;
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace net
278