Compare commits
6 Commits
master
...
1c4b42544c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c4b42544c | ||
|
|
9550734743 | ||
|
|
73b97d6929 | ||
|
|
018c59fe42 | ||
|
|
9cac72a8d2 | ||
|
|
3f56b25f46 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,3 +7,5 @@ README.md~
|
||||
dist/*
|
||||
build/*
|
||||
rdpy.egg-info/*
|
||||
*.pyd
|
||||
.idea
|
||||
|
||||
@@ -145,7 +145,7 @@ In a nutshell RDPY can be used as a protocol library with a twisted engine.
|
||||
### Simple RDP Client
|
||||
|
||||
```python
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from rdpy.core import rdp
|
||||
|
||||
class MyRDPFactory(rdp.ClientFactory):
|
||||
|
||||
@@ -201,7 +201,7 @@ reactor.run()
|
||||
|
||||
### Simple RDP Server
|
||||
```python
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from rdpy.core import rdp
|
||||
|
||||
class MyRDPFactory(rdp.ServerFactory):
|
||||
|
||||
|
||||
@@ -21,268 +21,27 @@
|
||||
example of use rdpy as rdp client
|
||||
"""
|
||||
|
||||
import sys, os, getopt, socket
|
||||
import sys
|
||||
import asyncio
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from rdpy.ui.qt4 import RDPClientQt
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from rdpy.core.error import RDPSecurityNegoFail
|
||||
from rdpy.core import rss
|
||||
from rdpy.core import tpkt, x224
|
||||
from rdpy.core.nla import ntlm
|
||||
from rdpy.core.t125 import mcs
|
||||
from rdpy.model.message import UInt8
|
||||
|
||||
import rdpy.core.log as log
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
|
||||
class RDPClientQtRecorder(RDPClientQt):
|
||||
"""
|
||||
@summary: Widget with record session
|
||||
"""
|
||||
def __init__(self, controller, width, height, rssRecorder):
|
||||
"""
|
||||
@param controller: {RDPClientController} RDP controller
|
||||
@param width: {int} width of widget
|
||||
@param height: {int} height of widget
|
||||
@param rssRecorder: {rss.FileRecorder}
|
||||
"""
|
||||
RDPClientQt.__init__(self, controller, width, height)
|
||||
self._screensize = width, height
|
||||
self._rssRecorder = rssRecorder
|
||||
|
||||
def onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
@summary: Notify bitmap update
|
||||
@param destLeft: {int} xmin position
|
||||
@param destTop: {int} ymin position
|
||||
@param destRight: {int} xmax position because RDP can send bitmap with padding
|
||||
@param destBottom: {int} ymax position because RDP can send bitmap with padding
|
||||
@param width: {int} width of bitmap
|
||||
@param height: {int} height of bitmap
|
||||
@param bitsPerPixel: {int} number of bit per pixel
|
||||
@param isCompress: {bool} use RLE compression
|
||||
@param data: {str} bitmap data
|
||||
"""
|
||||
#record update
|
||||
self._rssRecorder.update(destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, rss.UpdateFormat.BMP if isCompress else rss.UpdateFormat.RAW, data)
|
||||
RDPClientQt.onUpdate(self, destLeft, destTop, destRight, destBottom, width, height, bitsPerPixel, isCompress, data)
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Call when stack is ready
|
||||
"""
|
||||
self._rssRecorder.screen(self._screensize[0], self._screensize[1], self._controller.getColorDepth())
|
||||
RDPClientQt.onReady(self)
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
self._rssRecorder.close()
|
||||
RDPClientQt.onClose(self)
|
||||
|
||||
def closeEvent(self, e):
|
||||
"""
|
||||
@summary: Convert Qt close widget event into close stack event
|
||||
@param e: QCloseEvent
|
||||
"""
|
||||
self._rssRecorder.close()
|
||||
RDPClientQt.closeEvent(self, e)
|
||||
|
||||
class RDPClientQtFactory(rdp.ClientFactory):
|
||||
"""
|
||||
@summary: Factory create a RDP GUI client
|
||||
"""
|
||||
def __init__(self, width, height, username, password, domain, fullscreen, keyboardLayout, optimized, security, recodedPath):
|
||||
"""
|
||||
@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)
|
||||
@param recodedPath: {str | None} Rss file Path
|
||||
"""
|
||||
self._width = width
|
||||
self._height = height
|
||||
self._username = username
|
||||
self._passwod = password
|
||||
self._domain = domain
|
||||
self._fullscreen = fullscreen
|
||||
self._keyboardLayout = keyboardLayout
|
||||
self._optimized = optimized
|
||||
self._nego = security == "nego"
|
||||
self._recodedPath = recodedPath
|
||||
if self._nego:
|
||||
#compute start nego nla need credentials
|
||||
if username != "" and password != "":
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_NLA
|
||||
else:
|
||||
self._security = rdp.SecurityLevel.RDP_LEVEL_SSL
|
||||
else:
|
||||
self._security = security
|
||||
self._w = None
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@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
|
||||
if self._recodedPath is None:
|
||||
self._client = RDPClientQt(controller, self._width, self._height)
|
||||
else:
|
||||
self._client = RDPClientQtRecorder(controller, self._width, self._height, rss.createRecorder(self._recodedPath))
|
||||
#create qt widget
|
||||
self._w = self._client.getWidget()
|
||||
self._w.setWindowTitle('rdpy-rdpclient')
|
||||
if self._fullscreen:
|
||||
self._w.showFullScreen()
|
||||
else:
|
||||
self._w.show()
|
||||
|
||||
controller.setUsername(self._username)
|
||||
controller.setPassword(self._passwod)
|
||||
controller.setDomain(self._domain)
|
||||
controller.setKeyboardLayout(self._keyboardLayout)
|
||||
controller.setHostname(socket.gethostname())
|
||||
if self._optimized:
|
||||
controller.setPerformanceSession()
|
||||
controller.setSecurityLevel(self._security)
|
||||
|
||||
return self._client
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
"""
|
||||
@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
|
||||
"""
|
||||
#try reconnect with basic RDP security
|
||||
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.SecurityLevel.RDP_LEVEL_RDP
|
||||
self._client._widget.hide()
|
||||
connector.connect()
|
||||
return
|
||||
|
||||
log.info("Lost connection : %s"%reason)
|
||||
reactor.stop()
|
||||
app.exit()
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
"""
|
||||
@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
|
||||
"""
|
||||
log.info("Connection failed : %s"%reason)
|
||||
reactor.stop()
|
||||
app.exit()
|
||||
|
||||
def autoDetectKeyboardLayout():
|
||||
"""
|
||||
@summary: try to auto detect keyboard layout
|
||||
"""
|
||||
try:
|
||||
if os.name == 'posix':
|
||||
from subprocess import check_output
|
||||
result = check_output(["setxkbmap", "-print"])
|
||||
if 'azerty' in result:
|
||||
return "fr"
|
||||
elif os.name == 'nt':
|
||||
import win32api, win32con, win32process
|
||||
from ctypes import windll
|
||||
w = windll.user32.GetForegroundWindow()
|
||||
tid = windll.user32.GetWindowThreadProcessId(w, 0)
|
||||
result = windll.user32.GetKeyboardLayout(tid)
|
||||
log.info(result)
|
||||
if result == 0x40c040c:
|
||||
return "fr"
|
||||
except Exception as e:
|
||||
log.info("failed to auto detect keyboard layout " + str(e))
|
||||
pass
|
||||
return "en"
|
||||
|
||||
def help():
|
||||
print """
|
||||
Usage: rdpy-rdpclient [options] ip[:port]"
|
||||
\t-u: user name
|
||||
\t-p: password
|
||||
\t-d: domain
|
||||
\t-w: width of screen [default : 1024]
|
||||
\t-l: height of screen [default : 800]
|
||||
\t-f: enable full screen mode [default : False]
|
||||
\t-k: keyboard layout [en|fr] [default : en]
|
||||
\t-o: optimized session (disable costly effect) [default : False]
|
||||
\t-r: rss_filepath Recorded Session Scenario [default : None]
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
#default script argument
|
||||
username = ""
|
||||
password = ""
|
||||
domain = ""
|
||||
width = 1024
|
||||
height = 800
|
||||
fullscreen = False
|
||||
optimized = False
|
||||
recodedPath = None
|
||||
keyboardLayout = autoDetectKeyboardLayout()
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], "hfou:p:d:w:l:k:r:")
|
||||
except getopt.GetoptError:
|
||||
help()
|
||||
for opt, arg in opts:
|
||||
if opt == "-h":
|
||||
help()
|
||||
sys.exit()
|
||||
elif opt == "-u":
|
||||
username = arg
|
||||
elif opt == "-p":
|
||||
password = arg
|
||||
elif opt == "-d":
|
||||
domain = arg
|
||||
elif opt == "-w":
|
||||
width = int(arg)
|
||||
elif opt == "-l":
|
||||
height = int(arg)
|
||||
elif opt == "-f":
|
||||
fullscreen = True
|
||||
elif opt == "-o":
|
||||
optimized = True
|
||||
elif opt == "-k":
|
||||
keyboardLayout = arg
|
||||
elif opt == "-r":
|
||||
recodedPath = arg
|
||||
|
||||
if ':' in args[0]:
|
||||
ip, port = args[0].split(':')
|
||||
else:
|
||||
ip, port = args[0], "3389"
|
||||
|
||||
#create application
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
|
||||
#add qt4 reactor
|
||||
import qt4reactor
|
||||
qt4reactor.install()
|
||||
|
||||
if fullscreen:
|
||||
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, "nego", recodedPath))
|
||||
reactor.runReturn()
|
||||
app.exec_()
|
||||
|
||||
#sys.exit(app.exec_())
|
||||
|
||||
async def tcp_echo_client(message):
|
||||
reader, writer = await asyncio.open_connection(
|
||||
'127.0.0.1', 33389)
|
||||
|
||||
x224_layer = await x224.connect(tpkt.Tpkt(reader, writer), ntlm.NTLMv2("", "sylvain", "sylvain"))
|
||||
mcs_layer = mcs.Client(x224_layer)
|
||||
await mcs_layer.connect()
|
||||
|
||||
await asyncio.sleep(10)
|
||||
print("foooooooooooooooooooo")
|
||||
|
||||
asyncio.run(tcp_echo_client('Hello World!'))
|
||||
@@ -22,10 +22,10 @@
|
||||
RDP Honey pot use Rss scenario file to simulate RDP server
|
||||
"""
|
||||
|
||||
import sys, os, getopt, time, datetime
|
||||
import sys, getopt, datetime
|
||||
|
||||
from rdpy.core import log, error, rss
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from rdpy.core import log, rss
|
||||
from rdpy.core import rdp
|
||||
from twisted.internet import reactor
|
||||
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
@@ -29,13 +29,12 @@ Client RDP -> | ProxyServer | ProxyClient | -> Server RDP
|
||||
-----------------
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import time
|
||||
|
||||
from rdpy.core import log, error, rss
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from rdpy.core import log, rss
|
||||
from rdpy.core import rdp
|
||||
from twisted.internet import reactor
|
||||
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
@@ -24,15 +24,13 @@ take screenshot of login page
|
||||
"""
|
||||
|
||||
import getopt
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from rdpy.protocol.rdp import rdp
|
||||
from PyQt4 import QtGui
|
||||
from rdpy.core 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
|
||||
log._LOG_LEVEL = log.Level.INFO
|
||||
|
||||
13
ext/rle.c
13
ext/rle.c
@@ -938,10 +938,19 @@ static PyMethodDef rle_methods[] =
|
||||
{"bitmap_decompress", bitmap_decompress_wrapper, METH_VARARGS, "decompress bitmap from microsoft rle algorithm."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
static struct PyModuleDef rle =
|
||||
{
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"rle", /* name of module */
|
||||
"", /* module documentation, may be NULL */
|
||||
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
|
||||
rle_methods
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
initrle(void)
|
||||
PyInit_rle(void)
|
||||
{
|
||||
(void) Py_InitModule("rle", rle_methods);
|
||||
(void) PyModule_Create(&rle);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc241880.aspx
|
||||
"""
|
||||
|
||||
from rdpy.core.type import CompositeType, CallableValue, UInt8, UInt16Le, UInt32Le, String, sizeof, FactoryType, ArrayType, Stream
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
import rdpy.core.log as log
|
||||
import sec
|
||||
from t125 import gcc
|
||||
from rdpy.model.type import CompositeType, CallableValue, UInt8, UInt16Le, UInt32Le, Buffer, sizeof, FactoryType, ArrayType, Stream
|
||||
from rdpy.model.error import InvalidExpectedDataException
|
||||
import rdpy.model.log as log
|
||||
from rdpy.core import sec
|
||||
from rdpy.core.t125 import gcc
|
||||
from rdpy.security import rc4
|
||||
from rdpy.security import rsa_wrapper as rsa
|
||||
|
||||
@@ -102,7 +102,7 @@ class LicenseBinaryBlob(CompositeType):
|
||||
CompositeType.__init__(self, optional = optional)
|
||||
self.wBlobType = UInt16Le(blobType, constant = True if blobType != BinaryBlobType.BB_ANY_BLOB else False)
|
||||
self.wBlobLen = UInt16Le(lambda:sizeof(self.blobData))
|
||||
self.blobData = String(readLen = self.wBlobLen)
|
||||
self.blobData = Buffer(readLen = self.wBlobLen)
|
||||
|
||||
class LicensingErrorMessage(CompositeType):
|
||||
"""
|
||||
@@ -127,10 +127,10 @@ class ProductInformation(CompositeType):
|
||||
self.dwVersion = UInt32Le()
|
||||
self.cbCompanyName = UInt32Le(lambda:sizeof(self.pbCompanyName))
|
||||
#may contain "Microsoft Corporation" from server microsoft
|
||||
self.pbCompanyName = String("Microsoft Corporation", readLen = self.cbCompanyName, unicode = True)
|
||||
self.pbCompanyName = Buffer("Microsoft Corporation", readLen = self.cbCompanyName, unicode = True)
|
||||
self.cbProductId = UInt32Le(lambda:sizeof(self.pbProductId))
|
||||
#may contain "A02" from microsoft license server
|
||||
self.pbProductId = String("A02", readLen = self.cbProductId, unicode = True)
|
||||
self.pbProductId = Buffer("A02", readLen = self.cbProductId, unicode = True)
|
||||
|
||||
|
||||
class Scope(CompositeType):
|
||||
@@ -162,7 +162,7 @@ class ServerLicenseRequest(CompositeType):
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.serverRandom = String("\x00" * 32, readLen = CallableValue(32))
|
||||
self.serverRandom = Buffer("\x00" * 32, readLen = CallableValue(32))
|
||||
self.productInfo = ProductInformation()
|
||||
self.keyExchangeList = LicenseBinaryBlob(BinaryBlobType.BB_KEY_EXCHG_ALG_BLOB)
|
||||
self.serverCertificate = LicenseBinaryBlob(BinaryBlobType.BB_CERTIFICATE_BLOB)
|
||||
@@ -183,7 +183,7 @@ class ClientNewLicenseRequest(CompositeType):
|
||||
#pure microsoft client ;-)
|
||||
#http://msdn.microsoft.com/en-us/library/1040af38-c733-4fb3-acd1-8db8cc979eda#id10
|
||||
self.platformId = UInt32Le(0x04000000 | 0x00010000)
|
||||
self.clientRandom = String("\x00" * 32, readLen = CallableValue(32))
|
||||
self.clientRandom = Buffer("\x00" * 32, readLen = CallableValue(32))
|
||||
self.encryptedPreMasterSecret = LicenseBinaryBlob(BinaryBlobType.BB_RANDOM_BLOB)
|
||||
self.ClientUserName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_USER_NAME_BLOB)
|
||||
self.ClientMachineName = LicenseBinaryBlob(BinaryBlobType.BB_CLIENT_MACHINE_NAME_BLOB)
|
||||
@@ -199,7 +199,7 @@ class ServerPlatformChallenge(CompositeType):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.connectFlags = UInt32Le()
|
||||
self.encryptedPlatformChallenge = LicenseBinaryBlob(BinaryBlobType.BB_ANY_BLOB)
|
||||
self.MACData = String(readLen = CallableValue(16))
|
||||
self.MACData = Buffer(readLen = CallableValue(16))
|
||||
|
||||
class ClientPLatformChallengeResponse(CompositeType):
|
||||
"""
|
||||
@@ -212,7 +212,7 @@ class ClientPLatformChallengeResponse(CompositeType):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.encryptedPlatformChallengeResponse = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
||||
self.encryptedHWID = LicenseBinaryBlob(BinaryBlobType.BB_DATA_BLOB)
|
||||
self.MACData = String(readLen = CallableValue(16))
|
||||
self.MACData = Buffer(readLen = CallableValue(16))
|
||||
|
||||
class LicPacket(CompositeType):
|
||||
"""
|
||||
@@ -234,7 +234,7 @@ class LicPacket(CompositeType):
|
||||
if self.bMsgtype.value == c._MESSAGE_TYPE_:
|
||||
return c(readLen = self.wMsgSize - 4)
|
||||
log.debug("unknown license message : %s"%self.bMsgtype.value)
|
||||
return String(readLen = self.wMsgSize - 4)
|
||||
return Buffer(readLen =self.wMsgSize - 4)
|
||||
|
||||
if message is None:
|
||||
message = FactoryType(LicensingMessageFactory)
|
||||
@@ -272,7 +272,7 @@ class LicenseManager(object):
|
||||
@return true when license automata is finish
|
||||
"""
|
||||
licPacket = LicPacket()
|
||||
s.readType(licPacket)
|
||||
s.read_type(licPacket)
|
||||
|
||||
#end of automata
|
||||
if licPacket.bMsgtype.value == MessageType.ERROR_ALERT and licPacket.licensingMessage.dwErrorCode.value == ErrorCode.STATUS_VALID_CLIENT and licPacket.licensingMessage.dwStateTransition.value == StateTransition.ST_NO_TRANSITION:
|
||||
@@ -308,7 +308,7 @@ class LicenseManager(object):
|
||||
else:
|
||||
s = Stream(licenseRequest.serverCertificate.blobData.value)
|
||||
serverCertificate = gcc.ServerCertificate()
|
||||
s.readType(serverCertificate)
|
||||
s.read_type(serverCertificate)
|
||||
|
||||
#generate crypto values
|
||||
clientRandom = rsa.random(256)
|
||||
@@ -340,7 +340,7 @@ class LicenseManager(object):
|
||||
|
||||
#generate hwid
|
||||
s = Stream()
|
||||
s.writeType((UInt32Le(2), String(self._hostname + self._username + "\x00" * 16)))
|
||||
s.write_type((UInt32Le(2), Buffer(self._hostname + self._username + "\x00" * 16)))
|
||||
hwid = s.getvalue()[:20]
|
||||
|
||||
message = ClientPLatformChallengeResponse()
|
||||
206
rdpy/core/nla/cssp.py
Normal file
206
rdpy/core/nla/cssp.py
Normal file
@@ -0,0 +1,206 @@
|
||||
#
|
||||
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
@summary: Credential Security Support Provider
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226764.aspx
|
||||
"""
|
||||
|
||||
from pyasn1.type import namedtype, univ, tag
|
||||
import pyasn1.codec.der.encoder as der_encoder
|
||||
import pyasn1.codec.der.decoder as der_decoder
|
||||
|
||||
from rdpy.core.nla import sspi
|
||||
from rdpy.model.message import Stream
|
||||
from rdpy.model import error
|
||||
from rdpy.security.x509 import X509Certificate
|
||||
|
||||
|
||||
class NegoToken(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('negoToken', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
)
|
||||
|
||||
|
||||
class NegoData(univ.SequenceOf):
|
||||
"""
|
||||
@summary: contain spnego ntlm of kerberos data
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226781.aspx
|
||||
"""
|
||||
componentType = NegoToken()
|
||||
|
||||
|
||||
class TSRequest(univ.Sequence):
|
||||
"""
|
||||
@summary: main structure
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226780.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('version', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.OptionalNamedType('negoTokens', NegoData().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('authInfo', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('pubKeyAuth', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
|
||||
namedtype.OptionalNamedType('errorCode', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
|
||||
)
|
||||
|
||||
|
||||
class TSCredentials(univ.Sequence):
|
||||
"""
|
||||
@summary: contain user information
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226782.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('credType', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('credentials', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
|
||||
)
|
||||
|
||||
|
||||
class TSPasswordCreds(univ.Sequence):
|
||||
"""
|
||||
@summary: contain username and password
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226783.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('domainName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('userName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.NamedType('password', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
)
|
||||
|
||||
|
||||
class TSCspDataDetail(univ.Sequence):
|
||||
"""
|
||||
@summary: smart card credentials
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226785.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('keySpec', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.OptionalNamedType('cardName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('readerName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('containerName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
|
||||
namedtype.OptionalNamedType('cspName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
|
||||
)
|
||||
|
||||
|
||||
class TSSmartCardCreds(univ.Sequence):
|
||||
"""
|
||||
@summary: smart card credentials
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226784.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('pin', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('cspData', TSCspDataDetail().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('userHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('domainHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
|
||||
)
|
||||
|
||||
|
||||
|
||||
def encode_der_trequest(nego_types=[], auth_info=None, pub_key_auth=None):
|
||||
"""
|
||||
@summary: create TSRequest from list of Type
|
||||
@param nego_types: {list(Type)}
|
||||
@param auth_info: {str} authentication info TSCredentials encrypted with authentication protocol
|
||||
@param pub_key_auth: {str} public key encrypted with authentication protocol
|
||||
@return: {str} TRequest der encoded
|
||||
"""
|
||||
negoData = NegoData().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))
|
||||
|
||||
#fill nego data tokens
|
||||
i = 0
|
||||
for negoType in nego_types:
|
||||
s = Stream()
|
||||
s.write_type(negoType)
|
||||
negoToken = NegoToken()
|
||||
negoToken.setComponentByPosition(0, s.getvalue())
|
||||
negoData.setComponentByPosition(i, negoToken)
|
||||
i += 1
|
||||
|
||||
request = TSRequest()
|
||||
request.setComponentByName("version", univ.Integer(2).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
|
||||
|
||||
if i > 0:
|
||||
request.setComponentByName("negoTokens", negoData)
|
||||
|
||||
if not auth_info is None:
|
||||
request.setComponentByName("authInfo", univ.OctetString(auth_info).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
|
||||
if not pub_key_auth is None:
|
||||
request.setComponentByName("pubKeyAuth", univ.OctetString(pub_key_auth).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
|
||||
|
||||
return der_encoder.encode(request)
|
||||
|
||||
|
||||
def decode_der_trequest(s):
|
||||
"""
|
||||
@summary: Decode the stream as
|
||||
@param s: {str}
|
||||
"""
|
||||
return der_decoder.decode(s, asn1Spec=TSRequest())[0]
|
||||
|
||||
|
||||
def getNegoTokens(tRequest):
|
||||
negoData = tRequest.getComponentByName("negoTokens")
|
||||
return [Stream(negoData.getComponentByPosition(i).getComponentByPosition(0).asOctets()) for i in range(len(negoData))]
|
||||
|
||||
|
||||
def encode_der_tcredentials(domain, username, password):
|
||||
passwordCred = TSPasswordCreds()
|
||||
passwordCred.setComponentByName("domainName", domain)
|
||||
passwordCred.setComponentByName("userName", username)
|
||||
passwordCred.setComponentByName("password", password)
|
||||
|
||||
credentials = TSCredentials()
|
||||
credentials.setComponentByName("credType", 1)
|
||||
credentials.setComponentByName("credentials", der_encoder.encode(passwordCred))
|
||||
|
||||
return der_encoder.encode(credentials)
|
||||
|
||||
|
||||
async def connect(reader, writer, authentication_protocol: sspi.IAuthenticationProtocol):
|
||||
"""
|
||||
CSSP connection sequence
|
||||
"""
|
||||
# send negotiate message
|
||||
writer.write(encode_der_trequest(nego_types=[authentication_protocol.getNegotiateMessage()]))
|
||||
await writer.drain()
|
||||
|
||||
trequest = decode_der_trequest(await reader.read(1500))
|
||||
message, interface = authentication_protocol.getAuthenticateMessage(getNegoTokens(trequest)[0])
|
||||
|
||||
# get binary certificate
|
||||
# There is no other way to get certificate that have net been validated
|
||||
peer_certificate = writer.transport._ssl_protocol._sslpipe.ssl_object._sslobj.getpeercert(True)
|
||||
x509_certificate = der_decoder.decode(peer_certificate, asn1Spec=X509Certificate())[0]
|
||||
public_key = x509_certificate.getComponentByName("tbsCertificate").getComponentByName("subjectPublicKeyInfo").getComponentByName("subjectPublicKey").asOctets()
|
||||
|
||||
# send back public key encrypted with NTLM
|
||||
writer.write(encode_der_trequest(nego_types=[message], pub_key_auth=interface.GSS_WrapEx(public_key)))
|
||||
await writer.drain()
|
||||
|
||||
# now check the increment
|
||||
# sever must respond with the same key incremented to one
|
||||
trequest = decode_der_trequest(await reader.read(1500))
|
||||
public_key_inc = interface.GSS_UnWrapEx(trequest.getComponentByName("pubKeyAuth").asOctets())
|
||||
|
||||
if int.from_bytes(public_key, "little") + 1 != int.from_bytes(public_key_inc, "little"):
|
||||
raise error.InvalidExpectedDataException("CSSP : Invalid public key increment")
|
||||
|
||||
domain, user, password = authentication_protocol.getEncodedCredentials()
|
||||
writer.write(encode_der_trequest(auth_info=interface.GSS_WrapEx(encode_der_tcredentials(domain, user, password))))
|
||||
await writer.drain()
|
||||
@@ -23,12 +23,13 @@
|
||||
"""
|
||||
|
||||
import hashlib, hmac, struct, datetime
|
||||
import sspi
|
||||
from rdpy.core.nla import sspi
|
||||
import rdpy.security.pyDes as pyDes
|
||||
import rdpy.security.rc4 as rc4
|
||||
from rdpy.security.rsa_wrapper import random
|
||||
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt24Le, UInt32Le, sizeof, Stream
|
||||
from rdpy.core import filetimes, error
|
||||
from rdpy.model.message import CompositeType, Buffer, UInt8, UInt16Le, UInt24Le, UInt32Le, sizeof, Stream
|
||||
from rdpy.model import filetimes, error
|
||||
|
||||
|
||||
class MajorVersion(object):
|
||||
"""
|
||||
@@ -37,7 +38,8 @@ class MajorVersion(object):
|
||||
"""
|
||||
WINDOWS_MAJOR_VERSION_5 = 0x05
|
||||
WINDOWS_MAJOR_VERSION_6 = 0x06
|
||||
|
||||
|
||||
|
||||
class MinorVersion(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236654.aspx
|
||||
@@ -47,13 +49,15 @@ class MinorVersion(object):
|
||||
WINDOWS_MINOR_VERSION_1 = 0x01
|
||||
WINDOWS_MINOR_VERSION_2 = 0x02
|
||||
WINDOWS_MINOR_VERSION_3 = 0x03
|
||||
|
||||
|
||||
|
||||
class NTLMRevision(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236654.aspx
|
||||
"""
|
||||
NTLMSSP_REVISION_W2K3 = 0x0F
|
||||
|
||||
|
||||
|
||||
class Negotiate(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236650.aspx
|
||||
@@ -79,7 +83,8 @@ class Negotiate(object):
|
||||
NTLMSSP_REQUEST_TARGET = 0x00000004
|
||||
NTLM_NEGOTIATE_OEM = 0x00000002
|
||||
NTLMSSP_NEGOTIATE_UNICODE = 0x00000001
|
||||
|
||||
|
||||
|
||||
class AvId(object):
|
||||
"""
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236646.aspx
|
||||
@@ -124,8 +129,8 @@ class AvPair(CompositeType):
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.AvId = UInt16Le()
|
||||
self.AvLen = UInt16Le(lambda:sizeof(self.Value))
|
||||
self.Value = String(readLen = self.AvLen)
|
||||
self.AvLen = UInt16Le(lambda: sizeof(self.Value))
|
||||
self.Value = Buffer(read_len=lambda: self.AvLen.value)
|
||||
|
||||
class MessageSignatureEx(CompositeType):
|
||||
"""
|
||||
@@ -135,7 +140,7 @@ class MessageSignatureEx(CompositeType):
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Version = UInt32Le(0x00000001, constant = True)
|
||||
self.Checksum = String(readLen = CallableValue(8))
|
||||
self.Checksum = Buffer(read_len=lambda: 8)
|
||||
self.SeqNum = UInt32Le()
|
||||
|
||||
class NegotiateMessage(CompositeType):
|
||||
@@ -144,9 +149,9 @@ class NegotiateMessage(CompositeType):
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236641.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Signature = String("NTLMSSP\x00", readLen = CallableValue(8), constant = True)
|
||||
self.MessageType = UInt32Le(0x00000001, constant = True)
|
||||
super().__init__()
|
||||
self.Signature = Buffer(b"NTLMSSP\x00", read_len=lambda: 8, constant=True)
|
||||
self.MessageType = UInt32Le(0x00000001, constant=True)
|
||||
|
||||
self.NegotiateFlags = UInt32Le()
|
||||
|
||||
@@ -160,7 +165,7 @@ class NegotiateMessage(CompositeType):
|
||||
|
||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||
|
||||
self.Payload = String()
|
||||
self.Payload = Buffer()
|
||||
|
||||
class ChallengeMessage(CompositeType):
|
||||
"""
|
||||
@@ -169,8 +174,8 @@ class ChallengeMessage(CompositeType):
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Signature = String("NTLMSSP\x00", readLen = CallableValue(8), constant = True)
|
||||
self.MessageType = UInt32Le(0x00000002, constant = True)
|
||||
self.Signature = Buffer(b"NTLMSSP\x00", read_len=lambda: 8, constant=True)
|
||||
self.MessageType = UInt32Le(0x00000002, constant=True)
|
||||
|
||||
self.TargetNameLen = UInt16Le()
|
||||
self.TargetNameMaxLen = UInt16Le(lambda:self.TargetNameLen.value)
|
||||
@@ -178,15 +183,15 @@ class ChallengeMessage(CompositeType):
|
||||
|
||||
self.NegotiateFlags = UInt32Le()
|
||||
|
||||
self.ServerChallenge = String(readLen = CallableValue(8))
|
||||
self.Reserved = String("\x00" * 8, readLen = CallableValue(8))
|
||||
self.ServerChallenge = Buffer(read_len=lambda: 8)
|
||||
self.Reserved = Buffer(b"\x00" * 8, read_len=lambda: 8)
|
||||
|
||||
self.TargetInfoLen = UInt16Le()
|
||||
self.TargetInfoMaxLen = UInt16Le(lambda:self.TargetInfoLen.value)
|
||||
self.TargetInfoMaxLen = UInt16Le(lambda: self.TargetInfoLen.value)
|
||||
self.TargetInfoBufferOffset = UInt32Le()
|
||||
|
||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||
self.Payload = String()
|
||||
self.Version = Version(conditional=lambda: (self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||
self.Payload = Buffer()
|
||||
|
||||
def getTargetName(self):
|
||||
return getPayLoadField(self, self.TargetNameLen.value, self.TargetNameBufferOffset.value)
|
||||
@@ -203,7 +208,7 @@ class ChallengeMessage(CompositeType):
|
||||
s = Stream(self.getTargetInfo())
|
||||
while(True):
|
||||
avPair = AvPair()
|
||||
s.readType(avPair)
|
||||
s.read_type(avPair)
|
||||
if avPair.AvId.value == AvId.MsvAvEOL:
|
||||
return result
|
||||
result[avPair.AvId.value] = avPair.Value.value
|
||||
@@ -216,8 +221,8 @@ class AuthenticateMessage(CompositeType):
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.Signature = String("NTLMSSP\x00", readLen = CallableValue(8), constant = True)
|
||||
self.MessageType = UInt32Le(0x00000003, constant = True)
|
||||
self.Signature = Buffer(b"NTLMSSP\x00", read_len=lambda: 8, constant = True)
|
||||
self.MessageType = UInt32Le(0x00000003, constant=True)
|
||||
|
||||
self.LmChallengeResponseLen = UInt16Le()
|
||||
self.LmChallengeResponseMaxLen = UInt16Le(lambda:self.LmChallengeResponseLen.value)
|
||||
@@ -246,8 +251,8 @@ class AuthenticateMessage(CompositeType):
|
||||
self.NegotiateFlags = UInt32Le()
|
||||
self.Version = Version(conditional = lambda:(self.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_VERSION))
|
||||
|
||||
self.MIC = String("\x00" * 16, readLen = CallableValue(16))
|
||||
self.Payload = String()
|
||||
self.MIC = Buffer(b"\x00" * 16, read_len=lambda: 16)
|
||||
self.Payload = Buffer()
|
||||
|
||||
def getUserName(self):
|
||||
return getPayLoadField(self, self.UserNameLen.value, self.UserNameBufferOffset.value)
|
||||
@@ -347,6 +352,7 @@ def DESL(key, data):
|
||||
"""
|
||||
return DES(key[0:7], data) + DES(key[7:14], data) + DES(key[14:16] + "\x00" * 5, data)
|
||||
|
||||
|
||||
def UNICODE(s):
|
||||
"""
|
||||
@param s: source
|
||||
@@ -354,6 +360,7 @@ def UNICODE(s):
|
||||
"""
|
||||
return s.encode('utf-16le')
|
||||
|
||||
|
||||
def MD4(s):
|
||||
"""
|
||||
@summary: compute the md4 sum
|
||||
@@ -362,6 +369,7 @@ def MD4(s):
|
||||
"""
|
||||
return hashlib.new('md4', s).digest()
|
||||
|
||||
|
||||
def MD5(s):
|
||||
"""
|
||||
@summary: compute the md5 sum
|
||||
@@ -370,13 +378,15 @@ def MD5(s):
|
||||
"""
|
||||
return hashlib.new('md5', s).digest()
|
||||
|
||||
|
||||
def Z(m):
|
||||
"""
|
||||
@summary: fill m zero in string
|
||||
@param m: {int} size of string
|
||||
@return: \x00 * m
|
||||
"""
|
||||
return "\x00" * m
|
||||
return b"\x00" * m
|
||||
|
||||
|
||||
def RC4K(key, plaintext):
|
||||
"""
|
||||
@@ -387,6 +397,7 @@ def RC4K(key, plaintext):
|
||||
"""
|
||||
return rc4.crypt(rc4.RC4Key(key), plaintext)
|
||||
|
||||
|
||||
def KXKEYv2(SessionBaseKey, LmChallengeResponse, ServerChallenge):
|
||||
"""
|
||||
@summary: Key eXchange Key for NTLMv2
|
||||
@@ -397,17 +408,20 @@ def KXKEYv2(SessionBaseKey, LmChallengeResponse, ServerChallenge):
|
||||
"""
|
||||
return SessionBaseKey
|
||||
|
||||
|
||||
def SEALKEY(ExportedSessionKey, client):
|
||||
if client:
|
||||
return MD5(ExportedSessionKey + "session key to client-to-server sealing key magic constant\0")
|
||||
return MD5(ExportedSessionKey + b"session key to client-to-server sealing key magic constant\0")
|
||||
else:
|
||||
return MD5(ExportedSessionKey + "session key to server-to-client sealing key magic constant\0")
|
||||
return MD5(ExportedSessionKey + b"session key to server-to-client sealing key magic constant\0")
|
||||
|
||||
|
||||
def SIGNKEY(ExportedSessionKey, client):
|
||||
if client:
|
||||
return MD5(ExportedSessionKey + "session key to client-to-server signing key magic constant\0")
|
||||
return MD5(ExportedSessionKey + b"session key to client-to-server signing key magic constant\0")
|
||||
else:
|
||||
return MD5(ExportedSessionKey + "session key to server-to-client signing key magic constant\0")
|
||||
return MD5(ExportedSessionKey + b"session key to server-to-client signing key magic constant\0")
|
||||
|
||||
|
||||
def HMAC_MD5(key, data):
|
||||
"""
|
||||
@@ -417,6 +431,7 @@ def HMAC_MD5(key, data):
|
||||
"""
|
||||
return hmac.new(key, data, hashlib.md5).digest()
|
||||
|
||||
|
||||
def NTOWFv2(Passwd, User, UserDom):
|
||||
"""
|
||||
@summary: Version 2 of NTLM hash function
|
||||
@@ -427,6 +442,7 @@ def NTOWFv2(Passwd, User, UserDom):
|
||||
"""
|
||||
return HMAC_MD5(MD4(UNICODE(Passwd)), UNICODE(User.upper() + UserDom))
|
||||
|
||||
|
||||
def LMOWFv2(Passwd, User, UserDom):
|
||||
"""
|
||||
@summary: Same as NTOWFv2
|
||||
@@ -437,14 +453,15 @@ def LMOWFv2(Passwd, User, UserDom):
|
||||
"""
|
||||
return NTOWFv2(Passwd, User, UserDom)
|
||||
|
||||
|
||||
def ComputeResponsev2(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClientChallenge, Time, ServerName):
|
||||
"""
|
||||
@summary: process NTLMv2 Authenticate hash
|
||||
@param NegFlg: {int} Negotiation flags come from challenge message
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236700.aspx
|
||||
"""
|
||||
Responserversion = "\x01"
|
||||
HiResponserversion = "\x01"
|
||||
Responserversion = b"\x01"
|
||||
HiResponserversion = b"\x01"
|
||||
|
||||
temp = Responserversion + HiResponserversion + Z(6) + Time + ClientChallenge + Z(4) + ServerName
|
||||
NTProofStr = HMAC_MD5(ResponseKeyNT, ServerChallenge + temp)
|
||||
@@ -455,6 +472,7 @@ def ComputeResponsev2(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClientChall
|
||||
|
||||
return NtChallengeResponse, LmChallengeResponse, SessionBaseKey
|
||||
|
||||
|
||||
def MAC(handle, SigningKey, SeqNum, Message):
|
||||
"""
|
||||
@summary: generate signature for application message
|
||||
@@ -469,12 +487,13 @@ def MAC(handle, SigningKey, SeqNum, Message):
|
||||
|
||||
#write the SeqNum
|
||||
s = Stream()
|
||||
s.writeType(signature.SeqNum)
|
||||
s.write_type(signature.SeqNum)
|
||||
|
||||
signature.Checksum.value = rc4.crypt(handle, HMAC_MD5(SigningKey, s.getvalue() + Message)[:8])
|
||||
|
||||
return signature
|
||||
|
||||
|
||||
def MIC(ExportedSessionKey, negotiateMessage, challengeMessage, authenticateMessage):
|
||||
"""
|
||||
@summary: Compute MIC signature
|
||||
@@ -485,14 +504,15 @@ def MIC(ExportedSessionKey, negotiateMessage, challengeMessage, authenticateMess
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236676.aspx
|
||||
"""
|
||||
s = Stream()
|
||||
s.writeType((negotiateMessage, challengeMessage, authenticateMessage))
|
||||
s.write_type((negotiateMessage, challengeMessage, authenticateMessage))
|
||||
return HMAC_MD5(ExportedSessionKey, s.getvalue())
|
||||
|
||||
|
||||
|
||||
class NTLMv2(sspi.IAuthenticationProtocol):
|
||||
"""
|
||||
@summary: Handle NTLMv2 Authentication
|
||||
"""
|
||||
def __init__(self, domain, user, password):
|
||||
def __init__(self, domain: str, user: str, password: str):
|
||||
self._domain = domain
|
||||
self._user = user
|
||||
self._password = password
|
||||
@@ -530,7 +550,7 @@ class NTLMv2(sspi.IAuthenticationProtocol):
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc236676.aspx
|
||||
"""
|
||||
self._challengeMessage = ChallengeMessage()
|
||||
s.readType(self._challengeMessage)
|
||||
s.read_type(self._challengeMessage)
|
||||
|
||||
ServerChallenge = self._challengeMessage.ServerChallenge.value
|
||||
ClientChallenge = random(64)
|
||||
@@ -538,7 +558,7 @@ class NTLMv2(sspi.IAuthenticationProtocol):
|
||||
computeMIC = False
|
||||
ServerName = self._challengeMessage.getTargetInfo()
|
||||
infos = self._challengeMessage.getTargetInfoAsAvPairArray()
|
||||
if infos.has_key(AvId.MsvAvTimestamp):
|
||||
if AvId.MsvAvTimestamp in infos.keys():
|
||||
Timestamp = infos[AvId.MsvAvTimestamp]
|
||||
computeMIC = True
|
||||
else:
|
||||
@@ -554,7 +574,7 @@ class NTLMv2(sspi.IAuthenticationProtocol):
|
||||
if self._challengeMessage.NegotiateFlags.value & Negotiate.NTLMSSP_NEGOTIATE_UNICODE:
|
||||
self._enableUnicode = True
|
||||
domain, user = UNICODE(domain), UNICODE(user)
|
||||
self._authenticateMessage = createAuthenticationMessage(self._challengeMessage.NegotiateFlags.value, domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey, "")
|
||||
self._authenticateMessage = createAuthenticationMessage(self._challengeMessage.NegotiateFlags.value, domain, user, NtChallengeResponse, LmChallengeResponse, EncryptedRandomSessionKey, b"")
|
||||
|
||||
if computeMIC:
|
||||
self._authenticateMessage.MIC.value = MIC(ExportedSessionKey, self._negotiateMessage, self._challengeMessage, self._authenticateMessage)
|
||||
@@ -608,7 +628,7 @@ class NTLMv2SecurityInterface(sspi.IGenericSecurityService):
|
||||
signature = MAC(self._encryptHandle, self._signingKey, self._seqNum, data)
|
||||
self._seqNum += 1
|
||||
s = Stream()
|
||||
s.writeType(signature)
|
||||
s.write_type(signature)
|
||||
return s.getvalue() + encryptedData
|
||||
|
||||
def GSS_UnWrapEx(self, data):
|
||||
@@ -616,18 +636,15 @@ class NTLMv2SecurityInterface(sspi.IGenericSecurityService):
|
||||
@summary: decrypt data with key exchange in Authentication protocol
|
||||
@param data: {str}
|
||||
"""
|
||||
signature = MessageSignatureEx()
|
||||
message = String()
|
||||
s = Stream(data)
|
||||
s.readType((signature, message))
|
||||
signature, message = Stream(data).read_type((MessageSignatureEx(), Buffer()))
|
||||
|
||||
#decrypt message
|
||||
plaintextMessage = rc4.crypt(self._decryptHandle, message.value)
|
||||
checksum = rc4.crypt(self._decryptHandle, signature.Checksum.value)
|
||||
|
||||
#recompute checksum
|
||||
# recompute checksum
|
||||
t = Stream()
|
||||
t.writeType(signature.SeqNum)
|
||||
t.write_type(signature.SeqNum)
|
||||
verify = HMAC_MD5(self._verifyKey, t.getvalue() + plaintextMessage)[:8]
|
||||
if verify != checksum:
|
||||
raise error.InvalidExpectedDataException("NTLMv2SecurityInterface : Invalid checksum")
|
||||
@@ -21,7 +21,7 @@
|
||||
@summary: security service provider interface (Microsoft)
|
||||
"""
|
||||
|
||||
from rdpy.core.error import CallPureVirtualFuntion
|
||||
from rdpy.model.error import CallPureVirtualFuntion
|
||||
|
||||
class IAuthenticationProtocol(object):
|
||||
"""
|
||||
@@ -16,16 +16,17 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
import rdpy.core.log as log
|
||||
from rdpy.model.error import InvalidExpectedDataException
|
||||
import rdpy.model.log as log
|
||||
|
||||
"""
|
||||
Definition of structure use for capabilities nego
|
||||
Use in PDU layer
|
||||
"""
|
||||
|
||||
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
|
||||
from rdpy.model.type import CompositeType, CallableValue, Buffer, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
|
||||
|
||||
class CapsType(object):
|
||||
"""
|
||||
@summary: Different type of capabilities
|
||||
@@ -240,7 +241,7 @@ class Capability(CompositeType):
|
||||
return c(readLen = self.lengthCapability - 4)
|
||||
log.debug("unknown Capability type : %s"%hex(self.capabilitySetType.value))
|
||||
#read entire packet
|
||||
return String(readLen = self.lengthCapability - 4)
|
||||
return Buffer(readLen =self.lengthCapability - 4)
|
||||
|
||||
if capability is None:
|
||||
capability = FactoryType(CapabilityFactory)
|
||||
@@ -308,7 +309,7 @@ class OrderCapability(CompositeType):
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.terminalDescriptor = String("\x00" * 16, readLen = CallableValue(16))
|
||||
self.terminalDescriptor = Buffer("\x00" * 16, readLen = CallableValue(16))
|
||||
self.pad4octetsA = UInt32Le(0)
|
||||
self.desktopSaveXGranularity = UInt16Le(1)
|
||||
self.desktopSaveYGranularity = UInt16Le(20)
|
||||
@@ -388,7 +389,7 @@ class InputCapability(CompositeType):
|
||||
#same value as gcc.ClientCoreSettings.keyboardFnKeys
|
||||
self.keyboardFunctionKey = UInt32Le()
|
||||
#same value as gcc.ClientCoreSettingrrs.imeFileName
|
||||
self.imeFileName = String("\x00" * 64, readLen = CallableValue(64))
|
||||
self.imeFileName = Buffer("\x00" * 64, readLen = CallableValue(64))
|
||||
|
||||
class BrushCapability(CompositeType):
|
||||
"""
|
||||
@@ -22,10 +22,10 @@ Implement the main graphic layer
|
||||
|
||||
In this layer are managed all mains bitmap update orders end user inputs
|
||||
"""
|
||||
from rdpy.core.type import CompositeType, CallableValue, String, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
import rdpy.core.log as log
|
||||
import caps, order
|
||||
from rdpy.model.type import CompositeType, CallableValue, Buffer, UInt8, UInt16Le, UInt32Le, sizeof, ArrayType, FactoryType
|
||||
from rdpy.model.error import InvalidExpectedDataException
|
||||
import rdpy.model.log as log
|
||||
from rdpy.core.pdu import caps, order
|
||||
|
||||
class PDUType(object):
|
||||
"""
|
||||
@@ -481,7 +481,7 @@ class PDU(CompositeType):
|
||||
return c(readLen = CallableValue(self.shareControlHeader.totalLength.value - sizeof(self.shareControlHeader)))
|
||||
log.debug("unknown PDU type : %s"%hex(self.shareControlHeader.pduType.value))
|
||||
#read entire packet
|
||||
return String(readLen = CallableValue(self.shareControlHeader.totalLength.value - sizeof(self.shareControlHeader)))
|
||||
return Buffer(readLen = CallableValue(self.shareControlHeader.totalLength.value - sizeof(self.shareControlHeader)))
|
||||
|
||||
if pduMessage is None:
|
||||
pduMessage = FactoryType(PDUMessageFactory)
|
||||
@@ -503,7 +503,7 @@ class DemandActivePDU(CompositeType):
|
||||
self.shareId = UInt32Le()
|
||||
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
|
||||
self.lengthCombinedCapabilities = UInt16Le(lambda:(sizeof(self.numberCapabilities) + sizeof(self.pad2Octets) + sizeof(self.capabilitySets)))
|
||||
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
self.sourceDescriptor = Buffer("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
self.numberCapabilities = UInt16Le(lambda:len(self.capabilitySets._array))
|
||||
self.pad2Octets = UInt16Le()
|
||||
self.capabilitySets = ArrayType(caps.Capability, readLen = self.numberCapabilities)
|
||||
@@ -523,7 +523,7 @@ class ConfirmActivePDU(CompositeType):
|
||||
self.originatorId = UInt16Le(0x03EA, constant = True)
|
||||
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
|
||||
self.lengthCombinedCapabilities = UInt16Le(lambda:(sizeof(self.numberCapabilities) + sizeof(self.pad2Octets) + sizeof(self.capabilitySets)))
|
||||
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
self.sourceDescriptor = Buffer("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
self.numberCapabilities = UInt16Le(lambda:len(self.capabilitySets._array))
|
||||
self.pad2Octets = UInt16Le()
|
||||
self.capabilitySets = ArrayType(caps.Capability, readLen = self.numberCapabilities)
|
||||
@@ -542,7 +542,7 @@ class DeactiveAllPDU(CompositeType):
|
||||
CompositeType.__init__(self, optional = True, readLen = readLen)
|
||||
self.shareId = UInt32Le()
|
||||
self.lengthSourceDescriptor = UInt16Le(lambda:sizeof(self.sourceDescriptor))
|
||||
self.sourceDescriptor = String("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
self.sourceDescriptor = Buffer("rdpy", readLen = self.lengthSourceDescriptor)
|
||||
|
||||
class DataPDU(CompositeType):
|
||||
"""
|
||||
@@ -563,7 +563,7 @@ class DataPDU(CompositeType):
|
||||
if self.shareDataHeader.pduType2.value == c._PDUTYPE2_:
|
||||
return c(readLen = CallableValue(readLen.value - sizeof(self.shareDataHeader)))
|
||||
log.debug("unknown PDU data type : %s"%hex(self.shareDataHeader.pduType2.value))
|
||||
return String(readLen = CallableValue(readLen.value - sizeof(self.shareDataHeader)))
|
||||
return Buffer(readLen = CallableValue(readLen.value - sizeof(self.shareDataHeader)))
|
||||
|
||||
if pduData is None:
|
||||
pduData = FactoryType(PDUDataFactory)
|
||||
@@ -780,7 +780,7 @@ class UpdateDataPDU(CompositeType):
|
||||
if self.updateType.value == c._UPDATE_TYPE_:
|
||||
return c(readLen = CallableValue(readLen.value - 2))
|
||||
log.debug("unknown PDU update data type : %s"%hex(self.updateType.value))
|
||||
return String(readLen = CallableValue(readLen.value - 2))
|
||||
return Buffer(readLen = CallableValue(readLen.value - 2))
|
||||
|
||||
if updateData is None:
|
||||
updateData = FactoryType(UpdateDataFactory, conditional = lambda:(self.updateType.value != UpdateType.UPDATETYPE_SYNCHRONIZE))
|
||||
@@ -799,7 +799,7 @@ class SaveSessionInfoPDU(CompositeType):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.infoType = UInt32Le()
|
||||
#TODO parse info data
|
||||
self.infoData = String()
|
||||
self.infoData = Buffer()
|
||||
|
||||
class FastPathUpdatePDU(CompositeType):
|
||||
"""
|
||||
@@ -820,7 +820,7 @@ class FastPathUpdatePDU(CompositeType):
|
||||
if (self.updateHeader.value & 0xf) == c._FASTPATH_UPDATE_TYPE_:
|
||||
return c(readLen = self.size)
|
||||
log.debug("unknown Fast Path PDU update data type : %s"%hex(self.updateHeader.value & 0xf))
|
||||
return String(readLen = self.size)
|
||||
return Buffer(readLen = self.size)
|
||||
|
||||
if updateData is None:
|
||||
updateData = FactoryType(UpdateDataFactory)
|
||||
@@ -902,7 +902,7 @@ class BitmapData(CompositeType):
|
||||
self.flags = UInt16Le()
|
||||
self.bitmapLength = UInt16Le(lambda:(sizeof(self.bitmapComprHdr) + sizeof(self.bitmapDataStream)))
|
||||
self.bitmapComprHdr = BitmapCompressedDataHeader(bodySize = lambda:sizeof(self.bitmapDataStream), scanWidth = lambda:self.width.value, uncompressedSize = lambda:(self.width.value * self.height.value * self.bitsPerPixel.value), conditional = lambda:((self.flags.value & BitmapFlag.BITMAP_COMPRESSION) and not (self.flags.value & BitmapFlag.NO_BITMAP_COMPRESSION_HDR)))
|
||||
self.bitmapDataStream = String(bitmapDataStream, readLen = CallableValue(lambda:(self.bitmapLength.value if (not self.flags.value & BitmapFlag.BITMAP_COMPRESSION or self.flags.value & BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
|
||||
self.bitmapDataStream = Buffer(bitmapDataStream, readLen = CallableValue(lambda:(self.bitmapLength.value if (not self.flags.value & BitmapFlag.BITMAP_COMPRESSION or self.flags.value & BitmapFlag.NO_BITMAP_COMPRESSION_HDR) else self.bitmapComprHdr.cbCompMainBodySize.value)))
|
||||
|
||||
class FastPathBitmapUpdateDataPDU(CompositeType):
|
||||
"""
|
||||
@@ -23,12 +23,13 @@ Implement the main graphic layer
|
||||
In this layer are managed all mains bitmap update orders end user inputs
|
||||
"""
|
||||
|
||||
from rdpy.core.layer import LayerAutomata
|
||||
from rdpy.core.error import CallPureVirtualFuntion
|
||||
from rdpy.core.type import ArrayType
|
||||
import rdpy.core.log as log
|
||||
import rdpy.protocol.rdp.tpkt as tpkt
|
||||
import data, caps
|
||||
from rdpy.model.layer import LayerAutomata
|
||||
from rdpy.model.error import CallPureVirtualFuntion
|
||||
from rdpy.model.type import ArrayType
|
||||
import rdpy.model.log as log
|
||||
from rdpy.core import tpkt
|
||||
from rdpy.core.pdu import data, caps
|
||||
|
||||
|
||||
class PDUClientListener(object):
|
||||
"""
|
||||
@@ -175,7 +176,7 @@ class Client(PDULayer):
|
||||
@param s: Stream
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DEMANDACTIVEPDU:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
@@ -203,7 +204,7 @@ class Client(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
@@ -219,7 +220,7 @@ class Client(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
@@ -235,7 +236,7 @@ class Client(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_GRANTED_CONTROL:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
@@ -251,7 +252,7 @@ class Client(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTMAP:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
@@ -268,7 +269,7 @@ class Client(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdus = ArrayType(data.PDU)
|
||||
s.readType(pdus)
|
||||
s.read_type(pdus)
|
||||
for pdu in pdus:
|
||||
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
|
||||
self.readDataPDU(pdu.pduMessage)
|
||||
@@ -286,7 +287,7 @@ class Client(PDULayer):
|
||||
@param secFlag: {SecFlags}
|
||||
"""
|
||||
updates = ArrayType(data.FastPathUpdatePDU)
|
||||
fastPathS.readType(updates)
|
||||
fastPathS.read_type(updates)
|
||||
for update in updates:
|
||||
if update.updateHeader.value == data.FastPathUpdateType.FASTPATH_UPDATETYPE_BITMAP:
|
||||
self._listener.onUpdate(update.updateData.rectangles._array)
|
||||
@@ -418,7 +419,7 @@ class Server(PDULayer):
|
||||
@param s: Stream
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_CONFIRMACTIVEPDU:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
@@ -444,7 +445,7 @@ class Server(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_SYNCHRONIZE:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
@@ -459,7 +460,7 @@ class Server(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_COOPERATE:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
@@ -474,7 +475,7 @@ class Server(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_CONTROL or pdu.pduMessage.pduData.action.value != data.Action.CTRLACTION_REQUEST_CONTROL:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send too but ignored
|
||||
@@ -490,7 +491,7 @@ class Server(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value != data.PDUType.PDUTYPE_DATAPDU or pdu.pduMessage.shareDataHeader.pduType2.value != data.PDUType2.PDUTYPE2_FONTLIST:
|
||||
#not a blocking error because in deactive reactive sequence
|
||||
#input can be send but ignored
|
||||
@@ -509,7 +510,7 @@ class Server(PDULayer):
|
||||
@param s: Stream from transport layer
|
||||
"""
|
||||
pdu = data.PDU()
|
||||
s.readType(pdu)
|
||||
s.read_type(pdu)
|
||||
if pdu.shareControlHeader.pduType.value == data.PDUType.PDUTYPE_DATAPDU:
|
||||
self.readDataPDU(pdu.pduMessage)
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
GDI order structure
|
||||
"""
|
||||
|
||||
from rdpy.core import log
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
from rdpy.core.type import CompositeType, UInt8, String, FactoryType, SInt8, SInt16Le
|
||||
from rdpy.model import log
|
||||
from rdpy.model.error import InvalidExpectedDataException
|
||||
from rdpy.model.type import CompositeType, UInt8, Buffer, FactoryType, SInt8, SInt16Le
|
||||
|
||||
class ControlFlag(object):
|
||||
"""
|
||||
@@ -100,7 +100,7 @@ class PrimaryDrawingOrder(CompositeType):
|
||||
return c(self.controlFlags)
|
||||
log.debug("unknown Order type : %s"%hex(self.orderType.value))
|
||||
#read entire packet
|
||||
return String()
|
||||
return Buffer()
|
||||
|
||||
if order is None:
|
||||
order = FactoryType(OrderFactory)
|
||||
@@ -21,15 +21,19 @@
|
||||
Use to manage RDP stack in twisted
|
||||
"""
|
||||
|
||||
from rdpy.core import layer
|
||||
from rdpy.core.error import CallPureVirtualFuntion, InvalidValue
|
||||
import pdu.layer
|
||||
import pdu.data
|
||||
import pdu.caps
|
||||
import rdpy.core.log as log
|
||||
import tpkt, x224, sec
|
||||
from t125 import mcs, gcc
|
||||
from nla import cssp, ntlm
|
||||
from rdpy.model import layer
|
||||
from rdpy.model.error import CallPureVirtualFuntion, InvalidValue
|
||||
from rdpy.core.pdu.layer import PDUClientListener, PDUServerListener
|
||||
from rdpy.core.pdu import data
|
||||
from rdpy.core.pdu import caps
|
||||
from rdpy.core.pdu import layer as pdu
|
||||
import rdpy.model.log as log
|
||||
import rdpy.core.tpkt as tpkt
|
||||
import rdpy.core.x224 as x224
|
||||
import rdpy.core.sec as sec
|
||||
from rdpy.core.t125 import mcs, gcc
|
||||
from rdpy.core.nla import cssp, ntlm
|
||||
|
||||
|
||||
class SecurityLevel(object):
|
||||
"""
|
||||
@@ -39,7 +43,8 @@ class SecurityLevel(object):
|
||||
RDP_LEVEL_SSL = 1
|
||||
RDP_LEVEL_NLA = 2
|
||||
|
||||
class RDPClientController(pdu.layer.PDUClientListener):
|
||||
|
||||
class RDPClientController(PDUClientListener):
|
||||
"""
|
||||
Manage RDP stack as client
|
||||
"""
|
||||
@@ -47,7 +52,7 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
#list of observer
|
||||
self._clientObserver = []
|
||||
#PDU layer
|
||||
self._pduLayer = pdu.layer.Client(self)
|
||||
self._pduLayer = pdu.Client(self)
|
||||
#secure layer
|
||||
self._secLayer = sec.Client(self._pduLayer)
|
||||
#multi channel service
|
||||
@@ -94,8 +99,8 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
@param height: height in pixel of screen
|
||||
"""
|
||||
#set screen definition in MCS layer
|
||||
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).desktopHeight.value = height
|
||||
self._mcsLayer._clientSettings.getBlock(gcc.MessageType.CS_CORE).desktopWidth.value = width
|
||||
self._mcsLayer._clientSettings.get_block(gcc.MessageType.CS_CORE).desktopHeight.value = height
|
||||
self._mcsLayer._clientSettings.get_block(gcc.MessageType.CS_CORE).desktopWidth.value = width
|
||||
|
||||
def setUsername(self, username):
|
||||
"""
|
||||
@@ -365,7 +370,8 @@ class RDPClientController(pdu.layer.PDUClientListener):
|
||||
"""
|
||||
self._pduLayer.close()
|
||||
|
||||
class RDPServerController(pdu.layer.PDUServerListener):
|
||||
|
||||
class RDPServerController(PDUServerListener):
|
||||
"""
|
||||
@summary: Controller use in server side mode
|
||||
"""
|
||||
@@ -585,49 +591,50 @@ class ClientFactory(layer.RawLayerClientFactory):
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ClientFactory"))
|
||||
|
||||
class ServerFactory(layer.RawLayerServerFactory):
|
||||
"""
|
||||
@summary: Factory of Server RDP protocol
|
||||
"""
|
||||
def __init__(self, colorDepth, privateKeyFileName = None, certificateFileName = None):
|
||||
"""
|
||||
@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
|
||||
|
||||
def connectionLost(self, tpktLayer, reason):
|
||||
"""
|
||||
@param reason: twisted reason
|
||||
"""
|
||||
#retrieve controller
|
||||
x224Layer = tpktLayer._presentation
|
||||
mcsLayer = x224Layer._presentation
|
||||
secLayer = mcsLayer._channels[mcs.Channel.MCS_GLOBAL_CHANNEL]
|
||||
pduLayer = secLayer._presentation
|
||||
controller = pduLayer._listener
|
||||
controller.onClose()
|
||||
|
||||
def buildRawLayer(self, addr):
|
||||
"""
|
||||
@summary: Function call from twisted and build rdp protocol stack
|
||||
@param addr: destination address
|
||||
"""
|
||||
controller = RDPServerController(self._colorDepth, self._privateKeyFileName, self._certificateFileName)
|
||||
self.buildObserver(controller, addr)
|
||||
return controller.getProtocol()
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@summary: Build observer use for connection
|
||||
@param controller: RDP stack controller
|
||||
@param addr: destination address
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ServerFactory"))
|
||||
|
||||
|
||||
# class ServerFactory(RawLayerServerFactory):
|
||||
# """
|
||||
# @summary: Factory of Server RDP protocol
|
||||
# """
|
||||
# def __init__(self, colorDepth, privateKeyFileName = None, certificateFileName = None):
|
||||
# """
|
||||
# @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
|
||||
#
|
||||
# def connectionLost(self, tpktLayer, reason):
|
||||
# """
|
||||
# @param reason: twisted reason
|
||||
# """
|
||||
# #retrieve controller
|
||||
# x224Layer = tpktLayer._presentation
|
||||
# mcsLayer = x224Layer._presentation
|
||||
# secLayer = mcsLayer._channels[mcs.Channel.MCS_GLOBAL_CHANNEL]
|
||||
# pduLayer = secLayer._presentation
|
||||
# controller = pduLayer._listener
|
||||
# controller.onClose()
|
||||
#
|
||||
# def buildRawLayer(self, addr):
|
||||
# """
|
||||
# @summary: Function call from twisted and build rdp protocol stack
|
||||
# @param addr: destination address
|
||||
# """
|
||||
# controller = RDPServerController(self._colorDepth, self._privateKeyFileName, self._certificateFileName)
|
||||
# self.buildObserver(controller, addr)
|
||||
# return controller.getProtocol()
|
||||
#
|
||||
# def buildObserver(self, controller, addr):
|
||||
# """
|
||||
# @summary: Build observer use for connection
|
||||
# @param controller: RDP stack controller
|
||||
# @param addr: destination address
|
||||
# """
|
||||
# raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ServerFactory"))
|
||||
#
|
||||
class RDPClientObserver(object):
|
||||
"""
|
||||
@summary: Class use to inform all RDP event handle by RDPY
|
||||
@@ -21,13 +21,14 @@
|
||||
RDP Standard security layer
|
||||
"""
|
||||
|
||||
import sha, md5
|
||||
import lic, tpkt
|
||||
from t125 import gcc, mcs
|
||||
from rdpy.core.type import CompositeType, CallableValue, Stream, UInt32Le, UInt16Le, String, sizeof, UInt8
|
||||
from rdpy.core.layer import LayerAutomata, IStreamSender
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
from rdpy.core import log
|
||||
from hashlib import sha1 as sha
|
||||
from hashlib import md5
|
||||
from rdpy.core import tpkt, lic
|
||||
from rdpy.core.t125 import gcc, mcs
|
||||
from rdpy.model.type import CompositeType, CallableValue, Stream, UInt32Le, UInt16Le, Buffer, sizeof, UInt8
|
||||
from rdpy.model.layer import LayerAutomata, IStreamSender
|
||||
from rdpy.model.error import InvalidExpectedDataException
|
||||
from rdpy.model import log
|
||||
from rdpy.security import rc4
|
||||
import rdpy.security.rsa_wrapper as rsa
|
||||
|
||||
@@ -166,7 +167,7 @@ def macData(macSaltKey, data):
|
||||
|
||||
#encode length
|
||||
dataLength = Stream()
|
||||
dataLength.writeType(UInt32Le(len(data)))
|
||||
dataLength.write_type(UInt32Le(len(data)))
|
||||
|
||||
sha1Digest.update(macSaltKey)
|
||||
sha1Digest.update("\x36" * 40)
|
||||
@@ -194,10 +195,10 @@ def macSaltedData(macSaltKey, data, encryptionCount):
|
||||
|
||||
#encode length
|
||||
dataLengthS = Stream()
|
||||
dataLengthS.writeType(UInt32Le(len(data)))
|
||||
dataLengthS.write_type(UInt32Le(len(data)))
|
||||
|
||||
encryptionCountS = Stream()
|
||||
encryptionCountS.writeType(UInt32Le(encryptionCount))
|
||||
encryptionCountS.write_type(UInt32Le(encryptionCount))
|
||||
|
||||
sha1Digest.update(macSaltKey)
|
||||
sha1Digest.update("\x36" * 40)
|
||||
@@ -309,8 +310,8 @@ class ClientSecurityExchangePDU(CompositeType):
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.length = UInt32Le(lambda:(sizeof(self) - 4))
|
||||
self.encryptedClientRandom = String(readLen = CallableValue(lambda:(self.length.value - 8)))
|
||||
self.padding = String("\x00" * 8, readLen = CallableValue(8))
|
||||
self.encryptedClientRandom = Buffer(readLen = CallableValue(lambda:(self.length.value - 8)))
|
||||
self.padding = Buffer("\x00" * 8, readLen = CallableValue(8))
|
||||
|
||||
class RDPInfo(CompositeType):
|
||||
"""
|
||||
@@ -330,13 +331,13 @@ class RDPInfo(CompositeType):
|
||||
self.cbAlternateShell = UInt16Le(lambda:sizeof(self.alternateShell) - 2)
|
||||
self.cbWorkingDir = UInt16Le(lambda:sizeof(self.workingDir) - 2)
|
||||
#microsoft domain
|
||||
self.domain = String(readLen = CallableValue(lambda:self.cbDomain.value + 2), unicode = True)
|
||||
self.userName = String(readLen = CallableValue(lambda:self.cbUserName.value + 2), unicode = True)
|
||||
self.password = String(readLen = CallableValue(lambda:self.cbPassword.value + 2), unicode = True)
|
||||
self.domain = Buffer(readLen = CallableValue(lambda: self.cbDomain.value + 2), unicode = True)
|
||||
self.userName = Buffer(readLen = CallableValue(lambda: self.cbUserName.value + 2), unicode = True)
|
||||
self.password = Buffer(readLen = CallableValue(lambda: self.cbPassword.value + 2), unicode = True)
|
||||
#shell execute at start of session
|
||||
self.alternateShell = String(readLen = CallableValue(lambda:self.cbAlternateShell.value + 2), unicode = True)
|
||||
self.alternateShell = Buffer(readLen = CallableValue(lambda: self.cbAlternateShell.value + 2), unicode = True)
|
||||
#working directory for session
|
||||
self.workingDir = String(readLen = CallableValue(lambda:self.cbWorkingDir.value + 2), unicode = True)
|
||||
self.workingDir = Buffer(readLen = CallableValue(lambda: self.cbWorkingDir.value + 2), unicode = True)
|
||||
self.extendedInfo = RDPExtendedInfo(conditional = extendedInfoConditional)
|
||||
|
||||
class RDPExtendedInfo(CompositeType):
|
||||
@@ -347,11 +348,11 @@ class RDPExtendedInfo(CompositeType):
|
||||
CompositeType.__init__(self, conditional = conditional)
|
||||
self.clientAddressFamily = UInt16Le(AfInet.AF_INET)
|
||||
self.cbClientAddress = UInt16Le(lambda:sizeof(self.clientAddress))
|
||||
self.clientAddress = String(readLen = self.cbClientAddress, unicode = True)
|
||||
self.clientAddress = Buffer(readLen = self.cbClientAddress, unicode = True)
|
||||
self.cbClientDir = UInt16Le(lambda:sizeof(self.clientDir))
|
||||
self.clientDir = String(readLen = self.cbClientDir, unicode = True)
|
||||
self.clientDir = Buffer(readLen = self.cbClientDir, unicode = True)
|
||||
#TODO make tiomezone
|
||||
self.clientTimeZone = String("\x00" * 172)
|
||||
self.clientTimeZone = Buffer("\x00" * 172)
|
||||
self.clientSessionId = UInt32Le()
|
||||
self.performanceFlags = UInt32Le()
|
||||
|
||||
@@ -409,9 +410,9 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
self._decryptRc4 = rc4.RC4Key(self._currentDecrytKey)
|
||||
self._nbDecryptedPacket = 0
|
||||
|
||||
signature = String(readLen = CallableValue(8))
|
||||
encryptedPayload = String()
|
||||
s.readType((signature, encryptedPayload))
|
||||
signature = Buffer(readLen = CallableValue(8))
|
||||
encryptedPayload = Buffer()
|
||||
s.read_type((signature, encryptedPayload))
|
||||
decrypted = rc4.crypt(self._decryptRc4, encryptedPayload.value)
|
||||
|
||||
#ckeck signature
|
||||
@@ -443,12 +444,12 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
self._nbEncryptedPacket += 1
|
||||
|
||||
s = Stream()
|
||||
s.writeType(data)
|
||||
s.write_type(data)
|
||||
|
||||
if saltedMacGeneration:
|
||||
return (String(macSaltedData(self._macKey, s.getvalue(), self._nbEncryptedPacket - 1)[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
||||
return (Buffer(macSaltedData(self._macKey, s.getvalue(), self._nbEncryptedPacket - 1)[:8]), Buffer(rc4.crypt(self._encryptRc4, s.getvalue())))
|
||||
else:
|
||||
return (String(macData(self._macKey, s.getvalue())[:8]), String(rc4.crypt(self._encryptRc4, s.getvalue())))
|
||||
return (Buffer(macData(self._macKey, s.getvalue())[:8]), Buffer(rc4.crypt(self._encryptRc4, s.getvalue())))
|
||||
|
||||
def recv(self, data):
|
||||
"""
|
||||
@@ -462,7 +463,7 @@ class SecLayer(LayerAutomata, IStreamSender, tpkt.IFastPathListener, tpkt.IFastP
|
||||
|
||||
securityFlag = UInt16Le()
|
||||
securityFlagHi = UInt16Le()
|
||||
data.readType((securityFlag, securityFlagHi))
|
||||
data.read_type((securityFlag, securityFlagHi))
|
||||
|
||||
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
||||
data = self.readEncryptedPayload(data, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
||||
@@ -630,7 +631,7 @@ class Client(SecLayer):
|
||||
#packet preambule
|
||||
securityFlag = UInt16Le()
|
||||
securityFlagHi = UInt16Le()
|
||||
s.readType((securityFlag, securityFlagHi))
|
||||
s.read_type((securityFlag, securityFlagHi))
|
||||
|
||||
if not (securityFlag.value & SecurityFlag.SEC_LICENSE_PKT):
|
||||
raise InvalidExpectedDataException("waiting license packet")
|
||||
@@ -679,13 +680,13 @@ class Server(SecLayer):
|
||||
#packet preambule
|
||||
securityFlag = UInt16Le()
|
||||
securityFlagHi = UInt16Le()
|
||||
s.readType((securityFlag, securityFlagHi))
|
||||
s.read_type((securityFlag, securityFlagHi))
|
||||
|
||||
if not (securityFlag.value & SecurityFlag.SEC_EXCHANGE_PKT):
|
||||
raise InvalidExpectedDataException("waiting client random")
|
||||
|
||||
message = ClientSecurityExchangePDU()
|
||||
s.readType(message)
|
||||
s.read_type(message)
|
||||
clientRandom = rsa.decrypt(message.encryptedClientRandom.value[::-1], self._rsaPrivateKey)[::-1]
|
||||
|
||||
self._macKey, self._initialEncryptKey, self._initialDecrytKey = generateKeys( clientRandom,
|
||||
@@ -710,7 +711,7 @@ class Server(SecLayer):
|
||||
"""
|
||||
securityFlag = UInt16Le()
|
||||
securityFlagHi = UInt16Le()
|
||||
s.readType((securityFlag, securityFlagHi))
|
||||
s.read_type((securityFlag, securityFlagHi))
|
||||
|
||||
if not (securityFlag.value & SecurityFlag.SEC_INFO_PKT):
|
||||
raise InvalidExpectedDataException("Waiting info packet")
|
||||
@@ -718,7 +719,7 @@ class Server(SecLayer):
|
||||
if securityFlag.value & SecurityFlag.SEC_ENCRYPT:
|
||||
s = self.readEncryptedPayload(s, securityFlag.value & SecurityFlag.SEC_SECURE_CHECKSUM)
|
||||
|
||||
s.readType(self._info)
|
||||
s.read_type(self._info)
|
||||
#next state send error license
|
||||
self.sendLicensingErrorMessage()
|
||||
#reinit state
|
||||
@@ -22,22 +22,25 @@ Basic Encoding Rules use in RDP.
|
||||
ASN.1 standard
|
||||
"""
|
||||
|
||||
from rdpy.core.type import UInt8, UInt16Be, UInt32Be, String
|
||||
from rdpy.core.error import InvalidExpectedDataException, InvalidSize
|
||||
from rdpy.model.message import UInt8, UInt16Be, UInt32Be, Buffer
|
||||
from rdpy.model.error import InvalidExpectedDataException, InvalidSize
|
||||
|
||||
class BerPc(object):
|
||||
|
||||
class BerPc:
|
||||
BER_PC_MASK = 0x20
|
||||
BER_PRIMITIVE = 0x00
|
||||
BER_CONSTRUCT = 0x20
|
||||
|
||||
class Class(object):
|
||||
|
||||
class Class:
|
||||
BER_CLASS_MASK = 0xC0
|
||||
BER_CLASS_UNIV = 0x00
|
||||
BER_CLASS_APPL = 0x40
|
||||
BER_CLASS_CTXT = 0x80
|
||||
BER_CLASS_PRIV = 0xC0
|
||||
|
||||
class Tag(object):
|
||||
|
||||
|
||||
class Tag:
|
||||
BER_TAG_MASK = 0x1F
|
||||
BER_TAG_BOOLEAN = 0x01
|
||||
BER_TAG_INTEGER = 0x02
|
||||
@@ -48,6 +51,7 @@ class Tag(object):
|
||||
BER_TAG_SEQUENCE = 0x10
|
||||
BER_TAG_SEQUENCE_OF = 0x10
|
||||
|
||||
|
||||
def berPC(pc):
|
||||
"""
|
||||
@summary: Return BER_CONSTRUCT if true
|
||||
@@ -59,7 +63,8 @@ def berPC(pc):
|
||||
return BerPc.BER_CONSTRUCT
|
||||
else:
|
||||
return BerPc.BER_PRIMITIVE
|
||||
|
||||
|
||||
|
||||
def readLength(s):
|
||||
"""
|
||||
@summary: Read length of BER structure
|
||||
@@ -69,7 +74,7 @@ def readLength(s):
|
||||
"""
|
||||
size = None
|
||||
length = UInt8()
|
||||
s.readType(length)
|
||||
s.read_type(length)
|
||||
byte = length.value
|
||||
if byte & 0x80:
|
||||
byte &= ~0x80
|
||||
@@ -79,11 +84,12 @@ def readLength(s):
|
||||
size = UInt16Be()
|
||||
else:
|
||||
raise InvalidExpectedDataException("BER length may be 1 or 2")
|
||||
s.readType(size)
|
||||
s.read_type(size)
|
||||
else:
|
||||
size = length
|
||||
return size.value
|
||||
|
||||
|
||||
def writeLength(size):
|
||||
"""
|
||||
@summary: Return structure length as expected in BER specification
|
||||
@@ -94,7 +100,8 @@ def writeLength(size):
|
||||
return (UInt8(0x82), UInt16Be(size))
|
||||
else:
|
||||
return UInt8(size)
|
||||
|
||||
|
||||
|
||||
def readUniversalTag(s, tag, pc):
|
||||
"""
|
||||
@summary: Read tag of BER packet
|
||||
@@ -103,9 +110,10 @@ def readUniversalTag(s, tag, pc):
|
||||
@return: true if tag is correctly read
|
||||
"""
|
||||
byte = UInt8()
|
||||
s.readType(byte)
|
||||
s.read_type(byte)
|
||||
return byte.value == ((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag))
|
||||
|
||||
|
||||
def writeUniversalTag(tag, pc):
|
||||
"""
|
||||
@summary: Return universal tag byte
|
||||
@@ -115,6 +123,7 @@ def writeUniversalTag(tag, pc):
|
||||
"""
|
||||
return UInt8((Class.BER_CLASS_UNIV | berPC(pc)) | (Tag.BER_TAG_MASK & tag))
|
||||
|
||||
|
||||
def readApplicationTag(s, tag):
|
||||
"""
|
||||
@summary: Read application tag
|
||||
@@ -123,11 +132,11 @@ def readApplicationTag(s, tag):
|
||||
@return: length of application packet
|
||||
"""
|
||||
byte = UInt8()
|
||||
s.readType(byte)
|
||||
s.read_type(byte)
|
||||
if tag.value > 30:
|
||||
if byte.value != ((Class.BER_CLASS_APPL | BerPc.BER_CONSTRUCT) | Tag.BER_TAG_MASK):
|
||||
raise InvalidExpectedDataException()
|
||||
s.readType(byte)
|
||||
s.read_type(byte)
|
||||
if byte.value != tag.value:
|
||||
raise InvalidExpectedDataException("bad tag")
|
||||
else:
|
||||
@@ -136,6 +145,7 @@ def readApplicationTag(s, tag):
|
||||
|
||||
return readLength(s)
|
||||
|
||||
|
||||
def writeApplicationTag(tag, size):
|
||||
"""
|
||||
@summary: Return structure that represent BER application tag
|
||||
@@ -159,7 +169,7 @@ def readBoolean(s):
|
||||
if size != 1:
|
||||
raise InvalidExpectedDataException("bad boolean size")
|
||||
b = UInt8()
|
||||
s.readType(b)
|
||||
s.read_type(b)
|
||||
return bool(b.value)
|
||||
|
||||
def writeBoolean(b):
|
||||
@@ -186,21 +196,21 @@ def readInteger(s):
|
||||
|
||||
if size == 1:
|
||||
integer = UInt8()
|
||||
s.readType(integer)
|
||||
s.read_type(integer)
|
||||
return integer.value
|
||||
elif size == 2:
|
||||
integer = UInt16Be()
|
||||
s.readType(integer)
|
||||
s.read_type(integer)
|
||||
return integer.value
|
||||
elif size == 3:
|
||||
integer1 = UInt8()
|
||||
integer2 = UInt16Be()
|
||||
s.readType(integer1)
|
||||
s.readType(integer2)
|
||||
s.read_type(integer1)
|
||||
s.read_type(integer2)
|
||||
return integer2.value + (integer1.value << 16)
|
||||
elif size == 4:
|
||||
integer = UInt32Be()
|
||||
s.readType(integer)
|
||||
s.read_type(integer)
|
||||
return integer.value
|
||||
else:
|
||||
raise InvalidExpectedDataException("Wrong integer size")
|
||||
@@ -235,7 +245,7 @@ def writeOctetstring(value):
|
||||
@param value: string
|
||||
@return: BER octet string block
|
||||
"""
|
||||
return (writeUniversalTag(Tag.BER_TAG_OCTET_STRING, False), writeLength(len(value)), String(value))
|
||||
return (writeUniversalTag(Tag.BER_TAG_OCTET_STRING, False), writeLength(len(value)), Buffer(value))
|
||||
|
||||
def readEnumerated(s):
|
||||
"""
|
||||
@@ -248,7 +258,7 @@ def readEnumerated(s):
|
||||
if readLength(s) != 1:
|
||||
raise InvalidSize("enumerate size is wrong")
|
||||
enumer = UInt8()
|
||||
s.readType(enumer)
|
||||
s.read_type(enumer)
|
||||
return enumer.value
|
||||
|
||||
def writeEnumerated(enumerated):
|
||||
@@ -22,18 +22,18 @@ Implement GCC structure use in RDP protocol
|
||||
http://msdn.microsoft.com/en-us/library/cc240508.aspx
|
||||
"""
|
||||
|
||||
import md5
|
||||
from rdpy.core.type import UInt8, UInt16Le, UInt32Le, CompositeType, CallableValue, String, Stream, sizeof, FactoryType, ArrayType
|
||||
import per, mcs
|
||||
from rdpy.core.error import InvalidExpectedDataException
|
||||
from rdpy.core import log
|
||||
from hashlib import md5
|
||||
from rdpy.model.message import UInt8, UInt16Le, UInt32Le, CompositeType, Buffer, Stream, sizeof, FactoryType, ArrayType
|
||||
from rdpy.core.t125 import per, mcs
|
||||
from rdpy.model.error import InvalidExpectedDataException
|
||||
from rdpy.model import log
|
||||
from rdpy.security import x509
|
||||
import rdpy.security.rsa_wrapper as rsa
|
||||
|
||||
t124_02_98_oid = ( 0, 0, 20, 124, 0, 1 )
|
||||
|
||||
h221_cs_key = "Duca";
|
||||
h221_sc_key = "McDn";
|
||||
h221_cs_key = b"Duca";
|
||||
h221_sc_key = b"McDn";
|
||||
|
||||
class MessageType(object):
|
||||
"""
|
||||
@@ -208,43 +208,47 @@ class CertificateType(object):
|
||||
"""
|
||||
CERT_CHAIN_VERSION_1 = 0x00000001
|
||||
CERT_CHAIN_VERSION_2 = 0x00000002
|
||||
|
||||
|
||||
|
||||
class DataBlock(CompositeType):
|
||||
"""
|
||||
@summary: Block settings
|
||||
"""
|
||||
def __init__(self, dataBlock = None):
|
||||
CompositeType.__init__(self)
|
||||
self.type = UInt16Le(lambda:self.dataBlock.__class__._TYPE_)
|
||||
self.length = UInt16Le(lambda:sizeof(self))
|
||||
def __init__(self, data_block=None):
|
||||
super().__init__()
|
||||
self.type = UInt16Le(lambda:data_block._TYPE_)
|
||||
self.length = UInt16Le(lambda: sizeof(self))
|
||||
|
||||
def DataBlockFactory():
|
||||
def factory():
|
||||
"""
|
||||
@summary: build settings in accordance of type self.type.value
|
||||
"""
|
||||
for c in [ClientCoreData, ClientSecurityData, ClientNetworkData, ServerCoreData, ServerNetworkData, ServerSecurityData]:
|
||||
gcc_type = [
|
||||
ClientCoreData, ClientSecurityData, ClientNetworkData,
|
||||
ServerCoreData, ServerNetworkData, ServerSecurityData
|
||||
]
|
||||
|
||||
for c in gcc_type:
|
||||
if self.type.value == c._TYPE_:
|
||||
return c(readLen = self.length - 4)
|
||||
return c(read_len=lambda: (self.length.value - 4))
|
||||
log.debug("unknown GCC block type : %s"%hex(self.type.value))
|
||||
#read entire packet
|
||||
return String(readLen = self.length - 4)
|
||||
# read entire packet
|
||||
return Buffer(read_len=lambda: (self.length.value - 4))
|
||||
|
||||
if dataBlock is None:
|
||||
dataBlock = FactoryType(DataBlockFactory)
|
||||
elif not "_TYPE_" in dataBlock.__class__.__dict__:
|
||||
if data_block is None:
|
||||
data_block = FactoryType(factory)
|
||||
elif "_TYPE_" not in data_block.__class__.__dict__:
|
||||
raise InvalidExpectedDataException("Try to send an invalid GCC blocks")
|
||||
|
||||
self.dataBlock = dataBlock
|
||||
self.dataBlock = data_block
|
||||
|
||||
|
||||
class ClientCoreData(CompositeType):
|
||||
"""
|
||||
@summary: Class that represent core setting of client
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240510.aspx
|
||||
"""
|
||||
_TYPE_ = MessageType.CS_CORE
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self, read_len=None):
|
||||
super().__init__(read_len=read_len)
|
||||
self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS)
|
||||
self.desktopWidth = UInt16Le(1280)
|
||||
self.desktopHeight = UInt16Le(800)
|
||||
@@ -252,70 +256,68 @@ class ClientCoreData(CompositeType):
|
||||
self.sasSequence = UInt16Le(Sequence.RNS_UD_SAS_DEL)
|
||||
self.kbdLayout = UInt32Le(KeyboardLayout.US)
|
||||
self.clientBuild = UInt32Le(3790)
|
||||
self.clientName = String("rdpy" + "\x00"*11, readLen = CallableValue(32), unicode = True)
|
||||
self.clientName = Buffer(("rdpy" + "\x00" * 12).encode("utf-16le"), read_len=lambda: 32)
|
||||
self.keyboardType = UInt32Le(KeyboardType.IBM_101_102_KEYS)
|
||||
self.keyboardSubType = UInt32Le(0)
|
||||
self.keyboardFnKeys = UInt32Le(12)
|
||||
self.imeFileName = String("\x00"*64, readLen = CallableValue(64), optional = True)
|
||||
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, optional = True)
|
||||
self.clientProductId = UInt16Le(1, optional = True)
|
||||
self.serialNumber = UInt32Le(0, optional = True)
|
||||
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP, optional = True)
|
||||
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT, optional = True)
|
||||
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU, optional = True)
|
||||
self.clientDigProductId = String("\x00"*64, readLen = CallableValue(64), optional = True)
|
||||
self.connectionType = UInt8(optional = True)
|
||||
self.pad1octet = UInt8(optional = True)
|
||||
self.serverSelectedProtocol = UInt32Le(optional = True)
|
||||
|
||||
self.imeFileName = Buffer(b"\x00" * 64, read_len=lambda: 64, optional=True)
|
||||
self.postBeta2ColorDepth = UInt16Le(ColorDepth.RNS_UD_COLOR_8BPP, optional=True)
|
||||
self.clientProductId = UInt16Le(1, optional=True)
|
||||
self.serialNumber = UInt32Le(0, optional=True)
|
||||
self.highColorDepth = UInt16Le(HighColor.HIGH_COLOR_24BPP, optional=True)
|
||||
self.supportedColorDepths = UInt16Le(Support.RNS_UD_15BPP_SUPPORT | Support.RNS_UD_16BPP_SUPPORT | Support.RNS_UD_24BPP_SUPPORT | Support.RNS_UD_32BPP_SUPPORT, optional=True)
|
||||
self.earlyCapabilityFlags = UInt16Le(CapabilityFlags.RNS_UD_CS_SUPPORT_ERRINFO_PDU, optional=True)
|
||||
self.clientDigProductId = Buffer(b"\x00" * 64, read_len=lambda: 64, optional=True)
|
||||
self.connectionType = UInt8(optional=True)
|
||||
self.pad1octet = UInt8(optional=True)
|
||||
self.serverSelectedProtocol = UInt32Le(optional=True)
|
||||
|
||||
|
||||
class ServerCoreData(CompositeType):
|
||||
"""
|
||||
@summary: Server side core settings structure
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240517.aspx
|
||||
"""
|
||||
_TYPE_ = MessageType.SC_CORE
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self, read_len=None):
|
||||
super().__init__(read_len=read_len)
|
||||
self.rdpVersion = UInt32Le(Version.RDP_VERSION_5_PLUS)
|
||||
self.clientRequestedProtocol = UInt32Le(optional = True)
|
||||
self.earlyCapabilityFlags = UInt32Le(optional = True)
|
||||
|
||||
self.clientRequestedProtocol = UInt32Le(optional=True)
|
||||
self.earlyCapabilityFlags = UInt32Le(optional=True)
|
||||
|
||||
|
||||
class ClientSecurityData(CompositeType):
|
||||
"""
|
||||
@summary: Client security setting
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240511.aspx
|
||||
"""
|
||||
_TYPE_ = MessageType.CS_SECURITY
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self, read_len=None):
|
||||
super().__init__(read_len=read_len)
|
||||
self.encryptionMethods = UInt32Le(EncryptionMethod.ENCRYPTION_FLAG_40BIT | EncryptionMethod.ENCRYPTION_FLAG_56BIT | EncryptionMethod.ENCRYPTION_FLAG_128BIT)
|
||||
self.extEncryptionMethods = UInt32Le()
|
||||
|
||||
|
||||
|
||||
class ServerSecurityData(CompositeType):
|
||||
"""
|
||||
@summary: Server security settings
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240518.aspx
|
||||
"""
|
||||
_TYPE_ = MessageType.SC_SECURITY
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self, read_len=None):
|
||||
super().__init__(read_len=read_len)
|
||||
self.encryptionMethod = UInt32Le()
|
||||
self.encryptionLevel = UInt32Le()
|
||||
self.serverRandomLen = UInt32Le(0x00000020, constant = True, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
|
||||
self.serverCertLen = UInt32Le(lambda:sizeof(self.serverCertificate), conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
|
||||
self.serverRandom = String(readLen = self.serverRandomLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
|
||||
self.serverCertificate = ServerCertificate(readLen = self.serverCertLen, conditional = lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel == 0))
|
||||
self.serverRandomLen = UInt32Le(0x00000020, constant=True, conditional=lambda: not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
|
||||
self.serverCertLen = UInt32Le(lambda: sizeof(self.serverCertificate), conditional=lambda:not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
|
||||
self.serverRandom = Buffer(read_len=lambda: self.serverRandomLen.value, conditional=lambda: not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
|
||||
self.serverCertificate = ServerCertificate(read_len=lambda: self.serverCertLen.value, conditional=lambda: not(self.encryptionMethod.value == 0 and self.encryptionLevel.value == 0))
|
||||
|
||||
|
||||
class ServerCertificate(CompositeType):
|
||||
"""
|
||||
@summary: Server certificate structure
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240521.aspx
|
||||
"""
|
||||
def __init__(self, certData = None, readLen = None, conditional = lambda:True):
|
||||
CompositeType.__init__(self, readLen = readLen, conditional = conditional)
|
||||
def __init__(self, certData = None, read_len = None, conditional = lambda:True):
|
||||
CompositeType.__init__(self, read_len=read_len, conditional = conditional)
|
||||
self.dwVersion = UInt32Le(lambda:(self.certData.__class__._TYPE_))
|
||||
|
||||
def CertificateFactory():
|
||||
@@ -355,8 +357,8 @@ class ProprietaryServerCertificate(CompositeType):
|
||||
self.PublicKeyBlob = RSAPublicKey(readLen = self.wPublicKeyBlobLen)
|
||||
self.wSignatureBlobType = UInt16Le(0x0008, constant = True)
|
||||
self.wSignatureBlobLen = UInt16Le(lambda:(sizeof(self.SignatureBlob) + sizeof(self.padding)))
|
||||
self.SignatureBlob = String(readLen = CallableValue(lambda:(self.wSignatureBlobLen.value - sizeof(self.padding))))
|
||||
self.padding = String(b"\x00" * 8, readLen = CallableValue(8))
|
||||
self.SignatureBlob = Buffer(readLen = CallableValue(lambda:(self.wSignatureBlobLen.value - sizeof(self.padding))))
|
||||
self.padding = Buffer(b"\x00" * 8, readLen = CallableValue(8))
|
||||
|
||||
def getPublicKey(self):
|
||||
"""
|
||||
@@ -371,12 +373,12 @@ class ProprietaryServerCertificate(CompositeType):
|
||||
@summary: compute hash
|
||||
"""
|
||||
s = Stream()
|
||||
s.writeType(UInt32Le(self.__class__._TYPE_))
|
||||
s.writeType(self.dwSigAlgId)
|
||||
s.writeType(self.dwKeyAlgId)
|
||||
s.writeType(self.wPublicKeyBlobType)
|
||||
s.writeType(self.wPublicKeyBlobLen)
|
||||
s.writeType(self.PublicKeyBlob)
|
||||
s.write_type(UInt32Le(self.__class__._TYPE_))
|
||||
s.write_type(self.dwSigAlgId)
|
||||
s.write_type(self.dwKeyAlgId)
|
||||
s.write_type(self.wPublicKeyBlobType)
|
||||
s.write_type(self.wPublicKeyBlobLen)
|
||||
s.write_type(self.PublicKeyBlob)
|
||||
|
||||
md5Digest = md5.new()
|
||||
md5Digest.update(s.getvalue())
|
||||
@@ -405,7 +407,7 @@ class CertBlob(CompositeType):
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.cbCert = UInt32Le(lambda:sizeof(self.abCert))
|
||||
self.abCert = String(readLen = self.cbCert)
|
||||
self.abCert = Buffer(readLen = self.cbCert)
|
||||
|
||||
class X509CertificateChain(CompositeType):
|
||||
"""
|
||||
@@ -418,7 +420,7 @@ class X509CertificateChain(CompositeType):
|
||||
CompositeType.__init__(self)
|
||||
self.NumCertBlobs = UInt32Le()
|
||||
self.CertBlobArray = ArrayType(CertBlob, readLen = self.NumCertBlobs)
|
||||
self.padding = String(readLen = CallableValue(lambda:(8 + 4 * self.NumCertBlobs.value)))
|
||||
self.padding = Buffer(readLen = CallableValue(lambda:(8 + 4 * self.NumCertBlobs.value)))
|
||||
|
||||
def getPublicKey(self):
|
||||
"""
|
||||
@@ -447,80 +449,63 @@ class RSAPublicKey(CompositeType):
|
||||
self.bitlen = UInt32Le(lambda:((self.keylen.value - 8) * 8))
|
||||
self.datalen = UInt32Le(lambda:((self.bitlen.value / 8) - 1))
|
||||
self.pubExp = UInt32Le()
|
||||
self.modulus = String(readLen = CallableValue(lambda:(self.keylen.value - 8)))
|
||||
self.padding = String("\x00" * 8, readLen = CallableValue(8))
|
||||
self.modulus = Buffer(readLen = CallableValue(lambda:(self.keylen.value - 8)))
|
||||
self.padding = Buffer(b"\x00" * 8, readLen = CallableValue(8))
|
||||
|
||||
|
||||
class ChannelDef(CompositeType):
|
||||
"""
|
||||
Channels structure share between client and server
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240513.aspx
|
||||
"""
|
||||
def __init__(self, name = "", options = 0):
|
||||
CompositeType.__init__(self)
|
||||
#name of channel
|
||||
self.name = String(name[0:8] + "\x00" * (8 - len(name)), readLen = CallableValue(8))
|
||||
#unknown
|
||||
def __init__(self, name=b""):
|
||||
super().__init__()
|
||||
# name of channel
|
||||
self.name = Buffer(name[0:8] + b"\x00" * (8 - len(name)), read_len=lambda: 8)
|
||||
# unknown
|
||||
self.options = UInt32Le()
|
||||
|
||||
|
||||
|
||||
class ClientNetworkData(CompositeType):
|
||||
"""
|
||||
@summary: GCC client network block
|
||||
All channels asked by client are listed here
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240512.aspx
|
||||
"""
|
||||
_TYPE_ = MessageType.CS_NET
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.channelCount = UInt32Le(lambda:len(self.channelDefArray._array))
|
||||
self.channelDefArray = ArrayType(ChannelDef, readLen = self.channelCount)
|
||||
|
||||
def __init__(self, read_len=None):
|
||||
CompositeType.__init__(self, read_len=read_len)
|
||||
self.channelCount = UInt32Le(lambda: len(self.channelDefArray))
|
||||
self.channelDefArray = ArrayType(ChannelDef, read_len=lambda: self.channelCount.value)
|
||||
|
||||
|
||||
class ServerNetworkData(CompositeType):
|
||||
"""
|
||||
@summary: GCC server network block
|
||||
All channels asked by client are listed here
|
||||
@see: All channels asked by client are listed here
|
||||
"""
|
||||
_TYPE_ = MessageType.SC_NET
|
||||
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
def __init__(self, read_len=None):
|
||||
super().__init__(read_len=read_len)
|
||||
self.MCSChannelId = UInt16Le(mcs.Channel.MCS_GLOBAL_CHANNEL)
|
||||
self.channelCount = UInt16Le(lambda:len(self.channelIdArray._array))
|
||||
self.channelIdArray = ArrayType(UInt16Le, readLen = self.channelCount)
|
||||
self.pad = UInt16Le(conditional = lambda:((self.channelCount.value % 2) == 1))
|
||||
|
||||
self.channelCount = UInt16Le(lambda: len(self.channelIdArray))
|
||||
self.channelIdArray = ArrayType(UInt16Le, read_len=lambda: self.channelCount.value)
|
||||
self.pad = UInt16Le(conditional=lambda: ((self.channelCount.value % 2) == 1))
|
||||
|
||||
|
||||
class Settings(CompositeType):
|
||||
"""
|
||||
@summary: Class which group all clients settings supported by RDPY
|
||||
"""
|
||||
def __init__(self, init = [], readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.settings = ArrayType(DataBlock, [DataBlock(i) for i in init])
|
||||
def __init__(self, init=None, read_len=None):
|
||||
super().__init__(read_len=read_len)
|
||||
self.settings = ArrayType(DataBlock, [DataBlock(i) for i in init or []])
|
||||
|
||||
def getBlock(self, messageType):
|
||||
def get_block(self, message_type):
|
||||
"""
|
||||
@param messageType: type of block
|
||||
@return: specific block of type messageType
|
||||
"""
|
||||
for i in self.settings._array:
|
||||
if i.type.value == messageType:
|
||||
if i.type.value == message_type:
|
||||
return i.dataBlock
|
||||
return None
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
@summary: Magic function for better access
|
||||
@return: _value parameter
|
||||
"""
|
||||
if not name in MessageType.__dict__:
|
||||
return None
|
||||
return self.getBlock(MessageType.__dict__[name])
|
||||
|
||||
def clientSettings():
|
||||
|
||||
|
||||
def client_settings():
|
||||
"""
|
||||
@summary: Build settings for client
|
||||
@return: Settings
|
||||
"""
|
||||
return Settings([ClientCoreData(), ClientNetworkData(), ClientSecurityData()])
|
||||
|
||||
@@ -555,7 +540,7 @@ def readConferenceCreateRequest(s):
|
||||
per.readOctetStream(s, h221_cs_key, 4)
|
||||
length = per.readLength(s)
|
||||
clientSettings = Settings(readLen = CallableValue(length))
|
||||
s.readType(clientSettings)
|
||||
s.read_type(clientSettings)
|
||||
return clientSettings
|
||||
|
||||
def readConferenceCreateResponse(s):
|
||||
@@ -578,9 +563,9 @@ def readConferenceCreateResponse(s):
|
||||
raise InvalidExpectedDataException("cannot read h221_sc_key")
|
||||
|
||||
length = per.readLength(s)
|
||||
serverSettings = Settings(readLen = CallableValue(length))
|
||||
s.readType(serverSettings)
|
||||
return serverSettings
|
||||
server_settings = Settings(read_len=lambda: length)
|
||||
s.read_type(server_settings)
|
||||
return server_settings
|
||||
|
||||
def writeConferenceCreateRequest(userData):
|
||||
"""
|
||||
@@ -589,11 +574,11 @@ def writeConferenceCreateRequest(userData):
|
||||
@return: GCC packet
|
||||
"""
|
||||
userDataStream = Stream()
|
||||
userDataStream.writeType(userData)
|
||||
userDataStream.write_type(userData)
|
||||
|
||||
return (per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(len(userDataStream.getvalue()) + 14), per.writeChoice(0),
|
||||
per.writeSelection(0x08), per.writeNumericString("1", 1), per.writePadding(1),
|
||||
per.writeSelection(0x08), per.writeNumericString(b"1", 1), per.writePadding(1),
|
||||
per.writeNumberOfSet(1), per.writeChoice(0xc0),
|
||||
per.writeOctetStream(h221_cs_key, 4), per.writeOctetStream(userDataStream.getvalue()))
|
||||
|
||||
@@ -604,7 +589,7 @@ def writeConferenceCreateResponse(serverData):
|
||||
@return: gcc packet
|
||||
"""
|
||||
serverDataStream = Stream()
|
||||
serverDataStream.writeType(serverData)
|
||||
serverDataStream.write_type(serverData)
|
||||
|
||||
return (per.writeChoice(0), per.writeObjectIdentifier(t124_02_98_oid),
|
||||
per.writeLength(len(serverDataStream.getvalue()) + 14), per.writeChoice(0x14),
|
||||
@@ -24,25 +24,28 @@ Each channel have a particular role.
|
||||
The main channel is the graphical channel.
|
||||
It exist channel for file system order, audio channel, clipboard etc...
|
||||
"""
|
||||
from rdpy.core.layer import LayerAutomata, IStreamSender, Layer
|
||||
from rdpy.core.type import sizeof, Stream, UInt8, UInt16Le, String
|
||||
from rdpy.core.error import InvalidExpectedDataException, InvalidValue, InvalidSize, CallPureVirtualFuntion
|
||||
from ber import writeLength
|
||||
import rdpy.core.log as log
|
||||
from typing import Tuple
|
||||
|
||||
import ber, gcc, per
|
||||
from rdpy.core import x224
|
||||
from rdpy.model.layer import LayerAutomata, IStreamSender, Layer
|
||||
from rdpy.model.message import sizeof, Stream, UInt8, UInt16Le, Buffer
|
||||
from rdpy.model.error import InvalidExpectedDataException, InvalidValue, InvalidSize, CallPureVirtualFuntion
|
||||
from rdpy.core.t125.ber import writeLength
|
||||
import rdpy.model.log as log
|
||||
|
||||
from rdpy.core.t125 import ber, gcc, per
|
||||
import rdpy.security.rsa_wrapper as rsa
|
||||
|
||||
class Message(object):
|
||||
|
||||
class Message:
|
||||
"""
|
||||
@summary: Message type
|
||||
"""
|
||||
MCS_TYPE_CONNECT_INITIAL = 0x65
|
||||
MCS_TYPE_CONNECT_RESPONSE = 0x66
|
||||
|
||||
|
||||
class DomainMCSPDU:
|
||||
"""
|
||||
@summary: Domain MCS PDU header
|
||||
"""
|
||||
ERECT_DOMAIN_REQUEST = 1
|
||||
DISCONNECT_PROVIDER_ULTIMATUM = 8
|
||||
@@ -53,44 +56,182 @@ class DomainMCSPDU:
|
||||
SEND_DATA_REQUEST = 25
|
||||
SEND_DATA_INDICATION = 26
|
||||
|
||||
|
||||
class Channel:
|
||||
"""
|
||||
@summary: Channel id of main channels use in RDP
|
||||
"""
|
||||
MCS_GLOBAL_CHANNEL = 1003
|
||||
MCS_USERCHANNEL_BASE = 1001
|
||||
|
||||
|
||||
|
||||
class IGCCConfig(object):
|
||||
"""
|
||||
@summary: Channel information
|
||||
"""
|
||||
|
||||
def getUserId(self):
|
||||
"""
|
||||
@return: {integer} mcs user id
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getUserId", "IGCCConfig"))
|
||||
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s" % (self.__class__, "getUserId", "IGCCConfig"))
|
||||
|
||||
def getChannelId(self):
|
||||
"""
|
||||
@return: {integer} return channel id of proxy
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getChannelId", "IGCCConfig"))
|
||||
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s" % (self.__class__, "getChannelId", "IGCCConfig"))
|
||||
|
||||
def getGCCClientSettings(self):
|
||||
"""
|
||||
@return: {gcc.Settings} mcs layer gcc client settings
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getGCCClientSettings", "IGCCConfig"))
|
||||
|
||||
raise CallPureVirtualFuntion(
|
||||
"%s:%s defined by interface %s" % (self.__class__, "getGCCClientSettings", "IGCCConfig"))
|
||||
|
||||
def getGCCServerSettings(self):
|
||||
"""
|
||||
@return: {gcc.Settings} mcs layer gcc server settings
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "getGCCServerSettings", "IGCCConfig"))
|
||||
raise CallPureVirtualFuntion(
|
||||
"%s:%s defined by interface %s" % (self.__class__, "getGCCServerSettings", "IGCCConfig"))
|
||||
|
||||
|
||||
def write_domain_params(max_channels: int, max_users: int, max_tokens: int, max_pdu_size: int) -> tuple:
|
||||
"""
|
||||
"""
|
||||
domain_param = (ber.writeInteger(max_channels), ber.writeInteger(max_users), ber.writeInteger(max_tokens),
|
||||
ber.writeInteger(1), ber.writeInteger(0), ber.writeInteger(1),
|
||||
ber.writeInteger(max_pdu_size), ber.writeInteger(2))
|
||||
return ber.writeUniversalTag(ber.Tag.BER_TAG_SEQUENCE, True), writeLength(sizeof(domain_param)), domain_param
|
||||
|
||||
|
||||
def read_domain_params(stream: Stream) -> Tuple[int, int, int, int]:
|
||||
"""
|
||||
"""
|
||||
if not ber.readUniversalTag(stream, ber.Tag.BER_TAG_SEQUENCE, True):
|
||||
raise InvalidValue("bad BER tags")
|
||||
ber.readLength(stream) # length
|
||||
max_channels = ber.readInteger(stream)
|
||||
max_users = ber.readInteger(stream)
|
||||
max_tokens = ber.readInteger(stream)
|
||||
ber.readInteger(stream)
|
||||
ber.readInteger(stream)
|
||||
ber.readInteger(stream)
|
||||
max_pdu_size = ber.readInteger(stream)
|
||||
ber.readInteger(stream)
|
||||
return max_channels, max_users, max_tokens, max_pdu_size
|
||||
|
||||
|
||||
def mcs_pdu_header(mcs_pdu: UInt8, options=0) -> UInt8:
|
||||
return (mcs_pdu << 2) | options
|
||||
|
||||
|
||||
def check_mcs_pdu_header(opcode: UInt8, mcs_pdu):
|
||||
return (opcode >> 2) == mcs_pdu
|
||||
|
||||
|
||||
def erect_domain_request() -> Tuple:
|
||||
return mcs_pdu_header(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)), per.writeInteger(0), per.writeInteger(0)
|
||||
|
||||
|
||||
def read_attach_confirm(data: Stream) -> int:
|
||||
"""
|
||||
"""
|
||||
opcode = data.read_type(UInt8())
|
||||
|
||||
if not check_mcs_pdu_header(opcode, DomainMCSPDU.ATTACH_USER_CONFIRM):
|
||||
raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_CONFIRM expected")
|
||||
|
||||
if per.readEnumerates(data) != 0:
|
||||
raise InvalidExpectedDataException("Server reject user")
|
||||
|
||||
return per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
|
||||
|
||||
|
||||
def attach_user_request() -> UInt8:
|
||||
return mcs_pdu_header(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST))
|
||||
|
||||
|
||||
def channel_join_request(user_id: int, channel_id: int):
|
||||
return (mcs_pdu_header(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)),
|
||||
per.writeInteger16(user_id, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channel_id))
|
||||
|
||||
|
||||
def channel_join_confirm(user_id: int, channel_id: int, data: Stream) -> bool:
|
||||
"""
|
||||
"""
|
||||
opcode = data.read_type(UInt8())
|
||||
|
||||
if not check_mcs_pdu_header(opcode.value, DomainMCSPDU.CHANNEL_JOIN_CONFIRM):
|
||||
raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_CONFIRM expected")
|
||||
|
||||
confirm = per.readEnumerates(data)
|
||||
|
||||
if user_id != per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE):
|
||||
raise InvalidExpectedDataException("Invalid MCS User Id")
|
||||
|
||||
if channel_id != per.readInteger16(data):
|
||||
raise InvalidExpectedDataException("Invalid MCS channel id")
|
||||
|
||||
return confirm == 0
|
||||
|
||||
|
||||
class Client:
|
||||
def __init__(self, x224_layer: x224.X224):
|
||||
self.x224 = x224_layer
|
||||
self.channel_ids = {}
|
||||
|
||||
async def write_connect_initial(self):
|
||||
"""
|
||||
"""
|
||||
settings = gcc.client_settings()
|
||||
settings.get_block(gcc.MessageType.CS_CORE).serverSelectedProtocol.value = self.x224.get_selected_protocol()
|
||||
|
||||
cc_req = gcc.writeConferenceCreateRequest(settings)
|
||||
cc_req_stream = Stream()
|
||||
cc_req_stream.write_type(cc_req)
|
||||
|
||||
tmp = (ber.writeOctetstring(b"\x01"), ber.writeOctetstring(b"\x01"), ber.writeBoolean(True),
|
||||
write_domain_params(34, 2, 0, 0xffff),
|
||||
write_domain_params(1, 1, 1, 0x420),
|
||||
write_domain_params(0xffff, 0xfc17, 0xffff, 0xffff),
|
||||
ber.writeOctetstring(cc_req_stream.getvalue()))
|
||||
await self.x224.write((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp))
|
||||
|
||||
async def read_connect_response(self):
|
||||
payload = await self.x224.read()
|
||||
ber.readApplicationTag(payload, UInt8(Message.MCS_TYPE_CONNECT_RESPONSE))
|
||||
ber.readEnumerated(payload)
|
||||
ber.readInteger(payload)
|
||||
read_domain_params(payload)
|
||||
if not ber.readUniversalTag(payload, ber.Tag.BER_TAG_OCTET_STRING, False):
|
||||
raise InvalidExpectedDataException("invalid expected BER tag")
|
||||
gccRequestLength = ber.readLength(payload)
|
||||
if payload.data_len() != gccRequestLength:
|
||||
raise InvalidSize("bad size of GCC request")
|
||||
gcc.readConferenceCreateResponse(payload)
|
||||
|
||||
|
||||
async def connect(self):
|
||||
await self.write_connect_initial()
|
||||
await self.read_connect_response()
|
||||
await self.x224.write(erect_domain_request())
|
||||
await self.x224.write(attach_user_request())
|
||||
user_id = read_attach_confirm(await self.x224.read())
|
||||
self.channel_ids["global"] = 1003
|
||||
self.channel_ids["user"] = user_id
|
||||
|
||||
# connect all channels
|
||||
for channel_id in self.channel_ids.values():
|
||||
await self.x224.write(channel_join_request(user_id, channel_id))
|
||||
if not channel_join_confirm(user_id, channel_id, await self.x224.read()):
|
||||
print("Server refused channel %s"%channel_id)
|
||||
|
||||
|
||||
class MCSLayer(LayerAutomata):
|
||||
"""
|
||||
@@ -98,11 +239,13 @@ class MCSLayer(LayerAutomata):
|
||||
the main layer of RDP protocol
|
||||
is why he can do everything and more!
|
||||
"""
|
||||
|
||||
class MCSProxySender(Layer, IStreamSender, IGCCConfig):
|
||||
"""
|
||||
@summary: Proxy use to set as transport layer for upper channel
|
||||
use to abstract channel id for presentation layer
|
||||
"""
|
||||
|
||||
def __init__(self, presentation, mcs, channelId):
|
||||
"""
|
||||
@param presentation: {Layer} presentation layer
|
||||
@@ -112,7 +255,7 @@ class MCSLayer(LayerAutomata):
|
||||
Layer.__init__(self, presentation)
|
||||
self._mcs = mcs
|
||||
self._channelId = channelId
|
||||
|
||||
|
||||
def send(self, data):
|
||||
"""
|
||||
@summary: A send proxy function, use channel id and specific
|
||||
@@ -120,43 +263,42 @@ class MCSLayer(LayerAutomata):
|
||||
@param data: {type.Type | Tuple}
|
||||
"""
|
||||
self._mcs.send(self._channelId, data)
|
||||
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
@summary: Close wrapped layer
|
||||
"""
|
||||
self._mcs.close()
|
||||
|
||||
|
||||
def getUserId(self):
|
||||
"""
|
||||
@return: {integer} mcs user id
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
return self._mcs._userId
|
||||
|
||||
|
||||
def getChannelId(self):
|
||||
"""
|
||||
@return: {integer} return channel id of proxy
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
return self._channelId
|
||||
|
||||
|
||||
def getGCCClientSettings(self):
|
||||
"""
|
||||
@return: {gcc.Settings} mcs layer gcc client settings
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
return self._mcs._clientSettings
|
||||
|
||||
|
||||
def getGCCServerSettings(self):
|
||||
"""
|
||||
@return: {gcc.Settings} mcs layer gcc server settings
|
||||
@see: mcs.IGCCConfig
|
||||
"""
|
||||
return self._mcs._serverSettings
|
||||
|
||||
|
||||
def __init__(self, presentation, receiveOpcode, sendOpcode, virtualChannels = []):
|
||||
|
||||
def __init__(self, presentation, receiveOpcode, sendOpcode, virtualChannels=[]):
|
||||
"""
|
||||
@param presentation: {Layer} presentation layer
|
||||
@param virtualChannels: {Array(Layer]} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
|
||||
@@ -166,82 +308,82 @@ class MCSLayer(LayerAutomata):
|
||||
LayerAutomata.__init__(self, presentation)
|
||||
self._clientSettings = gcc.clientSettings()
|
||||
self._serverSettings = gcc.serverSettings()
|
||||
#default user Id
|
||||
# default user Id
|
||||
self._userId = 1 + Channel.MCS_USERCHANNEL_BASE
|
||||
#list of channel use in this layer and connection state
|
||||
# list of channel use in this layer and connection state
|
||||
self._channels = {Channel.MCS_GLOBAL_CHANNEL: presentation}
|
||||
#virtual channels
|
||||
# virtual channels
|
||||
self._virtualChannels = virtualChannels
|
||||
#send opcode
|
||||
# send opcode
|
||||
self._sendOpcode = sendOpcode
|
||||
#receive opcode
|
||||
# receive opcode
|
||||
self._receiveOpcode = receiveOpcode
|
||||
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
@summary: Send disconnect provider ultimatum
|
||||
"""
|
||||
self._transport.send((UInt8(self.writeMCSPDUHeader(DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM, 1)),
|
||||
per.writeEnumerates(0x80), String("\x00" * 6)))
|
||||
per.writeEnumerates(0x80), Buffer(b"\x00" * 6)))
|
||||
self._transport.close()
|
||||
|
||||
|
||||
def allChannelConnected(self):
|
||||
"""
|
||||
@summary: All channels are connected to MCS layer
|
||||
Send connect to upper channel
|
||||
And prepare MCS layer to receive data
|
||||
"""
|
||||
#connection is done
|
||||
# connection is done
|
||||
self.setNextState(self.recvData)
|
||||
#try connection on all requested channel
|
||||
# try connection on all requested channel
|
||||
for (channelId, layer) in self._channels.iteritems():
|
||||
#use proxy for each channel
|
||||
# use proxy for each channel
|
||||
MCSLayer.MCSProxySender(layer, self, channelId).connect()
|
||||
|
||||
|
||||
def send(self, channelId, data):
|
||||
"""
|
||||
@summary: Specific send function for channelId
|
||||
@param channelId: {integer} Channel use to send
|
||||
@param data: {type.type | tuple} message to send
|
||||
"""
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(self._sendOpcode)),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId),
|
||||
UInt8(0x70),
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(self._sendOpcode)),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId),
|
||||
UInt8(0x70),
|
||||
per.writeLength(sizeof(data)), data))
|
||||
|
||||
|
||||
def recvData(self, data):
|
||||
"""
|
||||
@summary: Main receive method
|
||||
@param data: {Stream}
|
||||
"""
|
||||
opcode = UInt8()
|
||||
data.readType(opcode)
|
||||
|
||||
data.read_type(opcode)
|
||||
|
||||
if self.readMCSPDUHeader(opcode.value, DomainMCSPDU.DISCONNECT_PROVIDER_ULTIMATUM):
|
||||
log.info("MCS DISCONNECT_PROVIDER_ULTIMATUM")
|
||||
self._transport.close()
|
||||
return
|
||||
|
||||
#client case
|
||||
|
||||
# client case
|
||||
elif not self.readMCSPDUHeader(opcode.value, self._receiveOpcode):
|
||||
raise InvalidExpectedDataException("Invalid expected MCS opcode receive data")
|
||||
|
||||
#server user id
|
||||
|
||||
# server user id
|
||||
per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
|
||||
|
||||
|
||||
channelId = per.readInteger16(data)
|
||||
|
||||
per.readEnumerates(data)
|
||||
|
||||
per.readEnumerates(data)
|
||||
per.readLength(data)
|
||||
|
||||
#channel id doesn't match a requested layer
|
||||
|
||||
# channel id doesn't match a requested layer
|
||||
if not self._channels.has_key(channelId):
|
||||
log.error("receive data for an unconnected layer")
|
||||
return
|
||||
|
||||
self._channels[channelId].recv(data)
|
||||
|
||||
self._channels[channelId].recv(data)
|
||||
|
||||
def writeDomainParams(self, maxChannels, maxUsers, maxTokens, maxPduSize):
|
||||
"""
|
||||
@summary: Write a special domain parameter structure
|
||||
@@ -256,8 +398,8 @@ class MCSLayer(LayerAutomata):
|
||||
ber.writeInteger(1), ber.writeInteger(0), ber.writeInteger(1),
|
||||
ber.writeInteger(maxPduSize), ber.writeInteger(2))
|
||||
return (ber.writeUniversalTag(ber.Tag.BER_TAG_SEQUENCE, True), writeLength(sizeof(domainParam)), domainParam)
|
||||
|
||||
def writeMCSPDUHeader(self, mcsPdu, options = 0):
|
||||
|
||||
def writeMCSPDUHeader(self, mcsPdu, options=0):
|
||||
"""
|
||||
@summary: Write MCS PDU header
|
||||
@param mcsPdu: {integer} PDU code
|
||||
@@ -265,7 +407,7 @@ class MCSLayer(LayerAutomata):
|
||||
@return: {integer}
|
||||
"""
|
||||
return (mcsPdu << 2) | options
|
||||
|
||||
|
||||
def readMCSPDUHeader(self, opcode, mcsPdu):
|
||||
"""
|
||||
@summary: Read mcsPdu header and return options parameter
|
||||
@@ -274,7 +416,7 @@ class MCSLayer(LayerAutomata):
|
||||
@return: {boolean} true if opcode is correct
|
||||
"""
|
||||
return (opcode >> 2) == mcsPdu
|
||||
|
||||
|
||||
def readDomainParams(self, s):
|
||||
"""
|
||||
@summary: Read domain parameters structure
|
||||
@@ -283,7 +425,7 @@ class MCSLayer(LayerAutomata):
|
||||
"""
|
||||
if not ber.readUniversalTag(s, ber.Tag.BER_TAG_SEQUENCE, True):
|
||||
raise InvalidValue("bad BER tags")
|
||||
ber.readLength(s)#length
|
||||
ber.readLength(s) # length
|
||||
max_channels = ber.readInteger(s)
|
||||
max_users = ber.readInteger(s)
|
||||
max_tokens = ber.readInteger(s)
|
||||
@@ -293,23 +435,26 @@ class MCSLayer(LayerAutomata):
|
||||
max_pdu_size = ber.readInteger(s)
|
||||
ber.readInteger(s)
|
||||
return (max_channels, max_users, max_tokens, max_pdu_size)
|
||||
|
||||
class Client(MCSLayer):
|
||||
|
||||
|
||||
class ClientOld(MCSLayer):
|
||||
"""
|
||||
@summary: Client automata of multiple channel service layer
|
||||
"""
|
||||
def __init__(self, presentation, virtualChannels = []):
|
||||
|
||||
def __init__(self, presentation, virtualChannels=[]):
|
||||
"""
|
||||
@param presentation: {Layer} presentation layer
|
||||
@param virtualChannels: {Array(Layer)} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
|
||||
"""
|
||||
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST, virtualChannels)
|
||||
#use to know state of static channel
|
||||
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_INDICATION, DomainMCSPDU.SEND_DATA_REQUEST,
|
||||
virtualChannels)
|
||||
# use to know state of static channel
|
||||
self._isGlobalChannelRequested = False
|
||||
self._isUserChannelRequested = False
|
||||
#nb channel requested
|
||||
# nb channel requested
|
||||
self._nbChannelRequested = 0
|
||||
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
@summary: Connect message in client automata case
|
||||
@@ -317,13 +462,13 @@ class Client(MCSLayer):
|
||||
Wait ConnectResponse
|
||||
"""
|
||||
self._clientSettings.CS_CORE.serverSelectedProtocol.value = self._transport._selectedProtocol
|
||||
#ask for virtual channel
|
||||
# ask for virtual channel
|
||||
self._clientSettings.CS_NET.channelDefArray._array = [x for (x, _) in self._virtualChannels]
|
||||
#send connect initial
|
||||
# send connect initial
|
||||
self.sendConnectInitial()
|
||||
#next wait response
|
||||
# next wait response
|
||||
self.setNextState(self.recvConnectResponse)
|
||||
|
||||
|
||||
def connectNextChannel(self):
|
||||
"""
|
||||
@summary: Send sendChannelJoinRequest message on next disconnect channel
|
||||
@@ -331,27 +476,27 @@ class Client(MCSLayer):
|
||||
Wait channel confirm
|
||||
"""
|
||||
self.setNextState(self.recvChannelJoinConfirm)
|
||||
#global channel
|
||||
# global channel
|
||||
if not self._isGlobalChannelRequested:
|
||||
self.sendChannelJoinRequest(Channel.MCS_GLOBAL_CHANNEL)
|
||||
self._isGlobalChannelRequested = True
|
||||
return
|
||||
|
||||
#user channel
|
||||
|
||||
# user channel
|
||||
if not self._isUserChannelRequested:
|
||||
self.sendChannelJoinRequest(self._userId)
|
||||
self._isUserChannelRequested = True
|
||||
return
|
||||
|
||||
#static virtual channel
|
||||
if self._nbChannelRequested < self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value:
|
||||
channelId = self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelIdArray[self._nbChannelRequested]
|
||||
|
||||
# static virtual channel
|
||||
if self._nbChannelRequested < self._serverSettings.get_block(gcc.MessageType.SC_NET).channelCount.value:
|
||||
channelId = self._serverSettings.get_block(gcc.MessageType.SC_NET).channelIdArray[self._nbChannelRequested]
|
||||
self._nbChannelRequested += 1
|
||||
self.sendChannelJoinRequest(channelId)
|
||||
return
|
||||
|
||||
|
||||
self.allChannelConnected()
|
||||
|
||||
|
||||
def recvConnectResponse(self, data):
|
||||
"""
|
||||
@summary: Receive MCS connect response from server
|
||||
@@ -367,17 +512,17 @@ class Client(MCSLayer):
|
||||
if not ber.readUniversalTag(data, ber.Tag.BER_TAG_OCTET_STRING, False):
|
||||
raise InvalidExpectedDataException("invalid expected BER tag")
|
||||
gccRequestLength = ber.readLength(data)
|
||||
if data.dataLen() != gccRequestLength:
|
||||
if data.data_len() != gccRequestLength:
|
||||
raise InvalidSize("bad size of GCC request")
|
||||
self._serverSettings = gcc.readConferenceCreateResponse(data)
|
||||
|
||||
#send domain request
|
||||
|
||||
# send domain request
|
||||
self.sendErectDomainRequest()
|
||||
#send attach user request
|
||||
# send attach user request
|
||||
self.sendAttachUserRequest()
|
||||
#now wait user confirm from server
|
||||
# now wait user confirm from server
|
||||
self.setNextState(self.recvAttachUserConfirm)
|
||||
|
||||
|
||||
def recvAttachUserConfirm(self, data):
|
||||
"""
|
||||
@summary: Receive an attach user confirm
|
||||
@@ -385,18 +530,18 @@ class Client(MCSLayer):
|
||||
@param data: {Stream}
|
||||
"""
|
||||
opcode = UInt8()
|
||||
data.readType(opcode)
|
||||
|
||||
data.read_type(opcode)
|
||||
|
||||
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_CONFIRM):
|
||||
raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_CONFIRM expected")
|
||||
|
||||
|
||||
if per.readEnumerates(data) != 0:
|
||||
raise InvalidExpectedDataException("Server reject user")
|
||||
|
||||
|
||||
self._userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
|
||||
|
||||
|
||||
self.connectNextChannel()
|
||||
|
||||
|
||||
def recvChannelJoinConfirm(self, data):
|
||||
"""
|
||||
@summary: Receive a channel join confirm from server
|
||||
@@ -404,30 +549,30 @@ class Client(MCSLayer):
|
||||
@param data: {Stream}
|
||||
"""
|
||||
opcode = UInt8()
|
||||
data.readType(opcode)
|
||||
|
||||
data.read_type(opcode)
|
||||
|
||||
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_CONFIRM):
|
||||
raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_CONFIRM expected")
|
||||
|
||||
|
||||
confirm = per.readEnumerates(data)
|
||||
|
||||
|
||||
userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
|
||||
if self._userId != userId:
|
||||
raise InvalidExpectedDataException("Invalid MCS User Id")
|
||||
|
||||
|
||||
channelId = per.readInteger16(data)
|
||||
#must confirm global channel and user channel
|
||||
# must confirm global channel and user channel
|
||||
if (confirm != 0) and (channelId == Channel.MCS_GLOBAL_CHANNEL or channelId == self._userId):
|
||||
raise InvalidExpectedDataException("Server must confirm static channel")
|
||||
|
||||
|
||||
if confirm == 0:
|
||||
serverNet = self._serverSettings.getBlock(gcc.MessageType.SC_NET)
|
||||
serverNet = self._serverSettings.get_block(gcc.MessageType.SC_NET)
|
||||
for i in range(0, serverNet.channelCount.value):
|
||||
if channelId == serverNet.channelIdArray[i].value:
|
||||
self._channels[channelId] = self._virtualChannels[i][1]
|
||||
|
||||
self._channels[channelId] = self._virtualChannels[i][1]
|
||||
|
||||
self.connectNextChannel()
|
||||
|
||||
|
||||
def sendConnectInitial(self):
|
||||
"""
|
||||
@summary: Send connect initial packet
|
||||
@@ -435,68 +580,70 @@ class Client(MCSLayer):
|
||||
"""
|
||||
ccReq = gcc.writeConferenceCreateRequest(self._clientSettings)
|
||||
ccReqStream = Stream()
|
||||
ccReqStream.writeType(ccReq)
|
||||
|
||||
ccReqStream.write_type(ccReq)
|
||||
|
||||
tmp = (ber.writeOctetstring("\x01"), ber.writeOctetstring("\x01"), ber.writeBoolean(True),
|
||||
self.writeDomainParams(34, 2, 0, 0xffff),
|
||||
self.writeDomainParams(1, 1, 1, 0x420),
|
||||
self.writeDomainParams(0xffff, 0xfc17, 0xffff, 0xffff),
|
||||
ber.writeOctetstring(ccReqStream.getvalue()))
|
||||
self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_INITIAL, sizeof(tmp)), tmp))
|
||||
|
||||
|
||||
def sendErectDomainRequest(self):
|
||||
"""
|
||||
@summary: Send a formated erect domain request for RDP connection
|
||||
"""
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)),
|
||||
per.writeInteger(0),
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ERECT_DOMAIN_REQUEST)),
|
||||
per.writeInteger(0),
|
||||
per.writeInteger(0)))
|
||||
|
||||
|
||||
def sendAttachUserRequest(self):
|
||||
"""
|
||||
@summary: Send a formated attach user request for RDP connection
|
||||
"""
|
||||
self._transport.send(self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_REQUEST)))
|
||||
|
||||
|
||||
def sendChannelJoinRequest(self, channelId):
|
||||
"""
|
||||
@summary: Send a formated Channel join request from client to server
|
||||
client automata function
|
||||
@param channelId: {integer} id of channel requested
|
||||
"""
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_REQUEST)),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId)))
|
||||
|
||||
|
||||
|
||||
class Server(MCSLayer):
|
||||
"""
|
||||
@summary: Server automata of multiple channel service layer
|
||||
"""
|
||||
def __init__(self, presentation, virtualChannels = []):
|
||||
|
||||
def __init__(self, presentation, virtualChannels=[]):
|
||||
"""
|
||||
@param presentation: {Layer} presentation layer
|
||||
@param virtualChannels: {List(Layer)} list additional channels like rdpsnd... [tuple(mcs.ChannelDef, layer)]
|
||||
"""
|
||||
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION, virtualChannels)
|
||||
#nb channel requested
|
||||
MCSLayer.__init__(self, presentation, DomainMCSPDU.SEND_DATA_REQUEST, DomainMCSPDU.SEND_DATA_INDICATION,
|
||||
virtualChannels)
|
||||
# nb channel requested
|
||||
self._nbChannelConfirmed = 0
|
||||
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
@summary: Connect message for server automata
|
||||
Wait Connect Initial
|
||||
"""
|
||||
#basic rdp security layer
|
||||
# basic rdp security layer
|
||||
if self._transport._selectedProtocol == 0:
|
||||
|
||||
self._serverSettings.SC_SECURITY.encryptionMethod.value = gcc.EncryptionMethod.ENCRYPTION_FLAG_128BIT
|
||||
self._serverSettings.SC_SECURITY.encryptionLevel.value = gcc.EncryptionLevel.ENCRYPTION_LEVEL_HIGH
|
||||
self._serverSettings.SC_SECURITY.serverRandom.value = rsa.random(256)
|
||||
self._serverSettings.SC_SECURITY.serverCertificate = self._presentation.getCertificate()
|
||||
|
||||
|
||||
self._serverSettings.SC_CORE.clientRequestedProtocol.value = self._transport._requestedProtocol
|
||||
self.setNextState(self.recvConnectInitial)
|
||||
|
||||
|
||||
def recvConnectInitial(self, data):
|
||||
"""
|
||||
@summary: Receive MCS connect initial from client
|
||||
@@ -507,28 +654,28 @@ class Server(MCSLayer):
|
||||
ber.readApplicationTag(data, UInt8(Message.MCS_TYPE_CONNECT_INITIAL))
|
||||
ber.readOctetString(data)
|
||||
ber.readOctetString(data)
|
||||
|
||||
|
||||
if not ber.readBoolean(data):
|
||||
raise InvalidExpectedDataException("invalid expected BER boolean tag")
|
||||
|
||||
|
||||
self.readDomainParams(data)
|
||||
self.readDomainParams(data)
|
||||
self.readDomainParams(data)
|
||||
self._clientSettings = gcc.readConferenceCreateRequest(Stream(ber.readOctetString(data)))
|
||||
|
||||
|
||||
if not self._clientSettings.CS_NET is None:
|
||||
i = 1
|
||||
for channelDef in self._clientSettings.CS_NET.channelDefArray._array:
|
||||
self._serverSettings.SC_NET.channelIdArray._array.append(UInt16Le(i + Channel.MCS_GLOBAL_CHANNEL))
|
||||
#if channel can be handle by serve add it
|
||||
# if channel can be handle by serve add it
|
||||
for serverChannelDef, layer in self._virtualChannels:
|
||||
if channelDef.name == serverChannelDef.name:
|
||||
self._channels[i + Channel.MCS_GLOBAL_CHANNEL] = layer
|
||||
i += 1
|
||||
|
||||
|
||||
self.sendConnectResponse()
|
||||
self.setNextState(self.recvErectDomainRequest)
|
||||
|
||||
|
||||
def recvErectDomainRequest(self, data):
|
||||
"""
|
||||
@summary: Receive erect domain request
|
||||
@@ -536,16 +683,16 @@ class Server(MCSLayer):
|
||||
@param data: {Stream}
|
||||
"""
|
||||
opcode = UInt8()
|
||||
data.readType(opcode)
|
||||
|
||||
data.read_type(opcode)
|
||||
|
||||
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ERECT_DOMAIN_REQUEST):
|
||||
raise InvalidExpectedDataException("Invalid MCS PDU : ERECT_DOMAIN_REQUEST expected")
|
||||
|
||||
|
||||
per.readInteger(data)
|
||||
per.readInteger(data)
|
||||
|
||||
|
||||
self.setNextState(self.recvAttachUserRequest)
|
||||
|
||||
|
||||
def recvAttachUserRequest(self, data):
|
||||
"""
|
||||
@summary: Receive Attach user request
|
||||
@@ -554,14 +701,14 @@ class Server(MCSLayer):
|
||||
@param data: {Stream}
|
||||
"""
|
||||
opcode = UInt8()
|
||||
data.readType(opcode)
|
||||
|
||||
data.read_type(opcode)
|
||||
|
||||
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.ATTACH_USER_REQUEST):
|
||||
raise InvalidExpectedDataException("Invalid MCS PDU : ATTACH_USER_REQUEST expected")
|
||||
|
||||
|
||||
self.sendAttachUserConfirm()
|
||||
self.setNextState(self.recvChannelJoinRequest)
|
||||
|
||||
|
||||
def recvChannelJoinRequest(self, data):
|
||||
"""
|
||||
@summary: Receive for each client channel a request
|
||||
@@ -570,51 +717,51 @@ class Server(MCSLayer):
|
||||
|
||||
"""
|
||||
opcode = UInt8()
|
||||
data.readType(opcode)
|
||||
|
||||
data.read_type(opcode)
|
||||
|
||||
if not self.readMCSPDUHeader(opcode.value, DomainMCSPDU.CHANNEL_JOIN_REQUEST):
|
||||
raise InvalidExpectedDataException("Invalid MCS PDU : CHANNEL_JOIN_REQUEST expected")
|
||||
|
||||
|
||||
userId = per.readInteger16(data, Channel.MCS_USERCHANNEL_BASE)
|
||||
if self._userId != userId:
|
||||
raise InvalidExpectedDataException("Invalid MCS User Id")
|
||||
|
||||
|
||||
channelId = per.readInteger16(data)
|
||||
#actually algo support virtual channel but RDPY have no virtual channel
|
||||
# actually algo support virtual channel but RDPY have no virtual channel
|
||||
confirm = 0 if channelId in self._channels.keys() or channelId == self._userId else 1
|
||||
self.sendChannelJoinConfirm(channelId, confirm)
|
||||
self._nbChannelConfirmed += 1
|
||||
if self._nbChannelConfirmed == self._serverSettings.getBlock(gcc.MessageType.SC_NET).channelCount.value + 2:
|
||||
if self._nbChannelConfirmed == self._serverSettings.get_block(gcc.MessageType.SC_NET).channelCount.value + 2:
|
||||
self.allChannelConnected()
|
||||
|
||||
|
||||
def sendConnectResponse(self):
|
||||
"""
|
||||
@summary: Send connect response
|
||||
"""
|
||||
ccReq = gcc.writeConferenceCreateResponse(self._serverSettings)
|
||||
ccReqStream = Stream()
|
||||
ccReqStream.writeType(ccReq)
|
||||
|
||||
tmp = (ber.writeEnumerated(0), ber.writeInteger(0), self.writeDomainParams(22, 3, 0, 0xfff8),
|
||||
ccReqStream.write_type(ccReq)
|
||||
|
||||
tmp = (ber.writeEnumerated(0), ber.writeInteger(0), self.writeDomainParams(22, 3, 0, 0xfff8),
|
||||
ber.writeOctetstring(ccReqStream.getvalue()))
|
||||
self._transport.send((ber.writeApplicationTag(Message.MCS_TYPE_CONNECT_RESPONSE, sizeof(tmp)), tmp))
|
||||
|
||||
|
||||
def sendAttachUserConfirm(self):
|
||||
"""
|
||||
@summary: Send attach user confirm
|
||||
"""
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2),
|
||||
per.writeEnumerates(0),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE)))
|
||||
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.ATTACH_USER_CONFIRM), 2),
|
||||
per.writeEnumerates(0),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE)))
|
||||
|
||||
def sendChannelJoinConfirm(self, channelId, confirm):
|
||||
"""
|
||||
@summary: Send a confirm channel (or not) to client
|
||||
@param channelId: {integer} id of channel
|
||||
@param confirm: {boolean} connection state
|
||||
"""
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM), 2),
|
||||
per.writeEnumerates(int(confirm)),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId),
|
||||
per.writeInteger16(channelId)))
|
||||
self._transport.send((self.writeMCSPDUHeader(UInt8(DomainMCSPDU.CHANNEL_JOIN_CONFIRM), 2),
|
||||
per.writeEnumerates(int(confirm)),
|
||||
per.writeInteger16(self._userId, Channel.MCS_USERCHANNEL_BASE),
|
||||
per.writeInteger16(channelId),
|
||||
per.writeInteger16(channelId)))
|
||||
@@ -21,8 +21,8 @@
|
||||
Per encoded function
|
||||
"""
|
||||
|
||||
from rdpy.core.type import UInt8, UInt16Be, UInt32Be, String
|
||||
from rdpy.core.error import InvalidValue, InvalidExpectedDataException
|
||||
from rdpy.model.message import UInt8, UInt16Be, UInt32Be, Buffer
|
||||
from rdpy.model.error import InvalidValue, InvalidExpectedDataException
|
||||
|
||||
def readLength(s):
|
||||
"""
|
||||
@@ -31,12 +31,12 @@ def readLength(s):
|
||||
@return: int python
|
||||
"""
|
||||
byte = UInt8()
|
||||
s.readType(byte)
|
||||
s.read_type(byte)
|
||||
size = 0
|
||||
if byte.value & 0x80:
|
||||
byte.value &= ~0x80
|
||||
size = byte.value << 8
|
||||
s.readType(byte)
|
||||
s.read_type(byte)
|
||||
size += byte.value
|
||||
else:
|
||||
size = byte.value
|
||||
@@ -60,7 +60,7 @@ def readChoice(s):
|
||||
@return: int that represent choice
|
||||
"""
|
||||
choice = UInt8()
|
||||
s.readType(choice)
|
||||
s.read_type(choice)
|
||||
return choice.value
|
||||
|
||||
def writeChoice(choice):
|
||||
@@ -78,7 +78,7 @@ def readSelection(s):
|
||||
@return: int that represent selection
|
||||
"""
|
||||
choice = UInt8()
|
||||
s.readType(choice)
|
||||
s.read_type(choice)
|
||||
return choice.value
|
||||
|
||||
def writeSelection(selection):
|
||||
@@ -96,7 +96,7 @@ def readNumberOfSet(s):
|
||||
@return: int that represent numberOfSet
|
||||
"""
|
||||
choice = UInt8()
|
||||
s.readType(choice)
|
||||
s.read_type(choice)
|
||||
return choice.value
|
||||
|
||||
def writeNumberOfSet(numberOfSet):
|
||||
@@ -114,7 +114,7 @@ def readEnumerates(s):
|
||||
@return: int that represent enumerate
|
||||
"""
|
||||
choice = UInt8()
|
||||
s.readType(choice)
|
||||
s.read_type(choice)
|
||||
return choice.value
|
||||
|
||||
def writeEnumerates(enumer):
|
||||
@@ -142,7 +142,7 @@ def readInteger(s):
|
||||
result = UInt32Be()
|
||||
else:
|
||||
raise InvalidValue("invalid integer size %d"%size)
|
||||
s.readType(result)
|
||||
s.read_type(result)
|
||||
return result.value
|
||||
|
||||
def writeInteger(value):
|
||||
@@ -166,7 +166,7 @@ def readInteger16(s, minimum = 0):
|
||||
@return: int or long python value
|
||||
"""
|
||||
result = UInt16Be()
|
||||
s.readType(result)
|
||||
s.read_type(result)
|
||||
return result.value + minimum
|
||||
|
||||
def writeInteger16(value, minimum = 0):
|
||||
@@ -190,16 +190,16 @@ def readObjectIdentifier(s, oid):
|
||||
raise InvalidValue("size of stream oid is wrong %d != 5"%size)
|
||||
a_oid = [0, 0, 0, 0, 0, 0]
|
||||
t12 = UInt8()
|
||||
s.readType(t12)
|
||||
s.read_type(t12)
|
||||
a_oid[0] = t12.value >> 4
|
||||
a_oid[1] = t12.value & 0x0f
|
||||
s.readType(t12)
|
||||
s.read_type(t12)
|
||||
a_oid[2] = t12.value
|
||||
s.readType(t12)
|
||||
s.read_type(t12)
|
||||
a_oid[3] = t12.value
|
||||
s.readType(t12)
|
||||
s.read_type(t12)
|
||||
a_oid[4] = t12.value
|
||||
s.readType(t12)
|
||||
s.read_type(t12)
|
||||
a_oid[5] = t12.value
|
||||
|
||||
if list(oid) != a_oid:
|
||||
@@ -223,12 +223,9 @@ def readNumericString(s, minValue):
|
||||
length = (length + minValue + 1) / 2
|
||||
s.read(length)
|
||||
|
||||
|
||||
def writeNumericString(nStr, minValue):
|
||||
"""
|
||||
@summary: write string in per format
|
||||
@param str: python string to write
|
||||
@param min: min value
|
||||
@return: String type that contain str encoded in per format
|
||||
"""
|
||||
length = len(nStr)
|
||||
mlength = minValue
|
||||
@@ -238,9 +235,9 @@ def writeNumericString(nStr, minValue):
|
||||
result = []
|
||||
|
||||
for i in range(0, length, 2):
|
||||
c1 = ord(nStr[i])
|
||||
c1 = nStr[i]
|
||||
if i + 1 < length:
|
||||
c2 = ord(nStr[i + 1])
|
||||
c2 = nStr[i + 1]
|
||||
else:
|
||||
c2 = 0x30
|
||||
c1 = (c1 - 0x30) % 10
|
||||
@@ -248,7 +245,7 @@ def writeNumericString(nStr, minValue):
|
||||
|
||||
result.append(UInt8((c1 << 4) | c2))
|
||||
|
||||
return (writeLength(mlength), tuple(result))
|
||||
return writeLength(mlength), tuple(result)
|
||||
|
||||
def readPadding(s, length):
|
||||
"""
|
||||
@@ -264,7 +261,7 @@ def writePadding(length):
|
||||
@param length: length of padding
|
||||
@return: String with \x00 * length
|
||||
"""
|
||||
return String("\x00"*length)
|
||||
return Buffer(b"\x00" * length)
|
||||
|
||||
def readOctetStream(s, octetStream, minValue = 0):
|
||||
"""
|
||||
@@ -276,11 +273,11 @@ def readOctetStream(s, octetStream, minValue = 0):
|
||||
"""
|
||||
size = readLength(s) + minValue
|
||||
if size != len(octetStream):
|
||||
raise InvalidValue("incompatible size %d != %d"(len(octetStream), size))
|
||||
raise InvalidValue("incompatible size %d != %d"%(len(octetStream), size))
|
||||
for i in range(0, size):
|
||||
c = UInt8()
|
||||
s.readType(c)
|
||||
if ord(octetStream[i]) != c.value:
|
||||
s.read_type(c)
|
||||
if octetStream[i] != c.value:
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -300,6 +297,6 @@ def writeOctetStream(oStr, minValue = 0):
|
||||
|
||||
result = []
|
||||
for i in range(0, length):
|
||||
result.append(UInt8(ord(oStr[i])))
|
||||
result.append(UInt8(oStr[i]))
|
||||
|
||||
return (writeLength(mlength), tuple(result))
|
||||
return writeLength(mlength), tuple(result)
|
||||
179
rdpy/core/tpkt.py
Normal file
179
rdpy/core/tpkt.py
Normal file
@@ -0,0 +1,179 @@
|
||||
#
|
||||
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Transport packet layer implementation
|
||||
|
||||
Use to build correct size packet and handle slow path and fast path mode
|
||||
"""
|
||||
import asyncio
|
||||
import ssl
|
||||
|
||||
from rdpy.core.nla import cssp, sspi
|
||||
from rdpy.model.layer import RawLayer
|
||||
from rdpy.model.message import UInt8, UInt16Be, sizeof, Stream
|
||||
|
||||
|
||||
class Action:
|
||||
"""
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240621.aspx
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240589.aspx
|
||||
"""
|
||||
FASTPATH_ACTION_FASTPATH = 0x0
|
||||
FASTPATH_ACTION_X224 = 0x3
|
||||
|
||||
|
||||
class SecFlags:
|
||||
"""
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240621.aspx
|
||||
"""
|
||||
FASTPATH_OUTPUT_SECURE_CHECKSUM = 0x1
|
||||
FASTPATH_OUTPUT_ENCRYPTED = 0x2
|
||||
|
||||
|
||||
class Tpkt:
|
||||
"""
|
||||
@summary: TPKT layer in RDP protocol stack
|
||||
represent the Raw Layer in stack (first layer)
|
||||
This layer only handle size of packet and determine if is a fast path packet
|
||||
"""
|
||||
|
||||
def __init__(self, reader, writer):
|
||||
self.reader = reader
|
||||
self.writer = writer
|
||||
|
||||
def readHeader(self, data):
|
||||
"""
|
||||
@summary: Read header of TPKT packet
|
||||
@param data: {Stream} received from twisted layer
|
||||
"""
|
||||
#first read packet version
|
||||
version = UInt8()
|
||||
data.read_type(version)
|
||||
#classic packet
|
||||
if version.value == Action.FASTPATH_ACTION_X224:
|
||||
#padding
|
||||
data.read_type(UInt8())
|
||||
#read end header
|
||||
self.expect(2, self.readExtendedHeader)
|
||||
else:
|
||||
#is fast path packet
|
||||
self._secFlag = ((version.value >> 6) & 0x3)
|
||||
data.read_type(self._lastShortLength)
|
||||
if self._lastShortLength.value & 0x80:
|
||||
#size is 1 byte more
|
||||
self.expect(1, self.readExtendedFastPathHeader)
|
||||
return
|
||||
self.expect(self._lastShortLength.value - 2, self.readFastPath)
|
||||
|
||||
|
||||
def readExtendedHeader(self, data):
|
||||
"""
|
||||
@summary: Header may be on 4 bytes
|
||||
@param data: {Stream} from twisted layer
|
||||
"""
|
||||
#next state is read data
|
||||
size = UInt16Be()
|
||||
data.read_type(size)
|
||||
self.expect(size.value - 4, self.readData)
|
||||
|
||||
def readExtendedFastPathHeader(self, data):
|
||||
"""
|
||||
@summary: Fast path header may be on 1 byte more
|
||||
@param data: {Stream} from twisted layer
|
||||
"""
|
||||
leftPart = UInt8()
|
||||
data.read_type(leftPart)
|
||||
self._lastShortLength.value &= ~0x80
|
||||
packetSize = (self._lastShortLength.value << 8) + leftPart.value
|
||||
#next state is fast patn data
|
||||
self.expect(packetSize - 3, self.readFastPath)
|
||||
|
||||
def readFastPath(self, data):
|
||||
"""
|
||||
@summary: Fast path data
|
||||
@param data: {Stream} from twisted layer
|
||||
"""
|
||||
self._fastPathListener.recvFastPath(self._secFlag, data)
|
||||
self.expect(2, self.readHeader)
|
||||
|
||||
def readData(self, data):
|
||||
"""
|
||||
@summary: 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)
|
||||
|
||||
async def write(self, message):
|
||||
"""
|
||||
@summary: Send encompassed data
|
||||
@param message: {network.Type} message to send
|
||||
"""
|
||||
s = Stream()
|
||||
s.write_type((UInt8(Action.FASTPATH_ACTION_X224), UInt8(0), UInt16Be(sizeof(message) + 4), message))
|
||||
self.writer.write(s.getvalue())
|
||||
await self.writer.drain()
|
||||
|
||||
async def read(self):
|
||||
"""
|
||||
Read an entire payload from the reader stream
|
||||
"""
|
||||
header = Stream(await self.reader.readexactly(2))
|
||||
action = UInt8()
|
||||
header.read_type(action)
|
||||
if action.value == Action.FASTPATH_ACTION_X224:
|
||||
# read padding
|
||||
header.read_type(UInt8())
|
||||
|
||||
size = UInt16Be()
|
||||
Stream(await self.reader.readexactly(2)).read_type(size)
|
||||
return Stream(await self.reader.readexactly(size.value - 4))
|
||||
|
||||
|
||||
def sendFastPath(self, secFlag, fastPathS):
|
||||
"""
|
||||
@param fastPathS: {Type | Tuple} type transform to stream and send as fastpath
|
||||
@param secFlag: {integer} Security flag for fastpath packet
|
||||
"""
|
||||
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH | ((secFlag & 0x3) << 6)), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS))
|
||||
|
||||
async def start_tls(self):
|
||||
"""
|
||||
Start TLS protocol
|
||||
"""
|
||||
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
ssl_ctx.check_hostname = False
|
||||
ssl_ctx.verify_mode = ssl.VerifyMode.CERT_NONE
|
||||
|
||||
reader, writer = await asyncio.open_connection(sock=self.writer.transport._sock, ssl=ssl_ctx, server_hostname="")
|
||||
return Tpkt(reader, writer)
|
||||
|
||||
async def start_nla(self, authentication_protocol: sspi.IAuthenticationProtocol):
|
||||
"""
|
||||
use to start NLA (NTLM over SSL) protocol
|
||||
must be called after startTLS function
|
||||
|
||||
:ivar authentication_protocol: Authentication protocol use by CSSP to authenticate user
|
||||
and transfert credentials
|
||||
"""
|
||||
tpkt = await self.start_tls()
|
||||
await cssp.connect(tpkt.reader, tpkt.writer, authentication_protocol)
|
||||
return tpkt
|
||||
@@ -23,13 +23,15 @@ Implement transport PDU layer
|
||||
This layer have main goal to negociate SSL transport
|
||||
RDP basic security is supported only on client side
|
||||
"""
|
||||
from rdpy.core import log
|
||||
from rdpy.core import tpkt
|
||||
from rdpy.core.nla import sspi
|
||||
from rdpy.model import log
|
||||
|
||||
from rdpy.core.layer import LayerAutomata, IStreamSender
|
||||
from rdpy.core.type import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof, String
|
||||
from rdpy.core.error import InvalidExpectedDataException, RDPSecurityNegoFail
|
||||
from rdpy.model.message import UInt8, UInt16Le, UInt16Be, UInt32Le, CompositeType, sizeof, Buffer, Stream
|
||||
from rdpy.model.error import InvalidExpectedDataException, RDPSecurityNegoFail
|
||||
|
||||
class MessageType(object):
|
||||
|
||||
class MessageType:
|
||||
"""
|
||||
@summary: Message type
|
||||
"""
|
||||
@@ -39,7 +41,8 @@ class MessageType(object):
|
||||
X224_TPDU_DATA = 0xF0
|
||||
X224_TPDU_ERROR = 0x70
|
||||
|
||||
class NegociationType(object):
|
||||
|
||||
class NegociationType:
|
||||
"""
|
||||
@summary: Negotiation header
|
||||
"""
|
||||
@@ -47,7 +50,8 @@ class NegociationType(object):
|
||||
TYPE_RDP_NEG_RSP = 0x02
|
||||
TYPE_RDP_NEG_FAILURE = 0x03
|
||||
|
||||
class Protocols(object):
|
||||
|
||||
class Protocols:
|
||||
"""
|
||||
@summary: Protocols available for x224 layer
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
@@ -56,8 +60,9 @@ class Protocols(object):
|
||||
PROTOCOL_SSL = 0x00000001
|
||||
PROTOCOL_HYBRID = 0x00000002
|
||||
PROTOCOL_HYBRID_EX = 0x00000008
|
||||
|
||||
class NegotiationFailureCode(object):
|
||||
|
||||
|
||||
class NegotiationFailureCode:
|
||||
"""
|
||||
@summary: Protocol negotiation failure code
|
||||
"""
|
||||
@@ -67,23 +72,25 @@ class NegotiationFailureCode(object):
|
||||
INCONSISTENT_FLAGS = 0x00000004
|
||||
HYBRID_REQUIRED_BY_SERVER = 0x00000005
|
||||
SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 0x00000006
|
||||
|
||||
class ClientConnectionRequestPDU(CompositeType):
|
||||
|
||||
|
||||
class ConnectionRequestPDU(CompositeType):
|
||||
"""
|
||||
@summary: Connection request
|
||||
client -> server
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
Connection Request PDU
|
||||
Use to send protocol security level available for the client
|
||||
:see: http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.len = UInt8(lambda:sizeof(self) - 1)
|
||||
self.code = UInt8(MessageType.X224_TPDU_CONNECTION_REQUEST, constant = True)
|
||||
self.code = UInt8(MessageType.X224_TPDU_CONNECTION_REQUEST, constant=True)
|
||||
self.padding = (UInt16Be(), UInt16Be(), UInt8())
|
||||
self.cookie = String(until = "\x0d\x0a", conditional = lambda:(self.len._is_readed and self.len.value > 14))
|
||||
#read if there is enough data
|
||||
self.cookie = Buffer(until=b"\x0d\x0a", conditional=lambda: (self.len._is_readed and self.len.value > 14))
|
||||
# read if there is enough data
|
||||
self.protocolNeg = Negotiation(optional = True)
|
||||
|
||||
class ServerConnectionConfirm(CompositeType):
|
||||
|
||||
class ConnectionConfirmPDU(CompositeType):
|
||||
"""
|
||||
@summary: Server response
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240506.aspx
|
||||
@@ -95,7 +102,8 @@ class ServerConnectionConfirm(CompositeType):
|
||||
self.padding = (UInt16Be(), UInt16Be(), UInt8())
|
||||
#read if there is enough data
|
||||
self.protocolNeg = Negotiation(optional = True)
|
||||
|
||||
|
||||
|
||||
class X224DataHeader(CompositeType):
|
||||
"""
|
||||
@summary: Header send when x224 exchange application data
|
||||
@@ -105,7 +113,8 @@ class X224DataHeader(CompositeType):
|
||||
self.header = UInt8(2)
|
||||
self.messageType = UInt8(MessageType.X224_TPDU_DATA, constant = True)
|
||||
self.separator = UInt8(0x80, constant = True)
|
||||
|
||||
|
||||
|
||||
class Negotiation(CompositeType):
|
||||
"""
|
||||
@summary: Negociate request message
|
||||
@@ -122,117 +131,68 @@ class Negotiation(CompositeType):
|
||||
self.selectedProtocol = UInt32Le(conditional = lambda: (self.code.value != NegociationType.TYPE_RDP_NEG_FAILURE))
|
||||
self.failureCode = UInt32Le(conditional = lambda: (self.code.value == NegociationType.TYPE_RDP_NEG_FAILURE))
|
||||
|
||||
class X224Layer(LayerAutomata, IStreamSender):
|
||||
|
||||
class X224:
|
||||
"""
|
||||
@summary: x224 layer management
|
||||
there is an connection automata
|
||||
"""
|
||||
def __init__(self, presentation):
|
||||
def __init__(self, tpkt: tpkt.Tpkt, selected_protocol: int):
|
||||
"""
|
||||
@param presentation: upper layer, MCS layer in RDP case
|
||||
"""
|
||||
LayerAutomata.__init__(self, presentation)
|
||||
#client requested selectedProtocol
|
||||
self._requestedProtocol = Protocols.PROTOCOL_SSL | Protocols.PROTOCOL_HYBRID
|
||||
#server selected selectedProtocol
|
||||
self._selectedProtocol = Protocols.PROTOCOL_SSL
|
||||
self.tpkt = tpkt
|
||||
self.selected_protocol = selected_protocol
|
||||
|
||||
def recvData(self, data):
|
||||
async def read(self) -> Stream:
|
||||
"""
|
||||
@summary: Read data header from packet
|
||||
And pass to presentation layer
|
||||
@param data: Stream
|
||||
"""
|
||||
header = X224DataHeader()
|
||||
data.readType(header)
|
||||
self._presentation.recv(data)
|
||||
payload = await self.tpkt.read()
|
||||
payload.read_type(header)
|
||||
return payload
|
||||
|
||||
def send(self, message):
|
||||
async def write(self, message):
|
||||
"""
|
||||
@summary: Write message packet for TPDU layer
|
||||
Add TPDU header
|
||||
@param message: network.Type message
|
||||
"""
|
||||
self._transport.send((X224DataHeader(), message))
|
||||
|
||||
class Client(X224Layer):
|
||||
"""
|
||||
@summary: Client automata of TPDU layer
|
||||
"""
|
||||
def __init__(self, presentation):
|
||||
"""
|
||||
@param presentation: upper layer, MCS layer in RDP case
|
||||
"""
|
||||
X224Layer.__init__(self, presentation)
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
@summary: Connection request for client send a connection request packet
|
||||
"""
|
||||
self.sendConnectionRequest()
|
||||
|
||||
def sendConnectionRequest(self):
|
||||
"""
|
||||
@summary: Write connection request message
|
||||
Next state is recvConnectionConfirm
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
"""
|
||||
message = ClientConnectionRequestPDU()
|
||||
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_REQ
|
||||
message.protocolNeg.selectedProtocol.value = self._requestedProtocol
|
||||
self._transport.send(message)
|
||||
self.setNextState(self.recvConnectionConfirm)
|
||||
|
||||
def recvConnectionConfirm(self, data):
|
||||
"""
|
||||
@summary: 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 = ServerConnectionConfirm()
|
||||
data.readType(message)
|
||||
|
||||
if message.protocolNeg.failureCode._is_readed:
|
||||
raise RDPSecurityNegoFail("negotiation failure code %x"%message.protocolNeg.failureCode.value)
|
||||
|
||||
#check presence of negotiation response
|
||||
if message.protocolNeg._is_readed:
|
||||
self._selectedProtocol = message.protocolNeg.selectedProtocol.value
|
||||
else:
|
||||
self._selectedProtocol = Protocols.PROTOCOL_RDP
|
||||
|
||||
#NLA protocol doesn't support in actual version of RDPY
|
||||
if self._selectedProtocol in [ Protocols.PROTOCOL_HYBRID_EX ]:
|
||||
raise InvalidExpectedDataException("RDPY doesn't support PROTOCOL_HYBRID_EX security Layer")
|
||||
|
||||
#now i'm ready to receive data
|
||||
self.setNextState(self.recvData)
|
||||
|
||||
if self._selectedProtocol == Protocols.PROTOCOL_RDP:
|
||||
log.warning("*" * 43)
|
||||
log.warning("*" + " " * 10 + "RDP Security selected" + " " * 10 + "*")
|
||||
log.warning("*" * 43)
|
||||
#connection is done send to presentation
|
||||
self._presentation.connect()
|
||||
|
||||
elif self._selectedProtocol == Protocols.PROTOCOL_SSL:
|
||||
log.info("*" * 43)
|
||||
log.info("*" + " " * 10 + "SSL Security selected" + " " * 10 + "*")
|
||||
log.info("*" * 43)
|
||||
self._transport.startTLS(ClientTLSContext())
|
||||
#connection is done send to presentation
|
||||
self._presentation.connect()
|
||||
|
||||
elif self._selectedProtocol == Protocols.PROTOCOL_HYBRID:
|
||||
log.info("*" * 43)
|
||||
log.info("*" + " " * 10 + "NLA Security selected" + " " * 10 + "*")
|
||||
log.info("*" * 43)
|
||||
self._transport.startNLA(ClientTLSContext(), lambda:self._presentation.connect())
|
||||
await self.tpkt.write((X224DataHeader(), message))
|
||||
|
||||
class Server(X224Layer):
|
||||
def get_selected_protocol(self):
|
||||
return self.selected_protocol
|
||||
|
||||
|
||||
async def connect(tpkt: tpkt.Tpkt, authentication_protocol: sspi.IAuthenticationProtocol) -> X224:
|
||||
"""
|
||||
Negotiate the security level and generate a X224 configured layer
|
||||
|
||||
:ivar tpkt: this is the tpkt layer use to negotiate the security level
|
||||
:ivar authentication_protocol: Authentication protocol is used by NLA authentication
|
||||
Actually only NTLMv2 is available
|
||||
|
||||
:see: http://msdn.microsoft.com/en-us/library/cc240500.aspx
|
||||
"""
|
||||
request = ConnectionRequestPDU()
|
||||
request.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_REQ
|
||||
request.protocolNeg.selectedProtocol.value = Protocols.PROTOCOL_HYBRID | Protocols.PROTOCOL_SSL
|
||||
await tpkt.write(request)
|
||||
|
||||
respond = (await tpkt.read()).read_type(ConnectionConfirmPDU())
|
||||
if respond.protocolNeg.failureCode._is_readed:
|
||||
raise RDPSecurityNegoFail("negotiation failure code %x"%respond.protocolNeg.failureCode.value)
|
||||
|
||||
selected_protocol = Protocols.PROTOCOL_RDP
|
||||
if respond.protocolNeg._is_readed:
|
||||
selected_protocol = respond.protocolNeg.selectedProtocol.value
|
||||
|
||||
if selected_protocol in [Protocols.PROTOCOL_HYBRID_EX]:
|
||||
raise InvalidExpectedDataException("RDPY doesn't support PROTOCOL_HYBRID_EX security Layer")
|
||||
|
||||
if selected_protocol == Protocols.PROTOCOL_RDP:
|
||||
return X224(tpkt, selected_protocol)
|
||||
elif selected_protocol == Protocols.PROTOCOL_SSL:
|
||||
return X224(await tpkt.start_tls(), selected_protocol)
|
||||
elif selected_protocol == Protocols.PROTOCOL_HYBRID:
|
||||
return X224(await tpkt.start_nla(authentication_protocol), selected_protocol)
|
||||
|
||||
|
||||
class Server(X224):
|
||||
"""
|
||||
@summary: Server automata of X224 layer
|
||||
"""
|
||||
@@ -262,8 +222,8 @@ class Server(X224Layer):
|
||||
@param data: {Stream}
|
||||
@see : http://msdn.microsoft.com/en-us/library/cc240470.aspx
|
||||
"""
|
||||
message = ClientConnectionRequestPDU()
|
||||
data.readType(message)
|
||||
message = ConnectionRequestPDU()
|
||||
data.read_type(message)
|
||||
|
||||
if not message.protocolNeg._is_readed:
|
||||
self._requestedProtocol = Protocols.PROTOCOL_RDP
|
||||
@@ -280,7 +240,7 @@ class Server(X224Layer):
|
||||
if not self._selectedProtocol & Protocols.PROTOCOL_SSL and self._forceSSL:
|
||||
log.warning("server reject client because doesn't support SSL")
|
||||
#send error message and quit
|
||||
message = ServerConnectionConfirm()
|
||||
message = ConnectionConfirmPDU()
|
||||
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_FAILURE
|
||||
message.protocolNeg.failureCode.value = NegotiationFailureCode.SSL_REQUIRED_BY_SERVER
|
||||
self._transport.send(message)
|
||||
@@ -296,45 +256,15 @@ class Server(X224Layer):
|
||||
Next state is recvData
|
||||
@see : http://msdn.microsoft.com/en-us/library/cc240501.aspx
|
||||
"""
|
||||
message = ServerConnectionConfirm()
|
||||
message = ConnectionConfirmPDU()
|
||||
message.protocolNeg.code.value = NegociationType.TYPE_RDP_NEG_RSP
|
||||
message.protocolNeg.selectedProtocol.value = self._selectedProtocol
|
||||
self._transport.send(message)
|
||||
if self._selectedProtocol == Protocols.PROTOCOL_SSL:
|
||||
log.debug("*" * 10 + " select SSL layer " + "*" * 10)
|
||||
#_transport is TPKT and transport is TCP layer of twisted
|
||||
self._transport.startTLS(ServerTLSContext(self._serverPrivateKeyFileName, self._serverCertificateFileName))
|
||||
#self._transport.startTLS(ServerTLSContext(self._serverPrivateKeyFileName, self._serverCertificateFileName))
|
||||
|
||||
#connection is done send to presentation
|
||||
self.setNextState(self.recvData)
|
||||
self._presentation.connect()
|
||||
|
||||
#open ssl needed
|
||||
from twisted.internet import ssl
|
||||
from OpenSSL import SSL
|
||||
|
||||
class ClientTLSContext(ssl.ClientContextFactory):
|
||||
"""
|
||||
@summary: client context factory for open ssl
|
||||
"""
|
||||
def getContext(self):
|
||||
context = SSL.Context(SSL.TLSv1_METHOD)
|
||||
context.set_options(SSL.OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
||||
context.set_options(SSL.OP_TLS_BLOCK_PADDING_BUG)
|
||||
return context
|
||||
|
||||
class ServerTLSContext(ssl.DefaultOpenSSLContextFactory):
|
||||
"""
|
||||
@summary: Server context factory for open ssl
|
||||
@param privateKeyFileName: Name of a file containing a private key
|
||||
@param certificateFileName: Name of a file containing a certificate
|
||||
"""
|
||||
def __init__(self, privateKeyFileName, certificateFileName):
|
||||
class TPDUSSLContext(SSL.Context):
|
||||
def __init__(self, method):
|
||||
SSL.Context.__init__(self, method)
|
||||
self.set_options(SSL.OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
||||
self.set_options(SSL.OP_TLS_BLOCK_PADDING_BUG)
|
||||
|
||||
ssl.DefaultOpenSSLContextFactory.__init__(self, privateKeyFileName, certificateFileName, SSL.SSLv23_METHOD, TPDUSSLContext)
|
||||
|
||||
@@ -22,8 +22,9 @@ Join RDPY design with twisted design
|
||||
|
||||
RDPY use Layer Protocol design (like twisted)
|
||||
"""
|
||||
import asyncio
|
||||
from rdpy.model.error import CallPureVirtualFuntion
|
||||
|
||||
from rdpy.core.error import CallPureVirtualFuntion
|
||||
|
||||
class IStreamListener(object):
|
||||
"""
|
||||
@@ -35,7 +36,8 @@ class IStreamListener(object):
|
||||
@param s: Stream
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener"))
|
||||
|
||||
|
||||
|
||||
class IStreamSender(object):
|
||||
"""
|
||||
@summary: Interface use to inform stream sender capability
|
||||
@@ -46,7 +48,8 @@ class IStreamSender(object):
|
||||
@param data: Type or tuple element handle by transport layer
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "send", "IStreamSender"))
|
||||
|
||||
|
||||
|
||||
class Layer(object):
|
||||
"""
|
||||
@summary: A simple double linked list with presentation and transport layer
|
||||
@@ -79,7 +82,8 @@ class Layer(object):
|
||||
"""
|
||||
if not self._transport is None:
|
||||
self._transport.close()
|
||||
|
||||
|
||||
|
||||
class LayerAutomata(Layer, IStreamListener):
|
||||
"""
|
||||
@summary: Layer with automata callback
|
||||
@@ -103,13 +107,8 @@ class LayerAutomata(Layer, IStreamListener):
|
||||
|
||||
self.recv = callback
|
||||
|
||||
#twisted layer concept
|
||||
from twisted.internet import protocol
|
||||
from twisted.internet.abstract import FileDescriptor
|
||||
#first that handle stream
|
||||
from type import Stream
|
||||
|
||||
class RawLayerClientFactory(protocol.ClientFactory):
|
||||
class RawLayerClientFactory(asyncio.Protocol):
|
||||
"""
|
||||
@summary: Abstract class for Raw layer client factory
|
||||
"""
|
||||
@@ -136,37 +135,38 @@ class RawLayerClientFactory(protocol.ClientFactory):
|
||||
@param reason: twisted reason
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "connectionLost", "RawLayerClientFactory"))
|
||||
|
||||
class RawLayerServerFactory(protocol.ServerFactory):
|
||||
"""
|
||||
@summary: Abstract class for Raw layer server factory
|
||||
"""
|
||||
def buildProtocol(self, addr):
|
||||
"""
|
||||
@summary: Function call from twisted
|
||||
@param addr: destination address
|
||||
"""
|
||||
rawLayer = self.buildRawLayer(addr)
|
||||
rawLayer.setFactory(self)
|
||||
return rawLayer
|
||||
|
||||
def buildRawLayer(self, addr):
|
||||
"""
|
||||
@summary: Override this function to build raw layer
|
||||
@param addr: destination address
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener"))
|
||||
|
||||
def connectionLost(self, rawlayer, reason):
|
||||
"""
|
||||
@summary: Override this method to handle connection lost
|
||||
@param rawlayer: rawLayer that cause connectionLost event
|
||||
@param reason: twisted reason
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener"))
|
||||
|
||||
|
||||
class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
|
||||
# class RawLayerServerFactory(protocol.ServerFactory):
|
||||
# """
|
||||
# @summary: Abstract class for Raw layer server factory
|
||||
# """
|
||||
# def buildProtocol(self, addr):
|
||||
# """
|
||||
# @summary: Function call from twisted
|
||||
# @param addr: destination address
|
||||
# """
|
||||
# rawLayer = self.buildRawLayer(addr)
|
||||
# rawLayer.setFactory(self)
|
||||
# return rawLayer
|
||||
#
|
||||
# def buildRawLayer(self, addr):
|
||||
# """
|
||||
# @summary: Override this function to build raw layer
|
||||
# @param addr: destination address
|
||||
# """
|
||||
# raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener"))
|
||||
#
|
||||
# def connectionLost(self, rawlayer, reason):
|
||||
# """
|
||||
# @summary: Override this method to handle connection lost
|
||||
# @param rawlayer: rawLayer that cause connectionLost event
|
||||
# @param reason: twisted reason
|
||||
# """
|
||||
# raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recv", "IStreamListener"))
|
||||
#
|
||||
#
|
||||
class RawLayer(asyncio.Protocol, LayerAutomata, IStreamSender):
|
||||
"""
|
||||
@summary: Wait event from twisted engine
|
||||
And format correct size packet
|
||||
@@ -183,14 +183,14 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
#len of next packet pass to next state function
|
||||
self._expectedLen = 0
|
||||
self._factory = None
|
||||
|
||||
|
||||
def setFactory(self, factory):
|
||||
"""
|
||||
@summary: Call by RawLayer Factory
|
||||
@param param: RawLayerClientFactory or RawLayerFactory
|
||||
"""
|
||||
self._factory = factory
|
||||
|
||||
|
||||
def dataReceived(self, data):
|
||||
"""
|
||||
@summary: Inherit from twisted.protocol class
|
||||
@@ -207,27 +207,27 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
self._buffer = self._buffer[self._expectedLen:]
|
||||
#call recv function
|
||||
self.recv(expectedData)
|
||||
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
@summary: inherit from twisted protocol
|
||||
"""
|
||||
#join two scheme
|
||||
self.connect()
|
||||
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"""
|
||||
@summary: Call from twisted engine when protocol is closed
|
||||
@param reason: str represent reason of close connection
|
||||
"""
|
||||
self._factory.connectionLost(self, reason)
|
||||
|
||||
|
||||
def getDescriptor(self):
|
||||
"""
|
||||
@return: the twited file descriptor
|
||||
"""
|
||||
return self.transport
|
||||
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
@summary: Close raw layer
|
||||
@@ -235,10 +235,10 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
Because is bugged
|
||||
"""
|
||||
FileDescriptor.loseConnection(self.getDescriptor())
|
||||
|
||||
|
||||
def expect(self, expectedLen, callback = None):
|
||||
"""
|
||||
@summary: Set next automata callback,
|
||||
@summary: Set next automata callback,
|
||||
But this callback will be only called when
|
||||
data have expectedLen
|
||||
@param expectedLen: in bytes length use to call next state
|
||||
@@ -247,7 +247,7 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
self._expectedLen = expectedLen
|
||||
#default callback is recv from LayerAutomata
|
||||
self.setNextState(callback)
|
||||
|
||||
|
||||
def send(self, message):
|
||||
"""
|
||||
@summary: Send Stream on TCP layer
|
||||
@@ -256,5 +256,5 @@ class RawLayer(protocol.Protocol, LayerAutomata, IStreamSender):
|
||||
@param message: (tuple | Type)
|
||||
"""
|
||||
s = Stream()
|
||||
s.writeType(message)
|
||||
s.write_type(message)
|
||||
self.transport.write(s.getvalue())
|
||||
@@ -44,7 +44,7 @@ def log(message):
|
||||
f = open(_LOG_FILE, "a+")
|
||||
f.write("%s\n"%message)
|
||||
f.close()
|
||||
print "[*] %s"%message
|
||||
print("[*] %s"%message)
|
||||
|
||||
def error(message):
|
||||
"""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,8 @@ Remote Session Scenario File format
|
||||
Private protocol format to save events
|
||||
"""
|
||||
|
||||
from rdpy.core.type import CompositeType, FactoryType, UInt8, UInt16Le, UInt32Le, String, sizeof, Stream
|
||||
from rdpy.core import log, error
|
||||
from rdpy.model.type import CompositeType, FactoryType, UInt8, UInt16Le, UInt32Le, Buffer, sizeof, Stream
|
||||
from rdpy.model import log, error
|
||||
import time
|
||||
|
||||
class EventType(object):
|
||||
@@ -63,7 +63,7 @@ class Event(CompositeType):
|
||||
return c(readLen = self.length)
|
||||
log.debug("unknown event type : %s"%hex(self.type.value))
|
||||
#read entire packet
|
||||
return String(readLen = self.length)
|
||||
return Buffer(readLen = self.length)
|
||||
|
||||
if event is None:
|
||||
event = FactoryType(EventFactory)
|
||||
@@ -88,7 +88,7 @@ class UpdateEvent(CompositeType):
|
||||
self.bpp = UInt8()
|
||||
self.format = UInt8()
|
||||
self.length = UInt32Le(lambda:sizeof(self.data))
|
||||
self.data = String(readLen = self.length)
|
||||
self.data = Buffer(readLen = self.length)
|
||||
|
||||
class InfoEvent(CompositeType):
|
||||
"""
|
||||
@@ -98,13 +98,13 @@ class InfoEvent(CompositeType):
|
||||
def __init__(self, readLen = None):
|
||||
CompositeType.__init__(self, readLen = readLen)
|
||||
self.lenUsername = UInt16Le(lambda:sizeof(self.username))
|
||||
self.username = String(readLen = self.lenUsername)
|
||||
self.username = Buffer(readLen = self.lenUsername)
|
||||
self.lenPassword = UInt16Le(lambda:sizeof(self.password))
|
||||
self.password = String(readLen = self.lenPassword)
|
||||
self.password = Buffer(readLen = self.lenPassword)
|
||||
self.lenDomain = UInt16Le(lambda:sizeof(self.domain))
|
||||
self.domain = String(readLen = self.lenDomain)
|
||||
self.domain = Buffer(readLen = self.lenDomain)
|
||||
self.lenHostname = UInt16Le(lambda:sizeof(self.hostname))
|
||||
self.hostname = String(readLen = self.lenHostname)
|
||||
self.hostname = Buffer(readLen = self.lenHostname)
|
||||
|
||||
class ScreenEvent(CompositeType):
|
||||
"""
|
||||
@@ -177,7 +177,7 @@ class FileRecorder(object):
|
||||
self._lastEventTimer = now
|
||||
|
||||
s = Stream()
|
||||
s.writeType(e)
|
||||
s.write_type(e)
|
||||
|
||||
self._file.write(s.getvalue())
|
||||
|
||||
@@ -276,10 +276,10 @@ class FileReader(object):
|
||||
"""
|
||||
@summary: read next event and return it
|
||||
"""
|
||||
if self._s.dataLen() == 0:
|
||||
if self._s.data_len() == 0:
|
||||
return None
|
||||
e = Event()
|
||||
self._s.readType(e)
|
||||
self._s.read_type(e)
|
||||
return e
|
||||
|
||||
def createRecorder(path):
|
||||
@@ -1,291 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
@summary: Credential Security Support Provider
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226764.aspx
|
||||
"""
|
||||
|
||||
from pyasn1.type import namedtype, univ, tag
|
||||
import pyasn1.codec.der.encoder as der_encoder
|
||||
import pyasn1.codec.der.decoder as der_decoder
|
||||
import pyasn1.codec.ber.encoder as ber_encoder
|
||||
|
||||
from rdpy.core.type import Stream
|
||||
from twisted.internet import protocol
|
||||
from OpenSSL import crypto
|
||||
from rdpy.security import x509
|
||||
from rdpy.core import error
|
||||
|
||||
class NegoToken(univ.Sequence):
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('negoToken', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
)
|
||||
|
||||
class NegoData(univ.SequenceOf):
|
||||
"""
|
||||
@summary: contain spnego ntlm of kerberos data
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226781.aspx
|
||||
"""
|
||||
componentType = NegoToken()
|
||||
|
||||
class TSRequest(univ.Sequence):
|
||||
"""
|
||||
@summary: main structure
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226780.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('version', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.OptionalNamedType('negoTokens', NegoData().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('authInfo', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('pubKeyAuth', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
|
||||
namedtype.OptionalNamedType('errorCode', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
|
||||
)
|
||||
|
||||
class TSCredentials(univ.Sequence):
|
||||
"""
|
||||
@summary: contain user information
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226782.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('credType', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('credentials', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
|
||||
)
|
||||
|
||||
class TSPasswordCreds(univ.Sequence):
|
||||
"""
|
||||
@summary: contain username and password
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226783.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('domainName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('userName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.NamedType('password', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
)
|
||||
|
||||
class TSCspDataDetail(univ.Sequence):
|
||||
"""
|
||||
@summary: smart card credentials
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226785.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('keySpec', univ.Integer().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.OptionalNamedType('cardName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('readerName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('containerName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))),
|
||||
namedtype.OptionalNamedType('cspName', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4)))
|
||||
)
|
||||
|
||||
class TSSmartCardCreds(univ.Sequence):
|
||||
"""
|
||||
@summary: smart card credentials
|
||||
@see: https://msdn.microsoft.com/en-us/library/cc226784.aspx
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('pin', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
|
||||
namedtype.NamedType('cspData', TSCspDataDetail().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
|
||||
namedtype.OptionalNamedType('userHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))),
|
||||
namedtype.OptionalNamedType('domainHint', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
|
||||
)
|
||||
|
||||
class OpenSSLRSAPublicKey(univ.Sequence):
|
||||
"""
|
||||
@summary: asn1 public rsa key
|
||||
@see: https://tools.ietf.org/html/rfc3447
|
||||
"""
|
||||
componentType = namedtype.NamedTypes(
|
||||
namedtype.NamedType('unknow', univ.Integer()),
|
||||
namedtype.NamedType('modulus', univ.Integer()),
|
||||
namedtype.NamedType('publicExponent', univ.Integer()),
|
||||
)
|
||||
|
||||
def encodeDERTRequest(negoTypes = [], authInfo = None, pubKeyAuth = None):
|
||||
"""
|
||||
@summary: create TSRequest from list of Type
|
||||
@param negoTypes: {list(Type)}
|
||||
@param authInfo: {str} authentication info TSCredentials encrypted with authentication protocol
|
||||
@param pubKeyAuth: {str} public key encrypted with authentication protocol
|
||||
@return: {str} TRequest der encoded
|
||||
"""
|
||||
negoData = NegoData().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))
|
||||
|
||||
#fill nego data tokens
|
||||
i = 0
|
||||
for negoType in negoTypes:
|
||||
s = Stream()
|
||||
s.writeType(negoType)
|
||||
negoToken = NegoToken()
|
||||
negoToken.setComponentByPosition(0, s.getvalue())
|
||||
negoData.setComponentByPosition(i, negoToken)
|
||||
i += 1
|
||||
|
||||
request = TSRequest()
|
||||
request.setComponentByName("version", univ.Integer(2).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
|
||||
|
||||
if i > 0:
|
||||
request.setComponentByName("negoTokens", negoData)
|
||||
|
||||
if not authInfo is None:
|
||||
request.setComponentByName("authInfo", univ.OctetString(authInfo).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
|
||||
if not pubKeyAuth is None:
|
||||
request.setComponentByName("pubKeyAuth", univ.OctetString(pubKeyAuth).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3)))
|
||||
|
||||
return der_encoder.encode(request)
|
||||
|
||||
def decodeDERTRequest(s):
|
||||
"""
|
||||
@summary: Decode the stream as
|
||||
@param s: {str}
|
||||
"""
|
||||
return der_decoder.decode(s, asn1Spec=TSRequest())[0]
|
||||
|
||||
def getNegoTokens(tRequest):
|
||||
negoData = tRequest.getComponentByName("negoTokens")
|
||||
return [Stream(negoData.getComponentByPosition(i).getComponentByPosition(0).asOctets()) for i in range(len(negoData))]
|
||||
|
||||
def getPubKeyAuth(tRequest):
|
||||
return tRequest.getComponentByName("pubKeyAuth").asOctets()
|
||||
|
||||
def encodeDERTCredentials(domain, username, password):
|
||||
passwordCred = TSPasswordCreds()
|
||||
passwordCred.setComponentByName("domainName", univ.OctetString(domain).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
|
||||
passwordCred.setComponentByName("userName", univ.OctetString(username).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
|
||||
passwordCred.setComponentByName("password", univ.OctetString(password).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2)))
|
||||
|
||||
credentials = TSCredentials()
|
||||
credentials.setComponentByName("credType", univ.Integer(1).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0)))
|
||||
credentials.setComponentByName("credentials", univ.OctetString(der_encoder.encode(passwordCred)).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
|
||||
|
||||
return der_encoder.encode(credentials)
|
||||
|
||||
class CSSP(protocol.Protocol):
|
||||
"""
|
||||
@summary: Handle CSSP connection
|
||||
Proxy class for authentication
|
||||
"""
|
||||
def __init__(self, layer, authenticationProtocol):
|
||||
"""
|
||||
@param layer: {type.Layer.RawLayer}
|
||||
@param authenticationProtocol: {sspi.IAuthenticationProtocol}
|
||||
"""
|
||||
self._layer = layer
|
||||
self._authenticationProtocol = authenticationProtocol
|
||||
#IGenericSecurityService
|
||||
self._interface = None
|
||||
#function call at the end of nego
|
||||
self._callback = None
|
||||
|
||||
def setFactory(self, factory):
|
||||
"""
|
||||
@summary: Call by RawLayer Factory
|
||||
@param param: RawLayerClientFactory or RawLayerFactory
|
||||
"""
|
||||
self._layer.setFactory(factory)
|
||||
|
||||
def dataReceived(self, data):
|
||||
"""
|
||||
@summary: Inherit from twisted.protocol class
|
||||
main event of received data
|
||||
@param data: string data receive from twisted
|
||||
"""
|
||||
self._layer.dataReceived(data)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"""
|
||||
@summary: Call from twisted engine when protocol is closed
|
||||
@param reason: str represent reason of close connection
|
||||
"""
|
||||
self._layer._factory.connectionLost(self, reason)
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
@summary: install proxy
|
||||
"""
|
||||
self._layer.transport = self
|
||||
self._layer.getDescriptor = lambda:self.transport
|
||||
self._layer.connectionMade()
|
||||
|
||||
def write(self, data):
|
||||
"""
|
||||
@summary: write data on transport layer
|
||||
@param data: {str}
|
||||
"""
|
||||
self.transport.write(data)
|
||||
|
||||
def startTLS(self, sslContext):
|
||||
"""
|
||||
@summary: start TLS protocol
|
||||
@param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for TLS protocol
|
||||
"""
|
||||
self.transport.startTLS(sslContext)
|
||||
|
||||
def startNLA(self, sslContext, callback = None):
|
||||
"""
|
||||
@summary: start NLA authentication
|
||||
@param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for TLS protocol
|
||||
@param callback: {function} function call when cssp layer is read
|
||||
"""
|
||||
self._callback = callback
|
||||
self.startTLS(sslContext)
|
||||
#send negotiate message
|
||||
self.transport.write(encodeDERTRequest( negoTypes = [ self._authenticationProtocol.getNegotiateMessage() ] ))
|
||||
#next state is receive a challenge
|
||||
self.dataReceived = self.recvChallenge
|
||||
|
||||
def recvChallenge(self, data):
|
||||
"""
|
||||
@summary: second state in cssp automata
|
||||
@param data : {str} all data available on buffer
|
||||
"""
|
||||
request = decodeDERTRequest(data)
|
||||
message, self._interface = self._authenticationProtocol.getAuthenticateMessage(getNegoTokens(request)[0])
|
||||
#get back public key
|
||||
#convert from der to ber...
|
||||
pubKeyDer = crypto.dump_privatekey(crypto.FILETYPE_ASN1, self.transport.protocol._tlsConnection.get_peer_certificate().get_pubkey())
|
||||
pubKey = der_decoder.decode(pubKeyDer, asn1Spec=OpenSSLRSAPublicKey())[0]
|
||||
|
||||
rsa = x509.RSAPublicKey()
|
||||
rsa.setComponentByName("modulus", univ.Integer(pubKey.getComponentByName('modulus')._value))
|
||||
rsa.setComponentByName("publicExponent", univ.Integer(pubKey.getComponentByName('publicExponent')._value))
|
||||
self._pubKeyBer = ber_encoder.encode(rsa)
|
||||
|
||||
#send authenticate message with public key encoded
|
||||
self.transport.write(encodeDERTRequest( negoTypes = [ message ], pubKeyAuth = self._interface.GSS_WrapEx(self._pubKeyBer)))
|
||||
#next step is received public key incremented by one
|
||||
self.dataReceived = self.recvPubKeyInc
|
||||
|
||||
def recvPubKeyInc(self, data):
|
||||
"""
|
||||
@summary: the server send the pubKeyBer + 1
|
||||
@param data : {str} all data available on buffer
|
||||
"""
|
||||
request = decodeDERTRequest(data)
|
||||
pubKeyInc = self._interface.GSS_UnWrapEx(getPubKeyAuth(request))
|
||||
#check pubKeyInc = self._pubKeyBer + 1
|
||||
if not (self._pubKeyBer[1:] == pubKeyInc[1:] and ord(self._pubKeyBer[0]) + 1 == ord(pubKeyInc[0])):
|
||||
raise error.InvalidExpectedDataException("CSSP : Invalid public key increment")
|
||||
|
||||
domain, user, password = self._authenticationProtocol.getEncodedCredentials()
|
||||
#send credentials
|
||||
self.transport.write(encodeDERTRequest( authInfo = self._interface.GSS_WrapEx(encodeDERTCredentials(domain, user, password))))
|
||||
#reset state back to normal state
|
||||
self.dataReceived = lambda x: self.__class__.dataReceived(self, x)
|
||||
if not self._callback is None:
|
||||
self._callback()
|
||||
@@ -1,224 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Transport packet layer implementation
|
||||
|
||||
Use to build correct size packet and handle slow path and fast path mode
|
||||
"""
|
||||
from rdpy.core.layer import RawLayer
|
||||
from rdpy.core.type import UInt8, UInt16Be, sizeof
|
||||
from rdpy.core.error import CallPureVirtualFuntion
|
||||
|
||||
class Action(object):
|
||||
"""
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240621.aspx
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240589.aspx
|
||||
"""
|
||||
FASTPATH_ACTION_FASTPATH = 0x0
|
||||
FASTPATH_ACTION_X224 = 0x3
|
||||
|
||||
class SecFlags(object):
|
||||
"""
|
||||
@see: http://msdn.microsoft.com/en-us/library/cc240621.aspx
|
||||
"""
|
||||
#hihi 'secure' checksum but private key is public !!!
|
||||
FASTPATH_OUTPUT_SECURE_CHECKSUM = 0x1
|
||||
FASTPATH_OUTPUT_ENCRYPTED = 0x2
|
||||
|
||||
class IFastPathListener(object):
|
||||
"""
|
||||
@summary: Fast path packet listener
|
||||
Usually X224 layer
|
||||
"""
|
||||
def recvFastPath(self, secFlag, fastPathS):
|
||||
"""
|
||||
@summary: Call when fast path packet is received
|
||||
@param secFlag: {SecFlags}
|
||||
@param fastPathS: {Stream}
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recvFastPath", "IFastPathListener"))
|
||||
|
||||
def initFastPath(self, fastPathSender):
|
||||
"""
|
||||
@summary: initialize stack
|
||||
@param fastPathSender: {IFastPathSender}
|
||||
"""
|
||||
self.setFastPathSender(fastPathSender)
|
||||
fastPathSender.setFastPathListener(self)
|
||||
|
||||
def setFastPathSender(self, fastPathSender):
|
||||
"""
|
||||
@param fastPathSender : {IFastPathSender}
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "setFastPathSender", "IFastPathListener"))
|
||||
|
||||
class IFastPathSender(object):
|
||||
"""
|
||||
@summary: Fast path send capability
|
||||
"""
|
||||
def sendFastPath(self, secFlag, fastPathS):
|
||||
"""
|
||||
@summary: Send fastPathS Type as fast path packet
|
||||
@param secFlag: {integer} Security flag for fastpath packet
|
||||
@param fastPathS: {Type | Tuple} type transform to stream and send as fastpath
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "sendFastPath", "IFastPathSender"))
|
||||
|
||||
def initFastPath(self, fastPathListener):
|
||||
"""
|
||||
@summary: initialize stack
|
||||
@param fastPathListener: {IFastPathListener}
|
||||
"""
|
||||
self.setFastPathListener(fastPathListener)
|
||||
fastPathListener.setFastPathSender(self)
|
||||
|
||||
def setFastPathListener(self, fastPathListener):
|
||||
"""
|
||||
@param fastPathListener: {IFastPathListener}
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "setFastPathListener", "IFastPathSender"))
|
||||
|
||||
class TPKT(RawLayer, IFastPathSender):
|
||||
"""
|
||||
@summary: TPKT layer in RDP protocol stack
|
||||
represent the Raw Layer in stack (first layer)
|
||||
This layer only handle size of packet and determine if is a fast path packet
|
||||
"""
|
||||
def __init__(self, presentation):
|
||||
"""
|
||||
@param presentation: {Layer} presentation layer, in RDP case is x224 layer
|
||||
"""
|
||||
RawLayer.__init__(self, presentation)
|
||||
#length may be coded on more than 1 bytes
|
||||
self._lastShortLength = UInt8()
|
||||
#fast path listener
|
||||
self._fastPathListener = None
|
||||
#last secure flag
|
||||
self._secFlag = 0
|
||||
|
||||
def setFastPathListener(self, fastPathListener):
|
||||
"""
|
||||
@param fastPathListener : {IFastPathListener}
|
||||
@note: implement IFastPathSender
|
||||
"""
|
||||
self._fastPathListener = fastPathListener
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
@summary: 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
|
||||
if not self._presentation is None:
|
||||
self._presentation.connect()
|
||||
|
||||
def readHeader(self, data):
|
||||
"""
|
||||
@summary: Read header of TPKT packet
|
||||
@param data: {Stream} received from twisted layer
|
||||
"""
|
||||
#first read packet version
|
||||
version = UInt8()
|
||||
data.readType(version)
|
||||
#classic packet
|
||||
if version.value == Action.FASTPATH_ACTION_X224:
|
||||
#padding
|
||||
data.readType(UInt8())
|
||||
#read end header
|
||||
self.expect(2, self.readExtendedHeader)
|
||||
else:
|
||||
#is fast path packet
|
||||
self._secFlag = ((version.value >> 6) & 0x3)
|
||||
data.readType(self._lastShortLength)
|
||||
if self._lastShortLength.value & 0x80:
|
||||
#size is 1 byte more
|
||||
self.expect(1, self.readExtendedFastPathHeader)
|
||||
return
|
||||
self.expect(self._lastShortLength.value - 2, self.readFastPath)
|
||||
|
||||
|
||||
def readExtendedHeader(self, data):
|
||||
"""
|
||||
@summary: 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):
|
||||
"""
|
||||
@summary: Fast path header may be on 1 byte more
|
||||
@param data: {Stream} from twisted layer
|
||||
"""
|
||||
leftPart = UInt8()
|
||||
data.readType(leftPart)
|
||||
self._lastShortLength.value &= ~0x80
|
||||
packetSize = (self._lastShortLength.value << 8) + leftPart.value
|
||||
#next state is fast patn data
|
||||
self.expect(packetSize - 3, self.readFastPath)
|
||||
|
||||
def readFastPath(self, data):
|
||||
"""
|
||||
@summary: Fast path data
|
||||
@param data: {Stream} from twisted layer
|
||||
"""
|
||||
self._fastPathListener.recvFastPath(self._secFlag, data)
|
||||
self.expect(2, self.readHeader)
|
||||
|
||||
def readData(self, data):
|
||||
"""
|
||||
@summary: 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):
|
||||
"""
|
||||
@summary: Send encompassed data
|
||||
@param message: {network.Type} message to send
|
||||
"""
|
||||
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_X224), UInt8(0), UInt16Be(sizeof(message) + 4), message))
|
||||
|
||||
def sendFastPath(self, secFlag, fastPathS):
|
||||
"""
|
||||
@param fastPathS: {Type | Tuple} type transform to stream and send as fastpath
|
||||
@param secFlag: {integer} Security flag for fastpath packet
|
||||
"""
|
||||
RawLayer.send(self, (UInt8(Action.FASTPATH_ACTION_FASTPATH | ((secFlag & 0x3) << 6)), UInt16Be((sizeof(fastPathS) + 3) | 0x8000), fastPathS))
|
||||
|
||||
def startTLS(self, sslContext):
|
||||
"""
|
||||
@summary: start TLS protocol
|
||||
@param sslContext: {ssl.ClientContextFactory | ssl.DefaultOpenSSLContextFactory} context use for TLS protocol
|
||||
"""
|
||||
self.transport.startTLS(sslContext)
|
||||
|
||||
def startNLA(self, sslContext, callback):
|
||||
"""
|
||||
@summary: use to start NLA (NTLM over SSL) protocol
|
||||
must be called after startTLS function
|
||||
"""
|
||||
self.transport.startNLA(sslContext, callback)
|
||||
@@ -1,765 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2014-2015 Sylvain Peyrefitte
|
||||
#
|
||||
# This file is part of rdpy.
|
||||
#
|
||||
# rdpy is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
"""
|
||||
Implement Remote FrameBuffer protocol use in VNC client and server
|
||||
@see: http://www.realvnc.com/docs/rfbproto.pdf
|
||||
|
||||
@todo: server side of protocol
|
||||
@todo: more encoding rectangle
|
||||
"""
|
||||
|
||||
from rdpy.core.layer import RawLayer, RawLayerClientFactory
|
||||
from rdpy.core.type import UInt8, UInt16Be, UInt32Be, SInt32Be, String, CompositeType
|
||||
from rdpy.core.error import InvalidValue, CallPureVirtualFuntion
|
||||
from rdpy.security.pyDes import des
|
||||
import rdpy.core.log as log
|
||||
|
||||
class ProtocolVersion(object):
|
||||
"""
|
||||
@summary: Different protocol version
|
||||
"""
|
||||
UNKNOWN = ""
|
||||
RFB003003 = "RFB 003.003\n"
|
||||
RFB003007 = "RFB 003.007\n"
|
||||
RFB003008 = "RFB 003.008\n"
|
||||
|
||||
class SecurityType(object):
|
||||
"""
|
||||
@summary: Security type supported
|
||||
"""
|
||||
INVALID = 0
|
||||
NONE = 1
|
||||
VNC = 2
|
||||
|
||||
class Pointer(object):
|
||||
"""
|
||||
@summary: Mouse event code (which button)
|
||||
actually in RFB specification only
|
||||
three buttons are supported
|
||||
"""
|
||||
BUTTON1 = 0x1
|
||||
BUTTON2 = 0x2
|
||||
BUTTON3 = 0x4
|
||||
|
||||
class Encoding(object):
|
||||
"""
|
||||
@summary: Encoding types of FrameBuffer update
|
||||
"""
|
||||
RAW = 0
|
||||
|
||||
class ClientToServerMessages(object):
|
||||
"""
|
||||
@summary: Client to server messages types
|
||||
"""
|
||||
PIXEL_FORMAT = 0
|
||||
ENCODING = 2
|
||||
FRAME_BUFFER_UPDATE_REQUEST = 3
|
||||
KEY_EVENT = 4
|
||||
POINTER_EVENT = 5
|
||||
CUT_TEXT = 6
|
||||
|
||||
class PixelFormat(CompositeType):
|
||||
"""
|
||||
@summary: Pixel format structure
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.BitsPerPixel = UInt8(32)
|
||||
self.Depth = UInt8(24)
|
||||
self.BigEndianFlag = UInt8(False)
|
||||
self.TrueColorFlag = UInt8(True)
|
||||
self.RedMax = UInt16Be(255)
|
||||
self.GreenMax = UInt16Be(255)
|
||||
self.BlueMax = UInt16Be(255)
|
||||
self.RedShift = UInt8(16)
|
||||
self.GreenShift = UInt8(8)
|
||||
self.BlueShift = UInt8(0)
|
||||
self.padding = (UInt16Be(), UInt8())
|
||||
|
||||
class ServerInit(CompositeType):
|
||||
"""
|
||||
@summary: Server init structure
|
||||
FrameBuffer configuration
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.width = UInt16Be()
|
||||
self.height = UInt16Be()
|
||||
self.pixelFormat = PixelFormat()
|
||||
|
||||
class FrameBufferUpdateRequest(CompositeType):
|
||||
"""
|
||||
@summary: FrameBuffer update request send from client to server
|
||||
Incremental means that server send update with a specific
|
||||
order, and client must draw orders in same order
|
||||
"""
|
||||
def __init__(self, incremental = False, x = 0, y = 0, width = 0, height = 0):
|
||||
CompositeType.__init__(self)
|
||||
self.incremental = UInt8(incremental)
|
||||
self.x = UInt16Be(x)
|
||||
self.y = UInt16Be(y)
|
||||
self.width = UInt16Be(width)
|
||||
self.height = UInt16Be(height)
|
||||
|
||||
|
||||
class Rectangle(CompositeType):
|
||||
"""
|
||||
@summary: Header message of update rectangle
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.x = UInt16Be()
|
||||
self.y = UInt16Be()
|
||||
self.width = UInt16Be()
|
||||
self.height = UInt16Be()
|
||||
self.encoding = SInt32Be()
|
||||
|
||||
class KeyEvent(CompositeType):
|
||||
"""
|
||||
@summary: Key event structure message
|
||||
Use to send a keyboard event
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.downFlag = UInt8(False)
|
||||
self.padding = UInt16Be()
|
||||
self.key = UInt32Be()
|
||||
|
||||
class PointerEvent(CompositeType):
|
||||
"""
|
||||
@summary: Pointer event structure message
|
||||
Use to send mouse event
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.mask = UInt8()
|
||||
self.x = UInt16Be()
|
||||
self.y = UInt16Be()
|
||||
|
||||
class ClientCutText(CompositeType):
|
||||
"""
|
||||
@summary: Client cut text message message
|
||||
Use to simulate copy paste (ctrl-c ctrl-v) only for text
|
||||
"""
|
||||
def __init__(self, text = ""):
|
||||
CompositeType.__init__(self)
|
||||
self.padding = (UInt16Be(), UInt8())
|
||||
self.size = UInt32Be(len(text))
|
||||
self.message = String(text)
|
||||
|
||||
class ServerCutTextHeader(CompositeType):
|
||||
"""
|
||||
@summary: Cut text header send from server to client
|
||||
"""
|
||||
def __init__(self):
|
||||
CompositeType.__init__(self)
|
||||
self.padding = (UInt16Be(), UInt8())
|
||||
self.size = UInt32Be()
|
||||
|
||||
class RFB(RawLayer):
|
||||
"""
|
||||
@summary: Implement RFB protocol
|
||||
"""
|
||||
def __init__(self, listener):
|
||||
"""
|
||||
@param listener: listener use to inform new orders
|
||||
"""
|
||||
RawLayer.__init__(self)
|
||||
#set client listener
|
||||
self._clientListener = listener
|
||||
#useful for RFB protocol
|
||||
self._callbackBody = None
|
||||
#protocol version negotiated
|
||||
self._version = String(ProtocolVersion.RFB003008)
|
||||
#number security launch by server
|
||||
self._securityLevel = UInt8(SecurityType.INVALID)
|
||||
#shared FrameBuffer client init message
|
||||
self._sharedFlag = UInt8(False)
|
||||
#server init message
|
||||
#which contain FrameBuffer dim and pixel format
|
||||
self._serverInit = ServerInit()
|
||||
#client pixel format
|
||||
self._pixelFormat = PixelFormat()
|
||||
#server name
|
||||
self._serverName = String()
|
||||
#nb rectangle
|
||||
self._nbRect = 0
|
||||
#current rectangle header
|
||||
self._currentRect = Rectangle()
|
||||
#for vnc security type
|
||||
self._password = '\0' * 8
|
||||
|
||||
def expectWithHeader(self, expectedHeaderLen, callbackBody):
|
||||
"""
|
||||
2nd level of waiting event
|
||||
read expectedHeaderLen that contain body size
|
||||
@param expectedHeaderLen: contains the number of bytes, which body length needs to be encoded
|
||||
@param callbackBody: next state use when expected date from expectedHeaderLen
|
||||
are received
|
||||
"""
|
||||
self._callbackBody = callbackBody
|
||||
self.expect(expectedHeaderLen, self.expectedBody)
|
||||
|
||||
def expectedBody(self, data):
|
||||
"""
|
||||
Read header and wait header value to call next state
|
||||
@param data: Stream that length are to header length (1|2|4 bytes)
|
||||
set next state to callBack body when length read from header
|
||||
are received
|
||||
"""
|
||||
bodyLen = None
|
||||
if data.len == 1:
|
||||
bodyLen = UInt8()
|
||||
elif data.len == 2:
|
||||
bodyLen = UInt16Be()
|
||||
elif data.len == 4:
|
||||
bodyLen = UInt32Be()
|
||||
else:
|
||||
log.error("invalid header length")
|
||||
return
|
||||
data.readType(bodyLen)
|
||||
self.expect(bodyLen.value, self._callbackBody)
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Call when transport layer connection is made
|
||||
in Client mode -> wait protocol version
|
||||
"""
|
||||
self.expect(12, self.recvProtocolVersion)
|
||||
|
||||
def readProtocolVersion(self, data):
|
||||
"""
|
||||
Read protocol version
|
||||
@param data: Stream may contain protocol version string (ProtocolVersion)
|
||||
"""
|
||||
data.readType(self._version)
|
||||
if not self._version.value in [ProtocolVersion.RFB003003, ProtocolVersion.RFB003007, ProtocolVersion.RFB003008]:
|
||||
self._version.value = ProtocolVersion.UNKNOWN
|
||||
|
||||
def recvProtocolVersion(self, data):
|
||||
"""
|
||||
Read handshake packet
|
||||
If protocol receive from client is unknown
|
||||
try best version of protocol version (ProtocolVersion.RFB003008)
|
||||
@param data: Stream
|
||||
"""
|
||||
self.readProtocolVersion(data)
|
||||
if self._version.value == ProtocolVersion.UNKNOWN:
|
||||
log.info("Unknown protocol version %s send 003.008"%data.getvalue())
|
||||
#protocol version is unknown try best version we can handle
|
||||
self._version.value = ProtocolVersion.RFB003008
|
||||
#send same version of
|
||||
self.send(self._version)
|
||||
|
||||
#next state read security
|
||||
if self._version.value == ProtocolVersion.RFB003003:
|
||||
self.expect(4, self.recvSecurityServer)
|
||||
else:
|
||||
self.expectWithHeader(1, self.recvSecurityList)
|
||||
|
||||
def recvSecurityServer(self, data):
|
||||
"""
|
||||
Security handshake for 33 RFB version
|
||||
Server imposed security level
|
||||
@param data: well formed packet
|
||||
"""
|
||||
#TODO!!!
|
||||
pass
|
||||
|
||||
def recvSecurityList(self, data):
|
||||
"""
|
||||
Read security list packet send from server to client
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
securityList = []
|
||||
while data.dataLen() > 0:
|
||||
securityElement = UInt8()
|
||||
data.readType(securityElement)
|
||||
securityList.append(securityElement)
|
||||
#select high security level
|
||||
for s in securityList:
|
||||
if s.value in [SecurityType.NONE, SecurityType.VNC] and s > self._securityLevel:
|
||||
self._securityLevel = s
|
||||
break
|
||||
#send back security level choosen
|
||||
self.send(self._securityLevel)
|
||||
if self._securityLevel.value == SecurityType.VNC:
|
||||
self.expect(16, self.recvVNCChallenge)
|
||||
else:
|
||||
self.expect(4, self.recvSecurityResult)
|
||||
|
||||
def recvVNCChallenge(self, data):
|
||||
"""
|
||||
@summary: receive challenge in VNC authentication case
|
||||
@param data: Stream that contain well formed packet
|
||||
"""
|
||||
key = (self._password + '\0' * 8)[:8]
|
||||
newkey = []
|
||||
for ki in range(len(key)):
|
||||
bsrc = ord(key[ki])
|
||||
btgt = 0
|
||||
for i in range(8):
|
||||
if bsrc & (1 << i):
|
||||
btgt = btgt | (1 << 7-i)
|
||||
newkey.append(chr(btgt))
|
||||
|
||||
algo = des(newkey)
|
||||
self.send(String(algo.encrypt(data.getvalue())))
|
||||
self.expect(4, self.recvSecurityResult)
|
||||
|
||||
def recvSecurityResult(self, data):
|
||||
"""
|
||||
Read security result packet
|
||||
Use by server to inform connection status of client
|
||||
@param data: Stream that contain well formed packet
|
||||
"""
|
||||
result = UInt32Be()
|
||||
data.readType(result)
|
||||
if result == UInt32Be(1):
|
||||
log.info("Authentification failed")
|
||||
if self._version.value == ProtocolVersion.RFB003008:
|
||||
self.expectWithHeader(4, self.recvSecurityFailed)
|
||||
else:
|
||||
log.debug("Authentification OK")
|
||||
self.sendClientInit()
|
||||
|
||||
def recvSecurityFailed(self, data):
|
||||
"""
|
||||
Send by server to inform reason of why it's refused client
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
log.info("Security failed cause to %s"%data.getvalue())
|
||||
|
||||
def recvServerInit(self, data):
|
||||
"""
|
||||
Read server init packet
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
data.readType(self._serverInit)
|
||||
self.expectWithHeader(4, self.recvServerName)
|
||||
|
||||
def recvServerName(self, data):
|
||||
"""
|
||||
@summary: Read server name
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
data.readType(self._serverName)
|
||||
log.info("Server name %s"%str(self._serverName))
|
||||
#end of handshake
|
||||
#send pixel format
|
||||
self.sendPixelFormat(self._pixelFormat)
|
||||
#write encoding
|
||||
self.sendSetEncoding()
|
||||
#request entire zone
|
||||
self.sendFramebufferUpdateRequest(False, 0, 0, self._serverInit.width.value, self._serverInit.height.value)
|
||||
#now i'm ready to send event
|
||||
self._clientListener.onReady()
|
||||
self.expect(1, self.recvServerOrder)
|
||||
|
||||
def recvServerOrder(self, data):
|
||||
"""
|
||||
@summary: Read order receive from server
|
||||
Main function for bitmap update from server to client
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
packetType = UInt8()
|
||||
data.readType(packetType)
|
||||
if packetType.value == 0:
|
||||
self.expect(3, self.recvFrameBufferUpdateHeader)
|
||||
elif packetType.value == 2:
|
||||
self._clientListener.onBell()
|
||||
elif packetType.value == 3:
|
||||
self.expect(7, self.recvServerCutTextHeader)
|
||||
else:
|
||||
log.error("Unknown message type %s"%packetType.value)
|
||||
|
||||
def recvFrameBufferUpdateHeader(self, data):
|
||||
"""
|
||||
@summary: Read frame buffer update packet header
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
#padding
|
||||
nbRect = UInt16Be()
|
||||
self._nbRect = data.readType((UInt8(), nbRect))
|
||||
self._nbRect = nbRect.value
|
||||
self.expect(12, self.recvRectHeader)
|
||||
|
||||
def recvRectHeader(self, data):
|
||||
"""
|
||||
@summary: Read rectangle header
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
data.readType(self._currentRect)
|
||||
if self._currentRect.encoding.value == Encoding.RAW:
|
||||
self.expect(self._currentRect.width.value * self._currentRect.height.value * (self._pixelFormat.BitsPerPixel.value / 8), self.recvRectBody)
|
||||
|
||||
def recvRectBody(self, data):
|
||||
"""
|
||||
@summary: Read body of rectangle update
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
self._clientListener.recvRectangle(self._currentRect, self._pixelFormat, data.getvalue())
|
||||
|
||||
self._nbRect -= 1
|
||||
#if there is another rect to read
|
||||
if self._nbRect == 0:
|
||||
#job is finish send a request
|
||||
self.sendFramebufferUpdateRequest(True, 0, 0, self._serverInit.width.value, self._serverInit.height.value)
|
||||
self.expect(1, self.recvServerOrder)
|
||||
else:
|
||||
self.expect(12, self.recvRectHeader)
|
||||
|
||||
def recvServerCutTextHeader(self, data):
|
||||
"""
|
||||
@summary: callback when expect server cut text message
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
header = ServerCutTextHeader()
|
||||
data.readType(header)
|
||||
self.expect(header.size.value, self.recvServerCutTextBody)
|
||||
|
||||
def recvServerCutTextBody(self, data):
|
||||
"""
|
||||
@summary: Receive server cut text body
|
||||
@param data: Stream that contains well formed packet
|
||||
"""
|
||||
self._clientListener.onCutText(data.getvalue())
|
||||
self.expect(1, self.recvServerOrder)
|
||||
|
||||
def sendClientInit(self):
|
||||
"""
|
||||
@summary: Send client init packet
|
||||
"""
|
||||
self.send(self._sharedFlag)
|
||||
self.expect(20, self.recvServerInit)
|
||||
|
||||
def sendPixelFormat(self, pixelFormat):
|
||||
"""
|
||||
@summary: Send pixel format structure
|
||||
Very important packet that inform the image struct supported by the client
|
||||
@param pixelFormat: PixelFormat struct
|
||||
"""
|
||||
self.send((UInt8(ClientToServerMessages.PIXEL_FORMAT), UInt16Be(), UInt8(), pixelFormat))
|
||||
|
||||
def sendSetEncoding(self):
|
||||
"""
|
||||
@summary: Send set encoding packet
|
||||
Actually only RAW bitmap encoding are used
|
||||
"""
|
||||
self.send((UInt8(ClientToServerMessages.ENCODING), UInt8(), UInt16Be(1), SInt32Be(Encoding.RAW)))
|
||||
|
||||
def sendFramebufferUpdateRequest(self, incremental, x, y, width, height):
|
||||
"""
|
||||
@summary: Request server the specified zone
|
||||
incremental means request only change before last update
|
||||
"""
|
||||
self.send((UInt8(ClientToServerMessages.FRAME_BUFFER_UPDATE_REQUEST), FrameBufferUpdateRequest(incremental, x, y, width, height)))
|
||||
|
||||
def sendKeyEvent(self, keyEvent):
|
||||
"""
|
||||
@summary: Write key event packet
|
||||
@param keyEvent: KeyEvent struct to send
|
||||
"""
|
||||
self.send((UInt8(ClientToServerMessages.KEY_EVENT), keyEvent))
|
||||
|
||||
def sendPointerEvent(self, pointerEvent):
|
||||
"""
|
||||
@summary: Write pointer event packet
|
||||
@param pointerEvent: PointerEvent struct use
|
||||
"""
|
||||
self.send((UInt8(ClientToServerMessages.POINTER_EVENT), pointerEvent))
|
||||
|
||||
def sendClientCutText(self, text):
|
||||
"""
|
||||
@summary: write client cut text event packet
|
||||
"""
|
||||
self.send((UInt8(ClientToServerMessages.CUT_TEXT), ClientCutText(text)))
|
||||
|
||||
class RFBClientListener(object):
|
||||
"""
|
||||
@summary: Interface use to expose event receive from RFB layer
|
||||
"""
|
||||
def recvRectangle(self, rectangle, pixelFormat, data):
|
||||
"""
|
||||
@summary: Receive rectangle order
|
||||
Main update order type
|
||||
@param rectangle: Rectangle type header of packet
|
||||
@param pixelFormat: pixelFormat struct of current session
|
||||
@param data: image data
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "recvRectangle", "RFBClientListener"))
|
||||
|
||||
def onBell(self):
|
||||
"""
|
||||
@summary: receive bip from server
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onBell", "RFBClientListener"))
|
||||
|
||||
def onCutText(self, text):
|
||||
"""
|
||||
@summary: Receive cut text from server
|
||||
@param text: text inner cut text event
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onCutText", "RFBClientListener"))
|
||||
|
||||
|
||||
class RFBClientController(RFBClientListener):
|
||||
"""
|
||||
@summary: Class use to manage RFB order and dispatch throw observers for client side
|
||||
"""
|
||||
def __init__(self):
|
||||
self._clientObservers = []
|
||||
#rfb layer to send client orders
|
||||
self._rfbLayer = RFB(self)
|
||||
self._isReady = False
|
||||
|
||||
def getProtocol(self):
|
||||
"""
|
||||
@return: RFB layer build by controller
|
||||
"""
|
||||
return self._rfbLayer
|
||||
|
||||
def addClientObserver(self, observer):
|
||||
"""
|
||||
@summary: Add new observer for this protocol
|
||||
@param observer: new observer
|
||||
"""
|
||||
self._clientObservers.append(observer)
|
||||
observer._clientListener = self
|
||||
|
||||
def getWidth(self):
|
||||
"""
|
||||
@return: width of framebuffer
|
||||
"""
|
||||
return self._rfbLayer._serverInit.width.value
|
||||
|
||||
def getHeight(self):
|
||||
"""
|
||||
@return: height of framebuffer
|
||||
"""
|
||||
return self._rfbLayer._serverInit.height.value
|
||||
|
||||
def getScreen(self):
|
||||
"""
|
||||
@return: (width, height) of screen
|
||||
"""
|
||||
return (self.getWidth(), self.getHeight())
|
||||
|
||||
def setPassword(self, password):
|
||||
"""
|
||||
@summary: set password for vnc authentication type
|
||||
@param password: password for session
|
||||
"""
|
||||
self._rfbLayer._password = password
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: rfb stack is reday to send or receive event
|
||||
"""
|
||||
self._isReady = True
|
||||
for observer in self._clientObservers:
|
||||
observer.onReady()
|
||||
|
||||
def recvRectangle(self, rectangle, pixelFormat, data):
|
||||
"""
|
||||
@summary: Receive rectangle order
|
||||
Main update order type
|
||||
@param rectangle: Rectangle type header of packet
|
||||
@param pixelFormat: pixelFormat struct of current session
|
||||
@param data: image data
|
||||
"""
|
||||
for observer in self._clientObservers:
|
||||
observer.onUpdate(rectangle.width.value, rectangle.height.value, rectangle.x.value, rectangle.y.value, pixelFormat, rectangle.encoding, data)
|
||||
|
||||
def onBell(self):
|
||||
"""
|
||||
@summary: biiiip event
|
||||
"""
|
||||
for observer in self._clientObservers:
|
||||
observer.onBell()
|
||||
|
||||
def onCutText(self, text):
|
||||
"""
|
||||
@summary: receive cut text event
|
||||
@param text: text in cut text event
|
||||
"""
|
||||
for observer in self._clientObservers:
|
||||
observer.onCutText(text)
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: handle on close events
|
||||
"""
|
||||
if not self._isReady:
|
||||
log.debug("Close on non ready layer means authentication error")
|
||||
return
|
||||
for observer in self._clientObservers:
|
||||
observer.onClose()
|
||||
|
||||
def sendKeyEvent(self, isDown, key):
|
||||
"""
|
||||
@summary: Send a key event throw RFB protocol
|
||||
@param isDown: boolean notify if key is pressed or not (True if key is pressed)
|
||||
@param key: ASCII code of key
|
||||
"""
|
||||
if not self._isReady:
|
||||
log.info("Try to send key event on non ready layer")
|
||||
return
|
||||
try:
|
||||
event = KeyEvent()
|
||||
event.downFlag.value = isDown
|
||||
event.key.value = key
|
||||
|
||||
self._rfbLayer.sendKeyEvent(event)
|
||||
except InvalidValue:
|
||||
log.debug("Try to send an invalid key event")
|
||||
|
||||
def sendPointerEvent(self, mask, x, y):
|
||||
"""
|
||||
@summary: Send a pointer event throw RFB protocol
|
||||
@param mask: mask of button if button 1 and 3 are pressed then mask is 00000101
|
||||
@param x: x coordinate of mouse pointer
|
||||
@param y: y pointer of mouse pointer
|
||||
"""
|
||||
if not self._isReady:
|
||||
log.info("Try to send pointer event on non ready layer")
|
||||
return
|
||||
try:
|
||||
event = PointerEvent()
|
||||
event.mask.value = mask
|
||||
event.x.value = x
|
||||
event.y.value = y
|
||||
|
||||
self._rfbLayer.sendPointerEvent(event)
|
||||
except InvalidValue:
|
||||
log.debug("Try to send an invalid pointer event")
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
@summary: close rfb stack
|
||||
"""
|
||||
self._rfbLayer.close()
|
||||
|
||||
|
||||
class ClientFactory(RawLayerClientFactory):
|
||||
"""
|
||||
@summary: Twisted Factory of RFB protocol
|
||||
"""
|
||||
def buildRawLayer(self, addr):
|
||||
"""
|
||||
@summary: Function call by twisted on connection
|
||||
@param addr: address where client try to connect
|
||||
"""
|
||||
controller = RFBClientController()
|
||||
self.buildObserver(controller, addr)
|
||||
return controller.getProtocol()
|
||||
|
||||
def connectionLost(self, rfblayer, reason):
|
||||
"""
|
||||
@summary: Override this method to handle connection lost
|
||||
@param rfblayer: rfblayer that cause connectionLost event
|
||||
@param reason: twisted reason
|
||||
"""
|
||||
#call controller
|
||||
rfblayer._clientListener.onClose()
|
||||
|
||||
def buildObserver(self, controller, addr):
|
||||
"""
|
||||
@summary: Build an RFB observer object
|
||||
@param controller: controller use for rfb session
|
||||
@param addr: destination
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "buildObserver", "ClientFactory"))
|
||||
|
||||
|
||||
class RFBClientObserver(object):
|
||||
"""
|
||||
@summary: RFB client protocol observer
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
self._controller = controller
|
||||
self._controller.addClientObserver(self)
|
||||
|
||||
def getController(self):
|
||||
"""
|
||||
@return: RFB controller use by observer
|
||||
"""
|
||||
return self._controller
|
||||
|
||||
def keyEvent(self, isPressed, key):
|
||||
"""
|
||||
@summary: Send a key event
|
||||
@param isPressed: state of key
|
||||
@param key: ASCII code of key
|
||||
"""
|
||||
self._controller.sendKeyEvent(isPressed, key)
|
||||
|
||||
def mouseEvent(self, button, x, y):
|
||||
"""
|
||||
@summary: Send a mouse event to RFB Layer
|
||||
@param button: button number which is pressed (0,1,2,3,4,5,6,7)
|
||||
@param x: x coordinate of mouse pointer
|
||||
@param y: y coordinate of mouse pointer
|
||||
"""
|
||||
mask = 0
|
||||
if button == 1:
|
||||
mask = 1
|
||||
elif button > 1:
|
||||
mask = 1 << button - 1
|
||||
|
||||
self._controller.sendPointerEvent(mask, x, y)
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Event when network stack is ready to receive or send event
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onReady", "RFBClientObserver"))
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: On close event
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onClose", "RFBClientObserver"))
|
||||
|
||||
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
|
||||
"""
|
||||
@summary: Receive FrameBuffer update
|
||||
@param width : width of image
|
||||
@param height : height of image
|
||||
@param x : x position
|
||||
@param y : y position
|
||||
@param pixelFormat : pixel format struct from rfb.types
|
||||
@param encoding : encoding struct from rfb.types
|
||||
@param data : in respect of dataFormat and pixelFormat
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onUpdate", "RFBClientObserver"))
|
||||
|
||||
def onCutText(self, text):
|
||||
"""
|
||||
@summary: event when server send cut text event
|
||||
@param text: text received
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onCutText", "RFBClientObserver"))
|
||||
|
||||
def onBell(self):
|
||||
"""
|
||||
@summary: event when server send biiip
|
||||
"""
|
||||
raise CallPureVirtualFuntion("%s:%s defined by interface %s"%(self.__class__, "onBell", "RFBClientObserver"))
|
||||
@@ -24,7 +24,7 @@
|
||||
def KSA(key):
|
||||
keylength = len(key)
|
||||
|
||||
S = range(256)
|
||||
S = list(range(256))
|
||||
|
||||
j = 0
|
||||
for i in range(256):
|
||||
@@ -50,8 +50,10 @@ def RC4(key):
|
||||
S = KSA(key)
|
||||
return PRGA(S)
|
||||
|
||||
def RC4Key(key):
|
||||
return RC4([ord(c) for c in key])
|
||||
|
||||
def crypt(keystream, plaintext):
|
||||
return "".join([chr(ord(c) ^ keystream.next()) for c in plaintext])
|
||||
def RC4Key(key):
|
||||
return RC4(key)
|
||||
|
||||
|
||||
def crypt(keystream, plaintext: bytes) -> bytes:
|
||||
return bytes([c ^ next(keystream) for c in plaintext])
|
||||
|
||||
145
rdpy/ui/qt4.py
145
rdpy/ui/qt4.py
@@ -23,13 +23,12 @@ Qt specific code
|
||||
QRemoteDesktop is a widget use for render in rdpy
|
||||
"""
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from rdpy.protocol.rfb.rfb import RFBClientObserver
|
||||
from rdpy.protocol.rdp.rdp import RDPClientObserver
|
||||
from rdpy.core.error import CallPureVirtualFuntion
|
||||
from PyQt5 import QtWidgets
|
||||
from rdpy.core.rdp import RDPClientObserver
|
||||
from rdpy.model.error import CallPureVirtualFuntion
|
||||
import sys
|
||||
|
||||
import rdpy.core.log as log
|
||||
import rdpy.model.log as log
|
||||
import rle
|
||||
|
||||
class QAdaptor(object):
|
||||
@@ -72,112 +71,10 @@ def qtImageFormatFromRFBPixelFormat(pixelFormat):
|
||||
@summary: convert RFB pixel format to QtGui.QImage format
|
||||
"""
|
||||
if pixelFormat.BitsPerPixel.value == 32:
|
||||
return QtGui.QImage.Format_RGB32
|
||||
return QtWidgets.QImage.Format_RGB32
|
||||
elif pixelFormat.BitsPerPixel.value == 16:
|
||||
return QtGui.QImage.Format_RGB16
|
||||
return QtWidgets.QImage.Format_RGB16
|
||||
|
||||
class RFBClientQt(RFBClientObserver, QAdaptor):
|
||||
"""
|
||||
@summary: QAdaptor for specific RFB protocol stack
|
||||
is to an RFB observer
|
||||
"""
|
||||
def __init__(self, controller):
|
||||
"""
|
||||
@param controller: controller for observer
|
||||
@param width: width of widget
|
||||
@param height: height of widget
|
||||
"""
|
||||
RFBClientObserver.__init__(self, controller)
|
||||
self._widget = QRemoteDesktop(1024, 800, self)
|
||||
|
||||
def getWidget(self):
|
||||
"""
|
||||
@return: widget use for render
|
||||
"""
|
||||
return self._widget
|
||||
|
||||
def onUpdate(self, width, height, x, y, pixelFormat, encoding, data):
|
||||
"""
|
||||
@summary: Implement RFBClientObserver interface
|
||||
@param width: width of new image
|
||||
@param height: height of new image
|
||||
@param x: x position of new image
|
||||
@param y: y position of new image
|
||||
@param pixelFormat: pixefFormat structure in rfb.message.PixelFormat
|
||||
@param encoding: encoding type rfb.message.Encoding
|
||||
@param data: image data in accordance with pixel format and encoding
|
||||
"""
|
||||
imageFormat = qtImageFormatFromRFBPixelFormat(pixelFormat)
|
||||
if imageFormat is None:
|
||||
log.error("Receive image in bad format")
|
||||
return
|
||||
|
||||
image = QtGui.QImage(data, width, height, imageFormat)
|
||||
self._widget.notifyImage(x, y, image, width, height)
|
||||
|
||||
def onCutText(self, text):
|
||||
"""
|
||||
@summary: event when server send cut text event
|
||||
@param text: text received
|
||||
"""
|
||||
|
||||
def onBell(self):
|
||||
"""
|
||||
@summary: event when server send biiip
|
||||
"""
|
||||
|
||||
def onReady(self):
|
||||
"""
|
||||
@summary: Event when network stack is ready to receive or send event
|
||||
"""
|
||||
(width, height) = self._controller.getScreen()
|
||||
self._widget.resize(width, height)
|
||||
|
||||
def sendMouseEvent(self, e, isPressed):
|
||||
"""
|
||||
@summary: Convert Qt mouse event to RFB mouse event
|
||||
@param e: qMouseEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
button = e.button()
|
||||
buttonNumber = 0
|
||||
if button == QtCore.Qt.LeftButton:
|
||||
buttonNumber = 1
|
||||
elif button == QtCore.Qt.MidButton:
|
||||
buttonNumber = 2
|
||||
elif button == QtCore.Qt.RightButton:
|
||||
buttonNumber = 3
|
||||
self.mouseEvent(buttonNumber, e.pos().x(), e.pos().y())
|
||||
|
||||
def sendKeyEvent(self, e, isPressed):
|
||||
"""
|
||||
@summary: Convert Qt key press event to RFB press event
|
||||
@param e: qKeyEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
self.keyEvent(isPressed, e.nativeVirtualKey())
|
||||
|
||||
def sendWheelEvent(self, e):
|
||||
"""
|
||||
@summary: Convert Qt wheel event to RFB Wheel event
|
||||
@param e: QKeyEvent
|
||||
@param isPressed: event come from press or release action
|
||||
"""
|
||||
pass
|
||||
|
||||
def closeEvent(self, e):
|
||||
"""
|
||||
@summary: Call when you want to close connection
|
||||
@param: QCloseEvent
|
||||
"""
|
||||
self._controller.close()
|
||||
|
||||
def onClose(self):
|
||||
"""
|
||||
@summary: Call when stack is close
|
||||
"""
|
||||
#do something maybe a message
|
||||
pass
|
||||
|
||||
def RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data):
|
||||
"""
|
||||
@@ -195,36 +92,36 @@ def RDPBitmapToQtImage(width, height, bitsPerPixel, isCompress, data):
|
||||
if isCompress:
|
||||
buf = bytearray(width * height * 2)
|
||||
rle.bitmap_decompress(buf, width, height, data, 2)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB555)
|
||||
image = QtWidgets.QImage(buf, width, height, QtWidgets.QImage.Format_RGB555)
|
||||
else:
|
||||
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB555).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
image = QtWidgets.QImage(data, width, height, QtWidgets.QImage.Format_RGB555).transformed(QtWidgets.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
|
||||
elif bitsPerPixel == 16:
|
||||
if isCompress:
|
||||
buf = bytearray(width * height * 2)
|
||||
rle.bitmap_decompress(buf, width, height, data, 2)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB16)
|
||||
image = QtWidgets.QImage(buf, width, height, QtWidgets.QImage.Format_RGB16)
|
||||
else:
|
||||
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB16).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
image = QtWidgets.QImage(data, width, height, QtWidgets.QImage.Format_RGB16).transformed(QtWidgets.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
|
||||
elif bitsPerPixel == 24:
|
||||
if isCompress:
|
||||
buf = bytearray(width * height * 3)
|
||||
rle.bitmap_decompress(buf, width, height, data, 3)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB888)
|
||||
image = QtWidgets.QImage(buf, width, height, QtWidgets.QImage.Format_RGB888)
|
||||
else:
|
||||
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB888).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
image = QtWidgets.QImage(data, width, height, QtWidgets.QImage.Format_RGB888).transformed(QtWidgets.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
|
||||
elif bitsPerPixel == 32:
|
||||
if isCompress:
|
||||
buf = bytearray(width * height * 4)
|
||||
rle.bitmap_decompress(buf, width, height, data, 4)
|
||||
image = QtGui.QImage(buf, width, height, QtGui.QImage.Format_RGB32)
|
||||
image = QtWidgets.QImage(buf, width, height, QtWidgets.QImage.Format_RGB32)
|
||||
else:
|
||||
image = QtGui.QImage(data, width, height, QtGui.QImage.Format_RGB32).transformed(QtGui.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
image = QtWidgets.QImage(data, width, height, QtWidgets.QImage.Format_RGB32).transformed(QtWidgets.QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))
|
||||
else:
|
||||
log.error("Receive image in bad format")
|
||||
image = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
|
||||
image = QtWidgets.QImage(width, height, QtWidgets.QImage.Format_RGB32)
|
||||
return image
|
||||
|
||||
class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
@@ -330,7 +227,7 @@ class RDPClientQt(RDPClientObserver, QAdaptor):
|
||||
#do something maybe a message
|
||||
|
||||
|
||||
class QRemoteDesktop(QtGui.QWidget):
|
||||
class QRemoteDesktop(QtWidgets.QWidget):
|
||||
"""
|
||||
@summary: Qt display widget
|
||||
"""
|
||||
@@ -348,7 +245,7 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
#bind mouse event
|
||||
self.setMouseTracking(True)
|
||||
#buffer image
|
||||
self._buffer = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
|
||||
self._buffer = QtWidgets.QImage(width, height, QtWidgets.QImage.Format_RGB32)
|
||||
|
||||
def notifyImage(self, x, y, qimage, width, height):
|
||||
"""
|
||||
@@ -358,7 +255,7 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
@param qimage: new QImage
|
||||
"""
|
||||
#fill buffer image
|
||||
with QtGui.QPainter(self._buffer) as qp:
|
||||
with QtWidgets.QPainter(self._buffer) as qp:
|
||||
qp.drawImage(x, y, qimage, 0, 0, width, height)
|
||||
#force update
|
||||
self.update()
|
||||
@@ -369,8 +266,8 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
@param width: {int} width of widget
|
||||
@param height: {int} height of widget
|
||||
"""
|
||||
self._buffer = QtGui.QImage(width, height, QtGui.QImage.Format_RGB32)
|
||||
QtGui.QWidget.resize(self, width, height)
|
||||
self._buffer = QtWidgets.QImage(width, height, QtWidgets.QImage.Format_RGB32)
|
||||
QtWidgets.QWidget.resize(self, width, height)
|
||||
|
||||
def paintEvent(self, e):
|
||||
"""
|
||||
@@ -378,7 +275,7 @@ class QRemoteDesktop(QtGui.QWidget):
|
||||
@param e: QEvent
|
||||
"""
|
||||
#draw in widget
|
||||
with QtGui.QPainter(self) as qp:
|
||||
with QtWidgets.QPainter(self) as qp:
|
||||
qp.drawImage(0, 0, self._buffer)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
|
||||
20
setup.py
20
setup.py
@@ -4,7 +4,7 @@ import setuptools
|
||||
from distutils.core import setup, Extension
|
||||
|
||||
setup(name='rdpy',
|
||||
version='1.3.2',
|
||||
version='2.0.0',
|
||||
description='Remote Desktop Protocol in Python',
|
||||
long_description="""
|
||||
RDPY is a pure Python implementation of the Microsoft RDP (Remote Desktop Protocol) protocol (Client and Server side). RDPY is built over the event driven network engine Twisted.
|
||||
@@ -16,14 +16,11 @@ setup(name='rdpy',
|
||||
url='https://github.com/citronneur/rdpy',
|
||||
packages=[
|
||||
'rdpy',
|
||||
'rdpy.core',
|
||||
'rdpy.security',
|
||||
'rdpy.protocol',
|
||||
'rdpy.protocol.rdp',
|
||||
'rdpy.protocol.rdp.pdu',
|
||||
'rdpy.protocol.rdp.nla',
|
||||
'rdpy.protocol.rdp.t125',
|
||||
'rdpy.protocol.rfb',
|
||||
'rdpy.model',
|
||||
'rdpy.security',
|
||||
'rdpy.core.pdu',
|
||||
'rdpy.core.nla',
|
||||
'rdpy.core.t125',
|
||||
'rdpy.ui'
|
||||
],
|
||||
ext_modules=[Extension('rle', ['ext/rle.c'])],
|
||||
@@ -37,10 +34,9 @@ setup(name='rdpy',
|
||||
'bin/rdpy-vncscreenshot.py'
|
||||
],
|
||||
install_requires=[
|
||||
'twisted',
|
||||
'pyopenssl',
|
||||
'PyQt5',
|
||||
'PyQt5-sip',
|
||||
'service_identity',
|
||||
'qt4reactor',
|
||||
'rsa',
|
||||
'pyasn1'
|
||||
],
|
||||
|
||||
@@ -56,7 +56,7 @@ class LayerTest(unittest.TestCase):
|
||||
"""
|
||||
class TestAutomata(rdpy.core.layer.RawLayer):
|
||||
def expectedCallBack(self, data):
|
||||
if data.dataLen() == 4:
|
||||
if data.data_len() == 4:
|
||||
raise LayerTest.LayerCaseException()
|
||||
|
||||
t = TestAutomata()
|
||||
@@ -69,7 +69,7 @@ class LayerTest(unittest.TestCase):
|
||||
"""
|
||||
class TestAutomata(rdpy.core.layer.RawLayer):
|
||||
def expectedCallBack(self, data):
|
||||
if data.dataLen() == 4:
|
||||
if data.data_len() == 4:
|
||||
raise LayerTest.LayerCaseException()
|
||||
|
||||
t = TestAutomata()
|
||||
|
||||
@@ -56,7 +56,7 @@ class TypeTest(unittest.TestCase):
|
||||
def __write__(self, s):
|
||||
raise Exception()
|
||||
s = rdpy.core.type.Stream()
|
||||
self.assertRaises(Exception, s.writeType, TestType(conditional = lambda:True))
|
||||
self.assertRaises(Exception, s.write_type, TestType(conditional = lambda:True))
|
||||
|
||||
@unittest.expectedFailure
|
||||
def test_type_write_conditional_false(self):
|
||||
@@ -67,7 +67,7 @@ class TypeTest(unittest.TestCase):
|
||||
def __write__(self, s):
|
||||
raise Exception()
|
||||
s = rdpy.core.type.Stream()
|
||||
self.assertRaises(Exception, s.writeType, TestType(conditional = lambda:False))
|
||||
self.assertRaises(Exception, s.write_type, TestType(conditional = lambda:False))
|
||||
|
||||
def test_type_read_conditional_true(self):
|
||||
"""
|
||||
@@ -77,7 +77,7 @@ class TypeTest(unittest.TestCase):
|
||||
def __read__(self, s):
|
||||
raise Exception()
|
||||
s = rdpy.core.type.Stream()
|
||||
self.assertRaises(Exception, s.readType, TestType(conditional = lambda:True))
|
||||
self.assertRaises(Exception, s.read_type, TestType(conditional = lambda:True))
|
||||
|
||||
@unittest.expectedFailure
|
||||
def test_type_read_conditional_false(self):
|
||||
@@ -88,7 +88,7 @@ class TypeTest(unittest.TestCase):
|
||||
def __read__(self, s):
|
||||
raise Exception()
|
||||
s = rdpy.core.type.Stream()
|
||||
self.assertRaises(Exception, s.readType, TestType(conditional = lambda:False))
|
||||
self.assertRaises(Exception, s.read_type, TestType(conditional = lambda:False))
|
||||
|
||||
|
||||
def test_sizeof_conditional_true(self):
|
||||
@@ -138,7 +138,7 @@ class TypeTest(unittest.TestCase):
|
||||
@summary: test write uint8 in stream
|
||||
"""
|
||||
s = rdpy.core.type.Stream()
|
||||
s.writeType(rdpy.core.type.UInt8(1))
|
||||
s.write_type(rdpy.core.type.UInt8(1))
|
||||
self.assertEqual(''.join(s.buflist), '\x01', "invalid stream write")
|
||||
|
||||
def test_stream_write_uint16Le_type(self):
|
||||
@@ -146,7 +146,7 @@ class TypeTest(unittest.TestCase):
|
||||
@summary: test write UInt16Le in stream
|
||||
"""
|
||||
s = rdpy.core.type.Stream()
|
||||
s.writeType(rdpy.core.type.UInt16Le(1))
|
||||
s.write_type(rdpy.core.type.UInt16Le(1))
|
||||
self.assertEqual(''.join(s.buflist), '\x01\x00', "invalid stream write")
|
||||
|
||||
def test_stream_write_uint16Be_type(self):
|
||||
@@ -154,7 +154,7 @@ class TypeTest(unittest.TestCase):
|
||||
@summary: test write UInt16Be in stream
|
||||
"""
|
||||
s = rdpy.core.type.Stream()
|
||||
s.writeType(rdpy.core.type.UInt16Be(1))
|
||||
s.write_type(rdpy.core.type.UInt16Be(1))
|
||||
self.assertEqual(''.join(s.buflist), '\x00\x01', "invalid stream write")
|
||||
|
||||
def test_stream_write_uint24Le_type(self):
|
||||
@@ -162,7 +162,7 @@ class TypeTest(unittest.TestCase):
|
||||
@summary: test write UInt24Le in stream
|
||||
"""
|
||||
s = rdpy.core.type.Stream()
|
||||
s.writeType(rdpy.core.type.UInt24Le(1))
|
||||
s.write_type(rdpy.core.type.UInt24Le(1))
|
||||
self.assertEqual(''.join(s.buflist), '\x01\x00\x00', "invalid stream write")
|
||||
|
||||
def test_stream_write_uint24Be_type(self):
|
||||
@@ -170,7 +170,7 @@ class TypeTest(unittest.TestCase):
|
||||
@summary: test write uint24Be in stream
|
||||
"""
|
||||
s = rdpy.core.type.Stream()
|
||||
s.writeType(rdpy.core.type.UInt24Be(1))
|
||||
s.write_type(rdpy.core.type.UInt24Be(1))
|
||||
self.assertEqual(''.join(s.buflist), '\x00\x00\x01', "invalid stream write")
|
||||
|
||||
def test_stream_write_uint32Le_type(self):
|
||||
@@ -178,7 +178,7 @@ class TypeTest(unittest.TestCase):
|
||||
@summary: test write UInt32Le in stream
|
||||
"""
|
||||
s = rdpy.core.type.Stream()
|
||||
s.writeType(rdpy.core.type.UInt32Le(1))
|
||||
s.write_type(rdpy.core.type.UInt32Le(1))
|
||||
self.assertEqual(''.join(s.buflist), '\x01\x00\x00\x00', "invalid stream write")
|
||||
|
||||
def test_stream_write_uint32Be_type(self):
|
||||
@@ -186,7 +186,7 @@ class TypeTest(unittest.TestCase):
|
||||
@summary: test write UInt32Be in stream
|
||||
"""
|
||||
s = rdpy.core.type.Stream()
|
||||
s.writeType(rdpy.core.type.UInt32Be(1))
|
||||
s.write_type(rdpy.core.type.UInt32Be(1))
|
||||
self.assertEqual(''.join(s.buflist), '\x00\x00\x00\x01', "invalid stream write")
|
||||
|
||||
def test_stream_read_uint8_type(self):
|
||||
@@ -195,9 +195,9 @@ class TypeTest(unittest.TestCase):
|
||||
"""
|
||||
s = rdpy.core.type.Stream('\x01')
|
||||
t = rdpy.core.type.UInt8()
|
||||
s.readType(t)
|
||||
s.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read value")
|
||||
self.assertEqual(s.dataLen(), 0, "not read all stream")
|
||||
self.assertEqual(s.data_len(), 0, "not read all stream")
|
||||
|
||||
def test_stream_read_uint16Le_type(self):
|
||||
"""
|
||||
@@ -205,9 +205,9 @@ class TypeTest(unittest.TestCase):
|
||||
"""
|
||||
s = rdpy.core.type.Stream('\x01\x00')
|
||||
t = rdpy.core.type.UInt16Le()
|
||||
s.readType(t)
|
||||
s.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read value")
|
||||
self.assertEqual(s.dataLen(), 0, "not read all stream")
|
||||
self.assertEqual(s.data_len(), 0, "not read all stream")
|
||||
|
||||
def test_stream_read_uint16Be_type(self):
|
||||
"""
|
||||
@@ -215,9 +215,9 @@ class TypeTest(unittest.TestCase):
|
||||
"""
|
||||
s = rdpy.core.type.Stream('\x00\x01')
|
||||
t = rdpy.core.type.UInt16Be()
|
||||
s.readType(t)
|
||||
s.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read value")
|
||||
self.assertEqual(s.dataLen(), 0, "not read all stream")
|
||||
self.assertEqual(s.data_len(), 0, "not read all stream")
|
||||
|
||||
def test_stream_read_uint24Le_type(self):
|
||||
"""
|
||||
@@ -225,9 +225,9 @@ class TypeTest(unittest.TestCase):
|
||||
"""
|
||||
s = rdpy.core.type.Stream('\x01\x00\x00')
|
||||
t = rdpy.core.type.UInt24Le()
|
||||
s.readType(t)
|
||||
s.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read value")
|
||||
self.assertEqual(s.dataLen(), 0, "not read all stream")
|
||||
self.assertEqual(s.data_len(), 0, "not read all stream")
|
||||
|
||||
def test_stream_read_uint24Be_type(self):
|
||||
"""
|
||||
@@ -235,9 +235,9 @@ class TypeTest(unittest.TestCase):
|
||||
"""
|
||||
s = rdpy.core.type.Stream('\x00\x00\x01')
|
||||
t = rdpy.core.type.UInt24Be()
|
||||
s.readType(t)
|
||||
s.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read")
|
||||
self.assertEqual(s.dataLen(), 0, "not read all stream")
|
||||
self.assertEqual(s.data_len(), 0, "not read all stream")
|
||||
|
||||
def test_stream_read_uint32Le_type(self):
|
||||
"""
|
||||
@@ -245,9 +245,9 @@ class TypeTest(unittest.TestCase):
|
||||
"""
|
||||
s = rdpy.core.type.Stream('\x01\x00\x00\x00')
|
||||
t = rdpy.core.type.UInt32Le()
|
||||
s.readType(t)
|
||||
s.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read value")
|
||||
self.assertEqual(s.dataLen(), 0, "not read all stream")
|
||||
self.assertEqual(s.data_len(), 0, "not read all stream")
|
||||
|
||||
def test_stream_read_uint32Be_type(self):
|
||||
"""
|
||||
@@ -255,9 +255,9 @@ class TypeTest(unittest.TestCase):
|
||||
"""
|
||||
s = rdpy.core.type.Stream('\x00\x00\x00\x01')
|
||||
t = rdpy.core.type.UInt32Be()
|
||||
s.readType(t)
|
||||
s.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read")
|
||||
self.assertEqual(s.dataLen(), 0, "not read all stream")
|
||||
self.assertEqual(s.data_len(), 0, "not read all stream")
|
||||
|
||||
def test_stream_read_optional_singletype(self):
|
||||
"""
|
||||
@@ -267,7 +267,7 @@ class TypeTest(unittest.TestCase):
|
||||
t = rdpy.core.type.SimpleType("I", 4, False, 0, optional = True)
|
||||
#empty stream
|
||||
s1 = rdpy.core.type.Stream()
|
||||
s1.readType(t)
|
||||
s1.read_type(t)
|
||||
self.assertEqual(t.value, 0, "invalid stream read optional value")
|
||||
|
||||
def test_stream_read_conditional_singletype_false(self):
|
||||
@@ -277,7 +277,7 @@ class TypeTest(unittest.TestCase):
|
||||
#unsigned int case
|
||||
t = rdpy.core.type.SimpleType("I", 4, False, 0, conditional = lambda:False)
|
||||
s1 = rdpy.core.type.Stream("\x01\x00\x00\x00")
|
||||
s1.readType(t)
|
||||
s1.read_type(t)
|
||||
self.assertEqual(t.value, 0, "invalid stream read conditional value")
|
||||
|
||||
def test_stream_read_conditional_singletype_true(self):
|
||||
@@ -287,7 +287,7 @@ class TypeTest(unittest.TestCase):
|
||||
#unsigned int case
|
||||
t = rdpy.core.type.SimpleType("I", 4, False, 0, conditional = lambda:True)
|
||||
s1 = rdpy.core.type.Stream("\x01\x00\x00\x00")
|
||||
s1.readType(t)
|
||||
s1.read_type(t)
|
||||
self.assertEqual(t.value, 1, "invalid stream read conditional value")
|
||||
|
||||
def test_stream_read_rollback_constant_constraint(self):
|
||||
@@ -302,9 +302,9 @@ class TypeTest(unittest.TestCase):
|
||||
|
||||
s = rdpy.core.type.Stream("\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
try:
|
||||
s.readType(TestComposite())
|
||||
s.read_type(TestComposite())
|
||||
except Exception:
|
||||
self.assertEqual(s.readLen(), 0, "invalid stream roll back operation")
|
||||
self.assertEqual(s.read_len(), 0, "invalid stream roll back operation")
|
||||
return
|
||||
self.assertTrue(False, "Constant constraint fail")
|
||||
|
||||
@@ -327,9 +327,9 @@ class TypeTest(unittest.TestCase):
|
||||
|
||||
s = rdpy.core.type.Stream("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
try:
|
||||
s.readType(TestComposite())
|
||||
s.read_type(TestComposite())
|
||||
except Exception:
|
||||
self.assertEqual(s.readLen(), 0, "invalid stream roll back operation")
|
||||
self.assertEqual(s.read_len(), 0, "invalid stream roll back operation")
|
||||
return
|
||||
self.assertTrue(False, "Constant constraint fail")
|
||||
|
||||
@@ -352,9 +352,9 @@ class TypeTest(unittest.TestCase):
|
||||
|
||||
s = rdpy.core.type.Stream("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
try:
|
||||
s.readType(TestComposite())
|
||||
s.read_type(TestComposite())
|
||||
except Exception:
|
||||
self.assertEqual(s.readLen(), 0, "invalid stream roll back operation")
|
||||
self.assertEqual(s.read_len(), 0, "invalid stream roll back operation")
|
||||
return
|
||||
self.assertTrue(False, "Constant constraint fail")
|
||||
|
||||
@@ -369,8 +369,8 @@ class TypeTest(unittest.TestCase):
|
||||
rdpy.core.type.CompositeType.__init__(self, readLen = readLen)
|
||||
self.padding = rdpy.core.type.UInt32Le(0)
|
||||
s = rdpy.core.type.Stream("\x00" * 10)
|
||||
s.readType(TestReadLength(rdpy.core.type.UInt8(10)))
|
||||
self.assertEqual(s.dataLen(), 0, "invalid stream read trash data as padding")
|
||||
s.read_type(TestReadLength(rdpy.core.type.UInt8(10)))
|
||||
self.assertEqual(s.data_len(), 0, "invalid stream read trash data as padding")
|
||||
|
||||
def test_stream_read_with_static_length_inferior(self):
|
||||
"""
|
||||
@@ -383,7 +383,7 @@ class TypeTest(unittest.TestCase):
|
||||
rdpy.core.type.CompositeType.__init__(self, readLen = readLen)
|
||||
self.padding = rdpy.core.type.UInt32Le(0)
|
||||
s = rdpy.core.type.Stream("\x00" * 10)
|
||||
self.assertRaises(InvalidSize, s.readType, TestReadLength(rdpy.core.type.UInt8(2)))
|
||||
self.assertRaises(InvalidSize, s.read_type, TestReadLength(rdpy.core.type.UInt8(2)))
|
||||
|
||||
def test_stream_read_string(self):
|
||||
"""
|
||||
|
||||
@@ -26,9 +26,9 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
import rdpy.protocol.rdp.t125.ber as ber
|
||||
import rdpy.core.t125.ber as ber
|
||||
import rdpy.core.type as type
|
||||
import rdpy.core.error as error
|
||||
|
||||
|
||||
class BERTest(unittest.TestCase):
|
||||
"""
|
||||
@@ -40,7 +40,7 @@ class BERTest(unittest.TestCase):
|
||||
@summary: test readLength function in ber module
|
||||
"""
|
||||
s1 = type.Stream()
|
||||
s1.writeType(type.UInt8(0x1a))
|
||||
s1.write_type(type.UInt8(0x1a))
|
||||
s1.pos = 0
|
||||
|
||||
l1 = ber.readLength(s1)
|
||||
@@ -48,7 +48,7 @@ class BERTest(unittest.TestCase):
|
||||
self.assertTrue(l1 == 0x1a, "readLength fail in small format")
|
||||
|
||||
s2 = type.Stream()
|
||||
s2.writeType((type.UInt8(0x81),type.UInt8(0xab)))
|
||||
s2.write_type((type.UInt8(0x81), type.UInt8(0xab)))
|
||||
s2.pos = 0
|
||||
|
||||
l2 = ber.readLength(s2)
|
||||
@@ -56,7 +56,7 @@ class BERTest(unittest.TestCase):
|
||||
self.assertTrue(l2 == 0xab, "readLength fail in big format of size 1")
|
||||
|
||||
s3 = type.Stream()
|
||||
s3.writeType((type.UInt8(0x82),type.UInt16Be(0xabab)))
|
||||
s3.write_type((type.UInt8(0x82), type.UInt16Be(0xabab)))
|
||||
s3.pos = 0
|
||||
|
||||
l3 = ber.readLength(s3)
|
||||
|
||||
@@ -25,7 +25,7 @@ import os, sys
|
||||
# Change path so we find rdpy
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
from rdpy.protocol.rdp.nla import cssp, ntlm
|
||||
from rdpy.core.nla import ntlm, cssp
|
||||
from rdpy.security import rc4
|
||||
|
||||
pubKeyHex = """
|
||||
@@ -73,25 +73,25 @@ class TestCsspNtlm(unittest.TestCase):
|
||||
@summary: test generate ntlmv2 over cssp authentication protocol
|
||||
"""
|
||||
def testCSSPNTLMAuthentication(self):
|
||||
negotiate_data_request = cssp.decodeDERTRequest(peer0_0.decode('base64'))
|
||||
challenge_data_request = cssp.decodeDERTRequest(peer1_0.decode('base64'))
|
||||
authenticate_data_request = cssp.decodeDERTRequest(peer0_1.decode('base64'))
|
||||
negotiate_data_request = cssp.decode_der_trequest(peer0_0.decode('base64'))
|
||||
challenge_data_request = cssp.decode_der_trequest(peer1_0.decode('base64'))
|
||||
authenticate_data_request = cssp.decode_der_trequest(peer0_1.decode('base64'))
|
||||
|
||||
negotiate_data = cssp.getNegoTokens(negotiate_data_request)[0]
|
||||
challenge_data = cssp.getNegoTokens(challenge_data_request)[0]
|
||||
authenticate_data = cssp.getNegoTokens(authenticate_data_request)[0]
|
||||
|
||||
negotiate = ntlm.NegotiateMessage()
|
||||
negotiate_data.readType(negotiate)
|
||||
negotiate_data.read_type(negotiate)
|
||||
|
||||
challenge = ntlm.ChallengeMessage()
|
||||
challenge_data.readType(challenge)
|
||||
challenge_data.read_type(challenge)
|
||||
|
||||
ServerChallenge = challenge.ServerChallenge.value
|
||||
ServerName = challenge.getTargetInfo()
|
||||
|
||||
authenticate = ntlm.AuthenticateMessage()
|
||||
authenticate_data.readType(authenticate)
|
||||
authenticate_data.read_type(authenticate)
|
||||
|
||||
NtChallengeResponseTemp = authenticate.getNtChallengeResponse()
|
||||
NTProofStr = NtChallengeResponseTemp[:16]
|
||||
|
||||
@@ -26,7 +26,7 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
from rdpy.protocol.rdp import lic, sec
|
||||
from rdpy.core import lic, sec
|
||||
import rdpy.core.type as type
|
||||
|
||||
#dump of server request
|
||||
@@ -91,7 +91,7 @@ class TestLic(unittest.TestCase):
|
||||
def test_valid_client_licensing_error_message(self):
|
||||
l = lic.LicenseManager(None)
|
||||
s = type.Stream()
|
||||
s.writeType(lic.createValidClientLicensingErrorMessage())
|
||||
s.write_type(lic.createValidClientLicensingErrorMessage())
|
||||
#reinit position
|
||||
s.pos = 0
|
||||
|
||||
@@ -105,9 +105,9 @@ class TestLic(unittest.TestCase):
|
||||
if flag != sec.SecurityFlag.SEC_LICENSE_PKT:
|
||||
return
|
||||
s = type.Stream()
|
||||
s.writeType(message)
|
||||
s.write_type(message)
|
||||
s.pos = 0
|
||||
s.readType(lic.LicPacket(lic.ClientNewLicenseRequest()))
|
||||
s.read_type(lic.LicPacket(lic.ClientNewLicenseRequest()))
|
||||
self._state = True
|
||||
def getGCCServerSettings(self):
|
||||
class A:
|
||||
|
||||
@@ -26,7 +26,7 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
import rdpy.protocol.rdp.t125.per as per
|
||||
import rdpy.core.t125.per as per
|
||||
import rdpy.core.type as type
|
||||
import rdpy.core.error as error
|
||||
|
||||
@@ -40,7 +40,7 @@ class PERTest(unittest.TestCase):
|
||||
@summary: test readLength function in per module
|
||||
"""
|
||||
s1 = type.Stream()
|
||||
s1.writeType(type.UInt8(0x1a))
|
||||
s1.write_type(type.UInt8(0x1a))
|
||||
s1.pos = 0
|
||||
|
||||
l1 = per.readLength(s1)
|
||||
@@ -48,7 +48,7 @@ class PERTest(unittest.TestCase):
|
||||
self.assertTrue(l1 == 0x1a, "readLength fail in small format")
|
||||
|
||||
s2 = type.Stream()
|
||||
s2.writeType(type.UInt16Be(0x1abc | 0x8000))
|
||||
s2.write_type(type.UInt16Be(0x1abc | 0x8000))
|
||||
s2.pos = 0
|
||||
|
||||
l2 = per.readLength(s2)
|
||||
@@ -78,15 +78,15 @@ class PERTest(unittest.TestCase):
|
||||
for t in [type.UInt8, type.UInt16Be, type.UInt32Be]:
|
||||
v = t(3)
|
||||
s = type.Stream()
|
||||
s.writeType((per.writeLength(type.sizeof(v)), v))
|
||||
s.write_type((per.writeLength(type.sizeof(v)), v))
|
||||
s.pos = 0
|
||||
|
||||
self.assertTrue(per.readInteger(s) == 3, "invalid readLength for type %s"%t)
|
||||
self.assertTrue(per.readInteger(s) == 3, "invalid readLength for type %s" % t)
|
||||
|
||||
#error case
|
||||
for l in [0, 3, 5]:
|
||||
s = type.Stream()
|
||||
s.writeType(per.writeLength(l))
|
||||
s.write_type(per.writeLength(l))
|
||||
s.pos = 0
|
||||
|
||||
self.assertRaises(error.InvalidValue, per.readInteger, s)
|
||||
|
||||
@@ -26,9 +26,9 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
import rdpy.protocol.rdp.tpkt as tpkt
|
||||
import rdpy.core.tpkt as tpkt
|
||||
import rdpy.core.type as type
|
||||
import rdpy.core.error as error
|
||||
|
||||
|
||||
class TPKTTest(unittest.TestCase):
|
||||
"""
|
||||
@@ -60,13 +60,13 @@ class TPKTTest(unittest.TestCase):
|
||||
def connect(self):
|
||||
pass
|
||||
def recv(self, data):
|
||||
data.readType(type.String("test_tpkt_layer_recv", constant = True))
|
||||
data.read_type(type.String("test_tpkt_layer_recv", constant = True))
|
||||
raise TPKTTest.TPKT_PASS()
|
||||
|
||||
message = type.String("test_tpkt_layer_recv")
|
||||
|
||||
s = type.Stream()
|
||||
s.writeType((type.UInt8(tpkt.Action.FASTPATH_ACTION_X224), type.UInt8(), type.UInt16Be(type.sizeof(message) + 4), message))
|
||||
s.write_type((type.UInt8(tpkt.Action.FASTPATH_ACTION_X224), type.UInt8(), type.UInt16Be(type.sizeof(message) + 4), message))
|
||||
|
||||
layer = tpkt.TPKT(Presentation())
|
||||
layer.connect()
|
||||
@@ -80,13 +80,13 @@ class TPKTTest(unittest.TestCase):
|
||||
def setFastPathSender(self, fastPathSender):
|
||||
pass
|
||||
def recvFastPath(self, secFlag, fastPathS):
|
||||
fastPathS.readType(type.String("test_tpkt_layer_recv_fastpath", constant = True))
|
||||
fastPathS.read_type(type.String("test_tpkt_layer_recv_fastpath", constant = True))
|
||||
raise TPKTTest.TPKT_PASS()
|
||||
|
||||
message = type.String("test_tpkt_layer_recv_fastpath")
|
||||
|
||||
s = type.Stream()
|
||||
s.writeType((type.UInt8(tpkt.Action.FASTPATH_ACTION_FASTPATH), type.UInt8(type.sizeof(message) + 2), message))
|
||||
s.write_type((type.UInt8(tpkt.Action.FASTPATH_ACTION_FASTPATH), type.UInt8(type.sizeof(message) + 2), message))
|
||||
|
||||
layer = tpkt.TPKT(None)
|
||||
layer.initFastPath(FastPathLayer())
|
||||
@@ -101,13 +101,13 @@ class TPKTTest(unittest.TestCase):
|
||||
def setFastPathSender(self, fastPathSender):
|
||||
pass
|
||||
def recvFastPath(self, secflag, fastPathS):
|
||||
fastPathS.readType(type.String("test_tpkt_layer_recv_fastpath_ext_length", constant = True))
|
||||
fastPathS.read_type(type.String("test_tpkt_layer_recv_fastpath_ext_length", constant = True))
|
||||
raise TPKTTest.TPKT_PASS()
|
||||
|
||||
message = type.String("test_tpkt_layer_recv_fastpath_ext_length")
|
||||
|
||||
s = type.Stream()
|
||||
s.writeType((type.UInt8(tpkt.Action.FASTPATH_ACTION_FASTPATH), type.UInt16Be((type.sizeof(message) + 3) | 0x8000), message))
|
||||
s.write_type((type.UInt8(tpkt.Action.FASTPATH_ACTION_FASTPATH), type.UInt16Be((type.sizeof(message) + 3) | 0x8000), message))
|
||||
|
||||
layer = tpkt.TPKT(None)
|
||||
layer.initFastPath(FastPathLayer())
|
||||
|
||||
@@ -26,7 +26,7 @@ import os, sys
|
||||
sys.path.insert(1, os.path.join(sys.path[0], '..'))
|
||||
|
||||
import unittest
|
||||
import rdpy.protocol.rdp.x224 as x224
|
||||
import rdpy.core.x224 as x224
|
||||
import rdpy.core.type as type
|
||||
import rdpy.core.error as error
|
||||
|
||||
@@ -53,12 +53,12 @@ class X224Test(unittest.TestCase):
|
||||
"""
|
||||
class Presentation(object):
|
||||
def recv(self, data):
|
||||
data.readType(type.String('test_x224_layer_recvData', constant = True))
|
||||
data.read_type(type.String('test_x224_layer_recvData', constant = True))
|
||||
raise X224Test.X224_PASS()
|
||||
|
||||
layer = x224.X224Layer(Presentation())
|
||||
s = type.Stream()
|
||||
s.writeType((x224.X224DataHeader(), type.String('test_x224_layer_recvData')))
|
||||
s.write_type((x224.X224DataHeader(), type.String('test_x224_layer_recvData')))
|
||||
#reinit position
|
||||
s.pos = 0
|
||||
|
||||
@@ -71,10 +71,10 @@ class X224Test(unittest.TestCase):
|
||||
class Transport(object):
|
||||
def send(self, data):
|
||||
s = type.Stream()
|
||||
s.writeType(data)
|
||||
s.write_type(data)
|
||||
s.pos = 0
|
||||
s.readType(x224.X224DataHeader())
|
||||
s.readType(type.String('test_x224_layer_send', constant = True))
|
||||
s.read_type(x224.X224DataHeader())
|
||||
s.read_type(type.String('test_x224_layer_send', constant = True))
|
||||
raise X224Test.X224_PASS()
|
||||
|
||||
layer = x224.X224Layer(None)
|
||||
@@ -89,10 +89,10 @@ class X224Test(unittest.TestCase):
|
||||
class Transport(object):
|
||||
def send(self, data):
|
||||
s = type.Stream()
|
||||
s.writeType(data)
|
||||
s.write_type(data)
|
||||
s.pos = 0
|
||||
t = x224.ClientConnectionRequestPDU()
|
||||
s.readType(t)
|
||||
t = x224.ConnectionRequestPDU()
|
||||
s.read_type(t)
|
||||
|
||||
if t.protocolNeg.code != x224.NegociationType.TYPE_RDP_NEG_REQ:
|
||||
raise X224Test.X224_FAIL()
|
||||
@@ -112,10 +112,10 @@ class X224Test(unittest.TestCase):
|
||||
@summary: unit test for X224Client.recvConnectionConfirm and sendConnectionRequest function
|
||||
check negotiation failure
|
||||
"""
|
||||
message = x224.ServerConnectionConfirm()
|
||||
message = x224.ConnectionConfirmPDU()
|
||||
message.protocolNeg.code.value = x224.NegociationType.TYPE_RDP_NEG_FAILURE
|
||||
s = type.Stream()
|
||||
s.writeType(message)
|
||||
s.write_type(message)
|
||||
s.pos = 0
|
||||
layer = x224.Client(None)
|
||||
self.assertRaises(error.RDPSecurityNegoFail, layer.recvConnectionConfirm, s)
|
||||
@@ -141,11 +141,11 @@ class X224Test(unittest.TestCase):
|
||||
def recvData(data):
|
||||
raise X224Test.X224_PASS()
|
||||
|
||||
message = x224.ServerConnectionConfirm()
|
||||
message = x224.ConnectionConfirmPDU()
|
||||
message.protocolNeg.selectedProtocol.value = x224.Protocols.PROTOCOL_SSL
|
||||
|
||||
s = type.Stream()
|
||||
s.writeType(message)
|
||||
s.write_type(message)
|
||||
s.pos = 0
|
||||
layer = x224.Client(Presentation())
|
||||
layer._transport = Transport()
|
||||
@@ -165,17 +165,17 @@ class X224Test(unittest.TestCase):
|
||||
|
||||
class Transport(object):
|
||||
def send(self, data):
|
||||
if not isinstance(data, x224.ServerConnectionConfirm):
|
||||
if not isinstance(data, x224.ConnectionConfirmPDU):
|
||||
raise X224Test.X224_FAIL()
|
||||
if data.protocolNeg.code.value != x224.NegociationType.TYPE_RDP_NEG_FAILURE or data.protocolNeg.failureCode.value != x224.NegotiationFailureCode.SSL_REQUIRED_BY_SERVER:
|
||||
raise X224Test.X224_FAIL()
|
||||
def close(self):
|
||||
raise X224Test.X224_PASS()
|
||||
|
||||
message = x224.ClientConnectionRequestPDU()
|
||||
message = x224.ConnectionRequestPDU()
|
||||
message.protocolNeg.selectedProtocol.value = x224.Protocols.PROTOCOL_HYBRID
|
||||
s = type.Stream()
|
||||
s.writeType(message)
|
||||
s.write_type(message)
|
||||
s.pos = 0
|
||||
|
||||
layer = x224.Server(None, "key", "cert", True)
|
||||
@@ -204,7 +204,7 @@ class X224Test(unittest.TestCase):
|
||||
tls = True
|
||||
|
||||
def send(self, data):
|
||||
if not isinstance(data, x224.ServerConnectionConfirm):
|
||||
if not isinstance(data, x224.ConnectionConfirmPDU):
|
||||
raise X224Test.X224_FAIL()
|
||||
if data.protocolNeg.code.value != x224.NegociationType.TYPE_RDP_NEG_RSP or data.protocolNeg.selectedProtocol.value != x224.Protocols.PROTOCOL_SSL:
|
||||
raise X224Test.X224_FAIL()
|
||||
@@ -214,10 +214,10 @@ class X224Test(unittest.TestCase):
|
||||
global connect_event
|
||||
connect_event = True
|
||||
|
||||
message = x224.ClientConnectionRequestPDU()
|
||||
message = x224.ConnectionRequestPDU()
|
||||
message.protocolNeg.selectedProtocol.value = x224.Protocols.PROTOCOL_SSL | x224.Protocols.PROTOCOL_RDP
|
||||
s = type.Stream()
|
||||
s.writeType(message)
|
||||
s.write_type(message)
|
||||
s.pos = 0
|
||||
|
||||
layer = x224.Server(Presentation(), "key", "cert")
|
||||
|
||||
Reference in New Issue
Block a user