kea/CVE-2025-11232.patch

181 lines
8.1 KiB
Diff

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/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/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 @@ public:
/// 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 @@ public:
/// @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.