CVE-2019-20892 (published 2020-06-25T10:15Z)

net-snmp before 5.8.1.pre1 has a double free in usm_free_usmStateReference in snmplib/snmpusm.c via an SNMPv3 GetBulk request. NOTE: this affects net-snmp packages shipped to end users by multiple Linux distributions, but might not affect an upstream release.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:L/Au:S/C:N/I:N/A:P)
CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-415
cwe_name: Double Free
description: The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
extended_description: When a program calls free() twice with the same argument, the program's memory management data structures become corrupted. This corruption can cause the program to crash or, in some circumstances, cause two later calls to malloc() to return the same pointer. If malloc() returns the same value twice and the program later gives the attacker control over the data that is written into this doubly-allocated memory, the program becomes vulnerable to a buffer overflow attack.
         

Fix information:
repo_url: https://github.com/net-snmp/net-snmp
commit_details: repo_url: https://github.com/net-snmp/net-snmp
author: Bart Van Assche
author_date: 2019-07-27 19:34:09-07:00
msg: libsnmp, USM: Introduce a reference count in struct usmStateReference

This patch fixes https://sourceforge.net/p/net-snmp/bugs/2956/.
repository: repo_url: https://github.com/net-snmp/net-snmp
repo_name: net-snmp/net-snmp
description: A SNMP application library, tools and daemon
repo_language: C
owner: net-snmp
file_changes: filename: snmp_client.c
change_type: ModificationType.MODIFY
diff: @@ -405,28 +405,16 @@ _clone_pdu_header(netsnmp_pdu *pdu)
         return NULL;
     }
 
-    if (pdu->securityStateRef &&
-        pdu->command == SNMP_MSG_TRAP2) {
-
-        netsnmp_assert(pdu->securityModel == SNMP_DEFAULT_SECMODEL);
-        ret = usm_clone_usmStateReference((struct usmStateReference *) pdu->securityStateRef,
-                (struct usmStateReference **) &newpdu->securityStateRef );
-
-        if (ret)
-        {
+    sptr = find_sec_mod(newpdu->securityModel);
+    if (sptr && sptr->pdu_clone) {
+        /* call security model if it needs to know about this */
+        ret = sptr->pdu_clone(pdu, newpdu);
+        if (ret) {
             snmp_free_pdu(newpdu);
             return NULL;
         }
     }
 
-    if ((sptr = find_sec_mod(newpdu->securityModel)) != NULL &&
-        sptr->pdu_clone != NULL) {
-        /*
-         * call security model if it needs to know about this 
-         */
-        (*sptr->pdu_clone) (pdu, newpdu);
-    }
-
     return newpdu;
 }
 

programming_language: C
filename: snmpusm.c
change_type: ModificationType.MODIFY
diff: @@ -85,6 +85,7 @@ netsnmp_feature_child_of(usm_support, usm_all)
 netsnmp_feature_require(usm_support)
 
 struct usmStateReference {
+    int             refcnt;
     char           *usr_name;
     size_t          usr_name_length;
     u_char         *usr_engine_id;
@@ -280,42 +281,63 @@ free_enginetime_on_shutdown(int majorid, int minorid, void *serverarg,
 static struct usmStateReference *
 usm_malloc_usmStateReference(void)
 {
-    struct usmStateReference *retval = (struct usmStateReference *)
-        calloc(1, sizeof(struct usmStateReference));
+    struct usmStateReference *retval;
+
+    retval = calloc(1, sizeof(struct usmStateReference));
+    if (retval)
+        retval->refcnt = 1;
 
     return retval;
 }                               /* end usm_malloc_usmStateReference() */
 
+static int
+usm_clone(netsnmp_pdu *pdu, netsnmp_pdu *new_pdu)
+{
+    struct usmStateReference *ref = pdu->securityStateRef;
+    struct usmStateReference **new_ref =
+        (struct usmStateReference **)&new_pdu->securityStateRef;
+    int ret = 0;
+
+    if (!ref)
+        return ret;
+
+    if (pdu->command == SNMP_MSG_TRAP2) {
+        netsnmp_assert(pdu->securityModel == SNMP_DEFAULT_SECMODEL);
+        ret = usm_clone_usmStateReference(ref, new_ref);
+    } else {
+        netsnmp_assert(ref == *new_ref);
+        ref->refcnt++;
+    }
+
+    return ret;
+}
+
 static void
 usm_free_usmStateReference(void *old)
 {
-    struct usmStateReference *old_ref = (struct usmStateReference *) old;
-
-    if (old_ref) {
-
-        if (old_ref->usr_name_length)
-            SNMP_FREE(old_ref->usr_name);
-        if (old_ref->usr_engine_id_length)
-            SNMP_FREE(old_ref->usr_engine_id);
-        if (old_ref->usr_auth_protocol_length)
-            SNMP_FREE(old_ref->usr_auth_protocol);
-        if (old_ref->usr_priv_protocol_length)
-            SNMP_FREE(old_ref->usr_priv_protocol);
-
-        if (old_ref->usr_auth_key_length && old_ref->usr_auth_key) {
-            SNMP_ZERO(old_ref->usr_auth_key, old_ref->usr_auth_key_length);
-            SNMP_FREE(old_ref->usr_auth_key);
-        }
-        if (old_ref->usr_priv_key_length && old_ref->usr_priv_key) {
-            SNMP_ZERO(old_ref->usr_priv_key, old_ref->usr_priv_key_length);
-            SNMP_FREE(old_ref->usr_priv_key);
-        }
+    struct usmStateReference *ref = old;
+
+    if (!ref)
+        return;
+
+    if (--ref->refcnt > 0)
+        return;
 
-        SNMP_ZERO(old_ref, sizeof(*old_ref));
-        SNMP_FREE(old_ref);
+    SNMP_FREE(ref->usr_name);
+    SNMP_FREE(ref->usr_engine_id);
+    SNMP_FREE(ref->usr_auth_protocol);
+    SNMP_FREE(ref->usr_priv_protocol);
 
+    if (ref->usr_auth_key_length && ref->usr_auth_key) {
+        SNMP_ZERO(ref->usr_auth_key, ref->usr_auth_key_length);
+        SNMP_FREE(ref->usr_auth_key);
+    }
+    if (ref->usr_priv_key_length && ref->usr_priv_key) {
+        SNMP_ZERO(ref->usr_priv_key, ref->usr_priv_key_length);
+        SNMP_FREE(ref->usr_priv_key);
     }
 
+    SNMP_FREE(ref);
 }                               /* end usm_free_usmStateReference() */
 
 struct usmUser *
@@ -4916,6 +4938,7 @@ init_usm(void)
     def->encode_reverse = usm_secmod_rgenerate_out_msg;
     def->encode_forward = usm_secmod_generate_out_msg;
     def->decode = usm_secmod_process_in_msg;
+    def->pdu_clone = usm_clone;
     def->pdu_free_state_ref = usm_free_usmStateReference;
     def->session_setup = usm_session_init;
     def->handle_report = usm_handle_report;

programming_language: C