/** * VMware Continuent Tungsten Replicator * Copyright (C) 2015 VMware, Inc. All rights reserved. * * Licensed 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. * * Initial developer(s): Ludovic Launer * Contributor(s): */ package com.continuent.tungsten.common.security; import java.text.MessageFormat; import java.util.Collections; import java.util.Random; import javax.management.remote.JMXAuthenticator; import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; import org.apache.log4j.Logger; import com.continuent.tungsten.common.config.cluster.ConfigurationException; import com.continuent.tungsten.common.security.PasswordManager.ClientApplicationType; /** * Custom Authentication Realm * * @author <a href="mailto:ludovic.launer@continuent.com">Ludovic Launer</a> * @version 1.0 */ public class RealmJMXAuthenticator implements JMXAuthenticator { private static final Logger logger = Logger .getLogger(RealmJMXAuthenticator.class); private AuthenticationInfo authenticationInfo = null; private PasswordManager passwordManager = null; private static final String INVALID_CREDENTIALS = "Invalid credentials"; private static final String AUTHENTICATION_PROBLEM = "Error while trying to authenticate"; public RealmJMXAuthenticator(AuthenticationInfo authenticationInfo) throws ConfigurationException { this.authenticationInfo = authenticationInfo; this.passwordManager = new PasswordManager( authenticationInfo.getParentPropertiesFileLocation(), ClientApplicationType.RMI_JMX); // this.passwordProps = SecurityHelper // .loadPasswordsFromAuthenticationInfo(authenticationInfo); } /** * Authenticate {@inheritDoc} * * @see javax.management.remote.JMXAuthenticator#authenticate(java.lang.Object) */ public Subject authenticate(Object credentials) { boolean authenticationOK = false; String[] aCredentials = this.checkCredentials(credentials); // --- Get auth parameters --- String username = (String) aCredentials[0]; String password = (String) aCredentials[1]; // String realm = (String) aCredentials[2]; // --- Perform authentication --- try { // Password file syntax: // username=password // String goodPassword = this.passwordProps.get(username); String goodPassword = this.passwordManager .getClearTextPasswordForUser(username); // this.authenticationInfo.setPassword(goodPassword); // // Decrypt password if needed // goodPassword = this.authenticationInfo.getPassword(); if (goodPassword.equals(password)) authenticationOK = true; } catch (Exception e) { // Throw same exception as authentication not OK : // Do not give any hint on failure reason throw new SecurityException(AUTHENTICATION_PROBLEM); } if (authenticationOK) { return new Subject(true, Collections.singleton(new JMXPrincipal(username)), Collections.EMPTY_SET, Collections.EMPTY_SET); } else { try { // Generate a random number between // security.randomWaitOnFailedLogin.min and // security.randomWaitOnFailedLogin.max int min = authenticationInfo.getMinWaitOnFailedLogin(); int max = authenticationInfo.getMaxWaitOnFailedLogin(); int increment = authenticationInfo .getIncrementStepWaitOnFailedLogin(); int randomNum = SecurityHelper.getRandomInt(min, max, increment); // Sleep a random number of seconds = between min and max logger.info(MessageFormat.format( "Invalid credentials. Sleeping (ms): {0,number,#}", randomNum)); if (randomNum > 0) Thread.sleep(randomNum); } catch (InterruptedException e) { logger.error(MessageFormat.format("Could not sleep !: {0}", e)); } throw new SecurityException(INVALID_CREDENTIALS); } } /** * Check credentials are OK * * @param credentials * @return String[] containing {username, password, realm} */ private String[] checkCredentials(Object credentials) { // Verify that credentials is of type String[]. if (!(credentials instanceof String[])) { // Special case for null so we get a more informative message if (credentials == null) { throw new SecurityException("Credentials required"); } throw new SecurityException("Credentials should be String[]"); } // Verify that the array contains three elements // (username/password/realm). final String[] aCredentials = (String[]) credentials; // if (aCredentials.length != 3) // { // throw new SecurityException("Credentials should have 3 elements"); // } return aCredentials; } /** * Returns the authenticationInfo value. * * @return Returns the authenticationInfo. */ public AuthenticationInfo getAuthenticationInfo() { return authenticationInfo; } /** * Sets the authenticationInfo value. * * @param authenticationInfo The authenticationInfo to set. */ public void setAuthenticationInfo(AuthenticationInfo authenticationInfo) { this.authenticationInfo = authenticationInfo; } }