From: Thomas Markwalder Date: Thu, 7 Aug 2025 18:14:27 +0000 (-0400) Subject: [#4048] Backport CVE-2025-40779 fix X-Git-Tag: Kea-3.0.1~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b25d7e8a81273e4099bf6c7f639ed774de2f3d08;p=thirdparty%2Fkea.git [#4048] Backport CVE-2025-40779 fix new file: changelog_unreleased/CVE-2025-40779-Kea-crash-on-108-in-PRL modified: src/bin/dhcp4/dhcp4_srv.cc modified: src/bin/dhcp4/tests/dhcp4_srv_unittest.cc modified: src/bin/dhcp4/tests/dhcp4_test_utils.h --- diff --git a/changelog_unreleased/CVE-2025-40779-Kea-crash-on-108-in-PRL b/changelog_unreleased/CVE-2025-40779-Kea-crash-on-108-in-PRL new file mode 100644 index 0000000000..22a04cc550 --- /dev/null +++ b/changelog_unreleased/CVE-2025-40779-Kea-crash-on-108-in-PRL @@ -0,0 +1,7 @@ +[sec] tmark + Corrected an issue in kea-dhcp4 that caused + the server to exit after receiving a broadcast + query that requests option 108 and fails to + match a subnet. + CVE:2025-40779 + (Gitlab #4048) diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 730afa3fe4..0701ed41e9 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -775,7 +775,6 @@ Dhcpv4Srv::shutdown() { isc::dhcp::ConstSubnet4Ptr Dhcpv4Srv::selectSubnet(const Pkt4Ptr& query, bool& drop, bool sanity_only, bool allow_answer_park) { - // DHCPv4-over-DHCPv6 is a special (and complex) case if (query->isDhcp4o6()) { return (selectSubnet4o6(query, drop, sanity_only, allow_answer_park)); @@ -2969,21 +2968,23 @@ Dhcpv4Srv::assignLease(Dhcpv4Exchange& ex) { // allocation. bool fake_allocation = (query->getType() == DHCPDISCOVER); - // Check if IPv6-Only Preferred was requested. - OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast< - OptionUint8Array>(query->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST)); - if (option_prl) { - auto const& requested_opts = option_prl->getValues(); - if ((std::find(requested_opts.cbegin(), requested_opts.cend(), - DHO_V6_ONLY_PREFERRED) != requested_opts.cend()) && - assignZero(subnet, query->getClasses())) { - ex.setIPv6OnlyPreferred(true); - ctx->subnet_ = subnet; - resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS()); - if (!fake_allocation) { - resp->setCiaddr(query->getCiaddr()); + if (subnet) { + // Check if IPv6-Only Preferred was requested. + OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast< + OptionUint8Array>(query->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST)); + if (option_prl) { + auto const& requested_opts = option_prl->getValues(); + if ((std::find(requested_opts.cbegin(), requested_opts.cend(), + DHO_V6_ONLY_PREFERRED) != requested_opts.cend()) && + assignZero(subnet, query->getClasses())) { + ex.setIPv6OnlyPreferred(true); + ctx->subnet_ = subnet; + resp->setYiaddr(IOAddress::IPV4_ZERO_ADDRESS()); + if (!fake_allocation) { + resp->setCiaddr(query->getCiaddr()); + } + return; } - return; } } diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 43bd2aa947..1310406e92 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -6265,6 +6265,38 @@ TEST_F(Dhcpv4SrvTest, v6OnlyPreferredRequestError) { EXPECT_FALSE(srv_->processRequest(req)); } +// Verify that a discover requesting v6-only-preferred +// is handled gracefully when there is no matching subnet. +TEST_F(Dhcpv4SrvTest, v6OnlyPreferredNoSelectedSubnet) { + IfaceMgrTestConfig test_config(true); + IfaceMgr::instance().openSockets4(); + + Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234)); + OptionPtr clientid = generateClientId(); + dis->addOption(clientid); + // Sort source address to non-matching subnet. + dis->setRemoteAddr(IOAddress("192.0.99.1")); + dis->setIface("eth1"); + dis->setIndex(ETH1_INDEX); + + // Add a PRL with v6-only-preferred. + OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4, + DHO_DHCP_PARAMETER_REQUEST_LIST)); + ASSERT_TRUE(prl); + prl->addValue(DHO_V6_ONLY_PREFERRED); + dis->addOption(prl); + + // Should not have gotten a response. + Pkt4Ptr resp = srv_->processDiscover(dis); + ASSERT_FALSE(resp); + + // Should have logged a NAK_0001. + EXPECT_EQ(1, countFile("DHCP4_PACKET_NAK_0001 [hwtype=1 ], cid=[64:65:66:67]," + " tid=0x4d2: failed to select a subnet for incoming packet," + " src 192.0.99.1, type DHCPDISCOVER")); + +} + /// @brief Test fixture for recoverStashedAgentOption. class StashAgentOptionTest : public Dhcpv4SrvTest { public: diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index c4867adcd1..2422341369 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -333,7 +334,7 @@ class Dhcp4Client; /// Currently it configures the test data path directory in /// the @c CfgMgr. When the object is destroyed, the original /// path is reverted. -class BaseServerTest : public ::testing::Test { +class BaseServerTest : public LogContentTest { public: /// @brief Constructor.