diff --git a/doc/licenses/bouncycastle-pkix-fips-1.0.7/LICENSE b/doc/licenses/bouncycastle-pkix-fips-1.0.7/LICENSE new file mode 100644 index 000000000..a02bc176b --- /dev/null +++ b/doc/licenses/bouncycastle-pkix-fips-1.0.7/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2000 - 2021 The Legion of the Bouncy Castle Inc. +(https://www.bouncycastle.org) + +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. diff --git a/doc/licenses/bouncycastle-pkix-fips-1.0.7/README b/doc/licenses/bouncycastle-pkix-fips-1.0.7/README new file mode 100644 index 000000000..b362257b8 --- /dev/null +++ b/doc/licenses/bouncycastle-pkix-fips-1.0.7/README @@ -0,0 +1,8 @@ +BouncyCastle PKIX APIs, FIPS Distribution (https://www.bouncycastle.org/fips-java) +----------------------------------------------------------------------- + + Version: 1.0.7 + From: 'The Legion of Bouncy Castle' (https://www.bouncycastle.org) + License(s): + MIT (bundled/bouncycastle-pkix-fips-1.0.7/LICENSE) + diff --git a/doc/licenses/bouncycastle-pkix-fips-1.0.7/dep-coordinates.txt b/doc/licenses/bouncycastle-pkix-fips-1.0.7/dep-coordinates.txt new file mode 100644 index 000000000..23ca14e38 --- /dev/null +++ b/doc/licenses/bouncycastle-pkix-fips-1.0.7/dep-coordinates.txt @@ -0,0 +1 @@ +org.bouncycastle:bcpkix-fips:jar:1.0.7 diff --git a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/pom.xml b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/pom.xml index c6542ae56..7ece6b40e 100644 --- a/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/pom.xml +++ b/extensions/guacamole-auth-sso/modules/guacamole-auth-sso-ssl/pom.xml @@ -119,6 +119,13 @@ jsr311-api + + + org.bouncycastle + bcpkix-fips + 1.0.7 + + 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 21a2e0c25..75a190d35 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 @@ -19,17 +19,14 @@ package org.apache.guacamole.auth.ssl; import com.google.inject.Inject; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import java.security.Principal; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; +import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import javax.naming.InvalidNameException; @@ -46,9 +43,14 @@ import javax.ws.rs.core.UriBuilder; import org.apache.guacamole.GuacamoleClientException; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleResourceNotFoundException; +import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.ssl.conf.ConfigurationService; import org.apache.guacamole.auth.sso.NonceService; import org.apache.guacamole.auth.sso.SSOResource; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.style.RFC4519Style; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.openssl.PEMParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -247,27 +249,25 @@ public class SSLClientAuthenticationResource extends SSOResource { public String getUsername(byte[] certificate) throws GuacamoleException { // Parse and re-verify certificate is valid with respect to timestamps - X509Certificate cert; - try (InputStream input = new ByteArrayInputStream(certificate)) { + X509CertificateHolder cert; + try (Reader reader = new StringReader(new String(certificate, StandardCharsets.UTF_8))) { - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - cert = (X509Certificate) certFactory.generateCertificate(input); + PEMParser parser = new PEMParser(reader); + cert = (X509CertificateHolder) parser.readObject(); // Verify certificate is valid (it should be given pre-validation // from SSL termination, but it's worth rechecking for sanity) - cert.checkValidity(); + if (!cert.isValidOn(new Date())) + throw new GuacamoleClientException("Certificate has expired."); } - catch (CertificateException e) { - throw new GuacamoleClientException("Certificate is not valid: " + e.getMessage(), e); - } catch (IOException e) { - throw new GuacamoleClientException("Certificate could not be read: " + e.getMessage(), e); + throw new GuacamoleServerException("Certificate could not be read: " + e.getMessage(), e); } - // Extract user's DN from their X.509 certificate - Principal principal = cert.getSubjectX500Principal(); - return getUsername(principal.getName()); + // Extract user's DN from their X.509 certificate in LDAP (RFC 4919) format + X500Name subject = X500Name.getInstance(RFC4519Style.INSTANCE, cert.getSubject()); + return getUsername(subject.toString()); }