diff --git a/include/my_bit.h b/include/my_bit.h index 64097a750ac24..7bf3c6206d9dd 100644 --- a/include/my_bit.h +++ b/include/my_bit.h @@ -17,13 +17,17 @@ #ifndef MY_BIT_INCLUDED #define MY_BIT_INCLUDED +#ifdef __cplusplus +extern "C" { +#else +#define constexpr +#endif + /* Some useful bit functions */ -C_MODE_START - -extern const uchar _my_bits_reverse_table[256]; +extern const unsigned char _my_bits_reverse_table[256]; /* @@ -43,42 +47,42 @@ extern const uchar _my_bits_reverse_table[256]; Let's return 0 for the input 0, for the code simplicity. See the 000x branch. It covers both (1<<0) and 0. */ -static inline CONSTEXPR uint my_bit_log2_hex_digit(uint8 value) +static inline constexpr unsigned int my_bit_log2_hex_digit(unsigned char value) { return value & 0x0C ? /*1100*/ (value & 0x08 ? /*1000*/ 3 : /*0100*/ 2) : /*0010*/ (value & 0x02 ? /*0010*/ 1 : /*000x*/ 0); } -static inline CONSTEXPR uint my_bit_log2_uint8(uint8 value) +static inline constexpr unsigned int my_bit_log2_uint8(unsigned char value) { - return value & 0xF0 ? my_bit_log2_hex_digit((uint8) (value >> 4)) + 4: + return value & 0xF0 ? my_bit_log2_hex_digit((unsigned char) (value >> 4)) + 4: my_bit_log2_hex_digit(value); } -static inline CONSTEXPR uint my_bit_log2_uint16(uint16 value) +static inline constexpr unsigned int my_bit_log2_uint16(unsigned short value) { - return value & 0xFF00 ? my_bit_log2_uint8((uint8) (value >> 8)) + 8 : - my_bit_log2_uint8((uint8) value); + return value & 0xFF00 ? my_bit_log2_uint8((unsigned char) (value >> 8)) + 8 : + my_bit_log2_uint8((unsigned char) value); } -static inline CONSTEXPR uint my_bit_log2_uint32(uint32 value) +static inline constexpr unsigned int my_bit_log2_uint32(unsigned int value) { return value & 0xFFFF0000UL ? - my_bit_log2_uint16((uint16) (value >> 16)) + 16 : - my_bit_log2_uint16((uint16) value); + my_bit_log2_uint16((unsigned short) (value >> 16)) + 16 : + my_bit_log2_uint16((unsigned short) value); } -static inline CONSTEXPR uint my_bit_log2_uint64(ulonglong value) +static inline constexpr unsigned int my_bit_log2_uint64(unsigned long long int value) { return value & 0xFFFFFFFF00000000ULL ? - my_bit_log2_uint32((uint32) (value >> 32)) + 32 : - my_bit_log2_uint32((uint32) value); + my_bit_log2_uint32((unsigned int) (value >> 32)) + 32 : + my_bit_log2_uint32((unsigned int) value); } -static inline CONSTEXPR uint my_bit_log2_size_t(size_t value) +static inline constexpr unsigned int my_bit_log2_size_t(size_t value) { #ifdef __cplusplus - static_assert(sizeof(size_t) <= sizeof(ulonglong), - "size_t <= ulonglong is an assumption that needs to be fixed " + static_assert(sizeof(size_t) <= sizeof(unsigned long long int), + "size_t <= unsigned long long int is an assumption that needs to be fixed " "for this architecture. Please create an issue on " "https://jira.mariadb.org"); #endif - return my_bit_log2_uint64((ulonglong) value); + return my_bit_log2_uint64((unsigned long long int) value); } @@ -91,7 +95,7 @@ Count bits in 32bit integer (Original code public domain). */ -static inline uint my_count_bits_uint32(uint32 v) +static inline unsigned int my_count_bits_uint32(unsigned int v) { v = v - ((v >> 1) & 0x55555555); v = (v & 0x33333333) + ((v >> 2) & 0x33333333); @@ -99,9 +103,9 @@ static inline uint my_count_bits_uint32(uint32 v) } -static inline uint my_count_bits(ulonglong x) +static inline unsigned int my_count_bits(unsigned long long int x) { - return my_count_bits_uint32((uint32)x) + my_count_bits_uint32((uint32)(x >> 32)); + return my_count_bits_uint32((unsigned int)x) + my_count_bits_uint32((unsigned int)(x >> 32)); } @@ -126,7 +130,7 @@ static inline uint my_count_bits(ulonglong x) Comments shows how this works with 01100000000000000000000000001011 */ -static inline uint32 my_round_up_to_next_power(uint32 v) +static inline unsigned int my_round_up_to_next_power(unsigned int v) { v--; /* 01100000000000000000000000001010 */ v|= v >> 1; /* 01110000000000000000000000001111 */ @@ -137,9 +141,9 @@ static inline uint32 my_round_up_to_next_power(uint32 v) return v+1; /* 10000000000000000000000000000000 */ } -static inline uint32 my_clear_highest_bit(uint32 v) +static inline unsigned int my_clear_highest_bit(unsigned int v) { - uint32 w=v >> 1; + unsigned int w=v >> 1; w|= w >> 1; w|= w >> 2; w|= w >> 4; @@ -148,34 +152,34 @@ static inline uint32 my_clear_highest_bit(uint32 v) return v & w; } -static inline uint32 my_reverse_bits(uint32 key) +static inline unsigned int my_reverse_bits(unsigned int key) { return - ((uint32)_my_bits_reverse_table[ key & 255] << 24) | - ((uint32)_my_bits_reverse_table[(key>> 8) & 255] << 16) | - ((uint32)_my_bits_reverse_table[(key>>16) & 255] << 8) | - (uint32)_my_bits_reverse_table[(key>>24) ]; + ((unsigned int)_my_bits_reverse_table[ key & 255] << 24) | + ((unsigned int)_my_bits_reverse_table[(key>> 8) & 255] << 16) | + ((unsigned int)_my_bits_reverse_table[(key>>16) & 255] << 8) | + (unsigned int)_my_bits_reverse_table[(key>>24) ]; } /* a number with the n lowest bits set an overflow-safe version of (1 << n) - 1 */ -static inline uint64 my_set_bits(int n) +static inline unsigned long long int my_set_bits(int n) { return (((1ULL << (n - 1)) - 1) << 1) | 1; } /* Create a mask of the significant bits for the last byte (1,3,7,..255) */ -static inline uchar last_byte_mask(uint bits) +static inline unsigned char last_byte_mask(unsigned int bits) { /* Get the number of used bits-1 (0..7) in the last byte */ unsigned int const used = (bits - 1U) & 7U; /* Return bitmask for the significant bits */ - return (uchar) ((2U << used) - 1); + return (unsigned char) ((2U << used) - 1); } -static inline uint my_bits_in_bytes(uint n) +static inline unsigned int my_bits_in_bytes(unsigned int n) { return ((n + 7) / 8); } @@ -188,7 +192,7 @@ static inline uint my_bits_in_bytes(uint n) Find the position of the first(least significant) bit set in the argument. Returns 64 if the argument was 0. */ -static inline uint my_find_first_bit(ulonglong n) +static inline unsigned int my_find_first_bit(unsigned long long int n) { if(!n) return 64; @@ -197,9 +201,9 @@ static inline uint my_find_first_bit(ulonglong n) #elif defined(_MSC_VER) #if defined(_M_IX86) unsigned long bit; - if( _BitScanForward(&bit, (uint)n)) + if( _BitScanForward(&bit, (unsigned int)n)) return bit; - _BitScanForward(&bit, (uint)(n>>32)); + _BitScanForward(&bit, (unsigned int)(n>>32)); return bit + 32; #else unsigned long bit; @@ -208,18 +212,23 @@ static inline uint my_find_first_bit(ulonglong n) #endif #else /* Generic case */ - uint shift= 0; - static const uchar last_bit[16] = { 32, 0, 1, 0, + unsigned int shift= 0; + static const unsigned char last_bit[16] = { 32, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; - uint bit; + unsigned int bit; while ((bit = last_bit[(n >> shift) & 0xF]) == 32) shift+= 4; return shift+bit; #endif } -C_MODE_END + +#ifdef __cplusplus +} +#else +#undef constexpr +#endif /* The helper function my_nlz(x) calculates the number of leading zeros @@ -289,4 +298,4 @@ inline unsigned int my_nlz (unsigned long long x) } #endif -#endif /* MY_BIT_INCLUDED */ +#endif /* MY_BIT_INCLUDED */ \ No newline at end of file diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index 5c2a7311dd27e..b4d1c1c6b54d8 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -84,7 +84,7 @@ typedef struct st_mysql_xid MYSQL_XID; #define MYSQL_PLUGIN_INTERFACE_VERSION 0x0105 /** MariaDB plugin interface version */ -#define MARIA_PLUGIN_INTERFACE_VERSION 0x010f +#define MARIA_PLUGIN_INTERFACE_VERSION 0x0110 /* The allowable types of plugins diff --git a/include/mysql/plugin_audit.h.pp b/include/mysql/plugin_audit.h.pp index 46001f62f48e0..d0be440ecf759 100644 --- a/include/mysql/plugin_audit.h.pp +++ b/include/mysql/plugin_audit.h.pp @@ -337,6 +337,12 @@ void my_sha512_result(void *context, unsigned char *digest); } extern "C" { +extern struct thd_service_st { + THD* (*get_current_thd)(void); +} *thd_service; +THD* get_current_thd(); +} +extern "C" { struct st_mysql_const_lex_string { const char *str; diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp index 51802b36f1dc2..6a68eb9723ff4 100644 --- a/include/mysql/plugin_auth.h.pp +++ b/include/mysql/plugin_auth.h.pp @@ -337,6 +337,12 @@ void my_sha512_result(void *context, unsigned char *digest); } extern "C" { +extern struct thd_service_st { + THD* (*get_current_thd)(void); +} *thd_service; +THD* get_current_thd(); +} +extern "C" { struct st_mysql_const_lex_string { const char *str; diff --git a/include/mysql/plugin_data_type.h.pp b/include/mysql/plugin_data_type.h.pp index e28f5fd8c110c..5b4d8b153e97c 100644 --- a/include/mysql/plugin_data_type.h.pp +++ b/include/mysql/plugin_data_type.h.pp @@ -337,6 +337,12 @@ void my_sha512_result(void *context, unsigned char *digest); } extern "C" { +extern struct thd_service_st { + THD* (*get_current_thd)(void); +} *thd_service; +THD* get_current_thd(); +} +extern "C" { struct st_mysql_const_lex_string { const char *str; diff --git a/include/mysql/plugin_encryption.h.pp b/include/mysql/plugin_encryption.h.pp index d744a3880f26d..9eefb757cb7c4 100644 --- a/include/mysql/plugin_encryption.h.pp +++ b/include/mysql/plugin_encryption.h.pp @@ -337,6 +337,12 @@ void my_sha512_result(void *context, unsigned char *digest); } extern "C" { +extern struct thd_service_st { + THD* (*get_current_thd)(void); +} *thd_service; +THD* get_current_thd(); +} +extern "C" { struct st_mysql_const_lex_string { const char *str; diff --git a/include/mysql/plugin_ftparser.h.pp b/include/mysql/plugin_ftparser.h.pp index 1245e8e6b7786..cead92423a55f 100644 --- a/include/mysql/plugin_ftparser.h.pp +++ b/include/mysql/plugin_ftparser.h.pp @@ -337,6 +337,12 @@ void my_sha512_result(void *context, unsigned char *digest); } extern "C" { +extern struct thd_service_st { + THD* (*get_current_thd)(void); +} *thd_service; +THD* get_current_thd(); +} +extern "C" { struct st_mysql_const_lex_string { const char *str; diff --git a/include/mysql/plugin_function.h.pp b/include/mysql/plugin_function.h.pp index dc231c9147154..c6cfc6a35cec8 100644 --- a/include/mysql/plugin_function.h.pp +++ b/include/mysql/plugin_function.h.pp @@ -337,6 +337,12 @@ void my_sha512_result(void *context, unsigned char *digest); } extern "C" { +extern struct thd_service_st { + THD* (*get_current_thd)(void); +} *thd_service; +THD* get_current_thd(); +} +extern "C" { struct st_mysql_const_lex_string { const char *str; diff --git a/include/mysql/plugin_password_validation.h.pp b/include/mysql/plugin_password_validation.h.pp index 16b33fe266c49..c7d00ec2e6aca 100644 --- a/include/mysql/plugin_password_validation.h.pp +++ b/include/mysql/plugin_password_validation.h.pp @@ -337,6 +337,12 @@ void my_sha512_result(void *context, unsigned char *digest); } extern "C" { +extern struct thd_service_st { + THD* (*get_current_thd)(void); +} *thd_service; +THD* get_current_thd(); +} +extern "C" { struct st_mysql_const_lex_string { const char *str; diff --git a/include/mysql/service_thd.h b/include/mysql/service_thd.h new file mode 100644 index 0000000000000..5be9cb8b81ed3 --- /dev/null +++ b/include/mysql/service_thd.h @@ -0,0 +1,46 @@ +#ifndef MYSQL_SERVICE_THD_INCLUDED +/* Copyright (c) 2026, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file include/mysql/service_thd.h + This service provides functions for plugins and storage engines to access + current thd. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct thd_service_st { + MYSQL_THD (*get_current_thd)(void); +} *thd_service; + +#ifdef MYSQL_DYNAMIC_PLUGIN +# define get_current_thd() thd_service->get_current_thd() +#else +/** + current thd accessor + @return pointer to current thd +*/ +MYSQL_THD get_current_thd(); +#endif + +#ifdef __cplusplus +} +#endif + +#define MYSQL_SERVICE_THD_INCLUDED +#endif diff --git a/include/mysql/services.h b/include/mysql/services.h index 94f7bb3b2da14..c5607d424b2ba 100644 --- a/include/mysql/services.h +++ b/include/mysql/services.h @@ -32,6 +32,7 @@ extern "C" { #include #include #include +#include #include #include #include diff --git a/include/service_versions.h b/include/service_versions.h index 45cad4d86ae06..72f5218c9cc00 100644 --- a/include/service_versions.h +++ b/include/service_versions.h @@ -41,6 +41,7 @@ #define VERSION_thd_specifics 0x0100 #define VERSION_thd_timezone 0x0100 #define VERSION_thd_wait 0x0100 +#define VERSION_thd 0x0100 #define VERSION_wsrep 0x0500 #define VERSION_json 0x0100 #define VERSION_sql_service 0x0102 diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt index 37c24646791f7..7cd4abeda3dd7 100644 --- a/libservices/CMakeLists.txt +++ b/libservices/CMakeLists.txt @@ -35,6 +35,7 @@ SET(MYSQLSERVICES_SOURCES thd_error_context_service.c thd_rnd_service.c thd_specifics_service.c + thd_service.c thd_timezone_service.c thd_wait_service.c wsrep_service.c diff --git a/libservices/thd_service.c b/libservices/thd_service.c new file mode 100644 index 0000000000000..ddd9fb17f7843 --- /dev/null +++ b/libservices/thd_service.c @@ -0,0 +1,17 @@ +/* Copyright (C) 2026 MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include +SERVICE_VERSION thd_service= (void *) VERSION_thd; diff --git a/mysql-test/main/handlersocket.result b/mysql-test/main/handlersocket.result index 38027afc4147c..c2f883ea8c83f 100644 --- a/mysql-test/main/handlersocket.result +++ b/mysql-test/main/handlersocket.result @@ -5,7 +5,7 @@ plugin_version 1.0 plugin_status ACTIVE plugin_type DAEMON plugin_library handlersocket.so -plugin_library_version 1.15 +plugin_library_version 1.16 plugin_author higuchi dot akira at dena dot jp plugin_description Direct access into InnoDB plugin_license BSD diff --git a/mysql-test/main/plugin.result b/mysql-test/main/plugin.result index 1df9c47cf1f33..e22d4eacf9280 100644 --- a/mysql-test/main/plugin.result +++ b/mysql-test/main/plugin.result @@ -14,7 +14,7 @@ PLUGIN_STATUS ACTIVE PLUGIN_TYPE STORAGE ENGINE PLUGIN_TYPE_VERSION # PLUGIN_LIBRARY ha_example.so -PLUGIN_LIBRARY_VERSION 1.15 +PLUGIN_LIBRARY_VERSION 1.16 PLUGIN_AUTHOR Brian Aker, MySQL AB PLUGIN_DESCRIPTION Example storage engine PLUGIN_LICENSE GPL @@ -27,7 +27,7 @@ PLUGIN_STATUS ACTIVE PLUGIN_TYPE DAEMON PLUGIN_TYPE_VERSION # PLUGIN_LIBRARY ha_example.so -PLUGIN_LIBRARY_VERSION 1.15 +PLUGIN_LIBRARY_VERSION 1.16 PLUGIN_AUTHOR Sergei Golubchik PLUGIN_DESCRIPTION Unusable Daemon PLUGIN_LICENSE GPL @@ -70,7 +70,7 @@ PLUGIN_STATUS DELETED PLUGIN_TYPE STORAGE ENGINE PLUGIN_TYPE_VERSION # PLUGIN_LIBRARY ha_example.so -PLUGIN_LIBRARY_VERSION 1.15 +PLUGIN_LIBRARY_VERSION 1.16 PLUGIN_AUTHOR Brian Aker, MySQL AB PLUGIN_DESCRIPTION Example storage engine PLUGIN_LICENSE GPL diff --git a/mysql-test/suite/plugins/r/auth_ed25519.result b/mysql-test/suite/plugins/r/auth_ed25519.result index 061a18a8dc512..436d99e16a76c 100644 --- a/mysql-test/suite/plugins/r/auth_ed25519.result +++ b/mysql-test/suite/plugins/r/auth_ed25519.result @@ -27,7 +27,7 @@ PLUGIN_STATUS ACTIVE PLUGIN_TYPE AUTHENTICATION PLUGIN_TYPE_VERSION 2.3 PLUGIN_LIBRARY auth_ed25519.so -PLUGIN_LIBRARY_VERSION 1.15 +PLUGIN_LIBRARY_VERSION 1.16 PLUGIN_AUTHOR Sergei Golubchik PLUGIN_DESCRIPTION Elliptic curve ED25519 based authentication PLUGIN_LICENSE GPL diff --git a/mysql-test/suite/plugins/r/cracklib_password_check.result b/mysql-test/suite/plugins/r/cracklib_password_check.result index 192d41d2ccf01..248beeaaf4d74 100644 --- a/mysql-test/suite/plugins/r/cracklib_password_check.result +++ b/mysql-test/suite/plugins/r/cracklib_password_check.result @@ -6,7 +6,7 @@ PLUGIN_STATUS ACTIVE PLUGIN_TYPE PASSWORD VALIDATION PLUGIN_TYPE_VERSION 1.1 PLUGIN_LIBRARY cracklib_password_check.so -PLUGIN_LIBRARY_VERSION 1.15 +PLUGIN_LIBRARY_VERSION 1.16 PLUGIN_AUTHOR Sergei Golubchik PLUGIN_DESCRIPTION Password validation via CrackLib PLUGIN_LICENSE GPL diff --git a/mysql-test/suite/plugins/r/parsec.result b/mysql-test/suite/plugins/r/parsec.result index a7472d0e6668b..c7904c1bddf67 100644 --- a/mysql-test/suite/plugins/r/parsec.result +++ b/mysql-test/suite/plugins/r/parsec.result @@ -43,3 +43,119 @@ connect con5, localhost, test2, "wrong_pwd"; ERROR 28000: Access denied for user 'test2'@'localhost' (using password: NO) connection default; drop user test2@'%'; +# +# MDEV-35254 PARSEC plugin should allow DBAs to specify number of iterations +# +# Basic visibility and default value +set @init_value := @@global.parsec_iterations; +# Valid power-of-two values should be accepted +set global parsec_iterations = 1024; +select @@global.parsec_iterations; +@@global.parsec_iterations +1024 +set global parsec_iterations = 262144; +select @@global.parsec_iterations; +@@global.parsec_iterations +262144 +# Values should be rounded UP to next power of two +# 5000 -> 8192 +set global parsec_iterations = 5000; +Warnings: +Warning 1231 parsec_iterations rounded up to 8192 +select @@global.parsec_iterations; +@@global.parsec_iterations +8192 +# 131073 -> 262144 +set global parsec_iterations = 131073; +Warnings: +Warning 1231 parsec_iterations rounded up to 262144 +select @@global.parsec_iterations; +@@global.parsec_iterations +262144 +# Lower bound enforcement +set global parsec_iterations = 512; +Warnings: +Warning 1292 Truncated incorrect parsec_iterations value: '512' +select @@global.parsec_iterations; +@@global.parsec_iterations +1024 +# Upper bound enforcement +set global parsec_iterations = 2147483649; +Warnings: +Warning 1292 Truncated incorrect parsec_iterations value: '2147483649' +select @@global.parsec_iterations; +@@global.parsec_iterations +1073741824 +# Session variable access +set session parsec_iterations = 1024; +select @@session.parsec_iterations; +@@session.parsec_iterations +1024 +# parsec_iterations should not get accidently mutated during user creation +set @iter_before_global := @@global.parsec_iterations; +set @iter_before_session := @@session.parsec_iterations; +create user t_iter@'%' identified via parsec using password('pwd'); +select @@global.parsec_iterations = @iter_before_global; +@@global.parsec_iterations = @iter_before_global +1 +select @@session.parsec_iterations = @iter_before_session; +@@session.parsec_iterations = @iter_before_session +1 +drop user t_iter@'%'; +# Verify the iteration character encoded in the password hash is correct +# --- 1024 iterations: expect iter char '0' --- +set session parsec_iterations = 1024; +create user t_iter@'%' identified via parsec using password('pwd'); +select left(json_value(Priv, '$.authentication_string'), 2) from mysql.global_priv where user='t_iter'; +left(json_value(Priv, '$.authentication_string'), 2) +P0 +drop user t_iter@'%'; +# --- 8192 iterations: expect iter char '3' --- +set session parsec_iterations = 8192; +create user t_iter@'%' identified via parsec using password('pwd'); +select left(json_value(Priv, '$.authentication_string'), 2) from mysql.global_priv where user='t_iter'; +left(json_value(Priv, '$.authentication_string'), 2) +P3 +drop user t_iter@'%'; +# --- 262147 iterations: expect iter char '9' --- +set session parsec_iterations = 262147; +Warnings: +Warning 1231 parsec_iterations rounded up to 524288 +create user t_iter@'%' identified via parsec using password('pwd'); +select left(json_value(Priv, '$.authentication_string'), 2) from mysql.global_priv where user='t_iter'; +left(json_value(Priv, '$.authentication_string'), 2) +P9 +drop user t_iter@'%'; +set global parsec_iterations = @init_value; +# connection test +set session parsec_iterations = 4096; +create user test1@'%' identified via parsec using password('pwd'); +show grants for test1@'%'; +Grants for test1@% +GRANT USAGE ON *.* TO `test1`@`%` IDENTIFIED VIA parsec USING 'P2:salt:password' +connect con1, localhost, test1, pwd; +select 1, user(), current_user(); +1 user() current_user() +1 test1@localhost test1@% +disconnect con1; +connection default; +drop user test1@'%'; +select * from information_schema.system_variables where variable_name like 'parsec%'; +VARIABLE_NAME PARSEC_ITERATIONS +SESSION_VALUE 4096 +GLOBAL_VALUE 1024 +GLOBAL_VALUE_ORIGIN SQL +DEFAULT_VALUE 1024 +VARIABLE_SCOPE SESSION +VARIABLE_TYPE INT UNSIGNED +VARIABLE_COMMENT Number of iterations to be used when generating the key corresponding to the password. Will be rounded up to a power of two +NUMERIC_MIN_VALUE 1024 +NUMERIC_MAX_VALUE 1073741824 +NUMERIC_BLOCK_SIZE 1 +ENUM_VALUE_LIST NULL +READ_ONLY NO +COMMAND_LINE_ARGUMENT REQUIRED +GLOBAL_VALUE_PATH NULL +IS_DEPRECATED NO +DEPRECATED_REPLACEMENT NULL +# End of parsec.test diff --git a/mysql-test/suite/plugins/r/show_all_plugins.result b/mysql-test/suite/plugins/r/show_all_plugins.result index 9fdc9a39882db..3cef7cf59b147 100644 --- a/mysql-test/suite/plugins/r/show_all_plugins.result +++ b/mysql-test/suite/plugins/r/show_all_plugins.result @@ -4,8 +4,8 @@ Variable_name Value Opened_plugin_libraries 0 select * from information_schema.all_plugins where plugin_library='ha_example.so'; PLUGIN_NAME PLUGIN_VERSION PLUGIN_STATUS PLUGIN_TYPE PLUGIN_TYPE_VERSION PLUGIN_LIBRARY PLUGIN_LIBRARY_VERSION PLUGIN_AUTHOR PLUGIN_DESCRIPTION PLUGIN_LICENSE LOAD_OPTION PLUGIN_MATURITY PLUGIN_AUTH_VERSION -EXAMPLE 0.1 NOT INSTALLED STORAGE ENGINE MYSQL_VERSION_ID ha_example.so 1.15 Brian Aker, MySQL AB Example storage engine GPL OFF Experimental 0.1 -UNUSABLE 3.14 NOT INSTALLED DAEMON MYSQL_VERSION_ID ha_example.so 1.15 Sergei Golubchik Unusable Daemon GPL OFF Experimental 3.14.15.926 +EXAMPLE 0.1 NOT INSTALLED STORAGE ENGINE MYSQL_VERSION_ID ha_example.so 1.16 Brian Aker, MySQL AB Example storage engine GPL OFF Experimental 0.1 +UNUSABLE 3.14 NOT INSTALLED DAEMON MYSQL_VERSION_ID ha_example.so 1.16 Sergei Golubchik Unusable Daemon GPL OFF Experimental 3.14.15.926 show status like '%libraries%'; Variable_name Value Opened_plugin_libraries 1 diff --git a/mysql-test/suite/plugins/r/simple_password_check.result b/mysql-test/suite/plugins/r/simple_password_check.result index c3eebd0186c5b..3b47167947ece 100644 --- a/mysql-test/suite/plugins/r/simple_password_check.result +++ b/mysql-test/suite/plugins/r/simple_password_check.result @@ -6,7 +6,7 @@ PLUGIN_STATUS ACTIVE PLUGIN_TYPE PASSWORD VALIDATION PLUGIN_TYPE_VERSION 1.1 PLUGIN_LIBRARY simple_password_check.so -PLUGIN_LIBRARY_VERSION 1.15 +PLUGIN_LIBRARY_VERSION 1.16 PLUGIN_AUTHOR Sergei Golubchik PLUGIN_DESCRIPTION Simple password strength checks PLUGIN_LICENSE GPL diff --git a/mysql-test/suite/plugins/t/parsec.test b/mysql-test/suite/plugins/t/parsec.test index f3b2a91cd9f9f..0eea562e933f9 100644 --- a/mysql-test/suite/plugins/t/parsec.test +++ b/mysql-test/suite/plugins/t/parsec.test @@ -61,3 +61,83 @@ connect con5, localhost, test2, "wrong_pwd"; connection default; source include/wait_until_count_sessions.inc; drop user test2@'%'; + +--echo # +--echo # MDEV-35254 PARSEC plugin should allow DBAs to specify number of iterations +--echo # + +--echo # Basic visibility and default value +set @init_value := @@global.parsec_iterations; + +--echo # Valid power-of-two values should be accepted +set global parsec_iterations = 1024; +select @@global.parsec_iterations; + +set global parsec_iterations = 262144; +select @@global.parsec_iterations; + +--echo # Values should be rounded UP to next power of two +--echo # 5000 -> 8192 +set global parsec_iterations = 5000; +select @@global.parsec_iterations; + +--echo # 131073 -> 262144 +set global parsec_iterations = 131073; +select @@global.parsec_iterations; + +--echo # Lower bound enforcement +set global parsec_iterations = 512; +select @@global.parsec_iterations; + +--echo # Upper bound enforcement +set global parsec_iterations = 2147483649; +select @@global.parsec_iterations; + +--echo # Session variable access +set session parsec_iterations = 1024; +select @@session.parsec_iterations; + +--echo # parsec_iterations should not get accidently mutated during user creation +set @iter_before_global := @@global.parsec_iterations; +set @iter_before_session := @@session.parsec_iterations; +create user t_iter@'%' identified via parsec using password('pwd'); +select @@global.parsec_iterations = @iter_before_global; +select @@session.parsec_iterations = @iter_before_session; +drop user t_iter@'%'; + +--echo # Verify the iteration character encoded in the password hash is correct +--echo # --- 1024 iterations: expect iter char '0' --- +set session parsec_iterations = 1024; +create user t_iter@'%' identified via parsec using password('pwd'); +select left(json_value(Priv, '$.authentication_string'), 2) from mysql.global_priv where user='t_iter'; +drop user t_iter@'%'; + +--echo # --- 8192 iterations: expect iter char '3' --- +set session parsec_iterations = 8192; +create user t_iter@'%' identified via parsec using password('pwd'); +select left(json_value(Priv, '$.authentication_string'), 2) from mysql.global_priv where user='t_iter'; +drop user t_iter@'%'; + +--echo # --- 262147 iterations: expect iter char '9' --- +set session parsec_iterations = 262147; +create user t_iter@'%' identified via parsec using password('pwd'); +select left(json_value(Priv, '$.authentication_string'), 2) from mysql.global_priv where user='t_iter'; +drop user t_iter@'%'; + +set global parsec_iterations = @init_value; + +--echo # connection test +set session parsec_iterations = 4096; +create user test1@'%' identified via parsec using password('pwd'); +--replace_regex /:[A-Za-z0-9+\/]{43}'/:password'/ /:[A-Za-z0-9+\/]{24}:/:salt:/ +show grants for test1@'%'; +connect con1, localhost, test1, pwd; +select 1, user(), current_user(); +disconnect con1; +connection default; +source include/wait_until_count_sessions.inc; +drop user test1@'%'; + +query_vertical select * from information_schema.system_variables where variable_name like 'parsec%'; + +--echo # End of parsec.test diff --git a/plugin/auth_parsec/server_parsec.cc b/plugin/auth_parsec/server_parsec.cc index 7e09ca06acc4d..e6ff02ba0604e 100644 --- a/plugin/auth_parsec/server_parsec.cc +++ b/plugin/auth_parsec/server_parsec.cc @@ -25,11 +25,13 @@ #include #include #include +#include #include "scope.h" - +#include "my_bit.h" #include typedef unsigned char uchar; +typedef unsigned int parsec_iterations_t; constexpr size_t CHALLENGE_SCRAMBLE_LENGTH= 32; constexpr size_t CHALLENGE_SALT_LENGTH= 18; constexpr size_t ED25519_SIG_LENGTH= 64; @@ -38,6 +40,42 @@ constexpr size_t PBKDF2_HASH_LENGTH= ED25519_KEY_LENGTH; constexpr size_t CLIENT_RESPONSE_LENGTH= CHALLENGE_SCRAMBLE_LENGTH + ED25519_SIG_LENGTH; +constexpr parsec_iterations_t ITER_FACTOR_BASE_VAL= 10u; +constexpr parsec_iterations_t ITER_BASE_VAL= 1u << ITER_FACTOR_BASE_VAL; +constexpr parsec_iterations_t PARSEC_ITERATIONS_MAX= 1u << 30; + +static inline uchar base62_to_uchar(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'Z') return c - 'A' + 10; + if (c >= 'a' && c <= 'z') return c - 'a' + 36; + return 255; +} + +const char parsec_dig_vec_base62[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void update_parsec_iterations(MYSQL_THD thd, + struct st_mysql_sys_var *var, void *var_ptr, const void *save) +{ + parsec_iterations_t iterations_user_input= *static_cast(save); + parsec_iterations_t iterations= my_round_up_to_next_power(iterations_user_input); + if (iterations != iterations_user_input) + my_printf_error(ER_WRONG_VALUE_FOR_VAR, "parsec_iterations rounded up to %d", + ME_WARNING, iterations); + *static_cast(var_ptr)= iterations; +} + +static MYSQL_THDVAR_UINT(iterations, PLUGIN_VAR_RQCMDARG, + "Number of iterations to be used when generating the key corresponding to the password. " + "Will be rounded up to a power of two", + NULL, update_parsec_iterations, ITER_BASE_VAL, ITER_BASE_VAL, + PARSEC_ITERATIONS_MAX, 1); + +static struct st_mysql_sys_var* system_vars[] = { + MYSQL_SYSVAR(iterations), + NULL +}; + constexpr size_t base64_length(size_t input_length) { return ((input_length + 2) / 3) * 4; // with padding @@ -95,7 +133,7 @@ int compute_derived_key(const char* password, size_t pass_len, { assert(params->algorithm == 'P'); int ret = PKCS5_PBKDF2_HMAC(password, (int)pass_len, params->salt, - sizeof(params->salt), 1024 << params->iterations, + sizeof(params->salt), ITER_BASE_VAL << params->iterations, EVP_sha512(), PBKDF2_HASH_LENGTH, derived_key); if(ret == 0) return print_ssl_error(); @@ -174,9 +212,10 @@ int hash_password(const char *password, size_t password_length, auto stored= (Passwd_as_stored*)hash; assert(*hash_length >= sizeof(*stored) + 2); // it should fit the buffer + MYSQL_THD thd = get_current_thd(); Passwd_in_memory memory; memory.algorithm= 'P'; - memory.iterations= 0; + memory.iterations= my_bit_log2_uint32(THDVAR(thd, iterations)) - ITER_FACTOR_BASE_VAL; my_random_bytes(memory.salt, sizeof(memory.salt)); uchar derived_key[PBKDF2_HASH_LENGTH]; @@ -187,7 +226,7 @@ int hash_password(const char *password, size_t password_length, return 1; stored->algorithm= memory.algorithm; - stored->iterations= memory.iterations + '0'; + stored->iterations= parsec_dig_vec_base62[memory.iterations]; my_base64_encode(memory.salt, sizeof(memory.salt), stored->salt); my_base64_encode(memory.pub_key, sizeof(memory.pub_key), stored->pub_key); stored->colon= stored->colon2= ':'; @@ -198,6 +237,7 @@ int hash_password(const char *password, size_t password_length, return 0; } + static int digest_to_binary(const char *hash, size_t hash_length, unsigned char *out, size_t *out_length) @@ -205,9 +245,10 @@ int digest_to_binary(const char *hash, size_t hash_length, auto stored= (Passwd_as_stored*)hash; auto memory= (Passwd_in_memory*)out; + memory->iterations= base62_to_uchar(stored->iterations); + if (hash_length != sizeof (*stored) || *out_length < sizeof(*memory) || - stored->algorithm != 'P' || - stored->iterations < '0' || stored->iterations > '3' || + stored->algorithm != 'P' || memory->iterations == 255 || stored->colon != ':' || stored->colon2 != ':') { my_printf_error(ER_PASSWD_LENGTH, "Wrong ext-salt format", 0); @@ -216,7 +257,7 @@ int digest_to_binary(const char *hash, size_t hash_length, *out_length = sizeof(*memory); memory->algorithm= stored->algorithm; - memory->iterations= stored->iterations - '0'; + static_assert(base64_length(CHALLENGE_SALT_LENGTH) == base64_length_raw(CHALLENGE_SALT_LENGTH), "Salt is base64-aligned"); @@ -313,7 +354,7 @@ maria_declare_plugin(auth_parsec) NULL, 0x0100, NULL, - NULL, + system_vars, "1.0", MariaDB_PLUGIN_MATURITY_STABLE } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 70c571f14e6ec..5329b1dd974f9 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -557,6 +557,14 @@ extern "C" } +extern "C" +{ + MYSQL_THD get_current_thd() { + return current_thd; + } +} + + #if MARIA_PLUGIN_INTERFACE_VERSION < 0x0200 /** TODO: This function is for API compatibility, remove it eventually. diff --git a/sql/sql_plugin_services.inl b/sql/sql_plugin_services.inl index 7ef6f0a7efba8..e88b834136f80 100644 --- a/sql/sql_plugin_services.inl +++ b/sql/sql_plugin_services.inl @@ -19,6 +19,7 @@ #include #include #include +#include struct st_service_ref { const char *name; @@ -272,6 +273,11 @@ static struct thd_mdl_service_st thd_mdl_handler= thd_mdl_context }; +static struct thd_service_st thd_handler= +{ + get_current_thd +}; + #define DEFINE_warning_function(name, ret) { \ static query_id_t last_query_id= -1; \ THD *thd= current_thd; \ @@ -373,5 +379,6 @@ static struct st_service_ref list_of_services[]= { "provider_service_lz4", VERSION_provider_lz4, &provider_handler_lz4 }, { "provider_service_lzma", VERSION_provider_lzma, &provider_handler_lzma }, { "provider_service_lzo", VERSION_provider_lzo, &provider_handler_lzo }, - { "provider_service_snappy", VERSION_provider_snappy, &provider_handler_snappy } + { "provider_service_snappy", VERSION_provider_snappy, &provider_handler_snappy }, + { "thd_service", VERSION_thd, &thd_handler }, };