Isolate our patches to the VNC client from the upstream TightVNC
[invirt/packages/invirt-vnc-client.git] / debian / patches / invirt-ssl-proxy.patch
diff --git a/debian/patches/invirt-ssl-proxy.patch b/debian/patches/invirt-ssl-proxy.patch
new file mode 100644 (file)
index 0000000..f777283
--- /dev/null
@@ -0,0 +1,685 @@
+Index: invirt-vnc-client/InvirtTrustManager.java
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ invirt-vnc-client/InvirtTrustManager.java  2008-10-31 06:09:10.000000000 -0400
+@@ -0,0 +1,122 @@
++/*
++ * Copyright 2006 Perry Nguyen <pfnguyen@hanhuy.com>
++ * Licensed under the Apache License, Version 2.0 (the "License");
++ * you may not use this file except in compliance with the License.
++ * You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++import java.io.IOException;
++import java.io.InputStream;
++import java.security.KeyStore;
++import java.security.KeyStoreException;
++import java.security.NoSuchAlgorithmException;
++import java.security.cert.CertificateException;
++import java.security.cert.X509Certificate;
++import java.util.Enumeration;
++import java.util.logging.Level;
++import java.util.logging.Logger;
++
++import javax.net.ssl.TrustManager;
++import javax.net.ssl.TrustManagerFactory;
++import javax.net.ssl.X509TrustManager;
++
++public class InvirtTrustManager implements X509TrustManager {
++    private X509TrustManager trustManager;
++    private final static char[] KEY_STORE_PASSWORD =
++        { 'f', 'o', 'o', 'b', 'a', 'r' };
++    private final static String KEY_STORE_RESOURCE =
++        "trust.store";
++
++    private KeyStore loadKeyStore() throws Exception {
++        InputStream in = getClass().getClassLoader().getResourceAsStream(
++                KEY_STORE_RESOURCE);
++        KeyStore ks = null;
++        try {
++            if (in == null) {
++                //log.severe("Unable to open KeyStore");
++                throw new NullPointerException();
++            }
++            ks = KeyStore.getInstance(KeyStore.getDefaultType());
++            ks.load(in, KEY_STORE_PASSWORD);
++          /*if (log.isLoggable(Level.FINEST)) {
++                for (Enumeration<String> aliases = ks.aliases();
++                aliases.hasMoreElements();) {
++                    String alias = aliases.nextElement();
++                    log.finest("ALIAS: " + alias);
++                }
++              }*/
++        } catch (NoSuchAlgorithmException e) {
++            throwError(e);
++        } catch (CertificateException e) {
++            throwError(e);
++        } catch (IOException e) {
++            throwError(e);
++        } catch (KeyStoreException e) {
++            throwError(e);
++        } finally {
++            try {
++                if (in != null)
++                    in.close();
++            }
++            catch (IOException e) { } // ignore
++        }
++        return ks;
++    }
++    private void createTrustManager() {
++      try {
++          try {
++              KeyStore keystore = loadKeyStore();
++              TrustManagerFactory factory = TrustManagerFactory.getInstance(
++                                                                            TrustManagerFactory.getDefaultAlgorithm());
++              factory.init(keystore);
++              TrustManager[] trustManagers = factory.getTrustManagers();
++              if (trustManagers.length == 0)
++                  throw new IllegalStateException("No trust manager found");
++              setTrustManager((X509TrustManager) trustManagers[0]);
++          } catch (NoSuchAlgorithmException e) {
++              throwError(e);
++          } catch (KeyStoreException e) {
++              throwError(e);
++          }
++      } catch (Exception e) {
++          e.printStackTrace();
++      }
++    }
++    private void throwError(Exception e) throws Exception {
++        //HttpClientError error = new HttpClientError(e.getMessage());
++        //error.initCause(e);
++        throw e;
++    }
++    public X509TrustManager getTrustManager() {
++        if (trustManager == null)
++            createTrustManager();
++        return trustManager;
++    }
++
++    public void setTrustManager(X509TrustManager trustManager) {
++        this.trustManager = trustManager;
++    }
++
++    public void checkClientTrusted(X509Certificate[] chain, String authType)
++            throws CertificateException {
++        getTrustManager().checkClientTrusted(chain, authType);
++    }
++
++    public void checkServerTrusted(X509Certificate[] chain, String authType)
++            throws CertificateException {
++        getTrustManager().checkServerTrusted(chain, authType);
++
++    }
++
++    public X509Certificate[] getAcceptedIssuers() {
++        return getTrustManager().getAcceptedIssuers();
++    }
++
++}
+\ No newline at end of file
+Index: invirt-vnc-client/Makefile
+===================================================================
+--- invirt-vnc-client.orig/Makefile    2008-10-31 06:09:10.000000000 -0400
++++ invirt-vnc-client/Makefile 2008-10-31 06:09:10.000000000 -0400
+@@ -17,8 +17,10 @@
+         DesCipher.class CapabilityInfo.class CapsContainer.class \
+         RecordingFrame.class SessionRecorder.class \
+         SocketFactory.class HTTPConnectSocketFactory.class \
++        VNCProxyConnectSocketFactory.class VNCProxyConnectSocket.class \
+         HTTPConnectSocket.class ReloginPanel.class \
+-        InStream.class MemInStream.class ZlibInStream.class
++        InStream.class MemInStream.class ZlibInStream.class \
++        VNCProxyConnectSocketWrapper.class SocketWrapper.class SocketWrapper\$$WrappingSocketImpl.class InvirtTrustManager.class
+ SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \
+         VncCanvas2.java \
+@@ -26,8 +28,10 @@
+         DesCipher.java CapabilityInfo.java CapsContainer.java \
+         RecordingFrame.java SessionRecorder.java \
+         SocketFactory.java HTTPConnectSocketFactory.java \
++        VNCProxyConnectSocketFactory.java VNCProxyConnectSocket.java \
+         HTTPConnectSocket.java ReloginPanel.java \
+-        InStream.java MemInStream.java ZlibInStream.java
++        InStream.java MemInStream.java ZlibInStream.java \
++        VNCProxyConnectSocketWrapper.java SocketWrapper.java InvirtTrustManager.java
+ all: $(CLASSES) $(ARCHIVE)
+Index: invirt-vnc-client/RfbProto.java
+===================================================================
+--- invirt-vnc-client.orig/RfbProto.java       2007-04-26 22:36:00.000000000 -0400
++++ invirt-vnc-client/RfbProto.java    2008-10-31 06:09:10.000000000 -0400
+@@ -208,11 +208,13 @@
+     port = p;
+     if (viewer.socketFactory == null) {
++      System.out.println("Null socketFactory");
+       sock = new Socket(host, port);
+     } else {
+       try {
+       Class factoryClass = Class.forName(viewer.socketFactory);
+       SocketFactory factory = (SocketFactory)factoryClass.newInstance();
++      System.out.println("Using socketFactory " + factory);
+       if (viewer.inAnApplet)
+         sock = factory.createSocket(host, port, viewer);
+       else
+@@ -236,7 +238,7 @@
+     try {
+       sock.close();
+       closed = true;
+-      System.out.println("RFB socket closed");
++      System.out.println("RFB socket closed " + sock);
+       if (rec != null) {
+       rec.close();
+       rec = null;
+Index: invirt-vnc-client/SocketWrapper.java
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ invirt-vnc-client/SocketWrapper.java       2008-10-31 06:09:10.000000000 -0400
+@@ -0,0 +1,262 @@
++/*
++ * Written by Dawid Kurzyniec and released to the public domain, as explained
++ * at http://creativecommons.org/licenses/publicdomain
++ */
++
++//package edu.emory.mathcs.util.net;
++
++import java.io.*;
++import java.net.*;
++import java.nio.channels.*;
++
++/**
++ * Wrapper for sockets which enables to add functionality in subclasses
++ * on top of existing, connected sockets. It is useful when direct subclassing
++ * of delegate socket class is not possible, e.g. if the delegate socket is
++ * created by a library. Possible usage example is socket factory chaining.
++ * This class delegates all socket-related requests to the wrapped delegate,
++ * as of JDK 1.4.
++ *
++ * @author Dawid Kurzyniec
++ * @version 1.4
++ */
++public abstract class SocketWrapper extends Socket {
++
++    /**
++     * the wrapped delegate socket.
++     */
++    protected final Socket delegate;
++
++    /**
++     * Creates new socket wrapper for a given socket. The delegate
++     * must be connected and bound and it must not be closed.
++     * @param delegate the delegate socket to wrap
++     * @throws SocketException if the delegate socket is closed, not bound,
++     *                         or not connected
++     */
++    protected SocketWrapper(Socket delegate) throws SocketException {
++        super(new WrappingSocketImpl(delegate));
++        this.delegate = delegate;
++      System.out.println("Creating SocketWrapper $Rev$");
++    }
++
++    public SocketChannel getChannel() {
++        return delegate.getChannel();
++    }
++
++    /**
++     * Returns true, indicating that the socket is bound.
++     *
++     * @return true
++     */
++    public boolean isBound() {
++        return true;
++    }
++
++    public boolean isClosed() {
++        return super.isClosed() || delegate.isClosed();
++    }
++
++    /**
++     * Returns true, indicating that the socket is connected.
++     *
++     * @return true
++     */
++    public boolean isConnected() {
++        return true;
++    }
++
++    public boolean isInputShutdown() {
++        return super.isInputShutdown() || delegate.isInputShutdown();
++    }
++
++    public boolean isOutputShutdown() {
++        return super.isInputShutdown() || delegate.isOutputShutdown();
++    }
++
++    private static class WrappingSocketImpl extends SocketImpl {
++        private final Socket delegate;
++        WrappingSocketImpl(Socket delegate) throws SocketException {
++            if (delegate == null) {
++                throw new NullPointerException();
++            }
++            if (delegate.isClosed()) {
++                throw new SocketException("Delegate server socket is closed");
++            }
++            if (!(delegate.isBound())) {
++                throw new SocketException("Delegate server socket is not bound");
++            }
++            if (!(delegate.isConnected())) {
++                throw new SocketException("Delegate server socket is not connected");
++            }
++            this.delegate = delegate;
++        }
++
++        protected void create(boolean stream) {}
++
++        protected void connect(String host, int port) {
++            // delegate is always connected, thus this method is never called
++            throw new UnsupportedOperationException();
++        }
++
++        protected void connect(InetAddress address, int port) {
++            // delegate is always connected, thus this method is never called
++            throw new UnsupportedOperationException();
++        }
++
++        protected void connect(SocketAddress address, int timeout) {
++            // delegate is always connected, thus this method is never called
++            throw new UnsupportedOperationException();
++        }
++
++        protected void bind(InetAddress host, int port) {
++            // delegate is always bound, thus this method is never called
++            throw new UnsupportedOperationException();
++        }
++
++        protected void listen(int backlog) {
++            // this wrapper is never used by a ServerSocket
++            throw new UnsupportedOperationException();
++        }
++
++        protected void accept(SocketImpl s) {
++            // this wrapper is never used by a ServerSocket
++            throw new UnsupportedOperationException();
++        }
++
++        protected InputStream getInputStream() throws IOException {
++            return delegate.getInputStream();
++        }
++
++        protected OutputStream getOutputStream() throws IOException {
++            return delegate.getOutputStream();
++        }
++
++        protected int available() throws IOException {
++            return getInputStream().available();
++        }
++
++        protected void close() throws IOException {
++          System.out.println("Calling delegate.close");
++            delegate.close();
++        }
++
++        protected void shutdownInput() throws IOException {
++            delegate.shutdownInput();
++        }
++
++        protected void shutdownOutput() throws IOException {
++            delegate.shutdownOutput();
++        }
++
++        protected FileDescriptor getFileDescriptor() {
++            // this wrapper is never used by a ServerSocket
++            throw new UnsupportedOperationException();
++        }
++
++        protected InetAddress getInetAddress() {
++            return delegate.getInetAddress();
++        }
++
++        protected int getPort() {
++            return delegate.getPort();
++        }
++
++        protected boolean supportsUrgentData() {
++            return false; // must be overridden in sub-class
++        }
++
++        protected void sendUrgentData (int data) throws IOException {
++            delegate.sendUrgentData(data);
++        }
++
++        protected int getLocalPort() {
++            return delegate.getLocalPort();
++        }
++
++        public Object getOption(int optID) throws SocketException {
++            switch (optID) {
++                case SocketOptions.IP_TOS:
++                    return new Integer(delegate.getTrafficClass());
++                case SocketOptions.SO_BINDADDR:
++                    return delegate.getLocalAddress();
++                case SocketOptions.SO_KEEPALIVE:
++                    return Boolean.valueOf(delegate.getKeepAlive());
++                case SocketOptions.SO_LINGER:
++                    return new Integer(delegate.getSoLinger());
++                case SocketOptions.SO_OOBINLINE:
++                    return Boolean.valueOf(delegate.getOOBInline());
++                case SocketOptions.SO_RCVBUF:
++                    return new Integer(delegate.getReceiveBufferSize());
++                case SocketOptions.SO_REUSEADDR:
++                    return Boolean.valueOf(delegate.getReuseAddress());
++                case SocketOptions.SO_SNDBUF:
++                    return new Integer(delegate.getSendBufferSize());
++                case SocketOptions.SO_TIMEOUT:
++                    return new Integer(delegate.getSoTimeout());
++                case SocketOptions.TCP_NODELAY:
++                    return Boolean.valueOf(delegate.getTcpNoDelay());
++                case SocketOptions.SO_BROADCAST:
++                default:
++                    throw new IllegalArgumentException("Unsupported option type");
++            }
++        }
++
++        public void setOption(int optID, Object value) throws SocketException {
++            switch (optID) {
++                case SocketOptions.SO_BINDADDR:
++                    throw new IllegalArgumentException("Socket is bound");
++                case SocketOptions.SO_KEEPALIVE:
++                    delegate.setKeepAlive(((Boolean)value).booleanValue());
++                    break;
++                case SocketOptions.SO_LINGER:
++                    if (value instanceof Boolean) {
++                        delegate.setSoLinger(((Boolean)value).booleanValue(), 0);
++                    }
++                    else {
++                        delegate.setSoLinger(true, ((Integer)value).intValue());
++                    }
++                    break;
++                case SocketOptions.SO_OOBINLINE:
++                    delegate.setOOBInline(((Boolean)value).booleanValue());
++                    break;
++                case SocketOptions.SO_RCVBUF:
++                    delegate.setReceiveBufferSize(((Integer)value).intValue());
++                    break;
++                case SocketOptions.SO_REUSEADDR:
++                    delegate.setReuseAddress(((Boolean)value).booleanValue());
++                    break;
++                case SocketOptions.SO_SNDBUF:
++                    delegate.setSendBufferSize(((Integer)value).intValue());
++                    break;
++                case SocketOptions.SO_TIMEOUT:
++                    delegate.setSoTimeout(((Integer)value).intValue());
++                    break;
++                case SocketOptions.TCP_NODELAY:
++                    delegate.setTcpNoDelay(((Boolean)value).booleanValue());
++                    break;
++                case SocketOptions.SO_BROADCAST:
++                default:
++                    throw new IllegalArgumentException("Unsupported option type");
++            }
++        }
++    }
++
++    public void close() throws IOException {
++      System.out.println("Calling SocketWrapper.delegate.close");
++      delegate.close();
++    }
++
++    public boolean equals(Object obj) {
++        if (!(obj instanceof SocketWrapper)) return false;
++        SocketWrapper that = (SocketWrapper)obj;
++        return this.delegate.equals(that.delegate);
++    }
++
++    public int hashCode() {
++        return delegate.hashCode() ^ 0x01010101;
++    }
++    public String toString() {
++      return "<SocketWrapper " + super.toString() + "(delegating to " + delegate.toString() +  ")" + ">";
++    }
++}
+\ No newline at end of file
+Index: invirt-vnc-client/VNCProxyConnectSocket.java
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ invirt-vnc-client/VNCProxyConnectSocket.java       2008-10-31 06:09:10.000000000 -0400
+@@ -0,0 +1,61 @@
++//
++//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  All Rights Reserved.
++//  Copyright 2007 MIT Student Information Processing Board
++//
++//  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; either 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.
++//
++
++//
++// VNCProxySocket.java together with VNCProxySocketFactory.java
++// implement an alternate way to connect to VNC servers via one or two
++// VNCProxy proxies supporting the VNCProxy VNCCONNECT method.
++//
++
++import java.net.*;
++import java.io.*;
++
++class VNCProxyConnectSocket extends Socket {
++
++  public VNCProxyConnectSocket(String host, int port,
++                               String vmname, String authtoken)
++    throws IOException {
++
++    // Connect to the specified HTTP proxy
++    super(host, port);
++
++    // Send the CONNECT request
++    getOutputStream().write(("CONNECTVNC " + vmname +
++                             " VNCProxy/1.0\r\nAuth-token: " + authtoken +
++                             "\r\n\r\n").getBytes());
++
++    // Read the first line of the response
++    DataInputStream is = new DataInputStream(getInputStream());
++    String str = is.readLine();
++
++    // Check the HTTP error code -- it should be "200" on success
++    if (!str.startsWith("VNCProxy/1.0 200 ")) {
++      if (str.startsWith("VNCProxy/1.0 "))
++        str = str.substring(13);
++      throw new IOException("Proxy reports \"" + str + "\"");
++    }
++
++    // Success -- skip remaining HTTP headers
++    do {
++      str = is.readLine();
++    } while (str.length() != 0);
++  }
++}
++
+Index: invirt-vnc-client/VNCProxyConnectSocketFactory.java
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ invirt-vnc-client/VNCProxyConnectSocketFactory.java        2008-10-31 06:09:10.000000000 -0400
+@@ -0,0 +1,98 @@
++//
++//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  All Rights Reserved.
++//  Copyright 2007 MIT Student Information Processing Board
++//
++//  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; either 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.
++//
++
++//
++// VNCProxyConnectSocketFactory.java together with VNCProxyConnectSocket.java
++// implement an alternate way to connect to VNC servers via one or two
++// VNCProxy proxies supporting the VNCProxy CONNECT method.
++//
++
++import java.applet.*;
++import java.net.*;
++import javax.net.ssl.*;
++import java.io.*;
++
++class VNCProxyConnectSocketFactory implements SocketFactory {
++
++    SSLSocketFactory factory;
++    
++    public VNCProxyConnectSocketFactory() {
++      try {
++          SSLContext c = SSLContext.getInstance("SSL");
++          c.init(null,
++                 new TrustManager[] { new InvirtTrustManager() },
++                 null);
++          factory =
++              (SSLSocketFactory)c.getSocketFactory();
++      } catch (Exception e) {
++          e.printStackTrace();
++      }
++    }
++
++  public Socket createSocket(String host, int port, Applet applet)
++    throws IOException {
++
++    return createSocket(host, port,
++                      applet.getParameter("VMNAME"),
++                      applet.getParameter("AUTHTOKEN"));
++  }
++
++  public Socket createSocket(String host, int port, String[] args)
++    throws IOException {
++
++    return createSocket(host, port,
++                      readArg(args, "VMNAME"),
++                      readArg(args, "AUTHTOKEN"));
++  }
++
++  public Socket createSocket(String host, int port,
++                           String vmname, String authtoken)
++    throws IOException {
++
++    if (vmname == null || authtoken == null) {
++      System.out.println("Incomplete parameter list for VNCProxyConnectSocket");
++      return new Socket(host, port);
++    }
++
++    System.out.println("VNCProxy CONNECT via proxy " + host +
++                     " port " + port + " to vm " + vmname);
++    SSLSocket ssls = (SSLSocket)factory.createSocket(host, port);
++    ssls.startHandshake();
++    VNCProxyConnectSocketWrapper s =
++      new VNCProxyConnectSocketWrapper(ssls, vmname, authtoken);
++
++    return (Socket)s;
++  }
++
++  private String readArg(String[] args, String name) {
++
++    for (int i = 0; i < args.length; i += 2) {
++      if (args[i].equalsIgnoreCase(name)) {
++      try {
++        return args[i+1];
++      } catch (Exception e) {
++        return null;
++      }
++      }
++    }
++    return null;
++  }
++}
++
+Index: invirt-vnc-client/VNCProxyConnectSocketWrapper.java
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ invirt-vnc-client/VNCProxyConnectSocketWrapper.java        2008-10-31 06:09:10.000000000 -0400
+@@ -0,0 +1,60 @@
++//
++//  Copyright (C) 2002 Constantin Kaplinsky, Inc.  All Rights Reserved.
++//  Copyright 2007 MIT Student Information Processing Board
++//
++//  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; either 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.
++//
++
++//
++// VNCProxySocket.java together with VNCProxySocketFactory.java
++// implement an alternate way to connect to VNC servers via one or two
++// VNCProxy proxies supporting the VNCProxy VNCCONNECT method.
++//
++
++import java.net.*;
++import java.io.*;
++
++class VNCProxyConnectSocketWrapper extends SocketWrapper {
++
++  public VNCProxyConnectSocketWrapper(Socket sock,
++                               String vmname, String authtoken)
++    throws IOException {
++
++    super(sock);
++
++    // Send the CONNECT request
++    getOutputStream().write(("CONNECTVNC " + vmname +
++                             " VNCProxy/1.0\r\nAuth-token: " + authtoken +
++                             "\r\n\r\n").getBytes());
++
++    // Read the first line of the response
++    DataInputStream is = new DataInputStream(getInputStream());
++    String str = is.readLine();
++
++    // Check the HTTP error code -- it should be "200" on success
++    if (!str.startsWith("VNCProxy/1.0 200 ")) {
++      if (str.startsWith("VNCProxy/1.0 "))
++        str = str.substring(13);
++      throw new IOException("Proxy reports \"" + str + "\"");
++    }
++
++    // Success -- skip remaining HTTP headers
++    do {
++      str = is.readLine();
++    } while (str.length() != 0);
++  }
++}
++