diff --git a/src/minimysql/connection_context.cpp b/src/minimysql/connection_context.cpp index f859bac..a459668 100644 --- a/src/minimysql/connection_context.cpp +++ b/src/minimysql/connection_context.cpp @@ -178,6 +178,43 @@ void connection_context::parse_client_greeting( client_attributes_ = client_greeting.attributes(); } +[[nodiscard]] network_buffer_type +connection_context::generate_encoded_auth_method_switch() { + std::string result_buffer{}; + + classic_protocol::message::server::AuthMethodSwitch auth_method_switch{ + get_server_auth_method(), generate_server_auth_method_switch_data()}; + using auth_method_switch_frame = classic_protocol::frame::Frame< + classic_protocol::message::server::AuthMethodSwitch>; + auto encode_result{classic_protocol::encode( + {generate_sequence_number(), auth_method_switch}, + get_shared_capabilities(), boost::asio::dynamic_buffer(result_buffer))}; + + if (!encode_result) { + throw boost::system::system_error{encode_result.error()}; + } + return result_buffer; +} + +void connection_context::parse_client_auth_method_data( + const network_buffer_type &payload) { + auto buffer{boost::asio::buffer(payload)}; + using auth_method_data_frame = classic_protocol::frame::Frame< + classic_protocol::message::client::AuthMethodData>; + auto decode_result{classic_protocol::decode( + buffer, get_shared_capabilities())}; + if (!decode_result) { + throw boost::system::system_error{decode_result.error()}; + } + + validate_and_update_sequence_number(decode_result.value().second.seq_id()); + + // after the auth method switch the client uses the server's auth method + client_auth_method_ = server_auth_method_; + client_auth_method_data_ = + decode_result.value().second.payload().auth_method_data(); +} + [[nodiscard]] network_buffer_type connection_context::generate_encoded_fast_auth() { std::string result_buffer{}; @@ -429,6 +466,18 @@ connection_context::generate_server_auth_method_data() { return server_auth_method_data_; } +[[nodiscard]] std::string +connection_context::generate_server_auth_method_switch_data() const { + if (get_server_auth_method() == + caching_sha2_password_authenticator::plugin_name) { + // caching_sha2_password follows the historical handshake seed shape: + // 20 bytes of challenge data plus a trailing NUL filler. + return get_server_auth_method_data() + '\0'; + } + + return get_server_auth_method_data(); +} + [[nodiscard]] std::uint8_t connection_context::generate_sequence_number() { return sequence_number_++; } diff --git a/src/minimysql/connection_context.hpp b/src/minimysql/connection_context.hpp index a2c69d7..40d5875 100644 --- a/src/minimysql/connection_context.hpp +++ b/src/minimysql/connection_context.hpp @@ -130,6 +130,9 @@ class connection_context { [[nodiscard]] network_buffer_type generate_encoded_server_greeting(); void parse_client_greeting(const network_buffer_type &payload); + [[nodiscard]] network_buffer_type generate_encoded_auth_method_switch(); + void parse_client_auth_method_data(const network_buffer_type &payload); + [[nodiscard]] network_buffer_type generate_encoded_fast_auth(); [[nodiscard]] network_buffer_type generate_encoded_ok(); [[nodiscard]] network_buffer_type generate_encoded_eof(); @@ -228,6 +231,7 @@ class connection_context { [[nodiscard]] static capability_bitset get_default_server_capabilities() noexcept; [[nodiscard]] const std::string &generate_server_auth_method_data(); + [[nodiscard]] std::string generate_server_auth_method_switch_data() const; [[nodiscard]] std::uint8_t generate_sequence_number(); void validate_and_update_sequence_number(std::uint8_t sequence_number); diff --git a/src/minimysql/network_service.cpp b/src/minimysql/network_service.cpp index 412472f..c977aba 100644 --- a/src/minimysql/network_service.cpp +++ b/src/minimysql/network_service.cpp @@ -291,8 +291,28 @@ void handle_exception(std::string_view context) { << " authentication that does not match the one associated " "with the user account (" << context.get_server_auth_method() << ")\n"; - // TODO: send SwitchAuthentication packet - co_return; + + const auto auth_method_switch{ + context.generate_encoded_auth_method_switch()}; + print_generic(remote_endpoint, context, "auth method switch"); + co_await minimysql::async_write_mysql_frame( + socket, auth_method_switch, + network_service::session_authentication_timeout); + std::cout << "sent server auth method switch (" + << std::size(auth_method_switch) << " bytes to " + << remote_endpoint << ")\n"; + + co_await minimysql::async_read_mysql_frame( + socket, data, network_service::session_authentication_timeout); + std::cout << "received client auth method switch response (" + << std::size(data) << " bytes from " << remote_endpoint + << ")\n"; + context.parse_client_auth_method_data(data); + std::cout << "client auth method after switch: " + << context.get_client_auth_method() << '\n' + << " auth_method_data: " + << std::size(context.get_client_auth_method_data()) + << " byte(s)\n"; } if (!context.check_client_authentication()) { std::cout << "client authentication failed for "