Pārlūkot izejas kodu

Add certificate and session verifiers to WebSocketClient

A wss client could set its CAs but had no way to pin a certificate. Add
set_server_certificate_verifier() and set_session_verifier(), as
SSLClient has.
yhirose 2 dienas atpakaļ
vecāks
revīzija
f361a08f67
3 mainītis faili ar 50 papildinājumiem un 0 dzēšanām
  1. 4 0
      README-websocket.md
  2. 15 0
      httplib.h
  3. 31 0
      test/test.cc

+ 4 - 0
README-websocket.md

@@ -204,6 +204,10 @@ void set_ca_cert_path(const std::string &ca_cert_file_path,
 void set_ca_cert_store(tls::ca_store_t store);
 void enable_server_certificate_verification(bool enabled);
 void enable_server_hostname_verification(bool enabled);
+// The same verifiers as SSLClient's, e.g. to pin the server's certificate
+void set_server_certificate_verifier(tls::VerifyCallback verifier);
+void set_session_verifier(
+    std::function<SSLVerifierResponse(tls::session_t)> verifier);
 ```
 
 ## Examples

+ 15 - 0
httplib.h

@@ -4670,6 +4670,9 @@ public:
   void enable_server_certificate_verification(bool enabled);
   void enable_server_hostname_verification(bool enabled);
   void enable_system_ca(bool enabled);
+  void set_server_certificate_verifier(tls::VerifyCallback verifier);
+  void set_session_verifier(
+      std::function<SSLVerifierResponse(tls::session_t)> verifier);
 #endif
 
 private:
@@ -4717,6 +4720,7 @@ private:
   SystemCAMode system_ca_mode_ = SystemCAMode::Auto;
   bool server_certificate_verification_ = true;
   bool server_hostname_verification_ = true;
+  std::function<SSLVerifierResponse(tls::session_t)> session_verifier_;
 #endif
 };
 
@@ -23360,6 +23364,7 @@ inline bool WebSocketClient::create_stream(std::unique_ptr<Stream> &strm,
 
     detail::ClientTlsSessionOptions options;
     options.server_hostname_verification = server_hostname_verification_;
+    options.session_verifier = session_verifier_;
 
     detail::ClientTlsSessionError tls_error;
     if (!detail::setup_client_tls_session(host_, tls_ctx_, tls_session_, sock_,
@@ -23544,6 +23549,16 @@ WebSocketClient::set_ca_cert_path(const std::string &ca_cert_file_path,
   ca_cert_dir_path_ = ca_cert_dir_path;
 }
 
+inline void
+WebSocketClient::set_server_certificate_verifier(tls::VerifyCallback verifier) {
+  if (tls_ctx_) { tls::set_verify_callback(tls_ctx_, std::move(verifier)); }
+}
+
+inline void WebSocketClient::set_session_verifier(
+    std::function<SSLVerifierResponse(tls::session_t)> verifier) {
+  session_verifier_ = std::move(verifier);
+}
+
 inline void WebSocketClient::set_ca_cert_store(tls::ca_store_t store) {
   if (store && tls_ctx_) {
     // set_ca_store takes ownership of store

+ 31 - 0
test/test.cc

@@ -27003,6 +27003,37 @@ protected:
   int port_ = 0;
 };
 
+TEST_F(WebSocketSSLIntegrationTest, VerifiersDecide) {
+  auto url = "wss://localhost:" + std::to_string(port_) + "/ws-echo";
+  {
+    ws::WebSocketClient client(url);
+    client.enable_server_certificate_verification(false);
+    auto calls = 0;
+    client.set_server_certificate_verifier([&](const tls::VerifyContext &) {
+      calls++;
+      return false;
+    });
+    EXPECT_FALSE(client.connect());
+    EXPECT_GT(calls, 0);
+  }
+  {
+    ws::WebSocketClient client(url);
+    client.enable_server_certificate_verification(false);
+    client.set_session_verifier([](tls::session_t) {
+      return SSLVerifierResponse::CertificateRejected;
+    });
+    EXPECT_FALSE(client.connect());
+  }
+  {
+    ws::WebSocketClient client(url);
+    client.enable_server_certificate_verification(false);
+    client.set_session_verifier([](tls::session_t) {
+      return SSLVerifierResponse::CertificateAccepted;
+    });
+    EXPECT_TRUE(client.connect());
+  }
+}
+
 TEST_F(WebSocketSSLIntegrationTest, TextEcho) {
   ws::WebSocketClient client("wss://localhost:" + std::to_string(port_) +
                              "/ws-echo");