mirror of
git://git.yoctoproject.org/poky
synced 2025-12-31 13:38:04 +00:00
kea: fix CVE-2025-11232
Backport a patch from upstream to resolve CVE-2025-11232: Invalid characters cause assert To trigger the issue, three configuration parameters must have specific settings: "hostname-char-set" must be left at the default setting, which is "[^A-Za-z0-9.-]"; "hostname-char-replacement" must be empty (the default); and "ddns-qualifying-suffix" must NOT be empty (the default is empty). DDNS updates do not need to be enabled for this issue to manifest. A client that sends certain option content would then cause kea-dhcp4 to exit unexpectedly. (From OE-Core rev: f9331b42fd8b0df64517969a794a93d41624bd96) Signed-off-by: Ross Burton <ross.burton@arm.com> Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
310183b813
commit
1db7c5487b
474
meta/recipes-connectivity/kea/files/CVE-2025-11232.patch
Normal file
474
meta/recipes-connectivity/kea/files/CVE-2025-11232.patch
Normal file
@ -0,0 +1,474 @@
|
||||
From 92b65b2345e07d826b56ffd65cf47538f1c7a271 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Markwalder <tmark@isc.org>
|
||||
Date: Tue, 7 Oct 2025 14:41:16 -0400
|
||||
Subject: [PATCH] [#4155] Backport #4142 to v3_0
|
||||
|
||||
Invalid characters cause assert
|
||||
|
||||
To trigger the issue, three configuration parameters must have
|
||||
specific settings: "hostname-char-set" must be left at the default
|
||||
setting, which is "[^A-Za-z0-9.-]"; "hostname-char-replacement" must
|
||||
be empty (the default); and "ddns-qualifying-suffix" must NOT be empty
|
||||
(the default is empty). DDNS updates do not need to be enabled for
|
||||
this issue to manifest. A client that sends certain option content
|
||||
would then cause kea-dhcp4 to exit unexpectedly.
|
||||
|
||||
CVE: CVE-2025-11232
|
||||
Upstream-Status: Backport [https://github.com/isc-projects/kea/commit/92b65b2345e07d826b56ffd65cf47538f1c7a271]
|
||||
Signed-off-by: Ross Burton <ross.burton@arm.com>
|
||||
|
||||
new file: changelog_unreleased/CVE-2025-11232-catch-empty-sanitized-hostname
|
||||
modified: src/bin/dhcp4/dhcp4_messages.cc
|
||||
modified: src/bin/dhcp4/dhcp4_messages.h
|
||||
modified: src/bin/dhcp4/dhcp4_messages.mes
|
||||
modified: src/bin/dhcp4/dhcp4_srv.cc
|
||||
modified: src/bin/dhcp4/tests/fqdn_unittest.cc
|
||||
modified: src/bin/dhcp6/dhcp6_messages.cc
|
||||
modified: src/bin/dhcp6/dhcp6_messages.h
|
||||
modified: src/bin/dhcp6/dhcp6_messages.mes
|
||||
modified: src/bin/dhcp6/dhcp6_srv.cc
|
||||
modified: src/bin/dhcp6/tests/fqdn_unittest.cc
|
||||
modified: src/lib/dhcpsrv/d2_client_mgr.cc
|
||||
modified: src/lib/dhcpsrv/d2_client_mgr.h
|
||||
modified: src/lib/dhcpsrv/tests/d2_client_unittest.cc
|
||||
---
|
||||
...-2025-11232-catch-empty-sanitized-hostname | 6 +++
|
||||
src/bin/dhcp4/dhcp4_messages.cc | 4 ++
|
||||
src/bin/dhcp4/dhcp4_messages.h | 2 +
|
||||
src/bin/dhcp4/dhcp4_messages.mes | 14 +++++
|
||||
src/bin/dhcp4/dhcp4_srv.cc | 21 ++++++--
|
||||
src/bin/dhcp4/tests/fqdn_unittest.cc | 54 ++++++++++++++++++-
|
||||
src/bin/dhcp6/dhcp6_messages.cc | 2 +
|
||||
src/bin/dhcp6/dhcp6_messages.h | 1 +
|
||||
src/bin/dhcp6/dhcp6_messages.mes | 7 +++
|
||||
src/bin/dhcp6/dhcp6_srv.cc | 9 +++-
|
||||
src/bin/dhcp6/tests/fqdn_unittest.cc | 23 ++++++++
|
||||
src/lib/dhcpsrv/d2_client_mgr.cc | 9 +++-
|
||||
src/lib/dhcpsrv/d2_client_mgr.h | 19 ++++++-
|
||||
src/lib/dhcpsrv/tests/d2_client_unittest.cc | 42 +++++++++++++++
|
||||
14 files changed, 205 insertions(+), 8 deletions(-)
|
||||
create mode 100644 changelog_unreleased/CVE-2025-11232-catch-empty-sanitized-hostname
|
||||
|
||||
diff --git a/src/bin/dhcp4/dhcp4_messages.cc b/src/bin/dhcp4/dhcp4_messages.cc
|
||||
index e06ce6a121..5c6a334bad 100644
|
||||
--- a/src/bin/dhcp4/dhcp4_messages.cc
|
||||
+++ b/src/bin/dhcp4/dhcp4_messages.cc
|
||||
@@ -26,9 +26,11 @@ extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED = "DHCP4_CLASS_UNCONFI
|
||||
extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES = "DHCP4_CLIENTID_IGNORED_FOR_LEASES";
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA = "DHCP4_CLIENT_FQDN_DATA";
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS = "DHCP4_CLIENT_FQDN_PROCESS";
|
||||
+extern const isc::log::MessageID DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY = "DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY";
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_DATA = "DHCP4_CLIENT_HOSTNAME_DATA";
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_MALFORMED = "DHCP4_CLIENT_HOSTNAME_MALFORMED";
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_PROCESS = "DHCP4_CLIENT_HOSTNAME_PROCESS";
|
||||
+extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY = "DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY";
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_NAME_PROC_FAIL = "DHCP4_CLIENT_NAME_PROC_FAIL";
|
||||
extern const isc::log::MessageID DHCP4_CONFIG_COMPLETE = "DHCP4_CONFIG_COMPLETE";
|
||||
extern const isc::log::MessageID DHCP4_CONFIG_LOAD_FAIL = "DHCP4_CONFIG_LOAD_FAIL";
|
||||
@@ -206,9 +208,11 @@ const char* values[] = {
|
||||
"DHCP4_CLIENTID_IGNORED_FOR_LEASES", "%1: not using client identifier for lease allocation for subnet %2",
|
||||
"DHCP4_CLIENT_FQDN_DATA", "%1: Client sent FQDN option: %2",
|
||||
"DHCP4_CLIENT_FQDN_PROCESS", "%1: processing Client FQDN option",
|
||||
+ "DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY", "%1: sanitizing client's FQDN option '%2' yielded an empty string",
|
||||
"DHCP4_CLIENT_HOSTNAME_DATA", "%1: client sent Hostname option: %2",
|
||||
"DHCP4_CLIENT_HOSTNAME_MALFORMED", "%1: client hostname option malformed: %2",
|
||||
"DHCP4_CLIENT_HOSTNAME_PROCESS", "%1: processing client's Hostname option",
|
||||
+ "DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY", "%1: sanitizing client's Hostname option '%2' yielded an empty string",
|
||||
"DHCP4_CLIENT_NAME_PROC_FAIL", "%1: failed to process the fqdn or hostname sent by a client: %2",
|
||||
"DHCP4_CONFIG_COMPLETE", "DHCPv4 server has completed configuration: %1",
|
||||
"DHCP4_CONFIG_LOAD_FAIL", "configuration error using file: %1, reason: %2",
|
||||
diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h
|
||||
index 9a4d0cda21..6e45c63053 100644
|
||||
--- a/src/bin/dhcp4/dhcp4_messages.h
|
||||
+++ b/src/bin/dhcp4/dhcp4_messages.h
|
||||
@@ -27,9 +27,11 @@ extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED;
|
||||
extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES;
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA;
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS;
|
||||
+extern const isc::log::MessageID DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY;
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_DATA;
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_MALFORMED;
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_PROCESS;
|
||||
+extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY;
|
||||
extern const isc::log::MessageID DHCP4_CLIENT_NAME_PROC_FAIL;
|
||||
extern const isc::log::MessageID DHCP4_CONFIG_COMPLETE;
|
||||
extern const isc::log::MessageID DHCP4_CONFIG_LOAD_FAIL;
|
||||
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
|
||||
index 1deb2e6074..b359d09616 100644
|
||||
--- a/src/bin/dhcp4/dhcp4_messages.mes
|
||||
+++ b/src/bin/dhcp4/dhcp4_messages.mes
|
||||
@@ -164,6 +164,20 @@ This debug message is issued when the server starts processing the Hostname
|
||||
option sent in the client's query. The argument includes the client and
|
||||
transaction identification information.
|
||||
|
||||
+% DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY %1: sanitizing client's Hostname option '%2' yielded an empty string
|
||||
+Logged at debug log level 50.
|
||||
+This debug message is issued when the result of sanitizing the
|
||||
+hostname option(12) sent by the client is an empty string. When this occurs
|
||||
+the server will ignore the hostname option. The arguments include the
|
||||
+client and the hostname option it sent.
|
||||
+
|
||||
+% DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY %1: sanitizing client's FQDN option '%2' yielded an empty string
|
||||
+Logged at debug log level 50.
|
||||
+This debug message is issued when the result of sanitizing the
|
||||
+FQDN option(81) sent by the client is an empty string. When this occurs
|
||||
+the server will ignore the FQDN option. The arguments include the
|
||||
+client and the FQDN option it sent.
|
||||
+
|
||||
% DHCP4_CLIENT_NAME_PROC_FAIL %1: failed to process the fqdn or hostname sent by a client: %2
|
||||
Logged at debug log level 55.
|
||||
This debug message is issued when the DHCP server was unable to process the
|
||||
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
|
||||
index 0701ed41e9..a6be662889 100644
|
||||
--- a/src/bin/dhcp4/dhcp4_srv.cc
|
||||
+++ b/src/bin/dhcp4/dhcp4_srv.cc
|
||||
@@ -2714,8 +2714,15 @@ Dhcpv4Srv::processClientFqdnOption(Dhcpv4Exchange& ex) {
|
||||
} else {
|
||||
// Adjust the domain name based on domain name value and type sent by the
|
||||
// client and current configuration.
|
||||
- d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp,
|
||||
- *(ex.getContext()->getDdnsParams()));
|
||||
+ try {
|
||||
+ d2_mgr.adjustDomainName<Option4ClientFqdn>(*fqdn, *fqdn_resp,
|
||||
+ *(ex.getContext()->getDdnsParams()));
|
||||
+ } catch (const FQDNScrubbedEmpty& scrubbed) {
|
||||
+ LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY)
|
||||
+ .arg(ex.getQuery()->getLabel())
|
||||
+ .arg(scrubbed.what());
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
|
||||
// Add FQDN option to the response message. Note that, there may be some
|
||||
@@ -2857,7 +2864,15 @@ Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) {
|
||||
ex.getContext()->getDdnsParams()->getHostnameSanitizer();
|
||||
|
||||
if (sanitizer) {
|
||||
- hostname = sanitizer->scrub(hostname);
|
||||
+ auto tmp = sanitizer->scrub(hostname);
|
||||
+ if (tmp.empty()) {
|
||||
+ LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY)
|
||||
+ .arg(ex.getQuery()->getLabel())
|
||||
+ .arg(hostname);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ hostname = tmp;
|
||||
}
|
||||
|
||||
// Convert hostname to lower case.
|
||||
diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc
|
||||
index a5d3e4c21a..18e4c6d4b9 100644
|
||||
--- a/src/bin/dhcp4/tests/fqdn_unittest.cc
|
||||
+++ b/src/bin/dhcp4/tests/fqdn_unittest.cc
|
||||
@@ -2253,7 +2253,7 @@ TEST_F(NameDhcpv4SrvTest, sanitizeHostDefault) {
|
||||
},
|
||||
{
|
||||
"qualified host name with nuls",
|
||||
- std::string("four-ok-host\000.other.org",23),
|
||||
+ std::string("four-ok-host\000.other.org", 23),
|
||||
"four-ok-host.other.org"
|
||||
}
|
||||
};
|
||||
@@ -3203,4 +3203,56 @@ TEST_F(NameDhcpv4SrvTest, poolDdnsParametersTest) {
|
||||
}
|
||||
}
|
||||
|
||||
+// Verifies that when the FQDN option is scrubbed empty it is logged
|
||||
+// and ignored.
|
||||
+TEST_F(NameDhcpv4SrvTest, hostnameScrubbedEmpty) {
|
||||
+ Dhcp4Client client(srv_, Dhcp4Client::SELECTING);
|
||||
+
|
||||
+ // Configure DHCP server.
|
||||
+ configure(CONFIGS[2], *client.getServer());
|
||||
+
|
||||
+ // Set the hostname option.
|
||||
+ ASSERT_NO_THROW(client.includeHostname("___"));
|
||||
+
|
||||
+ // Send the DHCPDISCOVER and make sure that the server responded.
|
||||
+ ASSERT_NO_THROW(client.doDiscover());
|
||||
+ auto resp = client.getContext().response_;
|
||||
+ ASSERT_TRUE(resp);
|
||||
+ ASSERT_EQ(DHCPOFFER, static_cast<int>(resp->getType()));
|
||||
+
|
||||
+ // Should have logged that it was scrubbed empty.
|
||||
+ std::string log = "DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY";
|
||||
+ EXPECT_EQ(1, countFile(log));
|
||||
+
|
||||
+ // Hostname should not be in the response.
|
||||
+ ASSERT_FALSE(resp->getOption(DHO_HOST_NAME));
|
||||
+}
|
||||
+
|
||||
+// Verifies that when the FQDN option is scrubbed empty it is logged
|
||||
+// and ignored.
|
||||
+TEST_F(NameDhcpv4SrvTest, fqdnScrubbedEmpty) {
|
||||
+ Dhcp4Client client(srv_, Dhcp4Client::SELECTING);
|
||||
+
|
||||
+ // Configure DHCP server.
|
||||
+ configure(CONFIGS[2], *client.getServer());
|
||||
+
|
||||
+ // Include the Client FQDN option.
|
||||
+ ASSERT_NO_THROW(client.includeFQDN(Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E,
|
||||
+ "___", Option4ClientFqdn::PARTIAL));
|
||||
+
|
||||
+ // Send the DHCPDISCOVER and make sure that the server responded.
|
||||
+ ASSERT_NO_THROW(client.doDiscover());
|
||||
+ auto resp = client.getContext().response_;
|
||||
+ ASSERT_TRUE(resp);
|
||||
+ ASSERT_EQ(DHCPOFFER, static_cast<int>(resp->getType()));
|
||||
+
|
||||
+ // Should have logged that it was scrubbed empty.
|
||||
+ std::string log = "DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY";
|
||||
+ EXPECT_EQ(1, countFile(log));
|
||||
+
|
||||
+ // Hostname should not be in the response.
|
||||
+ ASSERT_FALSE(resp->getOption(DHO_FQDN));
|
||||
+}
|
||||
+
|
||||
+
|
||||
} // end of anonymous namespace
|
||||
diff --git a/src/bin/dhcp6/dhcp6_messages.cc b/src/bin/dhcp6/dhcp6_messages.cc
|
||||
index 229ba74450..9619481aba 100644
|
||||
--- a/src/bin/dhcp6/dhcp6_messages.cc
|
||||
+++ b/src/bin/dhcp6/dhcp6_messages.cc
|
||||
@@ -27,6 +27,7 @@ extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED = "DHCP6_CLASSES_ASSIGNE
|
||||
extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION = "DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION";
|
||||
extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED = "DHCP6_CLASS_ASSIGNED";
|
||||
extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED = "DHCP6_CLASS_UNCONFIGURED";
|
||||
+extern const isc::log::MessageID DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY = "DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY";
|
||||
extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE = "DHCP6_CONFIG_COMPLETE";
|
||||
extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL = "DHCP6_CONFIG_LOAD_FAIL";
|
||||
extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE = "DHCP6_CONFIG_PACKET_QUEUE";
|
||||
@@ -203,6 +204,7 @@ const char* values[] = {
|
||||
"DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION", "%1: client packet has been assigned to the following classes: %2",
|
||||
"DHCP6_CLASS_ASSIGNED", "%1: client packet has been assigned to the following class: %2",
|
||||
"DHCP6_CLASS_UNCONFIGURED", "%1: client packet belongs to an unconfigured class: %2",
|
||||
+ "DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY", "%1: sanitizing client's FQDN option '%2' yielded an empty string",
|
||||
"DHCP6_CONFIG_COMPLETE", "DHCPv6 server has completed configuration: %1",
|
||||
"DHCP6_CONFIG_LOAD_FAIL", "configuration error using file: %1, reason: %2",
|
||||
"DHCP6_CONFIG_PACKET_QUEUE", "DHCPv6 packet queue info after configuration: %1",
|
||||
diff --git a/src/bin/dhcp6/dhcp6_messages.h b/src/bin/dhcp6/dhcp6_messages.h
|
||||
index 186f7d557a..7af56e716a 100644
|
||||
--- a/src/bin/dhcp6/dhcp6_messages.h
|
||||
+++ b/src/bin/dhcp6/dhcp6_messages.h
|
||||
@@ -28,6 +28,7 @@ extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED;
|
||||
extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION;
|
||||
extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED;
|
||||
extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED;
|
||||
+extern const isc::log::MessageID DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY;
|
||||
extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE;
|
||||
extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL;
|
||||
extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE;
|
||||
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
|
||||
index fff50ed367..79fc984ff5 100644
|
||||
--- a/src/bin/dhcp6/dhcp6_messages.mes
|
||||
+++ b/src/bin/dhcp6/dhcp6_messages.mes
|
||||
@@ -1167,3 +1167,10 @@ such modification. The clients will remember previous server-id, and will
|
||||
use it to extend their leases. As a result, they will have to go through
|
||||
a rebinding phase to re-acquire their leases and associate them with a
|
||||
new server id.
|
||||
+
|
||||
+% DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY %1: sanitizing client's FQDN option '%2' yielded an empty string
|
||||
+Logged at debug log level 50.
|
||||
+This debug message is issued when the result of sanitizing the
|
||||
+FQDN option(39) sent by the client is an empty string. When this occurs
|
||||
+the server will ignore the FQDN option. The arguments include the
|
||||
+client and the FQDN option it sent.
|
||||
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
|
||||
index 417960b126..f999c3178f 100644
|
||||
--- a/src/bin/dhcp6/dhcp6_srv.cc
|
||||
+++ b/src/bin/dhcp6/dhcp6_srv.cc
|
||||
@@ -2332,7 +2332,14 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
|
||||
} else {
|
||||
// Adjust the domain name based on domain name value and type sent by
|
||||
// the client and current configuration.
|
||||
- d2_mgr.adjustDomainName<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
|
||||
+ try {
|
||||
+ d2_mgr.adjustDomainName<Option6ClientFqdn>(*fqdn, *fqdn_resp, *ddns_params);
|
||||
+ } catch(const FQDNScrubbedEmpty& scrubbed) {
|
||||
+ LOG_DEBUG(ddns6_logger, DBG_DHCP6_DETAIL, DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY)
|
||||
+ .arg(question->getLabel())
|
||||
+ .arg(scrubbed.what());
|
||||
+ return;
|
||||
+ }
|
||||
}
|
||||
|
||||
// Once we have the FQDN setup to use it for the lease hostname. This
|
||||
diff --git a/src/bin/dhcp6/tests/fqdn_unittest.cc b/src/bin/dhcp6/tests/fqdn_unittest.cc
|
||||
index ca51856e67..7891c1f5e6 100644
|
||||
--- a/src/bin/dhcp6/tests/fqdn_unittest.cc
|
||||
+++ b/src/bin/dhcp6/tests/fqdn_unittest.cc
|
||||
@@ -2425,4 +2425,27 @@ TEST_F(FqdnDhcpv6SrvTest, poolDdnsParametersTest) {
|
||||
}
|
||||
}
|
||||
|
||||
+// Verify an FQDN with all invalid chars is ignored.
|
||||
+TEST_F(FqdnDhcpv6SrvTest, fqdnScrubbedEmpty) {
|
||||
+ // Create the query.
|
||||
+ Pkt6Ptr question = generateMessage(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S,
|
||||
+ "___" , Option6ClientFqdn::FULL, true);
|
||||
+ ASSERT_TRUE(getClientFqdnOption(question));
|
||||
+ subnet_->setHostnameCharReplacement("");
|
||||
+
|
||||
+ // Create the response with an "assigned" lease.
|
||||
+ // Set the selected subnet so ddns params get returned correctly.
|
||||
+ AllocEngine::ClientContext6 ctx;
|
||||
+ ctx.subnet_ = subnet_;
|
||||
+ Pkt6Ptr answer = generateMessageWithIds(DHCPV6_ADVERTISE);
|
||||
+ addIA(1234, IOAddress("2001:db8:1::1"), answer, ctx);
|
||||
+
|
||||
+ // Process the client's FQDN.
|
||||
+ ASSERT_NO_THROW(srv_->processClientFqdn(question, answer, ctx));
|
||||
+
|
||||
+ // Should not have an FQDN option in the answer.
|
||||
+ EXPECT_FALSE(answer->getOption(D6O_CLIENT_FQDN));
|
||||
+ countFile("DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY");
|
||||
+}
|
||||
+
|
||||
} // end of anonymous namespace
|
||||
diff --git a/src/lib/dhcpsrv/d2_client_mgr.cc b/src/lib/dhcpsrv/d2_client_mgr.cc
|
||||
index 84ee11d9fb..54c815176e 100644
|
||||
--- a/src/lib/dhcpsrv/d2_client_mgr.cc
|
||||
+++ b/src/lib/dhcpsrv/d2_client_mgr.cc
|
||||
@@ -186,10 +186,15 @@ std::string
|
||||
D2ClientMgr::qualifyName(const std::string& partial_name,
|
||||
const DdnsParams& ddns_params,
|
||||
const bool trailing_dot) const {
|
||||
+ if (partial_name.empty()) {
|
||||
+ isc_throw(BadValue, "D2ClientMgr::qualifyName"
|
||||
+ " - partial_name cannot be an empty string");
|
||||
+ }
|
||||
+
|
||||
std::ostringstream gen_name;
|
||||
gen_name << partial_name;
|
||||
std::string suffix = ddns_params.getQualifyingSuffix();
|
||||
- if (!suffix.empty() && partial_name.back() != '.') {
|
||||
+ if (!suffix.empty() && (partial_name.back() != '.')) {
|
||||
bool suffix_present = true;
|
||||
std::string str = gen_name.str();
|
||||
auto suffix_rit = suffix.rbegin();
|
||||
@@ -241,7 +246,7 @@ D2ClientMgr::qualifyName(const std::string& partial_name,
|
||||
// If the trailing dot should not be appended but it is present,
|
||||
// remove it.
|
||||
if ((len > 0) && (str[len - 1] == '.')) {
|
||||
- gen_name.str(str.substr(0,len-1));
|
||||
+ gen_name.str(str.substr(0, len-1));
|
||||
}
|
||||
|
||||
}
|
||||
diff --git a/src/lib/dhcpsrv/d2_client_mgr.h b/src/lib/dhcpsrv/d2_client_mgr.h
|
||||
index 7344f19a40..238fd0a415 100644
|
||||
--- a/src/lib/dhcpsrv/d2_client_mgr.h
|
||||
+++ b/src/lib/dhcpsrv/d2_client_mgr.h
|
||||
@@ -30,6 +30,14 @@
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
+/// @brief Exception thrown when host name sanitizing reduces
|
||||
+/// the domain name to an empty string.
|
||||
+class FQDNScrubbedEmpty : public Exception {
|
||||
+public:
|
||||
+ FQDNScrubbedEmpty(const char* file, size_t line, const char* what) :
|
||||
+ isc::Exception(file, line, what) { }
|
||||
+};
|
||||
+
|
||||
/// @brief Defines the type for D2 IO error handler.
|
||||
/// This callback is invoked when a send to kea-dhcp-ddns completes with a
|
||||
/// failed status. This provides the application layer (Kea) with a means to
|
||||
@@ -197,6 +205,7 @@ class D2ClientMgr : public dhcp_ddns::NameChangeSender::RequestSendHandler,
|
||||
/// suffix itself is empty (i.e. "").
|
||||
///
|
||||
/// @return std::string containing the qualified name.
|
||||
+ /// @throw BadValue if partial_name is empty.
|
||||
std::string qualifyName(const std::string& partial_name,
|
||||
const DdnsParams& ddns_params,
|
||||
const bool trailing_dot) const;
|
||||
@@ -264,6 +273,9 @@ class D2ClientMgr : public dhcp_ddns::NameChangeSender::RequestSendHandler,
|
||||
/// @param ddns_params DDNS behavioral configuration parameters
|
||||
/// @tparam T FQDN Option class containing the FQDN data such as
|
||||
/// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
|
||||
+ ///
|
||||
+ /// @throw FQDNScrubbedEmpty if hostname sanitizing reduces the input domain
|
||||
+ /// name to an empty string.
|
||||
template <class T>
|
||||
void adjustDomainName(const T& fqdn, T& fqdn_resp,
|
||||
const DdnsParams& ddns_params);
|
||||
@@ -515,7 +527,12 @@ D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp, const DdnsParams& ddn
|
||||
ss << sanitizer->scrub(label);
|
||||
}
|
||||
|
||||
- client_name = ss.str();
|
||||
+ std::string clean_name = ss.str();
|
||||
+ if (clean_name.empty() || clean_name == ".") {
|
||||
+ isc_throw(FQDNScrubbedEmpty, client_name);
|
||||
+ }
|
||||
+
|
||||
+ client_name = clean_name;
|
||||
}
|
||||
|
||||
// If the supplied name is partial, qualify it by adding the suffix.
|
||||
diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
|
||||
index 68ad2189d6..00375d0066 100644
|
||||
--- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc
|
||||
+++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <dhcp/option6_client_fqdn.h>
|
||||
#include <dhcpsrv/d2_client_mgr.h>
|
||||
#include <testutils/test_to_element.h>
|
||||
+#include <testutils/gtest_utils.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/str.h>
|
||||
|
||||
@@ -627,6 +628,10 @@ TEST_F(D2ClientMgrParamsTest, qualifyName) {
|
||||
qualified_name = mgr.qualifyName(partial_name, *ddns_params_, do_not_dot);
|
||||
EXPECT_EQ("somehost.suffix.com", qualified_name);
|
||||
|
||||
+ // Verify that an empty name throws.
|
||||
+ partial_name = "";
|
||||
+ ASSERT_THROW(mgr.qualifyName(partial_name, *ddns_params_, do_not_dot), BadValue);
|
||||
+
|
||||
// Verify that an empty suffix and false flag, does not change the name
|
||||
subnet_->setDdnsQualifyingSuffix("");
|
||||
partial_name = "somehost";
|
||||
@@ -1257,4 +1262,41 @@ TEST_F(D2ClientMgrParamsTest, sanitizeFqdnV6) {
|
||||
}
|
||||
}
|
||||
|
||||
+/// @brief Tests adjustDomainName template method with Option4ClientFqdn
|
||||
+/// when sanitizing scrubs input name empty.
|
||||
+TEST_F(D2ClientMgrParamsTest, adjustDomainNameV4ScrubbedEmpty) {
|
||||
+ D2ClientMgr mgr;
|
||||
+
|
||||
+ // Create enabled configuration
|
||||
+ subnet_->setDdnsSendUpdates(false);
|
||||
+ subnet_->setDdnsQualifyingSuffix("suffix.com");
|
||||
+ subnet_->setHostnameCharSet("[^A-Za-z0-9.-]");
|
||||
+ subnet_->setHostnameCharReplacement("");
|
||||
+
|
||||
+ Option4ClientFqdn request(0, Option4ClientFqdn::RCODE_CLIENT(),
|
||||
+ "___", Option4ClientFqdn::FULL);
|
||||
+
|
||||
+ Option4ClientFqdn response(request);
|
||||
+ ASSERT_THROW_MSG(mgr.adjustDomainName<Option4ClientFqdn>(request, response, *ddns_params_),
|
||||
+ FQDNScrubbedEmpty, "___.");
|
||||
+}
|
||||
+
|
||||
+/// @brief Tests adjustDomainName template method with Option4ClientFqdn
|
||||
+/// when sanitizing scrubs input name empty.
|
||||
+TEST_F(D2ClientMgrParamsTest, adjustDomainNameV6ScrubbedEmpty) {
|
||||
+ D2ClientMgr mgr;
|
||||
+
|
||||
+ // Create enabled configuration
|
||||
+ subnet_->setDdnsSendUpdates(false);
|
||||
+ subnet_->setDdnsQualifyingSuffix("suffix.com");
|
||||
+ subnet_->setHostnameCharSet("[^A-Za-z0-9.-]");
|
||||
+ subnet_->setHostnameCharReplacement("");
|
||||
+
|
||||
+ Option6ClientFqdn request(0, "___", Option6ClientFqdn::FULL);
|
||||
+
|
||||
+ Option6ClientFqdn response(request);
|
||||
+ ASSERT_THROW_MSG(mgr.adjustDomainName<Option6ClientFqdn>(request, response, *ddns_params_),
|
||||
+ FQDNScrubbedEmpty, "___.");
|
||||
+}
|
||||
+
|
||||
} // end of anonymous namespace
|
||||
@ -21,6 +21,7 @@ SRC_URI = "http://ftp.isc.org/isc/kea/${PV}/${BP}.tar.xz \
|
||||
file://0001-meson-use-a-runtime-safe-interpreter-string.patch \
|
||||
file://0001-mk_cfgrpt.sh-strip-prefixes.patch \
|
||||
file://0001-d2-dhcp-46-radius-dhcpsrv-Avoid-Boost-lexical_cast-o.patch \
|
||||
file://CVE-2025-11232.patch \
|
||||
"
|
||||
SRC_URI[sha256sum] = "ec84fec4bb7f6b9d15a82e755a571e9348eb4d6fbc62bb3f6f1296cd7a24c566"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user