[jboss-cvs] JBossAS SVN: r114613 - in projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src: main/java/org/jboss/security/auth/spi and 5 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Thu Dec 19 07:38:58 EST 2013


Author: pskopek
Date: 2013-12-19 07:38:58 -0500 (Thu, 19 Dec 2013)
New Revision: 114613

Modified:
   projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/Util.java
   projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java
   projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/mapping/providers/attribute/LdapAttributeMappingProvider.java
   projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/plugins/vault/PicketBoxSecurityVault.java
   projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java
   projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java
   projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/resources/bin/askpass.sh
Log:
[SECURITY-774] Enable white-space in parameters for external password command

Modified: projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/Util.java
===================================================================
--- projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/Util.java	2013-12-19 11:41:15 UTC (rev 114612)
+++ projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/Util.java	2013-12-19 12:38:58 UTC (rev 114613)
@@ -37,7 +37,7 @@
 
 /**
  * Util.
- * 
+ *
  * @author Scott.Stark at jboss.org
  * @author <a href="adrian at jboss.com">Adrian Brock</a>
  * @version $Revision: 1.1 $
@@ -46,7 +46,7 @@
 {
    private static Logger log = Logger.getLogger(Util.class);
    private static PasswordCache externalPasswordCache;
-   
+
    /**
     * Execute a password load command to obtain the char[] contents of a
     * password.
@@ -58,7 +58,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.
@@ -90,7 +94,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 {
@@ -108,14 +112,14 @@
             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 {
@@ -125,6 +129,17 @@
       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 new IllegalArgumentException("Invalid password command type: " + passwordCmdType);
+   }
+
    /**
     * Execute a Runtime command to load a password.
     * @param passwordCmd
@@ -132,7 +147,7 @@
     * @throws Exception
     */
    private static char[] execPasswordCmd(String passwordCmd)
-      throws Exception
+         throws Exception
    {
       log.debug("Executing command: "+passwordCmd);
       String password = execCmd(passwordCmd);
@@ -140,7 +155,7 @@
    }
 
    private static char[] invokePasswordClass(String passwordCmd)
-      throws Exception
+         throws Exception
    {
       char[] password = null;
 
@@ -219,24 +234,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
+   {
+      log.debug("Executing command: "+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;
             }
@@ -249,7 +287,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);
@@ -275,6 +313,93 @@
             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();
+            log.debug("Command exited with: "+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));
+   }
+
 }

Modified: projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java
===================================================================
--- projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java	2013-12-19 11:41:15 UTC (rev 114612)
+++ projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/auth/spi/LdapExtLoginModule.java	2013-12-19 12:38:58 UTC (rev 114613)
@@ -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)

Modified: projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/mapping/providers/attribute/LdapAttributeMappingProvider.java
===================================================================
--- projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/mapping/providers/attribute/LdapAttributeMappingProvider.java	2013-12-19 11:41:15 UTC (rev 114612)
+++ projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/mapping/providers/attribute/LdapAttributeMappingProvider.java	2013-12-19 12:38:58 UTC (rev 114613)
@@ -139,7 +139,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));

Modified: projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/plugins/vault/PicketBoxSecurityVault.java
===================================================================
--- projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/plugins/vault/PicketBoxSecurityVault.java	2013-12-19 11:41:15 UTC (rev 114612)
+++ projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/main/java/org/jboss/security/plugins/vault/PicketBoxSecurityVault.java	2013-12-19 12:38:58 UTC (rev 114613)
@@ -70,6 +70,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.
@@ -125,10 +129,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,9 +169,8 @@
       if(password == null)
          throw new SecurityVaultException("Option " + KEYSTORE_PASSWORD + "is null or empty");
       if(password.startsWith(PASS_MASK_PREFIX) == false
-            && password.startsWith(PASS_EXT_PREFIX) == false
-            && password.startsWith(PASS_CLASS_PREFIX) == false)
-         throw new SecurityVaultException("Keystore password should be either masked or prefixed with {EXT} or {CLASS}");
+            && Util.isPasswordCommand(password) == false)
+         throw new SecurityVaultException("Keystore password should be either masked or prefixed with one of {EXT}, {EXTC}, {CMD}, {CMDC}, {CLASS}");
 
       String salt = (String) options.get(SALT);
       if(salt == null)

Modified: projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java
===================================================================
--- projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java	2013-12-19 11:41:15 UTC (rev 114612)
+++ projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/helpers/SecurityUtilUnitTestCase.java	2013-12-19 12:38:58 UTC (rev 114613)
@@ -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;
    }
-   
+
 }

Modified: projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java
===================================================================
--- projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java	2013-12-19 11:41:15 UTC (rev 114612)
+++ projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/java/org/jboss/test/security/vault/SecurityVaultUnitTestCase.java	2013-12-19 12:38:58 UTC (rev 114613)
@@ -23,6 +23,7 @@
 
 import junit.framework.TestCase;
 
+import org.jboss.security.Util;
 import org.jboss.security.plugins.PBEUtils;
 import org.jboss.security.vault.SecurityVault;
 import org.jboss.security.vault.SecurityVaultException;
@@ -218,7 +219,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";
@@ -437,8 +438,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";

Modified: projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/resources/bin/askpass.sh
===================================================================
--- projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/resources/bin/askpass.sh	2013-12-19 11:41:15 UTC (rev 114612)
+++ projects/security/security-jboss-sx/branches/Branch_2_0/jbosssx/src/test/resources/bin/askpass.sh	2013-12-19 12:38:58 UTC (rev 114613)
@@ -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



More information about the jboss-cvs-commits mailing list