First part seems to work fine on mac os x with Microsoft Remote Desktop

This commit is contained in:
Adolfo Gómez García 2015-04-01 09:44:41 +02:00
parent 8a17d5d017
commit c5138dbc9c
10 changed files with 365 additions and 42 deletions

View File

@ -190,8 +190,6 @@ if __name__ == "__main__":
except Exception as e:
QtGui.QMessageBox.critical(None, 'Error', six.text_type(e), QtGui.QMessageBox.Ok)
sys.stdin.read()
QtGui.QMessageBox.critical(None, 'Error', 'test', QtGui.QMessageBox.Ok)
# Build base REST
# v = RestRequest('', done)

View File

@ -86,4 +86,4 @@ def addExecBeforeExit(fnc):
def execBeforeExit():
for fnc in _execBeforeExit:
fnc.call()
fnc.__call__()

View File

@ -120,10 +120,11 @@ class Client(Handler):
if res is not None:
ip, userService, userServiceInstance, transport, transportInstance = res
password = cryptoManager().xor(data['password'], scrambler).decode('utf-8')
logger.debug('Password: {}'.format(password))
transportScript = transportInstance.getUDSTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request)
logger.debug('Transport script: {}'.format(transportScript))
logger.debug('Script:\n{}'.format(transportScript))
return Client.result(transportScript)
except Exception as e:
logger.exception("Exception")

View File

@ -55,7 +55,6 @@ 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:
@ -69,8 +68,6 @@ def getOsFromUA(ua):
break
except Exception:
pass
logger.debug('User-Agent: {0}'.format(ua))
logger.debug('Detected OS: {0}'.format(res))
return res

View File

@ -33,10 +33,9 @@
from __future__ import unicode_literals
from django.http import HttpResponse
from uds.core.util.Cache import Cache
from uds.models import TicketStore
from uds.core.util import net
from uds.core.auths import auth
from uds.models import TicketStore
import logging
@ -58,11 +57,7 @@ def guacamole(request, tunnelId):
try:
val = TicketStore.get(tunnelId, invalidate=False)
# Remove key from cache, just 1 use
# Cache has a limit lifetime, so we will allow to "reload" the page
# cache.remove(tunnelId)
# response = 'protocol\trdp\rhostname\tw7adolfo\rusername\tadmin\rpassword\ttemporal'
response = dict2resp(val)
except Exception:
return HttpResponse(ERROR, content_type=CONTENT_TYPE)

View File

@ -33,7 +33,7 @@
from __future__ import unicode_literals
from django.http import HttpResponseNotAllowed, HttpResponse
from uds.core.util.Cache import Cache
from uds.models import TicketStore
from uds.core.auths import auth
import logging
@ -45,17 +45,15 @@ logger = logging.getLogger(__name__)
@auth.trustedSourceRequired
def pam(request):
response = ''
cache = Cache('pam')
if request.method == 'POST':
return HttpResponseNotAllowed(['GET'])
if 'id' in request.GET and 'pass' in request.GET:
# This is an "auth" request
logger.debug("Auth request for user [{0}] and pass [{1}]".format(request.GET['id'], request.GET['pass']))
password = cache.get(request.GET['id'])
password = TicketStore.get(request.GET['id'])
response = '0'
if password == request.GET['pass']:
response = '1'
cache.remove(request.GET['id']) # Ticket valid for just 1 login
elif 'uid' in request.GET:
# This is an "get name for id" call

View File

@ -36,6 +36,7 @@ Created on Jul 29, 2011
'''
from __future__ import unicode_literals
from uds.core.util import OsDetector
import six
@ -58,13 +59,20 @@ class RDPFile(object):
showWallpaper = False
multimon = False
def __init__(self, fullScreen, width, height, bpp):
def __init__(self, fullScreen, width, height, bpp, target=OsDetector.Windows):
self.width = six.text_type(width)
self.height = six.text_type(height)
self.bpp = six.text_type(bpp)
self.fullScreen = fullScreen
self.target = target
def get(self):
if self.target == OsDetector.Windows:
return self.getWindows()
elif self.target == OsDetector.Macintosh:
return self.getMacOsX()
def getWindows(self):
password = "{password}"
screenMode = self.fullScreen and "2" or "1"
audioMode = self.redirectAudio and "0" or "2"
@ -75,7 +83,7 @@ class RDPFile(object):
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"
useMultimon = self.multimon and "1" or "0"
res = ''
res += 'screen mode id:i:' + screenMode + '\n'
@ -112,3 +120,201 @@ class RDPFile(object):
res += 'negotiate security layer:i:1' + '\n'
return res
def getMacOsX(self):
if self.fullScreen:
desktopSize = ' <string>DesktopFullScreen</string>'
else:
desktopSize = ''' <dict>
<key>DesktopHeight</key>
<integer>{}</integer>
<key>DesktopWidth</key>
<integer>{}</integer>
</dict>'''.format(self.width, self.height)
return '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AddToKeychain</key>
<true/>
<key>ApplicationPath</key>
<string></string>
<key>AudioRedirectionMode</key>
<integer>0</integer>
<key>AuthenticateLevel</key>
<integer>0</integer>
<key>AutoReconnect</key>
<true/>
<key>BitmapCaching</key>
<true/>
<key>ColorDepth</key>
<integer>1</integer>
<key>ConnectionString</key>
<string>{host}</string>
<key>DesktopSize</key>
{desktopSize}
<key>Display</key>
<integer>0</integer>
<key>Domain</key>
<string>{domain}</string>
<key>DontWarnOnChange</key>
<true/>
<key>DontWarnOnDriveMount</key>
<true/>
<key>DontWarnOnQuit</key>
<true/>
<key>DriveRedirectionMode</key>
<integer>0</integer>
<key>FontSmoothing</key>
<true/>
<key>FullWindowDrag</key>
<false/>
<key>HideMacDock</key>
<true/>
<key>KeyMappingTable</key>
<dict>
<key>UI_ALPHANUMERIC_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>102</integer>
<key>MacModifier</key>
<integer>0</integer>
<key>On</key>
<true/>
</dict>
<key>UI_ALT_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>4294967295</integer>
<key>MacModifier</key>
<integer>2048</integer>
<key>On</key>
<true/>
</dict>
<key>UI_CONTEXT_MENU_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>120</integer>
<key>MacModifier</key>
<integer>2048</integer>
<key>On</key>
<true/>
</dict>
<key>UI_CONVERSION_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>4294967295</integer>
<key>MacModifier</key>
<integer>0</integer>
<key>On</key>
<false/>
</dict>
<key>UI_HALF_FULL_WIDTH_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>49</integer>
<key>MacModifier</key>
<integer>256</integer>
<key>On</key>
<true/>
</dict>
<key>UI_HIRAGANA_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>104</integer>
<key>MacModifier</key>
<integer>0</integer>
<key>On</key>
<true/>
</dict>
<key>UI_NON_CONVERSION_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>4294967295</integer>
<key>MacModifier</key>
<integer>0</integer>
<key>On</key>
<false/>
</dict>
<key>UI_NUM_LOCK_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>71</integer>
<key>MacModifier</key>
<integer>0</integer>
<key>On</key>
<true/>
</dict>
<key>UI_PAUSE_BREAK_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>99</integer>
<key>MacModifier</key>
<integer>2048</integer>
<key>On</key>
<true/>
</dict>
<key>UI_PRINT_SCREEN_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>118</integer>
<key>MacModifier</key>
<integer>2048</integer>
<key>On</key>
<true/>
</dict>
<key>UI_SCROLL_LOCK_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>107</integer>
<key>MacModifier</key>
<integer>0</integer>
<key>On</key>
<true/>
</dict>
<key>UI_SECONDARY_MOUSE_BUTTON</key>
<dict>
<key>MacKeyCode</key>
<integer>256</integer>
<key>MacModifier</key>
<integer>4608</integer>
<key>On</key>
<true/>
</dict>
<key>UI_WINDOWS_START_KEY</key>
<dict>
<key>MacKeyCode</key>
<integer>122</integer>
<key>MacModifier</key>
<integer>2048</integer>
<key>On</key>
<true/>
</dict>
</dict>
<key>MenuAnimations</key>
<false/>
<key>PrinterRedirection</key>
<true/>
<key>RedirectFolder</key>
<string>/Users/admin</string>
<key>RedirectPrinter</key>
<string></string>
<key>RemoteApplication</key>
<false/>
<key>Themes</key>
<true/>
<key>UserName</key>
<string>{username}</string>
<key>Wallpaper</key>
<false/>
<key>WorkingDirectory</key>
<string></string>
</dict>
</plist>'''.format(
desktopSize=desktopSize,
host=self.address,
domain=self.domain,
username=self.username
)

View File

@ -33,8 +33,6 @@
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.util import OsDetector
from .BaseRDPTransport import BaseRDPTransport
from .RDPFile import RDPFile
@ -68,7 +66,7 @@ class RDPTransport(BaseRDPTransport):
multimon = BaseRDPTransport.multimon
def windowsScript(self, data):
r = RDPFile(data['fullScreen'], data['width'], data['height'], data['depth'])
r = RDPFile(data['fullScreen'], data['width'], data['height'], data['depth'], target=OsDetector.Windows)
r.address = '{}:{}'.format(data['ip'], 3389)
r.username = data['username']
r.password = '{password}'
@ -88,7 +86,6 @@ from PyQt4 import QtCore, QtGui
import win32crypt
import os
import subprocess
import time
from uds import tools
@ -99,12 +96,73 @@ file = \'\'\'{file}\'\'\'.format(password=win32crypt.CryptProtectData(six.binary
filename = tools.saveTempFile(file)
executable = os.path.join(os.path.join(os.environ['WINDIR'], 'system32'), 'mstsc.exe')
subprocess.call([executable, filename])
tools.addFileToUnlink(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 macOsXScript(self, data):
r = RDPFile(data['fullScreen'], data['width'], data['height'], data['depth'], target=OsDetector.Macintosh)
r.address = '{}:{}'.format(data['ip'], 3389)
r.username = data['username']
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()
if data['domain'] != '':
username = '{}\\\\{}'.format(data['domain'], data['username'])
else:
username = data['username']
return '''
from __future__ import unicode_literals
from PyQt4 import QtCore, QtGui
import subprocess
from uds import tools
import six
file = \'\'\'{file}\'\'\'
filename = tools.saveTempFile(file)
executable = '/Applications/Remote Desktop Connection.app/Contents/MacOS/Remote Desktop Connection'
def onExit():
import subprocess
subprocess.call(['security',
'delete-generic-password',
'-a', '{username}',
'-s', 'Remote Desktop Connection 2 Password for {ip}',
])
try:
subprocess.call(['security',
'add-generic-password',
'-w', '{password}',
'-U',
'-a', '{username}',
'-s', 'Remote Desktop Connection 2 Password for {ip}',
])
# Call but do not wait for exit
tools.addTaskToWait(subprocess.Popen([executable, filename]))
tools.addExecBeforeExit(onExit)
tools.addFileToUnlink(filename)
except Exception as e:
QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok)
'''.format(os=data['os'],
file=r.get(),
password=data['password'],
username=username,
ip=data['ip']
)
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
# We use helper to keep this clean
prefs = user.prefs('rdp')
@ -136,7 +194,11 @@ tools.addFileToUnlink(filename)
'fullScreen': width == -1 or height == -1
}
logger.debug('Detected os: {}'.format(data['os']))
if data['os'] == OsDetector.Windows:
return self.windowsScript(data)
elif data['os'] == OsDetector.Macintosh:
return self.macOsXScript(data)
return ''

View File

@ -36,10 +36,11 @@ 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 uds.core.util.Cache import Cache
from .web import generateHtmlForRdp, getHtmlComponent
from uds.models import TicketStore
from uds.core.util import OsDetector
from .BaseRDPTransport import BaseRDPTransport
from .RDPFile import RDPFile
import logging
import random
@ -83,7 +84,59 @@ class TSRDPTransport(BaseRDPTransport):
if values['tunnelServer'].count(':') != 1:
raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))
def renderForHtml(self, userService, transport, ip, os, user, password):
def windowsScript(self, data):
r = RDPFile(data['fullScreen'], data['width'], data['height'], data['depth'], target=OsDetector.Windows)
r.address = '{address}'
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
from uds.forward import forward
from uds import tools
import six
forwardThread, port = forward('{tunHost}', {tunPort}, '{tunUser}', '{tunPass}', '{server}', {port})
file = \'\'\'{file}\'\'\'.format(
password=win32crypt.CryptProtectData(six.binary_type('{password}'.encode('UTF-16LE')), None, None, None, None, 0x01).encode('hex'),
address='127.0.0.1:{{}}'.format(port)
)
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(
file=r.get(), password=data['password'],
server=data['ip'],
port=3389,
tunUser=data['tunUser'],
tunPass=data['tunPass'],
tunHost=data['tunHost'],
tunPort=data['tunPort']
)
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
# We use helper to keep this clean
prefs = user.prefs('rdp')
@ -92,20 +145,25 @@ class TSRDPTransport(BaseRDPTransport):
width, height = CommonPrefs.getWidthHeight(prefs)
depth = CommonPrefs.getDepth(prefs)
cache = Cache('pam')
tunuser = ''.join(random.choice(string.letters + string.digits) for _i in range(12)) + ("%f" % time.time()).split('.')[1]
tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
cache.put(tunuser, tunpass, 60 * 10) # Credential valid for ten minutes, and for 1 use only
tunuser = TicketStore.create(tunpass)
sshHost, sshPort = self.tunnelServer.value.split(':')
logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass))
tun = "{0} {1} {2} {3} {4} {5} {6}".format(tunuser, tunpass, sshHost, sshPort, ip, '3389', '9')
ip = '127.0.0.1'
# Extra data
extra = {
# data
data = {
'os': os['OS'],
'ip': ip,
'tunUser': tunuser,
'tunPass': tunpass,
'tunHost': sshHost,
'tunPort': sshPort,
'username': username,
'password': password,
'domain': domain,
'width': width,
'height': height,
'depth': depth,
@ -113,10 +171,13 @@ class TSRDPTransport(BaseRDPTransport):
'smartcards': self.allowSmartcards.isTrue(),
'drives': self.allowDrives.isTrue(),
'serials': self.allowSerials.isTrue(),
'tun': tun,
'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, '-1', username, password, domain, extra)
if data['os'] == OsDetector.Windows:
return self.windowsScript(data)
return ''

View File

@ -30,7 +30,7 @@
'''
from __future__ import unicode_literals
__updated__ = '2015-03-31'
__updated__ = '2015-04-01'
from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseRedirect
@ -39,7 +39,7 @@ from django.template import RequestContext
from django.views.decorators.cache import cache_page, never_cache
from uds.core.auths.auth import webLoginRequired, webPassword
from uds.core.services.Exceptions import ServiceInMaintenanceMode
from uds.core.services.Exceptions import ServiceInMaintenanceMode, InvalidServiceException
from uds.core.managers.UserServiceManager import UserServiceManager
from uds.models import TicketStore
from uds.core.ui.images import DEFAULT_IMAGE
@ -69,6 +69,7 @@ def getService(request, idService, idTransport, doTest=True):
if kind == 'A': # This is an assigned service
logger.debug('Getting A service {}'.format(idService))
ads = UserService.objects.get(uuid=idService)
ads.deployed_service.validateUser(request.user)
else:
ds = DeployedService.objects.get(uuid=idService)
# We first do a sanity check for this, if the user has access to this service
@ -83,6 +84,10 @@ def getService(request, idService, idTransport, doTest=True):
logger.debug('Found service: {0}'.format(ads))
trans = Transport.objects.get(uuid=idTransport)
# Ensures that the transport is allowed for this service
if trans not in ads.deployed_service.transports.all():
raise InvalidServiceException()
if doTest is False:
return (None, ads, None, trans, None)