libsoup3/CVE-2025-4035.patch

232 lines
7.8 KiB
Diff

From 845e198402377f8644e9ae5200f1715df1ddfc08 Mon Sep 17 00:00:00 2001
From: Patrick Griffis <pgriffis@igalia.com>
Date: Thu, 27 Mar 2025 14:43:26 -0500
Subject: [PATCH 1/3] cookie: Always normalize domain value
In order for libpsl to give accurate results the domain must be lowercased.
To make it easiest we normalize it at construction time of the cookie.
---
libsoup/cookies/soup-cookie-jar.c | 11 ++++++++---
libsoup/cookies/soup-cookie.c | 22 +++++++++++++++++++---
libsoup/soup-tld.c | 11 ++++++++++-
libsoup/soup-uri-utils-private.h | 2 ++
libsoup/soup-uri-utils.c | 18 ++++++++++++++++++
tests/cookies-test.c | 19 +++++++++++++++++++
6 files changed, 76 insertions(+), 7 deletions(-)
diff --git a/libsoup/cookies/soup-cookie-jar.c b/libsoup/cookies/soup-cookie-jar.c
index fac53a5f9..7f92ace1f 100644
--- a/libsoup/cookies/soup-cookie-jar.c
+++ b/libsoup/cookies/soup-cookie-jar.c
@@ -519,6 +519,7 @@ incoming_cookie_is_third_party (SoupCookieJar *jar,
{
SoupCookieJarPrivate *priv;
const char *normalized_cookie_domain;
+ char *normalized_first_party_host;
const char *cookie_base_domain;
const char *first_party_base_domain;
const char *first_party_host;
@@ -540,12 +541,16 @@ incoming_cookie_is_third_party (SoupCookieJar *jar,
if (cookie_base_domain == NULL)
cookie_base_domain = soup_cookie_get_domain (cookie);
- first_party_base_domain = soup_tld_get_base_domain (first_party_host, NULL);
+ normalized_first_party_host = soup_uri_normalize_domain (first_party_host);
+ first_party_base_domain = soup_tld_get_base_domain (normalized_first_party_host, NULL);
if (first_party_base_domain == NULL)
- first_party_base_domain = first_party_host;
+ first_party_base_domain = normalized_first_party_host;
- if (soup_host_matches_host (cookie_base_domain, first_party_base_domain))
+ if (soup_host_matches_host (cookie_base_domain, first_party_base_domain)) {
+ g_free (normalized_first_party_host);
return FALSE;
+ }
+ g_free (normalized_first_party_host);
if (policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY)
return TRUE;
diff --git a/libsoup/cookies/soup-cookie.c b/libsoup/cookies/soup-cookie.c
index cc80d001f..b6d771646 100644
--- a/libsoup/cookies/soup-cookie.c
+++ b/libsoup/cookies/soup-cookie.c
@@ -168,6 +168,16 @@ parse_date (const char **val_p)
return date;
}
+static gboolean
+is_lowercase_ascii_string (const char *str)
+{
+ for (; *str; str++) {
+ if (!g_ascii_islower (*str))
+ return FALSE;
+ }
+ return TRUE;
+}
+
#define MAX_AGE_CAP_IN_SECONDS 31536000 // 1 year
#define MAX_ATTRIBUTE_SIZE 1024
@@ -311,6 +321,12 @@ parse_one_cookie (const char *header, GUri *origin)
g_free (cookie->domain);
cookie->domain = tmp;
}
+
+ if (!is_lowercase_ascii_string (cookie->domain)) {
+ char *tmp = soup_uri_normalize_domain (cookie->domain);
+ g_free (cookie->domain);
+ cookie->domain = tmp;
+ }
}
if (origin) {
@@ -321,7 +337,7 @@ parse_one_cookie (const char *header, GUri *origin)
return NULL;
}
} else
- cookie->domain = g_strdup (g_uri_get_host (origin));
+ cookie->domain = soup_uri_normalize_domain (g_uri_get_host (origin));
/* The original cookie spec didn't say that pages
* could only set cookies for paths they were under.
@@ -364,7 +380,7 @@ cookie_new_internal (const char *name, const char *value,
cookie = g_slice_new0 (SoupCookie);
cookie->name = g_strdup (name);
cookie->value = g_strdup (value);
- cookie->domain = g_strdup (domain);
+ cookie->domain = soup_uri_normalize_domain (domain);
cookie->path = g_strdup (path);
soup_cookie_set_max_age (cookie, max_age);
cookie->same_site_policy = SOUP_SAME_SITE_POLICY_LAX;
@@ -537,7 +553,7 @@ void
soup_cookie_set_domain (SoupCookie *cookie, const char *domain)
{
g_free (cookie->domain);
- cookie->domain = g_strdup (domain);
+ cookie->domain = soup_uri_normalize_domain (domain);
}
/**
diff --git a/libsoup/soup-tld.c b/libsoup/soup-tld.c
index 2d8151662..71fac5749 100644
--- a/libsoup/soup-tld.c
+++ b/libsoup/soup-tld.c
@@ -15,6 +15,7 @@
#include <libpsl.h>
#include "soup-tld.h"
+#include "soup-uri-utils-private.h"
#include "soup.h"
static const char *soup_tld_get_base_domain_internal (const char *hostname,
@@ -41,6 +42,8 @@ static const char *soup_tld_get_base_domain_internal (const char *hostname,
* UTF-8 or ASCII format (and the return value will be in the same
* format).
*
+ * For accurate results @hostname must be lowercase.
+ *
* Returns: a pointer to the start of the base domain in @hostname. If
* an error occurs, %NULL will be returned and @error set.
**/
@@ -80,6 +83,8 @@ gboolean
soup_tld_domain_is_public_suffix (const char *domain)
{
const psl_ctx_t* psl = soup_psl_context ();
+ char *normalized;
+ gboolean is_public_suffix;
g_return_val_if_fail (domain, FALSE);
@@ -88,7 +93,11 @@ soup_tld_domain_is_public_suffix (const char *domain)
return FALSE;
}
- return psl_is_public_suffix2 (psl, domain, PSL_TYPE_ANY | PSL_TYPE_NO_STAR_RULE);
+ normalized = soup_uri_normalize_domain (domain);
+ is_public_suffix = psl_is_public_suffix2 (psl, normalized, PSL_TYPE_ANY | PSL_TYPE_NO_STAR_RULE);
+ g_free (normalized);
+
+ return is_public_suffix;
}
/**
diff --git a/libsoup/soup-uri-utils-private.h b/libsoup/soup-uri-utils-private.h
index 0119f0814..c2f984f86 100644
--- a/libsoup/soup-uri-utils-private.h
+++ b/libsoup/soup-uri-utils-private.h
@@ -28,6 +28,8 @@ GUri *soup_uri_copy_with_normalized_flags (GUri *uri);
char *soup_uri_get_host_for_headers (GUri *uri);
+char *soup_uri_normalize_domain (const char *domain);
+
#define SOUP_URI_IS_VALID(x) ((x) && g_uri_get_host(x) && g_uri_get_host(x)[0])
G_END_DECLS
diff --git a/libsoup/soup-uri-utils.c b/libsoup/soup-uri-utils.c
index 0963a1143..1f65faede 100644
--- a/libsoup/soup-uri-utils.c
+++ b/libsoup/soup-uri-utils.c
@@ -506,3 +506,21 @@ soup_uri_get_host_for_headers (GUri *uri)
return g_strdup (host);
}
+
+char *
+soup_uri_normalize_domain (const char *domain)
+{
+ char *lower;
+ char *normalized;
+
+ g_assert (domain);
+
+ if (g_str_is_ascii (domain))
+ return g_ascii_strdown (domain, -1);
+
+ lower = g_utf8_casefold (domain, -1);
+ normalized = g_utf8_normalize (lower, -1, G_NORMALIZE_NFKC);
+ g_free (lower);
+
+ return normalized;
+}
diff --git a/tests/cookies-test.c b/tests/cookies-test.c
index 1d2d45630..7007aaf59 100644
--- a/tests/cookies-test.c
+++ b/tests/cookies-test.c
@@ -695,6 +695,24 @@ do_cookies_threads_test (void)
soup_test_session_abort_unref (session);
}
+static void
+do_cookies_public_suffix_test (void)
+{
+ SoupCookieJar *jar = soup_cookie_jar_new ();
+ GUri *uri = g_uri_parse ("http://example.CO.uk", SOUP_HTTP_URI_FLAGS, NULL);
+ GSList *cookies;
+
+ soup_cookie_jar_set_cookie (jar, uri, "value=1; domain=.co.uk");
+ soup_cookie_jar_set_cookie (jar, uri, "value=1; domain=.CO.uk");
+ soup_cookie_jar_set_cookie (jar, uri, "value=1; domain=.CO.UK");
+
+ cookies = soup_cookie_jar_all_cookies (jar);
+ g_assert_cmpint (g_slist_length (cookies), ==, 0);
+
+ g_uri_unref (uri);
+ g_object_unref (jar);
+}
+
int
main (int argc, char **argv)
{
@@ -726,6 +744,7 @@ main (int argc, char **argv)
g_test_add_func ("/cookies/secure-cookies", do_cookies_strict_secure_test);
g_test_add_func ("/cookies/prefix", do_cookies_prefix_test);
g_test_add_func ("/cookies/threads", do_cookies_threads_test);
+ g_test_add_func ("/cookies/public-suffix", do_cookies_public_suffix_test);
ret = g_test_run ();
--
GitLab