diff --git a/README.md b/README.md
index 4cc460a..53462a8 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/rdpy/examples/rdpserver.py b/rdpy/examples/rdpserver.py
index 9125de5..2505223 100644
--- a/rdpy/examples/rdpserver.py
+++ b/rdpy/examples/rdpserver.py
@@ -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()
\ No newline at end of file
diff --git a/rdpy/protocol/rdp/rdp.py b/rdpy/protocol/rdp/rdp.py
index 9e12bba..6ebf813 100644
--- a/rdpy/protocol/rdp/rdp.py
+++ b/rdpy/protocol/rdp/rdp.py
@@ -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):
'''
diff --git a/rdpy/protocol/rdp/tpdu.py b/rdpy/protocol/rdp/tpdu.py
index f02c3dd..f124fcd 100644
--- a/rdpy/protocol/rdp/tpdu.py
+++ b/rdpy/protocol/rdp/tpdu.py
@@ -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 .
+#
+
+"""
+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)
\ No newline at end of file
+ @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)
\ No newline at end of file
diff --git a/rdpy/protocol/rdp/tpkt.py b/rdpy/protocol/rdp/tpkt.py
index 32af92e..f1c4330 100644
--- a/rdpy/protocol/rdp/tpkt.py
+++ b/rdpy/protocol/rdp/tpkt.py
@@ -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 .
+#
+
+"""
+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))
\ No newline at end of file