Index: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/Util.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/jboss/security/Util.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/main/java/org/jboss/security/Util.java	(working copy)
@@ -35,7 +35,7 @@
 
 /**
  * Util.
- * 
+ *
  * @author Scott.Stark@jboss.org
  * @author <a href="adrian@jboss.com">Adrian Brock</a>
  * @version $Revision: 1.1 $
@@ -43,7 +43,7 @@
 public class Util
 {
    private static PasswordCache externalPasswordCache;
-   
+
    /**
     * Execute a password load command to obtain the char[] contents of a
     * password.
@@ -55,7 +55,11 @@
     * 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. 
+    * Default cache expiration is 0 = infinity.
+    * '{CMD}...' or '{CMDC}...' 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}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.
@@ -64,13 +68,13 @@
     * method is used.
     * @return the password characters
     * @throws Exception
-    */ 
+    */
    public static char[] loadPassword(String passwordCmd)
-      throws Exception
+         throws Exception
    {
       char[] password = null;
       String passwordCmdType = null;
-      
+
       // Look for a {...} prefix indicating a password command
       if( passwordCmd.charAt(0) == '{' )
       {
@@ -87,7 +91,7 @@
       if( password == null )
       {
          // Load the password
-         if (passwordCmdType.startsWith("EXTC")) {
+         if (passwordCmdType.startsWith("EXTC") || passwordCmdType.startsWith("CMDC")) {
             long timeOut = 0;
             if (passwordCmdType.indexOf(':') > -1) {
                try {
@@ -105,23 +109,34 @@
             if (externalPasswordCache.contains(passwordCmd, timeOut)) {
                password = externalPasswordCache.getPassword(passwordCmd);
             } else {
-               password = execPasswordCmd(passwordCmd);
+               password = switchCommandExecution(passwordCmdType, passwordCmd);
                if (password != null) {
                   externalPasswordCache.storePassword(passwordCmd, password);
                }
             }
-         } else if (passwordCmdType.startsWith("EXT")) {
-            // non-cached EXT variant
-            password = execPasswordCmd(passwordCmd);
+         } else if (passwordCmdType.startsWith("EXT") || passwordCmdType.startsWith("CMD")) {
+            // non-cached variant
+            password = switchCommandExecution(passwordCmdType, passwordCmd);
          } else if (passwordCmdType.equals("CLASS")) {
             password = invokePasswordClass(passwordCmd);
          } 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
@@ -129,7 +144,7 @@
     * @throws Exception
     */
    private static char[] execPasswordCmd(String passwordCmd)
-      throws Exception
+         throws Exception
    {
       PicketBoxLogger.LOGGER.traceBeginExecPasswordCmd(passwordCmd);
       String password = execCmd(passwordCmd);
@@ -137,7 +152,7 @@
    }
 
    private static char[] invokePasswordClass(String passwordCmd)
-      throws Exception
+         throws Exception
    {
       char[] password = null;
 
@@ -212,24 +227,47 @@
       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
+               throws Exception
          {
             try
             {
                String line = AccessController.doPrivileged(
-               new PrivilegedExceptionAction<String>()
-                  {
-                     public String run() throws Exception
+                     new PrivilegedExceptionAction<String>()
                      {
-                        return NON_PRIVILEGED.execCmd(cmd);
+                        public String run() throws Exception
+                        {
+                           return NON_PRIVILEGED.execCmd(cmd);
+                        }
                      }
-                  }
                );
                return line;
             }
@@ -242,7 +280,7 @@
       RuntimeActions NON_PRIVILEGED = new RuntimeActions()
       {
          public String execCmd(final String cmd)
-            throws Exception
+               throws Exception
          {
             Runtime rt = Runtime.getRuntime();
             Process p = rt.exec(cmd);
@@ -253,21 +291,108 @@
             {
                stdin = p.getInputStream();
                reader = new BufferedReader(new InputStreamReader(stdin));
-               line = reader.readLine();   
+               line = reader.readLine();
             }
             finally
             {
                if(reader != null)
                   reader.close();
                if(stdin != null)
-                 stdin.close();   
+                  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<String>()
+                     {
+                        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("(?<!\\\\),");
+            for (int k=0; k < parsedCommand.length; k++)
+            {
+               if (parsedCommand[k].indexOf('\\') != -1)
+                  parsedCommand[k] = parsedCommand[k].replaceAll("\\\\,", ",");
+            }
+            return parsedCommand;
+         }
+      };
       String execCmd(String cmd) throws Exception;
    }
+
+   /**
+    * Checks whether password can be loaded by {@link #loadPassword(String)}.
+    * @param passwordCmd a potential password command
+    * @return true if password can be loaded by {@link #loadPassword(String)}, false otherwise.
+    */
+   public static boolean isPasswordCommand(String passwordCmd)
+   {
+      return (passwordCmd != null)
+            && (passwordCmd.startsWith("{EXT}")
+                  || passwordCmd.startsWith("{EXTC}")
+                  || passwordCmd.startsWith("{CMD}")
+                  || passwordCmd.startsWith("{CMDC}")
+                  || passwordCmd.startsWith("{CLASS}"));
+   }
+
+   /**
+    * Checks whether password can be loaded by {@link #loadPassword(String)}.
+    * @param passwordCmd a potential password command
+    * @return true if password can be loaded by {@link #loadPassword(String)}, false otherwise.
+    */
+   public static boolean isPasswordCommand(char[] passwordCmd)
+   {
+      return (passwordCmd != null) && isPasswordCommand(new String(passwordCmd));
+   }
+
 }
Index: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/LdapCallbackHandler.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/LdapCallbackHandler.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/LdapCallbackHandler.java	(working copy)
@@ -322,7 +322,7 @@
 	protected String getBindCredential()
 	{
 		String bindCredential = options.get(BIND_CREDENTIAL);
-		if (bindCredential.startsWith("{EXT}"))
+		if (org.jboss.security.Util.isPasswordCommand(bindCredential))
 		{
 			try
 			{
Index: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java	(working copy)
@@ -383,7 +383,7 @@
    {
       bindDN = (String) options.get(BIND_DN);
       bindCredential = (String) options.get(BIND_CREDENTIAL);
-      if ((bindCredential != null) && bindCredential.startsWith("{EXT"))
+      if ((bindCredential != null) && Util.isPasswordCommand(bindCredential))
          bindCredential = new String(Util.loadPassword(bindCredential));
       String securityDomain = (String) options.get(SECURITY_DOMAIN_OPT);
       if (securityDomain != null)
Index: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapUsersLoginModule.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapUsersLoginModule.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapUsersLoginModule.java	(working copy)
@@ -140,7 +140,7 @@
       super.initialize(subject, callbackHandler, sharedState, options);
       bindDN = (String) options.get(BIND_DN);
       bindCredential = (String) options.get(BIND_CREDENTIAL);
-      if ((bindCredential != null) && bindCredential.startsWith("{EXT}"))
+      if ((bindCredential != null) && Util.isPasswordCommand(bindCredential))
       {
          try
          {
Index: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/mapping/providers/attribute/LdapAttributeMappingProvider.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/jboss/security/mapping/providers/attribute/LdapAttributeMappingProvider.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/main/java/org/jboss/security/mapping/providers/attribute/LdapAttributeMappingProvider.java	(working copy)
@@ -134,7 +134,7 @@
             return;
          }
          String bindCredential = (String) options.get(BIND_CREDENTIAL);
-         if (bindCredential.startsWith("{EXT}"))
+         if (org.jboss.security.Util.isPasswordCommand(bindCredential))
             try
             {
                bindCredential = new String(org.jboss.security.Util.loadPassword(bindCredential));
Index: security-jboss-sx/jbosssx/src/main/java/org/jboss/security/mapping/providers/role/LdapRolesMappingProvider.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/jboss/security/mapping/providers/role/LdapRolesMappingProvider.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/main/java/org/jboss/security/mapping/providers/role/LdapRolesMappingProvider.java	(working copy)
@@ -107,7 +107,7 @@
          this.options = options;
          bindDN = (String) options.get(BIND_DN);
          bindCredential = (String) options.get(BIND_CREDENTIAL);
-         if ((bindCredential != null) && bindCredential.startsWith("{EXT}"))
+         if ((bindCredential != null) && Util.isPasswordCommand(bindCredential))
          {
             try
             {
Index: security-jboss-sx/jbosssx/src/main/java/org/picketbox/plugins/vault/PicketBoxSecurityVault.java
===================================================================
--- security-jboss-sx/jbosssx/src/main/java/org/picketbox/plugins/vault/PicketBoxSecurityVault.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/main/java/org/picketbox/plugins/vault/PicketBoxSecurityVault.java	(working copy)
@@ -72,6 +72,10 @@
  * password.
  * EXTC variant will cache the passwords for expiration_in_millis milliseconds.
  * Default cache expiration is 0 = infinity.
+ * '{CMD}...' or '{CMDC}...' 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 the part of a parameter.
  * '{CLASS}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.
@@ -127,10 +131,6 @@
    
    public static final String PASS_MASK_PREFIX = "MASK-";
    
-   public static final String PASS_CLASS_PREFIX = "{CLASS}";
-
-   public static final String PASS_EXT_PREFIX = "{EXT";
-
    public static final String PUBLIC_CERT = "PUBLIC_CERT";
    
    public static final String KEY_SIZE = "KEY_SIZE"; 
@@ -169,8 +169,7 @@
       if(password == null)
          throw new SecurityVaultException(PicketBoxMessages.MESSAGES.invalidNullOrEmptyOptionMessage(KEYSTORE_PASSWORD));
       if(password.startsWith(PASS_MASK_PREFIX) == false
-              && password.startsWith(PASS_EXT_PREFIX) == false
-              && password.startsWith(PASS_CLASS_PREFIX) == false)
+            && Util.isPasswordCommand(password) == false)
          throw new SecurityVaultException(PicketBoxMessages.MESSAGES.invalidKeystorePasswordFormatMessage());
 
       String salt = (String) options.get(SALT);
Index: security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java
===================================================================
--- security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java	(working copy)
@@ -172,8 +172,39 @@
       
    }
    
+   /**
+    * Test {CMD}org.jboss.test.security.helpers.ExecPasswordCmd
+    * @throws Exception
+    */
+   public void testCmdPassword() throws Exception
+   {
+      String passwordCmd = buildExtCommand("CMD", ',');
+      log.info("Executing password command:" + passwordCmd);
+      char[] password = Util.loadPassword(passwordCmd);
+      assertTrue("password3", Arrays.equals(password, "password3".toCharArray()));
 
+      String passwordCmdWithParam = passwordCmd + ",Parameter 1";
+      log.info("Executing password command:" + passwordCmdWithParam);
+      password = Util.loadPassword(passwordCmdWithParam);
+      assertTrue("passwordParameter 1", Arrays.equals(password, "passwordParameter 1".toCharArray()));
+
+      passwordCmdWithParam = passwordCmd + ",Parameter\\,1";
+      log.info("Executing password command:" + passwordCmdWithParam);
+      password = Util.loadPassword(passwordCmdWithParam);
+      assertTrue("passwordParameter,1", Arrays.equals(password, "passwordParameter,1".toCharArray()));
+
+      String passwordCmdWithTwoParams = passwordCmd + ",Parameter,1";
+      log.info("Executing password command:" + passwordCmdWithTwoParams);
+      password = Util.loadPassword(passwordCmdWithTwoParams);
+      assertTrue("passwordParameter", Arrays.equals(new String(password).substring(0, "passwordParameter".length()).toCharArray(), "passwordParameter".toCharArray()));
+      assertTrue("passwordParameter", new String(password).substring("passwordParameter".length()).matches("^\\d+$"));
+   }
+
    private String buildExtCommand(String extOption) {
+      return buildExtCommand(extOption, ' ');
+   }
+   
+   private String buildExtCommand(String extOption, char delim) {
       // First check for java.exe or java as the binary
       File java = new File(System.getProperty("java.home"), "/bin/java");
       File javaExe = new File(System.getProperty("java.home"), "/bin/java.exe");
@@ -184,10 +215,10 @@
          jre = javaExe.getAbsolutePath();
       // Build the command to run this jre
       String cmd = jre
-      + " -cp "+System.getProperty("java.class.path")
-      + " org.jboss.test.security.helpers.ExecPasswordCmd";
-      
+      + delim + "-cp" + delim + System.getProperty("java.class.path")
+      + delim + "org.jboss.test.security.helpers.ExecPasswordCmd";
+
       return "{" + extOption +"}"+cmd;
    }
-   
+
 }
Index: security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java
===================================================================
--- security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java	(revision 485)
+++ security-jboss-sx/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java	(working copy)
@@ -21,6 +21,7 @@
  */
 package org.jboss.test.security.vault;
 
+import org.jboss.security.Util;
 import org.jboss.security.plugins.PBEUtils;
 import org.jboss.security.vault.SecurityVault;
 import org.jboss.security.vault.SecurityVaultException;
@@ -223,7 +224,7 @@
       Map<String,Object> options = getVaultOptionsMap(
             "target/vaults/vault2/vault.jks",
             "target/vaults/vault2/vault_data",
-            "vault", "12438567", 50, "{EXT}/bin/sh " + absolutePathToAskPass + " Enter passphrase for askpass test");
+            "vault", "12438567", 50, "{CMD}/bin/sh," + absolutePathToAskPass + ",Enter passphrase for askpass test");
 
       String vaultBlock = "aBlock";
       String attributeName = "anAttribute";
@@ -435,8 +436,7 @@
    
    private String getMaskedPassword(String pwd, String salt, int iterationCount) throws Exception
    {
-      if (pwd.startsWith(PicketBoxSecurityVault.PASS_EXT_PREFIX)
-            || pwd.startsWith(PicketBoxSecurityVault.PASS_CLASS_PREFIX))
+      if (Util.isPasswordCommand(pwd))
          return pwd;
 
       String algo = "PBEwithMD5andDES";
Index: security-jboss-sx/jbosssx/src/test/resources/bin/askpass.sh
===================================================================
--- security-jboss-sx/jbosssx/src/test/resources/bin/askpass.sh	(revision 485)
+++ security-jboss-sx/jbosssx/src/test/resources/bin/askpass.sh	(working copy)
@@ -1,2 +1,7 @@
 #!/bin/sh
-echo vault22
\ No newline at end of file
+
+if [ "$1" = "Enter passphrase for askpass test" ]; then
+    echo vault22
+else
+    echo $1
+fi
Index: security-spi/common/src/main/java/org/jboss/security/PicketBoxMessages.java
===================================================================
--- security-spi/common/src/main/java/org/jboss/security/PicketBoxMessages.java	(revision 485)
+++ security-spi/common/src/main/java/org/jboss/security/PicketBoxMessages.java	(working copy)
@@ -457,6 +457,6 @@
     @Message(id = 140, value = "Unable to get keystore (%s)")
     RuntimeException unableToGetKeyStore(@Cause Throwable throwable, String file);
 
-    @Message(id = 141, value = "Keystore password should be either masked or prefixed with {EXT} or {CLASS}")
+    @Message(id = 141, value = "Keystore password should be either masked or prefixed with one of {EXT}, {EXTC}, {CMD}, {CMDC}, {CLASS}")
     String invalidKeystorePasswordFormatMessage();
 }
\ No newline at end of file
