|
|
|
@ -653,6 +653,94 @@ bool init_new_connection_handler_thread() |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
/**
|
|
|
|
Get a null character terminated string from a user-supplied buffer. |
|
|
|
|
|
|
|
@param buffer[in, out] Pointer to the buffer to be scanned. |
|
|
|
@param max_bytes_available[in, out] Limit the bytes to scan. |
|
|
|
@param string_length[out] The number of characters scanned not including |
|
|
|
the null character. |
|
|
|
|
|
|
|
@remark The string_length does not include the terminating null character. |
|
|
|
However, after the call, the buffer is increased by string_length+1 |
|
|
|
bytes, beyond the null character if there still available bytes to |
|
|
|
scan. |
|
|
|
|
|
|
|
@return pointer to beginning of the string scanned. |
|
|
|
@retval NULL The buffer content is malformed |
|
|
|
*/ |
|
|
|
|
|
|
|
static |
|
|
|
char *get_null_terminated_string(char **buffer, |
|
|
|
size_t *max_bytes_available, |
|
|
|
size_t *string_length) |
|
|
|
{ |
|
|
|
char *str= (char *)memchr(*buffer, '\0', *max_bytes_available); |
|
|
|
|
|
|
|
if (str == NULL) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
*string_length= (size_t)(str - *buffer); |
|
|
|
*max_bytes_available-= *string_length + 1; |
|
|
|
str= *buffer; |
|
|
|
*buffer += *string_length + 1; |
|
|
|
|
|
|
|
return str; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Get a length encoded string from a user-supplied buffer. |
|
|
|
|
|
|
|
@param buffer[in, out] The buffer to scan; updates position after scan. |
|
|
|
@param max_bytes_available[in, out] Limit the number of bytes to scan |
|
|
|
@param string_length[out] Number of characters scanned |
|
|
|
|
|
|
|
@remark In case the length is zero, then the total size of the string is |
|
|
|
considered to be 1 byte; the size byte. |
|
|
|
|
|
|
|
@return pointer to first byte after the header in buffer. |
|
|
|
@retval NULL The buffer content is malformed |
|
|
|
*/ |
|
|
|
|
|
|
|
static |
|
|
|
char *get_length_encoded_string(char **buffer, |
|
|
|
size_t *max_bytes_available, |
|
|
|
size_t *string_length) |
|
|
|
{ |
|
|
|
if (*max_bytes_available == 0) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
/* Do double cast to prevent overflow from signed / unsigned conversion */ |
|
|
|
size_t str_len= (size_t)(unsigned char)**buffer; |
|
|
|
|
|
|
|
/*
|
|
|
|
If the length encoded string has the length 0 |
|
|
|
the total size of the string is only one byte long (the size byte) |
|
|
|
*/ |
|
|
|
if (str_len == 0) |
|
|
|
{ |
|
|
|
++*buffer; |
|
|
|
*string_length= 0; |
|
|
|
/*
|
|
|
|
Return a pointer to the 0 character so the return value will be |
|
|
|
an empty string. |
|
|
|
*/ |
|
|
|
return *buffer-1; |
|
|
|
} |
|
|
|
|
|
|
|
if (str_len >= *max_bytes_available) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
char *str= *buffer+1; |
|
|
|
*string_length= str_len; |
|
|
|
*max_bytes_available-= *string_length + 1; |
|
|
|
*buffer+= *string_length + 1; |
|
|
|
return str; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Perform handshake, authorize client and update thd ACL variables. |
|
|
|
|
|
|
|
@ -666,7 +754,6 @@ bool init_new_connection_handler_thread() |
|
|
|
> 0 error code (not sent to user) |
|
|
|
*/ |
|
|
|
|
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
static int check_connection(THD *thd) |
|
|
|
{ |
|
|
|
uint connect_errors= 0; |
|
|
|
@ -855,7 +942,7 @@ static int check_connection(THD *thd) |
|
|
|
} |
|
|
|
#endif /* HAVE_OPENSSL */
|
|
|
|
|
|
|
|
if (end >= (char*) net->read_pos+ pkt_len +2) |
|
|
|
if (end > (char *)net->read_pos + pkt_len) |
|
|
|
{ |
|
|
|
inc_host_errors(&thd->remote.sin_addr); |
|
|
|
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
|
|
@ -867,39 +954,75 @@ static int check_connection(THD *thd) |
|
|
|
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && |
|
|
|
opt_using_transactions) |
|
|
|
net->return_status= &thd->server_status; |
|
|
|
|
|
|
|
/*
|
|
|
|
In order to safely scan a head for '\0' string terminators |
|
|
|
we must keep track of how many bytes remain in the allocated |
|
|
|
buffer or we might read past the end of the buffer. |
|
|
|
*/ |
|
|
|
size_t bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos); |
|
|
|
|
|
|
|
char *user= end; |
|
|
|
char *passwd= strend(user)+1; |
|
|
|
uint user_len= passwd - user - 1; |
|
|
|
char *db= passwd; |
|
|
|
char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
|
|
|
|
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
|
|
|
|
uint dummy_errors; |
|
|
|
size_t user_len; |
|
|
|
char *user= get_null_terminated_string(&end, &bytes_remaining_in_packet, |
|
|
|
&user_len); |
|
|
|
if (user == NULL) |
|
|
|
{ |
|
|
|
inc_host_errors(&thd->remote.sin_addr); |
|
|
|
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/*
|
|
|
|
Old clients send null-terminated string as password; new clients send |
|
|
|
Old clients send a null-terminated string as password; new clients send |
|
|
|
the size (1 byte) + string (not null-terminated). Hence in case of empty |
|
|
|
password both send '\0'. |
|
|
|
*/ |
|
|
|
size_t passwd_len= 0; |
|
|
|
char *passwd= NULL; |
|
|
|
|
|
|
|
This strlen() can't be easily deleted without changing protocol. |
|
|
|
if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) |
|
|
|
{ |
|
|
|
/*
|
|
|
|
4.1+ password. First byte is password length. |
|
|
|
*/ |
|
|
|
passwd= get_length_encoded_string(&end, &bytes_remaining_in_packet, |
|
|
|
&passwd_len); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
/*
|
|
|
|
Old passwords are zero terminated strings. |
|
|
|
*/ |
|
|
|
passwd= get_null_terminated_string(&end, &bytes_remaining_in_packet, |
|
|
|
&passwd_len); |
|
|
|
} |
|
|
|
|
|
|
|
Cast *passwd to an unsigned char, so that it doesn't extend the sign for |
|
|
|
*passwd > 127 and become 2**32-127+ after casting to uint. |
|
|
|
*/ |
|
|
|
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? |
|
|
|
(uchar)(*passwd++) : strlen(passwd); |
|
|
|
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? |
|
|
|
db + passwd_len + 1 : 0; |
|
|
|
/* strlen() can't be easily deleted without changing protocol */ |
|
|
|
uint db_len= db ? strlen(db) : 0; |
|
|
|
|
|
|
|
if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) |
|
|
|
if (passwd == NULL) |
|
|
|
{ |
|
|
|
inc_host_errors(&thd->remote.sin_addr); |
|
|
|
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
size_t db_len= 0; |
|
|
|
char *db= NULL; |
|
|
|
|
|
|
|
if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) |
|
|
|
{ |
|
|
|
db= get_null_terminated_string(&end, &bytes_remaining_in_packet, |
|
|
|
&db_len); |
|
|
|
if (db == NULL) |
|
|
|
{ |
|
|
|
inc_host_errors(&thd->remote.sin_addr); |
|
|
|
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
|
|
|
|
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
|
|
|
|
uint dummy_errors; |
|
|
|
|
|
|
|
/* Since 4.1 all database names are stored in utf8 */ |
|
|
|
if (db) |
|
|
|
{ |
|
|
|
|