Add bcrypt authentification type

This commit is contained in:
Nikolay Degterinsky 2023-01-04 14:22:39 +00:00
parent cb8e7c8059
commit aa52956412
13 changed files with 116 additions and 1 deletions

3
.gitmodules vendored
View File

@ -294,3 +294,6 @@
[submodule "contrib/libdivide"] [submodule "contrib/libdivide"]
path = contrib/libdivide path = contrib/libdivide
url = https://github.com/ridiculousfish/libdivide.git url = https://github.com/ridiculousfish/libdivide.git
[submodule "contrib/libbcrypt"]
path = contrib/libbcrypt
url = https://github.com/rg3/libbcrypt.git

View File

@ -170,6 +170,8 @@ add_contrib (annoy-cmake annoy)
add_contrib (xxHash-cmake xxHash) add_contrib (xxHash-cmake xxHash)
add_contrib (libbcrypt-cmake libbcrypt)
add_contrib (google-benchmark-cmake google-benchmark) add_contrib (google-benchmark-cmake google-benchmark)
# Put all targets defined here and in subdirectories under "contrib/<immediate-subdir>" folders in GUI-based IDEs. # Put all targets defined here and in subdirectories under "contrib/<immediate-subdir>" folders in GUI-based IDEs.

1
contrib/libbcrypt vendored Submodule

@ -0,0 +1 @@
Subproject commit 8aa32ad94ebe06b76853b0767c910c9fbf7ccef4

View File

@ -0,0 +1,19 @@
option(ENABLE_BCRYPT "Enable bcrypt" ${ENABLE_LIBRARIES})
if (NOT ENABLE_BCRYPT)
message(STATUS "Not using bcrypt")
return()
endif()
set (LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/libbcrypt")
set(SRCS
"${LIBRARY_DIR}/bcrypt.c"
"${LIBRARY_DIR}/crypt_blowfish/crypt_blowfish.c"
"${LIBRARY_DIR}/crypt_blowfish/crypt_gensalt.c"
"${LIBRARY_DIR}/crypt_blowfish/wrapper.c"
)
add_library(_bcrypt ${SRCS})
target_include_directories(_bcrypt SYSTEM PUBLIC "${LIBRARY_DIR}")
add_library(ch_contrib::bcrypt ALIAS _bcrypt)

View File

@ -31,6 +31,11 @@ namespace
return (Util::encodeDoubleSHA1(password) == password_double_sha1); return (Util::encodeDoubleSHA1(password) == password_double_sha1);
} }
bool checkPasswordBcrypt(std::string_view password, const Digest & password_bcrypt)
{
return Util::checkPasswordBcrypt(password, password_bcrypt);
}
bool checkPasswordSHA256(std::string_view password, const Digest & password_sha256, const String & salt) bool checkPasswordSHA256(std::string_view password, const Digest & password_sha256, const String & salt)
{ {
return Util::encodeSHA256(String(password).append(salt)) == password_sha256; return Util::encodeSHA256(String(password).append(salt)) == password_sha256;
@ -81,6 +86,7 @@ bool Authentication::areCredentialsValid(const Credentials & credentials, const
case AuthenticationType::PLAINTEXT_PASSWORD: case AuthenticationType::PLAINTEXT_PASSWORD:
case AuthenticationType::SHA256_PASSWORD: case AuthenticationType::SHA256_PASSWORD:
case AuthenticationType::DOUBLE_SHA1_PASSWORD: case AuthenticationType::DOUBLE_SHA1_PASSWORD:
case AuthenticationType::BCRYPT_PASSWORD:
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication"); throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
@ -109,6 +115,7 @@ bool Authentication::areCredentialsValid(const Credentials & credentials, const
return checkPasswordDoubleSHA1MySQL(mysql_credentials->getScramble(), mysql_credentials->getScrambledPassword(), auth_data.getPasswordHashBinary()); return checkPasswordDoubleSHA1MySQL(mysql_credentials->getScramble(), mysql_credentials->getScrambledPassword(), auth_data.getPasswordHashBinary());
case AuthenticationType::SHA256_PASSWORD: case AuthenticationType::SHA256_PASSWORD:
case AuthenticationType::BCRYPT_PASSWORD:
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
case AuthenticationType::KERBEROS: case AuthenticationType::KERBEROS:
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication"); throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
@ -137,6 +144,9 @@ bool Authentication::areCredentialsValid(const Credentials & credentials, const
case AuthenticationType::DOUBLE_SHA1_PASSWORD: case AuthenticationType::DOUBLE_SHA1_PASSWORD:
return checkPasswordDoubleSHA1(basic_credentials->getPassword(), auth_data.getPasswordHashBinary()); return checkPasswordDoubleSHA1(basic_credentials->getPassword(), auth_data.getPasswordHashBinary());
case AuthenticationType::BCRYPT_PASSWORD:
return checkPasswordBcrypt(basic_credentials->getPassword(), auth_data.getPasswordHashBinary());
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
return external_authenticators.checkLDAPCredentials(auth_data.getLDAPServerName(), *basic_credentials); return external_authenticators.checkLDAPCredentials(auth_data.getLDAPServerName(), *basic_credentials);
@ -159,6 +169,7 @@ bool Authentication::areCredentialsValid(const Credentials & credentials, const
case AuthenticationType::PLAINTEXT_PASSWORD: case AuthenticationType::PLAINTEXT_PASSWORD:
case AuthenticationType::SHA256_PASSWORD: case AuthenticationType::SHA256_PASSWORD:
case AuthenticationType::DOUBLE_SHA1_PASSWORD: case AuthenticationType::DOUBLE_SHA1_PASSWORD:
case AuthenticationType::BCRYPT_PASSWORD:
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication"); throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");

View File

@ -6,6 +6,11 @@
#include <boost/algorithm/hex.hpp> #include <boost/algorithm/hex.hpp>
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
#include "config.h"
#if USE_BCRYPT
# include <bcrypt.h>
#endif
namespace DB namespace DB
{ {
@ -49,6 +54,11 @@ const AuthenticationTypeInfo & AuthenticationTypeInfo::get(AuthenticationType ty
static const auto info = make_info("DOUBLE_SHA1_PASSWORD"); static const auto info = make_info("DOUBLE_SHA1_PASSWORD");
return info; return info;
} }
case AuthenticationType::BCRYPT_PASSWORD:
{
static const auto info = make_info("BCRYPT_PASSWORD");
return info;
}
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
{ {
static const auto info = make_info("LDAP"); static const auto info = make_info("LDAP");
@ -85,6 +95,40 @@ AuthenticationData::Digest AuthenticationData::Util::encodeSHA256(std::string_vi
#endif #endif
} }
AuthenticationData::Digest AuthenticationData::Util::encodeBcrypt(std::string_view text [[maybe_unused]])
{
#if USE_BCRYPT
char salt[BCRYPT_HASHSIZE];
Digest hash;
hash.resize(64);
int ret = bcrypt_gensalt(12, salt);
if (ret != 0)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_gensalt returned {}", ret);
ret = bcrypt_hashpw(text.data(), salt, reinterpret_cast<char *>(hash.data()));
if (ret != 0)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_hashpw returned {}", ret);
return hash;
#else
throw DB::Exception(
"bcrypt passwords support is disabled, because ClickHouse was built without bcrypt library",
DB::ErrorCodes::SUPPORT_IS_DISABLED);
#endif
}
bool AuthenticationData::Util::checkPasswordBcrypt(std::string_view password [[maybe_unused]], const Digest & password_bcrypt [[maybe_unused]])
{
#if USE_BCRYPT
int ret = bcrypt_checkpw(password.data(), reinterpret_cast<const char *>(password_bcrypt.data()));
if (ret == -1)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_checkpw returned {}", ret);
return (ret == 0);
#else
throw DB::Exception(
"bcrypt passwords support is disabled, because ClickHouse was built without bcrypt library",
DB::ErrorCodes::SUPPORT_IS_DISABLED);
#endif
}
AuthenticationData::Digest AuthenticationData::Util::encodeSHA1(std::string_view text) AuthenticationData::Digest AuthenticationData::Util::encodeSHA1(std::string_view text)
{ {
@ -115,6 +159,9 @@ void AuthenticationData::setPassword(const String & password_)
case AuthenticationType::DOUBLE_SHA1_PASSWORD: case AuthenticationType::DOUBLE_SHA1_PASSWORD:
return setPasswordHashBinary(Util::encodeDoubleSHA1(password_)); return setPasswordHashBinary(Util::encodeDoubleSHA1(password_));
case AuthenticationType::BCRYPT_PASSWORD:
return setPasswordHashBinary(Util::encodeBcrypt(password_));
case AuthenticationType::NO_PASSWORD: case AuthenticationType::NO_PASSWORD:
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
case AuthenticationType::KERBEROS: case AuthenticationType::KERBEROS:
@ -198,6 +245,12 @@ void AuthenticationData::setPasswordHashBinary(const Digest & hash)
return; return;
} }
case AuthenticationType::BCRYPT_PASSWORD:
{
password_hash = hash;
return;
}
case AuthenticationType::NO_PASSWORD: case AuthenticationType::NO_PASSWORD:
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
case AuthenticationType::KERBEROS: case AuthenticationType::KERBEROS:

View File

@ -22,6 +22,9 @@ enum class AuthenticationType
/// This kind of hash is used by the `mysql_native_password` authentication plugin. /// This kind of hash is used by the `mysql_native_password` authentication plugin.
DOUBLE_SHA1_PASSWORD, DOUBLE_SHA1_PASSWORD,
/// Password is encrypted in bcrypt hash.
BCRYPT_PASSWORD,
/// Password is checked by a [remote] LDAP server. Connection will be made at each authentication attempt. /// Password is checked by a [remote] LDAP server. Connection will be made at each authentication attempt.
LDAP, LDAP,
@ -102,6 +105,8 @@ public:
static Digest encodeSHA1(const Digest & text) { return encodeSHA1(std::string_view{reinterpret_cast<const char *>(text.data()), text.size()}); } static Digest encodeSHA1(const Digest & text) { return encodeSHA1(std::string_view{reinterpret_cast<const char *>(text.data()), text.size()}); }
static Digest encodeDoubleSHA1(std::string_view text) { return encodeSHA1(encodeSHA1(text)); } static Digest encodeDoubleSHA1(std::string_view text) { return encodeSHA1(encodeSHA1(text)); }
static Digest encodeDoubleSHA1(const Digest & text) { return encodeSHA1(encodeSHA1(text)); } static Digest encodeDoubleSHA1(const Digest & text) { return encodeSHA1(encodeSHA1(text)); }
static Digest encodeBcrypt(std::string_view text);
static bool checkPasswordBcrypt(std::string_view password, const Digest & password_bcrypt);
}; };
private: private:

View File

@ -536,6 +536,10 @@ if (TARGET ch_contrib::sqlite)
dbms_target_link_libraries(PUBLIC ch_contrib::sqlite) dbms_target_link_libraries(PUBLIC ch_contrib::sqlite)
endif() endif()
if (TARGET ch_contrib::bcrypt)
target_link_libraries (clickhouse_common_io PUBLIC ch_contrib::bcrypt)
endif()
if (TARGET ch_contrib::msgpack) if (TARGET ch_contrib::msgpack)
target_link_libraries (clickhouse_common_io PUBLIC ch_contrib::msgpack) target_link_libraries (clickhouse_common_io PUBLIC ch_contrib::msgpack)
endif() endif()

View File

@ -54,3 +54,4 @@
#cmakedefine01 USE_BLAKE3 #cmakedefine01 USE_BLAKE3
#cmakedefine01 USE_SKIM #cmakedefine01 USE_SKIM
#cmakedefine01 USE_OPENSSL_INTREE #cmakedefine01 USE_OPENSSL_INTREE
#cmakedefine01 USE_BCRYPT

View File

@ -84,12 +84,13 @@ NamesAndTypesList SessionLogElement::getNamesAndTypes()
AUTH_TYPE_NAME_AND_VALUE(AuthType::PLAINTEXT_PASSWORD), AUTH_TYPE_NAME_AND_VALUE(AuthType::PLAINTEXT_PASSWORD),
AUTH_TYPE_NAME_AND_VALUE(AuthType::SHA256_PASSWORD), AUTH_TYPE_NAME_AND_VALUE(AuthType::SHA256_PASSWORD),
AUTH_TYPE_NAME_AND_VALUE(AuthType::DOUBLE_SHA1_PASSWORD), AUTH_TYPE_NAME_AND_VALUE(AuthType::DOUBLE_SHA1_PASSWORD),
AUTH_TYPE_NAME_AND_VALUE(AuthType::BCRYPT_PASSWORD),
AUTH_TYPE_NAME_AND_VALUE(AuthType::LDAP), AUTH_TYPE_NAME_AND_VALUE(AuthType::LDAP),
AUTH_TYPE_NAME_AND_VALUE(AuthType::KERBEROS), AUTH_TYPE_NAME_AND_VALUE(AuthType::KERBEROS),
AUTH_TYPE_NAME_AND_VALUE(AuthType::SSL_CERTIFICATE), AUTH_TYPE_NAME_AND_VALUE(AuthType::SSL_CERTIFICATE),
}); });
#undef AUTH_TYPE_NAME_AND_VALUE #undef AUTH_TYPE_NAME_AND_VALUE
static_assert(static_cast<int>(AuthenticationType::MAX) == 7); static_assert(static_cast<int>(AuthenticationType::MAX) == 8);
auto interface_type_column = std::make_shared<DataTypeEnum8>( auto interface_type_column = std::make_shared<DataTypeEnum8>(
DataTypeEnum8::Values DataTypeEnum8::Values

View File

@ -64,6 +64,13 @@ namespace
password = auth_data.getPasswordHashHex(); password = auth_data.getPasswordHashHex();
break; break;
} }
case AuthenticationType::BCRYPT_PASSWORD:
{
auth_type_name = "bcrypt_hash";
prefix = "BY";
password = auth_data.getPasswordHashHex();
break;
}
case AuthenticationType::LDAP: case AuthenticationType::LDAP:
{ {
prefix = "SERVER"; prefix = "SERVER";

View File

@ -104,6 +104,11 @@ namespace
type = AuthenticationType::DOUBLE_SHA1_PASSWORD; type = AuthenticationType::DOUBLE_SHA1_PASSWORD;
expect_hash = true; expect_hash = true;
} }
else if (ParserKeyword{"BCRYPT_HASH"}.ignore(pos, expected))
{
type = AuthenticationType::BCRYPT_PASSWORD;
expect_hash = true;
}
else else
return false; return false;
} }

View File

@ -138,6 +138,9 @@ endif()
if (TARGET ch_contrib::capnp) if (TARGET ch_contrib::capnp)
set(USE_CAPNP 1) set(USE_CAPNP 1)
endif() endif()
if (TARGET ch_contrib::bcrypt)
set(USE_BCRYPT 1)
endif()
if (NOT (ENABLE_OPENSSL OR ENABLE_OPENSSL_DYNAMIC)) if (NOT (ENABLE_OPENSSL OR ENABLE_OPENSSL_DYNAMIC))
set(USE_BORINGSSL 1) set(USE_BORINGSSL 1)
endif () endif ()