Browse Source

Read a NUL-terminated client CA in SSLServer::PemMemory

SSLServer::PemMemory takes a length of 0 to mean a NUL-terminated
string for cert_pem and key_pem, but skipped client_ca_pem when its
length was 0, so a server given its client CA that way started without
requiring client certificates. Read the client CA under the same rule
as the other fields. An empty client CA now fails to load instead of
turning the check off, and the README documents the length-0 rule.
yhirose 2 days ago
parent
commit
71b5f0a1c8
3 changed files with 20 additions and 6 deletions
  1. 2 0
      README.md
  2. 4 2
      httplib.h
  3. 14 4
      test/test.cc

+ 2 - 0
README.md

@@ -210,6 +210,8 @@ client_pem.key_pem_len = client_key.size();
 httplib::SSLClient cli("api.example.com", 443, client_pem);
 ```
 
+A length of 0 means the field is a NUL-terminated string.
+
 `httplib::ws::WebSocketClient` has the same `PemMemory` constructor for `wss://` connections. See [README-websocket.md](README-websocket.md) for details.
 
 ### Peer Certificate Inspection

+ 4 - 2
httplib.h

@@ -18824,8 +18824,10 @@ inline SSLServer::SSLServer(const PemMemory &pem) {
       last_ssl_error_ = static_cast<int>(get_error());
       free_context(ctx_);
       ctx_ = nullptr;
-    } else if (pem.client_ca_pem && pem.client_ca_pem_len > 0) {
-      if (!load_ca_pem(ctx_, pem.client_ca_pem, pem.client_ca_pem_len)) {
+    } else if (pem.client_ca_pem) {
+      auto ca_len = pem.client_ca_pem_len ? pem.client_ca_pem_len
+                                          : strlen(pem.client_ca_pem);
+      if (!load_ca_pem(ctx_, pem.client_ca_pem, ca_len)) {
         last_ssl_error_ = static_cast<int>(get_error());
         free_context(ctx_);
         ctx_ = nullptr;

+ 14 - 4
test/test.cc

@@ -16309,7 +16309,8 @@ TEST(SSLClientServerTest, PemMemoryClientEncryptedCertPresent) {
                              CLIENT_ENCRYPTED_PRIVATE_KEY_PASS);
 }
 
-// A server built from PEM memory with a client CA requires client certificates
+// A server built from PEM memory with a client CA requires client certificates,
+// also when the CA is a NUL-terminated string passed with a length of 0
 TEST(SSLClientServerTest, PemMemoryClientCertMissing) {
   std::string server_cert_pem, server_key_pem, client_ca_pem;
   read_file(SERVER_CERT_FILE, server_cert_pem);
@@ -16325,9 +16326,18 @@ TEST(SSLClientServerTest, PemMemoryClientCertMissing) {
       client_ca_pem.size(),
       nullptr // no password for server key
   };
-  SSLServer svr(server_pem);
-  ASSERT_TRUE(svr.is_valid());
-  RejectsClientWithoutCert(svr);
+  for (auto client_ca_pem_len : {client_ca_pem.size(), size_t{0}}) {
+    SCOPED_TRACE(client_ca_pem_len);
+    server_pem.client_ca_pem_len = client_ca_pem_len;
+    SSLServer svr(server_pem);
+    ASSERT_TRUE(svr.is_valid());
+    RejectsClientWithoutCert(svr);
+  }
+
+  // An empty CA fails to load instead of turning the check off
+  server_pem.client_ca_pem = "";
+  server_pem.client_ca_pem_len = 0;
+  EXPECT_FALSE(SSLServer(server_pem).is_valid());
 }
 
 // The PemMemory lengths are honoured, so the PEM needs no NUL terminator