add basic RDP secure layer...

This commit is contained in:
speyrefitte
2014-12-08 18:15:28 +01:00
parent e7c6e61a25
commit ccf0156150
14 changed files with 391 additions and 215 deletions

View File

@@ -26,6 +26,7 @@ import sys, os, getopt, socket
from PyQt4 import QtGui, QtCore from PyQt4 import QtGui, QtCore
from rdpy.ui.qt4 import RDPClientQt from rdpy.ui.qt4 import RDPClientQt
from rdpy.protocol.rdp import rdp from rdpy.protocol.rdp import rdp
from rdpy.base.error import RDPSecurityNegoFail
import rdpy.base.log as log import rdpy.base.log as log
log._LOG_LEVEL = log.Level.INFO log._LOG_LEVEL = log.Level.INFO
@@ -54,6 +55,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
self._keyboardLayout = keyboardLayout self._keyboardLayout = keyboardLayout
self._optimized = optimized self._optimized = optimized
self._w = None self._w = None
self._basicRDPSecurity = False
def buildObserver(self, controller, addr): def buildObserver(self, controller, addr):
""" """
@@ -80,6 +82,9 @@ class RDPClientQtFactory(rdp.ClientFactory):
controller.setHostname(socket.gethostname()) controller.setHostname(socket.gethostname())
if self._optimized: if self._optimized:
controller.setPerformanceSession() controller.setPerformanceSession()
if self._basicRDPSecurity:
controller.setRDPBasicSecurity()
return client return client
@@ -92,6 +97,12 @@ class RDPClientQtFactory(rdp.ClientFactory):
@param connector: twisted connector use for rdp connection (use reconnect to restart connection) @param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection @param reason: str use to advertise reason of lost connection
""" """
#try reconnect with basic RDP security
if reason.type == RDPSecurityNegoFail and not self._basicRDPSecurity:
self._basicRDPSecurity = True
connector.connect()
return
QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason) QtGui.QMessageBox.warning(self._w, "Warning", "Lost connection : %s"%reason)
reactor.stop() reactor.stop()
app.exit() app.exit()

View File

@@ -90,3 +90,13 @@ class ErrorReportedFromPeer(Exception):
@param message: message show when exception is raised @param message: message show when exception is raised
""" """
Exception.__init__(self, message) Exception.__init__(self, message)
class RDPSecurityNegoFail(Exception):
"""
@summary: Raise when security nego fail
"""
def __init__(self, message = ""):
"""
@param message: message show when exception is raised
"""
Exception.__init__(self, message)

View File

@@ -129,10 +129,11 @@ class RawLayerClientFactory(protocol.ClientFactory):
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildRawLayer", "RawLayerClientFactory")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildRawLayer", "RawLayerClientFactory"))
def connectionLost(self, rawlayer): def connectionLost(self, rawlayer, reason):
""" """
@summary: Override this method to handle connection lost @summary: Override this method to handle connection lost
@param rawlayer: rawLayer that cause connectionLost event @param rawlayer: rawLayer that cause connectionLost event
@param reason: twisted reason
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "connectionLost", "RawLayerClientFactory")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "connectionLost", "RawLayerClientFactory"))
@@ -156,10 +157,11 @@ class RawLayerServerFactory(protocol.ClientFactory):
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener"))
def connectionLost(self, rawlayer): def connectionLost(self, rawlayer, reason):
""" """
@summary: Override this method to handle connection lost @summary: Override this method to handle connection lost
@param rawlayer: rawLayer that cause connectionLost event @param rawlayer: rawLayer that cause connectionLost event
@param reason: twisted reason
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener"))
@@ -218,7 +220,7 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
@summary: Call from twisted engine when protocol is closed @summary: Call from twisted engine when protocol is closed
@param reason: str represent reason of close connection @param reason: str represent reason of close connection
""" """
self._factory.connectionLost(self) self._factory.connectionLost(self, reason)
def close(self): def close(self):
""" """

View File

@@ -188,6 +188,13 @@ class KeyboardLayout(object):
DUTCH = 0x00000413 DUTCH = 0x00000413
NORWEGIAN = 0x00000414 NORWEGIAN = 0x00000414
class CertificateType(object):
"""
@see: http://msdn.microsoft.com/en-us/library/cc240521.aspx
"""
CERT_CHAIN_VERSION_1 = 0x00000001
CERT_CHAIN_VERSION_2 = 0x00000002
class DataBlock(CompositeType): class DataBlock(CompositeType):
""" """
Block settings Block settings
@@ -268,7 +275,7 @@ class ClientSecurityData(CompositeType):
def __init__(self, readLen = None): def __init__(self, readLen = None):
CompositeType.__init__(self, readLen = readLen) CompositeType.__init__(self, readLen = readLen)
self.encryptionMethods = UInt32Le(Encryption.ENCRYPTION_FLAG_128BIT) self.encryptionMethods = UInt32Le(Encryption.ENCRYPTION_FLAG_40BIT | Encryption.ENCRYPTION_FLAG_56BIT | Encryption.ENCRYPTION_FLAG_128BIT)
self.extEncryptionMethods = UInt32Le() self.extEncryptionMethods = UInt32Le()
class ServerSecurityData(CompositeType): class ServerSecurityData(CompositeType):
@@ -285,7 +292,59 @@ class ServerSecurityData(CompositeType):
self.serverRandomLen = UInt32Le(0x00000020, constant = True, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0)) self.serverRandomLen = UInt32Le(0x00000020, constant = True, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
self.serverCertLen = UInt32Le(lambda:sizeof(self.serverCertificate), conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0)) self.serverCertLen = UInt32Le(lambda:sizeof(self.serverCertificate), conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
self.serverRandom = String(readLen = self.serverRandomLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0)) self.serverRandom = String(readLen = self.serverRandomLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
self.serverCertificate = String(readLen = self.serverCertLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0)) self.serverCertificate = ServerCertificate(readLen = self.serverCertLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
class ServerCertificate(CompositeType):
"""
@summary: Server certificate structure
@see: http://msdn.microsoft.com/en-us/library/cc240521.aspx
"""
def __init__(self, readLen, conditional):
CompositeType.__init__(self, readLen = readLen, conditional = conditional)
self.dwVersion = UInt32Le()
def CertificateFactory():
"""
Closure for capability factory
"""
for c in [ProprietaryServerCertificate]:
if self.dwVersion.value & 0x7fffffff == c._TYPE_:
return c()
raise InvalidExpectedDataException("unknown certificate type : %s (RDPY doesn't support x.509 format please repport a bug)"%hex(self.dwVersion.value))
self.certData = FactoryType(CertificateFactory)
class ProprietaryServerCertificate(CompositeType):
"""
@summary: microsoft proprietary certificate
@see: http://msdn.microsoft.com/en-us/library/cc240519.aspx
"""
_TYPE_ = CertificateType.CERT_CHAIN_VERSION_1
def __init__(self):
CompositeType.__init__(self)
self.dwSigAlgId = UInt32Le(0x00000001, constant = True)
self.dwKeyAlgId = UInt32Le(0x00000001, constant = True)
self.wPublicKeyBlobType = UInt16Le(0x0006, constant = True)
self.wPublicKeyBlobLen = UInt16Le(lambda:sizeof(self.PublicKeyBlob))
self.PublicKeyBlob = RSAPublicKey(readLen = self.wPublicKeyBlobLen)
self.wSignatureBlobType = UInt16Le(0x0008, constant = True)
self.wSignatureBlobLen = UInt16Le(lambda:sizeof(self.SignatureBlob))
self.SignatureBlob = String(readLen = self.wSignatureBlobLen)
class RSAPublicKey(CompositeType):
"""
@see: http://msdn.microsoft.com/en-us/library/cc240520.aspx
"""
def __init__(self, readLen):
CompositeType.__init__(self, readLen = readLen)
self.magic = UInt32Le(0x31415352, constant = True)
self.keylen = UInt32Le(lambda:sizeof(self.modulus))
self.bitlen = UInt32Le()
self.datalen = UInt32Le()
self.pubExp = UInt32Le()
self.modulus = String(readLen = lambda:(self.keylen - 8))
self.padding = String("\x00" * 8, constant = True)
class ChannelDef(CompositeType): class ChannelDef(CompositeType):
""" """

View File

@@ -25,7 +25,7 @@
from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream from rdpy.network.type import CompositeType, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream
from rdpy.base.error import InvalidExpectedDataException from rdpy.base.error import InvalidExpectedDataException
import rdpy.base.log as log import rdpy.base.log as log
import sec, rc4 import rc4, sec
class MessageType(object): class MessageType(object):
""" """
@@ -271,8 +271,8 @@ class LicenseManager(object):
""" """
@summary: generate key for license session @summary: generate key for license session
""" """
masterSecret = sec.generateMicrosoftKey(self._preMasterSecret, self._clientRandom, self._serverRandom) masterSecret = sec.generateMicrosoftKeyABBCCC(self._preMasterSecret, self._clientRandom, self._serverRandom)
sessionKeyBlob = sec.generateMicrosoftKey(masterSecret, self._serverRandom, self._clientRandom) sessionKeyBlob = sec.generateMicrosoftKeyABBCCC(masterSecret, self._serverRandom, self._clientRandom)
self._macSalt = sessionKeyBlob[:16] self._macSalt = sessionKeyBlob[:16]
self._licenseKey = sec.finalHash(sessionKeyBlob[16:32], self._clientRandom, self._serverRandom) self._licenseKey = sec.finalHash(sessionKeyBlob[16:32], self._clientRandom, self._serverRandom)

View File

@@ -30,18 +30,18 @@ from rdpy.base.error import InvalidExpectedDataException, InvalidValue, InvalidS
from rdpy.protocol.rdp.ber import writeLength from rdpy.protocol.rdp.ber import writeLength
import rdpy.base.log as log import rdpy.base.log as log
import ber, gcc, per import ber, gcc, per, sec
class Message(object): class Message(object):
""" """
Message type @summary: Message type
""" """
MCS_TYPE_CONNECT_INITIAL = 0x65 MCS_TYPE_CONNECT_INITIAL = 0x65
MCS_TYPE_CONNECT_RESPONSE = 0x66 MCS_TYPE_CONNECT_RESPONSE = 0x66
class DomainMCSPDU: class DomainMCSPDU:
""" """
Domain MCS PDU header @summary: Domain MCS PDU header
""" """
ERECT_DOMAIN_REQUEST = 1 ERECT_DOMAIN_REQUEST = 1
DISCONNECT_PROVIDER_ULTIMATUM = 8 DISCONNECT_PROVIDER_ULTIMATUM = 8
@@ -54,40 +54,42 @@ class DomainMCSPDU:
class Channel: class Channel:
""" """
Channel id of main channels use in RDP @summary: Channel id of main channels use in RDP
""" """
MCS_GLOBAL_CHANNEL = 1003 MCS_GLOBAL_CHANNEL = 1003
MCS_USERCHANNEL_BASE = 1001 MCS_USERCHANNEL_BASE = 1001
class MCSLayer(LayerAutomata): class MCSLayer(LayerAutomata):
""" """
Multiple Channel Service layer @summary: Multiple Channel Service layer
the main layer of RDP protocol the main layer of RDP protocol
is why he can do everything and more! is why he can do everything and more!
""" """
class MCSProxySender(Layer, IStreamSender): class MCSProxySender(Layer, IStreamSender):
""" """
Proxy use to set as transport layer for upper channel @summary: Proxy use to set as transport layer for upper channel
use to abstract channel id for presentation layer use to abstract channel id for presentation layer
""" """
def __init__(self, mcs, channelId): def __init__(self, presentation, mcs, channelId):
""" """
@param presentation: presentation layer
@param mcs: MCS layer use as proxy @param mcs: MCS layer use as proxy
@param channelId: channel id for presentation layer @param channelId: channel id for presentation layer
""" """
Layer.__init__(self, presentation)
self._mcs = mcs self._mcs = mcs
self._channelId = channelId self._channelId = channelId
def send(self, data): def send(self, data):
""" """
A send proxy function, use channel id and specific @summary: A send proxy function, use channel id and specific
send function of MCS layer send function of MCS layer
""" """
self._mcs.send(self._channelId, data) self._mcs.send(self._channelId, data)
def close(self): def close(self):
""" """
Close wrapped layer @summary: Close wrapped layer
""" """
self._mcs.close() self._mcs.close()
@@ -139,7 +141,7 @@ class MCSLayer(LayerAutomata):
def close(self): def close(self):
""" """
Send disconnect provider ultimatum @summary: Send disconnect provider ultimatum
""" """
self._transport.send((UInt8(self.writeMCSPDUHeader(DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM, 1)), self._transport.send((UInt8(self.writeMCSPDUHeader(DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM, 1)),
per.writeEnumerates(0x80), String("\x00" * 6))) per.writeEnumerates(0x80), String("\x00" * 6)))
@@ -147,7 +149,7 @@ class MCSLayer(LayerAutomata):
def allChannelConnected(self): def allChannelConnected(self):
""" """
All channels are connected to MCS layer @summary: All channels are connected to MCS layer
Send connect to upper channel Send connect to upper channel
And prepare MCS layer to receive data And prepare MCS layer to receive data
""" """
@@ -156,12 +158,11 @@ class MCSLayer(LayerAutomata):
#try connection on all requested channel #try connection on all requested channel
for (channelId, layer) in self._channels.iteritems(): for (channelId, layer) in self._channels.iteritems():
#use proxy for each channel #use proxy for each channel
layer._transport = MCSLayer.MCSProxySender(self, channelId) MCSLayer.MCSProxySender(layer, self, channelId).connect()
layer.connect()
def send(self, channelId, data): def send(self, channelId, data):
""" """
Specific send function for channelId @summary: Specific send function for channelId
@param channelId: Channel use to send @param channelId: Channel use to send
@param data: message to send @param data: message to send
""" """
@@ -173,7 +174,7 @@ class MCSLayer(LayerAutomata):
def recvData(self, data): def recvData(self, data):
""" """
Main receive method @summary: Main receive method
@param data: Stream @param data: Stream
""" """
opcode = UInt8() opcode = UInt8()
@@ -205,7 +206,7 @@ class MCSLayer(LayerAutomata):
def writeDomainParams(self, maxChannels, maxUsers, maxTokens, maxPduSize): def writeDomainParams(self, maxChannels, maxUsers, maxTokens, maxPduSize):
""" """
Write a special domain parameter structure @summary: Write a special domain parameter structure
use in connection sequence use in connection sequence
@param maxChannels: number of MCS channel use @param maxChannels: number of MCS channel use
@param maxUsers: number of MCS user used (1) @param maxUsers: number of MCS user used (1)
@@ -220,7 +221,7 @@ class MCSLayer(LayerAutomata):
def writeMCSPDUHeader(self, mcsPdu, options = 0): def writeMCSPDUHeader(self, mcsPdu, options = 0):
""" """
Write MCS PDU header @summary: Write MCS PDU header
@param mcsPdu: PDU code @param mcsPdu: PDU code
@param options: option contains in header @param options: option contains in header
@return: UInt8 @return: UInt8
@@ -229,7 +230,7 @@ class MCSLayer(LayerAutomata):
def readMCSPDUHeader(self, opcode, mcsPdu): def readMCSPDUHeader(self, opcode, mcsPdu):
""" """
Read mcsPdu header and return options parameter @summary: Read mcsPdu header and return options parameter
@param opcode: opcode @param opcode: opcode
@param mcsPdu: mcsPdu will be checked @param mcsPdu: mcsPdu will be checked
@return: true if opcode is correct @return: true if opcode is correct
@@ -238,7 +239,7 @@ class MCSLayer(LayerAutomata):
def readDomainParams(self, s): def readDomainParams(self, s):
""" """
Read domain parameters structure @summary: Read domain parameters structure
@return: (max_channels, max_users, max_tokens, max_pdu_size) @return: (max_channels, max_users, max_tokens, max_pdu_size)
""" """
if not ber.readUniversalTag(s, ber.Tag.BER_TAG_SEQUENCE, True): if not ber.readUniversalTag(s, ber.Tag.BER_TAG_SEQUENCE, True):
@@ -256,7 +257,7 @@ class MCSLayer(LayerAutomata):
class Client(MCSLayer): class Client(MCSLayer):
""" """
Client automata of multiple channel service layer @summary: Client automata of multiple channel service layer
""" """
def __init__(self, presentation, virtualChannels = []): def __init__(self, presentation, virtualChannels = []):
""" """
@@ -272,7 +273,7 @@ class Client(MCSLayer):
def connect(self): def connect(self):
""" """
Connect message in client automata case @summary: Connect message in client automata case
Send ConnectInitial Send ConnectInitial
Wait ConnectResponse Wait ConnectResponse
""" """
@@ -286,7 +287,7 @@ class Client(MCSLayer):
def connectNextChannel(self): def connectNextChannel(self):
""" """
Send sendChannelJoinRequest message on next disconnect channel @summary: Send sendChannelJoinRequest message on next disconnect channel
Send channel request or connect upper layer if all channels are connected Send channel request or connect upper layer if all channels are connected
Wait channel confirm Wait channel confirm
""" """
@@ -314,7 +315,7 @@ class Client(MCSLayer):
def recvConnectResponse(self, data): def recvConnectResponse(self, data):
""" """
Receive MCS connect response from server @summary: Receive MCS connect response from server
Send Erect domain Request Send Erect domain Request
Send Attach User Request Send Attach User Request
Wait Attach User Confirm Wait Attach User Confirm
@@ -340,7 +341,7 @@ class Client(MCSLayer):
def recvAttachUserConfirm(self, data): def recvAttachUserConfirm(self, data):
""" """
Receive an attach user confirm @summary: Receive an attach user confirm
Send Connect Channel Send Connect Channel
@param data: Stream @param data: Stream
""" """
@@ -359,7 +360,7 @@ class Client(MCSLayer):
def recvChannelJoinConfirm(self, data): def recvChannelJoinConfirm(self, data):
""" """
Receive a channel join confirm from server @summary: Receive a channel join confirm from server
client automata function client automata function
@param data: Stream @param data: Stream
""" """
@@ -390,7 +391,7 @@ class Client(MCSLayer):
def sendConnectInitial(self): def sendConnectInitial(self):
""" """
Send connect initial packet @summary: Send connect initial packet
client automata function client automata function
""" """
ccReq = gcc.writeConferenceCreateRequest(self._clientSettings) ccReq = gcc.writeConferenceCreateRequest(self._clientSettings)
@@ -406,7 +407,7 @@ class Client(MCSLayer):
def sendErectDomainRequest(self): def sendErectDomainRequest(self):
""" """
Send a formated erect domain request for RDP connection @summary: Send a formated erect domain request for RDP connection
""" """
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)), self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)),
per.writeInteger(0), per.writeInteger(0),
@@ -414,13 +415,13 @@ class Client(MCSLayer):
def sendAttachUserRequest(self): def sendAttachUserRequest(self):
""" """
Send a formated attach user request for RDP connection @summary: Send a formated attach user request for RDP connection
""" """
self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST))) self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST)))
def sendChannelJoinRequest(self, channelId): def sendChannelJoinRequest(self, channelId):
""" """
Send a formated Channel join request from client to server @summary: Send a formated Channel join request from client to server
client automata function client automata function
@param channelId: id of channel requested @param channelId: id of channel requested
""" """
@@ -430,7 +431,7 @@ class Client(MCSLayer):
class Server(MCSLayer): class Server(MCSLayer):
""" """
Server automata of multiple channel service layer @summary: Server automata of multiple channel service layer
""" """
def __init__(self, presentation, virtualChannels = []): def __init__(self, presentation, virtualChannels = []):
""" """
@@ -443,7 +444,7 @@ class Server(MCSLayer):
def connect(self): def connect(self):
""" """
Connect message for server automata @summary: Connect message for server automata
Wait Connect Initial Wait Connect Initial
""" """
self._serverSettings.getBlock(gcc.MessageType.SC_CORE).clientRequestedProtocol.value = self._transport._requestedProtocol self._serverSettings.getBlock(gcc.MessageType.SC_CORE).clientRequestedProtocol.value = self._transport._requestedProtocol
@@ -451,7 +452,7 @@ class Server(MCSLayer):
def recvConnectInitial(self, data): def recvConnectInitial(self, data):
""" """
Receive MCS connect initial from client @summary: Receive MCS connect initial from client
Send Connect Response Send Connect Response
Wait Erect Domain Request Wait Erect Domain Request
@param data: Stream @param data: Stream
@@ -482,7 +483,7 @@ class Server(MCSLayer):
def recvErectDomainRequest(self, data): def recvErectDomainRequest(self, data):
""" """
Receive erect domain request @summary: Receive erect domain request
Wait Attach User Request Wait Attach User Request
@param data: Stream @param data: Stream
""" """
@@ -499,7 +500,7 @@ class Server(MCSLayer):
def recvAttachUserRequest(self, data): def recvAttachUserRequest(self, data):
""" """
Receive Attach user request @summary: Receive Attach user request
Send Attach User Confirm Send Attach User Confirm
Wait Channel Join Request Wait Channel Join Request
@param data: Stream @param data: Stream
@@ -515,7 +516,7 @@ class Server(MCSLayer):
def recvChannelJoinRequest(self, data): def recvChannelJoinRequest(self, data):
""" """
Receive for each client channel a request @summary: Receive for each client channel a request
Send Channel Join Confirm or Connect upper layer when all channel are joined Send Channel Join Confirm or Connect upper layer when all channel are joined
@param data: Stream @param data: Stream
@@ -540,7 +541,7 @@ class Server(MCSLayer):
def sendConnectResponse(self): def sendConnectResponse(self):
""" """
Send connect response @summary: Send connect response
""" """
ccReq = gcc.writeConferenceCreateResponse(self._serverSettings) ccReq = gcc.writeConferenceCreateResponse(self._serverSettings)
ccReqStream = Stream() ccReqStream = Stream()
@@ -552,7 +553,7 @@ class Server(MCSLayer):
def sendAttachUserConfirm(self): def sendAttachUserConfirm(self):
""" """
Send attach user confirm @summary: Send attach user confirm
""" """
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2), self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2),
per.writeEnumerates(0), per.writeEnumerates(0),
@@ -560,7 +561,7 @@ class Server(MCSLayer):
def sendChannelJoinConfirm(self, channelId, confirm): def sendChannelJoinConfirm(self, channelId, confirm):
""" """
Send a confirm channel (or not) to client @summary: Send a confirm channel (or not) to client
@param channelId: id of channel @param channelId: id of channel
@param confirm: connection state @param confirm: connection state
""" """

View File

@@ -27,28 +27,6 @@ from rdpy.base.error import InvalidExpectedDataException
import rdpy.base.log as log import rdpy.base.log as log
import caps, order import caps, order
class SecurityFlag(object):
"""
Microsoft security flags
@see: http://msdn.microsoft.com/en-us/library/cc240579.aspx
"""
SEC_EXCHANGE_PKT = 0x0001
SEC_TRANSPORT_REQ = 0x0002
RDP_SEC_TRANSPORT_RSP = 0x0004
SEC_ENCRYPT = 0x0008
SEC_RESET_SEQNO = 0x0010
SEC_IGNORE_SEQNO = 0x0020
SEC_INFO_PKT = 0x0040
SEC_LICENSE_PKT = 0x0080
SEC_LICENSE_ENCRYPT_CS = 0x0200
SEC_LICENSE_ENCRYPT_SC = 0x0200
SEC_REDIRECTION_PKT = 0x0400
SEC_SECURE_CHECKSUM = 0x0800
SEC_AUTODETECT_REQ = 0x1000
SEC_AUTODETECT_RSP = 0x2000
SEC_HEARTBEAT = 0x4000
SEC_FLAGSHI_VALID = 0x8000
class InfoFlag(object): class InfoFlag(object):
""" """
Client capabilities informations Client capabilities informations

View File

@@ -33,17 +33,17 @@ import lic, data, caps
class PDUClientListener(object): class PDUClientListener(object):
""" """
Interface for PDU client automata listener @summary: Interface for PDU client automata listener
""" """
def onReady(self): def onReady(self):
""" """
Event call when PDU layer is ready to send events @summary: Event call when PDU layer is ready to send events
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUClientListener"))
def onUpdate(self, rectangles): def onUpdate(self, rectangles):
""" """
call when a bitmap data is received from update PDU @summary: call when a bitmap data is received from update PDU
@param rectangles: [pdu.BitmapData] struct @param rectangles: [pdu.BitmapData] struct
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "PDUClientListener"))
@@ -56,24 +56,24 @@ class PDUClientListener(object):
class PDUServerListener(object): class PDUServerListener(object):
""" """
Interface for PDU server automata listener @summary: Interface for PDU server automata listener
""" """
def onReady(self): def onReady(self):
""" """
Event call when PDU layer is ready to send update @summary: Event call when PDU layer is ready to send update
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUServerListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "PDUServerListener"))
def onSlowPathInput(self, slowPathInputEvents): def onSlowPathInput(self, slowPathInputEvents):
""" """
Event call when slow path input are available @summary: Event call when slow path input are available
@param slowPathInputEvents: [data.SlowPathInputEvent] @param slowPathInputEvents: [data.SlowPathInputEvent]
""" """
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onSlowPathInput", "PDUServerListener")) raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onSlowPathInput", "PDUServerListener"))
class PDULayer(LayerAutomata): class PDULayer(LayerAutomata):
""" """
Global channel for MCS that handle session @summary: Global channel for MCS that handle session
identification user, licensing management, and capabilities exchange identification user, licensing management, and capabilities exchange
""" """
def __init__(self): def __init__(self):
@@ -112,21 +112,21 @@ class PDULayer(LayerAutomata):
def sendPDU(self, pduMessage): def sendPDU(self, pduMessage):
""" """
Send a PDU data to transport layer @summary: Send a PDU data to transport layer
@param pduMessage: PDU message @param pduMessage: PDU message
""" """
self._transport.send(data.PDU(self._transport.getUserId(), pduMessage)) self._transport.send(data.PDU(self._transport.getUserId(), pduMessage))
def sendDataPDU(self, pduData): def sendDataPDU(self, pduData):
""" """
Send an PDUData to transport layer @summary: Send an PDUData to transport layer
@param pduData: PDU data message @param pduData: PDU data message
""" """
self.sendPDU(data.DataPDU(pduData, self._shareId)) self.sendPDU(data.DataPDU(pduData, self._shareId))
class Client(PDULayer, tpkt.IFastPathListener): class Client(PDULayer, tpkt.IFastPathListener):
""" """
Client automata of PDU layer @summary: Client automata of PDU layer
""" """
def __init__(self, listener): def __init__(self, listener):
""" """
@@ -140,7 +140,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def connect(self): def connect(self):
""" """
Connect message in client automata @summary: Connect message in client automata
Send INfo packet (credentials) Send INfo packet (credentials)
Wait License info Wait License info
""" """
@@ -152,7 +152,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def close(self): def close(self):
""" """
Send PDU close packet and call close method on transport method @summary: Send PDU close packet and call close method on transport method
""" """
self._transport.close() self._transport.close()
#self.sendDataPDU(data.ShutdownRequestPDU()) #self.sendDataPDU(data.ShutdownRequestPDU())
@@ -166,7 +166,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvLicenceInfo(self, s): def recvLicenceInfo(self, s):
""" """
Read license info packet and check if is a valid client info @summary: Read license info packet and check if is a valid client info
Wait Demand Active PDU Wait Demand Active PDU
@param s: Stream @param s: Stream
""" """
@@ -183,7 +183,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvDemandActivePDU(self, s): def recvDemandActivePDU(self, s):
""" """
Receive demand active PDU which contains @summary: Receive demand active PDU which contains
Server capabilities. In this version of RDPY only Server capabilities. In this version of RDPY only
Restricted group of capabilities are used. Restricted group of capabilities are used.
Send Confirm Active PDU Send Confirm Active PDU
@@ -212,7 +212,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvServerSynchronizePDU(self, s): def recvServerSynchronizePDU(self, s):
""" """
Receive from server @summary: Receive from server
Wait Control Cooperate PDU Wait Control Cooperate PDU
@param s: Stream from transport layer @param s: Stream from transport layer
""" """
@@ -228,7 +228,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvServerControlCooperatePDU(self, s): def recvServerControlCooperatePDU(self, s):
""" """
Receive control cooperate PDU from server @summary: Receive control cooperate PDU from server
Wait Control Granted PDU Wait Control Granted PDU
@param s: Stream from transport layer @param s: Stream from transport layer
""" """
@@ -244,7 +244,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvServerControlGrantedPDU(self, s): def recvServerControlGrantedPDU(self, s):
""" """
Receive last control PDU the granted control PDU @summary: Receive last control PDU the granted control PDU
Wait Font map PDU Wait Font map PDU
@param s: Stream from transport layer @param s: Stream from transport layer
""" """
@@ -260,7 +260,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvServerFontMapPDU(self, s): def recvServerFontMapPDU(self, s):
""" """
Last useless connection packet from server to client @summary: Last useless connection packet from server to client
Wait any PDU Wait any PDU
@param s: Stream from transport layer @param s: Stream from transport layer
""" """
@@ -278,7 +278,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvPDU(self, s): def recvPDU(self, s):
""" """
Main receive function after connection sequence @summary: Main receive function after connection sequence
@param s: Stream from transport layer @param s: Stream from transport layer
""" """
pdu = data.PDU() pdu = data.PDU()
@@ -293,7 +293,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def recvFastPath(self, fastPathS): def recvFastPath(self, fastPathS):
""" """
Implement IFastPathListener interface @summary: Implement IFastPathListener interface
Fast path is needed by RDP 8.0 Fast path is needed by RDP 8.0
@param fastPathS: Stream that contain fast path data @param fastPathS: Stream that contain fast path data
""" """
@@ -304,7 +304,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def readDataPDU(self, dataPDU): def readDataPDU(self, dataPDU):
""" """
read a data PDU object @summary: read a data PDU object
@param dataPDU: DataPDU object @param dataPDU: DataPDU object
""" """
if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU: if dataPDU.shareDataHeader.pduType2.value == data.PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU:
@@ -321,7 +321,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def readUpdateDataPDU(self, updateDataPDU): def readUpdateDataPDU(self, updateDataPDU):
""" """
Read an update data PDU data @summary: Read an update data PDU data
dispatch update data dispatch update data
@param: UpdateDataPDU object @param: UpdateDataPDU object
""" """
@@ -330,7 +330,7 @@ class Client(PDULayer, tpkt.IFastPathListener):
def sendInfoPkt(self): def sendInfoPkt(self):
""" """
Send a logon info packet @summary: Send a logon info packet
client automata data client automata data
""" """
self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info)) self._transport.send((UInt16Le(data.SecurityFlag.SEC_INFO_PKT), UInt16Le(), self._info))

View File

@@ -1,113 +0,0 @@
#
# Copyright (c) 2014 Sylvain Peyrefitte
#
# This file is part of rdpy.
#
# rdpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Some use full methods for security in RDP
"""
import sha, md5
from rdpy.network.type import CompositeType, Stream, UInt32Le, String, sizeof
def saltedHash(inputData, salt, salt1, salt2):
"""
@summary: Generate particular signature from combination of sha1 and md5
@see: http://msdn.microsoft.com/en-us/library/cc241992.aspx
@param inputData: strange input (see doc)
@param salt: salt for context call
@param salt1: another salt (ex : client random)
@param salt2: another another salt (ex: server random)
@return : MD5(Salt + SHA1(Input + Salt + Salt1 + Salt2))
"""
sha1Digest = sha.new()
md5Digest = md5.new()
sha1Digest.update(inputData)
sha1Digest.update(salt[:48])
sha1Digest.update(salt1)
sha1Digest.update(salt2)
sha1Sig = sha1Digest.digest()
md5Digest.update(salt[:48])
md5Digest.update(sha1Sig)
return md5Digest.digest()
def finalHash(key, random1, random2):
"""
@summary: MD5(in0[:16] + in1[:32] + in2[:32])
@param key: in 16
@param random1: in 32
@param random2: in 32
@return MD5(in0[:16] + in1[:32] + in2[:32])
"""
md5Digest = md5.new()
md5Digest.update(key)
md5Digest.update(random1)
md5Digest.update(random2)
return md5Digest.digest()
def generateMicrosoftKey(secret, random1, random2):
"""
@summary: Generate master secret
@param secret: secret
@param clientRandom : client random
@param serverRandom : server random
"""
return saltedHash("A", secret, random1, random2) + saltedHash("BB", secret, random1, random2) + saltedHash("CCC", secret, random1, random2)
def macData(macSaltKey, data):
"""
@see: http://msdn.microsoft.com/en-us/library/cc241995.aspx
"""
sha1Digest = sha.new()
md5Digest = md5.new()
#encode length
s = Stream()
s.writeType(UInt32Le(len(data)))
sha1Digest.update(macSaltKey)
sha1Digest.update("\x36" * 40)
sha1Digest.update(s.getvalue())
sha1Digest.update(data)
sha1Sig = sha1Digest.digest()
md5Digest.update(macSaltKey)
md5Digest.update("\x5c" * 48)
md5Digest.update(sha1Sig)
return md5Digest.digest()
class ClientSecurityExchangePDU(CompositeType):
"""
@summary: contain client random for basic security
@see: http://msdn.microsoft.com/en-us/library/cc240472.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.length = UInt32Le(lambda:(sizeof(self) - 4))
self.encryptedClientRandom = String(readLen = self.length)
class SecManager(object):
"""
@summary: Basic RDP security manager
"""
def __init__(self):
pass

View File

@@ -27,7 +27,7 @@ import pdu.layer
import pdu.data import pdu.data
import pdu.caps import pdu.caps
import rdpy.base.log as log import rdpy.base.log as log
import tpkt, x224, mcs, gcc import tpkt, x224, mcs, gcc, sec
class RDPClientController(pdu.layer.PDUClientListener): class RDPClientController(pdu.layer.PDUClientListener):
""" """
@@ -38,8 +38,10 @@ class RDPClientController(pdu.layer.PDUClientListener):
self._clientObserver = [] self._clientObserver = []
#PDU layer #PDU layer
self._pduLayer = pdu.layer.Client(self) self._pduLayer = pdu.layer.Client(self)
#secure layer
self._secLayer = sec.SecLayer(self._pduLayer)
#multi channel service #multi channel service
self._mcsLayer = mcs.Client(self._pduLayer) self._mcsLayer = mcs.Client(self._secLayer)
#transport pdu layer #transport pdu layer
self._x224Layer = x224.Client(self._mcsLayer) self._x224Layer = x224.Client(self._mcsLayer)
#transport packet (protocol layer) #transport packet (protocol layer)
@@ -129,6 +131,12 @@ class RDPClientController(pdu.layer.PDUClientListener):
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).clientName.value = hostname[:15] + "\x00" * (15 - len(hostname)) self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).clientName.value = hostname[:15] + "\x00" * (15 - len(hostname))
self._pduLayer._licenceManager._hostname = hostname self._pduLayer._licenceManager._hostname = hostname
def setRDPBasicSecurity(self):
"""
@summary: Request basic security
"""
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_RDP
def addClientObserver(self, observer): def addClientObserver(self, observer):
""" """
@summary: Add observer to RDP protocol @summary: Add observer to RDP protocol
@@ -478,8 +486,9 @@ class RDPServerController(pdu.layer.PDUServerListener):
class ClientFactory(layer.RawLayerClientFactory): class ClientFactory(layer.RawLayerClientFactory):
""" """
@summary: Factory of Client RDP protocol @summary: Factory of Client RDP protocol
@param reason: twisted reason
""" """
def connectionLost(self, tpktLayer): def connectionLost(self, tpktLayer, reason):
#retrieve controller #retrieve controller
x224Layer = tpktLayer._presentation x224Layer = tpktLayer._presentation
mcsLayer = x224Layer._presentation mcsLayer = x224Layer._presentation
@@ -518,7 +527,10 @@ class ServerFactory(layer.RawLayerServerFactory):
self._certificateFileName = certificateFileName self._certificateFileName = certificateFileName
self._colorDepth = colorDepth self._colorDepth = colorDepth
def connectionLost(self, tpktLayer): def connectionLost(self, tpktLayer, reason):
"""
@param reason: twisted reason
"""
#retrieve controller #retrieve controller
x224Layer = tpktLayer._presentation x224Layer = tpktLayer._presentation
mcsLayer = x224Layer._presentation mcsLayer = x224Layer._presentation

216
rdpy/protocol/rdp/sec.py Normal file
View File

@@ -0,0 +1,216 @@
#
# Copyright (c) 2014 Sylvain Peyrefitte
#
# This file is part of rdpy.
#
# rdpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Some use full methods for security in RDP
"""
import sha, md5, rsa, gcc, rc4
from rdpy.network.type import CompositeType, Stream, UInt32Le, String, sizeof
from rdpy.network.layer import LayerAutomata, IStreamSender
class SecurityFlag(object):
"""
@summary: Microsoft security flags
@see: http://msdn.microsoft.com/en-us/library/cc240579.aspx
"""
SEC_EXCHANGE_PKT = 0x0001
SEC_TRANSPORT_REQ = 0x0002
RDP_SEC_TRANSPORT_RSP = 0x0004
SEC_ENCRYPT = 0x0008
SEC_RESET_SEQNO = 0x0010
SEC_IGNORE_SEQNO = 0x0020
SEC_INFO_PKT = 0x0040
SEC_LICENSE_PKT = 0x0080
SEC_LICENSE_ENCRYPT_CS = 0x0200
SEC_LICENSE_ENCRYPT_SC = 0x0200
SEC_REDIRECTION_PKT = 0x0400
SEC_SECURE_CHECKSUM = 0x0800
SEC_AUTODETECT_REQ = 0x1000
SEC_AUTODETECT_RSP = 0x2000
SEC_HEARTBEAT = 0x4000
SEC_FLAGSHI_VALID = 0x8000
def saltedHash(inputData, salt, salt1, salt2):
"""
@summary: Generate particular signature from combination of sha1 and md5
@see: http://msdn.microsoft.com/en-us/library/cc241992.aspx
@param inputData: strange input (see doc)
@param salt: salt for context call
@param salt1: another salt (ex : client random)
@param salt2: another another salt (ex: server random)
@return : MD5(Salt + SHA1(Input + Salt + Salt1 + Salt2))
"""
sha1Digest = sha.new()
md5Digest = md5.new()
sha1Digest.update(inputData)
sha1Digest.update(salt[:48])
sha1Digest.update(salt1)
sha1Digest.update(salt2)
sha1Sig = sha1Digest.digest()
md5Digest.update(salt[:48])
md5Digest.update(sha1Sig)
return md5Digest.digest()
def finalHash(key, random1, random2):
"""
@summary: MD5(in0[:16] + in1[:32] + in2[:32])
@param key: in 16
@param random1: in 32
@param random2: in 32
@return MD5(in0[:16] + in1[:32] + in2[:32])
"""
md5Digest = md5.new()
md5Digest.update(key)
md5Digest.update(random1)
md5Digest.update(random2)
return md5Digest.digest()
def generateMicrosoftKeyABBCCC(secret, random1, random2):
"""
@summary: Generate master secret
@param secret: secret
@param clientRandom : client random
@param serverRandom : server random
"""
return saltedHash("A", secret, random1, random2) + saltedHash("BB", secret, random1, random2) + saltedHash("CCC", secret, random1, random2)
def generateMicrosoftKeyXYYZZZ(secret, random1, random2):
"""
@summary: Generate master secret
@param secret: secret
@param clientRandom : client random
@param serverRandom : server random
"""
return saltedHash("X", secret, random1, random2) + saltedHash("YY", secret, random1, random2) + saltedHash("ZZZ", secret, random1, random2)
def macData(macSaltKey, data):
"""
@see: http://msdn.microsoft.com/en-us/library/cc241995.aspx
"""
sha1Digest = sha.new()
md5Digest = md5.new()
#encode length
s = Stream()
s.writeType(UInt32Le(len(data)))
sha1Digest.update(macSaltKey)
sha1Digest.update("\x36" * 40)
sha1Digest.update(s.getvalue())
sha1Digest.update(data)
sha1Sig = sha1Digest.digest()
md5Digest.update(macSaltKey)
md5Digest.update("\x5c" * 48)
md5Digest.update(sha1Sig)
return md5Digest.digest()
def bin2bn(b):
"""
@summary: convert binary string to bignum
@param b: binary string
@return bignum
"""
l = 0L
for ch in b:
l = (l<<8) | ord(ch)
return l
def bn2bin(b):
s = bytearray()
i = (b.bit_length() + 7) / 8
while i > 0:
s.append((b >> ((i - 1) * 8)) & 0xff)
i -= 1
return s
class ClientSecurityExchangePDU(CompositeType):
"""
@summary: contain client random for basic security
@see: http://msdn.microsoft.com/en-us/library/cc240472.aspx
"""
def __init__(self):
CompositeType.__init__(self)
self.length = UInt32Le(lambda:(sizeof(self) - 4))
self.encryptedClientRandom = String(readLen = self.length)
class SecLayer(LayerAutomata, IStreamSender):
"""
@summary: Basic RDP security manager
This layer is Transparent as possible for upper layer
"""
def __init__(self, presentation):
LayerAutomata.__init__(self, presentation)
self._enableEncryption = False
def connect(self):
"""
@summary: send client random
"""
self._enableEncryption = (self._transport.getGCCClientSettings.getBlock(gcc.MessageType.CS_CORE).serverSelectedProtocol == 0)
if not self._enableEncryption:
self._presentation.connect()
return
#generate client random
self._clientRandom = rsa.randnum.read_random_bits(128)
self._serverRandom = self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_SECURITY).serverRandom.value
self.generateKeys()
#send client random encrypted with
certificate = self._transport.getGCCServerSettings().getBlock(gcc.MessageType.SC_SECURITY).serverCertificate.certData
serverPublicKey = rsa.PublicKey(bin2bn(certificate.PublicKeyBlob.modulus.value), certificate.PublicKeyBlob.pubExp.value)
message = ClientSecurityExchangePDU()
message.encryptedClientRandom.value = rsa.encrypt(self._clientRandom, serverPublicKey)
self._transport.send(message)
self._presentation.connect()
def generateKeys(self):
"""
@see: http://msdn.microsoft.com/en-us/library/cc240785.aspx
"""
preMasterSecret = self._clientRandom[:24] + self._serverRandom[:24]
masterSecret = generateMicrosoftKeyABBCCC(preMasterSecret, self._clientRandom, self._serverRandom)
self._sessionKey = generateMicrosoftKeyXYYZZZ(masterSecret, self._clientRandom, self._serverRandom)
self._macKey128 = self._sessionKey[:16]
self._decrypt = finalHash(self._sessionKey[16:32], self._clientRandom, self._serverRandom)
self._encrypt = finalHash(self._sessionKey[32:48], self._clientRandom, self._serverRandom)
def recv(self, data):
if not self._enableEncryption:
self._presentation.recv(data)
return
def send(self, data):
if not self._enableEncryption:
self._presentation.recv(data)
return
def sendInfoPkt(self, data):
self._transport.send()

View File

@@ -26,8 +26,7 @@ RDP basic security is supported only on client side
from rdpy.network.layer import LayerAutomata, IStreamSender from rdpy.network.layer import LayerAutomata, IStreamSender
from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof, String from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof, String
from rdpy.base.error import InvalidExpectedDataException from rdpy.base.error import InvalidExpectedDataException, RDPSecurityNegoFail
import rdpy.base.log as log
class MessageType(object): class MessageType(object):
""" """
@@ -133,7 +132,7 @@ class X224Layer(LayerAutomata, IStreamSender):
LayerAutomata.__init__(self, presentation) LayerAutomata.__init__(self, presentation)
#default selectedProtocol is SSl #default selectedProtocol is SSl
#client requested selectedProtocol #client requested selectedProtocol
self._requestedProtocol = Protocols.PROTOCOL_RDP self._requestedProtocol = Protocols.PROTOCOL_RDP | Protocols.PROTOCOL_SSL
#server selected selectedProtocol #server selected selectedProtocol
self._selectedProtocol = Protocols.PROTOCOL_SSL self._selectedProtocol = Protocols.PROTOCOL_SSL
@@ -195,6 +194,9 @@ class Client(X224Layer):
message = ServerConnectionConfirm() message = ServerConnectionConfirm()
data.readType(message) data.readType(message)
if message.protocolNeg.failureCode._is_readed:
raise RDPSecurityNegoFail("negotiation failure code %x"%message.protocolNeg.failureCode.value)
#check presence of negotiation response #check presence of negotiation response
if message.protocolNeg._is_readed: if message.protocolNeg._is_readed:
self._selectedProtocol = message.protocolNeg.selectedProtocol.value self._selectedProtocol = message.protocolNeg.selectedProtocol.value
@@ -205,9 +207,6 @@ class Client(X224Layer):
if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID, Protocols.PROTOCOL_HYBRID_EX ]: if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID, Protocols.PROTOCOL_HYBRID_EX ]:
raise InvalidExpectedDataException("RDPY doesn't support NLA security Layer") raise InvalidExpectedDataException("RDPY doesn't support NLA security Layer")
if message.protocolNeg.failureCode._is_readed:
log.info("negotiation failure code %x"%message.protocolNeg.failureCode.value)
if self._selectedProtocol == Protocols.PROTOCOL_SSL: if self._selectedProtocol == Protocols.PROTOCOL_SSL:
#_transport is TPKT and transport is TCP layer of twisted #_transport is TPKT and transport is TCP layer of twisted
self._transport.transport.startTLS(ClientTLSContext()) self._transport.transport.startTLS(ClientTLSContext())

View File

@@ -671,10 +671,11 @@ class ClientFactory(RawLayerClientFactory):
self.buildObserver(controller, addr) self.buildObserver(controller, addr)
return controller.getProtocol() return controller.getProtocol()
def connectionLost(self, rfblayer): def connectionLost(self, rfblayer, reason):
""" """
@summary: Override this method to handle connection lost @summary: Override this method to handle connection lost
@param rfblayer: rfblayer that cause connectionLost event @param rfblayer: rfblayer that cause connectionLost event
@param reason: twisted reason
""" """
#call controller #call controller
rfblayer._clientListener.onClose() rfblayer._clientListener.onClose()