summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2019-07-29 09:57:45 +0200
committerGitHub <noreply@github.com>2019-07-29 09:57:45 +0200
commit9441850839bf8ebf0c8b365dcbebe22cb98b3baa (patch)
tree9e83be3527742dab90849040303d2cb4655a3892
parent0228371e12b4a19a6b4942626924f8040a92f4cd (diff)
downloadpysnmp-git-9441850839bf8ebf0c8b365dcbebe22cb98b3baa.tar.gz
Rework VACM access control function (#287)
Most important changes include: * Added subtree match negation support (vacmViewTreeFamilyType) * Added subtree family mask support (vacmViewTreeFamilyMask) * Added prefix content name matching support (vacmAccessContextMatch) * Added key VACM tables caching for better lookup performance
-rw-r--r--CHANGES.txt14
-rw-r--r--docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst10
-rw-r--r--examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py117
-rw-r--r--pysnmp/entity/config.py58
-rw-r--r--pysnmp/proto/acmod/rfc3415.py428
5 files changed, 508 insertions, 119 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 3a1efbb6..37765b7b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,7 +1,19 @@
-Revision 4.4.10, released 2019-04-XX
+Revision 4.4.10, released 2019-07-XX
------------------------------------
+- Reworked VACM access control function. Most important changes include:
+
+ * Added subtree match negation support (vacmViewTreeFamilyType)
+ * Added subtree family mask support (vacmViewTreeFamilyMask)
+ * Added prefix content name matching support (vacmAccessContextMatch)
+ * Added key VACM tables caching for better `isAccessAllowed` lookup
+ performance
+
+ One potential incompatibility may be caused by the `addContext()` call
+ which now needs to be made explicitly during low-level VACM configuration
+ rather than be a side effect of `addVacmAccess()` call.
+
- Rebased MIB importing code onto `importlib` because `imp` is long
deprecated
- Fixed asyncore main loop to respect non-default timer resolution
diff --git a/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst b/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst
index 3b1d95db..38af9cb4 100644
--- a/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst
+++ b/docs/source/examples/v3arch/asyncore/agent/cmdrsp/advanced-topics.rst
@@ -37,4 +37,14 @@ Advanced topics
:download:`Download</../../examples/v3arch/asyncore/agent/cmdrsp/multiple-snmp-engines.py>` script.
+.. include:: /../../examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py
+ :start-after: """
+ :end-before: """#
+
+.. literalinclude:: /../../examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py
+ :start-after: """#
+ :language: python
+
+:download:`Download</../../examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py>` script.
+
See also: :doc:`library reference </docs/api-reference>`.
diff --git a/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py b/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py
new file mode 100644
index 00000000..374d4a82
--- /dev/null
+++ b/examples/v3arch/asyncore/agent/cmdrsp/detailed-vacm-configuration.py
@@ -0,0 +1,117 @@
+"""
+Detailed VACM configuration
++++++++++++++++++++++++++++
+
+Serves MIB subtrees under different conditions:
+
+* Respond to SNMPv2c commands
+* with SNMP community "public"
+* over IPv4/UDP, listening at 127.0.0.1:161
+* Serve MIB under non-default contextName `abcd`
+* Allow access to `SNMPv2-MIB::system` subtree
+* Although deny access to `SNMPv2-MIB::sysUpTime` by a bit mask
+* Use partial context name matching (`a`)
+
+This example demonstrates detailed VACM configuration performed via
+low-level VACM calls: `addContext`, `addVacmGroup`, `addVacmAccess`
+and `addVacmView`. Each function populates one of the tables
+defined in `SNMP-VIEW-BASED-ACM-MIB` and used strictly as described
+in the above mentioned MIB.
+
+The following Net-SNMP's commands will GET a value at this Agent:
+
+| $ snmpget -v2c -c public 127.0.0.1 SNMPv2-MIB::sysLocation.0
+
+However this command will fail:
+
+| $ snmpget -v2c -c public 127.0.0.1 SNMPv2-MIB::sysUpTime.0
+
+This command will not reveal `SNMPv2-MIB::sysUpTime.0` among other objects:
+
+| $ snmpwalk -v2c -c public 127.0.0.1 SNMPv2-MIB::system
+"""#
+from pysnmp.entity import engine, config
+from pysnmp.entity.rfc3413 import cmdrsp, context
+from pysnmp.carrier.asyncore.dgram import udp
+
+# Create SNMP engine with autogenernated engineID and pre-bound
+# to socket transport dispatcher
+snmpEngine = engine.SnmpEngine()
+
+# Transport setup
+
+# UDP over IPv4
+config.addTransport(
+ snmpEngine,
+ udp.domainName,
+ udp.UdpTransport().openServerMode(('127.0.0.1', 1161))
+)
+
+# Register default MIB instrumentation controller with a new SNMP context
+
+contextName = 'abcd'
+
+snmpContext = context.SnmpContext(snmpEngine)
+
+snmpContext.registerContextName(
+ contextName, snmpEngine.msgAndPduDsp.mibInstrumController)
+
+# Add new SNMP community name, map it to a new security name and
+# SNMP context
+
+securityName = 'my-area'
+communityName = 'public'
+
+config.addV1System(
+ snmpEngine, securityName, communityName,
+ contextEngineId=snmpContext.contextEngineId,
+ contextName=contextName)
+
+# VACM configuration settings
+
+securityModel = 2 # SNMPv2c
+securityLevel = 1 # noAuthNoPriv
+
+vacmGroup = 'my-group'
+readViewName = 'my-read-view'
+
+# We will match by context name prefix
+contextPrefix = contextName[:1]
+
+# Populate SNMP-VIEW-BASED-ACM-MIB::vacmContextTable
+config.addContext(snmpEngine, contextName)
+
+# Populate SNMP-VIEW-BASED-ACM-MIB::vacmSecurityToGroupTable
+config.addVacmGroup(
+ snmpEngine, vacmGroup, securityModel, securityName)
+
+# Populate SNMP-VIEW-BASED-ACM-MIB::vacmAccessTable
+config.addVacmAccess(
+ snmpEngine, vacmGroup, contextPrefix, securityModel, securityLevel,
+ 'prefix', readViewName, '', '')
+
+# Populate SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyTable
+
+# Allow the whole system subtree
+config.addVacmView(
+ snmpEngine, readViewName, 'included', '1.3.6.1.2.1.1.1', '1.1.1.1.1.1.1.0')
+
+# ...but exclude one sub-branch (just one scalar OID)
+config.addVacmView(
+ snmpEngine, readViewName, 'excluded', '1.3.6.1.2.1.1.3', '1.1.1.1.1.1.1.1')
+
+# Register SNMP Applications at the SNMP engine for particular SNMP context
+cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
+cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
+cmdrsp.NextCommandResponder(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 Exception:
+ snmpEngine.transportDispatcher.closeDispatcher()
+ raise
diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py
index 20fe63a0..8e76bf3e 100644
--- a/pysnmp/entity/config.py
+++ b/pysnmp/entity/config.py
@@ -11,6 +11,7 @@ from pysnmp.proto.secmod.rfc3414.priv import des, nopriv
from pysnmp.proto.secmod.rfc3826.priv import aes
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256
+from pysnmp.proto import rfc1902
from pysnmp.proto import rfc1905
from pysnmp import error
@@ -461,22 +462,20 @@ def __cookVacmAccessInfo(snmpEngine, groupName, contextName, securityModel,
return vacmAccessEntry, tblIdx
-def addVacmAccess(snmpEngine, groupName, contextName, securityModel,
- securityLevel, prefix, readView, writeView, notifyView):
- vacmAccessEntry, tblIdx = __cookVacmAccessInfo(snmpEngine, groupName,
- contextName, securityModel,
- securityLevel)
-
- addContext(snmpEngine, contextName)
+def addVacmAccess(snmpEngine, groupName, contextPrefix, securityModel,
+ securityLevel, contextMatch, readView, writeView, notifyView):
+ vacmAccessEntry, tblIdx = __cookVacmAccessInfo(
+ snmpEngine, groupName, contextPrefix, securityModel,
+ securityLevel)
snmpEngine.msgAndPduDsp.mibInstrumController.writeVars(
((vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),)
)
snmpEngine.msgAndPduDsp.mibInstrumController.writeVars(
- ((vacmAccessEntry.name + (1,) + tblIdx, contextName),
+ ((vacmAccessEntry.name + (1,) + tblIdx, contextPrefix),
(vacmAccessEntry.name + (2,) + tblIdx, securityModel),
(vacmAccessEntry.name + (3,) + tblIdx, securityLevel),
- (vacmAccessEntry.name + (4,) + tblIdx, prefix),
+ (vacmAccessEntry.name + (4,) + tblIdx, contextMatch),
(vacmAccessEntry.name + (5,) + tblIdx, readView),
(vacmAccessEntry.name + (6,) + tblIdx, writeView),
(vacmAccessEntry.name + (7,) + tblIdx, notifyView),
@@ -484,13 +483,10 @@ def addVacmAccess(snmpEngine, groupName, contextName, securityModel,
)
-def delVacmAccess(snmpEngine, groupName, contextName, securityModel,
+def delVacmAccess(snmpEngine, groupName, contextPrefix, securityModel,
securityLevel):
- vacmAccessEntry, tblIdx = __cookVacmAccessInfo(snmpEngine, groupName,
- contextName, securityModel,
- securityLevel)
-
- delContext(snmpEngine, contextName)
+ vacmAccessEntry, tblIdx = __cookVacmAccessInfo(
+ snmpEngine, groupName, contextPrefix, securityModel, securityLevel)
snmpEngine.msgAndPduDsp.mibInstrumController.writeVars(
((vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),)
@@ -507,16 +503,30 @@ def __cookVacmViewInfo(snmpEngine, viewName, subTree):
return vacmViewTreeFamilyEntry, tblIdx
-def addVacmView(snmpEngine, viewName, viewType, subTree, mask):
- vacmViewTreeFamilyEntry, tblIdx = __cookVacmViewInfo(snmpEngine, viewName,
- subTree)
+def addVacmView(snmpEngine, viewName, viewType, subTree, subTreeMask):
+ vacmViewTreeFamilyEntry, tblIdx = __cookVacmViewInfo(
+ snmpEngine, viewName, subTree)
+
+ # Allow bitmask specification in form of an OID
+ if '.' in subTreeMask:
+ subTreeMask = rfc1902.ObjectIdentifier(subTreeMask)
+
+ if isinstance(subTreeMask, rfc1902.ObjectIdentifier):
+ subTreeMask = tuple(subTreeMask)
+ if len(subTreeMask) < len(subTree):
+ subTreeMask += (1,) * (len(subTree) - len(subTreeMask))
+
+ subTreeMask = rfc1902.OctetString.fromBinaryString(
+ ''.join(str(x) for x in subTreeMask))
+
snmpEngine.msgAndPduDsp.mibInstrumController.writeVars(
((vacmViewTreeFamilyEntry.name + (6,) + tblIdx, 'destroy'),)
)
+
snmpEngine.msgAndPduDsp.mibInstrumController.writeVars(
((vacmViewTreeFamilyEntry.name + (1,) + tblIdx, viewName),
(vacmViewTreeFamilyEntry.name + (2,) + tblIdx, subTree),
- (vacmViewTreeFamilyEntry.name + (3,) + tblIdx, mask),
+ (vacmViewTreeFamilyEntry.name + (3,) + tblIdx, subTreeMask),
(vacmViewTreeFamilyEntry.name + (4,) + tblIdx, viewType),
(vacmViewTreeFamilyEntry.name + (6,) + tblIdx, 'createAndGo'))
)
@@ -548,15 +558,16 @@ def addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
(groupName, securityLevel, readView, writeView,
notifyView) = __cookVacmUserInfo(snmpEngine, securityModel, securityName,
securityLevel)
+ addContext(snmpEngine, contextName)
addVacmGroup(snmpEngine, groupName, securityModel, securityName)
addVacmAccess(snmpEngine, groupName, contextName, securityModel,
- securityLevel, 1, readView, writeView, notifyView)
+ securityLevel, 'exact', readView, writeView, notifyView)
if readSubTree:
- addVacmView(snmpEngine, readView, "included", readSubTree, null)
+ addVacmView(snmpEngine, readView, 'included', readSubTree, null)
if writeSubTree:
- addVacmView(snmpEngine, writeView, "included", writeSubTree, null)
+ addVacmView(snmpEngine, writeView, 'included', writeSubTree, null)
if notifySubTree:
- addVacmView(snmpEngine, notifyView, "included", notifySubTree, null)
+ addVacmView(snmpEngine, notifyView, 'included', notifySubTree, null)
def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
@@ -565,6 +576,7 @@ def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
(groupName, securityLevel, readView, writeView,
notifyView) = __cookVacmUserInfo(snmpEngine, securityModel,
securityName, securityLevel)
+ delContext(snmpEngine, contextName)
delVacmGroup(snmpEngine, securityModel, securityName)
delVacmAccess(snmpEngine, groupName, contextName, securityModel, securityLevel)
if readSubTree:
diff --git a/pysnmp/proto/acmod/rfc3415.py b/pysnmp/proto/acmod/rfc3415.py
index cf020d9f..a1e9f6f7 100644
--- a/pysnmp/proto/acmod/rfc3415.py
+++ b/pysnmp/proto/acmod/rfc3415.py
@@ -16,6 +16,117 @@ class Vacm(object):
_powOfTwoSeq = (128, 64, 32, 16, 8, 4, 2, 1)
+ def __init__(self):
+ self._contextBranchId = -1
+ self._groupNameBranchId = -1
+ self._accessBranchId = -1
+ self._viewTreeBranchId = -1
+
+ self._contextMap = {}
+ self._groupNameMap = {}
+ self._accessMap = {}
+ self._viewTreeMap = {}
+
+ def _addAccessEntry(self, groupName, contextPrefix, securityModel,
+ securityLevel, prefixMatch, readView, writeView,
+ notifyView):
+ if not groupName:
+ return
+
+ groups = self._accessMap
+
+ try:
+ views = groups[groupName]
+
+ except KeyError:
+ views = groups[groupName] = {}
+
+ for viewType, viewName in (
+ ('read', readView), ('write', writeView),
+ ('notify', notifyView)):
+
+ try:
+ matches = views[viewType]
+
+ except KeyError:
+ matches = views[viewType] = {}
+
+ try:
+ contexts = matches[prefixMatch]
+
+ except KeyError:
+ contexts = matches[prefixMatch] = {}
+
+ try:
+ models = contexts[contextPrefix]
+
+ except KeyError:
+ models = contexts[contextPrefix] = {}
+
+ try:
+ levels = models[securityModel]
+
+ except KeyError:
+ levels = models[securityModel] = {}
+
+ levels[securityLevel] = viewName
+
+ def _getFamilyViewName(self, groupName, contextName, securityModel, securityLevel, viewType):
+ groups = self._accessMap
+
+ try:
+ views = groups[groupName]
+
+ except KeyError:
+ raise error.StatusInformation(errorIndication=errind.noGroupName)
+
+ try:
+ matches = views[viewType]
+
+ except KeyError:
+ raise error.StatusInformation(errorIndication=errind.noAccessEntry)
+
+ try:
+ # vacmAccessTable #2: exact match shortcut
+ return matches[1][contextName][securityModel][securityLevel]
+
+ except KeyError:
+ pass
+
+ # vacmAccessTable #2: fuzzy look-up
+
+ candidates = []
+
+ for match, names in matches.items():
+
+ for context, models in names.items():
+
+ if match == 1 and contextName != context:
+ continue
+
+ if match == 2 and contextName[:len(context)] != context:
+ continue
+
+ for model, levels in models.items():
+ for level, viewName in levels.items():
+
+ # priorities:
+ # - matching securityModel
+ # - exact context name match
+ # - longer partial match
+ # - highest securityLevel
+ rating = securityModel == model, match == 1, len(context), level
+
+ candidates.append((rating, viewName))
+
+ if not candidates:
+ raise error.StatusInformation(errorIndication=errind.notInView)
+
+ candidates.sort()
+
+ rating, viewName = candidates[0]
+ return viewName
+
def isAccessAllowed(self,
snmpEngine,
securityModel,
@@ -24,118 +135,245 @@ class Vacm(object):
viewType,
contextName,
variableName):
+
mibInstrumController = snmpEngine.msgAndPduDsp.mibInstrumController
debug.logger & debug.flagACL and debug.logger(
- 'isAccessAllowed: securityModel %s, securityName %s, securityLevel %s, viewType %s, contextName %s for variableName %s' % (
- securityModel, securityName, securityLevel, viewType, contextName, variableName))
+ 'isAccessAllowed: securityModel %s, securityName %s, '
+ 'securityLevel %s, viewType %s, contextName %s for '
+ 'variableName %s' % (securityModel, securityName,
+ securityLevel, viewType, contextName,
+ variableName))
- # 3.2.1
- vacmContextEntry, = mibInstrumController.mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmContextEntry')
+ # Rebuild contextName map if changed
- tblIdx = vacmContextEntry.getInstIdFromIndices(contextName)
- try:
- vacmContextEntry.getNode(
- vacmContextEntry.name + (1,) + tblIdx
- ).syntax
+ vacmContextName, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmContextName')
+
+ if self._contextBranchId != vacmContextName.branchVersionId:
+
+ self._contextMap.clear()
+
+ nextMibNode = vacmContextName
+
+ while True:
+ try:
+ nextMibNode = vacmContextName.getNextNode(nextMibNode.name)
+
+ except NoSuchInstanceError:
+ break
- except NoSuchInstanceError:
+ self._contextMap[nextMibNode.syntax] = True
+
+ self._contextBranchId = vacmContextName.branchVersionId
+
+ # 3.2.1
+ if contextName not in self._contextMap:
raise error.StatusInformation(errorIndication=errind.noSuchContext)
+ # Rebuild groupName map if changed
+
+ vacmGroupName, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmGroupName')
+
+ if self._groupNameBranchId != vacmGroupName.branchVersionId:
+
+ vacmSecurityToGroupEntry, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmSecurityToGroupEntry')
+
+ self._groupNameMap.clear()
+
+ nextMibNode = vacmGroupName
+
+ while True:
+ try:
+ nextMibNode = vacmGroupName.getNextNode(nextMibNode.name)
+
+ except NoSuchInstanceError:
+ break
+
+ instId = nextMibNode.name[len(vacmGroupName.name):]
+
+ indices = vacmSecurityToGroupEntry.getIndicesFromInstId(instId)
+
+ self._groupNameMap[indices] = nextMibNode.syntax
+
+ self._groupNameBranchId = vacmGroupName.branchVersionId
+
# 3.2.2
- vacmSecurityToGroupEntry, = mibInstrumController.mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmSecurityToGroupEntry')
- tblIdx = vacmSecurityToGroupEntry.getInstIdFromIndices(
- securityModel, securityName
- )
+ indices = securityModel, securityName
try:
- vacmGroupName = vacmSecurityToGroupEntry.getNode(
- vacmSecurityToGroupEntry.name + (3,) + tblIdx
- ).syntax
+ groupName = self._groupNameMap[indices]
- except NoSuchInstanceError:
+ except KeyError:
raise error.StatusInformation(errorIndication=errind.noGroupName)
- # 3.2.3
- vacmAccessEntry, = mibInstrumController.mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmAccessEntry'
- )
-
- # XXX partial context name match
- tblIdx = vacmAccessEntry.getInstIdFromIndices(
- vacmGroupName, contextName, securityModel, securityLevel
- )
-
- # 3.2.4
- if viewType == 'read':
- entryIdx = vacmAccessEntry.name + (5,) + tblIdx
- elif viewType == 'write':
- entryIdx = vacmAccessEntry.name + (6,) + tblIdx
- elif viewType == 'notify':
- entryIdx = vacmAccessEntry.name + (7,) + tblIdx
- else:
- raise error.ProtocolError('Unknown view type %s' % viewType)
+ # Rebuild access map if changed
- try:
- viewName = vacmAccessEntry.getNode(entryIdx).syntax
+ vacmAccessStatus, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmAccessStatus')
- except NoSuchInstanceError:
- raise error.StatusInformation(errorIndication=errind.noAccessEntry)
+ if self._accessBranchId != vacmAccessStatus.branchVersionId:
+
+ (vacmAccessEntry,
+ vacmAccessContextPrefix,
+ vacmAccessSecurityModel,
+ vacmAccessSecurityLevel,
+ vacmAccessContextMatch,
+ vacmAccessReadViewName,
+ vacmAccessWriteViewName,
+ vacmAccessNotifyViewName) = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB',
+ 'vacmAccessEntry',
+ 'vacmAccessContextPrefix',
+ 'vacmAccessSecurityModel',
+ 'vacmAccessSecurityLevel',
+ 'vacmAccessContextMatch',
+ 'vacmAccessReadViewName',
+ 'vacmAccessWriteViewName',
+ 'vacmAccessNotifyViewName')
+
+ self._accessMap.clear()
+
+ nextMibNode = vacmAccessStatus
+
+ while True:
+ try:
+ nextMibNode = vacmAccessStatus.getNextNode(nextMibNode.name)
+
+ except NoSuchInstanceError:
+ break
+
+ if nextMibNode.syntax != 1: # active row
+ continue
+
+ instId = nextMibNode.name[len(vacmAccessStatus.name):]
+
+ indices = vacmAccessEntry.getIndicesFromInstId(instId)
+
+ vacmGroupName = indices[0]
+
+ self._addAccessEntry(
+ vacmGroupName,
+ vacmAccessContextPrefix.getNode(
+ vacmAccessContextPrefix.name + instId).syntax,
+ vacmAccessSecurityModel.getNode(
+ vacmAccessSecurityModel.name + instId).syntax,
+ vacmAccessSecurityLevel.getNode(
+ vacmAccessSecurityLevel.name + instId).syntax,
+ vacmAccessContextMatch.getNode(
+ vacmAccessContextMatch.name + instId).syntax,
+ vacmAccessReadViewName.getNode(
+ vacmAccessReadViewName.name + instId).syntax,
+ vacmAccessWriteViewName.getNode(
+ vacmAccessWriteViewName.name + instId).syntax,
+ vacmAccessNotifyViewName.getNode(
+ vacmAccessNotifyViewName.name + instId).syntax
+ )
+
+ self._accessBranchId = vacmAccessStatus.branchVersionId
+
+ viewName = self._getFamilyViewName(
+ groupName, contextName, securityModel, securityLevel, viewType)
+
+ # Rebuild family subtree map if changed
+
+ vacmViewTreeFamilyViewName, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmViewTreeFamilyViewName')
+
+ if self._viewTreeBranchId != vacmViewTreeFamilyViewName.branchVersionId:
+
+ (vacmViewTreeFamilySubtree,
+ vacmViewTreeFamilyMask,
+ vacmViewTreeFamilyType) = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB',
+ 'vacmViewTreeFamilySubtree',
+ 'vacmViewTreeFamilyMask',
+ 'vacmViewTreeFamilyType')
+
+ self._viewTreeMap.clear()
- if not viewName:
- raise error.StatusInformation(errorIndication=errind.noSuchView)
+ powerOfTwo = [2 ** exp for exp in range(7, -1, -1)]
- # XXX split onto object & instance ?
+ nextMibNode = vacmViewTreeFamilyViewName
+
+ while True:
+ try:
+ nextMibNode = vacmViewTreeFamilyViewName.getNextNode(
+ nextMibNode.name)
+
+ except NoSuchInstanceError:
+ break
+
+ if nextMibNode.syntax not in self._viewTreeMap:
+ self._viewTreeMap[nextMibNode.syntax] = []
+
+ instId = nextMibNode.name[len(vacmViewTreeFamilyViewName.name):]
+
+ subtree = vacmViewTreeFamilySubtree.getNode(
+ vacmViewTreeFamilySubtree.name + instId).syntax
+
+ mask = vacmViewTreeFamilyMask.getNode(
+ vacmViewTreeFamilyMask.name + instId).syntax
+
+ mode = vacmViewTreeFamilyType.getNode(
+ vacmViewTreeFamilyType.name + instId).syntax
+
+ mask = mask.asNumbers()
+ maskLength = min(len(mask) * 8, len(subtree))
+
+ ignoredSubOids = [
+ i * 8 + j for i, octet in enumerate(mask)
+ for j, bit in enumerate(powerOfTwo)
+ if not (bit & octet) and i * 8 + j < maskLength
+ ]
+
+ if ignoredSubOids:
+ pattern = list(subtree)
+
+ for ignoredSubOid in ignoredSubOids:
+ pattern[ignoredSubOid] = 0
+
+ subtree = subtree.clone(pattern)
+
+ entry = subtree, ignoredSubOids, mode == 1
+
+ self._viewTreeMap[nextMibNode.syntax].append(entry)
+
+ for entries in self._viewTreeMap.values():
+ entries.sort(key=lambda x: (len(x[0]), x[0]))
+
+ self._viewTreeBranchId = vacmViewTreeFamilyViewName.branchVersionId
# 3.2.5a
- vacmViewTreeFamilyEntry, = mibInstrumController.mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmViewTreeFamilyEntry')
- tblIdx = vacmViewTreeFamilyEntry.getInstIdFromIndices(viewName)
-
- # Walk over entries
- initialTreeName = treeName = vacmViewTreeFamilyEntry.name + (2,) + tblIdx
- maskName = vacmViewTreeFamilyEntry.name + (3,) + tblIdx
-
- while True:
- vacmViewTreeFamilySubtree = vacmViewTreeFamilyEntry.getNextNode(
- treeName
- )
- vacmViewTreeFamilyMask = vacmViewTreeFamilyEntry.getNextNode(
- maskName
- )
-
- treeName = vacmViewTreeFamilySubtree.name
- maskName = vacmViewTreeFamilyMask.name
-
- if initialTreeName != treeName[:len(initialTreeName)]:
- # 3.2.5b
- raise error.StatusInformation(errorIndication=errind.notInView)
-
- l = len(vacmViewTreeFamilySubtree.syntax)
- if l > len(variableName):
- continue
-
- if vacmViewTreeFamilyMask.syntax:
- mask = []
- for c in vacmViewTreeFamilyMask.syntax.asNumbers():
- mask.extend([b & c for b in self._powOfTwoSeq])
-
- m = len(mask) - 1
- idx = l - 1
- while idx:
- if (idx > m or mask[idx] and
- vacmViewTreeFamilySubtree.syntax[idx] != variableName[idx]):
- break
- idx -= 1
-
- if idx:
- continue # no match
-
- else: # no mask
- if vacmViewTreeFamilySubtree.syntax != variableName[:l]:
- continue # no match
-
- # 3.2.5c
- return error.StatusInformation(errorIndication=errind.accessAllowed)
+ indices = viewName
+
+ try:
+ entries = self._viewTreeMap[indices]
+
+ except KeyError:
+ return error.StatusInformation(errorIndication=errind.notInView)
+
+ accessAllowed = False
+
+ for entry in entries:
+ subtree, ignoredSubOids, included = entry
+
+ if ignoredSubOids:
+ subOids = list(variableName)
+
+ for ignoredSubOid in ignoredSubOids:
+ subOids[ignoredSubOid] = 0
+
+ normalizedVariableName = subtree.clone(subOids)
+
+ else:
+ normalizedVariableName = variableName
+
+ if subtree.isPrefixOf(normalizedVariableName):
+ accessAllowed = included
+
+ # 3.2.5c
+ if not accessAllowed:
+ raise error.StatusInformation(errorIndication=errind.notInView)