2019-10-08 13:44:44 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <Core/Types.h>
|
2020-02-06 01:48:14 +00:00
|
|
|
#include <Common/Exception.h>
|
|
|
|
#include <Common/OpenSSLHelpers.h>
|
|
|
|
#include <Poco/SHA1Engine.h>
|
|
|
|
#include <boost/algorithm/hex.hpp>
|
2019-10-08 13:44:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
2020-02-06 01:48:14 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int SUPPORT_IS_DISABLED;
|
|
|
|
extern const int BAD_ARGUMENTS;
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-08 13:44:44 +00:00
|
|
|
/// Authentication type and encrypted password for checking when an user logins.
|
|
|
|
class Authentication
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum Type
|
|
|
|
{
|
|
|
|
/// User doesn't have to enter password.
|
|
|
|
NO_PASSWORD,
|
|
|
|
|
|
|
|
/// Password is stored as is.
|
|
|
|
PLAINTEXT_PASSWORD,
|
|
|
|
|
|
|
|
/// Password is encrypted in SHA256 hash.
|
|
|
|
SHA256_PASSWORD,
|
|
|
|
|
|
|
|
/// SHA1(SHA1(password)).
|
|
|
|
/// This kind of hash is used by the `mysql_native_password` authentication plugin.
|
|
|
|
DOUBLE_SHA1_PASSWORD,
|
|
|
|
};
|
|
|
|
|
2020-01-03 14:29:31 +00:00
|
|
|
using Digest = std::vector<uint8_t>;
|
2019-10-08 13:44:44 +00:00
|
|
|
|
2020-02-06 01:48:14 +00:00
|
|
|
Authentication(Authentication::Type type_ = NO_PASSWORD) : type(type_) {}
|
2019-10-08 13:44:44 +00:00
|
|
|
Authentication(const Authentication & src) = default;
|
|
|
|
Authentication & operator =(const Authentication & src) = default;
|
|
|
|
Authentication(Authentication && src) = default;
|
|
|
|
Authentication & operator =(Authentication && src) = default;
|
|
|
|
|
|
|
|
Type getType() const { return type; }
|
|
|
|
|
2019-10-10 21:48:36 +00:00
|
|
|
/// Sets the password and encrypt it using the authentication type set in the constructor.
|
2020-02-06 01:48:14 +00:00
|
|
|
void setPassword(const String & password_);
|
2019-10-08 13:44:44 +00:00
|
|
|
|
|
|
|
/// Returns the password. Allowed to use only for Type::PLAINTEXT_PASSWORD.
|
|
|
|
String getPassword() const;
|
|
|
|
|
|
|
|
/// Sets the password as a string of hexadecimal digits.
|
|
|
|
void setPasswordHashHex(const String & hash);
|
2020-02-06 01:48:14 +00:00
|
|
|
|
2019-10-08 13:44:44 +00:00
|
|
|
String getPasswordHashHex() const;
|
|
|
|
|
|
|
|
/// Sets the password in binary form.
|
|
|
|
void setPasswordHashBinary(const Digest & hash);
|
2020-02-06 01:48:14 +00:00
|
|
|
|
2019-10-08 13:44:44 +00:00
|
|
|
const Digest & getPasswordHashBinary() const { return password_hash; }
|
|
|
|
|
2019-12-06 01:30:01 +00:00
|
|
|
/// Returns SHA1(SHA1(password)) used by MySQL compatibility server for authentication.
|
|
|
|
/// Allowed to use for Type::NO_PASSWORD, Type::PLAINTEXT_PASSWORD, Type::DOUBLE_SHA1_PASSWORD.
|
|
|
|
Digest getPasswordDoubleSHA1() const;
|
|
|
|
|
2019-10-08 13:44:44 +00:00
|
|
|
/// Checks if the provided password is correct. Returns false if not.
|
|
|
|
bool isCorrectPassword(const String & password) const;
|
|
|
|
|
2020-02-06 01:48:14 +00:00
|
|
|
friend bool operator ==(const Authentication & lhs, const Authentication & rhs) { return (lhs.type == rhs.type) && (lhs.password_hash == rhs.password_hash); }
|
2019-10-08 13:44:44 +00:00
|
|
|
friend bool operator !=(const Authentication & lhs, const Authentication & rhs) { return !(lhs == rhs); }
|
|
|
|
|
|
|
|
private:
|
2020-02-06 01:48:14 +00:00
|
|
|
static Digest encodePlainText(const std::string_view & text) { return Digest(text.data(), text.data() + text.size()); }
|
|
|
|
static Digest encodeSHA256(const std::string_view & text);
|
|
|
|
static Digest encodeSHA1(const std::string_view & text);
|
|
|
|
static Digest encodeSHA1(const Digest & text) { return encodeSHA1(std::string_view{reinterpret_cast<const char *>(text.data()), text.size()}); }
|
|
|
|
static Digest encodeDoubleSHA1(const std::string_view & text) { return encodeSHA1(encodeSHA1(text)); }
|
|
|
|
|
2019-10-08 13:44:44 +00:00
|
|
|
Type type = Type::NO_PASSWORD;
|
|
|
|
Digest password_hash;
|
|
|
|
};
|
2020-02-06 01:48:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
inline Authentication::Digest Authentication::encodeSHA256(const std::string_view & text [[maybe_unused]])
|
|
|
|
{
|
|
|
|
#if USE_SSL
|
|
|
|
Digest hash;
|
|
|
|
hash.resize(32);
|
|
|
|
::DB::encodeSHA256(text, hash.data());
|
|
|
|
return hash;
|
|
|
|
#else
|
|
|
|
throw DB::Exception(
|
|
|
|
"SHA256 passwords support is disabled, because ClickHouse was built without SSL library",
|
|
|
|
DB::ErrorCodes::SUPPORT_IS_DISABLED);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Authentication::Digest Authentication::encodeSHA1(const std::string_view & text)
|
|
|
|
{
|
|
|
|
Poco::SHA1Engine engine;
|
|
|
|
engine.update(text.data(), text.size());
|
|
|
|
return engine.digest();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline void Authentication::setPassword(const String & password_)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case NO_PASSWORD:
|
|
|
|
throw Exception("Cannot specify password for the 'NO_PASSWORD' authentication type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
case PLAINTEXT_PASSWORD:
|
|
|
|
return setPasswordHashBinary(encodePlainText(password_));
|
|
|
|
|
|
|
|
case SHA256_PASSWORD:
|
|
|
|
return setPasswordHashBinary(encodeSHA256(password_));
|
|
|
|
|
|
|
|
case DOUBLE_SHA1_PASSWORD:
|
|
|
|
return setPasswordHashBinary(encodeDoubleSHA1(password_));
|
|
|
|
}
|
|
|
|
throw Exception("Unknown authentication type: " + std::to_string(static_cast<int>(type)), ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline String Authentication::getPassword() const
|
|
|
|
{
|
|
|
|
if (type != PLAINTEXT_PASSWORD)
|
|
|
|
throw Exception("Cannot decode the password", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
return String(password_hash.data(), password_hash.data() + password_hash.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline void Authentication::setPasswordHashHex(const String & hash)
|
|
|
|
{
|
|
|
|
Digest digest;
|
|
|
|
digest.resize(hash.size() / 2);
|
|
|
|
boost::algorithm::unhex(hash.begin(), hash.end(), digest.data());
|
|
|
|
setPasswordHashBinary(digest);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline String Authentication::getPasswordHashHex() const
|
|
|
|
{
|
|
|
|
String hex;
|
|
|
|
hex.resize(password_hash.size() * 2);
|
|
|
|
boost::algorithm::hex(password_hash.begin(), password_hash.end(), hex.data());
|
|
|
|
return hex;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline void Authentication::setPasswordHashBinary(const Digest & hash)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case NO_PASSWORD:
|
|
|
|
throw Exception("Cannot specify password for the 'NO_PASSWORD' authentication type", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
case PLAINTEXT_PASSWORD:
|
|
|
|
{
|
|
|
|
password_hash = hash;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SHA256_PASSWORD:
|
|
|
|
{
|
|
|
|
if (hash.size() != 32)
|
|
|
|
throw Exception(
|
|
|
|
"Password hash for the 'SHA256_PASSWORD' authentication type has length " + std::to_string(hash.size())
|
|
|
|
+ " but must be exactly 32 bytes.",
|
|
|
|
ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
password_hash = hash;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case DOUBLE_SHA1_PASSWORD:
|
|
|
|
{
|
|
|
|
if (hash.size() != 20)
|
|
|
|
throw Exception(
|
|
|
|
"Password hash for the 'DOUBLE_SHA1_PASSWORD' authentication type has length " + std::to_string(hash.size())
|
|
|
|
+ " but must be exactly 20 bytes.",
|
|
|
|
ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
password_hash = hash;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw Exception("Unknown authentication type: " + std::to_string(static_cast<int>(type)), ErrorCodes::LOGICAL_ERROR);
|
|
|
|
}
|
|
|
|
|
2019-10-08 13:44:44 +00:00
|
|
|
}
|