Description: fix denial of service via AgentX subagent timeout
Origin: based on RedHat's backport of upstream commits. Thanks!
Origin: backport, http://sourceforge.net/p/net-snmp/code/ci/f9304c83f76202db0e684269ca1af32e43cd9db4
Origin: backport, http://sourceforge.net/p/net-snmp/code/ci/793d596838ff7cb48a73b675d62897c56c9e62df
Bug: http://sourceforge.net/p/net-snmp/bugs/2411/
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=731625
Bug-RedHat: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2012-6151

Index: net-snmp-5.7.2~dfsg/agent/mibgroup/agentx/master.c
===================================================================
--- net-snmp-5.7.2~dfsg.orig/agent/mibgroup/agentx/master.c	2012-10-09 18:28:58.000000000 -0400
+++ net-snmp-5.7.2~dfsg/agent/mibgroup/agentx/master.c	2014-03-11 09:16:01.212180291 -0400
@@ -219,7 +219,10 @@
     if (!cache) {
         DEBUGMSGTL(("agentx/master", "response too late on session %8p\n",
                     session));
-        return 0;
+        /* response is too late, free the cache */
+        if (magic)
+            netsnmp_free_delegated_cache((netsnmp_delegated_cache*) magic);
+        return 1;
     }
     requests = cache->requests;
 
@@ -606,6 +609,8 @@
     result = snmp_async_send(ax_session, pdu, agentx_got_response, cb_data);
     if (result == 0) {
         snmp_free_pdu(pdu);
+        if (cb_data)
+            netsnmp_free_delegated_cache((netsnmp_delegated_cache*) cb_data);
     }
 
     return SNMP_ERR_NOERROR;
Index: net-snmp-5.7.2~dfsg/agent/mibgroup/agentx/master_admin.c
===================================================================
--- net-snmp-5.7.2~dfsg.orig/agent/mibgroup/agentx/master_admin.c	2012-10-09 18:28:58.000000000 -0400
+++ net-snmp-5.7.2~dfsg/agent/mibgroup/agentx/master_admin.c	2014-03-11 09:15:22.624179258 -0400
@@ -133,11 +133,16 @@
          * requests, so that the delegated request will be completed and
          * further requests can be processed
          */
-        netsnmp_remove_delegated_requests_for_session(session);
+	while (netsnmp_remove_delegated_requests_for_session(session)) {
+		DEBUGMSGTL(("agentx/master", "Continue removing delegated reqests\n"));
+	}
+
         if (session->subsession != NULL) {
             netsnmp_session *subsession = session->subsession;
             for(; subsession; subsession = subsession->next) {
-                netsnmp_remove_delegated_requests_for_session(subsession);
+                while (netsnmp_remove_delegated_requests_for_session(subsession)) {
+			DEBUGMSGTL(("agentx/master", "Continue removing delegated subsession reqests\n"));
+		}
             }
         }
                 
@@ -153,6 +158,7 @@
     for (sp = session->subsession; sp != NULL; sp = sp->next) {
 
         if (sp->sessid == sessid) {
+            netsnmp_remove_delegated_requests_for_session(sp);
             unregister_mibs_by_session(sp);
             unregister_index_by_session(sp);
             unregister_sysORTable_by_session(sp);
Index: net-snmp-5.7.2~dfsg/agent/snmp_agent.c
===================================================================
--- net-snmp-5.7.2~dfsg.orig/agent/snmp_agent.c	2012-10-09 18:28:58.000000000 -0400
+++ net-snmp-5.7.2~dfsg/agent/snmp_agent.c	2014-03-11 09:17:09.304182115 -0400
@@ -1446,6 +1446,7 @@
         netsnmp_free_cachemap(asp->cache_store);
         asp->cache_store = NULL;
     }
+    agent_snmp_session_release_cancelled(asp);
     SNMP_FREE(asp);
 }
 
@@ -1457,6 +1458,10 @@
 
     if (NULL == asp->treecache)
         return 0;
+
+    if (agent_snmp_session_is_cancelled(asp)) {
+        return 0;
+    }
     
     for (i = 0; i <= asp->treecache_num; i++) {
         for (request = asp->treecache[i].requests_begin; request;
@@ -1535,39 +1540,48 @@
 netsnmp_remove_delegated_requests_for_session(netsnmp_session *sess)
 {
     netsnmp_agent_session *asp;
-    int count = 0;
+    int total_count = 0;
     
     for (asp = agent_delegated_list; asp; asp = asp->next) {
         /*
          * check each request
          */
+        int i;
+        int count = 0;
         netsnmp_request_info *request;
-        for(request = asp->requests; request; request = request->next) {
-            /*
-             * check session
-             */
-            netsnmp_assert(NULL!=request->subtree);
-            if(request->subtree->session != sess)
-                continue;
+        for (i = 0; i <= asp->treecache_num; i++) {
+            for (request = asp->treecache[i].requests_begin; request;
+                 request = request->next) {
+                /*
+                 * check session
+                 */
+                netsnmp_assert(NULL!=request->subtree);
+                if(request->subtree->session != sess)
+                    continue;
 
-            /*
-             * matched! mark request as done
-             */
-            netsnmp_request_set_error(request, SNMP_ERR_GENERR);
-            ++count;
+                /*
+                 * matched! mark request as done
+                 */
+                netsnmp_request_set_error(request, SNMP_ERR_GENERR);
+                ++count;
+            }
+        }
+        if (count) {
+            agent_snmp_session_mark_cancelled(asp);
+            total_count += count;
         }
     }
 
     /*
      * if we found any, that request may be finished now
      */
-    if(count) {
+    if(total_count) {
         DEBUGMSGTL(("snmp_agent", "removed %d delegated request(s) for session "
-                    "%8p\n", count, sess));
-        netsnmp_check_outstanding_agent_requests();
+                    "%8p\n", total_count, sess));
+        netsnmp_check_delegated_requests();
     }
     
-    return count;
+    return total_count;
 }
 
 int
@@ -2739,19 +2753,11 @@
     return final_status;
 }
 
-/*
- * loop through our sessions known delegated sessions and check to see
- * if they've completed yet. If there are no more delegated sessions,
- * check for and process any queued requests
- */
 void
-netsnmp_check_outstanding_agent_requests(void)
+netsnmp_check_delegated_requests(void)
 {
     netsnmp_agent_session *asp, *prev_asp = NULL, *next_asp = NULL;
 
-    /*
-     * deal with delegated requests
-     */
     for (asp = agent_delegated_list; asp; asp = next_asp) {
         next_asp = asp->next;   /* save in case we clean up asp */
         if (!netsnmp_check_for_delegated(asp)) {
@@ -2790,6 +2796,22 @@
             prev_asp = asp;
         }
     }
+}
+
+/*
+ * loop through our sessions known delegated sessions and check to see
+ * if they've completed yet. If there are no more delegated sessions,
+ * check for and process any queued requests
+ */
+void
+netsnmp_check_outstanding_agent_requests(void)
+{
+    netsnmp_agent_session *asp;
+
+    /*
+     * deal with delegated requests
+     */
+    netsnmp_check_delegated_requests();
 
     /*
      * if we are processing a set and there are more delegated
@@ -2819,7 +2841,8 @@
 
             netsnmp_processing_set = netsnmp_agent_queued_list;
             DEBUGMSGTL(("snmp_agent", "SET request remains queued while "
-                        "delegated requests finish, asp = %8p\n", asp));
+                        "delegated requests finish, asp = %8p\n",
+                        agent_delegated_list));
             break;
         }
 #endif /* NETSNMP_NO_WRITE_SUPPORT */
@@ -2880,6 +2903,10 @@
     case SNMP_MSG_GETBULK:
     case SNMP_MSG_GETNEXT:
         netsnmp_check_all_requests_status(asp, 0);
+        if (agent_snmp_session_is_cancelled(asp)) {
+            DEBUGMSGTL(("snmp_agent","canceling next walk for asp %p\n", asp));
+            break;
+        }
         handle_getnext_loop(asp);
         if (netsnmp_check_for_delegated(asp) &&
             netsnmp_check_transaction_id(asp->pdu->transid) !=
@@ -3836,3 +3863,71 @@
 }
 #endif /* NETSNMP_FEATURE_REMOVE_SET_ALL_REQUESTS_ERROR */
 /** @} */
+
+
+/*
+ * Ugly hack to fix bug #950602 and preserve ABI
+ * (the official patch adds netsnmp_agent_session->flags).
+ * We must create parallel database of netsnmp_agent_sessions
+ * and put cancelled requests there instead of marking
+ * netsnmp_agent_session->flags.
+ */
+static netsnmp_agent_session **cancelled_agent_snmp_sessions;
+static int cancelled_agent_snmp_sessions_count;
+static int cancelled_agent_snmp_sessions_max;
+
+int
+agent_snmp_session_mark_cancelled(netsnmp_agent_session *session)
+{
+    DEBUGMSGTL(("agent:cancelled", "Cancelling session %p\n", session));
+    if (!session)
+        return 0;
+    if (cancelled_agent_snmp_sessions_count + 1 > cancelled_agent_snmp_sessions_max) {
+        netsnmp_agent_session **aux;
+        int max = cancelled_agent_snmp_sessions_max + 10;
+        aux = realloc(cancelled_agent_snmp_sessions, sizeof(netsnmp_agent_session*) * max);
+        if (!aux)
+            return SNMP_ERR_GENERR;
+        cancelled_agent_snmp_sessions = aux;
+        cancelled_agent_snmp_sessions_max = max;
+    }
+    cancelled_agent_snmp_sessions[cancelled_agent_snmp_sessions_count] = session;
+    cancelled_agent_snmp_sessions_count++;
+    return 0;
+}
+
+int
+agent_snmp_session_is_cancelled(netsnmp_agent_session *session)
+{
+    int i;
+    for (i=0; i<cancelled_agent_snmp_sessions_count; i++)
+        if (cancelled_agent_snmp_sessions[i] == session) {
+            DEBUGMSGTL(("agent:cancelled", "session %p is cancelled\n", session));
+            return TRUE;
+    }
+    return FALSE;
+}
+
+int
+agent_snmp_session_release_cancelled(netsnmp_agent_session *session)
+{
+    int i, j;
+
+    if (!session)
+        return 0;
+
+    DEBUGMSGTL(("agent:cancelled", "Removing session %p\n", session));
+
+    /* delete the session from cancelled_agent_snmp_sessions */
+    for (i=0, j=0; j<cancelled_agent_snmp_sessions_count; i++, j++)
+        if (cancelled_agent_snmp_sessions[j] == session)
+            i--; /* don't increase i in this loop iteration */
+        else
+            cancelled_agent_snmp_sessions[i] = cancelled_agent_snmp_sessions[j];
+
+    cancelled_agent_snmp_sessions_count = i;
+
+    for (; i< cancelled_agent_snmp_sessions_max; i++)
+        cancelled_agent_snmp_sessions[i] = NULL;
+    return 0;
+}
Index: net-snmp-5.7.2~dfsg/include/net-snmp/agent/snmp_agent.h
===================================================================
--- net-snmp-5.7.2~dfsg.orig/include/net-snmp/agent/snmp_agent.h	2012-10-09 18:28:58.000000000 -0400
+++ net-snmp-5.7.2~dfsg/include/net-snmp/agent/snmp_agent.h	2014-03-11 09:15:22.628179258 -0400
@@ -240,6 +240,7 @@
     int             init_master_agent(void);
     void            shutdown_master_agent(void);
     int             agent_check_and_process(int block);
+    void            netsnmp_check_delegated_requests(void);
     void            netsnmp_check_outstanding_agent_requests(void);
 
     int             netsnmp_request_set_error(netsnmp_request_info *request,
