2019-03-16 02:08:21 +00:00
# pragma once
2019-07-28 13:12:26 +00:00
# include <ext/scope_guard.h>
# include <random>
# include <sstream>
2019-07-01 05:58:31 +00:00
# include <Common/MemoryTracker.h>
2019-07-28 13:12:26 +00:00
# include <Common/OpenSSLHelpers.h>
2019-07-01 05:58:31 +00:00
# include <Common/PODArray.h>
2019-05-26 06:52:29 +00:00
# include <Core/Types.h>
2019-07-28 13:12:26 +00:00
# include <Interpreters/Context.h>
2020-02-04 22:29:28 +00:00
# include <Access/AccessControlManager.h>
2020-01-26 09:49:53 +00:00
# include <Access/User.h>
2019-03-26 18:30:41 +00:00
# include <IO/copyData.h>
2019-07-28 13:12:26 +00:00
# include <IO/LimitReadBuffer.h>
2019-05-26 06:52:29 +00:00
# include <IO/ReadBuffer.h>
2019-07-01 05:58:31 +00:00
# include <IO/ReadBufferFromMemory.h>
2019-04-29 06:05:30 +00:00
# include <IO/ReadBufferFromPocoSocket.h>
2019-07-01 05:58:31 +00:00
# include <IO/ReadHelpers.h>
2019-05-26 06:52:29 +00:00
# include <IO/WriteBuffer.h>
2019-04-29 06:05:30 +00:00
# include <IO/WriteBufferFromPocoSocket.h>
2019-05-16 03:34:04 +00:00
# include <IO/WriteBufferFromString.h>
2019-07-01 05:58:31 +00:00
# include <IO/WriteHelpers.h>
2019-04-29 06:05:30 +00:00
# include <Poco/Net/StreamSocket.h>
2019-05-26 06:52:29 +00:00
# include <Poco/RandomStream.h>
2019-07-28 13:12:26 +00:00
# include <Poco/SHA1Engine.h>
2019-11-02 10:20:46 +00:00
# include "config_core.h"
# if USE_SSL
# include <openssl/pem.h>
# include <openssl/rsa.h>
# endif
2019-03-16 02:08:21 +00:00
2019-07-19 19:29:44 +00:00
/// Implementation of MySQL wire protocol.
/// Works only on little-endian architecture.
2019-03-16 02:08:21 +00:00
2019-03-26 18:30:41 +00:00
namespace DB
{
namespace ErrorCodes
{
2020-02-25 18:02:41 +00:00
extern const int CANNOT_WRITE_AFTER_END_OF_BUFFER ;
2019-03-26 18:30:41 +00:00
extern const int UNKNOWN_PACKET_FROM_CLIENT ;
2019-07-28 13:12:26 +00:00
extern const int MYSQL_CLIENT_INSUFFICIENT_CAPABILITIES ;
extern const int OPENSSL_ERROR ;
extern const int UNKNOWN_EXCEPTION ;
2019-03-26 18:30:41 +00:00
}
namespace MySQLProtocol
{
2019-03-16 02:08:21 +00:00
const size_t MAX_PACKET_LENGTH = ( 1 < < 24 ) - 1 ; // 16 mb
const size_t SCRAMBLE_LENGTH = 20 ;
const size_t AUTH_PLUGIN_DATA_PART_1_LENGTH = 8 ;
const size_t MYSQL_ERRMSG_SIZE = 512 ;
2019-05-16 17:15:43 +00:00
const size_t PACKET_HEADER_SIZE = 4 ;
const size_t SSL_REQUEST_PAYLOAD_SIZE = 32 ;
2019-03-16 02:08:21 +00:00
2019-03-26 18:30:41 +00:00
enum CharacterSet
{
utf8_general_ci = 33 ,
binary = 63
2019-03-16 02:08:21 +00:00
} ;
2019-03-26 18:30:41 +00:00
enum StatusFlags
{
2019-03-16 02:08:21 +00:00
SERVER_SESSION_STATE_CHANGED = 0x4000
} ;
2019-03-26 18:30:41 +00:00
enum Capability
{
2019-03-16 02:08:21 +00:00
CLIENT_CONNECT_WITH_DB = 0x00000008 ,
CLIENT_PROTOCOL_41 = 0x00000200 ,
2019-04-29 06:05:30 +00:00
CLIENT_SSL = 0x00000800 ,
2019-03-16 02:08:21 +00:00
CLIENT_TRANSACTIONS = 0x00002000 , // TODO
CLIENT_SESSION_TRACK = 0x00800000 , // TODO
CLIENT_SECURE_CONNECTION = 0x00008000 ,
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x00200000 ,
CLIENT_PLUGIN_AUTH = 0x00080000 ,
CLIENT_DEPRECATE_EOF = 0x01000000 ,
} ;
2019-03-26 18:30:41 +00:00
enum Command
{
2019-03-16 02:08:21 +00:00
COM_SLEEP = 0x0 ,
COM_QUIT = 0x1 ,
COM_INIT_DB = 0x2 ,
COM_QUERY = 0x3 ,
COM_FIELD_LIST = 0x4 ,
COM_CREATE_DB = 0x5 ,
COM_DROP_DB = 0x6 ,
COM_REFRESH = 0x7 ,
COM_SHUTDOWN = 0x8 ,
COM_STATISTICS = 0x9 ,
COM_PROCESS_INFO = 0xa ,
COM_CONNECT = 0xb ,
COM_PROCESS_KILL = 0xc ,
COM_DEBUG = 0xd ,
COM_PING = 0xe ,
COM_TIME = 0xf ,
COM_DELAYED_INSERT = 0x10 ,
COM_CHANGE_USER = 0x11 ,
COM_RESET_CONNECTION = 0x1f ,
COM_DAEMON = 0x1d
} ;
2019-03-26 18:30:41 +00:00
enum ColumnType
{
2019-03-16 02:08:21 +00:00
MYSQL_TYPE_DECIMAL = 0x00 ,
MYSQL_TYPE_TINY = 0x01 ,
MYSQL_TYPE_SHORT = 0x02 ,
MYSQL_TYPE_LONG = 0x03 ,
MYSQL_TYPE_FLOAT = 0x04 ,
MYSQL_TYPE_DOUBLE = 0x05 ,
MYSQL_TYPE_NULL = 0x06 ,
MYSQL_TYPE_TIMESTAMP = 0x07 ,
MYSQL_TYPE_LONGLONG = 0x08 ,
MYSQL_TYPE_INT24 = 0x09 ,
MYSQL_TYPE_DATE = 0x0a ,
MYSQL_TYPE_TIME = 0x0b ,
MYSQL_TYPE_DATETIME = 0x0c ,
MYSQL_TYPE_YEAR = 0x0d ,
MYSQL_TYPE_VARCHAR = 0x0f ,
MYSQL_TYPE_BIT = 0x10 ,
MYSQL_TYPE_NEWDECIMAL = 0xf6 ,
MYSQL_TYPE_ENUM = 0xf7 ,
MYSQL_TYPE_SET = 0xf8 ,
MYSQL_TYPE_TINY_BLOB = 0xf9 ,
MYSQL_TYPE_MEDIUM_BLOB = 0xfa ,
MYSQL_TYPE_LONG_BLOB = 0xfb ,
MYSQL_TYPE_BLOB = 0xfc ,
MYSQL_TYPE_VAR_STRING = 0xfd ,
MYSQL_TYPE_STRING = 0xfe ,
MYSQL_TYPE_GEOMETRY = 0xff
} ;
2019-12-01 11:21:43 +00:00
// https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__column__definition__flags.html
enum ColumnDefinitionFlags
{
UNSIGNED_FLAG = 32 ,
BINARY_FLAG = 128
} ;
2019-03-26 18:30:41 +00:00
class ProtocolError : public DB : : Exception
{
public :
using Exception : : Exception ;
} ;
2019-07-16 06:39:18 +00:00
/** Reading packets.
* Internally , it calls ( if no more data ) next ( ) method of the underlying ReadBufferFromPocoSocket , and sets the working buffer to the rest part of the current packet payload .
*/
2019-07-14 22:13:56 +00:00
class PacketPayloadReadBuffer : public ReadBuffer
{
public :
2019-08-03 11:02:40 +00:00
PacketPayloadReadBuffer ( ReadBuffer & in_ , uint8_t & sequence_id_ )
: ReadBuffer ( in_ . position ( ) , 0 ) // not in.buffer().begin(), because working buffer may include previous packet
, in ( in_ )
, sequence_id ( sequence_id_ )
2019-07-14 22:53:30 +00:00
{
2019-07-14 22:13:56 +00:00
}
2019-07-19 17:50:42 +00:00
2019-07-14 22:13:56 +00:00
private :
ReadBuffer & in ;
2019-07-16 06:39:18 +00:00
uint8_t & sequence_id ;
2019-07-14 22:13:56 +00:00
const size_t max_packet_size = MAX_PACKET_LENGTH ;
2019-07-28 13:12:26 +00:00
bool has_read_header = false ;
2019-07-14 22:13:56 +00:00
// Size of packet which is being read now.
size_t payload_length = 0 ;
// Offset in packet payload.
size_t offset = 0 ;
2019-07-16 06:39:18 +00:00
2019-07-14 22:13:56 +00:00
protected :
bool nextImpl ( ) override
{
2019-07-28 13:12:26 +00:00
if ( ! has_read_header | | ( payload_length = = max_packet_size & & offset = = payload_length ) )
2019-07-14 22:13:56 +00:00
{
2019-07-28 13:12:26 +00:00
has_read_header = true ;
2019-07-14 22:13:56 +00:00
working_buffer . resize ( 0 ) ;
offset = 0 ;
payload_length = 0 ;
in . readStrict ( reinterpret_cast < char * > ( & payload_length ) , 3 ) ;
if ( payload_length > max_packet_size )
{
std : : ostringstream tmp ;
tmp < < " Received packet with payload larger than max_packet_size: " < < payload_length ;
throw ProtocolError ( tmp . str ( ) , ErrorCodes : : UNKNOWN_PACKET_FROM_CLIENT ) ;
}
size_t packet_sequence_id = 0 ;
in . read ( reinterpret_cast < char & > ( packet_sequence_id ) ) ;
if ( packet_sequence_id ! = sequence_id )
{
std : : ostringstream tmp ;
2019-07-16 06:39:18 +00:00
tmp < < " Received packet with wrong sequence-id: " < < packet_sequence_id < < " . Expected: " < < static_cast < unsigned int > ( sequence_id ) < < ' . ' ;
2019-07-14 22:13:56 +00:00
throw ProtocolError ( tmp . str ( ) , ErrorCodes : : UNKNOWN_PACKET_FROM_CLIENT ) ;
}
sequence_id + + ;
2019-07-28 13:12:26 +00:00
if ( payload_length = = 0 )
return false ;
2019-07-14 22:13:56 +00:00
}
else if ( offset = = payload_length )
{
return false ;
}
in . nextIfAtEnd ( ) ;
working_buffer = ReadBuffer : : Buffer ( in . position ( ) , in . buffer ( ) . end ( ) ) ;
size_t count = std : : min ( in . available ( ) , payload_length - offset ) ;
working_buffer . resize ( count ) ;
in . ignore ( count ) ;
offset + = count ;
return true ;
}
} ;
2019-07-19 17:50:42 +00:00
class ClientPacket
{
public :
ClientPacket ( ) = default ;
2019-07-28 13:12:26 +00:00
2019-07-19 17:50:42 +00:00
ClientPacket ( ClientPacket & & ) = default ;
virtual void read ( ReadBuffer & in , uint8_t & sequence_id )
{
PacketPayloadReadBuffer payload ( in , sequence_id ) ;
readPayload ( payload ) ;
2019-07-19 19:29:44 +00:00
if ( ! payload . eof ( ) )
{
std : : stringstream tmp ;
tmp < < " Packet payload is not fully read. Stopped after " < < payload . count ( ) < < " bytes, while " < < payload . available ( ) < < " bytes are in buffer. " ;
throw ProtocolError ( tmp . str ( ) , ErrorCodes : : UNKNOWN_PACKET_FROM_CLIENT ) ;
}
2019-07-19 17:50:42 +00:00
}
virtual void readPayload ( ReadBuffer & buf ) = 0 ;
virtual ~ ClientPacket ( ) = default ;
} ;
class LimitedClientPacket : public ClientPacket
{
public :
void read ( ReadBuffer & in , uint8_t & sequence_id ) override
{
LimitReadBuffer limited ( in , 10000 , true , " too long MySQL packet. " ) ;
ClientPacket : : read ( limited , sequence_id ) ;
}
} ;
2019-07-16 06:39:18 +00:00
/** Writing packets.
* https : //dev.mysql.com/doc/internals/en/mysql-packet.html
*/
2019-07-15 20:37:01 +00:00
class PacketPayloadWriteBuffer : public WriteBuffer
{
2019-07-16 06:39:18 +00:00
public :
2019-08-03 11:02:40 +00:00
PacketPayloadWriteBuffer ( WriteBuffer & out_ , size_t payload_length_ , uint8_t & sequence_id_ )
: WriteBuffer ( out_ . position ( ) , 0 ) , out ( out_ ) , sequence_id ( sequence_id_ ) , total_left ( payload_length_ )
2019-07-16 06:39:18 +00:00
{
2019-07-29 15:41:47 +00:00
startNewPacket ( ) ;
setWorkingBuffer ( ) ;
pos = out . position ( ) ;
2019-07-16 06:39:18 +00:00
}
2019-07-29 15:41:47 +00:00
bool remainingPayloadSize ( )
2019-07-16 06:39:18 +00:00
{
2019-07-29 15:41:47 +00:00
return total_left ;
2019-07-16 06:39:18 +00:00
}
2019-07-15 20:37:01 +00:00
private :
2019-07-16 06:39:18 +00:00
WriteBuffer & out ;
uint8_t & sequence_id ;
size_t total_left = 0 ;
size_t payload_length = 0 ;
size_t bytes_written = 0 ;
2019-07-29 15:41:47 +00:00
bool eof = false ;
2019-07-16 06:39:18 +00:00
2019-07-29 15:41:47 +00:00
void startNewPacket ( )
2019-07-16 06:39:18 +00:00
{
payload_length = std : : min ( total_left , MAX_PACKET_LENGTH ) ;
bytes_written = 0 ;
total_left - = payload_length ;
2019-07-15 20:37:01 +00:00
2019-07-16 06:39:18 +00:00
out . write ( reinterpret_cast < char * > ( & payload_length ) , 3 ) ;
out . write ( sequence_id + + ) ;
2019-07-29 15:41:47 +00:00
bytes + = 4 ;
}
2019-07-16 06:39:18 +00:00
2019-07-29 15:41:47 +00:00
/// Sets working buffer to the rest of current packet payload.
void setWorkingBuffer ( )
{
out . nextIfAtEnd ( ) ;
2019-07-16 06:39:18 +00:00
working_buffer = WriteBuffer : : Buffer ( out . position ( ) , out . position ( ) + std : : min ( payload_length - bytes_written , out . available ( ) ) ) ;
2019-07-29 15:41:47 +00:00
if ( payload_length - bytes_written = = 0 )
{
/// Finished writing packet. Due to an implementation of WriteBuffer, working_buffer cannot be empty. Further write attempts will throw Exception.
eof = true ;
working_buffer . resize ( 1 ) ;
}
2019-07-16 06:39:18 +00:00
}
2019-07-28 13:12:26 +00:00
2019-07-15 20:37:01 +00:00
protected :
void nextImpl ( ) override
{
2019-07-29 15:41:47 +00:00
const int written = pos - working_buffer . begin ( ) ;
if ( eof )
throw Exception ( " Cannot write after end of buffer. " , ErrorCodes : : CANNOT_WRITE_AFTER_END_OF_BUFFER ) ;
2019-07-16 06:39:18 +00:00
out . position ( ) + = written ;
bytes_written + = written ;
2019-07-15 20:37:01 +00:00
2019-07-29 15:41:47 +00:00
/// Packets of size greater than MAX_PACKET_LENGTH are split into few packets of size MAX_PACKET_LENGTH and las packet of size < MAX_PACKET_LENGTH.
if ( bytes_written = = payload_length & & ( total_left > 0 | | payload_length = = MAX_PACKET_LENGTH ) )
startNewPacket ( ) ;
setWorkingBuffer ( ) ;
2019-07-15 20:37:01 +00:00
}
} ;
2019-07-16 06:39:18 +00:00
class WritePacket
{
public :
virtual void writePayload ( WriteBuffer & buffer , uint8_t & sequence_id ) const
{
PacketPayloadWriteBuffer buf ( buffer , getPayloadSize ( ) , sequence_id ) ;
writePayloadImpl ( buf ) ;
2019-07-29 15:41:47 +00:00
buf . next ( ) ;
if ( buf . remainingPayloadSize ( ) )
{
std : : stringstream ss ;
ss < < " Incomplete payload. Written " < < getPayloadSize ( ) - buf . remainingPayloadSize ( ) < < " bytes, expected " < < getPayloadSize ( ) < < " bytes. " ;
throw Exception ( ss . str ( ) , 0 ) ;
}
2019-07-16 06:39:18 +00:00
}
virtual ~ WritePacket ( ) = default ;
protected :
virtual size_t getPayloadSize ( ) const = 0 ;
virtual void writePayloadImpl ( WriteBuffer & buffer ) const = 0 ;
} ;
2019-03-26 18:30:41 +00:00
/* Writes and reads packets, keeping sequence-id.
* Throws ProtocolError , if packet with incorrect sequence - id was received .
*/
class PacketSender
{
public :
2019-07-16 06:39:18 +00:00
uint8_t & sequence_id ;
2019-05-16 03:34:04 +00:00
ReadBuffer * in ;
WriteBuffer * out ;
2019-05-16 17:15:43 +00:00
size_t max_packet_size = MAX_PACKET_LENGTH ;
2019-03-26 18:30:41 +00:00
2019-05-16 03:34:04 +00:00
/// For reading and writing.
2019-08-03 11:02:40 +00:00
PacketSender ( ReadBuffer & in_ , WriteBuffer & out_ , uint8_t & sequence_id_ )
: sequence_id ( sequence_id_ )
, in ( & in_ )
, out ( & out_ )
2019-05-16 03:34:04 +00:00
{
}
2019-04-29 06:05:30 +00:00
2019-05-16 03:34:04 +00:00
/// For writing.
2019-08-03 11:02:40 +00:00
PacketSender ( WriteBuffer & out_ , uint8_t & sequence_id_ )
: sequence_id ( sequence_id_ )
2019-05-16 03:34:04 +00:00
, in ( nullptr )
2019-08-03 11:02:40 +00:00
, out ( & out_ )
2019-03-26 18:30:41 +00:00
{
}
2019-07-19 17:50:42 +00:00
void receivePacket ( ClientPacket & packet )
2019-03-26 18:30:41 +00:00
{
2019-07-19 17:50:42 +00:00
packet . read ( * in , sequence_id ) ;
2019-03-26 18:30:41 +00:00
}
template < class T >
void sendPacket ( const T & packet , bool flush = false )
{
static_assert ( std : : is_base_of < WritePacket , T > ( ) ) ;
2019-07-16 06:39:18 +00:00
packet . writePayload ( * out , sequence_id ) ;
2019-03-26 18:30:41 +00:00
if ( flush )
out - > next ( ) ;
}
2019-07-19 17:50:42 +00:00
PacketPayloadReadBuffer getPayload ( )
{
return PacketPayloadReadBuffer ( * in , sequence_id ) ;
}
2019-03-26 18:30:41 +00:00
/// Sets sequence-id to 0. Must be called before each command phase.
void resetSequenceId ( ) ;
/// Converts packet to text. Is used for debug output.
2019-07-16 06:39:18 +00:00
static String packetToText ( const String & payload ) ;
2019-03-16 02:08:21 +00:00
} ;
2019-07-01 05:58:31 +00:00
uint64_t readLengthEncodedNumber ( ReadBuffer & ss ) ;
2019-03-16 02:08:21 +00:00
2019-07-01 05:58:31 +00:00
void writeLengthEncodedNumber ( uint64_t x , WriteBuffer & buffer ) ;
2019-03-16 02:08:21 +00:00
2019-07-16 06:39:18 +00:00
inline void writeLengthEncodedString ( const String & s , WriteBuffer & buffer )
2019-07-01 05:58:31 +00:00
{
writeLengthEncodedNumber ( s . size ( ) , buffer ) ;
buffer . write ( s . data ( ) , s . size ( ) ) ;
}
2019-03-26 18:30:41 +00:00
2019-07-16 06:39:18 +00:00
inline void writeNulTerminatedString ( const String & s , WriteBuffer & buffer )
2019-07-01 05:58:31 +00:00
{
buffer . write ( s . data ( ) , s . size ( ) ) ;
buffer . write ( 0 ) ;
}
2019-03-26 18:30:41 +00:00
2019-07-16 06:39:18 +00:00
size_t getLengthEncodedNumberSize ( uint64_t x ) ;
size_t getLengthEncodedStringSize ( const String & s ) ;
2019-03-26 18:30:41 +00:00
class Handshake : public WritePacket
{
2019-03-16 02:08:21 +00:00
int protocol_version = 0xa ;
2019-03-26 18:30:41 +00:00
String server_version ;
2019-03-16 02:08:21 +00:00
uint32_t connection_id ;
uint32_t capability_flags ;
uint8_t character_set ;
uint32_t status_flags ;
2019-07-28 00:41:41 +00:00
String auth_plugin_name ;
2019-03-26 18:30:41 +00:00
String auth_plugin_data ;
2019-03-16 02:08:21 +00:00
public :
2019-08-03 11:02:40 +00:00
explicit Handshake ( uint32_t capability_flags_ , uint32_t connection_id_ , String server_version_ , String auth_plugin_name_ , String auth_plugin_data_ )
2019-04-22 10:57:50 +00:00
: protocol_version ( 0xa )
2019-08-03 11:02:40 +00:00
, server_version ( std : : move ( server_version_ ) )
, connection_id ( connection_id_ )
, capability_flags ( capability_flags_ )
2019-04-22 10:57:50 +00:00
, character_set ( CharacterSet : : utf8_general_ci )
, status_flags ( 0 )
2019-08-03 11:02:40 +00:00
, auth_plugin_name ( std : : move ( auth_plugin_name_ ) )
, auth_plugin_data ( std : : move ( auth_plugin_data_ ) )
2019-03-26 18:30:41 +00:00
{
2019-03-16 02:08:21 +00:00
}
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
2019-07-28 00:41:41 +00:00
return 26 + server_version . size ( ) + auth_plugin_data . size ( ) + auth_plugin_name . size ( ) ;
2019-07-16 06:39:18 +00:00
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-26 18:30:41 +00:00
{
2019-07-01 05:58:31 +00:00
buffer . write ( static_cast < char > ( protocol_version ) ) ;
writeNulTerminatedString ( server_version , buffer ) ;
buffer . write ( reinterpret_cast < const char * > ( & connection_id ) , 4 ) ;
writeNulTerminatedString ( auth_plugin_data . substr ( 0 , AUTH_PLUGIN_DATA_PART_1_LENGTH ) , buffer ) ;
buffer . write ( reinterpret_cast < const char * > ( & capability_flags ) , 2 ) ;
buffer . write ( reinterpret_cast < const char * > ( & character_set ) , 1 ) ;
buffer . write ( reinterpret_cast < const char * > ( & status_flags ) , 2 ) ;
buffer . write ( ( reinterpret_cast < const char * > ( & capability_flags ) ) + 2 , 2 ) ;
buffer . write ( static_cast < char > ( auth_plugin_data . size ( ) ) ) ;
writeChar ( 0x0 , 10 , buffer ) ;
writeString ( auth_plugin_data . substr ( AUTH_PLUGIN_DATA_PART_1_LENGTH , auth_plugin_data . size ( ) - AUTH_PLUGIN_DATA_PART_1_LENGTH ) , buffer ) ;
2019-07-28 00:41:41 +00:00
writeString ( auth_plugin_name , buffer ) ;
2019-07-01 05:58:31 +00:00
writeChar ( 0x0 , 1 , buffer ) ;
2019-03-16 02:08:21 +00:00
}
} ;
2019-07-19 17:50:42 +00:00
class SSLRequest : public ClientPacket
2019-04-29 06:05:30 +00:00
{
public :
uint32_t capability_flags ;
uint32_t max_packet_size ;
uint8_t character_set ;
2019-07-14 22:13:56 +00:00
void readPayload ( ReadBuffer & buf ) override
2019-04-29 06:05:30 +00:00
{
2019-07-14 22:13:56 +00:00
buf . readStrict ( reinterpret_cast < char * > ( & capability_flags ) , 4 ) ;
buf . readStrict ( reinterpret_cast < char * > ( & max_packet_size ) , 4 ) ;
buf . readStrict ( reinterpret_cast < char * > ( & character_set ) , 1 ) ;
2019-04-29 06:05:30 +00:00
}
} ;
2019-07-19 17:50:42 +00:00
class HandshakeResponse : public LimitedClientPacket
2019-03-26 18:30:41 +00:00
{
2019-03-16 02:08:21 +00:00
public :
2019-07-01 05:58:31 +00:00
uint32_t capability_flags = 0 ;
uint32_t max_packet_size = 0 ;
uint8_t character_set = 0 ;
2019-03-26 18:30:41 +00:00
String username ;
String auth_response ;
String database ;
String auth_plugin_name ;
2019-03-16 02:08:21 +00:00
2019-04-29 06:05:30 +00:00
HandshakeResponse ( ) = default ;
2019-07-14 22:13:56 +00:00
void readPayload ( ReadBuffer & payload ) override
2019-03-26 18:30:41 +00:00
{
2019-07-14 22:13:56 +00:00
payload . readStrict ( reinterpret_cast < char * > ( & capability_flags ) , 4 ) ;
payload . readStrict ( reinterpret_cast < char * > ( & max_packet_size ) , 4 ) ;
payload . readStrict ( reinterpret_cast < char * > ( & character_set ) , 1 ) ;
payload . ignore ( 23 ) ;
2019-03-16 02:08:21 +00:00
2019-07-14 22:13:56 +00:00
readNullTerminated ( username , payload ) ;
2019-03-16 02:08:21 +00:00
2019-03-26 18:30:41 +00:00
if ( capability_flags & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA )
{
2019-07-14 22:13:56 +00:00
auto len = readLengthEncodedNumber ( payload ) ;
2019-03-16 02:08:21 +00:00
auth_response . resize ( len ) ;
2019-07-14 22:13:56 +00:00
payload . readStrict ( auth_response . data ( ) , len ) ;
2019-03-26 18:30:41 +00:00
}
else if ( capability_flags & CLIENT_SECURE_CONNECTION )
{
2019-07-01 05:58:31 +00:00
char len ;
2019-07-14 22:13:56 +00:00
payload . readStrict ( len ) ;
2019-07-01 05:58:31 +00:00
auth_response . resize ( static_cast < unsigned int > ( len ) ) ;
2019-07-14 22:13:56 +00:00
payload . readStrict ( auth_response . data ( ) , len ) ;
2019-03-26 18:30:41 +00:00
}
else
{
2019-07-14 22:13:56 +00:00
readNullTerminated ( auth_response , payload ) ;
2019-03-16 02:08:21 +00:00
}
2019-03-26 18:30:41 +00:00
if ( capability_flags & CLIENT_CONNECT_WITH_DB )
{
2019-07-14 22:13:56 +00:00
readNullTerminated ( database , payload ) ;
2019-03-16 02:08:21 +00:00
}
2019-03-26 18:30:41 +00:00
if ( capability_flags & CLIENT_PLUGIN_AUTH )
{
2019-07-14 22:13:56 +00:00
readNullTerminated ( auth_plugin_name , payload ) ;
2019-03-16 02:08:21 +00:00
}
}
} ;
2019-03-26 18:30:41 +00:00
class AuthSwitchRequest : public WritePacket
{
String plugin_name ;
String auth_plugin_data ;
public :
2019-08-03 11:02:40 +00:00
AuthSwitchRequest ( String plugin_name_ , String auth_plugin_data_ )
: plugin_name ( std : : move ( plugin_name_ ) ) , auth_plugin_data ( std : : move ( auth_plugin_data_ ) )
2019-03-26 18:30:41 +00:00
{
}
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
return 2 + plugin_name . size ( ) + auth_plugin_data . size ( ) ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-26 18:30:41 +00:00
{
2019-07-01 05:58:31 +00:00
buffer . write ( 0xfe ) ;
writeNulTerminatedString ( plugin_name , buffer ) ;
writeString ( auth_plugin_data , buffer ) ;
2019-03-26 18:30:41 +00:00
}
} ;
2019-07-19 17:50:42 +00:00
class AuthSwitchResponse : public LimitedClientPacket
2019-04-22 10:57:50 +00:00
{
public :
String value ;
2019-07-14 22:13:56 +00:00
void readPayload ( ReadBuffer & payload ) override
2019-04-22 10:57:50 +00:00
{
2019-07-14 22:13:56 +00:00
readStringUntilEOF ( value , payload ) ;
2019-04-22 10:57:50 +00:00
}
} ;
class AuthMoreData : public WritePacket
{
String data ;
public :
2019-08-03 11:02:40 +00:00
explicit AuthMoreData ( String data_ ) : data ( std : : move ( data_ ) ) { }
2019-04-22 10:57:50 +00:00
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
return 1 + data . size ( ) ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-04-22 10:57:50 +00:00
{
2019-07-01 05:58:31 +00:00
buffer . write ( 0x01 ) ;
writeString ( data , buffer ) ;
2019-04-22 10:57:50 +00:00
}
} ;
2019-03-26 18:30:41 +00:00
class OK_Packet : public WritePacket
{
2019-03-16 02:08:21 +00:00
uint8_t header ;
uint32_t capabilities ;
uint64_t affected_rows ;
int16_t warnings = 0 ;
uint32_t status_flags ;
2019-03-26 18:30:41 +00:00
String session_state_changes ;
2019-05-16 05:36:08 +00:00
String info ;
2019-03-16 02:08:21 +00:00
public :
2019-08-03 11:02:40 +00:00
OK_Packet ( uint8_t header_ ,
uint32_t capabilities_ ,
uint64_t affected_rows_ ,
uint32_t status_flags_ ,
int16_t warnings_ ,
String session_state_changes_ = " " ,
String info_ = " " )
: header ( header_ )
, capabilities ( capabilities_ )
, affected_rows ( affected_rows_ )
, warnings ( warnings_ )
, status_flags ( status_flags_ )
, session_state_changes ( std : : move ( session_state_changes_ ) )
, info ( std : : move ( info_ ) )
2019-03-16 02:08:21 +00:00
{
}
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
size_t result = 2 + getLengthEncodedNumberSize ( affected_rows ) ;
if ( capabilities & CLIENT_PROTOCOL_41 )
{
result + = 4 ;
}
else if ( capabilities & CLIENT_TRANSACTIONS )
{
result + = 2 ;
}
if ( capabilities & CLIENT_SESSION_TRACK )
{
result + = getLengthEncodedStringSize ( info ) ;
if ( status_flags & SERVER_SESSION_STATE_CHANGED )
result + = getLengthEncodedStringSize ( session_state_changes ) ;
}
else
{
result + = info . size ( ) ;
}
return result ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-26 18:30:41 +00:00
{
2019-07-01 05:58:31 +00:00
buffer . write ( header ) ;
writeLengthEncodedNumber ( affected_rows , buffer ) ;
writeLengthEncodedNumber ( 0 , buffer ) ; /// last insert-id
2019-03-16 02:08:21 +00:00
2019-03-26 18:30:41 +00:00
if ( capabilities & CLIENT_PROTOCOL_41 )
{
2019-07-01 05:58:31 +00:00
buffer . write ( reinterpret_cast < const char * > ( & status_flags ) , 2 ) ;
buffer . write ( reinterpret_cast < const char * > ( & warnings ) , 2 ) ;
2019-03-26 18:30:41 +00:00
}
else if ( capabilities & CLIENT_TRANSACTIONS )
{
2019-07-01 05:58:31 +00:00
buffer . write ( reinterpret_cast < const char * > ( & status_flags ) , 2 ) ;
2019-03-16 02:08:21 +00:00
}
2019-03-26 18:30:41 +00:00
if ( capabilities & CLIENT_SESSION_TRACK )
{
2019-07-16 06:39:18 +00:00
writeLengthEncodedString ( info , buffer ) ;
2019-03-26 18:30:41 +00:00
if ( status_flags & SERVER_SESSION_STATE_CHANGED )
2019-07-16 06:39:18 +00:00
writeLengthEncodedString ( session_state_changes , buffer ) ;
2019-03-26 18:30:41 +00:00
}
else
{
2019-07-01 05:58:31 +00:00
writeString ( info , buffer ) ;
2019-03-16 02:08:21 +00:00
}
}
} ;
2019-03-26 18:30:41 +00:00
class EOF_Packet : public WritePacket
{
2019-03-16 02:08:21 +00:00
int warnings ;
int status_flags ;
public :
2019-08-03 11:02:40 +00:00
EOF_Packet ( int warnings_ , int status_flags_ ) : warnings ( warnings_ ) , status_flags ( status_flags_ )
2019-03-26 18:30:41 +00:00
{ }
2019-03-16 02:08:21 +00:00
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
return 5 ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-26 18:30:41 +00:00
{
2019-07-01 05:58:31 +00:00
buffer . write ( 0xfe ) ; // EOF header
buffer . write ( reinterpret_cast < const char * > ( & warnings ) , 2 ) ;
buffer . write ( reinterpret_cast < const char * > ( & status_flags ) , 2 ) ;
2019-03-16 02:08:21 +00:00
}
} ;
2019-03-26 18:30:41 +00:00
class ERR_Packet : public WritePacket
{
2019-03-16 02:08:21 +00:00
int error_code ;
2019-03-26 18:30:41 +00:00
String sql_state ;
String error_message ;
2019-03-16 02:08:21 +00:00
public :
2019-08-03 11:02:40 +00:00
ERR_Packet ( int error_code_ , String sql_state_ , String error_message_ )
: error_code ( error_code_ ) , sql_state ( std : : move ( sql_state_ ) ) , error_message ( std : : move ( error_message_ ) )
2019-03-16 02:08:21 +00:00
{
}
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
return 4 + sql_state . length ( ) + std : : min ( error_message . length ( ) , MYSQL_ERRMSG_SIZE ) ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-16 02:08:21 +00:00
{
2019-07-01 05:58:31 +00:00
buffer . write ( 0xff ) ;
buffer . write ( reinterpret_cast < const char * > ( & error_code ) , 2 ) ;
buffer . write ( ' # ' ) ;
buffer . write ( sql_state . data ( ) , sql_state . length ( ) ) ;
buffer . write ( error_message . data ( ) , std : : min ( error_message . length ( ) , MYSQL_ERRMSG_SIZE ) ) ;
2019-03-16 02:08:21 +00:00
}
} ;
2019-03-26 18:30:41 +00:00
class ColumnDefinition : public WritePacket
{
String schema ;
String table ;
String org_table ;
String name ;
String org_name ;
2019-03-16 02:08:21 +00:00
size_t next_length = 0x0c ;
uint16_t character_set ;
uint32_t column_length ;
ColumnType column_type ;
uint16_t flags ;
uint8_t decimals = 0x00 ;
public :
2019-03-26 18:30:41 +00:00
ColumnDefinition (
2019-08-03 11:02:40 +00:00
String schema_ ,
String table_ ,
String org_table_ ,
String name_ ,
String org_name_ ,
uint16_t character_set_ ,
uint32_t column_length_ ,
ColumnType column_type_ ,
uint16_t flags_ ,
uint8_t decimals_ )
2019-03-16 02:08:21 +00:00
2019-08-03 11:02:40 +00:00
: schema ( std : : move ( schema_ ) ) , table ( std : : move ( table_ ) ) , org_table ( std : : move ( org_table_ ) ) , name ( std : : move ( name_ ) ) ,
org_name ( std : : move ( org_name_ ) ) , character_set ( character_set_ ) , column_length ( column_length_ ) , column_type ( column_type_ ) , flags ( flags_ ) ,
decimals ( decimals_ )
2019-03-16 02:08:21 +00:00
{
}
2019-03-26 18:30:41 +00:00
/// Should be used when column metadata (original name, table, original table, database) is unknown.
ColumnDefinition (
2019-08-03 11:02:40 +00:00
String name_ ,
uint16_t character_set_ ,
uint32_t column_length_ ,
ColumnType column_type_ ,
uint16_t flags_ ,
uint8_t decimals_ )
: ColumnDefinition ( " " , " " , " " , std : : move ( name_ ) , " " , character_set_ , column_length_ , column_type_ , flags_ , decimals_ )
2019-03-26 18:30:41 +00:00
{
}
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
return 13 + getLengthEncodedStringSize ( " def " ) + getLengthEncodedStringSize ( schema ) + getLengthEncodedStringSize ( table ) + getLengthEncodedStringSize ( org_table ) + \
getLengthEncodedStringSize ( name ) + getLengthEncodedStringSize ( org_name ) + getLengthEncodedNumberSize ( next_length ) ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-26 18:30:41 +00:00
{
2019-07-01 05:58:31 +00:00
writeLengthEncodedString ( std : : string ( " def " ) , buffer ) ; /// always "def"
writeLengthEncodedString ( schema , buffer ) ;
writeLengthEncodedString ( table , buffer ) ;
writeLengthEncodedString ( org_table , buffer ) ;
writeLengthEncodedString ( name , buffer ) ;
writeLengthEncodedString ( org_name , buffer ) ;
writeLengthEncodedNumber ( next_length , buffer ) ;
buffer . write ( reinterpret_cast < const char * > ( & character_set ) , 2 ) ;
buffer . write ( reinterpret_cast < const char * > ( & column_length ) , 4 ) ;
buffer . write ( reinterpret_cast < const char * > ( & column_type ) , 1 ) ;
buffer . write ( reinterpret_cast < const char * > ( & flags ) , 2 ) ;
buffer . write ( reinterpret_cast < const char * > ( & decimals ) , 2 ) ;
writeChar ( 0x0 , 2 , buffer ) ;
2019-03-16 02:08:21 +00:00
}
} ;
2019-07-19 17:50:42 +00:00
class ComFieldList : public LimitedClientPacket
2019-03-26 18:30:41 +00:00
{
2019-03-16 02:08:21 +00:00
public :
2019-03-26 18:30:41 +00:00
String table , field_wildcard ;
2019-03-16 02:08:21 +00:00
2019-07-14 22:13:56 +00:00
void readPayload ( ReadBuffer & payload ) override
2019-03-16 02:08:21 +00:00
{
2019-07-14 22:13:56 +00:00
// Command byte has been already read from payload.
readNullTerminated ( table , payload ) ;
readStringUntilEOF ( field_wildcard , payload ) ;
2019-03-16 02:08:21 +00:00
}
} ;
2019-03-26 18:30:41 +00:00
class LengthEncodedNumber : public WritePacket
{
uint64_t value ;
public :
2019-08-03 11:02:40 +00:00
explicit LengthEncodedNumber ( uint64_t value_ ) : value ( value_ )
2019-03-26 18:30:41 +00:00
{
}
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
return getLengthEncodedNumberSize ( value ) ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-26 18:30:41 +00:00
{
2019-07-01 05:58:31 +00:00
writeLengthEncodedNumber ( value , buffer ) ;
2019-03-26 18:30:41 +00:00
}
} ;
2019-12-01 11:21:43 +00:00
ColumnDefinition getColumnDefinition ( const String & column_name , const TypeIndex index ) ;
namespace ProtocolText
{
2019-03-26 18:30:41 +00:00
class ResultsetRow : public WritePacket
{
2019-12-01 11:21:43 +00:00
const Columns & columns ;
int row_num ;
2019-07-16 06:39:18 +00:00
size_t payload_size = 0 ;
2019-12-01 11:21:43 +00:00
std : : vector < String > serialized ;
2019-03-26 18:30:41 +00:00
public :
2019-12-01 11:21:43 +00:00
ResultsetRow ( const DataTypes & data_types , const Columns & columns_ , int row_num_ )
: columns ( columns_ )
, row_num ( row_num_ )
2019-03-26 18:30:41 +00:00
{
2019-12-01 11:21:43 +00:00
for ( size_t i = 0 ; i < columns . size ( ) ; i + + )
{
if ( columns [ i ] - > isNullAt ( row_num ) )
{
payload_size + = 1 ;
serialized . emplace_back ( " \xfb " ) ;
}
else
{
WriteBufferFromOwnString ostr ;
data_types [ i ] - > serializeAsText ( * columns [ i ] , row_num , ostr , FormatSettings ( ) ) ;
payload_size + = getLengthEncodedStringSize ( ostr . str ( ) ) ;
serialized . push_back ( std : : move ( ostr . str ( ) ) ) ;
}
}
2019-03-26 18:30:41 +00:00
}
2019-07-16 06:39:18 +00:00
protected :
size_t getPayloadSize ( ) const override
{
return payload_size ;
}
void writePayloadImpl ( WriteBuffer & buffer ) const override
2019-03-26 18:30:41 +00:00
{
2019-12-01 11:21:43 +00:00
for ( size_t i = 0 ; i < columns . size ( ) ; i + + )
{
if ( columns [ i ] - > isNullAt ( row_num ) )
buffer . write ( serialized [ i ] . data ( ) , 1 ) ;
else
writeLengthEncodedString ( serialized [ i ] , buffer ) ;
}
2019-03-26 18:30:41 +00:00
}
} ;
2019-03-16 02:08:21 +00:00
2019-12-01 11:21:43 +00:00
}
2019-07-28 13:12:26 +00:00
namespace Authentication
{
class IPlugin
{
public :
virtual String getName ( ) = 0 ;
virtual String getAuthPluginData ( ) = 0 ;
virtual void authenticate ( const String & user_name , std : : optional < String > auth_response , Context & context , std : : shared_ptr < PacketSender > packet_sender , bool is_secure_connection ,
const Poco : : Net : : SocketAddress & address ) = 0 ;
virtual ~ IPlugin ( ) = default ;
} ;
/// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
class Native41 : public IPlugin
{
public :
Native41 ( )
{
scramble . resize ( SCRAMBLE_LENGTH + 1 , 0 ) ;
Poco : : RandomInputStream generator ;
for ( size_t i = 0 ; i < SCRAMBLE_LENGTH ; i + + )
generator > > scramble [ i ] ;
}
String getName ( ) override
{
return " mysql_native_password " ;
}
String getAuthPluginData ( ) override
{
return scramble ;
}
void authenticate (
const String & user_name ,
std : : optional < String > auth_response ,
Context & context ,
std : : shared_ptr < PacketSender > packet_sender ,
bool /* is_secure_connection */ ,
const Poco : : Net : : SocketAddress & address ) override
{
if ( ! auth_response )
{
packet_sender - > sendPacket ( AuthSwitchRequest ( getName ( ) , scramble ) , true ) ;
AuthSwitchResponse response ;
packet_sender - > receivePacket ( response ) ;
auth_response = response . value ;
}
if ( auth_response - > empty ( ) )
{
context . setUser ( user_name , " " , address , " " ) ;
return ;
}
if ( auth_response - > size ( ) ! = Poco : : SHA1Engine : : DIGEST_SIZE )
throw Exception ( " Wrong size of auth response. Expected: " + std : : to_string ( Poco : : SHA1Engine : : DIGEST_SIZE ) + " bytes, received: " + std : : to_string ( auth_response - > size ( ) ) + " bytes. " ,
ErrorCodes : : UNKNOWN_EXCEPTION ) ;
2020-02-12 03:03:33 +00:00
auto user = context . getAccessControlManager ( ) . read < User > ( user_name ) ;
2019-07-28 13:12:26 +00:00
2019-12-06 01:30:01 +00:00
Poco : : SHA1Engine : : Digest double_sha1_value = user - > authentication . getPasswordDoubleSHA1 ( ) ;
2019-07-28 14:17:33 +00:00
assert ( double_sha1_value . size ( ) = = Poco : : SHA1Engine : : DIGEST_SIZE ) ;
2019-07-28 13:12:26 +00:00
2019-07-28 14:17:33 +00:00
Poco : : SHA1Engine engine ;
2019-07-28 13:12:26 +00:00
engine . update ( scramble . data ( ) , SCRAMBLE_LENGTH ) ;
engine . update ( double_sha1_value . data ( ) , double_sha1_value . size ( ) ) ;
String password_sha1 ( Poco : : SHA1Engine : : DIGEST_SIZE , 0x0 ) ;
const Poco : : SHA1Engine : : Digest & digest = engine . digest ( ) ;
for ( size_t i = 0 ; i < password_sha1 . size ( ) ; i + + )
{
password_sha1 [ i ] = digest [ i ] ^ static_cast < unsigned char > ( ( * auth_response ) [ i ] ) ;
}
context . setUser ( user_name , password_sha1 , address , " " ) ;
}
private :
String scramble ;
} ;
2019-11-02 10:20:46 +00:00
# if USE_SSL
2019-07-28 13:12:26 +00:00
/// Caching SHA2 plugin is not used because it would be possible to authenticate knowing hash from users.xml.
/// https://dev.mysql.com/doc/internals/en/sha256.html
class Sha256Password : public IPlugin
{
public :
2019-08-15 21:22:54 +00:00
Sha256Password ( RSA & public_key_ , RSA & private_key_ , Logger * log_ )
: public_key ( public_key_ )
, private_key ( private_key_ )
, log ( log_ )
2019-07-28 13:12:26 +00:00
{
/** Native authentication sent 20 bytes + '\0' character = 21 bytes.
* This plugin must do the same to stay consistent with historical behavior if it is set to operate as a default plugin . [ 1 ]
* https : //github.com/mysql/mysql-server/blob/8.0/sql/auth/sql_authentication.cc#L3994
*/
scramble . resize ( SCRAMBLE_LENGTH + 1 , 0 ) ;
Poco : : RandomInputStream generator ;
2019-07-28 14:17:33 +00:00
for ( size_t i = 0 ; i < SCRAMBLE_LENGTH ; i + + )
generator > > scramble [ i ] ;
2019-07-28 13:12:26 +00:00
}
String getName ( ) override
{
return " sha256_password " ;
}
String getAuthPluginData ( ) override
{
return scramble ;
}
void authenticate (
const String & user_name ,
std : : optional < String > auth_response ,
Context & context ,
std : : shared_ptr < PacketSender > packet_sender ,
bool is_secure_connection ,
const Poco : : Net : : SocketAddress & address ) override
{
if ( ! auth_response )
{
packet_sender - > sendPacket ( AuthSwitchRequest ( getName ( ) , scramble ) , true ) ;
if ( packet_sender - > in - > eof ( ) )
2019-07-28 14:17:33 +00:00
throw Exception ( " Client doesn't support authentication method " + getName ( ) + " used by ClickHouse. Specifying user password using 'password_double_sha1_hex' may fix the problem. " ,
ErrorCodes : : MYSQL_CLIENT_INSUFFICIENT_CAPABILITIES ) ;
2019-07-28 13:12:26 +00:00
AuthSwitchResponse response ;
packet_sender - > receivePacket ( response ) ;
auth_response = response . value ;
LOG_TRACE ( log , " Authentication method mismatch. " ) ;
}
else
{
LOG_TRACE ( log , " Authentication method match. " ) ;
}
2020-01-19 00:34:00 +00:00
bool sent_public_key = false ;
2019-07-28 13:12:26 +00:00
if ( auth_response = = " \1 " )
{
LOG_TRACE ( log , " Client requests public key. " ) ;
BIO * mem = BIO_new ( BIO_s_mem ( ) ) ;
SCOPE_EXIT ( BIO_free ( mem ) ) ;
if ( PEM_write_bio_RSA_PUBKEY ( mem , & public_key ) ! = 1 )
{
throw Exception ( " Failed to write public key to memory. Error: " + getOpenSSLErrors ( ) , ErrorCodes : : OPENSSL_ERROR ) ;
}
char * pem_buf = nullptr ;
2019-12-15 06:34:43 +00:00
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wold-style-cast"
2019-07-28 13:12:26 +00:00
long pem_size = BIO_get_mem_data ( mem , & pem_buf ) ;
2019-12-15 06:34:43 +00:00
# pragma GCC diagnostic pop
2019-07-28 13:12:26 +00:00
String pem ( pem_buf , pem_size ) ;
LOG_TRACE ( log , " Key: " < < pem ) ;
AuthMoreData data ( pem ) ;
packet_sender - > sendPacket ( data , true ) ;
2020-01-19 00:34:00 +00:00
sent_public_key = true ;
2019-07-28 13:12:26 +00:00
AuthSwitchResponse response ;
packet_sender - > receivePacket ( response ) ;
auth_response = response . value ;
}
else
{
LOG_TRACE ( log , " Client didn't request public key. " ) ;
}
String password ;
/** Decrypt password, if it's not empty.
* The original intention was that the password is a string [ NUL ] but this never got enforced properly so now we have to accept that
* an empty packet is a blank password , thus the check for auth_response . empty ( ) has to be made too .
* https : //github.com/mysql/mysql-server/blob/8.0/sql/auth/sql_authentication.cc#L4017
*/
if ( ! is_secure_connection & & ! auth_response - > empty ( ) & & auth_response ! = String ( " \0 " , 1 ) )
{
2020-01-19 00:34:00 +00:00
LOG_TRACE ( log , " Received nonempty password. " ) ;
2019-07-28 13:12:26 +00:00
auto ciphertext = reinterpret_cast < unsigned char * > ( auth_response - > data ( ) ) ;
unsigned char plaintext [ RSA_size ( & private_key ) ] ;
int plaintext_size = RSA_private_decrypt ( auth_response - > size ( ) , ciphertext , plaintext , & private_key , RSA_PKCS1_OAEP_PADDING ) ;
if ( plaintext_size = = - 1 )
{
2020-01-19 00:34:00 +00:00
if ( ! sent_public_key )
LOG_WARNING ( log , " Client could have encrypted password with different public key since it didn't request it from server. " ) ;
2019-07-28 13:12:26 +00:00
throw Exception ( " Failed to decrypt auth data. Error: " + getOpenSSLErrors ( ) , ErrorCodes : : OPENSSL_ERROR ) ;
}
password . resize ( plaintext_size ) ;
for ( int i = 0 ; i < plaintext_size ; i + + )
{
password [ i ] = plaintext [ i ] ^ static_cast < unsigned char > ( scramble [ i % scramble . size ( ) ] ) ;
}
}
else if ( is_secure_connection )
{
password = * auth_response ;
}
else
{
LOG_TRACE ( log , " Received empty password " ) ;
}
if ( ! password . empty ( ) & & password . back ( ) = = 0 )
{
password . pop_back ( ) ;
}
context . setUser ( user_name , password , address , " " ) ;
}
private :
RSA & public_key ;
RSA & private_key ;
Logger * log ;
String scramble ;
} ;
2019-11-02 10:20:46 +00:00
# endif
2019-07-28 13:12:26 +00:00
}
2019-03-16 02:08:21 +00:00
}
}