diff --git a/net-snmp-5.9.3-CVE-2026-89147.patch b/net-snmp-5.9.3-CVE-2026-89147.patch new file mode 100644 index 0000000000000000000000000000000000000000..e8dd3561028f909f5002eabc9cd9d8d2fce3e471 --- /dev/null +++ b/net-snmp-5.9.3-CVE-2026-89147.patch @@ -0,0 +1,387 @@ +From ad843e83da1c229847ae7884dc08311fad680389 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.") +Adapted-by: PkgAgent/deepseek-v4 (modified to adapt to opencloudos-stream) +--- + agent/mibgroup/smux/smux.c | 243 +++++++++++++++++++++---------------- + 1 file changed, 139 insertions(+), 104 deletions(-) + +diff --git a/agent/mibgroup/smux/smux.c b/agent/mibgroup/smux/smux.c +index 5023dfa..ed11c47 100644 +--- a/agent/mibgroup/smux/smux.c ++++ b/agent/mibgroup/smux/smux.c +@@ -63,6 +63,7 @@ + + #include + #include ++#include + #include + + #include "smux.h" +@@ -93,6 +94,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 *, +@@ -624,23 +627,47 @@ 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)(intptr_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 +@@ -650,112 +677,127 @@ smux_accept(int sd) + if ((fd = (int) accept(sd, (struct sockaddr *) &in_socket, &alen)) < 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; + } + + /* +@@ -763,10 +805,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 + */ +@@ -774,26 +818,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 +@@ -1908,11 +1950,6 @@ smux_peer_cleanup(int sd) + } + } + +- /* +- * decrement the peer count +- */ +- npeers--; +- + /* + * make his auth available again + */ +@@ -1923,6 +1960,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/net-snmp.spec b/net-snmp.spec index b5eb120842fd18ae06086bac4e82a7aab9231d73..e4786a1fd60e49772e17bfb4b46be91f1a459a0d 100644 --- a/net-snmp.spec +++ b/net-snmp.spec @@ -1,7 +1,7 @@ Summary: A collection of SNMP protocol tools and libraries Name: net-snmp Version: 5.9.3 -Release: 8%{?dist} +Release: 9%{?dist} License: BSD URL: http://net-snmp.sourceforge.net/ Source0: https://downloads.sourceforge.net/project/net-snmp/net-snmp/%{version}/net-snmp-%{version}.tar.gz @@ -12,6 +12,7 @@ Source4: snmptrapd.service Patch0001: net-snmp-5.9-add-compatibility-parsing-config.patch Patch0002: net-snmp-5.9-CVE-2025-68615.patch +Patch0003: net-snmp-5.9.3-CVE-2026-89147.patch Patch3001: net-snmp-5.9-pie.patch Patch3002: net-snmp-5.9-dir-fix.patch Patch3003: net-snmp-5.9-multilib.patch @@ -310,6 +311,10 @@ LD_LIBRARY_PATH=%{buildroot}/%{_libdir} make test %attr(0644,root,root) %{_mandir}/man1/net-snmp-config*.1.* %changelog +* Thu Sep 17 2026 PkgAgent Robot - 5.9.3-9 +- [Type] security +- [DESC] Fix CVE-2026-89147: SMUX unauthenticated blocking read in smux_accept() allows remote DoS + * Sun Jan 04 2026 ze-you-liu - 5.9.3-8 - [Type] security - [DESC] Fix CVE-2025-68615 vulnerability: snmptrapd: Fix out-of-bounds trapOid[] accesses