2021-11-01 14:03:20 +00:00
|
|
|
#include <Access/Authentication.h>
|
2023-04-24 15:12:45 +00:00
|
|
|
#include <Access/AuthenticationData.h>
|
2021-03-11 20:41:10 +00:00
|
|
|
#include <Access/Credentials.h>
|
2020-05-27 21:06:33 +00:00
|
|
|
#include <Access/ExternalAuthenticators.h>
|
2021-03-11 20:41:10 +00:00
|
|
|
#include <Access/LDAPClient.h>
|
|
|
|
#include <Access/GSSAcceptor.h>
|
2019-10-08 13:44:44 +00:00
|
|
|
#include <Poco/SHA1Engine.h>
|
2024-04-04 20:58:35 +00:00
|
|
|
#include <Common/Exception.h>
|
|
|
|
#include <Common/SSHWrapper.h>
|
2021-08-24 07:09:13 +00:00
|
|
|
#include <Common/typeid_cast.h>
|
2024-06-03 15:38:40 +00:00
|
|
|
#include <Access/Common/SSLCertificateSubjects.h>
|
2019-10-08 13:44:44 +00:00
|
|
|
|
2024-04-04 20:58:35 +00:00
|
|
|
#include "config.h"
|
2019-10-08 13:44:44 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
2020-05-20 20:31:55 +00:00
|
|
|
extern const int NOT_IMPLEMENTED;
|
2023-09-26 15:50:19 +00:00
|
|
|
extern const int SUPPORT_IS_DISABLED;
|
2019-10-08 13:44:44 +00:00
|
|
|
}
|
|
|
|
|
2021-08-24 07:09:13 +00:00
|
|
|
namespace
|
2019-12-06 01:30:01 +00:00
|
|
|
{
|
2021-11-01 14:03:20 +00:00
|
|
|
using Digest = AuthenticationData::Digest;
|
|
|
|
using Util = AuthenticationData::Util;
|
2021-08-24 07:09:13 +00:00
|
|
|
|
|
|
|
bool checkPasswordPlainText(const String & password, const Digest & password_plaintext)
|
2019-12-06 01:30:01 +00:00
|
|
|
{
|
2021-11-02 09:02:22 +00:00
|
|
|
return (Util::stringToDigest(password) == password_plaintext);
|
2021-08-24 07:09:13 +00:00
|
|
|
}
|
2019-12-06 01:30:01 +00:00
|
|
|
|
2022-07-14 16:11:35 +00:00
|
|
|
bool checkPasswordDoubleSHA1(std::string_view password, const Digest & password_double_sha1)
|
2021-08-24 07:09:13 +00:00
|
|
|
{
|
|
|
|
return (Util::encodeDoubleSHA1(password) == password_double_sha1);
|
|
|
|
}
|
2019-12-06 01:30:01 +00:00
|
|
|
|
2023-01-04 14:22:39 +00:00
|
|
|
bool checkPasswordBcrypt(std::string_view password, const Digest & password_bcrypt)
|
|
|
|
{
|
|
|
|
return Util::checkPasswordBcrypt(password, password_bcrypt);
|
|
|
|
}
|
|
|
|
|
2022-07-14 16:11:35 +00:00
|
|
|
bool checkPasswordSHA256(std::string_view password, const Digest & password_sha256, const String & salt)
|
2021-08-24 07:09:13 +00:00
|
|
|
{
|
2022-04-12 14:30:09 +00:00
|
|
|
return Util::encodeSHA256(String(password).append(salt)) == password_sha256;
|
2021-08-24 07:09:13 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 16:11:35 +00:00
|
|
|
bool checkPasswordDoubleSHA1MySQL(std::string_view scramble, std::string_view scrambled_password, const Digest & password_double_sha1)
|
2021-08-24 07:09:13 +00:00
|
|
|
{
|
|
|
|
/// scrambled_password = SHA1(password) XOR SHA1(scramble <concat> SHA1(SHA1(password)))
|
|
|
|
|
|
|
|
constexpr size_t scramble_length = 20;
|
|
|
|
constexpr size_t sha1_size = Poco::SHA1Engine::DIGEST_SIZE;
|
|
|
|
|
|
|
|
if ((scramble.size() < scramble_length) || (scramble.size() > scramble_length + 1)
|
|
|
|
|| ((scramble.size() == scramble_length + 1) && (scramble[scramble_length] != 0))
|
|
|
|
|| (scrambled_password.size() != sha1_size) || (password_double_sha1.size() != sha1_size))
|
|
|
|
return false;
|
2020-05-27 21:06:33 +00:00
|
|
|
|
2021-08-24 07:09:13 +00:00
|
|
|
Poco::SHA1Engine engine;
|
|
|
|
engine.update(scramble.data(), scramble_length);
|
|
|
|
engine.update(password_double_sha1.data(), sha1_size);
|
|
|
|
const Poco::SHA1Engine::Digest & digest = engine.digest();
|
2020-05-29 06:52:44 +00:00
|
|
|
|
2021-08-24 07:09:13 +00:00
|
|
|
Poco::SHA1Engine::Digest calculated_password_sha1(sha1_size);
|
2021-12-20 12:55:07 +00:00
|
|
|
for (size_t i = 0; i < sha1_size; ++i)
|
2021-08-24 07:09:13 +00:00
|
|
|
calculated_password_sha1[i] = scrambled_password[i] ^ digest[i];
|
|
|
|
|
|
|
|
auto calculated_password_double_sha1 = Util::encodeSHA1(calculated_password_sha1);
|
|
|
|
return calculated_password_double_sha1 == password_double_sha1;
|
|
|
|
}
|
|
|
|
|
2022-07-14 16:11:35 +00:00
|
|
|
bool checkPasswordPlainTextMySQL(std::string_view scramble, std::string_view scrambled_password, const Digest & password_plaintext)
|
2021-08-24 07:09:13 +00:00
|
|
|
{
|
|
|
|
return checkPasswordDoubleSHA1MySQL(scramble, scrambled_password, Util::encodeDoubleSHA1(password_plaintext));
|
2019-12-06 01:30:01 +00:00
|
|
|
}
|
2023-09-26 15:50:19 +00:00
|
|
|
|
2024-01-25 20:20:58 +00:00
|
|
|
#if USE_SSH
|
2024-04-04 20:58:35 +00:00
|
|
|
bool checkSshSignature(const std::vector<SSHKey> & keys, std::string_view signature, std::string_view original)
|
2023-09-26 15:50:19 +00:00
|
|
|
{
|
|
|
|
for (const auto & key: keys)
|
|
|
|
if (key.isPublic() && key.verifySignature(signature, original))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
2019-12-06 01:30:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-20 17:24:19 +00:00
|
|
|
bool Authentication::areCredentialsValid(
|
|
|
|
const Credentials & credentials,
|
|
|
|
const AuthenticationData & auth_data,
|
|
|
|
const ExternalAuthenticators & external_authenticators,
|
|
|
|
SettingsChanges & settings)
|
2019-10-08 13:44:44 +00:00
|
|
|
{
|
2021-03-11 20:41:10 +00:00
|
|
|
if (!credentials.isReady())
|
|
|
|
return false;
|
|
|
|
|
2021-08-24 07:09:13 +00:00
|
|
|
if (const auto * gss_acceptor_context = typeid_cast<const GSSAcceptorContext *>(&credentials))
|
2019-10-08 13:44:44 +00:00
|
|
|
{
|
2021-11-01 14:03:20 +00:00
|
|
|
switch (auth_data.getType())
|
2021-03-11 20:41:10 +00:00
|
|
|
{
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::NO_PASSWORD:
|
|
|
|
case AuthenticationType::PLAINTEXT_PASSWORD:
|
|
|
|
case AuthenticationType::SHA256_PASSWORD:
|
|
|
|
case AuthenticationType::DOUBLE_SHA1_PASSWORD:
|
2023-01-04 14:22:39 +00:00
|
|
|
case AuthenticationType::BCRYPT_PASSWORD:
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::LDAP:
|
2023-10-20 17:24:19 +00:00
|
|
|
case AuthenticationType::HTTP:
|
2021-11-01 14:03:20 +00:00
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
|
2021-03-11 20:41:10 +00:00
|
|
|
|
2024-06-18 18:44:16 +00:00
|
|
|
case AuthenticationType::JWT:
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "JWT is available only in ClickHouse Cloud");
|
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::KERBEROS:
|
|
|
|
return external_authenticators.checkKerberosCredentials(auth_data.getKerberosRealm(), *gss_acceptor_context);
|
2021-03-11 20:41:10 +00:00
|
|
|
|
2022-01-11 14:07:30 +00:00
|
|
|
case AuthenticationType::SSL_CERTIFICATE:
|
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse X.509 Authentication");
|
|
|
|
|
2023-09-26 15:50:19 +00:00
|
|
|
case AuthenticationType::SSH_KEY:
|
2024-04-04 20:58:35 +00:00
|
|
|
#if USE_SSH
|
|
|
|
throw Authentication::Require<SshCredentials>("SSH Keys Authentication");
|
|
|
|
#else
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SSH is disabled, because ClickHouse is built without libssh");
|
|
|
|
#endif
|
2023-09-26 15:50:19 +00:00
|
|
|
|
2021-11-02 09:02:22 +00:00
|
|
|
case AuthenticationType::MAX:
|
2021-03-11 20:41:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-10-08 13:44:44 +00:00
|
|
|
|
2021-08-24 07:09:13 +00:00
|
|
|
if (const auto * mysql_credentials = typeid_cast<const MySQLNative41Credentials *>(&credentials))
|
2021-03-11 20:41:10 +00:00
|
|
|
{
|
2021-11-01 14:03:20 +00:00
|
|
|
switch (auth_data.getType())
|
2019-12-05 00:19:36 +00:00
|
|
|
{
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::NO_PASSWORD:
|
2021-03-11 20:41:10 +00:00
|
|
|
return true; // N.B. even if the password is not empty!
|
2019-10-08 13:44:44 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::PLAINTEXT_PASSWORD:
|
|
|
|
return checkPasswordPlainTextMySQL(mysql_credentials->getScramble(), mysql_credentials->getScrambledPassword(), auth_data.getPasswordHashBinary());
|
2019-12-05 00:35:52 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::DOUBLE_SHA1_PASSWORD:
|
|
|
|
return checkPasswordDoubleSHA1MySQL(mysql_credentials->getScramble(), mysql_credentials->getScrambledPassword(), auth_data.getPasswordHashBinary());
|
2019-10-08 13:44:44 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::SHA256_PASSWORD:
|
2023-01-04 14:22:39 +00:00
|
|
|
case AuthenticationType::BCRYPT_PASSWORD:
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::LDAP:
|
|
|
|
case AuthenticationType::KERBEROS:
|
2023-10-20 17:24:19 +00:00
|
|
|
case AuthenticationType::HTTP:
|
2021-11-01 14:03:20 +00:00
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
|
2019-10-08 13:44:44 +00:00
|
|
|
|
2022-01-11 14:07:30 +00:00
|
|
|
case AuthenticationType::SSL_CERTIFICATE:
|
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse X.509 Authentication");
|
|
|
|
|
2024-06-18 18:44:16 +00:00
|
|
|
case AuthenticationType::JWT:
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "JWT is available only in ClickHouse Cloud");
|
|
|
|
|
2023-09-26 15:50:19 +00:00
|
|
|
case AuthenticationType::SSH_KEY:
|
2024-04-04 20:58:35 +00:00
|
|
|
#if USE_SSH
|
|
|
|
throw Authentication::Require<SshCredentials>("SSH Keys Authentication");
|
|
|
|
#else
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SSH is disabled, because ClickHouse is built without libssh");
|
|
|
|
#endif
|
2023-09-26 15:50:19 +00:00
|
|
|
|
2021-11-02 09:02:22 +00:00
|
|
|
case AuthenticationType::MAX:
|
2021-08-24 07:09:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-10-08 13:44:44 +00:00
|
|
|
|
2021-08-24 07:09:13 +00:00
|
|
|
if (const auto * basic_credentials = typeid_cast<const BasicCredentials *>(&credentials))
|
|
|
|
{
|
2021-11-01 14:03:20 +00:00
|
|
|
switch (auth_data.getType())
|
2021-08-24 07:09:13 +00:00
|
|
|
{
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::NO_PASSWORD:
|
2021-08-24 07:09:13 +00:00
|
|
|
return true; // N.B. even if the password is not empty!
|
2020-05-27 21:06:33 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::PLAINTEXT_PASSWORD:
|
|
|
|
return checkPasswordPlainText(basic_credentials->getPassword(), auth_data.getPasswordHashBinary());
|
2021-08-24 07:09:13 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::SHA256_PASSWORD:
|
2022-04-12 14:30:09 +00:00
|
|
|
return checkPasswordSHA256(basic_credentials->getPassword(), auth_data.getPasswordHashBinary(), auth_data.getSalt());
|
2021-08-24 07:09:13 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::DOUBLE_SHA1_PASSWORD:
|
|
|
|
return checkPasswordDoubleSHA1(basic_credentials->getPassword(), auth_data.getPasswordHashBinary());
|
2020-05-29 06:52:44 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::LDAP:
|
|
|
|
return external_authenticators.checkLDAPCredentials(auth_data.getLDAPServerName(), *basic_credentials);
|
2021-03-11 20:41:10 +00:00
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
case AuthenticationType::KERBEROS:
|
|
|
|
throw Authentication::Require<GSSAcceptorContext>(auth_data.getKerberosRealm());
|
2021-03-11 20:41:10 +00:00
|
|
|
|
2022-01-11 14:07:30 +00:00
|
|
|
case AuthenticationType::SSL_CERTIFICATE:
|
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse X.509 Authentication");
|
|
|
|
|
2023-09-26 15:50:19 +00:00
|
|
|
case AuthenticationType::SSH_KEY:
|
2024-04-04 20:58:35 +00:00
|
|
|
#if USE_SSH
|
|
|
|
throw Authentication::Require<SshCredentials>("SSH Keys Authentication");
|
|
|
|
#else
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SSH is disabled, because ClickHouse is built without libssh");
|
|
|
|
#endif
|
2023-09-26 15:50:19 +00:00
|
|
|
|
2024-06-18 18:44:16 +00:00
|
|
|
case AuthenticationType::JWT:
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "JWT is available only in ClickHouse Cloud");
|
|
|
|
|
2023-05-02 14:29:56 +00:00
|
|
|
case AuthenticationType::BCRYPT_PASSWORD:
|
|
|
|
return checkPasswordBcrypt(basic_credentials->getPassword(), auth_data.getPasswordHashBinary());
|
|
|
|
|
2023-10-20 17:24:19 +00:00
|
|
|
case AuthenticationType::HTTP:
|
|
|
|
switch (auth_data.getHTTPAuthenticationScheme())
|
|
|
|
{
|
|
|
|
case HTTPAuthenticationScheme::BASIC:
|
|
|
|
return external_authenticators.checkHTTPBasicCredentials(
|
|
|
|
auth_data.getHTTPAuthenticationServerName(), *basic_credentials, settings);
|
|
|
|
}
|
|
|
|
|
2022-01-11 14:07:30 +00:00
|
|
|
case AuthenticationType::MAX:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-18 20:26:49 +00:00
|
|
|
if (const auto * ssl_certificate_credentials = typeid_cast<const SSLCertificateCredentials *>(&credentials))
|
2022-01-11 14:07:30 +00:00
|
|
|
{
|
|
|
|
switch (auth_data.getType())
|
|
|
|
{
|
|
|
|
case AuthenticationType::NO_PASSWORD:
|
|
|
|
case AuthenticationType::PLAINTEXT_PASSWORD:
|
|
|
|
case AuthenticationType::SHA256_PASSWORD:
|
|
|
|
case AuthenticationType::DOUBLE_SHA1_PASSWORD:
|
2023-01-04 14:22:39 +00:00
|
|
|
case AuthenticationType::BCRYPT_PASSWORD:
|
2022-01-11 14:07:30 +00:00
|
|
|
case AuthenticationType::LDAP:
|
2023-10-20 17:24:19 +00:00
|
|
|
case AuthenticationType::HTTP:
|
2022-01-11 14:07:30 +00:00
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
|
|
|
|
|
2024-06-18 18:44:16 +00:00
|
|
|
case AuthenticationType::JWT:
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "JWT is available only in ClickHouse Cloud");
|
|
|
|
|
2022-01-11 14:07:30 +00:00
|
|
|
case AuthenticationType::KERBEROS:
|
|
|
|
throw Authentication::Require<GSSAcceptorContext>(auth_data.getKerberosRealm());
|
|
|
|
|
|
|
|
case AuthenticationType::SSL_CERTIFICATE:
|
2024-06-03 15:38:40 +00:00
|
|
|
for (SSLCertificateSubjects::Type type : {SSLCertificateSubjects::Type::CN, SSLCertificateSubjects::Type::SAN})
|
|
|
|
{
|
|
|
|
for (const auto & subject : auth_data.getSSLCertificateSubjects().at(type))
|
|
|
|
{
|
|
|
|
if (ssl_certificate_credentials->getSSLCertificateSubjects().at(type).contains(subject))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2022-01-11 14:07:30 +00:00
|
|
|
|
2023-09-26 15:50:19 +00:00
|
|
|
case AuthenticationType::SSH_KEY:
|
2024-04-04 20:58:35 +00:00
|
|
|
#if USE_SSH
|
|
|
|
throw Authentication::Require<SshCredentials>("SSH Keys Authentication");
|
|
|
|
#else
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "SSH is disabled, because ClickHouse is built without libssh");
|
|
|
|
#endif
|
2023-09-26 15:50:19 +00:00
|
|
|
|
|
|
|
case AuthenticationType::MAX:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-04 20:58:35 +00:00
|
|
|
#if USE_SSH
|
2023-09-26 15:50:19 +00:00
|
|
|
if (const auto * ssh_credentials = typeid_cast<const SshCredentials *>(&credentials))
|
|
|
|
{
|
|
|
|
switch (auth_data.getType())
|
|
|
|
{
|
|
|
|
case AuthenticationType::NO_PASSWORD:
|
|
|
|
case AuthenticationType::PLAINTEXT_PASSWORD:
|
|
|
|
case AuthenticationType::SHA256_PASSWORD:
|
|
|
|
case AuthenticationType::DOUBLE_SHA1_PASSWORD:
|
|
|
|
case AuthenticationType::BCRYPT_PASSWORD:
|
|
|
|
case AuthenticationType::LDAP:
|
2023-10-20 17:24:19 +00:00
|
|
|
case AuthenticationType::HTTP:
|
2023-09-26 15:50:19 +00:00
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
|
|
|
|
|
2024-06-18 18:44:16 +00:00
|
|
|
case AuthenticationType::JWT:
|
|
|
|
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "JWT is available only in ClickHouse Cloud");
|
|
|
|
|
2023-09-26 15:50:19 +00:00
|
|
|
case AuthenticationType::KERBEROS:
|
|
|
|
throw Authentication::Require<GSSAcceptorContext>(auth_data.getKerberosRealm());
|
|
|
|
|
|
|
|
case AuthenticationType::SSL_CERTIFICATE:
|
|
|
|
throw Authentication::Require<SSLCertificateCredentials>("ClickHouse X.509 Authentication");
|
|
|
|
|
|
|
|
case AuthenticationType::SSH_KEY:
|
|
|
|
return checkSshSignature(auth_data.getSSHKeys(), ssh_credentials->getSignature(), ssh_credentials->getOriginal());
|
2021-11-02 09:02:22 +00:00
|
|
|
case AuthenticationType::MAX:
|
2021-03-11 20:41:10 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-10-08 13:44:44 +00:00
|
|
|
}
|
2024-04-04 20:58:35 +00:00
|
|
|
#endif
|
2021-03-11 20:41:10 +00:00
|
|
|
|
2021-08-24 07:09:13 +00:00
|
|
|
if ([[maybe_unused]] const auto * always_allow_credentials = typeid_cast<const AlwaysAllowCredentials *>(&credentials))
|
2021-03-11 20:41:10 +00:00
|
|
|
return true;
|
|
|
|
|
2023-01-23 21:13:58 +00:00
|
|
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "areCredentialsValid(): authentication type {} not supported", toString(auth_data.getType()));
|
2019-10-08 13:44:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|