From 0f80d0ddf25977208b9be72d8319e3ece15a28bc Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 10 Apr 2025 01:24:01 -0700 Subject: [PATCH 1/2] GUACAMOLE-839: Fix check for lack of request headers (may be null). --- .../guacamole/auth/ssl/SSLClientAuthenticationResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLClientAuthenticationResource.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLClientAuthenticationResource.java index fd3230b7d..443795001 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLClientAuthenticationResource.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/SSLClientAuthenticationResource.java @@ -133,7 +133,7 @@ public class SSLClientAuthenticationResource extends SSOResource { private String getHeader(HttpHeaders headers, String name) { List values = headers.getRequestHeader(name); - if (values.isEmpty()) + if (values == null || values.isEmpty()) return null; return values.get(0); From 91d47fea585faa24ad1f8e8fa40d5ea8fec0f98c Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 10 Apr 2025 01:25:54 -0700 Subject: [PATCH 2/2] GUACAMOLE-839: Do not consider port when comparing hostname from "Host" header. Within the scope of the SSL auth, the port noted in the "Host" header is not material to the routing of the request. We simply need to know whether we've received the request at the primary URI or a generated auth URI. The hostname is sufficient to determine this, and the underlying port may not be clear when omitted from "Host". --- .../auth/ssl/conf/ConfigurationService.java | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java index 3260c237c..6ca8c8e6e 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/src/main/java/org/apache/guacamole/auth/ssl/conf/ConfigurationService.java @@ -193,6 +193,50 @@ public class ConfigurationService { @Inject private Environment environment; + /** + * Returns whether the given hostname matches the hostname of the given + * URI. The provided hostname may be the value of an HTTP "Host" header, + * and may include a port number. If a port number is included in the + * hostname, it is ignored. + * + * @param hostname + * The hostname to check, which may alternatively be the value of an + * HTTP "Host" header, with or without port number. The port number is + * not considered when determining whether this hostname matches the + * hostname of the provided URI. + * + * @param offset + * The character offset within the provided hostname where checking + * should start. Any characters before this offset are ignored. This + * offset does not affect where checking starts within the hostname of + * the provided URI. + * + * @param uri + * The URI to check the given hostname against. + * + * @return + * true if the provided hostname from the given offset onward is + * identical to the hostname of the given URI, false otherwise. + */ + private boolean hostnameMatches(String hostname, int offset, URI uri) { + + // Locate end of actual hostname portion of "Host" header + int endOfHostname = hostname.indexOf(':'); + if (endOfHostname == -1) + endOfHostname = hostname.length(); + + // Before checking substring equivalence, we need to verify that the + // length actually matches what we expect (we'd otherwise consider the + // host to match if it starts with the expected hostname, ignoring any + // remaining characters) + String expectedHostname = uri.getHost(); + if (expectedHostname.length() != endOfHostname - offset) + return false; + + return hostname.regionMatches(true, offset, expectedHostname, 0, expectedHostname.length()); + + } + /** * Returns a URI that should be used to authenticate users with SSL/TLS * client authentication. The returned URI will consist of the configured @@ -248,9 +292,6 @@ public class ConfigurationService { if (isPrimaryHostname(hostname)) return null; - URI authURI = environment.getRequiredProperty(SSL_AUTH_URI); - String baseHostname = authURI.getHost(); - // Verify the first domain component is at least one character in // length int firstPeriod = hostname.indexOf('.'); @@ -259,7 +300,8 @@ public class ConfigurationService { // Verify domain matches the configured auth URI except for the leading // subdomain - if (!hostname.regionMatches(true, firstPeriod + 1, baseHostname, 0, baseHostname.length())) + URI authURI = environment.getRequiredProperty(SSL_AUTH_URI); + if (!hostnameMatches(hostname, firstPeriod + 1, authURI)) return null; // Extract subdomain @@ -326,7 +368,7 @@ public class ConfigurationService { */ public boolean isPrimaryHostname(String hostname) throws GuacamoleException { URI primaryURI = getPrimaryURI(); - return hostname.equalsIgnoreCase(primaryURI.getHost()); + return hostnameMatches(hostname, 0, primaryURI); } /**