maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #06265
Re: Rev 3814: Client attributes
Hi, Sanja!
On Sep 19, sanja@xxxxxxxxxxxx wrote:
> === modified file 'sql/sql_acl.cc'
> --- a/sql/sql_acl.cc 2013-07-21 14:39:19 +0000
> +++ b/sql/sql_acl.cc 2013-09-19 10:19:36 +0000
> @@ -4615,7 +4615,8 @@ bool check_grant(THD *thd, ulong want_ac
> case ACL_INTERNAL_ACCESS_GRANTED:
> /*
> Currently,
> - - the information_schema does not subclass ACL_internal_table_access,
> + - the information_schema does not subclass ACL_internal_table_access
> +,
Eh? What was that?
> there are no per table privilege checks for I_S,
> - the performance schema does use per tables checks, but at most
> returns 'CHECK_GRANT', and never 'ACCESS_GRANTED'.
> @@ -8222,6 +8223,43 @@ static bool find_mpvio_user(MPVIO_EXT *m
> mpvio->acl_user->plugin.str));
> DBUG_RETURN(0);
> }
> +
> +static bool
> +read_client_connect_attrs(char **ptr, size_t *max_bytes_available,
> + const CHARSET_INFO *from_cs)
> +{
> + size_t length, length_length;
> + char *ptr_save;
> + /* not enough bytes to hold the length */
> + if (*max_bytes_available < 1)
> + return true;
> +
> + /* read the length */
> + ptr_save= *ptr;
> + length= net_field_length_ll((uchar **) ptr);
> + length_length= *ptr - ptr_save;
> + if (*max_bytes_available < length_length)
> + return true;
This is incorrect, it might read past the end of the buffer.
it's too late to check the length *after* you've read the data.
Correct code might look like
if (*max_bytes_available >= 9)
{
ptr_save= *ptr;
length= net_field_length_ll((uchar **) ptr);
length_length= *ptr - ptr_save;
DBUG_ASSERT(length_length <= 9);
*max_bytes_available-= length_length;
}
else
{
char buf[10], *len_ptr= buf;
strncpy(buf, *ptr, 9)
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;
}
> +
> + *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() */
> @@ -8349,6 +8387,13 @@ static bool parse_com_change_user_packet
> }
> }
>
> + size_t bytes_remaining_in_packet= end - ptr;
1. This is very wrong, did you ever test it? You need a pointer
to *after* the plugin name. But 'ptr' is two bytes *before* the plugin.
2. remove bytes_remaining_in_packet, pass 'end' instead
to read_client_connect_attrs().
> +
> + if ((mpvio->thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
> + read_client_connect_attrs(&ptr, &bytes_remaining_in_packet,
> + 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
> @@ -8487,7 +8532,21 @@ static ulong parse_client_handshake_pack
> /* 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 *client_plugin= end= passwd + passwd_len + (db ? db_len + 1 : 0);
> +
> + if ((thd->client_capabilities & CLIENT_PLUGIN_AUTH) &&
> + (client_plugin < (char *)net->read_pos + pkt_len))
> + {
> + end+= strlen(end) + 1;
> + }
> +
> + size_t bytes_remaining_in_packet= (((char *)net->read_pos) + pkt_len) - end;
This is very-very wrong. if there're no connection attributes, then
your 'end' will be after the end of the packet, it'll be after
net->read_pos + pkt_len. But then you subtract, get -1 and cast it to
unsigned size_t !
Don't worry, when you remove bytes_remaining_in_packet as I wrote above,
this bug will go away.
> +
> + if (db &&
> + (thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
> + read_client_connect_attrs(&end, &bytes_remaining_in_packet,
> + mpvio->thd->charset()))
> + return packet_error;
Put it in the same place as in parse_com_change_user_packet,
that is after reading the plugin name (and fix_plugin_ptr).
And rename 'end' to 'connection_attributes'
>
> /* Since 4.1 all database names are stored in utf8 */
> if (db)
>
> === modified file 'sql/sys_vars.cc'
> --- a/sql/sys_vars.cc 2013-08-14 08:48:50 +0000
> +++ b/sql/sys_vars.cc 2013-09-19 10:19:36 +0000
> @@ -71,6 +71,10 @@
> #define export /* not static */
>
> #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
> +#ifndef EMBEDDED_LIBRARY
> +#define PFS_TRAILING_PROPERTIES \
> + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL), ON_UPDATE(NULL), \
> + NULL, sys_var::PARSE_EARLY
Remove PFS_TRAILING_PROPERTIES define
(not used in MariaDB)
>
> static Sys_var_mybool Sys_pfs_enabled(
> "performance_schema",
> @@ -331,8 +335,10 @@ static Sys_var_long Sys_pfs_connect_attr
> GLOBAL_VAR(pfs_param.m_session_connect_attrs_sizing),
> CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024 * 1024),
> DEFAULT(-1),
> - BLOCK_SIZE(1));
> + BLOCK_SIZE(1),
> + NO_MUTEX_GUARD, NOT_IN_BINLOG);
Remove this change too
(sysvars are NO_MUTEX_GUARD and NOT_IN_BINLOG by default)
>
> +#endif /* EMBEDDED_LIBRARY */
> #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
>
> static Sys_var_ulong Sys_auto_increment_increment(
>
> === modified file 'libmysqld/lib_sql.cc'
> --- a/libmysqld/lib_sql.cc 2013-08-01 12:09:26 +0000
> +++ b/libmysqld/lib_sql.cc 2013-09-19 10:19:36 +0000
> @@ -698,12 +698,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;
> +
Remove the similar line in init_embedded_mysql()
> thd_init_client_charset(thd, mysql->charset->number);
> thd->update_charset();
> Security_context *sctx= thd->security_ctx;
> @@ -728,11 +755,17 @@ int check_embedded_connection(MYSQL *mys
> 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->server_capabilities & CLIENT_CONNECT_ATTRS &&
This doesn't seem to make any sense. Why does it check for
CLIENT_CONNECT_ATTRS if it's part of the server (embedded).
If a client connects to the server, then the client needs to check
whether the server supports CLIENT_CONNECT_ATTRS, and the server
needs to check whether the client supports it.
But a server doesn't need to check whether it itself support it,
it knows it already :)
> + 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 +799,13 @@ int check_embedded_connection(MYSQL *mys
> int2store(end, (ushort) mysql->charset->number);
> end+= 2;
>
> + //end= strmake(end, "mysql_native_password", NAME_LEN) + 1;
Why is it commented out?
> +
> + /* 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;
Regards,
Sergei
Follow ups