diff options
author | Ilya Etingof <etingof@gmail.com> | 2019-08-06 22:56:33 +0200 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2019-08-06 22:56:33 +0200 |
commit | c351e90391b7772c3d93e63b64d479cae5f44f6b (patch) | |
tree | 523f5645b6f21d53024d645a58c5e944a441b05d | |
parent | 89d14d560a00f2ac1ab7228f00e0edbeedb31808 (diff) | |
download | pysnmp-git-c351e90391b7772c3d93e63b64d479cae5f44f6b.tar.gz |
Move most of SNMP engine discovery code to security module
This SNMP engine ID discovery procedure is spread across message
processing and security modules. This is weird!
Anyway, this change moves SNMP message rewriting, associated with
starting out SNMP discovery sequence, to security module. The
motivation is to let security module making the ultimate decision
whether or not SNMP engine discovery is required.
For example, if localized keys are committed directly to the DB,
security module may just use them without engine discovery phase.
-rw-r--r-- | pysnmp/hlapi/auth.py | 8 | ||||
-rw-r--r-- | pysnmp/proto/mpmod/rfc3412.py | 25 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/service.py | 46 |
3 files changed, 50 insertions, 29 deletions
diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py index 13120a6d..e784eec8 100644 --- a/pysnmp/hlapi/auth.py +++ b/pysnmp/hlapi/auth.py @@ -268,7 +268,9 @@ class UsmUserData(object): securityEngineId: :py:class:`~pysnmp.proto.rfc1902.OctetString` The snmpEngineID of the authoritative SNMP engine to which a dateRequest message is to be sent. Will be automatically - discovered from peer if not given. + discovered from peer if not given, unless localized keys + are used. In the latter case *securityEngineId* must be + specified. See :RFC:`3414#section-2.5.1` for technical explanation. @@ -304,6 +306,10 @@ class UsmUserData(object): * :py:class:`~pysnmp.hlapi.usmKeyTypeLocalized` + If `~pysnmp.hlapi.usmKeyTypeLocalized` is used, peer SNMP engine ID + discovery mechanism can't be leveraged for key localization, so + *securityEngineId* must be given by local configuration. + privKeyType: :py:class:`int` Type of `privKey` material. See :RFC:`3414#section-2.6` for technical explanation. diff --git a/pysnmp/proto/mpmod/rfc3412.py b/pysnmp/proto/mpmod/rfc3412.py index 58f3acc0..bfee07f9 100644 --- a/pysnmp/proto/mpmod/rfc3412.py +++ b/pysnmp/proto/mpmod/rfc3412.py @@ -203,31 +203,14 @@ class SnmpV3MessageProcessingModel(AbstractMessageProcessingModel): # 7.1.9.a if pdu.tagSet in rfc3411.unconfirmedClassPDUs: securityEngineId = snmpEngineID + else: if peerSnmpEngineData is None: - # Force engineID discovery (rfc3414, 4) - securityEngineId = securityName = self._emptyStr - securityLevel = 1 - # Clear possible auth&priv flags - headerData.setComponentByPosition( - 2, self._msgFlags[msgFlags & 0xfc], verifyConstraints=False, matchTags=False, matchConstraints=False - ) - # XXX - scopedPDU = self.__scopedPDU - scopedPDU.setComponentByPosition( - 0, self._emptyStr, verifyConstraints=False, matchTags=False, matchConstraints=False - ) - scopedPDU.setComponentByPosition(1, contextName) - scopedPDU.setComponentByPosition(2) + debug.logger & debug.flagMP and debug.logger( + 'prepareOutgoingMessage: peer SNMP engine is not known') - # Use dead-empty PDU for engine-discovery report - emptyPdu = pdu.clone() - pMod.apiPDU.setDefaults(emptyPdu) + securityEngineId = None - scopedPDU.getComponentByPosition(2).setComponentByType( - emptyPdu.tagSet, emptyPdu, verifyConstraints=False, matchTags=False, matchConstraints=False - ) - debug.logger & debug.flagMP and debug.logger('prepareOutgoingMessage: force engineID discovery') else: securityEngineId = peerSnmpEngineData['securityEngineId'] diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py index ab125aa7..244e9324 100644 --- a/pysnmp/proto/secmod/rfc3414/service.py +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -13,13 +13,16 @@ from pysnmp.proto.secmod.rfc3826.priv import aes from pysnmp.proto.secmod.rfc7860.auth import hmacsha2 from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256 from pysnmp.smi.error import NoSuchInstanceError -from pysnmp.proto import rfc1155, errind, error +from pysnmp.proto import api, rfc1155, errind, error from pysnmp import debug from pyasn1.type import univ, namedtype, constraint from pyasn1.codec.ber import encoder, decoder, eoo from pyasn1.error import PyAsn1Error from pyasn1.compat.octets import null +# API to rfc1905 protocol objects +pMod = api.protoModules[api.protoVersion2c] + # USM security params @@ -224,6 +227,7 @@ class SnmpUSMSecurityModel(AbstractSecurityModel): scopedPDU, securityStateReference): mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder snmpEngineID = mibBuilder.importSymbols('__SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax + msg = globalData # 3.1.1 if securityStateReference is not None: @@ -271,7 +275,7 @@ class SnmpUSMSecurityModel(AbstractSecurityModel): securityEngineID.prettyPrint(), securityName, securityStateReference)) - elif securityName: + elif securityEngineID: # 3.1.1b try: (usmUserName, usmUserSecurityName, usmUserAuthProtocol, @@ -352,14 +356,43 @@ class SnmpUSMSecurityModel(AbstractSecurityModel): ) else: - # empty username used for engineID discovery + # 4. (start SNMP engine ID discovery) + securityEngineID = securityName = null + securityLevel = 1 + + scopedPDU.setComponentByPosition( + 0, null, verifyConstraints=False, + matchTags=False, matchConstraints=False) + + headerData = msg.getComponentByPosition(1) + + # Clear possible auth&priv flags + headerData.setComponentByPosition( + 2, univ.OctetString(hexValue='00'), verifyConstraints=False, + matchTags=False, matchConstraints=False + ) + + emptyPdu = scopedPDU.getComponentByPosition(2).getComponent() + + # we edit the rest of the structures in-place because they + # are ours for as long as this stack lasts, however PDU + # is more persistent and should not be touched + + emptyPdu = emptyPdu.clone() + pMod.apiPDU.setDefaults(emptyPdu) + + scopedPDU.getComponentByPosition(2).setComponentByType( + emptyPdu.tagSet, emptyPdu, verifyConstraints=False, + matchTags=False, matchConstraints=False) + usmUserName = usmUserSecurityName = null usmUserAuthProtocol = noauth.NoAuth.serviceID usmUserPrivProtocol = nopriv.NoPriv.serviceID usmUserAuthKeyLocalized = usmUserPrivKeyLocalized = None debug.logger & debug.flagSM and debug.logger( - '__generateRequestOrResponseMsg: using blank USM info ' + '__generateRequestOrResponseMsg: using blank USM info for peer ' + 'SNMP engine ID discovery ' 'usmUserName "%s" ' 'usmUserSecurityName "%s" ' 'usmUserAuthProtocol "%s" ' @@ -370,9 +403,8 @@ class SnmpUSMSecurityModel(AbstractSecurityModel): usmUserName, usmUserSecurityName, usmUserAuthProtocol, usmUserAuthKeyLocalized, usmUserPrivProtocol, usmUserPrivKeyLocalized, - securityEngineID.prettyPrint(), securityName)) - - msg = globalData + securityEngineID and securityEngineID.prettyPrint(), + securityName)) # 3.1.2 if securityLevel == 3: |