From 3889d13317a2d4953d3a5d088955652b8ae88f35 Mon Sep 17 00:00:00 2001 From: Yura Sorokin Date: Mon, 11 Mar 2019 07:26:46 -0700 Subject: [PATCH] FB8-53: Expose user certificate details to command line (#959) Summary: Jira ticket: https://jira.percona.com/browse/FB8-53 Reference Patch: https://github.com/facebook/mysql-5.6/commit/f29eb03 Reference Patch: https://github.com/facebook/mysql-5.6/commit/c631017 'THD' class extended with the following convenience methods: - 'has_net_vio()' - 'get_net_vio()' - 'has_net_vio_ssl_arg()' - 'get_net_vio_ssl_arg()' - 'get_peer_certificate_info()' Implemented new 'Fill_authinfo_list' class derived from 'Do_THD_Impl' which populates 'INFORMATION_SCHEMA.AUTHINFO' table. This is a change to help the implementation of SSL based authentication efforts. Currently the certificate details are unavailable to the sys admin. The v3 extensions usually carry important information for identifying the user. This change adds a new table to the information schema to expose auth details. It is organized by process ID like processlist table. Pull Request resolved: https://github.com/facebook/mysql-5.6/pull/959 Reviewed By: lth Differential Revision: D14137362 Pulled By: lth fbshipit-source-id: b42aeba76c3 --- mysql-test/r/dd_is_compatibility_ci.result | 1 + mysql-test/r/dd_is_compatibility_cs.result | 1 + .../r/information_schema_authinfo.result | 6 + .../information_schema_authinfo_nossl.result | 6 + mysql-test/r/information_schema_ci.result | 8 +- mysql-test/r/information_schema_cs.result | 8 +- mysql-test/r/information_schema_db.result | 1 + mysql-test/r/mysqlshow_ci.result | 2 + mysql-test/r/mysqlshow_cs.result | 2 + .../audit_null/r/event_params_cert.result | 2 +- .../suite/audit_null/t/event_params_cert.test | 5 +- .../suite/funcs_1/r/is_columns_is_ci.result | 10 ++ .../suite/funcs_1/r/is_columns_is_cs.result | 10 ++ .../suite/funcs_1/r/is_tables_is.result | 46 ++++++++ .../t/information_schema_authinfo-client.opt | 4 + .../t/information_schema_authinfo-master.opt | 4 + mysql-test/t/information_schema_authinfo.test | 14 +++ .../t/information_schema_authinfo_nossl.test | 12 ++ sql/auth/sql_authentication.cc | 9 +- sql/sql_audit.cc | 5 +- sql/sql_class.cc | 49 ++++++-- sql/sql_class.h | 31 ++++- sql/sql_show.cc | 108 ++++++++++++++++++ 23 files changed, 321 insertions(+), 23 deletions(-) create mode 100644 mysql-test/r/information_schema_authinfo.result create mode 100644 mysql-test/r/information_schema_authinfo_nossl.result create mode 100644 mysql-test/t/information_schema_authinfo-client.opt create mode 100644 mysql-test/t/information_schema_authinfo-master.opt create mode 100644 mysql-test/t/information_schema_authinfo.test create mode 100644 mysql-test/t/information_schema_authinfo_nossl.test diff --git a/mysql-test/r/dd_is_compatibility_ci.result b/mysql-test/r/dd_is_compatibility_ci.result index 6b7618a69b40..0be81318dc45 100644 --- a/mysql-test/r/dd_is_compatibility_ci.result +++ b/mysql-test/r/dd_is_compatibility_ci.result @@ -153,6 +153,7 @@ WHERE table_schema LIKE 'information_schema' ORDER BY table_name COLLATE UTF8_GENERAL_CI; SELECT * FROM v1; table_name +AUTHINFO CHARACTER_SETS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY diff --git a/mysql-test/r/dd_is_compatibility_cs.result b/mysql-test/r/dd_is_compatibility_cs.result index 3eb794d4e8a7..3a06fa7233d1 100644 --- a/mysql-test/r/dd_is_compatibility_cs.result +++ b/mysql-test/r/dd_is_compatibility_cs.result @@ -153,6 +153,7 @@ WHERE table_schema LIKE 'information_schema' ORDER BY table_name COLLATE UTF8_GENERAL_CI; SELECT * FROM v1; table_name +AUTHINFO CHARACTER_SETS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY diff --git a/mysql-test/r/information_schema_authinfo.result b/mysql-test/r/information_schema_authinfo.result new file mode 100644 index 000000000000..92491a82a1fd --- /dev/null +++ b/mysql-test/r/information_schema_authinfo.result @@ -0,0 +1,6 @@ +# +# Test case for information_schema.authinfo table +# (SSL connection) +# +include/assert.inc [SSL field for the current connection must be set to 1] +include/assert.inc [Info field (peer certificate) for the current connection must be non-empty] diff --git a/mysql-test/r/information_schema_authinfo_nossl.result b/mysql-test/r/information_schema_authinfo_nossl.result new file mode 100644 index 000000000000..dc8f6befd719 --- /dev/null +++ b/mysql-test/r/information_schema_authinfo_nossl.result @@ -0,0 +1,6 @@ +# +# Test case for information_schema.authinfo table +# (non-SSL connection) +# +include/assert.inc [SSL field for the current connection must be set to 0] +include/assert.inc [Info field (peer certificate) for the current connection must be NULL] diff --git a/mysql-test/r/information_schema_ci.result b/mysql-test/r/information_schema_ci.result index 8709532143b3..63958e3663a6 100644 --- a/mysql-test/r/information_schema_ci.result +++ b/mysql-test/r/information_schema_ci.result @@ -60,6 +60,7 @@ table_name COLLATE utf8_general_ci not like 'ndb_%' AND table_name COLLATE utf8_general_ci not like 'innodb_%'; select * from v1; c +AUTHINFO CHARACTER_SETS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY @@ -609,8 +610,8 @@ from information_schema.tables where table_schema='information_schema' order by table_name collate utf8_general_ci limit 2; TABLE_NAME TABLE_TYPE ENGINE +AUTHINFO SYSTEM VIEW NULL CHARACTER_SETS SYSTEM VIEW NULL -COLLATIONS SYSTEM VIEW NULL show tables from information_schema like "T%"; Tables_in_information_schema (T%) TABLES @@ -846,7 +847,7 @@ table_schema IN ('mysql', 'information_schema', 'test', 'mysqltest') AND table_name not like 'ndb%' AND table_name COLLATE utf8_general_ci not like 'innodb_%' GROUP BY TABLE_SCHEMA; TABLE_SCHEMA count(*) -information_schema 35 +information_schema 36 mysql 31 create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row @@ -1287,6 +1288,7 @@ group by c2.column_type order by num limit 1) and t.table_name not like 'INNODB_%' group by t.table_name order by num1, t.table_name COLLATE utf8_general_ci; TABLE_NAME group_concat(t.table_schema, '.', t.table_name) num1 +AUTHINFO information_schema.AUTHINFO 1 CHARACTER_SETS information_schema.CHARACTER_SETS 1 COLLATIONS information_schema.COLLATIONS 1 COLLATION_CHARACTER_SET_APPLICABILITY information_schema.COLLATION_CHARACTER_SET_APPLICABILITY 1 @@ -2418,6 +2420,7 @@ AND t.table_name NOT LIKE 'ndb%' ORDER BY t.table_name COLLATE utf8_general_ci, c1.column_name COLLATE utf8_general_ci; TABLE_NAME COLUMN_NAME +AUTHINFO ID CHARACTER_SETS CHARACTER_SET_NAME COLLATIONS COLLATION_NAME COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME @@ -2472,6 +2475,7 @@ AND t.table_name NOT LIKE 'ndb%' ORDER BY t.table_name COLLATE utf8_general_ci, c1.column_name COLLATE utf8_general_ci; TABLE_NAME COLUMN_NAME +AUTHINFO ID CHARACTER_SETS CHARACTER_SET_NAME COLLATIONS COLLATION_NAME COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME diff --git a/mysql-test/r/information_schema_cs.result b/mysql-test/r/information_schema_cs.result index bf06b16d49c6..1f23c077c4b2 100644 --- a/mysql-test/r/information_schema_cs.result +++ b/mysql-test/r/information_schema_cs.result @@ -60,6 +60,7 @@ table_name COLLATE utf8_general_ci not like 'ndb_%' AND table_name COLLATE utf8_general_ci not like 'innodb_%'; select * from v1; c +AUTHINFO CHARACTER_SETS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY @@ -609,8 +610,8 @@ from information_schema.tables where table_schema='information_schema' order by table_name collate utf8_general_ci limit 2; TABLE_NAME TABLE_TYPE ENGINE +AUTHINFO SYSTEM VIEW NULL CHARACTER_SETS SYSTEM VIEW NULL -COLLATIONS SYSTEM VIEW NULL show tables from information_schema like "T%"; Tables_in_information_schema (T%) TABLES @@ -846,7 +847,7 @@ table_schema IN ('mysql', 'information_schema', 'test', 'mysqltest') AND table_name not like 'ndb%' AND table_name COLLATE utf8_general_ci not like 'innodb_%' GROUP BY TABLE_SCHEMA; TABLE_SCHEMA count(*) -information_schema 35 +information_schema 36 mysql 31 create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row @@ -1287,6 +1288,7 @@ group by c2.column_type order by num limit 1) and t.table_name not like 'INNODB_%' group by t.table_name order by num1, t.table_name COLLATE utf8_general_ci; TABLE_NAME group_concat(t.table_schema, '.', t.table_name) num1 +AUTHINFO information_schema.AUTHINFO 1 CHARACTER_SETS information_schema.CHARACTER_SETS 1 COLLATIONS information_schema.COLLATIONS 1 COLLATION_CHARACTER_SET_APPLICABILITY information_schema.COLLATION_CHARACTER_SET_APPLICABILITY 1 @@ -2418,6 +2420,7 @@ AND t.table_name NOT LIKE 'ndb%' ORDER BY t.table_name COLLATE utf8_general_ci, c1.column_name COLLATE utf8_general_ci; TABLE_NAME COLUMN_NAME +AUTHINFO ID CHARACTER_SETS CHARACTER_SET_NAME COLLATIONS COLLATION_NAME COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME @@ -2472,6 +2475,7 @@ AND t.table_name NOT LIKE 'ndb%' ORDER BY t.table_name COLLATE utf8_general_ci, c1.column_name COLLATE utf8_general_ci; TABLE_NAME COLUMN_NAME +AUTHINFO ID CHARACTER_SETS CHARACTER_SET_NAME COLLATIONS COLLATION_NAME COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index e18525136d7e..c6f7c6b60e21 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -6,6 +6,7 @@ drop function if exists f2; use INFORMATION_SCHEMA; show tables where Tables_in_information_schema NOT LIKE 'INNODB%' and Tables_in_information_schema NOT LIKE 'ndb%'; Tables_in_information_schema +AUTHINFO CHARACTER_SETS COLLATIONS COLLATION_CHARACTER_SET_APPLICABILITY diff --git a/mysql-test/r/mysqlshow_ci.result b/mysql-test/r/mysqlshow_ci.result index b209a9087af0..9b286328f49c 100644 --- a/mysql-test/r/mysqlshow_ci.result +++ b/mysql-test/r/mysqlshow_ci.result @@ -79,6 +79,7 @@ Database: information_schema +---------------------------------------+ | Tables | +---------------------------------------+ +| AUTHINFO | | CHARACTER_SETS | | COLLATION_CHARACTER_SET_APPLICABILITY | | COLLATIONS | @@ -150,6 +151,7 @@ Database: INFORMATION_SCHEMA +---------------------------------------+ | Tables | +---------------------------------------+ +| AUTHINFO | | CHARACTER_SETS | | COLLATION_CHARACTER_SET_APPLICABILITY | | COLLATIONS | diff --git a/mysql-test/r/mysqlshow_cs.result b/mysql-test/r/mysqlshow_cs.result index 270372da1932..63dbf741a10c 100644 --- a/mysql-test/r/mysqlshow_cs.result +++ b/mysql-test/r/mysqlshow_cs.result @@ -79,6 +79,7 @@ Database: information_schema +---------------------------------------+ | Tables | +---------------------------------------+ +| AUTHINFO | | CHARACTER_SETS | | COLLATIONS | | COLLATION_CHARACTER_SET_APPLICABILITY | @@ -150,6 +151,7 @@ Database: INFORMATION_SCHEMA +---------------------------------------+ | Tables | +---------------------------------------+ +| AUTHINFO | | CHARACTER_SETS | | COLLATIONS | | COLLATION_CHARACTER_SET_APPLICABILITY | diff --git a/mysql-test/suite/audit_null/r/event_params_cert.result b/mysql-test/suite/audit_null/r/event_params_cert.result index 2289e5fa89b8..9023323531c5 100644 --- a/mysql-test/suite/audit_null/r/event_params_cert.result +++ b/mysql-test/suite/audit_null/r/event_params_cert.result @@ -8,7 +8,7 @@ SELECT * FROM foo; i SHOW STATUS LIKE "Audit_null_generic_event_response"; Variable_name Value -Audit_null_generic_event_response connection_certificate:-----BEGIN CERTIFICATE-----\nMIIDyDCCArCgAwIBAgIJAOG0pVw936YVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV\nBAYTAlNFMRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAcMCVN0b2NraG9sbTEP\nMA0GA1UECgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDELMAkGA1UEAwwCQ0EwHhcN\nMTQxMjA1MDQ0OTIzWhcNMjkxMjAxMDQ0OTIzWjBnMQswCQYDVQQGEwJTRTESMBAG\nA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlTdG9ja2hvbG0xDzANBgNVBAoMBk9y\nYWNsZTEOMAwGA1UECwwFTXlTUUwxDzANBgNVBAMMBkNsaWVudDCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBAMjRof6kjPMbF3EbdDUR4A5sQAr7wPfw67vJ\nHaHH17CK9vHP+mvQeWTru2mlDYAG31IU0oUyz7/OKkcoW80LKKu7BzPVi9O0csSm\ntcw3uQOoeFYlWB8XMHzRCrvsPKMDkJeZkkmus1eWXBrp6AIjrsjJBVBj5XehmnMG\ndA5GUCjYyU/EHDe4UhgLrxkr1OVmdKTz8No +Audit_null_generic_event_response connection_certificate:-----BEGIN CERTIFICATE-----\nMIIDyDCCArCgAwIBAgIJAOG0pVw936YVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV\nBAYTAlNFMRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAcMCVN0b2NraG9sbTEP\nMA0GA1UECgwGT3JhY2xlMQ4wDAYDVQQLDAVNeVNRTDELMAkGA1UEAwwCQ0EwHhcN\nMTQxMjA1MDQ0OTIzWhcNMjkxMjAx DROP USER cert_auth@localhost; DROP TABLE foo; UNINSTALL PLUGIN null_audit; diff --git a/mysql-test/suite/audit_null/t/event_params_cert.test b/mysql-test/suite/audit_null/t/event_params_cert.test index 97cca0a28a68..dee3749050af 100644 --- a/mysql-test/suite/audit_null/t/event_params_cert.test +++ b/mysql-test/suite/audit_null/t/event_params_cert.test @@ -14,7 +14,10 @@ SET @@null_audit_extended_log = 1; SELECT * FROM foo; ---replace_regex /.*(connection_certificate:[^;]*).*/\1/ +# As "certificate:" part in the status var may be truncated because of the +# max status var length limit, taking only first 255 chars here to make this +# test stable +--replace_regex /.*(connection_certificate:[^;]{255}).*/\1/ SHOW STATUS LIKE "Audit_null_generic_event_response"; disconnect con1; diff --git a/mysql-test/suite/funcs_1/r/is_columns_is_ci.result b/mysql-test/suite/funcs_1/r/is_columns_is_ci.result index 0e195d1a021d..3be68f698dbf 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_is_ci.result +++ b/mysql-test/suite/funcs_1/r/is_columns_is_ci.result @@ -6,6 +6,11 @@ ORDER BY table_schema, table_name COLLATE utf8_general_ci, ordinal_position; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT GENERATION_EXPRESSION SRS_ID +def information_schema AUTHINFO ID 1 NO bigint NULL NULL NULL NULL NULL NULL NULL bigint(21) unsigned select NULL +def information_schema AUTHINFO USER 2 NO varchar 10 32 NULL NULL NULL utf8 utf8_general_ci varchar(32) select NULL +def information_schema AUTHINFO HOST 3 NO varchar 21 64 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NULL +def information_schema AUTHINFO SSL 4 NO int NULL NULL NULL NULL NULL NULL NULL int(7) select NULL +def information_schema AUTHINFO INFO 5 YES varchar 21845 65535 NULL NULL NULL utf8 utf8_general_ci varchar(65535) select NULL def information_schema CHARACTER_SETS CHARACTER_SET_NAME 1 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NULL def information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME 2 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NULL def information_schema CHARACTER_SETS DESCRIPTION 3 NULL NO varchar 2048 6144 NULL NULL NULL utf8 utf8_general_ci varchar(2048) select NULL @@ -458,6 +463,11 @@ WHERE table_schema = 'information_schema' AND table_name <> 'PROFILING' AND table_name not like 'INNODB_%' AND table_name not like 'ndb%' ORDER BY TABLE_SCHEMA, TABLE_NAME COLLATE utf8_general_ci, ORDINAL_POSITION; COL_CML TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE +NULL information_schema AUTHINFO ID bigint NULL NULL NULL NULL bigint(21) unsigned +3.2000 information_schema AUTHINFO USER varchar 10 32 utf8 utf8_general_ci varchar(32) +3.0476 information_schema AUTHINFO HOST varchar 21 64 utf8 utf8_general_ci varchar(64) +NULL information_schema AUTHINFO SSL int NULL NULL NULL NULL int(7) +3.0000 information_schema AUTHINFO INFO varchar 21845 65535 utf8 utf8_general_ci varchar(65535) 3.0000 information_schema CHARACTER_SETS CHARACTER_SET_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema CHARACTER_SETS DESCRIPTION varchar 2048 6144 utf8 utf8_general_ci varchar(2048) diff --git a/mysql-test/suite/funcs_1/r/is_columns_is_cs.result b/mysql-test/suite/funcs_1/r/is_columns_is_cs.result index 93fb2b8cfc91..5d9b20e89160 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_is_cs.result +++ b/mysql-test/suite/funcs_1/r/is_columns_is_cs.result @@ -6,6 +6,11 @@ ORDER BY table_schema, table_name COLLATE utf8_general_ci, ordinal_position; TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_SCALE DATETIME_PRECISION CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE COLUMN_KEY EXTRA PRIVILEGES COLUMN_COMMENT GENERATION_EXPRESSION SRS_ID +def information_schema AUTHINFO ID 1 NO bigint NULL NULL NULL NULL NULL NULL NULL bigint(21) unsigned select NULL +def information_schema AUTHINFO USER 2 NO varchar 10 32 NULL NULL NULL utf8 utf8_general_ci varchar(32) select NULL +def information_schema AUTHINFO HOST 3 NO varchar 21 64 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NULL +def information_schema AUTHINFO SSL 4 NO int NULL NULL NULL NULL NULL NULL NULL int(7) select NULL +def information_schema AUTHINFO INFO 5 YES varchar 21845 65535 NULL NULL NULL utf8 utf8_general_ci varchar(65535) select NULL def information_schema CHARACTER_SETS CHARACTER_SET_NAME 1 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NULL def information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME 2 NULL NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select NULL def information_schema CHARACTER_SETS DESCRIPTION 3 NULL NO varchar 2048 6144 NULL NULL NULL utf8 utf8_general_ci varchar(2048) select NULL @@ -458,6 +463,11 @@ WHERE table_schema = 'information_schema' AND table_name <> 'PROFILING' AND table_name not like 'INNODB_%' AND table_name not like 'ndb%' ORDER BY TABLE_SCHEMA, TABLE_NAME COLLATE utf8_general_ci, ORDINAL_POSITION; COL_CML TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH CHARACTER_SET_NAME COLLATION_NAME COLUMN_TYPE +NULL information_schema AUTHINFO ID bigint NULL NULL NULL NULL bigint(21) unsigned +3.2000 information_schema AUTHINFO USER varchar 10 32 utf8 utf8_general_ci varchar(32) +3.0476 information_schema AUTHINFO HOST varchar 21 64 utf8 utf8_general_ci varchar(64) +NULL information_schema AUTHINFO SSL int NULL NULL NULL NULL int(7) +3.0000 information_schema AUTHINFO INFO varchar 21845 65535 utf8 utf8_general_ci varchar(65535) 3.0000 information_schema CHARACTER_SETS CHARACTER_SET_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64) 3.0000 information_schema CHARACTER_SETS DESCRIPTION varchar 2048 6144 utf8 utf8_general_ci varchar(2048) diff --git a/mysql-test/suite/funcs_1/r/is_tables_is.result b/mysql-test/suite/funcs_1/r/is_tables_is.result index f60dc317fff8..7c60b97c8046 100644 --- a/mysql-test/suite/funcs_1/r/is_tables_is.result +++ b/mysql-test/suite/funcs_1/r/is_tables_is.result @@ -17,6 +17,29 @@ AND table_name not like 'ndb%' ORDER BY table_schema,table_name COLLATE utf8_general_ci; TABLE_CATALOG def TABLE_SCHEMA information_schema +TABLE_NAME AUTHINFO +TABLE_TYPE SYSTEM VIEW +ENGINE NULL +VERSION 10 +ROW_FORMAT NULL +TABLE_ROWS #TBLR# +AVG_ROW_LENGTH #ARL# +DATA_LENGTH #DL# +MAX_DATA_LENGTH #MDL# +INDEX_LENGTH #IL# +DATA_FREE #DF# +AUTO_INCREMENT #AI# +CREATE_TIME #CRT# +UPDATE_TIME #UT# +CHECK_TIME #CT# +TABLE_COLLATION NULL +CHECKSUM NULL +CREATE_OPTIONS #CO# +TABLE_COMMENT #TC# +user_comment +Separator ----------------------------------------------------- +TABLE_CATALOG def +TABLE_SCHEMA information_schema TABLE_NAME CHARACTER_SETS TABLE_TYPE SYSTEM VIEW ENGINE NULL @@ -818,6 +841,29 @@ AND table_name not like 'ndb%' ORDER BY table_schema,table_name COLLATE utf8_general_ci; TABLE_CATALOG def TABLE_SCHEMA information_schema +TABLE_NAME AUTHINFO +TABLE_TYPE SYSTEM VIEW +ENGINE NULL +VERSION 10 +ROW_FORMAT NULL +TABLE_ROWS #TBLR# +AVG_ROW_LENGTH #ARL# +DATA_LENGTH #DL# +MAX_DATA_LENGTH #MDL# +INDEX_LENGTH #IL# +DATA_FREE #DF# +AUTO_INCREMENT #AI# +CREATE_TIME #CRT# +UPDATE_TIME #UT# +CHECK_TIME #CT# +TABLE_COLLATION NULL +CHECKSUM NULL +CREATE_OPTIONS #CO# +TABLE_COMMENT #TC# +user_comment +Separator ----------------------------------------------------- +TABLE_CATALOG def +TABLE_SCHEMA information_schema TABLE_NAME CHARACTER_SETS TABLE_TYPE SYSTEM VIEW ENGINE NULL diff --git a/mysql-test/t/information_schema_authinfo-client.opt b/mysql-test/t/information_schema_authinfo-client.opt new file mode 100644 index 000000000000..88ed9f04cf43 --- /dev/null +++ b/mysql-test/t/information_schema_authinfo-client.opt @@ -0,0 +1,4 @@ +--ssl-mode=VERIFY_CA +--ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem +--ssl-cert=$MYSQL_TEST_DIR/std_data/client-cert.pem +--ssl-key=$MYSQL_TEST_DIR/std_data/client-key.pem diff --git a/mysql-test/t/information_schema_authinfo-master.opt b/mysql-test/t/information_schema_authinfo-master.opt new file mode 100644 index 000000000000..317b93f46621 --- /dev/null +++ b/mysql-test/t/information_schema_authinfo-master.opt @@ -0,0 +1,4 @@ +--ssl=1 +--ssl-ca=$MYSQL_TEST_DIR/std_data/cacert.pem +--ssl-cert=$MYSQL_TEST_DIR/std_data/server-cert.pem +--ssl-key=$MYSQL_TEST_DIR/std_data/server-key.pem diff --git a/mysql-test/t/information_schema_authinfo.test b/mysql-test/t/information_schema_authinfo.test new file mode 100644 index 000000000000..6adbd9794552 --- /dev/null +++ b/mysql-test/t/information_schema_authinfo.test @@ -0,0 +1,14 @@ +--source include/have_ssl.inc + +--echo # +--echo # Test case for information_schema.authinfo table +--echo # (SSL connection) +--echo # + +--let $assert_text= SSL field for the current connection must be set to 1 +--let $assert_cond= [ SELECT `ssl` = 1 FROM information_schema.authinfo WHERE id = CONNECTION_ID() ] +--source include/assert.inc + +--let $assert_text= Info field (peer certificate) for the current connection must be non-empty +--let $assert_cond= [ SELECT LENGTH(info) > 0 FROM information_schema.authinfo WHERE id = CONNECTION_ID() ] +--source include/assert.inc diff --git a/mysql-test/t/information_schema_authinfo_nossl.test b/mysql-test/t/information_schema_authinfo_nossl.test new file mode 100644 index 000000000000..1797665da3e4 --- /dev/null +++ b/mysql-test/t/information_schema_authinfo_nossl.test @@ -0,0 +1,12 @@ +--echo # +--echo # Test case for information_schema.authinfo table +--echo # (non-SSL connection) +--echo # + +--let $assert_text= SSL field for the current connection must be set to 0 +--let $assert_cond= [ SELECT `ssl` = 0 FROM information_schema.authinfo WHERE id = CONNECTION_ID() ] +--source include/assert.inc + +--let $assert_text= Info field (peer certificate) for the current connection must be NULL +--let $assert_cond= [ SELECT info IS NULL FROM information_schema.authinfo WHERE id = CONNECTION_ID() ] +--source include/assert.inc diff --git a/sql/auth/sql_authentication.cc b/sql/auth/sql_authentication.cc index 8eaa3508a67e..7ff3d830ea44 100644 --- a/sql/auth/sql_authentication.cc +++ b/sql/auth/sql_authentication.cc @@ -1843,9 +1843,6 @@ static bool read_client_connect_attrs(char **ptr, size_t *max_bytes_available, return false; } -typedef std::string Sql_string_t; -static Sql_string_t x509_cert_write(X509 *cert); - static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user) { #if defined(HAVE_OPENSSL) Vio *vio = thd->get_protocol_classic()->get_vio(); @@ -1877,7 +1874,6 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user) { if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_verify_result(ssl) == X509_V_OK && (cert = SSL_get_peer_certificate(ssl))) { - thd->set_connection_certificate(x509_cert_write(cert)); X509_free(cert); return 0; } @@ -1927,7 +1923,6 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user) { } OPENSSL_free(ptr); } - thd->set_connection_certificate(x509_cert_write(cert)); X509_free(cert); return 0; #else /* HAVE_OPENSSL */ @@ -3374,6 +3369,8 @@ int acl_authenticate(THD *thd, enum_server_command command) { DBUG_RETURN(1); } + thd->update_connection_certificate(); + /* Check whether the account has been locked. */ @@ -4167,6 +4164,8 @@ static SYS_VAR *sha256_password_sysvars[] = { MYSQL_SYSVAR(private_key_path), MYSQL_SYSVAR(public_key_path), MYSQL_SYSVAR(auto_generate_rsa_keys), 0}; +typedef std::string Sql_string_t; + /** Exception free resize diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index 32ce28d70bf1..659d7eee92f3 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -380,8 +380,9 @@ int mysql_audit_notify(THD *thd, mysql_event_general_subclass_t subclass, event.general_sql_command = sql_statement_names[thd->lex->sql_command]; event.affected_rows = thd->get_row_count_func(); event.port = mysqld_port; - event.connection_certificate.str = thd->connection_certificate().c_str(); - event.connection_certificate.length = thd->connection_certificate().size(); + event.connection_certificate.str = thd->get_connection_certificate().c_str(); + event.connection_certificate.length = + thd->get_connection_certificate().size(); thd_get_audit_query(thd, &event.general_query, (const CHARSET_INFO **)&event.general_charset); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index f26e4a1f16ad..068e48460e42 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -865,7 +866,7 @@ void THD::cleanup_connection(void) { sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); - m_connection_certificate = ""; + reset_connection_certificate(); clear_error(); // clear the warnings @@ -897,13 +898,45 @@ void THD::cleanup_connection(void) { #endif } -void THD::set_connection_certificate(std::string const &cert) { - DBUG_ASSERT(m_connection_certificate.empty()); - m_connection_certificate = cert; -} +std::string THD::extract_peer_certificate_info(const THD *thd, bool printable) { + // Extracting user certificate from the thread + if (!thd->has_net_vio_ssl_arg()) return {}; + + auto ssl = static_cast(thd->get_net_vio_ssl_arg()); + + // Creating new X509 abstraction + auto cert_deleter = [](X509 *cert) { + if (cert != nullptr) X509_free(cert); + }; + using x509_ptr = std::unique_ptr; + + x509_ptr cert{SSL_get_peer_certificate(ssl), cert_deleter}; + if (!cert) return {}; + + // Creating new memory-based BIO object + auto bio_deleter = [](BIO *bio) { + if (bio != nullptr) BIO_free(bio); + }; + using bio_ptr = std::unique_ptr; + + bio_ptr bio{BIO_new(BIO_s_mem()), bio_deleter}; + if (!bio) return {}; + + // Printing the certificate to the bio object + int print_result = 0; + if (printable) + print_result = X509_print(bio.get(), cert.get()); + else + print_result = PEM_write_bio_X509(bio.get(), cert.get()); + if (print_result != 1) return {}; + + // Extracting data from the bio object + BUF_MEM *buf_mem; + BIO_get_mem_ptr(bio.get(), &buf_mem); + assert(buf_mem->length <= buf_mem->max); + if (buf_mem->data == nullptr) return {}; -std::string const &THD::connection_certificate() const noexcept { - return m_connection_certificate; + return std::string{buf_mem->data, buf_mem->length}; } /* @@ -1071,7 +1104,7 @@ void THD::release_resources() { if (current_thd == this) restore_globals(); - m_connection_certificate = ""; + reset_connection_certificate(); m_release_resources_done = true; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 3dba9bf6d8d9..b2f808a74a6a 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1511,6 +1511,15 @@ class THD : public MDL_context_owner, NET net; // client connection descriptor String packet; // dynamic buffer for network I/O public: + bool has_net_vio() const noexcept { return net.vio != nullptr; } + const Vio *get_net_vio() const noexcept { return net.vio; } + bool has_net_vio_ssl_arg() const noexcept { + return has_net_vio() && get_net_vio()->ssl_arg != nullptr; + } + const void *get_net_vio_ssl_arg() const noexcept { + return has_net_vio() ? get_net_vio()->ssl_arg : nullptr; + } + void set_skip_readonly_check() { skip_readonly_check = true; } bool is_cmd_skip_readonly() { return skip_readonly_check; } @@ -3323,10 +3332,28 @@ class THD : public MDL_context_owner, Gtid_set owned_gtid_set; #endif + public: + static std::string extract_peer_certificate_info(const THD *thd, + bool printable); + + private: + friend int acl_authenticate(THD *, enum_server_command); + std::string m_connection_certificate; - std::string const &connection_certificate() const noexcept; - void set_connection_certificate(std::string const &cert); + void update_connection_certificate() { + m_connection_certificate = + extract_peer_certificate_info(this, false /* pem format */); + } + void reset_connection_certificate() { + m_connection_certificate.clear(); + m_connection_certificate.shrink_to_fit(); + } + + public: + std::string const &get_connection_certificate() const noexcept { + return m_connection_certificate; + } /* Replication related context. diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 388bc4dc975a..0d00e9663b27 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1914,6 +1914,101 @@ static const char *thread_state_info(THD *tmp) { } } +/** + This class implements callback function used by fill_schema_authinfo() + to populate all the client SSL auth information into I_S table. +*/ +class Fill_authinfo_list : public Do_THD_Impl { + private: + THD *const m_thd; + TABLE *const m_table; + const char *const m_user; + + public: + Fill_authinfo_list(THD *thd, TABLE_LIST *tables) noexcept + : m_thd(thd), + m_table(tables->table), + m_user(thd->security_context()->check_access(PROCESS_ACL) + ? nullptr + : thd->security_context()->priv_user().str) {} + + virtual void operator()(THD *current_thd) override { + const auto current_sctx = current_thd->security_context(); + const auto current_sctx_user = current_sctx->user(); + + if (!current_thd->has_net_vio() && + current_thd->system_thread == NON_SYSTEM_THREAD) + return; + if (m_user != nullptr && (current_sctx_user.str == nullptr || + strcmp(current_sctx_user.str, m_user) != 0)) + return; + + restore_record(m_table, s->default_values); + + /* ID */ + m_table->field[0]->store(static_cast(current_thd->thread_id()), + /*unsigned=*/true); + + /* USER */ + const char *val = current_sctx_user.str + ? current_sctx_user.str + : (current_thd->system_thread != NON_SYSTEM_THREAD + ? "system user" + : "unauthenticated user"); + m_table->field[1]->store(val, strlen(val), system_charset_info); + + /* HOST */ + const auto current_sctx_host = current_sctx->host(); + const auto current_sctx_ip = current_sctx->ip(); + const auto current_sctx_host_or_ip = current_sctx->host_or_ip(); + + std::string host_and_port; + if (current_sctx_host.str != nullptr && current_sctx_host.str[0] != '\0') { + host_and_port.assign(current_sctx_host.str, current_sctx_host.length); + } else if (current_sctx_ip.str != nullptr && + current_sctx_ip.str[0] != '\0') { + host_and_port.assign(current_sctx_ip.str, current_sctx_ip.length); + } else if (current_sctx_host_or_ip.str != nullptr && + current_sctx_host_or_ip.str[0] == '\0') { + host_and_port.assign(current_sctx_host_or_ip.str, + current_sctx_host_or_ip.length); + } + if (!host_and_port.empty() && current_thd->peer_port != 0) { + host_and_port += ':'; + host_and_port += std::to_string(current_thd->peer_port); + } + m_table->field[2]->store(host_and_port.c_str(), host_and_port.size(), + system_charset_info); + + /* SSL */ + const auto ssl = current_thd->has_net_vio_ssl_arg(); + m_table->field[3]->store(ssl, /*unsigned=*/true); + + /* Info */ + const auto cert = THD::extract_peer_certificate_info( + current_thd, true /* printable format */); + if (!cert.empty()) { + const auto width = std::min( + static_cast(PROCESS_LIST_INFO_WIDTH), cert.size()); + m_table->field[4]->store(cert.c_str(), width, system_charset_info); + m_table->field[4]->set_notnull(); + } + + schema_table_store_record(m_thd, m_table); + } +}; + +int fill_schema_authinfo(THD *thd, TABLE_LIST *tables, Item *) { + DBUG_ENTER("fill_schema_authinfo"); + + Fill_authinfo_list fill_authinfo_list(thd, tables); + if (!thd->killed) { + Global_THD_manager::get_instance()->do_for_all_thd_copy( + &fill_authinfo_list); + } + DBUG_RETURN(0); +} + /** This class implements callback function used by mysqld_list_processes() to list all the client process information. @@ -4993,6 +5088,17 @@ ST_FIELD_INFO processlist_fields_info[] = { SKIP_OPEN_TABLE}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}}; +ST_FIELD_INFO authinfo_fields_info[] = { + {"ID", 21, MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, "Id", SKIP_OPEN_TABLE}, + {"USER", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", + SKIP_OPEN_TABLE}, + {"HOST", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host", + SKIP_OPEN_TABLE}, + {"SSL", 7, MYSQL_TYPE_LONG, 0, 0, "Ssl", SKIP_OPEN_TABLE}, + {"INFO", PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info", + SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}}; + ST_FIELD_INFO plugin_fields_info[] = { {"PLUGIN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name", SKIP_OPEN_TABLE}, @@ -5091,6 +5197,8 @@ ST_SCHEMA_TABLE schema_tables[] = { {"TMP_TABLE_KEYS", tmp_table_keys_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_tmp_table_keys_record, -1, -1, 1, 0}, + {"AUTHINFO", authinfo_fields_info, create_schema_table, + fill_schema_authinfo, make_old_format, 0, -1, -1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; int initialize_schema_table(st_plugin_int *plugin) {