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>
2017-04-01 09:19:00 +00:00
# include <Common/Exception.h>
# include <IO/ReadHelpers.h>
# include <IO/HexWriteBuffer.h>
# include <IO/WriteBufferFromString.h>
# include <IO/WriteHelpers.h>
# include <Common/SimpleCache.h>
2018-01-15 19:07:47 +00:00
# include <Common/StringUtils/StringUtils.h>
2017-04-01 09:19:00 +00:00
# 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
{
2017-04-01 07:20:54 +00:00
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 )
{
2017-04-01 07:20:54 +00:00
if ( addr . family ( ) = = Poco : : Net : : IPAddress : : IPv6 )
return addr ;
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +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 :
2017-04-01 07:20:54 +00:00
/// 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 :
2017-04-01 07:20:54 +00:00
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
}
2017-04-01 07:20:54 +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-04-01 07:20:54 +00:00
}
2017-03-25 05:55:49 +00:00
private :
2017-04-01 07:20:54 +00:00
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 ) ;
2017-04-01 07:20:54 +00:00
}
2018-07-13 15:37:09 +00:00
void construct ( const Poco : : Net : : IPAddress & mask_address_ , UInt8 prefix_bits )
2017-04-01 07:20:54 +00:00
{
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 ) ;
2017-04-01 07:20:54 +00:00
}
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 )
2017-04-01 07:20:54 +00:00
{
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-04-01 07:20:54 +00:00
}
2017-03-25 05:55:49 +00:00
} ;
/// Check that address equals to one of hostname addresses.
class HostExactPattern : public IAddressPattern
{
private :
2017-04-01 07:20:54 +00:00
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 :
2017-09-07 21:04:48 +00:00
explicit HostExactPattern ( const String & host_ ) : host ( host_ ) { }
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +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 :
2017-04-01 07:20:54 +00:00
Poco : : RegularExpression host_regexp ;
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +00:00
return domain ;
}
2017-03-25 05:55:49 +00:00
public :
2017-09-07 21:04:48 +00:00
explicit HostRegexpPattern ( const String & host_regexp_ ) : host_regexp ( host_regexp_ ) { }
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +00:00
String domain = cache ( addr ) ;
Poco : : RegularExpression : : Match match ;
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +00:00
if ( host_regexp . match ( domain , match ) & & HostExactPattern ( domain ) . contains ( addr ) )
return true ;
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +00:00
return false ;
}
2017-03-25 05:55:49 +00:00
} ;
bool AddressPatterns : : contains ( const Poco : : Net : : IPAddress & addr ) const
{
2017-04-01 07:20:54 +00:00
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 )
{
2017-04-01 07:20:54 +00:00
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 )
2017-04-01 07:20:54 +00:00
: name ( name_ )
2017-03-25 05:55:49 +00:00
{
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +00:00
if ( has_password )
password = config . getString ( config_elem + " .password " ) ;
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +00:00
addresses . addFromConfig ( config_elem + " .networks " , config ) ;
2017-03-25 05:55:49 +00:00
2017-04-01 07:20:54 +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
2017-04-01 07:20:54 +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
}
}