From 9e23d9cf901504c0a9c949f1d8ebded0d8675bd9 Mon Sep 17 00:00:00 2001 From: Nikita Malyavin <nikitamalyavin@gmail.com> Date: Wed, 4 Sep 2024 19:57:45 +0200 Subject: [PATCH] MDEV-34854 Parsec sends garbage when using an empty password When an empty password is set, the server doesn't call st_mysql_auth::hash_password and leaves MYSQL_SERVER_AUTH_INFO::auth_string empty. Normally, auth_string would be a string of readable characters beginnging from "P", followed by a single digit, like "P0". In this case, auth_string[0] will be '\0', followed by a garbage. In case of empty auth_string, let's generate an empty password ad-hoc. It makes no sense save it to auth_string, since it's a sopy of a globally stored object. This means also, that ext-salt will be random per each login. --- mysql-test/suite/plugins/r/parsec.result | 15 ++++++++++ mysql-test/suite/plugins/t/parsec.test | 14 +++++++++ plugin/auth_parsec/server_parsec.cc | 37 +++++++++++++++++------- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/mysql-test/suite/plugins/r/parsec.result b/mysql-test/suite/plugins/r/parsec.result index 512c066e2d7..362a4533941 100644 --- a/mysql-test/suite/plugins/r/parsec.result +++ b/mysql-test/suite/plugins/r/parsec.result @@ -28,3 +28,18 @@ test.have_ssl() yes drop function have_ssl; drop user test1@'%'; +# MDEV-34854 Parsec sends garbage when using an empty password +create user test2@'%' identified via parsec using PASSWORD(''); +show grants for test2@'%'; +Grants for test2@% +GRANT USAGE ON *.* TO `test2`@`%` IDENTIFIED VIA parsec +connect con4, localhost, test2,; +select 4, USER(), CURRENT_USER(); +4 USER() CURRENT_USER() +4 test2@localhost test2@% +disconnect con4; +connect(localhost,test2,wrong_pwd,test,MASTER_MYPORT,MASTER_MYSOCK); +connect con5, localhost, test2, "wrong_pwd"; +ERROR 28000: Access denied for user 'test2'@'localhost' (using password: NO) +connection default; +drop user test2@'%'; diff --git a/mysql-test/suite/plugins/t/parsec.test b/mysql-test/suite/plugins/t/parsec.test index 2374d66dac4..e5fc04058eb 100644 --- a/mysql-test/suite/plugins/t/parsec.test +++ b/mysql-test/suite/plugins/t/parsec.test @@ -43,3 +43,17 @@ if ($MTR_COMBINATION_WIN) { drop function have_ssl; drop user test1@'%'; + + +--echo # MDEV-34854 Parsec sends garbage when using an empty password +create user test2@'%' identified via parsec using PASSWORD(''); +show grants for test2@'%'; +connect con4, localhost, test2,; +select 4, USER(), CURRENT_USER(); +disconnect con4; + +--replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT +--error ER_ACCESS_DENIED_ERROR +connect con5, localhost, test2, "wrong_pwd"; +connection default; +drop user test2@'%'; diff --git a/plugin/auth_parsec/server_parsec.cc b/plugin/auth_parsec/server_parsec.cc index 7a0d2a172d6..21ff5abdb9e 100644 --- a/plugin/auth_parsec/server_parsec.cc +++ b/plugin/auth_parsec/server_parsec.cc @@ -166,6 +166,24 @@ int ed25519_derive_public_key(const uchar *raw_private_key, uchar *pub_key) #endif } +static int hash_password(const char *password, size_t password_length, + Passwd_in_memory *derivation) +{ + derivation->algorithm= 'P'; + derivation->iterations= 0; + int err= my_random_bytes(derivation->salt, sizeof(derivation->salt)); + if (err != MY_AES_OK) + return 1; + + uchar derived_key[PBKDF2_HASH_LENGTH]; + if (compute_derived_key(password, password_length, derivation, derived_key)) + return 1; + + if (ed25519_derive_public_key(derived_key, derivation->pub_key)) + return 1; + return 0; +} + static int hash_password(const char *password, size_t password_length, char *hash, size_t *hash_length) @@ -175,15 +193,7 @@ int hash_password(const char *password, size_t password_length, return 1; Passwd_in_memory memory; - memory.algorithm= 'P'; - memory.iterations= 0; - my_random_bytes(memory.salt, sizeof(memory.salt)); - - uchar derived_key[PBKDF2_HASH_LENGTH]; - if (compute_derived_key(password, password_length, &memory, derived_key)) - return 1; - - if (ed25519_derive_public_key(derived_key, memory.pub_key)) + if (hash_password(password, password_length, &memory)) return 1; stored->algorithm= memory.algorithm; @@ -260,7 +270,14 @@ int auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) auto passwd= (Passwd_in_memory*)info->auth_string; - if (vio->write_packet(vio, (uchar*)info->auth_string, 2 + CHALLENGE_SALT_LENGTH)) + Passwd_in_memory zero_passwd_derivation; + if (info->auth_string_length == 0) // Empty passwd + { + hash_password("", 0, &zero_passwd_derivation); + passwd= &zero_passwd_derivation; + } + + if (vio->write_packet(vio, (uchar*)passwd, 2 + CHALLENGE_SALT_LENGTH)) return CR_ERROR; Client_signed_response *client_response; -- 2.30.9