From fcf853e823007a18f6db72026f104401d29eea12 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Tue, 10 Dec 2013 12:06:23 +0100 Subject: [PATCH] add string readlen arg to avoid recurcive loop --- rdpy/protocol/network/type.py | 168 +++++++++++++++++++++++++++++----- rdpy/protocol/rdp/gcc.py | 24 ++++- rdpy/protocol/rdp/gdl.py | 37 +++++--- rdpy/protocol/rdp/lic.py | 48 +++++++++- rdpy/protocol/rdp/tpdu.py | 6 ++ rdpy/protocol/rfb/rfb.py | 2 - 6 files changed, 238 insertions(+), 47 deletions(-) diff --git a/rdpy/protocol/network/type.py b/rdpy/protocol/network/type.py index 305a1d9..285411f 100644 --- a/rdpy/protocol/network/type.py +++ b/rdpy/protocol/network/type.py @@ -5,12 +5,15 @@ import struct from copy import deepcopy from StringIO import StringIO -from error import InvalidValue, InvalidSize +from error import InvalidValue from rdpy.protocol.network.error import InvalidExpectedDataException def sizeof(element): ''' byte size of type + sum sizeof of tuple element + and count only element that condition + is true at sizeof call @param element: Type or Tuple(Type | Tuple,) @return: size of element in byte ''' @@ -26,18 +29,31 @@ def sizeof(element): class Type(object): ''' - root type + root type object inheritance + record conditional optional of constant + mechanism ''' def __init__(self, conditional = lambda:True, optional = False, constant = False): + ''' + constructor of any type object + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' self._conditional = conditional self._optional = optional self._constant = constant - self._is_writed = False + #use to record read state + #if type is optional and not present during read + #this boolean stay false self._is_readed = False + #use to know if type was written + self._is_writed = False def write(self, s): ''' - interface definition of write function + write type into stream if conditional is true + and call private @param s: Stream which will be written ''' self._is_writed = self._conditional() @@ -47,7 +63,8 @@ class Type(object): def read(self, s): ''' - interface definition of read value + read type from stream s if conditional + is true and check constantness @param s: Stream ''' self._is_readed = self._conditional() @@ -58,6 +75,20 @@ class Type(object): #check constant value if self._constant and old != self: raise InvalidExpectedDataException("const value expected") + + def __read__(self, s): + ''' + interface definition of private read funtion + @param s: Stream + ''' + pass + + def __write__(self, s): + ''' + interface definition of private write funtion + @param s: Stream + ''' + pass def __sizeof__(self): ''' @@ -68,27 +99,33 @@ class Type(object): class CallableValue(object): ''' - type that wrap an inner type - acces with value getter and setter - value can be a callable which is call - at each access of value + wrap access of callable value. + When use getter value is call. + Constant value can also be wrap + and will be transformed into callable value(lambda function) ''' def __init__(self, value): + ''' + construtor + @param value: value will be wrapped (constant | lambda | function) + ''' self._value = None self.value = value def __getValue__(self): ''' - shortcut to access inner value - call lambda value - @return: inner value(python type value) + can be overwritten to add specific check before + self.value is call + @return: result of callbale value ''' return self._value() def __setValue__(self, value): ''' - setter of value wrap in lambda value - @param value: new value encompass in valuetype object + can be overwritten to add specific check before + self.value = value is call + check if value is callable and if not transform it + @param value: new value wrapped if constant -> lambda function ''' value_callable = lambda:value if callable(value): @@ -100,7 +137,8 @@ class CallableValue(object): def value(self): ''' shortcut to access inner value - @return: inner value(python type value) + main getter of value + @return: result of callable value ''' return self.__getValue__() @@ -108,6 +146,7 @@ class CallableValue(object): def value(self, value): ''' setter of value after check it + main setter of value @param value: new value encompass in valuetype object ''' self.__setValue__(value) @@ -122,7 +161,10 @@ class SimpleType(Type, CallableValue): @param structFormat: letter that represent type in struct package @param typeSize: size in byte of type @param signed: true if type represent a signed type - @param value: value recorded in this object + @param value: value recorded in this object (can be callable value which be call when is acces usefull with closure) + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading ''' self._signed = signed self._typeSize = typeSize @@ -132,8 +174,10 @@ class SimpleType(Type, CallableValue): def __getValue__(self): ''' - shortcut to access inner value - @return: inner value(python type value) + CallableValue overwrite + check mask type of value + use CallableValue access + @return: python value wrap into type @raise InvalidValue: if value doesn't respect type range ''' value = CallableValue.__getValue__(self) @@ -147,8 +191,9 @@ class SimpleType(Type, CallableValue): def __setValue__(self, value): ''' - setter of value after check it - @param value: new value encompass in simpletype object + CallableValue overwrite + check mask type of value + @param value: new value encompass in object (respect python type | lambda | function) @raise InvalidValue: if value doesn't respect type range ''' #check static value range @@ -304,6 +349,9 @@ class CompositeType(Type): ''' keep ordering declaration of simple type in list and transparent for other type + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading ''' def __init__(self, conditional = lambda:True, optional = False, constant = False): ''' @@ -376,6 +424,13 @@ class UInt8(SimpleType): unsigned byte ''' def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param value: python value wrap + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' SimpleType.__init__(self, "B", 1, False, value, conditional = conditional, optional = optional, constant = constant) class SInt8(SimpleType): @@ -383,6 +438,12 @@ class SInt8(SimpleType): signed byte ''' def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' SimpleType.__init__(self, "b", 1, True, value, conditional = conditional, optional = optional, constant = constant) @@ -393,6 +454,12 @@ class UInt16Be(SimpleType): Big endian is just for read or write in stream ''' def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' SimpleType.__init__(self, ">H", 2, False, value, conditional = conditional, optional = optional, constant = constant) class UInt16Le(SimpleType): @@ -402,6 +469,12 @@ class UInt16Le(SimpleType): Big endian is just for read or write in stream ''' def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' SimpleType.__init__(self, "I", 4, False, value, conditional = conditional, optional = optional, constant = constant) class UInt32Le(SimpleType): @@ -420,6 +499,12 @@ class UInt32Le(SimpleType): Big endian is just for read or write in stream ''' def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' SimpleType.__init__(self, "I", 4, True, value, conditional = conditional, optional = optional, constant = constant) class UInt24Be(SimpleType): @@ -447,6 +544,12 @@ class UInt24Be(SimpleType): Big endian is just for read or write in stream ''' def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' SimpleType.__init__(self, ">I", 3, False, value, conditional = conditional, optional = optional, constant = constant) def __write__(self, s): @@ -462,6 +565,12 @@ class UInt24Le(SimpleType): Big endian is just for read or write in stream ''' def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + ''' + constructor + @param conditional : function call before read or write type + @param optional: boolean check before read if there is still data in stream + @param constant: if true check any changement of object during reading + ''' SimpleType.__init__(self, " self.dataLen() and not value._optional: - raise InvalidSize("stream is too short to read non optional value") #read each tuple if isinstance(value, tuple): for element in value: self.readType(element) return + + #optional value not present + if self.dataLen() == 0 and value._optional: + return + value.read(self) def readNextType(self, t): diff --git a/rdpy/protocol/rdp/gcc.py b/rdpy/protocol/rdp/gcc.py index 4ebb572..590168d 100644 --- a/rdpy/protocol/rdp/gcc.py +++ b/rdpy/protocol/rdp/gcc.py @@ -18,6 +18,7 @@ class ServerToClientMessage(object): ''' Server to Client block gcc conference messages + @see: http://msdn.microsoft.com/en-us/library/cc240509.aspx ''' SC_CORE = 0x0C01 SC_SECURITY = 0x0C02 @@ -29,6 +30,7 @@ class ClientToServerMessage(object): ''' Client to Server block gcc conference messages + @see: http://msdn.microsoft.com/en-us/library/cc240509.aspx ''' CS_CORE = 0xC001 CS_SECURITY = 0xC002 @@ -41,6 +43,7 @@ class ClientToServerMessage(object): class ColorDepth(object): ''' depth color + @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx ''' RNS_UD_COLOR_8BPP = 0xCA01 RNS_UD_COLOR_16BPP_555 = 0xCA02 @@ -52,6 +55,7 @@ class ColorDepth(object): class HighColor(object): ''' high color of client + @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx ''' HIGH_COLOR_4BPP = 0x0004 HIGH_COLOR_8BPP = 0x0008 @@ -64,6 +68,7 @@ class HighColor(object): class Support(object): ''' support depth flag + @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx ''' RNS_UD_24BPP_SUPPORT = 0x0001 RNS_UD_16BPP_SUPPORT = 0x0002 @@ -74,8 +79,8 @@ class Support(object): @TypeAttributes(UInt16Le) class CapabilityFlags(object): ''' - @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx for more details on each flags click above + @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx ''' RNS_UD_CS_SUPPORT_ERRINFO_PDU = 0x0001 RNS_UD_CS_WANT_32BPP_SESSION = 0x0002 @@ -110,6 +115,7 @@ class ConnectionType(object): class Version(object): ''' supported version of RDP + @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx ''' RDP_VERSION_4 = 0x00080001 RDP_VERSION_5_PLUS = 0x00080004 @@ -125,6 +131,7 @@ class Encryption(object): ''' encryption method supported @deprecated: because rdpy use ssl but need to send to server... + @see: http://msdn.microsoft.com/en-us/library/cc240511.aspx ''' ENCRYPTION_FLAG_40BIT = 0x00000001 ENCRYPTION_FLAG_128BIT = 0x00000002 @@ -153,6 +160,7 @@ class ChannelOptions(object): class ClientCoreSettings(CompositeType): ''' class that represent core setting of client + @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx ''' def __init__(self): CompositeType.__init__(self) @@ -163,18 +171,18 @@ class ClientCoreSettings(CompositeType): self.sasSequence = Sequence.RNS_UD_SAS_DEL self.kbdLayout = UInt32Le(0x409) self.clientBuild = UInt32Le(3790) - self.clientName = UniString("rdpy" + "\x00"*11) + self.clientName = UniString("rdpy" + "\x00"*11, readLen = UInt8(30)) self.keyboardType = UInt32Le(4) self.keyboardSubType = UInt32Le(0) self.keyboardFnKeys = UInt32Le(12) - self.imeFileName = String("\x00"*64) + self.imeFileName = String("\x00"*64, readLen = UInt8(64)) self.postBeta2ColorDepth = ColorDepth.RNS_UD_COLOR_8BPP self.clientProductId = UInt16Le(1) self.serialNumber = UInt32Le(0) self.highColorDepth = HighColor.HIGH_COLOR_24BPP self.supportedColorDepths = Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_15BPP_SUPPORT self.earlyCapabilityFlags = CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU - self.clientDigProductId = String("\x00"*64) + self.clientDigProductId = String("\x00"*64, readLen = UInt8(64)) self.connectionType = UInt8() self.pad1octet = UInt8() self.serverSelectedProtocol = UInt32Le() @@ -182,6 +190,7 @@ class ClientCoreSettings(CompositeType): class ServerCoreSettings(CompositeType): ''' server side core settings structure + @see: http://msdn.microsoft.com/en-us/library/cc240517.aspx ''' def __init__(self): CompositeType.__init__(self) @@ -192,6 +201,7 @@ class ClientSecuritySettings(CompositeType): ''' client security setting @deprecated: because we use ssl + @see: http://msdn.microsoft.com/en-us/library/cc240511.aspx ''' def __init__(self): CompositeType.__init__(self) @@ -204,6 +214,7 @@ class ServerSecuritySettings(CompositeType): may be ignore because rdpy don't use RDP security level @deprecated: because we use ssl + @see: http://msdn.microsoft.com/en-us/library/cc240518.aspx ''' def __init__(self): CompositeType.__init__(self) @@ -215,11 +226,13 @@ class ClientRequestedChannel(CompositeType): ''' channels structure share between client and server + @see: http://msdn.microsoft.com/en-us/library/cc240512.aspx + @see: http://msdn.microsoft.com/en-us/library/cc240513.aspx ''' def __init__(self, name = "", options = UInt32Le()): CompositeType.__init__(self) #name of channel - self.name = String(name[0:8] + "\x00" * (8 - len(name))) + self.name = String(name[0:8] + "\x00" * (8 - len(name)), readLen = UInt8(8)) #unknown self.options = options @@ -355,6 +368,7 @@ def readServerSecurityData(s): read all channels accepted by server by server @param s: Stream @return: list of channel id selected by server + @see: http://msdn.microsoft.com/en-us/library/cc240522.aspx ''' channelsId = [] channelId = UInt16Le() diff --git a/rdpy/protocol/rdp/gdl.py b/rdpy/protocol/rdp/gdl.py index 2facba4..9b342ac 100644 --- a/rdpy/protocol/rdp/gdl.py +++ b/rdpy/protocol/rdp/gdl.py @@ -8,6 +8,7 @@ from rdpy.utils.const import ConstAttributes, TypeAttributes from rdpy.protocol.network.error import InvalidExpectedDataException import gcc +import lic @ConstAttributes @TypeAttributes(UInt16Le) @@ -70,7 +71,7 @@ class RDPInfo(CompositeType): client informations contains credentials (very important packet) ''' - def __init__(self, initForWrite, extendedInfoConditional): + def __init__(self, extendedInfoConditional): CompositeType.__init__(self) #code page self.codePage = UInt32Le() @@ -86,37 +87,36 @@ class RDPInfo(CompositeType): self.cbAlternateShell = UInt16Le(lambda:sizeof(self.alternateShell) - 2) #length of working directory unistring less 2 byte null terminate self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2) - #to avoid recurcive loop init differ from reading and writing #microsoft domain - self.domain = UniString("" if initForWrite else lambda:"\x00" * self.cbDomain.value) + self.domain = UniString(readLen = self.cbDomain) #session username - self.userName = UniString("" if initForWrite else lambda:"\x00" * self.cbUserName.value) + self.userName = UniString(readLen = self.cbUserName) #associate password - self.password = UniString("" if initForWrite else lambda:"\x00" * self.cbPassword.value) + self.password = UniString(readLen = self.cbPassword) #shell execute at start of session - self.alternateShell = UniString("" if initForWrite else lambda:"\x00" * self.cbAlternateShell.value) + self.alternateShell = UniString(readLen = self.cbAlternateShell) #working directory for session - self.workingDir = UniString("" if initForWrite else lambda:"\x00" * self.cbWorkingDir.value) + self.workingDir = UniString(readLen = self.cbWorkingDir) #more client informations - self.extendedInfo = RDPExtendedInfo(initForWrite, conditional = extendedInfoConditional) + self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional) class RDPExtendedInfo(CompositeType): ''' add more client informations use for performance flag!!! ''' - def __init__(self, initForWrite, conditional): + def __init__(self, conditional): CompositeType.__init__(self, conditional = conditional) #is an ip v4 or v6 adresse self.clientAddressFamily = AfInet.AF_INET #len of adress field self.cbClientAddress = UInt16Le(lambda:sizeof(self.clientAddress)) #adress of client - self.clientAddress = UniString("" if initForWrite else lambda:"\x00" * self.cbClientAddress.value) + self.clientAddress = UniString(readLen = self.cbClientAddress) #len of client directory self.cbClientDir = UInt16Le(lambda:sizeof(self.clientDir)) #self client directory - self.clientDir = UniString("" if initForWrite else lambda:"\x00" * self.cbClientDir.value) + self.clientDir = UniString(readLen = self.cbClientDir) #TODO make tiomezone #self.performanceFlags = PerfFlag.PERF_DISABLE_WALLPAPER | PerfFlag.PERF_DISABLE_MENUANIMATIONS | PerfFlag.PERF_DISABLE_CURSOR_SHADOW @@ -134,7 +134,7 @@ class GDL(LayerAutomata): #set by mcs layer channel init self._channelId = UInt16Be() #logon info send from client to server - self._info = RDPInfo(initForWrite = True, extendedInfoConditional = lambda:self._transport._serverSettings.core.rdpVersion == gcc.Version.RDP_VERSION_5_PLUS) + self._info = RDPInfo(extendedInfoConditional = lambda:self._transport._serverSettings.core.rdpVersion == gcc.Version.RDP_VERSION_5_PLUS) def connect(self): ''' @@ -147,7 +147,7 @@ class GDL(LayerAutomata): def sendInfoPkt(self): ''' - send a logon info packet for RDP version 5 protocol + send a logon info packet ''' #always send extended info because rdpy only accept rdp version 5 and more self._transport.send(self._channelId, (SecurityFlag.SEC_INFO_PKT, UInt16Le(), self._info)) @@ -158,4 +158,13 @@ class GDL(LayerAutomata): data.readType((securityFlag, securityFlagHi)) if securityFlag & SecurityFlag.SEC_LICENSE_PKT != SecurityFlag.SEC_LICENSE_PKT: - raise InvalidExpectedDataException("waiting license packet") \ No newline at end of file + raise InvalidExpectedDataException("waiting license packet") + + validClientPdu = lic.LicPacket() + data.readType(validClientPdu) + + if not validClientPdu.errorMessage._is_readed: + raise InvalidExpectedDataException("waiting valid client pdu : rdpy doesn't support licensing neg") + + if not (validClientPdu.errorMessage.dwErrorCode == lic.ErrorCode.STATUS_VALID_CLIENT and validClientPdu.errorMessage.dwStateTransition == lic.StateTransition.ST_NO_TRANSITION): + raise InvalidExpectedDataException("server refuse licensing negotiation") \ No newline at end of file diff --git a/rdpy/protocol/rdp/lic.py b/rdpy/protocol/rdp/lic.py index 9259774..653609c 100644 --- a/rdpy/protocol/rdp/lic.py +++ b/rdpy/protocol/rdp/lic.py @@ -1,12 +1,15 @@ ''' @author: sylvain ''' -from rdpy.protocol.network.type import CompositeType, UInt8, UInt16Le, sizeof +from rdpy.protocol.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof from rdpy.utils.const import ConstAttributes, TypeAttributes @ConstAttributes @TypeAttributes(UInt8) class MessageType(object): + ''' + License packet message type + ''' LICENSE_REQUEST = 0x01 PLATFORM_CHALLENGE = 0x02 NEW_LICENSE = 0x03 @@ -15,11 +18,54 @@ class MessageType(object): NEW_LICENSE_REQUEST = 0x13 PLATFORM_CHALLENGE_RESPONSE = 0x15 ERROR_ALERT = 0xFF + +@ConstAttributes +@TypeAttributes(UInt32Le) +class ErrorCode(object): + ''' + license error message code + ''' + ERR_INVALID_SERVER_CERTIFICATE = 0x00000001 + ERR_NO_LICENSE = 0x00000002 + ERR_INVALID_SCOPE = 0x00000004 + ERR_NO_LICENSE_SERVER = 0x00000006 + STATUS_VALID_CLIENT = 0x00000007 + ERR_INVALID_CLIENT = 0x00000008 + ERR_INVALID_PRODUCTID = 0x0000000B + ERR_INVALID_MESSAGE_LEN = 0x0000000C + ERR_INVALID_MAC = 0x00000003 + +@ConstAttributes +@TypeAttributes(UInt32Le) +class StateTransition(object): + ''' + automata state transition + ''' + ST_TOTAL_ABORT = 0x00000001 + ST_NO_TRANSITION = 0x00000002 + ST_RESET_PHASE_TO_START = 0x00000003 + ST_RESEND_LAST_MESSAGE = 0x00000004 + +class LicenceBinaryBlob(CompositeType): + def __init__(self): + CompositeType.__init__(self) + self.wBlobType = UInt16Le() + self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData)) + self.blobData = String(readLen = self.wBlobLen, conditional = lambda:self.wBlobLen.value > 0) + +class LicensingErrorMessage(CompositeType): + def __init__(self, conditional = lambda:True): + CompositeType.__init__(self, conditional = conditional) + self.dwErrorCode = UInt32Le() + self.dwStateTransition = UInt32Le() + self.blob = LicenceBinaryBlob() class LicPacket(CompositeType): def __init__(self): + CompositeType.__init__(self) #preambule self.bMsgtype = UInt8() self.flag = UInt8() self.wMsgSize = UInt16Le(lambda: sizeof(self)) + self.errorMessage = LicensingErrorMessage(conditional = lambda:self.bMsgtype == MessageType.ERROR_ALERT) \ No newline at end of file diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py index 032be4e..2cc0a85 100644 --- a/rdpy/protocol/rdp/tpdu.py +++ b/rdpy/protocol/rdp/tpdu.py @@ -77,6 +77,9 @@ class TPDUDataHeader(CompositeType): class Negotiation(CompositeType): ''' negociation request message + @see: request -> http://msdn.microsoft.com/en-us/library/cc240500.aspx + @see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx + @see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx ''' def __init__(self, optional = False): CompositeType.__init__(self, optional = optional) @@ -118,6 +121,8 @@ class TPDU(LayerAutomata): next state is recvData call connect on presentation layer if all is good @param data: Stream that contain connection confirm + @see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx + @see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx ''' message = TPDUConnectMessage() data.readType(message) @@ -161,6 +166,7 @@ class TPDU(LayerAutomata): ''' write connection request message next state is recvConnectionConfirm + @see: http://msdn.microsoft.com/en-us/library/cc240500.aspx ''' message = TPDUConnectMessage() message.code = MessageType.X224_TPDU_CONNECTION_REQUEST diff --git a/rdpy/protocol/rfb/rfb.py b/rdpy/protocol/rfb/rfb.py index 7da3a5e..9ce12bf 100644 --- a/rdpy/protocol/rfb/rfb.py +++ b/rdpy/protocol/rfb/rfb.py @@ -134,8 +134,6 @@ class Rfb(RawLayer): server imposed security level ''' #TODO!!! - self._version = data.read_beuint32() - def recvSecurityList(self, data): '''