This source file includes following definitions.
- CopyCertChain
- IsIssuedByInKeychain
- GetClientCertsImpl
- GetClientCerts
- SelectClientCertsForTesting
- SelectClientCertsGivenPreferredForTesting
#include "net/ssl/client_cert_store_mac.h"
#include <CommonCrypto/CommonDigest.h>
#include <CoreFoundation/CFArray.h>
#include <CoreServices/CoreServices.h>
#include <Security/SecBase.h>
#include <Security/Security.h>
#include <algorithm>
#include <string>
#include "base/callback.h"
#include "base/logging.h"
#include "base/mac/mac_logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/strings/sys_string_conversions.h"
#include "base/synchronization/lock.h"
#include "crypto/mac_security_services_lock.h"
#include "net/base/host_port_pair.h"
#include "net/cert/x509_util.h"
#include "net/cert/x509_util_mac.h"
using base::ScopedCFTypeRef;
namespace net {
namespace {
OSStatus CopyCertChain(SecCertificateRef cert_handle,
CFArrayRef* out_cert_chain) {
DCHECK(cert_handle);
DCHECK(out_cert_chain);
SecPolicyRef ssl_policy;
OSStatus result = x509_util::CreateSSLClientPolicy(&ssl_policy);
if (result)
return result;
ScopedCFTypeRef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
ScopedCFTypeRef<CFArrayRef> input_certs(CFArrayCreate(
NULL, const_cast<const void**>(reinterpret_cast<void**>(&cert_handle)),
1, &kCFTypeArrayCallBacks));
SecTrustRef trust_ref = NULL;
{
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
result = SecTrustCreateWithCertificates(input_certs, ssl_policy,
&trust_ref);
}
if (result)
return result;
ScopedCFTypeRef<SecTrustRef> trust(trust_ref);
SecTrustResultType status;
CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
{
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
result = SecTrustEvaluate(trust, &status);
}
if (result)
return result;
{
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
result = SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
}
return result;
}
bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
scoped_refptr<X509Certificate>* cert) {
DCHECK(cert);
DCHECK(cert->get());
X509Certificate::OSCertHandle cert_handle = (*cert)->os_cert_handle();
CFArrayRef cert_chain = NULL;
OSStatus result = CopyCertChain(cert_handle, &cert_chain);
if (result) {
OSSTATUS_LOG(ERROR, result) << "CopyCertChain error";
return false;
}
if (!cert_chain)
return false;
X509Certificate::OSCertHandles intermediates;
for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
i < chain_count; ++i) {
SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
intermediates.push_back(cert);
}
scoped_refptr<X509Certificate> new_cert(X509Certificate::CreateFromHandle(
cert_handle, intermediates));
CFRelease(cert_chain);
if (!new_cert->IsIssuedByEncoded(valid_issuers))
return false;
cert->swap(new_cert);
return true;
}
void GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert,
const CertificateList& regular_certs,
const SSLCertRequestInfo& request,
bool query_keychain,
CertificateList* selected_certs) {
CertificateList preliminary_list;
if (preferred_cert.get())
preliminary_list.push_back(preferred_cert);
preliminary_list.insert(preliminary_list.end(), regular_certs.begin(),
regular_certs.end());
selected_certs->clear();
for (size_t i = 0; i < preliminary_list.size(); ++i) {
scoped_refptr<X509Certificate>& cert = preliminary_list[i];
if (cert->HasExpired() || !cert->SupportsSSLClientAuth())
continue;
const SHA1HashValue& fingerprint = cert->fingerprint();
size_t pos;
for (pos = 0; pos < selected_certs->size(); ++pos) {
if ((*selected_certs)[pos]->fingerprint().Equals(fingerprint))
break;
}
if (pos < selected_certs->size())
continue;
if (request.cert_authorities.empty() ||
cert->IsIssuedByEncoded(request.cert_authorities) ||
(query_keychain &&
IsIssuedByInKeychain(request.cert_authorities, &cert))) {
selected_certs->push_back(cert);
}
}
CertificateList::iterator sort_begin = selected_certs->begin();
CertificateList::iterator sort_end = selected_certs->end();
if (preferred_cert.get() && sort_begin != sort_end &&
sort_begin->get() == preferred_cert.get()) {
++sort_begin;
}
sort(sort_begin, sort_end, x509_util::ClientCertSorter());
}
}
ClientCertStoreMac::ClientCertStoreMac() {}
ClientCertStoreMac::~ClientCertStoreMac() {}
void ClientCertStoreMac::GetClientCerts(const SSLCertRequestInfo& request,
CertificateList* selected_certs,
const base::Closure& callback) {
std::string server_domain = request.host_and_port.host();
ScopedCFTypeRef<SecIdentityRef> preferred_identity;
if (!server_domain.empty()) {
ScopedCFTypeRef<CFStringRef> domain_str(
base::SysUTF8ToCFStringRef("https://" + server_domain));
SecIdentityRef identity = NULL;
{
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr)
preferred_identity.reset(identity);
}
}
scoped_refptr<X509Certificate> preferred_cert = NULL;
CertificateList regular_certs;
SecIdentitySearchRef search = NULL;
OSStatus err;
{
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
}
if (err) {
selected_certs->clear();
callback.Run();
return;
}
ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
while (!err) {
SecIdentityRef identity = NULL;
{
base::AutoLock lock(crypto::GetMacSecurityServicesLock());
err = SecIdentitySearchCopyNext(search, &identity);
}
if (err)
break;
ScopedCFTypeRef<SecIdentityRef> scoped_identity(identity);
SecCertificateRef cert_handle;
err = SecIdentityCopyCertificate(identity, &cert_handle);
if (err != noErr)
continue;
ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);
scoped_refptr<X509Certificate> cert(
X509Certificate::CreateFromHandle(cert_handle,
X509Certificate::OSCertHandles()));
if (preferred_identity && CFEqual(preferred_identity, identity)) {
DCHECK(!preferred_cert.get());
preferred_cert = cert;
} else {
regular_certs.push_back(cert);
}
}
if (err != errSecItemNotFound) {
OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error";
selected_certs->clear();
callback.Run();
return;
}
GetClientCertsImpl(preferred_cert, regular_certs, request, true,
selected_certs);
callback.Run();
}
bool ClientCertStoreMac::SelectClientCertsForTesting(
const CertificateList& input_certs,
const SSLCertRequestInfo& request,
CertificateList* selected_certs) {
GetClientCertsImpl(NULL, input_certs, request, false, selected_certs);
return true;
}
bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting(
const scoped_refptr<X509Certificate>& preferred_cert,
const CertificateList& regular_certs,
const SSLCertRequestInfo& request,
CertificateList* selected_certs) {
GetClientCertsImpl(
preferred_cert, regular_certs, request, false, selected_certs);
return true;
}
}