bug fix on proxy + add comments

This commit is contained in:
speyrefitte
2014-08-01 17:47:49 +02:00
parent 9af647ab09
commit 17b57ddd4f
11 changed files with 677 additions and 517 deletions

View File

@@ -33,11 +33,15 @@ from rdpy.protocol.rdp import rdp
class RDPClientQtFactory(rdp.ClientFactory):
"""
Factory create a RDP GUI client
@summary: Factory create a RDP GUI client
"""
def __init__(self, width, height, username, password, domain):
"""
init client with correct definition
@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
"""
self._width = width
self._height = height
@@ -46,10 +50,13 @@ class RDPClientQtFactory(rdp.ClientFactory):
self._domain = domain
self._w = None
def buildObserver(self, controller):
def buildObserver(self, controller, addr):
"""
Build RFB observer
@summary: Build RFB observer
We use a RDPClientQt as RDP observer
@param controller: build factory and needed by observer
@param addr: destination address
@return: RDPClientQt
"""
#create client observer
client = RDPClientQt(controller, self._width, self._height)
@@ -70,7 +77,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
def clientConnectionLost(self, connector, reason):
"""
Connection lost event
@summary: Connection lost event
@param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection
"""
@@ -80,7 +87,7 @@ class RDPClientQtFactory(rdp.ClientFactory):
def clientConnectionFailed(self, connector, reason):
"""
Connection failed event
@summary: Connection failed event
@param connector: twisted connector use for rdp connection (use reconnect to restart connection)
@param reason: str use to advertise reason of lost connection
"""

View File

@@ -19,8 +19,14 @@
#
"""
RDP proxy recorder and spy function
Proxy RDP protocol
RDP proxy with spy capabilities
---------------------------
Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
---------------------------
| ProxyAdmin |
------------
^
Admin ----------------------|
"""
import sys, os, getopt, json
@@ -35,7 +41,7 @@ from PyQt4 import QtCore, QtGui
class ProxyServer(rdp.RDPServerObserver):
"""
Server side of proxy
@summary: Server side of proxy
"""
def __init__(self, controller, credentialProvider):
"""
@@ -48,42 +54,65 @@ class ProxyServer(rdp.RDPServerObserver):
self._window = None
def showSelectView(self, machines):
self._machines = dict([("%s:%s"%(ip, port), (ip, port)) for ip, port in 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(24, 93, 123))
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(24, 93, 123))))
self._window.addView(view.Anchor(width / 2 - 250, 150, view.List(self._machines.keys(), 500, 500, self.onSelectMachine, QtGui.QColor(24, 93, 123))), True)
self._window.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True)
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, machine):
ip, port = self._machines[machine]
def onSelectMachine(self, index):
"""
@summary: Callback of view.List in Select server view
@param: machine str name of machine selected
@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, "%s\\%s on %s:%s"%(domain, username, ip, port)))
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height, domain, username, password))
def clientConnected(self, client):
"""
Event throw by client when it's ready
@summary: Event throw by client when it's ready
@param client: ProxyClient
"""
self._client = client
#need to reevaluate color depth
self._controller.setColorDepth(self._client._controller.getColorDepth())
def showErrorMessage(self, message):
def showMessage(self, message):
"""
Print a message to the client
@summary: Print a message to the client
@param message: string
"""
width, height = self._controller.getScreen()
popup = view.Window(width, height, QtGui.QColor(24, 93, 123))
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(24, 93, 123))))
popup.update(view.RDPRenderer(self._controller, self._controller.getColorDepth()), True)
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)
def onReady(self):
"""
Event use to inform state of server stack
Use to connect client
On ready is not launch only after connection sequence but after a reactivation sequence too
@summary: Event use to inform state of server stack
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
@@ -91,11 +120,13 @@ class ProxyServer(rdp.RDPServerObserver):
machines = self._credentialProvider.getProxyPass(domain, username)
if len(machines) == 0:
self.showErrorMessage("No servers attach to account %s\\%s"%(domain, username))
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, domain, username, password, "%s\\%s on %s:%s"%(domain, username, ip, port)))
reactor.connectTCP(ip, port, ProxyClientFactory(self, width, height,
domain, username, password,
"%s\\%s on %s:%s"%(domain, username, ip, port)))
else:
self.showSelectView(machines)
else:
@@ -105,31 +136,35 @@ class ProxyServer(rdp.RDPServerObserver):
def onClose(self):
"""
Call when client close connection
@summary: Call when human client close connection
@see: rdp.RDPServerObserver.onClose
"""
if self._client is None:
return
#close proxy client
self._client._controller.close()
def onKeyEventScancode(self, code, isPressed):
"""
Event call when a keyboard event is catch in scan code format
@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
"""
#no client connected
if not self._client is None:
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, self._controller.getColorDepth()))
self._window.update(view.RDPRenderer(self._controller))
def onKeyEventUnicode(self, code, isPressed):
"""
Event call when a keyboard event is catch in unicode format
@summary: Event call when a keyboard event is catch in unicode format
@param code: unicode of key
@param isPressed: True if key is down
@see: rdp.RDPServerObserver.onKeyEventUnicode
"""
#no client connected domain
if self._client is None:
@@ -138,11 +173,12 @@ class ProxyServer(rdp.RDPServerObserver):
def onPointerEvent(self, x, y, button, isPressed):
"""
Event call on mouse event
@summary: Event call on mouse 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
"""
#no client connected
if self._client is None:
@@ -151,33 +187,36 @@ class ProxyServer(rdp.RDPServerObserver):
class ProxyServerFactory(rdp.ServerFactory):
"""
Factory on listening events
@summary: Factory on listening events
"""
def __init__(self, credentialProvider, privateKeyFilePath, certificateFilePath):
"""
@param config: rdp-proxy configuration
@param credentialProvider: CredentialProvider
@param privateKeyFilePath: file contain server private key
@param certificateFilePath: file contain server certificate
"""
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
self._credentialProvider = credentialProvider
def buildObserver(self, controller):
def buildObserver(self, controller, addr):
"""
Implement rdp.ServerFactory
@param controller: rdp.RDPServerController
@param addr: destination address
@see: rdp.ServerFactory.buildObserver
"""
return ProxyServer(controller, self._credentialProvider)
class ProxyClient(rdp.RDPClientObserver):
"""
Client side of proxy
@summary: Client side of proxy
"""
_CONNECTED_ = {}
def __init__(self, controller, server, name):
_CONNECTED_ = []
def __init__(self, controller, server, name = None):
"""
@param controller: RDPClientObserver
@param controller: rdp.RDPClientController
@param server: ProxyServer
@param name: name of session
@param name: name of session None if you don't
want to spy this session
"""
rdp.RDPClientObserver.__init__(self, controller)
self._server = server
@@ -185,25 +224,24 @@ class ProxyClient(rdp.RDPClientObserver):
def onReady(self):
"""
Event use to signal that RDP stack is ready
Inform proxy server that i'm connected
implement RDPClientObserver
@summary: Event use to signal that RDP stack is ready
Inform ProxyServer that i'm connected
@see: rdp.RDPClientObserver.onReady
"""
ProxyClient._CONNECTED_[self._name] = self
if not self._name is None:
ProxyClient._CONNECTED_.append(self)
self._server.clientConnected(self)
def onClose(self):
"""
Stack is closes
@summary: Event inform that stack is close
@see: rdp.RDPClientObserver.onClose
"""
if ProxyClient._CONNECTED_.has_key(self._name):
del ProxyClient._CONNECTED_[self._name]
self._server._controller.close()
ProxyClient._CONNECTED_.remove(self)
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
"""
Event use to inform bitmap update
implement RDPClientObserver
@summary: Event use to inform bitmap update
@param destLeft: xmin position
@param destTop: ymin position
@param destRight: xmax position because RDP can send bitmap with padding
@@ -213,14 +251,15 @@ class ProxyClient(rdp.RDPClientObserver):
@param bitsPerPixel: number of bit per pixel
@param isCompress: use RLE compression
@param data: bitmap data
@see: rdp.RDPClientObserver.onUpdate
"""
self._server._controller.sendUpdate(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data)
class ProxyClientFactory(rdp.ClientFactory):
"""
Factory for proxy client
@summary: Factory for proxy client
"""
def __init__(self, server, width, height, domain, username, password, name):
def __init__(self, server, width, height, domain, username, password):
"""
@param server: ProxyServer
@param width: screen width
@@ -228,7 +267,6 @@ class ProxyClientFactory(rdp.ClientFactory):
@param domain: domain session
@param username: username session
@param password: password session
@param name: name of session
"""
self._controller = server
self._width = width
@@ -236,13 +274,14 @@ class ProxyClientFactory(rdp.ClientFactory):
self._domain = domain
self._username = username
self._password = password
self._name = name
def buildObserver(self, controller):
def buildObserver(self, controller, addr):
"""
Implement rdp.ClientFactory
Build observer (ProxyClient)
@summary: Build observer
@param controller: rdp.RDPClientController
@param addr: destination address
@see: rdp.ClientFactory.buildObserver
@return: ProxyClient
"""
#set screen resolution
controller.setScreen(self._width, self._height)
@@ -250,7 +289,7 @@ class ProxyClientFactory(rdp.ClientFactory):
controller.setDomain(self._domain)
controller.setUsername(self._username)
controller.setPassword(self._password)
proxy = ProxyClient(controller, self._controller, self._name)
proxy = ProxyClient(controller, self._controller, "%s\\%s on %s"%(self._domain, self._username, addr))
return proxy
def startedConnecting(self, connector):
@@ -265,18 +304,14 @@ class ProxyClientFactory(rdp.ClientFactory):
class ProxyAdmin(rdp.RDPServerObserver):
"""
Use to manage client side of admin session
Add GUI to select which session to see
And manage see session
Just escape key is authorized during spy session
@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 state -> list of active session
SPY state -> watch active session
"""
GUI = 0
SPY = 1
GUI = 0 #->list of active session
SPY = 1 #->watch active session
def __init__(self, controller):
"""
@@ -289,21 +324,32 @@ class ProxyAdmin(rdp.RDPServerObserver):
def initView(self):
"""
Init GUI view
@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(24, 93, 123))
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(24, 93, 123))))
self._window.addView(view.Anchor(width / 2 - 250, 150, view.List(ProxyClient._CONNECTED_.keys(), 500, 500, self.onSelect, QtGui.QColor(24, 93, 123))), True)
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
def onReady(self):
"""
Stack is ready and connected
May be called after an setColorDepth too
@summary: Stack is ready and connected
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, self._controller.getColorDepth()), True)
self._window.update(view.RDPRenderer(self._controller), True)
elif self._state == ProxyAdmin.State.SPY:
#refresh client
width, height = self._controller.getScreen()
@@ -311,21 +357,24 @@ class ProxyAdmin(rdp.RDPServerObserver):
def onClose(self):
"""
Stack is closes
@summary: Stack is close
@see: rdp.RDPServerObserver.onClose
"""
pass
def onKeyEventScancode(self, code, isPressed):
"""
Event call when a keyboard event is catch in scan code format
@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, self._controller.getColorDepth()))
self._window.update(view.RDPRenderer(self._controller))
elif code == 1:
#escape button refresh GUI
self._state = ProxyAdmin.State.GUI
@@ -334,38 +383,39 @@ class ProxyAdmin(rdp.RDPServerObserver):
def onKeyEventUnicode(self, code, isPressed):
"""
Event call when a keyboard event is catch in unicode format
In admin mode is forbidden
@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):
"""
Event call on mouse event
In admin mode is forbidden
@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, name):
def onSelect(self, index):
"""
Call back of list view
@param name: name selected by user
@summary: Callback of list view of active session
Connect to select session
@param index: index in sessions array
"""
if not ProxyClient._CONNECTED_.has_key(name):
return
self._state = ProxyAdmin.State.SPY
self._spy = ProxyClient(ProxyClient._CONNECTED_[name]._controller, self, "Admin")
self._spy = ProxyClient(self._sessions[index]._controller, self)
self._controller.setColorDepth(self._spy._controller.getColorDepth())
class ProxyAdminFactory(rdp.ServerFactory):
"""
Factory for admin
@summary: Factory for admin session
"""
def __init__(self, privateKeyFilePath, certificateFilePath):
"""
@@ -374,16 +424,19 @@ class ProxyAdminFactory(rdp.ServerFactory):
"""
rdp.ServerFactory.__init__(self, privateKeyFilePath, certificateFilePath, 16)
def buildObserver(self, controller):
def buildObserver(self, controller, addr):
"""
Implement rdp.ServerFactory
@summary: Build ProxyAdmin
@param controller: rdp.RDPServerController
@param addr: destination address
@return: ProxyAdmin
@see: rdp.ServerFactory.buildObserver
"""
return ProxyAdmin(controller)
class CredentialProvider(object):
"""
Credential provider for proxy
@summary: Credential provider for proxy
"""
def __init__(self, config):
"""
@@ -392,12 +445,19 @@ class CredentialProvider(object):
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)]
@@ -409,13 +469,13 @@ class CredentialProvider(object):
def help():
"""
Print help in console
@summary: Print help in console
"""
print "Usage: rdpy-rdpproxy -f credential_file_path -k private_key_file_path -c certificate_file_path listen_port"
def loadConfig(configFilePath):
"""
Load and check config file
@summary: Load and check config file
@param configFilePath: config file path
"""
if not os.path.isfile(configFilePath):