Christoph Biedl reported: isc-dchpd aborts when receiving a DHCP request with a "dhcp-client- identifier" in certain configurations. This requires mixing host definitions using "dhcp-client-identifier" and "hardware ethernet", for a host that is not reachable via the interface the request is received from. Christoph confirmed the problem in versions 3.1.1, 3.1.2, 4.0.1, 4.0.2b1, and 4.1.1b1, and says 3.0.7 is not affected.
Full report from Christoph: Summary ======= isc-dchpd aborts when receiving a DHCP request with a "dhcp-client- identifier" in certain configurations. This requires mixing host definitions using "dhcp-client-identifier" and "hardware ethernet", for a host that is not reachable via the interface the request is received from. Versions affected ================= 3.1.1 (as in Debian lenny = stable) 3.1.2 4.0.1 4.0.2b1 4.1.1b1 Versions not affected ===================== 3.0.7 How to repeat ============= Configure dhcpd to listen on (at least) two interfaces. While this is not essential (see below), it reflects a real-world scenario. For the sake of this examples the interfaces are vlan225 and vlan226, configured as 10.1.225.1/24 and 10.1.226.1/24 respectively. Use the following server configuration that has two host definitions in vlan226's network: +---------------------------------------------------------- ddns-update-style none; default-lease-time 60; max-lease-time 720; allow bootp; authoritative; log-facility local7; subnet 10.1.225.0 netmask 255.255.255.0 { range dynamic-bootp 10.1.225.10 10.1.225.20; } subnet 10.1.226.0 netmask 255.255.255.0 { range dynamic-bootp 10.1.226.10 10.1.226.20; } host linux { option dhcp-client-identifier "linux"; fixed-address 10.1.226.31; } host ether { hardware ethernet 00:10:11:12:13:14; fixed-address 10.1.226.32; } +---------------------------------------------------------- Start dhcpd, e.g. "/usr/sbin/dhcpd -q vlan225 vlan226" Configure a client to request an IP address using that identifier, i.e. add 'send dhcp-client-identifier "linux";' to the dhclient.conf if using dhcp3-client. Request an IP via vlan225, the "host linux" definition will _not_ match since it's in the wrong network. Expected behavior: dhcpd issues an address from the pool like 10.1.225.10 as the host definition is not applicable: | dhcpd: DHCPDISCOVER from 00:00:0b:ad:be:ef via vlan225 | dhcpd: DHCPOFFER on 10.1.225.10 to 00:00:0b:ad:be:ef via vlan225 | dhcpd: DHCPREQUEST for 10.1.225.10 (10.1.225.1) from 00:00:0b:ad:be:ef via vlan225 | dhcpd: DHCPACK on 10.1.225.10 to 00:00:0b:ad:be:ef via vlan225 Observed behavior: dhcpd terminates, de-facto assertion failure: | dhcpd: Internal inconsistency: storage value has not been initialized to zero (from dhcp.c:1746). Impact ====== An attacker can kill the dhcp server using a regular DHCP packet, causing a denial of service attack. Note ==== The problem is in the lookup code that cannot deal with a "dhcp- client-identifier" host statement in an unreachable network iff both "dhcp-client-identifier" and "hardware ethernet" host definitions are given. The server still terminates if the "subnet 10.1.226.0" statement in the example above is missing, and if dhcpd listens on vlan225 only. Such a configuration doesn't make sense but may happen due to a typo. Workarounds =========== * Avoid mixing "dhcp-client-identifier" and "hardware ethernet" host definitions if possible. * Separate dhcpd instances for each interface, beware of pidfiles and similar (not tried). Patch ===== The following patch (against 3.1.1, similar for the other versions) seems to fix that problem but may have unknown side effects. diff --git a/server/dhcp.c b/server/dhcp.c index 8b97312..ce95c3b 100644 --- a/server/dhcp.c +++ b/server/dhcp.c @@ -1745,6 +1745,8 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) } if (h) host_reference (&host, h, MDL); + else + hp = (struct host_decl *)0; } if (!host) { find_hosts_by_haddr (&hp,
This should not affect dhcp 3.0.x and older, as the abort in this case is triggered by the '*vp != NULL' check, which was added to hash_lookup() in omapip/hash.c, probably some time during the 3.1.x development. Versions of dhcp with the check is currently only available in Fedora. This check "missing" in previous versions should mean no harm in this case. In ack_lease() in server/dhcp.c, hp is first initialized to 0/NULL. 1750 struct host_decl *hp = (struct host_decl *)0; Then dhcp-client-identifier lookup is performed, that may set hp to non-0. 1765 find_hosts_by_uid (&hp, d1.data, d1.len, MDL); Returned hp is checked as 1767 for (h = hp; h; h = h -> n_ipaddr) { 1768 if (!h -> fixed_addr) 1769 break; 1770 } and h is set as host if it is non-NULL after this loop 1771 if (h) 1772 host_reference (&host, h, MDL); However, is h (and hence host) is 0 / NULL, hardware address search is done 1774 if (!host) { 1775 find_hosts_by_haddr (&hp, which results in call to hash_lookup(), where abort occurs in recent dhcpd versions. In old versions, without the check, hp value is either overwritten with new result (if any definition for the hw address is found), or the hp is not changed by find_hosts_by_haddr and hence following for loop will leave h set to 0 (it's identical to the one above) 1780 for (h = hp; h; h = h -> n_ipaddr) { 1781 if (!h -> fixed_addr) 1782 break; 1783 }
Updated proposed patch based on my discussion with Christoph: --- a/server/dhcp.c +++ b/server/dhcp.c @@ -1747,6 +1747,8 @@ void ack_lease (packet, lease, offer, when, msg, ms_nulltp, hp) host_reference (&host, h, MDL); } if (!host) { + if (hp) + host_dereference (&hp, MDL); find_hosts_by_haddr (&hp, packet -> raw -> htype, packet -> raw -> chaddr,
Public now via DSA-1833: http://www.debian.org/security/2009/dsa-1833
Red Hat Enterprise Linux 5 and earlier provide ISC DHCP <= 3.0.5, which are not vulnerable to this issue.
MITRE's CVE-2009-1892 entry: dhcpd in ISC DHCP 3.0.4 and 3.1.1, when the dhcp-client-identifier and hardware ethernet configuration settings are both used, allows remote attackers to cause a denial of service (daemon crash) via unspecified requests. References: ---------- http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-1892 http://www.debian.org/security/2009/dsa-1833 http://www.securityfocus.com/bid/3566 http://secunia.com/advisories/35830 http://secunia.com/advisories/35851 http://xforce.iss.net/xforce/xfdb/51717
(In reply to comment #14) > dhcpd in ISC DHCP 3.0.4 and 3.1.1 This part is incorrect, this flaw does not affect any 3.0.x version. Check triggering this abort is not present in 3.0.x code base (it is not in the latest 3.0.x versions - 3.0.7 - and is in the first 3.1.x version available for download from isc.org - 3.1.0a1). Further discussion is in comment #2. Additionally, if hp is set during the call to find_hosts_by_uid(), but it is not used (no match in the mentioned for loop), following call to find_hosts_by_haddr() will never change it. find_hosts_by_haddr() intenally calls hash_lookup(). There are 2 possibilities there - either matching host declaration is found in the hash table or not. Not found case is trivial and boring, hp value is unchanged, hash_lookup() and find_hosts_by_haddr() returns 0, but this return value is ignored. Subsequent for loop iterates through old hp, so it can't find anything new and hp is correctly dereferenced later. If hash_lookup() finds matching entry in the hash table, it checks whether hash has referencer function defined. host_hw_addr_hash table uses host_reference() referencer, which calls omapi_object_reference(), which does: if (*r) { #if defined (POINTER_DEBUG) log_error ("%s(%d): reference store into non-null pointer!", file, line); abort (); #else return ISC_R_INVALIDARG; #endif } Hence r (i.e. hp) is not overwritten when it's non-NULL, and refcount is not incremented. hash_lookup() does not bother checking return value of the referencer function, but that does not seem to be a big deal here. So after return from find_hosts_by_haddr(), hp is unchanged and the rest is identical to the not found case.