From c2c3428cf0be150650f6a4c9706222c508b9e788 Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Mon, 3 Jan 2022 15:31:47 -0500 Subject: [PATCH] GUACAMOLE-1488: Add support for configuring LDAP SSL protocol. --- .../auth/ldap/ConnectedLDAPConfiguration.java | 6 + .../auth/ldap/LDAPConnectionService.java | 123 ++++++++++++------ .../ldap/conf/DefaultLDAPConfiguration.java | 5 + .../conf/EnvironmentLDAPConfiguration.java | 8 ++ .../ldap/conf/JacksonLDAPConfiguration.java | 14 ++ .../auth/ldap/conf/LDAPConfiguration.java | 14 ++ .../ldap/conf/LDAPGuacamoleProperties.java | 8 ++ .../auth/ldap/conf/LDAPSSLProtocol.java | 87 +++++++++++++ 8 files changed, 228 insertions(+), 37 deletions(-) create mode 100644 extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPSSLProtocol.java diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java index 5617bb785..493cafcb7 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/ConnectedLDAPConfiguration.java @@ -27,6 +27,7 @@ import org.apache.directory.ldap.client.api.LdapNetworkConnection; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.auth.ldap.conf.EncryptionMethod; import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; +import org.apache.guacamole.auth.ldap.conf.LDAPSSLProtocol; import org.apache.guacamole.auth.ldap.conf.MemberAttributeType; /** @@ -161,6 +162,11 @@ public class ConnectedLDAPConfiguration implements LDAPConfiguration, AutoClosea public EncryptionMethod getEncryptionMethod() throws GuacamoleException { return config.getEncryptionMethod(); } + + @Override + public LDAPSSLProtocol getSslProtocol() throws GuacamoleException { + return config.getSslProtocol(); + } @Override public int getMaxResults() throws GuacamoleException { diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java index b8f5d30ad..06f83b5eb 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/LDAPConnectionService.java @@ -35,6 +35,7 @@ import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.auth.ldap.conf.EncryptionMethod; import org.apache.guacamole.auth.ldap.conf.LDAPConfiguration; +import org.apache.guacamole.auth.ldap.conf.LDAPSSLProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,7 +52,8 @@ public class LDAPConnectionService { /** * Creates a new instance of LdapNetworkConnection, configured as required * to use the given encryption method to communicate with the LDAP server - * at the given hostname and port. The returned LdapNetworkConnection is + * at the given hostname and port, with the specified encryption method, + * SSL protocol version, and timeout. The returned LdapNetworkConnection is * configured for use but is not yet connected nor bound to the LDAP * server. It will not be bound until a bind operation is explicitly * requested, and will not be connected until it is used in an LDAP @@ -66,6 +68,85 @@ public class LDAPConnectionService { * @param encryptionMethod * The encryption method that should be used to communicate with the * LDAP server. + * + * @param sslProtocol + * The SSL protocol version to use to make a secure LDAP configuration, + * if SSL or STARTTLS is used. + * + * @param timeout + * The maximum number of milliseconds to wait for a response from the + * LDAP server. + * + * @return + * A new instance of LdapNetworkConnection which uses the given + * encryption method to communicate with the LDAP server at the given + * hostname and port. + * + * @throws GuacamoleException + * If the requested encryption method is actually not implemented (a + * bug). + */ + private LdapNetworkConnection createLDAPConnection(String host, int port, + EncryptionMethod encryptionMethod, LDAPSSLProtocol sslProtocol, + int timeout) + throws GuacamoleException { + + LdapConnectionConfig config = new LdapConnectionConfig(); + config.setLdapHost(host); + config.setLdapPort(port); + config.setTimeout(timeout); + + // Map encryption method to proper connection and socket factory + switch (encryptionMethod) { + + // Unencrypted LDAP connection + case NONE: + logger.debug("Connection to LDAP server without encryption."); + break; + + // LDAP over SSL (LDAPS) + case SSL: + logger.debug("Connecting to LDAP server using SSL/TLS."); + config.setUseSsl(true); + config.setSslProtocol(sslProtocol.toString()); + break; + + // LDAP + STARTTLS + case STARTTLS: + logger.debug("Connecting to LDAP server using STARTTLS."); + config.setUseTls(true); + config.setSslProtocol(sslProtocol.toString()); + break; + + // The encryption method, though known, is not actually + // implemented. If encountered, this would be a bug. + default: + throw new GuacamoleUnsupportedException("Unimplemented encryption method: " + encryptionMethod); + + } + + return new LdapNetworkConnection(config); + + } + + /** + * Creates a new instance of LdapNetworkConnection, configured as required + * to use the given encryption method to communicate with the LDAP server + * at the given hostname and port with the encryption method and timeout + * specified, as well. The returned LdapNetworkConnection is configured + * for use but is not yet connected nor bound to the LDAP server. It will + * not be bound until a bind operation is explicitly requested, and will + * not be connected until it is used in an LDAP operation (such as a bind). + * + * @param host + * The hostname or IP address of the LDAP server. + * + * @param port + * The TCP port that the LDAP server is listening on. + * + * @param encryptionMethod + * The encryption method that should be used to communicate with the + * LDAP server. * * @param timeout * The maximum number of milliseconds to wait for a response from the @@ -83,41 +164,8 @@ public class LDAPConnectionService { private LdapNetworkConnection createLDAPConnection(String host, int port, EncryptionMethod encryptionMethod, int timeout) throws GuacamoleException { - - LdapConnectionConfig config = new LdapConnectionConfig(); - config.setLdapHost(host); - config.setLdapPort(port); - config.setTimeout(timeout); - - // Map encryption method to proper connection and socket factory - switch (encryptionMethod) { - - // Unencrypted LDAP connection - case NONE: - logger.debug("Connection to LDAP server without encryption."); - break; - - // LDAP over SSL (LDAPS) - case SSL: - logger.debug("Connecting to LDAP server using SSL/TLS."); - config.setUseSsl(true); - break; - - // LDAP + STARTTLS - case STARTTLS: - logger.debug("Connecting to LDAP server using STARTTLS."); - config.setUseTls(true); - break; - - // The encryption method, though known, is not actually - // implemented. If encountered, this would be a bug. - default: - throw new GuacamoleUnsupportedException("Unimplemented encryption method: " + encryptionMethod); - - } - - return new LdapNetworkConnection(config); - + return createLDAPConnection(host, port, encryptionMethod, + LDAPSSLProtocol.TLSv1_3, timeout); } /** @@ -147,6 +195,7 @@ public class LDAPConnectionService { config.getServerHostname(), config.getServerPort(), config.getEncryptionMethod(), + config.getSslProtocol(), config.getNetworkTimeout()); } @@ -217,7 +266,7 @@ public class LDAPConnectionService { port = encryptionMethod.DEFAULT_PORT; return createLDAPConnection(host, port, encryptionMethod, - config.getNetworkTimeout()); + config.getSslProtocol(), config.getNetworkTimeout()); } diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java index 28ab8ed02..cb2db9b6b 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/DefaultLDAPConfiguration.java @@ -89,6 +89,11 @@ public class DefaultLDAPConfiguration implements LDAPConfiguration { public EncryptionMethod getEncryptionMethod() { return EncryptionMethod.NONE; } + + @Override + public LDAPSSLProtocol getSslProtocol() { + return LDAPSSLProtocol.TLSv1_3; + } @Override public int getMaxResults() { diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java index ae2d3cf3f..9fb44a1fa 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/EnvironmentLDAPConfiguration.java @@ -136,6 +136,14 @@ public class EnvironmentLDAPConfiguration implements LDAPConfiguration { DEFAULT.getEncryptionMethod() ); } + + @Override + public LDAPSSLProtocol getSslProtocol() throws GuacamoleException { + return environment.getProperty( + LDAPGuacamoleProperties.LDAP_SSL_PROTOCOL, + DEFAULT.getSslProtocol() + ); + } @Override public int getMaxResults() throws GuacamoleException { diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java index f888908ed..01d58a3e4 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/JacksonLDAPConfiguration.java @@ -116,6 +116,14 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration { */ @JsonProperty("encryption-method") private String encryptionMethod; + + /** + * The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_SSL_PROTOCOL}. If + * not set within the YAML, this will be null, and will default to the value + * specified by the LDAP API library. + */ + @JsonProperty("ssl-protocol") + private String sslProtocol; /** * The raw YAML value of {@link LDAPGuacamoleProperties#LDAP_MAX_SEARCH_RESULTS}. @@ -365,6 +373,12 @@ public class JacksonLDAPConfiguration implements LDAPConfiguration { return withDefault(LDAPGuacamoleProperties.LDAP_ENCRYPTION_METHOD, encryptionMethod, defaultConfig::getEncryptionMethod); } + + @Override + public LDAPSSLProtocol getSslProtocol() throws GuacamoleException { + return withDefault(LDAPGuacamoleProperties.LDAP_SSL_PROTOCOL, + sslProtocol, defaultConfig::getSslProtocol); + } @Override public int getMaxResults() throws GuacamoleException { diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java index 77eb31511..975631d02 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPConfiguration.java @@ -183,6 +183,20 @@ public interface LDAPConfiguration { * If the encryption method cannot be retrieved. */ EncryptionMethod getEncryptionMethod() throws GuacamoleException; + + /** + * Returns the SSL protocol that should be used when making a secure + * connection to the LDAP server. By default the latest available TLS + * version will be used. + * + * @return + * The SSL protocol that should be used when making a secure connection + * to the LDAP server. + * + * @throws GuacamoleException + * If the SSL protocol cannot be retrieved. + */ + LDAPSSLProtocol getSslProtocol() throws GuacamoleException; /** * Returns maximum number of results a LDAP query can return. diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java index 1db4f723c..cd0b724c0 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPGuacamoleProperties.java @@ -170,6 +170,14 @@ public class LDAPGuacamoleProperties { public String getName() { return "ldap-encryption-method"; } }; + + public static final EnumGuacamoleProperty LDAP_SSL_PROTOCOL = + new EnumGuacamoleProperty(LDAPSSLProtocol.class) { + + @Override + public String getName() { return "ldap-ssl-protocol"; } + + }; /** * The maximum number of results a LDAP query can return. diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPSSLProtocol.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPSSLProtocol.java new file mode 100644 index 000000000..c5b1ca88d --- /dev/null +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/conf/LDAPSSLProtocol.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +package org.apache.guacamole.auth.ldap.conf; + +import org.apache.guacamole.properties.EnumGuacamoleProperty.PropertyValue; + +/** + * All possible SSL protocols which may be used for secure LDAP connections. + */ +public enum LDAPSSLProtocol { + + /** + * Use SSLv3 for secure LDAP connection. + */ + @PropertyValue("SSLv3") + SSLv3("SSLv3"), + + /** + * Use original TLS for secure LDAP connection. + */ + @PropertyValue("TLS") + TLS("TLS"), + + /** + * Use TLSv1 for secure LDAP connection. + */ + @PropertyValue("TLSv1") + TLSv1("TLSv1"), + + /** + * Use TLSv1.1 for secure LDAP connection. + */ + @PropertyValue("TLSv1.1") + TLSv1_1("TLSv1.1"), + + /** + * Use TLSv1.2 for secure LDAP connection. + */ + @PropertyValue("TLSv1.2") + TLSv1_2("TLSv1.2"), + + /** + * Use TLSv1.3 for secure LDAP connection. + */ + @PropertyValue("TLSv1.3") + TLSv1_3("TLSv1.3"); + + /** + * The string value of the option to use which is ultimately what the LDAP + * API consumes to set the SSL protocol. + */ + public final String STRING_VALUE; + + /** + * Initializes this SSL protocol such that it is associated with the + * given string value. + * + * @param value + * The string value that will be associated with the enum value. + */ + private LDAPSSLProtocol(String value) { + this.STRING_VALUE = value; + } + + @Override + public String toString() { + return STRING_VALUE; + } + +}