Better tolerate improperly formatted bcrypt hashes

Patch by Jeff Jirsa; Reviewed by Sam Tunnicliffe for CASSANDRA-13626
This commit is contained in:
Jeff Jirsa 2017-08-29 15:21:08 -07:00
parent ded663622e
commit 5e7f60f6bf
3 changed files with 80 additions and 1 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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"));
}
}