This commit is contained in:
Nikolay Degterinsky 2023-04-28 15:12:06 +00:00
parent df1e56b985
commit 54c8f63a33
6 changed files with 46 additions and 19 deletions

View File

@ -350,7 +350,6 @@ try
/// Set user password complexity rules /// Set user password complexity rules
auto & access_control = global_context->getAccessControl(); auto & access_control = global_context->getAccessControl();
access_control.setPasswordComplexityRules(connection->getPasswordComplexityRules()); access_control.setPasswordComplexityRules(connection->getPasswordComplexityRules());
access_control.setBcryptWorkfactor(connection->getBcryptWorkfactor());
if (is_interactive && !delayed_interactive) if (is_interactive && !delayed_interactive)
{ {

View File

@ -701,11 +701,13 @@ void AccessControl::setBcryptWorkfactor(int workfactor_)
{ {
if (workfactor_ < 4) if (workfactor_ < 4)
bcrypt_workfactor = 4; bcrypt_workfactor = 4;
else if (workfactor_ > 31)
bcrypt_workfactor = 31;
else else
bcrypt_workfactor = workfactor_; bcrypt_workfactor = workfactor_;
} }
int AccessControl::getBcryptWorkfactor() int AccessControl::getBcryptWorkfactor() const
{ {
return bcrypt_workfactor; return bcrypt_workfactor;
} }

View File

@ -160,7 +160,7 @@ public:
/// Workfactor for bcrypt encoded passwords /// Workfactor for bcrypt encoded passwords
void setBcryptWorkfactor(int workfactor_); void setBcryptWorkfactor(int workfactor_);
int getBcryptWorkfactor(); int getBcryptWorkfactor() const;
/// Enables logic that users without permissive row policies can still read rows using a SELECT query. /// Enables logic that users without permissive row policies can still read rows using a SELECT query.
/// For example, if there two users A, B and a row policy is defined only for A, then /// For example, if there two users A, B and a row policy is defined only for A, then

View File

@ -44,7 +44,7 @@ AuthenticationData::Digest AuthenticationData::Util::encodeSHA256(std::string_vi
::DB::encodeSHA256(text, hash.data()); ::DB::encodeSHA256(text, hash.data());
return hash; return hash;
#else #else
throw DB::Exception(DB::ErrorCodes::SUPPORT_IS_DISABLED, "SHA256 passwords support is disabled, because ClickHouse was built without SSL library"); throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SHA256 passwords support is disabled, because ClickHouse was built without SSL library");
#endif #endif
} }
@ -60,9 +60,9 @@ AuthenticationData::Digest AuthenticationData::Util::encodeBcrypt(std::string_vi
{ {
#if USE_BCRYPT #if USE_BCRYPT
if (text.size() > 72) if (text.size() > 72)
throw DB::Exception( throw Exception(
"bcrypt does not support passwords with a length of more than 72 bytes", ErrorCodes::BAD_ARGUMENTS,
DB::ErrorCodes::BAD_ARGUMENTS); "bcrypt does not support passwords with a length of more than 72 bytes");
char salt[BCRYPT_HASHSIZE]; char salt[BCRYPT_HASHSIZE];
Digest hash; Digest hash;
@ -70,17 +70,17 @@ AuthenticationData::Digest AuthenticationData::Util::encodeBcrypt(std::string_vi
int ret = bcrypt_gensalt(workfactor, salt); int ret = bcrypt_gensalt(workfactor, salt);
if (ret != 0) if (ret != 0)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_gensalt returned {}", ret); throw Exception(ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_gensalt returned {}", ret);
ret = bcrypt_hashpw(text.data(), salt, reinterpret_cast<char *>(hash.data())); ret = bcrypt_hashpw(text.data(), salt, reinterpret_cast<char *>(hash.data()));
if (ret != 0) if (ret != 0)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_hashpw returned {}", ret); throw Exception(ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_hashpw returned {}", ret);
return hash; return hash;
#else #else
throw DB::Exception( throw Exception(
"bcrypt passwords support is disabled, because ClickHouse was built without bcrypt library", ErrorCodes::SUPPORT_IS_DISABLED,
DB::ErrorCodes::SUPPORT_IS_DISABLED); "bcrypt passwords support is disabled, because ClickHouse was built without bcrypt library");
#endif #endif
} }
@ -89,12 +89,12 @@ bool AuthenticationData::Util::checkPasswordBcrypt(std::string_view password [[m
#if USE_BCRYPT #if USE_BCRYPT
int ret = bcrypt_checkpw(password.data(), reinterpret_cast<const char *>(password_bcrypt.data())); int ret = bcrypt_checkpw(password.data(), reinterpret_cast<const char *>(password_bcrypt.data()));
if (ret == -1) if (ret == -1)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_checkpw returned {}", ret); throw Exception(ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_checkpw returned {}", ret);
return (ret == 0); return (ret == 0);
#else #else
throw DB::Exception( throw Exception(
"bcrypt passwords support is disabled, because ClickHouse was built without bcrypt library", ErrorCodes::SUPPORT_IS_DISABLED,
DB::ErrorCodes::SUPPORT_IS_DISABLED); "bcrypt passwords support is disabled, because ClickHouse was built without bcrypt library");
#endif #endif
} }
@ -210,8 +210,15 @@ void AuthenticationData::setPasswordHashBinary(const Digest & hash)
case AuthenticationType::BCRYPT_PASSWORD: case AuthenticationType::BCRYPT_PASSWORD:
{ {
/// TODO: check size /// Depending on the workfactor the resulting hash can be 59 or 60 characters long.
/// However the library we use to encode it requires hash string to be 64 characters long,
/// so we also allow the hash of this length.
if (hash.size() != 59 && hash.size() != 60 && hash.size() != 64)
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Password hash for the 'BCRYPT_PASSWORD' authentication type has length {} "
"but must be 59 or 60 bytes.", hash.size());
password_hash = hash; password_hash = hash;
password_hash.resize(64);
return; return;
} }
@ -278,7 +285,7 @@ std::shared_ptr<ASTAuthenticationData> AuthenticationData::toAST() const
case AuthenticationType::BCRYPT_PASSWORD: case AuthenticationType::BCRYPT_PASSWORD:
{ {
node->contains_hash = true; node->contains_hash = true;
node->children.push_back(std::make_shared<ASTLiteral>(getPasswordHashHex())); node->children.push_back(std::make_shared<ASTLiteral>(AuthenticationData::Util::digestToString(getPasswordHashBinary())));
break; break;
} }
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
@ -391,7 +398,16 @@ AuthenticationData AuthenticationData::fromAST(const ASTAuthenticationData & que
if (query.contains_hash) if (query.contains_hash)
{ {
String value = checkAndGetLiteralArgument<String>(args[0], "hash"); String value = checkAndGetLiteralArgument<String>(args[0], "hash");
auth_data.setPasswordHashHex(value);
if (query.type == AuthenticationType::BCRYPT_PASSWORD)
{
auth_data.setPasswordHashBinary(AuthenticationData::Util::stringToDigest(value));
return auth_data;
}
else
{
auth_data.setPasswordHashHex(value);
}
if (query.type == AuthenticationType::SHA256_PASSWORD && args_size == 2) if (query.type == AuthenticationType::SHA256_PASSWORD && args_size == 2)
{ {

View File

@ -65,6 +65,7 @@ public:
struct Util struct Util
{ {
static String digestToString(const Digest & text) { return String(text.data(), text.data() + text.size()); }
static Digest stringToDigest(std::string_view text) { return Digest(text.data(), text.data() + text.size()); } static Digest stringToDigest(std::string_view text) { return Digest(text.data(), text.data() + text.size()); }
static Digest encodeSHA256(std::string_view text); static Digest encodeSHA256(std::string_view text);
static Digest encodeSHA1(std::string_view text); static Digest encodeSHA1(std::string_view text);

View File

@ -87,6 +87,15 @@ void ASTAuthenticationData::formatImpl(const FormatSettings & settings, FormatSt
password = true; password = true;
break; break;
} }
case AuthenticationType::BCRYPT_PASSWORD:
{
if (contains_hash)
auth_type_name = "bcrypt_hash";
prefix = "BY";
password = true;
break;
}
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
{ {
prefix = "SERVER"; prefix = "SERVER";