Merge commit from fork

Fix PROXY protocol v1 address overflow
v3.0
René Cannaò 2 days ago committed by GitHub
commit fa6ebcdcc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -3,12 +3,17 @@
#include <stdio.h>
#include <assert.h>
#include <iostream>
#include <vector>
#ifdef __FreeBSD__
#include <sys/socket.h>
#endif
static bool DEBUG_ProxyProtocolInfo = false;
#define PROXY_PROTOCOL_STRINGIFY_HELPER(x) #x
#define PROXY_PROTOCOL_STRINGIFY(x) PROXY_PROTOCOL_STRINGIFY_HELPER(x)
#define PROXY_PROTOCOL_ADDR_SCAN_FMT "%" PROXY_PROTOCOL_STRINGIFY(INET6_ADDRSTRLEN) "s"
// Function to parse the PROXY protocol header
bool ProxyProtocolInfo::parseProxyProtocolHeader(const char* packet, size_t packet_length) {
// Check for minimum header length (including CRLF)
@ -16,16 +21,16 @@ bool ProxyProtocolInfo::parseProxyProtocolHeader(const char* packet, size_t pack
return false; // Not a valid PROXY protocol header
}
// Create a temporary buffer on the stack
char temp_buffer[packet_length + 1];
// Copy the header into a NUL-terminated buffer before using C string parsers.
std::vector<char> temp_buffer(packet_length + 1);
// Copy the packet data
memcpy(temp_buffer, packet, packet_length);
memcpy(temp_buffer.data(), packet, packet_length);
temp_buffer[packet_length] = '\0'; // Null-terminate the buffer
// Verify the PROXY protocol signature
if (memcmp(temp_buffer, "PROXY", 5) != 0) {
if (memcmp(temp_buffer.data(), "PROXY", 5) != 0) {
return false; // Not a valid PROXY protocol header
}
@ -35,14 +40,17 @@ bool ProxyProtocolInfo::parseProxyProtocolHeader(const char* packet, size_t pack
}
// Check for the protocol type
if (memcmp(temp_buffer + 6, "TCP4", 4) == 0 ||
memcmp(temp_buffer + 6, "TCP6", 4) == 0 ||
memcmp(temp_buffer + 6, "UNKNOWN", 7) == 0) {
if (memcmp(temp_buffer.data() + 6, "TCP4", 4) == 0 ||
memcmp(temp_buffer.data() + 6, "TCP6", 4) == 0 ||
memcmp(temp_buffer.data() + 6, "UNKNOWN", 7) == 0) {
// Parse the header using sscanf
int result = sscanf(temp_buffer, "PROXY %*s %s %s %hu %hu\r\n",
int result = sscanf(
temp_buffer.data(),
"PROXY %*s " PROXY_PROTOCOL_ADDR_SCAN_FMT " " PROXY_PROTOCOL_ADDR_SCAN_FMT " %hu %hu\r\n",
source_address, destination_address,
&source_port, &destination_port);
&source_port, &destination_port
);
// Check if sscanf successfully parsed all fields
if (result == 4) {
@ -385,4 +393,3 @@ bool ProxyProtocolInfo::is_valid_subnet(const char* subnet) {
return true; // Valid subnet
}

@ -190,6 +190,7 @@
"reg_test_com_change_user_malformed_packet-t" : [ "mysql84-g1" ],
"reg_test_3546-stmt_empty_params-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_3549-autocommit_tracking-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_proxy_protocol_oversized_address-t" : [ "mysql84-g1" ],
"reg_test_3585-stmt_metadata-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],
"reg_test_stmt_close_short_packet-t" : [ "mysql84-g1" ],
"reg_test_3591-restapi_num_fds-t" : [ "legacy-g1","mysql84-g1","mysql-auto_increment_delay_multiplex=0-g1","mysql-multiplexing=false-g1","mysql-query_digests=0-g1","mysql-query_digests_keep_comment=1-g1" ],

@ -0,0 +1,135 @@
/**
* @file reg_test_proxy_protocol_oversized_address-t.cpp
* @brief Verify oversized PROXY protocol v1 address fields do not corrupt ProxySQL.
*/
#include <string>
#include <stdio.h>
#include <arpa/inet.h>
#include "mysql.h"
#include "tap.h"
#include "command_line.h"
#include "utils.h"
#include "json.hpp"
using std::string;
using nlohmann::json;
static MYSQL* connect_admin(const CommandLine& cl) {
MYSQL* admin = mysql_init(nullptr);
if (!admin) {
return nullptr;
}
if (!mysql_real_connect(admin, cl.host, cl.admin_username, cl.admin_password, nullptr, cl.admin_port, nullptr, 0)) {
diag("Admin connection failed: %s", mysql_error(admin));
mysql_close(admin);
return nullptr;
}
return admin;
}
static MYSQL* connect_mysql_with_proxy_header(const CommandLine& cl, const string& header) {
MYSQL* proxy = mysql_init(nullptr);
if (!proxy) {
return nullptr;
}
mysql_optionsv(proxy, MARIADB_OPT_PROXY_HEADER, header.c_str(), header.size());
if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, nullptr, cl.port, nullptr, 0)) {
diag("Connection with PROXY header failed: %s", mysql_error(proxy));
mysql_close(proxy);
return nullptr;
}
return proxy;
}
static MYSQL* connect_mysql(const CommandLine& cl) {
MYSQL* proxy = mysql_init(nullptr);
if (!proxy) {
return nullptr;
}
if (!mysql_real_connect(proxy, cl.host, cl.username, cl.password, nullptr, cl.port, nullptr, 0)) {
diag("Regular client connection failed: %s", mysql_error(proxy));
mysql_close(proxy);
return nullptr;
}
return proxy;
}
static bool session_has_proxy_v1(MYSQL* proxy) {
json session = fetch_internal_session(proxy, false);
json::iterator client = session.find("client");
if (client == session.end()) {
return false;
}
return client->find("PROXY_V1") != client->end();
}
static bool run_select_one(MYSQL* proxy) {
if (mysql_query(proxy, "SELECT 1")) {
diag("'SELECT 1' failed: %s", mysql_error(proxy));
return false;
}
MYSQL_RES* result = mysql_store_result(proxy);
if (!result) {
diag("'SELECT 1' returned no result: %s", mysql_error(proxy));
return false;
}
mysql_free_result(result);
return true;
}
int main(int argc, char** argv) {
CommandLine cl;
if (cl.getEnv()) {
diag("Failed to get the required environmental variables.");
return EXIT_FAILURE;
}
plan(5);
MYSQL* admin = connect_admin(cl);
if (!admin) {
return exit_status();
}
MYSQL_QUERY(admin, "SET mysql-proxy_protocol_networks='*'");
MYSQL_QUERY(admin, "LOAD MYSQL VARIABLES TO RUNTIME");
string oversized_source(INET6_ADDRSTRLEN + 64, 'A');
string header = "PROXY TCP4 " + oversized_source + " 192.168.0.11 56324 443\r\n";
MYSQL* malformed = connect_mysql_with_proxy_header(cl, header);
ok(malformed != nullptr, "Malformed oversized PROXY header does not abort the client connection");
ok(malformed != nullptr && session_has_proxy_v1(malformed) == false, "Oversized PROXY header is ignored");
ok(malformed != nullptr && run_select_one(malformed), "Connection remains usable after oversized PROXY header");
if (malformed) {
mysql_close(malformed);
}
MYSQL* clean = connect_mysql(cl);
ok(clean != nullptr, "Fresh client connection succeeds after malformed PROXY header");
ok(clean != nullptr && run_select_one(clean), "Fresh client connection remains usable");
if (clean) {
mysql_close(clean);
}
MYSQL_QUERY(admin, "SET mysql-proxy_protocol_networks=''");
MYSQL_QUERY(admin, "LOAD MYSQL VARIABLES TO RUNTIME");
mysql_close(admin);
return exit_status();
}
Loading…
Cancel
Save