MDEV-9804 Implement a caching_sha2_password plugin
but without caching
This commit is contained in:
parent
6a2afb42ba
commit
e15904d564
1
debian/mariadb-server.install
vendored
1
debian/mariadb-server.install
vendored
@ -36,6 +36,7 @@ usr/bin/wsrep_sst_mysqldump
|
||||
usr/bin/wsrep_sst_rsync
|
||||
usr/bin/wsrep_sst_rsync_wan
|
||||
usr/lib/mysql/plugin/auth_ed25519.so
|
||||
usr/lib/mysql/plugin/auth_mysql_sha2.so
|
||||
usr/lib/mysql/plugin/auth_pam.so
|
||||
usr/lib/mysql/plugin/auth_pam_tool_dir/auth_pam_tool
|
||||
usr/lib/mysql/plugin/auth_pam_v1.so
|
||||
|
@ -8,6 +8,7 @@ typedef struct st_plugin_vio_info
|
||||
enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
|
||||
MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
|
||||
int socket;
|
||||
int tls;
|
||||
} MYSQL_PLUGIN_VIO_INFO;
|
||||
typedef struct st_plugin_vio
|
||||
{
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
#include <mysql/plugin.h>
|
||||
|
||||
#define MYSQL_AUTHENTICATION_INTERFACE_VERSION 0x0202
|
||||
#define MYSQL_AUTHENTICATION_INTERFACE_VERSION 0x0203
|
||||
|
||||
#include <mysql/plugin_auth_common.h>
|
||||
|
||||
|
@ -707,6 +707,7 @@ typedef struct st_plugin_vio_info
|
||||
enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
|
||||
MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
|
||||
int socket;
|
||||
int tls;
|
||||
} MYSQL_PLUGIN_VIO_INFO;
|
||||
typedef struct st_plugin_vio
|
||||
{
|
||||
|
@ -98,6 +98,7 @@ typedef struct st_plugin_vio_info
|
||||
#ifdef _WIN32
|
||||
HANDLE handle; /**< it's set, if the protocol is PIPE or MEMORY */
|
||||
#endif
|
||||
int tls;
|
||||
} MYSQL_PLUGIN_VIO_INFO;
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ plugins,^
|
||||
mariabackup,^
|
||||
roles,^
|
||||
auth_gssapi,^
|
||||
mysql_sha2,^
|
||||
query_response_time,^
|
||||
rocksdb,^
|
||||
sysschema
|
||||
|
@ -1,5 +1,5 @@
|
||||
if ($CLIENT_TLS_LIBRARY != "OpenSSL") {
|
||||
if ($CLIENT_TLS_LIBRARY != "LibreSSL") {
|
||||
skip "Test requires Connector/C with OpenSSL library";
|
||||
skip Test requires Connector/C with OpenSSL library;
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ perl;
|
||||
test-sql-discovery query-cache-info password-reuse-check
|
||||
query-response-time metadata-lock-info locales unix-socket
|
||||
wsrep file-key-management cracklib-password-check user-variables
|
||||
provider-bzip2 provider-lzma provider-lzo
|
||||
provider-bzip2 provider-lzma provider-lzo caching-sha2-password
|
||||
thread-pool-groups thread-pool-queues thread-pool-stats
|
||||
thread-pool-waits hashicorp provider gssapi parsec/;
|
||||
|
||||
|
@ -25,7 +25,7 @@ PLUGIN_NAME ed25519
|
||||
PLUGIN_VERSION 1.1
|
||||
PLUGIN_STATUS ACTIVE
|
||||
PLUGIN_TYPE AUTHENTICATION
|
||||
PLUGIN_TYPE_VERSION 2.2
|
||||
PLUGIN_TYPE_VERSION 2.3
|
||||
PLUGIN_LIBRARY auth_ed25519.so
|
||||
PLUGIN_LIBRARY_VERSION 1.15
|
||||
PLUGIN_AUTHOR Sergei Golubchik
|
||||
|
8
plugin/auth_mysql_sha2/CMakeLists.txt
Normal file
8
plugin/auth_mysql_sha2/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
ADD_DEFINITIONS(${SSL_DEFINES})
|
||||
IF(WITH_SSL STREQUAL "bundled")
|
||||
# WolfSSL is static, we don't want it linked both into plugin and server
|
||||
SET(static STATIC_ONLY DEFAULT)
|
||||
ENDIF()
|
||||
MYSQL_ADD_PLUGIN(auth_mysql_sha2
|
||||
mysql_sha2.c sha256crypt.c ssl_stuff.c openssl1-compat.c
|
||||
LINK_LIBRARIES ${SSL_LIBRARIES} ${static})
|
3
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/fini.inc
Normal file
3
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/fini.inc
Normal file
@ -0,0 +1,3 @@
|
||||
drop procedure checkme;
|
||||
drop user test1@'%';
|
||||
drop user test2@'%';
|
21
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/init.inc
Normal file
21
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/init.inc
Normal file
@ -0,0 +1,21 @@
|
||||
source include/not_embedded.inc;
|
||||
|
||||
call mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
|
||||
if (`select count(*) = 0 from information_schema.plugins where plugin_name = 'caching_sha2_password'`)
|
||||
{
|
||||
--skip Needs caching_sha2_password plugin
|
||||
}
|
||||
|
||||
show status like 'caching_sha2_password%';
|
||||
|
||||
create user test1@'%' identified via caching_sha2_password using PASSWORD('pwd');
|
||||
create user test2@'%' identified via caching_sha2_password;
|
||||
show grants for test2@'%';
|
||||
|
||||
create procedure checkme() sql security invoker
|
||||
select user(), current_user(), variable_value > '' as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher';
|
||||
|
||||
grant execute on test.* to test1@'%', test2@'%';
|
3
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/init.opt
Normal file
3
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/init.opt
Normal file
@ -0,0 +1,3 @@
|
||||
--plugin-load-add=$AUTH_MYSQL_SHA2_SO
|
||||
--loose-caching-sha2-password
|
||||
--loose-disable-caching-sha2-password-auto-generate-rsa-keys
|
1
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/socket.opt
Normal file
1
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/socket.opt
Normal file
@ -0,0 +1 @@
|
||||
--loose-enable-named-pipe
|
39
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/socket.result
Normal file
39
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/socket.result
Normal file
@ -0,0 +1,39 @@
|
||||
call mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
show status like 'caching_sha2_password%';
|
||||
Variable_name Value
|
||||
Caching_sha2_password_rsa_public_key
|
||||
create user test1@'%' identified via caching_sha2_password using PASSWORD('pwd');
|
||||
create user test2@'%' identified via caching_sha2_password;
|
||||
show grants for test2@'%';
|
||||
Grants for test2@%
|
||||
GRANT USAGE ON *.* TO `test2`@`%` IDENTIFIED VIA caching_sha2_password
|
||||
create procedure checkme() sql security invoker
|
||||
select user(), current_user(), variable_value > '' as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher';
|
||||
grant execute on test.* to test1@'%', test2@'%';
|
||||
connect con1, localhost,test1,pwd,,,,$proto NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test1@localhost test1@% 0
|
||||
disconnect con1;
|
||||
connect con2, localhost,test1,pwd,,,,$proto NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test1@localhost test1@% 0
|
||||
disconnect con2;
|
||||
connect(localhost,test1,wrong_pwd,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con3, localhost,test1,wrong_pwd,,,,$proto NOSSL;
|
||||
ERROR 28000: Access denied for user 'test1'@'localhost' (using password: YES)
|
||||
connect con4, localhost,test2,,,,,$proto NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test2@localhost test2@% 0
|
||||
disconnect con4;
|
||||
connect(localhost,test2 pwd,,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con5, localhost,test2 pwd,,,,,$proto NOSSL;
|
||||
ERROR 28000: Access denied for user 'test2 pwd'@'localhost' (using password: NO)
|
||||
connection default;
|
||||
drop procedure checkme;
|
||||
drop user test1@'%';
|
||||
drop user test2@'%';
|
27
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/socket.test
Normal file
27
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/socket.test
Normal file
@ -0,0 +1,27 @@
|
||||
source include/platform.inc;
|
||||
source init.inc;
|
||||
|
||||
let proto=SOCKET;
|
||||
if ($MTR_COMBINATION_WIN) {
|
||||
let proto=PIPE;
|
||||
}
|
||||
|
||||
connect con1, localhost,test1,pwd,,,,$proto NOSSL;
|
||||
call checkme();
|
||||
disconnect con1;
|
||||
connect con2, localhost,test1,pwd,,,,$proto NOSSL;
|
||||
call checkme();
|
||||
disconnect con2;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error ER_ACCESS_DENIED_ERROR;
|
||||
connect con3, localhost,test1,wrong_pwd,,,,$proto NOSSL;
|
||||
connect con4, localhost,test2,,,,,$proto NOSSL;
|
||||
call checkme();
|
||||
disconnect con4;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error ER_ACCESS_DENIED_ERROR;
|
||||
connect con5, localhost,test2 pwd,,,,,$proto NOSSL;
|
||||
|
||||
connection default;
|
||||
|
||||
source fini.inc;
|
@ -0,0 +1,3 @@
|
||||
--ssl-key=
|
||||
--ssl-cert=
|
||||
--ssl-ca=
|
29
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/ssl_auto.result
Normal file
29
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/ssl_auto.result
Normal file
@ -0,0 +1,29 @@
|
||||
call mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
show status like 'caching_sha2_password%';
|
||||
Variable_name Value
|
||||
Caching_sha2_password_rsa_public_key
|
||||
create user test1@'%' identified via caching_sha2_password using PASSWORD('pwd');
|
||||
create user test2@'%' identified via caching_sha2_password;
|
||||
show grants for test2@'%';
|
||||
Grants for test2@%
|
||||
GRANT USAGE ON *.* TO `test2`@`%` IDENTIFIED VIA caching_sha2_password
|
||||
create procedure checkme() sql security invoker
|
||||
select user(), current_user(), variable_value > '' as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher';
|
||||
grant execute on test.* to test1@'%', test2@'%';
|
||||
# mysql -utest1 -ppwd --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
user() current_user() have_ssl
|
||||
test1@localhost test1@% 1
|
||||
# mysql -utest1 -pwrong_pwd --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
ERROR 1045 (28000): Access denied for user 'test1'@'localhost' (using password: YES)
|
||||
# mysql -utest2 --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
user() current_user() have_ssl
|
||||
test2@localhost test2@% 1
|
||||
# mysql -utest2 -ppwd --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
ERROR 1045 (28000): Access denied for user 'test2'@'localhost' (using password: YES)
|
||||
# mysql -utest1 -ppwd --ssl-verify-server-cert -e "call test.checkme()"
|
||||
ERROR 2026 (HY000): TLS/SSL error: Certificate verification failure: The certificate is NOT trusted.
|
||||
drop procedure checkme;
|
||||
drop user test1@'%';
|
||||
drop user test2@'%';
|
27
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/ssl_auto.test
Normal file
27
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/ssl_auto.test
Normal file
@ -0,0 +1,27 @@
|
||||
source init.inc;
|
||||
|
||||
let MYSQL=$MYSQL --protocol tcp;
|
||||
if ($MARIADB_UPGRADE_EXE) { # windows
|
||||
# see ssl_autoverify.test
|
||||
let MYSQL=$MYSQL --host=127.0.0.2;
|
||||
}
|
||||
|
||||
--echo # mysql -utest1 -ppwd --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
--exec $MYSQL -utest1 -ppwd --disable-ssl-verify-server-cert -e "call test.checkme()" 2>&1
|
||||
|
||||
--echo # mysql -utest1 -pwrong_pwd --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
--error 1
|
||||
--exec $MYSQL -utest1 -pwrong_pwd --disable-ssl-verify-server-cert -e "call test.checkme()" 2>&1
|
||||
|
||||
--echo # mysql -utest2 --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
--exec $MYSQL -utest2 --disable-ssl-verify-server-cert -e "call test.checkme()" 2>&1
|
||||
|
||||
--echo # mysql -utest2 -ppwd --disable-ssl-verify-server-cert -e "call test.checkme()"
|
||||
--error 1
|
||||
--exec $MYSQL -utest2 -ppwd --disable-ssl-verify-server-cert -e "call test.checkme()" 2>&1
|
||||
|
||||
--echo # mysql -utest1 -ppwd --ssl-verify-server-cert -e "call test.checkme()"
|
||||
--error 1
|
||||
--exec $MYSQL -utest1 -ppwd --ssl-verify-server-cert -e "call test.checkme()" 2>&1
|
||||
|
||||
source fini.inc;
|
@ -0,0 +1,39 @@
|
||||
call mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
show status like 'caching_sha2_password%';
|
||||
Variable_name Value
|
||||
Caching_sha2_password_rsa_public_key
|
||||
create user test1@'%' identified via caching_sha2_password using PASSWORD('pwd');
|
||||
create user test2@'%' identified via caching_sha2_password;
|
||||
show grants for test2@'%';
|
||||
Grants for test2@%
|
||||
GRANT USAGE ON *.* TO `test2`@`%` IDENTIFIED VIA caching_sha2_password
|
||||
create procedure checkme() sql security invoker
|
||||
select user(), current_user(), variable_value > '' as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher';
|
||||
grant execute on test.* to test1@'%', test2@'%';
|
||||
connect con1, localhost,test1,pwd,,,,TCP SSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test1@localhost test1@% 1
|
||||
disconnect con1;
|
||||
connect con2, localhost,test1,pwd,,,,TCP SSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test1@localhost test1@% 1
|
||||
disconnect con2;
|
||||
connect(localhost,test1,wrong_pwd,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con3, localhost,test1,wrong_pwd,,,,TCP SSL;
|
||||
ERROR 28000: Access denied for user 'test1'@'localhost' (using password: YES)
|
||||
connect con4, localhost,test2,,,,,TCP SSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test2@localhost test2@% 1
|
||||
disconnect con4;
|
||||
connect(localhost,test2 pwd,,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con5, localhost,test2 pwd,,,,,TCP SSL;
|
||||
ERROR 28000: Access denied for user 'test2 pwd'@'localhost' (using password: NO)
|
||||
connection default;
|
||||
drop procedure checkme;
|
||||
drop user test1@'%';
|
||||
drop user test2@'%';
|
21
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/ssl_manual.test
Normal file
21
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/ssl_manual.test
Normal file
@ -0,0 +1,21 @@
|
||||
source init.inc;
|
||||
|
||||
connect con1, localhost,test1,pwd,,,,TCP SSL;
|
||||
call checkme();
|
||||
disconnect con1;
|
||||
connect con2, localhost,test1,pwd,,,,TCP SSL;
|
||||
call checkme();
|
||||
disconnect con2;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error ER_ACCESS_DENIED_ERROR;
|
||||
connect con3, localhost,test1,wrong_pwd,,,,TCP SSL;
|
||||
connect con4, localhost,test2,,,,,TCP SSL;
|
||||
call checkme();
|
||||
disconnect con4;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error ER_ACCESS_DENIED_ERROR;
|
||||
connect con5, localhost,test2 pwd,,,,,TCP SSL;
|
||||
|
||||
connection default;
|
||||
|
||||
source fini.inc;
|
5
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/suite.pm
Normal file
5
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/suite.pm
Normal file
@ -0,0 +1,5 @@
|
||||
package My::Suite::AuthSHA2;
|
||||
@ISA = qw(My::Suite);
|
||||
return "Not run for embedded server" if $::opt_embedded_server;
|
||||
sub is_default { 1 }
|
||||
bless { };
|
159
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/tcp_nossl.result
Normal file
159
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/tcp_nossl.result
Normal file
@ -0,0 +1,159 @@
|
||||
call mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
call mtr.add_suppression('Authentication requires either RSA keys or secure transport');
|
||||
call mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
show status like 'caching_sha2_password%';
|
||||
Variable_name Value
|
||||
Caching_sha2_password_rsa_public_key
|
||||
create user test1@'%' identified via caching_sha2_password using PASSWORD('pwd');
|
||||
create user test2@'%' identified via caching_sha2_password;
|
||||
show grants for test2@'%';
|
||||
Grants for test2@%
|
||||
GRANT USAGE ON *.* TO `test2`@`%` IDENTIFIED VIA caching_sha2_password
|
||||
create procedure checkme() sql security invoker
|
||||
select user(), current_user(), variable_value > '' as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher';
|
||||
grant execute on test.* to test1@'%', test2@'%';
|
||||
select * from information_schema.system_variables where variable_name like 'caching_sha2_password%' order by 1;
|
||||
VARIABLE_NAME CACHING_SHA2_PASSWORD_AUTO_GENERATE_RSA_KEYS
|
||||
SESSION_VALUE NULL
|
||||
GLOBAL_VALUE OFF
|
||||
GLOBAL_VALUE_ORIGIN COMMAND-LINE
|
||||
DEFAULT_VALUE ON
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE BOOLEAN
|
||||
VARIABLE_COMMENT Auto generate RSA keys at server startup if key paths are not explicitly set and key files are not present at their default locations
|
||||
NUMERIC_MIN_VALUE NULL
|
||||
NUMERIC_MAX_VALUE NULL
|
||||
NUMERIC_BLOCK_SIZE NULL
|
||||
ENUM_VALUE_LIST OFF,ON
|
||||
READ_ONLY YES
|
||||
COMMAND_LINE_ARGUMENT OPTIONAL
|
||||
GLOBAL_VALUE_PATH NULL
|
||||
VARIABLE_NAME CACHING_SHA2_PASSWORD_DIGEST_ROUNDS
|
||||
SESSION_VALUE NULL
|
||||
GLOBAL_VALUE 5000
|
||||
GLOBAL_VALUE_ORIGIN COMPILE-TIME
|
||||
DEFAULT_VALUE 5000
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE INT UNSIGNED
|
||||
VARIABLE_COMMENT Number of SHA2 rounds to be performed when computing a password hash
|
||||
NUMERIC_MIN_VALUE 5000
|
||||
NUMERIC_MAX_VALUE 4095000
|
||||
NUMERIC_BLOCK_SIZE 1
|
||||
ENUM_VALUE_LIST NULL
|
||||
READ_ONLY YES
|
||||
COMMAND_LINE_ARGUMENT REQUIRED
|
||||
GLOBAL_VALUE_PATH NULL
|
||||
VARIABLE_NAME CACHING_SHA2_PASSWORD_PRIVATE_KEY_PATH
|
||||
SESSION_VALUE NULL
|
||||
GLOBAL_VALUE private_key.pem
|
||||
GLOBAL_VALUE_ORIGIN COMPILE-TIME
|
||||
DEFAULT_VALUE private_key.pem
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE VARCHAR
|
||||
VARIABLE_COMMENT A path to the private RSA key used for authentication
|
||||
NUMERIC_MIN_VALUE NULL
|
||||
NUMERIC_MAX_VALUE NULL
|
||||
NUMERIC_BLOCK_SIZE NULL
|
||||
ENUM_VALUE_LIST NULL
|
||||
READ_ONLY YES
|
||||
COMMAND_LINE_ARGUMENT REQUIRED
|
||||
GLOBAL_VALUE_PATH NULL
|
||||
VARIABLE_NAME CACHING_SHA2_PASSWORD_PUBLIC_KEY_PATH
|
||||
SESSION_VALUE NULL
|
||||
GLOBAL_VALUE public_key.pem
|
||||
GLOBAL_VALUE_ORIGIN COMPILE-TIME
|
||||
DEFAULT_VALUE public_key.pem
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE VARCHAR
|
||||
VARIABLE_COMMENT A path to the public RSA key used for authentication
|
||||
NUMERIC_MIN_VALUE NULL
|
||||
NUMERIC_MAX_VALUE NULL
|
||||
NUMERIC_BLOCK_SIZE NULL
|
||||
ENUM_VALUE_LIST NULL
|
||||
READ_ONLY YES
|
||||
COMMAND_LINE_ARGUMENT REQUIRED
|
||||
GLOBAL_VALUE_PATH NULL
|
||||
create user test3@'%' identified via caching_sha2_password using 'pwd';
|
||||
ERROR HY000: Password hash should be 70 characters long
|
||||
create user test3@'%' identified via caching_sha2_password using '0000000000000000000000000000000000000000000000000000000000000000000000';
|
||||
ERROR HY000: Invalid password hash
|
||||
connect(localhost,test1,pwd,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con1, localhost,test1,pwd,,,,TCP NOSSL;
|
||||
ERROR HY000: Couldn't read RSA public key from server
|
||||
connect(localhost,test1,wrong_pwd,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con3, localhost,test1,wrong_pwd,,,,TCP NOSSL;
|
||||
ERROR HY000: Couldn't read RSA public key from server
|
||||
connect con4, localhost,test2,,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test2@localhost test2@% 0
|
||||
disconnect con4;
|
||||
connect(localhost,test2 pwd,,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con5, localhost,test2 pwd,,,,,TCP NOSSL;
|
||||
ERROR 28000: Access denied for user 'test2 pwd'@'localhost' (using password: NO)
|
||||
connection default;
|
||||
# restart: --caching_sha2_password-auto_generate_rsa_keys
|
||||
select length(variable_value) from information_schema.global_status
|
||||
where variable_name like 'caching_sha2_password%';
|
||||
length(variable_value)
|
||||
451
|
||||
# restart: --caching_sha2_password-auto_generate_rsa_keys
|
||||
select variable_value="$pubkey" as 'key did not change'
|
||||
from information_schema.global_status
|
||||
where variable_name like 'caching_sha2_password%';
|
||||
key did not change
|
||||
1
|
||||
connect con1, localhost,test1,pwd,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test1@localhost test1@% 0
|
||||
disconnect con1;
|
||||
connect con2, localhost,test1,pwd,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test1@localhost test1@% 0
|
||||
disconnect con2;
|
||||
connect(localhost,test1,wrong_pwd,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con3, localhost,test1,wrong_pwd,,,,TCP NOSSL;
|
||||
ERROR 28000: Access denied for user 'test1'@'localhost' (using password: YES)
|
||||
connect con4, localhost,test2,,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
test2@localhost test2@% 0
|
||||
disconnect con4;
|
||||
connect(localhost,test2 pwd,,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con5, localhost,test2 pwd,,,,,TCP NOSSL;
|
||||
ERROR 28000: Access denied for user 'test2 pwd'@'localhost' (using password: NO)
|
||||
connection default;
|
||||
create user u1@localhost identified via caching_sha2_password using '$A$005$5dx;X)z |kX]\ZNx7QTrl0oTy2C0/f4bggQMFIDnSDeZ7koLoO417jc9D';
|
||||
create user u2@localhost identified via caching_sha2_password using '$A$005$dL\Zq]<7d[YAbk}x!;^.qMuuUUBmB5aF7x7GsAKZzpb24p94NCCs8qPgwAvwc1';
|
||||
create user u3@localhost identified via caching_sha2_password using '$A$005$L9\ZKiwT''=%dMoqrPGFbywI9G8NecJqiy9D04S2abTLRvD32powG8nIxI9';
|
||||
grant execute on test.* to u1@localhost, u2@localhost, u3@localhost;
|
||||
connect u1,localhost,u1,abcd,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
u1@localhost u1@localhost 0
|
||||
disconnect u1;
|
||||
connect u2,localhost,u2,efghi,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
u2@localhost u2@localhost 0
|
||||
disconnect u2;
|
||||
connect u3,localhost,u3,xyz,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
user() current_user() have_ssl
|
||||
u3@localhost u3@localhost 0
|
||||
disconnect u3;
|
||||
connection default;
|
||||
drop user u1@localhost;
|
||||
drop user u2@localhost;
|
||||
drop user u3@localhost;
|
||||
# restart
|
||||
show status like 'caching_sha2_password%';
|
||||
Variable_name Value
|
||||
Caching_sha2_password_rsa_public_key
|
||||
drop procedure checkme;
|
||||
drop user test1@'%';
|
||||
drop user test2@'%';
|
93
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/tcp_nossl.test
Normal file
93
plugin/auth_mysql_sha2/mysql-test/mysql_sha2/tcp_nossl.test
Normal file
@ -0,0 +1,93 @@
|
||||
call mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
call mtr.add_suppression('Authentication requires either RSA keys or secure transport');
|
||||
|
||||
source include/require_openssl_client.inc;
|
||||
source init.inc;
|
||||
|
||||
query_vertical select * from information_schema.system_variables where variable_name like 'caching_sha2_password%' order by 1;
|
||||
|
||||
--error ER_PASSWD_LENGTH
|
||||
create user test3@'%' identified via caching_sha2_password using 'pwd';
|
||||
--error ER_PASSWD_LENGTH
|
||||
create user test3@'%' identified via caching_sha2_password using '0000000000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error 2061;
|
||||
connect con1, localhost,test1,pwd,,,,TCP NOSSL;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error 2061;
|
||||
connect con3, localhost,test1,wrong_pwd,,,,TCP NOSSL;
|
||||
connect con4, localhost,test2,,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
disconnect con4;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error ER_ACCESS_DENIED_ERROR;
|
||||
connect con5, localhost,test2 pwd,,,,,TCP NOSSL;
|
||||
|
||||
connection default;
|
||||
|
||||
let $restart_parameters= --caching_sha2_password-auto_generate_rsa_keys;
|
||||
source include/restart_mysqld.inc;
|
||||
select length(variable_value) from information_schema.global_status
|
||||
where variable_name like 'caching_sha2_password%';
|
||||
let pubkey=`select variable_value from information_schema.global_status
|
||||
where variable_name like 'caching_sha2_password%'`;
|
||||
|
||||
let $restart_parameters= --caching_sha2_password-auto_generate_rsa_keys;
|
||||
source include/restart_mysqld.inc;
|
||||
evalp select variable_value="$pubkey" as 'key did not change'
|
||||
from information_schema.global_status
|
||||
where variable_name like 'caching_sha2_password%';
|
||||
|
||||
|
||||
# again, this time with keys
|
||||
connect con1, localhost,test1,pwd,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
disconnect con1;
|
||||
connect con2, localhost,test1,pwd,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
disconnect con2;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error ER_ACCESS_DENIED_ERROR;
|
||||
connect con3, localhost,test1,wrong_pwd,,,,TCP NOSSL;
|
||||
connect con4, localhost,test2,,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
disconnect con4;
|
||||
replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT;
|
||||
error ER_ACCESS_DENIED_ERROR;
|
||||
connect con5, localhost,test2 pwd,,,,,TCP NOSSL;
|
||||
connection default;
|
||||
|
||||
#
|
||||
# Compatibility with MySQL password hashes
|
||||
#
|
||||
create user u1@localhost identified via caching_sha2_password using '$A$005$5dx;X)z |kX]\ZNx7QTrl0oTy2C0/f4bggQMFIDnSDeZ7koLoO417jc9D';
|
||||
create user u2@localhost identified via caching_sha2_password using '$A$005$dL\Zq]<7d[YAbk}x!;^.qMuuUUBmB5aF7x7GsAKZzpb24p94NCCs8qPgwAvwc1';
|
||||
create user u3@localhost identified via caching_sha2_password using '$A$005$L9\ZKiwT''=%dMoqrPGFbywI9G8NecJqiy9D04S2abTLRvD32powG8nIxI9';
|
||||
|
||||
grant execute on test.* to u1@localhost, u2@localhost, u3@localhost;
|
||||
|
||||
connect u1,localhost,u1,abcd,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
disconnect u1;
|
||||
connect u2,localhost,u2,efghi,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
disconnect u2;
|
||||
connect u3,localhost,u3,xyz,,,,TCP NOSSL;
|
||||
call checkme();
|
||||
disconnect u3;
|
||||
|
||||
# cleanup
|
||||
connection default;
|
||||
|
||||
drop user u1@localhost;
|
||||
drop user u2@localhost;
|
||||
drop user u3@localhost;
|
||||
|
||||
let datadir=`select @@datadir`;
|
||||
remove_file $datadir/private_key.pem;
|
||||
remove_file $datadir/public_key.pem;
|
||||
let $restart_parameters=;
|
||||
source include/restart_mysqld.inc;
|
||||
show status like 'caching_sha2_password%';
|
||||
source fini.inc;
|
256
plugin/auth_mysql_sha2/mysql_sha2.c
Normal file
256
plugin/auth_mysql_sha2/mysql_sha2.c
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
Copyright (c) 2025, MariaDB plc
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#if _WIN32
|
||||
#include <io.h>
|
||||
#define access _access
|
||||
#define F_OK 0
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "mysql_sha2.h"
|
||||
#include <mysql/plugin_auth.h>
|
||||
#include <mysqld_error.h>
|
||||
|
||||
char *private_key_path, *public_key_path, public_key[1024]={0};
|
||||
size_t public_key_len=0;
|
||||
EVP_PKEY *private_key= 0;
|
||||
|
||||
static my_bool auto_generate_keys;
|
||||
static unsigned int digest_rounds;
|
||||
|
||||
struct digest {
|
||||
unsigned int iterations;
|
||||
unsigned char salt[SCRAMBLE_LENGTH];
|
||||
unsigned char crypted[SHA256CRYPT_LEN];
|
||||
};
|
||||
|
||||
#define PASSWORD_LEN (SCRAMBLE_LENGTH + SHA256CRYPT_LEN + sizeof("$A$005$")-1)
|
||||
|
||||
#define ITERATION_MULTIPLIER 1000
|
||||
|
||||
static unsigned char request_public_key = '\2';
|
||||
static unsigned char perform_full_authentication = '\4';
|
||||
|
||||
static void make_salt(unsigned char *to)
|
||||
{
|
||||
unsigned char *end= to + SCRAMBLE_LENGTH;
|
||||
my_random_bytes(to, SCRAMBLE_LENGTH);
|
||||
for (; to < end; to++)
|
||||
*to = (*to % 90) + '$' + 1;
|
||||
/* in MySQL: if (*to == '\0' || *to == '$') (*to)++; */
|
||||
}
|
||||
|
||||
static int auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
|
||||
{
|
||||
struct digest *authstr;
|
||||
unsigned char to[SHA256CRYPT_LEN];
|
||||
unsigned char scramble[SCRAMBLE_LENGTH + 1], *pkt;
|
||||
int pkt_len;
|
||||
MYSQL_PLUGIN_VIO_INFO vio_info;
|
||||
unsigned char plain_text[1025];
|
||||
size_t plain_text_len= sizeof(plain_text)-1;
|
||||
|
||||
make_salt(scramble);
|
||||
scramble[SCRAMBLE_LENGTH]= 0;
|
||||
if (vio->write_packet(vio, scramble, sizeof(scramble)))
|
||||
return CR_ERROR;
|
||||
|
||||
if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
|
||||
return CR_ERROR;
|
||||
|
||||
if (!pkt_len || (pkt_len == 1 && *pkt == 0)) /* sic! */
|
||||
{
|
||||
if (info->auth_string_length == 0)
|
||||
return CR_OK;
|
||||
return CR_AUTH_USER_CREDENTIALS;
|
||||
}
|
||||
info->password_used= PASSWORD_USED_YES;
|
||||
|
||||
if (info->auth_string_length == 0)
|
||||
return CR_AUTH_USER_CREDENTIALS;
|
||||
|
||||
if (pkt_len != SHA256_DIGEST_LENGTH)
|
||||
return CR_ERROR;
|
||||
|
||||
/*
|
||||
TODO support caching: user@host -> plaintext password
|
||||
but for now - request full auth unconditionally
|
||||
*/
|
||||
|
||||
if (vio->write_packet(vio, &perform_full_authentication, 1))
|
||||
return CR_ERROR;
|
||||
|
||||
if ((pkt_len= vio->read_packet(vio, &pkt)) <= 0)
|
||||
return CR_ERROR;
|
||||
|
||||
vio->info(vio, &vio_info);
|
||||
/* secure transport, as in MySQL. SSL is "secure" even if not verified */
|
||||
if (vio_info.protocol == MYSQL_VIO_TCP && !vio_info.tls)
|
||||
{
|
||||
if (!private_key || !public_key_len)
|
||||
{
|
||||
my_printf_error(1, SELF ": Authentication requires either RSA keys "
|
||||
"or secure transport", ME_ERROR_LOG_ONLY);
|
||||
return CR_AUTH_PLUGIN_ERROR;
|
||||
}
|
||||
|
||||
if (pkt_len == 1 && *pkt == request_public_key)
|
||||
{
|
||||
if (vio->write_packet(vio, (unsigned char *)public_key,
|
||||
(int)public_key_len))
|
||||
return CR_ERROR;
|
||||
if ((pkt_len= vio->read_packet(vio, &pkt)) <= 0)
|
||||
return CR_ERROR;
|
||||
}
|
||||
|
||||
if (ssl_decrypt(private_key, pkt, pkt_len, plain_text, &plain_text_len))
|
||||
return CR_ERROR;
|
||||
|
||||
for (size_t i=0; i < plain_text_len; i++)
|
||||
plain_text[i]^= scramble[i % SCRAMBLE_LENGTH];
|
||||
pkt= plain_text;
|
||||
pkt_len= (int)plain_text_len;
|
||||
}
|
||||
/* now pkt contains plaintext password */
|
||||
|
||||
authstr= (struct digest*)info->auth_string;
|
||||
sha256_crypt_r(pkt, pkt_len-1, authstr->salt, sizeof(authstr->salt),
|
||||
to, authstr->iterations);
|
||||
|
||||
if (memcmp(to, authstr->crypted, SHA256CRYPT_LEN))
|
||||
return CR_AUTH_USER_CREDENTIALS;
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
static int password_hash(const char *password, size_t password_length,
|
||||
char *hash, size_t *hash_length)
|
||||
{
|
||||
struct digest authstr;
|
||||
|
||||
if (*hash_length < PASSWORD_LEN)
|
||||
return 1;
|
||||
|
||||
if (!password_length)
|
||||
return (int)(*hash_length= 0);
|
||||
|
||||
make_salt(authstr.salt);
|
||||
sha256_crypt_r((unsigned char*)password, password_length,
|
||||
authstr.salt, sizeof(authstr.salt),
|
||||
authstr.crypted, digest_rounds);
|
||||
*hash_length= my_snprintf(hash, *hash_length, "$A$%03X$%.20s%.43s",
|
||||
digest_rounds/ITERATION_MULTIPLIER,
|
||||
authstr.salt, authstr.crypted);
|
||||
assert(*hash_length == PASSWORD_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int digest_to_binary(const char *hash, size_t hash_length,
|
||||
unsigned char *out, size_t *out_length)
|
||||
{
|
||||
struct digest *authstr= (struct digest*)out;
|
||||
assert(*out_length > sizeof(*authstr));
|
||||
*out_length= sizeof(*authstr);
|
||||
memset(out, 0, *out_length);
|
||||
if (hash_length != PASSWORD_LEN)
|
||||
{
|
||||
my_printf_error(ER_PASSWD_LENGTH, "Password hash should be "
|
||||
"%zu characters long", 0, PASSWORD_LEN);
|
||||
return 1;
|
||||
}
|
||||
if (sscanf(hash, "$A$%X$%20c%43c", &authstr->iterations, authstr->salt,
|
||||
authstr->crypted) < 3)
|
||||
{
|
||||
my_printf_error(ER_PASSWD_LENGTH, "Invalid password hash", 0);
|
||||
return 1;
|
||||
}
|
||||
authstr->iterations*= ITERATION_MULTIPLIER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct st_mysql_auth info=
|
||||
{
|
||||
MYSQL_AUTHENTICATION_INTERFACE_VERSION,
|
||||
SELF, auth, password_hash, digest_to_binary
|
||||
};
|
||||
|
||||
static MYSQL_SYSVAR_STR(private_key_path, private_key_path, PLUGIN_VAR_READONLY,
|
||||
"A path to the private RSA key used for authentication",
|
||||
NULL, NULL, "private_key.pem");
|
||||
|
||||
static MYSQL_SYSVAR_STR(public_key_path, public_key_path, PLUGIN_VAR_READONLY,
|
||||
"A path to the public RSA key used for authentication",
|
||||
NULL, NULL, "public_key.pem");
|
||||
|
||||
static MYSQL_SYSVAR_BOOL(auto_generate_rsa_keys, auto_generate_keys,
|
||||
PLUGIN_VAR_READONLY | PLUGIN_VAR_OPCMDARG,
|
||||
"Auto generate RSA keys at server startup if key paths "
|
||||
"are not explicitly set and key files are not present "
|
||||
"at their default locations", NULL, NULL, 1);
|
||||
|
||||
static MYSQL_SYSVAR_UINT(digest_rounds, digest_rounds, PLUGIN_VAR_READONLY,
|
||||
"Number of SHA2 rounds to be performed when computing a password hash",
|
||||
NULL, NULL, 5000, 5000, 0xfff * ITERATION_MULTIPLIER, 1);
|
||||
|
||||
static int init_keys(void *p)
|
||||
{
|
||||
if (private_key_path == MYSQL_SYSVAR_NAME(private_key_path).def_val &&
|
||||
public_key_path == MYSQL_SYSVAR_NAME(public_key_path).def_val &&
|
||||
access(private_key_path, F_OK) && access(public_key_path, F_OK) &&
|
||||
auto_generate_keys)
|
||||
ssl_genkeys();
|
||||
ssl_loadkeys();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int free_keys(void *p)
|
||||
{
|
||||
EVP_PKEY_free(private_key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct st_mysql_sys_var *sysvars[]=
|
||||
{
|
||||
MYSQL_SYSVAR(private_key_path),
|
||||
MYSQL_SYSVAR(public_key_path),
|
||||
MYSQL_SYSVAR(auto_generate_rsa_keys),
|
||||
MYSQL_SYSVAR(digest_rounds),
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct st_mysql_show_var status_variables[]=
|
||||
{
|
||||
{"rsa_public_key", public_key, SHOW_CHAR},
|
||||
{NULL, NULL, 0}
|
||||
};
|
||||
|
||||
maria_declare_plugin(auth_mysql_sha2)
|
||||
{
|
||||
MYSQL_AUTHENTICATION_PLUGIN,
|
||||
&info,
|
||||
SELF,
|
||||
"Oracle Corporation, Sergei Golubchik",
|
||||
"MySQL-compatible SHA2 authentication",
|
||||
PLUGIN_LICENSE_GPL,
|
||||
init_keys,
|
||||
free_keys,
|
||||
0x0100,
|
||||
status_variables,
|
||||
sysvars,
|
||||
"1.0",
|
||||
MariaDB_PLUGIN_MATURITY_GAMMA
|
||||
}
|
||||
maria_declare_plugin_end;
|
41
plugin/auth_mysql_sha2/mysql_sha2.h
Normal file
41
plugin/auth_mysql_sha2/mysql_sha2.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright (c) 2025, MariaDB plc
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#include <string.h>
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <mysql/plugin.h>
|
||||
|
||||
#define SELF "caching_sha2_password"
|
||||
#define SHA256CRYPT_LEN 43
|
||||
|
||||
extern char *private_key_path, *public_key_path, public_key[1024];
|
||||
extern size_t public_key_len;
|
||||
extern EVP_PKEY *private_key;
|
||||
|
||||
int ssl_decrypt(EVP_PKEY *pkey, unsigned char *src, size_t srclen,
|
||||
unsigned char *dst, size_t *dstlen);
|
||||
int ssl_genkeys();
|
||||
int ssl_loadkeys();
|
||||
|
||||
void sha256_crypt_r(const unsigned char *key, size_t key_len,
|
||||
const unsigned char *salt, size_t salt_len,
|
||||
unsigned char *buffer, size_t rounds);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
EVP_PKEY *EVP_RSA_gen(unsigned int bits);
|
||||
#endif
|
39
plugin/auth_mysql_sha2/openssl1-compat.c
Normal file
39
plugin/auth_mysql_sha2/openssl1-compat.c
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (c) 2025, MariaDB plc
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#include "mysql_sha2.h"
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
EVP_PKEY *EVP_RSA_gen(unsigned int bits)
|
||||
{
|
||||
EVP_PKEY_CTX *ctx;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
if (EVP_PKEY_keygen_init(ctx) <= 0 ||
|
||||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0)
|
||||
goto err;
|
||||
|
||||
EVP_PKEY_keygen(ctx, &pkey);
|
||||
|
||||
err:
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return pkey;
|
||||
}
|
||||
#endif
|
120
plugin/auth_mysql_sha2/sha256crypt.c
Normal file
120
plugin/auth_mysql_sha2/sha256crypt.c
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright (c) 2025, MariaDB plc
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#include <my_alloca.h>
|
||||
#include "mysql_sha2.h"
|
||||
|
||||
/* based on https://www.akkadia.org/drepper/SHA-crypt.txt */
|
||||
|
||||
/* SHA256-based Unix crypt implementation.
|
||||
Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>. */
|
||||
|
||||
void sha256_crypt_r(const unsigned char *key, size_t key_len,
|
||||
const unsigned char *salt, size_t salt_len,
|
||||
unsigned char *buffer, size_t rounds)
|
||||
{
|
||||
unsigned char tmp[SHA256_DIGEST_LENGTH];
|
||||
unsigned char alt[SHA256_DIGEST_LENGTH];
|
||||
size_t cnt;
|
||||
static const char b64t[64] =
|
||||
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
void *ctx = alloca(my_sha256_context_size());
|
||||
unsigned char *p_bytes = alloca(key_len);
|
||||
unsigned char *s_bytes = alloca(salt_len);
|
||||
|
||||
my_sha256_multi(alt, key, key_len, salt, salt_len, key, key_len, NULL);
|
||||
|
||||
my_sha256_init(ctx);
|
||||
my_sha256_input(ctx, key, key_len);
|
||||
my_sha256_input(ctx, salt, salt_len);
|
||||
/* Add for every byte in the key one byte of the alternate sum. */
|
||||
for (cnt = key_len; cnt > sizeof(alt); cnt -= sizeof(alt))
|
||||
my_sha256_input(ctx, alt, sizeof(alt));
|
||||
my_sha256_input(ctx, alt, cnt);
|
||||
/* Take the binary representation of the length of the key and for every
|
||||
1 add the alternate sum, for every 0 the key. */
|
||||
for (cnt = key_len; cnt > 0; cnt >>= 1)
|
||||
if ((cnt & 1) != 0)
|
||||
my_sha256_input(ctx, alt, sizeof(alt));
|
||||
else
|
||||
my_sha256_input(ctx, key, key_len);
|
||||
my_sha256_result(ctx, alt);
|
||||
|
||||
/* Start computing S byte sequence. */
|
||||
my_sha256_init(ctx);
|
||||
for (cnt = 0; cnt < 16u + alt[0]; ++cnt)
|
||||
my_sha256_input(ctx, salt, salt_len);
|
||||
my_sha256_result(ctx, tmp);
|
||||
|
||||
/* Create byte sequence S. */
|
||||
for (cnt = salt_len; cnt >= sizeof(tmp); cnt -= sizeof(tmp))
|
||||
memcpy(s_bytes + salt_len - cnt, tmp, sizeof(tmp));
|
||||
memcpy(s_bytes + salt_len - cnt, tmp, cnt);
|
||||
|
||||
/* Start computing P byte sequence. */
|
||||
my_sha256_init(ctx);
|
||||
for (cnt = 0; cnt < key_len; ++cnt)
|
||||
my_sha256_input(ctx, key, key_len);
|
||||
my_sha256_result(ctx, tmp);
|
||||
|
||||
/* Create byte sequence P. */
|
||||
for (cnt = key_len; cnt >= sizeof(tmp); cnt -= sizeof(tmp))
|
||||
memcpy(p_bytes + key_len - cnt, tmp, sizeof(tmp));
|
||||
memcpy(p_bytes + key_len - cnt, tmp, cnt);
|
||||
|
||||
/* Repeatedly run the collected hash value through SHA256 to burn
|
||||
CPU cycles. */
|
||||
for (cnt = 0; cnt < rounds; ++cnt)
|
||||
{
|
||||
my_sha256_init(ctx);
|
||||
if ((cnt & 1) != 0)
|
||||
my_sha256_input(ctx, p_bytes, key_len);
|
||||
else
|
||||
my_sha256_input(ctx, cnt ? tmp : alt, sizeof(tmp));
|
||||
if (cnt % 3 != 0)
|
||||
my_sha256_input(ctx, s_bytes, salt_len);
|
||||
if (cnt % 7 != 0)
|
||||
my_sha256_input(ctx, p_bytes, key_len);
|
||||
if ((cnt & 1) != 0)
|
||||
my_sha256_input(ctx, tmp, sizeof(tmp));
|
||||
else
|
||||
my_sha256_input(ctx, p_bytes, key_len);
|
||||
my_sha256_result(ctx, tmp);
|
||||
}
|
||||
|
||||
#define b64_from_24bit(B2, B1, B0, N) \
|
||||
do { \
|
||||
unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
|
||||
int n = (N); \
|
||||
while (n-- > 0) \
|
||||
{ \
|
||||
*buffer++ = b64t[w & 0x3f]; \
|
||||
w >>= 6; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
b64_from_24bit (tmp[0], tmp[10], tmp[20], 4);
|
||||
b64_from_24bit (tmp[21], tmp[1], tmp[11], 4);
|
||||
b64_from_24bit (tmp[12], tmp[22], tmp[2], 4);
|
||||
b64_from_24bit (tmp[3], tmp[13], tmp[23], 4);
|
||||
b64_from_24bit (tmp[24], tmp[4], tmp[14], 4);
|
||||
b64_from_24bit (tmp[15], tmp[25], tmp[5], 4);
|
||||
b64_from_24bit (tmp[6], tmp[16], tmp[26], 4);
|
||||
b64_from_24bit (tmp[27], tmp[7], tmp[17], 4);
|
||||
b64_from_24bit (tmp[18], tmp[28], tmp[8], 4);
|
||||
b64_from_24bit (tmp[9], tmp[19], tmp[29], 4);
|
||||
b64_from_24bit (0, tmp[31], tmp[30], 3); /* == 43 bytes in total */
|
||||
}
|
136
plugin/auth_mysql_sha2/ssl_stuff.c
Normal file
136
plugin/auth_mysql_sha2/ssl_stuff.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (c) 2025, MariaDB plc
|
||||
|
||||
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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#include "mysql_sha2.h"
|
||||
#include <errno.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
/* openssl rsautl -decrypt -inkey private_key.pem -in src -out dst */
|
||||
int ssl_decrypt(EVP_PKEY *pkey, unsigned char *src, size_t srclen,
|
||||
unsigned char *dst, size_t *dstlen)
|
||||
{
|
||||
int res;
|
||||
EVP_PKEY_CTX *ctx= EVP_PKEY_CTX_new(pkey, NULL);
|
||||
if (!ctx)
|
||||
return 1;
|
||||
res= EVP_PKEY_decrypt_init(ctx) <= 0 ||
|
||||
EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0 ||
|
||||
EVP_PKEY_decrypt(ctx, dst, dstlen, src, srclen) <= 0;
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
#define FILE_ERROR(op,file) \
|
||||
do { \
|
||||
my_printf_error(1, SELF ": failed to " op " %s: %iE", \
|
||||
ME_ERROR_LOG_ONLY, file, errno); \
|
||||
goto err; \
|
||||
} while(0)
|
||||
|
||||
#define SSL_ERROR(op,file) \
|
||||
do { \
|
||||
unsigned long e= ERR_get_error(); \
|
||||
my_printf_error(1, SELF ": failed to " op " %s: %lu - %s", \
|
||||
ME_ERROR_LOG_ONLY, file, e, ERR_reason_error_string(e)); \
|
||||
goto err; \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
openssl genrsa -out private_key.pem 2048
|
||||
openssl rsa -in private_key.pem -pubout -out public_key.pem
|
||||
*/
|
||||
int ssl_genkeys()
|
||||
{
|
||||
#ifdef OPENSSL_IS_WOLFSSL
|
||||
/*
|
||||
doesn't have few functions from below and libmariadb doesn't support RSA
|
||||
encryption anyway, so not worth bothering
|
||||
*/
|
||||
my_printf_error(1, SELF ": cannot auto-generate keys with WolfSSL",
|
||||
ME_ERROR_LOG_ONLY);
|
||||
return 1;
|
||||
#else
|
||||
EVP_PKEY *pkey;
|
||||
FILE *f= NULL;
|
||||
|
||||
if (!(pkey= EVP_RSA_gen(2048)))
|
||||
goto err;
|
||||
|
||||
if (!(f= fopen(private_key_path, "w")))
|
||||
FILE_ERROR("write", private_key_path);
|
||||
|
||||
if (PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL) != 1)
|
||||
SSL_ERROR("write", private_key_path);
|
||||
fclose(f);
|
||||
|
||||
if (!(f= fopen(public_key_path, "w")))
|
||||
FILE_ERROR("write", public_key_path);
|
||||
|
||||
if (PEM_write_PUBKEY(f, pkey) != 1)
|
||||
SSL_ERROR("write", public_key_path);
|
||||
fclose(f);
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (f)
|
||||
fclose(f);
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ssl_loadkeys()
|
||||
{
|
||||
EVP_PKEY *pkey= 0;
|
||||
FILE *f;
|
||||
size_t len;
|
||||
|
||||
if (!(f= fopen(private_key_path, "r")))
|
||||
FILE_ERROR("read", private_key_path);
|
||||
|
||||
if (!(pkey= PEM_read_PrivateKey(f, NULL, NULL, NULL)))
|
||||
SSL_ERROR("read", private_key_path);
|
||||
fclose(f);
|
||||
|
||||
if (!(f= fopen(public_key_path, "r")))
|
||||
FILE_ERROR("read", public_key_path);
|
||||
len= fread(public_key, 1, sizeof(public_key)-1, f);
|
||||
|
||||
if (!feof(f))
|
||||
{
|
||||
my_printf_error(1, SELF ": failed to read %s: larger than %zu",
|
||||
ME_ERROR_LOG_ONLY, private_key_path, sizeof(public_key)-1);
|
||||
goto err;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
public_key[len]= 0;
|
||||
public_key_len= len;
|
||||
private_key= pkey;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (f)
|
||||
fclose(f);
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
return 1;
|
||||
}
|
@ -2395,6 +2395,7 @@ void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
|
||||
info->protocol= addr.sa_family == AF_UNIX ?
|
||||
MYSQL_VIO_SOCKET : MYSQL_VIO_TCP;
|
||||
info->socket= (int)vio_fd(vio);
|
||||
info->tls= 1;
|
||||
return;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
|
Loading…
x
Reference in New Issue
Block a user