diff --git a/bin/rdpy-rdpproxy b/bin/rdpy-rdpproxy index c5fb3e2..5774d02 100755 --- a/bin/rdpy-rdpproxy +++ b/bin/rdpy-rdpproxy @@ -11,7 +11,7 @@ from rdpy.protocol.rdp import rdp class TestServerFactory(rdp.ServerFactory): def __init__(self): - rdp.ServerFactory.__init__(self, "/home/sylvain/dev/certificate/rdpy.key", "/home/sylvain/dev/certificate/rdpy.crt") + rdp.ServerFactory.__init__(self, "/home/speyrefitte/dev/certificate/rdpy.key", "/home/speyrefitte/dev/certificate/rdpy.crt") def startedConnecting(self, connector): pass diff --git a/rdpy/network/type.py b/rdpy/network/type.py index 7546179..fd76c7b 100644 --- a/rdpy/network/type.py +++ b/rdpy/network/type.py @@ -439,6 +439,9 @@ class CompositeType(Type): break s.pos -= sizeof(self.__dict__[tmpName]) raise e + if not self._readLen is None and readLen < self._readLen.value: + print "WARNING : still have correct data in packet %s, read it as padding"%self.__class__ + s.read(self._readLen.value - readLen) def __write__(self, s): """ @@ -453,10 +456,10 @@ class CompositeType(Type): raise e def __sizeof__(self): - ''' - call sizeof on each subtype + """ + Call sizeof on each sub type @return: sum of sizeof of each public type attributes - ''' + """ size = 0 for name in self._typeName: size += sizeof(self.__dict__[name]) @@ -665,7 +668,7 @@ class String(Type, CallableValue): ''' String network type ''' - def __init__(self, value = "", readLen = UInt32Le(), conditional = lambda:True, optional = False, constant = False): + def __init__(self, value = "", readLen = UInt32Le(), conditional = lambda:True, optional = False, constant = False, unicode = False): ''' constructor with new string @param value: python string use for inner value @@ -673,11 +676,13 @@ class String(Type, CallableValue): @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 + @param unicode: Encode and decode value as unicode ''' Type.__init__(self, conditional = conditional, optional = optional, constant = constant) CallableValue.__init__(self, value) #type use to know read length self._readLen = readLen + self._unicode = unicode def __eq__(self, other): ''' @@ -702,52 +707,60 @@ class String(Type, CallableValue): return self.value def __write__(self, s): - ''' - write the entire raw value + """ + Write the entire raw value @param s: Stream - ''' - s.write(self.value) + """ + if self._unicode: + s.write(encodeUnicode(self.value)) + else: + s.write(self.value) def __read__(self, s): - ''' - read all stream if len of inner value is zero - else read the len of inner string + """ + read all stream if length of inner value is zero + else read the length of inner string @param s: Stream - ''' + """ if self._readLen.value == 0: self.value = s.getvalue() else: self.value = s.read(self._readLen.value) - def __sizeof__(self): - ''' - return len of string - @return: len of inner string - ''' - return len(self.value) - -class UniString(String): - ''' - string with unicode representation - ''' - def write(self, s): - ''' - separate each char with null char - and end with double null char - @param s: Stream - ''' - for c in self.value: - s.writeType(UInt8(ord(c))) - s.writeType(UInt8(0)) - s.writeType(UInt16Le(0)) + if self._unicode: + self.value = decodeUnicode(self.value) def __sizeof__(self): - ''' - return len of uni string - @return: 2*len + 2 - ''' - return len(self.value) * 2 + 2 + """ + return length of string + @return: length of inner string + """ + if self._unicode: + return 2 * len(self.value) + 2 + else: + return len(self.value) +def encodeUnicode(s): + """ + Encode string in unicode + @param s: str python + @return: unicode string + """ + return "".join([c + "\x00" for c in s]) + "\x00\x00" + +def decodeUnicode(s): + """ + Decode Unicode string + @param s: unicode string + @return: str python + """ + i = 0 + r = "" + while i < len(s) - 2: + if i % 2 == 0: + r += s[i] + i += 1 + return r class Stream(StringIO): ''' @@ -849,6 +862,8 @@ class ArrayType(Type): element = self._typeFactory() element._optional = self._readLen is None s.readType(element) + if not element._is_readed: + break self._array.append(element) i += 1 diff --git a/rdpy/protocol/rdp/ber.py b/rdpy/protocol/rdp/ber.py index 40d61f8..32af834 100644 --- a/rdpy/protocol/rdp/ber.py +++ b/rdpy/protocol/rdp/ber.py @@ -71,7 +71,7 @@ def readLength(s): length = UInt8() s.readType(length) byte = length.value - if (byte & 0x80): + if byte & 0x80: byte &= ~0x80 if byte == 1: size = UInt8() @@ -166,7 +166,7 @@ def writeBoolean(b): """ Return structure that represent boolean in BER specification @param b: boolean - @return: BER boolean structure + @return: BER boolean block """ boolean = UInt8(0) if b: @@ -209,7 +209,7 @@ def writeInteger(value): """ Write integer value @param param: INT or Python long - @return: BER integer structure + @return: BER integer block """ if value <= 0xff: return (writeUniversalTag(Tag.BER_TAG_INTEGER, False), writeLength(1), UInt8(value)) @@ -233,7 +233,7 @@ def writeOctetstring(value): """ Write string in BER representation @param value: string - @return: string BER structure + @return: BER octet string block """ return (writeUniversalTag(Tag.BER_TAG_OCTET_STRING, False), writeLength(len(value)), String(value)) @@ -250,3 +250,11 @@ def readEnumerated(s): enumer = UInt8() s.readType(enumer) return enumer.value + +def writeEnumerated(enumerated): + """ + Write enumerated structure + @param s: Stream + @return: BER enumerated block + """ + return (writeUniversalTag(Tag.BER_TAG_ENUMERATED, False), writeLength(1), UInt8(enumerated)) \ No newline at end of file diff --git a/rdpy/protocol/rdp/caps.py b/rdpy/protocol/rdp/caps.py index e04d3ff..c51f8a9 100644 --- a/rdpy/protocol/rdp/caps.py +++ b/rdpy/protocol/rdp/caps.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +from rdpy.network.error import InvalidExpectedDataException """ Definition of structure use for capabilities nego @@ -233,43 +234,17 @@ class Capability(CompositeType): """ Closure for capability factory """ - if self.capabilitySetType.value == CapsType.CAPSTYPE_GENERAL: - return GeneralCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_BITMAP: - return BitmapCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_ORDER: - return OrderCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_BITMAPCACHE: - return BitmapCacheCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_POINTER: - return PointerCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_INPUT: - return InputCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_BRUSH: - return BrushCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_GLYPHCACHE: - return GlyphCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_OFFSCREENCACHE: - return OffscreenBitmapCacheCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_VIRTUALCHANNEL: - return VirtualChannelCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_SOUND: - return SoundCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_CONTROL: - return ControlCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_ACTIVATION: - return WindowActivationCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_FONT: - return FontCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_COLORCACHE: - return ColorCacheCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - elif self.capabilitySetType.value == CapsType.CAPSTYPE_SHARE: - return ShareCapability(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) - else: - return String(readLen = UInt16Le(lambda:self.lengthCapability.value - 4)) + for c in [GeneralCapability, BitmapCapability, OrderCapability, BitmapCacheCapability, PointerCapability, InputCapability, BrushCapability, GlyphCapability, OffscreenBitmapCacheCapability, VirtualChannelCapability, SoundCapability, ControlCapability, WindowActivationCapability, FontCapability, ColorCacheCapability, ShareCapability]: + if self.capabilitySetType.value == c._TYPE_: + return c(readLen = self.lengthCapability - 4) + print "WARNING : unknown Capability type : %s"%hex(self.capabilitySetType.value) + #read entire packet + return String(readLen = self.lengthCapability - 4) if capability is None: capability = FactoryType(CapabilityFactory) + elif not "_TYPE_" in capability.__class__.__dict__: + raise InvalidExpectedDataException("Try to send an invalid capability block") self.capability = capability @@ -280,6 +255,8 @@ class GeneralCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240549.aspx """ + _TYPE_ = CapsType.CAPSTYPE_GENERAL + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.osMajorType = UInt16Le() @@ -301,6 +278,8 @@ class BitmapCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240554.aspx """ + _TYPE_ = CapsType.CAPSTYPE_BITMAP + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.preferredBitsPerPixel = UInt16Le() @@ -324,6 +303,8 @@ class OrderCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240556.aspx """ + _TYPE_ = CapsType.CAPSTYPE_ORDER + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.terminalDescriptor = String("\x00" * 16, readLen = UInt8(16)) @@ -350,6 +331,8 @@ class BitmapCacheCapability(CompositeType): client -> server @see: http://msdn.microsoft.com/en-us/library/cc240559.aspx """ + _TYPE_ = CapsType.CAPSTYPE_BITMAPCACHE + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.pad1 = UInt32Le() @@ -373,6 +356,8 @@ class PointerCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240562.aspx """ + _TYPE_ = CapsType.CAPSTYPE_POINTER + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.colorPointerFlag = UInt16Le() @@ -386,6 +371,8 @@ class InputCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240563.aspx """ + _TYPE_ = CapsType.CAPSTYPE_INPUT + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.inputFlags = UInt16Le() @@ -407,6 +394,8 @@ class BrushCapability(CompositeType): client -> server @see: http://msdn.microsoft.com/en-us/library/cc240564.aspx """ + _TYPE_ = CapsType.CAPSTYPE_BRUSH + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.brushSupportLevel = UInt32Le(BrushSupport.BRUSH_DEFAULT) @@ -417,6 +406,8 @@ class GlyphCapability(CompositeType): client -> server @see: http://msdn.microsoft.com/en-us/library/cc240565.aspx """ + _TYPE_ = CapsType.CAPSTYPE_GLYPHCACHE + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.glyphCache = ArrayType(CacheEntry, init = [CacheEntry() for _ in range(0,10)], readLen = UInt8(10)) @@ -431,6 +422,8 @@ class OffscreenBitmapCacheCapability(CompositeType): client -> server @see: http://msdn.microsoft.com/en-us/library/cc240550.aspx """ + _TYPE_ = CapsType.CAPSTYPE_OFFSCREENCACHE + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.offscreenSupportLevel = UInt32Le(OffscreenSupportLevel.FALSE) @@ -444,6 +437,8 @@ class VirtualChannelCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240551.aspx """ + _TYPE_ = CapsType.CAPSTYPE_VIRTUALCHANNEL + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.flags = UInt32Le(VirtualChannelCompressionFlag.VCCAPS_NO_COMPR) @@ -455,6 +450,8 @@ class SoundCapability(CompositeType): client -> server @see: http://msdn.microsoft.com/en-us/library/cc240552.aspx """ + _TYPE_ = CapsType.CAPSTYPE_SOUND + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.soundFlags = UInt16Le(SoundFlag.NONE) @@ -465,6 +462,8 @@ class ControlCapability(CompositeType): client -> server but server ignore contents! Thanks krosoft for brandwidth @see: http://msdn.microsoft.com/en-us/library/cc240568.aspx """ + _TYPE_ = CapsType.CAPSTYPE_CONTROL + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.controlFlags = UInt16Le() @@ -477,6 +476,8 @@ class WindowActivationCapability(CompositeType): client -> server but server ignore contents! Thanks krosoft for brandwidth @see: http://msdn.microsoft.com/en-us/library/cc240569.aspx """ + _TYPE_ = CapsType.CAPSTYPE_ACTIVATION + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.helpKeyFlag = UInt16Le() @@ -491,6 +492,8 @@ class FontCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240571.aspx """ + _TYPE_ = CapsType.CAPSTYPE_FONT + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.fontSupportFlags = UInt16Le(0x0001) @@ -502,6 +505,8 @@ class ColorCacheCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc241564.aspx """ + _TYPE_ = CapsType.CAPSTYPE_COLORCACHE + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.colorTableCacheSize = UInt16Le(0x0006) @@ -514,6 +519,8 @@ class ShareCapability(CompositeType): server -> client @see: http://msdn.microsoft.com/en-us/library/cc240570.aspx """ + _TYPE_ = CapsType.CAPSTYPE_SHARE + def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) self.nodeId = UInt16Le() diff --git a/rdpy/protocol/rdp/gcc.py b/rdpy/protocol/rdp/gcc.py index 703550a..735da1f 100644 --- a/rdpy/protocol/rdp/gcc.py +++ b/rdpy/protocol/rdp/gcc.py @@ -22,8 +22,7 @@ Implement GCC structure use in RDP protocol http://msdn.microsoft.com/en-us/library/cc240508.aspx """ -from rdpy.network.type import UInt8, UInt16Le, UInt32Le, CompositeType, String, UniString, Stream, sizeof, FactoryType,\ - ArrayType +from rdpy.network.type import UInt8, UInt16Le, UInt32Le, CompositeType, String, Stream, sizeof, FactoryType, ArrayType import per from rdpy.network.error import InvalidExpectedDataException @@ -204,10 +203,10 @@ class DataBlock(CompositeType): """ for c in [ClientCoreData, ClientSecurityData, ClientNetworkData, ServerCoreData, ServerNetworkData, ServerSecurityData]: if self.type.value == c._TYPE_: - return c() - print "WARNING : unknown GCC block type : %s"%self.type.value + return c(readLen = self.length - 4) + print "WARNING : unknown GCC block type : %s"%hex(self.type.value) #read entire packet - return String(readLen = self.length) + return String(readLen = self.length - 4) if dataBlock is None: dataBlock = FactoryType(DataBlockFactory) @@ -223,8 +222,8 @@ class ClientCoreData(CompositeType): """ _TYPE_ = MessageType.CS_CORE - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS) self.desktopWidth = UInt16Le(1280) self.desktopHeight = UInt16Le(800) @@ -232,7 +231,7 @@ class ClientCoreData(CompositeType): self.sasSequence = UInt16Le(Sequence.RNS_UD_SAS_DEL) self.kbdLayout = UInt32Le(KeyboardLayout.FRENCH) self.clientBuild = UInt32Le(3790) - self.clientName = UniString("rdpy" + "\x00"*11, readLen = UInt8(30)) + self.clientName = String("rdpy" + "\x00"*11, readLen = UInt8(32), unicode = True) self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS) self.keyboardSubType = UInt32Le(0) self.keyboardFnKeys = UInt32Le(12) @@ -255,8 +254,8 @@ class ServerCoreData(CompositeType): """ _TYPE_ = MessageType.SC_CORE - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS) self.clientRequestedProtocol = UInt32Le() @@ -268,8 +267,8 @@ class ClientSecurityData(CompositeType): """ _TYPE_ = MessageType.CS_SECURITY - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) self.encryptionMethods = UInt32Le() self.extEncryptionMethods = UInt32Le() @@ -283,8 +282,8 @@ class ServerSecurityData(CompositeType): """ _TYPE_ = MessageType.SC_SECURITY - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) self.encryptionMethod = UInt32Le() self.encryptionLevel = UInt32Le() @@ -309,8 +308,8 @@ class ClientNetworkData(CompositeType): """ _TYPE_ = MessageType.CS_NET - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) self.channelCount = UInt32Le() self.channelDefArray = ArrayType(ChannelDef, readLen = self.channelCount) @@ -322,19 +321,19 @@ class ServerNetworkData(CompositeType): """ _TYPE_ = MessageType.SC_NET - def __init__(self): - CompositeType.__init__(self) + def __init__(self, readLen = None): + CompositeType.__init__(self, readLen = readLen) self.MCSChannelId = UInt16Le() self.channelCount = UInt16Le(lambda:len(self.channelIdArray._array)) self.channelIdArray = ArrayType(UInt16Le, readLen = self.channelCount) - self.pad = UInt16Le(conditional = lambda:(self.channelCount.value % 2 == 1)) + self.pad = UInt16Le(conditional = lambda:((self.channelCount.value % 2) == 1)) class Settings(CompositeType): """ Class which group all clients settings supported by RDPY """ - def __init__(self, init = []): - CompositeType.__init__(self) + def __init__(self, init = [], readLen = None): + CompositeType.__init__(self, readLen = readLen) self.settings = ArrayType(DataBlock, [DataBlock(i) for i in init]) def getBlock(self, messageType): @@ -347,18 +346,6 @@ class Settings(CompositeType): return i.dataBlock return None -class ServerSettings(object): - """ - Server settings - """ - def __init__(self): - #core settings of server - self.core = ServerCoreData() - #unuse security informations - self.security = ServerSecurityData() - #channel id accepted by server - self.channelsId = [] - def clientSettings(): """ Build settings for client @@ -371,13 +358,14 @@ def serverSettings(): Build settings for server @return Settings """ - return Settings([ServerCoreData(), ServerSecurityData(), ServerSecurityData()]) + return Settings([ServerCoreData(), ServerSecurityData(), ServerNetworkData()]) def readConferenceCreateRequest(s): """ Read a response from client GCC create request @param s: Stream + @param client settings (Settings) """ per.readChoice(s) per.readObjectIdentifier(s, t124_02_98_oid) @@ -394,6 +382,10 @@ def readConferenceCreateRequest(s): raise InvalidExpectedDataException("Invalid choice in readConferenceCreateRequest") per.readOctetStream(s, h221_cs_key, 4) + length = per.readLength(s) + clientSettings = Settings(readLen = UInt32Le(length)) + s.readType(clientSettings) + return clientSettings def readConferenceCreateResponse(s): """ @@ -413,59 +405,11 @@ def readConferenceCreateResponse(s): per.readChoice(s) if not per.readOctetStream(s, h221_sc_key, 4): raise InvalidExpectedDataException("cannot read h221_sc_key") - #serverSettings = Settings() - #s.readType(serverSettings) - #return serverSettings - return readServerDataBlocks(s) -def readServerDataBlocks(s): - """ - Read GCC server data blocks - And return result in Server Settings object - @param s: Stream - @return: ServerSettings - """ - settings = ServerSettings() length = per.readLength(s) - while length > 0: - marker = s.readLen() - blockType = UInt16Le() - blockLength = UInt16Le() - s.readType((blockType, blockLength)) - #read core block - if blockType.value == MessageType.SC_CORE: - s.readType(settings.core) - #read network block - elif blockType.value == MessageType.SC_NET: - settings.channelsId = readServerSecurityData(s) - #read security block - #unused in rdpy because use SSL layer - elif blockType.value == MessageType.SC_SECURITY: - s.readType(settings.security) - else: - print "Unknown server block %s"%hex(type) - length -= blockLength.value - s.seek(marker + blockLength.value) - - return settings - -def readServerSecurityData(s): - """ - Read server security and fill it in settings - 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() - numberOfChannels = UInt16Le() - s.readType((channelId, numberOfChannels)) - for _ in range(0, numberOfChannels.value): - channelId = UInt16Le() - s.readType(channelId) - channelsId.append(channelId) - return channelsId + serverSettings = Settings(readLen = UInt32Le(length)) + s.readType(serverSettings) + return serverSettings def writeConferenceCreateRequest(userData): """ @@ -482,20 +426,17 @@ def writeConferenceCreateRequest(userData): per.writeNumberOfSet(1), per.writeChoice(0xc0), per.writeOctetStream(h221_cs_key, 4), per.writeOctetStream(userDataStream.getvalue())) -def writeConferenceCreateResponse(settings): +def writeConferenceCreateResponse(serverData): """ Write a conference create response packet - @param settings: ServerSettingsDataBlock + @param serverData: Settings for server @return: gcc packet """ - pass + serverDataStream = Stream() + serverDataStream.writeType(serverData) -def writeClientDataBlocks(settings): - """ - Write all blocks for client - and return GCC valid structure - @param settings: ClientSettings - """ - return (DataBlock(settings.core), - DataBlock(settings.security), - DataBlock(settings.network)) \ No newline at end of file + return (per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid), + per.writeLength(len(serverDataStream.getvalue()) + 14), per.writeChoice(0x14), + per.writeInteger16(0x79F3, 1001), per.writeInteger(1), per.writeEnumerates(16), + per.writeNumberOfSet(1), per.writeChoice(0xc0), + per.writeOctetStream(h221_sc_key, 4), per.writeOctetStream(serverDataStream.getvalue())) \ No newline at end of file diff --git a/rdpy/protocol/rdp/mcs.py b/rdpy/protocol/rdp/mcs.py index 4f4ae47..e117456 100644 --- a/rdpy/protocol/rdp/mcs.py +++ b/rdpy/protocol/rdp/mcs.py @@ -25,7 +25,7 @@ The main channel is the graphical channel. It exist channel for file system order, audio channel, clipboard etc... """ from rdpy.network.layer import LayerAutomata, StreamSender, Layer, LayerMode -from rdpy.network.type import sizeof, Stream, UInt8, UInt16Be +from rdpy.network.type import sizeof, Stream, UInt8, UInt16Le from rdpy.network.error import InvalidExpectedDataException, InvalidValue, InvalidSize from rdpy.protocol.rdp.ber import writeLength @@ -60,7 +60,7 @@ class Channel: class MCS(LayerAutomata): """ - Multi Channel Service layer + Multiple Channel Service layer the main layer of RDP protocol is why he can do everything and more! """ @@ -122,13 +122,13 @@ class MCS(LayerAutomata): """ LayerAutomata.__init__(self, mode, presentation) self._clientSettings = gcc.clientSettings() - self._serverSettings = gcc.ServerSettings() + self._serverSettings = gcc.serverSettings() #default user Id - self._userId = 1 + self._userId = 1 + Channel.MCS_USERCHANNEL_BASE #list of channel use in this layer and connection state self._channelIds = {Channel.MCS_GLOBAL_CHANNEL: presentation} #use to record already requested channel - self._channelIdsRequest = {} + self._channelIdsRequested = {} def connect(self): """ @@ -143,27 +143,29 @@ class MCS(LayerAutomata): def connectNextChannel(self): """ - Send sendChannelJoinRequest message on next unconnect channel + Send sendChannelJoinRequest message on next disconnect channel + client automata function """ for (channelId, layer) in self._channelIds.iteritems(): - #for each unconnect channel send a request - if not self._channelIdsRequest.has_key(channelId): + #for each disconnect channel send a request + if not self._channelIdsRequested.has_key(channelId): self.sendChannelJoinRequest(channelId) self.setNextState(self.recvChannelJoinConfirm) return - #connection is done reinit class + #connection is done self.setNextState(self.recvData) #try connection on all requested channel for (channelId, layer) in self._channelIds.iteritems(): - if self._channelIdsRequest[channelId] and not layer is None: - #use proxy foreach channell + if self._channelIdsRequested[channelId] and not layer is None: + #use proxy for each channel layer._transport = MCS.MCSProxySender(self, channelId) layer.connect() def sendConnectInitial(self): """ Send connect initial packet + client automata function """ ccReq = gcc.writeConferenceCreateRequest(self._clientSettings) ccReqStream = Stream() @@ -179,25 +181,66 @@ class MCS(LayerAutomata): self.setNextState(self.recvConnectResponse) def sendConnectResponse(self): - pass + """ + Send connect response + server automata function + """ + ccReq = gcc.writeConferenceCreateResponse(self._serverSettings) + ccReqStream = Stream() + ccReqStream.writeType(ccReq) + + tmp = (ber.writeEnumerated(0), ber.writeInteger(0), self.writeDomainParams(22, 3, 0, 0xfff8), + ber.writeOctetstring(ccReqStream.getvalue())) + self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_RESPONSE, sizeof(tmp)), tmp)) + + self.setNextState(self.recvErectDomainRequest) def sendErectDomainRequest(self): """ Send a formated erect domain request for RDP connection + client automata function """ - self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)), per.writeInteger(0), per.writeInteger(0))) + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)), + per.writeInteger(0), + per.writeInteger(0))) def sendAttachUserRequest(self): """ Send a formated attach user request for RDP connection + client automata function """ self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST))) + def sendAttachUserConfirm(self): + """ + Send attach user confirm + server automata function + """ + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM)), + per.writeEnumerates(0), + per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE))) + def sendChannelJoinRequest(self, channelId): """ Send a formated Channel join request from client to server + client automata function + @param channelId: id of channel requested """ - self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)), UInt16Be(self._userId), UInt16Be(channelId))) + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)), + per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), + per.writeInteger16(channelId))) + + def sendChannelJoinConfirm(self, channelId, confirm): + """ + Send a confirm channel (or not) to client + @param channelId: id of channel + @param confirm: connection state + """ + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM)), + per.writeEnumerates(int(confirm)), + per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), + per.writeInteger16(channelId), + per.writeInteger16(channelId))) def send(self, channelId, data): """ @@ -205,11 +248,16 @@ class MCS(LayerAutomata): @param channelId: Channel use to send @param data: message to send """ - self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.SEND_DATA_REQUEST)), UInt16Be(self._userId), UInt16Be(channelId), UInt8(0x70), UInt16Be(sizeof(data)) | UInt16Be(0x8000), data)) + self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.SEND_DATA_REQUEST)), + per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), + per.writeInteger16(channelId), + UInt8(0x70), + per.writeLength(sizeof(data)), data)) def recvConnectInitial(self, data): """ Receive MCS connect initial from client + server automata function @param data: Stream """ ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_INITIAL)) @@ -222,11 +270,16 @@ class MCS(LayerAutomata): self.readDomainParams(data) self.readDomainParams(data) self.readDomainParams(data) - gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data))) + self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data))) + + self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray._array = [UInt16Le(x + Channel.MCS_GLOBAL_CHANNEL) for x in range(1, len(self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array) + 1)] + + self.sendConnectResponse() def recvConnectResponse(self, data): """ Receive MCS connect response from server + client automata function @param data: Stream """ ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE)) @@ -237,7 +290,7 @@ class MCS(LayerAutomata): raise InvalidExpectedDataException("invalid expected BER tag") gccRequestLength = ber.readLength(data) if data.dataLen() != gccRequestLength: - raise InvalidSize("bad size of gcc request") + raise InvalidSize("bad size of GCC request") self._serverSettings = gcc.readConferenceCreateResponse(data) #send domain request @@ -247,49 +300,102 @@ class MCS(LayerAutomata): #now wait user confirm from server self.setNextState(self.recvAttachUserConfirm) - def recvAttachUserConfirm(self, data): + def recvErectDomainRequest(self, data): """ - Receive an attach user confirm + Receive erect domain request + server automata function @param data: Stream """ opcode = UInt8() - confirm = UInt8() - userId = UInt16Be() - data.readType((opcode, confirm)) + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ERECT_DOMAIN_REQUEST): + raise InvalidExpectedDataException("Invalid MCS PDU : ERECT_DOMAIN_REQUEST expected") + + per.readInteger(data) + per.readInteger(data) + + self.setNextState(self.recvAttachUserRequest) + + def recvAttachUserRequest(self, data): + """ + Receive Attach user request + server automata function + @param data: Stream + """ + opcode = UInt8() + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_REQUEST): + raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_REQUEST expected") + + self.sendAttachUserConfirm() + self.setNextState(self.recvChannelJoinRequest) + + def recvAttachUserConfirm(self, data): + """ + Receive an attach user confirm + client automata function + @param data: Stream + """ + opcode = UInt8() + data.readType(opcode) + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_CONFIRM): - raise InvalidExpectedDataException("invalid MCS PDU") - if confirm != 0: - raise Exception("server reject user") - if opcode & UInt8(2) == UInt8(2): - data.readType(userId) - self._userId = userId.value + raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_CONFIRM expected") + + if per.readEnumerates(data) != 0: + raise InvalidExpectedDataException("Server reject user") + + self._userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) #build channel list because we have user id - #add default channel + channels accepted by gcc connection sequence - self._channelIds[self._userId + Channel.MCS_USERCHANNEL_BASE] = None + #add default channel + channels accepted by GCC connection sequence + self._channelIds[self._userId] = None self.connectNextChannel() + + def recvChannelJoinRequest(self, data): + """ + Receive for each client channel a request + server automata function + @param data: Stream + + """ + opcode = UInt8() + data.readType(opcode) + + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_REQUEST): + raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_REQUEST expected") + + userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) + if self._userId != userId: + raise InvalidExpectedDataException("Invalid MCS User Id") + + channelId = per.readInteger16(data) + self.sendChannelJoinConfirm(channelId, channelId in self._channelIds.keys() or channelId == self._userId) def recvChannelJoinConfirm(self, data): """ Receive a channel join confirm from server + client automata function @param data: Stream """ opcode = UInt8() - confirm = UInt8() - data.readType((opcode, confirm)) + data.readType(opcode) + if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_CONFIRM): - raise InvalidExpectedDataException("invalid MCS PDU") - userId = UInt16Be() - channelId = UInt16Be() - data.readType((userId, channelId)) + raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_CONFIRM expected") + + confirm = per.readEnumerates(data) + + userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) + if self._userId != userId: + raise InvalidExpectedDataException("Invalid MCS User Id") + + channelId = per.readInteger16(data) #save state of channel - self._channelIdsRequest[channelId] = confirm == 0 - if confirm == 0: - print "server accept channel %d"%channelId.value - else: - print "server refused channel %d"%channelId.value - + self._channelIdsRequested[channelId] = (confirm == 0) self.connectNextChannel() def recvData(self, data): @@ -301,32 +407,29 @@ class MCS(LayerAutomata): data.readType(opcode) if self.readMCSPDUHeader(opcode.value, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM): - print "receive DISCONNECT_PROVIDER_ULTIMATUM" - self.close() + print "INFO : MCS DISCONNECT_PROVIDER_ULTIMATUM" + self._transport.close() + return elif not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.SEND_DATA_INDICATION): - raise InvalidExpectedDataException("invalid expected mcs opcode") + raise InvalidExpectedDataException("Invalid expected MCS opcode") - userId = UInt16Be() - channelId = UInt16Be() - flags = UInt8() - length = UInt8() + #server user id + per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE) - data.readType((userId, channelId, flags, length)) + channelId = per.readInteger16(data) - if length & UInt8(0x80) == UInt8(0x80): - lengthP2 = UInt8() - data.readType(lengthP2) - length = UInt16Be(length.value & 0x7f << 8 | lengthP2.value) + per.readEnumerates(data) + per.readLength(data) #channel id doesn't match a requested layer - if not self._channelIdsRequest.has_key(channelId): - print "receive data for an unrequested layer" + if not self._channelIdsRequested.has_key(channelId): + print "ERROR : receive data for an unrequested layer" return #channel id math an unconnected layer - if not self._channelIdsRequest[channelId]: - print "receive data for an unconnected layer" + if not self._channelIdsRequested[channelId]: + print "ERROR : receive data for an unconnected layer" return self._channelIds[channelId].recv(data) diff --git a/rdpy/protocol/rdp/pdu.py b/rdpy/protocol/rdp/pdu.py index af10fa0..141d937 100644 --- a/rdpy/protocol/rdp/pdu.py +++ b/rdpy/protocol/rdp/pdu.py @@ -24,7 +24,7 @@ In this layer are managed all mains bitmap update orders end user inputs """ from rdpy.network.layer import LayerAutomata, LayerMode -from rdpy.network.type import CompositeType, UniString, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType +from rdpy.network.type import CompositeType, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType from rdpy.network.error import InvalidExpectedDataException, CallPureVirtualFuntion, InvalidType import gcc, lic, caps, tpkt @@ -491,13 +491,13 @@ class RDPInfo(CompositeType): self.cbAlternateShell = UInt16Le(lambda:sizeof(self.alternateShell) - 2) self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2) #microsoft domain - self.domain = UniString(readLen = UInt16Le(lambda:self.cbDomain.value - 2)) - self.userName = UniString(readLen = UInt16Le(lambda:self.cbUserName.value - 2)) - self.password = UniString(readLen = UInt16Le(lambda:self.cbPassword.value - 2)) + self.domain = String(readLen = UInt16Le(lambda:self.cbDomain.value - 2), unicode = True) + self.userName = String(readLen = UInt16Le(lambda:self.cbUserName.value - 2), unicode = True) + self.password = String(readLen = UInt16Le(lambda:self.cbPassword.value - 2), unicode = True) #shell execute at start of session - self.alternateShell = UniString(readLen = UInt16Le(lambda:self.cbAlternateShell.value - 2)) + self.alternateShell = String(readLen = UInt16Le(lambda:self.cbAlternateShell.value - 2), unicode = True) #working directory for session - self.workingDir = UniString(readLen = UInt16Le(lambda:self.cbWorkingDir.value - 2)) + self.workingDir = String(readLen = UInt16Le(lambda:self.cbWorkingDir.value - 2), unicode = True) self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional) class RDPExtendedInfo(CompositeType): @@ -508,9 +508,9 @@ class RDPExtendedInfo(CompositeType): CompositeType.__init__(self, conditional = conditional) self.clientAddressFamily = UInt16Le(AfInet.AF_INET) self.cbClientAddress = UInt16Le(lambda:sizeof(self.clientAddress)) - self.clientAddress = UniString(readLen = self.cbClientAddress) + self.clientAddress = String(readLen = self.cbClientAddress, unicode = True) self.cbClientDir = UInt16Le(lambda:sizeof(self.clientDir)) - self.clientDir = UniString(readLen = self.cbClientDir) + self.clientDir = String(readLen = self.cbClientDir, unicode = True) #TODO make tiomezone self.clientTimeZone = String("\x00" * 172) self.clientSessionId = UInt32Le() @@ -530,7 +530,7 @@ class ShareControlHeader(CompositeType): #share control header self.totalLength = UInt16Le(totalLength) self.pduType = UInt16Le(pduType) - self.PDUSource = UInt16Le(userId + 1001) + self.PDUSource = UInt16Le(userId) class ShareDataHeader(CompositeType): """ @@ -562,7 +562,7 @@ class PDU(CompositeType): for c in [DemandActivePDU, ConfirmActivePDU, DataPDU, DeactiveAllPDU]: if self.shareControlHeader.pduType.value == c._PDUTYPE_: return c() - print "WARNING : unknown PDU type : %s"%self.shareControlHeader.pduType.value + print "WARNING : unknown PDU type : %s"%hex(self.shareControlHeader.pduType.value) #read entire packet return String() @@ -643,7 +643,7 @@ class DataPDU(CompositeType): for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU]: if self.shareDataHeader.pduType2.value == c._PDUTYPE2_: return c() - print "WARNING : unknown PDU data type : %s"%self.shareDataHeader.pduType2.value + print "WARNING : unknown PDU data type : %s"%hex(self.shareDataHeader.pduType2.value) return String() if pduData is None: @@ -936,7 +936,7 @@ class SlowPathInputEvent(CompositeType): for c in [PointerEvent, ScancodeKeyEvent, UnicodeKeyEvent]: if self.messageType.value == c._INPUT_MESSAGE_TYPE_: return c() - print "WARNING : unknown slow path input : %s"%self.messageType.value + print "WARNING : unknown slow path input : %s"%hex(self.messageType.value) return String() if messageData is None: @@ -1037,7 +1037,7 @@ class PDULayer(LayerAutomata, tpkt.FastPathListener): LayerAutomata.__init__(self, mode, None) #logon info send from client to server - self._info = RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().core.rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) + self._info = RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_CORE).rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) #server capabilities self._serverCapabilities = { caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.CapsType.CAPSTYPE_GENERAL, caps.GeneralCapability()), diff --git a/rdpy/protocol/rdp/per.py b/rdpy/protocol/rdp/per.py index f32fadd..f74a5dd 100644 --- a/rdpy/protocol/rdp/per.py +++ b/rdpy/protocol/rdp/per.py @@ -15,7 +15,7 @@ def readLength(s): s.readType(byte) size = 0 if byte.value & 0x80: - byte.value &= 0x80 + byte.value &= ~0x80 size = byte.value << 8 s.readType(byte) size += byte.value diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 3699c37..3f7b390 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -214,7 +214,7 @@ class RDPServerController(pdu.PDUServerListener): @param privateKeyFileName: file contain server private key @param certficiateFileName: file that contain public key """ - self._pduLayer = pdu.PDU(self) + self._pduLayer = pdu.PDULayer(self) #multi channel service self._mcsLayer = mcs.MCS(LayerMode.SERVER, self._pduLayer) #transport pdu layer diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py index aedc9d1..3b37861 100644 --- a/rdpy/protocol/rdp/tpdu.py +++ b/rdpy/protocol/rdp/tpdu.py @@ -294,4 +294,12 @@ class ServerTLSContext(ssl.DefaultOpenSSLContextFactory): @param certificateFileName: Name of a file containing a certificate """ def __init__(self, privateKeyFileName, certificateFileName): - ssl.DefaultOpenSSLContextFactory.__init__(self, privateKeyFileName, certificateFileName, SSL.TLSv1_METHOD) \ No newline at end of file + class TPDUSSLContext(SSL.Context): + def __init__(self, method): + SSL.Context.__init__(method) + self.set_options(0x00020000)#SSL_OP_NO_COMPRESSION + self.set_options(SSL.OP_DONT_INSERT_EMPTY_FRAGMENTS) + self.set_options(SSL.OP_TLS_BLOCK_PADDING_BUG) + + ssl.DefaultOpenSSLContextFactory.__init__(self, privateKeyFileName, certificateFileName, SSL.TLSv1_METHOD, TPDUSSLContext) + \ No newline at end of file