2
0
mirror of https://github.com/boostorg/mysql.git synced 2026-01-30 20:12:17 +00:00

Added handling for mysql_native_password

This commit is contained in:
ruben
2019-06-30 18:45:32 +01:00
parent bd9c81a692
commit f4e331978f
7 changed files with 174 additions and 25 deletions

View File

@@ -5,18 +5,21 @@ project(mysql-asio)
set(BOOST_ROOT /opt/boost_1_70_0 CACHE STRING "Path to boost installation")
find_package(Boost REQUIRED COMPONENTS system)
find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED)
# Library
add_library(
mysql_asio
SHARED
src/deserialization.cpp
src/auth.cpp
)
target_link_libraries(
mysql_asio
PUBLIC
Boost::system
Threads::Threads
OpenSSL::Crypto
)
target_include_directories(
mysql_asio

29
include/auth.hpp Normal file
View File

@@ -0,0 +1,29 @@
#ifndef INCLUDE_AUTH_HPP_
#define INCLUDE_AUTH_HPP_
#include <cstdint>
#include <string_view>
namespace mysql
{
namespace mysql_native_password
{
constexpr std::size_t challenge_length = 20;
constexpr std::size_t response_length = 20;
using response_buffer = std::uint8_t [response_length];
// challenge must point to challenge_length bytes of data
void compute_auth_string(std::string_view password, const void* challenge, response_buffer& output);
}
}
#endif /* INCLUDE_AUTH_HPP_ */

View File

@@ -78,16 +78,13 @@ inline ReadIterator deserialize(ReadIterator from, ReadIterator last, string_len
return from;
}
ReadIterator deserialize(ReadIterator from, ReadIterator last, OkPacket& output);
ReadIterator deserialize(ReadIterator from, ReadIterator last, ErrPacket& output);
ReadIterator deserialize(ReadIterator from, ReadIterator last, Handshake& output);
// SERIALIZATION
class DynamicBuffer
{
std::vector<std::uint8_t> buffer_;
public:
DynamicBuffer(int1 sequence_number): buffer_ { 0, 0, 0, sequence_number } {};
void add(const void* data, std::size_t size)
{
auto current_size = buffer_.size();
@@ -95,6 +92,14 @@ public:
memcpy(buffer_.data() + current_size, data, size);
}
void add(std::uint8_t value) { buffer_.push_back(value); }
void set_size()
{
int3 packet_length { static_cast<std::uint32_t>(buffer_.size() - 4) };
boost::endian::native_to_little_inplace(packet_length.value);
memcpy(buffer_.data(), &packet_length.value, 3);
}
const void* data() const { return buffer_.data(); }
std::size_t size() const { return buffer_.size(); }
};
template <typename T> void native_to_little(T& value) { boost::endian::native_to_little_inplace(value); }
@@ -107,7 +112,7 @@ template <typename T>
std::enable_if_t<is_fixed_size_v<T>>
serialize(DynamicBuffer& buffer, T value)
{
boost::endian::native_to_little_inplace(value);
native_to_little(value);
buffer.add(&value, get_size_v<T>);
}
@@ -145,6 +150,11 @@ inline void serialize(DynamicBuffer& buffer, const string_lenenc& value)
}
// Packet serialization and deserialization
ReadIterator deserialize(ReadIterator from, ReadIterator last, OkPacket& output);
ReadIterator deserialize(ReadIterator from, ReadIterator last, ErrPacket& output);
ReadIterator deserialize(ReadIterator from, ReadIterator last, Handshake& output);
void serialize(DynamicBuffer& buffer, const HandshakeResponse& value);
}

View File

@@ -7,6 +7,36 @@
namespace mysql
{
constexpr int4 CLIENT_LONG_PASSWORD = 1; // Use the improved version of Old Password Authentication
constexpr int4 CLIENT_FOUND_ROWS = 2; // Send found rows instead of affected rows in EOF_Packet
constexpr int4 CLIENT_LONG_FLAG = 4; // Get all column flags
constexpr int4 CLIENT_CONNECT_WITH_DB = 8; // Database (schema) name can be specified on connect in Handshake Response Packet
constexpr int4 CLIENT_NO_SCHEMA = 16; // Don't allow database.table.column
constexpr int4 CLIENT_COMPRESS = 32; // Compression protocol supported
constexpr int4 CLIENT_ODBC = 64; // Special handling of ODBC behavior
constexpr int4 CLIENT_LOCAL_FILES = 128; // Can use LOAD DATA LOCAL
constexpr int4 CLIENT_IGNORE_SPACE = 256; // Ignore spaces before '('
constexpr int4 CLIENT_PROTOCOL_41 = 512; // New 4.1 protocol
constexpr int4 CLIENT_INTERACTIVE = 1024; // This is an interactive client
constexpr int4 CLIENT_SSL = 2048; // Use SSL encryption for the session
constexpr int4 CLIENT_IGNORE_SIGPIPE = 4096; // Client only flag
constexpr int4 CLIENT_TRANSACTIONS = 8192; // Client knows about transactions
constexpr int4 CLIENT_RESERVED = 16384; // DEPRECATED: Old flag for 4.1 protocol
constexpr int4 CLIENT_RESERVED2 = 32768; // DEPRECATED: Old flag for 4.1 authentication \ CLIENT_SECURE_CONNECTION
constexpr int4 CLIENT_MULTI_STATEMENTS = (1UL << 16); // Enable/disable multi-stmt support
constexpr int4 CLIENT_MULTI_RESULTS = (1UL << 17); // Enable/disable multi-results
constexpr int4 CLIENT_PS_MULTI_RESULTS = (1UL << 18); // Multi-results and OUT parameters in PS-protocol
constexpr int4 CLIENT_PLUGIN_AUTH = (1UL << 19); // Client supports plugin authentication
constexpr int4 CLIENT_CONNECT_ATTRS = (1UL << 20); // Client supports connection attributes
constexpr int4 CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = (1UL << 21); // Enable authentication response packet to be larger than 255 bytes
constexpr int4 CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = (1UL << 22); // Don't close the connection for a user account with expired password
constexpr int4 CLIENT_SESSION_TRACK = (1UL << 23); // Capable of handling server state change information
constexpr int4 CLIENT_DEPRECATE_EOF = (1UL << 24); // Client no longer needs EOF_Packet and will use OK_Packet instead
constexpr int4 CLIENT_SSL_VERIFY_SERVER_CERT = (1UL << 30); // Verify server certificate
constexpr int4 CLIENT_OPTIONAL_RESULTSET_METADATA = (1UL << 25); // The client can handle optional metadata information in the resultset
constexpr int4 CLIENT_REMEMBER_OPTIONS = (1UL << 31); // Don't reset the options after an unsuccessful connect
enum server_status_flags
{
SERVER_STATUS_IN_TRANS = 1 << 0,

View File

@@ -7,11 +7,15 @@
#include <boost/asio/post.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <cassert>
#include "basic_types.hpp"
#include "deserialization.hpp"
#include "auth.hpp"
using namespace std;
using namespace boost::asio;
using namespace mysql;
constexpr auto HOSTNAME = "localhost"sv;
constexpr auto PORT = "3306"sv;
@@ -70,7 +74,28 @@ int main()
"character_set=" << handshake.character_set << ",\n" <<
"status_flags=" << std::bitset<16>{handshake.status_flags} << ",\n" <<
"auth_plugin_name=" << handshake.auth_plugin_name.value << endl;
mysql::HandshakeResponse handshake_response;
assert(handshake.auth_plugin_data.size() == mysql_native_password::challenge_length);
mysql_native_password::response_buffer auth_response;
mysql_native_password::compute_auth_string("root", handshake.auth_plugin_data.data(), auth_response);
handshake_response.client_flag =
CLIENT_CONNECT_WITH_DB |
CLIENT_PROTOCOL_41 |
CLIENT_PLUGIN_AUTH |
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
CLIENT_DEPRECATE_EOF;
handshake_response.max_packet_size = 0xffff;
handshake_response.character_set = 33; // utf8
handshake_response.username.value = "root";
handshake_response.auth_response.value = string_view {(const char*)auth_response, sizeof(auth_response)};
handshake_response.database.value = "mysql";
handshake_response.client_plugin_name.value = "mysql_native_password";
// Serialize and send
DynamicBuffer response_buffer { ++sequence_number };
serialize(response_buffer, handshake_response);
response_buffer.set_size();
boost::asio::write(sock, boost::asio::buffer(response_buffer.data(), response_buffer.size()));
}
else
{

36
src/auth.cpp Normal file
View File

@@ -0,0 +1,36 @@
/*
* auth.cpp
*
* Created on: Jun 30, 2019
* Author: ruben
*/
#include "auth.hpp"
#include <openssl/sha.h>
#include <cstring>
// SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
void mysql::mysql_native_password::compute_auth_string(
std::string_view password,
const void* challenge,
response_buffer& output
)
{
// SHA1 (password)
using sha1_buffer = unsigned char [SHA_DIGEST_LENGTH];
sha1_buffer password_sha1;
SHA1(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha1);
// Add server challenge (salt)
unsigned char salted_buffer [challenge_length + SHA_DIGEST_LENGTH];
memcpy(salted_buffer, challenge, challenge_length);
SHA1(password_sha1, sizeof(password_sha1), salted_buffer + 20);
sha1_buffer salted_sha1;
SHA1(salted_buffer, sizeof(salted_buffer), salted_sha1);
// XOR
static_assert(sizeof(output) == SHA_DIGEST_LENGTH);
for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
output[i] = password_sha1[i] ^ salted_sha1[i];
}

View File

@@ -53,6 +53,30 @@ mysql::ReadIterator mysql::deserialize(ReadIterator from, ReadIterator last, str
return string_end + 1; // skip the null terminator
}
void mysql::serialize(DynamicBuffer& buffer, int_lenenc value)
{
if (value.value < 251)
{
serialize(buffer, static_cast<int1>(value.value));
}
else if (value.value < 0x10000)
{
serialize(buffer, int1(0xfc));
serialize(buffer, static_cast<int2>(value.value));
}
else if (value.value < 0x1000000)
{
serialize(buffer, int1(0xfd));
serialize(buffer, int3 {static_cast<uint32_t>(value.value)});
}
else
{
serialize(buffer, int1(0xfe));
serialize(buffer, static_cast<int8>(value.value));
}
}
// Packet serialization and deserialization
mysql::ReadIterator mysql::deserialize(ReadIterator from, ReadIterator last, OkPacket& output)
{
// TODO: is packet header to be deserialized as part of this?
@@ -97,29 +121,21 @@ mysql::ReadIterator mysql::deserialize(ReadIterator from, ReadIterator last, Han
from = deserialize(from, last, output.auth_plugin_name);
output.auth_plugin_data = auth_plugin_data_part_1;
output.auth_plugin_data += auth_plugin_data_part_2;
output.auth_plugin_data.pop_back(); // includes a null byte at the end
boost::endian::little_to_native_inplace(output.capability_falgs);
return from;
}
void mysql::serialize(DynamicBuffer& buffer, int_lenenc value)
void mysql::serialize(DynamicBuffer& buffer, const HandshakeResponse& value)
{
if (value.value < 251)
{
serialize(buffer, static_cast<int1>(value.value));
}
else if (value.value < 0x10000)
{
serialize(buffer, int1(0xfc));
serialize(buffer, static_cast<int2>(value.value));
}
else if (value.value < 0x1000000)
{
serialize(buffer, int1(0xfd));
serialize(buffer, int3 {static_cast<uint32_t>(value.value)});
}
else
{
serialize(buffer, int1(0xfe));
serialize(buffer, static_cast<int8>(value.value));
}
serialize(buffer, value.client_flag);
serialize(buffer, value.max_packet_size);
serialize(buffer, value.character_set);
serialize(buffer, string_fixed<23>{}); // filler
serialize(buffer, value.username);
serialize(buffer, value.auth_response);
serialize(buffer, value.database);
serialize(buffer, value.client_plugin_name);
}