diff --git a/README.md b/README.md index e09e4cb5..efa4945f 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 d72e028e..e030ad6c 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 33810879..00000000 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 00000000..4753408d --- /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 39693633..40085c18 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 00000000..91da2f8c 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 00000000..f9f4c84c --- /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 00000000..93deb3d9 --- /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 ea66975d..c577068d 100644 --- a/vnc.html +++ b/vnc.html @@ -7,6 +7,7 @@ Host:   Port:   Password:   + Encrypt:    

@@ -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]; } } diff --git a/vnc.js b/vnc.js index b46aaacf..cc0692d7 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 00000000..f0bffe20 --- /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 a1710b19..6f6296a0 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"