diff --git a/README.md b/README.md index e09e4cb52b079c4a4519f7a082b1528b89a76321..efa4945f6cf870c87a22435f536f39cb3845b15c 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,24 @@ Description ----------- A VNC client implemented using HTML5, specifically Canvas and -WebSocket. +WebSocket (supports 'wss://' encryption). For browsers that do not have builtin WebSocket support, the project includes web-socket-js, a WebSocket emulator using Adobe Flash (http://github.com/gimite/web-socket-js). +In addition, as3crypto has been added to web-socket-js to implement +WebSocket SSL/TLS encryption, i.e. the "wss://" URI scheme. +(http://github.com/lyokato/as3crypto_patched). + Requirements ------------ Until there is VNC server support for WebSocket connections, you need to use a WebSocket to TCP socket proxy. There is a python proxy -included ('wsproxy'). +included ('wsproxy'). One advantage of using the proxy is that it has +builtin support for SSL/TLS encryption (i.e. "wss://"). There a few reasons why a proxy is required: @@ -38,6 +43,13 @@ There a few reasons why a proxy is required: the client asks the proxy (using the initial query string) to add sequence numbers to each packet. +To encrypt the traffic using the WebSocket 'wss://' URI scheme you +need to generate a certificate for the proxy to load. You can generate +a self-signed certificate using openssl. The common name should be the +hostname of the server where the proxy will be running: + + `openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem` + Usage ----- diff --git a/TODO b/TODO index d72e028e47b8f6bbc74b846c41a5c3c6a73cdadf..e030ad6cd76788e067360ae0e38fa321d14242ed 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,8 @@ -- Add WSS/https/SSL support to page and wsproxy.py - - Make C version of wsproxy.py - Implement UI option for VNC shared mode. - Upgrade to protocol 3.8 - implement ZRLE encoding + +- Get web-socket-js RFC2817 proxying working again. diff --git a/include/web-socket-js/WebSocketMain.swf b/include/web-socket-js/WebSocketMain.swf deleted file mode 100644 index 33810879d37a0a2a29236b04785daaeaca7c61b0..0000000000000000000000000000000000000000 Binary files a/include/web-socket-js/WebSocketMain.swf and /dev/null differ diff --git a/include/web-socket-js/WebSocketMain.swf b/include/web-socket-js/WebSocketMain.swf new file mode 120000 index 0000000000000000000000000000000000000000..4753408d885e490df3c9788d765006a4109f8a2c --- /dev/null +++ b/include/web-socket-js/WebSocketMain.swf @@ -0,0 +1 @@ +flash-src/WebSocketMain.swf \ No newline at end of file diff --git a/include/web-socket-js/flash-src/WebSocket.as b/include/web-socket-js/flash-src/WebSocket.as index 3969363391054120293ae0b8bbb31eae9225acdf..40085c18e5150fe021b0acd5e2e6b6e504fd645a 100644 --- a/include/web-socket-js/flash-src/WebSocket.as +++ b/include/web-socket-js/flash-src/WebSocket.as @@ -16,6 +16,10 @@ import mx.controls.*; import mx.events.*; import mx.utils.*; import com.adobe.net.proxies.RFC2817Socket; +import com.hurlant.crypto.tls.TLSSocket; +import com.hurlant.crypto.tls.TLSConfig; +import com.hurlant.crypto.tls.TLSEngine; +import com.hurlant.crypto.tls.TLSSecurityParameters; [Event(name="message", type="WebSocketMessageEvent")] [Event(name="open", type="flash.events.Event")] @@ -27,7 +31,11 @@ public class WebSocket extends EventDispatcher { private static var OPEN:int = 1; private static var CLOSED:int = 2; - private var socket:RFC2817Socket; + //private var rawSocket:RFC2817Socket; + private var rawSocket:Socket; + private var tlsSocket:TLSSocket; + private var tlsConfig:TLSConfig; + private var socket:Socket; private var main:WebSocketMain; private var scheme:String; private var host:String; @@ -59,6 +67,7 @@ public class WebSocket extends EventDispatcher { // "Header1: xxx\r\nHeader2: yyyy\r\n" this.headers = headers; + /* socket = new RFC2817Socket(); // if no proxy information is supplied, it acts like a normal Socket @@ -66,13 +75,30 @@ public class WebSocket extends EventDispatcher { if (proxyHost != null && proxyPort != 0){ socket.setProxyInfo(proxyHost, proxyPort); } - - socket.addEventListener(Event.CLOSE, onSocketClose); - socket.addEventListener(Event.CONNECT, onSocketConnect); - socket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError); - socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError); - socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); - socket.connect(host, port); + */ + + ExternalInterface.call("console.log", "[WebSocket] scheme: " + scheme); + rawSocket = new Socket(); + + rawSocket.addEventListener(Event.CLOSE, onSocketClose); + rawSocket.addEventListener(Event.CONNECT, onSocketConnect); + rawSocket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError); + rawSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError); + if (scheme == "wss") { + tlsConfig= new TLSConfig(TLSEngine.CLIENT, + null, null, null, null, null, + TLSSecurityParameters.PROTOCOL_VERSION); + tlsConfig.trustSelfSignedCertificates = true; + tlsConfig.ignoreCommonNameMismatch = true; + + tlsSocket = new TLSSocket(); + tlsSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); + socket = (tlsSocket as Socket); + } else { + rawSocket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); + socket = (rawSocket as Socket); + } + rawSocket.connect(host, port); } public function send(data:String):int { @@ -118,6 +144,12 @@ public class WebSocket extends EventDispatcher { private function onSocketConnect(event:Event):void { main.log("connected"); + + if (scheme == "wss") { + ExternalInterface.call("console.log", "[WebSocket] starting SSL/TLS"); + tlsSocket.startTLS(rawSocket, host, tlsConfig); + } + var hostValue:String = host + (port == 80 ? "" : ":" + port); var cookie:String = ""; if (main.getCallerHost() == host) { diff --git a/include/web-socket-js/flash-src/WebSocketMain.swf b/include/web-socket-js/flash-src/WebSocketMain.swf new file mode 100644 index 0000000000000000000000000000000000000000..91da2f8c3ba57291b042aab9a749a3f3e3c60cbb Binary files /dev/null and b/include/web-socket-js/flash-src/WebSocketMain.swf differ diff --git a/include/web-socket-js/flash-src/com/hurlant b/include/web-socket-js/flash-src/com/hurlant new file mode 120000 index 0000000000000000000000000000000000000000..f9f4c84c5c708410054bd531de276572698ae378 --- /dev/null +++ b/include/web-socket-js/flash-src/com/hurlant @@ -0,0 +1 @@ +../../../as3crypto_patched/src/com/hurlant \ No newline at end of file diff --git a/links b/links new file mode 100644 index 0000000000000000000000000000000000000000..93deb3d9a4245ccaa3bdc71ab64012a835887185 --- /dev/null +++ b/links @@ -0,0 +1,21 @@ +Canvas Browser Compatibility: + http://philip.html5.org/tests/canvas/suite/tests/results.html + +WebSockets API standard: + http://dev.w3.org/html5/websockets/ + +Browser Keyboard Events detailed: + http://unixpapa.com/js/key.html + +ActionScript (Flash) WebSocket implementation: + http://github.com/gimite/web-socket-js + +ActionScript (Flash) crypto/TLS library: + http://code.google.com/p/as3crypto + http://github.com/lyokato/as3crypto_patched + +TLS Protocol: + http://en.wikipedia.org/wiki/Transport_Layer_Security + +Generate self-signed certificate: + http://docs.python.org/dev/library/ssl.html#certificates diff --git a/vnc.html b/vnc.html index ea66975d556c6954386c975c885c06c28330c465..c577068d44a214297f3e3d802fc7495898b7eb2a 100644 --- a/vnc.html +++ b/vnc.html @@ -7,6 +7,7 @@ Host: <input id='host' style='width:100'> Port: <input id='port' style='width:50'> Password: <input id='password' type='password' style='width:80'> + Encrypt: <input id='encrypt' type='checkbox'> <input id='connectButton' type='button' value='Loading' style='width:100px' disabled> <br><br> @@ -75,6 +76,7 @@ $('host').value = (url.match(/host=([^&#]*)/) || ['',''])[1]; $('port').value = (url.match(/port=([^&#]*)/) || ['',''])[1]; $('password').value = (url.match(/password=([^&#]*)/) || ['',''])[1]; + $('encrypt').checked = (url.match(/encrypt=([^&#]*)/) || ['',''])[1]; } } </script> diff --git a/vnc.js b/vnc.js index b46aaacfad8c426c32c6a7206971fb5e8da85e5e..cc0692d76a0c3cf3eeecb40b00d4b4562cc187d1 100644 --- a/vnc.js +++ b/vnc.js @@ -906,7 +906,11 @@ updateState: function(state, statusMsg) { init_ws: function () { console.log(">> init_ws"); - var uri = "ws://" + RFB.host + ":" + RFB.port + "/?b64encode"; + var scheme = "ws://"; + if ($('encrypt').checked) { + scheme = "wss://"; + } + var uri = scheme + RFB.host + ":" + RFB.port + "/?b64encode"; if (RFB.use_seq) { uri += "&seq_num"; } diff --git a/webs.py b/webs.py new file mode 100755 index 0000000000000000000000000000000000000000..f0bffe2087dbdfd476749d009fb15c6be4183825 --- /dev/null +++ b/webs.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +''' +A super simple HTTP/HTTPS webserver for python. Automatically detect + +You can make a cert/key with openssl using: +openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem +as taken from http://docs.python.org/dev/library/ssl.html#certificates + +''' + +import traceback, sys +import socket +import ssl +#import http.server as server # python 3.X +import SimpleHTTPServer as server # python 2.X + +def do_request(connstream, from_addr): + x = object() + server.SimpleHTTPRequestHandler(connstream, from_addr, x) + +def serve(): + bindsocket = socket.socket() + #bindsocket.bind(('localhost', PORT)) + bindsocket.bind(('', PORT)) + bindsocket.listen(5) + + print("serving on port", PORT) + + while True: + try: + newsocket, from_addr = bindsocket.accept() + peek = newsocket.recv(1024, socket.MSG_PEEK) + if peek.startswith("\x16"): + connstream = ssl.wrap_socket( + newsocket, + server_side=True, + certfile='self.pem', + ssl_version=ssl.PROTOCOL_TLSv1) + else: + connstream = newsocket + + do_request(connstream, from_addr) + + except Exception: + traceback.print_exc() + +try: + PORT = int(sys.argv[1]) +except: + print "%s port" % sys.argv[0] + sys.exit(2) + +serve() diff --git a/wsproxy.py b/wsproxy.py index a1710b19ede5de47c13f9d3c5a26bcd8e564094b..6f6296a088f6e563acdada124c57bac4bd9ef0fb 100755 --- a/wsproxy.py +++ b/wsproxy.py @@ -1,5 +1,14 @@ #!/usr/bin/python +''' +A WebSocket to TCP socket proxy with support for "wss://" encryption. + +You can make a cert/key with openssl using: +openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem +as taken from http://docs.python.org/dev/library/ssl.html#certificates + +''' + import sys, os, socket, ssl, time, traceback, re from base64 import b64encode, b64decode from select import select @@ -129,7 +138,7 @@ def do_handshake(sock): retsock = ssl.wrap_socket( sock, server_side=True, - certfile='wsproxy.pem', + certfile='self.pem', ssl_version=ssl.PROTOCOL_TLSv1) scheme = "wss" print "Using SSL/TLS"