diff --git a/CHANGES.txt b/CHANGES.txt
index 388fad1519..002688b995 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -44,6 +44,7 @@ Merged from 3.11:
* Update Jackson from 2.9.10 to 2.12.5 (CASSANDRA-16851)
* Make assassinate more resilient to missing tokens (CASSANDRA-16847)
Merged from 3.0:
+ * CVE-2015-0886 Security vulnerability in jbcrypt is addressed (CASSANDRA-9384)
* Avoid useless SSTable reads during single partition queries (CASSANDRA-16944)
* Debian init respects CASSANDRA_HEAPDUMP_DIR (CASSANDRA-13843)
* Catch UnsatisfiedLinkError in WindowsTimer (CASSANDRA-16085)
diff --git a/NEWS.txt b/NEWS.txt
index bdd3dd6ffe..e8ae8e194e 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -51,6 +51,10 @@ New features
Upgrading
---------
+ - Before you upgrade, if you are using `cassandra.auth_bcrypt_gensalt_log2_rounds` property,
+ confirm it is set to value lower than 31 otherwise Cassandra will fail to start. See CASSANDRA-9384
+ for further details. You also need to regenerate passwords for users for who the password
+ was created while the above property was set to be more than 30 otherwise they will not be able to log in.
Deprecation
-----------
diff --git a/build.xml b/build.xml
index 1dc1c035cd..97ea512e9b 100644
--- a/build.xml
+++ b/build.xml
@@ -573,7 +573,7 @@
-
+
diff --git a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java
index b8421af488..f4c78acffd 100644
--- a/src/java/org/apache/cassandra/auth/CassandraRoleManager.java
+++ b/src/java/org/apache/cassandra/auth/CassandraRoleManager.java
@@ -110,15 +110,16 @@ public class CassandraRoleManager implements IRoleManager
};
// 2 ** GENSALT_LOG2_ROUNDS rounds of hashing will be performed.
- private static final String GENSALT_LOG2_ROUNDS_PROPERTY = Config.PROPERTY_PREFIX + "auth_bcrypt_gensalt_log2_rounds";
+ @VisibleForTesting
+ public static final String GENSALT_LOG2_ROUNDS_PROPERTY = Config.PROPERTY_PREFIX + "auth_bcrypt_gensalt_log2_rounds";
private static final int GENSALT_LOG2_ROUNDS = getGensaltLogRounds();
static int getGensaltLogRounds()
{
int rounds = Integer.getInteger(GENSALT_LOG2_ROUNDS_PROPERTY, 10);
- if (rounds < 4 || rounds > 31)
+ if (rounds < 4 || rounds > 30)
throw new ConfigurationException(String.format("Bad value for system property -D%s." +
- "Please use a value between 4 and 31 inclusively",
+ "Please use a value between 4 and 30 inclusively",
GENSALT_LOG2_ROUNDS_PROPERTY));
return rounds;
}
diff --git a/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
index 5dd4ab5559..8cd74d82f0 100644
--- a/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
+++ b/test/unit/org/apache/cassandra/auth/PasswordAuthenticatorTest.java
@@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets;
import com.google.common.collect.Iterables;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -31,6 +32,7 @@ import com.datastax.driver.core.PlainTextAuthProvider;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.exceptions.AuthenticationException;
+import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
@@ -48,17 +50,17 @@ public class PasswordAuthenticatorTest extends CQLTester
private static PasswordAuthenticator authenticator = new PasswordAuthenticator();
@Test
- public void testCheckpw() throws Exception
+ public void testCheckpw()
{
// 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))));
+ assertTrue(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw(DEFAULT_SUPERUSER_PASSWORD, gensalt(12))));
// 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))));
+ assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, hashpw("incorrect2", gensalt(12))));
// Invalid hash values, the jBCrypt library implementation
// throws an exception which we catch and treat as a failure
@@ -80,6 +82,37 @@ public class PasswordAuthenticatorTest extends CQLTester
assertFalse(checkpw(DEFAULT_SUPERUSER_PASSWORD, "$2a$6$abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ01234"));
}
+ @Test(expected = ConfigurationException.class)
+ public void testInvalidUpperBoundHashingRoundsValue()
+ {
+ executeSaltRoundsPropertyTest(31);
+ }
+
+ @Test(expected = ConfigurationException.class)
+ public void testInvalidLowerBoundHashingRoundsValue()
+ {
+ executeSaltRoundsPropertyTest(3);
+ }
+
+ private void executeSaltRoundsPropertyTest(Integer rounds)
+ {
+ String oldProperty = System.getProperty(GENSALT_LOG2_ROUNDS_PROPERTY);
+ try
+ {
+ System.setProperty(GENSALT_LOG2_ROUNDS_PROPERTY, rounds.toString());
+ getGensaltLogRounds();
+ Assert.fail("Property " + GENSALT_LOG2_ROUNDS_PROPERTY + " must be in interval [4,30]");
+ }
+ finally
+ {
+ if (oldProperty != null)
+ System.setProperty(GENSALT_LOG2_ROUNDS_PROPERTY, oldProperty);
+ else
+ System.clearProperty(GENSALT_LOG2_ROUNDS_PROPERTY);
+ }
+ }
+
+
@Test(expected = AuthenticationException.class)
public void testEmptyUsername()
{