GUACAMOLE-96: Block external access to TOTP-internal attributes.

This commit is contained in:
Michael Jumper
2017-11-20 16:15:01 -08:00
parent 2a894c487c
commit 96e3d02999
7 changed files with 180 additions and 22 deletions

View File

@@ -19,9 +19,11 @@
package org.apache.guacamole.auth.totp;
import org.apache.guacamole.auth.totp.user.UserVerificationService;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.totp.user.TOTPUserContext;
import org.apache.guacamole.net.auth.AuthenticatedUser;
import org.apache.guacamole.net.auth.AuthenticationProvider;
import org.apache.guacamole.net.auth.Credentials;
@@ -104,7 +106,7 @@ public class TOTPAuthenticationProvider implements AuthenticationProvider {
// User has been verified, and authentication should be allowed to
// continue
return context;
return new TOTPUserContext(context);
}
@@ -112,7 +114,7 @@ public class TOTPAuthenticationProvider implements AuthenticationProvider {
public UserContext redecorate(UserContext decorated, UserContext context,
AuthenticatedUser authenticatedUser, Credentials credentials)
throws GuacamoleException {
return context;
return new TOTPUserContext(context);
}
@Override

View File

@@ -19,6 +19,7 @@
package org.apache.guacamole.auth.totp;
import org.apache.guacamole.auth.totp.user.UserVerificationService;
import com.google.inject.AbstractModule;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.totp.conf.ConfigurationService;

View File

@@ -32,7 +32,7 @@ import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import javax.xml.bind.DatatypeConverter;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.totp.UserTOTPKey;
import org.apache.guacamole.auth.totp.user.UserTOTPKey;
import org.apache.guacamole.auth.totp.conf.ConfigurationService;
import org.apache.guacamole.form.Field;
import org.codehaus.jackson.annotate.JsonProperty;

View File

@@ -0,0 +1,102 @@
/*
* 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.auth.totp.user;
import java.util.HashMap;
import java.util.Map;
import org.apache.guacamole.net.auth.DelegatingUser;
import org.apache.guacamole.net.auth.User;
/**
* TOTP-specific User implementation which wraps a User from another extension,
* hiding and blocking access to the core attributes used by TOTP.
*/
public class TOTPUser extends DelegatingUser {
/**
* The name of the user attribute which stores the TOTP key.
*/
public static final String TOTP_KEY_SECRET_ATTRIBUTE_NAME = "guac-totp-key-secret";
/**
* The name of the user attribute defines whether the TOTP key has been
* confirmed by the user, and the user is thus fully enrolled.
*/
public static final String TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME = "guac-totp-key-confirmed";
/**
* The User object wrapped by this TOTPUser.
*/
private final User undecorated;
/**
* Wraps the given User object, hiding and blocking access to the core
* attributes used by TOTP.
*
* @param user
* The User object to wrap.
*/
public TOTPUser(User user) {
super(user);
this.undecorated = user;
}
/**
* Returns the User object wrapped by this TOTPUser.
*
* @return
* The wrapped User object.
*/
public User getUndecorated() {
return undecorated;
}
@Override
public Map<String, String> getAttributes() {
// Create independent, mutable copy of attributes
Map<String, String> attributes =
new HashMap<String, String>(super.getAttributes());
// Do not expose any TOTP-related attributes outside this extension
attributes.remove(TOTP_KEY_SECRET_ATTRIBUTE_NAME);
attributes.remove(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME);
// Expose only non-TOTP attributes
return attributes;
}
@Override
public void setAttributes(Map<String, String> attributes) {
// Create independent, mutable copy of attributes
attributes = new HashMap<String, String>(attributes);
// Do not expose any TOTP-related attributes outside this extension
attributes.remove(TOTP_KEY_SECRET_ATTRIBUTE_NAME);
attributes.remove(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME);
// Set only non-TOTP attributes
super.setAttributes(attributes);
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.auth.totp.user;
import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.net.auth.DecoratingDirectory;
import org.apache.guacamole.net.auth.DelegatingUserContext;
import org.apache.guacamole.net.auth.Directory;
import org.apache.guacamole.net.auth.User;
import org.apache.guacamole.net.auth.UserContext;
/**
* TOTP-specific UserContext implementation which wraps the UserContext of
* some other extension, providing (or hiding) additional data.
*/
public class TOTPUserContext extends DelegatingUserContext {
/**
* Creates a new TOTPUserContext which wraps the given UserContext,
* providing (or hiding) additional TOTP-specific data.
*
* @param userContext
* The UserContext to wrap.
*/
public TOTPUserContext(UserContext userContext) {
super(userContext);
}
@Override
public Directory<User> getUserDirectory() throws GuacamoleException {
return new DecoratingDirectory<User>(super.getUserDirectory()) {
@Override
protected User decorate(User object) {
return new TOTPUser(object);
}
@Override
protected User undecorate(User object) {
assert(object instanceof TOTPUser);
return ((TOTPUser) object).getUndecorated();
}
};
}
}

View File

@@ -17,7 +17,7 @@
* under the License.
*/
package org.apache.guacamole.auth.totp;
package org.apache.guacamole.auth.totp.user;
import java.security.SecureRandom;
import java.util.Random;

View File

@@ -17,7 +17,7 @@
* under the License.
*/
package org.apache.guacamole.auth.totp;
package org.apache.guacamole.auth.totp.user;
import com.google.common.io.BaseEncoding;
import com.google.inject.Inject;
@@ -53,17 +53,6 @@ public class UserVerificationService {
*/
private final Logger logger = LoggerFactory.getLogger(UserVerificationService.class);
/**
* The name of the user attribute which stores the TOTP key.
*/
private static final String TOTP_KEY_SECRET_ATTRIBUTE_NAME = "guac-totp-key-secret";
/**
* The name of the user attribute defines whether the TOTP key has been
* confirmed by the user, and the user is thus fully enrolled.
*/
private static final String TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME = "guac-totp-key-confirmed";
/**
* BaseEncoding instance which decoded/encodes base32.
*/
@@ -111,7 +100,7 @@ public class UserVerificationService {
Map<String, String> attributes = context.self().getAttributes();
// If no key is defined, attempt to generate a new key
String secret = attributes.get(TOTP_KEY_SECRET_ATTRIBUTE_NAME);
String secret = attributes.get(TOTPUser.TOTP_KEY_SECRET_ATTRIBUTE_NAME);
if (secret == null) {
// Generate random key for user
@@ -140,7 +129,7 @@ public class UserVerificationService {
}
// Otherwise, parse value from attributes
boolean confirmed = "true".equals(attributes.get(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME));
boolean confirmed = "true".equals(attributes.get(TOTPUser.TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME));
return new UserTOTPKey(username, key, confirmed);
}
@@ -173,14 +162,14 @@ public class UserVerificationService {
Map<String, String> attributes = new HashMap<String, String>();
// Set/overwrite current TOTP key state
attributes.put(TOTP_KEY_SECRET_ATTRIBUTE_NAME, BASE32.encode(key.getSecret()));
attributes.put(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME, key.isConfirmed() ? "true" : "false");
attributes.put(TOTPUser.TOTP_KEY_SECRET_ATTRIBUTE_NAME, BASE32.encode(key.getSecret()));
attributes.put(TOTPUser.TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME, key.isConfirmed() ? "true" : "false");
self.setAttributes(attributes);
// Confirm that attributes have actually been set
Map<String, String> setAttributes = self.getAttributes();
if (!setAttributes.containsKey(TOTP_KEY_SECRET_ATTRIBUTE_NAME)
|| !setAttributes.containsKey(TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME))
if (!setAttributes.containsKey(TOTPUser.TOTP_KEY_SECRET_ATTRIBUTE_NAME)
|| !setAttributes.containsKey(TOTPUser.TOTP_KEY_CONFIRMED_ATTRIBUTE_NAME))
return false;
// Update user object