forked from shaba/openuds
* Implemented uds connector script for RDP (just for windows right now)
* Advanced a lot with java removal process * Implemented usefull things on client part
This commit is contained in:
parent
62f50304fe
commit
f173146d8f
@ -38,7 +38,10 @@ import six
|
||||
|
||||
from uds.rest import RestRequest
|
||||
from uds.forward import forward
|
||||
from uds import tools
|
||||
|
||||
import webbrowser
|
||||
import time
|
||||
|
||||
from UDSWindow import Ui_MainWindow
|
||||
|
||||
@ -47,6 +50,10 @@ VERSION = '1.9.5'
|
||||
|
||||
|
||||
class UDSClient(QtGui.QMainWindow):
|
||||
|
||||
ticket = None
|
||||
scrambler = None
|
||||
|
||||
def __init__(self):
|
||||
QtGui.QMainWindow.__init__(self)
|
||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
|
||||
@ -57,46 +64,76 @@ class UDSClient(QtGui.QMainWindow):
|
||||
self.ui.progressBar.setValue(0)
|
||||
self.ui.cancelButton.clicked.connect(self.cancelPushed)
|
||||
|
||||
self.ui.info.setText('Initializing...')
|
||||
|
||||
self.activateWindow()
|
||||
|
||||
def closeWindow(self):
|
||||
self.close()
|
||||
|
||||
def processError(self, rest):
|
||||
if 'error' in rest.data:
|
||||
raise Exception(rest.data['error'])
|
||||
def processError(self, data):
|
||||
if 'error' in data:
|
||||
raise Exception(data['error'])
|
||||
# QtGui.QMessageBox.critical(self, 'Request error', rest.data['error'], QtGui.QMessageBox.Ok)
|
||||
# self.closeWindow()
|
||||
# return
|
||||
|
||||
def cancelPushed(self):
|
||||
self.close()
|
||||
|
||||
def version(self, rest):
|
||||
try:
|
||||
self.ui.progressBar.setValue(10)
|
||||
|
||||
self.processError(rest)
|
||||
|
||||
if rest.data['result']['requiredVersion'] > VERSION:
|
||||
QtGui.QMessageBox.critical(self, 'Upgrade required', 'A newer connector version is required.\nA browser will be opened to download it.', QtGui.QMessageBox.Ok)
|
||||
webbrowser.open(rest.data['result']['downloadUrl'])
|
||||
self.closeWindow()
|
||||
return
|
||||
|
||||
QtGui.QMessageBox.critical(self, 'Notice', six.text_type(rest.data), QtGui.QMessageBox.Ok)
|
||||
|
||||
except Exception as e:
|
||||
def showError(self, e):
|
||||
self.ui.progressBar.setValue(100)
|
||||
self.ui.info.setText('Error')
|
||||
QtGui.QMessageBox.critical(self, 'Error', six.text_type(e), QtGui.QMessageBox.Ok)
|
||||
self.closeWindow()
|
||||
|
||||
def showEvent(self, *args, **kwargs):
|
||||
def cancelPushed(self):
|
||||
self.close()
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def getVersion(self):
|
||||
self.req = RestRequest('', self, self.version)
|
||||
self.req.get()
|
||||
|
||||
@QtCore.pyqtSlot(dict)
|
||||
def version(self, data):
|
||||
try:
|
||||
self.ui.progressBar.setValue(10)
|
||||
|
||||
self.processError(data)
|
||||
|
||||
self.ui.info.setText('Processing...')
|
||||
|
||||
if data['result']['requiredVersion'] > VERSION:
|
||||
QtGui.QMessageBox.critical(self, 'Upgrade required', 'A newer connector version is required.\nA browser will be opened to download it.', QtGui.QMessageBox.Ok)
|
||||
webbrowser.open(data['result']['downloadUrl'])
|
||||
self.closeWindow()
|
||||
return
|
||||
|
||||
self.req = RestRequest('/{}/{}'.format(self.ticket, self.scrambler), self, self.transportDataReceived)
|
||||
self.req.get()
|
||||
|
||||
except Exception as e:
|
||||
self.showError(e)
|
||||
|
||||
@QtCore.pyqtSlot(dict)
|
||||
def transportDataReceived(self, data):
|
||||
try:
|
||||
self.ui.progressBar.setValue(20)
|
||||
self.processError(data)
|
||||
|
||||
script = data['result']
|
||||
print script
|
||||
|
||||
six.exec_(script, globals(), {'parent': self})
|
||||
|
||||
self.closeWindow()
|
||||
except Exception as e:
|
||||
self.showError(e)
|
||||
|
||||
def start(self):
|
||||
'''
|
||||
Starts proccess by requesting version info
|
||||
'''
|
||||
self.ui.info.setText('Requesting required connector version...')
|
||||
self.req = RestRequest('', self, self.version)
|
||||
self.req.get()
|
||||
self.ui.info.setText('Initializing...')
|
||||
QtCore.QTimer.singleShot(100, self.getVersion)
|
||||
|
||||
|
||||
def done(data):
|
||||
@ -104,7 +141,13 @@ def done(data):
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Initialize app
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
|
||||
# Set several info for settings
|
||||
QtCore.QCoreApplication.setOrganizationName('Virtual Cable S.L.U.')
|
||||
QtCore.QCoreApplication.setApplicationName('UDS Connector')
|
||||
|
||||
app.setStyle(QtGui.QStyleFactory.create('plastique'))
|
||||
|
||||
if six.PY3 is False:
|
||||
@ -118,7 +161,7 @@ if __name__ == "__main__":
|
||||
raise Exception()
|
||||
|
||||
ssl = uri[3] == 's'
|
||||
host, ticket, scrambler = uri.split('//')[1].split('/')
|
||||
host, UDSClient.ticket, UDSClient.scrambler = uri.split('//')[1].split('/')
|
||||
|
||||
except Exception:
|
||||
QtGui.QMessageBox.critical(None, 'Notice', 'This program is designed to be used by UDS', QtGui.QMessageBox.Ok)
|
||||
@ -131,8 +174,14 @@ if __name__ == "__main__":
|
||||
try:
|
||||
win = UDSClient()
|
||||
win.show()
|
||||
win.start()
|
||||
|
||||
sys.exit(app.exec_())
|
||||
exitVal = app.exec_()
|
||||
|
||||
time.sleep(3)
|
||||
tools.unlinkFiles()
|
||||
|
||||
sys.exit(exitVal)
|
||||
except Exception as e:
|
||||
QtGui.QMessageBox.critical(None, 'Error', six.text_type(e), QtGui.QMessageBox.Ok)
|
||||
|
||||
|
49
client/src/uds/osDetector.py
Normal file
49
client/src/uds/osDetector.py
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2015 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
LINUX = 'Linux'
|
||||
WINDOWS = 'Windows'
|
||||
MAC_OS_X = 'Mac os x'
|
||||
|
||||
|
||||
def getOs():
|
||||
if sys.platform.startswith('linux'):
|
||||
return LINUX
|
||||
elif sys.platform.startswith('win'):
|
||||
return WINDOWS
|
||||
elif sys.platform.startswith('darwin'):
|
||||
return MAC_OS_X
|
@ -32,25 +32,27 @@
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from PyQt4.QtCore import pyqtSignal
|
||||
from PyQt4.QtCore import QObject, QUrl
|
||||
from PyQt4.QtCore import pyqtSignal, pyqtSlot
|
||||
from PyQt4.QtCore import QObject, QUrl, QSettings
|
||||
from PyQt4.QtCore import Qt
|
||||
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply, QSslCertificate
|
||||
from PyQt4.QtGui import QMessageBox
|
||||
|
||||
import json
|
||||
import osDetector
|
||||
import six
|
||||
|
||||
|
||||
class RestRequest(QObject):
|
||||
|
||||
restApiUrl = '' #
|
||||
done = pyqtSignal(QObject)
|
||||
|
||||
done = pyqtSignal(dict, name='done')
|
||||
|
||||
def __init__(self, url, parentWindow, done): # parent not used
|
||||
super(RestRequest, self).__init__()
|
||||
# private
|
||||
self._manager = QNetworkAccessManager()
|
||||
self.data = None
|
||||
self.url = QUrl(RestRequest.restApiUrl + url)
|
||||
|
||||
# connect asynchronous result, when a request finishes
|
||||
@ -58,9 +60,10 @@ class RestRequest(QObject):
|
||||
self._manager.sslErrors.connect(self._sslError)
|
||||
self._parentWindow = parentWindow
|
||||
|
||||
self.done.connect(done)
|
||||
self.done.connect(done, Qt.QueuedConnection)
|
||||
|
||||
# private slot, no need to declare as slot
|
||||
@pyqtSlot(QNetworkReply)
|
||||
def _finished(self, reply):
|
||||
'''
|
||||
Handle signal 'finished'. A network request has finished.
|
||||
@ -69,30 +72,37 @@ class RestRequest(QObject):
|
||||
if reply.error() != QNetworkReply.NoError:
|
||||
raise Exception(reply.errorString())
|
||||
|
||||
data = six.text_type(reply.readAll())
|
||||
self.data = json.loads(data)
|
||||
data = json.loads(six.text_type(reply.readAll()))
|
||||
except Exception as e:
|
||||
self.data = {
|
||||
data = {
|
||||
'result': None,
|
||||
'error': six.text_type(e)
|
||||
}
|
||||
|
||||
self.done.emit(data)
|
||||
|
||||
reply.deleteLater() # schedule for delete from main event loop
|
||||
self.done.emit(self)
|
||||
|
||||
@pyqtSlot(QNetworkReply, list)
|
||||
def _sslError(self, reply, errors):
|
||||
|
||||
print "SSL Error"
|
||||
print reply
|
||||
settings = QSettings()
|
||||
cert = errors[0].certificate()
|
||||
errorString = 'The certificate for "{}" has the following errors:\n'.format(cert.subjectInfo(QSslCertificate.CommonName))
|
||||
for err in errors:
|
||||
errorString += err.errorString() + '\n'
|
||||
digest = six.text_type(cert.digest().toHex())
|
||||
|
||||
if QMessageBox.warning(self._parentWindow, 'SSL Error', errorString, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
||||
approved = settings.value(digest, False).toBool()
|
||||
|
||||
errorString = '<p>The certificate for <b>{}</b> has the following errors:</p><ul>'.format(cert.subjectInfo(QSslCertificate.CommonName))
|
||||
|
||||
for err in errors:
|
||||
errorString += '<li>' + err.errorString() + '</li>'
|
||||
|
||||
errorString += '</ul>'
|
||||
|
||||
if approved or QMessageBox.warning(self._parentWindow, 'SSL Warning', errorString, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes:
|
||||
settings.setValue(digest, True)
|
||||
reply.ignoreSslErrors()
|
||||
|
||||
def get(self):
|
||||
print self.url
|
||||
request = QNetworkRequest(self.url)
|
||||
request.setRawHeader('User-Agent', osDetector.getOs() + " - UDS Connector")
|
||||
self._manager.get(request)
|
||||
|
@ -31,3 +31,33 @@
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import tempfile
|
||||
import string
|
||||
import random
|
||||
import os
|
||||
|
||||
_unlinkFiles = []
|
||||
|
||||
|
||||
def saveTempFile(content, filename=None):
|
||||
if filename is None:
|
||||
filename = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16))
|
||||
filename = filename + '.uds'
|
||||
filename = os.path.join(tempfile.gettempdir(), filename)
|
||||
with open(filename, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
return filename
|
||||
|
||||
|
||||
def addFileToUnlink(filename):
|
||||
_unlinkFiles.append(filename)
|
||||
|
||||
|
||||
def unlinkFiles():
|
||||
for f in _unlinkFiles:
|
||||
try:
|
||||
os.unlink(f)
|
||||
except Exception:
|
||||
pass
|
||||
|
@ -92,7 +92,6 @@ class Client(Handler):
|
||||
Processes get requests
|
||||
'''
|
||||
logger.debug("Client args for GET: {0}".format(self._args))
|
||||
# return Client.result(error=errors.ACCESS_DENIED)
|
||||
|
||||
if len(self._args) == 0:
|
||||
url = self._request.build_absolute_uri(reverse('ClientDownload'))
|
||||
@ -102,24 +101,30 @@ class Client(Handler):
|
||||
'downloadUrl': url
|
||||
})
|
||||
|
||||
if len(self._args) != 2:
|
||||
try:
|
||||
ticket, scrambler = self._args
|
||||
except Exception:
|
||||
raise RequestError('Invalid request')
|
||||
|
||||
try:
|
||||
data = TicketStore.get(self._args[0])
|
||||
data = TicketStore.get(ticket)
|
||||
except Exception:
|
||||
return Client.result(error=errors.ACCESS_DENIED)
|
||||
|
||||
self._request.user = User.objects.get(uuid=data['user'])
|
||||
|
||||
try:
|
||||
logger.debug(data)
|
||||
res = getService(self._request, data['service'], data['transport'])
|
||||
logger.debug('Res: {}'.format(res))
|
||||
if res is not None:
|
||||
ip, userService, userServiceInstance, transport, transportInstance = res
|
||||
password = cryptoManager().xor(self._args[1], data['password']).decode('utf-8')
|
||||
password = cryptoManager().xor(data['password'], scrambler).decode('utf-8')
|
||||
logger.debug('Password: {}'.format(password))
|
||||
|
||||
transportInfo = transportInstance.getUDSTransportData(userService, transport, ip, self.request.os, self._request.user, password, self._request)
|
||||
return Client.result(transportInfo)
|
||||
transportScript = transportInstance.getUDSTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request)
|
||||
logger.debug('Transport script: {}'.format(transportScript))
|
||||
return Client.result(transportScript)
|
||||
except Exception as e:
|
||||
logger.exception("Exception")
|
||||
return Client.result(error=six.text_type(e))
|
||||
|
@ -158,15 +158,12 @@ class Transport(Module):
|
||||
'''
|
||||
return user.name
|
||||
|
||||
def getUDSTransportData(self, userService, transport, ip, os, user, password, request):
|
||||
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
|
||||
'''
|
||||
Must override if transport does not provides its own link (that is, it is an UDS native transport)
|
||||
Returns the transport data needed to connect with the userService
|
||||
This is invoked right before service is accesed (secuentally).
|
||||
|
||||
The class must provide either this method or the getLink method
|
||||
If this is an uds transport, this will return the tranport script needed for executing
|
||||
this on client
|
||||
'''
|
||||
return None
|
||||
return ''
|
||||
|
||||
def getLink(self, userService, transport, ip, os, user, password, request):
|
||||
'''
|
||||
|
@ -55,6 +55,7 @@ def getOsFromUA(ua):
|
||||
'''
|
||||
Basic OS Client detector (very basic indeed :-))
|
||||
'''
|
||||
logger.debug('Examining user agent {}'.format(ua))
|
||||
if ua is None:
|
||||
os = DEFAULT_OS
|
||||
else:
|
||||
|
@ -38,7 +38,7 @@ from uds.models import User
|
||||
import threading
|
||||
import logging
|
||||
|
||||
__updated__ = '2015-03-27'
|
||||
__updated__ = '2015-03-31'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -61,7 +61,7 @@ class GlobalRequestMiddleware(object):
|
||||
# Add IP to request
|
||||
GlobalRequestMiddleware.fillIps(request)
|
||||
# Ensures request contains os
|
||||
OsDetector.getOsFromRequest(request)
|
||||
request.os = OsDetector.getOsFromUA(request.META.get('HTTP_USER_AGENT'))
|
||||
# Ensures that requests contains the valid user
|
||||
GlobalRequestMiddleware.getUser(request)
|
||||
|
||||
|
207
server/src/uds/transports/NX/NXFile.py
Normal file
207
server/src/uds/transports/NX/NXFile.py
Normal file
@ -0,0 +1,207 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
'''
|
||||
Created on Jul 29, 2011
|
||||
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
EMPTY_PASSWORD = "EMPTY_PASSWORD"
|
||||
|
||||
NXTEMPLATE = (
|
||||
"<!DOCTYPE NXClientSettings>\n"
|
||||
"<NXClientSettings application=\"nxclient\" version=\"1.3\" >\n"
|
||||
" <group name=\"Advanced\" >\n"
|
||||
" <option key=\"Cache size\" value=\"{CACHEMEM}\" />\n"
|
||||
" <option key=\"Cache size on disk\" value=\"{CACHEDISK}\" />\n"
|
||||
" <option key=\"Current keyboard\" value=\"true\" />\n"
|
||||
" <option key=\"Custom keyboard layout\" value=\"{KEYLAYOUT}\" />\n"
|
||||
" <option key=\"Disable ZLIB stream compression\" value=\"false\" />\n"
|
||||
" <option key=\"Disable TCP no-delay\" value=\"false\" />\n" +
|
||||
" <option key=\"Disable deferred updates\" value=\"false\" />\n"
|
||||
" <option key=\"Enable HTTP proxy\" value=\"false\" />\n"
|
||||
" <option key=\"Enable SSL encryption\" value=\"true\" />\n"
|
||||
" <option key=\"Enable response time optimisations\" value=\"true\" />\n"
|
||||
" <option key=\"Grab keyboard\" value=\"false\" />\n"
|
||||
" <option key=\"HTTP proxy host\" value=\"\" />\n"
|
||||
" <option key=\"HTTP proxy port\" value=\"8080\" />\n"
|
||||
" <option key=\"HTTP proxy username\" value=\"\" />\n"
|
||||
" <option key=\"Remember HTTP proxy password\" value=\"false\" />\n"
|
||||
" <option key=\"Restore cache\" value=\"true\" />\n"
|
||||
" <option key=\"StreamCompression\" value=\"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Environment\" >\n"
|
||||
" <option key=\"Font server host\" value=\"\" />\n"
|
||||
" <option key=\"Font server port\" value=\"7100\" />\n"
|
||||
" <option key=\"Use font server\" value=\"false\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"General\" >\n"
|
||||
" <option key=\"Automatic reconnect\" value=\"true\" />\n"
|
||||
" <option key=\"Disable SHM\" value=\"false\" />\n"
|
||||
" <option key=\"Disable emulate shared pixmaps\" value=\"false\" />\n"
|
||||
" <option key=\"Link speed\" value=\"{LINKSPEED}\" />\n"
|
||||
" <option key=\"Remember password\" value=\"{REMEMBERPASS}\" />\n"
|
||||
" <option key=\"Resolution\" value=\"{RESOLUTION}\" />\n"
|
||||
" <option key=\"Resolution width\" value=\"{WIDTH}\" />\n"
|
||||
" <option key=\"Resolution height\" value=\"{HEIGHT}\" />\n"
|
||||
" <option key=\"Server host\" value=\"{HOST}\" />\n"
|
||||
" <option key=\"Server port\" value=\"{PORT}\" />\n"
|
||||
" <option key=\"Session\" value=\"unix\" />\n"
|
||||
" <option key=\"Desktop\" value=\"{DESKTOP}\" />\n"
|
||||
" <option key=\"Use default image encoding\" value=\"1\" />\n"
|
||||
" <option key=\"Use render\" value=\"false\" />\n"
|
||||
" <option key=\"Use taint\" value=\"true\" />\n"
|
||||
" <option key=\"Virtual desktop\" value=\"false\" />\n"
|
||||
" <option key=\"XAgent encoding\" value=\"true\" />\n"
|
||||
" <option key=\"displaySaveOnExit\" value=\"true\" />\n"
|
||||
" <option key=\"xdm broadcast port\" value=\"177\" />\n"
|
||||
" <option key=\"xdm list host\" value=\"localhost\" />\n"
|
||||
" <option key=\"xdm list port\" value=\"177\" />\n"
|
||||
" <option key=\"xdm mode\" value=\"server decide\" />\n"
|
||||
" <option key=\"xdm query host\" value=\"localhost\" />\n"
|
||||
" <option key=\"xdm query port\" value=\"177\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Images\" >\n"
|
||||
" <option key=\"Disable JPEG Compression\" value=\"0\" />\n"
|
||||
" <option key=\"Disable all image optimisations\" value=\"false\" />\n"
|
||||
" <option key=\"Disable backingstore\" value=\"false\" />\n"
|
||||
" <option key=\"Disable composite\" value=\"false\" />\n"
|
||||
" <option key=\"Image Compression Type\" value=\"3\" />\n"
|
||||
" <option key=\"Image Encoding Type\" value=\"0\" />\n"
|
||||
" <option key=\"Image JPEG Encoding\" value=\"false\" />\n"
|
||||
" <option key=\"JPEG Quality\" value=\"6\" />\n"
|
||||
" <option key=\"RDP Image Encoding\" value=\"3\" />\n"
|
||||
" <option key=\"RDP JPEG Quality\" value=\"6\" />\n"
|
||||
" <option key=\"RDP optimization for low-bandwidth link\" value=\"false\" />\n"
|
||||
" <option key=\"Reduce colors to\" value=\"\" />\n"
|
||||
" <option key=\"Use PNG Compression\" value=\"true\" />\n"
|
||||
" <option key=\"VNC JPEG Quality\" value=\"6\" />\n"
|
||||
" <option key=\"VNC images compression\" value=\"3\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Login\" >\n"
|
||||
" <option key=\"User\" value=\"{USERNAME}\" />\n"
|
||||
" <option key=\"Auth\" value=\"{PASSWORD}\" />\n"
|
||||
" <option key=\"Guest Mode\" value=\"false\" />\n"
|
||||
" <option key=\"Guest password\" value=\"\" />\n"
|
||||
" <option key=\"Guest username\" value=\"\" />\n"
|
||||
" <option key=\"Login Method\" value=\"nx\" />\n"
|
||||
" <option key=\"Public Key\" value=\"-----BEGIN DSA PRIVATE KEY-----\n"
|
||||
"MIIBuwIBAAKBgQCXv9AzQXjxvXWC1qu3CdEqskX9YomTfyG865gb4D02ZwWuRU/9\n"
|
||||
"C3I9/bEWLdaWgJYXIcFJsMCIkmWjjeSZyTmeoypI1iLifTHUxn3b7WNWi8AzKcVF\n"
|
||||
"aBsBGiljsop9NiD1mEpA0G+nHHrhvTXz7pUvYrsrXcdMyM6rxqn77nbbnwIVALCi\n"
|
||||
"xFdHZADw5KAVZI7r6QatEkqLAoGBAI4L1TQGFkq5xQ/nIIciW8setAAIyrcWdK/z\n"
|
||||
"5/ZPeELdq70KDJxoLf81NL/8uIc4PoNyTRJjtT3R4f8Az1TsZWeh2+ReCEJxDWgG\n"
|
||||
"fbk2YhRqoQTtXPFsI4qvzBWct42WonWqyyb1bPBHk+JmXFscJu5yFQ+JUVNsENpY\n"
|
||||
"+Gkz3HqTAoGANlgcCuA4wrC+3Cic9CFkqiwO/Rn1vk8dvGuEQqFJ6f6LVfPfRTfa\n"
|
||||
"QU7TGVLk2CzY4dasrwxJ1f6FsT8DHTNGnxELPKRuLstGrFY/PR7KeafeFZDf+fJ3\n"
|
||||
"mbX5nxrld3wi5titTnX+8s4IKv29HJguPvOK/SI7cjzA+SqNfD7qEo8CFDIm1xRf\n"
|
||||
"8xAPsSKs6yZ6j1FNklfu\n"
|
||||
"-----END DSA PRIVATE KEY-----\n"
|
||||
"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Services\" >\n"
|
||||
" <option key=\"Audio\" value=\"true\" />\n"
|
||||
" <option key=\"IPPPort\" value=\"631\" />\n"
|
||||
" <option key=\"IPPPrinting\" value=\"false\" />\n"
|
||||
" <option key=\"Shares\" value=\"false\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"VNC Session\" >\n"
|
||||
" <option key=\"Display\" value=\"0\" />\n"
|
||||
" <option key=\"Remember\" value=\"false\" />\n"
|
||||
" <option key=\"Server\" value=\"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"Windows Session\" >\n"
|
||||
" <option key=\"Application\" value=\"\" />\n"
|
||||
" <option key=\"Authentication\" value=\"2\" />\n"
|
||||
" <option key=\"Color Depth\" value=\"16\" />\n"
|
||||
" <option key=\"Domain\" value=\"\" />\n"
|
||||
" <option key=\"Image Cache\" value=\"true\" />\n"
|
||||
" <option key=\"Password\" value=\"EMPTY_PASSWORD\" />\n"
|
||||
" <option key=\"Remember\" value=\"true\" />\n"
|
||||
" <option key=\"Run application\" value=\"false\" />\n"
|
||||
" <option key=\"Server\" value=\"\" />\n"
|
||||
" <option key=\"User\" value=\"\" />\n"
|
||||
" </group>\n"
|
||||
" <group name=\"share chosen\" >\n"
|
||||
" <option key=\"Share number\" value=\"0\" />\n"
|
||||
" </group>\n"
|
||||
"</NXClientSettings>"
|
||||
)
|
||||
|
||||
|
||||
class NXFile(object):
|
||||
fullScreen = False
|
||||
width = '800'
|
||||
height = '600'
|
||||
cachemem = '4'
|
||||
cachedisk = '32'
|
||||
keyboardLayout = ''
|
||||
linkSpeed = 'wan'
|
||||
host = ''
|
||||
port = ''
|
||||
username = ''
|
||||
password = ''
|
||||
desktop = 'gnome'
|
||||
|
||||
def __init__(self, fullScreen, width, height):
|
||||
self.fullScreen = fullScreen
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
def get(self):
|
||||
rememberPass = 'true'
|
||||
# password = NXPassword.scrambleString(self.password)
|
||||
password = ''
|
||||
if password == '':
|
||||
rememberPass = "false"
|
||||
password = EMPTY_PASSWORD
|
||||
|
||||
resolution = self.width + "x" + self.height
|
||||
if self.fullScreen:
|
||||
resolution = "fullscreen"
|
||||
|
||||
return NXTEMPLATE.format(
|
||||
CACHEMEM=self.cachemem,
|
||||
CACHEDISK=self.cachedisk,
|
||||
KEYLAYOUT=self.keyboardLayout,
|
||||
LINKSPEED=self.linkSpeed,
|
||||
REMEMBERPASS=rememberPass,
|
||||
RESOLUTION=resolution,
|
||||
WIDTH=self.width,
|
||||
HEIGHT=self.height,
|
||||
HOST=self.host,
|
||||
PORT=self.port,
|
||||
DESKTOP=self.desktop,
|
||||
USERNAME=self.username,
|
||||
PASSWORD=self.password
|
||||
)
|
@ -52,7 +52,6 @@ class BaseRDPTransport(Transport):
|
||||
This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password
|
||||
'''
|
||||
iconFile = 'rdp.png'
|
||||
needsJava = True # If this transport needs java for rendering
|
||||
protocol = protocols.RDP
|
||||
|
||||
useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=1, tooltip=_('If checked, the credentials used to connect will be emtpy'))
|
||||
|
114
server/src/uds/transports/RDP/RDPFile.py
Normal file
114
server/src/uds/transports/RDP/RDPFile.py
Normal file
@ -0,0 +1,114 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
'''
|
||||
Created on Jul 29, 2011
|
||||
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class RDPFile(object):
|
||||
fullScreen = False
|
||||
width = '800'
|
||||
height = '600'
|
||||
bpp = '32'
|
||||
address = ''
|
||||
username = ''
|
||||
domain = ''
|
||||
password = ''
|
||||
redirectSerials = False
|
||||
redirectPrinters = False
|
||||
redirectDrives = False
|
||||
redirectSmartcards = False
|
||||
redirectAudio = True
|
||||
compression = True
|
||||
displayConnectionBar = True
|
||||
showWallpaper = False
|
||||
multimon = False
|
||||
|
||||
def __init__(self, fullScreen, width, height, bpp):
|
||||
self.width = six.text_type(width)
|
||||
self.height = six.text_type(height)
|
||||
self.bpp = six.text_type(bpp)
|
||||
self.fullScreen = fullScreen
|
||||
|
||||
def get(self):
|
||||
password = "{password}"
|
||||
screenMode = self.fullScreen and "2" or "1"
|
||||
audioMode = self.redirectAudio and "0" or "2"
|
||||
serials = self.redirectSerials and "1" or "0"
|
||||
drives = self.redirectDrives and "1" or "0"
|
||||
scards = self.redirectSmartcards and "1" or "0"
|
||||
printers = self.redirectPrinters and "1" or "0"
|
||||
compression = self.compression and "1" or "0"
|
||||
bar = self.displayConnectionBar and "1" or "0"
|
||||
disableWallpaper = self.showWallpaper and "0" or "1"
|
||||
useMultimon = self.multimon and "0" or "1"
|
||||
|
||||
res = ''
|
||||
res += 'screen mode id:i:' + screenMode + '\n'
|
||||
res += 'desktopwidth:i:' + self.width + '\n'
|
||||
res += 'desktopheight:i:' + self.height + '\n'
|
||||
res += 'session bpp:i:' + self.bpp + '\n'
|
||||
res += 'use multimon:i:' + useMultimon + '\n'
|
||||
res += 'auto connect:i:1' + '\n'
|
||||
res += 'full address:s:' + self.address + '\n'
|
||||
res += 'compression:i:' + compression + '\n'
|
||||
res += 'keyboardhook:i:2' + '\n'
|
||||
res += 'audiomode:i:' + audioMode + '\n'
|
||||
res += 'redirectdrives:i:' + drives + '\n'
|
||||
res += 'redirectprinters:i:' + printers + '\n'
|
||||
res += 'redirectcomports:i:' + serials + '\n'
|
||||
res += 'redirectsmartcards:i:' + scards + '\n'
|
||||
res += 'redirectclipboard:i:1' + '\n'
|
||||
res += 'displayconnectionbar:i:' + bar + '\n'
|
||||
if len(self.username) != 0:
|
||||
res += 'username:s:' + self.username + '\n'
|
||||
res += 'domain:s:' + self.domain + '\n'
|
||||
res += 'password 51:b:' + password + '\n'
|
||||
|
||||
res += 'alternate shell:s:' + '\n'
|
||||
res += 'shell working directory:s:' + '\n'
|
||||
res += 'disable wallpaper:i:' + disableWallpaper + '\n'
|
||||
res += 'disable full window drag:i:1' + '\n'
|
||||
res += 'disable menu anims:i:' + disableWallpaper + '\n'
|
||||
res += 'disable themes:i:' + disableWallpaper + '\n'
|
||||
res += 'bitmapcachepersistenable:i:1' + '\n'
|
||||
res += 'authentication level:i:0' + '\n'
|
||||
res += 'enablecredsspsupport:i:1' + '\n'
|
||||
res += 'prompt for credentials:i:0' + '\n'
|
||||
res += 'negotiate security layer:i:1' + '\n'
|
||||
|
||||
return res
|
@ -35,10 +35,9 @@ from django.utils.translation import ugettext_noop as _
|
||||
from uds.core.managers.UserPrefsManager import CommonPrefs
|
||||
from uds.core.ui.UserInterface import gui
|
||||
from uds.core.transports.BaseTransport import Transport
|
||||
from uds.core.transports import protocols
|
||||
from uds.core.util import connection
|
||||
from .web import generateHtmlForRdp, getHtmlComponent
|
||||
from uds.core.util import OsDetector
|
||||
from .BaseRDPTransport import BaseRDPTransport
|
||||
from .RDPFile import RDPFile
|
||||
|
||||
import logging
|
||||
|
||||
@ -68,7 +67,45 @@ class RDPTransport(BaseRDPTransport):
|
||||
wallpaper = BaseRDPTransport.wallpaper
|
||||
multimon = BaseRDPTransport.multimon
|
||||
|
||||
def renderForHtml(self, userService, transport, ip, os, user, password):
|
||||
def windowsScript(self, data):
|
||||
r = RDPFile(data['fullScreen'], data['width'], data['height'], data['depth'])
|
||||
r.address = '{}:{}'.format(data['ip'], 3389)
|
||||
r.username = data['username']
|
||||
r.password = '{password}'
|
||||
r.domain = data['domain']
|
||||
r.redirectPrinters = self.allowPrinters.isTrue()
|
||||
r.redirectSmartcards = self.allowSmartcards.isTrue()
|
||||
r.redirectDrives = self.allowDrives.isTrue()
|
||||
r.redirectSerials = self.allowSerials.isTrue()
|
||||
r.showWallpaper = self.wallpaper.isTrue()
|
||||
r.multimon = self.multimon.isTrue()
|
||||
|
||||
# The password must be encoded, to be included in a .rdp file, as 'UTF-16LE' before protecting (CtrpyProtectData) it in order to work with mstsc
|
||||
return '''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import win32crypt
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from uds import tools
|
||||
|
||||
import six
|
||||
|
||||
file = \'\'\'{file}\'\'\'.format(password=win32crypt.CryptProtectData(six.binary_type('{password}'.encode('UTF-16LE')), None, None, None, None, 0x01).encode('hex'))
|
||||
|
||||
filename = tools.saveTempFile(file)
|
||||
executable = os.path.join(os.path.join(os.environ['WINDIR'], 'system32'), 'mstsc.exe')
|
||||
subprocess.call([executable, filename])
|
||||
tools.addFileToUnlink(filename)
|
||||
|
||||
# QtGui.QMessageBox.critical(parent, 'Notice', filename + ", " + executable, QtGui.QMessageBox.Ok)
|
||||
|
||||
'''.format(os=data['os'], file=r.get(), password=data['password'])
|
||||
|
||||
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
|
||||
# We use helper to keep this clean
|
||||
prefs = user.prefs('rdp')
|
||||
|
||||
@ -78,8 +115,14 @@ class RDPTransport(BaseRDPTransport):
|
||||
width, height = CommonPrefs.getWidthHeight(prefs)
|
||||
depth = CommonPrefs.getDepth(prefs)
|
||||
|
||||
# Extra data
|
||||
extra = {
|
||||
# data
|
||||
data = {
|
||||
'os': os['OS'],
|
||||
'ip': ip,
|
||||
'port': 3389,
|
||||
'username': username,
|
||||
'password': password,
|
||||
'domain': domain,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'depth': depth,
|
||||
@ -89,7 +132,11 @@ class RDPTransport(BaseRDPTransport):
|
||||
'serials': self.allowSerials.isTrue(),
|
||||
'compression': True,
|
||||
'wallpaper': self.wallpaper.isTrue(),
|
||||
'multimon': self.multimon.isTrue()
|
||||
'multimon': self.multimon.isTrue(),
|
||||
'fullScreen': width == -1 or height == -1
|
||||
}
|
||||
|
||||
return generateHtmlForRdp(self, userService.uuid, transport.uuid, os, ip, '3389', username, password, domain, extra)
|
||||
if data['os'] == OsDetector.Windows:
|
||||
return self.windowsScript(data)
|
||||
|
||||
return ''
|
||||
|
@ -56,7 +56,6 @@ class X2GOTransport(Transport):
|
||||
typeType = 'X2GOTransport'
|
||||
typeDescription = _('X2GO Transport for direct connection')
|
||||
iconFile = 'x2go.png'
|
||||
needsJava = True # If this transport needs java for rendering
|
||||
protocol = protocols.NX
|
||||
|
||||
useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=1, tooltip=_('If checked, the credentials used to connect will be emtpy'))
|
||||
@ -117,7 +116,8 @@ class X2GOTransport(Transport):
|
||||
self.cache().put(ip, 'N', READY_CACHE_TIMEOUT)
|
||||
return ready == 'Y'
|
||||
|
||||
def getUDSTransportInfo(self, userService, transport, ip, os, user, password, request):
|
||||
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
|
||||
logger.debug('Getting X2Go Transport info')
|
||||
|
||||
prefs = user.prefs('nx')
|
||||
|
||||
@ -138,8 +138,8 @@ class X2GOTransport(Transport):
|
||||
# Fix username/password acording to os manager
|
||||
username, password = userService.processUserPassword(username, password)
|
||||
|
||||
# Extra data
|
||||
return {
|
||||
# data
|
||||
data = {
|
||||
'username': username,
|
||||
'password': password,
|
||||
'width': width,
|
||||
@ -150,3 +150,15 @@ class X2GOTransport(Transport):
|
||||
'cacheDisk': self.cacheDisk.value,
|
||||
'cacheMem': self.cacheMem.value
|
||||
}
|
||||
|
||||
return '''
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import six
|
||||
from uds import osDetector
|
||||
|
||||
data = {data}
|
||||
osname = {os}
|
||||
|
||||
QtGui.QMessageBox.critical(parent, 'Notice ' + osDetector.getOs(), six.text_type(data), QtGui.QMessageBox.Ok)
|
||||
'''.format(data=data, os=os)
|
||||
|
||||
|
@ -80,7 +80,7 @@ strings = [
|
||||
_('Invalid request received'),
|
||||
_('Your browser is not supported. Please, upgrade it to a modern HTML5 browser like Firefox or Chrome'),
|
||||
_('The requested service is in maintenance mode'),
|
||||
_('The service is not ready')
|
||||
_('The service is not ready.\nPlease, try again in a few moments.')
|
||||
]
|
||||
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
'''
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__updated__ = '2015-03-27'
|
||||
__updated__ = '2015-03-31'
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
@ -226,7 +226,7 @@ def clientEnabler(request, idService, idTransport):
|
||||
_x, ads, _x, trans, _x = res
|
||||
|
||||
data = {
|
||||
'service': ads.uuid,
|
||||
'service': 'A' + ads.uuid,
|
||||
'transport': trans.uuid,
|
||||
'user': request.user.uuid,
|
||||
'password': password
|
||||
|
Loading…
Reference in New Issue
Block a user