Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions mysql-test/suite/rpl/r/rpl_ssl_verify_server_cert.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
include/master-slave.inc
[connection master]
#
# Test 1: master_ssl_verify_server_cert=0 should PASS
# (verification disabled, simulated cert failure should not matter)
#
connection slave;
include/stop_slave.inc
SET @saved_dbug = @@GLOBAL.debug_dbug;
SET GLOBAL debug_dbug = '+d,simulate_ssl_unable_to_get_issuer_cert';
CHANGE MASTER TO
master_host='127.0.0.1',
master_port=MASTER_PORT,
master_user='root',
master_ssl=1,
master_ssl_verify_server_cert=0;
Master_SSL_Allowed = 'Yes'
Master_SSL_Verify_Server_Cert = 'No'
START SLAVE IO_THREAD;
include/wait_for_slave_param.inc [Slave_IO_Running]
# IO thread started successfully - verification was correctly disabled
#
# Test 2: master_ssl_verify_server_cert=1 should FAIL
# (verification enabled, simulated cert failure should be caught)
#
STOP SLAVE IO_THREAD;
include/wait_for_slave_io_to_stop.inc
CHANGE MASTER TO
master_host='127.0.0.1',
master_port=MASTER_PORT,
master_user='root',
master_ssl=1,
master_ssl_verify_server_cert=1;
Master_SSL_Allowed = 'Yes'
Master_SSL_Verify_Server_Cert = 'Yes'
START SLAVE IO_THREAD;
include/wait_for_slave_param.inc [Last_IO_Errno]
# IO thread failed as expected - verification correctly caught the failure
#
# Cleanup
#
STOP SLAVE IO_THREAD;
include/wait_for_slave_io_to_stop.inc
SET GLOBAL debug_dbug = @saved_dbug;
RESET SLAVE ALL;
CHANGE MASTER TO
master_host='127.0.0.1',
master_port=MASTER_PORT,
master_user='root',
master_ssl=1,
master_ssl_verify_server_cert=0;
include/rpl_end.inc
103 changes: 103 additions & 0 deletions mysql-test/suite/rpl/t/rpl_ssl_verify_server_cert.test
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we reword the filename and description a bit, so this test verifies master_ssl_verify_server_cert=0 in general, not just a specific bug?

Copy link
Copy Markdown
Contributor Author

@gengtianuiowa gengtianuiowa Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworded the file name and description

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#
# Test for master_ssl_verify_server_cert option
#
# This test verifies that master_ssl_verify_server_cert correctly controls
# SSL certificate verification during replication.
#
# Test strategy:
# - Use debug point to simulate SSL certificate verification failure
# - Test 1: master_ssl_verify_server_cert=0 -> connection succeeds (no verification)
# - Test 2: master_ssl_verify_server_cert=1 -> connection fails (verification enabled)
#

--source include/have_debug.inc
--source include/have_ssl_communication.inc
--source include/master-slave.inc

--echo #
--echo # Test 1: master_ssl_verify_server_cert=0 should PASS
--echo # (verification disabled, simulated cert failure should not matter)
--echo #

--connection slave
--source include/stop_slave.inc

# Enable debug point to simulate SSL certificate verification failure
SET @saved_dbug = @@GLOBAL.debug_dbug;
SET GLOBAL debug_dbug = '+d,simulate_ssl_unable_to_get_issuer_cert';

# Use SSL with verification disabled - simulated cert failure should not matter
--replace_result $MASTER_MYPORT MASTER_PORT
eval CHANGE MASTER TO
master_host='127.0.0.1',
master_port=$MASTER_MYPORT,
master_user='root',
master_ssl=1,
master_ssl_verify_server_cert=0;

--let $status_items= Master_SSL_Allowed, Master_SSL_Verify_Server_Cert
--source include/show_slave_status.inc

START SLAVE IO_THREAD;

# Wait for the connection to establish
--let $slave_param= Slave_IO_Running
--let $slave_param_value= Yes
--let $slave_timeout= 30
--source include/wait_for_slave_param.inc

--echo # IO thread started successfully - verification was correctly disabled

--echo #
--echo # Test 2: master_ssl_verify_server_cert=1 should FAIL
--echo # (verification enabled, simulated cert failure should be caught)
--echo #

STOP SLAVE IO_THREAD;
--source include/wait_for_slave_io_to_stop.inc

# Use SSL with verification enabled - connection should fail
# because of simulated certificate verification failure
--replace_result $MASTER_MYPORT MASTER_PORT
eval CHANGE MASTER TO
master_host='127.0.0.1',
master_port=$MASTER_MYPORT,
master_user='root',
master_ssl=1,
master_ssl_verify_server_cert=1;

--let $status_items= Master_SSL_Allowed, Master_SSL_Verify_Server_Cert
--source include/show_slave_status.inc

START SLAVE IO_THREAD;

# Wait for SSL connection error (2026 = CR_SSL_CONNECTION_ERROR)
# The slave will keep retrying, so we check Last_IO_Errno instead of waiting for IO thread to stop
--let $slave_param= Last_IO_Errno
--let $slave_param_value= 2026
--let $slave_timeout= 30
--source include/wait_for_slave_param.inc

--echo # IO thread failed as expected - verification correctly caught the failure

--echo #
--echo # Cleanup
--echo #

STOP SLAVE IO_THREAD;
--source include/wait_for_slave_io_to_stop.inc

SET GLOBAL debug_dbug = @saved_dbug;

RESET SLAVE ALL;

--replace_result $MASTER_MYPORT MASTER_PORT
eval CHANGE MASTER TO
master_host='127.0.0.1',
master_port=$MASTER_MYPORT,
master_user='root',
master_ssl=1,
master_ssl_verify_server_cert=0;

--let $rpl_only_running_threads= 1
--source include/rpl_end.inc
7 changes: 6 additions & 1 deletion sql-common/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1613,7 +1613,11 @@ static int ssl_verify_server_cert(MYSQL *mysql, const char **errptr, int is_loca
goto error;
}

switch (SSL_get_verify_result(ssl))
{
long verify_result= SSL_get_verify_result(ssl);
DBUG_EXECUTE_IF("simulate_ssl_unable_to_get_issuer_cert",
verify_result= X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY;);
switch (verify_result)
{
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: /* OpenSSL */
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: /* OpenSSL */
Expand All @@ -1637,6 +1641,7 @@ static int ssl_verify_server_cert(MYSQL *mysql, const char **errptr, int is_loca
*errptr= "Failed to verify the server certificate";
break;
}
}

error:
X509_free(server_cert);
Expand Down
14 changes: 13 additions & 1 deletion sql/rpl_mi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1602,8 +1602,20 @@ void setup_mysql_connection_for_master(MYSQL *mysql, Master_info *mi,
mi->master_ssl_capath, mi->master_ssl_cipher);
mysql_options(mysql, MYSQL_OPT_SSL_CRL, mi->master_ssl_crl);
mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH, mi->master_ssl_crlpath);
/*
Fix incorrect memory access for master_ssl_verify_server_cert
mysql_options(MYSQL_OPT_SSL_VERIFY_SERVER_CERT) expects a pointer to
my_bool (1 byte). However, master_ssl_verify_server_cert is of type
Optional_bool_value<>, which has the following memory layout:

struct Optional_bool_value : Persistent {
trilean value; // 4-byte enum (YES/NO/DEFAULT)
};
The fix is to pass &mi->master_ssl_verify_server_cert.value to access
the actual trilean value directly.
*/
mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
&mi->master_ssl_verify_server_cert);
&mi->master_ssl_verify_server_cert.value);
Comment on lines +1616 to +1618
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall MYSQL_OPT_SSL_VERIFY_SERVER_CERT (i.e., client flag --ssl-verify-server-cert) expects a boolean, so it should be

Suggested change
*/
mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
&mi->master_ssl_verify_server_cert);
&mi->master_ssl_verify_server_cert.value);
*/
bool ssl_verify_server_cert= mi->master_ssl_verify_server_cert;
mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
&ssl_verify_server_cert);

as MDEV-28302 intended.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did this slip past both CI and QA 🤦?

I read through part of the existing tests. It's easy for this issue to slip through the existing tests because the current tests only verify the response after running show replica status;. In the result, the Master_SSL_Verify_Server_Cert still shows the right (YES/NO) value because the logic for parsing the YES/NO value seems different than the value retrieval mentioned here.

The new implementation should be the same as the current implementation if there are only two possible values. Even though the default value (-1) is picked, !*(my_bool*) arg will convert it to false, which is the same as passing YES. This is consistent with the current default of turning on Master_SSL_Verify_Server_Cert by default.

}
else
#endif
Expand Down
Loading