fix rdp stander security layer server side bug, fix lisense automata, ready for next release
This commit is contained in:
@@ -7,7 +7,7 @@ before_install:
|
||||
- sudo apt-get install python-qt4
|
||||
- ln -s /usr/lib/python2.7/dist-packages/PyQt4/ $VIRTUAL_ENV/lib/python2.7/site-packages/
|
||||
- ln -s /usr/lib/python2.7/dist-packages/sip.so $VIRTUAL_ENV/lib/python2.7/site-packages/
|
||||
- pip install qt4reactor pyopenssl twisted service_identity
|
||||
- pip install qt4reactor pyopenssl twisted service_identity rsa
|
||||
|
||||
install:
|
||||
- python setup.py install
|
||||
|
||||
46
README.md
46
README.md
@@ -21,14 +21,14 @@ sudo apt-get install python-qt4
|
||||
|
||||
x86 | x86_64
|
||||
----|-------
|
||||
[PyQt4 x86](http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt4-4.11.3-gpl-Py2.7-Qt4.8.6-x32.exe) | [PyQt4 x86_64](http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt4-4.11.3-gpl-Py2.7-Qt4.8.6-x64.exe/download)
|
||||
[PyWin32 x86](http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win32-py2.7.exe/download) | [PyWin32 x86_64](http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win-amd64-py2.7.exe/download)
|
||||
[PyQt4](http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt4-4.11.3-gpl-Py2.7-Qt4.8.6-x32.exe) | [PyQt4](http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt4-4.11.3-gpl-Py2.7-Qt4.8.6-x64.exe/download)
|
||||
[PyWin32](http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win32-py2.7.exe/download) | [PyWin32](http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win-amd64-py2.7.exe/download)
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ git clone https://github.com/citronneur/rdpy.git rdpy
|
||||
$ pip install twisted pyopenssl qt4reactor
|
||||
$ pip install twisted pyopenssl qt4reactor service_identity rsa
|
||||
$ python rdpy/setup.py install
|
||||
```
|
||||
|
||||
@@ -81,36 +81,32 @@ $ rdpy-vncscreenshot.py [-p password] [-o output_file_path] XXX.XXX.XXX.XXX[:590
|
||||
|
||||
### rdpy-rdpproxy
|
||||
|
||||
rdpy-rdpproxy is a RDP proxy. It is used to manage and control access to the RDP servers as well as watch live sessions through any RDP client. It can be compared to a HTTP reverse proxy with added spy features.
|
||||
rdpy-rdpproxy is a RDP proxy with shadow and record function.
|
||||
|
||||
```
|
||||
$ rdpy-rdpproxy.py -f credentials_file_path -k private_key_file_path -c certificate_file_path [-i admin_ip[:admin_port]] listen_port
|
||||
$ rdpy-rdpproxy.py -t target_ip[:target_port] [-k private_key_file_path] [-c certificate_file_path] [-i admin_ip[:admin_port]] listen_port
|
||||
```
|
||||
|
||||
The credentials file is JSON file that must conform with the following format:
|
||||
The target ip and port represent the target host.
|
||||
|
||||
The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer. The CredSSP security layer is planned for an upcoming release. If one of both parameters are omitted, the server use standard RDP as security layer.
|
||||
|
||||
The IP and port admin are used in order to shadow active sessions thanks to a RDP client (rdpy-rdpclient, remina, mstsc) set username parameter like name of session printed by proxy.
|
||||
|
||||
Exemple :
|
||||
```
|
||||
{
|
||||
"domain1":
|
||||
{
|
||||
"username1":
|
||||
[
|
||||
{"ip":"machine1", "port":3389"},
|
||||
{"ip":"machine2", "port":3389"}
|
||||
],
|
||||
"username2":
|
||||
[
|
||||
{"ip":"machine1", "port":3389"}
|
||||
]
|
||||
}
|
||||
}
|
||||
$ rdpy-rdpproxy.py -t [my_computer] -i 0.0.0.0:56654 3389
|
||||
$ INFO : Shadow listener on 0.0.0.0:56654
|
||||
$ INFO : **************************************************
|
||||
$ INFO : Now connected
|
||||
$ INFO : ['super-administrator']
|
||||
$ INFO : **************************************************
|
||||
```
|
||||
|
||||
In this exemple domain1\username1 can access to machine1 and machine2 and domain1\username2 can only access to machine1.
|
||||
|
||||
The private key file and the certificate file are classic cryptographic files for SSL connections. The RDP protocol can negotiate its own security layer but RDPY is limited to SSL. The CredSSP security layer is planned for an upcoming release. The basic RDP security layer is not supported (windows wp sp1&2).
|
||||
|
||||
The IP and port admin are used in order to spy active sessions thanks to a RDP client (rdpy-rdpclient, remina, mstsc). Common values are 127.0.0.1:3389 to protect from connections by unauthorized user.
|
||||
To shadow 'super-administrator' session :
|
||||
```
|
||||
$ rdpy-rdpclient.py -u super-administrator 127.0.0.1:56654
|
||||
```
|
||||
|
||||
## RDPY Qt Widget
|
||||
|
||||
|
||||
@@ -35,16 +35,17 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
||||
"""
|
||||
@summary: Factory create a RDP GUI client
|
||||
"""
|
||||
def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized):
|
||||
def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized, security):
|
||||
"""
|
||||
@param width: width of client
|
||||
@param heigth: heigth of client
|
||||
@param username: username present to the server
|
||||
@param password: password present to the server
|
||||
@param domain: microsoft domain
|
||||
@param fullscreen: show widget in fullscreen mode
|
||||
@param keyboardLayout: keyboard layout
|
||||
@param optimized: enable optimized session orders
|
||||
@param width: {integer} width of client
|
||||
@param heigth: {integer} heigth of client
|
||||
@param username: {str} username present to the server
|
||||
@param password: {str} password present to the server
|
||||
@param domain: {str} microsoft domain
|
||||
@param fullscreen: {bool} show widget in fullscreen mode
|
||||
@param keyboardLayout: {str} (fr|en) keyboard layout
|
||||
@param optimized: {bool} enable optimized session orders
|
||||
@param security: {str} (ssl | rdp | nego)
|
||||
"""
|
||||
self._width = width
|
||||
self._height = height
|
||||
@@ -54,8 +55,12 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
||||
self._fullscreen = fullscreen
|
||||
self._keyboardLayout = keyboardLayout
|
||||
self._optimized = optimized
|
||||
self._nego = security == "nego"
|
||||
if self._nego:
|
||||
self._security = "ssl"
|
||||
else:
|
||||
self._security = security
|
||||
self._w = None
|
||||
self._basicRDPSecurity = False
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@@ -66,9 +71,9 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
||||
@return: RDPClientQt
|
||||
"""
|
||||
#create client observer
|
||||
client = RDPClientQt(controller, self._width, self._height)
|
||||
self._client = RDPClientQt(controller, self._width, self._height)
|
||||
#create qt widget
|
||||
self._w = client.getWidget()
|
||||
self._w = self._client.getWidget()
|
||||
self._w.setWindowTitle('rdpy-rdpclient')
|
||||
if self._fullscreen:
|
||||
self._w.showFullScreen()
|
||||
@@ -82,14 +87,9 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
||||
controller.setHostname(socket.gethostname())
|
||||
if self._optimized:
|
||||
controller.setPerformanceSession()
|
||||
controller.setSecurityLevel(self._security)
|
||||
|
||||
if self._basicRDPSecurity:
|
||||
controller.setRDPBasicSecurity()
|
||||
|
||||
return client
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
pass
|
||||
return self._client
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
"""
|
||||
@@ -98,8 +98,12 @@ class RDPClientQtFactory(rdp.ClientFactory):
|
||||
@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
|
||||
if reason.type == RDPSecurityNegoFail and self._nego:
|
||||
#stop nego
|
||||
log.info("due to security nego error back to standard RDP security layer")
|
||||
self._nego = False
|
||||
self._security = "rdp"
|
||||
self._client._widget.hide()
|
||||
connector.connect()
|
||||
return
|
||||
|
||||
@@ -205,12 +209,9 @@ if __name__ == '__main__':
|
||||
width = QtGui.QDesktopWidget().screenGeometry().width()
|
||||
height = QtGui.QDesktopWidget().screenGeometry().height()
|
||||
|
||||
|
||||
|
||||
|
||||
log.info("keyboard layout set to %s"%keyboardLayout)
|
||||
|
||||
from twisted.internet import reactor
|
||||
reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen, keyboardLayout, optimized))
|
||||
reactor.connectTCP(ip, int(port), RDPClientQtFactory(width, height, username, password, domain, fullscreen, keyboardLayout, optimized, "nego"))
|
||||
reactor.runReturn()
|
||||
app.exec_()
|
||||
@@ -23,10 +23,10 @@ RDP proxy with spy capabilities
|
||||
---------------------------
|
||||
Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
|
||||
---------------------------
|
||||
| ProxyAdmin |
|
||||
------------
|
||||
| ProxyShadow |
|
||||
--------------
|
||||
^
|
||||
Admin ----------------------|
|
||||
Shadow -------------------|
|
||||
"""
|
||||
|
||||
import sys, os, getopt, json
|
||||
@@ -37,77 +37,32 @@ from rdpy.ui import view
|
||||
from twisted.internet import reactor
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
#log._LOG_LEVEL = log.Level.INFO
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
class ProxyServer(rdp.RDPServerObserver):
|
||||
"""
|
||||
@summary: Server side of proxy
|
||||
"""
|
||||
def __init__(self, controller, credentialProvider):
|
||||
_SESSIONS_ = {}
|
||||
def __init__(self, controller, target):
|
||||
"""
|
||||
@param controller: RDPServerController
|
||||
@param credentialProvider: CredentialProvider
|
||||
@param controller: {RDPServerController}
|
||||
@param target: {tuple(ip, port)}
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._credentialProvider = credentialProvider
|
||||
self._target = target
|
||||
self._client = None
|
||||
self._window = None
|
||||
|
||||
def showSelectView(self, machines):
|
||||
"""
|
||||
@summary: Show select sever view to the client
|
||||
@param machines: [(ip, port)]
|
||||
"""
|
||||
self._machines = machines
|
||||
width, height = self._controller.getScreen()
|
||||
self._window = view.Window(width, height, QtGui.QColor(8, 24, 66))
|
||||
|
||||
self._window.addView(view.Anchor(width / 2 - 250, 100,
|
||||
view.Label("Please select following server",
|
||||
500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold),
|
||||
backgroundColor = QtGui.QColor(8, 24, 66))))
|
||||
|
||||
self._window.addView(view.Anchor(width / 2 - 250, 150,
|
||||
view.List(["%s:%s"%(ip, port) for ip, port in machines],
|
||||
500, 500, self.onSelectMachine,
|
||||
QtGui.QColor(8, 24, 66))), True)
|
||||
|
||||
self._window.update(view.RDPRenderer(self._controller), True)
|
||||
|
||||
def onSelectMachine(self, index):
|
||||
"""
|
||||
@summary: Callback of view.List in Select server view
|
||||
@param: index in list
|
||||
"""
|
||||
ip, port = self._machines[index]
|
||||
width, height = self._controller.getScreen()
|
||||
domain, username, password = self._controller.getCredentials()
|
||||
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password))
|
||||
|
||||
def clientConnected(self, client):
|
||||
"""
|
||||
@summary: Event throw by client when it's ready
|
||||
@param client: ProxyClient
|
||||
@param client: {ProxyClient}
|
||||
"""
|
||||
self._client = client
|
||||
#need to reevaluate color depth
|
||||
self._controller.setColorDepth(self._client._controller.getColorDepth())
|
||||
|
||||
def showMessage(self, message):
|
||||
"""
|
||||
@summary: Print a message to the client
|
||||
@param message: string
|
||||
"""
|
||||
width, height = self._controller.getScreen()
|
||||
|
||||
popup = view.Window(width, height, QtGui.QColor(8, 24, 66))
|
||||
|
||||
popup.addView(view.Anchor(width / 2 - 250, height / 2 - 25,
|
||||
view.Label(message, 500, 50,
|
||||
QtGui.QFont('arial', 18, QtGui.QFont.Bold),
|
||||
backgroundColor = QtGui.QColor(8, 24, 66))))
|
||||
|
||||
popup.update(view.RDPRenderer(self._controller), True)
|
||||
ProxyServer._SESSIONS_[self._controller.getHostname()] = client
|
||||
nowConnected()
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@@ -115,23 +70,15 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
First time this event is called is when human client is connected
|
||||
Second time is after color depth nego, because color depth nego
|
||||
restart a connection sequence
|
||||
Use to connect proxy client or show available server
|
||||
@see: rdp.RDPServerObserver.onReady
|
||||
"""
|
||||
if self._client is None:
|
||||
#try a connection
|
||||
domain, username, password = self._controller.getCredentials()
|
||||
machines = self._credentialProvider.getProxyPass(domain, username)
|
||||
|
||||
if len(machines) == 0:
|
||||
self.showMessage("No servers attach to account %s\\%s"%(domain, username))
|
||||
elif len(machines) == 1:
|
||||
ip, port = machines[0]
|
||||
width, height = self._controller.getScreen()
|
||||
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height,
|
||||
reactor.connectTCP(self._target[0], int(self._target[1]), ProxyClientFactory(self, width, height,
|
||||
domain, username, password))
|
||||
else:
|
||||
self.showSelectView(machines)
|
||||
else:
|
||||
#refresh client
|
||||
width, height = self._controller.getScreen()
|
||||
@@ -144,6 +91,9 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
"""
|
||||
if self._client is None:
|
||||
return
|
||||
|
||||
del ProxyServer._SESSIONS_[self._controller.getHostname()]
|
||||
nowConnected()
|
||||
#close proxy client
|
||||
self._client._controller.close()
|
||||
|
||||
@@ -154,13 +104,9 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
@param isPressed: True if key is down
|
||||
@see: rdp.RDPServerObserver.onKeyEventScancode
|
||||
"""
|
||||
#no client connected
|
||||
if not self._client is None:
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendKeyEventScancode(code, isPressed)
|
||||
elif not self._window is None and isPressed:
|
||||
self._window.keyEvent(code)
|
||||
self._window.update(view.RDPRenderer(self._controller))
|
||||
|
||||
|
||||
def onKeyEventUnicode(self, code, isPressed):
|
||||
"""
|
||||
@@ -169,7 +115,6 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
@param isPressed: True if key is down
|
||||
@see: rdp.RDPServerObserver.onKeyEventUnicode
|
||||
"""
|
||||
#no client connected domain
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendKeyEventUnicode(code, isPressed)
|
||||
@@ -183,7 +128,6 @@ class ProxyServer(rdp.RDPServerObserver):
|
||||
@param isPressed: True if mouse button is pressed
|
||||
@see: rdp.RDPServerObserver.onPointerEvent
|
||||
"""
|
||||
#no client connected
|
||||
if self._client is None:
|
||||
return
|
||||
self._client._controller.sendPointerEvent(x, y, button, isPressed)
|
||||
@@ -192,14 +136,14 @@ class ProxyServerFactory(rdp.ServerFactory):
|
||||
"""
|
||||
@summary: Factory on listening events
|
||||
"""
|
||||
def __init__(self, credentialProvider, privateKeyFilePath, certificateFilePath):
|
||||
def __init__(self, target, privateKeyFilePath = None, certificateFilePath = None):
|
||||
"""
|
||||
@param credentialProvider: CredentialProvider
|
||||
@param privateKeyFilePath: file contain server private key
|
||||
@param certificateFilePath: file contain server certificate
|
||||
@param target: {tuple(ip, prt)}
|
||||
@param privateKeyFilePath: {str} file contain server private key (if none -> back to standard RDP security)
|
||||
@param certificateFilePath: {str} file contain server certificate (if none -> back to standard RDP security)
|
||||
"""
|
||||
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
|
||||
self._credentialProvider = credentialProvider
|
||||
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
||||
self._target = target
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@@ -207,23 +151,19 @@ class ProxyServerFactory(rdp.ServerFactory):
|
||||
@param addr: destination address
|
||||
@see: rdp.ServerFactory.buildObserver
|
||||
"""
|
||||
return ProxyServer(controller, self._credentialProvider)
|
||||
return ProxyServer(controller, self._target)
|
||||
|
||||
class ProxyClient(rdp.RDPClientObserver):
|
||||
"""
|
||||
@summary: Client side of proxy
|
||||
"""
|
||||
_CONNECTED_ = []
|
||||
def __init__(self, controller, server, name = None):
|
||||
def __init__(self, controller, server):
|
||||
"""
|
||||
@param controller: rdp.RDPClientController
|
||||
@param server: ProxyServer
|
||||
@param name: name of session None if you don't
|
||||
want to spy this session
|
||||
"""
|
||||
rdp.RDPClientObserver.__init__(self, controller)
|
||||
self._server = server
|
||||
self._name = name
|
||||
self._connected = False
|
||||
|
||||
def onReady(self):
|
||||
@@ -240,8 +180,6 @@ class ProxyClient(rdp.RDPClientObserver):
|
||||
else:
|
||||
self._connected = True
|
||||
|
||||
if not self._name is None:
|
||||
ProxyClient._CONNECTED_.append(self)
|
||||
self._server.clientConnected(self)
|
||||
|
||||
def onClose(self):
|
||||
@@ -249,8 +187,6 @@ class ProxyClient(rdp.RDPClientObserver):
|
||||
@summary: Event inform that stack is close
|
||||
@see: rdp.RDPClientObserver.onClose
|
||||
"""
|
||||
if not self._name is None:
|
||||
ProxyClient._CONNECTED_.remove(self)
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
@@ -302,57 +238,18 @@ class ProxyClientFactory(rdp.ClientFactory):
|
||||
controller.setDomain(self._domain)
|
||||
controller.setUsername(self._username)
|
||||
controller.setPassword(self._password)
|
||||
return ProxyClient(controller, self._server, "%s\\%s on %s"%(self._domain, self._username, addr))
|
||||
return ProxyClient(controller, self._server)
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
pass
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
pass
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
pass
|
||||
|
||||
|
||||
class ProxyAdmin(rdp.RDPServerObserver):
|
||||
class Shadow(rdp.RDPServerObserver):
|
||||
"""
|
||||
@summary: Use to manage admin session
|
||||
Add GUI to select which session to see
|
||||
Just escape key is authorized during spy session
|
||||
To switch from spy state to admin state
|
||||
"""
|
||||
class State(object):
|
||||
GUI = 0 #->list of active session
|
||||
SPY = 1 #->watch active session
|
||||
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@param server: rdp.RDPServerController
|
||||
"""
|
||||
rdp.RDPServerObserver.__init__(self, controller)
|
||||
self._spy = None
|
||||
self._state = ProxyAdmin.State.GUI
|
||||
|
||||
def initView(self):
|
||||
"""
|
||||
@summary: Initialize Admin GUI view
|
||||
"""
|
||||
self._sessions = list(ProxyClient._CONNECTED_) #copy at t time
|
||||
width, height = self._controller.getScreen()
|
||||
self._window = view.Window(width, height, QtGui.QColor(8, 24, 66))
|
||||
|
||||
self._window.addView(view.Anchor(width / 2 - 250, 100,
|
||||
view.Label("Please select following session",
|
||||
500, 50, QtGui.QFont('arial', 18, QtGui.QFont.Bold),
|
||||
backgroundColor = QtGui.QColor(8, 24, 66))))
|
||||
|
||||
self._window.addView(view.Anchor(width / 2 - 250, 150,
|
||||
view.List([p._name for p in self._sessions],
|
||||
500, 500, self.onSelect,
|
||||
QtGui.QColor(8, 24, 66))), True)
|
||||
|
||||
def clientConnected(self, client):
|
||||
pass
|
||||
self._client = None
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@@ -360,73 +257,39 @@ class ProxyAdmin(rdp.RDPServerObserver):
|
||||
May be called after an setColorDepth too
|
||||
@see: rdp.RDPServerObserver.onReady
|
||||
"""
|
||||
if self._state == ProxyAdmin.State.GUI:
|
||||
self.initView()
|
||||
self._window.update(view.RDPRenderer(self._controller), True)
|
||||
elif self._state == ProxyAdmin.State.SPY:
|
||||
if self._client is None:
|
||||
username = self._controller.getUsername()
|
||||
if not ProxyServer._SESSIONS_.has_key(username):
|
||||
log.info("invalid session name [%s]"%username)
|
||||
self._controller.close()
|
||||
return
|
||||
|
||||
self._client = ProxyClient(ProxyServer._SESSIONS_[username]._controller, self)
|
||||
self._controller.setColorDepth(self._client._controller.getColorDepth())
|
||||
else:
|
||||
#refresh client
|
||||
width, height = self._controller.getScreen()
|
||||
self._spy._controller.sendRefreshOrder(0, 0, width, height)
|
||||
self._client._controller.sendRefreshOrder(0, 0, width, height)
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Stack is close
|
||||
@see: rdp.RDPServerObserver.onClose
|
||||
"""
|
||||
pass
|
||||
|
||||
def onKeyEventScancode(self, code, isPressed):
|
||||
""" Shadow
|
||||
"""
|
||||
@summary: Event call when a keyboard event
|
||||
is catch in scan code format
|
||||
@param code: scan code of key
|
||||
@param isPressed: True if key is down
|
||||
@see: rdp.RDPServerObserver.onKeyEventScancode
|
||||
"""
|
||||
if self._state == ProxyAdmin.State.GUI:
|
||||
if not isPressed:
|
||||
return
|
||||
self._window.keyEvent(code)
|
||||
self._window.update(view.RDPRenderer(self._controller))
|
||||
elif code == 1:
|
||||
#escape button refresh GUI
|
||||
self._state = ProxyAdmin.State.GUI
|
||||
self._spy._controller.removeClientObserver(self._spy)
|
||||
self.onReady()
|
||||
|
||||
def onKeyEventUnicode(self, code, isPressed):
|
||||
""" Shadow
|
||||
"""
|
||||
@summary: Event call when a keyboard event is catch in unicode format
|
||||
Admin GUI add filter for this event
|
||||
@param code: unicode of key
|
||||
@param isPressed: True if key is down
|
||||
@see: rdp.RDPServerObserver.onKeyEventUnicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def onPointerEvent(self, x, y, button, isPressed):
|
||||
""" Shadow
|
||||
"""
|
||||
@summary: Event call on mouse event
|
||||
Admin GUI add filter for this event
|
||||
@param x: x position
|
||||
@param y: y position
|
||||
@param button: 1, 2 or 3 button
|
||||
@param isPressed: True if mouse button is pressed
|
||||
@see: rdp.RDPServerObserver.onPointerEvent
|
||||
"""
|
||||
pass
|
||||
|
||||
def onSelect(self, index):
|
||||
"""
|
||||
@summary: Callback of list view of active session
|
||||
Connect to select session
|
||||
@param index: index in sessions array
|
||||
"""
|
||||
self._state = ProxyAdmin.State.SPY
|
||||
self._spy = ProxyClient(self._sessions[index]._controller, self)
|
||||
self._controller.setColorDepth(self._spy._controller.getColorDepth())
|
||||
|
||||
class ProxyAdminFactory(rdp.ServerFactory):
|
||||
class ShadowFactory(rdp.ServerFactory):
|
||||
"""
|
||||
@summary: Factory for admin session
|
||||
"""
|
||||
@@ -435,7 +298,7 @@ class ProxyAdminFactory(rdp.ServerFactory):
|
||||
@param privateKeyFilePath: private key for admin session
|
||||
@param certificateFilePath: certificate for admin session
|
||||
"""
|
||||
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
|
||||
rdp.ServerFactory.__init__(self, 16, privateKeyFilePath, certificateFilePath)
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@@ -445,116 +308,58 @@ class ProxyAdminFactory(rdp.ServerFactory):
|
||||
@return: ProxyAdmin
|
||||
@see: rdp.ServerFactory.buildObserver
|
||||
"""
|
||||
return ProxyAdmin(controller)
|
||||
|
||||
class CredentialProvider(object):
|
||||
"""
|
||||
@summary: Credential provider for proxy
|
||||
"""
|
||||
def __init__(self, config):
|
||||
"""
|
||||
@param config: rdp proxy config
|
||||
"""
|
||||
self._config = config
|
||||
|
||||
def getAccount(self, domain, username):
|
||||
"""
|
||||
@summary: Find account that match domain::username in config file
|
||||
@param domain: Windows domain
|
||||
@param username: username for session
|
||||
@return: [(unicode(ip), port] or None if not found
|
||||
"""
|
||||
if not self._config.has_key(domain) or not self._config[domain].has_key(username):
|
||||
return None
|
||||
return self._config[domain][username]
|
||||
|
||||
def getProxyPass(self, domain, username):
|
||||
"""
|
||||
@summary: Find list of server available for thi account
|
||||
@param domain: domain to check
|
||||
@param username: username in domain
|
||||
@return: [(ip, port)]
|
||||
"""
|
||||
account = self.getAccount(domain, username)
|
||||
if account is None:
|
||||
return []
|
||||
return [(str(machine["ip"]), machine["port"]) for machine in account]
|
||||
return Shadow(controller)
|
||||
|
||||
def help():
|
||||
"""
|
||||
@summary: Print help in console
|
||||
"""
|
||||
print "Usage: rdpy-rdpproxy -f credential_file_path -k private_key_file_path -c certificate_file_path [-i admin_ip[:admin_port]] listen_port"
|
||||
print "Usage: rdpy-rdpproxy -t target_ip[:target_port] [-k private_key_file_path (mandatory for SSL)] [-c certificate_file_path (mandatory for SSL)] [-i admin_ip[:admin_port]] listen_port"
|
||||
|
||||
def loadConfig(configFilePath):
|
||||
"""
|
||||
@summary: Load and check config file
|
||||
@param configFilePath: config file path
|
||||
"""
|
||||
if not os.path.isfile(configFilePath):
|
||||
log.error("File %s doesn't exist"%configFilePath)
|
||||
return None
|
||||
def parseIpPort(interface, defaultPort = "3389"):
|
||||
if ':' in interface:
|
||||
return interface.split(':')
|
||||
else:
|
||||
return interface, defaultPort
|
||||
|
||||
f = open(configFilePath, 'r')
|
||||
config = json.load(f)
|
||||
f.close()
|
||||
|
||||
return config
|
||||
def nowConnected():
|
||||
log.info("*" * 50)
|
||||
log.info("Now connected")
|
||||
log.info(ProxyServer._SESSIONS_.keys())
|
||||
log.info("*" * 50)
|
||||
|
||||
if __name__ == '__main__':
|
||||
configFilePath = None
|
||||
target = None
|
||||
privateKeyFilePath = None
|
||||
certificateFilePath = None
|
||||
adminInterface = None
|
||||
shadowInterface = None
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hf:k:c:i:")
|
||||
opts, args = getopt.getopt(sys.argv[1:], "ht:k:c:i:")
|
||||
except getopt.GetoptError:
|
||||
help()
|
||||
for opt, arg in opts:
|
||||
if opt == "-h":
|
||||
help()
|
||||
sys.exit()
|
||||
elif opt == "-f":
|
||||
configFilePath = arg
|
||||
elif opt == "-t":
|
||||
target = arg
|
||||
elif opt == "-k":
|
||||
privateKeyFilePath = arg
|
||||
elif opt == "-c":
|
||||
certificateFilePath = arg
|
||||
elif opt == "-i":
|
||||
adminInterface = arg
|
||||
shadowInterface = arg
|
||||
|
||||
if configFilePath is None:
|
||||
print "Config file is mandatory"
|
||||
if target is None:
|
||||
log.error("Target is mandatory")
|
||||
help()
|
||||
sys.exit()
|
||||
|
||||
if certificateFilePath is None:
|
||||
print "Certificate file is mandatory"
|
||||
help()
|
||||
sys.exit()
|
||||
reactor.listenTCP(int(args[0]), ProxyServerFactory(parseIpPort(target), privateKeyFilePath, certificateFilePath))
|
||||
|
||||
if privateKeyFilePath is None:
|
||||
print "Private key file is mandatory"
|
||||
help()
|
||||
sys.exit()
|
||||
|
||||
#load config file
|
||||
config = loadConfig(configFilePath)
|
||||
if config is None:
|
||||
log.error("Bad configuration file")
|
||||
sys.exit()
|
||||
|
||||
#use to init font
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
|
||||
reactor.listenTCP(int(args[0]), ProxyServerFactory(CredentialProvider(config), privateKeyFilePath, certificateFilePath))
|
||||
|
||||
if not adminInterface is None:
|
||||
if ':' in adminInterface:
|
||||
adminInterface, adminPort = adminInterface.split(':')
|
||||
else:
|
||||
adminInterface, adminPort = adminInterface, "3390"
|
||||
log.info("Admin listen on %s:%s"%(adminInterface, adminPort))
|
||||
reactor.listenTCP(int(adminPort), ProxyAdminFactory(privateKeyFilePath, certificateFilePath), interface = adminInterface)
|
||||
if not shadowInterface is None:
|
||||
shadowInterface, shadowPort = parseIpPort(shadowInterface)
|
||||
log.info("Shadow listener on %s:%s"%(shadowInterface, shadowPort))
|
||||
reactor.listenTCP(int(shadowPort), ShadowFactory(privateKeyFilePath, certificateFilePath), interface = shadowInterface)
|
||||
reactor.run()
|
||||
@@ -29,6 +29,7 @@ from PyQt4 import QtCore, QtGui
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from rdpy.ui.qt4 import RDPBitmapToQtImage
|
||||
import rdpy.core.log as log
|
||||
from rdpy.core.error import RDPSecurityNegoFail
|
||||
from twisted.internet import task
|
||||
|
||||
#set log level
|
||||
@@ -39,18 +40,23 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
@summary: Factory for screenshot exemple
|
||||
"""
|
||||
__INSTANCE__ = 0
|
||||
def __init__(self, width, height, path, timeout):
|
||||
__STATE__ = []
|
||||
def __init__(self, reactor, app, width, height, path, timeout):
|
||||
"""
|
||||
@param width: width of screen
|
||||
@param height: height of screen
|
||||
@param path: path of output screenshot
|
||||
@param timeout: close connection after timeout s without any updating
|
||||
@param reactor: twisted reactor
|
||||
@param width: {integer} width of screen
|
||||
@param height: {integer} height of screen
|
||||
@param path: {str} path of output screenshot
|
||||
@param timeout: {float} close connection after timeout s without any updating
|
||||
"""
|
||||
RDPScreenShotFactory.__INSTANCE__ += 1
|
||||
self._reactor = reactor
|
||||
self._app = app
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._path = path
|
||||
self._timeout = timeout
|
||||
self._security = "ssl"
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
"""
|
||||
@@ -58,11 +64,18 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
@param connector: twisted connector use for rdp connection (use reconnect to restart connection)
|
||||
@param reason: str use to advertise reason of lost connection
|
||||
"""
|
||||
if reason.type == RDPSecurityNegoFail and self._security != "rdp":
|
||||
log.info("due to RDPSecurityNegoFail try standard security layer")
|
||||
self._security = "rdp"
|
||||
connector.connect()
|
||||
return
|
||||
|
||||
log.info("connection lost : %s"%reason)
|
||||
RDPScreenShotFactory.__STATE__.append((connector.host, connector.port, reason))
|
||||
RDPScreenShotFactory.__INSTANCE__ -= 1
|
||||
if(RDPScreenShotFactory.__INSTANCE__ == 0):
|
||||
reactor.stop()
|
||||
app.exit()
|
||||
self._reactor.stop()
|
||||
self._app.exit()
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
"""
|
||||
@@ -71,10 +84,11 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
@param reason: str use to advertise reason of lost connection
|
||||
"""
|
||||
log.info("connection failed : %s"%reason)
|
||||
RDPScreenShotFactory.__STATE__.append((connector.host, connector.port, reason))
|
||||
RDPScreenShotFactory.__INSTANCE__ -= 1
|
||||
if(RDPScreenShotFactory.__INSTANCE__ == 0):
|
||||
reactor.stop()
|
||||
app.exit()
|
||||
self._reactor.stop()
|
||||
self._app.exit()
|
||||
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
@@ -87,20 +101,24 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
"""
|
||||
@summary: observer that connect, cache every image received and save at deconnection
|
||||
"""
|
||||
def __init__(self, controller, width, height, path, timeout):
|
||||
def __init__(self, controller, width, height, security, path, timeout, reactor):
|
||||
"""
|
||||
@param controller: RDPClientController
|
||||
@param width: width of screen
|
||||
@param height: height of screen
|
||||
@param path: path of output screenshot
|
||||
@param timeout: close connection after timeout s without any updating
|
||||
@param controller: {RDPClientController}
|
||||
@param width: {integer} width of screen
|
||||
@param height: {integer} height of screen
|
||||
@param security: {str} (ssl | rdp) security level
|
||||
@param path: {str} path of output screenshot
|
||||
@param timeout: {float} close connection after timeout s without any updating
|
||||
@param reactor: twisted reactor
|
||||
"""
|
||||
rdp.RDPClientObserver.__init__(self, controller)
|
||||
controller.setScreen(width, height);
|
||||
controller.setSecurityLevel(security)
|
||||
self._buffer = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
|
||||
self._path = path
|
||||
self._timeout = timeout
|
||||
self._startTimeout = False
|
||||
self._reactor = reactor
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
@@ -112,7 +130,7 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
qp.drawImage(destLeft, destTop, image, 0, 0, destRight - destLeft + 1, destBottom - destTop + 1)
|
||||
if not self._startTimeout:
|
||||
self._startTimeout = False
|
||||
reactor.callLater(self._timeout, self.checkUpdate)
|
||||
self._reactor.callLater(self._timeout, self.checkUpdate)
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@@ -130,7 +148,37 @@ class RDPScreenShotFactory(rdp.ClientFactory):
|
||||
def checkUpdate(self):
|
||||
self._controller.close();
|
||||
|
||||
return ScreenShotObserver(controller, self._width, self._height, self._path, self._timeout)
|
||||
return ScreenShotObserver(controller, self._width, self._height, self._security, self._path, self._timeout, self._reactor)
|
||||
|
||||
def main(width, height, path, timeout, hosts):
|
||||
"""
|
||||
@summary: main algorithm
|
||||
@param height: {integer} height of screenshot
|
||||
@param width: {integer} width of screenshot
|
||||
@param timeout: {float} in sec
|
||||
@param hosts: {list(str(ip[:port]))}
|
||||
@return: {list(tuple(ip, port, Failure instance)} list of connection state
|
||||
"""
|
||||
#create application
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
|
||||
#add qt4 reactor
|
||||
import qt4reactor
|
||||
qt4reactor.install()
|
||||
|
||||
from twisted.internet import reactor
|
||||
|
||||
for host in hosts:
|
||||
if ':' in host:
|
||||
ip, port = host.split(':')
|
||||
else:
|
||||
ip, port = host, "3389"
|
||||
|
||||
reactor.connectTCP(ip, int(port), RDPScreenShotFactory(reactor, app, width, height, path + "%s.jpg"%ip, timeout))
|
||||
|
||||
reactor.runReturn()
|
||||
app.exec_()
|
||||
return RDPScreenShotFactory.__STATE__
|
||||
|
||||
def help():
|
||||
print "Usage: rdpy-rdpscreenshot [options] ip[:port]"
|
||||
@@ -163,22 +211,4 @@ if __name__ == '__main__':
|
||||
elif opt == "-t":
|
||||
timeout = float(arg)
|
||||
|
||||
#create application
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
|
||||
#add qt4 reactor
|
||||
import qt4reactor
|
||||
qt4reactor.install()
|
||||
|
||||
from twisted.internet import reactor
|
||||
|
||||
for arg in args:
|
||||
if ':' in arg:
|
||||
ip, port = arg.split(':')
|
||||
else:
|
||||
ip, port = arg, "3389"
|
||||
|
||||
reactor.connectTCP(ip, int(port), RDPScreenShotFactory(width, height, path + "%s.jpg"%ip, timeout))
|
||||
|
||||
reactor.runReturn()
|
||||
app.exec_()
|
||||
main(width, height, path, timeout, args)
|
||||
@@ -30,6 +30,7 @@ class Level(object):
|
||||
INFO = 1
|
||||
WARNING = 2
|
||||
ERROR = 3
|
||||
NONE = 4
|
||||
|
||||
_LOG_LEVEL = Level.DEBUG
|
||||
|
||||
|
||||
@@ -142,10 +142,10 @@ class EncryptionLevel(object):
|
||||
@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
|
||||
ENCRYPTION_LEVEL_LOW = 0x00000001
|
||||
ENCRYPTION_LEVEL_CLIENT_COMPATIBLE = 0x00000002
|
||||
ENCRYPTION_LEVEL_HIGH = 0x00000003
|
||||
ENCRYPTION_LEVEL_FIPS = 0x00000004
|
||||
|
||||
class ChannelOptions(object):
|
||||
"""
|
||||
@@ -355,7 +355,7 @@ class ProprietaryServerCertificate(CompositeType):
|
||||
self.wSignatureBlobType = UInt16Le(0x0008, constant = True)
|
||||
self.wSignatureBlobLen = UInt16Le(lambda:(sizeof(self.SignatureBlob) - 8))
|
||||
self.SignatureBlob = String(readLen = self.wSignatureBlobLen)
|
||||
self.padding = String("\x00" * 8, readLen = UInt8(8))
|
||||
self.padding = String(b"\x00" * 8, readLen = UInt8(8))
|
||||
|
||||
def getPublicKey(self):
|
||||
"""
|
||||
@@ -380,7 +380,7 @@ class ProprietaryServerCertificate(CompositeType):
|
||||
md5Digest = md5.new()
|
||||
md5Digest.update(s.getvalue())
|
||||
|
||||
return md5Digest.digest() + "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01"
|
||||
return md5Digest.digest() + "\x00" + "\xff" * 46 + "\x01"
|
||||
|
||||
def sign(self):
|
||||
"""
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
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 sec
|
||||
import sec, gcc
|
||||
from rdpy.security import rc4
|
||||
from rdpy.security import rsa_wrapper as rsa
|
||||
|
||||
class MessageType(object):
|
||||
"""
|
||||
@@ -253,35 +254,31 @@ def createValidClientLicensingErrorMessage():
|
||||
|
||||
class LicenseManager(object):
|
||||
"""
|
||||
@summary: handle license automata
|
||||
@summary: handle license automata (client side)
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241890.aspx
|
||||
"""
|
||||
def __init__(self, transport):
|
||||
"""
|
||||
@param transport: layer use to send packet
|
||||
"""
|
||||
self._preMasterSecret = "\x00" * 64
|
||||
self._clientRandom = "\x00" * 32
|
||||
self._serverRandom = None
|
||||
self._serverEncryptedChallenge = None
|
||||
self._transport = transport
|
||||
self._username = ""
|
||||
self._hostname = ""
|
||||
|
||||
def generateKeys(self):
|
||||
"""
|
||||
@summary: generate key for license session
|
||||
@summary: generate keys for license session
|
||||
"""
|
||||
masterSecret = sec.masterSecret(self._preMasterSecret, self._clientRandom, self._serverRandom)
|
||||
sessionKeyBlob = sec.masterSecret(masterSecret, self._serverRandom, self._clientRandom)
|
||||
self._macSalt = sessionKeyBlob[:16]
|
||||
self._licenseKey = sec.finalHash(sessionKeyBlob[16:32], self._clientRandom, self._serverRandom)
|
||||
|
||||
|
||||
def recv(self, s):
|
||||
"""
|
||||
@summary: receive license packet from PDU layer
|
||||
@return true when license automata is finish
|
||||
"""
|
||||
with open("/tmp/toto", "wb") as f:
|
||||
f.write(s.getvalue()[s.pos:].encode('base64'))
|
||||
|
||||
licPacket = LicPacket()
|
||||
s.readType(licPacket)
|
||||
|
||||
@@ -290,14 +287,11 @@ class LicenseManager(object):
|
||||
return True
|
||||
|
||||
elif licPacket.bMsgtype.value == MessageType.LICENSE_REQUEST:
|
||||
self._serverRandom = licPacket.licensingMessage.serverRandom.value
|
||||
self.generateKeys()
|
||||
self.sendClientNewLicenseRequest()
|
||||
self.sendClientNewLicenseRequest(licPacket.licensingMessage)
|
||||
return False
|
||||
|
||||
elif licPacket.bMsgtype.value == MessageType.PLATFORM_CHALLENGE:
|
||||
self._serverEncryptedChallenge = licPacket.licensingMessage.encryptedPlatformChallenge.blobData.value
|
||||
self.sendClientChallengeResponse()
|
||||
self.sendClientChallengeResponse(licPacket.licensingMessage)
|
||||
return False
|
||||
|
||||
#yes get a new license
|
||||
@@ -308,34 +302,52 @@ class LicenseManager(object):
|
||||
raise InvalidExpectedDataException("Not a valid license packet")
|
||||
|
||||
|
||||
def sendClientNewLicenseRequest(self):
|
||||
def sendClientNewLicenseRequest(self, licenseRequest):
|
||||
"""
|
||||
@summary: Create new license request in response to server license request
|
||||
@param licenseRequest: {ServerLicenseRequest}
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241989.aspx
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241918.aspx
|
||||
"""
|
||||
#get server information
|
||||
serverRandom = licenseRequest.serverRandom.value
|
||||
s = Stream(licenseRequest.serverCertificate.blobData.value)
|
||||
serverCertificate = gcc.ServerCertificate()
|
||||
s.readType(serverCertificate)
|
||||
|
||||
#generate crypto values
|
||||
clientRandom = rsa.random(256)
|
||||
preMasterSecret = rsa.random(384)
|
||||
masterSecret = sec.masterSecret(preMasterSecret, clientRandom, serverRandom)
|
||||
sessionKeyBlob = sec.masterSecret(masterSecret, serverRandom, clientRandom)
|
||||
self._macSalt = sessionKeyBlob[:16]
|
||||
self._licenseKey = sec.finalHash(sessionKeyBlob[16:32], clientRandom, serverRandom)
|
||||
|
||||
#format message
|
||||
message = ClientNewLicenseRequest()
|
||||
message.clientRandom.value = self._clientRandom
|
||||
message.encryptedPreMasterSecret.blobData = String(self._preMasterSecret + "\x00" * 8)
|
||||
message.ClientMachineName.blobData = String(self._hostname + "\x00")
|
||||
message.ClientUserName.blobData = String(self._username + "\x00")
|
||||
message.clientRandom.value = clientRandom
|
||||
message.encryptedPreMasterSecret.blobData.value = rsa.encrypt(preMasterSecret[::-1], serverCertificate.certData.getPublicKey())[::-1] + "\x00" * 8
|
||||
message.ClientMachineName.blobData.value = self._hostname + "\x00"
|
||||
message.ClientUserName.blobData.value = self._username + "\x00"
|
||||
self._transport.sendFlagged(sec.SecurityFlag.SEC_LICENSE_PKT, LicPacket(message))
|
||||
|
||||
def sendClientChallengeResponse(self):
|
||||
def sendClientChallengeResponse(self, platformChallenge):
|
||||
"""
|
||||
@summary: generate valid challenge response
|
||||
@param platformChallenge: {ServerPlatformChallenge}
|
||||
"""
|
||||
serverEncryptedChallenge = platformChallenge.encryptedPlatformChallenge.blobData.value
|
||||
#decrypt server challenge
|
||||
#it should be TEST word in unicode format
|
||||
serverChallenge = rc4.crypt(rc4.RC4Key(self._licenseKey), self._serverEncryptedChallenge)
|
||||
serverChallenge = rc4.crypt(rc4.RC4Key(self._licenseKey), serverEncryptedChallenge)
|
||||
|
||||
#generate hwid
|
||||
s = Stream()
|
||||
s.writeType((UInt32Le(2), String(self._hostname + "\x00" * 16)))
|
||||
s.writeType((UInt32Le(2), String(self._hostname + self._username + "\x00" * 16)))
|
||||
hwid = s.getvalue()[:20]
|
||||
|
||||
message = ClientPLatformChallengeResponse()
|
||||
message.encryptedPlatformChallengeResponse.blobData.value = self._serverEncryptedChallenge
|
||||
message.encryptedPlatformChallengeResponse.blobData.value = serverEncryptedChallenge
|
||||
message.encryptedHWID.blobData.value = rc4.crypt(rc4.RC4Key(self._licenseKey), hwid)
|
||||
message.MACData.value = sec.macData(self._macSalt, serverChallenge + hwid)
|
||||
|
||||
|
||||
@@ -539,4 +539,4 @@ class MultiFragmentUpdate(CompositeType):
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.MaxRequestSize = UInt32Le(0xffffffff)
|
||||
self.MaxRequestSize = UInt32Le(0)
|
||||
@@ -29,7 +29,7 @@ import caps, order
|
||||
|
||||
class PDUType(object):
|
||||
"""
|
||||
Data PDU type primary index
|
||||
@summary: Data PDU type primary index
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240576.aspx
|
||||
"""
|
||||
PDUTYPE_DEMANDACTIVEPDU = 0x11
|
||||
@@ -40,7 +40,7 @@ class PDUType(object):
|
||||
|
||||
class PDUType2(object):
|
||||
"""
|
||||
Data PDU type secondary index
|
||||
@summary: Data PDU type secondary index
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240577.aspx
|
||||
"""
|
||||
PDUTYPE2_UPDATE = 0x02
|
||||
@@ -70,7 +70,7 @@ class PDUType2(object):
|
||||
|
||||
class StreamId(object):
|
||||
"""
|
||||
Stream priority
|
||||
@summary: Stream priority
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240577.aspx
|
||||
"""
|
||||
STREAM_UNDEFINED = 0x00
|
||||
@@ -80,7 +80,7 @@ class StreamId(object):
|
||||
|
||||
class CompressionOrder(object):
|
||||
"""
|
||||
PDU compression order
|
||||
@summary: PDU compression order
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240577.aspx
|
||||
"""
|
||||
CompressionTypeMask = 0x0F
|
||||
@@ -90,7 +90,7 @@ class CompressionOrder(object):
|
||||
|
||||
class CompressionType(object):
|
||||
"""
|
||||
PDU compression type
|
||||
@summary: PDU compression type
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240577.aspx
|
||||
"""
|
||||
PACKET_COMPR_TYPE_8K = 0x0
|
||||
@@ -100,7 +100,7 @@ class CompressionType(object):
|
||||
|
||||
class Action(object):
|
||||
"""
|
||||
Action flag use in Control PDU packet
|
||||
@summary: Action flag use in Control PDU packet
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240492.aspx
|
||||
"""
|
||||
CTRLACTION_REQUEST_CONTROL = 0x0001
|
||||
@@ -110,7 +110,7 @@ class Action(object):
|
||||
|
||||
class PersistentKeyListFlag(object):
|
||||
"""
|
||||
Use to determine the number of persistent key packet
|
||||
@summary: Use to determine the number of persistent key packet
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240495.aspx
|
||||
"""
|
||||
PERSIST_FIRST_PDU = 0x01
|
||||
@@ -118,7 +118,7 @@ class PersistentKeyListFlag(object):
|
||||
|
||||
class BitmapFlag(object):
|
||||
"""
|
||||
Use in bitmap update PDU
|
||||
@summary: Use in bitmap update PDU
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240612.aspx
|
||||
"""
|
||||
BITMAP_COMPRESSION = 0x0001
|
||||
@@ -126,7 +126,7 @@ class BitmapFlag(object):
|
||||
|
||||
class UpdateType(object):
|
||||
"""
|
||||
Use in update PDU to determine which type of update
|
||||
@summary: Use in update PDU to determine which type of update
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240608.aspx
|
||||
"""
|
||||
UPDATETYPE_ORDERS = 0x0000
|
||||
@@ -136,7 +136,7 @@ class UpdateType(object):
|
||||
|
||||
class InputMessageType(object):
|
||||
"""
|
||||
Use in slow-path input PDU
|
||||
@summary: Use in slow-path input PDU
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240583.aspx
|
||||
"""
|
||||
INPUT_EVENT_SYNC = 0x0000
|
||||
@@ -148,7 +148,7 @@ class InputMessageType(object):
|
||||
|
||||
class PointerFlag(object):
|
||||
"""
|
||||
Use in Pointer event
|
||||
@summary: Use in Pointer event
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240586.aspx
|
||||
"""
|
||||
PTRFLAGS_HWHEEL = 0x0400
|
||||
@@ -163,7 +163,7 @@ class PointerFlag(object):
|
||||
|
||||
class KeyboardFlag(object):
|
||||
"""
|
||||
Use in scan code key event
|
||||
@summary: Use in scan code key event
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240584.aspx
|
||||
"""
|
||||
KBDFLAGS_EXTENDED = 0x0100
|
||||
@@ -172,7 +172,7 @@ class KeyboardFlag(object):
|
||||
|
||||
class FastPathUpdateType(object):
|
||||
"""
|
||||
Use in Fast Path update packet
|
||||
@summary: Use in Fast Path update packet
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240622.aspx
|
||||
"""
|
||||
FASTPATH_UPDATETYPE_ORDERS = 0x0
|
||||
@@ -189,14 +189,14 @@ class FastPathUpdateType(object):
|
||||
|
||||
class FastPathOutputCompression(object):
|
||||
"""
|
||||
Flag for compression
|
||||
@summary: Flag for compression
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240622.aspx
|
||||
"""
|
||||
FASTPATH_OUTPUT_COMPRESSION_USED = 0x2
|
||||
|
||||
class Display(object):
|
||||
"""
|
||||
Use in supress output PDU
|
||||
@summary: Use in supress output PDU
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240648.aspx
|
||||
"""
|
||||
SUPPRESS_DISPLAY_UPDATES = 0x00
|
||||
@@ -204,7 +204,7 @@ class Display(object):
|
||||
|
||||
class ErrorInfo(object):
|
||||
"""
|
||||
Error code use in Error info PDU
|
||||
@summary: Error code use in Error info PDU
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240544.aspx
|
||||
"""
|
||||
ERRINFO_RPC_INITIATED_DISCONNECT = 0x00000001
|
||||
@@ -417,12 +417,12 @@ class ErrorInfo(object):
|
||||
|
||||
class ShareControlHeader(CompositeType):
|
||||
"""
|
||||
PDU share control header
|
||||
@summary: PDU share control header
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240576.aspx
|
||||
"""
|
||||
def __init__(self, totalLength, pduType, userId):
|
||||
"""
|
||||
Set pduType as constant
|
||||
@summary: Set pduType as constant
|
||||
@param totalLength: total length of PDU packet
|
||||
"""
|
||||
CompositeType.__init__(self)
|
||||
@@ -433,7 +433,7 @@ class ShareControlHeader(CompositeType):
|
||||
|
||||
class ShareDataHeader(CompositeType):
|
||||
"""
|
||||
PDU share data header
|
||||
@summary: PDU share data header
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240577.aspx
|
||||
"""
|
||||
def __init__(self, size, pduType2 = 0, shareId = 0):
|
||||
@@ -448,7 +448,7 @@ class ShareDataHeader(CompositeType):
|
||||
|
||||
class PDU(CompositeType):
|
||||
"""
|
||||
Main PDU message
|
||||
@summary: Main PDU message
|
||||
"""
|
||||
def __init__(self, userId = 0, pduMessage = None):
|
||||
CompositeType.__init__(self)
|
||||
@@ -456,7 +456,7 @@ class PDU(CompositeType):
|
||||
|
||||
def PDUMessageFactory():
|
||||
"""
|
||||
build message in accordance of type self.shareControlHeader.pduType.value
|
||||
@summary: build message in accordance of type self.shareControlHeader.pduType.value
|
||||
"""
|
||||
for c in [DemandActivePDU, ConfirmActivePDU, DataPDU, DeactiveAllPDU]:
|
||||
if self.shareControlHeader.pduType.value == c._PDUTYPE_:
|
||||
@@ -475,7 +475,7 @@ class PDU(CompositeType):
|
||||
class DemandActivePDU(CompositeType):
|
||||
"""
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240485.aspx
|
||||
Main use for capabilities exchange server -> client
|
||||
@summary: Main use for capabilities exchange server -> client
|
||||
"""
|
||||
#may declare the PDU type
|
||||
_PDUTYPE_ = PDUType.PDUTYPE_DEMANDACTIVEPDU
|
||||
@@ -494,7 +494,7 @@ class DemandActivePDU(CompositeType):
|
||||
class ConfirmActivePDU(CompositeType):
|
||||
"""
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240488.aspx
|
||||
Main use for capabilities confirm client -> sever
|
||||
@summary: Main use for capabilities confirm client -> sever
|
||||
"""
|
||||
#may declare the PDU type
|
||||
_PDUTYPE_ = PDUType.PDUTYPE_CONFIRMACTIVEPDU
|
||||
@@ -512,7 +512,7 @@ class ConfirmActivePDU(CompositeType):
|
||||
|
||||
class DeactiveAllPDU(CompositeType):
|
||||
"""
|
||||
Use to signal already connected session
|
||||
@summary: Use to signal already connected session
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240536.aspx
|
||||
"""
|
||||
#may declare the PDU type
|
||||
@@ -526,7 +526,7 @@ class DeactiveAllPDU(CompositeType):
|
||||
|
||||
class DataPDU(CompositeType):
|
||||
"""
|
||||
Generic PDU packet use after connection sequence
|
||||
@summary: Generic PDU packet use after connection sequence
|
||||
"""
|
||||
#may declare the PDU type
|
||||
_PDUTYPE_ = PDUType.PDUTYPE_DATAPDU
|
||||
@@ -537,7 +537,7 @@ class DataPDU(CompositeType):
|
||||
|
||||
def PDUDataFactory():
|
||||
"""
|
||||
Create object in accordance self.shareDataHeader.pduType2 value
|
||||
@summary: Create object in accordance self.shareDataHeader.pduType2 value
|
||||
"""
|
||||
for c in [UpdateDataPDU, SynchronizeDataPDU, ControlDataPDU, ErrorInfoDataPDU, FontListDataPDU, FontMapDataPDU, PersistentListPDU, ClientInputEventPDU, ShutdownDeniedPDU, ShutdownRequestPDU, SupressOutputDataPDU]:
|
||||
if self.shareDataHeader.pduType2.value == c._PDUTYPE2_:
|
||||
@@ -584,7 +584,7 @@ class ControlDataPDU(CompositeType):
|
||||
|
||||
class ErrorInfoDataPDU(CompositeType):
|
||||
"""
|
||||
Use to inform error in PDU layer
|
||||
@summary: Use to inform error in PDU layer
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240544.aspx
|
||||
"""
|
||||
_PDUTYPE2_ = PDUType2.PDUTYPE2_SET_ERROR_INFO_PDU
|
||||
@@ -600,7 +600,7 @@ class ErrorInfoDataPDU(CompositeType):
|
||||
|
||||
class FontListDataPDU(CompositeType):
|
||||
"""
|
||||
Use to indicate list of font. Deprecated packet
|
||||
@summary: Use to indicate list of font. Deprecated packet
|
||||
client -> server
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240498.aspx
|
||||
"""
|
||||
@@ -618,7 +618,7 @@ class FontListDataPDU(CompositeType):
|
||||
|
||||
class FontMapDataPDU(CompositeType):
|
||||
"""
|
||||
Use to indicate map of font. Deprecated packet (maybe the same as FontListDataPDU)
|
||||
@summary: Use to indicate map of font. Deprecated packet (maybe the same as FontListDataPDU)
|
||||
server -> client
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240498.aspx
|
||||
"""
|
||||
@@ -636,7 +636,7 @@ class FontMapDataPDU(CompositeType):
|
||||
|
||||
class PersistentListEntry(CompositeType):
|
||||
"""
|
||||
Use to record persistent key in PersistentListPDU
|
||||
@summary: Use to record persistent key in PersistentListPDU
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240496.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
@@ -646,7 +646,7 @@ class PersistentListEntry(CompositeType):
|
||||
|
||||
class PersistentListPDU(CompositeType):
|
||||
"""
|
||||
Use to indicate that bitmap cache was already
|
||||
@summary: Use to indicate that bitmap cache was already
|
||||
Fill with some keys from previous session
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240495.aspx
|
||||
"""
|
||||
@@ -671,7 +671,7 @@ class PersistentListPDU(CompositeType):
|
||||
|
||||
class ClientInputEventPDU(CompositeType):
|
||||
"""
|
||||
PDU use to send client inputs in slow path mode
|
||||
@summary: PDU use to send client inputs in slow path mode
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc746160.aspx
|
||||
"""
|
||||
_PDUTYPE2_ = PDUType2.PDUTYPE2_INPUT
|
||||
@@ -684,7 +684,7 @@ class ClientInputEventPDU(CompositeType):
|
||||
|
||||
class ShutdownRequestPDU(CompositeType):
|
||||
"""
|
||||
PDU use to signal that the session will be closed
|
||||
@summary: PDU use to signal that the session will be closed
|
||||
client -> server
|
||||
"""
|
||||
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_REQUEST
|
||||
@@ -693,7 +693,7 @@ class ShutdownRequestPDU(CompositeType):
|
||||
|
||||
class ShutdownDeniedPDU(CompositeType):
|
||||
"""
|
||||
PDU use to signal which the session will be closed is connected
|
||||
@summary: PDU use to signal which the session will be closed is connected
|
||||
server -> client
|
||||
"""
|
||||
_PDUTYPE2_ = PDUType2.PDUTYPE2_SHUTDOWN_DENIED
|
||||
@@ -737,7 +737,7 @@ class RefreshRectPDU(CompositeType):
|
||||
|
||||
class UpdateDataPDU(CompositeType):
|
||||
"""
|
||||
Update data PDU use by server to inform update image or palet
|
||||
@summary: Update data PDU use by server to inform update image or palet
|
||||
for example
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240608.aspx
|
||||
"""
|
||||
@@ -754,7 +754,7 @@ class UpdateDataPDU(CompositeType):
|
||||
|
||||
def UpdateDataFactory():
|
||||
"""
|
||||
Create object in accordance self.updateType value
|
||||
@summary: Create object in accordance self.updateType value
|
||||
"""
|
||||
for c in [BitmapUpdateDataPDU]:
|
||||
if self.updateType.value == c._UPDATE_TYPE_:
|
||||
@@ -771,7 +771,7 @@ class UpdateDataPDU(CompositeType):
|
||||
|
||||
class FastPathUpdatePDU(CompositeType):
|
||||
"""
|
||||
Fast path update PDU packet
|
||||
@summary: Fast path update PDU packet
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240622.aspx
|
||||
"""
|
||||
def __init__(self, updateData = None):
|
||||
@@ -782,7 +782,7 @@ class FastPathUpdatePDU(CompositeType):
|
||||
|
||||
def UpdateDataFactory():
|
||||
"""
|
||||
Create correct object in accordance to self.updateHeader field
|
||||
@summary: Create correct object in accordance to self.updateHeader field
|
||||
"""
|
||||
for c in [FastPathBitmapUpdateDataPDU]:
|
||||
if (self.updateHeader.value & 0xf) == c._FASTPATH_UPDATE_TYPE_:
|
||||
@@ -799,7 +799,7 @@ class FastPathUpdatePDU(CompositeType):
|
||||
|
||||
class BitmapUpdateDataPDU(CompositeType):
|
||||
"""
|
||||
PDU use to send raw bitmap compressed or not
|
||||
@summary: PDU use to send raw bitmap compressed or not
|
||||
@see: http://msdn.microsoft.com/en-us/library/dd306368.aspx
|
||||
"""
|
||||
_UPDATE_TYPE_ = UpdateType.UPDATETYPE_BITMAP
|
||||
@@ -814,7 +814,7 @@ class BitmapUpdateDataPDU(CompositeType):
|
||||
|
||||
class OrderUpdateDataPDU(CompositeType):
|
||||
"""
|
||||
PDU type use to communicate Accelerated order (GDI)
|
||||
@summary: PDU type use to communicate Accelerated order (GDI)
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241571.aspx
|
||||
@todo: not implemented yet but need it
|
||||
"""
|
||||
@@ -827,7 +827,7 @@ class OrderUpdateDataPDU(CompositeType):
|
||||
|
||||
class BitmapCompressedDataHeader(CompositeType):
|
||||
"""
|
||||
Compressed header of bitmap
|
||||
@summary: Compressed header of bitmap
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240644.aspx
|
||||
"""
|
||||
def __init__(self, bodySize = 0, scanWidth = 0, uncompressedSize = 0, conditional = lambda:True):
|
||||
@@ -846,7 +846,7 @@ class BitmapCompressedDataHeader(CompositeType):
|
||||
|
||||
class BitmapData(CompositeType):
|
||||
"""
|
||||
Bitmap data here the screen capture
|
||||
@summary: Bitmap data here the screen capture
|
||||
"""
|
||||
def __init__(self, destLeft = 0, destTop = 0, destRight = 0, destBottom = 0, width = 0, height = 0, bitsPerPixel = 0, bitmapDataStream = ""):
|
||||
"""
|
||||
@@ -874,7 +874,7 @@ class BitmapData(CompositeType):
|
||||
|
||||
class FastPathBitmapUpdateDataPDU(CompositeType):
|
||||
"""
|
||||
Fast path version of bitmap update PDU
|
||||
@summary: Fast path version of bitmap update PDU
|
||||
@see: http://msdn.microsoft.com/en-us/library/dd306368.aspx
|
||||
"""
|
||||
_FASTPATH_UPDATE_TYPE_ = FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP
|
||||
@@ -887,7 +887,7 @@ class FastPathBitmapUpdateDataPDU(CompositeType):
|
||||
|
||||
class SlowPathInputEvent(CompositeType):
|
||||
"""
|
||||
PDU use in slow-path sending client inputs
|
||||
@summary: PDU use in slow-path sending client inputs
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240583.aspx
|
||||
"""
|
||||
def __init__(self, messageData = None):
|
||||
@@ -911,7 +911,7 @@ class SlowPathInputEvent(CompositeType):
|
||||
|
||||
class PointerEvent(CompositeType):
|
||||
"""
|
||||
Event use to communicate mouse position
|
||||
@summary: Event use to communicate mouse position
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240586.aspx
|
||||
"""
|
||||
_INPUT_MESSAGE_TYPE_ = InputMessageType.INPUT_EVENT_MOUSE
|
||||
@@ -924,7 +924,7 @@ class PointerEvent(CompositeType):
|
||||
|
||||
class ScancodeKeyEvent(CompositeType):
|
||||
"""
|
||||
Event use to communicate keyboard informations
|
||||
@summary: Event use to communicate keyboard informations
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240584.aspx
|
||||
"""
|
||||
_INPUT_MESSAGE_TYPE_ = InputMessageType.INPUT_EVENT_SCANCODE
|
||||
@@ -937,7 +937,7 @@ class ScancodeKeyEvent(CompositeType):
|
||||
|
||||
class UnicodeKeyEvent(CompositeType):
|
||||
"""
|
||||
Event use to communicate keyboard informations
|
||||
@summary: Event use to communicate keyboard informations
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240585.aspx
|
||||
"""
|
||||
_INPUT_MESSAGE_TYPE_ = InputMessageType.INPUT_EVENT_UNICODE
|
||||
|
||||
@@ -411,7 +411,7 @@ class Server(PDULayer):
|
||||
self._clientCapabilities[cap.capabilitySetType] = cap
|
||||
|
||||
#find use full flag
|
||||
self._clientFastPathSupported = bool(self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & (caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED | caps.GeneralExtraFlag.LONG_CREDENTIALS_SUPPORTED))
|
||||
self._clientFastPathSupported = bool(self._clientCapabilities[caps.CapsType.CAPSTYPE_GENERAL].capability.extraFlags.value & caps.GeneralExtraFlag.FASTPATH_OUTPUT_SUPPORTED)
|
||||
|
||||
self.setNextState(self.recvClientSynchronizePDU)
|
||||
|
||||
|
||||
@@ -123,22 +123,26 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
@param layout: us | fr
|
||||
"""
|
||||
if layout == "fr":
|
||||
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).kbdLayout.value = gcc.KeyboardLayout.FRENCH
|
||||
self._mcsLayer._clientSettings.CS_CORE.kbdLayout.value = gcc.KeyboardLayout.FRENCH
|
||||
elif layout == "us":
|
||||
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).kbdLayout.value = gcc.KeyboardLayout.US
|
||||
self._mcsLayer._clientSettings.CS_CORE.kbdLayout.value = gcc.KeyboardLayout.US
|
||||
|
||||
def setHostname(self, hostname):
|
||||
"""
|
||||
@summary: set hostname of machine
|
||||
"""
|
||||
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).clientName.value = hostname[:15] + "\x00" * (15 - len(hostname))
|
||||
self._mcsLayer._clientSettings.CS_CORE.clientName.value = hostname[:15] + "\x00" * (15 - len(hostname))
|
||||
self._secLayer._licenceManager._hostname = hostname
|
||||
|
||||
def setRDPBasicSecurity(self):
|
||||
def setSecurityLevel(self, level):
|
||||
"""
|
||||
@summary: Request basic security
|
||||
@param level: {str} (ssl | rdp)
|
||||
"""
|
||||
if level == "rdp":
|
||||
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_RDP
|
||||
elif level == "ssl":
|
||||
self._x224Layer._requestedProtocol = x224.Protocols.PROTOCOL_SSL
|
||||
|
||||
def addClientObserver(self, observer):
|
||||
"""
|
||||
@@ -362,6 +366,12 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
"""
|
||||
return self._tpktLayer
|
||||
|
||||
def getHostname(self):
|
||||
"""
|
||||
@return: name of client (information done by RDP)
|
||||
"""
|
||||
return self._mcsLayer._clientSettings.CS_CORE.clientName.value.strip('\x00')
|
||||
|
||||
def getUsername(self):
|
||||
"""
|
||||
@summary: Must be call after on ready event else always empty string
|
||||
@@ -414,7 +424,8 @@ class RDPServerController(pdu.layer.PDUServerListener):
|
||||
"""
|
||||
@summary: Set color depth of session
|
||||
if PDU stack is already connected send a deactive-reactive sequence
|
||||
@param colorDepth: depth of session (15, 16, 24)
|
||||
and an onReady message is re send when client is ready
|
||||
@param colorDepth: {integer} depth of session (15, 16, 24)
|
||||
"""
|
||||
self._colorDepth = colorDepth
|
||||
self._pduLayer._serverCapabilities[pdu.caps.CapsType.CAPSTYPE_BITMAP].capability.preferredBitsPerPixel.value = colorDepth
|
||||
@@ -526,15 +537,15 @@ class ServerFactory(layer.RawLayerServerFactory):
|
||||
"""
|
||||
@summary: Factory of Server RDP protocol
|
||||
"""
|
||||
def __init__(self, privateKeyFileName, certificateFileName, colorDepth):
|
||||
def __init__(self, colorDepth, privateKeyFileName = None, certificateFileName = None):
|
||||
"""
|
||||
@param privateKeyFileName: file contain server private key
|
||||
@param certficiateFileName: file that contain public key
|
||||
@param colorDepth: color depth of session
|
||||
@param privateKeyFileName: file contain server private key (if none -> back to standard RDP security)
|
||||
@param certficiateFileName: file that contain public key (if none -> back to standard RDP security)
|
||||
"""
|
||||
self._colorDepth = colorDepth
|
||||
self._privateKeyFileName = privateKeyFileName
|
||||
self._certificateFileName = certificateFileName
|
||||
self._colorDepth = colorDepth
|
||||
|
||||
def connectionLost(self, tpktLayer, reason):
|
||||
"""
|
||||
|
||||
@@ -208,7 +208,7 @@ class Client(X224Layer):
|
||||
raise InvalidExpectedDataException("RDPY doesn't support NLA security Layer")
|
||||
|
||||
if self._selectedProtocol == Protocols.PROTOCOL_SSL:
|
||||
log.debug("*" * 10 + " select SSL layer" + "*" * 10)
|
||||
log.debug("*" * 10 + " select SSL layer " + "*" * 10)
|
||||
#_transport is TPKT and transport is TCP layer of twisted
|
||||
self._transport.transport.startTLS(ClientTLSContext())
|
||||
|
||||
@@ -287,7 +287,7 @@ class Server(X224Layer):
|
||||
message.protocolNeg.selectedProtocol.value = self._selectedProtocol
|
||||
self._transport.send(message)
|
||||
if self._selectedProtocol == Protocols.PROTOCOL_SSL:
|
||||
log.debug("*" * 5 + " select SSL layer")
|
||||
log.debug("*" * 10 + " select SSL layer " + "*" * 10)
|
||||
#_transport is TPKT and transport is TCP layer of twisted
|
||||
self._transport.transport.startTLS(ServerTLSContext(self._serverPrivateKeyFileName, self._serverCertificateFileName))
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ def int2bytes(i, fill_size=None):
|
||||
def random(size):
|
||||
"""
|
||||
@summary: wrapper around rsa.randnum.read_random_bits function
|
||||
@param size: {integer] size in bits
|
||||
@return: {str} random bytes array
|
||||
"""
|
||||
return rsa.randnum.read_random_bits(size)
|
||||
|
||||
@@ -29,6 +29,48 @@ import unittest
|
||||
from rdpy.protocol.rdp import lic, sec
|
||||
import rdpy.core.type as type
|
||||
|
||||
#dump of server request
|
||||
SERVERREQUEST = """
|
||||
AQNfCBkr6c1CVLRPx7PPYVgzW5uMQ1pSvtzs9XlTt74jwjslAAAGACwAAABNAGkAYwByAG8AcwBv
|
||||
AGYAdAAgAEMAbwByAHAAbwByAGEAdABpAG8AbgAAAAgAAABBADAAMgAAAA0ABAABAAAAAwDZBwIA
|
||||
AAACAAAAWAMAADCCA1QwggJAoAMCAQICCAGemF/kFo3QMAkGBSsOAwIdBQAwODE2MBUGA1UEBx4O
|
||||
AFMASQBSAEEARABFAEwwHQYDVQQDHhYAVgBTAEkALQBTAFIAVgAtADAAMAA0MB4XDTcwMTAyMTA4
|
||||
MDMxN1oXDTQ5MTAyMTA4MDMxN1owODE2MBUGA1UEBx4OAFMASQBSAEEARABFAEwwHQYDVQQDHhYA
|
||||
VgBTAEkALQBTAFIAVgAtADAAMAA0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0mh+
|
||||
rgsNk7WCR8Ck46jTV6OgnMgsFmdt0RqBJqtFwk+uqH8cBKFohtD9CeHTeQYkk+8m1C5GDpjQ/5BK
|
||||
jaBfZHW+g5lZQITKbTi2n8TZfDg76wHvfl71JzwB84AG736KOZSOkBg8mJk3xf8LX5hbYHPQCRc5
|
||||
y+06CWOyPiBYCEElXPOqsZYwum72N/NIsrKe98QHLn+X6MSbZ9Qzw6jnoBFgjk65bzpcChaOhnlW
|
||||
875D6+8KZXgFmHT6MSJX7eabKw1zbKXszYpPvPfMDybp+6z125AJ+GxJ847AQ10xOV2X13ZVfSvS
|
||||
Np62ogDD23duWHvcwLmHJ+45Jdgt7y4C0wIDAQABo2owaDAPBgNVHRMECDAGAQH/AgEAMFUGCSsG
|
||||
AQQBgjcSCARIQwBDAFIATQBWAEoANgBWAEMAVABUAFcAUgBXAEcATQA4AEgASwBIAFkAMwBKAEsA
|
||||
OABXADMAVgBNAEIARABXAFQAUQBGAAAAMAkGBSsOAwIdBQADggEBAD2NQPpBREuD+CZI9T4bUF9s
|
||||
2yyQ8HidxJ+7NMcDyW4Ozid/SrAT9JP42RQrBosgq8S8teWNubrsNMsJjGaeueeQisLP1OigXQH1
|
||||
8DcnLUc5TZKIsKjBqxJ40tssR5KaOfjGHXX5VoADsthnp3wO4bRuaXyhpLQzN1bUNKQk0Fok6Fzt
|
||||
zo/rNEiAQv+M/Y1vmCoTuulXwxymZ+UdbxHPr/IvmxD0Gcmz8Qm45Lpypt2t0lGwNiuV2/3dm13C
|
||||
spjIGxJFf/26QiRuZi5cps2YsOaQfW3xByklE5EbZWYIlsTCQpht+owKqnoPy7aD2On8z0IyGgY3
|
||||
YdsOzHDvRZ+S0hxhBAAAMIIEXTCCA0mgAwIBAgIFAQAAABgwCQYFKw4DAh0FADA4MTYwFQYDVQQH
|
||||
Hg4AUwBJAFIAQQBEAEUATDAdBgNVBAMeFgBWAFMASQAtAFMAUgBWAC0AMAAwADQwHhcNMTMxMTI3
|
||||
MTQyNDIyWhcNMzgwMTE5MDMxNDA3WjCBqDGBpTAnBgNVBAMeIABuAGMAYQBjAG4AXwBuAHAAOgAx
|
||||
ADkAMgAuADEANgA4MDUGA1UEBx4uAG4AYwBhAGMAbgBfAG4AcAA6ADEAOQAyAC4AMQA2ADgALgAx
|
||||
ADMANQAuADMAMDBDBgNVBAUePAAxAEIAYwBLAGUAYQBiADcAWABPAGoAbgBXAEUATQBuAHgAMQBw
|
||||
AFMAaQA2AEoAUgBBAHUAMAA9AA0ACjBYMAkGBSsOAwIPBQADSwAwSAJBANGDkkicUIVwIX2apm1b
|
||||
U9WXVMnVT+S081PVP87vGp6VtzYpT9cMNKv70Qi2U3MQoQ4MuKS1XN2uc9SrNC7RWoUCAwEAAaOC
|
||||
Ac8wggHLMBQGCSsGAQQBgjcSBAEB/wQEAQAFADA8BgkrBgEEAYI3EgIBAf8ELE0AaQBjAHIAbwBz
|
||||
AG8AZgB0ACAAQwBvAHIAcABvAHIAYQB0AGkAbwBuAAAAMIHNBgkrBgEEAYI3EgUBAf8EgbwAMAAA
|
||||
AQAAAAIAAAAJBAAAHABKAGYASgCwAAEAMwBkADIANgA3ADkANQA0AC0AZQBlAGIANwAtADEAMQBk
|
||||
ADEALQBiADkANABlAC0AMAAwAGMAMAA0AGYAYQAzADAAOAAwAGQAAAAzAGQAMgA2ADcAOQA1ADQA
|
||||
LQBlAGUAYgA3AC0AMQAxAGQAMQAtAGIAOQA0AGUALQAwADAAYwAwADQAZgBhADMAMAA4ADAAZAAA
|
||||
AAAAABAAgOQAAAAAADB0BgkrBgEEAYI3EgYBAf8EZAAwAAAAABgASABWAFMASQAtAFMAUgBWAC0A
|
||||
MAAwADQAAAA1ADUAMAA0ADEALQAwADAAOAAtADIAOQAxADMAMwA5ADEALQA4ADQANgAwADkAAABT
|
||||
AEkAUgBBAEQARQBMAAAAAAAwLwYDVR0jAQH/BCUwI6EapBhWAFMASQAtAFMAUgBWAC0AMAAwADQA
|
||||
AACCBQEAAAAYMAkGBSsOAwIdBQADggEBAGFI8teU2iypPG2BbpNLa+zZeb87MNjtzHgQlC0RfoK0
|
||||
dY91t3OXA5pdyD5IRRkvGUVKBf/5G/OVZZvKTnyh3daopD6InPy1X6Wlx5QveCL8ydd/H+ezm0zl
|
||||
KlMg0pImG0V2NX50q4b9R4I5xiaA7mUeww6rz6iAh1iB2CYHOc/uGAS1/PPtHqnfss5bY+wZR+tW
|
||||
dvyMFvn6DbvzAN9wT2KIcv1LtMGgxv28v+dnqGhcxrLRE4nRjMIV/AKpBJXhgH7DaUEc1NJNDPUt
|
||||
5Pdz4qy8WM2d8JO0yNXQVqykJahgt5TJxCoP46SPFUU5XBbNTZuKv7CtPkSxgtrdrzwFTtcAAAAA
|
||||
AAAAAAAAAAAAAAAAAQAAAA4ADgBtaWNyb3NvZnQuY29tAA==
|
||||
"""
|
||||
|
||||
class TestLic(unittest.TestCase):
|
||||
"""
|
||||
@summary: Test case for MCS automata
|
||||
@@ -71,9 +113,6 @@ class TestLic(unittest.TestCase):
|
||||
t = Transport()
|
||||
l = lic.LicenseManager(t)
|
||||
|
||||
s = type.Stream()
|
||||
s.writeType(lic.LicPacket(lic.ServerLicenseRequest()))
|
||||
#reinit position
|
||||
s.pos = 0
|
||||
s = type.Stream(SERVERREQUEST.decode("base64"))
|
||||
|
||||
self.assertFalse(l.recv(s) and t._state, "Bad message after license request")
|
||||
@@ -26,7 +26,7 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
import rdpy.core.rc4 as rc4
|
||||
import rdpy.security.rc4 as rc4
|
||||
|
||||
|
||||
class RC4Test(unittest.TestCase):
|
||||
|
||||
Reference in New Issue
Block a user