From 4202adbfe6d47e2568dd06f89b67fb5ef95cfec9 Mon Sep 17 00:00:00 2001 From: Darsh Kelaiya Date: Thu, 15 Oct 2026 23:49:08 -0700 Subject: [PATCH] net-snmp: fix CVE-2026-89147 This patch applies the upstream fix [1] for CVE-2026-89147 as shown in the advisory identifying the fix [2]. [1] https://github.com/net-snmp/net-snmp/commit/ad843e83da1c229847ae7884dc08311fad680389 [2] https://ubuntu.com/security/CVE-2026-89147 Signed-off-by: Darsh Kelaiya Signed-off-by: Anuj Mittal --- .../net-snmp/net-snmp/CVE-2026-89147.patch | 399 ++++++++++++++++++ .../net-snmp/net-snmp_5.9.5.2.bb | 1 + 2 files changed, 400 insertions(+) create mode 100644 meta-networking/recipes-protocols/net-snmp/net-snmp/CVE-2026-89147.patch diff --git a/meta-networking/recipes-protocols/net-snmp/net-snmp/CVE-2026-89147.patch b/meta-networking/recipes-protocols/net-snmp/net-snmp/CVE-2026-89147.patch new file mode 100644 index 0000000000..b158a92791 --- /dev/null +++ b/meta-networking/recipes-protocols/net-snmp/net-snmp/CVE-2026-89147.patch @@ -0,0 +1,399 @@ +From 6d5668341de9d91f0e4e9d71e36f4141fdb9a528 Mon Sep 17 00:00:00 2001 +From: Bart Van Assche +Date: Mon, 14 Sep 2026 10:19:57 -0700 +Subject: [PATCH] snmpd: SMUX: Make accepted peer connection handshake + non-blocking + +When accepting a new SMUX connection, smux_accept() previously executed +a synchronous, blocking recvfrom() in the main daemon loop to read the +initial OpenPDU before authenticating the peer. If an unauthenticated +client connected and sent no data, the entire snmpd process blocked, +preventing all SNMP request processing (CVE-2026-89147 / Issue #1134). + +Make the SMUX connection acceptance and authentication asynchronous: +- Set accepted SMUX peer sockets to non-blocking mode immediately upon + accept() in smux_accept() and return the socket to the select loop + without performing any synchronous read. +- In smux_process(), check whether the peer is authenticated. If not yet + authenticated, read and parse the OpenPDU non-blockingly when the + socket becomes readable, authenticating the peer and processing any + subsequent PDUs in the buffer. +- Register a 5-second one-shot alarm on accepted connections to close + and clean up unauthenticated sockets that fail to complete the + handshake in time. +- In smux_peer_cleanup(), only decrement npeers if the connection was + an authenticated peer. + +Fixes: https://github.com/net-snmp/net-snmp/issues/1134 +Fixes: ee0daa3b7bcd ("- (smux.c, smux.h): Patch from Nick Amato: - completely re-written smux modules.") + +CVE: CVE-2026-89147 +Upstream-Status: Backport [https://github.com/net-snmp/net-snmp/commit/ad843e83da1c229847ae7884dc08311fad680389] + +Backport Changes: +- Retain int sockets, -1 errors, and the older accept() check. +- Use %d because 5.9.5.2 lacks NETSNMP_FMT_SKT. +- Omit netsnmp_set_tcp_nodelay(), absent in 5.9.5.2. +- Include snmpSocketBaseDomain.h for the nonblocking helper. + +(cherry picked from commit ad843e83da1c229847ae7884dc08311fad680389) +Signed-off-by: Darsh Kelaiya +--- + agent/mibgroup/smux/smux.c | 245 +++++++++++++++++++++---------------- + 1 file changed, 141 insertions(+), 104 deletions(-) + +diff --git a/agent/mibgroup/smux/smux.c b/agent/mibgroup/smux/smux.c +index 078c4c6fa..5778f3590 100644 +--- a/agent/mibgroup/smux/smux.c ++++ b/agent/mibgroup/smux/smux.c +@@ -64,6 +64,7 @@ + #include + #include + #include ++#include + + #include "smux.h" + #include "snmpd.h" +@@ -92,6 +93,8 @@ static void smux_send_close(int, int); + static void smux_list_detach(smux_reg **, smux_reg *); + static void smux_replace_active(smux_reg *, smux_reg *); + static void smux_peer_cleanup(int); ++static int smux_peer_is_authenticated(int); ++static void smux_auth_timeout_cb(unsigned int, void *); + static int smux_auth_peer(oid *, size_t, char *, int); + static int smux_build(u_char, long, oid *, + size_t *, u_char, u_char *, size_t, u_char *, +@@ -623,138 +626,175 @@ var_smux_write(int action, + } + + ++static int ++smux_peer_is_authenticated(int sd) ++{ ++ int i; ++ ++ for (i = 0; i < nauths; i++) ++ if (Auths[i]->sa_active_fd == sd) ++ return 1; ++ ++ return 0; ++} ++ ++static void ++smux_auth_timeout_cb(unsigned int clientreg, void *clientarg) ++{ ++ int fd = (int)(uintptr_t)clientarg; ++ int i, found = 0; ++ ++ for (i = 0; i < smux_snmp_select_list_get_length(); i++) { ++ if (smux_snmp_select_list_get_SD_from_List(i) == fd) { ++ found = 1; ++ break; ++ } ++ } ++ if (found && !smux_peer_is_authenticated(fd)) { ++ DEBUGMSGTL(("smux", ++ "[smux_auth_timeout] closing idle unauthenticated fd %d\n", fd)); ++ close(fd); ++ smux_snmp_select_list_del(fd); ++ } ++} ++ + int + smux_accept(int sd) + { +- u_char data[SMUXMAXPKTSIZE], *ptr, type; + struct sockaddr_in in_socket; +- struct timeval tv; +- int fail, fd; ++ int fd; + socklen_t alen; +- int length; +- size_t len; + + alen = sizeof(struct sockaddr_in); +- /* +- * this may be too high +- */ +- tv.tv_sec = 5; +- tv.tv_usec = 0; + + /* + * connection request + */ + DEBUGMSGTL(("smux", "[smux_accept] Calling accept()\n")); + errno = 0; +- if ((fd = (int) accept(sd, (struct sockaddr *) &in_socket, &alen)) < 0) { ++ fd = accept(sd, (struct sockaddr *) &in_socket, &alen); ++ if (fd < 0) { + snmp_log_perror("[smux_accept] accept failed"); + return -1; +- } else { +- DEBUGMSGTL(("smux", "[smux_accept] accepted fd %d from %s:%d\n", +- fd, inet_ntoa(in_socket.sin_addr), +- ntohs(in_socket.sin_port))); +- if (npeers + 1 == SMUXMAXPEERS) { +- snmp_log(LOG_ERR, +- "[smux_accept] denied peer on fd %d, limit %d reached", +- fd, SMUXMAXPEERS); +- close(fd); +- return -1; +- } ++ } ++ ++ netsnmp_set_non_blocking_mode(fd, TRUE); ++ DEBUGMSGTL(("smux", "[smux_accept] accepted fd %d from %s:%d\n", ++ fd, inet_ntoa(in_socket.sin_addr), ++ ntohs(in_socket.sin_port))); ++ if (npeers + 1 == SMUXMAXPEERS) { ++ snmp_log(LOG_ERR, ++ "[smux_accept] denied peer on fd %d, limit %d reached", ++ fd, SMUXMAXPEERS); ++ close(fd); ++ return -1; ++ } ++ ++ snmp_alarm_register(5, 0, smux_auth_timeout_cb, (void *)(intptr_t)fd); ++ ++ return fd; ++} ++ ++int ++smux_process(int sock) ++{ ++ int length, tmp_length; ++ u_char data[SMUXMAXPKTSIZE]; ++ u_char type, *ptr; ++ size_t packet_len, len; ++ int fail; + ++ if (!smux_peer_is_authenticated(sock)) { + /* +- * now block for an OpenPDU ++ * Unauthenticated peer: read OpenPDU non-blockingly. + */ +- do +- { +- length = recvfrom(fd, (char *) data, SMUXMAXPKTSIZE, 0, NULL, NULL); +- } +- while((length == -1) && ((errno == EINTR) || (errno == EAGAIN))); ++ do { ++ length = recvfrom(sock, (char *) data, SMUXMAXPKTSIZE, 0, NULL, NULL); ++ } while (length == -1 && errno == EINTR); + +- if (length <= 0) { ++ if (length < 0) { ++ if (errno == EAGAIN || ++ (EAGAIN != EWOULDBLOCK && errno == EWOULDBLOCK)) ++ return 0; + DEBUGMSGTL(("smux", +- "[smux_accept] peer on fd %d died or timed out\n", +- fd)); +- close(fd); ++ "[smux_process] recvfrom failed on unauthenticated fd %d\n", sock)); ++ close(sock); ++ return -1; ++ } ++ if (length == 0) { ++ DEBUGMSGTL(("smux", ++ "[smux_process] peer on fd %d closed connection before auth\n", sock)); ++ close(sock); + return -1; + } ++ + /* +- * try to authorize him ++ * Try to authorize peer. + */ + ptr = data; + len = length; + if ((ptr = asn_parse_header(ptr, &len, &type)) == NULL) { +- smux_send_close(fd, SMUXC_PACKETFORMAT); +- close(fd); +- DEBUGMSGTL(("smux", "[smux_accept] peer on %d sent bad open", fd)); ++ smux_send_close(sock, SMUXC_PACKETFORMAT); ++ close(sock); ++ DEBUGMSGTL(("smux", ++ "[smux_process] peer on %d sent bad open\n", ++ sock)); + return -1; + } else if (type != (u_char) SMUX_OPEN) { +- smux_send_close(fd, SMUXC_PROTOCOLERROR); +- close(fd); ++ smux_send_close(sock, SMUXC_PROTOCOLERROR); ++ close(sock); + DEBUGMSGTL(("smux", +- "[smux_accept] peer on %d did not send open: (%d)\n", +- fd, type)); ++ "[smux_process] peer on %d did not send open: (%d)\n", ++ sock, type)); + return -1; + } +- ptr = smux_open_process(fd, ptr, &len, &fail); ++ ptr = smux_open_process(sock, ptr, &len, &fail); + if (fail) { +- smux_send_close(fd, SMUXC_AUTHENTICATIONFAILURE); +- close(fd); ++ smux_send_close(sock, SMUXC_AUTHENTICATIONFAILURE); ++ close(sock); + DEBUGMSGTL(("smux", +- "[smux_accept] peer on %d failed authentication\n", +- fd)); ++ "[smux_process] peer on %d failed authentication\n", ++ sock)); + return -1; + } + +- /* +- * he's OK +- */ +-#ifdef SO_RCVTIMEO +- if (setsockopt +- (fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv)) < 0) { +- DEBUGMSGTL(("smux", +- "[smux_accept] setsockopt(SO_RCVTIMEO) failed fd %d\n", +- fd)); +- snmp_log_perror("smux_accept: setsockopt SO_RCVTIMEO"); +- } +-#endif + npeers++; +- DEBUGMSGTL(("smux", "[smux_accept] fd %d\n", fd)); ++ DEBUGMSGTL(("smux", "[smux_process] authenticated peer on fd %d\n", sock)); + + /* +- * Process other PDUs already read, e.g. a registerRequest. ++ * Process other PDUs already read, e.g. a registerRequest. + */ + len = length - (ptr - data); +- if (smux_pdu_process(fd, ptr, len) < 0) { +- /* +- * Easy come, easy go. Clean-up is already done. +- */ +- return -1; ++ if (len > 0) { ++ if (smux_pdu_process(sock, ptr, len) < 0) { ++ return -1; ++ } + } ++ return 0; + } +- return fd; +-} + +-int +-smux_process(int fd) +-{ +- int length, tmp_length; +- u_char data[SMUXMAXPKTSIZE]; +- u_char type, *ptr; +- size_t packet_len; +- +- do +- { +- length = recvfrom(fd, (char *) data, SMUXMAXPKTSIZE, MSG_PEEK, NULL, ++ /* ++ * Authenticated peer: process incoming data. ++ */ ++ do { ++ length = recvfrom(sock, (char *) data, SMUXMAXPKTSIZE, MSG_PEEK, NULL, + NULL); +- } +- while((length == -1) && ((errno == EINTR) || (errno == EAGAIN))); ++ } while (length == -1 && errno == EINTR); + +- if (length <= 0) +- { +- if (length < 0) +- snmp_log_perror("[smux_process] peek failed"); +- smux_peer_cleanup(fd); +- return -1; ++ if (length < 0) { ++ if (errno == EAGAIN || ++ (EAGAIN != EWOULDBLOCK && errno == EWOULDBLOCK)) ++ return 0; ++ snmp_log_perror("[smux_process] peek failed"); ++ smux_peer_cleanup(sock); ++ return -1; ++ } ++ if (length == 0) { ++ DEBUGMSGTL(("smux", ++ "[smux_process] peer on fd %d died or disconnected\n", ++ sock)); ++ smux_peer_cleanup(sock); ++ return -1; + } + + /* +@@ -762,10 +802,12 @@ smux_process(int fd) + */ + packet_len = length; + ptr = asn_parse_header(data, &packet_len, &type); +- if (ptr == NULL) ++ if (ptr == NULL) { ++ smux_peer_cleanup(sock); + return -1; ++ } + packet_len += (ptr - data); +- if (length > packet_len) { ++ if ((size_t)length > packet_len) { + /* + * set length to receive only the first packet + */ +@@ -773,26 +815,24 @@ smux_process(int fd) + } + + tmp_length = length; +- do +- { ++ do { + length = tmp_length; +- length = recvfrom(fd, (char *) data, length, 0, NULL, NULL); +- } +- while((length == -1) && ((errno == EINTR) || (errno == EAGAIN))); ++ length = recvfrom(sock, (char *) data, length, 0, NULL, NULL); ++ } while (length == -1 && errno == EINTR); + + if (length <= 0) { +- /* +- * the peer went away, close this descriptor +- * * and delete it from the list +- */ ++ if (length < 0 && ++ (errno == EAGAIN || ++ (EAGAIN != EWOULDBLOCK && errno == EWOULDBLOCK))) ++ return 0; + DEBUGMSGTL(("smux", + "[smux_process] peer on fd %d died or timed out\n", +- fd)); +- smux_peer_cleanup(fd); ++ sock)); ++ smux_peer_cleanup(sock); + return -1; + } + +- return smux_pdu_process(fd, data, length); ++ return smux_pdu_process(sock, data, length); + } + + static int +@@ -1907,11 +1947,6 @@ smux_peer_cleanup(int sd) + } + } + +- /* +- * decrement the peer count +- */ +- npeers--; +- + /* + * make his auth available again + */ +@@ -1922,6 +1957,8 @@ smux_peer_cleanup(int sd) + snprint_objid(oid_name, sizeof(oid_name), Auths[i]->sa_oid, + Auths[i]->sa_oid_len); + DEBUGMSGTL(("smux", "peer disconnected: %s\n", oid_name)); ++ if (npeers > 0) ++ npeers--; + } + } + } diff --git a/meta-networking/recipes-protocols/net-snmp/net-snmp_5.9.5.2.bb b/meta-networking/recipes-protocols/net-snmp/net-snmp_5.9.5.2.bb index d5e2931969..1e3d1eba9a 100644 --- a/meta-networking/recipes-protocols/net-snmp/net-snmp_5.9.5.2.bb +++ b/meta-networking/recipes-protocols/net-snmp/net-snmp_5.9.5.2.bb @@ -25,6 +25,7 @@ SRC_URI = "${SOURCEFORGE_MIRROR}/net-snmp/net-snmp-${PV}.tar.gz \ file://0010-net-snmp-Reproducibility-Don-t-check-build-host-for.patch \ file://0011-ac_add_search_path.m4-keep-consistent-between-32bit-.patch \ file://netsnmp-swinst-crash.patch \ + file://CVE-2026-89147.patch \ " SRC_URI[sha256sum] = "16707719f833184a4b72835dac359ae188123b06b5e42817c00790d7dc1384bf"