示例#1
0
class SnmpUSMSecurityModel(AbstractSecurityModel):
    securityModelID = 3
    authServices = {hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(),
                    hmacsha.HmacSha.serviceID: hmacsha.HmacSha(),
                    noauth.NoAuth.serviceID: noauth.NoAuth()}
    privServices = {des.Des.serviceID: des.Des(),
                    des3.Des3.serviceID: des3.Des3(),
                    aes.Aes.serviceID: aes.Aes(),
                    aes192.AesBlumenthal192.serviceID: aes192.AesBlumenthal192(),
                    aes256.AesBlumenthal256.serviceID: aes256.AesBlumenthal256(),
                    aes192.Aes192.serviceID: aes192.Aes192(),  # non-standard
                    aes256.Aes256.serviceID: aes256.Aes256(),  # non-standard
                    nopriv.NoPriv.serviceID: nopriv.NoPriv()}

    def __init__(self):
        AbstractSecurityModel.__init__(self)
        self.__securityParametersSpec = UsmSecurityParameters()
        self.__timeline = {}
        self.__timelineExpQueue = {}
        self.__expirationTimer = 0
        self.__paramsBranchId = -1

    def __sec2usr(self, snmpEngine, securityName, securityEngineID=None):
        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
        usmUserEngineID, = mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB',
                                                    'usmUserEngineID')
        if self.__paramsBranchId != usmUserEngineID.branchVersionId:
            usmUserName, usmUserSecurityName = mibBuilder.importSymbols(
                'SNMP-USER-BASED-SM-MIB', 'usmUserName', 'usmUserSecurityName')

            self.__securityToUserMap = {}

            nextMibNode = usmUserEngineID

            while True:
                try:
                    nextMibNode = usmUserEngineID.getNextNode(nextMibNode.name)

                except NoSuchInstanceError:
                    self.__paramsBranchId = usmUserEngineID.branchVersionId
                    debug.logger & debug.flagSM and debug.logger(
                        '_sec2usr: built snmpEngineId + securityName to userName map, version %s: %r' % (
                            self.__paramsBranchId, self.__securityToUserMap))
                    break

                instId = nextMibNode.name[len(usmUserSecurityName.name):]

                __engineID = usmUserEngineID.getNode(usmUserEngineID.name + instId).syntax
                __userName = usmUserName.getNode(usmUserName.name + instId).syntax
                __securityName = usmUserSecurityName.getNode(usmUserSecurityName.name + instId).syntax

                k = __engineID, __securityName

                # first (lesser) securityName wins
                if k not in self.__securityToUserMap:
                    self.__securityToUserMap[k] = __userName

        if securityEngineID is None:
            snmpEngineID, = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
            securityEngineID = snmpEngineID.syntax

        try:
            userName = self.__securityToUserMap[(securityEngineID, securityName)]
        except KeyError:
            debug.logger & debug.flagSM and debug.logger(
                '_sec2usr: no entry exists for snmpEngineId %r, securityName %r' % (securityEngineID, securityName))
            raise NoSuchInstanceError()  # emulate MIB lookup

        debug.logger & debug.flagSM and debug.logger(
            '_sec2usr: using userName %r for snmpEngineId %r, securityName %r' % (
                userName, securityEngineID, securityName))

        return userName

    @staticmethod
    def __getUserInfo(mibInstrumController, securityEngineID, userName):
        usmUserEntry, = mibInstrumController.mibBuilder.importSymbols(
            'SNMP-USER-BASED-SM-MIB', 'usmUserEntry'
        )
        tblIdx = usmUserEntry.getInstIdFromIndices(securityEngineID, userName)
        # Get userName & securityName
        usmUserName = usmUserEntry.getNode(usmUserEntry.name + (2,) + tblIdx).syntax
        usmUserSecurityName = usmUserEntry.getNode(usmUserEntry.name + (3,) + tblIdx).syntax
        # Get protocols
        usmUserAuthProtocol = usmUserEntry.getNode(usmUserEntry.name + (5,) + tblIdx).syntax
        usmUserPrivProtocol = usmUserEntry.getNode(usmUserEntry.name + (8,) + tblIdx).syntax
        # Get keys
        pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
            'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
        )
        pysnmpUsmKeyAuthLocalized = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (1,) + tblIdx).syntax
        pysnmpUsmKeyPrivLocalized = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (2,) + tblIdx).syntax
        return (usmUserName, usmUserSecurityName, usmUserAuthProtocol,
                pysnmpUsmKeyAuthLocalized, usmUserPrivProtocol,
                pysnmpUsmKeyPrivLocalized)

    def __cloneUserInfo(self, mibInstrumController, securityEngineID,
                        userName):
        snmpEngineID, = mibInstrumController.mibBuilder.importSymbols(
            '__SNMP-FRAMEWORK-MIB', 'snmpEngineID'
        )
        # Proto entry
        usmUserEntry, = mibInstrumController.mibBuilder.importSymbols(
            'SNMP-USER-BASED-SM-MIB', 'usmUserEntry'
        )
        tblIdx1 = usmUserEntry.getInstIdFromIndices(
            snmpEngineID.syntax, userName
        )
        # Get proto protocols
        usmUserName = usmUserEntry.getNode(usmUserEntry.name + (2,) + tblIdx1)
        usmUserSecurityName = usmUserEntry.getNode(usmUserEntry.name + (3,) + tblIdx1)
        usmUserCloneFrom = usmUserEntry.getNode(usmUserEntry.name + (4,) + tblIdx1)
        usmUserAuthProtocol = usmUserEntry.getNode(usmUserEntry.name + (5,) + tblIdx1)
        usmUserPrivProtocol = usmUserEntry.getNode(usmUserEntry.name + (8,) + tblIdx1)
        # Get proto keys
        pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
            'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
        )
        pysnmpUsmKeyAuth = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (3,) + tblIdx1)
        pysnmpUsmKeyPriv = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (4,) + tblIdx1)

        # Create new row from proto values

        tblIdx2 = usmUserEntry.getInstIdFromIndices(securityEngineID, userName)

        # New row
        mibInstrumController.writeVars(
            ((usmUserEntry.name + (13,) + tblIdx2, 4),)
        )

        # Set user&securityNames
        usmUserEntry.getNode(usmUserEntry.name + (2,) + tblIdx2).syntax = usmUserName.syntax
        usmUserEntry.getNode(usmUserEntry.name + (3,) + tblIdx2).syntax = usmUserSecurityName.syntax

        # Store a reference to original row
        usmUserEntry.getNode(usmUserEntry.name + (4,) + tblIdx2).syntax = usmUserCloneFrom.syntax.clone(tblIdx1)

        # Set protocols
        usmUserEntry.getNode(usmUserEntry.name + (5,) + tblIdx2).syntax = usmUserAuthProtocol.syntax
        usmUserEntry.getNode(usmUserEntry.name + (8,) + tblIdx2).syntax = usmUserPrivProtocol.syntax

        # Localize and set keys
        pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
            'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
        )
        pysnmpUsmKeyAuthLocalized = pysnmpUsmKeyEntry.getNode(
            pysnmpUsmKeyEntry.name + (1,) + tblIdx2
        )
        if usmUserAuthProtocol.syntax in self.authServices:
            localizeKey = self.authServices[usmUserAuthProtocol.syntax].localizeKey
            localAuthKey = localizeKey(pysnmpUsmKeyAuth.syntax,
                                       securityEngineID)
        else:
            raise error.StatusInformation(
                errorIndication=errind.unsupportedAuthProtocol
            )
        if localAuthKey is not None:
            pysnmpUsmKeyAuthLocalized.syntax = pysnmpUsmKeyAuthLocalized.syntax.clone(localAuthKey)
        pysnmpUsmKeyPrivLocalized = pysnmpUsmKeyEntry.getNode(
            pysnmpUsmKeyEntry.name + (2,) + tblIdx2
        )
        if usmUserPrivProtocol.syntax in self.privServices:
            localizeKey = self.privServices[usmUserPrivProtocol.syntax].localizeKey
            localPrivKey = localizeKey(usmUserAuthProtocol.syntax,
                                       pysnmpUsmKeyPriv.syntax,
                                       securityEngineID)
        else:
            raise error.StatusInformation(errorIndication=errind.unsupportedPrivProtocol)
        if localPrivKey is not None:
            pysnmpUsmKeyPrivLocalized.syntax = pysnmpUsmKeyPrivLocalized.syntax.clone(localPrivKey)
        return (usmUserName.syntax, usmUserSecurityName.syntax,
                usmUserAuthProtocol.syntax, pysnmpUsmKeyAuthLocalized.syntax,
                usmUserPrivProtocol.syntax, pysnmpUsmKeyPrivLocalized.syntax)

    def __generateRequestOrResponseMsg(self, snmpEngine,
                                       messageProcessingModel,
                                       globalData, maxMessageSize,
                                       securityModel, securityEngineID,
                                       securityName, securityLevel,
                                       scopedPDU, securityStateReference):
        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
        snmpEngineID = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax

        # 3.1.1
        if securityStateReference is not None:
            # 3.1.1a
            cachedSecurityData = self._cache.pop(securityStateReference)
            usmUserName = cachedSecurityData['msgUserName']
            if 'usmUserSecurityName' in cachedSecurityData:
                usmUserSecurityName = cachedSecurityData['usmUserSecurityName']
            else:
                usmUserSecurityName = usmUserName
            if 'usmUserAuthProtocol' in cachedSecurityData:
                usmUserAuthProtocol = cachedSecurityData['usmUserAuthProtocol']
            else:
                usmUserAuthProtocol = noauth.NoAuth.serviceID
            if 'usmUserAuthKeyLocalized' in cachedSecurityData:
                usmUserAuthKeyLocalized = cachedSecurityData['usmUserAuthKeyLocalized']
            else:
                usmUserAuthKeyLocalized = None
            if 'usmUserPrivProtocol' in cachedSecurityData:
                usmUserPrivProtocol = cachedSecurityData['usmUserPrivProtocol']
            else:
                usmUserPrivProtocol = nopriv.NoPriv.serviceID
            if 'usmUserPrivKeyLocalized' in cachedSecurityData:
                usmUserPrivKeyLocalized = cachedSecurityData['usmUserPrivKeyLocalized']
            else:
                usmUserPrivKeyLocalized = None
            securityEngineID = snmpEngineID
            debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: user info read from cache')
        elif securityName:
            # 3.1.1b
            try:
                (usmUserName, usmUserSecurityName, usmUserAuthProtocol,
                 usmUserAuthKeyLocalized, usmUserPrivProtocol,
                 usmUserPrivKeyLocalized) = self.__getUserInfo(
                    snmpEngine.msgAndPduDsp.mibInstrumController,
                    securityEngineID,
                    self.__sec2usr(snmpEngine, securityName, securityEngineID)
                )
                debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: read user info')

            except NoSuchInstanceError:
                pysnmpUsmDiscovery, = mibBuilder.importSymbols('__PYSNMP-USM-MIB', 'pysnmpUsmDiscovery')
                reportUnknownName = not pysnmpUsmDiscovery.syntax
                if not reportUnknownName:
                    try:
                        (usmUserName, usmUserSecurityName,
                         usmUserAuthProtocol, usmUserAuthKeyLocalized,
                         usmUserPrivProtocol,
                         usmUserPrivKeyLocalized) = self.__cloneUserInfo(
                            snmpEngine.msgAndPduDsp.mibInstrumController,
                            securityEngineID,
                            self.__sec2usr(snmpEngine, securityName)
                        )

                    except NoSuchInstanceError:
                        reportUnknownName = True

                if reportUnknownName:
                    raise error.StatusInformation(
                        errorIndication=errind.unknownSecurityName
                    )

                debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: clone user info')

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: %s' % (sys.exc_info()[1],))
                snmpInGenErrs, = mibBuilder.importSymbols('__SNMPv2-MIB', 'snmpInGenErrs')
                snmpInGenErrs.syntax += 1
                raise error.StatusInformation(
                    errorIndication=errind.invalidMsg
                )
        else:
            # empty username used for engineID discovery
            usmUserName = usmUserSecurityName = null
            usmUserAuthProtocol = noauth.NoAuth.serviceID
            usmUserPrivProtocol = nopriv.NoPriv.serviceID
            usmUserAuthKeyLocalized = usmUserPrivKeyLocalized = None
            debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: use empty USM data')

        # noinspection PyUnboundLocalVariable
        debug.logger & debug.flagSM and debug.logger(
            '__generateRequestOrResponseMsg: local usmUserName %r usmUserSecurityName %r usmUserAuthProtocol %s usmUserPrivProtocol %s securityEngineID %r securityName %r' % (
                usmUserName, usmUserSecurityName, usmUserAuthProtocol, usmUserPrivProtocol, securityEngineID, securityName))

        msg = globalData

        # 3.1.2
        if securityLevel == 3:
            if (usmUserAuthProtocol == noauth.NoAuth.serviceID or
                    usmUserPrivProtocol == nopriv.NoPriv.serviceID):
                raise error.StatusInformation(
                    errorIndication=errind.unsupportedSecurityLevel
                )

        # 3.1.3
        if securityLevel == 3 or securityLevel == 2:
            if usmUserAuthProtocol == noauth.NoAuth.serviceID:
                raise error.StatusInformation(
                    errorIndication=errind.unsupportedSecurityLevel
                )

        securityParameters = self.__securityParametersSpec

        scopedPDUData = msg.setComponentByPosition(3).getComponentByPosition(3)
        scopedPDUData.setComponentByPosition(
            0, scopedPDU, verifyConstraints=False, matchTags=False, matchConstraints=False
        )

        # 3.1.6a
        if securityStateReference is None and securityLevel in (2, 3):
            if securityEngineID in self.__timeline:
                (snmpEngineBoots, snmpEngineTime, latestReceivedEngineTime,
                 latestUpdateTimestamp) = self.__timeline[securityEngineID]
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: read snmpEngineBoots, snmpEngineTime from timeline')
            else:
                # 2.3 XXX is this correct?
                snmpEngineBoots = snmpEngineTime = 0
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: no timeline for securityEngineID %r' % (securityEngineID,))
        # 3.1.6.b
        elif securityStateReference is not None:  # XXX Report?
            (snmpEngineBoots,
             snmpEngineTime) = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineBoots', 'snmpEngineTime')
            snmpEngineBoots = snmpEngineBoots.syntax
            snmpEngineTime = snmpEngineTime.syntax.clone()
            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: read snmpEngineBoots, snmpEngineTime from LCD')
        # 3.1.6.c
        else:
            snmpEngineBoots = snmpEngineTime = 0
            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: assuming zero snmpEngineBoots, snmpEngineTime')

        debug.logger & debug.flagSM and debug.logger(
            '__generateRequestOrResponseMsg: use snmpEngineBoots %s snmpEngineTime %s for securityEngineID %r' % (
                snmpEngineBoots, snmpEngineTime, securityEngineID))

        # 3.1.4a
        if securityLevel == 3:
            if usmUserPrivProtocol in self.privServices:
                privHandler = self.privServices[usmUserPrivProtocol]
            else:
                raise error.StatusInformation(
                    errorIndication=errind.encryptionError
                )

            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: scopedPDU %s' % scopedPDU.prettyPrint())

            try:
                dataToEncrypt = encoder.encode(scopedPDU)

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: scopedPDU serialization error: %s' % sys.exc_info()[1])
                raise error.StatusInformation(
                    errorIndication=errind.serializationError
                )

            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: scopedPDU encoded into %s' % debug.hexdump(dataToEncrypt))

            # noinspection PyUnboundLocalVariable
            (encryptedData,
             privParameters) = privHandler.encryptData(
                usmUserPrivKeyLocalized,
                (snmpEngineBoots, snmpEngineTime, None), dataToEncrypt
            )

            securityParameters.setComponentByPosition(
                5, privParameters, verifyConstraints=False, matchTags=False, matchConstraints=False
            )
            scopedPDUData.setComponentByPosition(
                1, encryptedData, verifyConstraints=False, matchTags=False, matchConstraints=False
            )

            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: scopedPDU ciphered into %s' % debug.hexdump(encryptedData))

        # 3.1.4b
        elif securityLevel == 1 or securityLevel == 2:
            securityParameters.setComponentByPosition(5, '')

        debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: %s' % scopedPDUData.prettyPrint())

        # 3.1.5
        securityParameters.setComponentByPosition(
            0, securityEngineID, verifyConstraints=False, matchTags=False, matchConstraints=False
        )
        securityParameters.setComponentByPosition(
            1, snmpEngineBoots, verifyConstraints=False, matchTags=False, matchConstraints=False
        )
        securityParameters.setComponentByPosition(
            2, snmpEngineTime, verifyConstraints=False, matchTags=False, matchConstraints=False
        )

        # 3.1.7
        securityParameters.setComponentByPosition(
            3, usmUserName, verifyConstraints=False, matchTags=False, matchConstraints=False
        )

        # 3.1.8a
        if securityLevel == 3 or securityLevel == 2:
            if usmUserAuthProtocol in self.authServices:
                authHandler = self.authServices[usmUserAuthProtocol]
            else:
                raise error.StatusInformation(
                    errorIndication=errind.authenticationFailure
                )

            # extra-wild hack to facilitate BER substrate in-place re-write
            securityParameters.setComponentByPosition(
                4, '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
            )

            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: %s' % (securityParameters.prettyPrint(),))

            try:
                msg.setComponentByPosition(2, encoder.encode(securityParameters), verifyConstraints=False)

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: securityParameters serialization error: %s' % sys.exc_info()[1])
                raise error.StatusInformation(
                    errorIndication=errind.serializationError
                )

            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: auth outgoing msg: %s' % msg.prettyPrint())

            try:
                wholeMsg = encoder.encode(msg)

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: msg serialization error: %s' % sys.exc_info()[1])
                raise error.StatusInformation(
                    errorIndication=errind.serializationError
                )

            # noinspection PyUnboundLocalVariable
            authenticatedWholeMsg = authHandler.authenticateOutgoingMsg(
                usmUserAuthKeyLocalized, wholeMsg
            )

        # 3.1.8b
        else:
            securityParameters.setComponentByPosition(
                4, '', verifyConstraints=False, matchTags=False, matchConstraints=False
            )

            debug.logger & debug.flagSM and debug.logger(
                '__generateRequestOrResponseMsg: %s' % (securityParameters.prettyPrint(),))

            try:
                msg.setComponentByPosition(2, encoder.encode(securityParameters), verifyConstraints=False, matchTags=False, matchConstraints=False)

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: secutiryParameters serialization error: %s' % sys.exc_info()[1])
                raise error.StatusInformation(
                    errorIndication=errind.serializationError
                )

            try:
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: plain outgoing msg: %s' % msg.prettyPrint())
                authenticatedWholeMsg = encoder.encode(msg)

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger(
                    '__generateRequestOrResponseMsg: msg serialization error: %s' % sys.exc_info()[1])
                raise error.StatusInformation(
                    errorIndication=errind.serializationError
                )

        debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: %s outgoing msg: %s' % (
            securityLevel > 1 and "authenticated" or "plain", debug.hexdump(authenticatedWholeMsg)))

        # 3.1.9
        return msg.getComponentByPosition(2), authenticatedWholeMsg

    def generateRequestMsg(self, snmpEngine, messageProcessingModel,
                           globalData, maxMessageSize, securityModel,
                           securityEngineID, securityName, securityLevel,
                           scopedPDU):
        return self.__generateRequestOrResponseMsg(snmpEngine,
                                                   messageProcessingModel,
                                                   globalData,
                                                   maxMessageSize,
                                                   securityModel,
                                                   securityEngineID,
                                                   securityName,
                                                   securityLevel,
                                                   scopedPDU,
                                                   None)

    def generateResponseMsg(self, snmpEngine, messageProcessingModel,
                            globalData, maxMessageSize, securityModel,
                            securityEngineID, securityName, securityLevel,
                            scopedPDU, securityStateReference):
        return self.__generateRequestOrResponseMsg(
            snmpEngine, messageProcessingModel, globalData,
            maxMessageSize, securityModel, securityEngineID,
            securityName, securityLevel, scopedPDU, securityStateReference
        )

    # 3.2
    def processIncomingMsg(self, snmpEngine, messageProcessingModel,
                           maxMessageSize, securityParameters,
                           securityModel, securityLevel, wholeMsg, msg):
        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder

        # 3.2.9 -- moved up here to be able to report
        # maxSizeResponseScopedPDU on error
        # (48 - maximum SNMPv3 header length)
        maxSizeResponseScopedPDU = int(maxMessageSize) - len(securityParameters) - 48

        debug.logger & debug.flagSM and debug.logger(
            'processIncomingMsg: securityParameters %s' % debug.hexdump(securityParameters))

        # 3.2.1
        try:
            securityParameters, rest = decoder.decode(
                securityParameters, asn1Spec=self.__securityParametersSpec
            )

        except PyAsn1Error:
            debug.logger & debug.flagSM and debug.logger('processIncomingMsg: %s' % (sys.exc_info()[1],))
            snmpInASNParseErrs, = mibBuilder.importSymbols('__SNMPv2-MIB', 'snmpInASNParseErrs')
            snmpInASNParseErrs.syntax += 1
            raise error.StatusInformation(errorIndication=errind.parseError)

        debug.logger & debug.flagSM and debug.logger('processIncomingMsg: %s' % (securityParameters.prettyPrint(),))

        if eoo.endOfOctets.isSameTypeWith(securityParameters):
            raise error.StatusInformation(errorIndication=errind.parseError)

        # 3.2.2
        msgAuthoritativeEngineId = securityParameters.getComponentByPosition(0)
        securityStateReference = self._cache.push(
            msgUserName=securityParameters.getComponentByPosition(3)
        )

        debug.logger & debug.flagSM and debug.logger(
            'processIncomingMsg: cache write securityStateReference %s by msgUserName %s' % (
                securityStateReference, securityParameters.getComponentByPosition(3)))

        scopedPduData = msg.getComponentByPosition(3)

        # Used for error reporting
        contextEngineId = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax
        contextName = null

        snmpEngineID = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax

        # 3.2.3
        if (msgAuthoritativeEngineId != snmpEngineID and
                msgAuthoritativeEngineId not in self.__timeline):
            if (msgAuthoritativeEngineId and
                    4 < len(msgAuthoritativeEngineId) < 33):
                # 3.2.3a - cloned user when request was sent
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: non-synchronized securityEngineID %r' % (msgAuthoritativeEngineId,))
            else:
                # 3.2.3b
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: peer requested snmpEngineID discovery')
                usmStatsUnknownEngineIDs, = mibBuilder.importSymbols(
                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownEngineIDs')
                usmStatsUnknownEngineIDs.syntax += 1
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: null or malformed msgAuthoritativeEngineId')
                pysnmpUsmDiscoverable, = mibBuilder.importSymbols(
                    '__PYSNMP-USM-MIB', 'pysnmpUsmDiscoverable')
                if pysnmpUsmDiscoverable.syntax:
                    debug.logger & debug.flagSM and debug.logger(
                        'processIncomingMsg: starting snmpEngineID discovery procedure')

                    # Report original contextName
                    if scopedPduData.getName() != 'plaintext':
                        debug.logger & debug.flagSM and debug.logger(
                            'processIncomingMsg: scopedPduData not plaintext %s' % scopedPduData.prettyPrint())
                        raise error.StatusInformation(
                            errorIndication=errind.unknownEngineID
                        )

                    # 7.2.6.a.1
                    scopedPdu = scopedPduData.getComponent()
                    contextEngineId = scopedPdu.getComponentByPosition(0)
                    contextName = scopedPdu.getComponentByPosition(1)

                    raise error.StatusInformation(
                        errorIndication=errind.unknownEngineID,
                        oid=usmStatsUnknownEngineIDs.name,
                        val=usmStatsUnknownEngineIDs.syntax,
                        securityStateReference=securityStateReference,
                        securityLevel=securityLevel,
                        contextEngineId=contextEngineId,
                        contextName=contextName,
                        scopedPDU=scopedPdu,
                        maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
                    )
                else:
                    debug.logger & debug.flagSM and debug.logger('processIncomingMsg: will not discover EngineID')
                    # free securityStateReference XXX
                    raise error.StatusInformation(
                        errorIndication=errind.unknownEngineID
                    )

        msgUserName = securityParameters.getComponentByPosition(3)

        debug.logger & debug.flagSM and debug.logger(
            'processIncomingMsg: read from securityParams msgAuthoritativeEngineId %r msgUserName %r' % (
                msgAuthoritativeEngineId, msgUserName))

        if msgUserName:
            # 3.2.4
            try:
                (usmUserName,
                 usmUserSecurityName,
                 usmUserAuthProtocol,
                 usmUserAuthKeyLocalized,
                 usmUserPrivProtocol,
                 usmUserPrivKeyLocalized) = self.__getUserInfo(
                    snmpEngine.msgAndPduDsp.mibInstrumController,
                    msgAuthoritativeEngineId, msgUserName
                )
                debug.logger & debug.flagSM and debug.logger('processIncomingMsg: read user info from LCD')

            except NoSuchInstanceError:
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: unknown securityEngineID %r msgUserName %r' % (
                        msgAuthoritativeEngineId, msgUserName))

                usmStatsUnknownUserNames, = mibBuilder.importSymbols(
                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames')
                usmStatsUnknownUserNames.syntax += 1

                raise error.StatusInformation(
                    errorIndication=errind.unknownSecurityName,
                    oid=usmStatsUnknownUserNames.name,
                    val=usmStatsUnknownUserNames.syntax,
                    securityStateReference=securityStateReference,
                    securityLevel=securityLevel,
                    contextEngineId=contextEngineId,
                    contextName=contextName,
                    msgUserName=msgUserName,
                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
                )

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger('processIncomingMsg: %s' % (sys.exc_info()[1],))
                snmpInGenErrs, = mibBuilder.importSymbols('__SNMPv2-MIB', 'snmpInGenErrs')
                snmpInGenErrs.syntax += 1
                raise error.StatusInformation(errorIndication=errind.invalidMsg)
        else:
            # empty username used for engineID discovery
            usmUserName = usmUserSecurityName = null
            usmUserAuthProtocol = noauth.NoAuth.serviceID
            usmUserPrivProtocol = nopriv.NoPriv.serviceID
            usmUserAuthKeyLocalized = usmUserPrivKeyLocalized = None

        debug.logger & debug.flagSM and debug.logger(
            'processIncomingMsg: now have usmUserName %r usmUserSecurityName %r usmUserAuthProtocol %r usmUserPrivProtocol %r for msgUserName %r' % (
                usmUserName, usmUserSecurityName, usmUserAuthProtocol, usmUserPrivProtocol, msgUserName))

        # 3.2.11 (moved up here to let Reports be authenticated & encrypted)
        self._cache.pop(securityStateReference)
        securityStateReference = self._cache.push(
            msgUserName=securityParameters.getComponentByPosition(3),
            usmUserSecurityName=usmUserSecurityName,
            usmUserAuthProtocol=usmUserAuthProtocol,
            usmUserAuthKeyLocalized=usmUserAuthKeyLocalized,
            usmUserPrivProtocol=usmUserPrivProtocol,
            usmUserPrivKeyLocalized=usmUserPrivKeyLocalized
        )

        msgAuthoritativeEngineBoots = securityParameters.getComponentByPosition(1)
        msgAuthoritativeEngineTime = securityParameters.getComponentByPosition(2)

        snmpEngine.observer.storeExecutionContext(
            snmpEngine, 'rfc3414.processIncomingMsg',
            dict(securityEngineId=msgAuthoritativeEngineId,
                 snmpEngineBoots=msgAuthoritativeEngineBoots,
                 snmpEngineTime=msgAuthoritativeEngineTime,
                 userName=usmUserName,
                 securityName=usmUserSecurityName,
                 authProtocol=usmUserAuthProtocol,
                 authKey=usmUserAuthKeyLocalized,
                 privProtocol=usmUserPrivProtocol,
                 privKey=usmUserPrivKeyLocalized)
        )
        snmpEngine.observer.clearExecutionContext(
            snmpEngine, 'rfc3414.processIncomingMsg'
        )

        # 3.2.5
        if msgAuthoritativeEngineId == snmpEngineID:
            # Authoritative SNMP engine: make sure securityLevel is sufficient
            badSecIndication = None
            if securityLevel == 3:
                if usmUserAuthProtocol == noauth.NoAuth.serviceID:
                    badSecIndication = 'authPriv wanted while auth not expected'
                if usmUserPrivProtocol == nopriv.NoPriv.serviceID:
                    badSecIndication = 'authPriv wanted while priv not expected'
            elif securityLevel == 2:
                if usmUserAuthProtocol == noauth.NoAuth.serviceID:
                    badSecIndication = 'authNoPriv wanted while auth not expected'
                if usmUserPrivProtocol != nopriv.NoPriv.serviceID:
                    # 4 (discovery phase always uses authenticated messages)
                    if msgAuthoritativeEngineBoots or msgAuthoritativeEngineTime:
                        badSecIndication = 'authNoPriv wanted while priv expected'

            elif securityLevel == 1:
                if usmUserAuthProtocol != noauth.NoAuth.serviceID:
                    badSecIndication = 'noAuthNoPriv wanted while auth expected'
                if usmUserPrivProtocol != nopriv.NoPriv.serviceID:
                    badSecIndication = 'noAuthNoPriv wanted while priv expected'
            if badSecIndication:
                usmStatsUnsupportedSecLevels, = mibBuilder.importSymbols(
                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnsupportedSecLevels')
                usmStatsUnsupportedSecLevels.syntax += 1
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: reporting inappropriate security level for user %s: %s' % (
                        msgUserName, badSecIndication))
                raise error.StatusInformation(
                    errorIndication=errind.unsupportedSecurityLevel,
                    oid=usmStatsUnsupportedSecLevels.name,
                    val=usmStatsUnsupportedSecLevels.syntax,
                    securityStateReference=securityStateReference,
                    securityLevel=securityLevel,
                    contextEngineId=contextEngineId,
                    contextName=contextName,
                    msgUserName=msgUserName,
                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
                )

        # 3.2.6
        if securityLevel == 3 or securityLevel == 2:
            if usmUserAuthProtocol in self.authServices:
                authHandler = self.authServices[usmUserAuthProtocol]
            else:
                raise error.StatusInformation(
                    errorIndication=errind.authenticationFailure
                )

            try:
                authHandler.authenticateIncomingMsg(
                    usmUserAuthKeyLocalized,
                    securityParameters.getComponentByPosition(4),
                    wholeMsg
                )

            except error.StatusInformation:
                usmStatsWrongDigests, = mibBuilder.importSymbols(
                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsWrongDigests')
                usmStatsWrongDigests.syntax += 1
                raise error.StatusInformation(
                    errorIndication=errind.authenticationFailure,
                    oid=usmStatsWrongDigests.name,
                    val=usmStatsWrongDigests.syntax,
                    securityStateReference=securityStateReference,
                    securityLevel=securityLevel,
                    contextEngineId=contextEngineId,
                    contextName=contextName,
                    msgUserName=msgUserName,
                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
                )

            debug.logger & debug.flagSM and debug.logger('processIncomingMsg: incoming msg authenticated')

            # synchronize time with authed peer
            self.__timeline[msgAuthoritativeEngineId] = (
                securityParameters.getComponentByPosition(1),
                securityParameters.getComponentByPosition(2),
                securityParameters.getComponentByPosition(2),
                int(time.time())
            )

            timerResolution = snmpEngine.transportDispatcher is None and 1.0 or snmpEngine.transportDispatcher.getTimerResolution()
            expireAt = int(self.__expirationTimer + 300 / timerResolution)
            if expireAt not in self.__timelineExpQueue:
                self.__timelineExpQueue[expireAt] = []
            self.__timelineExpQueue[expireAt].append(msgAuthoritativeEngineId)

            debug.logger & debug.flagSM and debug.logger(
                'processIncomingMsg: store timeline for securityEngineID %r' % (msgAuthoritativeEngineId,))

        # 3.2.7
        if securityLevel == 3 or securityLevel == 2:
            if msgAuthoritativeEngineId == snmpEngineID:
                # Authoritative SNMP engine: use local notion (SF bug #1649032)
                (snmpEngineBoots,
                 snmpEngineTime) = mibBuilder.importSymbols(
                    '__SNMP-FRAMEWORK-MIB', 'snmpEngineBoots', 'snmpEngineTime')
                snmpEngineBoots = snmpEngineBoots.syntax
                snmpEngineTime = snmpEngineTime.syntax.clone()
                idleTime = 0
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: read snmpEngineBoots (%s), snmpEngineTime (%s) from LCD' % (
                        snmpEngineBoots, snmpEngineTime))
            else:
                # Non-authoritative SNMP engine: use cached estimates
                if msgAuthoritativeEngineId in self.__timeline:
                    (snmpEngineBoots, snmpEngineTime,
                     latestReceivedEngineTime,
                     latestUpdateTimestamp) = self.__timeline[
                        msgAuthoritativeEngineId
                    ]
                    # time passed since last talk with this SNMP engine
                    idleTime = int(time.time()) - latestUpdateTimestamp
                    debug.logger & debug.flagSM and debug.logger(
                        'processIncomingMsg: read timeline snmpEngineBoots %s snmpEngineTime %s for msgAuthoritativeEngineId %r, idle time %s secs' % (
                            snmpEngineBoots, snmpEngineTime, msgAuthoritativeEngineId, idleTime))
                else:
                    raise error.ProtocolError('Peer SNMP engine info missing')

            # 3.2.7a
            if msgAuthoritativeEngineId == snmpEngineID:
                if (snmpEngineBoots == 2147483647 or
                        snmpEngineBoots != msgAuthoritativeEngineBoots or
                        abs(idleTime + int(snmpEngineTime) - int(msgAuthoritativeEngineTime)) > 150):
                    usmStatsNotInTimeWindows, = mibBuilder.importSymbols(
                        '__SNMP-USER-BASED-SM-MIB', 'usmStatsNotInTimeWindows')
                    usmStatsNotInTimeWindows.syntax += 1
                    raise error.StatusInformation(
                        errorIndication=errind.notInTimeWindow,
                        oid=usmStatsNotInTimeWindows.name,
                        val=usmStatsNotInTimeWindows.syntax,
                        securityStateReference=securityStateReference,
                        securityLevel=2,
                        contextEngineId=contextEngineId,
                        contextName=contextName,
                        msgUserName=msgUserName,
                        maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
                    )
            # 3.2.7b
            else:
                # 3.2.7b.1
                # noinspection PyUnboundLocalVariable
                if (msgAuthoritativeEngineBoots > snmpEngineBoots or
                        msgAuthoritativeEngineBoots == snmpEngineBoots and
                        msgAuthoritativeEngineTime > latestReceivedEngineTime):
                    self.__timeline[msgAuthoritativeEngineId] = (
                        msgAuthoritativeEngineBoots,
                        msgAuthoritativeEngineTime,
                        msgAuthoritativeEngineTime,
                        int(time.time())
                    )

                    timerResolution = snmpEngine.transportDispatcher is None and 1.0 or snmpEngine.transportDispatcher.getTimerResolution()
                    expireAt = int(self.__expirationTimer + 300 / timerResolution)
                    if expireAt not in self.__timelineExpQueue:
                        self.__timelineExpQueue[expireAt] = []
                    self.__timelineExpQueue[expireAt].append(msgAuthoritativeEngineId)

                    debug.logger & debug.flagSM and debug.logger(
                        'processIncomingMsg: stored timeline msgAuthoritativeEngineBoots %s msgAuthoritativeEngineTime %s for msgAuthoritativeEngineId %r' % (
                            msgAuthoritativeEngineBoots, msgAuthoritativeEngineTime, msgAuthoritativeEngineId))

                # 3.2.7b.2
                if (snmpEngineBoots == 2147483647 or
                        msgAuthoritativeEngineBoots < snmpEngineBoots or
                        msgAuthoritativeEngineBoots == snmpEngineBoots and
                        abs(idleTime + int(snmpEngineTime) - int(msgAuthoritativeEngineTime)) > 150):
                    raise error.StatusInformation(
                        errorIndication=errind.notInTimeWindow,
                        msgUserName=msgUserName
                    )

        # 3.2.8a
        if securityLevel == 3:
            if usmUserPrivProtocol in self.privServices:
                privHandler = self.privServices[usmUserPrivProtocol]
            else:
                raise error.StatusInformation(
                    errorIndication=errind.decryptionError,
                    msgUserName=msgUserName
                )
            encryptedPDU = scopedPduData.getComponentByPosition(1)
            if encryptedPDU is None:  # no ciphertext
                raise error.StatusInformation(
                    errorIndication=errind.decryptionError,
                    msgUserName=msgUserName
                )

            try:
                decryptedData = privHandler.decryptData(
                    usmUserPrivKeyLocalized,
                    (securityParameters.getComponentByPosition(1),
                     securityParameters.getComponentByPosition(2),
                     securityParameters.getComponentByPosition(5)),
                    encryptedPDU
                )
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: PDU deciphered into %s' % debug.hexdump(decryptedData))

            except error.StatusInformation:
                usmStatsDecryptionErrors, = mibBuilder.importSymbols(
                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsDecryptionErrors')
                usmStatsDecryptionErrors.syntax += 1
                raise error.StatusInformation(
                    errorIndication=errind.decryptionError,
                    oid=usmStatsDecryptionErrors.name,
                    val=usmStatsDecryptionErrors.syntax,
                    securityStateReference=securityStateReference,
                    securityLevel=securityLevel,
                    contextEngineId=contextEngineId,
                    contextName=contextName,
                    msgUserName=msgUserName,
                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
                )
            scopedPduSpec = scopedPduData.setComponentByPosition(0).getComponentByPosition(0)
            try:
                scopedPDU, rest = decoder.decode(decryptedData,
                                                 asn1Spec=scopedPduSpec)

            except PyAsn1Error:
                debug.logger & debug.flagSM and debug.logger(
                    'processIncomingMsg: scopedPDU decoder failed %s' % sys.exc_info()[0])
                raise error.StatusInformation(
                    errorIndication=errind.decryptionError,
                    msgUserName=msgUserName
                )

            if eoo.endOfOctets.isSameTypeWith(scopedPDU):
                raise error.StatusInformation(
                    errorIndication=errind.decryptionError,
                    msgUserName=msgUserName
                )
        else:
            # 3.2.8b
            scopedPDU = scopedPduData.getComponentByPosition(0)
            if scopedPDU is None:  # no plaintext
                raise error.StatusInformation(
                    errorIndication=errind.decryptionError,
                    msgUserName=msgUserName
                )

        debug.logger & debug.flagSM and debug.logger(
            'processIncomingMsg: scopedPDU decoded %s' % scopedPDU.prettyPrint())

        # 3.2.10
        securityName = usmUserSecurityName

        debug.logger & debug.flagSM and debug.logger(
            'processIncomingMsg: cached msgUserName %s info by securityStateReference %s' % (
                msgUserName, securityStateReference))

        # Delayed to include details
        if not msgUserName and not msgAuthoritativeEngineId:
            usmStatsUnknownUserNames, = mibBuilder.importSymbols(
                '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames')
            usmStatsUnknownUserNames.syntax += 1
            raise error.StatusInformation(
                errorIndication=errind.unknownSecurityName,
                oid=usmStatsUnknownUserNames.name,
                val=usmStatsUnknownUserNames.syntax,
                securityStateReference=securityStateReference,
                securityEngineID=msgAuthoritativeEngineId,
                securityLevel=securityLevel,
                contextEngineId=contextEngineId,
                contextName=contextName,
                msgUserName=msgUserName,
                maxSizeResponseScopedPDU=maxSizeResponseScopedPDU,
                PDU=scopedPDU
            )

        # 3.2.12
        return (msgAuthoritativeEngineId, securityName, scopedPDU,
                maxSizeResponseScopedPDU, securityStateReference)

    def __expireTimelineInfo(self):
        if self.__expirationTimer in self.__timelineExpQueue:
            for engineIdKey in self.__timelineExpQueue[self.__expirationTimer]:
                if engineIdKey in self.__timeline:
                    del self.__timeline[engineIdKey]
                    debug.logger & debug.flagSM and debug.logger('__expireTimelineInfo: expiring %r' % (engineIdKey,))
            del self.__timelineExpQueue[self.__expirationTimer]
        self.__expirationTimer += 1

    def receiveTimerTick(self, snmpEngine, timeNow):
        self.__expireTimelineInfo()
示例#2
0
usmNoPrivProtocol = nopriv.NoPriv.serviceID

# Auth services
authServices = {hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(),
                hmacsha.HmacSha.serviceID: hmacsha.HmacSha(),
                hmacsha2.HmacSha2.sha224ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha224ServiceID),
                hmacsha2.HmacSha2.sha256ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha256ServiceID),
                hmacsha2.HmacSha2.sha384ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha384ServiceID),
                hmacsha2.HmacSha2.sha512ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha512ServiceID),
                noauth.NoAuth.serviceID: noauth.NoAuth()}

# Privacy services
privServices = {des.Des.serviceID: des.Des(),
                des3.Des3.serviceID: des3.Des3(),
                aes.Aes.serviceID: aes.Aes(),
                aes192.AesBlumenthal192.serviceID: aes192.AesBlumenthal192(),
                aes256.AesBlumenthal256.serviceID: aes256.AesBlumenthal256(),
                aes192.Aes192.serviceID: aes192.Aes192(),  # non-standard
                aes256.Aes256.serviceID: aes256.Aes256(),  # non-standard
                nopriv.NoPriv.serviceID: nopriv.NoPriv()}


def __cookV1SystemInfo(snmpEngine, communityIndex):
    mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder

    snmpEngineID, = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
    snmpCommunityEntry, = mibBuilder.importSymbols('SNMP-COMMUNITY-MIB', 'snmpCommunityEntry')
    tblIdx = snmpCommunityEntry.getInstIdFromIndices(communityIndex)
    return snmpCommunityEntry, tblIdx, snmpEngineID

示例#3
0
文件: config.py 项目: zxx1819/pysnmp
    hmacsha2.HmacSha2(hmacsha2.HmacSha2.SHA224_SERVICE_ID),
    hmacsha2.HmacSha2.SHA256_SERVICE_ID:
    hmacsha2.HmacSha2(hmacsha2.HmacSha2.SHA256_SERVICE_ID),
    hmacsha2.HmacSha2.SHA384_SERVICE_ID:
    hmacsha2.HmacSha2(hmacsha2.HmacSha2.SHA384_SERVICE_ID),
    hmacsha2.HmacSha2.SHA512_SERVICE_ID:
    hmacsha2.HmacSha2(hmacsha2.HmacSha2.SHA512_SERVICE_ID),
    noauth.NoAuth.SERVICE_ID:
    noauth.NoAuth()
}

PRIV_SERVICES = {
    des.Des.SERVICE_ID: des.Des(),
    des3.Des3.SERVICE_ID: des3.Des3(),
    aes.Aes.SERVICE_ID: aes.Aes(),
    aes192.AesBlumenthal192.SERVICE_ID: aes192.AesBlumenthal192(),
    aes256.AesBlumenthal256.SERVICE_ID: aes256.AesBlumenthal256(),
    aes192.Aes192.SERVICE_ID: aes192.Aes192(),  # non-standard
    aes256.Aes256.SERVICE_ID: aes256.Aes256(),  # non-standard
    nopriv.NoPriv.SERVICE_ID: nopriv.NoPriv()
}

# This module uses Management Instrumentation subsystem in purely
# synchronous manner. The assumption is that the Management
# Instrumentation calls never yield control but block.


def __cookV1SystemInfo(snmpEngine, communityIndex):
    mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder

    snmpEngineID, = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB',