pysnmp icon indicating copy to clipboard operation
pysnmp copied to clipboard

[Bug]: USM read/write access not working as intended.

Open andrew-harness opened this issue 1 year ago • 0 comments

Expected behavior

I expect when setting the v2 and v3 users with no subtrees that they would not have read and write access.

# SNMPv2c setup

# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, "my-area", "public")

# Allow read MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, "my-area", "noAuthNoPriv")

Actual behavior

But the users have full access to my OIDs unless I give them a specific subTree to block write/read requests.

enterprise = (1, 3, 6, 1, 4, 1, 66666)
blockWriteSubtree = (1, 3, 6, 1, 4, 1, 66666, 4294967295)
config.addVacmUser(snmpEngine, 2, "my-area", "noAuthNoPriv", enterprise, blockWriteSubtree)

Detailed steps

Here is a simplifed example I am working with. Omitting the blockWriteSubtree will allow the v2 user to write. Same goes for the v3 user. Not passing in the readSubTree and writeSubTree allows the users read/write as well.

import os
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.asyncio.dgram import udp
from pysnmp.proto.api import v2c
from pysnmp.smi import builder
import pysnmp.debug as debug
from pysnmp.proto.rfc1902 import OctetString
from pysnmp.proto.secmod.rfc3414.priv import des
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5
from pyasn1.type.univ import OctetString
import random

# debug.setLogger(debug.Debug('all'))

enterprise = (1, 3, 6, 1, 4, 1, 66666)
blockWriteSubtree = (1, 3, 6, 1, 4, 1, 66666, 4294967295)

oid = (1, 3, 6, 1, 4, 1, 66666, 1)

# Get the path of the mibs folder
script_path = os.path.abspath(__file__)
mibs_path = os.path.join(os.path.dirname(script_path), 'mibs')

# Create SNMP engine
snmpEngine = engine.SnmpEngine()

# Transport setup

# UDP over IPv4
config.addTransport(
    snmpEngine, udp.domainName, udp.UdpTransport().openServerMode(("127.0.0.1", 161))
)

# SNMPv2c setup

# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, "my-area", "public")

# Allow read MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, "my-area", "noAuthNoPriv", enterprise, blockWriteSubtree)

# SNMPv3/USM setup
# use pre-hahsed keys later this will use the keys from the database
authKey = des.Des().hashPassphrase(config.usmHMACMD5AuthProtocol, OctetString("authkey1"))
privKey = des.Des().hashPassphrase(config.usmHMACMD5AuthProtocol, OctetString("privkey1"))

config.addV3User(
    snmpEngine,
    "usr-md5-des",
    config.usmHMACMD5AuthProtocol,
    authKey,
    config.usmDESPrivProtocol,
    privKey,
    authKeyType = config.usmKeyTypeMaster,
    privKeyType = config.usmKeyTypeMaster
)

# Allow full MIB access for each user at VACM
config.addVacmUser(
    snmpEngine, 3, "usr-md5-des", "authPriv", enterprise, enterprise
)

# Create an SNMP context
snmpContext = context.SnmpContext(snmpEngine)

# --- create custom Managed Object Instance ---
mibBuilder = snmpContext.getMibInstrum().getMibBuilder()

MibScalar, MibScalarInstance = mibBuilder.importSymbols(
    "SNMPv2-SMI", "MibScalar", "MibScalarInstance"
)


class MyStaticMibScalarInstance(MibScalarInstance):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.value = random.randint(1, 100)

    def getValue(self, name, idx):
        print(f"{name}: Returning value: {self.value}")
        return self.getSyntax().clone(self.value)

    def setValue(self, val, name, idx):
        self.value = val
        print(f"{name}: Setting value: {val}")
        return self.getSyntax().clone(self.value)

MyScaler = MibScalar(oid, v2c.Unsigned32())
MyScaler.maxAccess = 'readwrite'

mibBuilder.exportSymbols(
    "__MY_MIB",
    MyScaler,
    MyStaticMibScalarInstance(oid, (0,), v2c.Unsigned32()),
)

# --- end of Managed Object Instance initialization ----

# Register SNMP Applications at the SNMP engine for particular SNMP context
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)

# Register an imaginary never-ending job to keep I/O dispatcher running forever
snmpEngine.transportDispatcher.jobStarted(1)

# Run I/O dispatcher which would receive queries and send responses
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Python package information

pysnmp-lextudio 5.0.36

Operating system information

Windows 11

Python information

3.12.0

(Optional) Contents of your test script

No response

Relevant log output

No response

andrew-harness avatar Feb 15 '24 20:02 andrew-harness