| diff -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile |
| --- vnc_javasrc.orig/Makefile 2004-03-04 08:34:25.000000000 -0500 |
| +++ vnc_javasrc/Makefile 2010-05-18 20:56:26.000000000 -0400 |
| @@ -4,6 +4,7 @@ |
| |
| CP = cp |
| JC = javac |
| +JC_ARGS = -target 1.4 -source 1.4 |
| JAR = jar |
| ARCHIVE = VncViewer.jar |
| MANIFEST = MANIFEST.MF |
| @@ -15,25 +16,29 @@ |
| DesCipher.class CapabilityInfo.class CapsContainer.class \ |
| RecordingFrame.class SessionRecorder.class AuthUnixLoginPanel.class \ |
| SocketFactory.class HTTPConnectSocketFactory.class \ |
| - HTTPConnectSocket.class ReloginPanel.class |
| + HTTPConnectSocket.class ReloginPanel.class \ |
| + SSLSocketToMe.class |
| + |
| +SSL_CLASSES = SSLSocketToMe*.class TrustDialog.class |
| |
| SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \ |
| OptionsFrame.java ClipboardFrame.java ButtonPanel.java \ |
| DesCipher.java CapabilityInfo.java CapsContainer.java \ |
| RecordingFrame.java SessionRecorder.java AuthUnixLoginPanel.java \ |
| SocketFactory.java HTTPConnectSocketFactory.java \ |
| - HTTPConnectSocket.java ReloginPanel.java |
| + HTTPConnectSocket.java ReloginPanel.java \ |
| + SSLSocketToMe.java |
| |
| all: $(CLASSES) $(ARCHIVE) |
| |
| $(CLASSES): $(SOURCES) |
| - $(JC) -target 1.1 -O $(SOURCES) |
| + $(JC) $(JC_ARGS) -O $(SOURCES) |
| |
| $(ARCHIVE): $(CLASSES) $(MANIFEST) |
| - $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) |
| + $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) $(SSL_CLASSES) |
| |
| install: $(CLASSES) $(ARCHIVE) |
| - $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) |
| + $(CP) $(CLASSES) $(SSL_CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) |
| |
| export:: $(CLASSES) $(ARCHIVE) $(PAGES) |
| @$(ExportJavaClasses) |
| diff -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 2010-11-30 22:05:12.000000000 -0500 |
| @@ -199,7 +199,21 @@ |
| host = h; |
| port = p; |
| |
| - if (viewer.socketFactory == null) { |
| + if (! viewer.disableSSL) { |
| + System.out.println("new SSLSocketToMe"); |
| + SSLSocketToMe ssl; |
| + try { |
| + ssl = new SSLSocketToMe(host, port, v); |
| + } catch (Exception e) { |
| + throw new IOException(e.getMessage()); |
| + } |
| + |
| + try { |
| + sock = ssl.connectSock(); |
| + } catch (Exception es) { |
| + throw new IOException(es.getMessage()); |
| + } |
| + } else if (viewer.socketFactory == null) { |
| sock = new Socket(host, port); |
| } else { |
| try { |
| @@ -255,7 +269,7 @@ |
| || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) |
| { |
| throw new Exception("Host " + host + " port " + port + |
| - " is not an RFB server"); |
| + " is not an RFB server: " + b); |
| } |
| |
| serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); |
| @@ -892,6 +906,38 @@ |
| final static int ALT_MASK = InputEvent.ALT_MASK; |
| |
| |
| + void writeWheelEvent(MouseWheelEvent evt) throws IOException { |
| + |
| + eventBufLen = 0; |
| + |
| + int x = evt.getX(); |
| + int y = evt.getY(); |
| + |
| + if (x < 0) x = 0; |
| + if (y < 0) y = 0; |
| + |
| + int ptrmask; |
| + |
| + int clicks = evt.getWheelRotation(); |
| + System.out.println("writeWheelEvent: clicks: " + clicks); |
| + if (clicks > 0) { |
| + ptrmask = 16; |
| + } else if (clicks < 0) { |
| + ptrmask = 8; |
| + } else { |
| + return; |
| + } |
| + |
| + eventBuf[eventBufLen++] = (byte) PointerEvent; |
| + eventBuf[eventBufLen++] = (byte) ptrmask; |
| + eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); |
| + eventBuf[eventBufLen++] = (byte) (x & 0xff); |
| + eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); |
| + eventBuf[eventBufLen++] = (byte) (y & 0xff); |
| + |
| + os.write(eventBuf, 0, eventBufLen); |
| + } |
| + |
| // |
| // Write a pointer event message. We may need to send modifier key events |
| // around it to set the correct modifier state. |
| @@ -992,6 +1038,19 @@ |
| boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); |
| |
| int key; |
| + if (viewer.debugKeyboard) { |
| + System.out.println("----------------------------------------"); |
| + System.out.println("evt.getKeyChar: " + evt.getKeyChar()); |
| + System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); |
| + System.out.println("evt.getKeyCode: " + evt.getKeyCode()); |
| + System.out.println("evt.getID: " + evt.getID()); |
| + System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); |
| + System.out.println("evt.isActionKey: " + evt.isActionKey()); |
| + System.out.println("evt.isControlDown: " + evt.isControlDown()); |
| + System.out.println("evt.getModifiers: " + evt.getModifiers()); |
| + System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); |
| + System.out.println("evt.paramString: " + evt.paramString()); |
| + } |
| if (evt.isActionKey()) { |
| |
| // |
| @@ -1025,6 +1084,13 @@ |
| return; |
| } |
| |
| + if(key == 0xffc2 && viewer.mapF5_to_atsign) { |
| + if (viewer.debugKeyboard) { |
| + System.out.println("Mapping: F5 -> AT "); |
| + } |
| + key = 0x40; |
| + } |
| + |
| } else { |
| |
| // |
| @@ -1036,6 +1102,7 @@ |
| |
| key = keyChar; |
| |
| + |
| if (key < 0x20) { |
| if (evt.isControlDown()) { |
| key += 0x60; |
| @@ -1121,6 +1188,16 @@ |
| int oldModifiers = 0; |
| |
| void writeModifierKeyEvents(int newModifiers) { |
| + if(viewer.forbid_Ctrl_Alt) { |
| + if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { |
| + int orig = newModifiers; |
| + newModifiers &= ~ALT_MASK; |
| + newModifiers &= ~CTRL_MASK; |
| + if (viewer.debugKeyboard) { |
| + System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); |
| + } |
| + } |
| + } |
| if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) |
| writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); |
| |
| diff -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 2010-07-10 19:18:06.000000000 -0400 |
| @@ -0,0 +1,2067 @@ |
| +/* |
| + * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer. |
| + * |
| + * Copyright (c) 2006 Karl J. Runge <[email protected]> |
| + * All rights reserved. |
| + * |
| + * This is free software; you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License as published by |
| + * the Free Software Foundation; version 2 of the License, or |
| + * (at your option) any later version. |
| + * |
| + * This software is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with this software; if not, write to the Free Software |
| + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| + * USA. |
| + * |
| + */ |
| + |
| +import java.net.*; |
| +import java.io.*; |
| +import javax.net.ssl.*; |
| +import java.util.*; |
| + |
| +import java.security.*; |
| +import java.security.cert.*; |
| +import java.security.spec.*; |
| +import java.security.cert.Certificate; |
| +import java.security.cert.CertificateFactory; |
| + |
| +import java.awt.*; |
| +import java.awt.event.*; |
| + |
| +public class SSLSocketToMe { |
| + |
| + /* basic member data: */ |
| + String host; |
| + int port; |
| + VncViewer viewer; |
| + |
| + boolean debug = true; |
| + boolean debug_certs = false; |
| + |
| + /* sockets */ |
| + 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; |
| + |
| + /* strings from user WRT proxy: */ |
| + String proxy_auth_string = null; |
| + String proxy_dialog_host = null; |
| + int proxy_dialog_port = 0; |
| + |
| + Socket proxySock; |
| + DataInputStream proxy_is; |
| + OutputStream proxy_os; |
| + |
| + /* trust contexts */ |
| + SSLContext trustloc_ctx; |
| + SSLContext trustall_ctx; |
| + SSLContext trustsrv_ctx; |
| + SSLContext trusturl_ctx; |
| + SSLContext trustone_ctx; |
| + |
| + /* corresponding trust managers */ |
| + TrustManager[] trustAllCerts; |
| + TrustManager[] trustSrvCert; |
| + TrustManager[] trustUrlCert; |
| + TrustManager[] trustOneCert; |
| + |
| + /* client-side SSL auth key (oneTimeKey=...) */ |
| + KeyManager[] mykey = null; |
| + |
| + boolean user_wants_to_see_cert = true; |
| + String cert_fail = null; |
| + |
| + /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ |
| + java.security.cert.Certificate[] trustallCerts = null; |
| + java.security.cert.Certificate[] trustsrvCerts = null; |
| + java.security.cert.Certificate[] trusturlCerts = null; |
| + |
| + /* utility to decode hex oneTimeKey=... and serverCert=... */ |
| + byte[] hex2bytes(String s) { |
| + byte[] bytes = new byte[s.length()/2]; |
| + for (int i=0; i<s.length()/2; i++) { |
| + int j = 2*i; |
| + try { |
| + int val = Integer.parseInt(s.substring(j, j+2), 16); |
| + if (val > 127) { |
| + val -= 256; |
| + } |
| + Integer I = new Integer(val); |
| + bytes[i] = Byte.decode(I.toString()).byteValue(); |
| + |
| + } catch (Exception e) { |
| + ; |
| + } |
| + } |
| + return bytes; |
| + } |
| + |
| + SSLSocketToMe(String h, int p, VncViewer v) throws Exception { |
| + host = h; |
| + port = p; |
| + viewer = v; |
| + |
| + debug_certs = v.debugCerts; |
| + |
| + /* we will first try default factory for certification: */ |
| + |
| + factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); |
| + |
| + dbg("SSL startup: " + host + " " + port); |
| + |
| + |
| + /* create trust managers to be used if initial handshake fails: */ |
| + |
| + trustAllCerts = new TrustManager[] { |
| + /* |
| + * this one accepts everything. Only used if user |
| + * has disabled checking (trustAllVncCerts=yes) |
| + * or when we grab the cert to show it to them in |
| + * a dialog and ask them to manually verify/accept it. |
| + */ |
| + new X509TrustManager() { |
| + public java.security.cert.X509Certificate[] |
| + getAcceptedIssuers() { |
| + return null; |
| + } |
| + public void checkClientTrusted( |
| + java.security.cert.X509Certificate[] certs, |
| + String authType) { |
| + /* empty */ |
| + } |
| + public void checkServerTrusted( |
| + 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 and stored in |
| + * trusturlCerts. |
| + */ |
| + 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 (URL)"); |
| + } |
| + public void checkServerTrusted( |
| + java.security.cert.X509Certificate[] certs, |
| + String authType) throws CertificateException { |
| + /* we want to check 'certs' against 'trusturlCerts' */ |
| + if (trusturlCerts == null) { |
| + throw new CertificateException( |
| + "No Trust url Certs array."); |
| + } |
| + if (trusturlCerts.length < 1) { |
| + throw new CertificateException( |
| + "No 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 != trusturlCerts.length) { |
| + throw new CertificateException( |
| + "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); |
| + } |
| + boolean ok = true; |
| + for (int i = 0; i < certs.length; i++) { |
| + if (! trusturlCerts[i].equals(certs[i])) { |
| + ok = false; |
| + dbg("URL: cert mismatch at i=" + i); |
| + dbg("URL: cert mismatch cert" + certs[i]); |
| + dbg("URL: cert mismatch url" + trusturlCerts[i]); |
| + if (cert_fail == null) { |
| + cert_fail = "cert-mismatch"; |
| + } |
| + } |
| + if (debug_certs) { |
| + dbg("\n***********************************************"); |
| + dbg("URL: cert info at i=" + i); |
| + dbg("URL: cert info cert" + certs[i]); |
| + dbg("==============================================="); |
| + dbg("URL: cert info url" + trusturlCerts[i]); |
| + dbg("***********************************************"); |
| + } |
| + } |
| + if (!ok) { |
| + throw new CertificateException( |
| + "Server Cert Chain != URL Cert Chain."); |
| + } |
| + dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); |
| + } |
| + } |
| + }; |
| + |
| + trustSrvCert = new TrustManager[] { |
| + /* |
| + * this one accepts cert given to us in the serverCert |
| + * Applet Parameter we were started with. It is |
| + * currently a fatal error if the VNC Server's cert |
| + * doesn't match it. |
| + */ |
| + 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 (SRV)"); |
| + } |
| + public void checkServerTrusted( |
| + java.security.cert.X509Certificate[] certs, |
| + String authType) throws CertificateException { |
| + /* we want to check 'certs' against 'trustsrvCerts' */ |
| + if (trustsrvCerts == null) { |
| + throw new CertificateException( |
| + "No Trust srv Certs array."); |
| + } |
| + if (trustsrvCerts.length < 1) { |
| + throw new CertificateException( |
| + "No Trust srv 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 != trustsrvCerts.length) { |
| + throw new CertificateException( |
| + "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); |
| + } |
| + boolean ok = true; |
| + for (int i = 0; i < certs.length; i++) { |
| + if (! trustsrvCerts[i].equals(certs[i])) { |
| + ok = false; |
| + dbg("SRV: cert mismatch at i=" + i); |
| + dbg("SRV: cert mismatch cert" + certs[i]); |
| + dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); |
| + if (cert_fail == null) { |
| + cert_fail = "server-cert-mismatch"; |
| + } |
| + } |
| + if (debug_certs) { |
| + dbg("\n***********************************************"); |
| + dbg("SRV: cert info at i=" + i); |
| + dbg("SRV: cert info cert" + certs[i]); |
| + dbg("==============================================="); |
| + dbg("SRV: cert info srv" + trustsrvCerts[i]); |
| + dbg("***********************************************"); |
| + } |
| + } |
| + if (!ok) { |
| + throw new CertificateException( |
| + "Server Cert Chain != serverCert Applet Parameter Cert Chain."); |
| + } |
| + dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); |
| + } |
| + } |
| + }; |
| + |
| + trustOneCert = new TrustManager[] { |
| + /* |
| + * this one accepts only the retrieved server |
| + * cert by SSLSocket by this applet we stored in |
| + * trustallCerts that user has accepted or applet |
| + * parameter trustAllVncCerts=yes is set. This is |
| + * for when we reconnect after the user has manually |
| + * accepted the trustall cert in the dialog (or set |
| + * trustAllVncCerts=yes applet param.) |
| + */ |
| + 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 (ONE)"); |
| + } |
| + public void checkServerTrusted( |
| + java.security.cert.X509Certificate[] certs, |
| + String authType) throws CertificateException { |
| + /* we want to check 'certs' against 'trustallCerts' */ |
| + 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 (certs == null) { |
| + throw new CertificateException( |
| + "No this-certs array."); |
| + } |
| + if (certs.length < 1) { |
| + throw new CertificateException( |
| + "No this-certs Certs."); |
| + } |
| + if (certs.length != trustallCerts.length) { |
| + throw new CertificateException( |
| + "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); |
| + } |
| + boolean ok = true; |
| + for (int i = 0; i < certs.length; i++) { |
| + if (! trustallCerts[i].equals(certs[i])) { |
| + ok = false; |
| + dbg("ONE: cert mismatch at i=" + i); |
| + dbg("ONE: cert mismatch cert" + certs[i]); |
| + dbg("ONE: cert mismatch all" + trustallCerts[i]); |
| + } |
| + if (debug_certs) { |
| + dbg("\n***********************************************"); |
| + dbg("ONE: cert info at i=" + i); |
| + dbg("ONE: cert info cert" + certs[i]); |
| + dbg("==============================================="); |
| + dbg("ONE: cert info all" + trustallCerts[i]); |
| + dbg("***********************************************"); |
| + } |
| + } |
| + if (!ok) { |
| + throw new CertificateException( |
| + "Server Cert Chain != TRUSTALL Cert Chain."); |
| + } |
| + dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); |
| + } |
| + } |
| + }; |
| + |
| + /* |
| + * The above TrustManagers are used: |
| + * |
| + * 1) to retrieve the server cert in case of failure to |
| + * display it to the user in a dialog. |
| + * 2) to subsequently connect to the server if user agrees. |
| + */ |
| + |
| + /* |
| + * build oneTimeKey cert+key if supplied in applet parameter: |
| + */ |
| + if (viewer.oneTimeKey != null && viewer.oneTimeKey.equals("PROMPT")) { |
| + ClientCertDialog d = new ClientCertDialog(); |
| + viewer.oneTimeKey = d.queryUser(); |
| + } |
| + if (viewer.oneTimeKey != null && viewer.oneTimeKey.indexOf(",") > 0) { |
| + int idx = viewer.oneTimeKey.indexOf(","); |
| + |
| + String onetimekey = viewer.oneTimeKey.substring(0, idx); |
| + byte[] key = hex2bytes(onetimekey); |
| + String onetimecert = viewer.oneTimeKey.substring(idx+1); |
| + byte[] cert = hex2bytes(onetimecert); |
| + |
| + KeyFactory kf = KeyFactory.getInstance("RSA"); |
| + PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key ); |
| + PrivateKey ff = kf.generatePrivate (keysp); |
| + if (debug_certs) { |
| + dbg("one time key " + ff); |
| + } |
| + |
| + CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| + Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); |
| + Certificate[] certs = new Certificate[c.toArray().length]; |
| + if (c.size() == 1) { |
| + Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); |
| + if (debug_certs) { |
| + dbg("one time cert" + tmpcert); |
| + } |
| + certs[0] = tmpcert; |
| + } else { |
| + certs = (Certificate[]) c.toArray(); |
| + } |
| + |
| + KeyStore ks = KeyStore.getInstance("JKS"); |
| + ks.load(null, null); |
| + ks.setKeyEntry("onetimekey", ff, "".toCharArray(), certs); |
| + String da = KeyManagerFactory.getDefaultAlgorithm(); |
| + KeyManagerFactory kmf = KeyManagerFactory.getInstance(da); |
| + kmf.init(ks, "".toCharArray()); |
| + |
| + mykey = kmf.getKeyManagers(); |
| + } |
| + |
| + /* |
| + * build serverCert cert if supplied in applet parameter: |
| + */ |
| + if (viewer.serverCert != null) { |
| + CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| + byte[] cert = hex2bytes(viewer.serverCert); |
| + Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); |
| + trustsrvCerts = new Certificate[c.toArray().length]; |
| + if (c.size() == 1) { |
| + Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); |
| + trustsrvCerts[0] = tmpcert; |
| + } else { |
| + trustsrvCerts = (Certificate[]) c.toArray(); |
| + } |
| + } |
| + |
| + /* the trust loc certs context: */ |
| + try { |
| + trustloc_ctx = SSLContext.getInstance("SSL"); |
| + |
| + /* |
| + * below is a failed attempt to get jvm's default |
| + * trust manager using null (below) makes it so |
| + * for HttpsURLConnection the server cannot be |
| + * verified (no prompting.) |
| + */ |
| + if (false) { |
| + boolean didit = false; |
| + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); |
| + tmf.init((KeyStore) null); |
| + TrustManager [] tml = tmf.getTrustManagers(); |
| + for (int i = 0; i < tml.length; i++) { |
| + TrustManager tm = tml[i]; |
| + if (tm instanceof X509TrustManager) { |
| + TrustManager tm1[] = new TrustManager[1]; |
| + tm1[0] = tm; |
| + trustloc_ctx.init(mykey, tm1, null); |
| + didit = true; |
| + break; |
| + } |
| + } |
| + if (!didit) { |
| + trustloc_ctx.init(mykey, null, null); |
| + } |
| + } else { |
| + /* we have to set trust manager to null */ |
| + trustloc_ctx.init(mykey, null, null); |
| + } |
| + |
| + } catch (Exception e) { |
| + String msg = "SSL trustloc_ctx FAILED."; |
| + dbg(msg); |
| + throw new Exception(msg); |
| + } |
| + |
| + /* the trust all certs context: */ |
| + try { |
| + trustall_ctx = SSLContext.getInstance("SSL"); |
| + trustall_ctx.init(mykey, trustAllCerts, new |
| + java.security.SecureRandom()); |
| + |
| + } catch (Exception e) { |
| + String msg = "SSL trustall_ctx FAILED."; |
| + dbg(msg); |
| + throw new Exception(msg); |
| + } |
| + |
| + /* the trust url certs context: */ |
| + try { |
| + trusturl_ctx = SSLContext.getInstance("SSL"); |
| + trusturl_ctx.init(mykey, trustUrlCert, new |
| + java.security.SecureRandom()); |
| + |
| + } catch (Exception e) { |
| + String msg = "SSL trusturl_ctx FAILED."; |
| + dbg(msg); |
| + throw new Exception(msg); |
| + } |
| + |
| + /* the trust srv certs context: */ |
| + try { |
| + trustsrv_ctx = SSLContext.getInstance("SSL"); |
| + trustsrv_ctx.init(mykey, trustSrvCert, new |
| + java.security.SecureRandom()); |
| + |
| + } catch (Exception e) { |
| + String msg = "SSL trustsrv_ctx FAILED."; |
| + dbg(msg); |
| + throw new Exception(msg); |
| + } |
| + |
| + /* the trust the one cert from server context: */ |
| + try { |
| + trustone_ctx = SSLContext.getInstance("SSL"); |
| + trustone_ctx.init(mykey, trustOneCert, new |
| + java.security.SecureRandom()); |
| + |
| + } catch (Exception e) { |
| + String msg = "SSL trustone_ctx FAILED."; |
| + dbg(msg); |
| + throw new Exception(msg); |
| + } |
| + } |
| + |
| + /* |
| + * we call this early on to 1) check for a proxy, 2) grab |
| + * Browser/JVM accepted HTTPS cert. |
| + */ |
| + public void check_for_proxy_and_grab_vnc_server_cert() { |
| + |
| + trusturlCerts = null; |
| + proxy_in_use = false; |
| + |
| + if (viewer.ignoreProxy) { |
| + /* applet param says skip it. */ |
| + /* the downside is we do not set trusturlCerts for comparison later... */ |
| + /* nor do we autodetect x11vnc for GET=1. */ |
| + return; |
| + } |
| + |
| + dbg("------------------------------------------------"); |
| + dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); |
| + |
| + dbg("TRYING HTTPS:"); |
| + String ustr = "https://" + host + ":"; |
| + if (viewer.httpsPort != null) { |
| + ustr += viewer.httpsPort; |
| + } else { |
| + ustr += port; |
| + } |
| + ustr += viewer.urlPrefix + "/check.https.proxy.connection"; |
| + dbg("ustr is: " + ustr); |
| + |
| + try { |
| + /* prepare for an HTTPS URL connection to host:port */ |
| + URL url = new URL(ustr); |
| + HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); |
| + |
| + if (mykey != null) { |
| + /* with oneTimeKey (mykey) we can't use the default SSL context */ |
| + if (trustsrvCerts != null) { |
| + dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); |
| + https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); |
| + } else if (trustloc_ctx != null) { |
| + dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); |
| + https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); |
| + } |
| + } |
| + |
| + https.setUseCaches(false); |
| + https.setRequestMethod("GET"); |
| + https.setRequestProperty("Pragma", "No-Cache"); |
| + https.setRequestProperty("Proxy-Connection", "Keep-Alive"); |
| + https.setDoInput(true); |
| + |
| + dbg("trying https.connect()"); |
| + https.connect(); |
| + |
| + dbg("trying https.getServerCertificates()"); |
| + trusturlCerts = https.getServerCertificates(); |
| + |
| + if (trusturlCerts == null) { |
| + dbg("set trusturlCerts to null!"); |
| + } else { |
| + dbg("set trusturlCerts to non-null"); |
| + } |
| + |
| + if (https.usingProxy()) { |
| + proxy_in_use = true; |
| + dbg("An HTTPS proxy is in use. There may be connection problems."); |
| + } |
| + |
| + dbg("trying https.getContent()"); |
| + Object output = https.getContent(); |
| + dbg("trying https.disconnect()"); |
| + https.disconnect(); |
| + if (! viewer.GET) { |
| + String header = https.getHeaderField("VNC-Server"); |
| + if (header != null && header.startsWith("x11vnc")) { |
| + dbg("detected x11vnc server (1), setting GET=1"); |
| + viewer.GET = true; |
| + } |
| + } |
| + |
| + } catch(Exception e) { |
| + dbg("HttpsURLConnection: " + e.getMessage()); |
| + } |
| + |
| + if (proxy_in_use) { |
| + dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); |
| + dbg("------------------------------------------------"); |
| + return; |
| + } else if (trusturlCerts != null && !viewer.forceProxy) { |
| + /* Allow user to require HTTP check? use forceProxy for now. */ |
| + dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); |
| + dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); |
| + dbg("------------------------------------------------"); |
| + return; |
| + } |
| + |
| + /* |
| + * XXX need to remember scenario where this extra check |
| + * gives useful info. User's Browser proxy settings? |
| + */ |
| + dbg("TRYING HTTP:"); |
| + ustr = "http://" + host + ":" + port; |
| + ustr += viewer.urlPrefix + "/index.vnc"; |
| + dbg("ustr is: " + ustr); |
| + |
| + try { |
| + /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ |
| + URL url = new URL(ustr); |
| + HttpURLConnection http = (HttpURLConnection) |
| + url.openConnection(); |
| + |
| + http.setUseCaches(false); |
| + http.setRequestMethod("GET"); |
| + http.setRequestProperty("Pragma", "No-Cache"); |
| + http.setRequestProperty("Proxy-Connection", "Keep-Alive"); |
| + http.setDoInput(true); |
| + |
| + dbg("trying http.connect()"); |
| + http.connect(); |
| + |
| + if (http.usingProxy()) { |
| + proxy_in_use = true; |
| + dbg("An HTTP proxy is in use. There may be connection problems."); |
| + } |
| + dbg("trying http.getContent()"); |
| + Object output = http.getContent(); |
| + dbg("trying http.disconnect()"); |
| + http.disconnect(); |
| + if (! viewer.GET) { |
| + String header = http.getHeaderField("VNC-Server"); |
| + if (header != null && header.startsWith("x11vnc")) { |
| + dbg("detected x11vnc server (2), setting GET=1"); |
| + viewer.GET = true; |
| + } |
| + } |
| + } catch(Exception e) { |
| + dbg("HttpURLConnection: " + e.getMessage()); |
| + } |
| + dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); |
| + dbg("------------------------------------------------"); |
| + } |
| + |
| + public Socket connectSock() throws IOException { |
| + /* |
| + * first try a https connection to detect a proxy, and |
| + * grab the VNC server cert at the same time: |
| + */ |
| + check_for_proxy_and_grab_vnc_server_cert(); |
| + |
| + boolean srv_cert = false; |
| + |
| + if (trustsrvCerts != null) { |
| + /* applet parameter suppled serverCert */ |
| + dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); |
| + factory = trustsrv_ctx.getSocketFactory(); |
| + srv_cert = true; |
| + } else if (viewer.trustAllVncCerts) { |
| + /* trust all certs (no checking) */ |
| + dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); |
| + factory = trustall_ctx.getSocketFactory(); |
| + } else if (trusturlCerts != null) { |
| + /* trust certs the Browser/JVM accepted in check_for_proxy... */ |
| + dbg("using trusturl_ctx"); |
| + factory = trusturl_ctx.getSocketFactory(); |
| + } else { |
| + /* trust the local defaults */ |
| + dbg("using trustloc_ctx"); |
| + factory = trustloc_ctx.getSocketFactory(); |
| + } |
| + |
| + socket = null; |
| + |
| + try { |
| + if (proxy_in_use && viewer.forceProxy) { |
| + throw new Exception("forcing proxy (forceProxy)"); |
| + } else if (viewer.CONNECT != null) { |
| + throw new Exception("forcing CONNECT"); |
| + } |
| + |
| + int timeout = 6; |
| + if (timeout > 0) { |
| + socket = (SSLSocket) factory.createSocket(); |
| + InetSocketAddress inetaddr = new InetSocketAddress(host, port); |
| + dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); |
| + socket.connect(inetaddr, timeout * 1000); |
| + } else { |
| + socket = (SSLSocket) factory.createSocket(host, port); |
| + } |
| + |
| + } catch (Exception esock) { |
| + dbg("socket error: " + esock.getMessage()); |
| + if (proxy_in_use || viewer.CONNECT != null) { |
| + proxy_failure = true; |
| + if (proxy_in_use) { |
| + dbg("HTTPS proxy in use. Trying to go with it."); |
| + } else { |
| + dbg("viewer.CONNECT reverse proxy in use. Trying to go with it."); |
| + } |
| + try { |
| + socket = proxy_socket(factory); |
| + } catch (Exception e) { |
| + dbg("proxy_socket error: " + e.getMessage()); |
| + } |
| + } else { |
| + /* n.b. socket is left in error state to cause ex. below. */ |
| + } |
| + } |
| + |
| + try { |
| + socket.startHandshake(); |
| + |
| + dbg("The Server Connection Verified OK on 1st try."); |
| + |
| + java.security.cert.Certificate[] currentTrustedCerts; |
| + BrowserCertsDialog bcd; |
| + |
| + SSLSession sess = socket.getSession(); |
| + currentTrustedCerts = sess.getPeerCertificates(); |
| + |
| + if (viewer.trustAllVncCerts) { |
| + dbg("viewer.trustAllVncCerts-1 keeping socket."); |
| + } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { |
| + try { |
| + socket.close(); |
| + } catch (Exception e) { |
| + dbg("socket is grumpy."); |
| + } |
| + 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) { |
| + ; |
| + } |
| + |
| + if (viewer.trustAllVncCerts) { |
| + dbg("viewer.trustAllVncCerts-2 skipping browser certs dialog"); |
| + user_wants_to_see_cert = false; |
| + } else if (viewer.serverCert != null && trustsrvCerts != null) { |
| + dbg("viewer.serverCert-1 skipping browser certs dialog"); |
| + user_wants_to_see_cert = false; |
| + } else if (viewer.trustUrlVncCert) { |
| + dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); |
| + user_wants_to_see_cert = false; |
| + } else { |
| + /* have a dialog with the user: */ |
| + bcd = new BrowserCertsDialog(serv, host + ":" + port); |
| + dbg("browser certs dialog begin."); |
| + bcd.queryUser(); |
| + dbg("browser certs dialog finished."); |
| + |
| + if (bcd.showCertDialog) { |
| + String msg = "user wants to see cert"; |
| + dbg(msg); |
| + user_wants_to_see_cert = true; |
| + if (cert_fail == null) { |
| + cert_fail = "user-view"; |
| + } |
| + throw new SSLHandshakeException(msg); |
| + } else { |
| + user_wants_to_see_cert = false; |
| + dbg("browser certs dialog: user said yes, accept it"); |
| + } |
| + } |
| + |
| + } catch (SSLHandshakeException eh) { |
| + dbg("SSLHandshakeException: could not automatically verify Server."); |
| + dbg("msg: " + eh.getMessage()); |
| + |
| + |
| + /* send a cleanup string just in case: */ |
| + String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; |
| + |
| + try { |
| + OutputStream os = socket.getOutputStream(); |
| + os.write(getoutstr.getBytes()); |
| + socket.close(); |
| + } catch (Exception e) { |
| + dbg("socket is grumpy!"); |
| + } |
| + |
| + /* reload */ |
| + |
| + socket = null; |
| + |
| + String reason = null; |
| + |
| + if (srv_cert) { |
| + /* for serverCert usage we make this a fatal error. */ |
| + throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); |
| + /* see below in TrustDialog were we describe this case to user anyway */ |
| + } |
| + |
| + /* |
| + * Reconnect, trusting any cert, so we can grab |
| + * the cert to show it to the user in a dialog |
| + * for him to manually accept. This connection |
| + * is not used for anything else. |
| + */ |
| + factory = trustall_ctx.getSocketFactory(); |
| + if (proxy_failure) { |
| + socket = proxy_socket(factory); |
| + } else { |
| + socket = (SSLSocket) factory.createSocket(host, port); |
| + } |
| + |
| + if (debug_certs) { |
| + dbg("trusturlCerts: " + trusturlCerts); |
| + dbg("trustsrvCerts: " + trustsrvCerts); |
| + } |
| + if (trusturlCerts == null && cert_fail == null) { |
| + cert_fail = "missing-certs"; |
| + } |
| + |
| + try { |
| + socket.startHandshake(); |
| + |
| + dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); |
| + |
| + /* grab the cert: */ |
| + try { |
| + SSLSession sess = socket.getSession(); |
| + trustallCerts = sess.getPeerCertificates(); |
| + } catch (Exception e) { |
| + throw new Exception("Could not get " + |
| + "Peer Certificate"); |
| + } |
| + if (debug_certs) { |
| + dbg("trustallCerts: " + trustallCerts); |
| + } |
| + |
| + if (viewer.trustAllVncCerts) { |
| + dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); |
| + } else if (! browser_cert_match()) { |
| + /* |
| + * close socket now, we will reopen after |
| + * dialog if user agrees to use the cert. |
| + */ |
| + try { |
| + OutputStream os = socket.getOutputStream(); |
| + os.write(getoutstr.getBytes()); |
| + socket.close(); |
| + } catch (Exception e) { |
| + dbg("socket is grumpy!!"); |
| + } |
| + socket = null; |
| + |
| + /* dialog with user to accept cert or not: */ |
| + |
| + TrustDialog td= new TrustDialog(host, port, |
| + trustallCerts); |
| + |
| + if (cert_fail == null) { |
| + ; |
| + } else if (cert_fail.equals("user-view")) { |
| + reason = "Reason for this Dialog:\n\n" |
| + + " You Asked to View the Certificate."; |
| + } else if (cert_fail.equals("server-cert-mismatch")) { |
| + /* this is now fatal error, see above. */ |
| + reason = "Reason for this Dialog:\n\n" |
| + + " The VNC Server's Certificate does not match the Certificate\n" |
| + + " specified in the supplied 'serverCert' Applet Parameter."; |
| + } else if (cert_fail.equals("cert-mismatch")) { |
| + reason = "Reason for this Dialog:\n\n" |
| + + " The VNC Server's Certificate does not match the Website's\n" |
| + + " HTTPS Certificate (that you previously accepted; either\n" |
| + + " manually or automatically via Certificate Authority.)"; |
| + } else if (cert_fail.equals("missing-certs")) { |
| + reason = "Reason for this Dialog:\n\n" |
| + + " Not all Certificates could be obtained to check."; |
| + } |
| + |
| + if (! td.queryUser(reason)) { |
| + String msg = "User decided against it."; |
| + dbg(msg); |
| + throw new IOException(msg); |
| + } |
| + } |
| + |
| + } catch (Exception ehand2) { |
| + dbg("** Could not TrustAll Verify Server!"); |
| + |
| + throw new IOException(ehand2.getMessage()); |
| + } |
| + |
| + /* reload again: */ |
| + |
| + if (socket != null) { |
| + try { |
| + socket.close(); |
| + } catch (Exception e) { |
| + dbg("socket is grumpy!!!"); |
| + } |
| + socket = null; |
| + } |
| + |
| + /* |
| + * Now connect a 3rd time, using the cert |
| + * retrieved during connection 2 (sadly, that |
| + * the user likely blindly agreed to...) |
| + */ |
| + |
| + factory = trustone_ctx.getSocketFactory(); |
| + if (proxy_failure) { |
| + socket = proxy_socket(factory); |
| + } else { |
| + socket = (SSLSocket) factory.createSocket(host, port); |
| + } |
| + |
| + try { |
| + socket.startHandshake(); |
| + dbg("TrustAll/TrustOne Server Connection Verified #3."); |
| + |
| + } catch (Exception ehand3) { |
| + dbg("** Could not TrustAll/TrustOne Verify Server #3."); |
| + |
| + throw new IOException(ehand3.getMessage()); |
| + } |
| + } |
| + |
| + /* we have socket (possibly null) at this point, so proceed: */ |
| + |
| + /* handle x11vnc GET=1, if applicable: */ |
| + if (socket != null && viewer.GET) { |
| + String str = "GET "; |
| + str += viewer.urlPrefix; |
| + str += "/request.https.vnc.connection"; |
| + str += " HTTP/1.0\r\n"; |
| + str += "Pragma: No-Cache\r\n"; |
| + str += "\r\n"; |
| + |
| + System.out.println("sending: " + str); |
| + OutputStream os = socket.getOutputStream(); |
| + String type = "os"; |
| + |
| + if (type == "os") { |
| + os.write(str.getBytes()); |
| + os.flush(); |
| + System.out.println("used OutputStream"); |
| + } else if (type == "bs") { |
| + BufferedOutputStream bs = new BufferedOutputStream(os); |
| + bs.write(str.getBytes()); |
| + bs.flush(); |
| + System.out.println("used BufferedOutputStream"); |
| + } else if (type == "ds") { |
| + DataOutputStream ds = new DataOutputStream(os); |
| + ds.write(str.getBytes()); |
| + ds.flush(); |
| + System.out.println("used DataOutputStream"); |
| + } |
| + if (false) { |
| + String rep = ""; |
| + DataInputStream is = new DataInputStream( |
| + new BufferedInputStream(socket.getInputStream(), 16384)); |
| + while (true) { |
| + rep += readline(is); |
| + if (rep.indexOf("\r\n\r\n") >= 0) { |
| + break; |
| + } |
| + } |
| + System.out.println("rep: " + rep); |
| + } |
| + } |
| + |
| + dbg("SSL returning socket to caller."); |
| + dbg(""); |
| + |
| + /* could be null, let caller handle that. */ |
| + return (Socket) socket; |
| + } |
| + |
| + boolean browser_cert_match() { |
| + String msg = "Browser URL accept previously accepted cert"; |
| + |
| + if (user_wants_to_see_cert) { |
| + return false; |
| + } |
| + |
| + if (viewer.serverCert != null || trustsrvCerts != null) { |
| + if (cert_fail == null) { |
| + cert_fail = "server-cert-mismatch"; |
| + } |
| + } |
| + if (trustallCerts != null && trusturlCerts != null) { |
| + if (trustallCerts.length == trusturlCerts.length) { |
| + boolean ok = true; |
| + /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ |
| + for (int i = 0; i < trusturlCerts.length; i++) { |
| + if (! trustallCerts[i].equals(trusturlCerts[i])) { |
| + dbg("BCM: cert mismatch at i=" + i); |
| + dbg("BCM: cert mismatch url" + trusturlCerts[i]); |
| + dbg("BCM: cert mismatch all" + trustallCerts[i]); |
| + ok = false; |
| + } |
| + } |
| + if (ok) { |
| + System.out.println(msg); |
| + if (cert_fail == null) { |
| + cert_fail = "did-not-fail"; |
| + } |
| + return true; |
| + } else { |
| + if (cert_fail == null) { |
| + cert_fail = "cert-mismatch"; |
| + } |
| + return false; |
| + } |
| + } |
| + } |
| + if (cert_fail == null) { |
| + cert_fail = "missing-certs"; |
| + } |
| + return false; |
| + } |
| + |
| + private void dbg(String s) { |
| + if (debug) { |
| + System.out.println(s); |
| + } |
| + } |
| + |
| + private int gint(String s) { |
| + int n = -1; |
| + try { |
| + Integer I = new Integer(s); |
| + n = I.intValue(); |
| + } catch (Exception ex) { |
| + return -1; |
| + } |
| + return n; |
| + } |
| + |
| + /* this will do the proxy CONNECT negotiation and hook us up. */ |
| + |
| + private void proxy_helper(String proxyHost, int proxyPort) { |
| + |
| + boolean proxy_auth = false; |
| + String proxy_auth_basic_realm = ""; |
| + String hp = host + ":" + port; |
| + dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); |
| + |
| + /* we loop here a few times trying for the password case */ |
| + for (int k=0; k < 2; k++) { |
| + dbg("proxy_in_use psocket: " + k); |
| + |
| + if (proxySock != null) { |
| + try { |
| + proxySock.close(); |
| + } catch (Exception e) { |
| + dbg("proxy socket is grumpy."); |
| + } |
| + } |
| + |
| + proxySock = psocket(proxyHost, proxyPort); |
| + if (proxySock == null) { |
| + dbg("1-a sadly, returning a null socket"); |
| + return; |
| + } |
| + |
| + String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" |
| + + "Host: " + hp + "\r\n"; |
| + |
| + dbg("requesting via proxy: " + req1); |
| + |
| + if (proxy_auth) { |
| + if (proxy_auth_string == null) { |
| + ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); |
| + pp.queryUser(); |
| + proxy_auth_string = pp.getAuth(); |
| + } |
| + //dbg("auth1: " + proxy_auth_string); |
| + |
| + String auth2 = Base64Coder.encodeString(proxy_auth_string); |
| + //dbg("auth2: " + auth2); |
| + |
| + req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; |
| + //dbg("req1: " + req1); |
| + |
| + dbg("added Proxy-Authorization: Basic ... to request"); |
| + } |
| + req1 += "\r\n"; |
| + |
| + try { |
| + proxy_os.write(req1.getBytes()); |
| + String reply = readline(proxy_is); |
| + |
| + dbg("proxy replied: " + reply.trim()); |
| + |
| + if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { |
| + proxy_auth = true; |
| + proxySock.close(); |
| + } else if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) { |
| + proxySock.close(); |
| + proxySock = psocket(proxyHost, proxyPort); |
| + if (proxySock == null) { |
| + dbg("2-a sadly, returning a null socket"); |
| + return; |
| + } |
| + } |
| + } catch(Exception e) { |
| + dbg("some proxy socket problem: " + e.getMessage()); |
| + } |
| + |
| + /* read the rest of the HTTP headers */ |
| + while (true) { |
| + String line = readline(proxy_is); |
| + dbg("proxy line: " + line.trim()); |
| + if (proxy_auth) { |
| + String uc = line.toLowerCase(); |
| + if (uc.indexOf("proxy-authenticate:") == 0) { |
| + if (uc.indexOf(" basic ") >= 0) { |
| + int idx = uc.indexOf(" realm"); |
| + if (idx >= 0) { |
| + proxy_auth_basic_realm = uc.substring(idx+1); |
| + } |
| + } |
| + } |
| + } |
| + if (line.equals("\r\n") || line.equals("\n")) { |
| + break; |
| + } |
| + } |
| + if (!proxy_auth || proxy_auth_basic_realm.equals("")) { |
| + /* we only try once for the non-password case: */ |
| + break; |
| + } |
| + } |
| + } |
| + |
| + public SSLSocket proxy_socket(SSLSocketFactory factory) { |
| + Properties props = null; |
| + String proxyHost = null; |
| + int proxyPort = 0; |
| + String proxyHost_nossl = null; |
| + int proxyPort_nossl = 0; |
| + String str; |
| + |
| + /* see if we can guess the proxy info from Properties: */ |
| + try { |
| + props = System.getProperties(); |
| + } catch (Exception e) { |
| + /* sandboxed applet might not be able to read it. */ |
| + dbg("props failed: " + e.getMessage()); |
| + } |
| + if (viewer.proxyHost != null) { |
| + dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); |
| + proxyHost = viewer.proxyHost; |
| + if (viewer.proxyPort != null) { |
| + proxyPort = gint(viewer.proxyPort); |
| + } else { |
| + proxyPort = 8080; |
| + } |
| + |
| + } else if (props != null) { |
| + dbg("\n---------------\nAll props:"); |
| + props.list(System.out); |
| + dbg("\n---------------\n\n"); |
| + |
| + /* scrape throught properties looking for proxy info: */ |
| + |
| + for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) { |
| + String s = (String) e.nextElement(); |
| + String v = System.getProperty(s); |
| + String s2 = s.toLowerCase(); |
| + String v2 = v.toLowerCase(); |
| + |
| + if (s2.indexOf("proxy.https.host") >= 0) { |
| + proxyHost = v2; |
| + continue; |
| + } |
| + if (s2.indexOf("proxy.https.port") >= 0) { |
| + proxyPort = gint(v2); |
| + continue; |
| + } |
| + if (s2.indexOf("proxy.http.host") >= 0) { |
| + proxyHost_nossl = v2; |
| + continue; |
| + } |
| + if (s2.indexOf("proxy.http.port") >= 0) { |
| + proxyPort_nossl = gint(v2); |
| + continue; |
| + } |
| + } |
| + |
| + for (Enumeration e = props.propertyNames(); e.hasMoreElements(); ) { |
| + String s = (String) e.nextElement(); |
| + String v = System.getProperty(s); |
| + String s2 = s.toLowerCase(); |
| + String v2 = v.toLowerCase(); |
| + |
| + if (proxyHost != null && proxyPort > 0) { |
| + break; |
| + } |
| + |
| + // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 |
| + if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { |
| + continue; |
| + } |
| + if (v2.indexOf("http") < 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) { |
| + j = p.indexOf("http"); |
| + 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) { |
| + |
| + proxyPort = gint(hp[1]); |
| + if (proxyPort < 0) { |
| + continue; |
| + } |
| + proxyHost = new String(hp[0]); |
| + break; |
| + } |
| + } |
| + } |
| + } |
| + if (proxyHost != null) { |
| + if (proxyHost_nossl != null && proxyPort_nossl > 0) { |
| + dbg("Using http proxy info instead of https."); |
| + proxyHost = proxyHost_nossl; |
| + proxyPort = proxyPort_nossl; |
| + } |
| + } |
| + |
| + if (proxy_in_use) { |
| + if (proxy_dialog_host != null && proxy_dialog_port > 0) { |
| + proxyHost = proxy_dialog_host; |
| + proxyPort = proxy_dialog_port; |
| + } |
| + 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(); |
| + proxy_dialog_host = new String(proxyHost); |
| + proxy_dialog_port = proxyPort; |
| + dbg("User said host: " + pd.getHost() + " port: " + pd.getPort()); |
| + } |
| + |
| + proxy_helper(proxyHost, proxyPort); |
| + if (proxySock == null) { |
| + return null; |
| + } |
| + } else if (viewer.CONNECT != null) { |
| + dbg("viewer.CONNECT psocket:"); |
| + proxySock = psocket(host, port); |
| + if (proxySock == null) { |
| + dbg("1-b sadly, returning a null socket"); |
| + return null; |
| + } |
| + } |
| + |
| + if (viewer.CONNECT != null) { |
| + String hp = viewer.CONNECT; |
| + String req2 = "CONNECT " + hp + " HTTP/1.1\r\n" |
| + + "Host: " + hp + "\r\n\r\n"; |
| + |
| + dbg("requesting2: " + req2); |
| + |
| + try { |
| + proxy_os.write(req2.getBytes()); |
| + String reply = readline(proxy_is); |
| + |
| + dbg("proxy replied2: " + reply.trim()); |
| + |
| + if (reply.indexOf("HTTP/1.") < 0 && reply.indexOf(" 200") < 0) { |
| + proxySock.close(); |
| + proxySock = psocket(proxyHost, proxyPort); |
| + if (proxySock == null) { |
| + dbg("2-b sadly, returning a null socket"); |
| + return null; |
| + } |
| + } |
| + } catch(Exception e) { |
| + dbg("proxy socket problem-2: " + e.getMessage()); |
| + } |
| + |
| + while (true) { |
| + String line = readline(proxy_is); |
| + dbg("proxy line2: " + line.trim()); |
| + 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[] trustallCerts = null; |
| + boolean viewing_cert = false; |
| + boolean trust_this_session = false; |
| + |
| + /* |
| + * 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, cancel, viewcert; |
| + TextArea textarea; |
| + Checkbox accept, deny; |
| + Dialog dialog; |
| + |
| + String s1 = "Accept this certificate temporarily for this session"; |
| + String s2 = "Do not accept this certificate and do not connect to" |
| + + " this VNC server"; |
| + String ln = "\n---------------------------------------------------\n\n"; |
| + |
| + TrustDialog (String h, int p, java.security.cert.Certificate[] s) { |
| + host = h; |
| + port = p; |
| + trustallCerts = s; |
| + |
| + msg = "VNC Server " + host + ":" + port + " Not Verified"; |
| + } |
| + |
| + public boolean queryUser(String reason) { |
| + |
| + /* 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"); |
| + } |
| + if (reason != null) { |
| + reason += "\n\n"; |
| + } |
| + |
| + text = "\n" |
| ++ "Unable to verify the identity of\n" |
| ++ "\n" |
| ++ " " + host + ":" + port + "\n" |
| ++ "\n" |
| ++ infostr |
| ++ "\n" |
| ++ "as a trusted VNC server.\n" |
| ++ "\n" |
| ++ reason |
| ++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" |
| ++ "is due to one of the following:\n" |
| ++ "\n" |
| ++ " - Your requesting to View the Certificate before accepting.\n" |
| ++ "\n" |
| ++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" |
| ++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" |
| ++ "\n" |
| ++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" |
| ++ " the Apache Web server has a certificate *different* from the VNC server's.\n" |
| ++ "\n" |
| ++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" |
| ++ " obtained by this applet to compare the VNC Server Certificate against.\n" |
| ++ "\n" |
| ++ " - The VNC Server's Certificate does not match the one specified in the\n" |
| ++ " supplied 'serverCert' Java Applet Parameter.\n" |
| ++ "\n" |
| ++ " - A Man-In-The-Middle attack impersonating as the VNC server that you wish\n" |
| ++ " to connect to. (Wouldn't that be exciting!!)\n" |
| ++ "\n" |
| ++ "By safely copying the VNC server's Certificate (or using a common Certificate\n" |
| ++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" |
| ++ "automatically authenticate this VNC Server.\n" |
| ++ "\n" |
| ++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" |
| ++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" |
| ++ "certificate (except for the Apache portal case above where they don't match.)\n" |
| ++ "\n" |
| ++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" |
| ++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" |
| ++ "and thereby see no dialog from this VNC Viewer applet.\n" |
| +; |
| + |
| + /* the accept / do-not-accept radio buttons: */ |
| + CheckboxGroup checkbox = new CheckboxGroup(); |
| + accept = new Checkbox(s1, true, checkbox); |
| + deny = new Checkbox(s2, false, checkbox); |
| + |
| + /* put the checkboxes in a panel: */ |
| + Panel check = new Panel(); |
| + check.setLayout(new GridLayout(2, 1)); |
| + |
| + check.add(accept); |
| + check.add(deny); |
| + |
| + /* make the 3 buttons: */ |
| + ok = new Button("OK"); |
| + cancel = new Button("Cancel"); |
| + viewcert = new Button("View Certificate"); |
| + |
| + ok.addActionListener(this); |
| + cancel.addActionListener(this); |
| + viewcert.addActionListener(this); |
| + |
| + /* put the buttons in their own panel: */ |
| + Panel buttonrow = new Panel(); |
| + buttonrow.setLayout(new FlowLayout(FlowLayout.LEFT)); |
| + buttonrow.add(viewcert); |
| + buttonrow.add(ok); |
| + buttonrow.add(cancel); |
| + |
| + /* label at the top: */ |
| + Label label = new Label(msg, Label.CENTER); |
| + label.setFont(new Font("Helvetica", Font.BOLD, 16)); |
| + |
| + /* textarea in the middle */ |
| + textarea = new TextArea(text, 38, 64, |
| + TextArea.SCROLLBARS_VERTICAL_ONLY); |
| + textarea.setEditable(false); |
| + |
| + /* put the two panels in their own panel at bottom: */ |
| + Panel bot = new Panel(); |
| + bot.setLayout(new GridLayout(2, 1)); |
| + bot.add(check); |
| + bot.add(buttonrow); |
| + |
| + /* now arrange things inside the dialog: */ |
| + dialog.setLayout(new BorderLayout()); |
| + |
| + dialog.add("North", label); |
| + dialog.add("South", bot); |
| + dialog.add("Center", textarea); |
| + |
| + dialog.pack(); |
| + dialog.resize(dialog.preferredSize()); |
| + |
| + dialog.show(); /* block here til OK or Cancel pressed. */ |
| + |
| + return trust_this_session; |
| + } |
| + |
| + public synchronized void actionPerformed(ActionEvent evt) { |
| + |
| + if (evt.getSource() == viewcert) { |
| + /* View Certificate button clicked */ |
| + if (viewing_cert) { |
| + /* show the original info text: */ |
| + textarea.setText(text); |
| + viewcert.setLabel("View Certificate"); |
| + viewing_cert = false; |
| + } else { |
| + int i; |
| + /* show all (likely just one) certs: */ |
| + textarea.setText(""); |
| + for (i=0; i < trustallCerts.length; i++) { |
| + int j = i + 1; |
| + textarea.append("Certificate[" + |
| + j + "]\n\n"); |
| + textarea.append( |
| + trustallCerts[i].toString()); |
| + textarea.append(ln); |
| + } |
| + viewcert.setLabel("View Info"); |
| + viewing_cert = true; |
| + |
| + textarea.setCaretPosition(0); |
| + } |
| + |
| + } else if (evt.getSource() == ok) { |
| + /* OK button clicked */ |
| + if (accept.getState()) { |
| + trust_this_session = true; |
| + } else { |
| + trust_this_session = false; |
| + } |
| + //dialog.dispose(); |
| + dialog.hide(); |
| + |
| + } else if (evt.getSource() == cancel) { |
| + /* Cancel button clicked */ |
| + trust_this_session = false; |
| + |
| + //dialog.dispose(); |
| + dialog.hide(); |
| + } |
| + } |
| + |
| + String get_certinfo() { |
| + String all = ""; |
| + String fields[] = {"CN", "OU", "O", "L", "C"}; |
| + int i; |
| + if (trustallCerts.length < 1) { |
| + all = ""; |
| + return all; |
| + } |
| + String cert = trustallCerts[0].toString(); |
| + |
| + /* |
| + * 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 = cert.indexOf(mat, 0); |
| + if (f > 0) { |
| + t1 = cert.indexOf(", ", f); |
| + t2 = cert.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 = cert.substring(f, t); |
| + all = all + " " + sub + "\n"; |
| + } |
| + } |
| + } |
| + 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(); |
| + dialog.hide(); |
| + } |
| + } |
| +} |
| + |
| +class ProxyPasswdDialog implements ActionListener { |
| + String guessedHost = null; |
| + String guessedPort = null; |
| + String guessedUser = null; |
| + String guessedPasswd = null; |
| + String realm = 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 entry1; |
| + TextField entry2; |
| + String reply1 = ""; |
| + String reply2 = ""; |
| + |
| + ProxyPasswdDialog (String h, int p, String realm) { |
| + guessedHost = h; |
| + try { |
| + guessedPort = Integer.toString(p); |
| + } catch (Exception e) { |
| + guessedPort = "8080"; |
| + } |
| + this.realm = realm; |
| + } |
| + |
| + public void queryUser() { |
| + |
| + /* create and display the dialog for unverified cert. */ |
| + |
| + Frame frame = new Frame("Proxy Requires Username and Password"); |
| + |
| + dialog = new Dialog(frame, true); |
| + |
| + //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); |
| + TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); |
| + entry1 = new TextField(30); |
| + entry2 = new TextField(30); |
| + entry2.setEchoChar('*'); |
| + ok = new Button("OK"); |
| + ok.addActionListener(this); |
| + |
| + dialog.setLayout(new BorderLayout()); |
| + dialog.add("North", label); |
| + dialog.add("Center", entry1); |
| + dialog.add("South", entry2); |
| + dialog.add("East", ok); |
| + dialog.pack(); |
| + dialog.resize(dialog.preferredSize()); |
| + |
| + dialog.show(); /* block here til OK or Cancel pressed. */ |
| + return; |
| + } |
| + |
| + public String getAuth() { |
| + return reply1 + ":" + reply2; |
| + } |
| + |
| + public synchronized void actionPerformed(ActionEvent evt) { |
| + System.out.println(evt.getActionCommand()); |
| + if (evt.getSource() == ok) { |
| + reply1 = entry1.getText(); |
| + reply2 = entry2.getText(); |
| + //dialog.dispose(); |
| + dialog.hide(); |
| + } |
| + } |
| +} |
| + |
| +class ClientCertDialog implements ActionListener { |
| + |
| + Button ok; |
| + Dialog dialog; |
| + TextField entry; |
| + String reply = ""; |
| + |
| + ClientCertDialog() { |
| + ; |
| + } |
| + |
| + public String queryUser() { |
| + |
| + /* create and display the dialog for unverified cert. */ |
| + |
| + Frame frame = new Frame("Enter SSL Client Cert+Key String"); |
| + |
| + dialog = new Dialog(frame, true); |
| + |
| + |
| + Label label = new Label("Please Enter the SSL Client Cert+Key String 308204c0...,...522d2d0a", Label.CENTER); |
| + entry = new TextField(30); |
| + ok = new Button("OK"); |
| + ok.addActionListener(this); |
| + |
| + 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 reply; |
| + } |
| + |
| + public synchronized void actionPerformed(ActionEvent evt) { |
| + System.out.println(evt.getActionCommand()); |
| + if (evt.getSource() == ok) { |
| + reply = entry.getText(); |
| + //dialog.dispose(); |
| + dialog.hide(); |
| + } |
| + } |
| +} |
| + |
| +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 = ""; |
| +m += "\n"; |
| +m += "This VNC Viewer applet does not have its own keystore to track\n"; |
| +m += "SSL certificates, and so cannot authenticate the certificate\n"; |
| +m += "of the VNC Server:\n"; |
| +m += "\n"; |
| +m += " " + hostport + "\n\n " + vncServer + "\n"; |
| +m += "\n"; |
| +m += "on its own.\n"; |
| +m += "\n"; |
| +m += "However, it has noticed that your Web Browser and/or Java VM Plugin\n"; |
| +m += "has previously accepted the same certificate. You may have set\n"; |
| +m += "this up permanently or just for this session, or the server\n"; |
| +m += "certificate was signed by a CA cert that your Web Browser or\n"; |
| +m += "Java VM Plugin has.\n"; |
| +m += "\n"; |
| +m += "If the VNC Server connection times out while you are reading this\n"; |
| +m += "dialog, then restart the connection and try again.\n"; |
| +m += "\n"; |
| +m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; |
| +m += "\n"; |
| + |
| + TextArea textarea = new TextArea(m, 22, 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. */ |
| + System.out.println("done show()"); |
| + return; |
| + } |
| + |
| + public synchronized void actionPerformed(ActionEvent evt) { |
| + System.out.println(evt.getActionCommand()); |
| + if (evt.getSource() == yes) { |
| + showCertDialog = false; |
| + //dialog.dispose(); |
| + dialog.hide(); |
| + } else if (evt.getSource() == no) { |
| + showCertDialog = true; |
| + //dialog.dispose(); |
| + dialog.hide(); |
| + } |
| + System.out.println("done actionPerformed()"); |
| + } |
| +} |
| + |
| +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 ""; |
| + } |
| + } |
| +} |
| + |
| +class Base64Coder { |
| + |
| + // Mapping table from 6-bit nibbles to Base64 characters. |
| + private static char[] map1 = new char[64]; |
| + static { |
| + int i=0; |
| + for (char c='A'; c<='Z'; c++) map1[i++] = c; |
| + for (char c='a'; c<='z'; c++) map1[i++] = c; |
| + for (char c='0'; c<='9'; c++) map1[i++] = c; |
| + map1[i++] = '+'; map1[i++] = '/'; } |
| + |
| + // Mapping table from Base64 characters to 6-bit nibbles. |
| + private static byte[] map2 = new byte[128]; |
| + static { |
| + for (int i=0; i<map2.length; i++) map2[i] = -1; |
| + for (int i=0; i<64; i++) map2[map1[i]] = (byte)i; } |
| + |
| + /** |
| + * Encodes a string into Base64 format. |
| + * No blanks or line breaks are inserted. |
| + * @param s a String to be encoded. |
| + * @return A String with the Base64 encoded data. |
| + */ |
| + public static String encodeString (String s) { |
| + return new String(encode(s.getBytes())); } |
| + |
| + /** |
| + * Encodes a byte array into Base64 format. |
| + * No blanks or line breaks are inserted. |
| + * @param in an array containing the data bytes to be encoded. |
| + * @return A character array with the Base64 encoded data. |
| + */ |
| + public static char[] encode (byte[] in) { |
| + return encode(in,in.length); } |
| + |
| + /** |
| + * Encodes a byte array into Base64 format. |
| + * No blanks or line breaks are inserted. |
| + * @param in an array containing the data bytes to be encoded. |
| + * @param iLen number of bytes to process in <code>in</code>. |
| + * @return A character array with the Base64 encoded data. |
| + */ |
| + public static char[] encode (byte[] in, int iLen) { |
| + int oDataLen = (iLen*4+2)/3; // output length without padding |
| + int oLen = ((iLen+2)/3)*4; // output length including padding |
| + char[] out = new char[oLen]; |
| + int ip = 0; |
| + int op = 0; |
| + while (ip < iLen) { |
| + int i0 = in[ip++] & 0xff; |
| + int i1 = ip < iLen ? in[ip++] & 0xff : 0; |
| + int i2 = ip < iLen ? in[ip++] & 0xff : 0; |
| + int o0 = i0 >>> 2; |
| + int o1 = ((i0 & 3) << 4) | (i1 >>> 4); |
| + int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); |
| + int o3 = i2 & 0x3F; |
| + out[op++] = map1[o0]; |
| + out[op++] = map1[o1]; |
| + out[op] = op < oDataLen ? map1[o2] : '='; op++; |
| + out[op] = op < oDataLen ? map1[o3] : '='; op++; } |
| + return out; } |
| + |
| + /** |
| + * Decodes a string from Base64 format. |
| + * @param s a Base64 String to be decoded. |
| + * @return A String containing the decoded data. |
| + * @throws IllegalArgumentException if the input is not valid Base64 encoded data. |
| + */ |
| + public static String decodeString (String s) { |
| + return new String(decode(s)); } |
| + |
| + /** |
| + * Decodes a byte array from Base64 format. |
| + * @param s a Base64 String to be decoded. |
| + * @return An array containing the decoded data bytes. |
| + * @throws IllegalArgumentException if the input is not valid Base64 encoded data. |
| + */ |
| + public static byte[] decode (String s) { |
| + return decode(s.toCharArray()); } |
| + |
| + /** |
| + * Decodes a byte array from Base64 format. |
| + * No blanks or line breaks are allowed within the Base64 encoded data. |
| + * @param in a character array containing the Base64 encoded data. |
| + * @return An array containing the decoded data bytes. |
| + * @throws IllegalArgumentException if the input is not valid Base64 encoded data. |
| + */ |
| + public static byte[] decode (char[] in) { |
| + int iLen = in.length; |
| + if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); |
| + while (iLen > 0 && in[iLen-1] == '=') iLen--; |
| + int oLen = (iLen*3) / 4; |
| + byte[] out = new byte[oLen]; |
| + int ip = 0; |
| + int op = 0; |
| + while (ip < iLen) { |
| + int i0 = in[ip++]; |
| + int i1 = in[ip++]; |
| + int i2 = ip < iLen ? in[ip++] : 'A'; |
| + int i3 = ip < iLen ? in[ip++] : 'A'; |
| + if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) |
| + throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); |
| + int b0 = map2[i0]; |
| + int b1 = map2[i1]; |
| + int b2 = map2[i2]; |
| + int b3 = map2[i3]; |
| + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) |
| + throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); |
| + int o0 = ( b0 <<2) | (b1>>>4); |
| + int o1 = ((b1 & 0xf)<<4) | (b2>>>2); |
| + int o2 = ((b2 & 3)<<6) | b3; |
| + out[op++] = (byte)o0; |
| + if (op<oLen) out[op++] = (byte)o1; |
| + if (op<oLen) out[op++] = (byte)o2; } |
| + return out; } |
| + |
| + // Dummy constructor. |
| + private Base64Coder() {} |
| + |
| +} |
| diff -Naur vnc_javasrc.orig/VncCanvas.java vnc_javasrc/VncCanvas.java |
| --- vnc_javasrc.orig/VncCanvas.java 2004-10-10 02:15:54.000000000 -0400 |
| +++ vnc_javasrc/VncCanvas.java 2010-11-30 21:01:15.000000000 -0500 |
| @@ -28,13 +28,14 @@ |
| import java.lang.*; |
| import java.util.zip.*; |
| |
| +import java.util.Collections; |
| |
| // |
| // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. |
| // |
| |
| class VncCanvas extends Canvas |
| - implements KeyListener, MouseListener, MouseMotionListener { |
| + implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener { |
| |
| VncViewer viewer; |
| RfbProto rfb; |
| @@ -81,6 +82,20 @@ |
| cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); |
| cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); |
| |
| + // kludge to not show any Java cursor in the canvas since we are |
| + // showing the soft cursor (should be a user setting...) |
| + Cursor dot = Toolkit.getDefaultToolkit().createCustomCursor( |
| + Toolkit.getDefaultToolkit().createImage(new byte[4]), new Point(0,0), |
| + "dot"); |
| + this.setCursor(dot); |
| + |
| + // while we are at it... get rid of the keyboard traversals that |
| + // make it so we can't type a Tab character: |
| + this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, |
| + Collections.EMPTY_SET); |
| + this.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, |
| + Collections.EMPTY_SET); |
| + |
| colors = new Color[256]; |
| for (int i = 0; i < 256; i++) |
| colors[i] = new Color(cm8.getRGB(i)); |
| @@ -169,6 +184,7 @@ |
| inputEnabled = true; |
| addMouseListener(this); |
| addMouseMotionListener(this); |
| + addMouseWheelListener(this); |
| if (viewer.showControls) { |
| viewer.buttonPanel.enableRemoteAccessControls(true); |
| } |
| @@ -177,6 +193,7 @@ |
| inputEnabled = false; |
| removeMouseListener(this); |
| removeMouseMotionListener(this); |
| + removeMouseWheelListener(this); |
| if (viewer.showControls) { |
| viewer.buttonPanel.enableRemoteAccessControls(false); |
| } |
| @@ -1190,6 +1207,9 @@ |
| public void mouseDragged(MouseEvent evt) { |
| processLocalMouseEvent(evt, true); |
| } |
| + public void mouseWheelMoved(MouseWheelEvent evt) { |
| + processLocalMouseWheelEvent(evt); |
| + } |
| |
| public void processLocalKeyEvent(KeyEvent evt) { |
| if (viewer.rfb != null && rfb.inNormalProtocol) { |
| @@ -1221,6 +1241,19 @@ |
| evt.consume(); |
| } |
| |
| + public void processLocalMouseWheelEvent(MouseWheelEvent evt) { |
| + if (viewer.rfb != null && rfb.inNormalProtocol) { |
| + synchronized(rfb) { |
| + try { |
| + rfb.writeWheelEvent(evt); |
| + } catch (Exception e) { |
| + e.printStackTrace(); |
| + } |
| + rfb.notify(); |
| + } |
| + } |
| + } |
| + |
| public void processLocalMouseEvent(MouseEvent evt, boolean moved) { |
| if (viewer.rfb != null && rfb.inNormalProtocol) { |
| if (moved) { |
| @@ -1387,9 +1420,9 @@ |
| result = cm8.getRGB(pixBuf[i]); |
| } else { |
| result = 0xFF000000 | |
| - (pixBuf[i * 4 + 1] & 0xFF) << 16 | |
| - (pixBuf[i * 4 + 2] & 0xFF) << 8 | |
| - (pixBuf[i * 4 + 3] & 0xFF); |
| + (pixBuf[i * 4 + 2] & 0xFF) << 16 | |
| + (pixBuf[i * 4 + 1] & 0xFF) << 8 | |
| + (pixBuf[i * 4 + 0] & 0xFF); |
| } |
| } else { |
| result = 0; // Transparent pixel |
| @@ -1403,9 +1436,9 @@ |
| result = cm8.getRGB(pixBuf[i]); |
| } else { |
| result = 0xFF000000 | |
| - (pixBuf[i * 4 + 1] & 0xFF) << 16 | |
| - (pixBuf[i * 4 + 2] & 0xFF) << 8 | |
| - (pixBuf[i * 4 + 3] & 0xFF); |
| + (pixBuf[i * 4 + 2] & 0xFF) << 16 | |
| + (pixBuf[i * 4 + 1] & 0xFF) << 8 | |
| + (pixBuf[i * 4 + 0] & 0xFF); |
| } |
| } else { |
| result = 0; // Transparent pixel |
| diff -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 2010-03-27 17:57:04.000000000 -0400 |
| @@ -29,6 +29,7 @@ |
| import java.awt.event.*; |
| import java.io.*; |
| import java.net.*; |
| +import java.util.*; |
| |
| public class VncViewer extends java.applet.Applet |
| implements java.lang.Runnable, WindowListener { |
| @@ -80,7 +81,7 @@ |
| // Variables read from parameter values. |
| String socketFactory; |
| String host; |
| - int port; |
| + int port, vncserverport; |
| boolean showControls; |
| boolean offerRelogin; |
| boolean showOfflineDesktop; |
| @@ -88,6 +89,24 @@ |
| int deferCursorUpdates; |
| int deferUpdateRequests; |
| |
| + boolean disableSSL; |
| + boolean GET; |
| + String CONNECT; |
| + String urlPrefix; |
| + String httpsPort; |
| + String oneTimeKey; |
| + String serverCert; |
| + String proxyHost; |
| + String proxyPort; |
| + boolean forceProxy; |
| + boolean ignoreProxy; |
| + boolean trustAllVncCerts; |
| + boolean trustUrlVncCert; |
| + boolean debugCerts; |
| + boolean debugKeyboard; |
| + boolean mapF5_to_atsign; |
| + boolean forbid_Ctrl_Alt; |
| + |
| // Reference to this applet for inter-applet communication. |
| public static java.applet.Applet refApplet; |
| |
| @@ -282,10 +301,24 @@ |
| validate(); |
| } |
| |
| - while (!tryAuthenticate()) { |
| - authenticator.retry(); |
| - authenticatorUnixLogin.retry(); |
| - } |
| + if (false) { |
| + /* a bug on retries: 'Error: bad position: 8' sun.awt.X11.XTextFieldPeer.setCaretPosition(XTextFieldPeer.java:215) */ |
| + while (!tryAuthenticate()) { |
| + authenticator.retry(); |
| + authenticatorUnixLogin.retry(); |
| + } |
| + } else { |
| + /* just try once and not forever... */ |
| + if (!tryAuthenticate()) { |
| + showConnectionStatus("Authentication Failed."); |
| + showMessage("Authentication Failed."); |
| + if (!offerRelogin) { |
| + fatalError("auth failed."); |
| + } |
| + } else { |
| + //showConnectionStatus("Authentication OK."); |
| + } |
| + } |
| } |
| |
| |
| @@ -428,7 +461,10 @@ |
| gbc.ipadx = 100; |
| gbc.ipady = 50; |
| gridbag.setConstraints(authPanel, gbc); |
| + try { |
| vncContainer.add(authPanel); |
| + } catch (Exception e) { |
| + } |
| |
| if (inSeparateFrame) { |
| vncFrame.pack(); |
| @@ -590,9 +626,28 @@ |
| fatalError("HOST parameter not specified"); |
| } |
| } |
| + Date d = new Date(); |
| + System.out.println("-\nSSL VNC Java Applet starting. " + d); |
| |
| - String str = readParameter("PORT", true); |
| - port = Integer.parseInt(str); |
| + port = 0; |
| + String str = readParameter("PORT", false); |
| + if (str != null) { |
| + port = Integer.parseInt(str); |
| + } |
| + // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall). |
| + vncserverport = 0; |
| + str = readParameter("VNCSERVERPORT", false); |
| + if (str != null) { |
| + vncserverport = Integer.parseInt(str); |
| + } |
| + if (port == 0 && vncserverport == 0) { |
| + fatalError("Neither PORT nor VNCSERVERPORT parameters specified"); |
| + } |
| + if (port == 0) { |
| + // Nevertheless, fall back to vncserverport if we have to. |
| + System.out.println("using vncserverport: '" + vncserverport + "' for PORT."); |
| + port = vncserverport; |
| + } |
| |
| if (inAnApplet) { |
| str = readParameter("Open New Window", false); |
| @@ -626,6 +681,121 @@ |
| |
| // SocketFactory. |
| socketFactory = readParameter("SocketFactory", false); |
| + |
| + // SSL |
| + disableSSL = false; |
| + str = readParameter("DisableSSL", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) |
| + disableSSL = true; |
| + |
| + httpsPort = readParameter("httpsPort", false); |
| + |
| + // Extra GET, CONNECT string: |
| + CONNECT = readParameter("CONNECT", false); |
| + if (CONNECT != null) { |
| + CONNECT = CONNECT.replaceAll(" ", ":"); |
| + } |
| + |
| + GET = false; |
| + str = readParameter("GET", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + GET = true; |
| + } |
| + if (str != null && str.equalsIgnoreCase("1")) { |
| + GET = true; |
| + } |
| + |
| + urlPrefix = readParameter("urlPrefix", false); |
| + if (urlPrefix != null) { |
| + urlPrefix = urlPrefix.replaceAll("%2F", "/"); |
| + urlPrefix = urlPrefix.replaceAll("%2f", "/"); |
| + urlPrefix = urlPrefix.replaceAll("_2F_", "/"); |
| + if (urlPrefix.indexOf("/") != 0) { |
| + urlPrefix = "/" + urlPrefix; |
| + } |
| + } else { |
| + urlPrefix = ""; |
| + } |
| + System.out.println("urlPrefix: '" + urlPrefix + "'"); |
| + |
| + oneTimeKey = readParameter("oneTimeKey", false); |
| + if (oneTimeKey != null) { |
| + System.out.println("oneTimeKey is set."); |
| + } |
| + |
| + serverCert = readParameter("serverCert", false); |
| + if (serverCert != null) { |
| + System.out.println("serverCert is set."); |
| + } |
| + |
| + forceProxy = false; |
| + proxyHost = null; |
| + proxyPort = null; |
| + str = readParameter("forceProxy", false); |
| + if (str != null) { |
| + if (str.equalsIgnoreCase("Yes")) { |
| + forceProxy = true; |
| + } else if (str.equalsIgnoreCase("No")) { |
| + forceProxy = false; |
| + } else { |
| + forceProxy = true; |
| + String[] pieces = str.split(" "); |
| + proxyHost = new String(pieces[0]); |
| + if (pieces.length >= 2) { |
| + proxyPort = new String(pieces[1]); |
| + } else { |
| + proxyPort = new String("8080"); |
| + } |
| + } |
| + } |
| + str = readParameter("proxyHost", false); |
| + if (str != null) { |
| + proxyHost = new String(str); |
| + } |
| + str = readParameter("proxyPort", false); |
| + if (str != null) { |
| + proxyPort = new String(str); |
| + } |
| + if (proxyHost != null && proxyPort == null) { |
| + proxyPort = new String("8080"); |
| + } |
| + |
| + ignoreProxy = false; |
| + str = readParameter("ignoreProxy", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + ignoreProxy = true; |
| + } |
| + |
| + trustAllVncCerts = false; |
| + str = readParameter("trustAllVncCerts", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + trustAllVncCerts = true; |
| + } |
| + trustUrlVncCert = false; |
| + str = readParameter("trustUrlVncCert", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + trustUrlVncCert = true; |
| + } |
| + debugCerts = false; |
| + str = readParameter("debugCerts", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + debugCerts = true; |
| + } |
| + debugKeyboard = false; |
| + str = readParameter("debugKeyboard", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + debugKeyboard = true; |
| + } |
| + mapF5_to_atsign = false; |
| + str = readParameter("mapF5_to_atsign", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + mapF5_to_atsign = true; |
| + } |
| + forbid_Ctrl_Alt = false; |
| + str = readParameter("forbid_Ctrl_Alt", false); |
| + if (str != null && str.equalsIgnoreCase("Yes")) { |
| + forbid_Ctrl_Alt = true; |
| + } |
| } |
| |
| public String readParameter(String name, boolean required) { |