From 97fe40da79ac0d28d13a68f2c8181d18b8ff0ef8 Mon Sep 17 00:00:00 2001
From: s00423892 <shichenchen@huawei.com>
Date: Fri, 19 May 2023 18:02:52 +0800
Subject: [PATCH] [Backport]curl:fix CVE-2023-28321

Conflict:The hostmatch function does not have the hostlen and patternlen parameters.
However, the two parameters are the lengths of the existing input parameters host and pattern, 
which can be generated.
Reference:https://github.com/curl/curl/commit/199f2d440d8659b42

Signed-off-by: shichenchen shichenchen@huawei.com
---
 lib/hostcheck.c | 104 +++++++++++++++++++++++++-----------------------
 1 file changed, 55 insertions(+), 49 deletions(-)

diff --git a/lib/hostcheck.c b/lib/hostcheck.c
index cf267a7..e055958 100644
--- a/lib/hostcheck.c
+++ b/lib/hostcheck.c
@@ -33,6 +33,7 @@
 #ifdef HAVE_NETINET_IN6_H
 #include <netinet/in6.h>
 #endif
+#include "curl_memrchr.h"
 
 #include "hostcheck.h"
 #include "strcase.h"
@@ -42,13 +43,23 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
+/* check the two input strings with given length, but do not
+   assume they end in nul-bytes */
+static bool pmatch(const char *hostname, size_t hostlen,
+                   const char *pattern, size_t patternlen)
+{
+  if(hostlen != patternlen)
+    return FALSE;
+  return strncasecompare(hostname, pattern, hostlen);
+}
+
 /*
  * Match a hostname against a wildcard pattern.
  * E.g.
  *  "foo.host.com" matches "*.host.com".
  *
  * We use the matching rule described in RFC6125, section 6.4.3.
- * https://tools.ietf.org/html/rfc6125#section-6.4.3
+ * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3
  *
  * In addition: ignore trailing dots in the host names and wildcards, so that
  * the names are used normalized. This is what the browsers do.
@@ -58,65 +69,58 @@
  * apparent distinction between a name and an IP. We need to detect the use of
  * an IP address and not wildcard match on such names.
  *
- * NOTE: hostmatch() gets called with copied buffers so that it can modify the
- * contents at will.
+ * Only match on "*" being used for the leftmost label, not "a*", "a*b" nor
+ * "*b".
+ *
+ * Return TRUE on a match. FALSE if not.
+ *
+ * @unittest: 1397
  */
 
-static int hostmatch(char *hostname, char *pattern)
+static bool hostmatch(const char *hostname,
+                      size_t hostlen,
+                      const char *pattern,
+                      size_t patternlen)
 {
-  const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
-  int wildcard_enabled;
-  size_t prefixlen, suffixlen;
+  const char *pattern_label_end;
+
+  DEBUGASSERT(pattern);
+  DEBUGASSERT(patternlen);
+  DEBUGASSERT(hostname);
+  DEBUGASSERT(hostlen);
 
   /* normalize pattern and hostname by stripping off trailing dots */
-  size_t len = strlen(hostname);
-  if(hostname[len-1]=='.')
-    hostname[len-1] = 0;
-  len = strlen(pattern);
-  if(pattern[len-1]=='.')
-    pattern[len-1] = 0;
-
-  pattern_wildcard = strchr(pattern, '*');
-  if(!pattern_wildcard)
-    return strcasecompare(pattern, hostname) ?
-      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+  if(hostname[hostlen-1]=='.')
+    hostlen--;
+  if(pattern[patternlen-1]=='.')
+    patternlen--;
+
+  if(strncmp(pattern, "*.", 2))
+    return pmatch(hostname, hostlen, pattern, patternlen);
 
   /* detect IP address as hostname and fail the match if so */
-  if(Curl_host_is_ipnum(hostname))
-    return CURL_HOST_NOMATCH;
+  else if(Curl_host_is_ipnum(hostname))
+    return FALSE;
 
-  /* We require at least 2 dots in pattern to avoid too wide wildcard
+  /* We require at least 2 dots in the pattern to avoid too wide wildcard
      match. */
-  wildcard_enabled = 1;
-  pattern_label_end = strchr(pattern, '.');
-  if(!pattern_label_end || strchr(pattern_label_end + 1, '.') == NULL ||
-     pattern_wildcard > pattern_label_end ||
-     strncasecompare(pattern, "xn--", 4)) {
-    wildcard_enabled = 0;
+  pattern_label_end = memchr(pattern, '.', patternlen);
+  if(!pattern_label_end ||
+     (memrchr(pattern, '.', patternlen) == pattern_label_end))
+    return pmatch(hostname, hostlen, pattern, patternlen);
+  else {
+    const char *hostname_label_end = memchr(hostname, '.', hostlen);
+    if(hostname_label_end) {
+      size_t skiphost = hostname_label_end - hostname;
+      size_t skiplen = pattern_label_end - pattern;
+      return pmatch(hostname_label_end, hostlen - skiphost,
+                    pattern_label_end, patternlen - skiplen);
+    }
   }
-  if(!wildcard_enabled)
-    return strcasecompare(pattern, hostname) ?
-      CURL_HOST_MATCH : CURL_HOST_NOMATCH;
-
-  hostname_label_end = strchr(hostname, '.');
-  if(!hostname_label_end ||
-     !strcasecompare(pattern_label_end, hostname_label_end))
-    return CURL_HOST_NOMATCH;
-
-  /* The wildcard must match at least one character, so the left-most
-     label of the hostname is at least as large as the left-most label
-     of the pattern. */
-  if(hostname_label_end - hostname < pattern_label_end - pattern)
-    return CURL_HOST_NOMATCH;
-
-  prefixlen = pattern_wildcard - pattern;
-  suffixlen = pattern_label_end - (pattern_wildcard + 1);
-  return strncasecompare(pattern, hostname, prefixlen) &&
-    strncasecompare(pattern_wildcard + 1, hostname_label_end - suffixlen,
-                    suffixlen) ?
-    CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+  return FALSE;
 }
 
+
 int Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
 {
   int res = 0;
@@ -128,7 +132,9 @@ int Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
     if(matchp) {
       char *hostp = strdup(hostname);
       if(hostp) {
-        if(hostmatch(hostp, matchp) == CURL_HOST_MATCH)
+        size_t hostlen = strlen(hostp);
+        size_t matchlen = strlen(matchp);
+        if(hostmatch(hostp, hostlen, matchp, matchlen) == TRUE)
           res = 1;
         free(hostp);
       }
-- 
2.27.0

