2021-11-01 14:03:20 +00:00
|
|
|
#include <Access/Authentication.h>
|
|
|
|
#include <Access/Common/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 <Common/Exception.h>
|
|
|
|
#include <Poco/SHA1Engine.h>
|
2021-08-24 07:09:13 +00:00
|
|
|
#include <Common/typeid_cast.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;
|
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
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-01 14:03:20 +00:00
|
|
|
bool Authentication::areCredentialsValid(const Credentials & credentials, const AuthenticationData & auth_data, const ExternalAuthenticators & external_authenticators)
|
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:
|
|
|
|
case AuthenticationType::LDAP:
|
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
|
2021-03-11 20:41:10 +00:00
|
|
|
|
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");
|
|
|
|
|
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:
|
|
|
|
case AuthenticationType::LDAP:
|
|
|
|
case AuthenticationType::KERBEROS:
|
|
|
|
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");
|
|
|
|
|
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");
|
|
|
|
|
|
|
|
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:
|
|
|
|
case AuthenticationType::LDAP:
|
|
|
|
throw Authentication::Require<BasicCredentials>("ClickHouse Basic Authentication");
|
|
|
|
|
|
|
|
case AuthenticationType::KERBEROS:
|
|
|
|
throw Authentication::Require<GSSAcceptorContext>(auth_data.getKerberosRealm());
|
|
|
|
|
|
|
|
case AuthenticationType::SSL_CERTIFICATE:
|
2022-02-18 20:26:49 +00:00
|
|
|
return auth_data.getSSLCertificateCommonNames().contains(ssl_certificate_credentials->getCommonName());
|
2022-01-11 14:07:30 +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-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
|
|
|
}
|
|
|
|
|
|
|
|
}
|