diff --git a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java index 71e26944a..8ae351fdb 100644 --- a/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java +++ b/extensions/guacamole-auth-mysql/src/main/java/net/sourceforge/guacamole/net/auth/mysql/service/ConnectionService.java @@ -53,6 +53,7 @@ import net.sourceforge.guacamole.GuacamoleClientException; import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.net.GuacamoleSocket; import net.sourceforge.guacamole.net.InetGuacamoleSocket; +import net.sourceforge.guacamole.net.SSLGuacamoleSocket; import net.sourceforge.guacamole.net.auth.mysql.ActiveConnectionSet; import net.sourceforge.guacamole.net.auth.mysql.MySQLConnection; import net.sourceforge.guacamole.net.auth.mysql.MySQLConnectionRecord; @@ -348,10 +349,17 @@ public class ConnectionService { int port = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_PORT); // Get socket - GuacamoleSocket socket = new ConfiguredGuacamoleSocket( - new InetGuacamoleSocket(host, port), - connection.getConfiguration(), info - ); + GuacamoleSocket socket; + if (GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_SSL, false)) + socket = new ConfiguredGuacamoleSocket( + new SSLGuacamoleSocket(host, port), + connection.getConfiguration(), info + ); + else + socket = new ConfiguredGuacamoleSocket( + new InetGuacamoleSocket(host, port), + connection.getConfiguration(), info + ); // Mark this connection as active int historyID = activeConnectionSet.openConnection(connection.getConnectionID(), userID); diff --git a/guacamole-common/src/main/java/net/sourceforge/guacamole/net/SSLGuacamoleSocket.java b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/SSLGuacamoleSocket.java new file mode 100644 index 000000000..80e07d2cd --- /dev/null +++ b/guacamole-common/src/main/java/net/sourceforge/guacamole/net/SSLGuacamoleSocket.java @@ -0,0 +1,164 @@ + +package net.sourceforge.guacamole.net; + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is guacamole-common. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; +import net.sourceforge.guacamole.GuacamoleException; +import net.sourceforge.guacamole.GuacamoleServerException; +import net.sourceforge.guacamole.io.GuacamoleReader; +import net.sourceforge.guacamole.io.GuacamoleWriter; +import net.sourceforge.guacamole.io.ReaderGuacamoleReader; +import net.sourceforge.guacamole.io.WriterGuacamoleWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides abstract socket-like access to a Guacamole connection over SSL to + * a given hostname and port. + * + * @author Michael Jumper + */ +public class SSLGuacamoleSocket implements GuacamoleSocket { + + /** + * Logger for this class. + */ + private Logger logger = LoggerFactory.getLogger(SSLGuacamoleSocket.class); + + /** + * The GuacamoleReader this socket should read from. + */ + private GuacamoleReader reader; + + /** + * The GuacamoleWriter this socket should write to. + */ + private GuacamoleWriter writer; + + /** + * The number of milliseconds to wait for data on the TCP socket before + * timing out. + */ + private static final int SOCKET_TIMEOUT = 15000; + + /** + * The TCP socket that the GuacamoleReader and GuacamoleWriter exposed + * by this class should affect. + */ + private Socket sock; + + /** + * Creates a new InetGuacamoleSocket which reads and writes instructions + * to the Guacamole instruction stream of the Guacamole proxy server + * running at the given hostname and port. + * + * @param hostname The hostname of the Guacamole proxy server to connect to. + * @param port The port of the Guacamole proxy server to connect to. + * @throws GuacamoleException If an error occurs while connecting to the + * Guacamole proxy server. + */ + public SSLGuacamoleSocket(String hostname, int port) throws GuacamoleException { + + // Get factory for SSL sockets + SocketFactory socket_factory = SSLSocketFactory.getDefault(); + + try { + + logger.debug("Connecting to guacd at {}:{} via SSL/TLS.", + hostname, port); + + // Get address + SocketAddress address = new InetSocketAddress( + InetAddress.getByName(hostname), + port + ); + + // Connect with timeout + sock = socket_factory.createSocket(); + sock.connect(address, SOCKET_TIMEOUT); + + // Set read timeout + sock.setSoTimeout(SOCKET_TIMEOUT); + + // On successful connect, retrieve I/O streams + reader = new ReaderGuacamoleReader(new InputStreamReader(sock.getInputStream(), "UTF-8")); + writer = new WriterGuacamoleWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8")); + + } + catch (IOException e) { + throw new GuacamoleServerException(e); + } + + } + + @Override + public void close() throws GuacamoleException { + try { + logger.debug("Closing socket to guacd."); + sock.close(); + } + catch (IOException e) { + throw new GuacamoleServerException(e); + } + } + + @Override + public GuacamoleReader getReader() { + return reader; + } + + @Override + public GuacamoleWriter getWriter() { + return writer; + } + + @Override + public boolean isOpen() { + return !sock.isClosed(); + } + + +} diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java index c033dec41..25f4fb49d 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/net/auth/simple/SimpleConnection.java @@ -42,6 +42,7 @@ import java.util.List; import net.sourceforge.guacamole.GuacamoleException; import net.sourceforge.guacamole.net.GuacamoleSocket; import net.sourceforge.guacamole.net.InetGuacamoleSocket; +import net.sourceforge.guacamole.net.SSLGuacamoleSocket; import net.sourceforge.guacamole.net.auth.AbstractConnection; import net.sourceforge.guacamole.net.auth.ConnectionRecord; import net.sourceforge.guacamole.properties.GuacamoleProperties; @@ -96,10 +97,17 @@ public class SimpleConnection extends AbstractConnection { String hostname = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_HOSTNAME); int port = GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_PORT); + // If guacd requires SSL, use it + if (GuacamoleProperties.getProperty(GuacamoleProperties.GUACD_SSL, false)) + return new ConfiguredGuacamoleSocket( + new SSLGuacamoleSocket(hostname, port), + config, info + ); + // Return connected socket return new ConfiguredGuacamoleSocket( - new InetGuacamoleSocket(hostname, port), - config, info + new InetGuacamoleSocket(hostname, port), + config, info ); } diff --git a/guacamole-ext/src/main/java/net/sourceforge/guacamole/properties/GuacamoleProperties.java b/guacamole-ext/src/main/java/net/sourceforge/guacamole/properties/GuacamoleProperties.java index f152a461d..ac593a05d 100644 --- a/guacamole-ext/src/main/java/net/sourceforge/guacamole/properties/GuacamoleProperties.java +++ b/guacamole-ext/src/main/java/net/sourceforge/guacamole/properties/GuacamoleProperties.java @@ -84,6 +84,16 @@ public class GuacamoleProperties { }; + /** + * Whether guacd requires SSL/TLS on connections. + */ + public static final BooleanGuacamoleProperty GUACD_SSL = new BooleanGuacamoleProperty() { + + @Override + public String getName() { return "guacd-ssl"; } + + }; + /** * All properties read from guacamole.properties when this class was first * used.