From e6ca3a0458fbb4ddfc5fce5c03f1de8af1f67cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Tue, 23 Jun 2026 10:59:35 +0200 Subject: [PATCH 01/11] MDEV-39857 Add multi-certificate SSL support via --ssl-cert-add/--ssl-key-add Add --ssl-cert-add and --ssl-key-add server options to load additional certificate/key pairs into the same SSL_CTX. OpenSSL natively supports one certificate per key type (RSA, ECDSA, EdDSA) and automatically selects the matching certificate during the TLS handshake. Following the --plugin-load / --plugin-load-add pattern: - --ssl-cert / --ssl-key override the primary cert/key and reset any previously added alt certs/keys - --ssl-cert-add / --ssl-key-add append additional certs/keys. If no primary exists, the first add becomes the primary Each additional certificate is loaded via SSL_CTX_use_certificate_chain_file() so intermediate CA chains are included. Up to 5 certificates total are supported. New status variables: - Ssl_server_cert_type (session): key type used for this connection - Ssl_server_cert_types (global): all loaded cert key types WolfSSL: multiple certificates are not supported. The server rejects startup with an error if --ssl-cert-add/--ssl-key-add are specified. Cert type enumeration falls back to reporting only the primary type. Example configurations: [mysqld] ssl-cert=/path/to/server-rsa.crt ssl-key=/path/to/server-rsa.key ssl-cert-add=/path/to/server-ecdsa.crt ssl-key-add=/path/to/server-ecdsa.key or equivalently (first add becomes primary): [mysqld] ssl-cert-add=/path/to/server-rsa.crt ssl-key-add=/path/to/server-rsa.key ssl-cert-add=/path/to/server-ecdsa.crt ssl-key-add=/path/to/server-ecdsa.key Pre-generated ECDSA and EdDSA test certificates added to std_data/. Test coverage: 4 tests in ssl_multi_cert (3 cert types, cipher selection, TLS 1.3) and 15 tests in ssl_multi_cert_errors (invalid files, count mismatches, reset behavior, FLUSH SSL, order independence, first-add-becomes-primary for both cert and key sides). --- include/violite.h | 4 +- mysql-test/lib/generate-ssl-certs.sh | 12 ++ mysql-test/main/ssl.result | 2 + mysql-test/main/ssl_multi_cert.result | 66 ++++++ mysql-test/main/ssl_multi_cert.test | 84 ++++++++ mysql-test/main/ssl_multi_cert_errors.result | 87 ++++++++ mysql-test/main/ssl_multi_cert_errors.test | 205 +++++++++++++++++++ mysql-test/std_data/server-ecdsa-cert.pem | 21 ++ mysql-test/std_data/server-ecdsa-key.pem | 8 + mysql-test/std_data/server-eddsa-cert.pem | 20 ++ mysql-test/std_data/server-eddsa-key.pem | 3 + sql/mysqld.cc | 165 ++++++++++++++- sql/mysqld.h | 2 + vio/viosslfactories.c | 59 +++++- 14 files changed, 731 insertions(+), 7 deletions(-) create mode 100644 mysql-test/main/ssl_multi_cert.result create mode 100644 mysql-test/main/ssl_multi_cert.test create mode 100644 mysql-test/main/ssl_multi_cert_errors.result create mode 100644 mysql-test/main/ssl_multi_cert_errors.test create mode 100644 mysql-test/std_data/server-ecdsa-cert.pem create mode 100644 mysql-test/std_data/server-ecdsa-key.pem create mode 100644 mysql-test/std_data/server-eddsa-cert.pem create mode 100644 mysql-test/std_data/server-eddsa-key.pem diff --git a/include/violite.h b/include/violite.h index 5cc3ed4d43ea6..67a9b242da05c 100644 --- a/include/violite.h +++ b/include/violite.h @@ -186,7 +186,9 @@ struct st_VioSSLFd const char *ca_file,const char *ca_path, const char *cipher, enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path, - ulonglong tls_version, const char *passphrase); + ulonglong tls_version, const char *passphrase, + const char **alt_key_files, const char **alt_cert_files, + uint alt_cert_count); void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd); #endif /* HAVE_OPENSSL */ diff --git a/mysql-test/lib/generate-ssl-certs.sh b/mysql-test/lib/generate-ssl-certs.sh index 98e38f0f6d561..d62b6b046ec05 100755 --- a/mysql-test/lib/generate-ssl-certs.sh +++ b/mysql-test/lib/generate-ssl-certs.sh @@ -68,6 +68,18 @@ cp -v client-cert.crl crldir/`openssl x509 -in client-cert.pem -noout -issuer_ha rm -rf demoCA +# ECDSA server certificate signed by test CA +openssl ecparam -genkey -name prime256v1 -out server-ecdsa-key.pem +openssl req -new -key server-ecdsa-key.pem -out server-ecdsa.csr -subj '/CN=localhost' -batch +openssl x509 -req -in server-ecdsa.csr -CA cacert.pem -CAkey cakey.pem -CAcreateserial -out server-ecdsa-cert.pem -days 7300 +rm -f server-ecdsa.csr + +# EdDSA (Ed25519) server certificate signed by test CA +openssl genpkey -algorithm Ed25519 -out server-eddsa-key.pem +openssl req -new -key server-eddsa-key.pem -out server-eddsa.csr -subj '/CN=localhost' -batch +openssl x509 -req -in server-eddsa.csr -CA cacert.pem -CAkey cakey.pem -CAcreateserial -out server-eddsa-cert.pem -days 7300 +rm -f server-eddsa.csr cacert.srl + # --- Certificate Chain --- # These tests are inspired from the following commit from MySQL Server # https://github.com/mysql/mysql-server/commit/969afef933f1872c5f38ea93047ef05c4509c335 diff --git a/mysql-test/main/ssl.result b/mysql-test/main/ssl.result index 1859af85364f8..bc2efc90730be 100644 --- a/mysql-test/main/ssl.result +++ b/mysql-test/main/ssl.result @@ -4,6 +4,7 @@ variable_name Ssl_cipher Ssl_cipher_list Ssl_default_timeout +Ssl_server_cert_type Ssl_server_not_after Ssl_server_not_before Ssl_verify_depth @@ -12,6 +13,7 @@ Ssl_version Ssl_cipher Ssl_cipher_list Ssl_default_timeout +Ssl_server_cert_type Ssl_server_not_after Ssl_server_not_before Ssl_verify_depth diff --git a/mysql-test/main/ssl_multi_cert.result b/mysql-test/main/ssl_multi_cert.result new file mode 100644 index 0000000000000..d3d43d74b0027 --- /dev/null +++ b/mysql-test/main/ssl_multi_cert.result @@ -0,0 +1,66 @@ +# +# Restart server with RSA (primary), ECDSA and EdDSA certs +# +# Kill the server +# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.2 +# +# Verify Ssl_server_cert_types global status shows all three types +# +SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; +Variable_name Value +Ssl_server_cert_types RSA, ECDSA, EdDSA +# +# Test 1: Connect with RSA cipher - should get RSA cert +# +connect rsa_con,localhost,root,,,,,SSL-CIPHER=ECDHE-RSA-AES128-GCM-SHA256; +SHOW STATUS LIKE 'Ssl_cipher'; +Variable_name Value +Ssl_cipher ECDHE-RSA-AES128-GCM-SHA256 +SHOW STATUS LIKE 'Ssl_server_cert_type'; +Variable_name Value +Ssl_server_cert_type RSA +disconnect rsa_con; +# +# Test 2: Connect with ECDSA cipher - should get ECDSA cert +# +connection default; +connect ecdsa_con,localhost,root,,,,,SSL-CIPHER=ECDHE-ECDSA-AES128-GCM-SHA256; +SHOW STATUS LIKE 'Ssl_cipher'; +Variable_name Value +Ssl_cipher ECDHE-ECDSA-AES128-GCM-SHA256 +SHOW STATUS LIKE 'Ssl_server_cert_type'; +Variable_name Value +Ssl_server_cert_type ECDSA +disconnect ecdsa_con; +# +# Test 3: Connect with default ciphers - should succeed +# +connection default; +connect default_con,localhost,root,,,,,SSL; +SELECT (VARIABLE_VALUE <> '') AS have_ssl FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher'; +have_ssl +1 +disconnect default_con; +connection default; +# +# Test 4: TLS 1.3 with all three cert types (EdDSA requires TLS 1.3) +# +connection default; +# Kill the server +# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.3 +SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; +Variable_name Value +Ssl_server_cert_types RSA, ECDSA, EdDSA +connect tls13_con,localhost,root,,,,,SSL; +# TLS 1.3 connection should use one of the loaded cert types +Ssl_server_cert_type is valid: OK +SHOW STATUS LIKE 'Ssl_version'; +Variable_name Value +Ssl_version TLSv1.3 +disconnect tls13_con; +connection default; +# +# Cleanup +# +# Kill the server +# restart diff --git a/mysql-test/main/ssl_multi_cert.test b/mysql-test/main/ssl_multi_cert.test new file mode 100644 index 0000000000000..596d4948952fd --- /dev/null +++ b/mysql-test/main/ssl_multi_cert.test @@ -0,0 +1,84 @@ +# +# Test RSA + ECDSA + EdDSA certificate support via repeated --ssl-cert / --ssl-key +# +--source include/have_ssl_communication.inc + +# Skip on WolfSSL which may not support multiple certs +if (`select @@version_ssl_library like 'wolfSSL%'`) { + skip WolfSSL; +} + +# Ed25519 requires OpenSSL 1.1.1+ +if (`select @@version_ssl_library like 'OpenSSL 1.0%' or @@version_ssl_library like 'OpenSSL 1.1.0%'`) { + skip OpenSSL too old for Ed25519; +} + +--echo # +--echo # Restart server with RSA (primary), ECDSA and EdDSA certs +--echo # + +--let $restart_parameters=--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.2 +--source include/kill_mysqld.inc +--source include/start_mysqld.inc + +--echo # +--echo # Verify Ssl_server_cert_types global status shows all three types +--echo # +SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; + +--echo # +--echo # Test 1: Connect with RSA cipher - should get RSA cert +--echo # +connect (rsa_con,localhost,root,,,,,SSL-CIPHER=ECDHE-RSA-AES128-GCM-SHA256); +SHOW STATUS LIKE 'Ssl_cipher'; +SHOW STATUS LIKE 'Ssl_server_cert_type'; +disconnect rsa_con; + +--echo # +--echo # Test 2: Connect with ECDSA cipher - should get ECDSA cert +--echo # +connection default; +connect (ecdsa_con,localhost,root,,,,,SSL-CIPHER=ECDHE-ECDSA-AES128-GCM-SHA256); +SHOW STATUS LIKE 'Ssl_cipher'; +SHOW STATUS LIKE 'Ssl_server_cert_type'; +disconnect ecdsa_con; + +--echo # +--echo # Test 3: Connect with default ciphers - should succeed +--echo # +connection default; +connect (default_con,localhost,root,,,,,SSL); +SELECT (VARIABLE_VALUE <> '') AS have_ssl FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher'; +disconnect default_con; +connection default; + +--echo # +--echo # Test 4: TLS 1.3 with all three cert types (EdDSA requires TLS 1.3) +--echo # + +connection default; +--let $restart_parameters=--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.3 +--source include/kill_mysqld.inc +--source include/start_mysqld.inc + +SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; + +connect (tls13_con,localhost,root,,,,,SSL); +--echo # TLS 1.3 connection should use one of the loaded cert types +--let $cert_type= query_get_value(SHOW STATUS LIKE 'Ssl_server_cert_type', Value, 1) +if (`SELECT '$cert_type' NOT IN ('RSA', 'ECDSA', 'EdDSA')`) { + --echo # ERROR: Unexpected cert type: $cert_type + --die Ssl_server_cert_type is not RSA, ECDSA, or EdDSA +} +--echo Ssl_server_cert_type is valid: OK +SHOW STATUS LIKE 'Ssl_version'; +disconnect tls13_con; +connection default; + +--echo # +--echo # Cleanup +--echo # + +--let $restart_parameters= +--source include/kill_mysqld.inc +--source include/start_mysqld.inc diff --git a/mysql-test/main/ssl_multi_cert_errors.result b/mysql-test/main/ssl_multi_cert_errors.result new file mode 100644 index 0000000000000..31c45bc40e831 --- /dev/null +++ b/mysql-test/main/ssl_multi_cert_errors.result @@ -0,0 +1,87 @@ +# +# Test 1: --ssl-cert-add with invalid file +# +FOUND 1 /\[ERROR\] SSL error: Unable to get certificate/ in ssl_multi_cert_errors.err +# +# Test 2: --ssl-key-add with invalid file +# +FOUND 1 /\[ERROR\] SSL error: Unable to get private key/ in ssl_multi_cert_errors.err +# +# Test 3: --ssl-cert-add without --ssl-key-add (count mismatch) +# +FOUND 1 /Mismatched --ssl-cert-add/--ssl-key-add/ in ssl_multi_cert_errors.err +# +# Test 4: --ssl-key-add without --ssl-cert-add (count mismatch) +# +FOUND 1 /Mismatched --ssl-cert-add/--ssl-key-add/ in ssl_multi_cert_errors.err +# +# Test 5: same cert type added twice (OpenSSL replaces, last wins) +# +# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-key.pem +SELECT 1; +1 +1 +# Kill the server +# +# Test 6: one valid cert-add + one invalid cert-add (invalid /etc/hosts) +# +FOUND 1 /\[ERROR\] SSL error: Unable to get certificate/ in ssl_multi_cert_errors.err +# +# Test 7: add second pair (RSA + ECDSA), verify Ssl_server_cert_types +# +# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +Ssl_server_cert_types contains RSA and ECDSA: OK +# +# Test 8: FLUSH SSL reloads alt certs correctly +# +FLUSH SSL; +After FLUSH SSL, Ssl_server_cert_types still contains RSA and ECDSA: OK +# +# Test 9: --ssl-cert override + --ssl-cert-add (no interference) +# +# Kill the server +# restart: --ssl-cert=MYSQL_TEST_DIR/std_data/server-cert.pem --ssl-key=MYSQL_TEST_DIR/std_data/server-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--ssl-cert override + --ssl-cert-add: OK +# Kill the server +# +# Test 10: --ssl-cert-add then --ssl-cert resets alt certs +# (like --plugin-load resets --plugin-load-add) +# +# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert=MYSQL_TEST_DIR/std_data/server-cert.pem --ssl-key=MYSQL_TEST_DIR/std_data/server-key.pem +--ssl-cert-add then --ssl-cert resets alt certs: OK +# Kill the server +# +# Test 11: --ssl-cert-add alongside my.cnf default (appends to existing) +# +# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--ssl-cert-add alongside my.cnf default: OK +# Kill the server +# +# Test 12: --ssl-cert-add as sole cert (--ssl-cert= clears, first add becomes primary) +# +# restart: --ssl-cert= --ssl-key= --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--ssl-cert-add as sole cert (first add becomes primary): OK +# Kill the server +# +# Test 13: cert-adds before key-adds (order independent) +# --ssl-cert-add --ssl-cert-add --ssl-key-add --ssl-key-add +# +# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-new-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-new-key.pem +cert-adds before key-adds (order independent): OK +# Kill the server +# +# Test 14: --ssl-key override resets alt keys independently +# +# restart: --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key=MYSQL_TEST_DIR/std_data/server-key.pem --ssl-cert=MYSQL_TEST_DIR/std_data/server-cert.pem +--ssl-key override resets alt keys: OK +# Kill the server +# +# Test 15: --ssl-key-add as sole key (--ssl-key= clears, first add becomes primary) +# +# restart: --ssl-cert= --ssl-key= --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem +--ssl-key-add as sole key (first add becomes primary): OK +# Kill the server +# +# Cleanup +# +# restart diff --git a/mysql-test/main/ssl_multi_cert_errors.test b/mysql-test/main/ssl_multi_cert_errors.test new file mode 100644 index 0000000000000..9d3968576cfaf --- /dev/null +++ b/mysql-test/main/ssl_multi_cert_errors.test @@ -0,0 +1,205 @@ +# +# Test error handling for --ssl-cert-add / --ssl-key-add options +# +# --ssl-cert-add / --ssl-key-add append additional certificates to the SSL +# context. Tests cover invalid files, count mismatches, overflow, and +# interaction with --ssl-cert override. +# +--source include/not_embedded.inc +--source include/have_ssl_communication.inc + +# Skip on WolfSSL which does not support multiple certs +if (`select @@version_ssl_library like 'wolfSSL%'`) { + skip WolfSSL; +} + +--source include/shutdown_mysqld.inc + +--let errorlog=$MYSQL_TMP_DIR/ssl_multi_cert_errors.err +--let SEARCH_FILE=$errorlog + +# Use pre-generated certs from std_data +--let $ecdsa_cert=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem +--let $ecdsa_key=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--let $rsa_cert=$MYSQL_TEST_DIR/std_data/server-cert.pem +--let $rsa_key=$MYSQL_TEST_DIR/std_data/server-key.pem + +--echo # +--echo # Test 1: --ssl-cert-add with invalid file +--echo # +--error 1 +--exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --user=root --ssl-cert-add=/nonexistent/bad.pem --ssl-key-add=$ecdsa_key --log-error=$errorlog +--let SEARCH_PATTERN=\[ERROR\] SSL error: Unable to get certificate +--source include/search_pattern_in_file.inc +--remove_file $SEARCH_FILE + +--echo # +--echo # Test 2: --ssl-key-add with invalid file +--echo # +--error 1 +--exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --user=root --ssl-cert-add=$ecdsa_cert --ssl-key-add=/nonexistent/bad.pem --log-error=$errorlog +--let SEARCH_PATTERN=\[ERROR\] SSL error: Unable to get private key +--source include/search_pattern_in_file.inc +--remove_file $SEARCH_FILE + +--echo # +--echo # Test 3: --ssl-cert-add without --ssl-key-add (count mismatch) +--echo # +--error 1 +--exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --user=root --ssl-cert-add=$ecdsa_cert --log-error=$errorlog +--let SEARCH_PATTERN=Mismatched --ssl-cert-add/--ssl-key-add +--source include/search_pattern_in_file.inc +--remove_file $SEARCH_FILE + +--echo # +--echo # Test 4: --ssl-key-add without --ssl-cert-add (count mismatch) +--echo # +--error 1 +--exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --user=root --ssl-key-add=$ecdsa_key --log-error=$errorlog +--let SEARCH_PATTERN=Mismatched --ssl-cert-add/--ssl-key-add +--source include/search_pattern_in_file.inc +--remove_file $SEARCH_FILE + +--echo # +--echo # Test 5: same cert type added twice (OpenSSL replaces, last wins) +--echo # +--let $restart_parameters=--ssl-cert-add=$rsa_cert --ssl-key-add=$rsa_key +--source include/start_mysqld.inc +SELECT 1; +--source include/kill_mysqld.inc + +--echo # +--echo # Test 6: one valid cert-add + one invalid cert-add (invalid /etc/hosts) +--echo # +--error 1 +--exec $MYSQLD --defaults-group-suffix=.1 --defaults-file=$MYSQLTEST_VARDIR/my.cnf --user=root --ssl-cert-add=/etc/hosts --ssl-key-add=$ecdsa_key --log-error=$errorlog +--let SEARCH_PATTERN=\[ERROR\] SSL error: Unable to get certificate +--source include/search_pattern_in_file.inc +--remove_file $SEARCH_FILE + +--echo # +--echo # Test 7: add second pair (RSA + ECDSA), verify Ssl_server_cert_types +--echo # +--let $restart_parameters=--ssl-cert-add=$ecdsa_cert --ssl-key-add=$ecdsa_key +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%RSA%' OR '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: Expected both RSA and ECDSA in Ssl_server_cert_types, got: $cert_types + --die Ssl_server_cert_types does not contain both RSA and ECDSA +} +--echo Ssl_server_cert_types contains RSA and ECDSA: OK + +--echo # +--echo # Test 8: FLUSH SSL reloads alt certs correctly +--echo # +FLUSH SSL; +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%RSA%' OR '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: After FLUSH SSL, expected both RSA and ECDSA, got: $cert_types + --die Ssl_server_cert_types after FLUSH SSL does not contain both RSA and ECDSA +} +--echo After FLUSH SSL, Ssl_server_cert_types still contains RSA and ECDSA: OK + +--echo # +--echo # Test 9: --ssl-cert override + --ssl-cert-add (no interference) +--echo # +--source include/kill_mysqld.inc +--let $restart_parameters=--ssl-cert=$rsa_cert --ssl-key=$rsa_key --ssl-cert-add=$ecdsa_cert --ssl-key-add=$ecdsa_key +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%RSA%' OR '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: Expected both RSA and ECDSA in Ssl_server_cert_types, got: $cert_types + --die Ssl_server_cert_types does not contain both RSA and ECDSA +} +--echo --ssl-cert override + --ssl-cert-add: OK +--source include/kill_mysqld.inc + +--echo # +--echo # Test 10: --ssl-cert-add then --ssl-cert resets alt certs +--echo # (like --plugin-load resets --plugin-load-add) +--echo # +--let $restart_parameters=--ssl-cert-add=$ecdsa_cert --ssl-key-add=$ecdsa_key --ssl-cert=$rsa_cert --ssl-key=$rsa_key +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' LIKE '%ECDSA%'`) { + --echo # ERROR: ECDSA should have been reset by --ssl-cert, got: $cert_types + --die --ssl-cert did not reset alt certs +} +--echo --ssl-cert-add then --ssl-cert resets alt certs: OK +--source include/kill_mysqld.inc + +--echo # +--echo # Test 11: --ssl-cert-add alongside my.cnf default (appends to existing) +--echo # +--let $restart_parameters=--ssl-cert-add=$ecdsa_cert --ssl-key-add=$ecdsa_key +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%RSA%' OR '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: Expected both RSA and ECDSA in Ssl_server_cert_types, got: $cert_types + --die Ssl_server_cert_types does not contain both RSA and ECDSA +} +--echo --ssl-cert-add alongside my.cnf default: OK +--source include/kill_mysqld.inc + +--echo # +--echo # Test 12: --ssl-cert-add as sole cert (--ssl-cert= clears, first add becomes primary) +--echo # +--let $restart_parameters=--ssl-cert= --ssl-key= --ssl-cert-add=$ecdsa_cert --ssl-key-add=$ecdsa_key +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: Expected ECDSA in Ssl_server_cert_types, got: $cert_types + --die Ssl_server_cert_types does not contain ECDSA +} +--echo --ssl-cert-add as sole cert (first add becomes primary): OK +--source include/kill_mysqld.inc + +--echo # +--echo # Test 13: cert-adds before key-adds (order independent) +--echo # --ssl-cert-add --ssl-cert-add --ssl-key-add --ssl-key-add +--echo # +# Use ECDSA + second RSA to avoid EdDSA OpenSSL version dependency +--let $rsa2_cert=$MYSQL_TEST_DIR/std_data/server-new-cert.pem +--let $rsa2_key=$MYSQL_TEST_DIR/std_data/server-new-key.pem +--let $restart_parameters=--ssl-cert-add=$ecdsa_cert --ssl-cert-add=$rsa2_cert --ssl-key-add=$ecdsa_key --ssl-key-add=$rsa2_key +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%RSA%' OR '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: Expected RSA and ECDSA in Ssl_server_cert_types, got: $cert_types + --die Ssl_server_cert_types does not contain RSA and ECDSA +} +--echo cert-adds before key-adds (order independent): OK +--source include/kill_mysqld.inc + +--echo # +--echo # Test 14: --ssl-key override resets alt keys independently +--echo # +--let $restart_parameters=--ssl-key-add=$ecdsa_key --ssl-cert-add=$ecdsa_cert --ssl-key=$rsa_key --ssl-cert=$rsa_cert +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' LIKE '%ECDSA%'`) { + --echo # ERROR: ECDSA should have been reset by --ssl-key, got: $cert_types + --die --ssl-key did not reset alt keys +} +--echo --ssl-key override resets alt keys: OK +--source include/kill_mysqld.inc + +--echo # +--echo # Test 15: --ssl-key-add as sole key (--ssl-key= clears, first add becomes primary) +--echo # +--let $restart_parameters=--ssl-cert= --ssl-key= --ssl-key-add=$ecdsa_key --ssl-cert-add=$ecdsa_cert +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: Expected ECDSA in Ssl_server_cert_types, got: $cert_types + --die Ssl_server_cert_types does not contain ECDSA +} +--echo --ssl-key-add as sole key (first add becomes primary): OK +--source include/kill_mysqld.inc + +--echo # +--echo # Cleanup +--echo # + +--let $restart_parameters= +--source include/start_mysqld.inc diff --git a/mysql-test/std_data/server-ecdsa-cert.pem b/mysql-test/std_data/server-ecdsa-cert.pem new file mode 100644 index 0000000000000..75e0a84f4a1bf --- /dev/null +++ b/mysql-test/std_data/server-ecdsa-cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDbzCCAVegAwIBAgIUXgmAsz6m3esI6jBMT9wWFSczSzQwDQYJKoZIhvcNAQEL +BQAwVjEPMA0GA1UEAwwGY2FjZXJ0MQswCQYDVQQGEwJGSTERMA8GA1UECAwISGVs +c2lua2kxETAPBgNVBAcMCEhlbHNpbmtpMRAwDgYDVQQKDAdNYXJpYURCMB4XDTI2 +MDYxNzA5MjUzMFoXDTM2MDYxNDA5MjUzMFowFDESMBAGA1UEAwwJbG9jYWxob3N0 +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEde9gcIHtbkV+tstiSf3nphUz11dm +KCRDxOTPKS40Mey/bYw2+UfPNU8tt0DG6U8jbswAmT0oZAbRn/OB8tmc2KNCMEAw +HQYDVR0OBBYEFHUtnPG3ixuOeVig2gK14UjrVIl+MB8GA1UdIwQYMBaAFE38ehnz +Kwx99sB8TfhyNEyMNVJ0MA0GCSqGSIb3DQEBCwUAA4ICAQBsMpzeZqaqFaCEtftS +OwWwUQzrE2V6yxmoqaGv0uASKXGWcI7y/n6KElKrNEk5SqN5MBy0xtn/FE4b1ELK +tmWqmlSFyT7eK4Q6ZIHP3wt5TshyPQ9Oe/hL8XYwaM2UICEXnOm/PUd/AIFirKte +YrsBvqwxgn8SP20OZtGZqz3g6WOtFqKgr95/4C44YYPd1iaQTj+svJprgQXxU/tv +kwV9DhqWXu6Sa4dPxl/GEaffdP21RYUcId8nJ4zUKcSRAggS/B+Dusk5xkS6eD6E +wOX9ZoRKQLHb0TxZcrYdhmTaZNivPUhKyPvdlYtY24trluld9s9EiG6zevK2XARy +/QqFps+8Byav/2bERGINyKmS4tBN3BXa/f+fQ8g5VN73ZMVrkTZ3TkEfRjgjO5NV +qchn6Liujpws6f4H1rA5e6id4Hxk/CoVG0gM6p/EgKt7WjbwvfAvUHvBcJ+TYd+E +cbAFa0L8/h7OiPO7PLYrO0iS0ALl23ou/FuKCTpWabvwNUCSI7kW9BPzY6L1dxpq +d28SCyo0i8JQkj2az6Z/EQ4CkDbfMkEu29EQZ/wsUBtjgB6dgf82p6JoH9Q9EtR2 +pYpA4h9zMh5jLeoK3TIl4E21duoR2QC1jpd+C2LVT58opUjgxZ9ghVvWwB0dZ6H1 +MESz6mmklPd0+YpE4qSqCYFSzw== +-----END CERTIFICATE----- diff --git a/mysql-test/std_data/server-ecdsa-key.pem b/mysql-test/std_data/server-ecdsa-key.pem new file mode 100644 index 0000000000000..d378010116bbb --- /dev/null +++ b/mysql-test/std_data/server-ecdsa-key.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICtJnlc/6O9WSxo8sUnIpQIQxE5h7IKj0Dv5FDKZtYLGoAoGCCqGSM49 +AwEHoUQDQgAEde9gcIHtbkV+tstiSf3nphUz11dmKCRDxOTPKS40Mey/bYw2+UfP +NU8tt0DG6U8jbswAmT0oZAbRn/OB8tmc2A== +-----END EC PRIVATE KEY----- diff --git a/mysql-test/std_data/server-eddsa-cert.pem b/mysql-test/std_data/server-eddsa-cert.pem new file mode 100644 index 0000000000000..3dc7f6ac741e8 --- /dev/null +++ b/mysql-test/std_data/server-eddsa-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQDCCASigAwIBAgIUXgmAsz6m3esI6jBMT9wWFSczSzUwDQYJKoZIhvcNAQEL +BQAwVjEPMA0GA1UEAwwGY2FjZXJ0MQswCQYDVQQGEwJGSTERMA8GA1UECAwISGVs +c2lua2kxETAPBgNVBAcMCEhlbHNpbmtpMRAwDgYDVQQKDAdNYXJpYURCMB4XDTI2 +MDYxNzA5MjU0NVoXDTM2MDYxNDA5MjU0NVowFDESMBAGA1UEAwwJbG9jYWxob3N0 +MCowBQYDK2VwAyEAeFTczKoeJyLGiabGP3Zlszo6NnsvO9wCZwlQFlXvv/SjQjBA +MB0GA1UdDgQWBBS15uIv4N/mYM24995uplTciyKDrTAfBgNVHSMEGDAWgBRN/HoZ +8ysMffbAfE34cjRMjDVSdDANBgkqhkiG9w0BAQsFAAOCAgEAKq6yC7+YS10gQFNm +ektCedQqBx8UAE2M0p3Yc4OXH+yC+Zt6D51WUR2PKCIouflwbldytqrsXSsLQMv0 +ZwR9CdfxLT8UVAStDlhHyKwiWXoMMp+n8XXy0emYJ9MOhU8qF+1qKjSHnvtoF+sO +sMhHeSSyf/YyLvkF95cD1JCfaTgmIjiG3ZdkZaGUb9uzenfy1xZ+PtgnW4vsn2ag +nLHL02W0uUSNSzPZXgB9FBEvTeDuRaVO7ObXvQTlAA/mby2Tt5VE0nNsCbTPmXcM +jUPZuG4JixcGeRB36MW0TtN99F/6tkliC+wOvF6Md+cV3YvGfPzEIbGkQfXCJxQJ +8HNV91jA0uLiSdoRcAYsJROdQbeM6ISOzMuWbACxblmNhPbmvaimGTLxLjckb4+8 +sdIaKKvScrtsvZk09PaX7WObTQCpfxdxE/BO0ZwvPdb7InVW8zWpRJGaX/Cp5Gwx +IZ5f5nUH+UOzj/9X2qohRBDNWvjOofkHuOqZ/wHLf8Q3CfLg54WvGhLCLMHwkSii +MDh+w8DupU9irl/962hAPlw+Yjrcysgff/7VPImFdIFpvg8o5dVoSUJtV2EamWOp +cAIzRn8360TptIhKRuzLIW62XAUUxoys/n2Z/K378qG8kZCpiih4QgHU3WsT8c18 +ohSwLqCCE2523wae832VnGN5soM= +-----END CERTIFICATE----- diff --git a/mysql-test/std_data/server-eddsa-key.pem b/mysql-test/std_data/server-eddsa-key.pem new file mode 100644 index 0000000000000..4c5c048904b96 --- /dev/null +++ b/mysql-test/std_data/server-eddsa-key.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIGykiVmUkdt9XeZv7V1/bwdD9I579yWkRcnhc2xElJyd +-----END PRIVATE KEY----- diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 07a597b3e1106..342e3af8cb742 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1524,6 +1524,12 @@ my_bool opt_use_ssl = 1; char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL, *opt_ssl_cipher= NULL, *opt_ssl_key= NULL, *opt_ssl_crl= NULL, *opt_ssl_crlpath= NULL, *opt_tls_version= NULL; +#if !defined(EMBEDDED_LIBRARY) +#define SSL_MAX_ALT_CERTS 4 +static const char *opt_ssl_alt_certs[SSL_MAX_ALT_CERTS]; +static const char *opt_ssl_alt_keys[SSL_MAX_ALT_CERTS]; +static uint ssl_alt_cert_count= 0, ssl_alt_key_count= 0; +#endif ulonglong tls_version= 0; static scheduler_functions thread_scheduler_struct, extra_thread_scheduler_struct; @@ -4726,6 +4732,25 @@ static void openssl_lock(int mode, openssl_lock_t *lock, const char *file, #endif /* HAVE_OPENSSL10 */ +#ifndef EVP_PKEY_get_base_id +#define EVP_PKEY_get_base_id EVP_PKEY_base_id +#endif + +static const char *evp_pkey_type_name(int type) +{ + switch (type) { + case EVP_PKEY_RSA: return "RSA"; + case EVP_PKEY_EC: return "ECDSA"; +#ifdef EVP_PKEY_ED25519 + case EVP_PKEY_ED25519: return "EdDSA"; +#endif +#ifdef EVP_PKEY_ED448 + case EVP_PKEY_ED448: return "EdDSA"; +#endif + default: return "unknown"; + } +} + struct SSL_ACCEPTOR_STATS { long accept; @@ -4735,6 +4760,7 @@ struct SSL_ACCEPTOR_STATS long verify_depth; long zero; const char *session_cache_mode; + char cert_types[64]; uchar fprint[256/8]; SSL_ACCEPTOR_STATS(): @@ -4773,6 +4799,54 @@ struct SSL_ACCEPTOR_STATS X509 *cert= SSL_CTX_get0_certificate(ctx); uint fplen= sizeof(fprint); X509_digest(cert, EVP_sha256(), fprint, &fplen); + + /* Build list of loaded certificate types */ + cert_types[0]= 0; + { +#ifndef HAVE_WOLFSSL + SSL *tmp_ssl= SSL_new(ctx); + if (tmp_ssl) + { + int pos= 0; + SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST); + do { + X509 *c= SSL_CTX_get0_certificate(ctx); + if (c) + { + EVP_PKEY *pk= X509_get0_pubkey(c); + if (pk) + { + const char *name= evp_pkey_type_name(EVP_PKEY_get_base_id(pk)); + if (pos > 0 && pos + 2 < (int) sizeof(cert_types)) + { + cert_types[pos++]= ','; + cert_types[pos++]= ' '; + } + size_t nlen= strlen(name); + if (pos + nlen < sizeof(cert_types)) + { + memcpy(cert_types + pos, name, nlen); + pos+= (int) nlen; + } + } + } + } while (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT)); + cert_types[pos]= 0; + SSL_free(tmp_ssl); + } +#else + X509 *c= SSL_CTX_get0_certificate(ctx); + if (c) + { + EVP_PKEY *pk= X509_get0_pubkey(c); + if (pk) + { + const char *name= evp_pkey_type_name(EVP_PKEY_get_base_id(pk)); + strmake(cert_types, name, sizeof(cert_types) - 1); + } + } +#endif + } } }; @@ -4810,6 +4884,14 @@ static void init_ssl() #if defined(HAVE_OPENSSL) if (opt_use_ssl) { + if (ssl_alt_cert_count != ssl_alt_key_count) + { + sql_print_error("Mismatched --ssl-cert-add/--ssl-key-add: %u certs, %u keys", + ssl_alt_cert_count, ssl_alt_key_count); + if (!opt_bootstrap) + unireg_abort(1); + } + enum enum_ssl_init_error error= SSL_INITERR_NOERROR; /* having ssl_acceptor_fd != 0 signals the use of SSL */ @@ -4817,7 +4899,9 @@ static void init_ssl() opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher, &error, opt_ssl_crl, opt_ssl_crlpath, - tls_version, get_ssl_passphrase()); + tls_version, get_ssl_passphrase(), + opt_ssl_alt_keys, opt_ssl_alt_certs, + ssl_alt_cert_count); DBUG_PRINT("info",("ssl_acceptor_fd: %p", ssl_acceptor_fd)); if (!ssl_acceptor_fd) { @@ -4862,7 +4946,8 @@ int reinit_ssl() enum enum_ssl_init_error error = SSL_INITERR_NOERROR; st_VioSSLFd *new_fd = new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher, &error, opt_ssl_crl, - opt_ssl_crlpath, tls_version, get_ssl_passphrase()); + opt_ssl_crlpath, tls_version, get_ssl_passphrase(), + opt_ssl_alt_keys, opt_ssl_alt_certs, ssl_alt_cert_count); if (!new_fd) { @@ -7306,6 +7391,18 @@ struct my_option my_long_options[]= "It can be specified many times, adding more plugins every time", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) + {"ssl-cert-add", OPT_SSL_CERT_ADD, + "Additional X509 cert in PEM format for multi-certificate support" + " (implies --ssl)", + 0, 0, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"ssl-key-add", OPT_SSL_KEY_ADD, + "Additional X509 key in PEM format for multi-certificate support" + " (implies --ssl)", + 0, 0, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif {"table_cache", 0, "Sets table_open_cache", &tc_size, &tc_size, 0, GET_ULONG, REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, "table_open_cache", 1, 0}, @@ -7560,6 +7657,26 @@ static int show_ssl_get_cipher(THD *thd, SHOW_VAR *var, void *buff, return 0; } + +static int show_ssl_get_server_cert_type(THD *thd, SHOW_VAR *var, void *buff, + system_status_var *, enum_var_type) +{ + var->type= SHOW_CHAR; + var->value= const_cast(""); + if (thd->vio_ok() && thd->net.vio->ssl_arg) + { + SSL *ssl= (SSL *) thd->net.vio->ssl_arg; + X509 *cert= SSL_get_certificate(ssl); + if (cert) + { + EVP_PKEY *pkey= X509_get0_pubkey(cert); + if (pkey) + var->value= const_cast(evp_pkey_type_name(EVP_PKEY_get_base_id(pkey))); + } + } + return 0; +} + static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, void *buf, system_status_var *, enum_var_type) { @@ -8075,6 +8192,8 @@ SHOW_VAR status_vars[]= { {"Ssl_finished_connects", (char*) &ssl_acceptor_stats.zero, SHOW_LONG}, {"Ssl_server_not_after", (char*) &show_ssl_get_server_not_after, SHOW_SIMPLE_FUNC}, {"Ssl_server_not_before", (char*) &show_ssl_get_server_not_before, SHOW_SIMPLE_FUNC}, + {"Ssl_server_cert_type", (char*) &show_ssl_get_server_cert_type, SHOW_SIMPLE_FUNC}, + {"Ssl_server_cert_types", (char*) &ssl_acceptor_stats.cert_types, SHOW_CHAR}, {"Ssl_session_cache_hits", (char*) &ssl_acceptor_stats.zero, SHOW_LONG}, {"Ssl_session_cache_misses", (char*) &ssl_acceptor_stats.zero, SHOW_LONG}, {"Ssl_session_cache_mode", (char*) &ssl_acceptor_stats.session_cache_mode, SHOW_CHAR_PTR}, @@ -8506,6 +8625,14 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument, if (argument == autoset_my_option) my_getopt_init_one_value(opt, opt->value, opt->def_value); +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) + /* --ssl-cert/--ssl-key reset alt cert/key lists (like --plugin-load resets) */ + if (opt->id == OPT_SSL_CERT) + ssl_alt_cert_count= 0; + else if (opt->id == OPT_SSL_KEY) + ssl_alt_key_count= 0; +#endif + switch(opt->id) { case '#': #ifndef DBUG_OFF @@ -8569,6 +8696,40 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument, binlog_format_used= true; break; #include +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) + case OPT_SSL_CERT_ADD: + if (!opt_ssl_cert || !opt_ssl_cert[0]) + { + opt_ssl_cert= (char *) argument; + } + else + { + if (ssl_alt_cert_count >= SSL_MAX_ALT_CERTS) + { + sql_print_error("Too many --ssl-cert-add options (max %u)", + (uint) SSL_MAX_ALT_CERTS); + return 1; + } + opt_ssl_alt_certs[ssl_alt_cert_count++]= argument; + } + break; + case OPT_SSL_KEY_ADD: + if (!opt_ssl_key || !opt_ssl_key[0]) + { + opt_ssl_key= (char *) argument; + } + else + { + if (ssl_alt_key_count >= SSL_MAX_ALT_CERTS) + { + sql_print_error("Too many --ssl-key-add options (max %u)", + (uint) SSL_MAX_ALT_CERTS); + return 1; + } + opt_ssl_alt_keys[ssl_alt_key_count++]= argument; + } + break; +#endif case 'V': if (argument) { diff --git a/sql/mysqld.h b/sql/mysqld.h index f04d2bacaa5bb..96bdc6783f456 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -805,10 +805,12 @@ enum options_mysqld OPT_SSL_CA, OPT_SSL_CAPATH, OPT_SSL_CERT, + OPT_SSL_CERT_ADD, OPT_SSL_CIPHER, OPT_SSL_CRL, OPT_SSL_CRLPATH, OPT_SSL_KEY, + OPT_SSL_KEY_ADD, OPT_WANT_CORE, OPT_MYSQL_COMPATIBILITY, OPT_TLS_VERSION, diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index e001289b933f7..0e208e70f9962 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -439,7 +439,9 @@ static struct st_VioSSLFd * new_VioSSLFd(const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher, my_bool is_client_method, enum enum_ssl_init_error *error, const char *crl_file, - const char *crl_path, ulonglong tls_version, const char *passphrase) + const char *crl_path, ulonglong tls_version, const char *passphrase, + const char **alt_key_files, const char **alt_cert_files, + uint alt_cert_count) { struct st_VioSSLFd *ssl_fd; long ssl_ctx_options; @@ -562,6 +564,51 @@ new_VioSSLFd(const char *key_file, const char *cert_file, const char *ca_file, goto err2; } + /* Load additional certificates (e.g. ECDSA alongside RSA) into the same context */ +#ifndef HAVE_WOLFSSL + { + uint i; + for (i= 0; i < alt_cert_count; i++) + { + const char *acert= alt_cert_files[i]; + const char *akey= alt_key_files[i]; + if (SSL_CTX_use_certificate_chain_file(ssl_fd->ssl_context, acert) <= 0) + { + *error= SSL_INITERR_CERT; + fprintf(stderr, "SSL error: %s from '%s'\n", + sslGetErrString(*error), acert); + fflush(stderr); + goto err2; + } + if (SSL_CTX_use_PrivateKey_file(ssl_fd->ssl_context, akey, + SSL_FILETYPE_PEM) <= 0) + { + *error= SSL_INITERR_KEY; + fprintf(stderr, "SSL error: %s from '%s'\n", + sslGetErrString(*error), akey); + fflush(stderr); + goto err2; + } + if (!SSL_CTX_check_private_key(ssl_fd->ssl_context)) + { + *error= SSL_INITERR_NOMATCH; + fprintf(stderr, "SSL error: %s (cert '%s')\n", + sslGetErrString(*error), acert); + fflush(stderr); + goto err2; + } + } + } +#else + if (alt_cert_count > 0) + { + *error= SSL_INITERR_CERT; + fprintf(stderr, "SSL error: multiple certificates not supported with WolfSSL\n"); + fflush(stderr); + goto err2; + } +#endif + #ifndef HAVE_WOLFSSL /* DH stuff */ if (!is_client_method) @@ -621,7 +668,8 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file, /* Init the VioSSLFd as a "connector" ie. the client side */ if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, - TRUE, error, crl_file, crl_path, 0, NULL))) + TRUE, error, crl_file, crl_path, 0, NULL, + NULL, NULL, 0))) { return 0; } @@ -637,14 +685,17 @@ new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, const char *ca_file, const char *ca_path, const char *cipher, enum enum_ssl_init_error* error, const char *crl_file, const char *crl_path, - ulonglong tls_version, const char *passphrase) + ulonglong tls_version, const char *passphrase, + const char **alt_key_files, const char **alt_cert_files, + uint alt_cert_count) { struct st_VioSSLFd *ssl_fd; int verify= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; /* Init the the VioSSLFd as a "acceptor" ie. the server side */ if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, - FALSE, error, crl_file, crl_path, tls_version, passphrase))) + FALSE, error, crl_file, crl_path, tls_version, passphrase, + alt_key_files, alt_cert_files, alt_cert_count))) { return 0; } From 25c8a8402ae3201745bde222f6681afdec9f8123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Tue, 23 Jun 2026 13:03:57 +0200 Subject: [PATCH 02/11] MDEV-39857 Fix useless allocation of SSL context --- sql/mysqld.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 342e3af8cb742..acd25a38c30c2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4804,8 +4804,6 @@ struct SSL_ACCEPTOR_STATS cert_types[0]= 0; { #ifndef HAVE_WOLFSSL - SSL *tmp_ssl= SSL_new(ctx); - if (tmp_ssl) { int pos= 0; SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST); @@ -4832,7 +4830,6 @@ struct SSL_ACCEPTOR_STATS } } while (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT)); cert_types[pos]= 0; - SSL_free(tmp_ssl); } #else X509 *c= SSL_CTX_get0_certificate(ctx); From 02a05ca4c690f02fdb112f90909c4685666e4766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Tue, 23 Jun 2026 20:31:28 +0200 Subject: [PATCH 03/11] MDEV-39857 Document positional pairing for --ssl-cert-add/--ssl-key-add Each --ssl-cert-add must be positionally matched with a corresponding --ssl-key-add. The loading loop pairs cert[0] with key[0], cert[1] with key[1], etc. and verifies each pair with SSL_CTX_check_private_key(). --- mysql-test/main/ssl_multi_cert_errors.result | 11 +++++++++-- mysql-test/main/ssl_multi_cert_errors.test | 18 ++++++++++++++++-- sql/mysqld.cc | 10 ++++++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/mysql-test/main/ssl_multi_cert_errors.result b/mysql-test/main/ssl_multi_cert_errors.result index 31c45bc40e831..50ad373bd35d3 100644 --- a/mysql-test/main/ssl_multi_cert_errors.result +++ b/mysql-test/main/ssl_multi_cert_errors.result @@ -70,13 +70,20 @@ After FLUSH SSL, Ssl_server_cert_types still contains RSA and ECDSA: OK cert-adds before key-adds (order independent): OK # Kill the server # -# Test 14: --ssl-key override resets alt keys independently +# Test 14: key-adds before cert-adds (order independent) +# --ssl-key-add --ssl-key-add --ssl-cert-add --ssl-cert-add +# +# restart: --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-new-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-new-cert.pem +key-adds before cert-adds (order independent): OK +# Kill the server +# +# Test 15: --ssl-key override resets alt keys independently # # restart: --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key=MYSQL_TEST_DIR/std_data/server-key.pem --ssl-cert=MYSQL_TEST_DIR/std_data/server-cert.pem --ssl-key override resets alt keys: OK # Kill the server # -# Test 15: --ssl-key-add as sole key (--ssl-key= clears, first add becomes primary) +# Test 16: --ssl-key-add as sole key (--ssl-key= clears, first add becomes primary) # # restart: --ssl-cert= --ssl-key= --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add as sole key (first add becomes primary): OK diff --git a/mysql-test/main/ssl_multi_cert_errors.test b/mysql-test/main/ssl_multi_cert_errors.test index 9d3968576cfaf..90deace916122 100644 --- a/mysql-test/main/ssl_multi_cert_errors.test +++ b/mysql-test/main/ssl_multi_cert_errors.test @@ -172,7 +172,21 @@ if (`SELECT '$cert_types' NOT LIKE '%RSA%' OR '$cert_types' NOT LIKE '%ECDSA%'`) --source include/kill_mysqld.inc --echo # ---echo # Test 14: --ssl-key override resets alt keys independently +--echo # Test 14: key-adds before cert-adds (order independent) +--echo # --ssl-key-add --ssl-key-add --ssl-cert-add --ssl-cert-add +--echo # +--let $restart_parameters=--ssl-key-add=$ecdsa_key --ssl-key-add=$rsa2_key --ssl-cert-add=$ecdsa_cert --ssl-cert-add=$rsa2_cert +--source include/start_mysqld.inc +--let $cert_types= query_get_value(SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types', Value, 1) +if (`SELECT '$cert_types' NOT LIKE '%RSA%' OR '$cert_types' NOT LIKE '%ECDSA%'`) { + --echo # ERROR: Expected RSA and ECDSA in Ssl_server_cert_types, got: $cert_types + --die Ssl_server_cert_types does not contain RSA and ECDSA +} +--echo key-adds before cert-adds (order independent): OK +--source include/kill_mysqld.inc + +--echo # +--echo # Test 15: --ssl-key override resets alt keys independently --echo # --let $restart_parameters=--ssl-key-add=$ecdsa_key --ssl-cert-add=$ecdsa_cert --ssl-key=$rsa_key --ssl-cert=$rsa_cert --source include/start_mysqld.inc @@ -185,7 +199,7 @@ if (`SELECT '$cert_types' LIKE '%ECDSA%'`) { --source include/kill_mysqld.inc --echo # ---echo # Test 15: --ssl-key-add as sole key (--ssl-key= clears, first add becomes primary) +--echo # Test 16: --ssl-key-add as sole key (--ssl-key= clears, first add becomes primary) --echo # --let $restart_parameters=--ssl-cert= --ssl-key= --ssl-key-add=$ecdsa_key --ssl-cert-add=$ecdsa_cert --source include/start_mysqld.inc diff --git a/sql/mysqld.cc b/sql/mysqld.cc index acd25a38c30c2..d418b9e98d79c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7390,13 +7390,15 @@ struct my_option my_long_options[]= GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) {"ssl-cert-add", OPT_SSL_CERT_ADD, - "Additional X509 cert in PEM format for multi-certificate support" - " (implies --ssl)", + "Additional X509 cert in PEM format for multi-certificate support." + " Each --ssl-cert-add must be positionally matched with a" + " corresponding --ssl-key-add (implies --ssl)", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"ssl-key-add", OPT_SSL_KEY_ADD, - "Additional X509 key in PEM format for multi-certificate support" - " (implies --ssl)", + "Additional X509 key in PEM format for multi-certificate support." + " Each --ssl-key-add must be positionally matched with a" + " corresponding --ssl-cert-add (implies --ssl)", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif From 881da6f5750218c027fcf436f9a3c09ed0d640eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Wed, 24 Jun 2026 16:02:21 +0200 Subject: [PATCH 04/11] MDEV-39857 Use strmake --- sql/mysqld.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index d418b9e98d79c..a6b30c3ecf5c3 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4820,12 +4820,9 @@ struct SSL_ACCEPTOR_STATS cert_types[pos++]= ','; cert_types[pos++]= ' '; } - size_t nlen= strlen(name); - if (pos + nlen < sizeof(cert_types)) - { - memcpy(cert_types + pos, name, nlen); - pos+= (int) nlen; - } + pos+= (int)(strmake(cert_types + pos, name, + sizeof(cert_types) - pos - 1) - + (cert_types + pos)); } } } while (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT)); From f97a270d3d0db12ed41f1dfc6cf3d64cf65974b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Thu, 25 Jun 2026 08:50:48 +0200 Subject: [PATCH 05/11] MDEV-39857 Fix VioSSL API to use cert/key arrays Refactor new_VioSSLAcceptorFd() and new_VioSSLFd() to take cert/key arrays instead of separate primary + alt parameters. The caller builds combined arrays with the primary at index 0 and additional certs at subsequent indices. --- include/violite.h | 7 +++---- sql/mysqld.cc | 41 +++++++++++++++++++++++++++++++++------- vio/viosslfactories.c | 44 ++++++++++++++++++++++++------------------- 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/include/violite.h b/include/violite.h index 67a9b242da05c..9cc13ddde4924 100644 --- a/include/violite.h +++ b/include/violite.h @@ -182,13 +182,12 @@ struct st_VioSSLFd const char *cipher, enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path); struct st_VioSSLFd -*new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, +*new_VioSSLAcceptorFd(const char **key_files, const char **cert_files, + uint cert_count, const char *ca_file,const char *ca_path, const char *cipher, enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path, - ulonglong tls_version, const char *passphrase, - const char **alt_key_files, const char **alt_cert_files, - uint alt_cert_count); + ulonglong tls_version, const char *passphrase); void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd); #endif /* HAVE_OPENSSL */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a6b30c3ecf5c3..1bf3f65bb1df6 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4888,14 +4888,27 @@ static void init_ssl() enum enum_ssl_init_error error= SSL_INITERR_NOERROR; + /* Build combined cert/key arrays: primary at [0], alt certs after */ + const char *ssl_key_files[SSL_MAX_ALT_CERTS + 1]; + const char *ssl_cert_files[SSL_MAX_ALT_CERTS + 1]; + uint ssl_cert_count= 0; + ssl_cert_files[ssl_cert_count]= opt_ssl_cert; + ssl_key_files[ssl_cert_count]= opt_ssl_key; + ssl_cert_count++; + for (uint i= 0; i < ssl_alt_cert_count; i++) + { + ssl_cert_files[ssl_cert_count]= opt_ssl_alt_certs[i]; + ssl_key_files[ssl_cert_count]= opt_ssl_alt_keys[i]; + ssl_cert_count++; + } + /* having ssl_acceptor_fd != 0 signals the use of SSL */ - ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, + ssl_acceptor_fd= new_VioSSLAcceptorFd(ssl_key_files, ssl_cert_files, + ssl_cert_count, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher, &error, opt_ssl_crl, opt_ssl_crlpath, - tls_version, get_ssl_passphrase(), - opt_ssl_alt_keys, opt_ssl_alt_certs, - ssl_alt_cert_count); + tls_version, get_ssl_passphrase()); DBUG_PRINT("info",("ssl_acceptor_fd: %p", ssl_acceptor_fd)); if (!ssl_acceptor_fd) { @@ -4938,10 +4951,24 @@ int reinit_ssl() return 0; enum enum_ssl_init_error error = SSL_INITERR_NOERROR; - st_VioSSLFd *new_fd = new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, + + const char *ssl_key_files[SSL_MAX_ALT_CERTS + 1]; + const char *ssl_cert_files[SSL_MAX_ALT_CERTS + 1]; + uint ssl_cert_count= 0; + ssl_cert_files[ssl_cert_count]= opt_ssl_cert; + ssl_key_files[ssl_cert_count]= opt_ssl_key; + ssl_cert_count++; + for (uint i= 0; i < ssl_alt_cert_count; i++) + { + ssl_cert_files[ssl_cert_count]= opt_ssl_alt_certs[i]; + ssl_key_files[ssl_cert_count]= opt_ssl_alt_keys[i]; + ssl_cert_count++; + } + + st_VioSSLFd *new_fd = new_VioSSLAcceptorFd(ssl_key_files, ssl_cert_files, + ssl_cert_count, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher, &error, opt_ssl_crl, - opt_ssl_crlpath, tls_version, get_ssl_passphrase(), - opt_ssl_alt_keys, opt_ssl_alt_certs, ssl_alt_cert_count); + opt_ssl_crlpath, tls_version, get_ssl_passphrase()); if (!new_fd) { diff --git a/vio/viosslfactories.c b/vio/viosslfactories.c index 0e208e70f9962..75e4cfcc1301f 100644 --- a/vio/viosslfactories.c +++ b/vio/viosslfactories.c @@ -436,15 +436,16 @@ static int ssl_external_passwd_cb(char *buf, int size, int rw, void *userdata) /************************ VioSSLFd **********************************/ static struct st_VioSSLFd * -new_VioSSLFd(const char *key_file, const char *cert_file, const char *ca_file, +new_VioSSLFd(const char **key_files, const char **cert_files, uint cert_count, + const char *ca_file, const char *ca_path, const char *cipher, my_bool is_client_method, enum enum_ssl_init_error *error, const char *crl_file, - const char *crl_path, ulonglong tls_version, const char *passphrase, - const char **alt_key_files, const char **alt_cert_files, - uint alt_cert_count) + const char *crl_path, ulonglong tls_version, const char *passphrase) { struct st_VioSSLFd *ssl_fd; long ssl_ctx_options; + const char *key_file= cert_count > 0 ? key_files[0] : NULL; + const char *cert_file= cert_count > 0 ? cert_files[0] : NULL; DBUG_ENTER("new_VioSSLFd"); fix_value(key_file); @@ -568,10 +569,10 @@ new_VioSSLFd(const char *key_file, const char *cert_file, const char *ca_file, #ifndef HAVE_WOLFSSL { uint i; - for (i= 0; i < alt_cert_count; i++) + for (i= 1; i < cert_count; i++) { - const char *acert= alt_cert_files[i]; - const char *akey= alt_key_files[i]; + const char *acert= cert_files[i]; + const char *akey= key_files[i]; if (SSL_CTX_use_certificate_chain_file(ssl_fd->ssl_context, acert) <= 0) { *error= SSL_INITERR_CERT; @@ -600,7 +601,7 @@ new_VioSSLFd(const char *key_file, const char *cert_file, const char *ca_file, } } #else - if (alt_cert_count > 0) + if (cert_count > 1) { *error= SSL_INITERR_CERT; fprintf(stderr, "SSL error: multiple certificates not supported with WolfSSL\n"); @@ -667,11 +668,16 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file, cb= always_ok; /* Init the VioSSLFd as a "connector" ie. the client side */ - if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, - TRUE, error, crl_file, crl_path, 0, NULL, - NULL, NULL, 0))) { - return 0; + const char *key_files[]= { key_file }; + const char *cert_files[]= { cert_file }; + uint cert_count= (cert_file || key_file) ? 1 : 0; + if (!(ssl_fd= new_VioSSLFd(key_files, cert_files, cert_count, + ca_file, ca_path, cipher, + TRUE, error, crl_file, crl_path, 0, NULL))) + { + return 0; + } } SSL_CTX_set_verify(ssl_fd->ssl_context, SSL_VERIFY_PEER, cb); @@ -681,21 +687,21 @@ new_VioSSLConnectorFd(const char *key_file, const char *cert_file, /************************ VioSSLAcceptorFd **********************************/ struct st_VioSSLFd * -new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, +new_VioSSLAcceptorFd(const char **key_files, const char **cert_files, + uint cert_count, const char *ca_file, const char *ca_path, const char *cipher, enum enum_ssl_init_error* error, const char *crl_file, const char *crl_path, - ulonglong tls_version, const char *passphrase, - const char **alt_key_files, const char **alt_cert_files, - uint alt_cert_count) + ulonglong tls_version, const char *passphrase) { struct st_VioSSLFd *ssl_fd; int verify= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; /* Init the the VioSSLFd as a "acceptor" ie. the server side */ - if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, - FALSE, error, crl_file, crl_path, tls_version, passphrase, - alt_key_files, alt_cert_files, alt_cert_count))) + if (!(ssl_fd= new_VioSSLFd(key_files, cert_files, cert_count, + ca_file, ca_path, cipher, + FALSE, error, crl_file, crl_path, tls_version, + passphrase))) { return 0; } From a76a91b85dc20a4de939202f7cedaa24358afa89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Mon, 29 Jun 2026 14:37:45 +0200 Subject: [PATCH 06/11] MDEV-39857 Split ssl_multi_cert test to avoid mid-test server restarts Split ssl_multi_cert into two tests with .opt files: - ssl_multi_cert: TLS 1.2 with 3 cert types, cipher-based selection - ssl_multi_cert_tlsv13: TLS 1.3 with EdDSA verification Each test uses a -master.opt file for server parameters instead of restarting mid-test. This makes tests faster (24ms + 19ms vs ~4300ms), parallelizable by MTR, and easier to debug. --- mysql-test/main/ssl_multi_cert-master.opt | 5 +++ mysql-test/main/ssl_multi_cert.result | 27 ----------- mysql-test/main/ssl_multi_cert.test | 45 ++----------------- .../main/ssl_multi_cert_tlsv13-master.opt | 5 +++ mysql-test/main/ssl_multi_cert_tlsv13.result | 16 +++++++ mysql-test/main/ssl_multi_cert_tlsv13.test | 37 +++++++++++++++ 6 files changed, 67 insertions(+), 68 deletions(-) create mode 100644 mysql-test/main/ssl_multi_cert-master.opt create mode 100644 mysql-test/main/ssl_multi_cert_tlsv13-master.opt create mode 100644 mysql-test/main/ssl_multi_cert_tlsv13.result create mode 100644 mysql-test/main/ssl_multi_cert_tlsv13.test diff --git a/mysql-test/main/ssl_multi_cert-master.opt b/mysql-test/main/ssl_multi_cert-master.opt new file mode 100644 index 0000000000000..a7eb2425e6261 --- /dev/null +++ b/mysql-test/main/ssl_multi_cert-master.opt @@ -0,0 +1,5 @@ +--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem +--ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem +--ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem +--tls-version=TLSv1.2 diff --git a/mysql-test/main/ssl_multi_cert.result b/mysql-test/main/ssl_multi_cert.result index d3d43d74b0027..54501ea941133 100644 --- a/mysql-test/main/ssl_multi_cert.result +++ b/mysql-test/main/ssl_multi_cert.result @@ -1,9 +1,4 @@ # -# Restart server with RSA (primary), ECDSA and EdDSA certs -# -# Kill the server -# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.2 -# # Verify Ssl_server_cert_types global status shows all three types # SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; @@ -42,25 +37,3 @@ have_ssl 1 disconnect default_con; connection default; -# -# Test 4: TLS 1.3 with all three cert types (EdDSA requires TLS 1.3) -# -connection default; -# Kill the server -# restart: --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.3 -SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; -Variable_name Value -Ssl_server_cert_types RSA, ECDSA, EdDSA -connect tls13_con,localhost,root,,,,,SSL; -# TLS 1.3 connection should use one of the loaded cert types -Ssl_server_cert_type is valid: OK -SHOW STATUS LIKE 'Ssl_version'; -Variable_name Value -Ssl_version TLSv1.3 -disconnect tls13_con; -connection default; -# -# Cleanup -# -# Kill the server -# restart diff --git a/mysql-test/main/ssl_multi_cert.test b/mysql-test/main/ssl_multi_cert.test index 596d4948952fd..6b108438521f3 100644 --- a/mysql-test/main/ssl_multi_cert.test +++ b/mysql-test/main/ssl_multi_cert.test @@ -1,9 +1,11 @@ # -# Test RSA + ECDSA + EdDSA certificate support via repeated --ssl-cert / --ssl-key +# Test RSA + ECDSA + EdDSA certificate support via --ssl-cert-add / --ssl-key-add +# Server started with 3 cert types via ssl_multi_cert-master.opt # +--source include/not_embedded.inc --source include/have_ssl_communication.inc -# Skip on WolfSSL which may not support multiple certs +# Skip on WolfSSL which does not support multiple certs if (`select @@version_ssl_library like 'wolfSSL%'`) { skip WolfSSL; } @@ -13,14 +15,6 @@ if (`select @@version_ssl_library like 'OpenSSL 1.0%' or @@version_ssl_library l skip OpenSSL too old for Ed25519; } ---echo # ---echo # Restart server with RSA (primary), ECDSA and EdDSA certs ---echo # - ---let $restart_parameters=--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.2 ---source include/kill_mysqld.inc ---source include/start_mysqld.inc - --echo # --echo # Verify Ssl_server_cert_types global status shows all three types --echo # @@ -51,34 +45,3 @@ connect (default_con,localhost,root,,,,,SSL); SELECT (VARIABLE_VALUE <> '') AS have_ssl FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher'; disconnect default_con; connection default; - ---echo # ---echo # Test 4: TLS 1.3 with all three cert types (EdDSA requires TLS 1.3) ---echo # - -connection default; ---let $restart_parameters=--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem --ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem --ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.3 ---source include/kill_mysqld.inc ---source include/start_mysqld.inc - -SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; - -connect (tls13_con,localhost,root,,,,,SSL); ---echo # TLS 1.3 connection should use one of the loaded cert types ---let $cert_type= query_get_value(SHOW STATUS LIKE 'Ssl_server_cert_type', Value, 1) -if (`SELECT '$cert_type' NOT IN ('RSA', 'ECDSA', 'EdDSA')`) { - --echo # ERROR: Unexpected cert type: $cert_type - --die Ssl_server_cert_type is not RSA, ECDSA, or EdDSA -} ---echo Ssl_server_cert_type is valid: OK -SHOW STATUS LIKE 'Ssl_version'; -disconnect tls13_con; -connection default; - ---echo # ---echo # Cleanup ---echo # - ---let $restart_parameters= ---source include/kill_mysqld.inc ---source include/start_mysqld.inc diff --git a/mysql-test/main/ssl_multi_cert_tlsv13-master.opt b/mysql-test/main/ssl_multi_cert_tlsv13-master.opt new file mode 100644 index 0000000000000..d7f44d38d5cb6 --- /dev/null +++ b/mysql-test/main/ssl_multi_cert_tlsv13-master.opt @@ -0,0 +1,5 @@ +--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem +--ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem +--ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem +--tls-version=TLSv1.3 diff --git a/mysql-test/main/ssl_multi_cert_tlsv13.result b/mysql-test/main/ssl_multi_cert_tlsv13.result new file mode 100644 index 0000000000000..3342bed88c9c7 --- /dev/null +++ b/mysql-test/main/ssl_multi_cert_tlsv13.result @@ -0,0 +1,16 @@ +# +# Verify Ssl_server_cert_types global status shows all three types +# +SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; +Variable_name Value +Ssl_server_cert_types RSA, ECDSA, EdDSA +# +# TLS 1.3 connection should use one of the loaded cert types +# +connect tls13_con,localhost,root,,,,,SSL; +Ssl_server_cert_type is valid: OK +SHOW STATUS LIKE 'Ssl_version'; +Variable_name Value +Ssl_version TLSv1.3 +disconnect tls13_con; +connection default; diff --git a/mysql-test/main/ssl_multi_cert_tlsv13.test b/mysql-test/main/ssl_multi_cert_tlsv13.test new file mode 100644 index 0000000000000..86df5d831e339 --- /dev/null +++ b/mysql-test/main/ssl_multi_cert_tlsv13.test @@ -0,0 +1,37 @@ +# +# Test TLS 1.3 with RSA + ECDSA + EdDSA certificates +# EdDSA (Ed25519) requires TLS 1.3 for actual use in handshakes. +# Server started with 3 cert types via ssl_multi_cert_tlsv13-master.opt +# +--source include/not_embedded.inc +--source include/have_ssl_communication.inc +--source include/have_tlsv13.inc + +# Skip on WolfSSL which does not support multiple certs +if (`select @@version_ssl_library like 'wolfSSL%'`) { + skip WolfSSL; +} + +# Ed25519 requires OpenSSL 1.1.1+ +if (`select @@version_ssl_library like 'OpenSSL 1.0%' or @@version_ssl_library like 'OpenSSL 1.1.0%'`) { + skip OpenSSL too old for Ed25519; +} + +--echo # +--echo # Verify Ssl_server_cert_types global status shows all three types +--echo # +SHOW GLOBAL STATUS LIKE 'Ssl_server_cert_types'; + +--echo # +--echo # TLS 1.3 connection should use one of the loaded cert types +--echo # +connect (tls13_con,localhost,root,,,,,SSL); +--let $cert_type= query_get_value(SHOW STATUS LIKE 'Ssl_server_cert_type', Value, 1) +if (`SELECT '$cert_type' NOT IN ('RSA', 'ECDSA', 'EdDSA')`) { + --echo # ERROR: Unexpected cert type: $cert_type + --die Ssl_server_cert_type is not RSA, ECDSA, or EdDSA +} +--echo Ssl_server_cert_type is valid: OK +SHOW STATUS LIKE 'Ssl_version'; +disconnect tls13_con; +connection default; From 099bbd9e0e321b23eb19b0a7611328ae134cc9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Tue, 30 Jun 2026 09:59:26 +0200 Subject: [PATCH 07/11] MDEV-39857 Hide --ssl-cert-add/--ssl-key-add on WolfSSL builds WolfSSL does not support multiple certificates per SSL_CTX. When tests use .opt files to pass --ssl-cert-add at server startup, the server aborts before the test's WolfSSL skip guard can run. Work around this by not registering --ssl-cert-add/--ssl-key-add options on WolfSSL builds (!defined(HAVE_WOLFSSL) guard on option registration). The .opt files use the --loose- prefix so the options are silently ignored when unknown. This workaround can be removed once WolfSSL supports multiple certificate types per SSL_CTX (see MDEV-36656). --- mysql-test/main/ssl_multi_cert-master.opt | 8 ++++---- mysql-test/main/ssl_multi_cert_tlsv13-master.opt | 8 ++++---- sql/mysqld.cc | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mysql-test/main/ssl_multi_cert-master.opt b/mysql-test/main/ssl_multi_cert-master.opt index a7eb2425e6261..a0c527eab3575 100644 --- a/mysql-test/main/ssl_multi_cert-master.opt +++ b/mysql-test/main/ssl_multi_cert-master.opt @@ -1,5 +1,5 @@ ---ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem ---ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem ---ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem ---ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem +--loose-ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem +--loose-ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--loose-ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem +--loose-ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.2 diff --git a/mysql-test/main/ssl_multi_cert_tlsv13-master.opt b/mysql-test/main/ssl_multi_cert_tlsv13-master.opt index d7f44d38d5cb6..7f5fdb96db9b1 100644 --- a/mysql-test/main/ssl_multi_cert_tlsv13-master.opt +++ b/mysql-test/main/ssl_multi_cert_tlsv13-master.opt @@ -1,5 +1,5 @@ ---ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem ---ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem ---ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem ---ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem +--loose-ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-cert.pem +--loose-ssl-key-add=$MYSQL_TEST_DIR/std_data/server-ecdsa-key.pem +--loose-ssl-cert-add=$MYSQL_TEST_DIR/std_data/server-eddsa-cert.pem +--loose-ssl-key-add=$MYSQL_TEST_DIR/std_data/server-eddsa-key.pem --tls-version=TLSv1.3 diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1bf3f65bb1df6..db5722a1c4ad4 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7412,7 +7412,7 @@ struct my_option my_long_options[]= "It can be specified many times, adding more plugins every time", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) && !defined(HAVE_WOLFSSL) {"ssl-cert-add", OPT_SSL_CERT_ADD, "Additional X509 cert in PEM format for multi-certificate support." " Each --ssl-cert-add must be positionally matched with a" From a68e633e54b294560a2ec5c4544000978dba8697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Fri, 3 Jul 2026 11:55:17 +0200 Subject: [PATCH 08/11] MDEV-39857 tests: use include/have_openssl.inc (reviewer comment) --- mysql-test/main/ssl_multi_cert.test | 18 +----------------- mysql-test/main/ssl_multi_cert_errors.test | 7 ++----- mysql-test/main/ssl_multi_cert_tlsv13.test | 11 +---------- 3 files changed, 4 insertions(+), 32 deletions(-) diff --git a/mysql-test/main/ssl_multi_cert.test b/mysql-test/main/ssl_multi_cert.test index 6b108438521f3..2387e7c44f824 100644 --- a/mysql-test/main/ssl_multi_cert.test +++ b/mysql-test/main/ssl_multi_cert.test @@ -4,16 +4,7 @@ # --source include/not_embedded.inc --source include/have_ssl_communication.inc - -# Skip on WolfSSL which does not support multiple certs -if (`select @@version_ssl_library like 'wolfSSL%'`) { - skip WolfSSL; -} - -# Ed25519 requires OpenSSL 1.1.1+ -if (`select @@version_ssl_library like 'OpenSSL 1.0%' or @@version_ssl_library like 'OpenSSL 1.1.0%'`) { - skip OpenSSL too old for Ed25519; -} +--source include/have_openssl.inc --echo # --echo # Verify Ssl_server_cert_types global status shows all three types @@ -37,11 +28,4 @@ SHOW STATUS LIKE 'Ssl_cipher'; SHOW STATUS LIKE 'Ssl_server_cert_type'; disconnect ecdsa_con; ---echo # ---echo # Test 3: Connect with default ciphers - should succeed ---echo # -connection default; -connect (default_con,localhost,root,,,,,SSL); -SELECT (VARIABLE_VALUE <> '') AS have_ssl FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher'; -disconnect default_con; connection default; diff --git a/mysql-test/main/ssl_multi_cert_errors.test b/mysql-test/main/ssl_multi_cert_errors.test index 90deace916122..8eebfc9a37a0b 100644 --- a/mysql-test/main/ssl_multi_cert_errors.test +++ b/mysql-test/main/ssl_multi_cert_errors.test @@ -7,11 +7,8 @@ # --source include/not_embedded.inc --source include/have_ssl_communication.inc - -# Skip on WolfSSL which does not support multiple certs -if (`select @@version_ssl_library like 'wolfSSL%'`) { - skip WolfSSL; -} +--source include/have_openssl.inc +--source include/big_test.inc --source include/shutdown_mysqld.inc diff --git a/mysql-test/main/ssl_multi_cert_tlsv13.test b/mysql-test/main/ssl_multi_cert_tlsv13.test index 86df5d831e339..113e5920b19ea 100644 --- a/mysql-test/main/ssl_multi_cert_tlsv13.test +++ b/mysql-test/main/ssl_multi_cert_tlsv13.test @@ -5,18 +5,9 @@ # --source include/not_embedded.inc --source include/have_ssl_communication.inc +--source include/have_openssl.inc --source include/have_tlsv13.inc -# Skip on WolfSSL which does not support multiple certs -if (`select @@version_ssl_library like 'wolfSSL%'`) { - skip WolfSSL; -} - -# Ed25519 requires OpenSSL 1.1.1+ -if (`select @@version_ssl_library like 'OpenSSL 1.0%' or @@version_ssl_library like 'OpenSSL 1.1.0%'`) { - skip OpenSSL too old for Ed25519; -} - --echo # --echo # Verify Ssl_server_cert_types global status shows all three types --echo # From 9ff6417f0d23fd067b0efc207b7acdb3ea1346c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Fri, 3 Jul 2026 11:57:57 +0200 Subject: [PATCH 09/11] MDEV-39857 tests: removed redundant Test 3 (reviewer comment) --- mysql-test/main/ssl_multi_cert.result | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mysql-test/main/ssl_multi_cert.result b/mysql-test/main/ssl_multi_cert.result index 54501ea941133..27d219e01676d 100644 --- a/mysql-test/main/ssl_multi_cert.result +++ b/mysql-test/main/ssl_multi_cert.result @@ -27,13 +27,4 @@ SHOW STATUS LIKE 'Ssl_server_cert_type'; Variable_name Value Ssl_server_cert_type ECDSA disconnect ecdsa_con; -# -# Test 3: Connect with default ciphers - should succeed -# -connection default; -connect default_con,localhost,root,,,,,SSL; -SELECT (VARIABLE_VALUE <> '') AS have_ssl FROM INFORMATION_SCHEMA.SESSION_STATUS WHERE VARIABLE_NAME='Ssl_cipher'; -have_ssl -1 -disconnect default_con; connection default; From f35cddc66b666f0e6109e141731a7a0562ff37ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Mon, 6 Jul 2026 08:29:32 +0200 Subject: [PATCH 10/11] MDEV-39857 Consolidate cert/key arrays + other comments --- sql/mysqld.cc | 94 +++++++++++++++++---------------------------------- 1 file changed, 31 insertions(+), 63 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index db5722a1c4ad4..c242699076601 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1525,10 +1525,10 @@ char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL, *opt_ssl_cipher= NULL, *opt_ssl_key= NULL, *opt_ssl_crl= NULL, *opt_ssl_crlpath= NULL, *opt_tls_version= NULL; #if !defined(EMBEDDED_LIBRARY) -#define SSL_MAX_ALT_CERTS 4 -static const char *opt_ssl_alt_certs[SSL_MAX_ALT_CERTS]; -static const char *opt_ssl_alt_keys[SSL_MAX_ALT_CERTS]; -static uint ssl_alt_cert_count= 0, ssl_alt_key_count= 0; +#define SSL_MAX_CERTS 3 +static const char *ssl_cert_files[SSL_MAX_CERTS]; +static const char *ssl_key_files[SSL_MAX_CERTS]; +static uint ssl_cert_count= 0, ssl_key_count= 0; #endif ulonglong tls_version= 0; @@ -4760,7 +4760,8 @@ struct SSL_ACCEPTOR_STATS long verify_depth; long zero; const char *session_cache_mode; - char cert_types[64]; + char cert_types[sizeof("RSA") + sizeof("ECDSA") + sizeof("EdDSA") + + 2 * sizeof(", ")]; uchar fprint[256/8]; SSL_ACCEPTOR_STATS(): @@ -4805,7 +4806,7 @@ struct SSL_ACCEPTOR_STATS { #ifndef HAVE_WOLFSSL { - int pos= 0; + size_t pos= 0; SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST); do { X509 *c= SSL_CTX_get0_certificate(ctx); @@ -4815,18 +4816,16 @@ struct SSL_ACCEPTOR_STATS if (pk) { const char *name= evp_pkey_type_name(EVP_PKEY_get_base_id(pk)); - if (pos > 0 && pos + 2 < (int) sizeof(cert_types)) + if (pos > 0 && pos + 2 < sizeof(cert_types)) { cert_types[pos++]= ','; cert_types[pos++]= ' '; } - pos+= (int)(strmake(cert_types + pos, name, - sizeof(cert_types) - pos - 1) - - (cert_types + pos)); + pos= strmake(cert_types + pos, name, + sizeof(cert_types) - pos - 1) - cert_types; } } } while (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT)); - cert_types[pos]= 0; } #else X509 *c= SSL_CTX_get0_certificate(ctx); @@ -4878,30 +4877,16 @@ static void init_ssl() #if defined(HAVE_OPENSSL) if (opt_use_ssl) { - if (ssl_alt_cert_count != ssl_alt_key_count) + if (ssl_cert_count != ssl_key_count) { sql_print_error("Mismatched --ssl-cert-add/--ssl-key-add: %u certs, %u keys", - ssl_alt_cert_count, ssl_alt_key_count); + ssl_cert_count, ssl_key_count); if (!opt_bootstrap) unireg_abort(1); } enum enum_ssl_init_error error= SSL_INITERR_NOERROR; - /* Build combined cert/key arrays: primary at [0], alt certs after */ - const char *ssl_key_files[SSL_MAX_ALT_CERTS + 1]; - const char *ssl_cert_files[SSL_MAX_ALT_CERTS + 1]; - uint ssl_cert_count= 0; - ssl_cert_files[ssl_cert_count]= opt_ssl_cert; - ssl_key_files[ssl_cert_count]= opt_ssl_key; - ssl_cert_count++; - for (uint i= 0; i < ssl_alt_cert_count; i++) - { - ssl_cert_files[ssl_cert_count]= opt_ssl_alt_certs[i]; - ssl_key_files[ssl_cert_count]= opt_ssl_alt_keys[i]; - ssl_cert_count++; - } - /* having ssl_acceptor_fd != 0 signals the use of SSL */ ssl_acceptor_fd= new_VioSSLAcceptorFd(ssl_key_files, ssl_cert_files, ssl_cert_count, @@ -4952,19 +4937,6 @@ int reinit_ssl() enum enum_ssl_init_error error = SSL_INITERR_NOERROR; - const char *ssl_key_files[SSL_MAX_ALT_CERTS + 1]; - const char *ssl_cert_files[SSL_MAX_ALT_CERTS + 1]; - uint ssl_cert_count= 0; - ssl_cert_files[ssl_cert_count]= opt_ssl_cert; - ssl_key_files[ssl_cert_count]= opt_ssl_key; - ssl_cert_count++; - for (uint i= 0; i < ssl_alt_cert_count; i++) - { - ssl_cert_files[ssl_cert_count]= opt_ssl_alt_certs[i]; - ssl_key_files[ssl_cert_count]= opt_ssl_alt_keys[i]; - ssl_cert_count++; - } - st_VioSSLFd *new_fd = new_VioSSLAcceptorFd(ssl_key_files, ssl_cert_files, ssl_cert_count, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher, &error, opt_ssl_crl, @@ -8649,11 +8621,17 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument, my_getopt_init_one_value(opt, opt->value, opt->def_value); #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) - /* --ssl-cert/--ssl-key reset alt cert/key lists (like --plugin-load resets) */ + /* --ssl-cert/--ssl-key reset and re-add primary (like --plugin-load) */ if (opt->id == OPT_SSL_CERT) - ssl_alt_cert_count= 0; + { + ssl_cert_count= 0; + ssl_cert_files[ssl_cert_count++]= opt_ssl_cert; + } else if (opt->id == OPT_SSL_KEY) - ssl_alt_key_count= 0; + { + ssl_key_count= 0; + ssl_key_files[ssl_key_count++]= opt_ssl_key; + } #endif switch(opt->id) { @@ -8722,35 +8700,25 @@ mysqld_get_one_option(const struct my_option *opt, const char *argument, #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) case OPT_SSL_CERT_ADD: if (!opt_ssl_cert || !opt_ssl_cert[0]) - { opt_ssl_cert= (char *) argument; - } - else + if (ssl_cert_count >= SSL_MAX_CERTS) { - if (ssl_alt_cert_count >= SSL_MAX_ALT_CERTS) - { - sql_print_error("Too many --ssl-cert-add options (max %u)", - (uint) SSL_MAX_ALT_CERTS); - return 1; - } - opt_ssl_alt_certs[ssl_alt_cert_count++]= argument; + sql_print_error("Too many --ssl-cert-add options (max %u)", + (uint) (SSL_MAX_CERTS - 1)); + return 1; } + ssl_cert_files[ssl_cert_count++]= argument; break; case OPT_SSL_KEY_ADD: if (!opt_ssl_key || !opt_ssl_key[0]) - { opt_ssl_key= (char *) argument; - } - else + if (ssl_key_count >= SSL_MAX_CERTS) { - if (ssl_alt_key_count >= SSL_MAX_ALT_CERTS) - { - sql_print_error("Too many --ssl-key-add options (max %u)", - (uint) SSL_MAX_ALT_CERTS); - return 1; - } - opt_ssl_alt_keys[ssl_alt_key_count++]= argument; + sql_print_error("Too many --ssl-key-add options (max %u)", + (uint) (SSL_MAX_CERTS - 1)); + return 1; } + ssl_key_files[ssl_key_count++]= argument; break; #endif case 'V': From 1989f2158b1cc36034bfc9d1be1558b57e0651c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Mon, 6 Jul 2026 08:34:06 +0200 Subject: [PATCH 11/11] MDEV-39857 Reverse the order of HAVE_WOLFSSL Also remove the useless "name" variable. --- sql/mysqld.cc | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c242699076601..04d1cabce4f31 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4804,7 +4804,17 @@ struct SSL_ACCEPTOR_STATS /* Build list of loaded certificate types */ cert_types[0]= 0; { -#ifndef HAVE_WOLFSSL +#ifdef HAVE_WOLFSSL + X509 *c= SSL_CTX_get0_certificate(ctx); + if (c) + { + EVP_PKEY *pk= X509_get0_pubkey(c); + if (pk) + strmake(cert_types, + evp_pkey_type_name(EVP_PKEY_get_base_id(pk)), + sizeof(cert_types) - 1); + } +#else { size_t pos= 0; SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST); @@ -4827,17 +4837,6 @@ struct SSL_ACCEPTOR_STATS } } while (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT)); } -#else - X509 *c= SSL_CTX_get0_certificate(ctx); - if (c) - { - EVP_PKEY *pk= X509_get0_pubkey(c); - if (pk) - { - const char *name= evp_pkey_type_name(EVP_PKEY_get_base_id(pk)); - strmake(cert_types, name, sizeof(cert_types) - 1); - } - } #endif } }