فهرست منبع

Keep an accepting verifier from overriding Mbed TLS verification

With built-in verification on, a server certificate verifier returning
true made Mbed TLS accept a certificate its own check had rejected,
such as a self-signed one, while OpenSSL and wolfSSL still failed with
SSLServerVerification. On a client, keep the flags of Mbed TLS's own
verification, so the verifier is an additional check as on the other
backends. With verification off, the verifier is still the only check.

A server still clears them: there all three backends let an accepting
callback admit a client certificate.
yhirose 2 روز پیش
والد
کامیت
906a7bf04e
2فایلهای تغییر یافته به همراه46 افزوده شده و 2 حذف شده
  1. 7 2
      httplib.h
  2. 39 0
      test/test.cc

+ 7 - 2
httplib.h

@@ -20770,6 +20770,8 @@ struct MbedTlsSession {
   // ever runs the callback of its own context.
   // ever runs the callback of its own context.
   VerifyCallback verify_callback;
   VerifyCallback verify_callback;
 
 
+  bool is_server = false;
+
   MbedTlsSession() { mbedtls_ssl_init(&ssl); }
   MbedTlsSession() { mbedtls_ssl_init(&ssl); }
 
 
   ~MbedTlsSession() { mbedtls_ssl_free(&ssl); }
   ~MbedTlsSession() { mbedtls_ssl_free(&ssl); }
@@ -20949,7 +20951,7 @@ inline int mbedtls_verify_callback(void *data, mbedtls_x509_crt *crt,
 
 
   // set_sni() disabled hostname verification for this session: ignore the
   // set_sni() disabled hostname verification for this session: ignore the
   // CN/SAN mismatch flag, as the OpenSSL/wolfSSL backends do. The final
   // CN/SAN mismatch flag, as the OpenSSL/wolfSSL backends do. The final
-  // pass/fail decision still comes from the remaining flags (or, below, from
+  // pass/fail decision still comes from the remaining flags (and, below, from
   // the user's own verify callback).
   // the user's own verify callback).
   if (session->suppress_hostname_mismatch) { mbedtls_clear_cn_mismatch(flags); }
   if (session->suppress_hostname_mismatch) { mbedtls_clear_cn_mismatch(flags); }
 
 
@@ -20976,7 +20978,9 @@ inline int mbedtls_verify_callback(void *data, mbedtls_x509_crt *crt,
   bool accepted = run_verify_callback(callback, verify_ctx);
   bool accepted = run_verify_callback(callback, verify_ctx);
 
 
   if (accepted) {
   if (accepted) {
-    *flags = 0; // Clear all error flags
+    // Overrides the built-in verification only on a server, as on OpenSSL and
+    // wolfSSL, whose clients check the verify result after the handshake
+    if (session->is_server) { *flags = 0; }
     return 0;
     return 0;
   }
   }
   return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
   return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED;
@@ -21379,6 +21383,7 @@ inline session_t create_session(ctx_t ctx, socket_t sock) {
   // may ask for. The session runs its own copy of the user's callback, which
   // may ask for. The session runs its own copy of the user's callback, which
   // set_verify_callback() cannot replace under a handshake.
   // set_verify_callback() cannot replace under a handshake.
   session->verify_callback = mctx->verify_callback;
   session->verify_callback = mctx->verify_callback;
+  session->is_server = mctx->is_server;
   mbedtls_ssl_set_verify(&session->ssl, impl::mbedtls_verify_callback, session);
   mbedtls_ssl_set_verify(&session->ssl, impl::mbedtls_verify_callback, session);
 
 
   return static_cast<session_t>(session);
   return static_cast<session_t>(session);

+ 39 - 0
test/test.cc

@@ -17060,6 +17060,45 @@ TEST(VerifyCallbackTest, VerifyContextFields) {
   EXPECT_TRUE(saw_leaf_cert);
   EXPECT_TRUE(saw_leaf_cert);
 }
 }
 
 
+// A verifier that accepts runs alongside the built-in verification, and is the
+// only check once that is turned off.
+TEST(VerifyCallbackTest, AcceptingVerifierDoesNotOverrideVerification) {
+  using namespace httplib::tls;
+
+  SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
+  ASSERT_TRUE(svr.is_valid());
+  svr.Get("/test", [](const Request &, Response &res) {
+    res.set_content("ok", "text/plain");
+  });
+
+  auto port = svr.bind_to_any_port(HOST);
+  thread t([&]() { svr.listen_after_bind(); });
+  auto se = detail::scope_exit([&] {
+    svr.stop();
+    t.join();
+  });
+  svr.wait_until_ready();
+
+  SSLClient cli(HOST, port);
+  cli.enable_server_hostname_verification(false);
+  auto calls = 0;
+  cli.set_server_certificate_verifier([&](const VerifyContext &) {
+    calls++;
+    return true;
+  });
+
+  // cert.pem is not trusted
+  auto res = cli.Get("/test");
+  ASSERT_FALSE(res);
+  EXPECT_EQ(Error::SSLServerVerification, res.error());
+  EXPECT_GT(calls, 0);
+
+  cli.enable_server_certificate_verification(false);
+  res = cli.Get("/test");
+  ASSERT_TRUE(res) << "Error: " << to_string(res.error());
+  EXPECT_EQ("ok", res->body);
+}
+
 // A verifier belongs to the client that set it, not to the thread that set it.
 // A verifier belongs to the client that set it, not to the thread that set it.
 TEST(VerifyCallbackTest, VerifierStaysWithItsClient) {
 TEST(VerifyCallbackTest, VerifierStaysWithItsClient) {
   using namespace httplib::tls;
   using namespace httplib::tls;