From f2481149d9585dd956541cef1881be7f9ab81dbd Mon Sep 17 00:00:00 2001 From: speyrefitte Date: Mon, 15 Dec 2014 18:26:27 +0100 Subject: [PATCH] finish RDP basic security layer client side, start server side --- rdpy/{protocol/rdp => core}/rc4.py | 0 rdpy/core/x509.py | 10 +- rdpy/protocol/rdp/gcc.py | 52 +++++++++-- rdpy/protocol/rdp/lic.py | 4 +- rdpy/protocol/rdp/mcs.py | 133 +++++++++++++++++--------- rdpy/protocol/rdp/pdu/caps.py | 4 +- rdpy/protocol/rdp/pdu/layer.py | 44 ++++----- rdpy/protocol/rdp/rdp.py | 5 +- rdpy/protocol/rdp/sec.py | 144 ++++++++++++++++++----------- rdpy/protocol/rdp/tpkt.py | 32 +++---- rdpy/protocol/rdp/x224.py | 3 +- 11 files changed, 275 insertions(+), 156 deletions(-) rename rdpy/{protocol/rdp => core}/rc4.py (100%) diff --git a/rdpy/protocol/rdp/rc4.py b/rdpy/core/rc4.py similarity index 100% rename from rdpy/protocol/rdp/rc4.py rename to rdpy/core/rc4.py diff --git a/rdpy/core/x509.py b/rdpy/core/x509.py index d507b2f..3bdcd42 100644 --- a/rdpy/core/x509.py +++ b/rdpy/core/x509.py @@ -149,10 +149,16 @@ def extractRSAKey(certificate): @return: (modulus, public exponent) """ #http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html - #if certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('algorithm').getComponentByName('algorithm')._value != (1, 2, 840, 113549, 1, 1, 1): + #disable check because nobody respect + #if certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('algorithm').getComponentByName('algorithm')._value != (1, 3, 14, 3, 2, 15): # raise InvalidExpectedDataException("Certificate doesn't contain RSA public key") - rsaKey = decoder.decode(encoder.encode(certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('subjectPublicKey'))[3:], asn1Spec=RSAPublicKey())[0] + #extract binary data + l = 0L + for b in certificate.getComponentByName('tbsCertificate').getComponentByName('subjectPublicKeyInfo').getComponentByName('subjectPublicKey'): + l = (l << 1) | b + + rsaKey = decoder.decode(hex(l)[2:-1].decode('hex'), asn1Spec=RSAPublicKey())[0] return rsaKey.getComponentByName('modulus')._value , rsaKey.getComponentByName('publicExponent')._value diff --git a/rdpy/protocol/rdp/gcc.py b/rdpy/protocol/rdp/gcc.py index ee9ad50..3570e3f 100644 --- a/rdpy/protocol/rdp/gcc.py +++ b/rdpy/protocol/rdp/gcc.py @@ -25,7 +25,7 @@ http://msdn.microsoft.com/en-us/library/cc240508.aspx from rdpy.core.type import UInt8, UInt16Le, UInt32Le, CompositeType, String, Stream, sizeof, FactoryType, ArrayType import per, mcs from rdpy.core.error import InvalidExpectedDataException -import rdpy.core.log as log +from rdpy.core import log, x509 t124_02_98_oid = ( 0, 0, 20, 124, 0, 1 ) @@ -123,7 +123,7 @@ class Version(object): class Sequence(object): RNS_UD_SAS_DEL = 0xAA03 -class Encryption(object): +class EncryptionMethod(object): """ @summary: Encryption methods supported @see: http://msdn.microsoft.com/en-us/library/cc240511.aspx @@ -132,6 +132,17 @@ class Encryption(object): ENCRYPTION_FLAG_128BIT = 0x00000002 ENCRYPTION_FLAG_56BIT = 0x00000008 FIPS_ENCRYPTION_FLAG = 0x00000010 + +class EncryptionLevel(object): + """ + @summary: level of 'security' + @see: http://msdn.microsoft.com/en-us/library/cc240518.aspx + """ + ENCRYPTION_LEVEL_NONE = 0x00000000 + ENCRYPTION_LEVEL_LOW = 0x00000000 + ENCRYPTION_LEVEL_CLIENT_COMPATIBLE = 0x00000000 + ENCRYPTION_LEVEL_HIGH = 0x00000000 + ENCRYPTION_LEVEL_FIPS = 0x00000000 class ChannelOptions(object): """ @@ -206,7 +217,7 @@ class DataBlock(CompositeType): def DataBlockFactory(): """ - build settings in accordance of type self.type.value + @summary: build settings in accordance of type self.type.value """ for c in [ClientCoreData, ClientSecurityData, ClientNetworkData, ServerCoreData, ServerNetworkData, ServerSecurityData]: if self.type.value == c._TYPE_: @@ -224,7 +235,7 @@ class DataBlock(CompositeType): class ClientCoreData(CompositeType): """ - Class that represent core setting of client + @summary: Class that represent core setting of client @see: http://msdn.microsoft.com/en-us/library/cc240510.aspx """ _TYPE_ = MessageType.CS_CORE @@ -256,7 +267,7 @@ class ClientCoreData(CompositeType): class ServerCoreData(CompositeType): """ - Server side core settings structure + @summary: Server side core settings structure @see: http://msdn.microsoft.com/en-us/library/cc240517.aspx """ _TYPE_ = MessageType.SC_CORE @@ -268,19 +279,19 @@ class ServerCoreData(CompositeType): class ClientSecurityData(CompositeType): """ - Client security setting + @summary: Client security setting @see: http://msdn.microsoft.com/en-us/library/cc240511.aspx """ _TYPE_ = MessageType.CS_SECURITY def __init__(self, readLen = None): CompositeType.__init__(self, readLen = readLen) - self.encryptionMethods = UInt32Le(Encryption.ENCRYPTION_FLAG_40BIT) + self.encryptionMethods = UInt32Le(EncryptionMethod.ENCRYPTION_FLAG_40BIT | EncryptionMethod.ENCRYPTION_FLAG_56BIT | EncryptionMethod.ENCRYPTION_FLAG_128BIT) self.extEncryptionMethods = UInt32Le() class ServerSecurityData(CompositeType): """ - Server security settings + @summary: Server security settings @see: http://msdn.microsoft.com/en-us/library/cc240518.aspx """ _TYPE_ = MessageType.SC_SECURITY @@ -314,6 +325,17 @@ class ServerCertificate(CompositeType): self.certData = FactoryType(CertificateFactory) +def bin2bn(b): + """ + @summary: convert binary string to bignum + @param b: {str} binary string + @return: {long} bignum + """ + l = 0L + for ch in b: + l = (l<<8) | ord(ch) + return l + class ProprietaryServerCertificate(CompositeType): """ @summary: microsoft proprietary certificate @@ -331,6 +353,13 @@ class ProprietaryServerCertificate(CompositeType): self.wSignatureBlobType = UInt16Le(0x0008, constant = True) self.wSignatureBlobLen = UInt16Le(lambda:sizeof(self.SignatureBlob)) self.SignatureBlob = String(readLen = self.wSignatureBlobLen) + + def getPublicKey(self): + """ + @return: {Tuple} (modulus, publicExponent) + """ + #reverse because bignum in little endian + return bin2bn(self.PublicKeyBlob.modulus.value[::-1]), self.PublicKeyBlob.pubExp.value class CertBlob(CompositeType): """ @@ -355,6 +384,13 @@ class X509CertificateChain(CompositeType): self.CertBlobArray = ArrayType(CertBlob, readLen = self.NumCertBlobs) self.padding = String(readLen = UInt8(lambda:(8 + 4 * self.NumCertBlobs.value))) + def getPublicKey(self): + """ + @return: {Tuple} (modulus, publicExponent) + """ + #last certifcate contain publi key + return x509.extractRSAKey(x509.load(self.CertBlobArray[-1].abCert.value)) + class RSAPublicKey(CompositeType): """ @see: http://msdn.microsoft.com/en-us/library/cc240520.aspx diff --git a/rdpy/protocol/rdp/lic.py b/rdpy/protocol/rdp/lic.py index b7c4aa1..25d41b0 100644 --- a/rdpy/protocol/rdp/lic.py +++ b/rdpy/protocol/rdp/lic.py @@ -25,8 +25,8 @@ from rdpy.core.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream from rdpy.core.error import InvalidExpectedDataException import rdpy.core.log as log -import rc4, sec, gcc -from rdpy.core import x509 +import sec, gcc +from rdpy.core import x509, rc4 class MessageType(object): """ diff --git a/rdpy/protocol/rdp/mcs.py b/rdpy/protocol/rdp/mcs.py index 1f6d908..e7be155 100644 --- a/rdpy/protocol/rdp/mcs.py +++ b/rdpy/protocol/rdp/mcs.py @@ -26,7 +26,7 @@ It exist channel for file system order, audio channel, clipboard etc... """ from rdpy.core.layer import LayerAutomata, IStreamSender, Layer from rdpy.core.type import sizeof, Stream, UInt8, UInt16Le, String -from rdpy.core.error import InvalidExpectedDataException, InvalidValue, InvalidSize +from rdpy.core.error import InvalidExpectedDataException, InvalidValue, InvalidSize, CallPureVirtualFuntion from rdpy.protocol.rdp.ber import writeLength import rdpy.core.log as log @@ -58,6 +58,38 @@ class Channel: """ MCS_GLOBAL_CHANNEL = 1003 MCS_USERCHANNEL_BASE = 1001 + +class IGCCConfig(object): + """ + @summary: Channel information + """ + def getUserId(self): + """ + @return: {integer} mcs user id + @see: mcs.IGCCConfig + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getUserId", "IGCCConfig")) + + def getChannelId(self): + """ + @return: {integer} return channel id of proxy + @see: mcs.IGCCConfig + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getChannelId", "IGCCConfig")) + + def getGCCClientSettings(self): + """ + @return: {gcc.Settings} mcs layer gcc client settings + @see: mcs.IGCCConfig + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getGCCClientSettings", "IGCCConfig")) + + def getGCCServerSettings(self): + """ + @return: {gcc.Settings} mcs layer gcc server settings + @see: mcs.IGCCConfig + """ + raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getGCCServerSettings", "IGCCConfig")) class MCSLayer(LayerAutomata): """ @@ -65,16 +97,16 @@ class MCSLayer(LayerAutomata): the main layer of RDP protocol is why he can do everything and more! """ - class MCSProxySender(Layer, IStreamSender): + class MCSProxySender(Layer, IStreamSender, IGCCConfig): """ @summary: Proxy use to set as transport layer for upper channel use to abstract channel id for presentation layer """ def __init__(self, presentation, mcs, channelId): """ - @param presentation: presentation layer - @param mcs: MCS layer use as proxy - @param channelId: channel id for presentation layer + @param presentation: {Layer} presentation layer + @param mcs: {MCSLayer} MCS layer use as proxy + @param channelId: {integer} channel id for presentation layer """ Layer.__init__(self, presentation) self._mcs = mcs @@ -84,6 +116,7 @@ class MCSLayer(LayerAutomata): """ @summary: A send proxy function, use channel id and specific send function of MCS layer + @param data: {type.Type | Tuple} """ self._mcs.send(self._channelId, data) @@ -95,35 +128,39 @@ class MCSLayer(LayerAutomata): def getUserId(self): """ - @return: mcs user id + @return: {integer} mcs user id + @see: mcs.IGCCConfig """ return self._mcs._userId def getChannelId(self): """ - @return: return channel id of proxy + @return: {integer} return channel id of proxy + @see: mcs.IGCCConfig """ return self._channelId def getGCCClientSettings(self): """ - @return: mcs layer gcc client settings + @return: {gcc.Settings} mcs layer gcc client settings + @see: mcs.IGCCConfig """ return self._mcs._clientSettings def getGCCServerSettings(self): """ - @return: mcs layer gcc server settings + @return: {gcc.Settings} mcs layer gcc server settings + @see: mcs.IGCCConfig """ return self._mcs._serverSettings def __init__(self, presentation, receiveOpcode, sendOpcode, virtualChannels = []): """ - @param presentation: presentation layer - @param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] - @param receiveOpcode: opcode check when receive data - @param sendOpcode: opcode use when send data + @param presentation: {Layer} presentation layer + @param virtualChannels: {Array(Layer]} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] + @param receiveOpcode: {integer} opcode check when receive data + @param sendOpcode: {integer} opcode use when send data """ LayerAutomata.__init__(self, presentation) self._clientSettings = gcc.clientSettings() @@ -163,8 +200,8 @@ class MCSLayer(LayerAutomata): def send(self, channelId, data): """ @summary: Specific send function for channelId - @param channelId: Channel use to send - @param data: message to send + @param channelId: {integer} Channel use to send + @param data: {type.type | tuple} message to send """ self._transport.send((self.writeMCSPDUHeader(UInt8(self._sendOpcode)), per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), @@ -175,7 +212,7 @@ class MCSLayer(LayerAutomata): def recvData(self, data): """ @summary: Main receive method - @param data: Stream + @param data: {Stream} """ opcode = UInt8() data.readType(opcode) @@ -208,11 +245,11 @@ class MCSLayer(LayerAutomata): """ @summary: Write a special domain parameter structure use in connection sequence - @param maxChannels: number of MCS channel use - @param maxUsers: number of MCS user used (1) - @param maxTokens: unknown - @param maxPduSize: unknown - @return: domain parameter structure + @param maxChannels: {integer} number of MCS channel use + @param maxUsers: {integer} number of MCS user used (1) + @param maxTokens: {integer} unknown + @param maxPduSize: {integer} unknown + @return: {Tuple(type)} domain parameter structure """ domainParam = (ber.writeInteger(maxChannels), ber.writeInteger(maxUsers), ber.writeInteger(maxTokens), ber.writeInteger(1), ber.writeInteger(0), ber.writeInteger(1), @@ -222,25 +259,26 @@ class MCSLayer(LayerAutomata): def writeMCSPDUHeader(self, mcsPdu, options = 0): """ @summary: Write MCS PDU header - @param mcsPdu: PDU code - @param options: option contains in header - @return: UInt8 + @param mcsPdu: {integer} PDU code + @param options: {integer} option contains in header + @return: {integer} """ return (mcsPdu << 2) | options def readMCSPDUHeader(self, opcode, mcsPdu): """ @summary: Read mcsPdu header and return options parameter - @param opcode: opcode - @param mcsPdu: mcsPdu will be checked - @return: true if opcode is correct + @param opcode: {integer} opcode + @param mcsPdu: {integer} mcsPdu will be checked + @return: {boolean} true if opcode is correct """ return (opcode >> 2) == mcsPdu def readDomainParams(self, s): """ @summary: Read domain parameters structure - @return: (max_channels, max_users, max_tokens, max_pdu_size) + @param s: {Stream} + @return: {Tuple} (max_channels, max_users, max_tokens, max_pdu_size) """ if not ber.readUniversalTag(s, ber.Tag.BER_TAG_SEQUENCE, True): raise InvalidValue("bad BER tags") @@ -261,8 +299,8 @@ class Client(MCSLayer): """ def __init__(self, presentation, virtualChannels = []): """ - @param presentation: presentation layer - @param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] + @param presentation: {Layer} presentation layer + @param virtualChannels: {Array(Layer)} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] """ MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST, virtualChannels) #use to know state of static channel @@ -277,9 +315,9 @@ class Client(MCSLayer): Send ConnectInitial Wait ConnectResponse """ - self._clientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol.value = self._transport._selectedProtocol + self._clientSettings.CS_CORE.serverSelectedProtocol.value = self._transport._selectedProtocol #ask for virtual channel - self._clientSettings.getBlock(gcc.MessageType.CS_NET).channelDefArray._array = [x for (x, _) in self._virtualChannels] + self._clientSettings.CS_NET.channelDefArray._array = [x for (x, _) in self._virtualChannels] #send connect initial self.sendConnectInitial() #next wait response @@ -319,7 +357,7 @@ class Client(MCSLayer): Send Erect domain Request Send Attach User Request Wait Attach User Confirm - @param data: Stream + @param data: {Stream} """ ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE)) ber.readEnumerated(data) @@ -343,7 +381,7 @@ class Client(MCSLayer): """ @summary: Receive an attach user confirm Send Connect Channel - @param data: Stream + @param data: {Stream} """ opcode = UInt8() data.readType(opcode) @@ -362,7 +400,7 @@ class Client(MCSLayer): """ @summary: Receive a channel join confirm from server client automata function - @param data: Stream + @param data: {Stream} """ opcode = UInt8() data.readType(opcode) @@ -423,7 +461,7 @@ class Client(MCSLayer): """ @summary: Send a formated Channel join request from client to server client automata function - @param channelId: id of channel requested + @param channelId: {integer} id of channel requested """ self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)), per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE), @@ -435,8 +473,8 @@ class Server(MCSLayer): """ def __init__(self, presentation, virtualChannels = []): """ - @param presentation: presentation layer - @param virtualChannels: list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] + @param presentation: {Layer} presentation layer + @param virtualChannels: {List(Layer)} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)] """ MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION, virtualChannels) #nb channel requested @@ -447,7 +485,12 @@ class Server(MCSLayer): @summary: Connect message for server automata Wait Connect Initial """ - self._serverSettings.getBlock(gcc.MessageType.SC_CORE).clientRequestedProtocol.value = self._transport._requestedProtocol + #basic rdp security layer + if self._transport._selectedProtocol == 0: + self._serverSettings.SC_SECURITY.encryptionMethod.value = gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT + self._serverSettings.SC_SECURITY.encryptionLevel = gcc.EncryptionLevel.ENCRYPTION_LEVEL_HIGH + + self._serverSettings.SC_CORE.clientRequestedProtocol.value = self._transport._requestedProtocol self.setNextState(self.recvConnectInitial) def recvConnectInitial(self, data): @@ -455,7 +498,7 @@ class Server(MCSLayer): @summary: Receive MCS connect initial from client Send Connect Response Wait Erect Domain Request - @param data: Stream + @param data: {Stream} """ ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_INITIAL)) ber.readOctetString(data) @@ -485,7 +528,7 @@ class Server(MCSLayer): """ @summary: Receive erect domain request Wait Attach User Request - @param data: Stream + @param data: {Stream} """ opcode = UInt8() data.readType(opcode) @@ -503,7 +546,7 @@ class Server(MCSLayer): @summary: Receive Attach user request Send Attach User Confirm Wait Channel Join Request - @param data: Stream + @param data: {Stream} """ opcode = UInt8() data.readType(opcode) @@ -518,7 +561,7 @@ class Server(MCSLayer): """ @summary: Receive for each client channel a request Send Channel Join Confirm or Connect upper layer when all channel are joined - @param data: Stream + @param data: {Stream} """ opcode = UInt8() @@ -562,8 +605,8 @@ class Server(MCSLayer): def sendChannelJoinConfirm(self, channelId, confirm): """ @summary: Send a confirm channel (or not) to client - @param channelId: id of channel - @param confirm: connection state + @param channelId: {integer} id of channel + @param confirm: {boolean} connection state """ self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM), 2), per.writeEnumerates(int(confirm)), diff --git a/rdpy/protocol/rdp/pdu/caps.py b/rdpy/protocol/rdp/pdu/caps.py index f06989d..35bffd6 100644 --- a/rdpy/protocol/rdp/pdu/caps.py +++ b/rdpy/protocol/rdp/pdu/caps.py @@ -359,12 +359,12 @@ class PointerCapability(CompositeType): """ _TYPE_ = CapsType.CAPSTYPE_POINTER - def __init__(self, readLen = None): + def __init__(self, isServer = False, readLen = None): CompositeType.__init__(self, readLen = readLen) self.colorPointerFlag = UInt16Le() self.colorPointerCacheSize = UInt16Le(20) #old version of rdp doesn't support ... - #self.pointerCacheSize = UInt16Le() + self.pointerCacheSize = UInt16Le(conditional = lambda:isServer) class InputCapability(CompositeType): """ diff --git a/rdpy/protocol/rdp/pdu/layer.py b/rdpy/protocol/rdp/pdu/layer.py index 4e63fc0..6f70d4f 100644 --- a/rdpy/protocol/rdp/pdu/layer.py +++ b/rdpy/protocol/rdp/pdu/layer.py @@ -26,7 +26,6 @@ In this layer are managed all mains bitmap update orders end user inputs from rdpy.core.layer import LayerAutomata from rdpy.core.error import CallPureVirtualFuntion import rdpy.core.log as log -import rdpy.protocol.rdp.gcc as gcc import rdpy.protocol.rdp.tpkt as tpkt import data, caps @@ -82,7 +81,7 @@ class PDULayer(LayerAutomata, tpkt.IFastPathListener): caps.CapsType.CAPSTYPE_GENERAL : caps.Capability(caps.GeneralCapability()), caps.CapsType.CAPSTYPE_BITMAP : caps.Capability(caps.BitmapCapability()), caps.CapsType.CAPSTYPE_ORDER : caps.Capability(caps.OrderCapability()), - caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.PointerCapability()), + caps.CapsType.CAPSTYPE_POINTER : caps.Capability(caps.PointerCapability(isServer = True)), caps.CapsType.CAPSTYPE_INPUT : caps.Capability(caps.InputCapability()), caps.CapsType.CAPSTYPE_VIRTUALCHANNEL : caps.Capability(caps.VirtualChannelCapability()), caps.CapsType.CAPSTYPE_FONT : caps.Capability(caps.FontCapability()), @@ -120,7 +119,7 @@ class PDULayer(LayerAutomata, tpkt.IFastPathListener): @summary: Send a PDU data to transport layer @param pduMessage: PDU message """ - self._transport.send(data.PDU(self._transport._transport.getUserId(), pduMessage)) + self._transport.send(data.PDU(self._transport.getUserId(), pduMessage)) def sendDataPDU(self, pduData): """ @@ -144,7 +143,7 @@ class Client(PDULayer): """ @summary: Connect message in client automata """ - self._gccCore = self._transport._transport.getGCCClientSettings().CS_CORE + self._gccCore = self._transport.getGCCClientSettings().CS_CORE self.setNextState(self.recvDemandActivePDU) #check if client support fast path message self._clientFastPathSupported = False @@ -299,7 +298,7 @@ class Client(PDULayer): """ @summary: Read an update data PDU data dispatch update data - @param: UpdateDataPDU object + @param: {UpdateDataPDU} object """ if updateDataPDU.updateType.value == data.UpdateType.UPDATETYPE_BITMAP: self._listener.onUpdate(updateDataPDU.updateData.rectangles._array) @@ -345,7 +344,7 @@ class Client(PDULayer): """ @summary: send a synchronize PDU from client to server """ - synchronizePDU = data.SynchronizeDataPDU(self._transport._transport.getChannelId()) + synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId()) self.sendDataPDU(synchronizePDU) #ask for cooperation @@ -364,7 +363,7 @@ class Client(PDULayer): def sendInputEvents(self, pointerEvents): """ - send client input events + @summary: send client input events @param pointerEvents: list of pointer events """ pdu = data.ClientInputEventPDU() @@ -373,7 +372,7 @@ class Client(PDULayer): class Server(PDULayer): """ - Server Automata of PDU layer + @summary: Server Automata of PDU layer """ def __init__(self, listener): """ @@ -386,14 +385,14 @@ class Server(PDULayer): def connect(self): """ - Connect message for server automata + @summary: Connect message for server automata """ self.sendDemandActivePDU() self.setNextState(self.recvConfirmActivePDU) def recvConfirmActivePDU(self, s): """ - Receive confirm active PDU from client + @summary: Receive confirm active PDU from client Capabilities exchange Wait Client Synchronize PDU @param s: Stream @@ -417,7 +416,7 @@ class Server(PDULayer): def recvClientSynchronizePDU(self, s): """ - Receive from client + @summary: Receive from client Wait Control Cooperate PDU @param s: Stream from transport layer """ @@ -432,7 +431,7 @@ class Server(PDULayer): def recvClientControlCooperatePDU(self, s): """ - Receive control cooperate PDU from client + @summary: Receive control cooperate PDU from client Wait Control Request PDU @param s: Stream from transport layer """ @@ -447,7 +446,7 @@ class Server(PDULayer): def recvClientControlRequestPDU(self, s): """ - Receive last control PDU the request control PDU from client + @summary: Receive last control PDU the request control PDU from client Wait Font List PDU @param s: Stream from transport layer """ @@ -462,7 +461,7 @@ class Server(PDULayer): def recvClientFontListPDU(self, s): """ - Last synchronize packet from client to server + @summary: Last synchronize packet from client to server Send Server Finalize PDUs Wait any PDU @param s: Stream from transport layer @@ -483,7 +482,7 @@ class Server(PDULayer): def recvPDU(self, s): """ - Main receive function after connection sequence + @summary: Main receive function after connection sequence @param s: Stream from transport layer """ pdu = data.PDU() @@ -493,24 +492,25 @@ class Server(PDULayer): def readDataPDU(self, dataPDU): """ - read a data PDU object + @summary: read a data PDU object @param dataPDU: DataPDU object """ if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: errorMessage = "Unknown code %s"%hex(dataPDU.pduData.errorInfo.value) if data.ErrorInfo._MESSAGES_.has_key(dataPDU.pduData.errorInfo): errorMessage = data.ErrorInfo._MESSAGES_[dataPDU.pduData.errorInfo] - log.error("INFO PDU : %s"%errorMessage) + elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_INPUT: self._listener.onSlowPathInput(dataPDU.pduData.slowPathInputEvents._array) + elif dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SHUTDOWN_REQUEST: log.debug("Receive Shutdown Request") self._transport.close() def recvFastPath(self, fastPathS): """ - Implement IFastPathListener interface + @summary: Implement IFastPathListener interface Fast path is needed by RDP 8.0 @param fastPathS: Stream that contain fast path data """ @@ -536,9 +536,9 @@ class Server(PDULayer): def sendServerFinalizeSynchronizePDU(self): """ - Send last synchronize packet from server to client + @summary: Send last synchronize packet from server to client """ - synchronizePDU = data.SynchronizeDataPDU(self._transport._transport.getChannelId()) + synchronizePDU = data.SynchronizeDataPDU(self._transport.getChannelId()) self.sendDataPDU(synchronizePDU) #ask for cooperation @@ -557,7 +557,7 @@ class Server(PDULayer): def sendPDU(self, pduMessage): """ - Send a PDU data to transport layer + @summary: Send a PDU data to transport layer @param pduMessage: PDU message """ PDULayer.sendPDU(self, pduMessage) @@ -568,7 +568,7 @@ class Server(PDULayer): def sendBitmapUpdatePDU(self, bitmapDatas): """ - Send bitmap update data + @summary: Send bitmap update data @param bitmapDatas: List of data.BitmapData """ #check bitmap header for client that want it (very old client) diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py index 9d33e2f..be9ca67 100644 --- a/rdpy/protocol/rdp/rdp.py +++ b/rdpy/protocol/rdp/rdp.py @@ -342,7 +342,10 @@ class RDPServerController(pdu.layer.PDUServerListener): #transport pdu layer self._x224Layer = x224.Server(self._mcsLayer, privateKeyFileName, certificateFileName) #transport packet (protocol layer) - self._tpktLayer = tpkt.TPKT(self._x224Layer, self._pduLayer) + self._tpktLayer = tpkt.TPKT(self._x224Layer) + #fastpath stack + self._pduLayer.initFastPath(self._secLayer) + self._secLayer.initFastPath(self._tpktLayer) #set color depth of session self.setColorDepth(colorDepth) diff --git a/rdpy/protocol/rdp/sec.py b/rdpy/protocol/rdp/sec.py index de426e5..2fce0e8 100644 --- a/rdpy/protocol/rdp/sec.py +++ b/rdpy/protocol/rdp/sec.py @@ -21,12 +21,12 @@ Some use full methods for security in RDP """ -import sha, md5, rsa, rc4 -import gcc, lic, tpkt +import sha, md5, rsa +import gcc, lic, tpkt, mcs from rdpy.core.type import CompositeType, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8 from rdpy.core.layer import LayerAutomata, IStreamSender from rdpy.core.error import InvalidExpectedDataException -from rdpy.core import log, x509 +from rdpy.core import log, x509, rc4 class SecurityFlag(object): """ @@ -235,13 +235,13 @@ def generateKeys(clientRandom, serverRandom, method): initialSecondKey128 = finalHash(sessionKey[32:48], clientRandom, serverRandom) #generate valid key - if method == gcc.Encryption.ENCRYPTION_FLAG_40BIT: + if method == gcc.EncryptionMethod.ENCRYPTION_FLAG_40BIT: return gen40bits(macKey128), gen40bits(initialFirstKey128), gen40bits(initialSecondKey128) - elif method == gcc.Encryption.ENCRYPTION_FLAG_56BIT: + elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_56BIT: return gen56bits(macKey128), gen56bits(initialFirstKey128), gen56bits(initialSecondKey128) - elif method == gcc.Encryption.ENCRYPTION_FLAG_128BIT: + elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT: return macKey128, initialFirstKey128, initialSecondKey128 raise InvalidExpectedDataException("Bad encryption method") @@ -255,28 +255,17 @@ def updateKey(initialKey, currentKey, method): @see: http://msdn.microsoft.com/en-us/library/cc240792.aspx """ #generate valid key - if method == gcc.Encryption.ENCRYPTION_FLAG_40BIT: + if method == gcc.EncryptionMethod.ENCRYPTION_FLAG_40BIT: tempKey128 = tempKey(initialKey[:8], currentKey[:8]) return gen40bits(rc4.crypt(rc4.RC4Key(tempKey128[:8]), tempKey128[:8])) - elif method == gcc.Encryption.ENCRYPTION_FLAG_56BIT: + elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_56BIT: tempKey128 = tempKey(initialKey[:8], currentKey[:8]) return gen56bits(rc4.crypt(rc4.RC4Key(tempKey128[:8]), tempKey128[:8])) - elif method == gcc.Encryption.ENCRYPTION_FLAG_128BIT: + elif method == gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT: tempKey128 = tempKey(initialKey, currentKey) return rc4.crypt(rc4.RC4Key(tempKey128), tempKey128) - -def bin2bn(b): - """ - @summary: convert binary string to bignum - @param b: {str} binary string - @return: {long} bignum - """ - l = 0L - for ch in b: - l = (l<<8) | ord(ch) - return l class ClientSecurityExchangePDU(CompositeType): """ @@ -332,7 +321,7 @@ class RDPExtendedInfo(CompositeType): self.clientSessionId = UInt32Le() self.performanceFlags = UInt32Le() -class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastPathSender): +class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastPathSender, mcs.IGCCConfig): """ @summary: Standard RDP security layer This layer is Transparent as possible for upper layer @@ -347,7 +336,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP self._fastPathPresentation = None #credentials - self._info = RDPInfo(extendedInfoConditional = lambda:(self._transport.getGCCServerSettings().SC_CORE.rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) + self._info = RDPInfo(extendedInfoConditional = lambda:(self.getGCCServerSettings().SC_CORE.rdpVersion.value == gcc.Version.RDP_VERSION_5_PLUS)) #True if classic encryption is enable self._enableEncryption = False @@ -378,7 +367,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP if self._nbDecryptedPacket == 4096: log.info("update decrypt key") self._currentDecrytKey = updateKey( self._initialDecrytKey, self._currentDecrytKey, - self._transport.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) + self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey) self._nbDecryptedPacket = 0 @@ -405,7 +394,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP if self._nbEncryptedPacket == 4096: log.info("update encrypt key") self._currentEncryptKey = updateKey( self._initialEncryptKey, self._currentEncryptKey, - self._transport.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) + self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey) self._nbEncryptedPacket = 0 @@ -491,6 +480,34 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP @param fastPathSender: {tpkt.FastPathSender} """ self._fastPathTransport = fastPathSender + + def getUserId(self): + """ + @return: {integer} mcs user id + @see: mcs.IGCCConfig + """ + return self._transport.getUserId() + + def getChannelId(self): + """ + @return: {integer} return channel id of proxy + @see: mcs.IGCCConfig + """ + return self._transport.getChannelId() + + def getGCCClientSettings(self): + """ + @return: {gcc.Settings} mcs layer gcc client settings + @see: mcs.IGCCConfig + """ + return self._transport.getGCCClientSettings() + + def getGCCServerSettings(self): + """ + @return: {gcc.Settings} mcs layer gcc server settings + @see: mcs.IGCCConfig + """ + return self._transport.getGCCServerSettings() class Client(SecLayer): """ @@ -502,41 +519,20 @@ class Client(SecLayer): def connect(self): """ - @summary: send client random + @summary: send client random if needed and send info packet """ - - self._enableEncryption = self._transport.getGCCClientSettings().CS_CORE.serverSelectedProtocol == 0 + self._enableEncryption = self.getGCCClientSettings().CS_CORE.serverSelectedProtocol == 0 if self._enableEncryption: - #generate client random - clientRandom = rsa.randnum.read_random_bits(256) - self._macKey, self._initialDecrytKey, self._initialEncryptKey = generateKeys( clientRandom, - self._transport.getGCCServerSettings().SC_SECURITY.serverRandom.value, - self._transport.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) - #initialize keys - self._currentDecrytKey = self._initialDecrytKey - self._currentEncryptKey = self._initialEncryptKey - self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey) - self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey) - - #send client random encrypted with - certificate = self._transport.getGCCServerSettings().SC_SECURITY.serverCertificate.certData._value - if isinstance(certificate, gcc.ProprietaryServerCertificate): - modulus = bin2bn(certificate.PublicKeyBlob.modulus.value[::-1]) - publicExponent = certificate.PublicKeyBlob.pubExp.value - elif isinstance(certificate, gcc.X509CertificateChain): - modulus, publicExponent = x509.extractRSAKey(x509.load(certificate.CertBlobArray[-1].abCert.value)) - else: - raise InvalidExpectedDataException("unknown certificate type") - - #reverse because bignum in little endian - serverPublicKey = rsa.PublicKey(modulus, publicExponent) - - message = ClientSecurityExchangePDU() - #reverse because bignum in little endian - message.encryptedClientRandom.value = rsa.encrypt(clientRandom[::-1], serverPublicKey)[::-1] - self.sendFlagged(SecurityFlag.SEC_EXCHANGE_PKT, message) + self.sendClientRandom() + self.sendInfoPkt() + + def sendInfoPkt(self): + """ + @summary: send information packet (with credentials) + next state -> recvLicenceInfo + """ secFlag = SecurityFlag.SEC_INFO_PKT if self._enableEncryption: secFlag |= SecurityFlag.SEC_ENCRYPT @@ -544,6 +540,29 @@ class Client(SecLayer): self.setNextState(self.recvLicenceInfo) + def sendClientRandom(self): + """ + @summary: generate and send client random and init session keys + """ + #generate client random + clientRandom = rsa.randnum.read_random_bits(256) + self._macKey, self._initialDecrytKey, self._initialEncryptKey = generateKeys( clientRandom, + self.getGCCServerSettings().SC_SECURITY.serverRandom.value, + self.getGCCServerSettings().SC_SECURITY.encryptionMethod.value) + #initialize keys + self._currentDecrytKey = self._initialDecrytKey + self._currentEncryptKey = self._initialEncryptKey + self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey) + self._encryptRc4 = rc4.RC4Key(self._currentEncryptKey) + + #send client random encrypted with + modulus, publicExponent = self.getGCCServerSettings().SC_SECURITY.serverCertificate.certData.getPublicKey() + serverPublicKey = rsa.PublicKey(modulus, publicExponent) + message = ClientSecurityExchangePDU() + #reverse because bignum in little endian + message.encryptedClientRandom.value = rsa.encrypt(clientRandom[::-1], serverPublicKey)[::-1] + self.sendFlagged(SecurityFlag.SEC_EXCHANGE_PKT, message) + def recvLicenceInfo(self, s): """ @summary: Read license info packet and check if is a valid client info @@ -574,7 +593,20 @@ class Server(SecLayer): """ @summary: init automata to wait info packet """ - self.setNextState(self.recvInfoPkt) + self._enableEncryption = self.getGCCClientSettings().CS_CORE.serverSelectedProtocol == 0 + if self._enableEncryption: + self.setNextState(self.recvClientRandom) + else: + self.setNextState(self.recvInfoPkt) + + def recvClientRandom(self, s): + """ + @summary: receive client random and generate session keys + @param s: {Stream} + """ + message = ClientSecurityExchangePDU() + s.readType(message) + def recvInfoPkt(self, s): """ diff --git a/rdpy/protocol/rdp/tpkt.py b/rdpy/protocol/rdp/tpkt.py index 14d42d3..65cc51e 100644 --- a/rdpy/protocol/rdp/tpkt.py +++ b/rdpy/protocol/rdp/tpkt.py @@ -76,8 +76,8 @@ class IFastPathSender(object): def sendFastPath(self, secFlag, fastPathS): """ @summary: Send fastPathS Type as fast path packet - @param secFlag: {SecFlags} - @param fastPathS: type transform to stream and send as fastpath + @param secFlag: {integer} Security flag for fastpath packet + @param fastPathS: {Type | Tuple} type transform to stream and send as fastpath """ raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendFastPath", "IFastPathSender")) @@ -103,7 +103,7 @@ class TPKT(RawLayer, IFastPathSender): """ def __init__(self, presentation): """ - @param presentation: presentation layer, in RDP case is x224 layer + @param presentation: {Layer} presentation layer, in RDP case is x224 layer """ RawLayer.__init__(self, presentation) #length may be coded on more than 1 bytes @@ -133,8 +133,8 @@ class TPKT(RawLayer, IFastPathSender): def readHeader(self, data): """ - Read header of TPKT packet - @param data: Stream received from twisted layer + @summary: Read header of TPKT packet + @param data: {Stream} received from twisted layer """ #first read packet version version = UInt8() @@ -158,8 +158,8 @@ class TPKT(RawLayer, IFastPathSender): def readExtendedHeader(self, data): """ - Header may be on 4 bytes - @param data: Stream from twisted layer + @summary: Header may be on 4 bytes + @param data: {Stream} from twisted layer """ #next state is read data size = UInt16Be() @@ -168,8 +168,8 @@ class TPKT(RawLayer, IFastPathSender): def readExtendedFastPathHeader(self, data): """ - Fast path header may be on 1 byte more - @param data: Stream from twisted layer + @summary: Fast path header may be on 1 byte more + @param data: {Stream} from twisted layer """ leftPart = UInt8() data.readType(leftPart) @@ -180,16 +180,16 @@ class TPKT(RawLayer, IFastPathSender): def readFastPath(self, data): """ - Fast path data - @param data: Stream from twisted layer + @summary: Fast path data + @param data: {Stream} from twisted layer """ self._fastPathListener.recvFastPath(self._secFlag, data) self.expect(2, self.readHeader) def readData(self, data): """ - Read classic TPKT packet, last state in tpkt automata - @param data: Stream with correct size + @summary: Read classic TPKT packet, last state in tpkt automata + @param data: {Stream} with correct size """ #next state is pass to self._presentation.recv(data) @@ -197,14 +197,14 @@ class TPKT(RawLayer, IFastPathSender): def send(self, message): """ - Send encompassed data - @param message: network.Type message to send + @summary: Send encompassed data + @param message: {network.Type} message to send """ RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_X224), UInt8(0), UInt16Be(sizeof(message) + 4), message)) def sendFastPath(self, secFlag, fastPathS): """ @param fastPathS: type transform to stream and send as fastpath - @param secFlag: {SecFlags} + @param secFlag: {integer} Security flag for fastpath packet """ RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH | secFlag), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS)) \ No newline at end of file diff --git a/rdpy/protocol/rdp/x224.py b/rdpy/protocol/rdp/x224.py index d52562f..5f04c04 100644 --- a/rdpy/protocol/rdp/x224.py +++ b/rdpy/protocol/rdp/x224.py @@ -130,9 +130,8 @@ class X224Layer(LayerAutomata, IStreamSender): @param presentation: upper layer, MCS layer in RDP case """ LayerAutomata.__init__(self, presentation) - #default selectedProtocol is SSl #client requested selectedProtocol - self._requestedProtocol = Protocols.PROTOCOL_RDP + self._requestedProtocol = Protocols.PROTOCOL_RDP | Protocols.PROTOCOL_SSL #server selected selectedProtocol self._selectedProtocol = Protocols.PROTOCOL_SSL