From e09d96eac742166fadb9a27cf0464ad2e3b69695 Mon Sep 17 00:00:00 2001
From: Jan Vaclav <jvaclav@redhat.com>
Date: Mon, 8 Jun 2026 13:05:33 +0200
Subject: [PATCH 1/3] libnm-sd-shared: reject urls containing unexpected
 characters

_http_url_is_valid() only rejected non-ASCII bytes (>= 0x80), but
accepted control characters, double quotes, and backslashes. These
characters can cause injection issues when the URL is pasted into
configuration files that interpret them as metacharacters (e.g. quoted
strings in dhclient.conf).

Reject control characters (< 0x20), double quotes, and backslashes in
addition to non-ASCII bytes.

(cherry picked from commit 5326760073c06157652b7e0d0865ada2eb0e393b)
---
 src/libnm-systemd-shared/nm-sd-utils-shared.c | 23 +++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/libnm-systemd-shared/nm-sd-utils-shared.c b/src/libnm-systemd-shared/nm-sd-utils-shared.c
index dad21596cf..b51faebcf8 100644
--- a/src/libnm-systemd-shared/nm-sd-utils-shared.c
+++ b/src/libnm-systemd-shared/nm-sd-utils-shared.c
@@ -53,6 +53,20 @@ nm_sd_dns_name_normalize(const char *s)
 
 /*****************************************************************************/
 
+static gboolean
+_http_url_is_valid_char(char ch)
+{
+    if (g_ascii_isalnum(ch))
+        return TRUE;
+
+    /* Allow symbols which are allowed by the URL standard, or unlikely
+     * to be problematic in this scenario. */
+    if (strchr(":/%=;&+|^`-._~?#<>{}[]@!$'()*, ", ch) != NULL)
+        return TRUE;
+
+    return FALSE;
+}
+
 static gboolean
 _http_url_is_valid(const char *url, gboolean only_https)
 {
@@ -69,7 +83,7 @@ _http_url_is_valid(const char *url, gboolean only_https)
     if (!url[0])
         return FALSE;
 
-    return !NM_STRCHAR_ANY(url, ch, (guchar) ch >= 128u);
+    return NM_STRCHAR_ALL(url, ch, _http_url_is_valid_char(ch));
 }
 
 gboolean
@@ -82,12 +96,13 @@ nm_sd_http_url_is_valid_https(const char *url)
      * assert with http_url_is_valid() that the argument is valid. We thus must make
      * sure to only pass URLs that are valid according to http_url_is_valid().
      *
-     * This is given, because our nm_sd_http_url_is_valid_https() is more strict
-     * than http_url_is_valid().
+     * This is given, because our nm_sd_http_url_is_valid_https() is more restrictive
+     * than http_url_is_valid(). The assertion below checks that anything we accept,
+     * systemd must also accept.
      *
      * We only must make sure that this is also correct in the future, when we
      * re-import systemd code. */
-    nm_assert(_http_url_is_valid(url, FALSE) == http_url_is_valid(url));
+    nm_assert(!_http_url_is_valid(url, FALSE) || http_url_is_valid(url));
     return _http_url_is_valid(url, TRUE);
 }
 

From ed231c82cc15ac360c7f75a663ea0f2c30ad0ba6 Mon Sep 17 00:00:00 2001
From: Jan Vaclav <jvaclav@redhat.com>
Date: Tue, 9 Jun 2026 11:28:15 +0200
Subject: [PATCH 2/3] dhcp/dhclient: validate hostname before pasting it into
 dhclient config

nm_sd_dns_name_is_valid() accepts double quotes, which could break out
of the quoted string context in dhclient.conf.

Add a check in create_dhclient_config() that rejects hostnames
containing characters unsafe for dhclient.conf.

(cherry picked from commit ca571c6819e01651be2197abff8eafff22ef80ef)
---
 src/core/dhcp/nm-dhcp-dhclient.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/core/dhcp/nm-dhcp-dhclient.c b/src/core/dhcp/nm-dhcp-dhclient.c
index 35b2fb2ebd..33ec95f917 100644
--- a/src/core/dhcp/nm-dhcp-dhclient.c
+++ b/src/core/dhcp/nm-dhcp-dhclient.c
@@ -271,6 +271,16 @@ find_existing_config(NMDhcpDhclient *self, int addr_family, const char *iface, c
     return NULL;
 }
 
+static gboolean
+_dhclient_hostname_is_valid(const char *hostname)
+{
+    for (const char *p = hostname; *p; p++) {
+        if (!g_ascii_isalnum(*p) && !NM_IN_SET(*p, '-', '.'))
+            return FALSE;
+    }
+    return TRUE;
+}
+
 /* NM provides interface-specific options; thus the same dhclient config
  * file cannot be used since DHCP transactions can happen in parallel.
  * Since some distros don't have default per-interface dhclient config files,
@@ -298,6 +308,12 @@ create_dhclient_config(NMDhcpDhclient     *self,
 
     g_return_val_if_fail(iface != NULL, NULL);
 
+    if (hostname && !_dhclient_hostname_is_valid(hostname)) {
+        _LOGW("hostname '%s' contains unsafe characters for dhclient config, will be ignored",
+              hostname);
+        hostname = NULL;
+    }
+
     new = g_strdup_printf(NMSTATEDIR "/dhclient%s-%s.conf",
                           _addr_family_to_path_part(addr_family),
                           iface);

From 0f867fb5d4695af96655a527f47aa9383abff3a5 Mon Sep 17 00:00:00 2001
From: Josephine Pfeiffer <josie@redhat.com>
Date: Tue, 23 Jun 2026 14:01:41 +0200
Subject: [PATCH 3/3] systemd: test nm_sd_http_url_is_valid_https() character
 rejection

Add a regression test that the validator rejects characters that could
break out of that string (double quote, backslash, control bytes,
non-ASCII) while still accepting normal https URLs.

(cherry picked from commit 58c0fe9d84892621de19adf4234894f34221f92a)
---
 src/core/tests/test-systemd.c | 37 +++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/src/core/tests/test-systemd.c b/src/core/tests/test-systemd.c
index 1b0b7f6536..09481a64ac 100644
--- a/src/core/tests/test-systemd.c
+++ b/src/core/tests/test-systemd.c
@@ -83,6 +83,42 @@ test_sd_event(void)
 
 /*****************************************************************************/
 
+static void
+test_http_url_is_valid_https(void)
+{
+    /* CVE-2026-10805: connection.mud-url is pasted verbatim into the dhclient
+     * config inside a quoted string ("send mudurl \"%s\";"). This function
+     * gates the property at verify() time, so it must reject characters that
+     * break out of the quotes or inject config syntax. */
+#define _assert_valid(url)   g_assert(nm_sd_http_url_is_valid_https("" url))
+#define _assert_invalid(url) g_assert(!nm_sd_http_url_is_valid_https("" url))
+
+    _assert_valid("https://example.com/mud.json");
+    _assert_valid("https://example.com");
+    _assert_valid("https://example.com/a?b=c&d=e#frag");
+    _assert_valid("https://[2001:db8::1]/x");
+    _assert_valid("https://user@example.com/~p/(a)*,;=+!$'");
+    _assert_valid("https://user:pass@example.com/p%20q?x=%2F");
+
+    _assert_invalid("http://example.com");
+    _assert_invalid("ftp://example.com");
+    _assert_invalid("example.com");
+    _assert_invalid("");
+    _assert_invalid("https://");
+
+    _assert_invalid("https://example.com/\""); /* breaks out of the quoted string */
+    _assert_invalid("https://example.com/\\"); /* escapes the following char */
+    _assert_invalid("https://example.com/\n");
+    _assert_invalid("https://example.com/\t");
+    _assert_invalid("https://example.com/a\x01b");
+    _assert_invalid("https://example.com/\xc3\xa4"); /* non-ASCII */
+
+#undef _assert_valid
+#undef _assert_invalid
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE();
 
 int
@@ -91,6 +127,7 @@ main(int argc, char **argv)
     nmtst_init(&argc, &argv, TRUE);
 
     g_test_add_func("/systemd/sd-event", test_sd_event);
+    g_test_add_func("/systemd/http-url-is-valid-https", test_http_url_is_valid_https);
 
     return g_test_run();
 }
