GUACAMOLE-96: Verify TOTP of all users against hard-coded key. Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/19e03a16 Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/19e03a16 Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/19e03a16
Branch: refs/heads/master Commit: 19e03a1632eee39508378a434f3362b9e9f9a3f8 Parents: b55e561 Author: Michael Jumper <mjum...@apache.org> Authored: Mon Nov 20 00:57:37 2017 -0800 Committer: Michael Jumper <mjum...@apache.org> Committed: Sun Feb 4 19:45:17 2018 -0800 ---------------------------------------------------------------------- .../auth/totp/UserVerificationService.java | 75 +++++++++++++++++--- 1 file changed, 66 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/19e03a16/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java ---------------------------------------------------------------------- diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java index f28149a..7cffffe 100644 --- a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java +++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/auth/totp/UserVerificationService.java @@ -19,6 +19,8 @@ package org.apache.guacamole.auth.totp; +import com.google.common.io.BaseEncoding; +import java.security.InvalidKeyException; import java.util.Collections; import javax.servlet.http.HttpServletRequest; import org.apache.guacamole.GuacamoleClientException; @@ -30,6 +32,9 @@ import org.apache.guacamole.net.auth.Credentials; import org.apache.guacamole.net.auth.UserContext; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; +import org.apache.guacamole.totp.TOTPGenerator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Service for verifying the identity of a user using TOTP. @@ -37,6 +42,11 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredential public class UserVerificationService { /** + * Logger for this class. + */ + private final Logger logger = LoggerFactory.getLogger(UserVerificationService.class); + + /** * The name of the HTTP parameter which will contain the TOTP code provided * by the user to verify their identity. */ @@ -57,6 +67,30 @@ public class UserVerificationService { ); /** + * BaseEncoding instance which decoded/encodes base32. + */ + private static final BaseEncoding BASE32 = BaseEncoding.base32(); + + /** + * Retrieves the base32-encoded TOTP key associated with user having the + * given UserContext. If no TOTP key is associated with the user, null is + * returned. + * + * @param context + * The UserContext of the user whose TOTP key should be retrieved. + * + * @return + * The base32-encoded TOTP key associated with user having the given + * UserContext, or null if no TOTP key is associated with the user. + */ + public String getKey(UserContext context){ + + // FIXME: Hard-coded key + return "JBSWY3DPEHPK3PXP"; + + } + + /** * Verifies the identity of the given user using TOTP. If a authentication * code from the user's TOTP device has not already been provided, a code is * requested in the form of additional expected credentials. Any provided @@ -77,25 +111,48 @@ public class UserVerificationService { public void verifyIdentity(UserContext context, AuthenticatedUser authenticatedUser) throws GuacamoleException { + // Ignore anonymous users + String username = authenticatedUser.getIdentifier(); + if (username.equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER)) + return; + + // Ignore users which do not have an associated key + String encodedKey = getKey(context); + if (encodedKey == null) + return; + // Pull the original HTTP request used to authenticate Credentials credentials = authenticatedUser.getCredentials(); HttpServletRequest request = credentials.getRequest(); - // Ignore anonymous users - if (authenticatedUser.getIdentifier().equals(AuthenticatedUser.ANONYMOUS_IDENTIFIER)) - return; - // Retrieve TOTP from request - String totp = request.getParameter(TOTP_PARAMETER_NAME); + String code = request.getParameter(TOTP_PARAMETER_NAME); // If no TOTP provided, request one - if (totp == null) + if (code == null) throw new GuacamoleInsufficientCredentialsException( "LOGIN.INFO_TOTP_REQUIRED", TOTP_CREDENTIALS); - // FIXME: Hard-coded code - if (!totp.equals("123456")) - throw new GuacamoleClientException("LOGIN.INFO_TOTP_VERIFICATION_FAILED"); + try { + + // Verify provided TOTP against value produced by generator + byte[] key = BASE32.decode(encodedKey); + TOTPGenerator totp = new TOTPGenerator(key, TOTPGenerator.Mode.SHA1, 6); + if (code.equals(totp.generate())) + return; + + } + catch (InvalidKeyException e) { + logger.warn("User \"{}\" is associated with an invalid TOTP key.", username); + logger.debug("TOTP key is not valid.", e); + } + catch (IllegalArgumentException e) { + logger.warn("TOTP key of user \"{}\" is not valid base32.", username); + logger.debug("TOTP key is not valid base32.", e); + } + + // Provided code is not valid + throw new GuacamoleClientException("LOGIN.INFO_TOTP_VERIFICATION_FAILED"); }