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:
@@ -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
29
include/auth.hpp
Normal 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_ */
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
25
main.cpp
25
main.cpp
@@ -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
36
src/auth.cpp
Normal 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];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user