summaryrefslogtreecommitdiff
path: root/sql/sql_connect.cc
diff options
context:
space:
mode:
authorKristofer Pettersson <kristofer.pettersson@oracle.com>2011-03-11 15:10:15 +0100
committerKristofer Pettersson <kristofer.pettersson@oracle.com>2011-03-11 15:10:15 +0100
commit17e77a7e49c0359fa66d4bd0b7c43246728f2a09 (patch)
tree38e4c2c82799849a56e640b95fdabae0230b8ee1 /sql/sql_connect.cc
parent27166fc64f54ef82f1800988ffff431868377645 (diff)
downloadmariadb-git-17e77a7e49c0359fa66d4bd0b7c43246728f2a09.tar.gz
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.
Diffstat (limited to 'sql/sql_connect.cc')
-rw-r--r--sql/sql_connect.cc167
1 files changed, 145 insertions, 22 deletions
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 9fa6966baa2..4c4f30600de 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -630,6 +630,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.
@@ -643,7 +731,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;
@@ -831,7 +918,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);
@@ -843,39 +930,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)
{