Merge pull request #297 from glyptodon/ldap-ssl-starttls

GUAC-1166: Add support for SSL/TLS and STARTTLS to LDAP.
This commit is contained in:
James Muehlner
2015-12-08 20:24:56 -08:00
5 changed files with 221 additions and 7 deletions

View File

@@ -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
);
}
}

View File

@@ -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;
}
}

View File

@@ -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<EncryptionMethod> {
@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\".");
}
}

View File

@@ -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,15 +113,21 @@ 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();
// 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());

View File

@@ -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"; }
};
}