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 cdbfcc974f507..bdc5124a465bf 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; @@ -4732,6 +4738,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; @@ -4741,6 +4766,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(): @@ -4779,6 +4805,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 + } } }; @@ -4816,6 +4890,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 */ @@ -4823,7 +4905,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) { @@ -4868,7 +4952,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/sql/sql_select.h b/sql/sql_select.h index 23a927bfcb14e..7216938ebfd00 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -191,251 +191,12 @@ typedef struct st_table_ref /* - The order of the values below is significant; it is used as the index - into join_type_str[] in sql_select.cc to produce the EXPLAIN "type" - column. Keep additions at the end and update join_type_str[] in - parallel. + The structs which holds the join connections and join states */ -enum join_type -{ - /* - Placeholder for an access method that has not been chosen yet. - Its value is 0 and it is the initial state of every JOIN_TAB and - POSITION. Optimization must replace it with a real access - method; the server aborts if a JOIN_TAB still has this type when - the plan is finalized. - */ - JT_UNKNOWN, - - /* - The table is known to contain at most one row, for example an - empty table or a one row table whose storage engine reports exact - row count statistics. The row is read once and kept in the record - buffer for the rest of the query, the remaining conditions treat - its columns as constants, and the table becomes part of the - constant prefix of the join order, so the join order search only - covers the remaining tables. - - Shown as "system" in EXPLAIN. - */ - JT_SYSTEM, - - /* - The table yields at most one row, read through a unique index - whose key parts are all bound by equalities to constants or to - columns of other const tables. The unique constraint guarantees - at most one match, so the optimizer promotes the table to a - const table and fetches the row once during optimization with a - single exact key lookup. As with JT_SYSTEM the table becomes - part of the constant prefix of the join order. Outer joins and - table elimination also use this type for inner tables known to - contribute at most one row, possibly NULL complemented. - - Shown as "const" in EXPLAIN. - */ - JT_CONST, - - /* - Unique index lookup returning at most one row per row combination - of the earlier tables in the join order. The lookup key is built - from constants and from columns of earlier tables in the order. - Every key part of the unique index must be matched, and the lookup - must never search for NULL, since the unique constraint does not - apply to NULL entries; that holds when no key part is nullable, or - when each equality either has a right hand side that cannot be - NULL or rejects NULL (as = does and <=> does not). Execution - caches the most recently read row and skips the lookup when - consecutive key values are identical. The lookup into a - materialized semijoin temp table also uses this type. - - Shown as "eq_ref" in EXPLAIN. - */ - JT_EQ_REF, - - /* - Index lookup that may match more than one row, used when the - bound key parts do not guarantee a unique match; that is, the - index is not unique, only a prefix of its key parts is bound, or - the index is unique but the lookup may search for NULL in a - nullable key part. The first key part must always be bound. - - Shown as "ref" in EXPLAIN. - */ - JT_REF, - - /* - Legacy value that the current code never assigns. It stays in - the enum so that the values after it keep their indices into - join_type_str[]. The server aborts on a JOIN_TAB with this - type, as for JT_UNKNOWN. - */ - JT_MAYBE_REF, - - /* - Sequential full table scan, chosen when no usable index access - exists or when scanning costs less than the indexed - alternatives. At execution this type is also used for range and - index merge access; the JOIN_TAB keeps JT_ALL, the attached - quick select supplies the actual access method, and the EXPLAIN - type is derived from the quick select. A "Range checked for - each record" plan also runs under this type, deciding for each - row combination of the earlier tables whether to use a quick - select or a full scan. - - Shown as "ALL" in EXPLAIN. - */ - JT_ALL, - - /* - Reads a set of ranges from one index through a quick select. - During join order search, the chosen POSITION records this type - when a range quick select wins the cost comparison, but the - executed JOIN_TAB normally keeps JT_ALL with the quick select - attached, and the EXPLAIN type is derived from the quick select - (group min max plans also display as "range"). A JOIN_TAB gets - this type directly when a ref access bound to constants is - replaced by a quick select using more key parts of the same - index, or when a quick select is chosen to return rows in ORDER - BY order. - - Shown as "range" in EXPLAIN. - */ - JT_RANGE, - - /* - Full index scan, reading every index entry in order. Replaces a - full table scan when a covering index can supply all columns the - query needs, so only the index is read. Also chosen under old - style FORCE INDEX hint when no index lookup is usable and a - covering index exists, and for reading rows in index order to - satisfy ORDER BY, forward or in reverse. The new style INDEX - and JOIN_INDEX hints have the same effect here as FORCE INDEX. - - Shown as "index" in EXPLAIN. - */ - JT_NEXT, - - /* - Full text MATCH ... AGAINST access through a FULLTEXT index. - Fulltext predicates produce KEYUSE entries marked with the - special key part number FT_KEYPART. The fulltext engine runs - the search once before the join starts and returns an FT_INFO - handle over the matching rows; execution then fetches those rows - from the handle one at a time. - - Shown as "fulltext" in EXPLAIN. - */ - JT_FT, - - /* - A variant of JT_REF that also fetches the rows where the key - column is NULL. Chosen for predicates of the form - t.key = expr OR t.key IS NULL - on a nullable column. Such predicates appear not only in user - queries, but also in the IN to EXISTS subquery rewrite when on the - "IS NULL" branch when a NULL subquery result must be distinguished - from FALSE. Execution first reads the rows matching the key - value, then repeats the lookup with the NULL key. - - Shown as "ref_or_null" in EXPLAIN. - */ - JT_REF_OR_NULL, - - /* - Replacement of the whole join of an IN subquery by a direct - unique index lookup. Applies when the subquery still runs - through the IN to EXISTS rewrite (neither converted to a - semijoin nor materialized); consists of a single table with no - GROUP BY, ORDER BY, HAVING, or UNION; and the unique lookup key - is the IN predicate's left expression. Each evaluation of the - IN predicate then performs one exact index lookup instead of - executing the subquery as a join. - - Shown as "unique_subquery" in EXPLAIN. - */ - JT_UNIQUE_SUBQUERY, - - /* - Like JT_UNIQUE_SUBQUERY but for a lookup that can match several - rows, replacing a JT_REF access, or a JT_REF_OR_NULL access when - the rewrite added a HAVING condition for NULL handling. Each - evaluation of the IN predicate reads the matching rows in turn - and stops at the first one that satisfies the remaining - conditions, since IN only needs to know whether a match exists; - the JT_REF_OR_NULL form then repeats the lookup with the NULL - key. - - Shown as "index_subquery" in EXPLAIN. - */ - JT_INDEX_SUBQUERY, - - /* - Index merge, combining the results of several index scans. - QUICK_INDEX_MERGE_SELECT computes a union of range scans and - QUICK_INDEX_INTERSECT_SELECT an intersection, both sorting the - collected rowids to remove duplicates; QUICK_ROR_UNION_SELECT - and QUICK_ROR_INTERSECT_SELECT combine scans that return rowids - in rowid order and need no sorting step. As with JT_RANGE the - execution JOIN_TAB keeps JT_ALL with the quick select attached, - and the EXPLAIN type is derived from the quick select. - - Shown as "index_merge" in EXPLAIN. - */ - JT_INDEX_MERGE, - - /* - Block Nested Loop Hash (BNLH) join. Row combinations of the - earlier tables accumulate in the join buffer, a hash table over - the join key is built inside the buffer, and the joined table is - scanned once per buffer refill with every scanned row looked up - in the hash. Chosen when no index lookup on the table is usable - but an equality connects it to earlier tables; requires the - join_cache_hashed optimizer switch and join_cache_level 3 or - higher (the default level is 2, which leaves hash join off). A - ref or eq_ref access executed through a hashed join buffer also - runs under this type. - - Shown as "hash_ALL" in EXPLAIN. - */ - JT_HASH, - - /* - JT_HASH where the scan of the joined table runs through a range - quick select instead of a full scan, so only rows inside the - ranges are fetched and looked up in the hash of buffered rows. - Appears only in EXPLAIN output, derived from a JT_HASH table - whose attached quick select is a range scan; never the type of a - JOIN_TAB. - - Shown as "hash_range" in EXPLAIN. - */ - JT_HASH_RANGE, - - /* - JT_HASH where the joined table is read through a covering index - instead of a full table scan, with every row from the index - looked up in the hash of buffered rows. The promotion from - JT_HASH follows the same rule as the one from JT_ALL to JT_NEXT; - a covering index supplies all columns the query needs from the - table. - - Shown as "hash_index" in EXPLAIN. - */ - JT_HASH_NEXT, - - /* - JT_HASH where the scan of the joined table runs through an index - merge quick select. Appears only in EXPLAIN output, derived - from a JT_HASH table whose attached quick select is an index - merge; never the type of a JOIN_TAB. The combination arises - when OR conditions narrow the scan of the joined table while the - equality joining it to the buffered rows goes through the hash, - as in the LEFT JOIN examples in join_cache.test. - - Shown as "hash_index_merge" in EXPLAIN. - */ - JT_HASH_INDEX_MERGE -}; +enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF, + JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL, + JT_UNIQUE_SUBQUERY, JT_INDEX_SUBQUERY, JT_INDEX_MERGE, + JT_HASH, JT_HASH_RANGE, JT_HASH_NEXT, JT_HASH_INDEX_MERGE}; class JOIN; 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; }