https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050805 https://github.com/NetworkConfiguration/dhcpcd/issues/179 No CVE assigned yet. Package: dhcpcd-base Version: 9.4.1-22 Severity: critical Tags: security Justification: breaks unrelated software X-Debbugs-Cc: Debian Security Team <team.org> When the dhcpcd DHCPv4 client receives a zero-length UDP packet on port 68, the "network proxy" dhcpcd process exits with status 0. dhcpcd then stops all network activity: It does not renew leases and eventually expires the current lease (unless it has infinite duration) and removes the IP address, leaving the system without networking. This bug can be triggered remotely over the internet from any UDP port and is critical on an internet-facing system that needs DHCP to get an IP address, such as a gateway, a dedicated server or a VM. This affects version 9.4.1-22 (stable) and 1:9.4.1-24~deb12u2 (stable proposed update) but not 1:10.0.2-4 (testing/unstable) as upstream fixed it in 10.0.2: Upstream Bug report: https://github.com/NetworkConfiguration/dhcpcd/issues/179 Upstream Fix: https://github.com/NetworkConfiguration/dhcpcd/commit/8b29c0ddf026c1c5647c3b8c6cfe21699c4056ae This patch does not apply cleanly to 9.4.1 because the privsep structure changed in 10.0.2. It's likely that only the src/privsep.c hunks about len == 0 and eloop_exit() needs to be backported, the other changes are just here to avoid compiler warnings about unused parameters. -- System Information: Debian Release: 12.1 APT prefers stable-updates APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable') Architecture: amd64 (x86_64) Kernel: Linux 6.1.0-11-amd64 (SMP w/8 CPU threads; PREEMPT) Kernel taint flags: TAINT_WARN Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set Shell: /bin/sh linked to /usr/bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages dhcpcd-base depends on: ii adduser 3.134 ii libc6 2.36-9+deb12u1 ii libudev1 252.12-1~deb12u1 Versions of packages dhcpcd-base recommends: pn wpasupplicant <none> Versions of packages dhcpcd-base suggests: ii openresolv [resolvconf] 3.12.0-3 -- Configuration Files: /etc/dhcpcd.conf changed [not included] -- no debconf information
Created dhcpcd tracking bugs for this issue: Affects: epel-all [bug 2236299] Affects: fedora-all [bug 2236300]
(In reply to Chess Hazlett from comment #1) > Created dhcpcd tracking bugs for this issue: > > Affects: epel-all [bug 2236299] > Affects: fedora-all [bug 2236300] This affects "only" F37 and EPEL7 - they are based on dhcpcd-9.5.2. F38, F39, F40, and EPEL8 & EPEL9 are based on dhcpcd-10.0.2 which is fixed (manually checked the code to be sure). The released versions for individual Fedoras and EPELs can be found here: https://src.fedoraproject.org/rpms/dhcpcd
This needs to be doublechecked but due to this commit included in dhcpcd-9.5.2: https://github.com/NetworkConfiguration/dhcpcd/commit/828e858d3d7bbdb82bec7cd56616795426861783 F37 and EPEL7: don't seem to be affected. See output from gdb DHCPCD_PRIVSEP is turned on: ~~~ (gdb) b ps_init Breakpoint 2 at 0x5570f7122e00: file /usr/src/debug/dhcpcd-9.5.2-2.fc37.x86_64/src/privsep.c, line 95. (gdb) c Continuing. Breakpoint 2, ps_init (ctx=0x7fff478a23a0) at /usr/src/debug/dhcpcd-9.5.2-2.fc37.x86_64/src/privsep.c:95 95 { (gdb) n 99 errno = 0; (gdb) 100 if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) { (gdb) 111 if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) { (gdb) p *ctx->ps_user $2 = {pw_name = 0x5570f7feecd0 "dhcpcd", pw_passwd = 0x5570f7feecd7 "x", pw_uid = 993, pw_gid = 992, pw_gecos = 0x5570f7feece1 "Minimalistic DHCP client", pw_dir = 0x5570f7feecfa "/var/lib/dhcpcd", pw_shell = 0x5570f7feed0a "/usr/sbin/nologin"} (gdb) n 119 ctx->options |= DHCPCD_PRIVSEP; (gdb) 120 return 0; (gdb) ~~~ and look at the current code from F37 branch - line #920: dhcpcd-9.5.2/src/privsep.c: ~~~ 899 ssize_t 900 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd) 901 { 902 struct sockaddr_storage ss = { .ss_family = AF_UNSPEC }; 903 uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 }; 904 uint8_t databuf[64 * 1024]; 905 struct iovec iov[] = { 906 { .iov_base = databuf, .iov_len = sizeof(databuf) } 907 }; 908 struct msghdr msg = { 909 .msg_name = &ss, .msg_namelen = sizeof(ss), 910 .msg_control = controlbuf, .msg_controllen = sizeof(controlbuf), 911 .msg_iov = iov, .msg_iovlen = 1, 912 }; 913 914 ssize_t len = recvmsg(rfd, &msg, 0); 915 916 if (len == -1) 917 logerr("%s: recvmsg", __func__); 918 if (len == -1 || len == 0) { 919 if (ctx->options & DHCPCD_FORKED && 920 !(ctx->options & DHCPCD_PRIVSEPROOT)) 921 eloop_exit(ctx->eloop, 922 len == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 923 return len; 924 } 925 926 iov[0].iov_len = (size_t)len; 927 len = ps_sendcmdmsg(wfd, cmd, &msg); 928 if (len == -1) { 929 logerr("ps_sendcmdmsg"); 930 if (ctx->options & DHCPCD_FORKED && 931 !(ctx->options & DHCPCD_PRIVSEPROOT)) 932 eloop_exit(ctx->eloop, EXIT_FAILURE); 933 } 934 return len; 935 } ~~~ From above the behavior is that if DHCPCD_PRIVSEPROOT is not set in ctx->options it will exit, but as it is set, it won't.
Additional bits and pieces for DHCPCD_PRIVSEPROOT and DHCPCD_PRIVSEP relation: ~~~~ 653 static int 654 ps_root_startcb(void *arg) 655 { 656 struct dhcpcd_ctx *ctx = arg; 657 658 if (ctx->options & DHCPCD_MANAGER) 659 setproctitle("[privileged proxy]"); 660 else 661 setproctitle("[privileged proxy] %s%s%s", 662 ctx->ifv[0], 663 ctx->options & DHCPCD_IPV4 ? " [ip4]" : "", 664 ctx->options & DHCPCD_IPV6 ? " [ip6]" : ""); 665 ctx->ps_root_pid = getpid(); 666 ctx->options |= DHCPCD_PRIVSEPROOT; <<<--- ... 809 pid_t 810 ps_root_start(struct dhcpcd_ctx *ctx) 811 { 812 int logfd[2], datafd[2]; 813 pid_t pid; 814 815 if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, logfd) == -1) 816 return -1; 817 #ifdef PRIVSEP_RIGHTS 818 if (ps_rights_limit_fdpair(logfd) == -1) 819 return -1; 820 #endif 821 822 if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, datafd) == -1) 823 return -1; 824 if (ps_setbuf_fdpair(datafd) == -1) 825 return -1; 826 #ifdef PRIVSEP_RIGHTS 827 if (ps_rights_limit_fdpair(datafd) == -1) 828 return -1; 829 #endif 830 831 pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd, 832 ps_root_recvmsg, NULL, ctx, 833 ps_root_startcb, ps_root_signalcb, 0); <<<--- ~~~ src/privsep.h: ~~~ 95 /* Handy macro to work out if in the privsep engine or not. */ 96 #define IN_PRIVSEP(ctx) \ 97 ((ctx)->options & DHCPCD_PRIVSEP) ~~~ src/dhcpcd.c: ~~~ 464 int 465 ps_start(struct dhcpcd_ctx *ctx) 466 { 467 pid_t pid; 468 469 TAILQ_INIT(&ctx->ps_processes); 470 471 switch (pid = ps_root_start(ctx)) { <<<--- 1808 int 1809 main(int argc, char **argv, char **envp) 1810 { ... 2403 #ifdef PRIVSEP 2404 if (IN_PRIVSEP(&ctx) && ps_start(&ctx) == -1) { <<<--- 2405 logerr("ps_start"); 2406 goto exit_failure; 2407 } 2408 if (ctx.options & DHCPCD_FORKED) 2409 goto run_loop; 2410 #endif ~~~
Strange is no fix were imported into branch dhcpcd-9, even though a release from it were recently made. Original Debian report had version 9.4.1, which has quite similar code in relevant places. I cannot find how they differ in relevant places to not be vulnerable. Unless I am missing something important.
It seems bug #2262996 reported issue related to this one. There is still unfixed code path causing dhcpcd to crash. Reported on upstream issue: https://github.com/NetworkConfiguration/dhcpcd/issues/283