From 1c4831dd5191a9043edff7b771fef919a56281db Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 16 Aug 2017 11:58:26 -0400 Subject: [PATCH 01/23] GUACAMOLE-362: Add support for CAS ClearPass, parsing and decrypting the password and assigning it a token. --- .../cas/AuthenticationProviderService.java | 103 +++++++++++++++++- .../auth/cas/conf/CASGuacamoleProperties.java | 13 +++ .../auth/cas/conf/ConfigurationService.java | 17 +++ .../cas/ticket/TicketValidationService.java | 9 +- 4 files changed, 134 insertions(+), 8 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index f3870a6d8..0422d30e3 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -21,11 +21,26 @@ package org.apache.guacamole.auth.cas; import com.google.inject.Inject; import com.google.inject.Provider; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; import java.util.Enumeration; +import javax.crypto.Cipher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import javax.xml.bind.DatatypeConverter; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.LocalEnvironment; import org.apache.guacamole.form.Field; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; @@ -34,6 +49,9 @@ import org.apache.guacamole.auth.cas.conf.ConfigurationService; import org.apache.guacamole.auth.cas.form.CASTicketField; import org.apache.guacamole.auth.cas.ticket.TicketValidationService; import org.apache.guacamole.auth.cas.user.AuthenticatedUser; +import org.jasig.cas.client.authentication.AttributePrincipal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Service providing convenience functions for the CAS AuthenticationProvider @@ -41,6 +59,11 @@ import org.apache.guacamole.auth.cas.user.AuthenticatedUser; */ public class AuthenticationProviderService { + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); + /** * Service for retrieving CAS configuration information. */ @@ -83,7 +106,13 @@ public class AuthenticationProviderService { String ticket = request.getParameter(CASTicketField.PARAMETER_NAME); if (ticket != null) { AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init(ticketService.processUsername(ticket), credentials); + AttributePrincipal principal = ticketService.validateTicket(ticket); + String username = principal.getName(); + credentials.setUsername(username); + String clearPass = decryptPassword(principal.getAttributes().get("credential").toString()); + if (clearPass != null && !clearPass.isEmpty()) + credentials.setPassword(clearPass); + authenticatedUser.init(username, credentials); return authenticatedUser; } } @@ -105,4 +134,76 @@ public class AuthenticationProviderService { } + /** + * Takes an encrypted string representing a password provided by + * the CAS ClearPass service and decrypts it using the private + * key configured for this extension. Returns null if it is + * unable to decrypt the password. + * + * @param encryptedPassword + * A string with the encrypted password provided by the + * CAS service. + * + * @return + * The decrypted password, or null if it is unable to + * decrypt the password. + * @throws GuacamoleException + * If unable to get Guacamole configuration data + */ + private final String decryptPassword(String encryptedPassword) + throws GuacamoleException { + + // If we get nothing, we return nothing. + if (encryptedPassword == null || encryptedPassword.isEmpty()) + return null; + + try { + + // Open and read the file specified in the configuration. + File keyFile = new File(new LocalEnvironment().getGuacamoleHome(), confService.getClearpassKey().toString()); + InputStream keyInput = new BufferedInputStream(new FileInputStream(keyFile)); + final byte[] keyBytes = new byte[(int) keyFile.length()]; + keyInput.read(keyBytes); + keyInput.close(); + + // Set up decryption infrastructure + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + KeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + final PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + final Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); + final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + // Decrypt and return a new string. + final byte[] cipherData = cipher.doFinal(pass64); + return new String(cipherData); + } + catch (FileNotFoundException e) { + logger.error("ClearPass key file not found, password will not be decrypted."); + logger.debug("Error locating the ClearPass key file: {}", e.getMessage()); + return null; + } + catch (IOException e) { + logger.error("Error reading ClearPass key file, password will not be decrypted."); + logger.debug("Error reading the ClearPass key file: {}", e.getMessage()); + return null; + } + catch (NoSuchAlgorithmException e) { + logger.error("Unable to find the specified algorithm, password will not be decrypted."); + logger.debug("Algorithm was not found: {}", e.getMessage()); + return null; + } + catch (InvalidKeyException e) { + logger.error("Invalid key was loaded, password will not be decrypted."); + logger.debug("The loaded key was invalid: {}", e.getMessage()); + return null; + } + catch (Throwable t) { + logger.error("Error decrypting password, it will not be available as a token."); + logger.debug("Error in one of the components to decrypt the password: {}", t.getMessage()); + return null; + } + + } + } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java index 00fb901a1..410e848ee 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java @@ -19,6 +19,7 @@ package org.apache.guacamole.auth.cas.conf; +import org.apache.guacamole.properties.FileGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; /** @@ -57,4 +58,16 @@ public class CASGuacamoleProperties { }; + /** + * The location of the private key file used to retrieve the + * password if CAS is configured to support ClearPass. + */ + public static final FileGuacamoleProperty CAS_CLEARPASS_KEY = + new FileGuacamoleProperty() { + + @Override + public String getName() { return "cas-clearpass-key"; } + + }; + } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java index 17be2d361..b2d74d54c 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java @@ -20,6 +20,7 @@ package org.apache.guacamole.auth.cas.conf; import com.google.inject.Inject; +import java.io.File; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; @@ -68,4 +69,20 @@ public class ConfigurationService { return environment.getRequiredProperty(CASGuacamoleProperties.CAS_REDIRECT_URI); } + /** + * Returns the path to the file that contains the private key + * used to decrypt the credential that is sent encrypted by CAS, + * or null if no key is defined. + * + * @return + * The path to the private key to decrypt the ClearPass + * credential returned by CAS. + * + * @throws GuacamoleException + * If guacamole.properties cannot be parsed. + */ + public File getClearpassKey() throws GuacamoleException { + return environment.getProperty(CASGuacamoleProperties.CAS_CLEARPASS_KEY); + } + } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index 9644c684d..fca2ccf49 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -57,9 +57,7 @@ public class TicketValidationService { * If the ID ticket is not valid, the username claim type is missing, or * guacamole.properties could not be parsed. */ - public String processUsername(String ticket) throws GuacamoleException { - - AttributePrincipal principal = null; + public AttributePrincipal validateTicket(String ticket) throws GuacamoleException { // Retrieve the configured CAS URL, establish a ticket validator, // and then attempt to validate the supplied ticket. If that succeeds, @@ -70,15 +68,12 @@ public class TicketValidationService { try { String confRedirectURI = confService.getRedirectURI(); Assertion a = validator.validate(ticket, confRedirectURI); - principal = a.getPrincipal(); + return a.getPrincipal(); } catch (TicketValidationException e) { throw new GuacamoleException("Ticket validation failed.", e); } - // Return the principal name as the username. - return principal.getName(); - } } From 1c333106c03a188418679e7e248a3e848dd604b7 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sat, 19 Aug 2017 14:06:05 -0400 Subject: [PATCH 02/23] GUACAMOLE-362: Fix case where credential object is null. --- .../auth/cas/AuthenticationProviderService.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index 0422d30e3..6a13a83ad 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -109,9 +109,12 @@ public class AuthenticationProviderService { AttributePrincipal principal = ticketService.validateTicket(ticket); String username = principal.getName(); credentials.setUsername(username); - String clearPass = decryptPassword(principal.getAttributes().get("credential").toString()); - if (clearPass != null && !clearPass.isEmpty()) - credentials.setPassword(clearPass); + Object credObj = principal.getAttributes().get("credential"); + if (credObj != null) { + String clearPass = decryptPassword(credObj.toString()); + if (clearPass != null && !clearPass.isEmpty()) + credentials.setPassword(clearPass); + } authenticatedUser.init(username, credentials); return authenticatedUser; } From 87fba0ed7bb640e305a81626e6e6b03cc57221ea Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 23 Aug 2017 10:21:24 -0400 Subject: [PATCH 03/23] GUACAMOLE-362: Fix style issue; remove unnecessary LocalEnvironment initilization. --- .../auth/cas/AuthenticationProviderService.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index 6a13a83ad..17970600b 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -39,9 +39,9 @@ import javax.crypto.Cipher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.xml.bind.DatatypeConverter; -import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.environment.LocalEnvironment; +import org.apache.guacamole.environment.Environment; import org.apache.guacamole.form.Field; +import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; @@ -70,6 +70,12 @@ public class AuthenticationProviderService { @Inject private ConfigurationService confService; + /** + * The Guacamole server environment. + */ + @Inject + private Environment environment; + /** * Service for validating received ID tickets. */ @@ -150,6 +156,7 @@ public class AuthenticationProviderService { * @return * The decrypted password, or null if it is unable to * decrypt the password. + * * @throws GuacamoleException * If unable to get Guacamole configuration data */ @@ -163,7 +170,7 @@ public class AuthenticationProviderService { try { // Open and read the file specified in the configuration. - File keyFile = new File(new LocalEnvironment().getGuacamoleHome(), confService.getClearpassKey().toString()); + File keyFile = new File(environment.getGuacamoleHome(), confService.getClearpassKey().toString()); InputStream keyInput = new BufferedInputStream(new FileInputStream(keyFile)); final byte[] keyBytes = new byte[(int) keyFile.length()]; keyInput.read(keyBytes); From c3aaf0aa032713e70c72326e19e3733db8f7670f Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 23 Aug 2017 10:27:09 -0400 Subject: [PATCH 04/23] GUACAMOLE-362: Debug should throw full exception, not just getMessage() output. --- .../auth/cas/AuthenticationProviderService.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index 17970600b..feb842dd2 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -27,6 +27,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; +import java.lang.IllegalArgumentException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -190,27 +191,32 @@ public class AuthenticationProviderService { } catch (FileNotFoundException e) { logger.error("ClearPass key file not found, password will not be decrypted."); - logger.debug("Error locating the ClearPass key file: {}", e.getMessage()); + logger.debug("Error locating the ClearPass key file: {}", e); return null; } catch (IOException e) { logger.error("Error reading ClearPass key file, password will not be decrypted."); - logger.debug("Error reading the ClearPass key file: {}", e.getMessage()); + logger.debug("Error reading the ClearPass key file: {}", e); return null; } catch (NoSuchAlgorithmException e) { logger.error("Unable to find the specified algorithm, password will not be decrypted."); - logger.debug("Algorithm was not found: {}", e.getMessage()); + logger.debug("Algorithm was not found: {}", e); return null; } catch (InvalidKeyException e) { logger.error("Invalid key was loaded, password will not be decrypted."); - logger.debug("The loaded key was invalid: {}", e.getMessage()); + logger.debug("The loaded key was invalid: {}", e); + return null; + } + catch (IllegalArgumentException e) { + logger.error("Failed to parse Base64 data, password will not be decrypted."); + logger.debug("Data received was not valid Base64 data, so decryption cannot continue: {}", e); return null; } catch (Throwable t) { logger.error("Error decrypting password, it will not be available as a token."); - logger.debug("Error in one of the components to decrypt the password: {}", t.getMessage()); + logger.debug("Error in one of the components to decrypt the password: {}", t); return null; } From 36489ff403ae66219e858773b7e5095241628c2a Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sun, 27 Aug 2017 20:34:46 -0400 Subject: [PATCH 05/23] GUACAMOLE-362: Implement new CipherGuacamoleProperty and move cipher functionality to it. --- .../cas/AuthenticationProviderService.java | 46 +--------- .../auth/cas/conf/CASGuacamoleProperties.java | 6 +- .../auth/cas/conf/ConfigurationService.java | 3 +- .../properties/CipherGuacamoleProperty.java | 92 +++++++++++++++++++ 4 files changed, 102 insertions(+), 45 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index feb842dd2..b7ebdf768 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -37,6 +37,7 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; import java.util.Enumeration; import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.xml.bind.DatatypeConverter; @@ -170,53 +171,16 @@ public class AuthenticationProviderService { try { - // Open and read the file specified in the configuration. - File keyFile = new File(environment.getGuacamoleHome(), confService.getClearpassKey().toString()); - InputStream keyInput = new BufferedInputStream(new FileInputStream(keyFile)); - final byte[] keyBytes = new byte[(int) keyFile.length()]; - keyInput.read(keyBytes); - keyInput.close(); - - // Set up decryption infrastructure - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - KeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); - final PrivateKey privateKey = keyFactory.generatePrivate(keySpec); - final Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); - final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); - cipher.init(Cipher.DECRYPT_MODE, privateKey); + final Cipher cipher = confService.getClearpassCipher(); // Decrypt and return a new string. + final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); final byte[] cipherData = cipher.doFinal(pass64); return new String(cipherData); } - catch (FileNotFoundException e) { - logger.error("ClearPass key file not found, password will not be decrypted."); - logger.debug("Error locating the ClearPass key file: {}", e); - return null; - } - catch (IOException e) { - logger.error("Error reading ClearPass key file, password will not be decrypted."); - logger.debug("Error reading the ClearPass key file: {}", e); - return null; - } - catch (NoSuchAlgorithmException e) { - logger.error("Unable to find the specified algorithm, password will not be decrypted."); - logger.debug("Algorithm was not found: {}", e); - return null; - } - catch (InvalidKeyException e) { - logger.error("Invalid key was loaded, password will not be decrypted."); - logger.debug("The loaded key was invalid: {}", e); - return null; - } - catch (IllegalArgumentException e) { - logger.error("Failed to parse Base64 data, password will not be decrypted."); - logger.debug("Data received was not valid Base64 data, so decryption cannot continue: {}", e); - return null; - } catch (Throwable t) { - logger.error("Error decrypting password, it will not be available as a token."); - logger.debug("Error in one of the components to decrypt the password: {}", t); + logger.error("Failed to decrypt the data, password token will not be available."); + logger.debug("Failed to either convert Base64 or decrypt the password. CAS Password will not be available inside Guacamole. Exception is: {}", t); return null; } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java index 410e848ee..7a600c91b 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java @@ -19,7 +19,7 @@ package org.apache.guacamole.auth.cas.conf; -import org.apache.guacamole.properties.FileGuacamoleProperty; +import org.apache.guacamole.properties.CipherGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; /** @@ -62,8 +62,8 @@ public class CASGuacamoleProperties { * The location of the private key file used to retrieve the * password if CAS is configured to support ClearPass. */ - public static final FileGuacamoleProperty CAS_CLEARPASS_KEY = - new FileGuacamoleProperty() { + public static final CipherGuacamoleProperty CAS_CLEARPASS_KEY = + new CipherGuacamoleProperty() { @Override public String getName() { return "cas-clearpass-key"; } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java index b2d74d54c..ba969d409 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java @@ -21,6 +21,7 @@ package org.apache.guacamole.auth.cas.conf; import com.google.inject.Inject; import java.io.File; +import javax.crypto.Cipher; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; @@ -81,7 +82,7 @@ public class ConfigurationService { * @throws GuacamoleException * If guacamole.properties cannot be parsed. */ - public File getClearpassKey() throws GuacamoleException { + public Cipher getClearpassCipher() throws GuacamoleException { return environment.getProperty(CASGuacamoleProperties.CAS_CLEARPASS_KEY); } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java new file mode 100644 index 000000000..e2f95ec76 --- /dev/null +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java @@ -0,0 +1,92 @@ +/* + * 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.properties; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.lang.IllegalArgumentException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.environment.Environment; +import org.apache.guacamole.environment.LocalEnvironment; + +/** + * A GuacamoleProperty whose value is derived from a private key file. + */ +public abstract class CipherGuacamoleProperty implements GuacamoleProperty { + + @Override + public Cipher parseValue(String value) throws GuacamoleException { + + try { + + final Environment environment = new LocalEnvironment(); + + // Open and read the file specified in the configuration. + File keyFile = new File(environment.getGuacamoleHome(), value); + InputStream keyInput = new BufferedInputStream(new FileInputStream(keyFile)); + final byte[] keyBytes = new byte[(int) keyFile.length()]; + keyInput.read(keyBytes); + keyInput.close(); + + // Set up decryption infrastructure + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + KeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + final PrivateKey privateKey = keyFactory.generatePrivate(keySpec); + final Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm()); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + + return cipher; + + } + catch (FileNotFoundException e) { + throw new GuacamoleException("Could not find the specified key file.", e); + } + catch (IOException e) { + throw new GuacamoleException("Could not read in the specified key file.", e); + } + catch (NoSuchAlgorithmException e) { + throw new GuacamoleException("Specified algorithm does not exist.", e); + } + catch (InvalidKeyException e) { + throw new GuacamoleException("Specified key is invalid.", e); + } + catch (InvalidKeySpecException e) { + throw new GuacamoleException("Invalid KeySpec initialization.", e); + } + catch (NoSuchPaddingException e) { + throw new GuacamoleException("No such padding exception.", e); + } + + } + +} From ed4c025a2e642899427a1866a418d119ebff3bf8 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sun, 27 Aug 2017 20:55:27 -0400 Subject: [PATCH 06/23] GUACAMOLE-362: Deal gracefully with situations where password cannot be decrypted. --- .../auth/cas/AuthenticationProviderService.java | 16 ++++++++++++---- .../properties/CipherGuacamoleProperty.java | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index b7ebdf768..da32f72eb 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -173,10 +173,15 @@ public class AuthenticationProviderService { final Cipher cipher = confService.getClearpassCipher(); - // Decrypt and return a new string. - final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); - final byte[] cipherData = cipher.doFinal(pass64); - return new String(cipherData); + if (cipher != null) { + + // Decode and decrypt, and return a new string. + final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); + final byte[] cipherData = cipher.doFinal(pass64); + return new String(cipherData); + + } + } catch (Throwable t) { logger.error("Failed to decrypt the data, password token will not be available."); @@ -184,6 +189,9 @@ public class AuthenticationProviderService { return null; } + logger.warn("Encrypted password provided by CAS, but no Private Key was available to decrypt it."); + return null; + } } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java index e2f95ec76..d4d763f7f 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java @@ -47,6 +47,9 @@ public abstract class CipherGuacamoleProperty implements GuacamoleProperty Date: Sun, 24 Sep 2017 15:58:09 -0400 Subject: [PATCH 07/23] GUACAMOLE-362: Change new property to a PrivateKey and refactor code accordingly. --- .../cas/AuthenticationProviderService.java | 33 +++++++++++-------- .../auth/cas/conf/CASGuacamoleProperties.java | 6 ++-- .../auth/cas/conf/ConfigurationService.java | 4 +-- ....java => PrivateKeyGuacamoleProperty.java} | 32 +++++------------- 4 files changed, 34 insertions(+), 41 deletions(-) rename guacamole-ext/src/main/java/org/apache/guacamole/properties/{CipherGuacamoleProperty.java => PrivateKeyGuacamoleProperty.java} (65%) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index da32f72eb..22a63bddc 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -44,6 +44,7 @@ import javax.xml.bind.DatatypeConverter; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.form.Field; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; @@ -166,32 +167,38 @@ public class AuthenticationProviderService { throws GuacamoleException { // If we get nothing, we return nothing. - if (encryptedPassword == null || encryptedPassword.isEmpty()) + if (encryptedPassword == null || encryptedPassword.isEmpty()) { + logger.warn("No or empty encrypted password, no password will be available."); return null; + } + + final PrivateKey clearpassKey = confService.getClearpassKey(); + if (clearpassKey == null) { + logger.warn("No private key available to decrypt password."); + return null; + } try { - final Cipher cipher = confService.getClearpassCipher(); + final Cipher cipher = Cipher.getInstance(clearpassKey.getAlgorithm()); - if (cipher != null) { + if (cipher == null) + throw new GuacamoleServerException("Failed to initialize cipher object with private key."); - // Decode and decrypt, and return a new string. - final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); - final byte[] cipherData = cipher.doFinal(pass64); - return new String(cipherData); + // Initialize the Cipher in decrypt mode. + cipher.init(Cipher.DECRYPT_MODE, clearpassKey); - } + // Decode and decrypt, and return a new string. + final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); + final byte[] cipherData = cipher.doFinal(pass64); + return new String(cipherData); } catch (Throwable t) { - logger.error("Failed to decrypt the data, password token will not be available."); logger.debug("Failed to either convert Base64 or decrypt the password. CAS Password will not be available inside Guacamole. Exception is: {}", t); - return null; + throw new GuacamoleServerException("Failed to decrypt CAS ClearPass password.", t); } - logger.warn("Encrypted password provided by CAS, but no Private Key was available to decrypt it."); - return null; - } } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java index 7a600c91b..aa4a06ef4 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java @@ -19,7 +19,7 @@ package org.apache.guacamole.auth.cas.conf; -import org.apache.guacamole.properties.CipherGuacamoleProperty; +import org.apache.guacamole.properties.PrivateKeyGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; /** @@ -62,8 +62,8 @@ public class CASGuacamoleProperties { * The location of the private key file used to retrieve the * password if CAS is configured to support ClearPass. */ - public static final CipherGuacamoleProperty CAS_CLEARPASS_KEY = - new CipherGuacamoleProperty() { + public static final PrivateKeyGuacamoleProperty CAS_CLEARPASS_KEY = + new PrivateKeyGuacamoleProperty() { @Override public String getName() { return "cas-clearpass-key"; } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java index ba969d409..409097e54 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java @@ -21,7 +21,7 @@ package org.apache.guacamole.auth.cas.conf; import com.google.inject.Inject; import java.io.File; -import javax.crypto.Cipher; +import java.security.PrivateKey; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.environment.Environment; @@ -82,7 +82,7 @@ public class ConfigurationService { * @throws GuacamoleException * If guacamole.properties cannot be parsed. */ - public Cipher getClearpassCipher() throws GuacamoleException { + public PrivateKey getClearpassKey() throws GuacamoleException { return environment.getProperty(CASGuacamoleProperties.CAS_CLEARPASS_KEY); } diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java similarity index 65% rename from guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java rename to guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java index d4d763f7f..904a4d1de 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/CipherGuacamoleProperty.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java @@ -33,29 +33,25 @@ import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; -import javax.crypto.Cipher; -import javax.crypto.NoSuchPaddingException; -import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.LocalEnvironment; /** * A GuacamoleProperty whose value is derived from a private key file. */ -public abstract class CipherGuacamoleProperty implements GuacamoleProperty { +public abstract class PrivateKeyGuacamoleProperty implements GuacamoleProperty { @Override - public Cipher parseValue(String value) throws GuacamoleException { + public PrivateKey parseValue(String value) throws GuacamoleServerException { if (value == null || value.isEmpty()) return null; try { - final Environment environment = new LocalEnvironment(); - // Open and read the file specified in the configuration. - File keyFile = new File(environment.getGuacamoleHome(), value); + File keyFile = new File(value); InputStream keyInput = new BufferedInputStream(new FileInputStream(keyFile)); final byte[] keyBytes = new byte[(int) keyFile.length()]; keyInput.read(keyBytes); @@ -64,30 +60,20 @@ public abstract class CipherGuacamoleProperty implements GuacamoleProperty Date: Tue, 26 Sep 2017 22:21:08 -0400 Subject: [PATCH 08/23] GUACAMOLE-362: Removed unnecesary addition of username to credentials. --- .../apache/guacamole/auth/cas/AuthenticationProviderService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index 22a63bddc..617d3d964 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -117,7 +117,6 @@ public class AuthenticationProviderService { AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); AttributePrincipal principal = ticketService.validateTicket(ticket); String username = principal.getName(); - credentials.setUsername(username); Object credObj = principal.getAttributes().get("credential"); if (credObj != null) { String clearPass = decryptPassword(credObj.toString()); From 6f04573b8476a817dffa3bbd1fa97288644e37d3 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 27 Sep 2017 10:25:52 -0400 Subject: [PATCH 09/23] GUACAMOLE-362: Update documentation to reflect code. --- .../auth/cas/conf/ConfigurationService.java | 7 +++---- .../auth/cas/ticket/TicketValidationService.java | 13 ++++++------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java index 409097e54..dee9c4d69 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/ConfigurationService.java @@ -71,12 +71,11 @@ public class ConfigurationService { } /** - * Returns the path to the file that contains the private key - * used to decrypt the credential that is sent encrypted by CAS, - * or null if no key is defined. + * Returns the PrivateKey used to decrypt the credential object + * sent encrypted by CAS, or null if no key is defined. * * @return - * The path to the private key to decrypt the ClearPass + * The PrivateKey used to decrypt the ClearPass * credential returned by CAS. * * @throws GuacamoleException diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index fca2ccf49..96da6ff44 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -42,20 +42,19 @@ public class TicketValidationService { private ConfigurationService confService; /** - * Validates and parses the given ID ticket, returning the username contained - * therein, as defined by the username claim type given in - * guacamole.properties. If the username claim type is missing or the ID - * ticket is invalid, an exception is thrown instead. + * Validates and parses the given ID ticket, returning the AttributePrincipal + * derived from the parameters provided by the CAS server in the ticket. If the + * ticket is invalid an exception is thrown. * * @param ticket * The ID ticket to validate and parse. * * @return - * The username contained within the given ID ticket. + * The AttributePrincipal derived from parameters provided in the ticket. * * @throws GuacamoleException - * If the ID ticket is not valid, the username claim type is missing, or - * guacamole.properties could not be parsed. + * If the ID ticket is not valid or guacamole.properties could + * not be parsed. */ public AttributePrincipal validateTicket(String ticket) throws GuacamoleException { From 3ce0980efcb170a0ca96373ba842466dc3aa1445 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 27 Sep 2017 10:28:34 -0400 Subject: [PATCH 10/23] GUACAMOLE-362: More useful error messages for exceptions in PrivateKey property. --- .../guacamole/properties/PrivateKeyGuacamoleProperty.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java index 904a4d1de..570b776a7 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java @@ -70,10 +70,10 @@ public abstract class PrivateKeyGuacamoleProperty implements GuacamoleProperty

Date: Wed, 27 Sep 2017 10:42:20 -0400 Subject: [PATCH 11/23] GUACAMOLE-362: Catch exceptions individually and display useful error messages. --- .../cas/AuthenticationProviderService.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index 617d3d964..ecb02d277 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -36,8 +36,10 @@ import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; import java.util.Enumeration; +import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.xml.bind.DatatypeConverter; @@ -193,9 +195,23 @@ public class AuthenticationProviderService { return new String(cipherData); } - catch (Throwable t) { - logger.debug("Failed to either convert Base64 or decrypt the password. CAS Password will not be available inside Guacamole. Exception is: {}", t); - throw new GuacamoleServerException("Failed to decrypt CAS ClearPass password.", t); + catch (BadPaddingException e) { + throw new GuacamoleServerException("Bad padding when decrypting cipher data.", e); + } + catch (IllegalBlockSizeException e) { + throw new GuacamoleServerException("Illegal block size while opening private key.", e); + } + catch (InvalidKeyException e) { + throw new GuacamoleServerException("Specified private key for ClearPass decryption is invalid.", e); + } + catch (NoSuchAlgorithmException e) { + throw new GuacamoleServerException("Unexpected algorithm for the private key.", e); + } + catch (NoSuchPaddingException e) { + throw new GuacamoleServerException("No such padding tryingto initialize cipher with private key.", e); + } + finally { + logger.debug("Yah."); } } From 63134322b0ca5e788b6206c252d8e7e7262c0b55 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 27 Sep 2017 10:42:47 -0400 Subject: [PATCH 12/23] GUACAMOLE-362: Remove debug line. --- .../guacamole/auth/cas/AuthenticationProviderService.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index ecb02d277..171c30f1e 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -210,9 +210,6 @@ public class AuthenticationProviderService { catch (NoSuchPaddingException e) { throw new GuacamoleServerException("No such padding tryingto initialize cipher with private key.", e); } - finally { - logger.debug("Yah."); - } } From 62fafcb379f96bb6850f1b4cbb0339072fe0f9f6 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Wed, 27 Sep 2017 11:13:51 -0400 Subject: [PATCH 13/23] GUACAMOLE-362: Move password decryption logic into TicketValidationService class. --- .../cas/AuthenticationProviderService.java | 114 ++---------------- .../cas/ticket/TicketValidationService.java | 108 ++++++++++++++++- 2 files changed, 113 insertions(+), 109 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index 171c30f1e..325268e5c 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -21,32 +21,11 @@ package org.apache.guacamole.auth.cas; import com.google.inject.Inject; import com.google.inject.Provider; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.IOException; -import java.lang.IllegalArgumentException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.spec.KeySpec; -import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; -import java.util.Enumeration; -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import javax.xml.bind.DatatypeConverter; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.form.Field; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; @@ -54,9 +33,6 @@ import org.apache.guacamole.auth.cas.conf.ConfigurationService; import org.apache.guacamole.auth.cas.form.CASTicketField; import org.apache.guacamole.auth.cas.ticket.TicketValidationService; import org.apache.guacamole.auth.cas.user.AuthenticatedUser; -import org.jasig.cas.client.authentication.AttributePrincipal; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Service providing convenience functions for the CAS AuthenticationProvider @@ -64,11 +40,6 @@ import org.slf4j.LoggerFactory; */ public class AuthenticationProviderService { - /** - * Logger for this class. - */ - private static final Logger logger = LoggerFactory.getLogger(AuthenticationProviderService.class); - /** * Service for retrieving CAS configuration information. */ @@ -116,16 +87,17 @@ public class AuthenticationProviderService { if (request != null) { String ticket = request.getParameter(CASTicketField.PARAMETER_NAME); if (ticket != null) { - AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - AttributePrincipal principal = ticketService.validateTicket(ticket); - String username = principal.getName(); - Object credObj = principal.getAttributes().get("credential"); - if (credObj != null) { - String clearPass = decryptPassword(credObj.toString()); - if (clearPass != null && !clearPass.isEmpty()) - credentials.setPassword(clearPass); + Credentials ticketCredentials = ticketService.validateTicket(ticket); + if (ticketCredentials != null) { + String username = ticketCredentials.getUsername(); + if (username != null) + credentials.setUsername(username); + String password = ticketCredentials.getPassword(); + if (password != null) + credentials.setPassword(password); } - authenticatedUser.init(username, credentials); + AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); + authenticatedUser.init(credentials.getUsername(), credentials); return authenticatedUser; } } @@ -147,70 +119,4 @@ public class AuthenticationProviderService { } - /** - * Takes an encrypted string representing a password provided by - * the CAS ClearPass service and decrypts it using the private - * key configured for this extension. Returns null if it is - * unable to decrypt the password. - * - * @param encryptedPassword - * A string with the encrypted password provided by the - * CAS service. - * - * @return - * The decrypted password, or null if it is unable to - * decrypt the password. - * - * @throws GuacamoleException - * If unable to get Guacamole configuration data - */ - private final String decryptPassword(String encryptedPassword) - throws GuacamoleException { - - // If we get nothing, we return nothing. - if (encryptedPassword == null || encryptedPassword.isEmpty()) { - logger.warn("No or empty encrypted password, no password will be available."); - return null; - } - - final PrivateKey clearpassKey = confService.getClearpassKey(); - if (clearpassKey == null) { - logger.warn("No private key available to decrypt password."); - return null; - } - - try { - - final Cipher cipher = Cipher.getInstance(clearpassKey.getAlgorithm()); - - if (cipher == null) - throw new GuacamoleServerException("Failed to initialize cipher object with private key."); - - // Initialize the Cipher in decrypt mode. - cipher.init(Cipher.DECRYPT_MODE, clearpassKey); - - // Decode and decrypt, and return a new string. - final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); - final byte[] cipherData = cipher.doFinal(pass64); - return new String(cipherData); - - } - catch (BadPaddingException e) { - throw new GuacamoleServerException("Bad padding when decrypting cipher data.", e); - } - catch (IllegalBlockSizeException e) { - throw new GuacamoleServerException("Illegal block size while opening private key.", e); - } - catch (InvalidKeyException e) { - throw new GuacamoleServerException("Specified private key for ClearPass decryption is invalid.", e); - } - catch (NoSuchAlgorithmException e) { - throw new GuacamoleServerException("Unexpected algorithm for the private key.", e); - } - catch (NoSuchPaddingException e) { - throw new GuacamoleServerException("No such padding tryingto initialize cipher with private key.", e); - } - - } - } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index 96da6ff44..0162801b1 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -20,14 +20,24 @@ package org.apache.guacamole.auth.cas.ticket; import com.google.inject.Inject; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.xml.bind.DatatypeConverter; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.auth.cas.conf.ConfigurationService; +import org.apache.guacamole.net.auth.Credentials; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.validation.Assertion; import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; import org.jasig.cas.client.validation.TicketValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Service for validating ID tickets forwarded to us by the client, verifying @@ -35,6 +45,11 @@ import org.jasig.cas.client.validation.TicketValidationException; */ public class TicketValidationService { + /** + * Logger for this class. + */ + private static final Logger logger = LoggerFactory.getLogger(TicketValidationService.class); + /** * Service for retrieving CAS configuration information. */ @@ -42,7 +57,7 @@ public class TicketValidationService { private ConfigurationService confService; /** - * Validates and parses the given ID ticket, returning the AttributePrincipal + * Validates and parses the given ID ticket, returning the Credentials object * derived from the parameters provided by the CAS server in the ticket. If the * ticket is invalid an exception is thrown. * @@ -50,13 +65,13 @@ public class TicketValidationService { * The ID ticket to validate and parse. * * @return - * The AttributePrincipal derived from parameters provided in the ticket. + * The Credentials object derived from parameters provided in the ticket. * * @throws GuacamoleException * If the ID ticket is not valid or guacamole.properties could * not be parsed. */ - public AttributePrincipal validateTicket(String ticket) throws GuacamoleException { + public Credentials validateTicket(String ticket) throws GuacamoleException { // Retrieve the configured CAS URL, establish a ticket validator, // and then attempt to validate the supplied ticket. If that succeeds, @@ -65,9 +80,26 @@ public class TicketValidationService { Cas20ProxyTicketValidator validator = new Cas20ProxyTicketValidator(casServerUrl); validator.setAcceptAnyProxy(true); try { + Credentials ticketCredentials = new Credentials(); String confRedirectURI = confService.getRedirectURI(); Assertion a = validator.validate(ticket, confRedirectURI); - return a.getPrincipal(); + AttributePrincipal principal = a.getPrincipal(); + + // Retrieve username and set the credentials. + String username = principal.getName(); + if (username != null) + ticketCredentials.setUsername(username); + + // Retrieve password, attempt decryption, and set credentials. + Object credObj = principal.getAttributes().get("credential"); + if (credObj != null) { + String clearPass = decryptPassword(credObj.toString()); + if (clearPass != null && !clearPass.isEmpty()) + ticketCredentials.setPassword(clearPass); + } + + return ticketCredentials; + } catch (TicketValidationException e) { throw new GuacamoleException("Ticket validation failed.", e); @@ -75,4 +107,70 @@ public class TicketValidationService { } + /** + * Takes an encrypted string representing a password provided by + * the CAS ClearPass service and decrypts it using the private + * key configured for this extension. Returns null if it is + * unable to decrypt the password. + * + * @param encryptedPassword + * A string with the encrypted password provided by the + * CAS service. + * + * @return + * The decrypted password, or null if it is unable to + * decrypt the password. + * + * @throws GuacamoleException + * If unable to get Guacamole configuration data + */ + private final String decryptPassword(String encryptedPassword) + throws GuacamoleException { + + // If we get nothing, we return nothing. + if (encryptedPassword == null || encryptedPassword.isEmpty()) { + logger.warn("No or empty encrypted password, no password will be available."); + return null; + } + + final PrivateKey clearpassKey = confService.getClearpassKey(); + if (clearpassKey == null) { + logger.warn("No private key available to decrypt password."); + return null; + } + + try { + + final Cipher cipher = Cipher.getInstance(clearpassKey.getAlgorithm()); + + if (cipher == null) + throw new GuacamoleServerException("Failed to initialize cipher object with private key."); + + // Initialize the Cipher in decrypt mode. + cipher.init(Cipher.DECRYPT_MODE, clearpassKey); + + // Decode and decrypt, and return a new string. + final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); + final byte[] cipherData = cipher.doFinal(pass64); + return new String(cipherData); + + } + catch (BadPaddingException e) { + throw new GuacamoleServerException("Bad padding when decrypting cipher data.", e); + } + catch (IllegalBlockSizeException e) { + throw new GuacamoleServerException("Illegal block size while opening private key.", e); + } + catch (InvalidKeyException e) { + throw new GuacamoleServerException("Specified private key for ClearPass decryption is invalid.", e); + } + catch (NoSuchAlgorithmException e) { + throw new GuacamoleServerException("Unexpected algorithm for the private key.", e); + } + catch (NoSuchPaddingException e) { + throw new GuacamoleServerException("No such padding tryingto initialize cipher with private key.", e); + } + + } + } From b410b99d49c4fd259c3af60a336808a59fec7ba5 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sat, 30 Sep 2017 20:55:44 -0400 Subject: [PATCH 14/23] GUACAMOLE-362: Refactor ticket validation handling of credentials. --- .../auth/cas/AuthenticationProviderService.java | 16 +++++----------- .../auth/cas/ticket/TicketValidationService.java | 15 +++++++-------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java index 325268e5c..a9b3230cc 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/AuthenticationProviderService.java @@ -87,18 +87,12 @@ public class AuthenticationProviderService { if (request != null) { String ticket = request.getParameter(CASTicketField.PARAMETER_NAME); if (ticket != null) { - Credentials ticketCredentials = ticketService.validateTicket(ticket); - if (ticketCredentials != null) { - String username = ticketCredentials.getUsername(); - if (username != null) - credentials.setUsername(username); - String password = ticketCredentials.getPassword(); - if (password != null) - credentials.setPassword(password); + String username = ticketService.validateTicket(ticket, credentials); + if (username != null) { + AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); + authenticatedUser.init(username, credentials); + return authenticatedUser; } - AuthenticatedUser authenticatedUser = authenticatedUserProvider.get(); - authenticatedUser.init(credentials.getUsername(), credentials); - return authenticatedUser; } } diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index 0162801b1..de389643b 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -57,21 +57,21 @@ public class TicketValidationService { private ConfigurationService confService; /** - * Validates and parses the given ID ticket, returning the Credentials object - * derived from the parameters provided by the CAS server in the ticket. If the + * Validates and parses the given ID ticket, returning the username + * provided by the CAS server in the ticket. If the * ticket is invalid an exception is thrown. * * @param ticket * The ID ticket to validate and parse. * * @return - * The Credentials object derived from parameters provided in the ticket. + * The username derived from the ticket. * * @throws GuacamoleException * If the ID ticket is not valid or guacamole.properties could * not be parsed. */ - public Credentials validateTicket(String ticket) throws GuacamoleException { + public String validateTicket(String ticket, Credentials credentials) throws GuacamoleException { // Retrieve the configured CAS URL, establish a ticket validator, // and then attempt to validate the supplied ticket. If that succeeds, @@ -80,7 +80,6 @@ public class TicketValidationService { Cas20ProxyTicketValidator validator = new Cas20ProxyTicketValidator(casServerUrl); validator.setAcceptAnyProxy(true); try { - Credentials ticketCredentials = new Credentials(); String confRedirectURI = confService.getRedirectURI(); Assertion a = validator.validate(ticket, confRedirectURI); AttributePrincipal principal = a.getPrincipal(); @@ -88,17 +87,17 @@ public class TicketValidationService { // Retrieve username and set the credentials. String username = principal.getName(); if (username != null) - ticketCredentials.setUsername(username); + credentials.setUsername(username); // Retrieve password, attempt decryption, and set credentials. Object credObj = principal.getAttributes().get("credential"); if (credObj != null) { String clearPass = decryptPassword(credObj.toString()); if (clearPass != null && !clearPass.isEmpty()) - ticketCredentials.setPassword(clearPass); + credentials.setPassword(clearPass); } - return ticketCredentials; + return username; } catch (TicketValidationException e) { From bd57938dff460ca0f90d9bd8258c4dbc0bb2966e Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sat, 30 Sep 2017 22:30:46 -0400 Subject: [PATCH 15/23] GUACAMOLE-362: Add missing parameter documentation for credentials object. --- .../guacamole/auth/cas/ticket/TicketValidationService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index de389643b..482c73af3 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -63,6 +63,9 @@ public class TicketValidationService { * * @param ticket * The ID ticket to validate and parse. + * @param credentials + * The Credentials object to store retrieved username and + * password values in. * * @return * The username derived from the ticket. From ab41f441003bb5524509fc22aeb130184dab271f Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sun, 1 Oct 2017 07:06:20 -0400 Subject: [PATCH 16/23] GUACAMOLE-362: Fix style and error messages. --- .../guacamole/auth/cas/ticket/TicketValidationService.java | 1 + .../guacamole/properties/PrivateKeyGuacamoleProperty.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index 482c73af3..009f9555d 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -63,6 +63,7 @@ public class TicketValidationService { * * @param ticket * The ID ticket to validate and parse. + * * @param credentials * The Credentials object to store retrieved username and * password values in. diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java index 570b776a7..174f18399 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java @@ -70,10 +70,10 @@ public abstract class PrivateKeyGuacamoleProperty implements GuacamoleProperty

Date: Sun, 1 Oct 2017 07:15:19 -0400 Subject: [PATCH 17/23] GUACAMOLE-362: Deal correctly with return value when reading the key. --- .../PrivateKeyGuacamoleProperty.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java index 174f18399..c360f4b9e 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java @@ -53,8 +53,22 @@ public abstract class PrivateKeyGuacamoleProperty implements GuacamoleProperty

Date: Sun, 1 Oct 2017 08:17:10 -0400 Subject: [PATCH 18/23] GUACAMOLE-362: Set encoding of ticket validator to UTF-8. --- .../guacamole/auth/cas/ticket/TicketValidationService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index 009f9555d..515deed07 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -27,6 +27,7 @@ import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; +import java.nio.charset.Charset; import javax.xml.bind.DatatypeConverter; import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleServerException; @@ -83,6 +84,7 @@ public class TicketValidationService { String casServerUrl = confService.getAuthorizationEndpoint(); Cas20ProxyTicketValidator validator = new Cas20ProxyTicketValidator(casServerUrl); validator.setAcceptAnyProxy(true); + validator.setEncoding("UTF-8"); try { String confRedirectURI = confService.getRedirectURI(); Assertion a = validator.validate(ticket, confRedirectURI); @@ -155,7 +157,7 @@ public class TicketValidationService { // Decode and decrypt, and return a new string. final byte[] pass64 = DatatypeConverter.parseBase64Binary(encryptedPassword); final byte[] cipherData = cipher.doFinal(pass64); - return new String(cipherData); + return new String(cipherData, Charset.forName("UTF-8")); } catch (BadPaddingException e) { From a196134f2dd266fc3b8fac7b781eef05b016a209 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Sun, 1 Oct 2017 15:39:32 -0400 Subject: [PATCH 19/23] GUACAMOLE-362: Change warn to debug for missing private key. --- .../guacamole/auth/cas/ticket/TicketValidationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index 515deed07..f64481dda 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -140,7 +140,7 @@ public class TicketValidationService { final PrivateKey clearpassKey = confService.getClearpassKey(); if (clearpassKey == null) { - logger.warn("No private key available to decrypt password."); + logger.debug("No private key available to decrypt password."); return null; } From 61f70c57be2fab5a815aabb0ea5688f63fbda6e4 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Tue, 10 Oct 2017 21:49:02 -0400 Subject: [PATCH 20/23] GUACAMOLE-362: Fix error message typo. --- .../guacamole/auth/cas/ticket/TicketValidationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java index f64481dda..122059c6d 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/ticket/TicketValidationService.java @@ -173,7 +173,7 @@ public class TicketValidationService { throw new GuacamoleServerException("Unexpected algorithm for the private key.", e); } catch (NoSuchPaddingException e) { - throw new GuacamoleServerException("No such padding tryingto initialize cipher with private key.", e); + throw new GuacamoleServerException("No such padding trying to initialize cipher with private key.", e); } } From c92d2e35986730d859f36015169b98127fefba60 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Tue, 10 Oct 2017 22:29:38 -0400 Subject: [PATCH 21/23] GUACAMOLE-362: Loop through reading bytes from key file. --- .../properties/PrivateKeyGuacamoleProperty.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java index c360f4b9e..68070f5e7 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java +++ b/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java @@ -55,18 +55,19 @@ public abstract class PrivateKeyGuacamoleProperty implements GuacamoleProperty

= 0; + keyRead = keyInput.read(keyBytes, totalBytesRead, (keyBytes.length - totalBytesRead))) { + totalBytesRead += keyRead; + } // Zero-sized key - else if(keyRead == 0) + if (totalBytesRead == 0) throw new GuacamoleServerException("Failed to ready key because key is empty."); // Fewer bytes read than contained in the key - else if (keyRead < keyLength) + else if (totalBytesRead < keyLength) throw new GuacamoleServerException("Unable to read the full length of the key."); keyInput.close(); From b968e073c26d2bcfeeb6ce8a813f201136e1cad0 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Fri, 27 Oct 2017 12:57:15 -0400 Subject: [PATCH 22/23] GUACAMOLE-362: Move PrivateKeyGuacamoleProperty into CAS extension and use ByteArrayOutputStream for reading thefile. --- .../auth/cas/conf/CASGuacamoleProperties.java | 1 - .../conf}/PrivateKeyGuacamoleProperty.java | 32 ++++++++----------- 2 files changed, 13 insertions(+), 20 deletions(-) rename {guacamole-ext/src/main/java/org/apache/guacamole/properties => extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf}/PrivateKeyGuacamoleProperty.java (74%) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java index aa4a06ef4..dd741a3f8 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/CASGuacamoleProperties.java @@ -19,7 +19,6 @@ package org.apache.guacamole.auth.cas.conf; -import org.apache.guacamole.properties.PrivateKeyGuacamoleProperty; import org.apache.guacamole.properties.StringGuacamoleProperty; /** diff --git a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/PrivateKeyGuacamoleProperty.java similarity index 74% rename from guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java rename to extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/PrivateKeyGuacamoleProperty.java index 68070f5e7..caa84cc33 100644 --- a/guacamole-ext/src/main/java/org/apache/guacamole/properties/PrivateKeyGuacamoleProperty.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/PrivateKeyGuacamoleProperty.java @@ -17,9 +17,9 @@ * under the License. */ -package org.apache.guacamole.properties; +package org.apache.guacamole.auth.cas.conf; -import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -33,6 +33,7 @@ import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; +import org.apache.guacamole.properties.GuacamoleProperty; import org.apache.guacamole.GuacamoleServerException; import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.LocalEnvironment; @@ -52,25 +53,18 @@ public abstract class PrivateKeyGuacamoleProperty implements GuacamoleProperty

= 0; - keyRead = keyInput.read(keyBytes, totalBytesRead, (keyBytes.length - totalBytesRead))) { - totalBytesRead += keyRead; + FileInputStream keyStreamIn = new FileInputStream(keyFile); + ByteArrayOutputStream keyStreamOut = new ByteArrayOutputStream(); + byte[] keyBuffer = new byte[1024]; + try { + for (int readBytes; (readBytes = keyStreamIn.read(keyBuffer)) != -1;) + keyStreamOut.write(keyBuffer, 0, readBytes); + } + catch (IOException e) { + throw new GuacamoleServerException("IOException while trying to read bytes from file.", e); } - // Zero-sized key - if (totalBytesRead == 0) - throw new GuacamoleServerException("Failed to ready key because key is empty."); - - // Fewer bytes read than contained in the key - else if (totalBytesRead < keyLength) - throw new GuacamoleServerException("Unable to read the full length of the key."); - - keyInput.close(); + final byte[] keyBytes = keyStreamOut.toByteArray(); // Set up decryption infrastructure KeyFactory keyFactory = KeyFactory.getInstance("RSA"); From ad8820cf054721eda95d72fc06a42356501c6ca2 Mon Sep 17 00:00:00 2001 From: Nick Couchman Date: Fri, 27 Oct 2017 13:45:25 -0400 Subject: [PATCH 23/23] GUACAMOLE-362: Remove unnecessary IOException catch. --- .../auth/cas/conf/PrivateKeyGuacamoleProperty.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/PrivateKeyGuacamoleProperty.java b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/PrivateKeyGuacamoleProperty.java index caa84cc33..bd0bd6956 100644 --- a/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/PrivateKeyGuacamoleProperty.java +++ b/extensions/guacamole-auth-cas/src/main/java/org/apache/guacamole/auth/cas/conf/PrivateKeyGuacamoleProperty.java @@ -56,13 +56,9 @@ public abstract class PrivateKeyGuacamoleProperty implements GuacamoleProperty