org.jboss.security.Util Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.security;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoader;
/**
* Util.
*
* @author [email protected]
* @author Adrian Brock
* @version $Revision: 1.1 $
*/
public class Util
{
private static PasswordCache externalPasswordCache;
/**
* Execute a password load command to obtain the char[] contents of a
* password.
* @param passwordCmd - A command to execute to obtain the plaintext
* password. The format is one of:
* '{EXT}...' where the '...' is the exact command
* '{EXTC[:expiration_in_millis]}...' where the '...' is the exact command
* line that will be passed to the Runtime.exec(String) method to execute a
* platform command. The first line of the command output is used as the
* password.
* EXTC variant will cache the passwords for expiration_in_millis milliseconds.
* Default cache expiration is 0 = infinity.
* '{CMD}...' or '{CMDC[:expiration_in_millis]}...' for a general command to execute. The general
* command is a string delimited by ',' where the first part is the actual
* command and further parts represents its parameters. The comma can be
* backslashed in order to keep it as a part of the parameter.
* '{CLASS[@jboss_module_spec]}classname[:ctorargs]' where the '[:ctorargs]' is an optional
* string delimited by the ':' from the classname that will be passed to the
* classname ctor. The ctorargs itself is a comma delimited list of strings.
* The jboss_module is JBoss Modules module identifier to load the CLASS from.
* The password is obtained from classname by invoking a
* 'char[] toCharArray()' method if found, otherwise, the 'String toString()'
* method is used.
* @return the password characters
* @throws Exception
*/
public static char[] loadPassword(String passwordCmd)
throws Exception
{
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission(Util.class.getName() + ".loadPassword"));
}
char[] password = null;
String passwordCmdType = null;
// Look for a {...} prefix indicating a password command
if( passwordCmd.charAt(0) == '{' )
{
StringTokenizer tokenizer = new StringTokenizer(passwordCmd, "{}");
passwordCmdType = tokenizer.nextToken();
passwordCmd = tokenizer.nextToken();
}
else
{
// Its just the password string
password = passwordCmd.toCharArray();
}
if( password == null )
{
// Load the password
if (passwordCmdType.startsWith("EXTC") || passwordCmdType.startsWith("CMDC")) {
long timeOut = 0;
if (passwordCmdType.indexOf(':') > -1) {
try {
String[] token = passwordCmdType.split(":");
timeOut = Long.parseLong(token[1]);
} catch (Throwable e) {
// ignore
PicketBoxLogger.LOGGER.errorParsingTimeoutNumber();
}
}
if (externalPasswordCache == null) {
externalPasswordCache = ExternalPasswordCache
.getExternalPasswordCacheInstance();
}
if (externalPasswordCache.contains(passwordCmd, timeOut)) {
password = externalPasswordCache.getPassword(passwordCmd);
} else {
password = switchCommandExecution(passwordCmdType, passwordCmd);
if (password != null) {
externalPasswordCache.storePassword(passwordCmd, password);
}
}
} else if (passwordCmdType.startsWith("EXT") || passwordCmdType.startsWith("CMD")) {
// non-cached variant
password = switchCommandExecution(passwordCmdType, passwordCmd);
} else if (passwordCmdType.startsWith("CLASS")) {
String module = null;
if (passwordCmdType.indexOf('@') > -1) {
module = passwordCmdType.split("@")[1];
}
password = invokePasswordClass(passwordCmd, module);
} else {
throw PicketBoxMessages.MESSAGES.invalidPasswordCommandType(passwordCmdType);
}
}
return password;
}
private static char[] switchCommandExecution(String passwordCmdType, String passwordCmd)
throws Exception
{
if (passwordCmdType.startsWith("EXT"))
return execPasswordCmd(passwordCmd);
else if (passwordCmdType.startsWith("CMD"))
return execPBBasedPasswordCommand(passwordCmd);
else
throw PicketBoxMessages.MESSAGES.invalidPasswordCommandType(passwordCmdType);
}
/**
* Execute a Runtime command to load a password.
* @param passwordCmd
* @return
* @throws Exception
*/
private static char[] execPasswordCmd(String passwordCmd)
throws Exception
{
PicketBoxLogger.LOGGER.traceBeginExecPasswordCmd(passwordCmd);
String password = execCmd(passwordCmd);
return password.toCharArray();
}
private static char[] invokePasswordClass(String passwordCmd, String moduleSpec)
throws Exception
{
char[] password = null;
// Check for a ctor argument delimited by ':'
String classname = passwordCmd;
String ctorArgs = null;
int colon = passwordCmd.indexOf(':');
if( colon > 0 )
{
classname = passwordCmd.substring(0, colon);
ctorArgs = passwordCmd.substring(colon+1);
}
Class> c = loadClass(classname, moduleSpec);
Object instance = null;
// Check for a ctor(String,...) if ctorArg is not null
if( ctorArgs != null )
{
Object[] args = ctorArgs.split(",");
Class>[] sig = new Class[args.length];
ArrayList> sigl = new ArrayList>();
for(int n = 0; n < args.length; n ++)
sigl.add(String.class);
sigl.toArray(sig);
Constructor> ctor = c.getConstructor(sig);
instance = ctor.newInstance(args);
}
else
{
// Use the default ctor
instance = c.newInstance();
}
// Look for a toCharArray() method
try
{
Class>[] sig = {};
Method toCharArray = c.getMethod("toCharArray", sig);
Object[] args = {};
password = (char[]) toCharArray.invoke(instance, args);
}
catch(NoSuchMethodException e)
{
String tmp = instance.toString();
if( tmp != null )
password = tmp.toCharArray();
}
return password;
}
private static Class> loadClass(final String fqn, final String module) {
try {
Class> passwdClass = AccessController.doPrivileged(new PrivilegedExceptionAction>() {
@Override
public Class> run() throws Exception {
if (fqn == null || fqn.isEmpty()) {
throw PicketBoxMessages.MESSAGES.loadingNullorEmptyClass();
} else if (module == null ) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return cl.loadClass(fqn);
} else {
ModuleLoader loader = Module.getCallerModuleLoader();
final Module pwdClassModule = loader.loadModule(ModuleIdentifier.fromString(module));
return pwdClassModule.getClassLoader().loadClass(fqn);
}
}
});
return passwdClass;
} catch (PrivilegedActionException e) {
throw PicketBoxMessages.MESSAGES.unableToLoadPasswordClass(e.getCause(), fqn);
}
}
private static String execCmd(String cmd) throws Exception
{
SecurityManager sm = System.getSecurityManager();
String line;
if( sm != null )
{
line = RuntimeActions.PRIVILEGED.execCmd(cmd);
}
else
{
line = RuntimeActions.NON_PRIVILEGED.execCmd(cmd);
}
return line;
}
/**
* Execute a Runtime command to load a password.
* It uses ProcessBuilder to execute the command.
* @param passwordCmd
* @return the loaded password
* @throws Exception
*/
private static char[] execPBBasedPasswordCommand(String passwordCmd) throws Exception
{
PicketBoxLogger.LOGGER.traceBeginExecPasswordCmd(passwordCmd);
SecurityManager sm = System.getSecurityManager();
String password;
if( sm != null )
{
password = RuntimeActions.PB_BASED_PRIVILEGED.execCmd(passwordCmd);
}
else
{
password = RuntimeActions.PB_BASED_NON_PRIVILEGED.execCmd(passwordCmd);
}
return password.toCharArray();
}
interface RuntimeActions
{
RuntimeActions PRIVILEGED = new RuntimeActions()
{
public String execCmd(final String cmd)
throws Exception
{
try
{
String line = AccessController.doPrivileged(
new PrivilegedExceptionAction()
{
public String run() throws Exception
{
return NON_PRIVILEGED.execCmd(cmd);
}
}
);
return line;
}
catch(PrivilegedActionException e)
{
throw e.getException();
}
}
};
RuntimeActions NON_PRIVILEGED = new RuntimeActions()
{
public String execCmd(final String cmd)
throws Exception
{
Runtime rt = Runtime.getRuntime();
Process p = rt.exec(cmd);
InputStream stdin = null;
String line;
BufferedReader reader = null;
try
{
stdin = p.getInputStream();
reader = new BufferedReader(new InputStreamReader(stdin));
line = reader.readLine();
}
finally
{
if(reader != null)
reader.close();
if(stdin != null)
stdin.close();
}
int exitCode = p.waitFor();
PicketBoxLogger.LOGGER.traceEndExecPasswordCmd(exitCode);
return line;
}
};
RuntimeActions PB_BASED_PRIVILEGED = new RuntimeActions()
{
public String execCmd(final String command)
throws Exception
{
try
{
String password = AccessController.doPrivileged(
new PrivilegedExceptionAction()
{
public String run() throws Exception
{
return PB_BASED_NON_PRIVILEGED.execCmd(command);
}
}
);
return password;
}
catch(PrivilegedActionException e)
{
throw e.getException();
}
}
};
RuntimeActions PB_BASED_NON_PRIVILEGED = new RuntimeActions()
{
public String execCmd(final String command) throws Exception
{
final String[] parsedCommand = parseCommand(command);
final ProcessBuilder builder = new ProcessBuilder(parsedCommand);
final Process process = builder.start();
final String line;
BufferedReader reader = null;
try
{
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
line = reader.readLine();
}
finally
{
if (reader != null)
reader.close();
}
int exitCode = process.waitFor();
PicketBoxLogger.LOGGER.traceEndExecPasswordCmd(exitCode);
return line;
}
protected String[] parseCommand(String command)
{
// comma can be backslashed
final String[] parsedCommand = command.split("(?
|| passwordCmd.startsWith("{CMD}")
|| passwordCmd.startsWith("{CMDC") // it has to be without closing brace to cover :