Skip to content

Client SSL hostname verification is case-sensitive #2333

@ctabor-itracs

Description

@ctabor-itracs

Thank you for providing such an awesome, easy to use project! It has proved to be very beneficial for me!

My environment is using M$ VC17 & OpenSSL 3.x. I bumped into a little issue with SSLClient SSL hostname verification being case sensitive. As this is essentially a DNS name comparison, I don't think it should be case sensitive. Please let me know if I can provide any further information on my dev/test environments!

Looking at SSLClient::check_host_name, it appears that both scenarios of dns name matching & wild-card matching had the same issue. I was able to replace the host == pattern matching locally, using httplib::detail::case_ignore_equal and the pattern matching portion use detail::case_ignore:to_lower. It seems to work pretty well.

Below is the diff of the code I'm running locally. If it is acceptable, I should be able to create a PR. I would also be happy to try it with different different approach, if you like. Thanks again for providing a great library!

 inline bool SSLClient::check_host_name(const char *pattern,
                                        size_t pattern_len) const {
/
+  // Exact match (case-insensitive)
+  if (host_.size() == pattern_len &&
+      detail::case_ignore::equal(host_, std::string(pattern, pattern_len))) {
+    return true;
+  }

   // Wildcard match
   // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
@@ -13220,9 +13224,21 @@ inline bool SSLClient::check_host_name(const char *pattern,
   auto itr = pattern_components.begin();
   for (const auto &h : host_components_) {
     auto &p = *itr;
-    if (p != h && p != "*") {
-      auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
-                            !p.compare(0, p.size() - 1, h));
+    if (!httplib::detail::case_ignore::equal(p, h) && p != "*") {
+      bool partial_match = false;
+      if (!p.empty() && p[p.size() - 1] == '*') {
+        const auto prefix_length = p.size() - 1;
:
 inline bool SSLClient::check_host_name(const char *pattern,
                                        size_t pattern_len) const {
-  if (host_.size() == pattern_len && host_ == pattern) { return true; }
+  // Exact match (case-insensitive)
+  if (host_.size() == pattern_len &&
+      detail::case_ignore::equal(host_, std::string(pattern, pattern_len))) {
+    return true;
+  }

   // Wildcard match
   // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484
@@ -13220,9 +13224,21 @@ inline bool SSLClient::check_host_name(const char *pattern,
   auto itr = pattern_components.begin();
   for (const auto &h : host_components_) {
     auto &p = *itr;
-    if (p != h && p != "*") {
-      auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&
-                            !p.compare(0, p.size() - 1, h));
+    if (!httplib::detail::case_ignore::equal(p, h) && p != "*") {
+      bool partial_match = false;
+      if (!p.empty() && p[p.size() - 1] == '*') {
+        const auto prefix_length = p.size() - 1;
+        if (prefix_length == 0) {
+          partial_match = true;
+        } else if (h.size() >= prefix_length) {
+          partial_match =
+              std::equal(p.begin(), p.begin() + prefix_length, h.begin(),
+                         [](const char ca, const char cb) {
+                           return httplib::detail::case_ignore::to_lower(ca) ==
+                                  httplib::detail::case_ignore::to_lower(cb);
+                         });
+        }
+      }
       if (!partial_match) { return false; }
     }
     ++itr;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions