ClickHouse/dbms/src/Interpreters/Users.cpp

324 lines
10 KiB
C++
Raw Normal View History

2017-03-25 05:55:49 +00:00
#include <string.h>
#include <Poco/RegularExpression.h>
#include <Poco/Net/IPAddress.h>
#include <Poco/Net/SocketAddress.h>
#include <Poco/Net/DNS.h>
#include <Poco/Util/Application.h>
#include <Poco/Util/AbstractConfiguration.h>
#include <Poco/String.h>
#include <Common/Exception.h>
#include <IO/ReadHelpers.h>
#include <IO/HexWriteBuffer.h>
#include <IO/WriteBufferFromString.h>
#include <IO/WriteHelpers.h>
#include <Common/SimpleCache.h>
#include <Common/StringUtils/StringUtils.h>
#include <Interpreters/Users.h>
2017-03-25 05:55:49 +00:00
#include <openssl/sha.h>
#include <common/logger_useful.h>
2017-06-06 17:18:32 +00:00
#include <ext/scope_guard.h>
2017-03-25 05:55:49 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int DNS_ERROR;
extern const int UNKNOWN_ADDRESS_PATTERN_TYPE;
extern const int UNKNOWN_USER;
extern const int REQUIRED_PASSWORD;
extern const int WRONG_PASSWORD;
extern const int IP_ADDRESS_NOT_ALLOWED;
2017-04-08 01:32:05 +00:00
extern const int BAD_ARGUMENTS;
2017-03-25 05:55:49 +00:00
}
static Poco::Net::IPAddress toIPv6(const Poco::Net::IPAddress addr)
{
if (addr.family() == Poco::Net::IPAddress::IPv6)
return addr;
2017-03-25 05:55:49 +00:00
return Poco::Net::IPAddress("::FFFF:" + addr.toString());
2017-03-25 05:55:49 +00:00
}
2018-07-13 15:37:09 +00:00
/// IP-address or subnet mask. Example: 213.180.204.3 or 10.0.0.1/8 or 312.234.1.1/255.255.255.0
/// 2a02:6b8::3 or 2a02:6b8::3/64 or 2a02:6b8::3/ffff:ffff:ffff:ffff::
2017-03-25 05:55:49 +00:00
class IPAddressPattern : public IAddressPattern
{
private:
/// Address of mask. Always transformed to IPv6.
Poco::Net::IPAddress mask_address;
2018-07-13 12:18:08 +00:00
/// Mask of net (ip form). Always transformed to IPv6.
2018-07-13 15:37:09 +00:00
Poco::Net::IPAddress subnet_mask;
2017-03-25 05:55:49 +00:00
public:
explicit IPAddressPattern(const String & str)
{
const char * pos = strchr(str.c_str(), '/');
if (nullptr == pos)
{
construct(Poco::Net::IPAddress(str));
}
else
{
String addr(str, 0, pos - str.c_str());
2018-07-12 15:37:26 +00:00
String str_mask(str, addr.length() + 1, str.length() - addr.length() - 1);
2018-07-13 13:35:49 +00:00
if (isDigit(str_mask))
2018-07-12 15:37:26 +00:00
{
2018-07-13 12:18:08 +00:00
UInt8 prefix_bits = parse<UInt8>(pos + 1);
construct(Poco::Net::IPAddress(addr), prefix_bits);
2018-07-12 15:37:26 +00:00
}
else
{
2018-07-13 15:37:09 +00:00
subnet_mask = netmaskToIPv6(Poco::Net::IPAddress(str_mask));
2018-07-12 15:37:26 +00:00
}
}
}
bool contains(const Poco::Net::IPAddress & addr) const override
{
2018-07-13 15:37:09 +00:00
return prefixBitsEquals(addr, mask_address, subnet_mask);
}
2017-03-25 05:55:49 +00:00
private:
void construct(const Poco::Net::IPAddress & mask_address_)
{
2018-07-13 13:35:49 +00:00
mask_address = toIPv6(mask_address_);
2018-07-13 15:37:09 +00:00
subnet_mask = Poco::Net::IPAddress(128, Poco::Net::IPAddress::IPv6);
}
2018-07-13 15:37:09 +00:00
void construct(const Poco::Net::IPAddress & mask_address_, UInt8 prefix_bits)
{
mask_address = toIPv6(mask_address_);
2018-07-13 15:37:09 +00:00
prefix_bits = mask_address_.family() == Poco::Net::IPAddress::IPv4 ? prefix_bits + 96 : prefix_bits;
subnet_mask = Poco::Net::IPAddress(prefix_bits, Poco::Net::IPAddress::IPv6);
}
2018-07-13 12:18:08 +00:00
static bool prefixBitsEquals(const Poco::Net::IPAddress & ip, const Poco::Net::IPAddress & address, const Poco::Net::IPAddress & mask)
{
2018-07-13 15:37:09 +00:00
return ((toIPv6(ip) & mask) == (toIPv6(address) & mask));
2018-07-12 15:37:26 +00:00
}
2018-07-13 12:18:08 +00:00
bool isDigit(const std::string & str)
2018-07-12 15:37:26 +00:00
{
2018-07-13 15:37:09 +00:00
return std::all_of(str.begin(), str.end(), isNumericASCII);
}
Poco::Net::IPAddress netmaskToIPv6(Poco::Net::IPAddress mask)
{
if (mask.family() == Poco::Net::IPAddress::IPv6)
return mask;
return Poco::Net::IPAddress("ffff:ffff:ffff:ffff:" + mask.toString());
}
2017-03-25 05:55:49 +00:00
};
/// Check that address equals to one of hostname addresses.
class HostExactPattern : public IAddressPattern
{
private:
String host;
static bool containsImpl(const String & host, const Poco::Net::IPAddress & addr)
{
Poco::Net::IPAddress addr_v6 = toIPv6(addr);
/// Resolve by hand, because Poco don't use AI_ALL flag but we need it.
addrinfo * ai = nullptr;
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_flags |= AI_V4MAPPED | AI_ALL;
int ret = getaddrinfo(host.c_str(), nullptr, &hints, &ai);
if (0 != ret)
throw Exception("Cannot getaddrinfo: " + std::string(gai_strerror(ret)), ErrorCodes::DNS_ERROR);
SCOPE_EXIT(
{
freeaddrinfo(ai);
});
for (; ai != nullptr; ai = ai->ai_next)
{
if (ai->ai_addrlen && ai->ai_addr)
{
if (ai->ai_family == AF_INET6)
{
if (addr_v6 == Poco::Net::IPAddress(
&reinterpret_cast<sockaddr_in6*>(ai->ai_addr)->sin6_addr, sizeof(in6_addr),
reinterpret_cast<sockaddr_in6*>(ai->ai_addr)->sin6_scope_id))
{
return true;
}
}
else if (ai->ai_family == AF_INET)
{
if (addr_v6 == toIPv6(Poco::Net::IPAddress(
&reinterpret_cast<sockaddr_in*>(ai->ai_addr)->sin_addr, sizeof(in_addr))))
{
return true;
}
}
}
}
return false;
}
2017-03-25 05:55:49 +00:00
public:
explicit HostExactPattern(const String & host_) : host(host_) {}
2017-03-25 05:55:49 +00:00
bool contains(const Poco::Net::IPAddress & addr) const override
{
static SimpleCache<decltype(containsImpl), &containsImpl> cache;
return cache(host, addr);
}
2017-03-25 05:55:49 +00:00
};
/// Check that PTR record for address match the regexp (and in addition, check that PTR record is resolved back to client address).
class HostRegexpPattern : public IAddressPattern
{
private:
Poco::RegularExpression host_regexp;
2017-03-25 05:55:49 +00:00
static String getDomain(const Poco::Net::IPAddress & addr)
{
Poco::Net::SocketAddress sock_addr(addr, 0);
2017-03-25 05:55:49 +00:00
/// Resolve by hand, because Poco library doesn't have such functionality.
char domain[1024];
int gai_errno = getnameinfo(sock_addr.addr(), sock_addr.length(), domain, sizeof(domain), nullptr, 0, NI_NAMEREQD);
if (0 != gai_errno)
throw Exception("Cannot getnameinfo: " + std::string(gai_strerror(gai_errno)), ErrorCodes::DNS_ERROR);
2017-03-25 05:55:49 +00:00
return domain;
}
2017-03-25 05:55:49 +00:00
public:
explicit HostRegexpPattern(const String & host_regexp_) : host_regexp(host_regexp_) {}
2017-03-25 05:55:49 +00:00
bool contains(const Poco::Net::IPAddress & addr) const override
{
static SimpleCache<decltype(getDomain), &getDomain> cache;
2017-03-25 05:55:49 +00:00
String domain = cache(addr);
Poco::RegularExpression::Match match;
2017-03-25 05:55:49 +00:00
if (host_regexp.match(domain, match) && HostExactPattern(domain).contains(addr))
return true;
2017-03-25 05:55:49 +00:00
return false;
}
2017-03-25 05:55:49 +00:00
};
bool AddressPatterns::contains(const Poco::Net::IPAddress & addr) const
{
for (size_t i = 0, size = patterns.size(); i < size; ++i)
{
/// If host cannot be resolved, skip it and try next.
try
{
if (patterns[i]->contains(addr))
return true;
}
catch (const DB::Exception & e)
{
LOG_WARNING(&Logger::get("AddressPatterns"),
"Failed to check if pattern contains address " << addr.toString() << ". " << e.displayText() << ", code = " << e.code());
if (e.code() == ErrorCodes::DNS_ERROR)
{
continue;
}
else
throw;
}
}
return false;
2017-03-25 05:55:49 +00:00
}
void AddressPatterns::addFromConfig(const String & config_elem, Poco::Util::AbstractConfiguration & config)
{
Poco::Util::AbstractConfiguration::Keys config_keys;
config.keys(config_elem, config_keys);
for (Poco::Util::AbstractConfiguration::Keys::const_iterator it = config_keys.begin(); it != config_keys.end(); ++it)
{
Container::value_type pattern;
String value = config.getString(config_elem + "." + *it);
if (startsWith(*it, "ip"))
pattern = std::make_unique<IPAddressPattern>(value);
else if (startsWith(*it, "host_regexp"))
pattern = std::make_unique<HostRegexpPattern>(value);
else if (startsWith(*it, "host"))
pattern = std::make_unique<HostExactPattern>(value);
else
throw Exception("Unknown address pattern type: " + *it, ErrorCodes::UNKNOWN_ADDRESS_PATTERN_TYPE);
patterns.emplace_back(std::move(pattern));
}
2017-03-25 05:55:49 +00:00
}
User::User(const String & name_, const String & config_elem, Poco::Util::AbstractConfiguration & config)
: name(name_)
2017-03-25 05:55:49 +00:00
{
bool has_password = config.has(config_elem + ".password");
bool has_password_sha256_hex = config.has(config_elem + ".password_sha256_hex");
2017-03-25 05:55:49 +00:00
if (has_password && has_password_sha256_hex)
throw Exception("Both fields 'password' and 'password_sha256_hex' are specified for user " + name + ". Must be only one of them.", ErrorCodes::BAD_ARGUMENTS);
2017-03-25 05:55:49 +00:00
if (!has_password && !has_password_sha256_hex)
throw Exception("Either 'password' or 'password_sha256_hex' must be specified for user " + name + ".", ErrorCodes::BAD_ARGUMENTS);
2017-03-25 05:55:49 +00:00
if (has_password)
password = config.getString(config_elem + ".password");
2017-03-25 05:55:49 +00:00
if (has_password_sha256_hex)
{
password_sha256_hex = Poco::toLower(config.getString(config_elem + ".password_sha256_hex"));
2017-03-25 05:55:49 +00:00
if (password_sha256_hex.size() != 64)
throw Exception("password_sha256_hex for user " + name + " has length " + toString(password_sha256_hex.size()) + " but must be exactly 64 symbols.", ErrorCodes::BAD_ARGUMENTS);
}
2017-03-25 05:55:49 +00:00
2017-09-08 16:46:56 +00:00
profile = config.getString(config_elem + ".profile");
quota = config.getString(config_elem + ".quota");
2017-03-25 05:55:49 +00:00
addresses.addFromConfig(config_elem + ".networks", config);
2017-03-25 05:55:49 +00:00
/// Fill list of allowed databases.
const auto config_sub_elem = config_elem + ".allow_databases";
if (config.has(config_sub_elem))
{
Poco::Util::AbstractConfiguration::Keys config_keys;
config.keys(config_sub_elem, config_keys);
2017-03-25 05:55:49 +00:00
databases.reserve(config_keys.size());
for (const auto & key : config_keys)
{
const auto database_name = config.getString(config_sub_elem + "." + key);
databases.insert(database_name);
}
}
2017-03-25 05:55:49 +00:00
}
}