From 6fab0f5036f7d489790962b6a8a3e9c709f4b64b Mon Sep 17 00:00:00 2001 From: Virtually Nick Date: Fri, 14 May 2021 10:15:15 -0400 Subject: [PATCH] GUACAMOLE-577: Add support for Proxy Configuration to Connections stored in LDAP. --- .../schema/guacConfigGroup.ldif | 27 ++++- .../schema/guacConfigGroup.schema | 20 +++- .../ldap/connection/ConnectionService.java | 112 ++++++++++++++++-- .../net/auth/simple/SimpleConnection.java | 45 ++++++- 4 files changed, 184 insertions(+), 20 deletions(-) diff --git a/extensions/guacamole-auth-ldap/schema/guacConfigGroup.ldif b/extensions/guacamole-auth-ldap/schema/guacConfigGroup.ldif index 6e3f60b258..36622e280e 100644 --- a/extensions/guacamole-auth-ldap/schema/guacConfigGroup.ldif +++ b/extensions/guacamole-auth-ldap/schema/guacConfigGroup.ldif @@ -20,9 +20,24 @@ dn: cn=guacConfigGroup,cn=schema,cn=config objectClass: olcSchemaConfig cn: guacConfigGroup -olcAttributeTypes: {0}( 1.3.6.1.4.1.38971.1.1.1 NAME 'guacConfigProtocol' SYNTAX 1.3.6.1.4.1.1466 - .115.121.1.15 ) -olcAttributeTypes: {1}( 1.3.6.1.4.1.38971.1.1.2 NAME 'guacConfigParameter' SYNTAX 1.3.6.1.4.1.146 - 6.115.121.1.15 ) -olcObjectClasses: {0}( 1.3.6.1.4.1.38971.1.2.1 NAME 'guacConfigGroup' DESC 'Guacamole config - uration group' SUP groupOfNames MUST guacConfigProtocol MAY guacConfigParameter ) + +olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.1 NAME 'guacConfigProtocol' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.2 NAME 'guacConfigParameter' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.3 NAME 'guacConfigProxyHostname' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.4 NAME 'guacConfigProxyPort' + SINGLE-VALUE + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) +olcAttributeTypes: ( 1.3.6.1.4.1.38971.1.1.5 NAME 'guacConfigProxyEncryption' + SINGLE-VALUE + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcObjectClasses: ( 1.3.6.1.4.1.38971.1.2.1 NAME 'guacConfigGroup' + DESC 'Guacamole configuration group' + SUP groupOfNames + MUST guacConfigProtocol + MAY ( guacConfigParameter $ + guacConfigProxyHostname $ + guacConfigProxyPort $ + guacConfigProxyEncryption ) ) diff --git a/extensions/guacamole-auth-ldap/schema/guacConfigGroup.schema b/extensions/guacamole-auth-ldap/schema/guacConfigGroup.schema index 129a41b77d..fb7ec433eb 100644 --- a/extensions/guacamole-auth-ldap/schema/guacConfigGroup.schema +++ b/extensions/guacamole-auth-ldap/schema/guacConfigGroup.schema @@ -18,14 +18,28 @@ # attributetype ( 1.3.6.1.4.1.38971.1.1.1 NAME 'guacConfigProtocol' - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) attributetype ( 1.3.6.1.4.1.38971.1.1.2 NAME 'guacConfigParameter' - SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + +attributetype ( 1.3.6.1.4.1.38971.1.1.3 NAME 'guacConfigProxyHostname' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + +attributetype ( 1.3.6.1.4.1.38971.1.1.4 NAME 'guacConfigProxyPort' + SINGLE-VALUE + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) + +attributetype ( 1.3.6.1.4.1.38971.1.1.5 NAME 'guacConfigProxyEncryption' + SINGLE-VALUE + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) objectClass ( 1.3.6.1.4.1.38971.1.2.1 NAME 'guacConfigGroup' DESC 'Guacamole configuration group' SUP groupOfNames MUST guacConfigProtocol - MAY guacConfigParameter ) + MAY ( guacConfigParameter $ + guacConfigProxyHostname $ + guacConfigProxyPort $ + guacConfigProxyEncryption ) ) diff --git a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java index 9da1547ba6..629b101318 100644 --- a/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java +++ b/extensions/guacamole-auth-ldap/src/main/java/org/apache/guacamole/auth/ldap/connection/ConnectionService.java @@ -42,7 +42,11 @@ import org.apache.guacamole.auth.ldap.ObjectQueryService; import org.apache.guacamole.auth.ldap.group.UserGroupService; import org.apache.guacamole.auth.ldap.user.LDAPAuthenticatedUser; +import org.apache.guacamole.environment.LocalEnvironment; +import org.apache.guacamole.net.auth.AuthenticatedUser; import org.apache.guacamole.net.auth.Connection; +import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration; +import org.apache.guacamole.net.auth.GuacamoleProxyConfiguration.EncryptionMethod; import org.apache.guacamole.net.auth.TokenInjectingConnection; import org.apache.guacamole.net.auth.simple.SimpleConnection; import org.apache.guacamole.protocol.GuacamoleConfiguration; @@ -59,6 +63,33 @@ public class ConnectionService { * Logger for this class. */ private static final Logger logger = LoggerFactory.getLogger(ConnectionService.class); + + /** + * The name of the LDAP attribute that stores connection configuration + * parameters for Guacamole. + */ + public static final String LDAP_ATTRIBUTE_PARAMETER = "guacConfigParameter"; + + /** + * The name of the LDAP attribute that stores the protocol for a Guacamole + * connection. + */ + public static final String LDAP_ATTRIBUTE_PROTOCOL = "guacConfigProtocol"; + + /** + * The name of the LDAP attribute that stores guacd proxy hostname. + */ + public static final String LDAP_ATTRIBUTE_PROXY_HOSTNAME = "guacConfigProxyHostname"; + + /** + * The name of the LDAP attribute that stores guacd proxy port. + */ + public static final String LDAP_ATTRIBUTE_PROXY_PORT = "guacConfigProxyPort"; + + /** + * The name of the LDAP attribute that stores guacd proxy hostname. + */ + public static final String LDAP_ATTRIBUTE_PROXY_ENCRYPTION = "guacConfigProxyEncryption"; /** * Service for executing LDAP queries. @@ -192,11 +223,21 @@ public Map getConnections(LDAPAuthenticatedUser user) config.setProtocol(protocol.getString()); } catch (LdapInvalidAttributeValueException e) { - logger.error("Invalid value of the protocol entry: {}", - e.getMessage()); + logger.error("Invalid value of the protocol entry: {}", e.getMessage()); logger.debug("LDAP exception when getting protocol value.", e); return null; } + + // Get proxy configuration, if any + GuacamoleProxyConfiguration proxyConfig; + try { + proxyConfig = getProxyConfiguration(entry); + } + catch (GuacamoleException e) { + logger.error("Failed to retrieve proxy configuration.", e.getMessage()); + logger.debug("Guacamole Exception when retrieving proxy configuration.", e); + return null; + } // Get parameters, if any Attribute parameterAttribute = entry.get(LDAP_ATTRIBUTE_NAME_PARAMETER); @@ -209,10 +250,8 @@ public Map getConnections(LDAPAuthenticatedUser user) parameter = parameterAttribute.getString(); } catch (LdapInvalidAttributeValueException e) { - logger.warn("Parameter value not valid for {}: {}", - cnName, e.getMessage()); - logger.debug("LDAP exception when getting parameter value.", - e); + logger.warn("Parameter value not valid for {}: {}", cnName, e.getMessage()); + logger.debug("LDAP exception when getting parameter value.", e); return null; } parameterAttribute.remove(parameter); @@ -234,7 +273,7 @@ public Map getConnections(LDAPAuthenticatedUser user) } // Store connection using cn for both identifier and name - Connection connection = new SimpleConnection(cnName, cnName, config, true); + Connection connection = new SimpleConnection(cnName, cnName, proxyConfig, config, true); connection.setParentIdentifier(LDAPAuthenticationProvider.ROOT_CONNECTION_GROUP); // Inject LDAP-specific tokens only if LDAP handled user @@ -301,5 +340,64 @@ private ExprNode getConnectionSearchFilter(LDAPAuthenticatedUser user) return searchFilter; } + + /** + * Given an LDAP entry that stores a GuacamoleConfiguration, generate a + * GuacamoleProxyConfiguration that tells the client how to connect to guacd. + * If the proxy configuration values are not found in the LDAP entry the + * defaults from the environment are used. If errors occur while trying to + * ready or parse values from the LDAP entry a GuacamoleException is thrown. + * + * @param connectionEntry + * The LDAP entry that should be checked for proxy configuration values. + * + * @return + * The GuacamoleProxyConfiguration that contains information on how + * to contact guacd for the given Guacamole connection configuration. + * + * @throws GuacamoleException + * If errors occur trying to parse LDAP values from the entry. + */ + private GuacamoleProxyConfiguration getProxyConfiguration(Entry connectionEntry) + throws GuacamoleException { + + try { + + // Get default proxy configuration values + GuacamoleProxyConfiguration proxyConfig = LocalEnvironment.getInstance().getDefaultGuacamoleProxyConfiguration(); + String proxyHostname = proxyConfig.getHostname(); + int proxyPort = proxyConfig.getPort(); + EncryptionMethod proxyEncryption = proxyConfig.getEncryptionMethod(); + + // Get the proxy hostname + Attribute proxyHostAttr = connectionEntry.get(LDAP_ATTRIBUTE_PROXY_HOSTNAME); + if (proxyHostAttr != null && proxyHostAttr.size() > 0) + proxyHostname = proxyHostAttr.getString(); + + // Get the proxy port + Attribute proxyPortAttr = connectionEntry.get(LDAP_ATTRIBUTE_PROXY_PORT); + if (proxyPortAttr != null && proxyPortAttr.size() > 0) + proxyPort = Integer.parseInt(proxyPortAttr.getString()); + + // Get the proxy encryption method + Attribute proxyEncryptionAttr = connectionEntry.get(LDAP_ATTRIBUTE_PROXY_ENCRYPTION); + if (proxyEncryptionAttr != null && proxyEncryptionAttr.size() > 0) { + try { + proxyEncryption = EncryptionMethod.valueOf(proxyEncryptionAttr.getString()); + } + catch (IllegalArgumentException e) { + throw new GuacamoleServerException("Unknown encryption method specified, value must be either \"NONE\" or \"SSL\".", e); + } + } + + // Return a new proxy configuration + return new GuacamoleProxyConfiguration(proxyHostname, proxyPort, proxyEncryption); + } + catch (LdapInvalidAttributeValueException e) { + logger.error("Invalid value in proxy configuration: {}", e.getMessage()); + logger.debug("LDAP exception fetching proxy attribute value.", e); + throw new GuacamoleServerException("Invalid LDAP value in proxy configuration.", e); + } + } } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java index aa7e748531..8e59c3c590 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/net/auth/simple/SimpleConnection.java @@ -24,7 +24,6 @@ import java.util.Map; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; -import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.LocalEnvironment; import org.apache.guacamole.net.GuacamoleSocket; import org.apache.guacamole.net.GuacamoleTunnel; @@ -53,6 +52,11 @@ public class SimpleConnection extends AbstractConnection { * Backing configuration, containing all sensitive information. */ private GuacamoleConfiguration fullConfig; + + /** + * The proxy configuration describing how to connect to guacd. + */ + private GuacamoleProxyConfiguration proxyConfig; /** * Whether parameter tokens in the underlying GuacamoleConfiguration should @@ -158,6 +162,39 @@ public SimpleConnection(String name, String identifier, this.interpretTokens = interpretTokens; } + + /** + * Creates a new SimpleConnection having the given identifier, + * GuacamoleConfiguration, and GuacamoleProxyConfiguration. Parameter tokens + * will be interpreted if explicitly requested. + * + * @param name + * The name to associate with this connection. + * + * @param identifier + * The identifier to associate with this connection. + * + * @param proxyConfig + * The Guacamole proxy configuration describing how the connection to + * guacd should be established, or null if the default settings will be + * used. + * + * @param config + * The configuration describing how to connect to this connection. + * + * @param interpretTokens + * Whether parameter tokens in the underlying GuacamoleConfiguration + * should be automatically applied upon connecting. If false, parameter + * tokens will not be interpreted at all. + */ + public SimpleConnection(String name, String identifier, + GuacamoleProxyConfiguration proxyConfig, + GuacamoleConfiguration config, boolean interpretTokens) { + + this(name, identifier, config, interpretTokens); + this.proxyConfig = proxyConfig; + + } /** * Returns the GuacamoleConfiguration describing how to connect to this @@ -201,9 +238,9 @@ public void setAttributes(Map attributes) { public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { - // Retrieve proxy configuration from environment - Environment environment = LocalEnvironment.getInstance(); - GuacamoleProxyConfiguration proxyConfig = environment.getDefaultGuacamoleProxyConfiguration(); + // Retrieve proxy configuration from environment if we don't have one + if (proxyConfig == null) + proxyConfig = LocalEnvironment.getInstance().getDefaultGuacamoleProxyConfiguration(); // Get guacd connection parameters String hostname = proxyConfig.getHostname();