Add license header + SSL server config
This commit is contained in:
@@ -10,4 +10,10 @@ Remote Desktop Protocol in Twisted Python
|
||||
* python-qt4
|
||||
* python-qt4reactor
|
||||
|
||||
##Must be implemented before first release
|
||||
* CreedSSP
|
||||
* Packet redirection
|
||||
* License
|
||||
* Most common orders
|
||||
|
||||
this project is still in progress.
|
||||
|
||||
@@ -7,9 +7,18 @@ import sys, os
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '../..'))
|
||||
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from rdpy.network.layer import LayerMode
|
||||
|
||||
class TestServerFactory(rdp.ServerFactory):
|
||||
def startedConnecting(self, connector):
|
||||
pass
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
pass
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
from twisted.internet import reactor
|
||||
reactor.listenTCP(33389, rdp.Factory(LayerMode.SERVER))
|
||||
reactor.listenTCP(33389, TestServerFactory())
|
||||
reactor.run()
|
||||
@@ -47,14 +47,41 @@ class ClientFactory(protocol.Factory):
|
||||
'''
|
||||
pduLayer = pdu.PDU(LayerMode.CLIENT)
|
||||
pduLayer.getController().addObserver(self.buildObserver())
|
||||
return tpkt.TPKT(tpdu.TPDU(mcs.MCS(pduLayer)));
|
||||
return tpkt.TPKT(tpdu.createClient(mcs.MCS(pduLayer)));
|
||||
|
||||
def buildObserver(self):
|
||||
'''
|
||||
build observer use for connection
|
||||
'''
|
||||
pass
|
||||
|
||||
class ServerFactory(protocol.Factory):
|
||||
'''
|
||||
Factory of Serrve RDP protocol
|
||||
'''
|
||||
def __init__(self, privateKeyFileName, certificateFileName):
|
||||
'''
|
||||
ctor
|
||||
@param privateKeyFileName: file contain server private key
|
||||
@param certficiateFileName: file that contain publi key
|
||||
'''
|
||||
self._privateKeyFileName = privateKeyFileName
|
||||
self._certificateFileName = certificateFileName
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
'''
|
||||
Function call from twisted and build rdp protocol stack
|
||||
@param addr: destination address
|
||||
'''
|
||||
pduLayer = pdu.PDU(LayerMode.SERVER)
|
||||
#pduLayer.getController().addObserver(self.buildObserver())
|
||||
return tpkt.TPKT(tpdu.createServer(mcs.MCS(pduLayer), self._privateKeyFileName, self._certificateFileName));
|
||||
|
||||
def buildObserver(self):
|
||||
'''
|
||||
build observer use for connection
|
||||
'''
|
||||
pass
|
||||
|
||||
class RDPClientObserver(object):
|
||||
'''
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
'''
|
||||
@author: sylvain
|
||||
'''
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Implement transport pdu layer
|
||||
|
||||
This layer have main goal to negociate ssl transport
|
||||
RDP basic security is not supported by RDPY (because is not a true security layer...)
|
||||
"""
|
||||
|
||||
from rdpy.network.layer import LayerAutomata, LayerMode
|
||||
from rdpy.network.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof
|
||||
from rdpy.network.error import InvalidExpectedDataException
|
||||
@@ -9,9 +32,9 @@ from rdpy.network.const import ConstAttributes, TypeAttributes
|
||||
@ConstAttributes
|
||||
@TypeAttributes(UInt8)
|
||||
class MessageType(object):
|
||||
'''
|
||||
"""
|
||||
message type
|
||||
'''
|
||||
"""
|
||||
X224_TPDU_CONNECTION_REQUEST = 0xE0
|
||||
X224_TPDU_CONNECTION_CONFIRM = 0xD0
|
||||
X224_TPDU_DISCONNECT_REQUEST = 0x80
|
||||
@@ -21,9 +44,9 @@ class MessageType(object):
|
||||
@ConstAttributes
|
||||
@TypeAttributes(UInt8)
|
||||
class NegociationType(object):
|
||||
'''
|
||||
"""
|
||||
negotiation header
|
||||
'''
|
||||
"""
|
||||
TYPE_RDP_NEG_REQ = 0x01
|
||||
TYPE_RDP_NEG_RSP = 0x02
|
||||
TYPE_RDP_NEG_FAILURE = 0x03
|
||||
@@ -31,9 +54,9 @@ class NegociationType(object):
|
||||
@ConstAttributes
|
||||
@TypeAttributes(UInt32Le)
|
||||
class Protocols(object):
|
||||
'''
|
||||
"""
|
||||
protocols available for TPDU layer
|
||||
'''
|
||||
"""
|
||||
PROTOCOL_RDP = 0x00000000
|
||||
PROTOCOL_SSL = 0x00000001
|
||||
PROTOCOL_HYBRID = 0x00000002
|
||||
@@ -42,9 +65,9 @@ class Protocols(object):
|
||||
@ConstAttributes
|
||||
@TypeAttributes(UInt32Le)
|
||||
class NegotiationFailureCode(object):
|
||||
'''
|
||||
"""
|
||||
protocol negotiation failure code
|
||||
'''
|
||||
"""
|
||||
SSL_REQUIRED_BY_SERVER = 0x00000001
|
||||
SSL_NOT_ALLOWED_BY_SERVER = 0x00000002
|
||||
SSL_CERT_NOT_ON_SERVER = 0x00000003
|
||||
@@ -53,10 +76,13 @@ class NegotiationFailureCode(object):
|
||||
SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 0x00000006
|
||||
|
||||
class TPDUConnectMessage(CompositeType):
|
||||
'''
|
||||
"""
|
||||
header of TPDU connection messages
|
||||
'''
|
||||
"""
|
||||
def __init__(self, code):
|
||||
"""
|
||||
@param code: MessageType
|
||||
"""
|
||||
CompositeType.__init__(self)
|
||||
self.len = UInt8(lambda:sizeof(self) - 1)
|
||||
self.code = UInt8(code.value, constant = True)
|
||||
@@ -65,9 +91,9 @@ class TPDUConnectMessage(CompositeType):
|
||||
self.protocolNeg = Negotiation(optional = True)
|
||||
|
||||
class TPDUDataHeader(CompositeType):
|
||||
'''
|
||||
"""
|
||||
header send when tpdu exchange application data
|
||||
'''
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.header = UInt8(2, constant = True)
|
||||
@@ -75,12 +101,12 @@ class TPDUDataHeader(CompositeType):
|
||||
self.separator = UInt8(0x80, constant = True)
|
||||
|
||||
class Negotiation(CompositeType):
|
||||
'''
|
||||
negociation request message
|
||||
"""
|
||||
negociate request message
|
||||
@see: request -> http://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
@see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||
@see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
|
||||
'''
|
||||
"""
|
||||
def __init__(self, optional = False):
|
||||
CompositeType.__init__(self, optional = optional)
|
||||
self.code = UInt8()
|
||||
@@ -91,42 +117,55 @@ class Negotiation(CompositeType):
|
||||
self.failureCode = UInt32Le(conditional = lambda: self.code == NegociationType.TYPE_RDP_NEG_FAILURE)
|
||||
|
||||
class TPDU(LayerAutomata):
|
||||
'''
|
||||
"""
|
||||
TPDU layer management
|
||||
there is an connection automata
|
||||
'''
|
||||
def __init__(self, presentation):
|
||||
'''
|
||||
Constructor
|
||||
@param presentation: MCS layer
|
||||
'''
|
||||
LayerAutomata.__init__(self, presentation._mode, presentation)
|
||||
"""
|
||||
def __init__(self, mode, presentation):
|
||||
"""
|
||||
@param mode: automata mode (client or server)
|
||||
@param presentation: upper layer, MCS layer in RDP case
|
||||
"""
|
||||
LayerAutomata.__init__(self, mode, presentation)
|
||||
#default selectedProtocol is SSl because is the only supported
|
||||
#in this version of RDPY
|
||||
#client requested selectedProtocol
|
||||
self._requestedProtocol = Protocols.PROTOCOL_SSL
|
||||
#server selected selectedProtocol
|
||||
self._selectedProtocol = Protocols.PROTOCOL_SSL
|
||||
|
||||
#Server mode informations for tls connexion
|
||||
self._serverPrivateKeyFileName = None
|
||||
self._serverCertificateFileName = None
|
||||
|
||||
def initTLSServerInfos(self, privateKeyFileName, certificateFileName):
|
||||
"""
|
||||
Init informations for ssl server connexion
|
||||
@param privateKeyFileName: file contain server private key
|
||||
@param certficiateFileName: file that contain publi key
|
||||
"""
|
||||
self._serverPrivateKeyFileName = privateKeyFileName
|
||||
self._serverCertificateFileName = certificateFileName
|
||||
|
||||
def connect(self):
|
||||
'''
|
||||
"""
|
||||
connection request
|
||||
for client send a connection request packet
|
||||
'''
|
||||
"""
|
||||
if self._mode == LayerMode.CLIENT:
|
||||
self.sendConnectionRequest()
|
||||
else:
|
||||
self.setNextState(self.recvConnectionRequest)
|
||||
|
||||
def recvConnectionConfirm(self, data):
|
||||
'''
|
||||
recv connection confirm message
|
||||
"""
|
||||
receive connection confirm message
|
||||
next state is recvData
|
||||
call connect on presentation layer if all is good
|
||||
@param data: Stream that contain connection confirm
|
||||
@see: response -> http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||
@see: failure ->http://msdn.microsoft.com/en-us/library/cc240507.aspx
|
||||
'''
|
||||
"""
|
||||
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_CONFIRM)
|
||||
data.readType(message)
|
||||
|
||||
@@ -150,12 +189,12 @@ class TPDU(LayerAutomata):
|
||||
LayerAutomata.connect(self)
|
||||
|
||||
def recvConnectionRequest(self, data):
|
||||
'''
|
||||
"""
|
||||
read connection confirm packet
|
||||
next state is send connection confirm
|
||||
@param data: stream
|
||||
@param data: Stream
|
||||
@see : http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
'''
|
||||
"""
|
||||
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST)
|
||||
data.readType(message)
|
||||
|
||||
@@ -177,21 +216,21 @@ class TPDU(LayerAutomata):
|
||||
self.sendConnectionConfirm()
|
||||
|
||||
def recvData(self, data):
|
||||
'''
|
||||
"""
|
||||
read data header from packet
|
||||
and pass to presentation layer
|
||||
@param data: stream
|
||||
'''
|
||||
@param data: Stream
|
||||
"""
|
||||
header = TPDUDataHeader()
|
||||
data.readType(header)
|
||||
LayerAutomata.recv(self, data)
|
||||
|
||||
def sendConnectionRequest(self):
|
||||
'''
|
||||
"""
|
||||
write connection request message
|
||||
next state is recvConnectionConfirm
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
'''
|
||||
"""
|
||||
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_REQUEST)
|
||||
message.protocolNeg.code = NegociationType.TYPE_RDP_NEG_REQ
|
||||
message.protocolNeg.selectedProtocol = self._requestedProtocol
|
||||
@@ -199,36 +238,53 @@ class TPDU(LayerAutomata):
|
||||
self.setNextState(self.recvConnectionConfirm)
|
||||
|
||||
def sendConnectionConfirm(self):
|
||||
'''
|
||||
"""
|
||||
write connection confirm message
|
||||
next state is recvData
|
||||
@see : http://msdn.microsoft.com/en-us/library/cc240501.aspx
|
||||
'''
|
||||
"""
|
||||
message = TPDUConnectMessage(MessageType.X224_TPDU_CONNECTION_CONFIRM)
|
||||
message.protocolNeg.code = NegociationType.TYPE_RDP_NEG_REQ
|
||||
message.protocolNeg.selectedProtocol = self._selectedProtocol
|
||||
self._transport.send(message)
|
||||
#_transport is TPKT and transport is TCP layer of twisted
|
||||
self._transport.transport.startTLS(ServerTLSContext())
|
||||
self._transport.transport.startTLS(ServerTLSContext(self._serverPrivateKeyFileName, self._serverCertificateFileName))
|
||||
#connection is done send to presentation
|
||||
LayerAutomata.connect(self)
|
||||
|
||||
def send(self, message):
|
||||
'''
|
||||
"""
|
||||
write message packet for TPDU layer
|
||||
add TPDU header
|
||||
'''
|
||||
@param message: network.Type message
|
||||
"""
|
||||
self._transport.send((TPDUDataHeader(), message))
|
||||
|
||||
def createClient(presentation):
|
||||
"""
|
||||
Factory of TPDU layer in Client mode
|
||||
@param presentation: presentation layer, in RDP mode is MCS layer
|
||||
"""
|
||||
return TPDU(LayerMode.CLIENT, presentation)
|
||||
|
||||
def createServer(presentation, privateKeyFileName, certificateFileName):
|
||||
"""
|
||||
Factory of TPDU layer in Server mode
|
||||
@param privateKeyFileName: file contain server private key
|
||||
@param certficiateFileName: file that contain publi key
|
||||
"""
|
||||
tpdu = TPDU(LayerMode.SERVER, presentation)
|
||||
tpdu.initTLSServerInfos(privateKeyFileName, certificateFileName)
|
||||
return tpdu
|
||||
|
||||
#open ssl needed
|
||||
from twisted.internet import ssl
|
||||
from OpenSSL import SSL
|
||||
|
||||
class ClientTLSContext(ssl.ClientContextFactory):
|
||||
'''
|
||||
"""
|
||||
client context factory for open ssl
|
||||
'''
|
||||
"""
|
||||
def getContext(self):
|
||||
context = SSL.Context(SSL.TLSv1_METHOD)
|
||||
context.set_options(0x00020000)#SSL_OP_NO_COMPRESSION
|
||||
@@ -237,9 +293,10 @@ class ClientTLSContext(ssl.ClientContextFactory):
|
||||
return context
|
||||
|
||||
class ServerTLSContext(ssl.DefaultOpenSSLContextFactory):
|
||||
'''
|
||||
"""
|
||||
server context factory for open ssl
|
||||
'''
|
||||
def __init__(self, *args, **kw):
|
||||
kw['sslmethod'] = SSL.TLSv1_METHOD
|
||||
ssl.DefaultOpenSSLContextFactory.__init__(self, *args, **kw)
|
||||
@param privateKeyFileName: Name of a file containing a private key
|
||||
@param certificateFileName: Name of a file containing a certificate
|
||||
"""
|
||||
def __init__(self, privateKeyFileName, certificateFileName):
|
||||
ssl.DefaultOpenSSLContextFactory.__init__(self, privateKeyFileName, certificateFileName, SSL.TLSv1_METHOD)
|
||||
@@ -1,22 +1,44 @@
|
||||
'''
|
||||
@author: sylvain
|
||||
'''
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Transport packet layer implementation
|
||||
|
||||
Use to build correct size packet and handle slow path and fast path mode
|
||||
"""
|
||||
from rdpy.network.layer import RawLayer, LayerMode
|
||||
from rdpy.network.type import UInt8, UInt16Be, sizeof
|
||||
|
||||
class TPKT(RawLayer):
|
||||
'''
|
||||
"""
|
||||
TPKT layer in RDP protocol stack
|
||||
this layer only handle size of packet
|
||||
and determine if is a fast path packet
|
||||
'''
|
||||
"""
|
||||
#first byte of classic tpkt header
|
||||
TPKT_PACKET = UInt8(3)
|
||||
|
||||
def __init__(self, presentation):
|
||||
'''
|
||||
"""
|
||||
Constructor
|
||||
'''
|
||||
@param presentation: presentation layer, in RDP case is TPDU layer
|
||||
"""
|
||||
RawLayer.__init__(self, LayerMode.NONE, presentation)
|
||||
#last packet version read from header
|
||||
self._lastPacketVersion = UInt8()
|
||||
@@ -24,10 +46,10 @@ class TPKT(RawLayer):
|
||||
self._lastShortLength = UInt8()
|
||||
|
||||
def connect(self):
|
||||
'''
|
||||
"""
|
||||
call when transport layer connection
|
||||
is made (inherit from RawLayer)
|
||||
'''
|
||||
"""
|
||||
#header is on two bytes
|
||||
self.expect(2, self.readHeader)
|
||||
#no connection automata on this layer
|
||||
@@ -35,9 +57,10 @@ class TPKT(RawLayer):
|
||||
self._presentation.connect()
|
||||
|
||||
def readHeader(self, data):
|
||||
'''
|
||||
"""
|
||||
read header of TPKT packet
|
||||
'''
|
||||
@param data: Stream received from twisted layer
|
||||
"""
|
||||
#first read packet version
|
||||
data.readType(self._lastPacketVersion)
|
||||
#classic packet
|
||||
@@ -57,18 +80,20 @@ class TPKT(RawLayer):
|
||||
|
||||
|
||||
def readExtendedHeader(self, data):
|
||||
'''
|
||||
"""
|
||||
header may be on 4 bytes
|
||||
'''
|
||||
@param data: Stream from twisted layer
|
||||
"""
|
||||
#next state is read data
|
||||
size = UInt16Be()
|
||||
data.readType(size)
|
||||
self.expect(size.value - 4, self.readData)
|
||||
|
||||
def readExtendedFastPathHeader(self, data):
|
||||
'''
|
||||
fast ath header may be on 1 byte more
|
||||
'''
|
||||
"""
|
||||
fast path header may be on 1 byte more
|
||||
@param data: Stream from twisted layer
|
||||
"""
|
||||
leftPart = UInt8()
|
||||
data.readType(leftPart)
|
||||
self._lastShortLength.value &= ~0x80
|
||||
@@ -77,21 +102,24 @@ class TPKT(RawLayer):
|
||||
self.expect(self._lastShortLength.value - 3, self.readFastPath)
|
||||
|
||||
def readFastPath(self, data):
|
||||
'''
|
||||
"""
|
||||
fast path data
|
||||
'''
|
||||
@param data: Stream from twisted layer
|
||||
"""
|
||||
pass
|
||||
|
||||
def readData(self, data):
|
||||
'''
|
||||
read classic TPKT packet
|
||||
'''
|
||||
"""
|
||||
read classic TPKT packet, last state in tpkt automata
|
||||
@param data: Stream with correct size
|
||||
"""
|
||||
#next state is pass to
|
||||
self._presentation.recv(data)
|
||||
self.expect(2, self.readHeader)
|
||||
|
||||
def send(self, message):
|
||||
'''
|
||||
send encapsuled data
|
||||
'''
|
||||
"""
|
||||
send encompassed data
|
||||
@param message: network.Type message to send
|
||||
"""
|
||||
RawLayer.send(self, (TPKT.TPKT_PACKET, UInt8(0), UInt16Be(sizeof(message) + 4), message))
|
||||
Reference in New Issue
Block a user