From f1a72768d4256416e5c0bf01b254c303f6135bd0 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 23 Nov 2023 07:11:13 +0100 Subject: [PATCH 01/34] Bump version to 3.1.23 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb684fa1..1131f13c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ SET(CC_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) SET(CPACK_PACKAGE_VERSION_MAJOR 3) SET(CPACK_PACKAGE_VERSION_MINOR 1) -SET(CPACK_PACKAGE_VERSION_PATCH 22) +SET(CPACK_PACKAGE_VERSION_PATCH 23) SET(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}") MATH(EXPR MARIADB_PACKAGE_VERSION_ID "${CPACK_PACKAGE_VERSION_MAJOR} * 10000 + ${CPACK_PACKAGE_VERSION_MINOR} * 100 + From 12cc91ab6d7066e580a7a0c8199e444974d1ac57 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 10 Sep 2021 01:45:09 +0200 Subject: [PATCH 02/34] MDEV-26579 - support minor upgrades of the server MSI Connector library version resource should contain the server's numeric version as FileVersion, when connector will be included into server installation. Otherwise, file version will match the connectors CPACK variables. Prior to this patch, all libraries produced by connector, had a hardcoded file version 3.0.0.7, which was neither the server's , nor the client's. Also fix the Translation info in the version resource so it has valid LCID --- cmake/version_info.cmake | 11 +++++++++++ win/resource.rc.in | 8 ++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cmake/version_info.cmake b/cmake/version_info.cmake index ca0716da..fbb8e280 100644 --- a/cmake/version_info.cmake +++ b/cmake/version_info.cmake @@ -35,6 +35,17 @@ MACRO(SET_VERSION_INFO) STRING(REPLACE "FILE_DESCRIPTION:" "" FILE_DESCRIPTION ${PROPERTY}) ENDIF() ENDFOREACH() + # Connector can be packaged with server, so set the "file version" + # to the server version + # In this case, numeric file version should be consistent with server + # Only this way MSI minor upgrade can work. + # The product version string will still refer to Connectors own version + IF((NOT DEFINED MAJOR_VERSION) OR (NOT DEFINED MINOR_VERSION) OR (NOT DEFINED PATCH_VERSION) OR (NOT DEFINED TINY_VERSION)) + SET(MAJOR_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}) + SET(MINOR_VERSION ${CPACK_PACKAGE_VERSION_MINOR}) + SET(PATCH_VERSION ${CPACK_PACKAGE_VERSION_PATCH}) + SET(TINY_VERSION ${FILE_VERSION}) + ENDIF() CONFIGURE_FILE(${CC_SOURCE_DIR}/win/resource.rc.in ${CC_BINARY_DIR}/win/${TARGET}.rc) SET(${TARGET}_RC ${CC_BINARY_DIR}/win/${TARGET}.rc) diff --git a/win/resource.rc.in b/win/resource.rc.in index 399c88cb..69acd598 100644 --- a/win/resource.rc.in +++ b/win/resource.rc.in @@ -1,8 +1,8 @@ #include "winver.h" VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,0,0,7 - PRODUCTVERSION 3,0,0,0 + FILEVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@,@TINY_VERSION@ + PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@,@TINY_VERSION@ FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -15,7 +15,7 @@ VS_VERSION_INFO VERSIONINFO BEGIN BLOCK "StringFileInfo" BEGIN - BLOCK "000904b0" + BLOCK "040904B0" BEGIN VALUE "CompanyName", "MariaDB Corporation AB" VALUE "FileDescription", "@FILE_DESCRIPTION@" @@ -28,7 +28,7 @@ BEGIN END BLOCK "VarFileInfo" BEGIN - VALUE "Translation", 0x9, 1200 + VALUE "Translation", 0x409, 1200 END END From 12f3b29c14e222553fa868c846b418319a104df4 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 16 Sep 2021 13:36:51 +0200 Subject: [PATCH 03/34] MDEV-26579 - post-fix, fix standalone C/C build --- CMakeLists.txt | 20 ++++++++++++++++++++ cmake/version_info.cmake | 11 ----------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1131f13c..e8b498bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,26 @@ ELSE() SET(MARIADB_CLIENT_VERSION_PATCH "20") SET(MARIADB_CLIENT_VERSION_EXTRA "") ENDIF() + +IF(WIN32) + # version in resource files need to be consistent + # with server's , so that MSI minor upgrade work. + # if this is not a subproject build, C/C version is used. + FOREACH(v MAJOR MINOR PATCH TINY) + IF(DEFINED ${v}_VERSION) + SET(${v}_FILE_VERSION "${${v}_VERSION}") + ELSEIF(DEFINED CPACK_PACKAGE_VERSION_${v}) + SET(${v}_FILE_VERSION "${CPACK_PACKAGE_VERSION_${v}}") + ELSE() + SET(${v}_FILE_VERSION "0") + ENDIF() + IF(NOT ${v}_FILE_VERSION MATCHES "^[0-9]+$") + MESSAGE(FATAL_ERROR + "${v}_FILE_VERSION is not numeric - '${${v}_FILE_VERSION}'") + ENDIF() + ENDFOREACH() +ENDIF() + SET(MARIADB_CLIENT_VERSION "${MARIADB_CLIENT_VERSION_MAJOR}.${MARIADB_CLIENT_VERSION_MINOR}.${MARIADB_CLIENT_VERSION_PATCH}${MARIADB_CLIENT_VERSION_EXTRA}") SET(MARIADB_BASE_VERSION "mariadb-${MARIADB_CLIENT_VERSION_MAJOR}.${MARIADB_CLIENT_VERSION_MINOR}") MATH(EXPR MARIADB_VERSION_ID "${MARIADB_CLIENT_VERSION_MAJOR} * 10000 + diff --git a/cmake/version_info.cmake b/cmake/version_info.cmake index fbb8e280..ca0716da 100644 --- a/cmake/version_info.cmake +++ b/cmake/version_info.cmake @@ -35,17 +35,6 @@ MACRO(SET_VERSION_INFO) STRING(REPLACE "FILE_DESCRIPTION:" "" FILE_DESCRIPTION ${PROPERTY}) ENDIF() ENDFOREACH() - # Connector can be packaged with server, so set the "file version" - # to the server version - # In this case, numeric file version should be consistent with server - # Only this way MSI minor upgrade can work. - # The product version string will still refer to Connectors own version - IF((NOT DEFINED MAJOR_VERSION) OR (NOT DEFINED MINOR_VERSION) OR (NOT DEFINED PATCH_VERSION) OR (NOT DEFINED TINY_VERSION)) - SET(MAJOR_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}) - SET(MINOR_VERSION ${CPACK_PACKAGE_VERSION_MINOR}) - SET(PATCH_VERSION ${CPACK_PACKAGE_VERSION_PATCH}) - SET(TINY_VERSION ${FILE_VERSION}) - ENDIF() CONFIGURE_FILE(${CC_SOURCE_DIR}/win/resource.rc.in ${CC_BINARY_DIR}/win/${TARGET}.rc) SET(${TARGET}_RC ${CC_BINARY_DIR}/win/${TARGET}.rc) From 9155b19b462ac15fc69d0b58ae51370b7523ced5 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 26 Jan 2024 10:40:03 +0100 Subject: [PATCH 04/34] MDEV-26579 - fix resource.rc.in --- win/resource.rc.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/win/resource.rc.in b/win/resource.rc.in index 69acd598..0b3e5976 100644 --- a/win/resource.rc.in +++ b/win/resource.rc.in @@ -1,8 +1,8 @@ #include "winver.h" VS_VERSION_INFO VERSIONINFO - FILEVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@,@TINY_VERSION@ - PRODUCTVERSION @MAJOR_VERSION@,@MINOR_VERSION@,@PATCH_VERSION@,@TINY_VERSION@ + FILEVERSION @MAJOR_FILE_VERSION@,@MINOR_FILE_VERSION@,@PATCH_FILE_VERSION@,@TINY_FILE_VERSION@ + PRODUCTVERSION @MAJOR_FILE_VERSION@,@MINOR_FILE_VERSION@,@PATCH_FILE_VERSION@,@TINY_FILE_VERSION@ FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L From 558ad7d68d139ea5c84c4005426846dcb0c18159 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sat, 24 Feb 2024 17:06:03 +0100 Subject: [PATCH 05/34] CONC-677: Fix possible memory leak: If get_default_configuration_dirs() function fails, we need to release previously allocated memory for configuration_dirs. --- libmariadb/ma_default.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libmariadb/ma_default.c b/libmariadb/ma_default.c index a3eb2119..01f84a26 100644 --- a/libmariadb/ma_default.c +++ b/libmariadb/ma_default.c @@ -133,6 +133,7 @@ char **get_default_configuration_dirs() end: return configuration_dirs; error: + release_configuration_dirs(); return NULL; } From 86e2e87fa22ace6e46353c13a09fa4b8878b7992 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sun, 10 Mar 2024 14:04:27 +0100 Subject: [PATCH 06/34] Follow up of fix for CONC-680: mysql_old_password is disabled by default (setting DISABLED YES), but can be used if the plugin was added to the list of restricted authentication plugins (via mysql_optionsv using option MARIADB_OPT_RESTRICTED_AUTH). --- CMakeLists.txt | 1 + cmake/plugins.cmake | 8 +++++++- libmariadb/ma_client_plugin.c.in | 2 ++ plugins/auth/CMakeLists.txt | 3 ++- plugins/auth/my_auth.c | 9 +++++---- unittest/libmariadb/my_test.h | 1 + 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3bea923..ed9572cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -547,6 +547,7 @@ ENDIF() MESSAGE1(STATUS "MariaDB Connector/c configuration: -- Static PLUGINS ${PLUGINS_STATIC} -- Dynamic PLUGINS ${PLUGINS_DYNAMIC} +-- Disabled PLUGINS ${PLUGINS_DISABLED} -- CPack generation: ${CPACK_GENERATOR} -- SSL support: ${WITH_SSL} Libs: ${SSL_LIBRARIES} -- Zlib support: ${zlib_status} diff --git a/cmake/plugins.cmake b/cmake/plugins.cmake index 1f321b14..89f9948e 100644 --- a/cmake/plugins.cmake +++ b/cmake/plugins.cmake @@ -12,7 +12,7 @@ include(${CC_SOURCE_DIR}/cmake/sign.cmake) FUNCTION(REGISTER_PLUGIN) - SET(one_value_keywords TARGET DEFAULT TYPE) + SET(one_value_keywords TARGET DISABLED TYPE DEFAULT) SET(multi_value_keywords CONFIGURATIONS SOURCES LIBRARIES INCLUDES COMPILE_OPTIONS) cmake_parse_arguments(CC_PLUGIN @@ -43,6 +43,12 @@ FUNCTION(REGISTER_PLUGIN) message(FATAL_ERROR "Invalid plugin type ${CC_PLUGIN_DEFAULT}. Allowed plugin types are ${CC_PLUGIN_CONFIGURATIONS}") endif() +# check if plugin is disabled + string(TOUPPER "${CC_PLUGIN_DISABLED}" CC_PLUGIN_DISABLED) + if("${CC_PLUGIN_DISABLED}" STREQUAL "YES") + set(PLUGINS_DISABLED ${PLUGINS_DISABLED} ${CC_PLUGIN_TARGET} PARENT_SCOPE) + endif() + if(NOT ${CC_PLUGIN_DEFAULT} STREQUAL "OFF") set(PLUGIN_${CC_PLUGIN_TARGET}_TYPE ${CC_PLUGIN_TYPE}) diff --git a/libmariadb/ma_client_plugin.c.in b/libmariadb/ma_client_plugin.c.in index 573feb4e..a402d082 100644 --- a/libmariadb/ma_client_plugin.c.in +++ b/libmariadb/ma_client_plugin.c.in @@ -46,6 +46,8 @@ #include #endif +const char *disabled_plugins= "@PLUGINS_DISABLED@"; + struct st_client_plugin_int { struct st_client_plugin_int *next; void *dlhandle; diff --git a/plugins/auth/CMakeLists.txt b/plugins/auth/CMakeLists.txt index f5fd2f75..c7e06c33 100644 --- a/plugins/auth/CMakeLists.txt +++ b/plugins/auth/CMakeLists.txt @@ -130,7 +130,8 @@ ENDIF() REGISTER_PLUGIN(TARGET mysql_old_password TYPE MARIADB_CLIENT_PLUGIN_AUTH CONFIGURATIONS STATIC DYNAMIC OFF - DEFAULT OFF + DEFAULT STATIC + DISABLED YES SOURCES ${AUTH_DIR}/old_password.c) # Cleartext diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c index 1195f1cd..6c551c32 100644 --- a/plugins/auth/my_auth.c +++ b/plugins/auth/my_auth.c @@ -15,6 +15,7 @@ extern void read_user_name(char *name); extern char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer); extern int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length); extern unsigned char *mysql_net_store_length(unsigned char *packet, ulonglong length); +extern const char *disabled_plugins; #define hashing(p) (p->interface_version >= 0x0101 && p->hash_password_bin) @@ -696,11 +697,11 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, retry: mpvio.plugin= auth_plugin; - if (auth_plugin_name && - mysql->options.extension && - mysql->options.extension->restricted_auth) + if (auth_plugin_name) { - if (!strstr(mysql->options.extension->restricted_auth, auth_plugin_name)) + if ((mysql->options.extension && mysql->options.extension->restricted_auth) + ? !strstr(mysql->options.extension->restricted_auth, auth_plugin_name) + : strstr(disabled_plugins, auth_plugin_name) != NULL) { my_set_error(mysql, CR_PLUGIN_NOT_ALLOWED, SQLSTATE_UNKNOWN, 0, data_plugin); return 1; diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index 9f75d67a..f6a2c42c 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -688,6 +688,7 @@ void run_tests(struct my_tests_st *test) { if (!mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0)) { + diag("Error: %s", mysql_error(mysql)); BAIL_OUT("Can't establish TLS connection to server."); } From 2fc64d791c46fe50b413e579e07300132b28e77a Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 18 Mar 2024 08:09:02 +0100 Subject: [PATCH 07/34] CONC-689: Fix parsing of HEARTBEAT_LOG_EVENT: The heartbeat log event now contains the filename, instead of wrong header information (which was already processed when reading event header). --- include/mariadb_rpl.h | 5 +-- libmariadb/mariadb_rpl.c | 21 ++++----- unittest/libmariadb/rpl_api.c | 82 ++++++++++++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/include/mariadb_rpl.h b/include/mariadb_rpl.h index ea0ca4db..f12fdfa1 100644 --- a/include/mariadb_rpl.h +++ b/include/mariadb_rpl.h @@ -501,10 +501,7 @@ struct st_mariadb_rpl_rows_event { }; struct st_mariadb_rpl_heartbeat_event { - uint32_t timestamp; - uint32_t next_position; - uint8_t type; - uint16_t flags; + MARIADB_STRING filename; }; struct st_mariadb_rpl_xa_prepare_log_event { diff --git a/libmariadb/mariadb_rpl.c b/libmariadb/mariadb_rpl.c index 0019f246..163c9723 100644 --- a/libmariadb/mariadb_rpl.c +++ b/libmariadb/mariadb_rpl.c @@ -1165,20 +1165,15 @@ MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVEN switch(rpl_event->event_type) { case UNKNOWN_EVENT: case SLAVE_EVENT: - return rpl_event; - break; + return rpl_event; + break; + case HEARTBEAT_LOG_EVENT: - /* no post header size */ - RPL_CHECK_POS(ev, ev_end, 11); - rpl_event->event.heartbeat.timestamp= uint4korr(ev); - ev+= 4; - rpl_event->event.heartbeat.next_position= uint4korr(ev); - ev+= 4; - rpl_event->event.heartbeat.type= (uint8_t)*ev; - ev+= 1; - rpl_event->event.heartbeat.flags= uint2korr(ev); - ev+= 2; - + len= rpl_event->event_length - (ev - ev_start) - (rpl->use_checksum ? 4 : 0) - (EVENT_HEADER_OFS - 1); + RPL_CHECK_POS(ev, ev_end, len); + rpl_event->event.heartbeat.filename.length= len; + rpl_event->event.heartbeat.filename.str= ev; + ev+= len; break; case BEGIN_LOAD_QUERY_EVENT: diff --git a/unittest/libmariadb/rpl_api.c b/unittest/libmariadb/rpl_api.c index f65a2915..84586a58 100644 --- a/unittest/libmariadb/rpl_api.c +++ b/unittest/libmariadb/rpl_api.c @@ -72,6 +72,7 @@ static int test_rpl_async(MYSQL *my __attribute__((unused))) mysql_query(mysql, "SET @mariadb_slave_capability=4"); mysql_query(mysql, "SET NAMES latin1"); mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @master_heartbeat_period=10"); mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); mysql_query(mysql, "SET NAMES utf8"); mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); @@ -85,7 +86,7 @@ static int test_rpl_async(MYSQL *my __attribute__((unused))) /* We run rpl_api as very last test, too make sure binary log contains > 10000 events. */ - while((event= mariadb_rpl_fetch(rpl, event)) && events < 10000) + while((event= mariadb_rpl_fetch(rpl, event)) && event->event_type != HEARTBEAT_LOG_EVENT) { events++; } @@ -142,6 +143,7 @@ static int test_rpl_semisync(MYSQL *my __attribute__((unused))) mysql_query(mysql, "SET NAMES latin1"); mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET @master_heartbeat_period=10"); mysql_query(mysql, "SET NAMES utf8"); mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); rpl->server_id= 12; @@ -161,10 +163,7 @@ static int test_rpl_semisync(MYSQL *my __attribute__((unused))) if (mariadb_rpl_open(rpl)) return FAIL; - /* We run rpl_api as very last test, too make sure - binary log contains > 10000 events. - */ - while((event= mariadb_rpl_fetch(rpl, event)) && events < 10000) + while((event= mariadb_rpl_fetch(rpl, event)) && event->event_type != HEARTBEAT_LOG_EVENT) { events++; } @@ -341,7 +340,80 @@ static int test_conc592(MYSQL *my __attribute__((unused))) return OK; } +static int test_conc689(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + MYSQL_RES *result; + MYSQL_ROW row; + MARIADB_RPL_EVENT *event= NULL; + MARIADB_RPL *rpl; + int events= 0, rc; + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "SELECT @@log_bin"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + if (!atoi(row[0])) + rc= SKIP; + mysql_free_result(result); + + if (rc == SKIP) + { + diag("binary log disabled -> skip"); + mysql_close(mysql); + return SKIP; + } + + rpl = mariadb_rpl_init(mysql); + + mysql_query(mysql, "SET @mariadb_slave_capability=4"); + mysql_query(mysql, "SET NAMES latin1"); + mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @master_heartbeat_period=10"); + mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET NAMES utf8"); + mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); + rpl->server_id= 12; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + if (mariadb_rpl_open(rpl)) + return FAIL; + + /* We run rpl_api as very last test, too make sure + binary log contains > 10000 events. + */ + while((event= mariadb_rpl_fetch(rpl, event)) && event->event_type != HEARTBEAT_LOG_EVENT) + { + events++; + } + FAIL_IF(event->event.heartbeat.filename.length == 0, "Invalid filename"); + diag("Filename: %.*s", event->event.heartbeat.filename.length, + event->event.heartbeat.filename.str); + mariadb_free_rpl_event(event); + mariadb_rpl_close(rpl); + mysql_close(mysql); + return OK; +} + + struct my_tests_st my_tests[] = { + {"test_conc689", test_conc689, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc592", test_conc592, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_rpl_async", test_rpl_async, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_rpl_semisync", test_rpl_semisync, TEST_CONNECTION_NEW, 0, NULL, NULL}, From 4a1c5ef53b8dac04a445a972121a1d0a6655f21c Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Fri, 22 Mar 2024 15:35:21 +0100 Subject: [PATCH 08/34] CONC-688: mariadb_rpl_fetch() crashes if table is partitioned Follow up fix of CONC-657 where we didn't set offset of event content correctly. Thanks to Sruli Ganor! --- libmariadb/mariadb_rpl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb/mariadb_rpl.c b/libmariadb/mariadb_rpl.c index 163c9723..0ed91bb8 100644 --- a/libmariadb/mariadb_rpl.c +++ b/libmariadb/mariadb_rpl.c @@ -1830,7 +1830,7 @@ MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVEN if (rpl_event->event.rows.extra_data_size - 2 > 0) { rpl_alloc_set_string_and_len(rpl_event, rpl_event->event.rows.extra_data, ev, rpl_event->event.rows.extra_data_size - 2); - ev+= rpl_event->event.rows.extra_data_size; + ev+= (rpl_event->event.rows.extra_data_size -2); } } /* END_ROWS_EVENT_V2 */ From b64282a9dd0473102121dd4de9e232f95744eda9 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sat, 23 Mar 2024 12:27:55 +0100 Subject: [PATCH 09/34] CONC-667: Fix statement handling when unbuffered results are pending. Resetting a statement will result in an error, if another (different) statement has a pending unbuffered result set (CR_COMMANDS_OUT_OF_SYNC). Freeing a statement result set will return an error, if the statement has no result set or was not executed (CR_STMT_NO_RESULT). --- include/errmsg.h | 3 ++- libmariadb/ma_errmsg.c | 1 + libmariadb/mariadb_stmt.c | 22 ++++++++++++++++++- unittest/libmariadb/ps_bugs.c | 40 +++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/include/errmsg.h b/include/errmsg.h index 4afe8e8a..0d8ddcaf 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -114,10 +114,11 @@ extern const char *mariadb_client_errors[]; /* Error messages */ #define CR_BINLOG_ERROR 5021 #define CR_BINLOG_INVALID_FILE 5022 #define CR_BINLOG_SEMI_SYNC_ERROR 5023 +#define CR_STMT_NO_RESULT 5024 /* Always last, if you add new error codes please update the value for CR_MARIADB_LAST_ERROR */ -#define CR_MARIADB_LAST_ERROR CR_BINLOG_INVALID_FILE +#define CR_MARIADB_LAST_ERROR CR_STMT_NO_RESULT #endif diff --git a/libmariadb/ma_errmsg.c b/libmariadb/ma_errmsg.c index 1f3fa408..1fcc6d4a 100644 --- a/libmariadb/ma_errmsg.c +++ b/libmariadb/ma_errmsg.c @@ -118,6 +118,7 @@ const char *mariadb_client_errors[] = /* 5021 */ "Binary log error (File: %.*s start_pos=%ld): %s.", /* 5022 */ "File '%s' is not a binary log file", /* 5023 */ "Semi sync request error: %s", + /* 5024 */ "Statement has no result set", "" }; diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index 1a20bcca..92f1869c 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -1613,6 +1613,12 @@ unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt) my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) { + if (stmt->state < MYSQL_STMT_EXECUTED || !stmt->field_count) + { + stmt_set_error(stmt, CR_STMT_NO_RESULT, SQLSTATE_UNKNOWN, 0); + return 1; + } + return madb_reset_stmt(stmt, MADB_RESET_LONGDATA | MADB_RESET_STORED | MADB_RESET_BUFFER | MADB_RESET_ERROR); } @@ -2198,6 +2204,19 @@ static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) { MYSQL *mysql= stmt->mysql; my_bool ret= 0; + LIST *li_stmt= mysql->stmts; + + /* CONC-667: If an other statement has a pending result set, we + need to return an error */ + for (;li_stmt;li_stmt= li_stmt->next) + { + MYSQL_STMT *s= (MYSQL_STMT *)li_stmt->data; + if (s != stmt && s->state == MYSQL_STMT_WAITING_USE_OR_STORE) + { + stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return 1; + } + } if (!stmt->mysql) { @@ -2297,7 +2316,8 @@ static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close) stmt->fetch_row_func == stmt_unbuffered_fetch) flags|= MADB_RESET_BUFFER; - ret= madb_reset_stmt(stmt, flags); + if (ret= madb_reset_stmt(stmt, flags)) + return ret; if (stmt->stmt_id) { diff --git a/unittest/libmariadb/ps_bugs.c b/unittest/libmariadb/ps_bugs.c index 804ef3e3..562ea3ff 100644 --- a/unittest/libmariadb/ps_bugs.c +++ b/unittest/libmariadb/ps_bugs.c @@ -5741,7 +5741,47 @@ end: return ret; } +static int test_conc667(MYSQL *mysql) +{ + MYSQL_STMT *stmt1, *stmt2; + int rc; + + stmt1= mysql_stmt_init(mysql); + stmt2= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt1, "SELECT 1", -1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_prepare(stmt2, "SELECT 2", -1); + check_stmt_rc(rc, stmt2); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_free_result(stmt2); + FAIL_IF(!rc || mysql_stmt_errno(stmt2) != CR_STMT_NO_RESULT, + "Expected CR_STMT_NO_RESULT"); + diag("Error (expected) %s", mysql_stmt_error(stmt2)); + + rc= mysql_stmt_reset(stmt2); + FAIL_IF(!rc || mysql_stmt_errno(stmt2) != CR_COMMANDS_OUT_OF_SYNC, + "Expected commands out of sync error"); + + rc= mysql_stmt_fetch(stmt1); + check_stmt_rc(rc, stmt1); + + mysql_stmt_free_result(stmt1); + + rc= mysql_stmt_close(stmt1); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_close(stmt2); + check_stmt_rc(rc, stmt2); + + return OK; +} + struct my_tests_st my_tests[] = { + {"test_conc667", test_conc667, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc633", test_conc633, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc623", test_conc623, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc627", test_conc627, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, From fef3e4ed6d60f0a4fd14a08e8902d30db0215f1d Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sat, 23 Mar 2024 12:53:24 +0100 Subject: [PATCH 10/34] CONC-683: Check pending results when closing statement. Similiar to fix for CONC-667 we need to check if other statements have a pending result set before we can close a statement handle. --- libmariadb/mariadb_stmt.c | 30 +++++++++++++++++++++++------- unittest/libmariadb/ps_bugs.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index 92f1869c..594d149f 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -123,6 +123,20 @@ void stmt_set_error(MYSQL_STMT *stmt, return; } +/* checks if there are any other statements which have a + pending result set */ +static my_bool madb_have_pending_results(MYSQL_STMT *stmt) +{ + LIST *li_stmt= stmt->mysql->stmts; + for (;li_stmt;li_stmt= li_stmt->next) + { + MYSQL_STMT *s= (MYSQL_STMT *)li_stmt->data; + if (s != stmt && s->state == MYSQL_STMT_WAITING_USE_OR_STORE) + return 1; + } + return 0; +} + my_bool mthd_supported_buffer_type(enum enum_field_types type) { switch (type) { @@ -1489,6 +1503,12 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) { my_bool rc= 1; + if (madb_have_pending_results(stmt)) + { + stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return 1; + } + if (stmt) { if (stmt->mysql && stmt->mysql->net.pvio) @@ -2208,14 +2228,10 @@ static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) /* CONC-667: If an other statement has a pending result set, we need to return an error */ - for (;li_stmt;li_stmt= li_stmt->next) + if (madb_have_pending_results(stmt)) { - MYSQL_STMT *s= (MYSQL_STMT *)li_stmt->data; - if (s != stmt && s->state == MYSQL_STMT_WAITING_USE_OR_STORE) - { - stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); - return 1; - } + stmt_set_error(stmt, CR_COMMANDS_OUT_OF_SYNC, SQLSTATE_UNKNOWN, 0); + return 1; } if (!stmt->mysql) diff --git a/unittest/libmariadb/ps_bugs.c b/unittest/libmariadb/ps_bugs.c index 562ea3ff..0623f088 100644 --- a/unittest/libmariadb/ps_bugs.c +++ b/unittest/libmariadb/ps_bugs.c @@ -5780,7 +5780,37 @@ static int test_conc667(MYSQL *mysql) return OK; } +static int test_conc683(MYSQL *mysql) +{ + MYSQL_STMT *stmt1, *stmt2; + int rc; + + stmt1= mysql_stmt_init(mysql); + stmt2= mysql_stmt_init(mysql); + + rc= mysql_stmt_prepare(stmt1, "SELECT 1 UNION SELECT 2", -1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_prepare(stmt2, "SELECT 1", -1); + check_stmt_rc(rc, stmt2); + + rc= mysql_stmt_execute(stmt1); + check_stmt_rc(rc, stmt1); + + rc= mysql_stmt_close(stmt2); + FAIL_IF(!rc || mysql_stmt_errno(stmt2) != CR_COMMANDS_OUT_OF_SYNC, + "Expected commands out of sync error"); + + rc= mysql_stmt_close(stmt1); + check_stmt_rc(rc, stmt1); + rc= mysql_stmt_close(stmt2); + check_stmt_rc(rc, stmt2); + + return OK; +} + struct my_tests_st my_tests[] = { + {"test_conc683", test_conc683, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc667", test_conc667, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc633", test_conc633, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc623", test_conc623, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, From 1d3fd5818a0f97832b69bb60ee1a49d5c1faea4c Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Wed, 29 Jun 2022 13:27:28 +0200 Subject: [PATCH 11/34] Test fix for test_bug4236 --- client/ma_plugin_info.c | 2 +- unittest/libmariadb/ps_bugs.c | 18 +----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/client/ma_plugin_info.c b/client/ma_plugin_info.c index 15951318..73834e12 100644 --- a/client/ma_plugin_info.c +++ b/client/ma_plugin_info.c @@ -204,7 +204,7 @@ int main(int argc, char *argv[]) usage(); break; default: - printf("unrecocognized option: %s", argv[1]); + printf("unrecognized option: %s", argv[1]); exit(1); } exit(0); diff --git a/unittest/libmariadb/ps_bugs.c b/unittest/libmariadb/ps_bugs.c index b5173a76..8e0a102f 100644 --- a/unittest/libmariadb/ps_bugs.c +++ b/unittest/libmariadb/ps_bugs.c @@ -2474,12 +2474,10 @@ static int test_bug4231(MYSQL *mysql) static int test_bug4236(MYSQL *mysql) { - MYSQL_STMT *stmt, *stmt1; + MYSQL_STMT *stmt; const char *stmt_text; int rc; MYSQL_STMT backup; - MYSQL *mysql1; - stmt= mysql_stmt_init(mysql); @@ -2493,20 +2491,6 @@ static int test_bug4236(MYSQL *mysql) rc= mysql_stmt_execute(stmt); FAIL_IF(!rc, "Error expected"); - /* lets try to hack with a new connection */ - mysql1= test_connect(NULL); - stmt1= mysql_stmt_init(mysql1); - stmt_text= "SELECT 2"; - rc= mysql_stmt_prepare(stmt1, SL(stmt_text)); - check_stmt_rc(rc, stmt); - - stmt->stmt_id= stmt1->stmt_id; - rc= mysql_stmt_execute(stmt); - FAIL_IF(!rc, "Error expected"); - - mysql_stmt_close(stmt1); - mysql_close(mysql1); - /* Restore original statement id to be able to reprepare it */ stmt->stmt_id= backup.stmt_id; From dab59732d976f082101caea2501e21ae733da1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 11 Apr 2024 14:47:28 +0300 Subject: [PATCH 12/34] Fix GCC 14 -Wcalloc-transposed-args --- unittest/libmariadb/bulk1.c | 4 ++-- unittest/libmariadb/ps_bugs.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/unittest/libmariadb/bulk1.c b/unittest/libmariadb/bulk1.c index e1c31eaa..62267cd7 100644 --- a/unittest/libmariadb/bulk1.c +++ b/unittest/libmariadb/bulk1.c @@ -74,8 +74,8 @@ static int bulk1(MYSQL *mysql) /* allocate memory */ buffer= calloc(TEST_ARRAY_SIZE, sizeof(char *)); - lengths= (unsigned long *)calloc(sizeof(long), TEST_ARRAY_SIZE); - vals= (unsigned int *)calloc(sizeof(int), TEST_ARRAY_SIZE); + lengths= calloc(TEST_ARRAY_SIZE, sizeof *lengths); + vals= calloc(TEST_ARRAY_SIZE, sizeof *vals); for (i=0; i < TEST_ARRAY_SIZE; i++) { diff --git a/unittest/libmariadb/ps_bugs.c b/unittest/libmariadb/ps_bugs.c index 8e0a102f..f73ce643 100644 --- a/unittest/libmariadb/ps_bugs.c +++ b/unittest/libmariadb/ps_bugs.c @@ -5115,7 +5115,7 @@ static int test_maxparam(MYSQL *mysql) MYSQL_STMT *stmt= mysql_stmt_init(mysql); MYSQL_BIND* bind; - bind = calloc(sizeof(MYSQL_BIND), 65535); + bind = calloc(65535, sizeof *bind); rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); check_mysql_rc(rc, mysql); From f4e8c085fc9ced9b9455e64a1938871b5cc9c79a Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Wed, 24 Apr 2024 11:05:26 +0200 Subject: [PATCH 13/34] Fix compiler warnings --- libmariadb/mariadb_rpl.c | 2 +- libmariadb/mariadb_stmt.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libmariadb/mariadb_rpl.c b/libmariadb/mariadb_rpl.c index 0ed91bb8..cfaf228d 100644 --- a/libmariadb/mariadb_rpl.c +++ b/libmariadb/mariadb_rpl.c @@ -1172,7 +1172,7 @@ MARIADB_RPL_EVENT * STDCALL mariadb_rpl_fetch(MARIADB_RPL *rpl, MARIADB_RPL_EVEN len= rpl_event->event_length - (ev - ev_start) - (rpl->use_checksum ? 4 : 0) - (EVENT_HEADER_OFS - 1); RPL_CHECK_POS(ev, ev_end, len); rpl_event->event.heartbeat.filename.length= len; - rpl_event->event.heartbeat.filename.str= ev; + rpl_event->event.heartbeat.filename.str= (char *)ev; ev+= len; break; diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index 594d149f..4f99ab01 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -2224,7 +2224,6 @@ static my_bool madb_reset_stmt(MYSQL_STMT *stmt, unsigned int flags) { MYSQL *mysql= stmt->mysql; my_bool ret= 0; - LIST *li_stmt= mysql->stmts; /* CONC-667: If an other statement has a pending result set, we need to return an error */ @@ -2332,7 +2331,7 @@ static my_bool mysql_stmt_internal_reset(MYSQL_STMT *stmt, my_bool is_close) stmt->fetch_row_func == stmt_unbuffered_fetch) flags|= MADB_RESET_BUFFER; - if (ret= madb_reset_stmt(stmt, flags)) + if ((ret= madb_reset_stmt(stmt, flags))) return ret; if (stmt->stmt_id) From 19dffea4dc4f6e57e099a57790631308624d387f Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Wed, 24 Apr 2024 11:21:28 +0200 Subject: [PATCH 14/34] CONC-692: Provide X509 peer certificate information Added a new structure MARIADB_X509_INFO, which contains information about servers certificate. The information can be obtained via mysql_get_infov API function: MARIADB_X509_INFO *info; mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); --- include/ma_tls.h | 1 + include/mysql.h | 25 +++++++++++++- libmariadb/ma_pvio.c | 14 ++++++++ libmariadb/mariadb_lib.c | 5 +++ libmariadb/secure/gnutls.c | 37 ++++++++++++++++++++ libmariadb/secure/openssl.c | 44 +++++++++++++++++++----- libmariadb/secure/schannel.c | 59 ++++++++++++++++++++++++++++++++ unittest/libmariadb/connection.c | 51 +++++++++++++++++++++++++++ 8 files changed, 226 insertions(+), 10 deletions(-) diff --git a/include/ma_tls.h b/include/ma_tls.h index 616124c0..6601f896 100644 --- a/include/ma_tls.h +++ b/include/ma_tls.h @@ -27,6 +27,7 @@ typedef struct st_ma_pvio_tls { void *data; MARIADB_PVIO *pvio; void *ssl; + MARIADB_X509_INFO cert_info; } MARIADB_TLS; /* Function prototypes */ diff --git a/include/mysql.h b/include/mysql.h index 093f19e0..1d30486a 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -33,6 +33,7 @@ extern "C" { #endif #include +#include #if !defined (_global_h) && !defined (MY_GLOBAL_INCLUDED) /* If not standard header */ #include @@ -297,7 +298,8 @@ extern const char *SQLSTATE_UNKNOWN; MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES, MARIADB_CONNECTION_CLIENT_CAPABILITIES, MARIADB_CONNECTION_BYTES_READ, - MARIADB_CONNECTION_BYTES_SENT + MARIADB_CONNECTION_BYTES_SENT, + MARIADB_TLS_PEER_CERT_INFO, }; enum mysql_status { MYSQL_STATUS_READY, @@ -481,6 +483,27 @@ struct st_mysql_client_plugin MYSQL_CLIENT_PLUGIN_HEADER }; +enum mariadb_tls_verification { + MARIADB_VERIFY_NONE = 0, + MARIADB_VERIFY_PIPE, + MARIADB_VERIFY_UNIXSOCKET, + MARIADB_VERIFY_LOCALHOST, + MARIADB_VERIFY_FINGERPRINT, + MARIADB_VERIFY_PEER_CERT +}; + +typedef struct +{ + int version; + char *issuer; + char *subject; + char fingerprint[65]; + struct tm not_before; + struct tm not_after; + enum mariadb_tls_verification verify_mode; +} MARIADB_X509_INFO; + + struct st_mysql_client_plugin * mysql_load_plugin(struct st_mysql *mysql, const char *name, int type, int argc, ...); diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 6f726dbd..95d02d57 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -549,13 +549,24 @@ static my_bool ignore_self_signed_cert_error(MARIADB_PVIO *pvio) "127.0.0.1", "::1", NULL}; int i; if (pvio->type != PVIO_TYPE_SOCKET) + { + pvio->ctls->cert_info.verify_mode= +#ifdef WIN32 + MARIADB_VERIFY_PIPE; +#else + MARIADB_VERIFY_UNIXSOCKET; +#endif return TRUE; + } if (!hostname) return FALSE; for (i= 0; local_host_names[i]; i++) { if (strcmp(hostname, local_host_names[i]) == 0) + { + pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_LOCALHOST; return TRUE; + } } return FALSE; } @@ -586,6 +597,8 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) !pvio->mysql->net.tls_self_signed_error && ma_pvio_tls_verify_server_cert(pvio->ctls)) return 1; + else + pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_PEER_CERT; if (pvio->mysql->options.extension && ((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) || @@ -595,6 +608,7 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) pvio->mysql->options.extension->tls_fp, pvio->mysql->options.extension->tls_fp_list)) return 1; + pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_FINGERPRINT; reset_tls_self_signed_error(pvio->mysql); // validated } diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 31a062e6..52f96889 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -4519,6 +4519,11 @@ my_bool mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ... va_start(ap, arg); switch(value) { +#ifdef HAVE_TLS + case MARIADB_TLS_PEER_CERT_INFO: + *((MARIADB_X509_INFO **)arg)= mysql->net.pvio->ctls ? (MARIADB_X509_INFO *)&mysql->net.pvio->ctls->cert_info : NULL; + break; +#endif case MARIADB_MAX_ALLOWED_PACKET: *((size_t *)arg)= (size_t)max_allowed_packet; break; diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index 9a83a682..7b039f35 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -1173,6 +1173,8 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl); MARIADB_PVIO *pvio; int ret; + const gnutls_datum_t *cert_list; + unsigned int list_size= 0; if (!mysql) return 1; @@ -1214,6 +1216,39 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) return 1; } ctls->ssl= (void *)ssl; + + /* retrieve peer certificate information */ + if ((cert_list= gnutls_certificate_get_peers(ssl, &list_size))) + { + gnutls_x509_crt_t cert; + + gnutls_x509_crt_init(&cert); + memset(&ctls->cert_info, 0, sizeof(MARIADB_X509_INFO)); + + if (!gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)) + { + size_t len= 0; + time_t notBefore, notAfter; + + ctls->cert_info.version= gnutls_x509_crt_get_version(cert); + + gnutls_x509_crt_get_issuer_dn(cert, NULL, &len); + if ((ctls->cert_info.issuer= (char *)malloc(len))) + gnutls_x509_crt_get_issuer_dn(cert, ctls->cert_info.issuer, &len); + + gnutls_x509_crt_get_dn(cert, NULL, &len); + if ((ctls->cert_info.subject= (char *)malloc(len))) + gnutls_x509_crt_get_dn(cert, ctls->cert_info.subject, &len); + + notBefore= gnutls_x509_crt_get_activation_time(cert); + memcpy(&ctls->cert_info.not_before, gmtime(¬Before), sizeof(struct tm)); + + notAfter= gnutls_x509_crt_get_expiration_time(cert); + memcpy(&ctls->cert_info.not_after, gmtime(¬After), sizeof(struct tm)); + + ma_tls_get_finger_print(ctls, MA_HASH_SHA256, (char *)&ctls->cert_info.fingerprint, 32); + } + } return 0; } @@ -1321,6 +1356,8 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) gnutls_certificate_free_ca_names(ctx); gnutls_certificate_free_credentials(ctx); gnutls_deinit((gnutls_session_t )ctls->ssl); + free(ctls->cert_info.issuer); + free(ctls->cert_info.subject); ctls->ssl= NULL; } return 0; diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 2347d90d..8ec81872 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -463,6 +463,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) MYSQL *mysql; MARIADB_PVIO *pvio; int rc; + X509 *cert; #ifdef OPENSSL_USE_BIOMETHOD BIO_METHOD *bio_method= NULL; BIO *bio; @@ -515,7 +516,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) mysql->net.tls_self_signed_error= X509_verify_cert_error_string(x509_err); else if (x509_err != X509_V_OK) { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(x509_err)); /* restore blocking mode */ if (!blocking) @@ -529,6 +530,23 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) } pvio->ctls->ssl= ctls->ssl= (void *)ssl; + /* Store peer certificate information */ + if ((cert= SSL_get_peer_certificate(ssl))) + { + char fp[33]; + const ASN1_TIME *not_before= X509_get0_notBefore(cert), + *not_after= X509_get0_notAfter(cert); + ctls->cert_info.subject= X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + ctls->cert_info.issuer= X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + ctls->cert_info.version= X509_get_version(cert) + 1; + + ASN1_TIME_to_tm(not_before, (struct tm *)&ctls->cert_info.not_before); + ASN1_TIME_to_tm(not_after, (struct tm *)&ctls->cert_info.not_after); + + ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33); + mysql_hex_string(ctls->cert_info.fingerprint, fp, 32); + } + return 0; } @@ -654,6 +672,9 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) SSL_free(ssl); ctls->ssl= NULL; + OPENSSL_free(ctls->cert_info.issuer); + OPENSSL_free(ctls->cert_info.subject); + return rc; } @@ -739,33 +760,30 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp MYSQL *mysql; unsigned int fp_len; const EVP_MD *hash_alg; + unsigned int max_len= EVP_MAX_MD_SIZE; if (!ctls || !ctls->ssl) return 0; mysql = SSL_get_app_data(ctls->ssl); - if (len < EVP_MAX_MD_SIZE) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Finger print buffer too small"); - return 0; - } - switch (hash_type) { case MA_HASH_SHA1: hash_alg = EVP_sha1(); + max_len= 20; break; case MA_HASH_SHA224: hash_alg = EVP_sha224(); + max_len= 28; break; case MA_HASH_SHA256: hash_alg = EVP_sha256(); + max_len= 32; break; case MA_HASH_SHA384: hash_alg = EVP_sha384(); + max_len= 48; break; case MA_HASH_SHA512: hash_alg = EVP_sha512(); @@ -776,6 +794,14 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp "Cannot detect hash algorithm for fingerprint verification"); return 0; } + + if (len < max_len) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Finger print buffer too small"); + return 0; + } if (!(cert= SSL_get_peer_certificate(ctls->ssl))) { diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index a0f94903..a6617d2e 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -32,6 +32,8 @@ char tls_library_version[] = "Schannel"; #define PROT_TLS1_2 4 #define PROT_TLS1_3 8 +unsigned int ma_set_tls_x509_info(MARIADB_TLS *ctls); + static struct { DWORD cipher_id; @@ -457,6 +459,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) goto end; } + ma_set_tls_x509_info(ctls); rc = 0; end: @@ -511,6 +514,8 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) DeleteSecurityContext(&sctx->hCtxt); } LocalFree(sctx); + LocalFree(ctls->cert_info.issuer); + LocalFree(ctls->cert_info.subject); return 0; } /* }}} */ @@ -551,6 +556,60 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls) return cipher_name(&CipherInfo); } +unsigned char *ma_cert_blob_to_str(PCERT_NAME_BLOB cnblob) +{ + DWORD type= CERT_X500_NAME_STR; + DWORD size= CertNameToStrA(X509_ASN_ENCODING, cnblob, type, NULL, 0); + char *str= NULL; + + if (!size) + return NULL; + + str= (char *)LocalAlloc(LMEM_ZEROINIT,size); + CertNameToStrA(X509_ASN_ENCODING, cnblob, type, str, size); + return str; +} + +static void ma_systime_to_tm(SYSTEMTIME sys_tm, struct tm *tm) +{ + memset(tm, 0, sizeof(struct tm)); + tm->tm_year= sys_tm.wYear - 1900; + tm->tm_mon= sys_tm.wMonth - 1; + tm->tm_mday= sys_tm.wDay; + tm->tm_hour = sys_tm.wHour; + tm->tm_min = sys_tm.wMinute; +} + +unsigned int ma_set_tls_x509_info(MARIADB_TLS *ctls) +{ + PCCERT_CONTEXT pCertCtx= NULL; + SC_CTX *sctx= (SC_CTX *)ctls->ssl; + PCERT_INFO pci= NULL; + DWORD size´= 0; + SYSTEMTIME tm; + char fp[33]; + + if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pCertCtx) != SEC_E_OK) + return 1; + + pci= pCertCtx->pCertInfo; + + ctls->cert_info.version= pci->dwVersion; + ctls->cert_info.subject = ma_cert_blob_to_str(&pci->Subject); + ctls->cert_info.issuer = ma_cert_blob_to_str(&pci->Issuer); + + FileTimeToSystemTime(&pci->NotBefore, &tm); + ma_systime_to_tm(tm, &ctls->cert_info.not_before); + FileTimeToSystemTime(&pci->NotAfter, &tm); + ma_systime_to_tm(tm, &ctls->cert_info.not_after); + + ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33); + mysql_hex_string(ctls->cert_info.fingerprint, fp, 32); + + return 0; +} + + unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp, unsigned int len) { MA_HASH_CTX* hash_ctx; diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index 761e6394..6c8ca17b 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -1369,6 +1369,7 @@ static int test_conc276(MYSQL *unused __attribute__((unused))) MYSQL *mysql= mysql_init(NULL); int rc; my_bool val= 1; + MARIADB_X509_INFO *info; mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(mysql, MYSQL_OPT_RECONNECT, &val); @@ -1380,7 +1381,15 @@ static int test_conc276(MYSQL *unused __attribute__((unused))) return FAIL; } diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); + diag("subject: %s", info->subject); + diag("issuer: %s", info->issuer); + diag("fingerprint: %s", info->fingerprint); + diag("not before : %04d.%02d.%02d", info->not_before.tm_year + 1900, + info->not_before.tm_mon + 1, info->not_before.tm_mday); + diag("not after : %04d.%02d.%02d", info->not_after.tm_year + 1900, + info->not_after.tm_mon + 1, info->not_after.tm_mday); rc= mariadb_reconnect(mysql); check_mysql_rc(rc, mysql); @@ -2309,7 +2318,49 @@ static int test_conc632(MYSQL *my __attribute__((unused))) return OK; } +static int test_x509(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql1, *mysql2; + my_bool val= 1; + my_bool verify= 0; + char fp[65]; + MARIADB_X509_INFO *info; + + mysql1= mysql_init(NULL); + mysql2= mysql_init(NULL); + + mysql_options(mysql1, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(mysql2, MYSQL_OPT_SSL_ENFORCE, &val); + + mysql_options(mysql1, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + if (!(my_test_connect(mysql1, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + return FAIL; + } + mariadb_get_infov(mysql1, MARIADB_TLS_PEER_CERT_INFO, &info); + memset(fp, 0, 65); + diag("fingerprint: %s", info->fingerprint); + mysql_options(mysql2, MARIADB_OPT_TLS_PEER_FP, info->fingerprint); + if (!(my_test_connect(mysql2, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + return FAIL; + } + mariadb_get_infov(mysql2, MARIADB_TLS_PEER_CERT_INFO, &info); + FAIL_IF(info->verify_mode != MARIADB_VERIFY_FINGERPRINT, "Fingerprint verification expected"); + + mysql_close(mysql1); + mysql_close(mysql2); + return OK; +} + struct my_tests_st my_tests[] = { + {"test_x509", test_x509, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc632", test_conc632, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_status_callback", test_status_callback, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL}, From ffd0a0e4be4a84401330889dfcc0535c1ed3f472 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Sat, 27 Apr 2024 20:52:13 +0200 Subject: [PATCH 15/34] Fix identation error. --- libmariadb/ma_pvio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 95d02d57..09bc1810 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -608,7 +608,7 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) pvio->mysql->options.extension->tls_fp, pvio->mysql->options.extension->tls_fp_list)) return 1; - pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_FINGERPRINT; + pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_FINGERPRINT; reset_tls_self_signed_error(pvio->mysql); // validated } From 20fbb3c3b967bb4e8325636c72f06aae1f121bcd Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 29 Apr 2024 10:57:12 +0200 Subject: [PATCH 16/34] Avoid possible crash if connection was closed Before checking pending result sets in prepared statements, we need to check if the connection was already closed or resetted by checking stmt->mysql. In case of NULL we return false, since there are no more pending result sets. --- libmariadb/mariadb_stmt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index 4f99ab01..cbf88eed 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -127,7 +127,12 @@ void stmt_set_error(MYSQL_STMT *stmt, pending result set */ static my_bool madb_have_pending_results(MYSQL_STMT *stmt) { - LIST *li_stmt= stmt->mysql->stmts; + LIST *li_stmt; + + if (!stmt->mysql) + return 0; + + li_stmt= stmt->mysql->stmts; for (;li_stmt;li_stmt= li_stmt->next) { MYSQL_STMT *s= (MYSQL_STMT *)li_stmt->data; From 9644f52796210ea1cfcec867eb61457f8d76c3f2 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 29 Apr 2024 11:15:32 +0200 Subject: [PATCH 17/34] Skip async test on Windows Schannel implementation doesn't support async mode yet. --- unittest/libmariadb/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittest/libmariadb/CMakeLists.txt b/unittest/libmariadb/CMakeLists.txt index 876e5cfc..d5b93716 100644 --- a/unittest/libmariadb/CMakeLists.txt +++ b/unittest/libmariadb/CMakeLists.txt @@ -31,7 +31,9 @@ IF(WITH_DYNCOL) SET(API_TESTS ${API_TESTS} "dyncol") ENDIF() -SET(API_TESTS ${API_TESTS} "async") +IF(NOT WIN32) + SET(API_TESTS ${API_TESTS} "async") +ENDIF() #exclude following tests from ctests, since we need to run them manually with different credentials SET(MANUAL_TESTS "t_conc173" "rpl_api") From a25049ba49669e153cd771955d772ca4a6a4b381 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 29 Apr 2024 11:17:35 +0200 Subject: [PATCH 18/34] Remove temp. diagnostic information --- unittest/libmariadb/rpl_api.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/unittest/libmariadb/rpl_api.c b/unittest/libmariadb/rpl_api.c index 84586a58..b502eec8 100644 --- a/unittest/libmariadb/rpl_api.c +++ b/unittest/libmariadb/rpl_api.c @@ -403,8 +403,6 @@ static int test_conc689(MYSQL *my __attribute__((unused))) events++; } FAIL_IF(event->event.heartbeat.filename.length == 0, "Invalid filename"); - diag("Filename: %.*s", event->event.heartbeat.filename.length, - event->event.heartbeat.filename.str); mariadb_free_rpl_event(event); mariadb_rpl_close(rpl); mysql_close(mysql); From 89d11c8b05eef6ddb0dcb90c42cb63f235e71d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Wed, 3 Apr 2024 21:21:35 +0100 Subject: [PATCH 19/34] Fix `sys/poll.h` -> `poll.h` See https://pubs.opengroup.org/onlinepubs/7908799/xsh/poll.h.html, or the other files in this project referencing `poll.h`: ```console % grep -r 'poll.h' libmariadb/ma_net.c:#include libmariadb/mariadb_lib.c:#include plugins/pvio/pvio_socket.c:#include unittest/libmariadb/async.c:#include ``` --- plugins/pvio/pvio_socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pvio/pvio_socket.c b/plugins/pvio/pvio_socket.c index d6046a9e..c891a206 100644 --- a/plugins/pvio/pvio_socket.c +++ b/plugins/pvio/pvio_socket.c @@ -39,7 +39,7 @@ #include #endif #ifdef HAVE_POLL -#include +#include #endif #ifdef HAVE_SYS_IOCTL_H #include From 4c1c7f37d69448d186f47be28689c6af13d22392 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Wed, 27 Mar 2024 16:50:20 -0700 Subject: [PATCH 20/34] Fix SSL_read/write return value checking in ma_tls_async_check_result SSL_{read,write}'s return values == 0 signify the operation was unsuccessful, but here it's being treated as success. Other calls of these functions already properly checks the return value. Signed-off-by: Josh Hunt --- libmariadb/mariadb_async.c | 2 +- libmariadb/secure/openssl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libmariadb/mariadb_async.c b/libmariadb/mariadb_async.c index 8e5d77b1..abb66a16 100644 --- a/libmariadb/mariadb_async.c +++ b/libmariadb/mariadb_async.c @@ -140,7 +140,7 @@ my_ssl_async_check_result(int res, struct mysql_async_context *b, MARIADB_SSL *c { int ssl_err; b->events_to_wait_for= 0; - if (res >= 0) + if (res > 0) return 1; ssl_err= SSL_get_error(ssl, res); if (ssl_err == SSL_ERROR_WANT_READ) diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index a21d692e..ef6be9b1 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -529,7 +529,7 @@ ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl) { int ssl_err; b->events_to_wait_for= 0; - if (res >= 0) + if (res > 0) return 1; ssl_err= SSL_get_error(ssl, res); if (ssl_err == SSL_ERROR_WANT_READ) From 51b2a621b3d5ef949098dcb7912048caaf878793 Mon Sep 17 00:00:00 2001 From: Sam James Date: Mon, 4 Mar 2024 04:33:30 +0000 Subject: [PATCH 21/34] Fix -Wcalloc-transposed-args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes warnings like: ``` unittest/libmariadb/bulk1.c: In function ‘bulk1’: unittest/libmariadb/bulk1.c:77:43: error: ‘calloc’ sizes specified with ‘sizeof’ in the earlier argument and not in the later argument [-Werror=calloc-transposed-args] 77 | lengths= (unsigned long *)calloc(sizeof(long), TEST_ARRAY_SIZE); | ^~~~ unittest/libmariadb/bulk1.c:77:43: note: earlier argument should specify number of elements, later size of each element unittest/libmariadb/bulk1.c:78:39: error: ‘calloc’ sizes specified with ‘sizeof’ in the earlier argu ment and not in the later argument [-Werror=calloc-transposed-args] 78 | vals= (unsigned int *)calloc(sizeof(int), TEST_ARRAY_SIZE); | ^~~ ``` The calloc prototype is: ``` void *calloc(size_t nmemb, size_t size); ``` So, just swap the number of members and size arguments to match the prototype, as we're initialising N struct of size Y. GCC then sees we're not doing anything wrong. Signed-off-by: Sam James --- plugins/io/remote_io.c | 4 ++-- plugins/trace/trace_example.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/io/remote_io.c b/plugins/io/remote_io.c index c06ecacd..eb5b5f37 100644 --- a/plugins/io/remote_io.c +++ b/plugins/io/remote_io.c @@ -279,11 +279,11 @@ MA_FILE *ma_rio_open(const char *url,const char *operation) MA_REMOTE_FILE *rf; (void)operation; - if (!(file = (MA_FILE *)calloc(sizeof(MA_FILE), 1))) + if (!(file = (MA_FILE *)calloc(1, sizeof(MA_FILE)))) return NULL; file->type= MA_FILE_REMOTE; - if (!(file->ptr= rf= (MA_REMOTE_FILE *)calloc(sizeof(MA_REMOTE_FILE), 1))) + if (!(file->ptr= rf= (MA_REMOTE_FILE *)calloc(1, sizeof(MA_REMOTE_FILE)))) { free(file); return NULL; diff --git a/plugins/trace/trace_example.c b/plugins/trace/trace_example.c index 1060542c..e177c206 100644 --- a/plugins/trace/trace_example.c +++ b/plugins/trace/trace_example.c @@ -132,7 +132,7 @@ static TRACE_INFO *get_trace_info(unsigned long thread_id) info= (TRACE_INFO *)info->next; } - if (!(info= (TRACE_INFO *)calloc(sizeof(TRACE_INFO), 1))) + if (!(info= (TRACE_INFO *)calloc(1, sizeof(TRACE_INFO)))) return NULL; info->thread_id= thread_id; info->next= trace_info; From 67cb58a203ce3fa80d6e41ebc9f506d77c138741 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 30 Apr 2024 10:48:41 +0200 Subject: [PATCH 22/34] CONC-692: Provide X509 peer certificate information Added a new structure MARIADB_X509_INFO, which contains information about servers certificate. The information can be obtained via mysql_get_infov API function: MARIADB_X509_INFO *info; mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); --- include/ma_tls.h | 1 + include/mysql.h | 25 +++++++++++++- libmariadb/ma_pvio.c | 14 ++++++++ libmariadb/mariadb_lib.c | 5 +++ libmariadb/secure/gnutls.c | 37 ++++++++++++++++++++ libmariadb/secure/openssl.c | 44 +++++++++++++++++++----- libmariadb/secure/schannel.c | 59 ++++++++++++++++++++++++++++++++ unittest/libmariadb/connection.c | 51 +++++++++++++++++++++++++++ 8 files changed, 226 insertions(+), 10 deletions(-) diff --git a/include/ma_tls.h b/include/ma_tls.h index 616124c0..6601f896 100644 --- a/include/ma_tls.h +++ b/include/ma_tls.h @@ -27,6 +27,7 @@ typedef struct st_ma_pvio_tls { void *data; MARIADB_PVIO *pvio; void *ssl; + MARIADB_X509_INFO cert_info; } MARIADB_TLS; /* Function prototypes */ diff --git a/include/mysql.h b/include/mysql.h index 093f19e0..1d30486a 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -33,6 +33,7 @@ extern "C" { #endif #include +#include #if !defined (_global_h) && !defined (MY_GLOBAL_INCLUDED) /* If not standard header */ #include @@ -297,7 +298,8 @@ extern const char *SQLSTATE_UNKNOWN; MARIADB_CONNECTION_EXTENDED_SERVER_CAPABILITIES, MARIADB_CONNECTION_CLIENT_CAPABILITIES, MARIADB_CONNECTION_BYTES_READ, - MARIADB_CONNECTION_BYTES_SENT + MARIADB_CONNECTION_BYTES_SENT, + MARIADB_TLS_PEER_CERT_INFO, }; enum mysql_status { MYSQL_STATUS_READY, @@ -481,6 +483,27 @@ struct st_mysql_client_plugin MYSQL_CLIENT_PLUGIN_HEADER }; +enum mariadb_tls_verification { + MARIADB_VERIFY_NONE = 0, + MARIADB_VERIFY_PIPE, + MARIADB_VERIFY_UNIXSOCKET, + MARIADB_VERIFY_LOCALHOST, + MARIADB_VERIFY_FINGERPRINT, + MARIADB_VERIFY_PEER_CERT +}; + +typedef struct +{ + int version; + char *issuer; + char *subject; + char fingerprint[65]; + struct tm not_before; + struct tm not_after; + enum mariadb_tls_verification verify_mode; +} MARIADB_X509_INFO; + + struct st_mysql_client_plugin * mysql_load_plugin(struct st_mysql *mysql, const char *name, int type, int argc, ...); diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 6f726dbd..09bc1810 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -549,13 +549,24 @@ static my_bool ignore_self_signed_cert_error(MARIADB_PVIO *pvio) "127.0.0.1", "::1", NULL}; int i; if (pvio->type != PVIO_TYPE_SOCKET) + { + pvio->ctls->cert_info.verify_mode= +#ifdef WIN32 + MARIADB_VERIFY_PIPE; +#else + MARIADB_VERIFY_UNIXSOCKET; +#endif return TRUE; + } if (!hostname) return FALSE; for (i= 0; local_host_names[i]; i++) { if (strcmp(hostname, local_host_names[i]) == 0) + { + pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_LOCALHOST; return TRUE; + } } return FALSE; } @@ -586,6 +597,8 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) !pvio->mysql->net.tls_self_signed_error && ma_pvio_tls_verify_server_cert(pvio->ctls)) return 1; + else + pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_PEER_CERT; if (pvio->mysql->options.extension && ((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) || @@ -595,6 +608,7 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) pvio->mysql->options.extension->tls_fp, pvio->mysql->options.extension->tls_fp_list)) return 1; + pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_FINGERPRINT; reset_tls_self_signed_error(pvio->mysql); // validated } diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 31a062e6..52f96889 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -4519,6 +4519,11 @@ my_bool mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *arg, ... va_start(ap, arg); switch(value) { +#ifdef HAVE_TLS + case MARIADB_TLS_PEER_CERT_INFO: + *((MARIADB_X509_INFO **)arg)= mysql->net.pvio->ctls ? (MARIADB_X509_INFO *)&mysql->net.pvio->ctls->cert_info : NULL; + break; +#endif case MARIADB_MAX_ALLOWED_PACKET: *((size_t *)arg)= (size_t)max_allowed_packet; break; diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index 9a83a682..7b039f35 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -1173,6 +1173,8 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl); MARIADB_PVIO *pvio; int ret; + const gnutls_datum_t *cert_list; + unsigned int list_size= 0; if (!mysql) return 1; @@ -1214,6 +1216,39 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) return 1; } ctls->ssl= (void *)ssl; + + /* retrieve peer certificate information */ + if ((cert_list= gnutls_certificate_get_peers(ssl, &list_size))) + { + gnutls_x509_crt_t cert; + + gnutls_x509_crt_init(&cert); + memset(&ctls->cert_info, 0, sizeof(MARIADB_X509_INFO)); + + if (!gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)) + { + size_t len= 0; + time_t notBefore, notAfter; + + ctls->cert_info.version= gnutls_x509_crt_get_version(cert); + + gnutls_x509_crt_get_issuer_dn(cert, NULL, &len); + if ((ctls->cert_info.issuer= (char *)malloc(len))) + gnutls_x509_crt_get_issuer_dn(cert, ctls->cert_info.issuer, &len); + + gnutls_x509_crt_get_dn(cert, NULL, &len); + if ((ctls->cert_info.subject= (char *)malloc(len))) + gnutls_x509_crt_get_dn(cert, ctls->cert_info.subject, &len); + + notBefore= gnutls_x509_crt_get_activation_time(cert); + memcpy(&ctls->cert_info.not_before, gmtime(¬Before), sizeof(struct tm)); + + notAfter= gnutls_x509_crt_get_expiration_time(cert); + memcpy(&ctls->cert_info.not_after, gmtime(¬After), sizeof(struct tm)); + + ma_tls_get_finger_print(ctls, MA_HASH_SHA256, (char *)&ctls->cert_info.fingerprint, 32); + } + } return 0; } @@ -1321,6 +1356,8 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) gnutls_certificate_free_ca_names(ctx); gnutls_certificate_free_credentials(ctx); gnutls_deinit((gnutls_session_t )ctls->ssl); + free(ctls->cert_info.issuer); + free(ctls->cert_info.subject); ctls->ssl= NULL; } return 0; diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 2347d90d..8ec81872 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -463,6 +463,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) MYSQL *mysql; MARIADB_PVIO *pvio; int rc; + X509 *cert; #ifdef OPENSSL_USE_BIOMETHOD BIO_METHOD *bio_method= NULL; BIO *bio; @@ -515,7 +516,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) mysql->net.tls_self_signed_error= X509_verify_cert_error_string(x509_err); else if (x509_err != X509_V_OK) { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(x509_err)); /* restore blocking mode */ if (!blocking) @@ -529,6 +530,23 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) } pvio->ctls->ssl= ctls->ssl= (void *)ssl; + /* Store peer certificate information */ + if ((cert= SSL_get_peer_certificate(ssl))) + { + char fp[33]; + const ASN1_TIME *not_before= X509_get0_notBefore(cert), + *not_after= X509_get0_notAfter(cert); + ctls->cert_info.subject= X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + ctls->cert_info.issuer= X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + ctls->cert_info.version= X509_get_version(cert) + 1; + + ASN1_TIME_to_tm(not_before, (struct tm *)&ctls->cert_info.not_before); + ASN1_TIME_to_tm(not_after, (struct tm *)&ctls->cert_info.not_after); + + ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33); + mysql_hex_string(ctls->cert_info.fingerprint, fp, 32); + } + return 0; } @@ -654,6 +672,9 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) SSL_free(ssl); ctls->ssl= NULL; + OPENSSL_free(ctls->cert_info.issuer); + OPENSSL_free(ctls->cert_info.subject); + return rc; } @@ -739,33 +760,30 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp MYSQL *mysql; unsigned int fp_len; const EVP_MD *hash_alg; + unsigned int max_len= EVP_MAX_MD_SIZE; if (!ctls || !ctls->ssl) return 0; mysql = SSL_get_app_data(ctls->ssl); - if (len < EVP_MAX_MD_SIZE) - { - my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, - ER(CR_SSL_CONNECTION_ERROR), - "Finger print buffer too small"); - return 0; - } - switch (hash_type) { case MA_HASH_SHA1: hash_alg = EVP_sha1(); + max_len= 20; break; case MA_HASH_SHA224: hash_alg = EVP_sha224(); + max_len= 28; break; case MA_HASH_SHA256: hash_alg = EVP_sha256(); + max_len= 32; break; case MA_HASH_SHA384: hash_alg = EVP_sha384(); + max_len= 48; break; case MA_HASH_SHA512: hash_alg = EVP_sha512(); @@ -776,6 +794,14 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp "Cannot detect hash algorithm for fingerprint verification"); return 0; } + + if (len < max_len) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Finger print buffer too small"); + return 0; + } if (!(cert= SSL_get_peer_certificate(ctls->ssl))) { diff --git a/libmariadb/secure/schannel.c b/libmariadb/secure/schannel.c index a0f94903..a6617d2e 100644 --- a/libmariadb/secure/schannel.c +++ b/libmariadb/secure/schannel.c @@ -32,6 +32,8 @@ char tls_library_version[] = "Schannel"; #define PROT_TLS1_2 4 #define PROT_TLS1_3 8 +unsigned int ma_set_tls_x509_info(MARIADB_TLS *ctls); + static struct { DWORD cipher_id; @@ -457,6 +459,7 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) goto end; } + ma_set_tls_x509_info(ctls); rc = 0; end: @@ -511,6 +514,8 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) DeleteSecurityContext(&sctx->hCtxt); } LocalFree(sctx); + LocalFree(ctls->cert_info.issuer); + LocalFree(ctls->cert_info.subject); return 0; } /* }}} */ @@ -551,6 +556,60 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls) return cipher_name(&CipherInfo); } +unsigned char *ma_cert_blob_to_str(PCERT_NAME_BLOB cnblob) +{ + DWORD type= CERT_X500_NAME_STR; + DWORD size= CertNameToStrA(X509_ASN_ENCODING, cnblob, type, NULL, 0); + char *str= NULL; + + if (!size) + return NULL; + + str= (char *)LocalAlloc(LMEM_ZEROINIT,size); + CertNameToStrA(X509_ASN_ENCODING, cnblob, type, str, size); + return str; +} + +static void ma_systime_to_tm(SYSTEMTIME sys_tm, struct tm *tm) +{ + memset(tm, 0, sizeof(struct tm)); + tm->tm_year= sys_tm.wYear - 1900; + tm->tm_mon= sys_tm.wMonth - 1; + tm->tm_mday= sys_tm.wDay; + tm->tm_hour = sys_tm.wHour; + tm->tm_min = sys_tm.wMinute; +} + +unsigned int ma_set_tls_x509_info(MARIADB_TLS *ctls) +{ + PCCERT_CONTEXT pCertCtx= NULL; + SC_CTX *sctx= (SC_CTX *)ctls->ssl; + PCERT_INFO pci= NULL; + DWORD size´= 0; + SYSTEMTIME tm; + char fp[33]; + + if (QueryContextAttributes(&sctx->hCtxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pCertCtx) != SEC_E_OK) + return 1; + + pci= pCertCtx->pCertInfo; + + ctls->cert_info.version= pci->dwVersion; + ctls->cert_info.subject = ma_cert_blob_to_str(&pci->Subject); + ctls->cert_info.issuer = ma_cert_blob_to_str(&pci->Issuer); + + FileTimeToSystemTime(&pci->NotBefore, &tm); + ma_systime_to_tm(tm, &ctls->cert_info.not_before); + FileTimeToSystemTime(&pci->NotAfter, &tm); + ma_systime_to_tm(tm, &ctls->cert_info.not_after); + + ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33); + mysql_hex_string(ctls->cert_info.fingerprint, fp, 32); + + return 0; +} + + unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, uint hash_type, char *fp, unsigned int len) { MA_HASH_CTX* hash_ctx; diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index 761e6394..6c8ca17b 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -1369,6 +1369,7 @@ static int test_conc276(MYSQL *unused __attribute__((unused))) MYSQL *mysql= mysql_init(NULL); int rc; my_bool val= 1; + MARIADB_X509_INFO *info; mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(mysql, MYSQL_OPT_RECONNECT, &val); @@ -1380,7 +1381,15 @@ static int test_conc276(MYSQL *unused __attribute__((unused))) return FAIL; } diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); + diag("subject: %s", info->subject); + diag("issuer: %s", info->issuer); + diag("fingerprint: %s", info->fingerprint); + diag("not before : %04d.%02d.%02d", info->not_before.tm_year + 1900, + info->not_before.tm_mon + 1, info->not_before.tm_mday); + diag("not after : %04d.%02d.%02d", info->not_after.tm_year + 1900, + info->not_after.tm_mon + 1, info->not_after.tm_mday); rc= mariadb_reconnect(mysql); check_mysql_rc(rc, mysql); @@ -2309,7 +2318,49 @@ static int test_conc632(MYSQL *my __attribute__((unused))) return OK; } +static int test_x509(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql1, *mysql2; + my_bool val= 1; + my_bool verify= 0; + char fp[65]; + MARIADB_X509_INFO *info; + + mysql1= mysql_init(NULL); + mysql2= mysql_init(NULL); + + mysql_options(mysql1, MYSQL_OPT_SSL_ENFORCE, &val); + mysql_options(mysql2, MYSQL_OPT_SSL_ENFORCE, &val); + + mysql_options(mysql1, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + if (!(my_test_connect(mysql1, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + return FAIL; + } + mariadb_get_infov(mysql1, MARIADB_TLS_PEER_CERT_INFO, &info); + memset(fp, 0, 65); + diag("fingerprint: %s", info->fingerprint); + mysql_options(mysql2, MARIADB_OPT_TLS_PEER_FP, info->fingerprint); + if (!(my_test_connect(mysql2, hostname, username, + password, schema, port, + socketname, 0))) + { + diag("connection failed"); + return FAIL; + } + mariadb_get_infov(mysql2, MARIADB_TLS_PEER_CERT_INFO, &info); + FAIL_IF(info->verify_mode != MARIADB_VERIFY_FINGERPRINT, "Fingerprint verification expected"); + + mysql_close(mysql1); + mysql_close(mysql2); + return OK; +} + struct my_tests_st my_tests[] = { + {"test_x509", test_x509, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc632", test_conc632, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_status_callback", test_status_callback, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL}, From a63b82619519101ec8520e5d7297eba21aea6674 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 30 Apr 2024 11:06:09 +0200 Subject: [PATCH 23/34] test fix: Always specify fingerprint for TLS connections --- unittest/libmariadb/my_test.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index f6a2c42c..7c23c91a 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -224,6 +224,7 @@ MYSQL *my_test_connect(MYSQL *mysql, static const char *schema = 0; static char *hostname = 0; static char *password = 0; +static char fingerprint[65]; static unsigned int port = 0; static unsigned int ssl_port = 0; static char *socketname = 0; @@ -656,7 +657,9 @@ MYSQL *my_test_connect(MYSQL *mysql, unsigned long clientflag) { if (force_tls) - mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &force_tls); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &force_tls); + if (fingerprint[0]) + mysql_options(mysql, MARIADB_OPT_SSL_FP, fingerprint); if (!mysql_real_connect(mysql, host, user, passwd, db, port, unix_socket, clientflag)) { diag("error: %s", mysql_error(mysql)); @@ -677,6 +680,8 @@ MYSQL *my_test_connect(MYSQL *mysql, void run_tests(struct my_tests_st *test) { int i, rc, total=0; MYSQL *mysql; + my_bool verify= 0; + MARIADB_X509_INFO *info; while (test[total].function) total++; @@ -684,6 +689,7 @@ void run_tests(struct my_tests_st *test) { /* display TLS stats */ mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, NULL); if (!mysql_real_connect(mysql, hostname, username, password, schema, port, socketname, 0)) @@ -691,7 +697,7 @@ void run_tests(struct my_tests_st *test) { diag("Error: %s", mysql_error(mysql)); BAIL_OUT("Can't establish TLS connection to server."); } - + fingerprint[0]= 0; if (!mysql_query(mysql, "SHOW VARIABLES LIKE '%ssl%'")) { MYSQL_RES *res; @@ -705,6 +711,9 @@ void run_tests(struct my_tests_st *test) { diag("%s: %s", row[0], row[1]); mysql_free_result(res); diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); + strcpy(fingerprint, info->fingerprint); + diag("Peer certificate fingerprint: %s", fingerprint); diag("--------------------"); } mysql_close(mysql); From c6fa37300ac457e83effd61200a3278555ac5ca4 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 30 Apr 2024 11:33:04 +0200 Subject: [PATCH 24/34] Fix SKIP_TLS macro (unittest) Since in 3.4 we use TLS connections by default, so checking force_tls is not enough. We also need to check if fingerprint was set. --- unittest/libmariadb/my_test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index 7c23c91a..8313a7d7 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -76,7 +76,7 @@ if (IS_SKYSQL(hostname)) \ #endif #define SKIP_TLS \ -if (force_tls)\ +if (force_tls || fingerprint[0])\ {\ diag("Test doesn't work with TLS");\ return SKIP;\ From bf0d299ae431d010e23f2457373616a6d3f8514e Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 30 Apr 2024 12:04:15 +0200 Subject: [PATCH 25/34] Text fix: Avoid crash in non TLS connections --- unittest/libmariadb/my_test.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/unittest/libmariadb/my_test.h b/unittest/libmariadb/my_test.h index 8313a7d7..3184b80d 100644 --- a/unittest/libmariadb/my_test.h +++ b/unittest/libmariadb/my_test.h @@ -681,7 +681,7 @@ void run_tests(struct my_tests_st *test) { int i, rc, total=0; MYSQL *mysql; my_bool verify= 0; - MARIADB_X509_INFO *info; + MARIADB_X509_INFO *info= NULL; while (test[total].function) total++; @@ -710,11 +710,15 @@ void run_tests(struct my_tests_st *test) { while ((row= mysql_fetch_row(res))) diag("%s: %s", row[0], row[1]); mysql_free_result(res); - diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); + if (mysql_get_ssl_cipher(mysql)) + diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); - strcpy(fingerprint, info->fingerprint); - diag("Peer certificate fingerprint: %s", fingerprint); - diag("--------------------"); + if (info) + { + strcpy(fingerprint, info->fingerprint); + diag("Peer certificate fingerprint: %s", fingerprint); + diag("--------------------"); + } } mysql_close(mysql); From 3652e503b766e6ba8db957e11d1421cf4f3b1bb1 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 30 Apr 2024 13:22:38 +0200 Subject: [PATCH 26/34] Disable test for MAXSCALE --- unittest/libmariadb/connection.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index 6c8ca17b..49f3abec 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -1369,7 +1369,6 @@ static int test_conc276(MYSQL *unused __attribute__((unused))) MYSQL *mysql= mysql_init(NULL); int rc; my_bool val= 1; - MARIADB_X509_INFO *info; mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val); mysql_options(mysql, MYSQL_OPT_RECONNECT, &val); @@ -1381,15 +1380,6 @@ static int test_conc276(MYSQL *unused __attribute__((unused))) return FAIL; } diag("Cipher in use: %s", mysql_get_ssl_cipher(mysql)); - mariadb_get_infov(mysql, MARIADB_TLS_PEER_CERT_INFO, &info); - - diag("subject: %s", info->subject); - diag("issuer: %s", info->issuer); - diag("fingerprint: %s", info->fingerprint); - diag("not before : %04d.%02d.%02d", info->not_before.tm_year + 1900, - info->not_before.tm_mon + 1, info->not_before.tm_mday); - diag("not after : %04d.%02d.%02d", info->not_after.tm_year + 1900, - info->not_after.tm_mon + 1, info->not_after.tm_mday); rc= mariadb_reconnect(mysql); check_mysql_rc(rc, mysql); @@ -2326,6 +2316,8 @@ static int test_x509(MYSQL *my __attribute__((unused))) char fp[65]; MARIADB_X509_INFO *info; + SKIP_MAXSCALE; + mysql1= mysql_init(NULL); mysql2= mysql_init(NULL); From 3f47c15241bdf75056ebfd4050b4daf82b251c8a Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 6 May 2024 14:31:49 +0200 Subject: [PATCH 27/34] Added missing support for restricted_auth in conf files --- libmariadb/mariadb_lib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 52f96889..e2c2d0a7 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -711,6 +711,7 @@ struct st_default_options mariadb_defaults[] = {{MARIADB_OPT_TLS_PASSPHRASE}, MARIADB_OPTION_STR, "tls-passphrase"}, {{MYSQL_OPT_SSL_ENFORCE}, MARIADB_OPTION_BOOL, "tls-enforce"}, {{MYSQL_OPT_SSL_VERIFY_SERVER_CERT}, MARIADB_OPTION_BOOL,"tls-verify-peer"}, + {{MARIADB_OPT_RESTRICTED_AUTH}, MARIADB_OPTION_STR, "restricted-auth"}, {{0}, 0, NULL} }; From ba137a4f5c49966799469bc250cdc425631a5a9d Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Mon, 6 May 2024 14:32:31 +0200 Subject: [PATCH 28/34] Exclude server side cursors when checking for pending results --- libmariadb/mariadb_stmt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index cbf88eed..cb37359e 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -136,7 +136,8 @@ static my_bool madb_have_pending_results(MYSQL_STMT *stmt) for (;li_stmt;li_stmt= li_stmt->next) { MYSQL_STMT *s= (MYSQL_STMT *)li_stmt->data; - if (s != stmt && s->state == MYSQL_STMT_WAITING_USE_OR_STORE) + if (s != stmt && s->state == MYSQL_STMT_WAITING_USE_OR_STORE && + !(s->flags & CURSOR_TYPE_READ_ONLY)) return 1; } return 0; From 989bd885460fcc68ddbe60d4644c57199be9b010 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 7 May 2024 07:23:32 +0200 Subject: [PATCH 29/34] Fix compile error (misleading-indentation) --- libmariadb/ma_pvio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 09bc1810..e18af51e 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -605,8 +605,8 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) (pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0]))) { if (ma_pvio_tls_check_fp(pvio->ctls, - pvio->mysql->options.extension->tls_fp, - pvio->mysql->options.extension->tls_fp_list)) + pvio->mysql->options.extension->tls_fp, + pvio->mysql->options.extension->tls_fp_list)) return 1; pvio->ctls->cert_info.verify_mode= MARIADB_VERIFY_FINGERPRINT; reset_tls_self_signed_error(pvio->mysql); // validated From dc1606781f8f86e4d9251bcb842c3d827bf51184 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 7 May 2024 11:40:29 +0200 Subject: [PATCH 30/34] Fix build for OpenSSL < 1.1 --- libmariadb/secure/openssl.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 87830c08..ce98d2ae 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -30,6 +30,9 @@ #include #include #include +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +#endif #if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C) #include @@ -534,15 +537,34 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) if ((cert= SSL_get_peer_certificate(ssl))) { char fp[33]; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L const ASN1_TIME *not_before= X509_get0_notBefore(cert), *not_after= X509_get0_notAfter(cert); + ASN1_TIME_to_tm(not_before, (struct tm *)&ctls->cert_info.not_before); + ASN1_TIME_to_tm(not_after, (struct tm *)&ctls->cert_info.not_after); +#else +#ifdef WIN32 +#define time64_t __time64_t +#define gmtime64 _gmtime64 +#endif + ASN1_TIME *not_before= X509_getm_notBefore(cert), + *not_after= X509_getm_notAfter(cert); + time64_t now, from, to; + int pday, psec; + /* ANS1_TIME_diff returns days and seconds between now and the + specified ASN1_TIME */ + _time64(&now); + ASN1_TIME_diff(&pday, &psec, (const ASN1_TIME *)not_before, NULL); + from= now - (pday * 86400 + psec); + memcpy(&ctls->cert_info.not_before, gmtime64(&from), sizeof(struct tm)); + ASN1_TIME_diff(&pday, &psec, NULL, (const ASN1_TIME *)not_before); + to= now + (pday * 86400 + psec); + memcpy(&ctls->cert_info.not_before, gmtime64(&to), sizeof(struct tm)); +#endif ctls->cert_info.subject= X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); ctls->cert_info.issuer= X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); ctls->cert_info.version= X509_get_version(cert) + 1; - ASN1_TIME_to_tm(not_before, (struct tm *)&ctls->cert_info.not_before); - ASN1_TIME_to_tm(not_after, (struct tm *)&ctls->cert_info.not_after); - ma_tls_get_finger_print(ctls, MA_HASH_SHA256, fp, 33); mysql_hex_string(ctls->cert_info.fingerprint, fp, 32); } From 3228ed2ea3ef65c8258c8b60816a82dddfb9e337 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Tue, 7 May 2024 15:13:15 +0200 Subject: [PATCH 31/34] Fix copy/paste error --- libmariadb/secure/openssl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index ce98d2ae..78fca1e0 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -557,9 +557,9 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) ASN1_TIME_diff(&pday, &psec, (const ASN1_TIME *)not_before, NULL); from= now - (pday * 86400 + psec); memcpy(&ctls->cert_info.not_before, gmtime64(&from), sizeof(struct tm)); - ASN1_TIME_diff(&pday, &psec, NULL, (const ASN1_TIME *)not_before); + ASN1_TIME_diff(&pday, &psec, NULL, (const ASN1_TIME *)not_after); to= now + (pday * 86400 + psec); - memcpy(&ctls->cert_info.not_before, gmtime64(&to), sizeof(struct tm)); + memcpy(&ctls->cert_info.not_after, gmtime64(&to), sizeof(struct tm)); #endif ctls->cert_info.subject= X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); ctls->cert_info.issuer= X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); From 55fe56fa42679e698036fcbf77fe4761feb56723 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 22 Feb 2024 09:03:51 +0100 Subject: [PATCH 32/34] Fix for CONC-505: Don't allow to specify unsupported client flags (like CLIENT_DEPRECATE_EOF) as client flag in mysql_real_connect api function. --- include/errmsg.h | 3 ++- include/mariadb_com.h | 6 ++++++ libmariadb/ma_errmsg.c | 4 ++-- libmariadb/mariadb_lib.c | 8 ++++++++ unittest/libmariadb/connection.c | 26 ++++++++++++++++++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/include/errmsg.h b/include/errmsg.h index 0d8ddcaf..0e6d24e0 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -114,7 +114,8 @@ extern const char *mariadb_client_errors[]; /* Error messages */ #define CR_BINLOG_ERROR 5021 #define CR_BINLOG_INVALID_FILE 5022 #define CR_BINLOG_SEMI_SYNC_ERROR 5023 -#define CR_STMT_NO_RESULT 5024 +#define CR_INVALID_CLIENT_FLAG 5024 +#define CR_STMT_NO_RESULT 5025 /* Always last, if you add new error codes please update the value for CR_MARIADB_LAST_ERROR */ diff --git a/include/mariadb_com.h b/include/mariadb_com.h index 44111000..f904f98d 100644 --- a/include/mariadb_com.h +++ b/include/mariadb_com.h @@ -213,6 +213,12 @@ enum enum_server_command CLIENT_PLUGIN_AUTH |\ CLIENT_SESSION_TRACKING |\ CLIENT_CONNECT_ATTRS) +#define CLIENT_ALLOWED_FLAGS ((CLIENT_SUPPORTED_FLAGS |\ + CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |\ + CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS |\ + CLIENT_ZSTD_COMPRESSION |\ + CLIENT_PS_MULTI_RESULTS |\ + CLIENT_REMEMBER_OPTIONS) & ~CLIENT_MYSQL) #define CLIENT_CAPABILITIES (CLIENT_MYSQL | \ CLIENT_LONG_FLAG |\ CLIENT_TRANSACTIONS |\ diff --git a/libmariadb/ma_errmsg.c b/libmariadb/ma_errmsg.c index 1fcc6d4a..775244d3 100644 --- a/libmariadb/ma_errmsg.c +++ b/libmariadb/ma_errmsg.c @@ -118,8 +118,8 @@ const char *mariadb_client_errors[] = /* 5021 */ "Binary log error (File: %.*s start_pos=%ld): %s.", /* 5022 */ "File '%s' is not a binary log file", /* 5023 */ "Semi sync request error: %s", - /* 5024 */ "Statement has no result set", - "" + /* 5024 */ "Invalid client flags (%lu) specified. Supported flags: %lu", + /* 5025 */ "Statement has no result set", }; const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0}; diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index e2c2d0a7..1db7cf34 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1444,6 +1444,14 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user, char *connection_handler= (mysql->options.extension) ? mysql->options.extension->connection_handler : 0; + if ((client_flag & CLIENT_ALLOWED_FLAGS) != client_flag) + { + my_set_error(mysql, CR_INVALID_CLIENT_FLAG, SQLSTATE_UNKNOWN, + ER(CR_INVALID_CLIENT_FLAG), + client_flag, CLIENT_ALLOWED_FLAGS); + return NULL; + } + if (!mysql->methods) mysql->methods= &MARIADB_DEFAULT_METHODS; diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index 49f3abec..bd4e0e0a 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -2351,8 +2351,34 @@ static int test_x509(MYSQL *my __attribute__((unused))) return OK; } +static int test_conc505(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + +#define CLIENT_DEPRECATE_EOF (1ULL << 24) + + if (my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_DEPRECATE_EOF)) + { + diag("Error expected: Invalid client flag"); + mysql_close(mysql); + return FAIL; + } + diag("Error (expected): %s", mysql_error(mysql)); + FAIL_IF(mysql_errno(mysql) != CR_INVALID_CLIENT_FLAG, "Wrong error number"); + if (!my_test_connect(mysql, hostname, username, password, schema, port, socketname, CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + mysql_close(mysql); + return OK; +} + struct my_tests_st my_tests[] = { {"test_x509", test_x509, TEST_CONNECTION_NONE, 0, NULL, NULL}, + {"test_conc505", test_conc505, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc632", test_conc632, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_status_callback", test_status_callback, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL}, From 23be94ade252d95ab88c6bbe748fbf4283ef0169 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Thu, 22 Feb 2024 16:24:20 +0100 Subject: [PATCH 33/34] Follow up for CONC-505 Don't remove CLIENT_MYSQL from supported flags, otherwise MySQL tests will fail. --- include/mariadb_com.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mariadb_com.h b/include/mariadb_com.h index f904f98d..6e7164a3 100644 --- a/include/mariadb_com.h +++ b/include/mariadb_com.h @@ -213,12 +213,12 @@ enum enum_server_command CLIENT_PLUGIN_AUTH |\ CLIENT_SESSION_TRACKING |\ CLIENT_CONNECT_ATTRS) -#define CLIENT_ALLOWED_FLAGS ((CLIENT_SUPPORTED_FLAGS |\ +#define CLIENT_ALLOWED_FLAGS (CLIENT_SUPPORTED_FLAGS |\ CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |\ CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS |\ CLIENT_ZSTD_COMPRESSION |\ CLIENT_PS_MULTI_RESULTS |\ - CLIENT_REMEMBER_OPTIONS) & ~CLIENT_MYSQL) + CLIENT_REMEMBER_OPTIONS) #define CLIENT_CAPABILITIES (CLIENT_MYSQL | \ CLIENT_LONG_FLAG |\ CLIENT_TRANSACTIONS |\ From fc337784922882d1f0cba3e9fb9c52a9e9af03a4 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Fri, 10 May 2024 11:06:37 +0200 Subject: [PATCH 34/34] Added new utf8 general1400_as_ci collations The following collations from 11.5 were added: 579: utf8mb3_general1400_as_ci 611: utf8mb4_general1400_as_ci --- libmariadb/ma_charset.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libmariadb/ma_charset.c b/libmariadb/ma_charset.c index f6ed6f80..10c191cd 100644 --- a/libmariadb/ma_charset.c +++ b/libmariadb/ma_charset.c @@ -830,9 +830,11 @@ const MARIADB_CHARSET_INFO mariadb_compiled_charsets[] = { 576, 1, UTF8_MB3, UTF8_MB3"_croatian_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/ { 577, 1, UTF8_MB3, UTF8_MB3"_myanmar_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/ { 578, 1, UTF8_MB3, UTF8_MB3"_thai_520_w2", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/ + { 579, 1, UTF8_MB3, UTF8_MB3"_general1400_as_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, /*MDB*/ { 608, 1, UTF8_MB4, UTF8_MB4"_croatian_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, { 609, 1, UTF8_MB4, UTF8_MB4"_myanmar_ci", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, { 610, 1, UTF8_MB4, UTF8_MB4"_thai_520_w2", "", 65001, "UTF-8", 1, 4, mysql_mbcharlen_utf8, check_mb_utf8_valid}, + { 611, 1, UTF8_MB4, UTF8_MB4"_general1400_as_ci", "", 65001, "UTF-8", 1, 3, mysql_mbcharlen_utf8, check_mb_utf8_valid}, { 640, 1, "ucs2", "ucs2_croatian_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, { 641, 1, "ucs2", "ucs2_myanmar_ci", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2}, { 642, 1, "ucs2", "ucs2_thai_520_w2", "", 1200, "UCS2-BE", 2, 2, mysql_mbcharlen_ucs2, check_mb_ucs2},