From e32def4f50cd38439e00d6e32fcf906d171fe2c4 Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Fri, 6 Dec 2013 16:53:41 +0100 Subject: [PATCH] add context value + conditional type and constant type --- rdpy/protocol/network/type.py | 101 ++++++++++++++++-------------- rdpy/protocol/rdp/gdl.py | 73 +++++++++++++++++----- rdpy/protocol/rdp/lic.py | 25 ++++++++ rdpy/protocol/rdp/tpdu.py | 114 ++++++++++++++++------------------ 4 files changed, 189 insertions(+), 124 deletions(-) create mode 100644 rdpy/protocol/rdp/lic.py diff --git a/rdpy/protocol/network/type.py b/rdpy/protocol/network/type.py index 0822111..305a1d9 100644 --- a/rdpy/protocol/network/type.py +++ b/rdpy/protocol/network/type.py @@ -5,40 +5,43 @@ import struct from copy import deepcopy from StringIO import StringIO -from error import InvalidValue, InvalidType +from error import InvalidValue, InvalidSize +from rdpy.protocol.network.error import InvalidExpectedDataException def sizeof(element): ''' byte size of type @param element: Type or Tuple(Type | Tuple,) @return: size of element in byte - @raise InvalidType: if type is different than tuple of Type inheritance ''' if isinstance(element, tuple): size = 0 for i in element: size += sizeof(i) return size - elif isinstance(element, Type): + elif isinstance(element, Type) and element._conditional(): return element.__sizeof__() - - raise InvalidType("invalid type for sizeof") + return 0 class Type(object): ''' root type ''' - def __init__(self, write_if = lambda:True, read_if = lambda:True): - self._write_if = write_if - self._read_if = read_if + def __init__(self, conditional = lambda:True, optional = False, constant = False): + self._conditional = conditional + self._optional = optional + self._constant = constant + self._is_writed = False + self._is_readed = False def write(self, s): ''' interface definition of write function @param s: Stream which will be written ''' - if not self._write_if(): + self._is_writed = self._conditional() + if not self._is_writed: return self.__write__(s) @@ -46,11 +49,15 @@ class Type(object): ''' interface definition of read value @param s: Stream - @return: Type read from Stream s ''' - if not self._read_if(): + self._is_readed = self._conditional() + if not self._is_readed: return + old = deepcopy(self) self.__read__(s) + #check constant value + if self._constant and old != self: + raise InvalidExpectedDataException("const value expected") def __sizeof__(self): ''' @@ -59,15 +66,14 @@ class Type(object): ''' pass -class ValueType(Type): +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 ''' - def __init__(self, value, write_if = lambda:True, read_if = lambda:True): - Type.__init__(self, write_if = write_if, read_if = read_if) + def __init__(self, value): self._value = None self.value = value @@ -102,16 +108,15 @@ class ValueType(Type): def value(self, value): ''' setter of value after check it - @param value: new value encompass in simpletype object - @raise InvalidValue: if value doesn't respect type range + @param value: new value encompass in valuetype object ''' self.__setValue__(value) -class SimpleType(ValueType): +class SimpleType(Type, CallableValue): ''' simple type ''' - def __init__(self, structFormat, typeSize, signed, value, write_if = lambda:True, read_if = lambda:True): + def __init__(self, structFormat, typeSize, signed, value, conditional = lambda:True, optional = False, constant = False): ''' constructor of simple type @param structFormat: letter that represent type in struct package @@ -122,7 +127,8 @@ class SimpleType(ValueType): self._signed = signed self._typeSize = typeSize self._structFormat = structFormat - ValueType.__init__(self, value, write_if = write_if, read_if = read_if) + Type.__init__(self, conditional = conditional, optional = optional, constant = constant) + CallableValue.__init__(self, value) def __getValue__(self): ''' @@ -130,7 +136,7 @@ class SimpleType(ValueType): @return: inner value(python type value) @raise InvalidValue: if value doesn't respect type range ''' - value = ValueType.__getValue__(self) + value = CallableValue.__getValue__(self) if not self.isInRange(value): raise InvalidValue("value is out of range for %s"%self.__class__) @@ -149,7 +155,7 @@ class SimpleType(ValueType): if not callable(value) and not self.isInRange(value): raise InvalidValue("value is out of range for %s"%self.__class__) - ValueType.__setValue__(self, value) + CallableValue.__setValue__(self, value) def __cmp__(self, other): @@ -299,11 +305,11 @@ class CompositeType(Type): keep ordering declaration of simple type in list and transparent for other type ''' - def __init__(self, write_if = lambda:True, read_if = lambda:True): + def __init__(self, conditional = lambda:True, optional = False, constant = False): ''' init list of simple value ''' - Type.__init__(self, write_if = write_if, read_if = read_if) + Type.__init__(self, conditional = conditional, optional = optional, constant = constant) #list of ordoned type self._typeName = [] @@ -369,15 +375,15 @@ class UInt8(SimpleType): ''' unsigned byte ''' - def __init__(self, value = 0, write_if = lambda:True, read_if = lambda:True): - SimpleType.__init__(self, "B", 1, False, value, write_if = write_if, read_if = read_if) + def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + SimpleType.__init__(self, "B", 1, False, value, conditional = conditional, optional = optional, constant = constant) class SInt8(SimpleType): ''' signed byte ''' - def __init__(self, value = 0, write_if = lambda:True, read_if = lambda:True): - SimpleType.__init__(self, "b", 1, True, value, write_if = write_if, read_if = read_if) + def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + SimpleType.__init__(self, "b", 1, True, value, conditional = conditional, optional = optional, constant = constant) class UInt16Be(SimpleType): @@ -386,8 +392,8 @@ class UInt16Be(SimpleType): @attention: inner value is in machine representation Big endian is just for read or write in stream ''' - def __init__(self, value = 0, write_if = lambda:True, read_if = lambda:True): - SimpleType.__init__(self, ">H", 2, False, value, write_if = write_if, read_if = read_if) + def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + SimpleType.__init__(self, ">H", 2, False, value, conditional = conditional, optional = optional, constant = constant) class UInt16Le(SimpleType): ''' @@ -395,8 +401,8 @@ class UInt16Le(SimpleType): @attention: inner value is in machine representation Big endian is just for read or write in stream ''' - def __init__(self, value = 0, write_if = lambda:True, read_if = lambda:True): - SimpleType.__init__(self, "I", 4, False, value, write_if = write_if, read_if = read_if) + def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + SimpleType.__init__(self, ">I", 4, False, value, conditional = conditional, optional = optional, constant = constant) class UInt32Le(SimpleType): ''' @@ -413,8 +419,8 @@ class UInt32Le(SimpleType): @attention: inner value is in machine representation Big endian is just for read or write in stream ''' - def __init__(self, value = 0, write_if = lambda:True, read_if = lambda:True): - SimpleType.__init__(self, "I", 4, True, value, write_if = write_if, read_if = read_if) + def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + SimpleType.__init__(self, ">I", 4, True, value, conditional = conditional, optional = optional, constant = constant) class UInt24Be(SimpleType): ''' @@ -440,8 +446,8 @@ class UInt24Be(SimpleType): @attention: inner value is in machine representation Big endian is just for read or write in stream ''' - def __init__(self, value = 0, write_if = lambda:True, read_if = lambda:True): - SimpleType.__init__(self, ">I", 3, False, value, write_if = write_if, read_if = read_if) + def __init__(self, value = 0, conditional = lambda:True, optional = False, constant = False): + SimpleType.__init__(self, ">I", 3, False, value, conditional = conditional, optional = optional, constant = constant) def __write__(self, s): ''' @@ -455,8 +461,8 @@ class UInt24Le(SimpleType): @attention: inner value is in machine representation Big endian is just for read or write in stream ''' - def __init__(self, value = 0, write_if = lambda:True, read_if = lambda:True): - 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: diff --git a/rdpy/protocol/rdp/gdl.py b/rdpy/protocol/rdp/gdl.py index e827805..2facba4 100644 --- a/rdpy/protocol/rdp/gdl.py +++ b/rdpy/protocol/rdp/gdl.py @@ -7,15 +7,23 @@ from rdpy.protocol.network.type import CompositeType, UniString, UInt16Le, UInt1 from rdpy.utils.const import ConstAttributes, TypeAttributes from rdpy.protocol.network.error import InvalidExpectedDataException +import gcc + @ConstAttributes @TypeAttributes(UInt16Le) class SecurityFlag(object): + ''' + microsoft security flags + ''' SEC_INFO_PKT = 0x0040 SEC_LICENSE_PKT = 0x0080 @ConstAttributes @TypeAttributes(UInt32Le) class InfoFlag(object): + ''' + client capabilities informations + ''' INFO_MOUSE = 0x00000001 INFO_DISABLECTRLALTDEL = 0x00000002 INFO_AUTOLOGON = 0x00000008 @@ -39,6 +47,9 @@ class InfoFlag(object): @ConstAttributes @TypeAttributes(UInt32Le) class PerfFlag(object): + ''' + network performances flag + ''' PERF_DISABLE_WALLPAPER = 0x00000001 PERF_DISABLE_FULLWINDOWDRAG = 0x00000002 PERF_DISABLE_MENUANIMATIONS = 0x00000004 @@ -55,29 +66,58 @@ class AfInet(object): AF_INET6 = 0x0017 class RDPInfo(CompositeType): - def __init__(self): + ''' + client informations + contains credentials (very important packet) + ''' + def __init__(self, initForWrite, extendedInfoConditional): CompositeType.__init__(self) + #code page self.codePage = UInt32Le() - self.flag = InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_ENABLEWINDOWSKEY | InfoFlag.INFO_DISABLECTRLALTDEL - self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2) - self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2) - self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2) + #support flag + self.flag = InfoFlag.INFO_MOUSE | InfoFlag.INFO_UNICODE | InfoFlag.INFO_LOGONERRORS | InfoFlag.INFO_LOGONNOTIFY | InfoFlag.INFO_ENABLEWINDOWSKEY | InfoFlag.INFO_DISABLECTRLALTDEL + #length of domain unistring less 2 byte null terminate + self.cbDomain = UInt16Le(lambda:sizeof(self.domain) - 2) + #length of username unistring less 2 byte null terminate + self.cbUserName = UInt16Le(lambda:sizeof(self.userName) - 2) + #length of password unistring less 2 byte null terminate + self.cbPassword = UInt16Le(lambda:sizeof(self.password) - 2) + #length of alternateshell unistring less 2 byte null terminate 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) - self.domain = UniString("coco") - self.userName = UniString("lolo") - self.password = UniString("toto") - self.alternateShell = UniString() - self.workingDir = UniString() + #to avoid recurcive loop init differ from reading and writing + #microsoft domain + self.domain = UniString("" if initForWrite else lambda:"\x00" * self.cbDomain.value) + #session username + self.userName = UniString("" if initForWrite else lambda:"\x00" * self.cbUserName.value) + #associate password + self.password = UniString("" if initForWrite else lambda:"\x00" * self.cbPassword.value) + #shell execute at start of session + self.alternateShell = UniString("" if initForWrite else lambda:"\x00" * self.cbAlternateShell.value) + #working directory for session + self.workingDir = UniString("" if initForWrite else lambda:"\x00" * self.cbWorkingDir.value) + #more client informations + self.extendedInfo = RDPExtendedInfo(initForWrite, conditional = extendedInfoConditional) class RDPExtendedInfo(CompositeType): - def __init__(self): - CompositeType.__init__(self) + ''' + add more client informations + use for performance flag!!! + ''' + def __init__(self, initForWrite, 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)) - self.clientAddress = UniString("192.168.135.10") + #adress of client + self.clientAddress = UniString("" if initForWrite else lambda:"\x00" * self.cbClientAddress.value) + #len of client directory self.cbClientDir = UInt16Le(lambda:sizeof(self.clientDir)) - self.clientDir = UniString("c:\\") + #self client directory + self.clientDir = UniString("" if initForWrite else lambda:"\x00" * self.cbClientDir.value) + #TODO make tiomezone #self.performanceFlags = PerfFlag.PERF_DISABLE_WALLPAPER | PerfFlag.PERF_DISABLE_MENUANIMATIONS | PerfFlag.PERF_DISABLE_CURSOR_SHADOW class GDL(LayerAutomata): @@ -94,8 +134,7 @@ class GDL(LayerAutomata): #set by mcs layer channel init self._channelId = UInt16Be() #logon info send from client to server - self._info = RDPInfo() - self._extendedInfo = RDPExtendedInfo() + self._info = RDPInfo(initForWrite = True, extendedInfoConditional = lambda:self._transport._serverSettings.core.rdpVersion == gcc.Version.RDP_VERSION_5_PLUS) def connect(self): ''' @@ -111,7 +150,7 @@ class GDL(LayerAutomata): send a logon info packet for RDP version 5 protocol ''' #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, self._extendedInfo)) + self._transport.send(self._channelId, (SecurityFlag.SEC_INFO_PKT, UInt16Le(), self._info)) def recvLicenceInfo(self, data): securityFlag = UInt16Le() diff --git a/rdpy/protocol/rdp/lic.py b/rdpy/protocol/rdp/lic.py new file mode 100644 index 0000000..9259774 --- /dev/null +++ b/rdpy/protocol/rdp/lic.py @@ -0,0 +1,25 @@ +''' +@author: sylvain +''' +from rdpy.protocol.network.type import CompositeType, UInt8, UInt16Le, sizeof +from rdpy.utils.const import ConstAttributes, TypeAttributes + +@ConstAttributes +@TypeAttributes(UInt8) +class MessageType(object): + LICENSE_REQUEST = 0x01 + PLATFORM_CHALLENGE = 0x02 + NEW_LICENSE = 0x03 + UPGRADE_LICENSE = 0x04 + LICENSE_INFO = 0x12 + NEW_LICENSE_REQUEST = 0x13 + PLATFORM_CHALLENGE_RESPONSE = 0x15 + ERROR_ALERT = 0xFF + +class LicPacket(CompositeType): + def __init__(self): + #preambule + self.bMsgtype = UInt8() + self.flag = UInt8() + self.wMsgSize = UInt16Le(lambda: sizeof(self)) + \ No newline at end of file diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py index 7373896..032be4e 100644 --- a/rdpy/protocol/rdp/tpdu.py +++ b/rdpy/protocol/rdp/tpdu.py @@ -3,7 +3,7 @@ ''' from rdpy.protocol.network.layer import LayerAutomata from rdpy.protocol.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof -from rdpy.protocol.network.error import InvalidExpectedDataException, NegotiationFailure +from rdpy.protocol.network.error import InvalidExpectedDataException from rdpy.utils.const import ConstAttributes, TypeAttributes @ConstAttributes @@ -39,15 +39,30 @@ class Protocols(object): PROTOCOL_HYBRID = 0x00000002 PROTOCOL_HYBRID_EX = 0x00000008 -class TPDUConnectHeader(CompositeType): +@ConstAttributes +@TypeAttributes(UInt32Le) +class NegotiationFailureCode(object): + ''' + protocol negotiation failure code + ''' + SSL_REQUIRED_BY_SERVER = 0x00000001 + SSL_NOT_ALLOWED_BY_SERVER = 0x00000002 + SSL_CERT_NOT_ON_SERVER = 0x00000003 + INCONSISTENT_FLAGS = 0x00000004 + HYBRID_REQUIRED_BY_SERVER = 0x00000005 + SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 0x00000006 + +class TPDUConnectMessage(CompositeType): ''' header of TPDU connection messages ''' - def __init__(self, code = MessageType.X224_TPDU_CONNECTION_REQUEST, messageSize = 0): + def __init__(self): CompositeType.__init__(self) - self.len = UInt8(messageSize + 6) - self.code = code + self.len = UInt8(lambda:sizeof(self) - 1) + self.code = UInt8() self.padding = (UInt16Be(), UInt16Be(), UInt8()) + #read if there is enought data + self.protocolNeg = Negotiation(optional = True) class TPDUDataHeader(CompositeType): ''' @@ -55,20 +70,22 @@ class TPDUDataHeader(CompositeType): ''' def __init__(self): CompositeType.__init__(self) - self.header = UInt8(2) + self.header = UInt8(2, constant = True) self.messageType = MessageType.X224_TPDU_DATA - self.separator = UInt8(0x80) + self.separator = UInt8(0x80, constant = True) class Negotiation(CompositeType): ''' negociation request message ''' - def __init__(self, protocol = Protocols.PROTOCOL_SSL): - CompositeType.__init__(self) + def __init__(self, optional = False): + CompositeType.__init__(self, optional = optional) + self.code = UInt8() self.flag = UInt8(0) #always 8 - self.len = UInt16Le(0x0008) - self.protocol = protocol + self.len = UInt16Le(0x0008, constant = True) + self.selectedProtocol = UInt32Le(conditional = lambda: self.code == NegociationType.TYPE_RDP_NEG_RSP) + self.failureCode = UInt32Le(conditional = lambda: self.code == NegociationType.TYPE_RDP_NEG_FAILURE) class TPDU(LayerAutomata): ''' @@ -81,11 +98,11 @@ class TPDU(LayerAutomata): @param presentation: MCS layer ''' LayerAutomata.__init__(self, presentation) - #default protocol is SSl because is the only supported + #default selectedProtocol is SSl because is the only supported #in this version of RDPY - #client requested protocol + #client requested selectedProtocol self._requestedProtocol = Protocols.PROTOCOL_SSL - #server selected protocol + #server selected selectedProtocol self._selectedProtocol = Protocols.PROTOCOL_SSL def connect(self): @@ -102,15 +119,24 @@ class TPDU(LayerAutomata): call connect on presentation layer if all is good @param data: Stream that contain connection confirm ''' - header = TPDUConnectHeader() - data.readType(header) - if header.code != MessageType.X224_TPDU_CONNECTION_CONFIRM: - raise InvalidExpectedDataException("invalid TPDU header code X224_TPDU_CONNECTION_CONFIRM != %d"%header.code) + message = TPDUConnectMessage() + data.readType(message) + if message.code != MessageType.X224_TPDU_CONNECTION_CONFIRM: + raise InvalidExpectedDataException("invalid TPDU header code X224_TPDU_CONNECTION_CONFIRM != %d"%message.code) #check presence of negotiation response - if data.dataLen() == 8: - self.readNeg(data) - else: - raise NegotiationFailure("server doesn't support SSL") + if not message.protocolNeg._is_readed: + raise InvalidExpectedDataException("server must support negotiation protocol to use SSL") + + if message.protocolNeg.failureCode._is_readed: + raise InvalidExpectedDataException("negotiation failure code %x"%message.protocolNeg.failureCode.value) + + self._selectedProtocol = message.protocolNeg.selectedProtocol + + if self._selectedProtocol != Protocols.PROTOCOL_SSL: + raise InvalidExpectedDataException("only ssl protocol is supported in RDPY version") + + #_transport is TPKT and transport is TCP layer of twisted + self._transport.transport.startTLS(ClientTLSContext()) self.setNextState(self.recvData) #connection is done send to presentation @@ -136,8 +162,11 @@ class TPDU(LayerAutomata): write connection request message next state is recvConnectionConfirm ''' - neqReq = (NegociationType.TYPE_RDP_NEG_REQ, Negotiation(self._requestedProtocol)) - self._transport.send((TPDUConnectHeader(MessageType.X224_TPDU_CONNECTION_REQUEST, sizeof(neqReq)), neqReq)) + message = TPDUConnectMessage() + message.code = MessageType.X224_TPDU_CONNECTION_REQUEST + message.protocolNeg.code = NegociationType.TYPE_RDP_NEG_REQ + message.protocolNeg.selectedProtocol = self._requestedProtocol + self._transport.send(message) self.setNextState(self.recvConnectionConfirm) def send(self, message): @@ -146,43 +175,6 @@ class TPDU(LayerAutomata): add TPDU header ''' self._transport.send((TPDUDataHeader(), message)) - - def readNeg(self, data): - ''' - read negotiation response - ''' - code = UInt8() - data.readType(code) - if code == NegociationType.TYPE_RDP_NEG_FAILURE: - self.readNegFailure(data) - elif code == NegociationType.TYPE_RDP_NEG_RSP: - self.readNegResp(data) - else: - raise InvalidExpectedDataException("bad protocol negotiation response code") - - def readNegFailure(self, data): - ''' - read negotiation failure packet - ''' - print "Negotiation failure" - - def readNegResp(self, data): - ''' - read negotiation response packet - ''' - negResp = Negotiation() - data.readType(negResp) - - if negResp.len != UInt16Le(0x0008): - raise InvalidExpectedDataException("invalid size of negotiation response") - - self._selectedProtocol = negResp.protocol - - if self._selectedProtocol == self._requestedProtocol and self._selectedProtocol == Protocols.PROTOCOL_SSL: - #_transport is TPKT and transport is TCP layer of twisted - self._transport.transport.startTLS(ClientTLSContext()) - else: - raise NegotiationFailure("protocol negociation failure") #open ssl needed