diff -x VncCanvas.java -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile --- vnc_javasrc.orig/Makefile 2004-03-04 08:34:25.000000000 -0500 +++ vnc_javasrc/Makefile 2006-03-26 17:29:25.000000000 -0500 @@ -15,25 +15,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) -target 1.4 -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 -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 @@ -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 { 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 @@ +/* + * SSLSocketToMe.java: add SSL encryption to Java VNC Viewer. + * + * Copyright (c) 2006 Karl J. Runge + * 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. + * + * 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.security.cert.*; +import java.util.Vector; + +import java.awt.*; +import java.awt.event.*; + +public class SSLSocketToMe { + + /* basic member data: */ + String host; + int port; + VncViewer viewer; + boolean debug = true; + + /* sockets */ + SSLSocket socket = null; + SSLSocketFactory factory; + + /* trust contexts */ + SSLContext trustall_ctx; + SSLContext trustone_ctx; + TrustManager[] trustAllCerts; + TrustManager[] trustOneCert; + + /* cert(s) we retrieve from VNC server */ + java.security.cert.Certificate[] serverCerts = null; + + SSLSocketToMe(String h, int p, VncViewer v) throws Exception { + host = h; + port = p; + viewer = v; + + /* we will first try default factory for certification: */ + + factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); + + dbg("SSL startup: " + host + " " + port); + + /* create trust managers used if initial handshake fails: */ + + trustAllCerts = new TrustManager[] { + /* + * this one accepts everything. + */ + 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 */ + } + } + }; + + trustOneCert = new TrustManager[] { + /* + * this one accepts only the retrieved server cert. + */ + 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 (serverCerts == null) { + throw new CertificateException( + "No Server Certs array."); + } + if (serverCerts.length < 1) { + throw new CertificateException( + "No Server Certs."); + } + if (! serverCerts[0].equals(certs[0])) { + throw new CertificateException( + "Server Cert Changed."); + } + dbg("serverCerts[0] matches certs[0]"); + } + } + }; + + /* + * They are used: + * + * 1) to retrieve the server cert in case of failure to + * display it to the user. + * 2) to subsequently connect to the server if user agrees. + */ + + /* trust all certs: */ + try { + trustall_ctx = SSLContext.getInstance("SSL"); + trustall_ctx.init(null, trustAllCerts, new + java.security.SecureRandom()); + + } catch (Exception e) { + String msg = "SSL trustall_ctx FAILED."; + dbg(msg); + throw new Exception(msg); + } + + /* trust the one cert from server: */ + try { + trustone_ctx = SSLContext.getInstance("SSL"); + trustone_ctx.init(null, trustOneCert, new + java.security.SecureRandom()); + + } catch (Exception e) { + String msg = "SSL trustone_ctx FAILED."; + dbg(msg); + throw new Exception(msg); + } + } + + public Socket connectSock() throws IOException { + + /* now connect to host:port */ + socket = (SSLSocket) factory.createSocket(host, port); + + 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."); + + } catch (Exception ehand) { + dbg("Could not automatically verify Server."); + + socket.close(); + + /* + * Reconnect, trusting any cert, so we can grab + * the cert to show it to the user. The connection + * is not used for anything else. + */ + factory = trustall_ctx.getSocketFactory(); + socket = (SSLSocket) factory.createSocket(host, port); + + try { + socket.startHandshake(); + dbg("TrustAll Server Connection Verified."); + + /* grab the cert: */ + try { + SSLSession sess = socket.getSession(); + serverCerts = 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(); + + /* dialog with user to accept cert or not: */ + + TrustDialog td= new TrustDialog(host, port, + serverCerts); + + 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."); + + throw new IOException(ehand2.getMessage()); + } + + /* + * Now connect a 3rd time, using the cert + * retrieved during connection 2 (that the user + * likely blindly agreed to). + */ + + factory = trustone_ctx.getSocketFactory(); + socket = (SSLSocket) factory.createSocket(host, port); + + try { + socket.startHandshake(); + dbg("TrustAll Server Connection Verified #3."); + + } catch (Exception ehand3) { + dbg("** Could not TrustAll Verify Server #3."); + + throw new IOException(ehand3.getMessage()); + } + } + + dbg("SSL returning socket to caller."); + return (Socket) socket; + } + + private void dbg(String s) { + if (debug) { + System.out.println(s); + } + } +} + +class TrustDialog implements ActionListener { + String msg, host, text; + int port; + java.security.cert.Certificate[] serverCerts = 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[] certs) { + host = h; + port = p; + serverCerts = certs; + + 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); + + text = "\n" ++ "Unable to verify the identity of\n" ++ "\n" ++ " " + host + ":" + port + "\n" ++ "\n" ++ get_certinfo() ++ "\n" ++ "as a trusted VNC server.\n" ++ "\n" ++ "This may be due to:\n" ++ "\n" ++ " - The VNC server using a Self-Signed Certificate.\n" ++ "\n" ++ " - The VNC server using a Certificate Authority not recognized by your\n" ++ " Java applet runtime.\n" ++ "\n" ++ " - A Man-In-The-Middle attack impersonating as the VNC server you wish\n" ++ " to connect to.\n" ++ "\n" ++ "By copying the VNC server's Certificate (or using a common Certificate\n" ++ "Authority certificate) you can configure your Java applet runtime to\n" ++ "automatically authenticate the Server.\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, 28, 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 < serverCerts.length; i++) { + int j = i + 1; + textarea.append("Certificate[" + + j + "]\n\n"); + textarea.append( + serverCerts[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(); + + } else if (evt.getSource() == cancel) { + /* Cancel button clicked */ + trust_this_session = false; + + dialog.dispose(); + } + } + + String get_certinfo() { + String all = ""; + String fields[] = {"CN", "OU", "O", "L", "C"}; + int i; + if (serverCerts.length < 1) { + all = ""; + return all; + } + String cert = serverCerts[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; + } +} 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 @@ -87,6 +87,7 @@ int deferScreenUpdates; int deferCursorUpdates; int deferUpdateRequests; + boolean disableSSL; // Reference to this applet for inter-applet communication. public static java.applet.Applet refApplet; @@ -626,6 +627,12 @@ // SocketFactory. socketFactory = readParameter("SocketFactory", false); + + // SSL + disableSSL = false; + str = readParameter("DisableSSL", false); + if (str != null && str.equalsIgnoreCase("Yes")) + disableSSL = true; } public String readParameter(String name, boolean required) {