summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2019-07-29 09:57:45 +0200
committerIlya Etingof <etingof@gmail.com>2019-07-29 21:37:34 +0200
commit6c7b09ac88be195db176c37ca7a197265ca978d0 (patch)
tree7424c9617f5895405843ebeda345a50688231f43
parent43cd9ab639a432c42c783f06772b4f3981b80c83 (diff)
downloadpysnmp-git-6c7b09ac88be195db176c37ca7a197265ca978d0.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.txt16
-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.py53
-rw-r--r--pysnmp/proto/acmod/rfc3415.py400
5 files changed, 494 insertions, 102 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 4933e9b4..3579840c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,5 @@
-Revision 5.0.0, released 2019-03-XX
+Revision 5.0.0, released 2019-08-XX
-----------------------------------
- Python 2.6 becomes the minimum supported Python version! \o/
@@ -99,9 +99,21 @@ Revision 5.0.0, released 2019-03-XX
* Global constants turned UPPER_CASED
* Long lines wrapped by more or less 80 chars
-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 57e8704f..b484355a 100644
--- a/pysnmp/entity/config.py
+++ b/pysnmp/entity/config.py
@@ -19,6 +19,9 @@ from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
from pysnmp.proto.secmod.eso.priv import aes192
from pysnmp.proto.secmod.eso.priv import aes256
from pysnmp.proto.secmod.eso.priv import des3
+from pysnmp.proto import rfc1902
+from pysnmp.proto import rfc1905
+from pysnmp import error
# A shortcut to popular constants
@@ -568,12 +571,11 @@ def __cookVacmAccessInfo(snmpEngine, groupName, contextName, securityModel,
return vacmAccessEntry, tblIdx
-def addVacmAccess(snmpEngine, groupName, contextName, securityModel,
- securityLevel, prefix, readView, writeView, notifyView):
+def addVacmAccess(snmpEngine, groupName, contextPrefix, securityModel,
+ securityLevel, contextMatch, readView, writeView, notifyView):
vacmAccessEntry, tblIdx = __cookVacmAccessInfo(
- snmpEngine, groupName, contextName, securityModel, securityLevel)
-
- addContext(snmpEngine, contextName)
+ snmpEngine, groupName, contextPrefix, securityModel,
+ securityLevel)
snmpEngine.msgAndPduDsp.mibInstrumController.writeMibObjects(
(vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),
@@ -581,10 +583,10 @@ def addVacmAccess(snmpEngine, groupName, contextName, securityModel,
)
snmpEngine.msgAndPduDsp.mibInstrumController.writeMibObjects(
- (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),
@@ -593,13 +595,11 @@ 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)
+ snmpEngine, groupName, contextPrefix, securityModel, securityLevel)
snmpEngine.msgAndPduDsp.mibInstrumController.writeMibObjects(
(vacmAccessEntry.name + (9,) + tblIdx, 'destroy'),
@@ -618,7 +618,19 @@ def __cookVacmViewInfo(snmpEngine, viewName, subTree):
return vacmViewTreeFamilyEntry, tblIdx
-def addVacmView(snmpEngine, viewName, viewType, subTree, mask):
+def addVacmView(snmpEngine, viewName, viewType, subTree, subTreeMask):
+
+ # 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))
vacmViewTreeFamilyEntry, tblIdx = __cookVacmViewInfo(
snmpEngine, viewName, subTree)
@@ -631,7 +643,7 @@ def addVacmView(snmpEngine, viewName, viewType, subTree, mask):
snmpEngine.msgAndPduDsp.mibInstrumController.writeMibObjects(
(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'),
snmpEngine=snmpEngine
@@ -671,19 +683,21 @@ def addVacmUser(snmpEngine, securityModel, securityName, securityLevel,
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,
@@ -693,9 +707,10 @@ def delVacmUser(snmpEngine, securityModel, securityName, securityLevel,
notifyView) = __cookVacmUserInfo(
snmpEngine, securityModel, securityName, securityLevel)
+ delContext(snmpEngine, contextName)
delVacmGroup(snmpEngine, securityModel, securityName)
-
- delVacmAccess(snmpEngine, groupName, contextName, securityModel, securityLevel)
+ delVacmAccess(snmpEngine, groupName, contextName,
+ securityModel, securityLevel)
if readSubTree:
delVacmView(snmpEngine, readView, readSubTree)
diff --git a/pysnmp/proto/acmod/rfc3415.py b/pysnmp/proto/acmod/rfc3415.py
index 13d9de12..4d987666 100644
--- a/pysnmp/proto/acmod/rfc3415.py
+++ b/pysnmp/proto/acmod/rfc3415.py
@@ -17,127 +17,365 @@ class Vacm(object):
_powOfTwoSeq = (128, 64, 32, 16, 8, 4, 2, 1)
- def isAccessAllowed(self, snmpEngine, securityModel, securityName,
- securityLevel, viewType, contextName, variableName):
+ 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,
+ securityName,
+ securityLevel,
+ viewType,
+ contextName,
+ variableName):
mibInstrumController = snmpEngine.msgAndPduDsp.mibInstrumController
mibBuilder = mibInstrumController.mibBuilder
- debug.logger & debug.FLAG_ACL and debug.logger(
+ 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))
+ 'variableName %s' % (securityModel, securityName,
+ securityLevel, viewType, contextName,
+ variableName))
- # 3.2.1
- vacmContextEntry, = mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmContextEntry')
+ # Rebuild contextName map if changed
- tblIdx = vacmContextEntry.getInstIdFromIndices(contextName)
+ vacmContextName, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmContextName')
- try:
- vacmContextEntry.getNode(
- vacmContextEntry.name + (1,) + tblIdx).syntax
+ if self._contextBranchId != vacmContextName.branchVersionId:
+
+ self._contextMap.clear()
+
+ nextMibNode = vacmContextName
+
+ while True:
+ try:
+ nextMibNode = vacmContextName.getNextNode(nextMibNode.name)
- except NoSuchInstanceError:
+ except NoSuchInstanceError:
+ break
+
+ self._contextMap[nextMibNode.syntax] = True
+
+ self._contextBranchId = vacmContextName.branchVersionId
+
+ # 3.2.1
+ if contextName not in self._contextMap:
raise error.StatusInformation(errorIndication=errind.noSuchContext)
- # 3.2.2
- vacmSecurityToGroupEntry, = mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmSecurityToGroupEntry')
+ # 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
- tblIdx = vacmSecurityToGroupEntry.getInstIdFromIndices(
- securityModel, securityName)
+ instId = nextMibNode.name[len(vacmGroupName.name):]
+
+ indices = vacmSecurityToGroupEntry.getIndicesFromInstId(instId)
+
+ self._groupNameMap[indices] = nextMibNode.syntax
+
+ self._groupNameBranchId = vacmGroupName.branchVersionId
+
+ # 3.2.2
+ 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, = mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmAccessEntry')
+ # Rebuild access map if changed
- # XXX partial context name match
- tblIdx = vacmAccessEntry.getInstIdFromIndices(
- vacmGroupName, contextName, securityModel, securityLevel)
+ vacmAccessStatus, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmAccessStatus')
- # 3.2.4
- if viewType == 'read':
- entryIdx = vacmAccessEntry.name + (5,) + tblIdx
+ if self._accessBranchId != vacmAccessStatus.branchVersionId:
- elif viewType == 'write':
- entryIdx = vacmAccessEntry.name + (6,) + tblIdx
+ (vacmAccessEntry,
+ vacmAccessContextPrefix,
+ vacmAccessSecurityModel,
+ vacmAccessSecurityLevel,
+ vacmAccessContextMatch,
+ vacmAccessReadViewName,
+ vacmAccessWriteViewName,
+ vacmAccessNotifyViewName) = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB',
+ 'vacmAccessEntry',
+ 'vacmAccessContextPrefix',
+ 'vacmAccessSecurityModel',
+ 'vacmAccessSecurityLevel',
+ 'vacmAccessContextMatch',
+ 'vacmAccessReadViewName',
+ 'vacmAccessWriteViewName',
+ 'vacmAccessNotifyViewName')
- elif viewType == 'notify':
- entryIdx = vacmAccessEntry.name + (7,) + tblIdx
+ self._accessMap.clear()
- else:
- raise error.ProtocolError('Unknown view type %s' % viewType)
+ nextMibNode = vacmAccessStatus
- try:
- viewName = vacmAccessEntry.getNode(entryIdx).syntax
+ while True:
+ try:
+ nextMibNode = vacmAccessStatus.getNextNode(nextMibNode.name)
- except NoSuchInstanceError:
- raise error.StatusInformation(errorIndication=errind.noAccessEntry)
+ except NoSuchInstanceError:
+ break
- if not viewName:
- raise error.StatusInformation(errorIndication=errind.noSuchView)
+ if nextMibNode.syntax != 1: # active row
+ continue
- # XXX split onto object & instance ?
+ instId = nextMibNode.name[len(vacmAccessStatus.name):]
- # 3.2.5a
- vacmViewTreeFamilyEntry, = mibInstrumController.mibBuilder.importSymbols(
- 'SNMP-VIEW-BASED-ACM-MIB', 'vacmViewTreeFamilyEntry')
+ 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
- tblIdx = vacmViewTreeFamilyEntry.getInstIdFromIndices(viewName)
+ viewName = self._getFamilyViewName(
+ groupName, contextName, securityModel, securityLevel, viewType)
- # Walk over entries
- initialTreeName = treeName = vacmViewTreeFamilyEntry.name + (2,) + tblIdx
+ # Rebuild family subtree map if changed
- maskName = vacmViewTreeFamilyEntry.name + (3,) + tblIdx
+ vacmViewTreeFamilyViewName, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB', 'vacmViewTreeFamilyViewName')
- while True:
- vacmViewTreeFamilySubtree = vacmViewTreeFamilyEntry.getNextNode(
- treeName)
+ if self._viewTreeBranchId != vacmViewTreeFamilyViewName.branchVersionId:
- vacmViewTreeFamilyMask = vacmViewTreeFamilyEntry.getNextNode(
- maskName)
+ (vacmViewTreeFamilySubtree,
+ vacmViewTreeFamilyMask,
+ vacmViewTreeFamilyType) = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-VIEW-BASED-ACM-MIB',
+ 'vacmViewTreeFamilySubtree',
+ 'vacmViewTreeFamilyMask',
+ 'vacmViewTreeFamilyType')
- treeName = vacmViewTreeFamilySubtree.name
- maskName = vacmViewTreeFamilyMask.name
+ self._viewTreeMap.clear()
+
+ powerOfTwo = [2 ** exp for exp in range(7, -1, -1)]
+
+ 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
+ indices = viewName
+
+ try:
+ entries = self._viewTreeMap[indices]
- if initialTreeName != treeName[:len(initialTreeName)]:
- # 3.2.5b
- raise error.StatusInformation(errorIndication=errind.notInView)
+ except KeyError:
+ return error.StatusInformation(errorIndication=errind.notInView)
- l = len(vacmViewTreeFamilySubtree.syntax)
- if l > len(variableName):
- continue
+ accessAllowed = False
- if vacmViewTreeFamilyMask.syntax:
- mask = []
- for c in vacmViewTreeFamilyMask.syntax.asNumbers():
- mask.extend([b & c for b in self._powOfTwoSeq])
+ for entry in entries:
+ subtree, ignoredSubOids, included = entry
- m = len(mask) - 1
- idx = l - 1
+ if ignoredSubOids:
+ subOids = list(variableName)
- while idx:
- if (idx > m or mask[idx] and
- vacmViewTreeFamilySubtree.syntax[idx] != variableName[idx]):
- break
+ for ignoredSubOid in ignoredSubOids:
+ subOids[ignoredSubOid] = 0
- idx -= 1
+ normalizedVariableName = subtree.clone(subOids)
- if idx:
- continue # no match
+ else:
+ normalizedVariableName = variableName
- else: # no mask
- if vacmViewTreeFamilySubtree.syntax != variableName[:l]:
- continue # no match
+ if subtree.isPrefixOf(normalizedVariableName):
+ accessAllowed = included
- # 3.2.5c
- return error.StatusInformation(errorIndication=errind.accessAllowed)
+ # 3.2.5c
+ if not accessAllowed:
+ raise error.StatusInformation(errorIndication=errind.notInView)