commit a7d16275a98a87aa0a7e0989cf4f3300308ca787
Author: Jenny Yao <jyao@cisco.com>
Date:   Wed Sep 6 18:14:50 2023 -0700

    Get contextName from access list

diff --git a/agent/mibgroup/mibII/vacm_conf.c b/agent/mibgroup/mibII/vacm_conf.c
index 01b13dedb4..937c48ff06 100644
--- a/agent/mibgroup/mibII/vacm_conf.c
+++ b/agent/mibgroup/mibII/vacm_conf.c
@@ -1417,6 +1417,40 @@ vacm_check_view_contents(netsnmp_pdu *pdu, oid * name, size_t namelen,
                     (int)pdu->contextNameLen));
         return VACM_NOSUCHCONTEXT;
     }
+
+    DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: sn=%s", sn));
+
+    gp = vacm_getGroupEntry(pdu->securityModel, sn);
+    if (gp == NULL) {
+        DEBUGMSG(("mibII/vacm_vars", "\n"));
+        return VACM_NOGROUP;
+    }
+    DEBUGMSG(("mibII/vacm_vars", ", gn=%s", gp->groupName));
+
+    /*
+     * If no contex from request, get the context from the access list.
+     */
+    if (pdu->contextNameLen == 0) {
+        ap = vacm_getAccessEntryNoContext(gp->groupName, pdu->securityModel,
+                                          pdu->securityLevel);
+        if (ap == NULL) {
+            DEBUGMSG(("mibII/vacm_vars", "\n"));
+            return VACM_NOACCESS;
+        }
+        SNMP_FREE(pdu->contextName);
+        pdu->contextName = strdup(ap->contextPrefix + 1);
+        pdu->contextNameLen = strlen(pdu->contextName);
+        DEBUGMSG(("mibII/vacm_vars", ", contextName=%s", pdu->contextName));
+        if (ap->contextPrefix[0] != 0) {
+            SNMP_FREE(pdu->contextName);
+            pdu->contextName = strdup(ap->contextPrefix + 1);
+            pdu->contextNameLen = strlen(pdu->contextName);
+            pdu->contextNameDerived = TRUE;
+            DEBUGMSG(("mibII/vacm_vars", ", derived contextName=%s contextNameLen %d",
+                       pdu->contextName, (int)pdu->contextNameLen));
+        }
+    }
+
     /*
      * NULL termination of the pdu field is ugly here.  Do in PDU parsing? 
      */
@@ -1437,15 +1471,6 @@ vacm_check_view_contents(netsnmp_pdu *pdu, oid * name, size_t namelen,
         return VACM_NOSUCHCONTEXT;
     }
 
-    DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: sn=%s", sn));
-
-    gp = vacm_getGroupEntry(pdu->securityModel, sn);
-    if (gp == NULL) {
-        DEBUGMSG(("mibII/vacm_vars", "\n"));
-        return VACM_NOGROUP;
-    }
-    DEBUGMSG(("mibII/vacm_vars", ", gn=%s", gp->groupName));
-
     ap = vacm_getAccessEntry(gp->groupName, contextNameIndex,
                              pdu->securityModel, pdu->securityLevel);
     if (ap == NULL) {
diff --git a/include/net-snmp/library/vacm.h b/include/net-snmp/library/vacm.h
index e5bc5fd99f..c04da507c8 100644
--- a/include/net-snmp/library/vacm.h
+++ b/include/net-snmp/library/vacm.h
@@ -224,6 +224,9 @@ extern          "C" {
     struct vacm_accessEntry *vacm_getAccessEntry(const char *,
                                                  const char *, int, int);
     NETSNMP_IMPORT
+    struct vacm_accessEntry *vacm_getAccessEntryNoContext(const char *,
+                                                          int, int);
+    NETSNMP_IMPORT
     void            vacm_scanAccessInit(void);
     NETSNMP_IMPORT
     struct vacm_accessEntry *vacm_scanAccessNext(void);
diff --git a/include/net-snmp/types.h b/include/net-snmp/types.h
index d489f37b16..b6ca09be0d 100644
--- a/include/net-snmp/types.h
+++ b/include/net-snmp/types.h
@@ -223,6 +223,8 @@ typedef struct snmp_pdu {
     char           *contextName;
     /** Length of contextName */
     size_t          contextNameLen;
+    /** contextName derived from securityName */
+    int             contextNameDerived;
     /** authoritative snmpEngineID for security */
     u_char         *securityEngineID;
     /** Length of securityEngineID */
diff --git a/snmplib/snmp_api.c b/snmplib/snmp_api.c
index 4042f80465..0f0f1228c2 100644
--- a/snmplib/snmp_api.c
+++ b/snmplib/snmp_api.c
@@ -2573,14 +2573,23 @@ snmpv3_scopedPDU_header_realloc_rbuild(u_char ** pkt, size_t * pkt_len,
     int             rc = 0;
 
     /*
-     * contextName.  
+     * contextName.
+     * If contextName is derived from securityName from the incoming packets,
+     * we don't send context for the outgoing patckets.
      */
     DEBUGDUMPHEADER("send", "contextName");
-    rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
-                                   (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
-                                             | ASN_OCTET_STR),
-                                   (u_char *) pdu->contextName,
-                                   pdu->contextNameLen);
+    if (pdu->contextNameDerived) {
+        rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
+                                       (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
+                                                 | ASN_OCTET_STR),
+                                       "", 0);
+    } else {
+        rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1,
+                                       (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE
+                                                 | ASN_OCTET_STR),
+                                       (u_char *) pdu->contextName,
+                                       pdu->contextNameLen);
+    }
     DEBUGINDENTLESS();
     if (rc == 0) {
         return 0;
diff --git a/snmplib/vacm.c b/snmplib/vacm.c
index 9f7394993f..51bb88c9e6 100644
--- a/snmplib/vacm.c
+++ b/snmplib/vacm.c
@@ -1076,6 +1076,30 @@ vacm_createAccessEntry(const char *groupName,
     return vp;
 }
 
+struct vacm_accessEntry *
+vacm_getAccessEntryNoContext(const char *groupName,
+                             int securityModel, int securityLevel)
+{
+    struct vacm_accessEntry *vp, *best=NULL;
+    char            group[VACMSTRINGLEN];
+    int             glen;
+
+    glen = (int) strlen(groupName);
+    if (glen < 0 || glen > VACM_MAX_STRING)
+        return NULL;
+
+    group[0] = glen;
+    strlcpy(group + 1, groupName, sizeof(group) - 1);
+    for (vp = accessList; vp; vp = vp->next) {
+        if ((securityModel == vp->securityModel
+             || vp->securityModel == SNMP_SEC_MODEL_ANY)
+            && securityLevel >= vp->securityLevel
+            && !memcmp(vp->groupName, group, glen + 1))
+            best = _vacm_choose_best( best, vp );
+    }
+    return best;
+}
+
 void
 vacm_destroyAccessEntry(const char *groupName,
                         const char *contextPrefix,
