You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jj...@apache.org on 2017/08/31 05:05:00 UTC

[1/6] cassandra git commit: Better tolerate improperly formatted bcrypt hashes

Repository: cassandra
Updated Branches:
  refs/heads/cassandra-3.0 ded663622 -> 5e7f60f6b
  refs/heads/cassandra-3.11 02e9846f1 -> 4f12c4092
  refs/heads/trunk c84269a39 -> f40198fc6


Better tolerate improperly formatted bcrypt hashes

Patch by Jeff Jirsa; Reviewed by Sam Tunnicliffe for CASSANDRA-13626


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5e7f60f6
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5e7f60f6
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5e7f60f6

Branch: refs/heads/cassandra-3.0
Commit: 5e7f60f6bf5da386076faa08cefb3970a6ba5cc0
Parents: ded6636
Author: Jeff Jirsa <jj...@apple.com>
Authored: Tue Aug 29 15:21:08 2017 -0700
Committer: Jeff Jirsa <jj...@apple.com>
Committed: Wed Aug 30 21:58:26 2017 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cassandra/auth/PasswordAuthenticator.java   | 16 ++++-
 .../auth/PasswordAuthenticatorTest.java         | 64 ++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index aca9e1f..b405fdf 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Better tolerate improperly formatted bcrypt hashes (CASSANDRA-13626) 
  * Fix race condition in read command serialization (CASSANDRA-13363)
  * Enable segement creation before recovering commitlogs (CASSANDRA-13587)
  * Fix AssertionError in short read protection (CASSANDRA-13747)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index ca610d1..58f61f5 100644
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@ -74,6 +74,20 @@ public class PasswordAuthenticator implements IAuthenticator
         return true;
     }
 
+    protected static boolean checkpw(String password, String hash)
+    {
+        try
+        {
+            return BCrypt.checkpw(password, hash);
+        }
+        catch (Exception e)
+        {
+            // Improperly formatted hashes may cause BCrypt.checkpw to throw, so trap any other exception as a failure
+            logger.warn("Error: invalid password hash encountered, rejecting user", e);
+            return false;
+        }
+    }
+
     private AuthenticatedUser authenticate(String username, String password) throws AuthenticationException
     {
         try
@@ -162,7 +176,7 @@ public class PasswordAuthenticator implements IAuthenticator
                                                                                                 Lists.newArrayList(ByteBufferUtil.bytes(username))));
         UntypedResultSet result = UntypedResultSet.create(rows.result);
 
-        if ((result.isEmpty() || !result.one().has(SALTED_HASH)) || !BCrypt.checkpw(password, result.one().getString(SALTED_HASH)))
+        if ((result.isEmpty() || !result.one().has(SALTED_HASH)) || !checkpw(password, result.one().getString(SALTED_HASH)))
             throw new AuthenticationException("Username and/or password are incorrect");
 
         return new AuthenticatedUser(username);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
new file mode 100644
index 0000000..37763d7
--- /dev/null
+++ b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
@@ -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.cassandra.auth;
+
+
+import org.junit.Test;
+
+import static org.apache.cassandra.auth.CassandraRoleManager.*;
+import static org.apache.cassandra.auth.PasswordAuthenticator.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mindrot.jbcrypt.BCrypt.hashpw;
+import static org.mindrot.jbcrypt.BCrypt.gensalt;
+
+public class PasswordAuthenticatorTest
+{
+    @Test
+    public void testCheckpw() throws Exception
+    {
+        // Valid and correct
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(getGensaltLogRounds()))));
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(4))));
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(31))));
+
+        // Valid but incorrect hashes
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect0", gensalt(4))));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect1", gensalt(10))));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect2", gensalt(31))));
+
+        // Invalid hash values, the jBCrypt library implementation
+        // throws an exception which we catch and treat as a failure
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, ""));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "0"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD,
+                            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
+
+        // Format is structurally right, but actually invalid
+        // bad salt version
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$5x$10$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        // invalid number of rounds, multiple salt versions but it's the rounds that are incorrect
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$02$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$02$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$99$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$99$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        // unpadded rounds
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$6$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$6$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[4/6] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.11

Posted by jj...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.11


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/4f12c409
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/4f12c409
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/4f12c409

Branch: refs/heads/trunk
Commit: 4f12c409267eb44cbf596193456c5a67a1775748
Parents: 02e9846 5e7f60f
Author: Jeff Jirsa <jj...@apple.com>
Authored: Wed Aug 30 21:58:44 2017 -0700
Committer: Jeff Jirsa <jj...@apple.com>
Committed: Wed Aug 30 21:59:33 2017 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cassandra/auth/PasswordAuthenticator.java   | 16 ++++-
 .../auth/PasswordAuthenticatorTest.java         | 64 ++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/4f12c409/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index d848eff,b405fdf..af185cf
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,12 -1,7 +1,13 @@@
 -3.0.15
 - * Better tolerate improperly formatted bcrypt hashes (CASSANDRA-13626) 
 +3.11.1
 + * Fix cassandra-stress hang issues when an error during cluster connection happens (CASSANDRA-12938)
 + * Better bootstrap failure message when blocked by (potential) range movement (CASSANDRA-13744)
 + * "ignore" option is ignored in sstableloader (CASSANDRA-13721)
 + * Deadlock in AbstractCommitLogSegmentManager (CASSANDRA-13652)
 + * Duplicate the buffer before passing it to analyser in SASI operation (CASSANDRA-13512)
 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
 +Merged from 3.0:
++ * Better tolerate improperly formatted bcrypt hashes (CASSANDRA-13626)
   * Fix race condition in read command serialization (CASSANDRA-13363)
 - * Enable segement creation before recovering commitlogs (CASSANDRA-13587)
   * Fix AssertionError in short read protection (CASSANDRA-13747)
   * Don't skip corrupted sstables on startup (CASSANDRA-13620)
   * Fix the merging of cells with different user type versions (CASSANDRA-13776)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4f12c409/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index 4b667ae,58f61f5..54f7985
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@@ -86,52 -92,8 +100,52 @@@ public class PasswordAuthenticator impl
      {
          try
          {
 +            String hash = cache.get(username);
-             if (!BCrypt.checkpw(password, hash))
++            if (!checkpw(password, hash))
 +                throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 +
 +            return new AuthenticatedUser(username);
 +        }
 +        catch (ExecutionException | UncheckedExecutionException e)
 +        {
 +            // the credentials were somehow invalid - either a non-existent role, or one without a defined password
 +            if (e.getCause() instanceof NoSuchCredentialsException)
 +                throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 +
 +            // an unanticipated exception occured whilst querying the credentials table
 +            if (e.getCause() instanceof RequestExecutionException)
 +            {
 +                logger.trace("Error performing internal authentication", e);
 +                throw new AuthenticationException(String.format("Error during authentication of user %s : %s", username, e.getMessage()));
 +            }
 +
 +            throw new RuntimeException(e);
 +        }
 +    }
 +
 +    private String queryHashedPassword(String username) throws NoSuchCredentialsException
 +    {
 +        try
 +        {
              SelectStatement authenticationStatement = authenticationStatement();
 -            return doAuthenticate(username, password, authenticationStatement);
 +
 +            ResultMessage.Rows rows =
 +                authenticationStatement.execute(QueryState.forInternalCalls(),
 +                                                QueryOptions.forInternalCalls(consistencyForRole(username),
 +                                                                              Lists.newArrayList(ByteBufferUtil.bytes(username))),
 +                                                System.nanoTime());
 +
 +            // If either a non-existent role name was supplied, or no credentials
 +            // were found for that role we don't want to cache the result so we throw
 +            // a specific, but unchecked, exception to keep LoadingCache happy.
 +            if (rows.result.isEmpty())
 +                throw new NoSuchCredentialsException();
 +
 +            UntypedResultSet result = UntypedResultSet.create(rows.result);
 +            if (!result.one().has(SALTED_HASH))
 +                throw new NoSuchCredentialsException();
 +
 +            return result.one().getString(SALTED_HASH);
          }
          catch (RequestExecutionException e)
          {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[3/6] cassandra git commit: Better tolerate improperly formatted bcrypt hashes

Posted by jj...@apache.org.
Better tolerate improperly formatted bcrypt hashes

Patch by Jeff Jirsa; Reviewed by Sam Tunnicliffe for CASSANDRA-13626


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5e7f60f6
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5e7f60f6
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5e7f60f6

Branch: refs/heads/trunk
Commit: 5e7f60f6bf5da386076faa08cefb3970a6ba5cc0
Parents: ded6636
Author: Jeff Jirsa <jj...@apple.com>
Authored: Tue Aug 29 15:21:08 2017 -0700
Committer: Jeff Jirsa <jj...@apple.com>
Committed: Wed Aug 30 21:58:26 2017 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cassandra/auth/PasswordAuthenticator.java   | 16 ++++-
 .../auth/PasswordAuthenticatorTest.java         | 64 ++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index aca9e1f..b405fdf 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Better tolerate improperly formatted bcrypt hashes (CASSANDRA-13626) 
  * Fix race condition in read command serialization (CASSANDRA-13363)
  * Enable segement creation before recovering commitlogs (CASSANDRA-13587)
  * Fix AssertionError in short read protection (CASSANDRA-13747)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index ca610d1..58f61f5 100644
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@ -74,6 +74,20 @@ public class PasswordAuthenticator implements IAuthenticator
         return true;
     }
 
+    protected static boolean checkpw(String password, String hash)
+    {
+        try
+        {
+            return BCrypt.checkpw(password, hash);
+        }
+        catch (Exception e)
+        {
+            // Improperly formatted hashes may cause BCrypt.checkpw to throw, so trap any other exception as a failure
+            logger.warn("Error: invalid password hash encountered, rejecting user", e);
+            return false;
+        }
+    }
+
     private AuthenticatedUser authenticate(String username, String password) throws AuthenticationException
     {
         try
@@ -162,7 +176,7 @@ public class PasswordAuthenticator implements IAuthenticator
                                                                                                 Lists.newArrayList(ByteBufferUtil.bytes(username))));
         UntypedResultSet result = UntypedResultSet.create(rows.result);
 
-        if ((result.isEmpty() || !result.one().has(SALTED_HASH)) || !BCrypt.checkpw(password, result.one().getString(SALTED_HASH)))
+        if ((result.isEmpty() || !result.one().has(SALTED_HASH)) || !checkpw(password, result.one().getString(SALTED_HASH)))
             throw new AuthenticationException("Username and/or password are incorrect");
 
         return new AuthenticatedUser(username);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
new file mode 100644
index 0000000..37763d7
--- /dev/null
+++ b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
@@ -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.cassandra.auth;
+
+
+import org.junit.Test;
+
+import static org.apache.cassandra.auth.CassandraRoleManager.*;
+import static org.apache.cassandra.auth.PasswordAuthenticator.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mindrot.jbcrypt.BCrypt.hashpw;
+import static org.mindrot.jbcrypt.BCrypt.gensalt;
+
+public class PasswordAuthenticatorTest
+{
+    @Test
+    public void testCheckpw() throws Exception
+    {
+        // Valid and correct
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(getGensaltLogRounds()))));
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(4))));
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(31))));
+
+        // Valid but incorrect hashes
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect0", gensalt(4))));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect1", gensalt(10))));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect2", gensalt(31))));
+
+        // Invalid hash values, the jBCrypt library implementation
+        // throws an exception which we catch and treat as a failure
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, ""));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "0"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD,
+                            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
+
+        // Format is structurally right, but actually invalid
+        // bad salt version
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$5x$10$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        // invalid number of rounds, multiple salt versions but it's the rounds that are incorrect
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$02$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$02$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$99$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$99$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        // unpadded rounds
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$6$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$6$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[6/6] cassandra git commit: Merge branch 'cassandra-3.11' into trunk

Posted by jj...@apache.org.
Merge branch 'cassandra-3.11' into trunk


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/f40198fc
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f40198fc
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f40198fc

Branch: refs/heads/trunk
Commit: f40198fc685028f87c26be4c51b2352e3cc01262
Parents: c84269a 4f12c40
Author: Jeff Jirsa <jj...@apple.com>
Authored: Wed Aug 30 21:59:43 2017 -0700
Committer: Jeff Jirsa <jj...@apple.com>
Committed: Wed Aug 30 22:02:07 2017 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cassandra/auth/PasswordAuthenticator.java   | 16 ++++-
 .../auth/PasswordAuthenticatorTest.java         | 64 ++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/f40198fc/CHANGES.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/f40198fc/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index b82b0ad,54f7985..b1604e7
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@@ -75,36 -82,97 +75,50 @@@ public class PasswordAuthenticator impl
          return true;
      }
  
+     protected static boolean checkpw(String password, String hash)
+     {
+         try
+         {
+             return BCrypt.checkpw(password, hash);
+         }
+         catch (Exception e)
+         {
+             // Improperly formatted hashes may cause BCrypt.checkpw to throw, so trap any other exception as a failure
+             logger.warn("Error: invalid password hash encountered, rejecting user", e);
+             return false;
+         }
+     }
+ 
      private AuthenticatedUser authenticate(String username, String password) throws AuthenticationException
      {
 -        try
 -        {
 -            String hash = cache.get(username);
 -            if (!checkpw(password, hash))
 -                throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 -
 -            return new AuthenticatedUser(username);
 -        }
 -        catch (ExecutionException | UncheckedExecutionException e)
 -        {
 -            // the credentials were somehow invalid - either a non-existent role, or one without a defined password
 -            if (e.getCause() instanceof NoSuchCredentialsException)
 -                throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 -
 -            // an unanticipated exception occured whilst querying the credentials table
 -            if (e.getCause() instanceof RequestExecutionException)
 -            {
 -                logger.trace("Error performing internal authentication", e);
 -                throw new AuthenticationException(String.format("Error during authentication of user %s : %s", username, e.getMessage()));
 -            }
 -
 -            throw new RuntimeException(e);
 -        }
 -    }
 -
 -    private String queryHashedPassword(String username) throws NoSuchCredentialsException
 -    {
 -        try
 -        {
 -            SelectStatement authenticationStatement = authenticationStatement();
 -
 -            ResultMessage.Rows rows =
 -                authenticationStatement.execute(QueryState.forInternalCalls(),
 -                                                QueryOptions.forInternalCalls(consistencyForRole(username),
 -                                                                              Lists.newArrayList(ByteBufferUtil.bytes(username))),
 -                                                System.nanoTime());
 +        String hash = cache.get(username);
-         if (!BCrypt.checkpw(password, hash))
++        if (!checkpw(password, hash))
 +            throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
  
 -            // If either a non-existent role name was supplied, or no credentials
 -            // were found for that role we don't want to cache the result so we throw
 -            // a specific, but unchecked, exception to keep LoadingCache happy.
 -            if (rows.result.isEmpty())
 -                throw new NoSuchCredentialsException();
 -
 -            UntypedResultSet result = UntypedResultSet.create(rows.result);
 -            if (!result.one().has(SALTED_HASH))
 -                throw new NoSuchCredentialsException();
 -
 -            return result.one().getString(SALTED_HASH);
 -        }
 -        catch (RequestExecutionException e)
 -        {
 -            logger.trace("Error performing internal authentication", e);
 -            throw e;
 -        }
 +        return new AuthenticatedUser(username);
      }
  
 -    /**
 -     * If the legacy users table exists try to verify credentials there. This is to handle the case
 -     * where the cluster is being upgraded and so is running with mixed versions of the authn tables
 -     */
 -    private SelectStatement authenticationStatement()
 +    private String queryHashedPassword(String username)
      {
 -        if (Schema.instance.getCFMetaData(SchemaConstants.AUTH_KEYSPACE_NAME, LEGACY_CREDENTIALS_TABLE) == null)
 -            return authenticateStatement;
 -        else
 -        {
 -            // the statement got prepared, we to try preparing it again.
 -            // If the credentials was initialised only after statement got prepared, re-prepare (CASSANDRA-12813).
 -            if (legacyAuthenticateStatement == null)
 -                prepareLegacyAuthenticateStatement();
 -            return legacyAuthenticateStatement;
 -        }
 +        ResultMessage.Rows rows =
 +        authenticateStatement.execute(QueryState.forInternalCalls(),
 +                                        QueryOptions.forInternalCalls(consistencyForRole(username),
 +                                                                      Lists.newArrayList(ByteBufferUtil.bytes(username))),
 +                                        System.nanoTime());
 +
 +        // If either a non-existent role name was supplied, or no credentials
 +        // were found for that role we don't want to cache the result so we throw
 +        // a specific, but unchecked, exception to keep LoadingCache happy.
 +        if (rows.result.isEmpty())
 +            throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 +
 +        UntypedResultSet result = UntypedResultSet.create(rows.result);
 +        if (!result.one().has(SALTED_HASH))
 +            throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 +
 +        return result.one().getString(SALTED_HASH);
      }
  
 -
      public Set<DataResource> protectedResources()
      {
          // Also protected by CassandraRoleManager, but the duplication doesn't hurt and is more explicit


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[5/6] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.11

Posted by jj...@apache.org.
Merge branch 'cassandra-3.0' into cassandra-3.11


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/4f12c409
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/4f12c409
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/4f12c409

Branch: refs/heads/cassandra-3.11
Commit: 4f12c409267eb44cbf596193456c5a67a1775748
Parents: 02e9846 5e7f60f
Author: Jeff Jirsa <jj...@apple.com>
Authored: Wed Aug 30 21:58:44 2017 -0700
Committer: Jeff Jirsa <jj...@apple.com>
Committed: Wed Aug 30 21:59:33 2017 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cassandra/auth/PasswordAuthenticator.java   | 16 ++++-
 .../auth/PasswordAuthenticatorTest.java         | 64 ++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/4f12c409/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index d848eff,b405fdf..af185cf
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,12 -1,7 +1,13 @@@
 -3.0.15
 - * Better tolerate improperly formatted bcrypt hashes (CASSANDRA-13626) 
 +3.11.1
 + * Fix cassandra-stress hang issues when an error during cluster connection happens (CASSANDRA-12938)
 + * Better bootstrap failure message when blocked by (potential) range movement (CASSANDRA-13744)
 + * "ignore" option is ignored in sstableloader (CASSANDRA-13721)
 + * Deadlock in AbstractCommitLogSegmentManager (CASSANDRA-13652)
 + * Duplicate the buffer before passing it to analyser in SASI operation (CASSANDRA-13512)
 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
 +Merged from 3.0:
++ * Better tolerate improperly formatted bcrypt hashes (CASSANDRA-13626)
   * Fix race condition in read command serialization (CASSANDRA-13363)
 - * Enable segement creation before recovering commitlogs (CASSANDRA-13587)
   * Fix AssertionError in short read protection (CASSANDRA-13747)
   * Don't skip corrupted sstables on startup (CASSANDRA-13620)
   * Fix the merging of cells with different user type versions (CASSANDRA-13776)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/4f12c409/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index 4b667ae,58f61f5..54f7985
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@@ -86,52 -92,8 +100,52 @@@ public class PasswordAuthenticator impl
      {
          try
          {
 +            String hash = cache.get(username);
-             if (!BCrypt.checkpw(password, hash))
++            if (!checkpw(password, hash))
 +                throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 +
 +            return new AuthenticatedUser(username);
 +        }
 +        catch (ExecutionException | UncheckedExecutionException e)
 +        {
 +            // the credentials were somehow invalid - either a non-existent role, or one without a defined password
 +            if (e.getCause() instanceof NoSuchCredentialsException)
 +                throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username));
 +
 +            // an unanticipated exception occured whilst querying the credentials table
 +            if (e.getCause() instanceof RequestExecutionException)
 +            {
 +                logger.trace("Error performing internal authentication", e);
 +                throw new AuthenticationException(String.format("Error during authentication of user %s : %s", username, e.getMessage()));
 +            }
 +
 +            throw new RuntimeException(e);
 +        }
 +    }
 +
 +    private String queryHashedPassword(String username) throws NoSuchCredentialsException
 +    {
 +        try
 +        {
              SelectStatement authenticationStatement = authenticationStatement();
 -            return doAuthenticate(username, password, authenticationStatement);
 +
 +            ResultMessage.Rows rows =
 +                authenticationStatement.execute(QueryState.forInternalCalls(),
 +                                                QueryOptions.forInternalCalls(consistencyForRole(username),
 +                                                                              Lists.newArrayList(ByteBufferUtil.bytes(username))),
 +                                                System.nanoTime());
 +
 +            // If either a non-existent role name was supplied, or no credentials
 +            // were found for that role we don't want to cache the result so we throw
 +            // a specific, but unchecked, exception to keep LoadingCache happy.
 +            if (rows.result.isEmpty())
 +                throw new NoSuchCredentialsException();
 +
 +            UntypedResultSet result = UntypedResultSet.create(rows.result);
 +            if (!result.one().has(SALTED_HASH))
 +                throw new NoSuchCredentialsException();
 +
 +            return result.one().getString(SALTED_HASH);
          }
          catch (RequestExecutionException e)
          {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[2/6] cassandra git commit: Better tolerate improperly formatted bcrypt hashes

Posted by jj...@apache.org.
Better tolerate improperly formatted bcrypt hashes

Patch by Jeff Jirsa; Reviewed by Sam Tunnicliffe for CASSANDRA-13626


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5e7f60f6
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5e7f60f6
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5e7f60f6

Branch: refs/heads/cassandra-3.11
Commit: 5e7f60f6bf5da386076faa08cefb3970a6ba5cc0
Parents: ded6636
Author: Jeff Jirsa <jj...@apple.com>
Authored: Tue Aug 29 15:21:08 2017 -0700
Committer: Jeff Jirsa <jj...@apple.com>
Committed: Wed Aug 30 21:58:26 2017 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../cassandra/auth/PasswordAuthenticator.java   | 16 ++++-
 .../auth/PasswordAuthenticatorTest.java         | 64 ++++++++++++++++++++
 3 files changed, 80 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index aca9e1f..b405fdf 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.15
+ * Better tolerate improperly formatted bcrypt hashes (CASSANDRA-13626) 
  * Fix race condition in read command serialization (CASSANDRA-13363)
  * Enable segement creation before recovering commitlogs (CASSANDRA-13587)
  * Fix AssertionError in short read protection (CASSANDRA-13747)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index ca610d1..58f61f5 100644
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@ -74,6 +74,20 @@ public class PasswordAuthenticator implements IAuthenticator
         return true;
     }
 
+    protected static boolean checkpw(String password, String hash)
+    {
+        try
+        {
+            return BCrypt.checkpw(password, hash);
+        }
+        catch (Exception e)
+        {
+            // Improperly formatted hashes may cause BCrypt.checkpw to throw, so trap any other exception as a failure
+            logger.warn("Error: invalid password hash encountered, rejecting user", e);
+            return false;
+        }
+    }
+
     private AuthenticatedUser authenticate(String username, String password) throws AuthenticationException
     {
         try
@@ -162,7 +176,7 @@ public class PasswordAuthenticator implements IAuthenticator
                                                                                                 Lists.newArrayList(ByteBufferUtil.bytes(username))));
         UntypedResultSet result = UntypedResultSet.create(rows.result);
 
-        if ((result.isEmpty() || !result.one().has(SALTED_HASH)) || !BCrypt.checkpw(password, result.one().getString(SALTED_HASH)))
+        if ((result.isEmpty() || !result.one().has(SALTED_HASH)) || !checkpw(password, result.one().getString(SALTED_HASH)))
             throw new AuthenticationException("Username and/or password are incorrect");
 
         return new AuthenticatedUser(username);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5e7f60f6/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
new file mode 100644
index 0000000..37763d7
--- /dev/null
+++ b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
@@ -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.cassandra.auth;
+
+
+import org.junit.Test;
+
+import static org.apache.cassandra.auth.CassandraRoleManager.*;
+import static org.apache.cassandra.auth.PasswordAuthenticator.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mindrot.jbcrypt.BCrypt.hashpw;
+import static org.mindrot.jbcrypt.BCrypt.gensalt;
+
+public class PasswordAuthenticatorTest
+{
+    @Test
+    public void testCheckpw() throws Exception
+    {
+        // Valid and correct
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(getGensaltLogRounds()))));
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(4))));
+        assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(31))));
+
+        // Valid but incorrect hashes
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect0", gensalt(4))));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect1", gensalt(10))));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect2", gensalt(31))));
+
+        // Invalid hash values, the jBCrypt library implementation
+        // throws an exception which we catch and treat as a failure
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, ""));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "0"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD,
+                            "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
+
+        // Format is structurally right, but actually invalid
+        // bad salt version
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$5x$10$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        // invalid number of rounds, multiple salt versions but it's the rounds that are incorrect
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$02$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$02$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$99$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$99$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        // unpadded rounds
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2$6$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+        assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$6$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org