1#
2# This file is part of pysnmp software.
3#
4# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
5# License: http://snmplabs.com/pysnmp/license.html
6#
7import time
8import sys
9from pysnmp.proto.secmod.base import AbstractSecurityModel
10from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha, noauth
11from pysnmp.proto.secmod.rfc3414.priv import des, nopriv
12from pysnmp.proto.secmod.rfc3826.priv import aes
13from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
14from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256
15from pysnmp.smi.error import NoSuchInstanceError
16from pysnmp.proto import rfc1155, errind, error
17from pysnmp import debug
18from pyasn1.type import univ, namedtype, constraint
19from pyasn1.codec.ber import encoder, decoder, eoo
20from pyasn1.error import PyAsn1Error
21from pyasn1.compat.octets import null
22
23
24# USM security params
25
26class UsmSecurityParameters(rfc1155.TypeCoercionHackMixIn, univ.Sequence):
27    componentType = namedtype.NamedTypes(
28        namedtype.NamedType('msgAuthoritativeEngineId', univ.OctetString()),
29        namedtype.NamedType('msgAuthoritativeEngineBoots',
30                            univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))),
31        namedtype.NamedType('msgAuthoritativeEngineTime',
32                            univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))),
33        namedtype.NamedType('msgUserName',
34                            univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, 32))),
35        namedtype.NamedType('msgAuthenticationParameters', univ.OctetString()),
36        namedtype.NamedType('msgPrivacyParameters', univ.OctetString())
37    )
38
39
40class SnmpUSMSecurityModel(AbstractSecurityModel):
41    securityModelID = 3
42    authServices = {hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(),
43                    hmacsha.HmacSha.serviceID: hmacsha.HmacSha(),
44                    hmacsha2.HmacSha2.sha224ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha224ServiceID),
45                    hmacsha2.HmacSha2.sha256ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha256ServiceID),
46                    hmacsha2.HmacSha2.sha384ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha384ServiceID),
47                    hmacsha2.HmacSha2.sha512ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha512ServiceID),
48                    noauth.NoAuth.serviceID: noauth.NoAuth(),
49                    }
50    privServices = {des.Des.serviceID: des.Des(),
51                    des3.Des3.serviceID: des3.Des3(),
52                    aes.Aes.serviceID: aes.Aes(),
53                    aes192.AesBlumenthal192.serviceID: aes192.AesBlumenthal192(),
54                    aes256.AesBlumenthal256.serviceID: aes256.AesBlumenthal256(),
55                    aes192.Aes192.serviceID: aes192.Aes192(),  # non-standard
56                    aes256.Aes256.serviceID: aes256.Aes256(),  # non-standard
57                    nopriv.NoPriv.serviceID: nopriv.NoPriv()}
58
59    def __init__(self):
60        AbstractSecurityModel.__init__(self)
61        self.__securityParametersSpec = UsmSecurityParameters()
62        self.__timeline = {}
63        self.__timelineExpQueue = {}
64        self.__expirationTimer = 0
65        self.__paramsBranchId = -1
66
67    def __sec2usr(self, snmpEngine, securityName, securityEngineID=None):
68        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
69        usmUserEngineID, = mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB',
70                                                    'usmUserEngineID')
71        if self.__paramsBranchId != usmUserEngineID.branchVersionId:
72            usmUserName, usmUserSecurityName = mibBuilder.importSymbols(
73                'SNMP-USER-BASED-SM-MIB', 'usmUserName', 'usmUserSecurityName')
74
75            self.__securityToUserMap = {}
76
77            nextMibNode = usmUserEngineID
78
79            while True:
80                try:
81                    nextMibNode = usmUserEngineID.getNextNode(nextMibNode.name)
82
83                except NoSuchInstanceError:
84                    self.__paramsBranchId = usmUserEngineID.branchVersionId
85                    debug.logger & debug.flagSM and debug.logger(
86                        '_sec2usr: built snmpEngineId + securityName to userName map, version %s: %r' % (
87                            self.__paramsBranchId, self.__securityToUserMap))
88                    break
89
90                instId = nextMibNode.name[len(usmUserSecurityName.name):]
91
92                __engineID = usmUserEngineID.getNode(usmUserEngineID.name + instId).syntax
93                __userName = usmUserName.getNode(usmUserName.name + instId).syntax
94                __securityName = usmUserSecurityName.getNode(usmUserSecurityName.name + instId).syntax
95
96                k = __engineID, __securityName
97
98                # first (lesser) securityName wins
99                if k not in self.__securityToUserMap:
100                    self.__securityToUserMap[k] = __userName
101
102        if securityEngineID is None:
103            snmpEngineID, = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')
104            securityEngineID = snmpEngineID.syntax
105
106        try:
107            userName = self.__securityToUserMap[(securityEngineID, securityName)]
108        except KeyError:
109            debug.logger & debug.flagSM and debug.logger(
110                '_sec2usr: no entry exists for snmpEngineId %r, securityName %r' % (securityEngineID, securityName))
111            raise NoSuchInstanceError()  # emulate MIB lookup
112
113        debug.logger & debug.flagSM and debug.logger(
114            '_sec2usr: using userName %r for snmpEngineId %r, securityName %r' % (
115                userName, securityEngineID, securityName))
116
117        return userName
118
119    @staticmethod
120    def __getUserInfo(mibInstrumController, securityEngineID, userName):
121        usmUserEntry, = mibInstrumController.mibBuilder.importSymbols(
122            'SNMP-USER-BASED-SM-MIB', 'usmUserEntry'
123        )
124        tblIdx = usmUserEntry.getInstIdFromIndices(securityEngineID, userName)
125        # Get userName & securityName
126        usmUserName = usmUserEntry.getNode(usmUserEntry.name + (2,) + tblIdx).syntax
127        usmUserSecurityName = usmUserEntry.getNode(usmUserEntry.name + (3,) + tblIdx).syntax
128        # Get protocols
129        usmUserAuthProtocol = usmUserEntry.getNode(usmUserEntry.name + (5,) + tblIdx).syntax
130        usmUserPrivProtocol = usmUserEntry.getNode(usmUserEntry.name + (8,) + tblIdx).syntax
131        # Get keys
132        pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
133            'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
134        )
135        pysnmpUsmKeyAuthLocalized = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (1,) + tblIdx).syntax
136        pysnmpUsmKeyPrivLocalized = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (2,) + tblIdx).syntax
137        return (usmUserName, usmUserSecurityName, usmUserAuthProtocol,
138                pysnmpUsmKeyAuthLocalized, usmUserPrivProtocol,
139                pysnmpUsmKeyPrivLocalized)
140
141    def __cloneUserInfo(self, mibInstrumController, securityEngineID,
142                        userName):
143        snmpEngineID, = mibInstrumController.mibBuilder.importSymbols(
144            '__SNMP-FRAMEWORK-MIB', 'snmpEngineID'
145        )
146        # Proto entry
147        usmUserEntry, = mibInstrumController.mibBuilder.importSymbols(
148            'SNMP-USER-BASED-SM-MIB', 'usmUserEntry'
149        )
150        tblIdx1 = usmUserEntry.getInstIdFromIndices(
151            snmpEngineID.syntax, userName
152        )
153        # Get proto protocols
154        usmUserName = usmUserEntry.getNode(usmUserEntry.name + (2,) + tblIdx1)
155        usmUserSecurityName = usmUserEntry.getNode(usmUserEntry.name + (3,) + tblIdx1)
156        usmUserCloneFrom = usmUserEntry.getNode(usmUserEntry.name + (4,) + tblIdx1)
157        usmUserAuthProtocol = usmUserEntry.getNode(usmUserEntry.name + (5,) + tblIdx1)
158        usmUserPrivProtocol = usmUserEntry.getNode(usmUserEntry.name + (8,) + tblIdx1)
159        # Get proto keys
160        pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
161            'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
162        )
163        pysnmpUsmKeyAuth = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (3,) + tblIdx1)
164        pysnmpUsmKeyPriv = pysnmpUsmKeyEntry.getNode(pysnmpUsmKeyEntry.name + (4,) + tblIdx1)
165
166        # Create new row from proto values
167
168        tblIdx2 = usmUserEntry.getInstIdFromIndices(securityEngineID, userName)
169
170        # New row
171        mibInstrumController.writeVars(
172            ((usmUserEntry.name + (13,) + tblIdx2, 4),)
173        )
174
175        # Set user&securityNames
176        usmUserEntry.getNode(usmUserEntry.name + (2,) + tblIdx2).syntax = usmUserName.syntax
177        usmUserEntry.getNode(usmUserEntry.name + (3,) + tblIdx2).syntax = usmUserSecurityName.syntax
178
179        # Store a reference to original row
180        usmUserEntry.getNode(usmUserEntry.name + (4,) + tblIdx2).syntax = usmUserCloneFrom.syntax.clone(tblIdx1)
181
182        # Set protocols
183        usmUserEntry.getNode(usmUserEntry.name + (5,) + tblIdx2).syntax = usmUserAuthProtocol.syntax
184        usmUserEntry.getNode(usmUserEntry.name + (8,) + tblIdx2).syntax = usmUserPrivProtocol.syntax
185
186        # Localize and set keys
187        pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
188            'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
189        )
190        pysnmpUsmKeyAuthLocalized = pysnmpUsmKeyEntry.getNode(
191            pysnmpUsmKeyEntry.name + (1,) + tblIdx2
192        )
193        if usmUserAuthProtocol.syntax in self.authServices:
194            localizeKey = self.authServices[usmUserAuthProtocol.syntax].localizeKey
195            localAuthKey = localizeKey(pysnmpUsmKeyAuth.syntax,
196                                       securityEngineID)
197        else:
198            raise error.StatusInformation(
199                errorIndication=errind.unsupportedAuthProtocol
200            )
201        if localAuthKey is not None:
202            pysnmpUsmKeyAuthLocalized.syntax = pysnmpUsmKeyAuthLocalized.syntax.clone(localAuthKey)
203        pysnmpUsmKeyPrivLocalized = pysnmpUsmKeyEntry.getNode(
204            pysnmpUsmKeyEntry.name + (2,) + tblIdx2
205        )
206        if usmUserPrivProtocol.syntax in self.privServices:
207            localizeKey = self.privServices[usmUserPrivProtocol.syntax].localizeKey
208            localPrivKey = localizeKey(usmUserAuthProtocol.syntax,
209                                       pysnmpUsmKeyPriv.syntax,
210                                       securityEngineID)
211        else:
212            raise error.StatusInformation(errorIndication=errind.unsupportedPrivProtocol)
213        if localPrivKey is not None:
214            pysnmpUsmKeyPrivLocalized.syntax = pysnmpUsmKeyPrivLocalized.syntax.clone(localPrivKey)
215        return (usmUserName.syntax, usmUserSecurityName.syntax,
216                usmUserAuthProtocol.syntax, pysnmpUsmKeyAuthLocalized.syntax,
217                usmUserPrivProtocol.syntax, pysnmpUsmKeyPrivLocalized.syntax)
218
219    def __generateRequestOrResponseMsg(self, snmpEngine,
220                                       messageProcessingModel,
221                                       globalData, maxMessageSize,
222                                       securityModel, securityEngineID,
223                                       securityName, securityLevel,
224                                       scopedPDU, securityStateReference):
225        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
226        snmpEngineID = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax
227
228        # 3.1.1
229        if securityStateReference is not None:
230            # 3.1.1a
231            cachedSecurityData = self._cache.pop(securityStateReference)
232            usmUserName = cachedSecurityData['msgUserName']
233            if 'usmUserSecurityName' in cachedSecurityData:
234                usmUserSecurityName = cachedSecurityData['usmUserSecurityName']
235            else:
236                usmUserSecurityName = usmUserName
237            if 'usmUserAuthProtocol' in cachedSecurityData:
238                usmUserAuthProtocol = cachedSecurityData['usmUserAuthProtocol']
239            else:
240                usmUserAuthProtocol = noauth.NoAuth.serviceID
241            if 'usmUserAuthKeyLocalized' in cachedSecurityData:
242                usmUserAuthKeyLocalized = cachedSecurityData['usmUserAuthKeyLocalized']
243            else:
244                usmUserAuthKeyLocalized = None
245            if 'usmUserPrivProtocol' in cachedSecurityData:
246                usmUserPrivProtocol = cachedSecurityData['usmUserPrivProtocol']
247            else:
248                usmUserPrivProtocol = nopriv.NoPriv.serviceID
249            if 'usmUserPrivKeyLocalized' in cachedSecurityData:
250                usmUserPrivKeyLocalized = cachedSecurityData['usmUserPrivKeyLocalized']
251            else:
252                usmUserPrivKeyLocalized = None
253            securityEngineID = snmpEngineID
254            debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: user info read from cache')
255        elif securityName:
256            # 3.1.1b
257            try:
258                (usmUserName, usmUserSecurityName, usmUserAuthProtocol,
259                 usmUserAuthKeyLocalized, usmUserPrivProtocol,
260                 usmUserPrivKeyLocalized) = self.__getUserInfo(
261                    snmpEngine.msgAndPduDsp.mibInstrumController,
262                    securityEngineID,
263                    self.__sec2usr(snmpEngine, securityName, securityEngineID)
264                )
265                debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: read user info')
266
267            except NoSuchInstanceError:
268                pysnmpUsmDiscovery, = mibBuilder.importSymbols('__PYSNMP-USM-MIB', 'pysnmpUsmDiscovery')
269                reportUnknownName = not pysnmpUsmDiscovery.syntax
270                if not reportUnknownName:
271                    try:
272                        (usmUserName, usmUserSecurityName,
273                         usmUserAuthProtocol, usmUserAuthKeyLocalized,
274                         usmUserPrivProtocol,
275                         usmUserPrivKeyLocalized) = self.__cloneUserInfo(
276                            snmpEngine.msgAndPduDsp.mibInstrumController,
277                            securityEngineID,
278                            self.__sec2usr(snmpEngine, securityName)
279                        )
280
281                    except NoSuchInstanceError:
282                        reportUnknownName = True
283
284                if reportUnknownName:
285                    raise error.StatusInformation(
286                        errorIndication=errind.unknownSecurityName
287                    )
288
289                debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: clone user info')
290
291            except PyAsn1Error:
292                debug.logger & debug.flagSM and debug.logger(
293                    '__generateRequestOrResponseMsg: %s' % (sys.exc_info()[1],))
294                snmpInGenErrs, = mibBuilder.importSymbols('__SNMPv2-MIB', 'snmpInGenErrs')
295                snmpInGenErrs.syntax += 1
296                raise error.StatusInformation(
297                    errorIndication=errind.invalidMsg
298                )
299        else:
300            # empty username used for engineID discovery
301            usmUserName = usmUserSecurityName = null
302            usmUserAuthProtocol = noauth.NoAuth.serviceID
303            usmUserPrivProtocol = nopriv.NoPriv.serviceID
304            usmUserAuthKeyLocalized = usmUserPrivKeyLocalized = None
305            debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: use empty USM data')
306
307        # noinspection PyUnboundLocalVariable
308        debug.logger & debug.flagSM and debug.logger(
309            '__generateRequestOrResponseMsg: local usmUserName %r usmUserSecurityName %r usmUserAuthProtocol %s usmUserPrivProtocol %s securityEngineID %r securityName %r' % (
310                usmUserName, usmUserSecurityName, usmUserAuthProtocol, usmUserPrivProtocol, securityEngineID, securityName))
311
312        msg = globalData
313
314        # 3.1.2
315        if securityLevel == 3:
316            if (usmUserAuthProtocol == noauth.NoAuth.serviceID or
317                    usmUserPrivProtocol == nopriv.NoPriv.serviceID):
318                raise error.StatusInformation(
319                    errorIndication=errind.unsupportedSecurityLevel
320                )
321
322        # 3.1.3
323        if securityLevel == 3 or securityLevel == 2:
324            if usmUserAuthProtocol == noauth.NoAuth.serviceID:
325                raise error.StatusInformation(
326                    errorIndication=errind.unsupportedSecurityLevel
327                )
328
329        securityParameters = self.__securityParametersSpec
330
331        scopedPDUData = msg.setComponentByPosition(3).getComponentByPosition(3)
332        scopedPDUData.setComponentByPosition(
333            0, scopedPDU, verifyConstraints=False, matchTags=False, matchConstraints=False
334        )
335
336        # 3.1.6a
337        if securityStateReference is None and securityLevel in (2, 3):
338            if securityEngineID in self.__timeline:
339                (snmpEngineBoots, snmpEngineTime, latestReceivedEngineTime,
340                 latestUpdateTimestamp) = self.__timeline[securityEngineID]
341                debug.logger & debug.flagSM and debug.logger(
342                    '__generateRequestOrResponseMsg: read snmpEngineBoots, snmpEngineTime from timeline')
343            else:
344                # 2.3 XXX is this correct?
345                snmpEngineBoots = snmpEngineTime = 0
346                debug.logger & debug.flagSM and debug.logger(
347                    '__generateRequestOrResponseMsg: no timeline for securityEngineID %r' % (securityEngineID,))
348        # 3.1.6.b
349        elif securityStateReference is not None:  # XXX Report?
350            (snmpEngineBoots,
351             snmpEngineTime) = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineBoots', 'snmpEngineTime')
352            snmpEngineBoots = snmpEngineBoots.syntax
353            snmpEngineTime = snmpEngineTime.syntax.clone()
354            debug.logger & debug.flagSM and debug.logger(
355                '__generateRequestOrResponseMsg: read snmpEngineBoots, snmpEngineTime from LCD')
356        # 3.1.6.c
357        else:
358            snmpEngineBoots = snmpEngineTime = 0
359            debug.logger & debug.flagSM and debug.logger(
360                '__generateRequestOrResponseMsg: assuming zero snmpEngineBoots, snmpEngineTime')
361
362        debug.logger & debug.flagSM and debug.logger(
363            '__generateRequestOrResponseMsg: use snmpEngineBoots %s snmpEngineTime %s for securityEngineID %r' % (
364                snmpEngineBoots, snmpEngineTime, securityEngineID))
365
366        # 3.1.4a
367        if securityLevel == 3:
368            if usmUserPrivProtocol in self.privServices:
369                privHandler = self.privServices[usmUserPrivProtocol]
370            else:
371                raise error.StatusInformation(
372                    errorIndication=errind.encryptionError
373                )
374
375            debug.logger & debug.flagSM and debug.logger(
376                '__generateRequestOrResponseMsg: scopedPDU %s' % scopedPDU.prettyPrint())
377
378            try:
379                dataToEncrypt = encoder.encode(scopedPDU)
380
381            except PyAsn1Error:
382                debug.logger & debug.flagSM and debug.logger(
383                    '__generateRequestOrResponseMsg: scopedPDU serialization error: %s' % sys.exc_info()[1])
384                raise error.StatusInformation(
385                    errorIndication=errind.serializationError
386                )
387
388            debug.logger & debug.flagSM and debug.logger(
389                '__generateRequestOrResponseMsg: scopedPDU encoded into %s' % debug.hexdump(dataToEncrypt))
390
391            # noinspection PyUnboundLocalVariable
392            (encryptedData,
393             privParameters) = privHandler.encryptData(
394                usmUserPrivKeyLocalized,
395                (snmpEngineBoots, snmpEngineTime, None), dataToEncrypt
396            )
397
398            securityParameters.setComponentByPosition(
399                5, privParameters, verifyConstraints=False, matchTags=False, matchConstraints=False
400            )
401            scopedPDUData.setComponentByPosition(
402                1, encryptedData, verifyConstraints=False, matchTags=False, matchConstraints=False
403            )
404
405            debug.logger & debug.flagSM and debug.logger(
406                '__generateRequestOrResponseMsg: scopedPDU ciphered into %s' % debug.hexdump(encryptedData))
407
408        # 3.1.4b
409        elif securityLevel == 1 or securityLevel == 2:
410            securityParameters.setComponentByPosition(5, '')
411
412        debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: %s' % scopedPDUData.prettyPrint())
413
414        # 3.1.5
415        securityParameters.setComponentByPosition(
416            0, securityEngineID, verifyConstraints=False, matchTags=False, matchConstraints=False
417        )
418        securityParameters.setComponentByPosition(
419            1, snmpEngineBoots, verifyConstraints=False, matchTags=False, matchConstraints=False
420        )
421        securityParameters.setComponentByPosition(
422            2, snmpEngineTime, verifyConstraints=False, matchTags=False, matchConstraints=False
423        )
424
425        # 3.1.7
426        securityParameters.setComponentByPosition(
427            3, usmUserName, verifyConstraints=False, matchTags=False, matchConstraints=False
428        )
429
430        # 3.1.8a
431        if securityLevel == 3 or securityLevel == 2:
432            if usmUserAuthProtocol in self.authServices:
433                authHandler = self.authServices[usmUserAuthProtocol]
434            else:
435                raise error.StatusInformation(
436                    errorIndication=errind.authenticationFailure
437                )
438
439            # extra-wild hack to facilitate BER substrate in-place re-write
440            securityParameters.setComponentByPosition(
441                4, '\x00' * authHandler.digestLength
442            )
443
444            debug.logger & debug.flagSM and debug.logger(
445                '__generateRequestOrResponseMsg: %s' % (securityParameters.prettyPrint(),))
446
447            try:
448                msg.setComponentByPosition(2, encoder.encode(securityParameters), verifyConstraints=False)
449
450            except PyAsn1Error:
451                debug.logger & debug.flagSM and debug.logger(
452                    '__generateRequestOrResponseMsg: securityParameters serialization error: %s' % sys.exc_info()[1])
453                raise error.StatusInformation(
454                    errorIndication=errind.serializationError
455                )
456
457            debug.logger & debug.flagSM and debug.logger(
458                '__generateRequestOrResponseMsg: auth outgoing msg: %s' % msg.prettyPrint())
459
460            try:
461                wholeMsg = encoder.encode(msg)
462
463            except PyAsn1Error:
464                debug.logger & debug.flagSM and debug.logger(
465                    '__generateRequestOrResponseMsg: msg serialization error: %s' % sys.exc_info()[1])
466                raise error.StatusInformation(
467                    errorIndication=errind.serializationError
468                )
469
470            # noinspection PyUnboundLocalVariable
471            authenticatedWholeMsg = authHandler.authenticateOutgoingMsg(
472                usmUserAuthKeyLocalized, wholeMsg
473            )
474
475        # 3.1.8b
476        else:
477            securityParameters.setComponentByPosition(
478                4, '', verifyConstraints=False, matchTags=False, matchConstraints=False
479            )
480
481            debug.logger & debug.flagSM and debug.logger(
482                '__generateRequestOrResponseMsg: %s' % (securityParameters.prettyPrint(),))
483
484            try:
485                msg.setComponentByPosition(2, encoder.encode(securityParameters), verifyConstraints=False, matchTags=False, matchConstraints=False)
486
487            except PyAsn1Error:
488                debug.logger & debug.flagSM and debug.logger(
489                    '__generateRequestOrResponseMsg: secutiryParameters serialization error: %s' % sys.exc_info()[1])
490                raise error.StatusInformation(
491                    errorIndication=errind.serializationError
492                )
493
494            try:
495                debug.logger & debug.flagSM and debug.logger(
496                    '__generateRequestOrResponseMsg: plain outgoing msg: %s' % msg.prettyPrint())
497                authenticatedWholeMsg = encoder.encode(msg)
498
499            except PyAsn1Error:
500                debug.logger & debug.flagSM and debug.logger(
501                    '__generateRequestOrResponseMsg: msg serialization error: %s' % sys.exc_info()[1])
502                raise error.StatusInformation(
503                    errorIndication=errind.serializationError
504                )
505
506        debug.logger & debug.flagSM and debug.logger('__generateRequestOrResponseMsg: %s outgoing msg: %s' % (
507            securityLevel > 1 and "authenticated" or "plain", debug.hexdump(authenticatedWholeMsg)))
508
509        # 3.1.9
510        return msg.getComponentByPosition(2), authenticatedWholeMsg
511
512    def generateRequestMsg(self, snmpEngine, messageProcessingModel,
513                           globalData, maxMessageSize, securityModel,
514                           securityEngineID, securityName, securityLevel,
515                           scopedPDU):
516        return self.__generateRequestOrResponseMsg(snmpEngine,
517                                                   messageProcessingModel,
518                                                   globalData,
519                                                   maxMessageSize,
520                                                   securityModel,
521                                                   securityEngineID,
522                                                   securityName,
523                                                   securityLevel,
524                                                   scopedPDU,
525                                                   None)
526
527    def generateResponseMsg(self, snmpEngine, messageProcessingModel,
528                            globalData, maxMessageSize, securityModel,
529                            securityEngineID, securityName, securityLevel,
530                            scopedPDU, securityStateReference):
531        return self.__generateRequestOrResponseMsg(
532            snmpEngine, messageProcessingModel, globalData,
533            maxMessageSize, securityModel, securityEngineID,
534            securityName, securityLevel, scopedPDU, securityStateReference
535        )
536
537    # 3.2
538    def processIncomingMsg(self, snmpEngine, messageProcessingModel,
539                           maxMessageSize, securityParameters,
540                           securityModel, securityLevel, wholeMsg, msg):
541        mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
542
543        # 3.2.9 -- moved up here to be able to report
544        # maxSizeResponseScopedPDU on error
545        # (48 - maximum SNMPv3 header length)
546        maxSizeResponseScopedPDU = int(maxMessageSize) - len(securityParameters) - 48
547
548        debug.logger & debug.flagSM and debug.logger(
549            'processIncomingMsg: securityParameters %s' % debug.hexdump(securityParameters))
550
551        # 3.2.1
552        securityParameters, rest = decoder.decode(
553            securityParameters, asn1Spec=self.__securityParametersSpec
554        )
555
556        debug.logger & debug.flagSM and debug.logger('processIncomingMsg: %s' % (securityParameters.prettyPrint(),))
557
558        if eoo.endOfOctets.isSameTypeWith(securityParameters):
559            raise error.StatusInformation(errorIndication=errind.parseError)
560
561        # 3.2.2
562        msgAuthoritativeEngineId = securityParameters.getComponentByPosition(0)
563        securityStateReference = self._cache.push(
564            msgUserName=securityParameters.getComponentByPosition(3)
565        )
566
567        debug.logger & debug.flagSM and debug.logger(
568            'processIncomingMsg: cache write securityStateReference %s by msgUserName %s' % (
569                securityStateReference, securityParameters.getComponentByPosition(3)))
570
571        scopedPduData = msg.getComponentByPosition(3)
572
573        # Used for error reporting
574        contextEngineId = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax
575        contextName = null
576
577        snmpEngineID = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax
578
579        # 3.2.3
580        if (msgAuthoritativeEngineId != snmpEngineID and
581                msgAuthoritativeEngineId not in self.__timeline):
582            if (msgAuthoritativeEngineId and
583                    4 < len(msgAuthoritativeEngineId) < 33):
584                # 3.2.3a - cloned user when request was sent
585                debug.logger & debug.flagSM and debug.logger(
586                    'processIncomingMsg: non-synchronized securityEngineID %r' % (msgAuthoritativeEngineId,))
587            else:
588                # 3.2.3b
589                debug.logger & debug.flagSM and debug.logger(
590                    'processIncomingMsg: peer requested snmpEngineID discovery')
591                usmStatsUnknownEngineIDs, = mibBuilder.importSymbols(
592                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownEngineIDs')
593                usmStatsUnknownEngineIDs.syntax += 1
594                debug.logger & debug.flagSM and debug.logger(
595                    'processIncomingMsg: null or malformed msgAuthoritativeEngineId')
596                pysnmpUsmDiscoverable, = mibBuilder.importSymbols(
597                    '__PYSNMP-USM-MIB', 'pysnmpUsmDiscoverable')
598                if pysnmpUsmDiscoverable.syntax:
599                    debug.logger & debug.flagSM and debug.logger(
600                        'processIncomingMsg: starting snmpEngineID discovery procedure')
601
602                    # Report original contextName
603                    if scopedPduData.getName() != 'plaintext':
604                        debug.logger & debug.flagSM and debug.logger(
605                            'processIncomingMsg: scopedPduData not plaintext %s' % scopedPduData.prettyPrint())
606                        raise error.StatusInformation(
607                            errorIndication=errind.unknownEngineID
608                        )
609
610                    # 7.2.6.a.1
611                    scopedPdu = scopedPduData.getComponent()
612                    contextEngineId = scopedPdu.getComponentByPosition(0)
613                    contextName = scopedPdu.getComponentByPosition(1)
614
615                    raise error.StatusInformation(
616                        errorIndication=errind.unknownEngineID,
617                        oid=usmStatsUnknownEngineIDs.name,
618                        val=usmStatsUnknownEngineIDs.syntax,
619                        securityStateReference=securityStateReference,
620                        securityLevel=securityLevel,
621                        contextEngineId=contextEngineId,
622                        contextName=contextName,
623                        scopedPDU=scopedPdu,
624                        maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
625                    )
626                else:
627                    debug.logger & debug.flagSM and debug.logger('processIncomingMsg: will not discover EngineID')
628                    # free securityStateReference XXX
629                    raise error.StatusInformation(
630                        errorIndication=errind.unknownEngineID
631                    )
632
633        msgUserName = securityParameters.getComponentByPosition(3)
634
635        debug.logger & debug.flagSM and debug.logger(
636            'processIncomingMsg: read from securityParams msgAuthoritativeEngineId %r msgUserName %r' % (
637                msgAuthoritativeEngineId, msgUserName))
638
639        if msgUserName:
640            # 3.2.4
641            try:
642                (usmUserName,
643                 usmUserSecurityName,
644                 usmUserAuthProtocol,
645                 usmUserAuthKeyLocalized,
646                 usmUserPrivProtocol,
647                 usmUserPrivKeyLocalized) = self.__getUserInfo(
648                    snmpEngine.msgAndPduDsp.mibInstrumController,
649                    msgAuthoritativeEngineId, msgUserName
650                )
651                debug.logger & debug.flagSM and debug.logger('processIncomingMsg: read user info from LCD')
652
653            except NoSuchInstanceError:
654                debug.logger & debug.flagSM and debug.logger(
655                    'processIncomingMsg: unknown securityEngineID %r msgUserName %r' % (
656                        msgAuthoritativeEngineId, msgUserName))
657
658                usmStatsUnknownUserNames, = mibBuilder.importSymbols(
659                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames')
660                usmStatsUnknownUserNames.syntax += 1
661
662                raise error.StatusInformation(
663                    errorIndication=errind.unknownSecurityName,
664                    oid=usmStatsUnknownUserNames.name,
665                    val=usmStatsUnknownUserNames.syntax,
666                    securityStateReference=securityStateReference,
667                    securityLevel=securityLevel,
668                    contextEngineId=contextEngineId,
669                    contextName=contextName,
670                    msgUserName=msgUserName,
671                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
672                )
673
674            except PyAsn1Error:
675                debug.logger & debug.flagSM and debug.logger('processIncomingMsg: %s' % (sys.exc_info()[1],))
676                snmpInGenErrs, = mibBuilder.importSymbols('__SNMPv2-MIB', 'snmpInGenErrs')
677                snmpInGenErrs.syntax += 1
678                raise error.StatusInformation(errorIndication=errind.invalidMsg)
679        else:
680            # empty username used for engineID discovery
681            usmUserName = usmUserSecurityName = null
682            usmUserAuthProtocol = noauth.NoAuth.serviceID
683            usmUserPrivProtocol = nopriv.NoPriv.serviceID
684            usmUserAuthKeyLocalized = usmUserPrivKeyLocalized = None
685
686        debug.logger & debug.flagSM and debug.logger(
687            'processIncomingMsg: now have usmUserName %r usmUserSecurityName %r usmUserAuthProtocol %r usmUserPrivProtocol %r for msgUserName %r' % (
688                usmUserName, usmUserSecurityName, usmUserAuthProtocol, usmUserPrivProtocol, msgUserName))
689
690        # 3.2.11 (moved up here to let Reports be authenticated & encrypted)
691        self._cache.pop(securityStateReference)
692        securityStateReference = self._cache.push(
693            msgUserName=securityParameters.getComponentByPosition(3),
694            usmUserSecurityName=usmUserSecurityName,
695            usmUserAuthProtocol=usmUserAuthProtocol,
696            usmUserAuthKeyLocalized=usmUserAuthKeyLocalized,
697            usmUserPrivProtocol=usmUserPrivProtocol,
698            usmUserPrivKeyLocalized=usmUserPrivKeyLocalized
699        )
700
701        msgAuthoritativeEngineBoots = securityParameters.getComponentByPosition(1)
702        msgAuthoritativeEngineTime = securityParameters.getComponentByPosition(2)
703
704        snmpEngine.observer.storeExecutionContext(
705            snmpEngine, 'rfc3414.processIncomingMsg',
706            dict(securityEngineId=msgAuthoritativeEngineId,
707                 snmpEngineBoots=msgAuthoritativeEngineBoots,
708                 snmpEngineTime=msgAuthoritativeEngineTime,
709                 userName=usmUserName,
710                 securityName=usmUserSecurityName,
711                 authProtocol=usmUserAuthProtocol,
712                 authKey=usmUserAuthKeyLocalized,
713                 privProtocol=usmUserPrivProtocol,
714                 privKey=usmUserPrivKeyLocalized)
715        )
716        snmpEngine.observer.clearExecutionContext(
717            snmpEngine, 'rfc3414.processIncomingMsg'
718        )
719
720        # 3.2.5
721        if msgAuthoritativeEngineId == snmpEngineID:
722            # Authoritative SNMP engine: make sure securityLevel is sufficient
723            badSecIndication = None
724            if securityLevel == 3:
725                if usmUserAuthProtocol == noauth.NoAuth.serviceID:
726                    badSecIndication = 'authPriv wanted while auth not expected'
727                if usmUserPrivProtocol == nopriv.NoPriv.serviceID:
728                    badSecIndication = 'authPriv wanted while priv not expected'
729            elif securityLevel == 2:
730                if usmUserAuthProtocol == noauth.NoAuth.serviceID:
731                    badSecIndication = 'authNoPriv wanted while auth not expected'
732                if usmUserPrivProtocol != nopriv.NoPriv.serviceID:
733                    # 4 (discovery phase always uses authenticated messages)
734                    if msgAuthoritativeEngineBoots or msgAuthoritativeEngineTime:
735                        badSecIndication = 'authNoPriv wanted while priv expected'
736
737            elif securityLevel == 1:
738                if usmUserAuthProtocol != noauth.NoAuth.serviceID:
739                    badSecIndication = 'noAuthNoPriv wanted while auth expected'
740                if usmUserPrivProtocol != nopriv.NoPriv.serviceID:
741                    badSecIndication = 'noAuthNoPriv wanted while priv expected'
742            if badSecIndication:
743                usmStatsUnsupportedSecLevels, = mibBuilder.importSymbols(
744                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnsupportedSecLevels')
745                usmStatsUnsupportedSecLevels.syntax += 1
746                debug.logger & debug.flagSM and debug.logger(
747                    'processIncomingMsg: reporting inappropriate security level for user %s: %s' % (
748                        msgUserName, badSecIndication))
749                raise error.StatusInformation(
750                    errorIndication=errind.unsupportedSecurityLevel,
751                    oid=usmStatsUnsupportedSecLevels.name,
752                    val=usmStatsUnsupportedSecLevels.syntax,
753                    securityStateReference=securityStateReference,
754                    securityLevel=securityLevel,
755                    contextEngineId=contextEngineId,
756                    contextName=contextName,
757                    msgUserName=msgUserName,
758                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
759                )
760
761        # 3.2.6
762        if securityLevel == 3 or securityLevel == 2:
763            if usmUserAuthProtocol in self.authServices:
764                authHandler = self.authServices[usmUserAuthProtocol]
765            else:
766                raise error.StatusInformation(
767                    errorIndication=errind.authenticationFailure
768                )
769
770            try:
771                authHandler.authenticateIncomingMsg(
772                    usmUserAuthKeyLocalized,
773                    securityParameters.getComponentByPosition(4),
774                    wholeMsg
775                )
776
777            except error.StatusInformation:
778                usmStatsWrongDigests, = mibBuilder.importSymbols(
779                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsWrongDigests')
780                usmStatsWrongDigests.syntax += 1
781                raise error.StatusInformation(
782                    errorIndication=errind.authenticationFailure,
783                    oid=usmStatsWrongDigests.name,
784                    val=usmStatsWrongDigests.syntax,
785                    securityStateReference=securityStateReference,
786                    securityLevel=securityLevel,
787                    contextEngineId=contextEngineId,
788                    contextName=contextName,
789                    msgUserName=msgUserName,
790                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
791                )
792
793            debug.logger & debug.flagSM and debug.logger('processIncomingMsg: incoming msg authenticated')
794
795            # synchronize time with authed peer
796            self.__timeline[msgAuthoritativeEngineId] = (
797                securityParameters.getComponentByPosition(1),
798                securityParameters.getComponentByPosition(2),
799                securityParameters.getComponentByPosition(2),
800                int(time.time())
801            )
802
803            timerResolution = snmpEngine.transportDispatcher is None and 1.0 or snmpEngine.transportDispatcher.getTimerResolution()
804            expireAt = int(self.__expirationTimer + 300 / timerResolution)
805            if expireAt not in self.__timelineExpQueue:
806                self.__timelineExpQueue[expireAt] = []
807            self.__timelineExpQueue[expireAt].append(msgAuthoritativeEngineId)
808
809            debug.logger & debug.flagSM and debug.logger(
810                'processIncomingMsg: store timeline for securityEngineID %r' % (msgAuthoritativeEngineId,))
811
812        # 3.2.7
813        if securityLevel == 3 or securityLevel == 2:
814            if msgAuthoritativeEngineId == snmpEngineID:
815                # Authoritative SNMP engine: use local notion (SF bug #1649032)
816                (snmpEngineBoots,
817                 snmpEngineTime) = mibBuilder.importSymbols(
818                    '__SNMP-FRAMEWORK-MIB', 'snmpEngineBoots', 'snmpEngineTime')
819                snmpEngineBoots = snmpEngineBoots.syntax
820                snmpEngineTime = snmpEngineTime.syntax.clone()
821                idleTime = 0
822                debug.logger & debug.flagSM and debug.logger(
823                    'processIncomingMsg: read snmpEngineBoots (%s), snmpEngineTime (%s) from LCD' % (
824                        snmpEngineBoots, snmpEngineTime))
825            else:
826                # Non-authoritative SNMP engine: use cached estimates
827                if msgAuthoritativeEngineId in self.__timeline:
828                    (snmpEngineBoots, snmpEngineTime,
829                     latestReceivedEngineTime,
830                     latestUpdateTimestamp) = self.__timeline[
831                        msgAuthoritativeEngineId
832                    ]
833                    # time passed since last talk with this SNMP engine
834                    idleTime = int(time.time()) - latestUpdateTimestamp
835                    debug.logger & debug.flagSM and debug.logger(
836                        'processIncomingMsg: read timeline snmpEngineBoots %s snmpEngineTime %s for msgAuthoritativeEngineId %r, idle time %s secs' % (
837                            snmpEngineBoots, snmpEngineTime, msgAuthoritativeEngineId, idleTime))
838                else:
839                    raise error.ProtocolError('Peer SNMP engine info missing')
840
841            # 3.2.7a
842            if msgAuthoritativeEngineId == snmpEngineID:
843                if (snmpEngineBoots == 2147483647 or
844                        snmpEngineBoots != msgAuthoritativeEngineBoots or
845                        abs(idleTime + int(snmpEngineTime) - int(msgAuthoritativeEngineTime)) > 150):
846                    usmStatsNotInTimeWindows, = mibBuilder.importSymbols(
847                        '__SNMP-USER-BASED-SM-MIB', 'usmStatsNotInTimeWindows')
848                    usmStatsNotInTimeWindows.syntax += 1
849                    raise error.StatusInformation(
850                        errorIndication=errind.notInTimeWindow,
851                        oid=usmStatsNotInTimeWindows.name,
852                        val=usmStatsNotInTimeWindows.syntax,
853                        securityStateReference=securityStateReference,
854                        securityLevel=2,
855                        contextEngineId=contextEngineId,
856                        contextName=contextName,
857                        msgUserName=msgUserName,
858                        maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
859                    )
860            # 3.2.7b
861            else:
862                # 3.2.7b.1
863                # noinspection PyUnboundLocalVariable
864                if (msgAuthoritativeEngineBoots > snmpEngineBoots or
865                        msgAuthoritativeEngineBoots == snmpEngineBoots and
866                        msgAuthoritativeEngineTime > latestReceivedEngineTime):
867                    self.__timeline[msgAuthoritativeEngineId] = (
868                        msgAuthoritativeEngineBoots,
869                        msgAuthoritativeEngineTime,
870                        msgAuthoritativeEngineTime,
871                        int(time.time())
872                    )
873
874                    timerResolution = snmpEngine.transportDispatcher is None and 1.0 or snmpEngine.transportDispatcher.getTimerResolution()
875                    expireAt = int(self.__expirationTimer + 300 / timerResolution)
876                    if expireAt not in self.__timelineExpQueue:
877                        self.__timelineExpQueue[expireAt] = []
878                    self.__timelineExpQueue[expireAt].append(msgAuthoritativeEngineId)
879
880                    debug.logger & debug.flagSM and debug.logger(
881                        'processIncomingMsg: stored timeline msgAuthoritativeEngineBoots %s msgAuthoritativeEngineTime %s for msgAuthoritativeEngineId %r' % (
882                            msgAuthoritativeEngineBoots, msgAuthoritativeEngineTime, msgAuthoritativeEngineId))
883
884                # 3.2.7b.2
885                if (snmpEngineBoots == 2147483647 or
886                        msgAuthoritativeEngineBoots < snmpEngineBoots or
887                        msgAuthoritativeEngineBoots == snmpEngineBoots and
888                        abs(idleTime + int(snmpEngineTime) - int(msgAuthoritativeEngineTime)) > 150):
889                    raise error.StatusInformation(
890                        errorIndication=errind.notInTimeWindow,
891                        msgUserName=msgUserName
892                    )
893
894        # 3.2.8a
895        if securityLevel == 3:
896            if usmUserPrivProtocol in self.privServices:
897                privHandler = self.privServices[usmUserPrivProtocol]
898            else:
899                raise error.StatusInformation(
900                    errorIndication=errind.decryptionError,
901                    msgUserName=msgUserName
902                )
903            encryptedPDU = scopedPduData.getComponentByPosition(1)
904            if encryptedPDU is None:  # no ciphertext
905                raise error.StatusInformation(
906                    errorIndication=errind.decryptionError,
907                    msgUserName=msgUserName
908                )
909
910            try:
911                decryptedData = privHandler.decryptData(
912                    usmUserPrivKeyLocalized,
913                    (securityParameters.getComponentByPosition(1),
914                     securityParameters.getComponentByPosition(2),
915                     securityParameters.getComponentByPosition(5)),
916                    encryptedPDU
917                )
918                debug.logger & debug.flagSM and debug.logger(
919                    'processIncomingMsg: PDU deciphered into %s' % debug.hexdump(decryptedData))
920
921            except error.StatusInformation:
922                usmStatsDecryptionErrors, = mibBuilder.importSymbols(
923                    '__SNMP-USER-BASED-SM-MIB', 'usmStatsDecryptionErrors')
924                usmStatsDecryptionErrors.syntax += 1
925                raise error.StatusInformation(
926                    errorIndication=errind.decryptionError,
927                    oid=usmStatsDecryptionErrors.name,
928                    val=usmStatsDecryptionErrors.syntax,
929                    securityStateReference=securityStateReference,
930                    securityLevel=securityLevel,
931                    contextEngineId=contextEngineId,
932                    contextName=contextName,
933                    msgUserName=msgUserName,
934                    maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
935                )
936            scopedPduSpec = scopedPduData.setComponentByPosition(0).getComponentByPosition(0)
937            try:
938                scopedPDU, rest = decoder.decode(decryptedData, asn1Spec=scopedPduSpec)
939
940            except PyAsn1Error:
941                debug.logger & debug.flagSM and debug.logger(
942                    'processIncomingMsg: scopedPDU decoder failed %s' % sys.exc_info()[0])
943                raise error.StatusInformation(
944                    errorIndication=errind.decryptionError,
945                    msgUserName=msgUserName
946                )
947
948            if eoo.endOfOctets.isSameTypeWith(scopedPDU):
949                raise error.StatusInformation(
950                    errorIndication=errind.decryptionError,
951                    msgUserName=msgUserName
952                )
953        else:
954            # 3.2.8b
955            scopedPDU = scopedPduData.getComponentByPosition(0)
956            if scopedPDU is None:  # no plaintext
957                raise error.StatusInformation(
958                    errorIndication=errind.decryptionError,
959                    msgUserName=msgUserName
960                )
961
962        debug.logger & debug.flagSM and debug.logger(
963            'processIncomingMsg: scopedPDU decoded %s' % scopedPDU.prettyPrint())
964
965        # 3.2.10
966        securityName = usmUserSecurityName
967
968        debug.logger & debug.flagSM and debug.logger(
969            'processIncomingMsg: cached msgUserName %s info by securityStateReference %s' % (
970                msgUserName, securityStateReference))
971
972        # Delayed to include details
973        if not msgUserName and not msgAuthoritativeEngineId:
974            usmStatsUnknownUserNames, = mibBuilder.importSymbols(
975                '__SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames')
976            usmStatsUnknownUserNames.syntax += 1
977            raise error.StatusInformation(
978                errorIndication=errind.unknownSecurityName,
979                oid=usmStatsUnknownUserNames.name,
980                val=usmStatsUnknownUserNames.syntax,
981                securityStateReference=securityStateReference,
982                securityEngineID=msgAuthoritativeEngineId,
983                securityLevel=securityLevel,
984                contextEngineId=contextEngineId,
985                contextName=contextName,
986                msgUserName=msgUserName,
987                maxSizeResponseScopedPDU=maxSizeResponseScopedPDU,
988                PDU=scopedPDU
989            )
990
991        # 3.2.12
992        return (msgAuthoritativeEngineId, securityName, scopedPDU,
993                maxSizeResponseScopedPDU, securityStateReference)
994
995    def __expireTimelineInfo(self):
996        if self.__expirationTimer in self.__timelineExpQueue:
997            for engineIdKey in self.__timelineExpQueue[self.__expirationTimer]:
998                if engineIdKey in self.__timeline:
999                    del self.__timeline[engineIdKey]
1000                    debug.logger & debug.flagSM and debug.logger('__expireTimelineInfo: expiring %r' % (engineIdKey,))
1001            del self.__timelineExpQueue[self.__expirationTimer]
1002        self.__expirationTimer += 1
1003
1004    def receiveTimerTick(self, snmpEngine, timeNow):
1005        self.__expireTimelineInfo()
1006