173 lines
5.8 KiB
Diff
173 lines
5.8 KiB
Diff
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
|
|
|