client_cert_store_mac.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. // Copyright 2013 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. #include "net/ssl/client_cert_store_mac.h"
  5. #include <CommonCrypto/CommonDigest.h>
  6. #include <CoreFoundation/CFArray.h>
  7. #include <CoreServices/CoreServices.h>
  8. #include <Security/SecBase.h>
  9. #include <Security/Security.h>
  10. #include <algorithm>
  11. #include <functional>
  12. #include <memory>
  13. #include <string>
  14. #include <utility>
  15. #include <vector>
  16. #include "base/bind.h"
  17. #include "base/callback.h"
  18. #include "base/callback_helpers.h"
  19. #include "base/logging.h"
  20. #include "base/mac/mac_logging.h"
  21. #include "base/mac/scoped_cftyperef.h"
  22. #include "base/strings/sys_string_conversions.h"
  23. #include "base/synchronization/lock.h"
  24. #include "base/task/task_runner_util.h"
  25. #include "crypto/mac_security_services_lock.h"
  26. #include "net/base/host_port_pair.h"
  27. #include "net/cert/pki/extended_key_usage.h"
  28. #include "net/cert/pki/parse_certificate.h"
  29. #include "net/cert/x509_util.h"
  30. #include "net/cert/x509_util_apple.h"
  31. #include "net/ssl/client_cert_identity_mac.h"
  32. #include "net/ssl/ssl_platform_key_util.h"
  33. using base::ScopedCFTypeRef;
  34. namespace net {
  35. namespace {
  36. using ClientCertIdentityMacList =
  37. std::vector<std::unique_ptr<ClientCertIdentityMac>>;
  38. // Gets the issuer for a given cert, starting with the cert itself and
  39. // including the intermediate and finally root certificates (if any).
  40. // This function calls SecTrust but doesn't actually pay attention to the trust
  41. // result: it shouldn't be used to determine trust, just to traverse the chain.
  42. OSStatus CopyCertChain(SecCertificateRef cert_handle,
  43. base::ScopedCFTypeRef<CFArrayRef>* out_cert_chain) {
  44. DCHECK(cert_handle);
  45. DCHECK(out_cert_chain);
  46. // Create an SSL policy ref configured for client cert evaluation.
  47. ScopedCFTypeRef<SecPolicyRef> ssl_policy(
  48. SecPolicyCreateSSL(/*server=*/false, /*hostname=*/nullptr));
  49. if (!ssl_policy)
  50. return errSecNoPolicyModule;
  51. // Create a SecTrustRef.
  52. ScopedCFTypeRef<CFArrayRef> input_certs(CFArrayCreate(
  53. nullptr, const_cast<const void**>(reinterpret_cast<void**>(&cert_handle)),
  54. 1, &kCFTypeArrayCallBacks));
  55. OSStatus result;
  56. SecTrustRef trust_ref = nullptr;
  57. {
  58. base::AutoLock lock(crypto::GetMacSecurityServicesLock());
  59. result = SecTrustCreateWithCertificates(input_certs, ssl_policy,
  60. &trust_ref);
  61. }
  62. if (result)
  63. return result;
  64. ScopedCFTypeRef<SecTrustRef> trust(trust_ref);
  65. // Evaluate trust, which creates the cert chain.
  66. SecTrustResultType status;
  67. {
  68. base::AutoLock lock(crypto::GetMacSecurityServicesLock());
  69. result = SecTrustEvaluate(trust, &status);
  70. if (result)
  71. return result;
  72. *out_cert_chain = x509_util::CertificateChainFromSecTrust(trust);
  73. }
  74. return result;
  75. }
  76. // Returns true if |*identity| is issued by an authority in |valid_issuers|
  77. // according to Keychain Services, rather than using |identity|'s intermediate
  78. // certificates. If it is, |*identity| is updated to include the intermediates.
  79. bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
  80. ClientCertIdentityMac* identity) {
  81. DCHECK(identity);
  82. DCHECK(identity->sec_identity_ref());
  83. ScopedCFTypeRef<SecCertificateRef> os_cert;
  84. int err = SecIdentityCopyCertificate(identity->sec_identity_ref(),
  85. os_cert.InitializeInto());
  86. if (err != noErr)
  87. return false;
  88. base::ScopedCFTypeRef<CFArrayRef> cert_chain;
  89. OSStatus result = CopyCertChain(os_cert.get(), &cert_chain);
  90. if (result) {
  91. OSSTATUS_LOG(ERROR, result) << "CopyCertChain error";
  92. return false;
  93. }
  94. if (!cert_chain)
  95. return false;
  96. std::vector<base::ScopedCFTypeRef<SecCertificateRef>> intermediates;
  97. for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
  98. i < chain_count; ++i) {
  99. SecCertificateRef sec_cert = reinterpret_cast<SecCertificateRef>(
  100. const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
  101. intermediates.emplace_back(sec_cert, base::scoped_policy::RETAIN);
  102. }
  103. // Allow UTF-8 inside PrintableStrings in client certificates. See
  104. // crbug.com/770323.
  105. X509Certificate::UnsafeCreateOptions options;
  106. options.printable_string_is_utf8 = true;
  107. scoped_refptr<X509Certificate> new_cert(
  108. x509_util::CreateX509CertificateFromSecCertificate(os_cert, intermediates,
  109. options));
  110. if (!new_cert || !new_cert->IsIssuedByEncoded(valid_issuers))
  111. return false;
  112. std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediate_buffers;
  113. intermediate_buffers.reserve(new_cert->intermediate_buffers().size());
  114. for (const auto& intermediate : new_cert->intermediate_buffers()) {
  115. intermediate_buffers.push_back(bssl::UpRef(intermediate.get()));
  116. }
  117. identity->SetIntermediates(std::move(intermediate_buffers));
  118. return true;
  119. }
  120. // Does |cert|'s usage allow SSL client authentication?
  121. bool SupportsSSLClientAuth(CRYPTO_BUFFER* cert) {
  122. DCHECK(cert);
  123. ParseCertificateOptions options;
  124. options.allow_invalid_serial_numbers = true;
  125. der::Input tbs_certificate_tlv;
  126. der::Input signature_algorithm_tlv;
  127. der::BitString signature_value;
  128. ParsedTbsCertificate tbs;
  129. if (!ParseCertificate(
  130. der::Input(CRYPTO_BUFFER_data(cert), CRYPTO_BUFFER_len(cert)),
  131. &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value,
  132. nullptr /* errors*/) ||
  133. !ParseTbsCertificate(tbs_certificate_tlv, options, &tbs,
  134. nullptr /*errors*/)) {
  135. return false;
  136. }
  137. if (!tbs.extensions_tlv)
  138. return true;
  139. std::map<der::Input, ParsedExtension> extensions;
  140. if (!ParseExtensions(tbs.extensions_tlv.value(), &extensions))
  141. return false;
  142. // RFC5280 says to take the intersection of the two extensions.
  143. //
  144. // We only support signature-based client certificates, so we need the
  145. // digitalSignature bit.
  146. //
  147. // In particular, if a key has the nonRepudiation bit and not the
  148. // digitalSignature one, we will not offer it to the user.
  149. if (auto it = extensions.find(der::Input(kKeyUsageOid));
  150. it != extensions.end()) {
  151. der::BitString key_usage;
  152. if (!ParseKeyUsage(it->second.value, &key_usage) ||
  153. !key_usage.AssertsBit(KEY_USAGE_BIT_DIGITAL_SIGNATURE)) {
  154. return false;
  155. }
  156. }
  157. if (auto it = extensions.find(der::Input(kExtKeyUsageOid));
  158. it != extensions.end()) {
  159. std::vector<der::Input> extended_key_usage;
  160. if (!ParseEKUExtension(it->second.value, &extended_key_usage))
  161. return false;
  162. bool found_acceptable_eku = false;
  163. for (const auto& oid : extended_key_usage) {
  164. if (oid == der::Input(kAnyEKU) || oid == der::Input(kClientAuth)) {
  165. found_acceptable_eku = true;
  166. break;
  167. }
  168. }
  169. if (!found_acceptable_eku)
  170. return false;
  171. }
  172. return true;
  173. }
  174. // Examines the certificates in |preferred_identity| and |regular_identities| to
  175. // find all certificates that match the client certificate request in |request|,
  176. // storing the matching certificates in |selected_identities|.
  177. // If |query_keychain| is true, Keychain Services will be queried to construct
  178. // full certificate chains. If it is false, only the the certificates and their
  179. // intermediates (available via X509Certificate::intermediate_buffers())
  180. // will be considered.
  181. void GetClientCertsImpl(
  182. std::unique_ptr<ClientCertIdentityMac> preferred_identity,
  183. ClientCertIdentityMacList regular_identities,
  184. const SSLCertRequestInfo& request,
  185. bool query_keychain,
  186. ClientCertIdentityList* selected_identities) {
  187. scoped_refptr<X509Certificate> preferred_cert_orig;
  188. ClientCertIdentityMacList preliminary_list = std::move(regular_identities);
  189. if (preferred_identity) {
  190. preferred_cert_orig = preferred_identity->certificate();
  191. preliminary_list.insert(preliminary_list.begin(),
  192. std::move(preferred_identity));
  193. }
  194. selected_identities->clear();
  195. for (size_t i = 0; i < preliminary_list.size(); ++i) {
  196. std::unique_ptr<ClientCertIdentityMac>& cert = preliminary_list[i];
  197. if (cert->certificate()->HasExpired() ||
  198. !SupportsSSLClientAuth(cert->certificate()->cert_buffer())) {
  199. continue;
  200. }
  201. // Skip duplicates (a cert may be in multiple keychains).
  202. auto cert_iter = std::find_if(
  203. selected_identities->begin(), selected_identities->end(),
  204. [&cert](
  205. const std::unique_ptr<ClientCertIdentity>& other_cert_identity) {
  206. return x509_util::CryptoBufferEqual(
  207. cert->certificate()->cert_buffer(),
  208. other_cert_identity->certificate()->cert_buffer());
  209. });
  210. if (cert_iter != selected_identities->end())
  211. continue;
  212. // Check if the certificate issuer is allowed by the server.
  213. if (request.cert_authorities.empty() ||
  214. cert->certificate()->IsIssuedByEncoded(request.cert_authorities) ||
  215. (query_keychain &&
  216. IsIssuedByInKeychain(request.cert_authorities, cert.get()))) {
  217. selected_identities->push_back(std::move(cert));
  218. }
  219. }
  220. // Preferred cert should appear first in the ui, so exclude it from the
  221. // sorting. Compare the cert_buffer since the X509Certificate object may
  222. // have changed if intermediates were added.
  223. ClientCertIdentityList::iterator sort_begin = selected_identities->begin();
  224. ClientCertIdentityList::iterator sort_end = selected_identities->end();
  225. if (preferred_cert_orig && sort_begin != sort_end &&
  226. x509_util::CryptoBufferEqual(
  227. sort_begin->get()->certificate()->cert_buffer(),
  228. preferred_cert_orig->cert_buffer())) {
  229. ++sort_begin;
  230. }
  231. sort(sort_begin, sort_end, ClientCertIdentitySorter());
  232. }
  233. // Given a |sec_identity|, identifies its corresponding certificate, and either
  234. // adds it to |regular_identities| or assigns it to |preferred_identity|, if the
  235. // |sec_identity| matches the |preferred_sec_identity|.
  236. void AddIdentity(ScopedCFTypeRef<SecIdentityRef> sec_identity,
  237. SecIdentityRef preferred_sec_identity,
  238. ClientCertIdentityMacList* regular_identities,
  239. std::unique_ptr<ClientCertIdentityMac>* preferred_identity) {
  240. OSStatus err;
  241. ScopedCFTypeRef<SecCertificateRef> cert_handle;
  242. err = SecIdentityCopyCertificate(sec_identity.get(),
  243. cert_handle.InitializeInto());
  244. if (err != noErr)
  245. return;
  246. // Allow UTF-8 inside PrintableStrings in client certificates. See
  247. // crbug.com/770323.
  248. X509Certificate::UnsafeCreateOptions options;
  249. options.printable_string_is_utf8 = true;
  250. scoped_refptr<X509Certificate> cert(
  251. x509_util::CreateX509CertificateFromSecCertificate(cert_handle, {},
  252. options));
  253. if (!cert)
  254. return;
  255. if (preferred_sec_identity &&
  256. CFEqual(preferred_sec_identity, sec_identity.get())) {
  257. *preferred_identity = std::make_unique<ClientCertIdentityMac>(
  258. std::move(cert), std::move(sec_identity));
  259. } else {
  260. regular_identities->push_back(std::make_unique<ClientCertIdentityMac>(
  261. std::move(cert), std::move(sec_identity)));
  262. }
  263. }
  264. ClientCertIdentityList GetClientCertsOnBackgroundThread(
  265. const SSLCertRequestInfo& request) {
  266. std::string server_domain = request.host_and_port.host();
  267. ScopedCFTypeRef<SecIdentityRef> preferred_sec_identity;
  268. if (!server_domain.empty()) {
  269. // See if there's an identity preference for this domain:
  270. ScopedCFTypeRef<CFStringRef> domain_str(
  271. base::SysUTF8ToCFStringRef("https://" + server_domain));
  272. // While SecIdentityCopyPreferred appears to take a list of CA issuers
  273. // to restrict the identity search to, within Security.framework the
  274. // argument is ignored and filtering unimplemented. See SecIdentity.cpp in
  275. // libsecurity_keychain, specifically
  276. // _SecIdentityCopyPreferenceMatchingName().
  277. {
  278. base::AutoLock lock(crypto::GetMacSecurityServicesLock());
  279. preferred_sec_identity.reset(
  280. SecIdentityCopyPreferred(domain_str, nullptr, nullptr));
  281. }
  282. }
  283. // Now enumerate the identities in the available keychains.
  284. std::unique_ptr<ClientCertIdentityMac> preferred_identity;
  285. ClientCertIdentityMacList regular_identities;
  286. // TODO(https://crbug.com/1348251): Is it still true, as claimed below, that
  287. // SecIdentitySearchCopyNext sometimes returns identities missed by
  288. // SecItemCopyMatching? Add some histograms to test this and, if none are
  289. // missing, remove this code.
  290. #pragma clang diagnostic push
  291. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  292. SecIdentitySearchRef search = nullptr;
  293. OSStatus err;
  294. {
  295. base::AutoLock lock(crypto::GetMacSecurityServicesLock());
  296. err = SecIdentitySearchCreate(nullptr, CSSM_KEYUSE_SIGN, &search);
  297. }
  298. if (err)
  299. return ClientCertIdentityList();
  300. ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
  301. while (!err) {
  302. ScopedCFTypeRef<SecIdentityRef> sec_identity;
  303. {
  304. base::AutoLock lock(crypto::GetMacSecurityServicesLock());
  305. err = SecIdentitySearchCopyNext(search, sec_identity.InitializeInto());
  306. }
  307. if (err)
  308. break;
  309. AddIdentity(std::move(sec_identity), preferred_sec_identity.get(),
  310. &regular_identities, &preferred_identity);
  311. }
  312. if (err != errSecItemNotFound) {
  313. OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error";
  314. return ClientCertIdentityList();
  315. }
  316. #pragma clang diagnostic pop // "-Wdeprecated-declarations"
  317. // macOS provides two ways to search for identities. SecIdentitySearchCreate()
  318. // is deprecated, as it relies on CSSM_KEYUSE_SIGN (part of the deprecated
  319. // CDSM/CSSA implementation), but is necessary to return some certificates
  320. // that would otherwise not be returned by SecItemCopyMatching(), which is the
  321. // non-deprecated way. However, SecIdentitySearchCreate() will not return all
  322. // items, particularly smart-card based identities, so it's necessary to call
  323. // both functions.
  324. static const void* kKeys[] = {
  325. kSecClass, kSecMatchLimit, kSecReturnRef, kSecAttrCanSign,
  326. };
  327. static const void* kValues[] = {
  328. kSecClassIdentity, kSecMatchLimitAll, kCFBooleanTrue, kCFBooleanTrue,
  329. };
  330. ScopedCFTypeRef<CFDictionaryRef> query(CFDictionaryCreate(
  331. kCFAllocatorDefault, kKeys, kValues, std::size(kValues),
  332. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
  333. ScopedCFTypeRef<CFArrayRef> result;
  334. {
  335. base::AutoLock lock(crypto::GetMacSecurityServicesLock());
  336. err = SecItemCopyMatching(
  337. query, reinterpret_cast<CFTypeRef*>(result.InitializeInto()));
  338. }
  339. if (!err) {
  340. for (CFIndex i = 0; i < CFArrayGetCount(result); i++) {
  341. SecIdentityRef item = reinterpret_cast<SecIdentityRef>(
  342. const_cast<void*>(CFArrayGetValueAtIndex(result, i)));
  343. AddIdentity(
  344. ScopedCFTypeRef<SecIdentityRef>(item, base::scoped_policy::RETAIN),
  345. preferred_sec_identity.get(), &regular_identities,
  346. &preferred_identity);
  347. }
  348. }
  349. ClientCertIdentityList selected_identities;
  350. GetClientCertsImpl(std::move(preferred_identity),
  351. std::move(regular_identities), request, true,
  352. &selected_identities);
  353. return selected_identities;
  354. }
  355. } // namespace
  356. ClientCertStoreMac::ClientCertStoreMac() = default;
  357. ClientCertStoreMac::~ClientCertStoreMac() = default;
  358. void ClientCertStoreMac::GetClientCerts(const SSLCertRequestInfo& request,
  359. ClientCertListCallback callback) {
  360. base::PostTaskAndReplyWithResult(
  361. GetSSLPlatformKeyTaskRunner().get(), FROM_HERE,
  362. // Caller is responsible for keeping the |request| alive
  363. // until the callback is run, so std::cref is safe.
  364. base::BindOnce(&GetClientCertsOnBackgroundThread, std::cref(request)),
  365. std::move(callback));
  366. }
  367. bool ClientCertStoreMac::SelectClientCertsForTesting(
  368. ClientCertIdentityMacList input_identities,
  369. const SSLCertRequestInfo& request,
  370. ClientCertIdentityList* selected_identities) {
  371. GetClientCertsImpl(nullptr, std::move(input_identities), request, false,
  372. selected_identities);
  373. return true;
  374. }
  375. bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting(
  376. std::unique_ptr<ClientCertIdentityMac> preferred_identity,
  377. ClientCertIdentityMacList regular_identities,
  378. const SSLCertRequestInfo& request,
  379. ClientCertIdentityList* selected_identities) {
  380. GetClientCertsImpl(std::move(preferred_identity),
  381. std::move(regular_identities), request, false,
  382. selected_identities);
  383. return true;
  384. }
  385. } // namespace net