Certain fields in the protcol required a strict formatting. If

off bound values were sent to the server this could under some
circumstances lead to a crash on the Windows platform.
This commit is contained in:
Kristofer Pettersson 2011-03-11 15:10:15 +01:00
parent a6ea6dc217
commit feeac7d1a4

View file

@ -630,6 +630,94 @@ bool init_new_connection_handler_thread()
return 0; 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. Perform handshake, authorize client and update thd ACL variables.
@ -643,7 +731,6 @@ bool init_new_connection_handler_thread()
> 0 error code (not sent to user) > 0 error code (not sent to user)
*/ */
#ifndef EMBEDDED_LIBRARY
static int check_connection(THD *thd) static int check_connection(THD *thd)
{ {
uint connect_errors= 0; uint connect_errors= 0;
@ -831,7 +918,7 @@ static int check_connection(THD *thd)
} }
#endif /* HAVE_OPENSSL */ #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); inc_host_errors(&thd->remote.sin_addr);
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
@ -843,39 +930,75 @@ static int check_connection(THD *thd)
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
opt_using_transactions) opt_using_transactions)
net->return_status= &thd->server_status; net->return_status= &thd->server_status;
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;
/* /*
Old clients send null-terminated string as password; new clients send In order to safely scan a head for '\0' string terminators
the size (1 byte) + string (not null-terminated). Hence in case of empty we must keep track of how many bytes remain in the allocated
password both send '\0'. buffer or we might read past the end of the buffer.
This strlen() can't be easily deleted without changing protocol.
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 ? size_t bytes_remaining_in_packet= pkt_len - (end - (char *)net->read_pos);
(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) 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); inc_host_errors(&thd->remote.sin_addr);
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
return 1; return 1;
} }
/*
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;
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);
}
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 */ /* Since 4.1 all database names are stored in utf8 */
if (db) if (db)
{ {