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 <jji...@apple.com> Authored: Tue Aug 29 15:21:08 2017 -0700 Committer: Jeff Jirsa <jji...@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-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org