Skip to content

Commit 087c1fa

Browse files
committed
MDEV-35254 Make iterations count configurable in PARSEC plugin
This patch adds a global plugin variable parsec_iterations to define define the number of iterations to be used when generating the key corresponding to the password. It has a default value, lower and upper bounds.
1 parent 5ed3668 commit 087c1fa

File tree

5 files changed

+199
-6
lines changed

5 files changed

+199
-6
lines changed

mysql-test/suite/plugins/r/parsec.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ ERROR HY000: Wrong ext-salt format
33
create user test1@'%' identified via parsec using PASSWORD('pwd');
44
show grants for test1@'%';
55
Grants for test1@%
6-
GRANT USAGE ON *.* TO `test1`@`%` IDENTIFIED VIA parsec USING 'P0:salt:password'
6+
GRANT USAGE ON *.* TO `test1`@`%` IDENTIFIED VIA parsec USING 'P8:salt:password'
77
connect con1, localhost, test1, pwd;
88
select 1, USER(), CURRENT_USER();
99
1 USER() CURRENT_USER()
@@ -32,7 +32,7 @@ drop user test1@'%';
3232
create user test2@'%' identified via parsec using PASSWORD('');
3333
show grants for test2@'%';
3434
Grants for test2@%
35-
GRANT USAGE ON *.* TO `test2`@`%` IDENTIFIED VIA parsec USING 'P0:salt:password'
35+
GRANT USAGE ON *.* TO `test2`@`%` IDENTIFIED VIA parsec USING 'P8:salt:password'
3636
connect con4, localhost, test2,;
3737
select 4, USER(), CURRENT_USER();
3838
4 USER() CURRENT_USER()
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#
2+
# Basic visibility and default value
3+
#
4+
select @@global.parsec_iterations;
5+
#
6+
# Valid power-of-two values should be accepted
7+
#
8+
set global parsec_iterations = 1024;
9+
select @@global.parsec_iterations;
10+
@@global.parsec_iterations
11+
1024
12+
set global parsec_iterations = 262144;
13+
select @@global.parsec_iterations;
14+
@@global.parsec_iterations
15+
262144
16+
#
17+
# Values should be rounded UP to next power of two
18+
#
19+
# 5000 -> 8192
20+
set global parsec_iterations = 5000;
21+
Warnings:
22+
Warning 1231 parsec: parsec_iterations rounded up to 8192 (next supported value)
23+
select @@global.parsec_iterations;
24+
@@global.parsec_iterations
25+
8192
26+
# 131073 -> 262144
27+
set global parsec_iterations = 131073;
28+
Warnings:
29+
Warning 1231 parsec: parsec_iterations rounded up to 262144 (next supported value)
30+
select @@global.parsec_iterations;
31+
@@global.parsec_iterations
32+
262144
33+
#
34+
# Lower bound enforcement
35+
#
36+
set global parsec_iterations = 512;
37+
Warnings:
38+
Warning 1292 Truncated incorrect parsec_iterations value: '512'
39+
select @@global.parsec_iterations;
40+
@@global.parsec_iterations
41+
1024
42+
#
43+
# Upper bound enforcement
44+
#
45+
set global parsec_iterations = 2097152;
46+
Warnings:
47+
Warning 1292 Truncated incorrect parsec_iterations value: '2097152'
48+
select @@global.parsec_iterations;
49+
@@global.parsec_iterations
50+
524288
51+
#
52+
# Session variable must not exist
53+
#
54+
select @@session.parsec_iterations;
55+
ERROR HY000: Variable 'parsec_iterations' is a GLOBAL variable
56+
set session parsec_iterations = 1024;
57+
ERROR HY000: Variable 'parsec_iterations' is a GLOBAL variable and should be set with SET GLOBAL
58+
#
59+
# parsec_iterations should should not get accidently mutated during user creation
60+
#
61+
set @iter_before := @@global.parsec_iterations;
62+
create user t_iter@'%' identified via parsec using password('pwd');
63+
select @@global.parsec_iterations = @iter_before;
64+
@@global.parsec_iterations = @iter_before
65+
1
66+
drop user t_iter@'%';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--plugin-load-add=$AUTH_PARSEC_SO
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#
2+
# Test for MDEV-35254: PARSEC plugin should allow DBAs to specify number of iterations
3+
#
4+
5+
source include/platform.inc;
6+
source include/not_embedded.inc;
7+
8+
if (`select count(*) = 0 from information_schema.plugins where plugin_name = 'parsec'`)
9+
{
10+
--skip Needs parsec plugin
11+
}
12+
13+
if (!$PARSEC_SO) {
14+
skip No auth_parsec plugin;
15+
}
16+
17+
--echo #
18+
--echo # Basic visibility and default value
19+
--echo #
20+
21+
--disable_result_log
22+
select @@global.parsec_iterations;
23+
--enable_result_log
24+
25+
--echo #
26+
--echo # Valid power-of-two values should be accepted
27+
--echo #
28+
29+
set global parsec_iterations = 1024;
30+
select @@global.parsec_iterations;
31+
32+
set global parsec_iterations = 262144;
33+
select @@global.parsec_iterations;
34+
35+
--echo #
36+
--echo # Values should be rounded UP to next power of two
37+
--echo #
38+
39+
--echo # 5000 -> 8192
40+
set global parsec_iterations = 5000;
41+
select @@global.parsec_iterations;
42+
43+
--echo # 131073 -> 262144
44+
set global parsec_iterations = 131073;
45+
select @@global.parsec_iterations;
46+
47+
--echo #
48+
--echo # Lower bound enforcement
49+
--echo #
50+
51+
set global parsec_iterations = 512;
52+
select @@global.parsec_iterations;
53+
54+
--echo #
55+
--echo # Upper bound enforcement
56+
--echo #
57+
58+
set global parsec_iterations = 2097152;
59+
select @@global.parsec_iterations;
60+
61+
--echo #
62+
--echo # Session variable must not exist
63+
--echo #
64+
65+
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
66+
select @@session.parsec_iterations;
67+
68+
--error ER_GLOBAL_VARIABLE
69+
set session parsec_iterations = 1024;
70+
71+
--echo #
72+
--echo # parsec_iterations should should not get accidently mutated during user creation
73+
--echo #
74+
75+
set @iter_before := @@global.parsec_iterations;
76+
create user t_iter@'%' identified via parsec using password('pwd');
77+
select @@global.parsec_iterations = @iter_before;
78+
drop user t_iter@'%';
79+

plugin/auth_parsec/server_parsec.cc

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,52 @@ constexpr size_t PBKDF2_HASH_LENGTH= ED25519_KEY_LENGTH;
3838
constexpr size_t CLIENT_RESPONSE_LENGTH= CHALLENGE_SCRAMBLE_LENGTH
3939
+ ED25519_SIG_LENGTH;
4040

41+
constexpr unsigned int PARSEC_ITERATIONS_DEFAULT= 1u << 18;
42+
constexpr unsigned int PARSEC_ITERATIONS_MIN= 1u << 10;
43+
constexpr unsigned int PARSEC_ITERATIONS_MAX= 1u << 19;
44+
constexpr unsigned int PARSEC_ITERATIONS_STEP= 1u;
45+
constexpr unsigned int ITER_FACTOR_BASE_VAL= 10u;
46+
47+
static unsigned int iterations= PARSEC_ITERATIONS_DEFAULT;
48+
49+
static inline unsigned int round_to_power_of_two(const unsigned int x) {
50+
unsigned int p = 1;
51+
while (p < x)
52+
p <<= 1;
53+
return p;
54+
}
55+
56+
57+
static inline unsigned int log_2_of_power_of_2(const unsigned int x) {
58+
// function will not work correctly for non power of 2 unsigned integers
59+
unsigned int p = 0;
60+
while ((1u << p) != x)
61+
p++;
62+
return p;
63+
}
64+
65+
66+
static void update_parsec_iterations(MYSQL_THD thd,
67+
struct st_mysql_sys_var *var __attribute__((unused)),
68+
void *var_ptr __attribute__((unused)), const void *save)
69+
{
70+
unsigned int iterations_user_input= *(unsigned int *) save;
71+
iterations= round_to_power_of_two(iterations_user_input);
72+
if (iterations != iterations_user_input)
73+
my_printf_error(ER_WRONG_VALUE_FOR_VAR, "parsec: parsec_iterations rounded up to %d (next supported value)",
74+
ME_WARNING, iterations);
75+
}
76+
77+
static MYSQL_SYSVAR_UINT(iterations, iterations, PLUGIN_VAR_NOCMDOPT,
78+
"Number of iterations to be used when generating the key corresponding to the password",
79+
NULL, update_parsec_iterations, PARSEC_ITERATIONS_DEFAULT, PARSEC_ITERATIONS_MIN,
80+
PARSEC_ITERATIONS_MAX, PARSEC_ITERATIONS_STEP);
81+
82+
static struct st_mysql_sys_var* system_vars[] = {
83+
MYSQL_SYSVAR(iterations),
84+
NULL
85+
};
86+
4187
constexpr size_t base64_length(size_t input_length)
4288
{
4389
return ((input_length + 2) / 3) * 4; // with padding
@@ -95,7 +141,7 @@ int compute_derived_key(const char* password, size_t pass_len,
95141
{
96142
assert(params->algorithm == 'P');
97143
int ret = PKCS5_PBKDF2_HMAC(password, (int)pass_len, params->salt,
98-
sizeof(params->salt), 1024 << params->iterations,
144+
sizeof(params->salt), 1024u << params->iterations,
99145
EVP_sha512(), PBKDF2_HASH_LENGTH, derived_key);
100146
if(ret == 0)
101147
return print_ssl_error();
@@ -176,7 +222,7 @@ int hash_password(const char *password, size_t password_length,
176222

177223
Passwd_in_memory memory;
178224
memory.algorithm= 'P';
179-
memory.iterations= 0;
225+
memory.iterations= log_2_of_power_of_2(iterations) - ITER_FACTOR_BASE_VAL;
180226
my_random_bytes(memory.salt, sizeof(memory.salt));
181227

182228
uchar derived_key[PBKDF2_HASH_LENGTH];
@@ -204,10 +250,11 @@ int digest_to_binary(const char *hash, size_t hash_length,
204250
{
205251
auto stored= (Passwd_as_stored*)hash;
206252
auto memory= (Passwd_in_memory*)out;
253+
const uchar ITER_MAX_VAL= '0' + (log_2_of_power_of_2(PARSEC_ITERATIONS_MAX) - ITER_FACTOR_BASE_VAL);
207254

208255
if (hash_length != sizeof (*stored) || *out_length < sizeof(*memory) ||
209256
stored->algorithm != 'P' ||
210-
stored->iterations < '0' || stored->iterations > '3' ||
257+
stored->iterations < '0' || stored->iterations > ITER_MAX_VAL ||
211258
stored->colon != ':' || stored->colon2 != ':')
212259
{
213260
my_printf_error(ER_PASSWD_LENGTH, "Wrong ext-salt format", 0);
@@ -313,7 +360,7 @@ maria_declare_plugin(auth_parsec)
313360
NULL,
314361
0x0100,
315362
NULL,
316-
NULL,
363+
system_vars,
317364
"1.0",
318365
MariaDB_PLUGIN_MATURITY_GAMMA
319366
}

0 commit comments

Comments
 (0)