418 lines
19 KiB
Python
418 lines
19 KiB
Python
#
|
|
# This file is part of pysnmp software.
|
|
#
|
|
# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
|
|
# License: http://pysnmp.sf.net/license.html
|
|
#
|
|
import sys
|
|
from pyasn1.codec.ber import encoder
|
|
from pyasn1.error import PyAsn1Error
|
|
from pysnmp.proto.secmod import base
|
|
from pysnmp.carrier.asyncore.dgram import udp, udp6, unix
|
|
from pysnmp.smi.error import NoSuchInstanceError
|
|
from pysnmp.proto import errind, error
|
|
from pysnmp import debug
|
|
|
|
|
|
class SnmpV1SecurityModel(base.AbstractSecurityModel):
|
|
securityModelID = 1
|
|
|
|
# According to rfc2576, community name <-> contextEngineId/contextName
|
|
# mapping is up to MP module for notifications but belongs to secmod
|
|
# responsibility for other PDU types. Since I do not yet understand
|
|
# the reason for this de-coupling, I've moved this code from MP-scope
|
|
# in here.
|
|
|
|
def __init__(self):
|
|
self.__transportBranchId = self.__paramsBranchId = self.__communityBranchId = self.__securityBranchId = -1
|
|
base.AbstractSecurityModel.__init__(self)
|
|
|
|
def _sec2com(self, snmpEngine, securityName, contextEngineId, contextName):
|
|
snmpTargetParamsSecurityName, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-TARGET-MIB', 'snmpTargetParamsSecurityName')
|
|
if self.__paramsBranchId != snmpTargetParamsSecurityName.branchVersionId:
|
|
snmpTargetParamsSecurityModel, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-TARGET-MIB', 'snmpTargetParamsSecurityModel')
|
|
|
|
self.__nameToModelMap = {}
|
|
|
|
nextMibNode = snmpTargetParamsSecurityName
|
|
|
|
while 1:
|
|
try:
|
|
nextMibNode = snmpTargetParamsSecurityName.getNextNode(nextMibNode.name)
|
|
|
|
except NoSuchInstanceError:
|
|
break
|
|
|
|
instId = nextMibNode.name[len(snmpTargetParamsSecurityName.name):]
|
|
|
|
mibNode = snmpTargetParamsSecurityModel.getNode(snmpTargetParamsSecurityModel.name + instId)
|
|
|
|
if mibNode.syntax not in self.__nameToModelMap:
|
|
self.__nameToModelMap[nextMibNode.syntax] = set()
|
|
|
|
self.__nameToModelMap[nextMibNode.syntax].add(mibNode.syntax)
|
|
|
|
self.__paramsBranchId = snmpTargetParamsSecurityName.branchVersionId
|
|
|
|
# invalidate next map as it include this one
|
|
self.__securityBranchId = -1
|
|
|
|
snmpCommunityName, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-COMMUNITY-MIB',
|
|
'snmpCommunityName')
|
|
if self.__securityBranchId != snmpCommunityName.branchVersionId:
|
|
(snmpCommunitySecurityName,
|
|
snmpCommunityContextEngineId,
|
|
snmpCommunityContextName) = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-COMMUNITY-MIB', 'snmpCommunitySecurityName',
|
|
'snmpCommunityContextEngineID', 'snmpCommunityContextName'
|
|
)
|
|
|
|
self.__securityMap = {}
|
|
|
|
nextMibNode = snmpCommunityName
|
|
while 1:
|
|
try:
|
|
nextMibNode = snmpCommunityName.getNextNode(nextMibNode.name)
|
|
|
|
except NoSuchInstanceError:
|
|
break
|
|
|
|
instId = nextMibNode.name[len(snmpCommunityName.name):]
|
|
|
|
_securityName = snmpCommunitySecurityName.getNode(snmpCommunitySecurityName.name + instId).syntax
|
|
|
|
_contextEngineId = snmpCommunityContextEngineId.getNode(
|
|
snmpCommunityContextEngineId.name + instId).syntax
|
|
|
|
_contextName = snmpCommunityContextName.getNode(snmpCommunityContextName.name + instId).syntax
|
|
|
|
self.__securityMap[(_securityName,
|
|
_contextEngineId,
|
|
_contextName)] = nextMibNode.syntax
|
|
|
|
self.__securityBranchId = snmpCommunityName.branchVersionId
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'_sec2com: built securityName to communityName map, version %s: %s' % (
|
|
self.__securityBranchId, self.__securityMap))
|
|
|
|
try:
|
|
return self.__securityMap[(securityName,
|
|
contextEngineId,
|
|
contextName)]
|
|
|
|
except KeyError:
|
|
raise error.StatusInformation(
|
|
errorIndication=errind.unknownCommunityName
|
|
)
|
|
|
|
def _com2sec(self, snmpEngine, communityName, transportInformation):
|
|
snmpTargetAddrTAddress, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-TARGET-MIB', 'snmpTargetAddrTAddress')
|
|
if self.__transportBranchId != snmpTargetAddrTAddress.branchVersionId:
|
|
(SnmpTagValue, snmpTargetAddrTDomain,
|
|
snmpTargetAddrTagList) = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-TARGET-MIB', 'SnmpTagValue', 'snmpTargetAddrTDomain',
|
|
'snmpTargetAddrTagList'
|
|
)
|
|
|
|
self.__emptyTag = SnmpTagValue('')
|
|
|
|
self.__transportToTagMap = {}
|
|
|
|
nextMibNode = snmpTargetAddrTagList
|
|
while True:
|
|
try:
|
|
nextMibNode = snmpTargetAddrTagList.getNextNode(nextMibNode.name)
|
|
except NoSuchInstanceError:
|
|
break
|
|
instId = nextMibNode.name[len(snmpTargetAddrTagList.name):]
|
|
targetAddrTDomain = snmpTargetAddrTDomain.getNode(snmpTargetAddrTDomain.name + instId).syntax
|
|
targetAddrTAddress = snmpTargetAddrTAddress.getNode(snmpTargetAddrTAddress.name + instId).syntax
|
|
|
|
targetAddrTDomain = tuple(targetAddrTDomain)
|
|
|
|
if targetAddrTDomain[:len(udp.snmpUDPDomain)] == udp.snmpUDPDomain:
|
|
SnmpUDPAddress, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMPv2-TM',
|
|
'SnmpUDPAddress')
|
|
targetAddrTAddress = tuple(SnmpUDPAddress(targetAddrTAddress))
|
|
elif targetAddrTDomain[:len(udp6.snmpUDP6Domain)] == udp6.snmpUDP6Domain:
|
|
TransportAddressIPv6, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'TRANSPORT-ADDRESS-MIB', 'TransportAddressIPv6')
|
|
targetAddrTAddress = tuple(TransportAddressIPv6(targetAddrTAddress))
|
|
elif targetAddrTDomain[:len(unix.snmpLocalDomain)] == unix.snmpLocalDomain:
|
|
targetAddrTAddress = str(targetAddrTAddress)
|
|
targetAddr = targetAddrTDomain, targetAddrTAddress
|
|
targetAddrTagList = snmpTargetAddrTagList.getNode(snmpTargetAddrTagList.name + instId).syntax
|
|
if targetAddr not in self.__transportToTagMap:
|
|
self.__transportToTagMap[targetAddr] = set()
|
|
if targetAddrTagList:
|
|
self.__transportToTagMap[targetAddr].update(
|
|
[SnmpTagValue(x)
|
|
for x in targetAddrTagList.asOctets().split()]
|
|
)
|
|
else:
|
|
self.__transportToTagMap[targetAddr].add(self.__emptyTag)
|
|
|
|
self.__transportBranchId = snmpTargetAddrTAddress.branchVersionId
|
|
|
|
debug.logger & debug.flagSM and debug.logger('_com2sec: built transport-to-tag map version %s: %s' % (
|
|
self.__transportBranchId, self.__transportToTagMap))
|
|
|
|
snmpTargetParamsSecurityName, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-TARGET-MIB', 'snmpTargetParamsSecurityName')
|
|
if self.__paramsBranchId != snmpTargetParamsSecurityName.branchVersionId:
|
|
snmpTargetParamsSecurityModel, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-TARGET-MIB', 'snmpTargetParamsSecurityModel')
|
|
|
|
self.__nameToModelMap = {}
|
|
|
|
nextMibNode = snmpTargetParamsSecurityName
|
|
|
|
while True:
|
|
try:
|
|
nextMibNode = snmpTargetParamsSecurityName.getNextNode(nextMibNode.name)
|
|
|
|
except NoSuchInstanceError:
|
|
break
|
|
|
|
instId = nextMibNode.name[len(snmpTargetParamsSecurityName.name):]
|
|
|
|
mibNode = snmpTargetParamsSecurityModel.getNode(snmpTargetParamsSecurityModel.name + instId)
|
|
|
|
if nextMibNode.syntax not in self.__nameToModelMap:
|
|
self.__nameToModelMap[nextMibNode.syntax] = set()
|
|
|
|
self.__nameToModelMap[nextMibNode.syntax].add(mibNode.syntax)
|
|
|
|
self.__paramsBranchId = snmpTargetParamsSecurityName.branchVersionId
|
|
|
|
# invalidate next map as it include this one
|
|
self.__communityBranchId = -1
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'_com2sec: built securityName to securityModel map, version %s: %s' % (
|
|
self.__paramsBranchId, self.__nameToModelMap))
|
|
|
|
snmpCommunityName, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-COMMUNITY-MIB',
|
|
'snmpCommunityName')
|
|
if self.__communityBranchId != snmpCommunityName.branchVersionId:
|
|
(snmpCommunitySecurityName, snmpCommunityContextEngineId,
|
|
snmpCommunityContextName,
|
|
snmpCommunityTransportTag) = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'SNMP-COMMUNITY-MIB', 'snmpCommunitySecurityName',
|
|
'snmpCommunityContextEngineID', 'snmpCommunityContextName',
|
|
'snmpCommunityTransportTag'
|
|
)
|
|
|
|
self.__communityToTagMap = {}
|
|
self.__tagAndCommunityToSecurityMap = {}
|
|
|
|
nextMibNode = snmpCommunityName
|
|
while True:
|
|
try:
|
|
nextMibNode = snmpCommunityName.getNextNode(nextMibNode.name)
|
|
|
|
except NoSuchInstanceError:
|
|
break
|
|
|
|
instId = nextMibNode.name[len(snmpCommunityName.name):]
|
|
|
|
securityName = snmpCommunitySecurityName.getNode(snmpCommunitySecurityName.name + instId).syntax
|
|
|
|
contextEngineId = snmpCommunityContextEngineId.getNode(
|
|
snmpCommunityContextEngineId.name + instId).syntax
|
|
|
|
contextName = snmpCommunityContextName.getNode(snmpCommunityContextName.name + instId).syntax
|
|
|
|
transportTag = snmpCommunityTransportTag.getNode(snmpCommunityTransportTag.name + instId).syntax
|
|
|
|
_tagAndCommunity = transportTag, nextMibNode.syntax
|
|
|
|
if _tagAndCommunity not in self.__tagAndCommunityToSecurityMap:
|
|
self.__tagAndCommunityToSecurityMap[_tagAndCommunity] = set()
|
|
|
|
self.__tagAndCommunityToSecurityMap[_tagAndCommunity].add(
|
|
(securityName, contextEngineId, contextName)
|
|
)
|
|
|
|
if nextMibNode.syntax not in self.__communityToTagMap:
|
|
self.__communityToTagMap[nextMibNode.syntax] = set()
|
|
|
|
self.__communityToTagMap[nextMibNode.syntax].add(transportTag)
|
|
|
|
self.__communityBranchId = snmpCommunityName.branchVersionId
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'_com2sec: built communityName to tag map (securityModel %s), version %s: %s' % (
|
|
self.securityModelID, self.__communityBranchId, self.__communityToTagMap))
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'_com2sec: built tag & community to securityName map (securityModel %s), version %s: %s' % (
|
|
self.securityModelID, self.__communityBranchId, self.__tagAndCommunityToSecurityMap))
|
|
|
|
if communityName in self.__communityToTagMap:
|
|
if transportInformation in self.__transportToTagMap:
|
|
tags = self.__transportToTagMap[transportInformation].intersection(
|
|
self.__communityToTagMap[communityName])
|
|
elif self.__emptyTag in self.__communityToTagMap[communityName]:
|
|
tags = [self.__emptyTag]
|
|
else:
|
|
raise error.StatusInformation(errorIndication=errind.unknownCommunityName)
|
|
|
|
candidateSecurityNames = []
|
|
|
|
for x in [self.__tagAndCommunityToSecurityMap[(t, communityName)] for t in tags]:
|
|
candidateSecurityNames.extend(list(x))
|
|
|
|
# 5.2.1 (row selection in snmpCommunityTable)
|
|
# Picks first match but favors entries already in targets table
|
|
if candidateSecurityNames:
|
|
candidateSecurityNames.sort(
|
|
key=lambda x, m=self.__nameToModelMap, v=self.securityModelID: (
|
|
not int(x[0] in m and v in m[x[0]]), str(x[0]))
|
|
)
|
|
chosenSecurityName = candidateSecurityNames[0] # min()
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'_com2sec: securityName candidates for communityName \'%s\' are %s; choosing securityName \'%s\'' % (
|
|
communityName, candidateSecurityNames, chosenSecurityName[0]))
|
|
return chosenSecurityName
|
|
|
|
raise error.StatusInformation(errorIndication=errind.unknownCommunityName)
|
|
|
|
def generateRequestMsg(self, snmpEngine, messageProcessingModel,
|
|
globalData, maxMessageSize, securityModel,
|
|
securityEngineId, securityName, securityLevel,
|
|
scopedPDU):
|
|
msg, = globalData
|
|
contextEngineId, contextName, pdu = scopedPDU
|
|
|
|
# rfc2576: 5.2.3
|
|
communityName = self._sec2com(snmpEngine, securityName,
|
|
contextEngineId, contextName)
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'generateRequestMsg: using community %r for securityModel %r, securityName %r, contextEngineId %r contextName %r' % (
|
|
communityName, securityModel, securityName, contextEngineId, contextName))
|
|
|
|
securityParameters = communityName
|
|
|
|
msg.setComponentByPosition(1, securityParameters)
|
|
msg.setComponentByPosition(2)
|
|
msg.getComponentByPosition(2).setComponentByType(
|
|
pdu.tagSet, pdu, verifyConstraints=False
|
|
)
|
|
|
|
debug.logger & debug.flagMP and debug.logger('generateRequestMsg: %s' % (msg.prettyPrint(),))
|
|
|
|
try:
|
|
return securityParameters, encoder.encode(msg)
|
|
|
|
except PyAsn1Error:
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'generateRequestMsg: serialization failure: %s' % sys.exc_info()[1])
|
|
raise error.StatusInformation(errorIndication=errind.serializationError)
|
|
|
|
def generateResponseMsg(self, snmpEngine, messageProcessingModel,
|
|
globalData, maxMessageSize, securityModel,
|
|
securityEngineID, securityName, securityLevel,
|
|
scopedPDU, securityStateReference):
|
|
# rfc2576: 5.2.2
|
|
msg, = globalData
|
|
contextEngineId, contextName, pdu = scopedPDU
|
|
cachedSecurityData = self._cache.pop(securityStateReference)
|
|
communityName = cachedSecurityData['communityName']
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'generateResponseMsg: recovered community %r by securityStateReference %s' % (
|
|
communityName, securityStateReference))
|
|
|
|
msg.setComponentByPosition(1, communityName)
|
|
msg.setComponentByPosition(2)
|
|
msg.getComponentByPosition(2).setComponentByType(
|
|
pdu.tagSet, pdu, verifyConstraints=False
|
|
)
|
|
|
|
debug.logger & debug.flagMP and debug.logger('generateResponseMsg: %s' % (msg.prettyPrint(),))
|
|
|
|
try:
|
|
return communityName, encoder.encode(msg)
|
|
|
|
except PyAsn1Error:
|
|
debug.logger & debug.flagMP and debug.logger(
|
|
'generateResponseMsg: serialization failure: %s' % sys.exc_info()[1])
|
|
raise error.StatusInformation(errorIndication=errind.serializationError)
|
|
|
|
def processIncomingMsg(self, snmpEngine, messageProcessingModel,
|
|
maxMessageSize, securityParameters, securityModel,
|
|
securityLevel, wholeMsg, msg):
|
|
# rfc2576: 5.2.1
|
|
communityName, transportInformation = securityParameters
|
|
|
|
scope = dict(communityName=communityName,
|
|
transportInformation=transportInformation)
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine, 'rfc2576.processIncomingMsg:writable', scope
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc2576.processIncomingMsg:writable'
|
|
)
|
|
|
|
try:
|
|
securityName, contextEngineId, contextName = self._com2sec(
|
|
snmpEngine, scope.get('communityName', communityName),
|
|
scope.get('transportInformation', transportInformation)
|
|
)
|
|
|
|
except error.StatusInformation:
|
|
snmpInBadCommunityNames, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
|
|
'__SNMPv2-MIB', 'snmpInBadCommunityNames')
|
|
snmpInBadCommunityNames.syntax += 1
|
|
raise error.StatusInformation(errorIndication=errind.unknownCommunityName)
|
|
|
|
snmpEngineID, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB',
|
|
'snmpEngineID')
|
|
|
|
securityEngineID = snmpEngineID.syntax
|
|
|
|
snmpEngine.observer.storeExecutionContext(
|
|
snmpEngine, 'rfc2576.processIncomingMsg',
|
|
dict(transportInformation=transportInformation,
|
|
securityEngineId=securityEngineID,
|
|
securityName=securityName,
|
|
communityName=communityName,
|
|
contextEngineId=contextEngineId,
|
|
contextName=contextName)
|
|
)
|
|
snmpEngine.observer.clearExecutionContext(
|
|
snmpEngine, 'rfc2576.processIncomingMsg'
|
|
)
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'processIncomingMsg: looked up securityName %r securityModel %r contextEngineId %r contextName %r by communityName %r AND transportInformation %r' % (
|
|
securityName, self.securityModelID, contextEngineId, contextName, communityName, transportInformation))
|
|
|
|
stateReference = self._cache.push(communityName=communityName)
|
|
|
|
scopedPDU = (contextEngineId, contextName,
|
|
msg.getComponentByPosition(2).getComponent())
|
|
maxSizeResponseScopedPDU = maxMessageSize - 128
|
|
securityStateReference = stateReference
|
|
|
|
debug.logger & debug.flagSM and debug.logger(
|
|
'processIncomingMsg: generated maxSizeResponseScopedPDU %s securityStateReference %s' % (
|
|
maxSizeResponseScopedPDU, securityStateReference))
|
|
|
|
return (securityEngineID, securityName, scopedPDU,
|
|
maxSizeResponseScopedPDU, securityStateReference)
|
|
|
|
|
|
class SnmpV2cSecurityModel(SnmpV1SecurityModel):
|
|
securityModelID = 2
|
|
|
|
# XXX
|
|
# contextEngineId/contextName goes to globalData
|