From a692253b687514ce0eb72d9ac8829b2b2965da27 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 8 Dec 2015 12:50:22 -0800 Subject: [PATCH 1/3] GUAC-1166: Add ldap-encryption-method property. Select appropriate default port depending on encryption method. --- .../auth/ldap/ConfigurationService.java | 25 ++++++- .../guacamole/auth/ldap/EncryptionMethod.java | 69 +++++++++++++++++++ .../auth/ldap/EncryptionMethodProperty.java | 63 +++++++++++++++++ .../auth/ldap/LDAPGuacamoleProperties.java | 14 +++- 4 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethod.java create mode 100644 extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethodProperty.java diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/ConfigurationService.java b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/ConfigurationService.java index 886e405e4..ae4a90a76 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/ConfigurationService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/ConfigurationService.java @@ -61,8 +61,9 @@ public class ConfigurationService { /** * Returns the port of the LDAP server configured with - * guacamole.properties. By default, this will be 389 - the standard LDAP - * port. + * guacamole.properties. The default value depends on which encryption + * method is being used. For unencrypted LDAP and STARTTLS, this will be + * 389. For LDAPS (LDAP over SSL) this will be 636. * * @return * The port of the LDAP server, as configured with @@ -74,7 +75,7 @@ public class ConfigurationService { public int getServerPort() throws GuacamoleException { return environment.getProperty( LDAPGuacamoleProperties.LDAP_PORT, - 389 + getEncryptionMethod().DEFAULT_PORT ); } @@ -172,4 +173,22 @@ public class ConfigurationService { ); } + /** + * Returns the encryption method that should be used when connecting to the + * LDAP server. By default, no encryption is used. + * + * @return + * The encryption method that should be used when connecting to the + * LDAP server. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public EncryptionMethod getEncryptionMethod() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_ENCRYPTION_METHOD, + EncryptionMethod.NONE + ); + } + } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethod.java b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethod.java new file mode 100644 index 000000000..94c112e13 --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethod.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.auth.ldap; + +/** + * All possible encryption methods which may be used when connecting to an LDAP + * server. + * + * @author Michael Jumper + */ +public enum EncryptionMethod { + + /** + * No encryption will be used. All data will be sent to the LDAP server in + * plaintext. Unencrypted LDAP connections use port 389 by default. + */ + NONE(389), + + /** + * The connection to the LDAP server will be encrypted with SSL. LDAP over + * SSL (LDAPS) will use port 636 by default. + */ + SSL(636), + + /** + * The connection to the LDAP server will be encrypted using STARTTLS. TLS + * connections are negotiated over the standard LDAP port of 389 - the same + * port used for unencrypted traffic. + */ + STARTTLS(389); + + /** + * The default port of this specific encryption method. As with most + * protocols, the default port for LDAP varies by whether SSL is used. + */ + public final int DEFAULT_PORT; + + /** + * Initializes this encryption method such that it is associated with the + * given default port. + * + * @param defaultPort + * The default port to associate with this encryption method. + */ + private EncryptionMethod(int defaultPort) { + this.DEFAULT_PORT = defaultPort; + } + +} diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethodProperty.java b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethodProperty.java new file mode 100644 index 000000000..bd41cc29c --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/EncryptionMethodProperty.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.glyptodon.guacamole.auth.ldap; + +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleServerException; +import org.glyptodon.guacamole.properties.GuacamoleProperty; + +/** + * A GuacamoleProperty whose value is an EncryptionMethod. The string values + * "none", "ssl", and "starttls" are each parsed to their corresponding values + * within the EncryptionMethod enum. All other string values result in parse + * errors. + * + * @author Michael Jumper + */ +public abstract class EncryptionMethodProperty implements GuacamoleProperty { + + @Override + public EncryptionMethod parseValue(String value) throws GuacamoleException { + + // If no value provided, return null. + if (value == null) + return null; + + // Plaintext (no encryption) + if (value.equals("none")) + return EncryptionMethod.NONE; + + // SSL + if (value.equals("ssl")) + return EncryptionMethod.SSL; + + // STARTTLS + if (value.equals("starttls")) + return EncryptionMethod.STARTTLS; + + // The provided value is not legal + throw new GuacamoleServerException("Encryption method must be one of \"none\", \"ssl\", or \"starttls\"."); + + } + +} diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPGuacamoleProperties.java b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPGuacamoleProperties.java index efd69e6fd..283584e28 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPGuacamoleProperties.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPGuacamoleProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Glyptodon LLC + * Copyright (C) 2015 Glyptodon LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -124,4 +124,16 @@ public class LDAPGuacamoleProperties { }; + /** + * The encryption method to use when connecting to the LDAP server, if any. + * The chosen method will also dictate the default port if not already + * explicitly specified via LDAP_PORT. + */ + public static final EncryptionMethodProperty LDAP_ENCRYPTION_METHOD = new EncryptionMethodProperty() { + + @Override + public String getName() { return "ldap-encryption-method"; } + + }; + } From 8724ef7de782d58341fab026a0401841786c826e Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 8 Dec 2015 13:13:27 -0800 Subject: [PATCH 2/3] GUAC-1166: Use appropriate socket factory depending on selected encryption method. --- .../auth/ldap/LDAPConnectionService.java | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java index cc0140a86..18b06b749 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java @@ -25,8 +25,11 @@ package org.glyptodon.guacamole.auth.ldap; import com.google.inject.Inject; import com.novell.ldap.LDAPConnection; import com.novell.ldap.LDAPException; +import com.novell.ldap.LDAPJSSESecureSocketFactory; +import com.novell.ldap.LDAPJSSEStartTLSFactory; import java.io.UnsupportedEncodingException; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleUnsupportedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,6 +51,48 @@ public class LDAPConnectionService { @Inject private ConfigurationService confService; + /** + * Creates a new instance of LDAPConnection, configured as required to use + * whichever encryption method is requested within guacamole.properties. + * + * @return + * A new LDAPConnection instance which has already been configured to + * use the encryption method requested within guacamole.properties. + * + * @throws GuacamoleException + * If an error occurs while parsing guacamole.properties, or if the + * requested encryption method is actually not implemented (a bug). + */ + private LDAPConnection createLDAPConnection() throws GuacamoleException { + + // Map encryption method to proper connection and socket factory + EncryptionMethod encryptionMethod = confService.getEncryptionMethod(); + switch (encryptionMethod) { + + // Unencrypted LDAP connection + case NONE: + logger.debug("Connection to LDAP server without encryption."); + return new LDAPConnection(); + + // LDAP over SSL (LDAPS) + case SSL: + logger.debug("Connecting to LDAP server using SSL/TLS."); + return new LDAPConnection(new LDAPJSSESecureSocketFactory()); + + // LDAP + STARTTLS + case STARTTLS: + logger.debug("Connecting to LDAP server using STARTTLS."); + return new LDAPConnection(new LDAPJSSEStartTLSFactory()); + + // The encryption method, though known, is not actually + // implemented. If encountered, this would be a bug. + default: + throw new GuacamoleUnsupportedException("Unimplemented encryption method: " + encryptionMethod); + + } + + } + /** * Binds to the LDAP server using the provided user DN and password. * @@ -68,11 +113,11 @@ public class LDAPConnectionService { public LDAPConnection bindAs(String userDN, String password) throws GuacamoleException { - LDAPConnection ldapConnection; + // Obtain appropriately-configured LDAPConnection instance + LDAPConnection ldapConnection = createLDAPConnection(); // Connect to LDAP server try { - ldapConnection = new LDAPConnection(); ldapConnection.connect( confService.getServerHostname(), confService.getServerPort() From d51a719f2fdbe9a21fb02f2aa3ab4f36087a9c46 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 8 Dec 2015 16:08:27 -0800 Subject: [PATCH 3/3] GUAC-1406: Explicitly call startTLS() if STARTTLS is enabled. --- .../guacamole/auth/ldap/LDAPConnectionService.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java index 18b06b749..27f3edd3d 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/glyptodon/guacamole/auth/ldap/LDAPConnectionService.java @@ -116,12 +116,18 @@ public class LDAPConnectionService { // Obtain appropriately-configured LDAPConnection instance LDAPConnection ldapConnection = createLDAPConnection(); - // Connect to LDAP server try { + + // Connect to LDAP server ldapConnection.connect( confService.getServerHostname(), confService.getServerPort() ); + + // Explicitly start TLS if requested + if (confService.getEncryptionMethod() == EncryptionMethod.STARTTLS) + ldapConnection.startTLS(); + } catch (LDAPException e) { logger.error("Unable to connect to LDAP server: {}", e.getMessage());