diff options
Diffstat (limited to 'pysnmp/proto')
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/__init__.py | 3 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/auth/__init__.py | 0 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/auth/base.py | 8 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py | 113 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/auth/hmacsha.py | 112 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/localkey.py | 38 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/priv/__init__.py | 0 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/priv/base.py | 4 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/priv/des.py | 117 | ||||
-rw-r--r-- | pysnmp/proto/secmod/rfc3414/service.py | 689 |
10 files changed, 1084 insertions, 0 deletions
diff --git a/pysnmp/proto/secmod/rfc3414/__init__.py b/pysnmp/proto/secmod/rfc3414/__init__.py new file mode 100644 index 00000000..59df8fb2 --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/__init__.py @@ -0,0 +1,3 @@ +from pysnmp.proto.secmod.rfc3414 import service + +SnmpUSMSecurityModel = service.SnmpUSMSecurityModel diff --git a/pysnmp/proto/secmod/rfc3414/auth/__init__.py b/pysnmp/proto/secmod/rfc3414/auth/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/auth/__init__.py diff --git a/pysnmp/proto/secmod/rfc3414/auth/base.py b/pysnmp/proto/secmod/rfc3414/auth/base.py new file mode 100644 index 00000000..6572faa8 --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/auth/base.py @@ -0,0 +1,8 @@ +class AbstractAuthenticationService: + serviceID = None + # 7.2.4.1 + def authenticateOutgoingMsg(self, authKey, wholeMsg): + pass + # 7.2.4.2 + def authenticateIncomingMsg(self, authKey, authParameters, wholeMsg): + pass diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py new file mode 100644 index 00000000..36d02219 --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py @@ -0,0 +1,113 @@ +import string, md5 +from pysnmp.proto.secmod.rfc3414.auth import base +from pysnmp.proto import error + +_twelveZeros = '\x00'*12 +_fortyEightZeros = '\x00'*48 + +# rfc3414: 6.2.4 + +class HmacMd5(base.AbstractAuthenticationService): + serviceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 2) # usmHMACMD5AuthProtocol + __ipad = [0x36]*64 + __opad = [0x5C]*64 + + # 6.3.1 + def authenticateOutgoingMsg(self, authKey, wholeMsg): + # Here we expect calling secmod to indicate where the digest + # should be in the substrate. Also, it pre-sets digest placeholder + # so we hash wholeMsg out of the box. + # Yes, that's ugly but that's rfc... + l = string.find(wholeMsg, _twelveZeros) + if l == -1: + raise error.ProtocolError('Cant locate digest placeholder') + wholeHead = wholeMsg[:l] + wholeTail = wholeMsg[l+12:] + + # 6.3.1.1 + + # 6.3.1.2a + extendedAuthKey = map(ord, str(authKey) + _fortyEightZeros) + + # 6.3.1.2b --> noop + + # 6.3.1.2c + k1 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), '' + ) + + # 6.3.1.2d --> noop + + # 6.3.1.2e + k2 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), '' + ) + + # 6.3.1.3 + d1 = md5.md5(k1+wholeMsg).digest() + + # 6.3.1.4 + d2 = md5.md5(k2+d1).digest() + mac = d2[:12] + + # 6.3.1.5 & 6 + return '%s%s%s' % (wholeHead, mac, wholeTail) + + # 6.3.2 + def authenticateIncomingMsg(self, authKey, authParameters, wholeMsg): + # 6.3.2.1 & 2 + if len(authParameters) != 12: + raise error.StatusInformation( + errorIndication='authenticationError' + ) + + # 6.3.2.3 + l = string.find(wholeMsg, str(authParameters)) + if l == -1: + raise error.ProtocolError('Cant locate digest in wholeMsg') + wholeHead = wholeMsg[:l] + wholeTail = wholeMsg[l+12:] + authenticatedWholeMsg = '%s%s%s' % ( + wholeHead, _twelveZeros, wholeTail + ) + + # 6.3.2.4a + extendedAuthKey = map(ord, str(authKey) + _fortyEightZeros) + + # 6.3.2.4b --> noop + + # 6.3.2.4c + k1 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), '' + ) + + # 6.3.2.4d --> noop + + # 6.3.2.4e + k2 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), '' + ) + + # 6.3.2.5a + d1 = md5.md5(k1+authenticatedWholeMsg).digest() + + # 6.3.2.5b + d2 = md5.md5(k2+d1).digest() + + # 6.3.2.5c + mac = d2[:12] + + # 6.3.2.6 + if mac != authParameters: + raise error.StatusInformation( + errorIndication='authenticationFailure' + ) + + return authenticatedWholeMsg + +# print repr( +# HmacMd5().authenticateOutgoingMsg('1234567890123456', 'adsd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00afadff') +# ) +# print repr( +# HmacMd5().authenticateIncomingMsg('1234567890123456', '\xc2i\x9bu\x92\x96\x00vR\xed$@', 'adsd\xc2i\x9bu\x92\x96\x00vR\xed$@afadff') +# ) diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py new file mode 100644 index 00000000..c274257a --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py @@ -0,0 +1,112 @@ +import string, sha +from pysnmp.proto.secmod.rfc3414.auth import base +from pysnmp.proto import error + +_twelveZeros = '\x00'*12 +_fortyFourZeros = '\x00'*44 + +# 7.2.4 + +class HmacSha(base.AbstractAuthenticationService): + serviceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 3) # usmHMACSHAAuthProtocol + __ipad = [0x36]*64 + __opad = [0x5C]*64 + + # 7.3.1 + def authenticateOutgoingMsg(self, authKey, wholeMsg): + # 7.3.1.1 + # Here we expect calling secmod to indicate where the digest + # should be in the substrate. Also, it pre-sets digest placeholder + # so we hash wholeMsg out of the box. + # Yes, that's ugly but that's rfc... + l = string.find(wholeMsg, _twelveZeros) + if l == -1: + raise error.ProtocolError('Cant locate digest placeholder') + wholeHead = wholeMsg[:l] + wholeTail = wholeMsg[l+12:] + + # 7.3.1.2a + extendedAuthKey = map(ord, str(authKey) + _fortyFourZeros) + + # 7.3.1.2b -- noop + + # 7.3.1.2c + k1 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), '' + ) + + # 7.3.1.2d -- noop + + # 7.3.1.2e + k2 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), '' + ) + + # 7.3.1.3 + d1 = sha.sha(k1+wholeMsg).digest() + + # 7.3.1.4 + d2 = sha.sha(k2+d1).digest() + mac = d2[:12] + + # 7.3.1.5 & 6 + return '%s%s%s' % (wholeHead, mac, wholeTail) + + # 7.3.2 + def authenticateIncomingMsg(self, authKey, authParameters, wholeMsg): + # 7.3.2.1 & 2 + if len(authParameters) != 12: + raise error.StatusInformation( + errorIndication='authenticationError' + ) + + # 7.3.2.3 + l = string.find(wholeMsg, str(authParameters)) + if l == -1: + raise error.ProtocolError('Cant locate digest in wholeMsg') + wholeHead = wholeMsg[:l] + wholeTail = wholeMsg[l+12:] + authenticatedWholeMsg = '%s%s%s' % ( + wholeHead, _twelveZeros, wholeTail + ) + + # 7.3.2.4a + extendedAuthKey = map(ord, str(authKey) + _fortyFourZeros) + + # 7.3.2.4b --> noop + + # 7.3.2.4c + k1 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), '' + ) + + # 7.3.2.4d --> noop + + # 7.3.2.4e + k2 = string.join( + map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), '' + ) + + # 7.3.2.5a + d1 = sha.sha(k1+authenticatedWholeMsg).digest() + + # 7.3.2.5b + d2 = sha.sha(k2+d1).digest() + + # 7.3.2.5c + mac = d2[:12] + + # 7.3.2.6 + if mac != authParameters: + raise error.StatusInformation( + errorIndication='authenticationFailure' + ) + + return authenticatedWholeMsg + +# print repr( +# HmacSha().authenticateOutgoingMsg('12345678901234567890', 'adsd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00afadff') +# ) +# print repr( +# HmacSha().authenticateIncomingMsg('12345678901234567890', '0\x9c\x98g\xdb\xfd\xcd\xb0\xf9y\xc7b', 'adsd0\x9c\x98g\xdb\xfd\xcd\xb0\xf9y\xc7bafadff') +# ) diff --git a/pysnmp/proto/secmod/rfc3414/localkey.py b/pysnmp/proto/secmod/rfc3414/localkey.py new file mode 100644 index 00000000..a5b923fd --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/localkey.py @@ -0,0 +1,38 @@ +# Convert plaintext passphrase into a localized key +import md5 + +# RFC3414: A.2.1 +def hashPassphrase(passphrase): + md = md5.new() + passLen = len(passphrase) + count = passIndex = 0 + while count < 1048575: # why rfc says 1048576? + i = 0; passBuf = '' + while i < 64: + passBuf = passBuf + passphrase[passIndex % passLen] + i = i + 1; passIndex = passIndex + 1; + md.update(passBuf) + count = count + 64 + return md.digest() + +def localizeKey(passKey, snmpEngineId): + return md5.new('%s%s%s' % (passKey, str(snmpEngineId), passKey)).digest() + +def passwordToKeyMD5(passphrase, snmpEngineId): + return localizeKey(hashPassphrase(passphrase), snmpEngineId) + +# XXX +# d, m = divmod(len(password)) +# prevM = count = 0 +# while count < 1048576: +# i = 0 +# while i < 64: +# md.update(password[prevM:] + password * (d-1) + \ +# password[:prevM] + password[prevM:prevM+m]) +# prevM = m + +#print map(lambda x: '%x' % ord(x), passwordToKeyMD5('maplesyrup', '\x00'*11+'\x02')) +import string + +#print repr(passwordToKeyMD5('12345678', '\x80\x00\x07\xe5\x80<\x93eE\xdb_\x88B')) +#print string.join(map(lambda x: x, passwordToKeyMD5('12345678', '\x80\x00\x07\xe5\x80<\x93eE\xdb_\x88B')), '') diff --git a/pysnmp/proto/secmod/rfc3414/priv/__init__.py b/pysnmp/proto/secmod/rfc3414/priv/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/priv/__init__.py diff --git a/pysnmp/proto/secmod/rfc3414/priv/base.py b/pysnmp/proto/secmod/rfc3414/priv/base.py new file mode 100644 index 00000000..8690d4de --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/priv/base.py @@ -0,0 +1,4 @@ +class AbstractEncryptionService: + serviceID = None + def encryptData(self, encryptKey, dataToEncrypt): pass + def decryptData(self, decryptKey, privParameters, encryptedData): pass diff --git a/pysnmp/proto/secmod/rfc3414/priv/des.py b/pysnmp/proto/secmod/rfc3414/priv/des.py new file mode 100644 index 00000000..aead8cd2 --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/priv/des.py @@ -0,0 +1,117 @@ +import random, string +from pysnmp.proto.secmod.rfc3414.priv import base +from pyasn1.type import univ +from pysnmp.proto import error + +try: + from Crypto.Cipher import DES +except ImportError: + DES = None + +random.seed() + +# 8.2.4 + +class Des(base.AbstractEncryptionService): + serviceID = (1, 3, 6, 1, 6, 3, 10, 1, 2, 2) # usmDESPrivProtocol + _localInt = long(random.random()*0xffffffff) + # 8.1.1.1 + def __getEncryptionKey(self, mibInstrumController, privKey): + desKey = privKey[:8] + preIV = privKey[8:16] + + snmpEngineBoots, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-FRAMEWORK-MIB', 'snmpEngineBoots' + ) + securityEngineBoots = long(snmpEngineBoots.syntax) + + salt = [ + securityEngineBoots>>24&0xff, + securityEngineBoots>>16&0xff, + securityEngineBoots>>8&0xff, + securityEngineBoots&0xff, + self._localInt>>24&0xff, + self._localInt>>16&0xff, + self._localInt>>8&0xff, + self._localInt&0xff + ] + if self._localInt == 0xffffffff: + self._localInt = 0 + else: + self._localInt = self._localInt + 1 + + return desKey, \ + string.join(map(lambda x: chr(x), salt), ''), \ + string.join(map(lambda x,y: chr(x^ord(y)), salt, preIV), '') + + def __getDecryptionKey(self, mibInstrumController, privKey, salt): + return privKey[:8], string.join( + map(lambda x,y: chr(ord(x)^ord(y)), salt, privKey[8:16]), '' + ) + + # 8.2.4.1 + def encryptData(self, mibInstrumController, encryptKey, dataToEncrypt): + if DES is None: + raise error.StatusInformation( + errorIndication='encryptionError' + ) + + # 8.3.1.1 + desKey, salt, iv = self.__getEncryptionKey( + mibInstrumController, str(encryptKey) + ) + + # 8.3.1.2 + privParameters = univ.OctetString(salt) + + # 8.1.1.2 + desObj = DES.new(desKey, DES.MODE_CBC, iv) # XXX + plaintext = dataToEncrypt + '\x00' * (8 - len(dataToEncrypt) % 8) + ciphertext = desObj.encrypt(plaintext) + + # 8.3.1.3 & 4 + return univ.OctetString(ciphertext), privParameters + + # 8.2.4.2 + def decryptData(self, mibInstrumController, decryptKey, + privParameters, encryptedData): + if DES is None: + raise error.StatusInformation( + errorIndication='decryptionError' + ) + + # 8.3.2.1 + if len(privParameters) != 8: + raise error.StatusInformation( + errorIndication='decryptionError' + ) + + # 8.3.2.2 + salt = str(privParameters) + + # 8.3.2.3 + desKey, iv = self.__getDecryptionKey( + mibInstrumController, str(decryptKey), salt + ) + + # 8.3.2.4 -> 8.1.1.3 + if len(encryptedData) % 8 != 0: + raise error.StatusInformation( + errorIndication='decryptionError' + ) + + desObj = DES.new(desKey, DES.MODE_CBC, iv) + + # 8.3.2.6 + return desObj.decrypt(str(encryptedData)) + +if __name__ == '__main__': + from pysnmp.smi import builder, instrum + + mibInstrumController = instrum.MibInstrumController( + builder.MibBuilder() + ) + + d = Des() + ct, p = d.encryptData(mibInstrumController, '1234567890123456', 'security? not my problem!') + print d.decryptData(mibInstrumController, '1234567890123456', p, ct) diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py new file mode 100644 index 00000000..adf05bb5 --- /dev/null +++ b/pysnmp/proto/secmod/rfc3414/service.py @@ -0,0 +1,689 @@ +# SNMP v3 USM model services +from pysnmp.proto.secmod.base import AbstractSecurityModel +from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha +from pysnmp.proto.secmod.rfc3414.priv import des +from pysnmp.proto.secmod.rfc3414 import localkey +from pysnmp.smi.error import NoSuchInstanceError +from pysnmp.proto import error +from pyasn1.type import univ, namedtype, constraint +from pyasn1.codec.ber import encoder, decoder +from pyasn1.error import PyAsn1Error + +# USM security params + +class UsmSecurityParameters(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('msgAuthoritativeEngineID', univ.OctetString()), + namedtype.NamedType('msgAuthoritativeEngineBoots', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))), + namedtype.NamedType('msgAuthoritativeEngineTime', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))), + namedtype.NamedType('msgUserName', univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, 32))), + namedtype.NamedType('msgAuthenticationParameters', univ.OctetString()), + namedtype.NamedType('msgPrivacyParameters', univ.OctetString()) + ) + +class SnmpUSMSecurityModel(AbstractSecurityModel): + securityModelID = 3 + authServices = { + hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(), + hmacsha.HmacSha.serviceID: hmacsha.HmacSha() + } + privServices = { + des.Des.serviceID: des.Des() + } + _securityParametersSpec = UsmSecurityParameters() + def __init__(self): + AbstractSecurityModel.__init__(self) + self.__timeline = {} + + def __getUserInfo( + self, mibInstrumController, securityEngineID, securityName + ): + usmUserEntry, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-USER-BASED-SM-MIB', 'usmUserEntry' + ) + tblIdx = usmUserEntry.getInstIdFromIndices( + securityEngineID, securityName + ) + # Get protocols + usmUserSecurityName = usmUserEntry.getNode( + usmUserEntry.name + (3,) + tblIdx + ).syntax + 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 ( + usmUserSecurityName, # XXX function needed? + usmUserAuthProtocol, + pysnmpUsmKeyAuthLocalized, + usmUserPrivProtocol, + pysnmpUsmKeyPrivLocalized + ) + + def __cloneUserInfo( + self, mibInstrumController, securityEngineID, securityName + ): + snmpEngineID, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-FRAMEWORK-MIB', 'snmpEngineID' + ) + # Proto entry + usmUserEntry, = mibInstrumController.mibBuilder.importSymbols( + 'SNMP-USER-BASED-SM-MIB', 'usmUserEntry' + ) + tblIdx = usmUserEntry.getInstIdFromIndices( + snmpEngineID.syntax, securityName + ) + # Get proto protocols + usmUserSecurityName = usmUserEntry.getNode( + usmUserEntry.name + (3,) + tblIdx + ) + usmUserAuthProtocol = usmUserEntry.getNode( + usmUserEntry.name + (5,) + tblIdx + ) + usmUserPrivProtocol = usmUserEntry.getNode( + usmUserEntry.name + (8,) + tblIdx + ) + # Get proto keys + pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols( + 'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry' + ) + pysnmpUsmKeyAuth = pysnmpUsmKeyEntry.getNode( + pysnmpUsmKeyEntry.name + (3,) + tblIdx + ) + pysnmpUsmKeyPriv = pysnmpUsmKeyEntry.getNode( + pysnmpUsmKeyEntry.name + (4,) + tblIdx + ) + + # Create new row from proto values + + tblIdx = usmUserEntry.getInstIdFromIndices( + securityEngineID, securityName + ) + # New row + mibInstrumController.writeVars( + ((usmUserEntry.name + (13,) + tblIdx, 4),) + ) + # Set protocols + usmUserEntry.getNode( + usmUserEntry.name + (3,) + tblIdx + ).syntax = usmUserSecurityName.syntax + usmUserEntry.getNode( + usmUserEntry.name + (5,) + tblIdx + ).syntax = usmUserAuthProtocol.syntax + usmUserEntry.getNode( + usmUserEntry.name + (8,) + tblIdx + ).syntax = usmUserPrivProtocol.syntax + + # Localize and set keys + pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols( + 'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry' + ) + pysnmpUsmKeyAuthLocalized = pysnmpUsmKeyEntry.getNode( + pysnmpUsmKeyEntry.name + (1,) + tblIdx + ) + pysnmpUsmKeyAuthLocalized.syntax = pysnmpUsmKeyAuthLocalized.syntax.clone(localkey.localizeKey(pysnmpUsmKeyAuth.syntax, securityEngineID)) + pysnmpUsmKeyPrivLocalized = pysnmpUsmKeyEntry.getNode( + pysnmpUsmKeyEntry.name + (2,) + tblIdx + ) + pysnmpUsmKeyPrivLocalized.syntax = pysnmpUsmKeyPrivLocalized.syntax.clone(localkey.localizeKey(pysnmpUsmKeyPriv.syntax, securityEngineID)) + return ( + usmUserSecurityName.syntax, # XXX function needed? + usmUserAuthProtocol.syntax, + pysnmpUsmKeyAuthLocalized.syntax, + usmUserPrivProtocol.syntax, + pysnmpUsmKeyPrivLocalized.syntax + ) + + def __generateRequestOrResponseMsg( + self, + snmpEngine, + messageProcessingModel, + globalData, + maxMessageSize, + securityModel, + securityEngineID, + securityName, + securityLevel, + scopedPDU, + securityStateReference + ): + snmpEngineID = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax + # 3.1.1 + if securityStateReference is not None: + # 3.1.1a + cachedSecurityData = self._cachePop(securityStateReference) + usmUserName = cachedSecurityData['msgUserName'] + usmUserAuthProtocol = cachedSecurityData.get('usmUserAuthProtocol') + usmUserAuthKeyLocalized = cachedSecurityData.get( + 'usmUserAuthKeyLocalized' + ) + usmUserPrivProtocol = cachedSecurityData.get('usmUserPrivProtocol') + usmUserPrivKeyLocalized = cachedSecurityData.get( + 'usmUserPrivKeyLocalized' + ) + securityEngineID = snmpEngineID + elif securityName: + # 3.1.1b + try: + ( usmUserName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized, + usmUserPrivProtocol, + usmUserPrivKeyLocalized ) = self.__getUserInfo( + snmpEngine.msgAndPduDsp.mibInstrumController, securityEngineID, securityName + ) + except NoSuchInstanceError: + pysnmpUsmDiscovery, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmDiscovery') + __reportUnknownName = not pysnmpUsmDiscovery.syntax + if not __reportUnknownName: + try: + ( usmUserName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized, + usmUserPrivProtocol, + usmUserPrivKeyLocalized ) = self.__cloneUserInfo( + snmpEngine.msgAndPduDsp.mibInstrumController,securityEngineID, securityName + ) + except NoSuchInstanceError: + __reportUnknownName = 1 + if __reportUnknownName: + raise error.StatusInformation( + errorIndication = 'unknownSecurityName' + ) + else: + # empty username used for engineID discovery + usmUserName = usmUserSecurityName = '' + usmUserAuthProtocol = usmUserAuthKeyLocalized = None + usmUserPrivProtocol = usmUserPrivKeyLocalized = None + + msg = globalData + + # 3.1.2 + if securityLevel == 3: + if not usmUserAuthProtocol or not usmUserPrivProtocol: + raise error.StatusInformation( + errorIndication = 'unsupportedSecurityLevel' + ) + + # 3.1.3 + if securityLevel == 3 or securityLevel == 2: + if not usmUserAuthProtocol: + raise error.StatusInformation( + errorIndication = 'unsupportedSecurityLevel' + ) + + securityParameters = UsmSecurityParameters() + + scopedPDUData = msg.setComponentByPosition(3).getComponentByPosition(3) + scopedPDUData.setComponentByPosition(0, scopedPDU) + + # 3.1.4a + if securityLevel == 3: + privHandler = self.privServices.get( + tuple(usmUserPrivProtocol) + ) + if privHandler is None: + raise error.StatusInformation( + errorIndication = 'encryptionError' + ) + dataToEncrypt = encoder.encode(scopedPDU) + try: + ( encryptedData, + privParameters ) = privHandler.encryptData( + snmpEngine.msgAndPduDsp.mibInstrumController, usmUserPrivKeyLocalized, dataToEncrypt + ) + except error.StatusInformation, statusInformation: + raise + + securityParameters.setComponentByPosition(5, privParameters) + scopedPDUData.setComponentByPosition(1, encryptedData) + + # 3.1.4b + elif securityLevel == 1 or securityLevel == 2: + securityParameters.setComponentByPosition(5, '') + + # 3.1.5 + securityParameters.setComponentByPosition(0, str(securityEngineID)) + + # 3.1.6a + if securityStateReference is None and ( # request type check added + securityLevel == 3 or securityLevel == 2 + ): + if self.__timeline.has_key(securityEngineID): + ( snmpEngineBoots, + snmpEngineTime, + latestReceivedEngineTime ) = self.__timeline[ + securityEngineID + ] + else: + # 2.3 XXX is this correct? + snmpEngineBoots = snmpEngineTime = 0 + # 3.1.6.b + elif securityStateReference is not None: # XXX Report? + ( snmpEngineBoots, + snmpEngineTime ) = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-FRAMEWORK-MIB', 'snmpEngineBoots', 'snmpEngineTime') + snmpEngineBoots = snmpEngineBoots.syntax + snmpEngineTime = snmpEngineTime.syntax + # 3.1.6.c + else: + snmpEngineBoots = snmpEngineTime = 0 + + securityParameters.setComponentByPosition(1, int(snmpEngineBoots)) + securityParameters.setComponentByPosition(2, int(snmpEngineTime)) + + # 3.1.7 + securityParameters.setComponentByPosition(3, str(usmUserName)) + + # 3.1.8a + if securityLevel == 3 or securityLevel == 2: + authHandler = self.authServices.get( + tuple(usmUserAuthProtocol) + ) + if authHandler is None: + raise error.StatusInformation( + errorIndication = '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' + ) + + msg.setComponentByPosition(2, encoder.encode(securityParameters)) + + wholeMsg = encoder.encode(msg) + + try: + authenticatedWholeMsg = authHandler.authenticateOutgoingMsg( + usmUserAuthKeyLocalized, wholeMsg + ) + except error.StatusInformation, statusInformation: + raise + + # 3.1.8b + else: + securityParameters.setComponentByPosition(4, '') + msg.setComponentByPosition(2, encoder.encode(securityParameters)) + authenticatedWholeMsg = encoder.encode(msg) + + # 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 # XXX + ): + # 3.2.1 + try: + securityParameters, rest = decoder.decode( + securityParameters, + asn1Spec=self._securityParametersSpec + ) + except PyAsn1Error, why: + snmpInASNParseErrs, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMPv2-MIB', 'snmpInASNParseErrs') + snmpInASNParseErrs.syntax = snmpInASNParseErrs.syntax + 1 + raise error.StatusInformation( + errorIndication='parseError' + ) + + # 3.2.9 -- moved up here to be able to report + # maxSizeResponseScopedPDU on error + maxSizeResponseScopedPDU = maxMessageSize - 512 # XXX + if maxSizeResponseScopedPDU < 0: + maxSizeResponseScopedPDU = 0 + + # 3.2.2 + securityEngineID = securityParameters.getComponentByPosition(0) + securityStateReference = self._cachePush( + msgUserName=securityParameters.getComponentByPosition(3) + ) + + # Used for error reporting + contextEngineID = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax + contextName = '' + + # 3.2.3 + if not self.__timeline.has_key(securityEngineID): + if securityEngineID: + # 3.2.3a XXX any other way to get auth engine in cache? + self.__timeline[securityEngineID] = ( + securityParameters.getComponentByPosition(1), + securityParameters.getComponentByPosition(2), + securityParameters.getComponentByPosition(2) + ) + else: + # 3.2.3b + usmStatsUnknownEngineIDs, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownEngineIDs') + usmStatsUnknownEngineIDs.syntax = usmStatsUnknownEngineIDs.syntax+1 + pysnmpUsmDiscoverable, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmDiscoverable') + if pysnmpUsmDiscoverable.syntax: + raise error.StatusInformation( + errorIndication = 'unknownEngineID', + oid=usmStatsUnknownEngineIDs.name, + val=usmStatsUnknownEngineIDs.syntax, + securityStateReference=securityStateReference, + securityLevel=securityLevel, + contextEngineID=contextEngineID, + contextName=contextName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU + ) + else: + # free securityStateReference XXX + raise error.StatusInformation( + errorIndication = 'unknownEngineID' + ) + + snmpEngineID = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols( + 'SNMP-FRAMEWORK-MIB', 'snmpEngineID' + )[0].syntax + + msgAuthoritativeEngineID = securityParameters.getComponentByPosition(0) + msgUserName = securityParameters.getComponentByPosition(3) + + if msgUserName: + # 3.2.4 + try: + ( usmUserSecurityName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized, + usmUserPrivProtocol, + usmUserPrivKeyLocalized ) = self.__getUserInfo( + snmpEngine.msgAndPduDsp.mibInstrumController, msgAuthoritativeEngineID, msgUserName + ) + except NoSuchInstanceError: + pysnmpUsmDiscoverable, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmDiscoverable') + __reportUnknownName = not pysnmpUsmDiscoverable.syntax + if not __reportUnknownName: + try: + ( usmUserSecurityName, + usmUserAuthProtocol, + usmUserAuthKeyLocalized, + usmUserPrivProtocol, + usmUserPrivKeyLocalized ) = self.__cloneUserInfo( + snmpEngine.msgAndPduDsp.mibInstrumController,securityEngineID, msgUserName + ) + except NoSuchInstanceError: + __reportUnknownName = 1 + if __reportUnknownName: + usmStatsUnknownUserNames, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames') + usmStatsUnknownUserNames.syntax = usmStatsUnknownUserNames.syntax+1 + raise error.StatusInformation( + errorIndication = 'unknownSecurityName', + oid = usmStatsUnknownUserNames.name, + val = usmStatsUnknownUserNames.syntax, + securityStateReference=securityStateReference, + securityLevel=securityLevel, + contextEngineID=contextEngineID, + contextName=contextName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU + ) + else: + # empty username used for engineID discovery + usmUserName = usmUserSecurityName = '' + usmUserAuthProtocol = usmUserAuthKeyLocalized = None + usmUserPrivProtocol = usmUserPrivKeyLocalized = None + + # 3.2.5 + __reportError = 0 + if securityLevel == 3: + if not usmUserAuthProtocol or not usmUserPrivProtocol: + __reportError = 1 + elif securityLevel == 2: + if not usmUserAuthProtocol: + __reportError = 1 + if __reportError: + usmStatsUnsupportedSecLevels, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnsupportedSecLevels') + usmStatsUnsupportedSecLevels.syntax = usmStatsUnsupportedSecLevels.syntax + 1 + raise error.StatusInformation( + errorIndication='unsupportedSecurityLevel', + oid=usmStatsUnknownEngineIDs.name, + val=usmStatsUnknownEngineIDs.syntax, + securityStateReference=securityStateReference, + securityLevel=securityLevel, + contextEngineID=contextEngineID, + contextName=contextName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU + ) + + # 3.2.6 + if securityLevel == 3 or securityLevel == 2: + authHandler = self.authServices.get( + tuple(usmUserAuthProtocol) + ) + if authHandler is None: + raise error.StatusInformation( + errorIndication = 'authenticationFailure' + ) + try: + authenticatedWholeMsg = authHandler.authenticateIncomingMsg( + usmUserAuthKeyLocalized, + securityParameters.getComponentByPosition(4), + wholeMsg + ) + except error.StatusInformation: + usmStatsWrongDigests, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsWrongDigests') + usmStatsWrongDigests.syntax = usmStatsWrongDigests.syntax+1 + raise error.StatusInformation( + errorIndication = 'authenticationFailure', + oid=usmStatsWrongDigests.name, + val=usmStatsWrongDigests.syntax, + securityStateReference=securityStateReference, + securityLevel=securityLevel, + contextEngineID=contextEngineID, + contextName=contextName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU + ) + + # 3.2.7 + if securityLevel == 3 or securityLevel == 2: + if self.__timeline.has_key(securityEngineID): + ( snmpEngineBoots, + snmpEngineTime, + latestReceivedEngineTime ) = self.__timeline[ + msgAuthoritativeEngineID + ] + else: + raise error.ProtocolError('Peer SNMP engine info missing') + + msgAuthoritativeEngineBoots = securityParameters.getComponentByPosition(1) + msgAuthoritativeEngineTime = securityParameters.getComponentByPosition(2) + + # 3.2.7a + if msgAuthoritativeEngineID == snmpEngineID: + if snmpEngineBoots == 2147483647 or \ + snmpEngineBoots != msgAuthoritativeEngineBoots or \ + abs(int(snmpEngineTime)-int(msgAuthoritativeEngineTime)) > 150: + usmStatsNotInTimeWindows, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsNotInTimeWindows') + usmStatsNotInTimeWindows.syntax = usmStatsNotInTimeWindows.syntax+1 + raise error.StatusInformation( + errorIndication = 'notInTimeWindow', + oid=usmStatsNotInTimeWindows.name, + val=usmStatsNotInTimeWindows.syntax, + securityStateReference=securityStateReference, + securityLevel=2, + contextEngineID=contextEngineID, + contextName=contextName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU + ) + # 3.2.7b + else: + # 3.2.7b.1 + if msgAuthoritativeEngineBoots > snmpEngineBoots or \ + msgAuthoritativeEngineBoots == snmpEngineBoots and \ + msgAuthoritativeEngineTime > latestReceivedEngineTime: + self.__timeline[msgAuthoritativeEngineID] = ( + msgAuthoritativeEngineBoots, + msgAuthoritativeEngineTime, + msgAuthoritativeEngineTime + ) + + # 3.2.7b.2 + if snmpEngineBoots == 2147483647 or \ + msgAuthoritativeEngineBoots < snmpEngineBoots or \ + msgAuthoritativeEngineBoots == snmpEngineBoots and \ + msgAuthoritativeEngineTime - snmpEngineTime < -150: + raise error.StatusInformation( + errorIndication = 'notInTimeWindow' + ) + + scopedPduData = msg.getComponentByPosition(3) + + # 3.2.8a + if securityLevel == 3: + privHandler = self.privServices.get( + tuple(usmUserPrivProtocol) + ) + if privHandler is None: + raise error.StatusInformation( + errorIndication = 'decryptionError' + ) + encryptedPDU = scopedPduData.getComponentByPosition(1) + if encryptedPDU is None: # no ciphertext + raise error.StatusInformation( + errorIndication = 'decryptionError' + ) + try: + decryptedData = privHandler.decryptData( + snmpEngine.msgAndPduDsp.mibInstrumController, usmUserPrivKeyLocalized, + securityParameters.getComponentByPosition(5), + encryptedPDU + ) + except error.StatusInformation: + usmStatsDecryptionErrors, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsDecryptionErrors') + usmStatsDecryptionErrors.syntax = usmStatsDecryptionErrors.syntax+1 + raise error.StatusInformation( + errorIndication = 'decryptionError', + oid=usmStatsDecryptionErrors.name, + val=usmStatsDecryptionErrors.syntax, + securityStateReference=securityStateReference, + securityLevel=securityLevel, + contextEngineID=contextEngineID, + contextName=contextName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU + ) + scopedPduSpec = scopedPduData.setComponentByPosition(0).getComponentByPosition(0) + scopedPDU, rest = decoder.decode( + decryptedData, asn1Spec=scopedPduSpec + ) + else: + # 3.2.8b + scopedPDU = scopedPduData.getComponentByPosition(0) + if scopedPDU is None: # no plaintext + raise error.StatusInformation( + errorIndication = 'decryptionError' + ) + + # 3.2.10 + securityName = usmUserSecurityName + + # 3.2.11 + self._cachePop(securityStateReference) + securityStateReference = self._cachePush( + msgUserName=securityParameters.getComponentByPosition(3), + usmUserAuthProtocol=usmUserAuthProtocol, + usmUserAuthKeyLocalized=usmUserAuthKeyLocalized, + usmUserPrivProtocol=usmUserPrivProtocol, + usmUserPrivKeyLocalized=usmUserPrivKeyLocalized + ) + + # Delayed to include details + if not msgUserName and not securityEngineID: + usmStatsUnknownUserNames, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames') + usmStatsUnknownUserNames.syntax = usmStatsUnknownUserNames.syntax+1 + raise error.StatusInformation( + errorIndication='unknownSecurityName', + oid=usmStatsUnknownUserNames.name, + val=usmStatsUnknownUserNames.syntax, + securityStateReference=securityStateReference, + securityEngineID=securityEngineID, + securityLevel=securityLevel, + contextEngineID=contextEngineID, + contextName=contextName, + maxSizeResponseScopedPDU=maxSizeResponseScopedPDU, + PDU=scopedPDU + ) + + # 3.2.12 + return ( securityEngineID, + securityName, + scopedPDU, + maxSizeResponseScopedPDU, + securityStateReference ) + +# XXX +# get rid of msg handler passed into processIncomingMsg +# usmUserName vs usmUserSecurityName -- map? |