diff options
author | runge <runge> | 2006-04-05 21:26:45 +0000 |
---|---|---|
committer | runge <runge> | 2006-04-05 21:26:45 +0000 |
commit | d14cf0a84c88a02222caad1692228584b610aacc (patch) | |
tree | 3482ef126e8b2bf3b9741f779539cfd74c77c698 /classes | |
parent | 1602b345f3e7e508b043133d5c289d9984e39f18 (diff) | |
download | libvncserver-d14cf0a84c88a02222caad1692228584b610aacc.zip libvncserver-d14cf0a84c88a02222caad1692228584b610aacc.tar.gz |
SSL Java viewer work thru proxy. -sslGenCA, etc key/cert management utils for x11vnc. FBPM "support".
Diffstat (limited to 'classes')
-rw-r--r-- | classes/ssl/Makefile.am | 2 | ||||
-rw-r--r-- | classes/ssl/README | 72 | ||||
-rw-r--r-- | classes/ssl/SignedVncViewer.jar | bin | 0 -> 73493 bytes | |||
-rw-r--r-- | classes/ssl/VncViewer.jar | bin | 61835 -> 70763 bytes | |||
-rw-r--r-- | classes/ssl/proxy.vnc | 70 | ||||
-rwxr-xr-x | classes/ssl/ssl_vncviewer | 142 | ||||
-rw-r--r-- | classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch | 701 |
7 files changed, 915 insertions, 72 deletions
diff --git a/classes/ssl/Makefile.am b/classes/ssl/Makefile.am index 66c8719..f7ac523 100644 --- a/classes/ssl/Makefile.am +++ b/classes/ssl/Makefile.am @@ -1,2 +1,2 @@ -EXTRA_DIST=VncViewer.jar index.vnc +EXTRA_DIST=VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ssl_vncviewer diff --git a/classes/ssl/README b/classes/ssl/README new file mode 100644 index 0000000..884e34a --- /dev/null +++ b/classes/ssl/README @@ -0,0 +1,72 @@ +This directory contains a patched Java applet VNC viewer that is SSL +enabled. + +The patches in the *.patch files are relative to the source tarball: + + tightvnc-1.3dev7_javasrc.tar.gz + +currently (4/06) available here: + + http://prdownloads.sourceforge.net/vnc-tight/tightvnc-1.3dev7_javasrc.tar.gz?download + +It also includes some simple patches to: + + - fix richcursor colors + + - make the Java Applet cursor (not the cursor drawn to the canvas + framebuffer) invisible when it is inside the canvas. + + - allow Tab (and some other) keystrokes to be sent to the vnc + server instead of doing widget traversal. + + +This SSL applet should work with any VNC viewer that has an SSL tunnel in +front of it. It has been tested on x11vnc and using the stunnel tunnel +to other VNC servers. + +By default this Vnc Viewer will only do SSL. To do unencrypted traffic +see the "DisableSSL" applet parameter (e.g. set it to Yes in index.vnc). + +Proxies: they are a general problem with java socket applets (a socket +connection does not go through the proxy). See the info in the proxy.vnc +file for a workaround. It uses SignedVncViewer.jar which is simply +a signed version of VncViewer.jar. The basic idea is the user clicks +"Yes" to trust the applet and then it can connect directly to the proxy +and issue a CONNECT request. + +This applet has been tested on versions 1.4.2 and 1.5.0 of the Sun +Java plugin. It may not work on older releases or different vendor VM's. +Send full Java Console output for failures. + +--------------------------------------------------------------- +Tips: + +When doing single-port proxy connections (e.g. both VNC and HTTPS +thru port 5900) it helps to move through the 'do you trust this site' +dialogs quickly. x11vnc has to wait to see if the traffic is VNC or +HTTP and this can cause timeouts if you don't move thru them quickly. + +You may have to restart your browser completely if it gets into a +weird state. For one case we saw the JVM requesting VncViewer.class +even when no such file exists. + + +--------------------------------------------------------------- +Extras: + +ssl_vncviewer (not Java): + + Wrapper script for native VNC viewer to connect to x11vnc in + SSL mode. Script launches stunnel(8) and then connects to it + via localhost which in turn is then redirected to x11vnc via an + SSL tunnel. stunnel(8) must be installed and available in PATH. + + +Running Java SSL VncViewer from the command line: + + From this directory: + + java -cp ./VncViewer.jar VncViewer HOST <thehost> PORT <theport> + + substitute <thehost> and <theport> with the actual values. + diff --git a/classes/ssl/SignedVncViewer.jar b/classes/ssl/SignedVncViewer.jar Binary files differnew file mode 100644 index 0000000..20b3ddc --- /dev/null +++ b/classes/ssl/SignedVncViewer.jar diff --git a/classes/ssl/VncViewer.jar b/classes/ssl/VncViewer.jar Binary files differindex 7e36267..116f49c 100644 --- a/classes/ssl/VncViewer.jar +++ b/classes/ssl/VncViewer.jar diff --git a/classes/ssl/proxy.vnc b/classes/ssl/proxy.vnc new file mode 100644 index 0000000..9bb30e4 --- /dev/null +++ b/classes/ssl/proxy.vnc @@ -0,0 +1,70 @@ +<!-- + index.vnc - default HTML page for TightVNC Java viewer applet, to be + used with Xvnc. On any file ending in .vnc, the HTTP server embedded in + Xvnc will substitute the following variables when preceded by a dollar: + USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT, + PARAMS. Use two dollar signs ($$) to get a dollar sign in the generated + HTML page. + + NOTE: the $PARAMS variable is not supported by the standard VNC, so + make sure you have TightVNC on the server side, if you're using this + variable. +--> + +<!-- +The idea behind using the signed applet in SignedVncViewer.jar for +firewall proxies: + +Java socket applets and http proxies do not get along well. + +Java security allows the applet to connect back via a socket to the +originating host, but the browser/plugin Proxy settings are not used for +socket connections (only http and the like). So the socket connection +fails in the proxy environment. + +The applet is not allowed to open a socket connection to the proxy (since +that would let it connect to just about any host, e.g. CONNECT method). + +This is indpendent of SSL but of course fails for that socket connection +as well. I.e. this is a problem for non-SSL VNC Viewers as well. + +Solution? Sign the applet and have the user click on "Yes" that they +fully trust the applet. Then the applet can connect to any host via +sockets, in particular the proxy. It next issues the request + + CONNECT host:port HTTP/1.1 + Host: host:port + +and if the proxy supports the CONNECT method we are finally connected to +the VNC server. + +For SSL connections, SSL is layered on top of this socket. However note +this scheme will work for non-SSL applet proxy tunnelling as well. + +It should be able to get non-SSL VNC connections to work via GET +command but that has not been done yet. + +Note that some proxies only allow CONNECT to only these the ports 443 +(HTTPS) and 563 (SNEWS). So you would have to run the VNC server on +those ports. + +SignedVncViewer.jar is just a signed version of VncViewer.jar + +The URL to use for this file: https://host:port/proxy.vnc + +--> + + +<HTML> +<TITLE> +$USER's $DESKTOP desktop ($DISPLAY) +</TITLE> +<APPLET CODE=VncViewer.class ARCHIVE=SignedVncViewer.jar + WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT> +<param name=PORT value=$PORT> +<param name="Open New Window" value=yes> +$PARAMS +</APPLET> +<BR> +<A href="http://www.tightvnc.com/">TightVNC site</A> +</HTML> diff --git a/classes/ssl/ssl_vncviewer b/classes/ssl/ssl_vncviewer new file mode 100755 index 0000000..4f69a1c --- /dev/null +++ b/classes/ssl/ssl_vncviewer @@ -0,0 +1,142 @@ +#!/bin/sh +# +# ssl_vncviewer: wrapper for vncviewer to use stunnel SSL tunnel. +# +# You must have stunnel(8) installed on the system and in your +# PATH (n.b. stunnel is usually in an sbin subdir). +# +# You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..." +# running as the VNC server. +# +# usage: ssl_vncviewer [cert-args] host:display <vncviewer-args> +# +# e.g.: ssl_vncviewer snoopy:0 +# ssl_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile" +# +# [cert-args] can be: +# -verify /path/to/cacert.pem +# -mycert /path/to/mycert.pem +# +# -verify specifies a CA cert PEM file (or a self-signed one) for +# authenticating the VNC server. +# +# -mycert specifies this client's cert+key PEM file for the VNC server to +# authenticate this client. +# + +VNCVIEWERCMD="vncviewer" +PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH + +help() { + head -26 $0 | tail +2 +} + +# grab our cmdline options: +while [ "X$1" != "X" ] +do + case $1 in + "-verify") shift; verify="$1" + ;; + "-mycert") shift; mycert="$1" + ;; + "-h"*) help; exit 0 + ;; + *) break + ;; + esac + shift +done + +orig="$1" +shift + +# play around with host:display port: +if ! echo "$orig" | grep ':' > /dev/null; then + orig="$orig:0" +fi + +host=`echo "$orig" | awk -F: '{print $1}'` +disp=`echo "$orig" | awk -F: '{print $2}'` +if [ $disp -lt 200 ]; then + port=`expr $disp + 5900` +fi + +# try to find an open listening port via netstat(1): +use="" +if uname | grep Linux > /dev/null; then + inuse=`netstat -ant | grep LISTEN | awk '{print $4}' | sed 's/^.*://'` + try=5920 + while [ $try -lt 6000 ] + do + if ! echo "$inuse" | grep -w $try > /dev/null; then + use=$try + break + fi + try=`expr $try + 1` + done +fi +if [ "X$use" = "X" ]; then + # otherwise choose a "random" one: + use=`date +%S` + use=`expr $use + 5920` +fi + +# create the stunnel config file: +if [ "X$verify" != "X" ]; then + if [ -d $verify ]; then + verify="CApath = $verify" + else + verify="CAfile = $verify" + fi + verify="$verify +verify = 2" +fi +if [ "X$mycert" != "X" ]; then + cert="cert = $mycert" +fi + +##debug = 7 +tmp=/tmp/ssl_vncviewer.$$ +cat > $tmp <<END +foreground = yes +pid = +client = yes +$verify +$cert + +[vnc_stunnel] +accept = $use +connect= $host:$port +END + +echo "" +echo "Using this stunnel configuration:" +cat $tmp +echo "" +sleep 1 + +echo "running: stunnel $tmp" +stunnel $tmp < /dev/tty > /dev/tty & +pid=$! +echo "" + +# pause here to let the user supply a possible passphrase for the +# mycert key: +if [ "X$mycert" != "X" ]; then + sleep 4 +fi +sleep 2 +rm -f $tmp + +if [ $use -ge 5900 ]; then + n=`expr $use - 5900` +fi + +if echo "$0" | grep vncip > /dev/null; then + # hack for runge's special wrapper script vncip. + vncip "$@" localhost:$n +else + $VNCVIEWERCMD "$@" localhost:$n +fi + +kill $pid diff --git a/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch b/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch index 48f0dc4..298f7f9 100644 --- a/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch +++ b/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch @@ -38,7 +38,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile @$(ExportJavaClasses) diff -x VncCanvas.java -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto.java --- vnc_javasrc.orig/RfbProto.java 2004-03-04 08:34:25.000000000 -0500 -+++ vnc_javasrc/RfbProto.java 2006-03-27 22:26:25.000000000 -0500 ++++ vnc_javasrc/RfbProto.java 2006-04-03 11:22:30.000000000 -0400 @@ -199,7 +199,21 @@ host = h; port = p; @@ -64,8 +64,8 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto try { diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java --- vnc_javasrc.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 -+++ vnc_javasrc/SSLSocketToMe.java 2006-03-27 20:45:59.000000000 -0500 -@@ -0,0 +1,481 @@ ++++ vnc_javasrc/SSLSocketToMe.java 2006-04-04 13:17:39.000000000 -0400 +@@ -0,0 +1,1040 @@ +/* + * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer. + * @@ -92,7 +92,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL +import java.io.*; +import javax.net.ssl.*; +import java.security.cert.*; -+import java.util.Vector; ++import java.util.*; + +import java.awt.*; +import java.awt.event.*; @@ -109,14 +109,32 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + SSLSocket socket = null; + SSLSocketFactory factory; + ++ /* fallback for Proxy connection */ ++ boolean proxy_in_use = false; ++ boolean proxy_failure = false; ++ public DataInputStream is = null; ++ public OutputStream os = null; ++ ++ Socket proxySock; ++ DataInputStream proxy_is; ++ OutputStream proxy_os; ++ + /* trust contexts */ ++ SSLContext trustloc_ctx; + SSLContext trustall_ctx; ++ SSLContext trusturl_ctx; + SSLContext trustone_ctx; ++ + TrustManager[] trustAllCerts; ++ TrustManager[] trustUrlCert; + TrustManager[] trustOneCert; + ++ boolean use_url_cert_for_auth = true; ++ boolean user_wants_to_see_cert = true; ++ + /* cert(s) we retrieve from VNC server */ -+ java.security.cert.Certificate[] serverCerts = null; ++ java.security.cert.Certificate[] trustallCerts = null; ++ java.security.cert.Certificate[] trusturlCerts = null; + + SSLSocketToMe(String h, int p, VncViewer v) throws Exception { + host = h; @@ -131,6 +149,7 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + + /* create trust managers used if initial handshake fails: */ + ++ + trustAllCerts = new TrustManager[] { + /* + * this one accepts everything. @@ -149,13 +168,65 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + java.security.cert.X509Certificate[] certs, + String authType) { + /* empty */ ++ dbg("ALL: an untrusted connect to grab cert."); + } + } + }; + ++ trustUrlCert = new TrustManager[] { ++ /* ++ * this one accepts only the retrieved server cert ++ * by SSLSocket by this applet. ++ */ ++ new X509TrustManager() { ++ public java.security.cert.X509Certificate[] ++ getAcceptedIssuers() { ++ return null; ++ } ++ public void checkClientTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ throw new CertificateException("No Clients"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ if (trusturlCerts == null) { ++ throw new CertificateException( ++ "No Trust url Certs array."); ++ } ++ if (trusturlCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust url Certs."); ++ } ++ if (trusturlCerts.length > 1) { ++ throw new CertificateException( ++ "Too many Trust url Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length > 1) { ++ throw new CertificateException( ++ "Too many this-certs."); ++ } ++ if (! trusturlCerts[0].equals(certs[0])) { ++ throw new CertificateException( ++ "Server Cert Changed != URL."); ++ } ++ dbg("URL: trusturlCerts[0] matches certs[0]"); ++ } ++ } ++ }; + trustOneCert = new TrustManager[] { + /* -+ * this one accepts only the retrieved server cert. ++ * this one accepts only the retrieved server cert ++ * by SSLSocket by this applet. + */ + new X509TrustManager() { + public java.security.cert.X509Certificate[] @@ -170,19 +241,35 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, + String authType) throws CertificateException { -+ if (serverCerts == null) { ++ if (trustallCerts == null) { ++ throw new CertificateException( ++ "No Trust All Server Certs array."); ++ } ++ if (trustallCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust All Server Certs."); ++ } ++ if (trustallCerts.length > 1) { ++ throw new CertificateException( ++ "Too many Trust All Server Certs."); ++ } ++ if (certs == null) { + throw new CertificateException( -+ "No Server Certs array."); ++ "No this-certs array."); + } -+ if (serverCerts.length < 1) { ++ if (certs.length < 1) { + throw new CertificateException( -+ "No Server Certs."); ++ "No this-certs Certs."); + } -+ if (! serverCerts[0].equals(certs[0])) { ++ if (certs.length > 1) { + throw new CertificateException( -+ "Server Cert Changed."); ++ "Too many this-certs."); + } -+ dbg("serverCerts[0] matches certs[0]"); ++ if (! trustallCerts[0].equals(certs[0])) { ++ throw new CertificateException( ++ "Server Cert Changed != TRUSTALL."); ++ } ++ dbg("ONE: trustallCerts[0] matches certs[0]"); + } + } + }; @@ -195,6 +282,18 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + * 2) to subsequently connect to the server if user agrees. + */ + ++ /* trust loc certs: */ ++ try { ++ trustloc_ctx = SSLContext.getInstance("SSL"); ++ trustloc_ctx.init(null, null, new ++ java.security.SecureRandom()); ++ ++ } catch (Exception e) { ++ String msg = "SSL trustloc_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ + /* trust all certs: */ + try { + trustall_ctx = SSLContext.getInstance("SSL"); @@ -207,6 +306,18 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + throw new Exception(msg); + } + ++ /* trust url certs: */ ++ try { ++ trusturl_ctx = SSLContext.getInstance("SSL"); ++ trusturl_ctx.init(null, trustUrlCert, new ++ java.security.SecureRandom()); ++ ++ } catch (Exception e) { ++ String msg = "SSL trusturl_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ + /* trust the one cert from server: */ + try { + trustone_ctx = SSLContext.getInstance("SSL"); @@ -220,23 +331,121 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + } + } + ++ boolean browser_cert_match() { ++ String msg = "Browser URL accept previously accepted cert"; ++ ++ if (user_wants_to_see_cert) { ++ return false; ++ } ++ ++ if (trustallCerts != null && trusturlCerts != null) { ++ if (trustallCerts.length == 1 && trusturlCerts.length == 1) { ++ if (trustallCerts[0].equals(trusturlCerts[0])) { ++ System.out.println(msg); ++ return true; ++ } ++ } ++ } ++ return false; ++ } ++ + public Socket connectSock() throws IOException { + -+ /* now connect to host:port */ -+ socket = (SSLSocket) factory.createSocket(host, port); ++ /* ++ * first try a https connection to detect a proxy, and ++ * also grab the VNC server cert. ++ */ ++ URL url = new URL("https://" + host + ":" + port + ++ "/check.https.proxy.connection"); ++ try { ++ HttpsURLConnection https = (HttpsURLConnection) ++ url.openConnection(); ++ ++ https.setUseCaches(false); ++ https.setRequestMethod("GET"); ++ https.setRequestProperty("Pragma", "No-Cache"); ++ https.setRequestProperty("Proxy-Connection", ++ "Keep-Alive"); ++ https.setDoInput(true); ++ ++ https.connect(); ++ ++ trusturlCerts = https.getServerCertificates(); ++ ++ if (https.usingProxy()) { ++ proxy_in_use = true; ++ dbg("HTTPS proxy in use. There may be connection problems."); ++ } ++ Object output = https.getContent(); ++ https.disconnect(); ++ ++ } catch(Exception e) { ++ trusturlCerts = null; ++ } ++ ++ if (use_url_cert_for_auth && trusturlCerts != null) { ++ factory = trusturl_ctx.getSocketFactory(); ++ } else { ++ factory = trustloc_ctx.getSocketFactory(); ++ } ++ ++ socket = null; ++ try { ++ socket = (SSLSocket) factory.createSocket(host, port); ++ } catch (Exception esock) { ++ if (proxy_in_use) { ++ proxy_failure = true; ++ dbg("HTTPS proxy in use. Trying to go with it."); ++ try { ++ socket = proxy_socket(factory); ++ } catch (Exception e) { ++ dbg("err proxy_socket: " + e.getMessage()); ++ } ++ } ++ } + + try { -+ /* -+ * Verified the first time! How can that be? ;-) -+ * They actually went thru the trouble to set it up? -+ */ + socket.startHandshake(); -+ dbg("Server Connection Verified."); ++ dbg("Server Connection Verified on 1st try."); ++ ++ java.security.cert.Certificate[] currentTrustedCerts; ++ BrowserCertsDialog bcd; ++ ++ SSLSession sess = socket.getSession(); ++ currentTrustedCerts = sess.getPeerCertificates(); ++ ++ if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { ++ socket.close(); ++ socket = null; ++ throw new SSLHandshakeException("no current certs"); ++ } ++ ++ String serv = ""; ++ try { ++ CertInfo ci = new CertInfo(currentTrustedCerts[0]); ++ serv = ci.get_certinfo("CN"); ++ } catch (Exception e) { ++ ; ++ } ++ ++ bcd = new BrowserCertsDialog(serv, host + ":" + port); ++ bcd.queryUser(); ++ if (bcd.showCertDialog) { ++ String msg = "user wants to see cert"; ++ dbg(msg); ++ user_wants_to_see_cert = true; ++ throw new SSLHandshakeException(msg); ++ } else { ++ user_wants_to_see_cert = false; ++ dbg("bcd: user said yes, accept it"); ++ } + -+ } catch (Exception ehand) { ++ } catch (SSLHandshakeException eh) { + dbg("Could not automatically verify Server."); ++ dbg("msg: " + eh.getMessage()); + + socket.close(); ++ socket = null; + + /* + * Reconnect, trusting any cert, so we can grab @@ -244,7 +453,11 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + * is not used for anything else. + */ + factory = trustall_ctx.getSocketFactory(); -+ socket = (SSLSocket) factory.createSocket(host, port); ++ if (proxy_failure) { ++ socket = proxy_socket(factory); ++ } else { ++ socket = (SSLSocket) factory.createSocket(host, port); ++ } + + try { + socket.startHandshake(); @@ -253,33 +466,32 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + /* grab the cert: */ + try { + SSLSession sess = socket.getSession(); -+ serverCerts = sess.getPeerCertificates(); ++ trustallCerts = sess.getPeerCertificates(); + } catch (Exception e) { + throw new Exception("Could not get " + + "Peer Certificate"); + } + -+ /* -+ * close socket now, we will reopen after -+ * dialog if user agrees to use the cert. -+ */ -+ socket.close(); ++ if (! browser_cert_match()) { ++ /* ++ * close socket now, we will reopen after ++ * dialog if user agrees to use the cert. ++ */ ++ socket.close(); ++ socket = null; + -+ /* dialog with user to accept cert or not: */ ++ /* dialog with user to accept cert or not: */ + -+ TrustDialog td= new TrustDialog(host, port, -+ serverCerts); ++ TrustDialog td= new TrustDialog(host, port, ++ trustallCerts); + -+ if (! td.queryUser()) { -+ String msg = "User decided against it."; -+ dbg(msg); -+ throw new IOException(msg); ++ if (! td.queryUser()) { ++ String msg = "User decided against it."; ++ dbg(msg); ++ throw new IOException(msg); ++ } + } + -+ // idea to save certs for reconnections. -+ // not working (RfbProto thread terminates). -+ //viewer.acceptedCerts.addCerts(serverCerts); -+ + } catch (Exception ehand2) { + dbg("** Could not TrustAll Verify Server."); + @@ -293,7 +505,11 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + */ + + factory = trustone_ctx.getSocketFactory(); -+ socket = (SSLSocket) factory.createSocket(host, port); ++ if (proxy_failure) { ++ socket = proxy_socket(factory); ++ } else { ++ socket = (SSLSocket) factory.createSocket(host, port); ++ } + + try { + socket.startHandshake(); @@ -315,12 +531,171 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + System.out.println(s); + } + } ++ ++ public SSLSocket proxy_socket(SSLSocketFactory factory) { ++ Properties props = null; ++ String proxyHost = null; ++ int proxyPort = 0; ++ ++ /* see if we can guess the proxy info from Properties: */ ++ try { ++ props = System.getProperties(); ++ } catch (Exception e) { ++ dbg("props failed: " + e.getMessage()); ++ } ++ if (props != null) { ++ dbg("\n---------------\nAll props:"); ++ props.list(System.out); ++ dbg("\n---------------\n\n"); ++ ++ for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) { ++ String s = (String) e.nextElement(); ++ String v = System.getProperty(s); ++ String l1 = s.toLowerCase(); ++ String l2 = v.toLowerCase(); ++ ++ if (l1.indexOf("proxy") < 0 && l2.indexOf("proxy") < 0) { ++ continue; ++ } ++ if (l2.indexOf("https") < 0) { ++ continue; ++ } ++ ++ String[] pieces = v.split("[,;]"); ++ for (int i = 0; i < pieces.length; i++) { ++ String p = pieces[i]; ++ int j = p.indexOf("https"); ++ if (j < 0) { ++ continue; ++ } ++ j = p.indexOf("=", j); ++ if (j < 0) { ++ continue; ++ } ++ p = p.substring(j+1); ++ String [] hp = p.split(":"); ++ if (hp.length != 2) { ++ continue; ++ } ++ if (hp[0].length() > 1 && hp[1].length() > 1) { ++ try { ++ Integer I = new Integer(hp[1]); ++ proxyPort = I.intValue(); ++ } catch (Exception ex) { ++ continue; ++ } ++ proxyHost = new String(hp[0]); ++ break; ++ } ++ } ++ } ++ } ++ if (proxyHost != null) { ++ dbg("Lucky us! we figured out the Proxy parameters: " + proxyHost + " " + proxyPort); ++ } else { ++ /* ask user to help us: */ ++ ProxyDialog pd = new ProxyDialog(proxyHost, proxyPort); ++ pd.queryUser(); ++ proxyHost = pd.getHost(); ++ proxyPort = pd.getPort(); ++ dbg("User said host: " + pd.getHost() + " port: " + pd.getPort()); ++ } ++ ++ proxySock = psocket(proxyHost, proxyPort); ++ if (proxySock == null) { ++ dbg("1 sadly, returning a null socket"); ++ return null; ++ } ++ String hp = host + ":" + port; ++ ++ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" ++ + "Host: " + hp + "\r\n\r\n"; ++ ++ /* not working for SSL yet: */ ++ String req2 = "GET https://" + hp ++ + "/request.https.proxy.connection HTTP/1.1\r\n" ++ + "Host: " + hp + "\r\n\r\n"; ++ ++ dbg("requesting: " + req1); ++ ++ try { ++ proxy_os.write(req1.getBytes()); ++ String reply = readline(proxy_is); ++ ++ dbg("proxy replied: " + reply); ++ ++ if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) { ++ proxySock.close(); ++ proxySock = psocket(proxyHost, proxyPort); ++ if (proxySock == null) { ++ dbg("2 sadly, returning a null socket"); ++ return null; ++ } ++ dbg("requesting: " + req2); ++ proxy_os.write(req2.getBytes()); ++ ++ reply = readline(proxy_is); ++ ++ dbg("proxy replied: " + reply); ++ } ++ } catch(Exception e) { ++ dbg("sock prob: " + e.getMessage()); ++ } ++ ++ while (true) { ++ String line = readline(proxy_is); ++ dbg("proxy line: " + line); ++ if (line.equals("\r\n") || line.equals("\n")) { ++ break; ++ } ++ } ++ ++ Socket sslsock = null; ++ try { ++ sslsock = factory.createSocket(proxySock, host, port, true); ++ } catch(Exception e) { ++ dbg("sslsock prob: " + e.getMessage()); ++ dbg("3 sadly, returning a null socket"); ++ } ++ ++ return (SSLSocket) sslsock; ++ } ++ ++ Socket psocket(String h, int p) { ++ Socket psock = null; ++ try { ++ psock = new Socket(h, p); ++ proxy_is = new DataInputStream(new BufferedInputStream( ++ psock.getInputStream(), 16384)); ++ proxy_os = psock.getOutputStream(); ++ } catch(Exception e) { ++ dbg("psocket prob: " + e.getMessage()); ++ return null; ++ } ++ ++ return psock; ++ } ++ ++ String readline(DataInputStream i) { ++ byte[] ba = new byte[1]; ++ String s = new String(""); ++ ba[0] = 0; ++ try { ++ while (ba[0] != 0xa) { ++ ba[0] = (byte) i.readUnsignedByte(); ++ s += new String(ba); ++ } ++ } catch (Exception e) { ++ ; ++ } ++ return s; ++ } +} + +class TrustDialog implements ActionListener { + String msg, host, text; + int port; -+ java.security.cert.Certificate[] serverCerts = null; ++ java.security.cert.Certificate[] trustallCerts = null; + boolean viewing_cert = false; + boolean trust_this_session = false; + @@ -339,51 +714,34 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + + " this VNC server"; + String ln = "\n---------------------------------------------------\n\n"; + -+ TrustDialog (String h, int p, java.security.cert.Certificate[] certs) { ++ TrustDialog (String h, int p, java.security.cert.Certificate[] s) { + host = h; + port = p; -+ serverCerts = certs; ++ trustallCerts = s; + + msg = "VNC Server " + host + ":" + port + " Not Verified"; + } + + public boolean queryUser() { + -+// idea to save certs between connections. not working, everything is -+// cleared after each new connection. -+// -+// public boolean queryUser(VncViewer viewer) { -+// int i, j; -+// java.security.cert.Certificate cert; -+// -+// for (i=0; i < viewer.acceptedCerts.allCerts.size(); i++) { -+// System.out.println("try " + i); -+// -+// cert = (java.security.cert.Certificate) -+// viewer.acceptedCerts.allCerts.elementAt(i); -+// -+// for (j=0; j < serverCerts.length; j++) { -+// System.out.println("try " + i + " " + j); -+// if (serverCerts[j].equals(cert)) { -+// System.out.println("accept previously accepted cert"); -+// /* matched, no need for dialog */ -+// return true; -+// } -+// } -+// } -+ + /* create and display the dialog for unverified cert. */ + + Frame frame = new Frame(msg); + + dialog = new Dialog(frame, true); + ++ String infostr = ""; ++ if (trustallCerts.length == 1) { ++ CertInfo ci = new CertInfo(trustallCerts[0]); ++ infostr = ci.get_certinfo("all"); ++ } ++ + text = "\n" ++ "Unable to verify the identity of\n" ++ "\n" ++ " " + host + ":" + port + "\n" ++ "\n" -++ get_certinfo() +++ infostr ++ "\n" ++ "as a trusted VNC server.\n" ++ "\n" @@ -473,12 +831,12 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + int i; + /* show all (likely just one) certs: */ + textarea.setText(""); -+ for (i=0; i < serverCerts.length; i++) { ++ for (i=0; i < trustallCerts.length; i++) { + int j = i + 1; + textarea.append("Certificate[" + + j + "]\n\n"); + textarea.append( -+ serverCerts[i].toString()); ++ trustallCerts[i].toString()); + textarea.append(ln); + } + viewcert.setLabel("View Info"); @@ -508,11 +866,11 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + String all = ""; + String fields[] = {"CN", "OU", "O", "L", "C"}; + int i; -+ if (serverCerts.length < 1) { ++ if (trustallCerts.length < 1) { + all = ""; + return all; + } -+ String cert = serverCerts[0].toString(); ++ String cert = trustallCerts[0].toString(); + + /* + * For now we simply scrape the cert string, there must @@ -547,6 +905,207 @@ diff -x VncCanvas.java -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSL + return all; + } +} ++ ++class ProxyDialog implements ActionListener { ++ String guessedHost = null; ++ String guessedPort = null; ++ /* ++ * this is the gui to show the user the cert and info and ask ++ * them if they want to continue using this cert. ++ */ ++ ++ Button ok; ++ Dialog dialog; ++ TextField entry; ++ String reply = ""; ++ ++ ProxyDialog (String h, int p) { ++ guessedHost = h; ++ try { ++ guessedPort = Integer.toString(p); ++ } catch (Exception e) { ++ guessedPort = "8080"; ++ } ++ } ++ ++ public void queryUser() { ++ ++ /* create and display the dialog for unverified cert. */ ++ ++ Frame frame = new Frame("Need Proxy host:port"); ++ ++ dialog = new Dialog(frame, true); ++ ++ ++ Label label = new Label("Please Enter your https Proxy info as host:port", Label.CENTER); ++ //label.setFont(new Font("Helvetica", Font.BOLD, 16)); ++ entry = new TextField(30); ++ ok = new Button("OK"); ++ ok.addActionListener(this); ++ ++ String guess = ""; ++ if (guessedHost != null) { ++ guess = guessedHost + ":" + guessedPort; ++ } ++ entry.setText(guess); ++ ++ dialog.setLayout(new BorderLayout()); ++ dialog.add("North", label); ++ dialog.add("Center", entry); ++ dialog.add("South", ok); ++ dialog.pack(); ++ dialog.resize(dialog.preferredSize()); ++ ++ dialog.show(); /* block here til OK or Cancel pressed. */ ++ return; ++ } ++ ++ public String getHost() { ++ int i = reply.indexOf(":"); ++ if (i < 0) { ++ return "unknown"; ++ } ++ String h = reply.substring(0, i); ++ return h; ++ } ++ ++ public int getPort() { ++ int i = reply.indexOf(":"); ++ int p = 8080; ++ if (i < 0) { ++ return p; ++ } ++ i++; ++ String ps = reply.substring(i); ++ try { ++ Integer I = new Integer(ps); ++ p = I.intValue(); ++ } catch (Exception e) { ++ ; ++ } ++ return p; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == ok) { ++ reply = entry.getText(); ++ dialog.dispose(); ++ } ++ } ++} ++ ++class BrowserCertsDialog implements ActionListener { ++ Button yes, no; ++ Dialog dialog; ++ String vncServer; ++ String hostport; ++ public boolean showCertDialog = true; ++ ++ BrowserCertsDialog(String serv, String hp) { ++ vncServer = serv; ++ hostport = hp; ++ } ++ ++ public void queryUser() { ++ ++ /* create and display the dialog for unverified cert. */ ++ ++ Frame frame = new Frame("Use Browser/JVM Certs?"); ++ ++ dialog = new Dialog(frame, true); ++ ++ String m = "\nShould this VNC Viewer applet use your Browser/JVM certs to\n"; ++ m += "authenticate the VNC Server:\n"; ++ m += "\n " + hostport + "\n\n " + vncServer + "\n\n"; ++ m += "(NOTE: this *includes* any certs you have Just Now accepted in a\n"; ++ m += "dialog box with your Web Browser or Java Applet Plugin)\n\n"; ++ ++ TextArea textarea = new TextArea(m, 12, 64, ++ TextArea.SCROLLBARS_VERTICAL_ONLY); ++ textarea.setEditable(false); ++ yes = new Button("Yes"); ++ yes.addActionListener(this); ++ no = new Button("No, Let Me See the Certificate."); ++ no.addActionListener(this); ++ ++ dialog.setLayout(new BorderLayout()); ++ dialog.add("North", textarea); ++ dialog.add("Center", yes); ++ dialog.add("South", no); ++ dialog.pack(); ++ dialog.resize(dialog.preferredSize()); ++ ++ dialog.show(); /* block here til Yes or No pressed. */ ++ return; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == yes) { ++ showCertDialog = false; ++ dialog.dispose(); ++ } else if (evt.getSource() == no) { ++ showCertDialog = true; ++ dialog.dispose(); ++ } ++ } ++} ++ ++class CertInfo { ++ String fields[] = {"CN", "OU", "O", "L", "C"}; ++ java.security.cert.Certificate cert; ++ String certString = ""; ++ ++ CertInfo(java.security.cert.Certificate c) { ++ cert = c; ++ certString = cert.toString(); ++ } ++ ++ String get_certinfo(String which) { ++ int i; ++ String cs = new String(certString); ++ String all = ""; ++ ++ /* ++ * For now we simply scrape the cert string, there must ++ * be an API for this... perhaps optionValue? ++ */ ++ for (i=0; i < fields.length; i++) { ++ int f, t, t1, t2; ++ String sub, mat = fields[i] + "="; ++ ++ f = cs.indexOf(mat, 0); ++ if (f > 0) { ++ t1 = cs.indexOf(", ", f); ++ t2 = cs.indexOf("\n", f); ++ if (t1 < 0 && t2 < 0) { ++ continue; ++ } else if (t1 < 0) { ++ t = t2; ++ } else if (t2 < 0) { ++ t = t1; ++ } else if (t1 < t2) { ++ t = t1; ++ } else { ++ t = t2; ++ } ++ if (t > f) { ++ sub = cs.substring(f, t); ++ all = all + " " + sub + "\n"; ++ if (which.equals(fields[i])) { ++ return sub; ++ } ++ } ++ } ++ } ++ if (which.equals("all")) { ++ return all; ++ } else { ++ return ""; ++ } ++ } ++} diff -x VncCanvas.java -Naur vnc_javasrc.orig/VncViewer.java vnc_javasrc/VncViewer.java --- vnc_javasrc.orig/VncViewer.java 2004-03-04 08:34:25.000000000 -0500 +++ vnc_javasrc/VncViewer.java 2006-03-27 22:20:19.000000000 -0500 |