cert_verify_proc_mac_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Copyright 2017 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/cert/cert_verify_proc_mac.h"
  5. #include <memory>
  6. #include "base/files/file_path.h"
  7. #include "base/files/file_util.h"
  8. #include "base/logging.h"
  9. #include "base/mac/mac_util.h"
  10. #include "base/memory/scoped_refptr.h"
  11. #include "net/base/net_errors.h"
  12. #include "net/cert/cert_verifier.h"
  13. #include "net/cert/cert_verify_result.h"
  14. #include "net/cert/crl_set.h"
  15. #include "net/cert/test_keychain_search_list_mac.h"
  16. #include "net/cert/test_root_certs.h"
  17. #include "net/cert/x509_certificate.h"
  18. #include "net/cert/x509_util.h"
  19. #include "net/log/net_log_with_source.h"
  20. #include "net/test/cert_test_util.h"
  21. #include "net/test/gtest_util.h"
  22. #include "net/test/test_data_directory.h"
  23. #include "testing/gmock/include/gmock/gmock.h"
  24. #include "testing/gtest/include/gtest/gtest.h"
  25. using net::test::IsError;
  26. using net::test::IsOk;
  27. namespace net {
  28. namespace {
  29. // Test that a CRLSet blocking one of the intermediates supplied by the server
  30. // can be worked around by the chopping workaround for path building. (Once the
  31. // supplied chain is chopped back to just the target, a better path can be
  32. // found out-of-band. Normally that would be by AIA fetching, for the purposes
  33. // of this test the better path is supplied by a test keychain.)
  34. //
  35. // In this test, there are two possible paths to validate a leaf (A):
  36. // 1. A(B) -> B(C) -> C(E) -> E(E)
  37. // 2. A(B) -> B(F) -> F(E) -> E(E)
  38. //
  39. // A(B) -> B(C) -> C(E) is supplied to the verifier.
  40. // B(F) and F(E) are supplied in a test keychain.
  41. // C is blocked by a CRLset.
  42. //
  43. // The verifier should rollback until it just tries A(B) alone, at which point
  44. // it will pull B(F) & F(E) from the keychain and succeed.
  45. // A SecTrustSetKeychains issue was discovered on macOS back when macOS 10.12
  46. // was new, so this test was disabled on >=10.12. A bug was filed, but no action
  47. // was ever taken. Six years later, we're removing 10.12 support, and it's
  48. // unclear if this remains broken, or if this test can be re-enabled.
  49. // TODO(crbug.com/671889): Figure this out.
  50. TEST(CertVerifyProcMacTest, DISABLED_MacCRLIntermediate) {
  51. CertificateList path_2_certs;
  52. ASSERT_TRUE(
  53. LoadCertificateFiles({"multi-root-A-by-B.pem", "multi-root-B-by-C.pem",
  54. "multi-root-C-by-E.pem", "multi-root-E-by-E.pem"},
  55. &path_2_certs));
  56. CertificateList path_3_certs;
  57. ASSERT_TRUE(
  58. LoadCertificateFiles({"multi-root-A-by-B.pem", "multi-root-B-by-F.pem",
  59. "multi-root-F-by-E.pem", "multi-root-E-by-E.pem"},
  60. &path_3_certs));
  61. // Add E as trust anchor.
  62. ScopedTestRoot test_root_E(path_3_certs[3].get()); // E-by-E
  63. std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
  64. intermediates.push_back(
  65. bssl::UpRef(path_2_certs[1]->cert_buffer())); // B-by-C
  66. intermediates.push_back(
  67. bssl::UpRef(path_2_certs[2]->cert_buffer())); // C-by-E
  68. scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromBuffer(
  69. bssl::UpRef(path_3_certs[0]->cert_buffer()), std::move(intermediates));
  70. ASSERT_TRUE(cert);
  71. std::unique_ptr<TestKeychainSearchList> test_keychain_search_list(
  72. TestKeychainSearchList::Create());
  73. ASSERT_TRUE(test_keychain_search_list);
  74. // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
  75. // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
  76. // warnings are disabled in the meanwhile.
  77. #pragma clang diagnostic push
  78. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  79. base::FilePath keychain_path(
  80. GetTestCertsDirectory().AppendASCII("multi-root-BFE.keychain"));
  81. // SecKeychainOpen does not fail if the file doesn't exist, so assert it here
  82. // for easier debugging.
  83. ASSERT_TRUE(base::PathExists(keychain_path));
  84. SecKeychainRef keychain;
  85. OSStatus status =
  86. SecKeychainOpen(keychain_path.MaybeAsASCII().c_str(), &keychain);
  87. ASSERT_EQ(errSecSuccess, status);
  88. ASSERT_TRUE(keychain);
  89. base::ScopedCFTypeRef<SecKeychainRef> scoped_keychain(keychain);
  90. test_keychain_search_list->AddKeychain(keychain);
  91. #pragma clang diagnostic pop
  92. scoped_refptr<CRLSet> crl_set;
  93. std::string crl_set_bytes;
  94. // CRL which blocks C by SPKI.
  95. EXPECT_TRUE(base::ReadFileToString(
  96. GetTestCertsDirectory().AppendASCII("multi-root-crlset-C.raw"),
  97. &crl_set_bytes));
  98. ASSERT_TRUE(CRLSet::Parse(crl_set_bytes, &crl_set));
  99. int flags = 0;
  100. CertVerifyResult verify_result;
  101. scoped_refptr<CertVerifyProc> verify_proc =
  102. base::MakeRefCounted<CertVerifyProcMac>();
  103. int error = verify_proc->Verify(
  104. cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
  105. /*sct_list=*/std::string(), flags, crl_set.get(), CertificateList(),
  106. &verify_result, NetLogWithSource());
  107. ASSERT_EQ(OK, error);
  108. ASSERT_EQ(0U, verify_result.cert_status);
  109. ASSERT_TRUE(verify_result.verified_cert.get());
  110. const auto& verified_intermediates =
  111. verify_result.verified_cert->intermediate_buffers();
  112. ASSERT_EQ(3U, verified_intermediates.size());
  113. scoped_refptr<X509Certificate> intermediate =
  114. X509Certificate::CreateFromBuffer(
  115. bssl::UpRef(verified_intermediates[1].get()), {});
  116. ASSERT_TRUE(intermediate);
  117. scoped_refptr<X509Certificate> expected_intermediate = path_3_certs[2];
  118. EXPECT_TRUE(expected_intermediate->EqualsExcludingChain(intermediate.get()))
  119. << "Expected: " << expected_intermediate->subject().common_name
  120. << " issued by " << expected_intermediate->issuer().common_name
  121. << "; Got: " << intermediate->subject().common_name << " issued by "
  122. << intermediate->issuer().common_name;
  123. }
  124. // Test that if a keychain is present which trusts a less-desirable root (ex,
  125. // one using SHA1), that the keychain reordering hack will cause the better
  126. // root in the System Roots to be used instead.
  127. // TODO(crbug.com/867174): Re-enable this test.
  128. TEST(CertVerifyProcMacTest, DISABLED_MacKeychainReordering) {
  129. // Note: target cert expires Dec 30 23:59:59 2019 GMT
  130. scoped_refptr<X509Certificate> cert = CreateCertificateChainFromFile(
  131. GetTestCertsDirectory(), "gms.hongleong.com.my-verisign-chain.pem",
  132. X509Certificate::FORMAT_AUTO);
  133. ASSERT_TRUE(cert);
  134. // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
  135. // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
  136. // warnings are disabled in the meanwhile.
  137. #pragma clang diagnostic push
  138. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  139. // Create a test keychain search list that will Always Trust the SHA1
  140. // cross-signed VeriSign Class 3 Public Primary Certification Authority - G5
  141. std::unique_ptr<TestKeychainSearchList> test_keychain_search_list(
  142. TestKeychainSearchList::Create());
  143. ASSERT_TRUE(test_keychain_search_list);
  144. base::FilePath keychain_path(GetTestCertsDirectory().AppendASCII(
  145. "verisign_class3_g5_crosssigned-trusted.keychain"));
  146. // SecKeychainOpen does not fail if the file doesn't exist, so assert it here
  147. // for easier debugging.
  148. ASSERT_TRUE(base::PathExists(keychain_path));
  149. SecKeychainRef keychain;
  150. OSStatus status =
  151. SecKeychainOpen(keychain_path.MaybeAsASCII().c_str(), &keychain);
  152. ASSERT_EQ(errSecSuccess, status);
  153. ASSERT_TRUE(keychain);
  154. base::ScopedCFTypeRef<SecKeychainRef> scoped_keychain(keychain);
  155. test_keychain_search_list->AddKeychain(keychain);
  156. #pragma clang diagnostic pop
  157. int flags = 0;
  158. CertVerifyResult verify_result;
  159. scoped_refptr<CertVerifyProc> verify_proc =
  160. base::MakeRefCounted<CertVerifyProcMac>();
  161. int error = verify_proc->Verify(
  162. cert.get(), "gms.hongleong.com.my", /*ocsp_response=*/std::string(),
  163. /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
  164. CertificateList(), &verify_result, NetLogWithSource());
  165. ASSERT_EQ(OK, error);
  166. EXPECT_FALSE(verify_result.has_sha1);
  167. ASSERT_TRUE(verify_result.verified_cert.get());
  168. const auto& verified_intermediates =
  169. verify_result.verified_cert->intermediate_buffers();
  170. ASSERT_EQ(2U, verified_intermediates.size());
  171. }
  172. // Much of the Keychain API was marked deprecated as of the macOS 13 SDK.
  173. // Removal of its use is tracked in https://crbug.com/1348251 but deprecation
  174. // warnings are disabled in the meanwhile.
  175. #pragma clang diagnostic push
  176. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  177. // Test that the system root certificate keychain is in the expected location
  178. // and can be opened. Other tests would fail if this was not true, but this
  179. // test makes the reason for the failure obvious.
  180. TEST(CertVerifyProcMacTest, MacSystemRootCertificateKeychainLocation) {
  181. const char* root_keychain_path =
  182. "/System/Library/Keychains/SystemRootCertificates.keychain";
  183. ASSERT_TRUE(base::PathExists(base::FilePath(root_keychain_path)));
  184. SecKeychainRef keychain;
  185. OSStatus status = SecKeychainOpen(root_keychain_path, &keychain);
  186. ASSERT_EQ(errSecSuccess, status);
  187. CFRelease(keychain);
  188. }
  189. #pragma clang diagnostic pop
  190. // Test that CertVerifyProcMac reacts appropriately when Apple's certificate
  191. // verifier rejects a certificate with a fatal error. This is a regression
  192. // test for https://crbug.com/472291.
  193. // (Since 10.12, this causes a recoverable error instead of a fatal one.)
  194. // TODO(mattm): Try to find a different way to cause a fatal error that works
  195. // on 10.12.
  196. TEST(CertVerifyProcMacTest, LargeKey) {
  197. // Load root_ca_cert.pem into the test root store.
  198. ScopedTestRoot test_root(
  199. ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem").get());
  200. scoped_refptr<X509Certificate> cert(
  201. ImportCertFromFile(GetTestCertsDirectory(), "large_key.pem"));
  202. // Apple's verifier rejects this certificate as invalid because the
  203. // RSA key is too large. If a future version of OS X changes this,
  204. // large_key.pem may need to be regenerated with a larger key.
  205. int flags = 0;
  206. CertVerifyResult verify_result;
  207. scoped_refptr<CertVerifyProc> verify_proc =
  208. base::MakeRefCounted<CertVerifyProcMac>();
  209. int error = verify_proc->Verify(
  210. cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
  211. /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
  212. CertificateList(), &verify_result, NetLogWithSource());
  213. EXPECT_THAT(error, IsError(ERR_CERT_INVALID));
  214. EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_INVALID);
  215. }
  216. // Test that CertVerifierMac on 10.15+ appropriately flags certificates
  217. // that violate https://support.apple.com/en-us/HT210176 as having
  218. // too long validity, rather than being invalid certificates.
  219. TEST(CertVerifyProcMacTest, CertValidityTooLong) {
  220. // Load root_ca_cert.pem into the test root store.
  221. ScopedTestRoot test_root(
  222. ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem").get());
  223. scoped_refptr<X509Certificate> cert(ImportCertFromFile(
  224. GetTestCertsDirectory(), "900_days_after_2019_07_01.pem"));
  225. int flags = 0;
  226. CertVerifyResult verify_result;
  227. scoped_refptr<CertVerifyProc> verify_proc =
  228. base::MakeRefCounted<CertVerifyProcMac>();
  229. int error = verify_proc->Verify(
  230. cert.get(), "127.0.0.1", /*ocsp_response=*/std::string(),
  231. /*sct_list=*/std::string(), flags, CRLSet::BuiltinCRLSet().get(),
  232. CertificateList(), &verify_result, NetLogWithSource());
  233. if (base::mac::IsAtLeastOS10_15()) {
  234. EXPECT_THAT(error, IsError(ERR_CERT_VALIDITY_TOO_LONG));
  235. EXPECT_EQ((verify_result.cert_status & CERT_STATUS_ALL_ERRORS),
  236. CERT_STATUS_VALIDITY_TOO_LONG);
  237. } else {
  238. EXPECT_THAT(error, IsOk());
  239. EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_VALIDITY_TOO_LONG);
  240. EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_INVALID);
  241. }
  242. }
  243. } // namespace
  244. } // namespace net