diff options
-rw-r--r-- | client/mysql.cc | 6 | ||||
-rw-r--r-- | client/mysqladmin.cc | 3 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 3 | ||||
-rw-r--r-- | client/mysqlcheck.c | 3 | ||||
-rw-r--r-- | client/mysqldump.c | 3 | ||||
-rw-r--r-- | client/mysqlimport.c | 3 | ||||
-rw-r--r-- | client/mysqlshow.c | 3 | ||||
-rw-r--r-- | client/mysqlslap.c | 3 | ||||
-rw-r--r-- | client/mysqltest.cc | 8 | ||||
-rw-r--r-- | include/mysql.h | 2 | ||||
-rw-r--r-- | include/mysql.h.pp | 2 | ||||
-rw-r--r-- | include/mysql/psi/psi.h | 1 | ||||
-rw-r--r-- | include/mysql_com.h | 3 | ||||
-rw-r--r-- | include/sql_common.h | 3 | ||||
-rw-r--r-- | libmysql/client_settings.h | 3 | ||||
-rw-r--r-- | libmysqld/lib_sql.cc | 44 | ||||
-rw-r--r-- | mysql-test/suite/perfschema/r/connect_attrs.result | 52 | ||||
-rw-r--r-- | mysql-test/suite/perfschema/t/connect_attrs.test | 83 | ||||
-rw-r--r-- | sql-common/client.c | 310 | ||||
-rw-r--r-- | sql/client_settings.h | 4 | ||||
-rw-r--r-- | sql/sql_acl.cc | 79 | ||||
-rw-r--r-- | storage/perfschema/pfs_server.h | 3 |
22 files changed, 597 insertions, 27 deletions
diff --git a/client/mysql.cc b/client/mysql.cc index 36567da5947..0e39818af5e 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1359,6 +1359,9 @@ sig_handler handle_sigint(int sig) } kill_mysql= mysql_init(kill_mysql); + mysql_options(kill_mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(kill_mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysql"); if (!mysql_real_connect(kill_mysql,current_host, current_user, opt_password, "", opt_mysql_port, opt_mysql_unix_port,0)) { @@ -4606,6 +4609,9 @@ sql_real_connect(char *host,char *database,char *user,char *password, if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); + mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysql"); if (!mysql_real_connect(&mysql, host, user, password, database, opt_mysql_port, opt_mysql_unix_port, connect_flag | CLIENT_MULTI_STATEMENTS)) diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 9a8901435c8..71bc37a4161 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -370,6 +370,9 @@ int main(int argc,char *argv[]) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); + mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqladmin"); if (sql_connect(&mysql, option_wait)) { /* diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 4f6475ef95f..ad38f4d39c5 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -1745,6 +1745,9 @@ static Exit_status safe_connect() mysql_options(mysql, MYSQL_SHARED_MEMORY_BASE_NAME, shared_memory_base_name); #endif + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqlbinlog"); if (!mysql_real_connect(mysql, host, user, pass, 0, port, sock, 0)) { error("Failed on connect: %s", mysql_error(mysql)); diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 3559ad2a361..74c33c75a27 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -915,6 +915,9 @@ static int dbConnect(char *host, char *user, char *passwd) mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth); mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset); + mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqlcheck"); if (!(sock = mysql_real_connect(&mysql_connection, host, user, passwd, NULL, opt_mysql_port, opt_mysql_unix_port, 0))) { diff --git a/client/mysqldump.c b/client/mysqldump.c index f9fcbb65515..2c84b89b3e2 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1542,6 +1542,9 @@ static int connect_to_db(char *host, char *user,char *passwd) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth); + mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqldump"); mysql= &mysql_connection; /* So we can mysql_close() it properly */ if (!mysql_real_connect(&mysql_connection,host,user,passwd, NULL,opt_mysql_port,opt_mysql_unix_port, 0)) diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 15be9341ca3..60e42c305aa 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -456,6 +456,9 @@ static MYSQL *db_connect(char *host, char *database, mysql_options(mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); mysql_options(mysql, MYSQL_SET_CHARSET_NAME, default_charset); + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqlimport"); if (!(mysql_real_connect(mysql,host,user,passwd, database,opt_mysql_port,opt_mysql_unix_port, 0))) diff --git a/client/mysqlshow.c b/client/mysqlshow.c index 5abeed107c2..58e679365de 100644 --- a/client/mysqlshow.c +++ b/client/mysqlshow.c @@ -141,6 +141,9 @@ int main(int argc, char **argv) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); + mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqlshow"); if (!(mysql_real_connect(&mysql,host,user,opt_password, (first_argument_uses_wildcards) ? "" : argv[0],opt_mysql_port,opt_mysql_unix_port, diff --git a/client/mysqlslap.c b/client/mysqlslap.c index 0beb089bf83..e4c118bf990 100644 --- a/client/mysqlslap.c +++ b/client/mysqlslap.c @@ -362,6 +362,9 @@ int main(int argc, char **argv) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); + mysql_options(&mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqlslap"); if (!opt_only_print) { if (!(mysql_real_connect(&mysql, host, user, opt_password, diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 87d6c80917a..629f85fe2ee 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -5648,6 +5648,10 @@ void safe_connect(MYSQL* mysql, const char *name, const char *host, verbose_msg("Connecting to server %s:%d (socket %s) as '%s'" ", connection '%s', attempt %d ...", host, port, sock, user, name, failed_attempts); + + mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqltest"); while(!mysql_real_connect(mysql, host,user, pass, db, port, sock, CLIENT_MULTI_STATEMENTS | CLIENT_REMEMBER_OPTIONS)) { @@ -5748,7 +5752,9 @@ int connect_n_handle_errors(struct st_command *command, replace_dynstr_append(ds, command->query); dynstr_append_mem(ds, ";\n", 2); } - + + mysql_options(con, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(con, MYSQL_OPT_CONNECT_ATTR_ADD, "program_name", "mysqltest"); while (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0, CLIENT_MULTI_STATEMENTS)) { diff --git a/include/mysql.h b/include/mysql.h index 56b2332342d..025bd397a65 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -557,6 +557,8 @@ int STDCALL mysql_list_processes_cont(MYSQL_RES **ret, MYSQL *mysql, int status); int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg); +int STDCALL mysql_options4(MYSQL *mysql,enum mysql_option option, + const void *arg1, const void *arg2); void STDCALL mysql_free_result(MYSQL_RES *result); int STDCALL mysql_free_result_start(MYSQL_RES *result); int STDCALL mysql_free_result_cont(MYSQL_RES *result, int status); diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 2487a7cd6e2..ca5b1ac05bf 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -552,6 +552,8 @@ int mysql_list_processes_cont(MYSQL_RES **ret, MYSQL *mysql, int status); int mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg); +int mysql_options4(MYSQL *mysql,enum mysql_option option, + const void *arg1, const void *arg2); void mysql_free_result(MYSQL_RES *result); int mysql_free_result_start(MYSQL_RES *result); int mysql_free_result_cont(MYSQL_RES *result, int status); diff --git a/include/mysql/psi/psi.h b/include/mysql/psi/psi.h index cc2057c630d..d6c25242707 100644 --- a/include/mysql/psi/psi.h +++ b/include/mysql/psi/psi.h @@ -1921,6 +1921,7 @@ typedef struct PSI_digest_locker* (*digest_add_token_v1_t) typedef int (*set_thread_connect_attrs_v1_t)(const char *buffer, uint length, const void *from_cs); + /** Performance Schema Interface, version 1. @since PSI_VERSION_1 diff --git a/include/mysql_com.h b/include/mysql_com.h index cc206a0230f..d9f905eb657 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -264,7 +264,8 @@ enum enum_server_command CLIENT_SSL_VERIFY_SERVER_CERT | \ CLIENT_REMEMBER_OPTIONS | \ CLIENT_PROGRESS | \ - CLIENT_PLUGIN_AUTH) + CLIENT_PLUGIN_AUTH | \ + CLIENT_CONNECT_ATTRS) /* To be added later: diff --git a/include/sql_common.h b/include/sql_common.h index 5a033fbe522..e7bade7e73b 100644 --- a/include/sql_common.h +++ b/include/sql_common.h @@ -43,6 +43,8 @@ struct st_mysql_options_extention { const char *proc_info, uint proc_info_length); struct mysql_async_context *async_context; + HASH connection_attributes; + size_t connection_attributes_length; }; typedef struct st_mysql_methods @@ -116,6 +118,7 @@ int mysql_client_plugin_init(); void mysql_client_plugin_deinit(); struct st_mysql_client_plugin; extern struct st_mysql_client_plugin *mysql_client_builtins[]; +uchar * send_client_connect_attrs(MYSQL *mysql, uchar *buf); /* Non-blocking client API. */ void my_context_install_suspend_resume_hook(struct mysql_async_context *b, diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index ecc9a7773ca..4fbec521238 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -34,7 +34,8 @@ extern char * mysql_unix_port; CLIENT_SECURE_CONNECTION | \ CLIENT_MULTI_RESULTS | \ CLIENT_PS_MULTI_RESULTS | \ - CLIENT_PLUGIN_AUTH) + CLIENT_PLUGIN_AUTH | \ + CLIENT_CONNECT_ATTRS) sig_handler my_pipe_sig_handler(int sig); void read_user_name(char *name); diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 2c959200457..d00556c3f17 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -638,7 +638,6 @@ void init_embedded_mysql(MYSQL *mysql, int client_flag) thd->mysql= mysql; mysql->server_version= server_version; mysql->client_flag= client_flag; - //mysql->server_capabilities= client_flag; init_alloc_root(&mysql->field_alloc, 8192, 0, MYF(0)); } @@ -698,12 +697,38 @@ err: } +static void +emb_transfer_connect_attrs(MYSQL *mysql) +{ +#ifdef HAVE_PSI_THREAD_INTERFACE + if (mysql->options.extension && + mysql->options.extension->connection_attributes_length) + { + uchar *buf, *ptr; + THD *thd= (THD*)mysql->thd; + size_t length= mysql->options.extension->connection_attributes_length; + + /* 9 = max length of the serialized length */ + ptr= buf= (uchar *) my_alloca(length + 9); + send_client_connect_attrs(mysql, buf); + net_field_length_ll(&ptr); + PSI_THREAD_CALL(set_thread_connect_attrs)((char *) ptr, length, thd->charset()); + my_afree(buf); + } +#endif +} + + #ifdef NO_EMBEDDED_ACCESS_CHECKS int check_embedded_connection(MYSQL *mysql, const char *db) { int result; LEX_STRING db_str = { (char*)db, db ? strlen(db) : 0 }; THD *thd= (THD*)mysql->thd; + + /* the server does the same as the client */ + mysql->server_capabilities= mysql->client_flag; + thd_init_client_charset(thd, mysql->charset->number); thd->update_charset(); Security_context *sctx= thd->security_ctx; @@ -713,6 +738,7 @@ int check_embedded_connection(MYSQL *mysql, const char *db) sctx->user= my_strdup(mysql->user, MYF(0)); sctx->proxy_user[0]= 0; sctx->master_access= GLOBAL_ACLS; // Full rights + emb_transfer_connect_attrs(mysql); /* Change database if necessary */ if (!(result= (db && db[0] && mysql_change_db(thd, &db_str, FALSE)))) my_ok(thd); @@ -728,11 +754,16 @@ int check_embedded_connection(MYSQL *mysql, const char *db) we emulate a COM_CHANGE_USER user here, it's easier than to emulate the complete 3-way handshake */ - char buf[USERNAME_LENGTH + SCRAMBLE_LENGTH + 1 + 2*NAME_LEN + 2], *end; + char *buf, *end; NET *net= &mysql->net; THD *thd= (THD*)mysql->thd; Security_context *sctx= thd->security_ctx; + size_t connect_attrs_len= + (mysql->options.extension) ? + mysql->options.extension->connection_attributes_length : 0; + buf= my_alloca(USERNAME_LENGTH + SCRAMBLE_LENGTH + 1 + 2*NAME_LEN + 2 + + connect_attrs_len + 2); if (mysql->options.client_ip) { sctx->host= my_strdup(mysql->options.client_ip, MYF(0)); @@ -766,6 +797,13 @@ int check_embedded_connection(MYSQL *mysql, const char *db) int2store(end, (ushort) mysql->charset->number); end+= 2; + end= strmake(end, "mysql_native_password", NAME_LEN) + 1; + + /* the server does the same as the client */ + mysql->server_capabilities= mysql->client_flag; + + end= (char *) send_client_connect_attrs(mysql, (uchar *) end); + /* acl_authenticate() takes the data from thd->net->read_pos */ thd->net.read_pos= (uchar*)buf; @@ -774,6 +812,7 @@ int check_embedded_connection(MYSQL *mysql, const char *db) my_free(thd->security_ctx->user); goto err; } + my_afree(buf); return 0; err: @@ -781,6 +820,7 @@ err: memcpy(net->sqlstate, mysql_errno_to_sqlstate(thd->get_stmt_da()->sql_errno()), sizeof(net->sqlstate)-1); + my_afree(buf); return 1; } #endif diff --git a/mysql-test/suite/perfschema/r/connect_attrs.result b/mysql-test/suite/perfschema/r/connect_attrs.result new file mode 100644 index 00000000000..0fb0ad34ab2 --- /dev/null +++ b/mysql-test/suite/perfschema/r/connect_attrs.result @@ -0,0 +1,52 @@ +# must return 0, 6 +SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*) +FROM performance_schema.session_connect_attrs +WHERE ATTR_NAME IN ('_os', '_client_name', '_pid', +'_client_version', '_platform', 'program_name') +AND PROCESSLIST_ID = CONNECTION_ID(); +SUM(ISNULL(ATTR_VALUE)) COUNT(*) +0 6 +# must return 1 +SELECT COUNT(DISTINCT PROCESSLIST_ID) +FROM performance_schema.session_connect_attrs; +COUNT(DISTINCT PROCESSLIST_ID) +1 +# must return 0, 6 +SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*) +FROM performance_schema.session_account_connect_attrs +WHERE ATTR_NAME IN ('_os', '_client_name', '_pid', +'_client_version', '_platform', 'program_name') +AND PROCESSLIST_ID = CONNECTION_ID(); +SUM(ISNULL(ATTR_VALUE)) COUNT(*) +0 6 +# must return 1 +SELECT COUNT(DISTINCT PROCESSLIST_ID) +FROM performance_schema.session_account_connect_attrs; +COUNT(DISTINCT PROCESSLIST_ID) +1 +CREATE USER wl5924@localhost; +# must return 1 +SELECT COUNT(DISTINCT PROCESSLIST_ID) +FROM performance_schema.session_account_connect_attrs; +COUNT(DISTINCT PROCESSLIST_ID) +1 +# must return 2 +SELECT COUNT(DISTINCT PROCESSLIST_ID) +FROM performance_schema.session_connect_attrs; +COUNT(DISTINCT PROCESSLIST_ID) +2 +SELECT COUNT(DISTINCT PROCESSLIST_ID) +FROM performance_schema.session_account_connect_attrs; +ERROR 42000: SELECT command denied to user 'wl5924'@'localhost' for table 'session_account_connect_attrs' +SELECT COUNT(DISTINCT PROCESSLIST_ID) +FROM performance_schema.session_connect_attrs; +ERROR 42000: SELECT command denied to user 'wl5924'@'localhost' for table 'session_connect_attrs' +grant select on performance_schema.* to wl5924@localhost; +SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*) +FROM performance_schema.session_account_connect_attrs +WHERE ATTR_NAME IN ('_os', '_client_name', '_pid', +'_client_version', '_platform', 'program_name') +AND PROCESSLIST_ID = CONNECTION_ID(); +SUM(ISNULL(ATTR_VALUE)) COUNT(*) +0 6 +DROP USER wl5924@localhost; diff --git a/mysql-test/suite/perfschema/t/connect_attrs.test b/mysql-test/suite/perfschema/t/connect_attrs.test new file mode 100644 index 00000000000..850d9ddd248 --- /dev/null +++ b/mysql-test/suite/perfschema/t/connect_attrs.test @@ -0,0 +1,83 @@ +# Session connect attributes test +--source include/have_perfschema.inc + +# although the connection attributes transfer code works +# with embedded P_S is not active, so the test won't run. +# TODO: remove this when P_S works with embedded. +--source include/not_embedded.inc + +# make sure we're alone +let $count_sessions= 1; +--source include/wait_until_count_sessions.inc + +# basic performance_schema.session_connect_attrs tests + +# check the presense of the pre-defined attributes +--echo # must return 0, 6 +SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*) + FROM performance_schema.session_connect_attrs + WHERE ATTR_NAME IN ('_os', '_client_name', '_pid', + '_client_version', '_platform', 'program_name') + AND PROCESSLIST_ID = CONNECTION_ID(); + +# check the presense of the pre-defined attributes +--echo # must return 1 +SELECT COUNT(DISTINCT PROCESSLIST_ID) + FROM performance_schema.session_connect_attrs; + + +# basic performance_schema.session_account_connect_attrs tests + +# check the presense of the pre-defined attributes +--echo # must return 0, 6 +SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*) + FROM performance_schema.session_account_connect_attrs + WHERE ATTR_NAME IN ('_os', '_client_name', '_pid', + '_client_version', '_platform', 'program_name') + AND PROCESSLIST_ID = CONNECTION_ID(); + +# check the presense of the pre-defined attributes +--echo # must return 1 +SELECT COUNT(DISTINCT PROCESSLIST_ID) + FROM performance_schema.session_account_connect_attrs; + + + +CREATE USER wl5924@localhost; + +connect(non_privileged_user,localhost,wl5924,,test); +connection default; + +--echo # must return 1 +SELECT COUNT(DISTINCT PROCESSLIST_ID) + FROM performance_schema.session_account_connect_attrs; + +--echo # must return 2 +SELECT COUNT(DISTINCT PROCESSLIST_ID) + FROM performance_schema.session_connect_attrs; + +connection non_privileged_user; +--error ER_TABLEACCESS_DENIED_ERROR +SELECT COUNT(DISTINCT PROCESSLIST_ID) + FROM performance_schema.session_account_connect_attrs; + +--error ER_TABLEACCESS_DENIED_ERROR +SELECT COUNT(DISTINCT PROCESSLIST_ID) + FROM performance_schema.session_connect_attrs; + +connection default; +disconnect non_privileged_user; + +grant select on performance_schema.* to wl5924@localhost; +change_user wl5924; +SELECT SUM(ISNULL(ATTR_VALUE)), COUNT(*) + FROM performance_schema.session_account_connect_attrs + WHERE ATTR_NAME IN ('_os', '_client_name', '_pid', + '_client_version', '_platform', 'program_name') + AND PROCESSLIST_ID = CONNECTION_ID(); +change_user root,,test; + +DROP USER wl5924@localhost; + +# Wait till all disconnects are completed +--source include/wait_until_count_sessions.inc diff --git a/sql-common/client.c b/sql-common/client.c index 6942822f889..87cbf45a3c6 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -37,6 +37,7 @@ #include <my_global.h> #include <my_default.h> #include "mysql.h" +#include "hash.h" /* Remove client convenience wrappers */ #undef max_allowed_packet @@ -1216,17 +1217,30 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd) return 0; } -#define EXTENSION_SET(OPTS, X, VAL) \ - if (!(OPTS)->extension) \ + +#define ALLOCATE_EXTENSIONS(OPTS) \ (OPTS)->extension= (struct st_mysql_options_extention *) \ my_malloc(sizeof(struct st_mysql_options_extention), \ - MYF(MY_WME | MY_ZEROFILL)); \ - (OPTS)->extension->X= (VAL); + MYF(MY_WME | MY_ZEROFILL)) \ + + +#define ENSURE_EXTENSIONS_PRESENT(OPTS) \ + do { \ + if (!(OPTS)->extension) \ + ALLOCATE_EXTENSIONS(OPTS); \ + } while (0) + #define EXTENSION_SET_STRING(OPTS, X, STR) \ - if ((OPTS)->extension) \ - my_free((OPTS)->extension->X); \ - EXTENSION_SET(OPTS, X, (STR) ? my_strdup((STR), MYF(MY_WME)) : NULL); + do { \ + if ((OPTS)->extension) \ + my_free((OPTS)->extension->X); \ + else \ + ALLOCATE_EXTENSIONS(OPTS); \ + (OPTS)->extension->X= ((STR) != NULL) ? \ + my_strdup((STR), MYF(MY_WME)) : NULL; \ + } while (0) + #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) #define SET_SSL_OPTION(OPTS, opt_var, arg) \ @@ -2376,6 +2390,7 @@ static auth_plugin_t old_password_client_plugin= old_password_auth_client }; + struct st_mysql_client_plugin *mysql_client_builtins[]= { (struct st_mysql_client_plugin *)&native_password_client_plugin, @@ -2383,6 +2398,67 @@ struct st_mysql_client_plugin *mysql_client_builtins[]= 0 }; + +static uchar * +write_length_encoded_string3(uchar *buf, char *string, size_t length) +{ + buf= net_store_length(buf, length); + memcpy(buf, string, length); + buf+= length; + return buf; +} + + +uchar * +send_client_connect_attrs(MYSQL *mysql, uchar *buf) +{ + /* check if the server supports connection attributes */ + if (mysql->server_capabilities & CLIENT_CONNECT_ATTRS) + { + + /* Always store the length if the client supports it */ + buf= net_store_length(buf, + mysql->options.extension ? + mysql->options.extension->connection_attributes_length : + 0); + + /* check if we have connection attributes */ + if (mysql->options.extension && + my_hash_inited(&mysql->options.extension->connection_attributes)) + { + HASH *attrs= &mysql->options.extension->connection_attributes; + ulong idx; + + /* loop over and dump the connection attributes */ + for (idx= 0; idx < attrs->records; idx++) + { + LEX_STRING *attr= (LEX_STRING *) my_hash_element(attrs, idx); + LEX_STRING *key= attr, *value= attr + 1; + + /* we can't have zero length keys */ + DBUG_ASSERT(key->length); + + buf= write_length_encoded_string3(buf, key->str, key->length); + buf= write_length_encoded_string3(buf, value->str, value->length); + } + } + } + return buf; +} + + +static size_t get_length_store_length(size_t length) +{ + /* as defined in net_store_length */ + #define MAX_VARIABLE_STRING_LENGTH 9 + uchar length_buffer[MAX_VARIABLE_STRING_LENGTH], *ptr; + + ptr= net_store_length(length_buffer, length); + + return ptr - &length_buffer[0]; +} + + /* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */ typedef struct { int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); @@ -2426,8 +2502,13 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio, MYSQL *mysql= mpvio->mysql; char *buff, *end; int res= 1; + size_t connect_attrs_len= + (mysql->server_capabilities & CLIENT_CONNECT_ATTRS && + mysql->options.extension) ? + mysql->options.extension->connection_attributes_length : 0; - buff= my_alloca(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1); + buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN + + connect_attrs_len + 9 /* for the length of the attrs */); end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1; @@ -2464,6 +2545,8 @@ static int send_change_user_packet(MCPVIO_EXT *mpvio, if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1; + end= (char *) send_client_connect_attrs(mysql, (uchar *) end); + res= simple_command(mysql, COM_CHANGE_USER, (uchar*)buff, (ulong)(end-buff), 1); @@ -2472,6 +2555,7 @@ error: return res; } +#define MAX_CONNECTION_ATTR_STORAGE_LENGTH 65536 /** sends a client authentication packet (second packet in the 3-way handshake) @@ -2509,10 +2593,21 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, MYSQL *mysql= mpvio->mysql; NET *net= &mysql->net; char *buff, *end; + size_t buff_size; + size_t connect_attrs_len= + (mysql->server_capabilities & CLIENT_CONNECT_ATTRS && + mysql->options.extension) ? + mysql->options.extension->connection_attributes_length : 0; + + DBUG_ASSERT(connect_attrs_len < MAX_CONNECTION_ATTR_STORAGE_LENGTH); + + /* + see end= buff+32 below, fixed size of the packet is 32 bytes. + +9 because data is a length encoded binary where meta data size is max 9. + */ + buff_size= 33 + USERNAME_LENGTH + data_len + 9 + NAME_LEN + NAME_LEN + connect_attrs_len + 9; + buff= my_alloca(buff_size); - /* see end= buff+32 below, fixed size of the packet is 32 bytes */ - buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN); - mysql->client_flag|= mysql->options.client_flag; mysql->client_flag|= CLIENT_CAPABILITIES; @@ -2665,6 +2760,8 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1; + end= (char *) send_client_connect_attrs(mysql, (uchar *) end); + /* Write authentication package */ if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net)) { @@ -3024,6 +3121,51 @@ connect_sync_or_async(MYSQL *mysql, NET *net, my_socket fd, return my_connect(fd, name, namelen, mysql->options.connect_timeout); } + +/** set some default attributes */ +static int +set_connect_attributes(MYSQL *mysql, char *buff, size_t buf_len) +{ + int rc= 0; + + /* + Clean up any values set by the client code. We want these options as + consistent as possible + */ + rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_name"); + rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_os"); + rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_platform"); + rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_pid"); + rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_thread"); + rc+= mysql_options(mysql, MYSQL_OPT_CONNECT_ATTR_DELETE, "_client_version"); + + /* + Now let's set up some values + */ + rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "_client_name", "libmysql"); + rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "_client_version", PACKAGE_VERSION); + rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "_os", SYSTEM_TYPE); + rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, + "_platform", MACHINE_TYPE); +#ifdef __WIN__ + snprintf(buff, buf_len, "%lu", (ulong) GetCurrentProcessId()); +#else + snprintf(buff, buf_len, "%lu", (ulong) getpid()); +#endif + rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_pid", buff); + +#ifdef __WIN__ + snprintf(buff, buf_len, "%lu", (ulong) GetCurrentThreadId()); + rc+= mysql_options4(mysql, MYSQL_OPT_CONNECT_ATTR_ADD, "_thread", buff); +#endif + + return rc > 0 ? 1 : 0; +} + + MYSQL * STDCALL CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, @@ -3057,6 +3199,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, DBUG_RETURN(0); } + if (set_connect_attributes(mysql, buff, sizeof(buff))) + DBUG_RETURN(0); + mysql->methods= &client_methods; net->vio = 0; /* If something goes wrong */ mysql->client_flag=0; /* For handshake */ @@ -3757,6 +3902,7 @@ static void mysql_close_free_options(MYSQL *mysql) struct mysql_async_context *ctxt= mysql->options.extension->async_context; my_free(mysql->options.extension->plugin_dir); my_free(mysql->options.extension->default_auth); + my_hash_free(&mysql->options.extension->connection_attributes); if (ctxt) { my_context_destroy(&ctxt->async_context); @@ -4320,7 +4466,8 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) my_free(ctxt); DBUG_RETURN(1); } - EXTENSION_SET(&(mysql->options), async_context, ctxt) + ENSURE_EXTENSIONS_PRESENT(&(mysql->options)); + mysql->options.extension->async_context= ctxt; if (mysql->net.vio) mysql->net.vio->async_context= ctxt; break; @@ -4345,6 +4492,145 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) case MYSQL_OPT_SSL_CRLPATH: EXTENSION_SET_SSL_STRING(&mysql->options, ssl_crlpath, arg); break; + case MYSQL_OPT_CONNECT_ATTR_RESET: + ENSURE_EXTENSIONS_PRESENT(&mysql->options); + if (my_hash_inited(&mysql->options.extension->connection_attributes)) + { + my_hash_free(&mysql->options.extension->connection_attributes); + mysql->options.extension->connection_attributes_length= 0; + } + break; + case MYSQL_OPT_CONNECT_ATTR_DELETE: + ENSURE_EXTENSIONS_PRESENT(&mysql->options); + if (my_hash_inited(&mysql->options.extension->connection_attributes)) + { + size_t len; + uchar *elt; + + len= arg ? strlen(arg) : 0; + + if (len) + { + elt= my_hash_search(&mysql->options.extension->connection_attributes, + arg, len); + if (elt) + { + LEX_STRING *attr= (LEX_STRING *) elt; + LEX_STRING *key= attr, *value= attr + 1; + + mysql->options.extension->connection_attributes_length-= + get_length_store_length(key->length) + key->length + + get_length_store_length(value->length) + value->length; + + my_hash_delete(&mysql->options.extension->connection_attributes, + elt); + + } + } + } + break; + default: + break; + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} +/** + A function to return the key from a connection attribute +*/ +uchar * +get_attr_key(LEX_STRING *part, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= part[0].length; + return (uchar *) part[0].str; +} + +int STDCALL +mysql_options4(MYSQL *mysql,enum mysql_option option, + const void *arg1, const void *arg2) +{ + DBUG_ENTER("mysql_option"); + DBUG_PRINT("enter",("option: %d",(int) option)); + + switch (option) + { + case MYSQL_OPT_CONNECT_ATTR_ADD: + { + LEX_STRING *elt; + char *key, *value; + size_t key_len= arg1 ? strlen(arg1) : 0, + value_len= arg2 ? strlen(arg2) : 0; + size_t attr_storage_length= key_len + value_len; + + /* we can't have a zero length key */ + if (!key_len) + { + set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate); + DBUG_RETURN(1); + } + + /* calculate the total storage length of the attribute */ + attr_storage_length+= get_length_store_length(key_len); + attr_storage_length+= get_length_store_length(value_len); + + ENSURE_EXTENSIONS_PRESENT(&mysql->options); + + /* + Throw and error if the maximum combined length of the attribute value + will be greater than the maximum that we can safely transmit. + */ + if (attr_storage_length + + mysql->options.extension->connection_attributes_length > + MAX_CONNECTION_ATTR_STORAGE_LENGTH) + { + set_mysql_error(mysql, CR_INVALID_PARAMETER_NO, unknown_sqlstate); + DBUG_RETURN(1); + } + + if (!my_hash_inited(&mysql->options.extension->connection_attributes)) + { + if (my_hash_init(&mysql->options.extension->connection_attributes, + &my_charset_bin, 0, 0, 0, (my_hash_get_key) get_attr_key, + my_free, HASH_UNIQUE)) + { + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); + DBUG_RETURN(1); + } + } + if (!my_multi_malloc(MY_WME, + &elt, 2 * sizeof(LEX_STRING), + &key, key_len + 1, + &value, value_len + 1, + NULL)) + { + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); + DBUG_RETURN(1); + } + elt[0].str= key; elt[0].length= key_len; + elt[1].str= value; elt[1].length= value_len; + if (key_len) + memcpy(key, arg1, key_len); + key[key_len]= 0; + if (value_len) + memcpy(value, arg2, value_len); + value[value_len]= 0; + if (my_hash_insert(&mysql->options.extension->connection_attributes, + (uchar *) elt)) + { + /* can't insert the value */ + my_free(elt); + set_mysql_error(mysql, CR_DUPLICATE_CONNECTION_ATTR, + unknown_sqlstate); + DBUG_RETURN(1); + } + + mysql->options.extension->connection_attributes_length+= + attr_storage_length; + + break; + } + default: DBUG_RETURN(1); } diff --git a/sql/client_settings.h b/sql/client_settings.h index 54cb72f9412..5707413f69f 100644 --- a/sql/client_settings.h +++ b/sql/client_settings.h @@ -30,11 +30,11 @@ */ #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \ CLIENT_LONG_FLAG | \ - CLIENT_SECURE_CONNECTION | \ CLIENT_TRANSACTIONS | \ CLIENT_PROTOCOL_41 | \ CLIENT_SECURE_CONNECTION | \ - CLIENT_PLUGIN_AUTH) + CLIENT_PLUGIN_AUTH | \ + CLIENT_CONNECT_ATTRS) #define read_user_name(A) {} #undef _CUSTOMCONFIG_ diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 67d14b9b965..89d7b413514 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -8217,6 +8217,56 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) mpvio->acl_user->plugin.str)); DBUG_RETURN(0); } + +static bool +read_client_connect_attrs(char **ptr, char *end, + const CHARSET_INFO *from_cs) +{ + size_t length, length_length; + size_t max_bytes_available= end - *ptr; + /* not enough bytes to hold the length */ + if ((*ptr) >= (end - 1)) + return true; + + /* read the length */ + if (max_bytes_available >= 9) + { + char *ptr_save= *ptr; + length= net_field_length_ll((uchar **) ptr); + length_length= *ptr - ptr_save; + DBUG_ASSERT(length_length <= 9); + } + else + { + /* to avoid reading unallocated and uninitialized memory */ + char buf[10]={'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',}, + *len_ptr= buf; + memcpy(buf, *ptr, max_bytes_available); + length= net_field_length_ll((uchar **) &len_ptr); + length_length= len_ptr - buf; + *ptr+= length_length; + if (max_bytes_available < length_length) + return true; + } + max_bytes_available-= length_length; + + /* length says there're more data than can fit into the packet */ + if (length > max_bytes_available) + return true; + + /* impose an artificial length limit of 64k */ + if (length > 65535) + return true; + +#ifdef HAVE_PSI_THREAD_INTERFACE + if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, length, from_cs) && + current_thd->variables.log_warnings) + sql_print_warning("Connection attributes of length %lu were truncated", + (unsigned long) length); +#endif + return false; +} + #endif /* the packet format is described in send_change_user_packet() */ @@ -8269,13 +8319,14 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) uint db_len= strlen(db); - char *ptr= db + db_len + 1; + char *next_field= db + db_len + 1; - if (ptr + 1 < end) + if (next_field + 1 < end) { - if (thd_init_client_charset(thd, uint2korr(ptr))) + if (thd_init_client_charset(thd, uint2korr(next_field))) DBUG_RETURN(1); thd->update_charset(); + next_field+= 2; } /* Convert database and user names to utf8 */ @@ -8318,13 +8369,13 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) char *client_plugin; if (thd->client_capabilities & CLIENT_PLUGIN_AUTH) { - client_plugin= ptr + 2; - if (client_plugin >= end) + if (next_field >= end) { my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); DBUG_RETURN(1); } - client_plugin= fix_plugin_ptr(client_plugin); + client_plugin= fix_plugin_ptr(next_field); + next_field+= strlen(next_field) + 1; } else { @@ -8344,6 +8395,11 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) } } + if ((mpvio->thd->client_capabilities & CLIENT_CONNECT_ATTRS) && + read_client_connect_attrs(&next_field, end, + mpvio->thd->charset())) + return packet_error; + DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin)); /* Remember the data part of the packet, to present it to plugin in @@ -8482,7 +8538,8 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, /* strlen() can't be easily deleted without changing protocol */ db_len= db ? strlen(db) : 0; - char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0); + char *next_field; + char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0); /* Since 4.1 all database names are stored in utf8 */ if (db) @@ -8549,6 +8606,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, (client_plugin < (char *)net->read_pos + pkt_len)) { client_plugin= fix_plugin_ptr(client_plugin); + next_field+= strlen(next_field) + 1; } else { @@ -8570,7 +8628,12 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, mpvio->acl_user->plugin= old_password_plugin_name; } } - + + if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) && + read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len, + mpvio->thd->charset())) + return packet_error; + /* if the acl_user needs a different plugin to authenticate (specified in GRANT ... AUTHENTICATED VIA plugin_name ..) diff --git a/storage/perfschema/pfs_server.h b/storage/perfschema/pfs_server.h index 139e5930cc9..b647833f43a 100644 --- a/storage/perfschema/pfs_server.h +++ b/storage/perfschema/pfs_server.h @@ -54,6 +54,9 @@ #ifndef PFS_STATEMENTS_STACK_SIZE #define PFS_STATEMENTS_STACK_SIZE 10 #endif +#ifndef PFS_CONNECT_ATTRS_SIZE + #define PFS_SESSION_CONNECT_ATTRS_SIZE 2048 +#endif struct PFS_sizing_hints { |