diff --git a/plugins/mysqlx/Makefile b/plugins/mysqlx/Makefile index f31051c32..3134afb8a 100644 --- a/plugins/mysqlx/Makefile +++ b/plugins/mysqlx/Makefile @@ -105,8 +105,6 @@ SRCS := $(PLUGIN_DIR)/src/mysqlx_plugin.cpp \ $(PLUGIN_DIR)/src/mysqlx_config_store.cpp \ $(PLUGIN_DIR)/src/mysqlx_listener_reconcile.cpp \ $(PLUGIN_DIR)/src/mysqlx_protocol.cpp \ - $(PLUGIN_DIR)/src/mysqlx_frontend_session.cpp \ - $(PLUGIN_DIR)/src/mysqlx_backend_session.cpp \ $(PLUGIN_DIR)/src/mysqlx_data_stream.cpp \ $(PLUGIN_DIR)/src/mysqlx_connection.cpp \ $(PLUGIN_DIR)/src/mysqlx_stats.cpp \ diff --git a/plugins/mysqlx/include/mysqlx_backend_session.h b/plugins/mysqlx/include/mysqlx_backend_session.h deleted file mode 100644 index b89cce8e2..000000000 --- a/plugins/mysqlx/include/mysqlx_backend_session.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef PROXYSQL_MYSQLX_BACKEND_SESSION_H -#define PROXYSQL_MYSQLX_BACKEND_SESSION_H - -#include "mysqlx_config_store.h" - -#include - -class MysqlxBackendSession { -public: - MysqlxBackendSession(); - ~MysqlxBackendSession(); - - MysqlxBackendSession(const MysqlxBackendSession&) = delete; - MysqlxBackendSession& operator=(const MysqlxBackendSession&) = delete; - - // Connect to backend and authenticate using resolved identity and endpoint. - bool connect(const MysqlxResolvedIdentity& identity, - const MysqlxBackendEndpoint& endpoint, - std::string& err); - - int fd() const { return backend_fd_; } - - // Relay data bidirectionally between frontend_fd and backend_fd. - // Runs until one side closes or an error occurs. - bool relay(int frontend_fd); - -private: - bool authenticate_backend(const std::string& username, - const std::string& password, - std::string& err); - - int backend_fd_ { -1 }; -}; - -#endif /* PROXYSQL_MYSQLX_BACKEND_SESSION_H */ diff --git a/plugins/mysqlx/include/mysqlx_frontend_session.h b/plugins/mysqlx/include/mysqlx_frontend_session.h deleted file mode 100644 index 29adf06d6..000000000 --- a/plugins/mysqlx/include/mysqlx_frontend_session.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef PROXYSQL_MYSQLX_FRONTEND_SESSION_H -#define PROXYSQL_MYSQLX_FRONTEND_SESSION_H - -#include "mysqlx_config_store.h" -#include "mysqlx_protocol.h" - -#include -#include -#include - -class MysqlxFrontendSession { -public: - explicit MysqlxFrontendSession(int client_fd); - ~MysqlxFrontendSession(); - - MysqlxFrontendSession(const MysqlxFrontendSession&) = delete; - MysqlxFrontendSession& operator=(const MysqlxFrontendSession&) = delete; - - // Run the full X Protocol handshake and authentication. - // On success, the session is authenticated and identity is resolved. - // On failure, an error has already been sent to the client. - bool run_handshake_and_auth(MysqlxConfigStore& config_store); - - // After successful auth, returns the resolved identity. - const MysqlxResolvedIdentity& identity() const { return identity_; } - - int client_fd() const { return client_fd_; } - -private: - bool handle_capabilities_get(); - bool handle_capabilities_set(const std::vector& payload); - bool handle_authenticate(MysqlxConfigStore& config_store); - - bool send_capabilities(); - bool send_auth_continue_challenge(); - - int client_fd_; - MysqlxResolvedIdentity identity_ {}; - std::vector auth_challenge_ {}; - std::string auth_method_ {}; -}; - -#endif /* PROXYSQL_MYSQLX_FRONTEND_SESSION_H */ diff --git a/plugins/mysqlx/include/mysqlx_session.h b/plugins/mysqlx/include/mysqlx_session.h index c6f0a0efa..5347a12ac 100644 --- a/plugins/mysqlx/include/mysqlx_session.h +++ b/plugins/mysqlx/include/mysqlx_session.h @@ -47,7 +47,6 @@ public: PROCESSING_X_QUERY, CONNECTING_SERVER, WAITING_SERVER_XMSG, - X_FAST_FORWARD, X_TLS_ACCEPT_INIT, X_TLS_ACCEPT_CONT, X_TLS_ACCEPT_DONE, @@ -117,7 +116,6 @@ private: void handler_auth_challenge_response(); void handler_waiting_client_msg(); void handler_waiting_server_msg(); - void handler_fast_forward(); void handler_session_closing(); void handler_connecting_server(); void handler_session_reset_waiting(); diff --git a/plugins/mysqlx/src/mysqlx_backend_session.cpp b/plugins/mysqlx/src/mysqlx_backend_session.cpp deleted file mode 100644 index af4132903..000000000 --- a/plugins/mysqlx/src/mysqlx_backend_session.cpp +++ /dev/null @@ -1,309 +0,0 @@ -#include "mysqlx_backend_session.h" -#include "mysqlx_protocol.h" - -#include "mysqlx.pb.h" -#include "mysqlx_connection.pb.h" -#include "mysqlx_session.pb.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MysqlxBackendSession::MysqlxBackendSession() = default; - -MysqlxBackendSession::~MysqlxBackendSession() { - if (backend_fd_ >= 0) { - close(backend_fd_); - backend_fd_ = -1; - } -} - -bool MysqlxBackendSession::connect(const MysqlxResolvedIdentity& identity, - const MysqlxBackendEndpoint& endpoint, - std::string& err) { - if (identity.backend_auth_mode == MysqlxBackendAuthMode::pass_through) { - err = "pass_through backend auth mode not supported in Phase 1"; - return false; - } - - if (endpoint.hostname.empty() || endpoint.mysqlx_port <= 0) { - err = "no valid backend endpoint"; - return false; - } - - std::string port_str = std::to_string(endpoint.mysqlx_port); - struct addrinfo hints {}, *result = nullptr; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - int gai_rc = getaddrinfo(endpoint.hostname.c_str(), port_str.c_str(), &hints, &result); - if (gai_rc != 0) { - err = "mysqlx backend: cannot resolve '"; - err += endpoint.hostname; - err += "': "; - err += gai_strerror(gai_rc); - return false; - } - - int fd = -1; - for (struct addrinfo* rp = result; rp != nullptr; rp = rp->ai_next) { - fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (fd < 0) continue; - - int flag = 1; - setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); - - struct timeval tv { 10, 0 }; - setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - - if (::connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) { - break; - } - close(fd); - fd = -1; - } - freeaddrinfo(result); - - if (fd < 0) { - err = "mysqlx backend: connect failed to "; - err += endpoint.hostname + ":" + std::to_string(endpoint.mysqlx_port); - return false; - } - backend_fd_ = fd; - - // Authenticate to the backend MySQL X server. - std::string backend_user = identity.backend_username; - std::string backend_pass = identity.backend_password; - - if (!authenticate_backend(backend_user, backend_pass, err)) { - // Auth failure: close the socket we just opened so a later retry - // on this MysqlxBackendSession doesn't overwrite backend_fd_ and - // leak the previous fd. - close(backend_fd_); - backend_fd_ = -1; - return false; - } - return true; -} - -bool MysqlxBackendSession::authenticate_backend(const std::string& username, - const std::string& password, - std::string& err) { - // Step 1: Send CapabilitiesGet to learn what the backend supports. - { - Mysqlx::Connection::CapabilitiesGet cap_get; - std::string serialized; - cap_get.SerializeToString(&serialized); - auto frame = mysqlx_build_frame( - Mysqlx::ClientMessages_Type_CON_CAPABILITIES_GET, - serialized - ); - if (!mysqlx_write_all(backend_fd_, frame.data(), frame.size())) { - err = "failed to send CapabilitiesGet to backend"; - return false; - } - } - - // Step 2: Read Capabilities response (skip any preceding Notice frames). - { - MysqlxFrameHeader header {}; - std::vector payload {}; - if (!mysqlx_read_frame(backend_fd_, header, payload)) { - err = "failed to read Capabilities from backend"; - return false; - } - while (header.message_type == Mysqlx::ServerMessages_Type_NOTICE) { - if (!mysqlx_read_frame(backend_fd_, header, payload)) { - err = "failed to read frame after notice during capabilities"; - return false; - } - } - if (header.message_type != Mysqlx::ServerMessages_Type_CONN_CAPABILITIES) { - err = "unexpected response to CapabilitiesGet from backend"; - return false; - } - } - - // Step 3: Send AuthenticateStart with MYSQL41. - { - Mysqlx::Session::AuthenticateStart auth_start; - auth_start.set_mech_name("MYSQL41"); - // auth_data: schema\0username\0 - std::string auth_data; - auth_data += '\0'; // empty schema - auth_data += username; - auth_data += '\0'; - auth_start.set_auth_data(auth_data); - - std::string serialized; - auth_start.SerializeToString(&serialized); - auto frame = mysqlx_build_frame( - Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_START, - serialized - ); - if (!mysqlx_write_all(backend_fd_, frame.data(), frame.size())) { - err = "failed to send AuthenticateStart to backend"; - return false; - } - } - - // Step 4: Read AuthenticateContinue with challenge (skip Notice frames). - std::vector challenge {}; - { - MysqlxFrameHeader header {}; - std::vector payload {}; - if (!mysqlx_read_frame(backend_fd_, header, payload)) { - err = "failed to read auth challenge from backend"; - return false; - } - - // Skip any intervening Notice frames. - while (header.message_type == Mysqlx::ServerMessages_Type_NOTICE) { - if (!mysqlx_read_frame(backend_fd_, header, payload)) { - err = "failed to read frame after notice during auth challenge"; - return false; - } - } - - if (header.message_type == Mysqlx::ServerMessages_Type_ERROR) { - Mysqlx::Error error_msg; - error_msg.ParseFromArray(payload.data(), static_cast(payload.size())); - err = "backend auth error: " + error_msg.msg(); - return false; - } - - if (header.message_type != Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_CONTINUE) { - err = "unexpected message type during backend auth"; - return false; - } - - Mysqlx::Session::AuthenticateContinue auth_continue; - if (!auth_continue.ParseFromArray(payload.data(), static_cast(payload.size()))) { - err = "failed to parse backend AuthenticateContinue"; - return false; - } - - const std::string& ch = auth_continue.auth_data(); - challenge.assign(ch.begin(), ch.end()); - } - - // Step 5: Compute MYSQL41 scramble and send AuthenticateContinue. - // Wire format: \0username\0*UPPERCASE_HEX(scramble) - { - auto scramble = mysqlx_mysql41_scramble(challenge, password); - std::string hex_scramble = mysqlx_hex_encode(scramble); - - std::string auth_data; - auth_data += '\0'; // empty schema - auth_data += username; - auth_data += '\0'; - auth_data += '*'; - auth_data += hex_scramble; - - Mysqlx::Session::AuthenticateContinue auth_continue; - auth_continue.set_auth_data(auth_data); - - std::string serialized; - auth_continue.SerializeToString(&serialized); - auto frame = mysqlx_build_frame( - Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_CONTINUE, - serialized - ); - if (!mysqlx_write_all(backend_fd_, frame.data(), frame.size())) { - err = "failed to send auth response to backend"; - return false; - } - } - - // Step 6: Read AuthenticateOk (or Error). - { - MysqlxFrameHeader header {}; - std::vector payload {}; - if (!mysqlx_read_frame(backend_fd_, header, payload)) { - err = "failed to read auth result from backend"; - return false; - } - - if (header.message_type == Mysqlx::ServerMessages_Type_ERROR) { - Mysqlx::Error error_msg; - error_msg.ParseFromArray(payload.data(), static_cast(payload.size())); - err = "backend auth failed: " + error_msg.msg(); - return false; - } - - // Accept both AuthenticateOk and Notice (some servers send notices first). - while (header.message_type == Mysqlx::ServerMessages_Type_NOTICE) { - if (!mysqlx_read_frame(backend_fd_, header, payload)) { - err = "failed to read post-notice frame from backend"; - return false; - } - } - - if (header.message_type != Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_OK) { - err = "unexpected message type after backend auth"; - return false; - } - } - - return true; -} - -bool MysqlxBackendSession::relay(int frontend_fd) { - // Byte-level bidirectional relay between frontend and backend. - uint8_t buf[65536]; - - struct pollfd pfds[2]; - pfds[0].fd = frontend_fd; - pfds[0].events = POLLIN; - pfds[1].fd = backend_fd_; - pfds[1].events = POLLIN; - - while (true) { - pfds[0].revents = 0; - pfds[1].revents = 0; - - int ready = poll(pfds, 2, 30000 /*ms*/); - if (ready < 0) { - return false; - } - if (ready == 0) { - return false; // idle timeout - } - - // Check for errors/hangups. - if ((pfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) || - (pfds[1].revents & (POLLERR | POLLHUP | POLLNVAL))) { - return false; - } - - // Frontend → Backend - if (pfds[0].revents & POLLIN) { - ssize_t n = read(frontend_fd, buf, sizeof(buf)); - if (n <= 0) { - return n == 0; - } - if (!mysqlx_write_all(backend_fd_, buf, static_cast(n))) { - return false; - } - } - - // Backend → Frontend - if (pfds[1].revents & POLLIN) { - ssize_t n = read(backend_fd_, buf, sizeof(buf)); - if (n <= 0) { - return n == 0; - } - if (!mysqlx_write_all(frontend_fd, buf, static_cast(n))) { - return false; - } - } - } -} diff --git a/plugins/mysqlx/src/mysqlx_frontend_session.cpp b/plugins/mysqlx/src/mysqlx_frontend_session.cpp deleted file mode 100644 index 4bbb02c4c..000000000 --- a/plugins/mysqlx/src/mysqlx_frontend_session.cpp +++ /dev/null @@ -1,406 +0,0 @@ -#include "mysqlx_frontend_session.h" - -#include "mysqlx.pb.h" -#include "mysqlx_connection.pb.h" -#include "mysqlx_session.pb.h" -#include "mysqlx_notice.pb.h" - -#include -#include -#include -#include -#include -#include - -namespace { - -constexpr size_t CHALLENGE_LENGTH = 20; - -// Parse "schema\0user\0" from auth_data (MYSQL41 initial message). -// Returns false if parsing fails. -bool parse_mysql41_auth_data(const std::string& auth_data, - std::string& schema, - std::string& username) { - // Format: \0-terminated schema, then \0-terminated username, then scramble. - // For AuthenticateStart, auth_data is just schema + \0 + user + \0. - size_t first_nul = auth_data.find('\0'); - if (first_nul == std::string::npos) { - return false; - } - - schema = auth_data.substr(0, first_nul); - - size_t rest_start = first_nul + 1; - size_t second_nul = auth_data.find('\0', rest_start); - if (second_nul == std::string::npos) { - // No second NUL — everything after first NUL is username. - username = auth_data.substr(rest_start); - } else { - username = auth_data.substr(rest_start, second_nul - rest_start); - } - - return !username.empty(); -} - -// Parse PLAIN auth data: \0username\0password -bool parse_plain_auth_data(const std::string& auth_data, - std::string& username, - std::string& password) { - // Format: \0user\0password - if (auth_data.empty() || auth_data[0] != '\0') { - return false; - } - - size_t second_nul = auth_data.find('\0', 1); - if (second_nul == std::string::npos) { - return false; - } - - username = auth_data.substr(1, second_nul - 1); - password = auth_data.substr(second_nul + 1); - return !username.empty(); -} - -// Check if the auth method is allowed for this user. -// Empty allowed_auth_methods means all supported methods are allowed. -bool is_auth_method_allowed(const std::string& method, const std::string& allowed) { - if (allowed.empty()) { - return true; - } - // allowed_auth_methods is comma-separated, e.g. "MYSQL41,PLAIN" - size_t pos = 0; - while (pos < allowed.size()) { - size_t comma = allowed.find(',', pos); - if (comma == std::string::npos) comma = allowed.size(); - std::string token = allowed.substr(pos, comma - pos); - // Trim whitespace. - while (!token.empty() && token.front() == ' ') token.erase(0, 1); - while (!token.empty() && token.back() == ' ') token.pop_back(); - if (token == method) return true; - pos = comma + 1; - } - return false; -} - -} // namespace - -MysqlxFrontendSession::MysqlxFrontendSession(int client_fd) - : client_fd_(client_fd) { - // Set socket timeouts to prevent slow-client DoS. - struct timeval tv { 30, 0 }; // 30 second timeout - setsockopt(client_fd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - setsockopt(client_fd_, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - - // Generate random challenge for MYSQL41. - auth_challenge_.resize(CHALLENGE_LENGTH); - RAND_bytes(auth_challenge_.data(), CHALLENGE_LENGTH); -} - -MysqlxFrontendSession::~MysqlxFrontendSession() = default; - -bool MysqlxFrontendSession::run_handshake_and_auth(MysqlxConfigStore& config_store) { - // X Protocol handshake loop: - // 1. Wait for CapabilitiesGet or AuthenticateStart. - // 2. If CapabilitiesGet → reply with Capabilities, then wait for next. - // 3. If CapabilitiesSet → handle TLS etc, reply Ok, wait for next. - // 4. If AuthenticateStart → run auth flow. - - while (true) { - MysqlxFrameHeader header {}; - std::vector payload {}; - - if (!mysqlx_read_frame(client_fd_, header, payload)) { - return false; - } - - switch (header.message_type) { - case Mysqlx::ClientMessages_Type_CON_CAPABILITIES_GET: - if (!handle_capabilities_get()) { - return false; - } - break; - - case Mysqlx::ClientMessages_Type_CON_CAPABILITIES_SET: - if (!handle_capabilities_set(payload)) { - return false; - } - break; - - case Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_START: { - // Parse AuthenticateStart. - Mysqlx::Session::AuthenticateStart auth_start; - if (!auth_start.ParseFromArray(payload.data(), static_cast(payload.size()))) { - mysqlx_send_error(client_fd_, 1045, "Invalid AuthenticateStart message"); - return false; - } - - auth_method_ = auth_start.mech_name(); - - if (!mysqlx_is_supported_auth_method(auth_method_)) { - mysqlx_send_error(client_fd_, 1251, - "Authentication method '" + auth_method_ + "' not supported"); - return false; - } - - if (auth_method_ == "PLAIN") { - // PLAIN: auth_data contains \0user\0password in one shot. - std::string username {}; - std::string password {}; - if (!parse_plain_auth_data(auth_start.auth_data(), username, password)) { - mysqlx_send_error(client_fd_, 1045, "Invalid PLAIN auth data"); - return false; - } - - auto resolved = config_store.resolve_identity(username); - if (!resolved.has_value()) { - mysqlx_send_error(client_fd_, 1045, - "Access denied for user '" + username + "'"); - return false; - } - - identity_ = resolved.value(); - - if (!identity_.x_enabled) { - mysqlx_send_error(client_fd_, 1045, - "X Protocol access not enabled for user '" + username + "'"); - return false; - } - - if (identity_.require_tls) { - mysqlx_send_error(client_fd_, 1045, - "User '" + username + "' requires TLS for X Protocol access"); - return false; - } - - if (!is_auth_method_allowed("PLAIN", identity_.allowed_auth_methods)) { - mysqlx_send_error(client_fd_, 1251, - "Authentication method 'PLAIN' not allowed for user '" + username + "'"); - return false; - } - - if (identity_.backend_auth_mode == MysqlxBackendAuthMode::pass_through) { - mysqlx_send_error(client_fd_, 1045, - "pass_through backend auth mode not supported in Phase 1"); - return false; - } - - // For PLAIN, verify password against backend_password using - // constant-time comparison to prevent timing side-channels. - bool pwd_match = false; - if (!password.empty() && !identity_.backend_password.empty() && - password.size() == identity_.backend_password.size()) { - pwd_match = CRYPTO_memcmp(password.data(), identity_.backend_password.data(), - password.size()) == 0; - } - if (!pwd_match) { - mysqlx_send_error(client_fd_, 1045, - "Access denied for user '" + username + "'"); - return false; - } - - // Send AuthenticateOk. - Mysqlx::Session::AuthenticateOk auth_ok; - std::string serialized; - auth_ok.SerializeToString(&serialized); - auto frame = mysqlx_build_frame( - Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_OK, - serialized - ); - if (!mysqlx_write_all(client_fd_, frame.data(), frame.size())) { - return false; - } - return true; - } - - if (auth_method_ == "MYSQL41") { - // MYSQL41 phase 1: parse schema+user, send challenge. - std::string schema {}; - std::string username {}; - if (!parse_mysql41_auth_data(auth_start.auth_data(), schema, username)) { - mysqlx_send_error(client_fd_, 1045, "Invalid MYSQL41 auth data"); - return false; - } - - auto resolved = config_store.resolve_identity(username); - if (!resolved.has_value()) { - mysqlx_send_error(client_fd_, 1045, - "Access denied for user '" + username + "'"); - return false; - } - - identity_ = resolved.value(); - - if (!identity_.x_enabled) { - mysqlx_send_error(client_fd_, 1045, - "X Protocol access not enabled for user '" + username + "'"); - return false; - } - - if (identity_.require_tls) { - mysqlx_send_error(client_fd_, 1045, - "User '" + username + "' requires TLS for X Protocol access"); - return false; - } - - if (!is_auth_method_allowed("MYSQL41", identity_.allowed_auth_methods)) { - mysqlx_send_error(client_fd_, 1251, - "Authentication method 'MYSQL41' not allowed for user '" + username + "'"); - return false; - } - - if (identity_.backend_auth_mode == MysqlxBackendAuthMode::pass_through) { - mysqlx_send_error(client_fd_, 1045, - "pass_through backend auth mode not supported in Phase 1"); - return false; - } - - // Send AuthenticateContinue with challenge. - if (!send_auth_continue_challenge()) { - return false; - } - - // Wait for AuthenticateContinue from client with scrambled response. - return handle_authenticate(config_store); - } - - mysqlx_send_error(client_fd_, 1251, "Unsupported auth method"); - return false; - } - - case Mysqlx::ClientMessages_Type_CON_CLOSE: - return false; - - default: - mysqlx_send_error(client_fd_, 5000, - "Unexpected message during handshake"); - return false; - } - } -} - -bool MysqlxFrontendSession::handle_capabilities_get() { - return send_capabilities(); -} - -bool MysqlxFrontendSession::handle_capabilities_set(const std::vector& payload) { - Mysqlx::Connection::CapabilitiesSet cap_set; - if (!cap_set.ParseFromArray(payload.data(), static_cast(payload.size()))) { - mysqlx_send_error(client_fd_, 5001, "Invalid CapabilitiesSet message"); - return false; - } - - // Phase 1: reject TLS requests explicitly since we don't implement it yet. - if (cap_set.has_capabilities()) { - for (const auto& cap : cap_set.capabilities().capabilities()) { - if (cap.name() == "tls") { - mysqlx_send_error(client_fd_, 5001, - "TLS capability not supported by mysqlx plugin in Phase 1"); - return false; - } - } - } - - return mysqlx_send_ok(client_fd_); -} - -bool MysqlxFrontendSession::send_capabilities() { - Mysqlx::Connection::Capabilities caps; - - // Advertise supported auth methods. - auto* cap_auth = caps.add_capabilities(); - cap_auth->set_name("authentication.mechanisms"); - auto* auth_array = cap_auth->mutable_value()->mutable_array(); - - auto* mysql41 = auth_array->add_value(); - mysql41->set_type(Mysqlx::Datatypes::Any::SCALAR); - mysql41->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_STRING); - mysql41->mutable_scalar()->mutable_v_string()->set_value("MYSQL41"); - - auto* plain = auth_array->add_value(); - plain->set_type(Mysqlx::Datatypes::Any::SCALAR); - plain->mutable_scalar()->set_type(Mysqlx::Datatypes::Scalar::V_STRING); - plain->mutable_scalar()->mutable_v_string()->set_value("PLAIN"); - - std::string serialized; - caps.SerializeToString(&serialized); - - auto frame = mysqlx_build_frame( - Mysqlx::ServerMessages_Type_CONN_CAPABILITIES, - serialized - ); - - return mysqlx_write_all(client_fd_, frame.data(), frame.size()); -} - -bool MysqlxFrontendSession::send_auth_continue_challenge() { - Mysqlx::Session::AuthenticateContinue auth_continue; - auth_continue.set_auth_data( - std::string(auth_challenge_.begin(), auth_challenge_.end()) - ); - - std::string serialized; - auth_continue.SerializeToString(&serialized); - - auto frame = mysqlx_build_frame( - Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_CONTINUE, - serialized - ); - - return mysqlx_write_all(client_fd_, frame.data(), frame.size()); -} - -bool MysqlxFrontendSession::handle_authenticate(MysqlxConfigStore& /* config_store */) { - // Read AuthenticateContinue from client. - MysqlxFrameHeader header {}; - std::vector payload {}; - - if (!mysqlx_read_frame(client_fd_, header, payload)) { - return false; - } - - if (header.message_type != Mysqlx::ClientMessages_Type_SESS_AUTHENTICATE_CONTINUE) { - mysqlx_send_error(client_fd_, 1045, "Expected AuthenticateContinue"); - return false; - } - - Mysqlx::Session::AuthenticateContinue auth_continue; - if (!auth_continue.ParseFromArray(payload.data(), static_cast(payload.size()))) { - mysqlx_send_error(client_fd_, 1045, "Invalid AuthenticateContinue"); - return false; - } - - // Client response is formatted as: schema\0username\0*HEX(scramble) - // Parse out the hex scramble after the '*' marker. - const std::string& response_str = auth_continue.auth_data(); - auto star_pos = response_str.find('*'); - if (star_pos == std::string::npos || star_pos + 1 >= response_str.size()) { - mysqlx_send_error(client_fd_, 1045, "Invalid MYSQL41 response format"); - return false; - } - - std::string hex_part = response_str.substr(star_pos + 1); - std::vector client_response {}; - if (!mysqlx_hex_decode(hex_part, client_response)) { - mysqlx_send_error(client_fd_, 1045, "Invalid MYSQL41 hex scramble"); - return false; - } - - // Verify MYSQL41 scramble against the stored password. - if (!mysqlx_mysql41_verify(auth_challenge_, client_response, identity_.backend_password)) { - mysqlx_send_error(client_fd_, 1045, - "Access denied for user '" + identity_.username + "'"); - return false; - } - - // Authentication succeeded — send AuthenticateOk. - Mysqlx::Session::AuthenticateOk auth_ok; - std::string serialized; - auth_ok.SerializeToString(&serialized); - - auto frame = mysqlx_build_frame( - Mysqlx::ServerMessages_Type_SESS_AUTHENTICATE_OK, - serialized - ); - - return mysqlx_write_all(client_fd_, frame.data(), frame.size()); -} diff --git a/plugins/mysqlx/src/mysqlx_session.cpp b/plugins/mysqlx/src/mysqlx_session.cpp index c59b60c4b..a7da3d49e 100644 --- a/plugins/mysqlx/src/mysqlx_session.cpp +++ b/plugins/mysqlx/src/mysqlx_session.cpp @@ -123,7 +123,6 @@ handler_again: case WAITING_CLIENT_XMSG: handler_waiting_client_msg(); break; case CONNECTING_SERVER: handler_connecting_server(); break; case WAITING_SERVER_XMSG: handler_waiting_server_msg(); break; - case X_FAST_FORWARD: handler_fast_forward(); break; case X_TLS_ACCEPT_INIT: handler_tls_accept_init(); break; case X_SESSION_RESET_WAITING: handler_session_reset_waiting(); break; case X_SESSION_CLOSING: handler_session_closing(); break; @@ -641,9 +640,6 @@ void MysqlxSession::handler_waiting_server_msg() { } } -void MysqlxSession::handler_fast_forward() { -} - void MysqlxSession::handler_session_reset_waiting() { if (server_ds().get_fd() < 0) { return_backend_to_pool();