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 <dkelaiya@cisco.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Darsh Kelaiya authored and Anuj Mittal committed 2026-10-29 10:19:01 +05:30
1 parent aa6d99fed0
commit 4202adbfe6
2 files changed
+400

No files matched your search

@@ -0,0 +1,399 @@
From 6d5668341de9d91f0e4e9d71e36f4141fdb9a528 Mon Sep 17 00:00:00 2001
From: Bart Van Assche <bvanassche@acm.org>
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 <dkelaiya@cisco.com>
---
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 <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include <net-snmp/library/tools.h>
+#include <net-snmp/library/snmpSocketBaseDomain.h>
#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--;
}
}
}
@@ -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"