finish RDP basic security layer client side, start server side

This commit is contained in:
speyrefitte
2014-12-15 18:26:27 +01:00
parent f9f92b8215
commit f2481149d9
11 changed files with 275 additions and 156 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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):
"""

View File

@@ -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)),

View File

@@ -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):
"""

View File

@@ -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)

View File

@@ -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)

View File

@@ -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):
"""

View File

@@ -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))

View File

@@ -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