Commit 8e04a52a authored by unknown's avatar unknown

Bug#21813 An attacker has the opportunity to bypass query logging, part2

 - Use the "%.*b" format when printing prepared and exeuted prepared statements to the log.
 - Add test case to check that also prepared statements end up in the query log
Bug#14346 Prepared statements corrupting general log/server memory
 - Use "stmt->query" when logging the newly prepared query instead of "packet"


sql/sql_prepare.cc:
  mysql_stmt_prepare
   - Use "%.*b" format when printing to log
   - Print the query from stmt instead of "packet", packet points at the net in/out buffer and has most likely been overwritten
     when  result for prepare was written to client.
  mysql_stmt_execute 
   - Use "%.*b" format when printing to log
   - Print the query from thd as the expanded query has been specifially set to be valid also after restore from backup statement
tests/mysql_client_test.c:
  Add tests for bug#21813 to already existing test for bug#17667. Add functionality for also executing prepared statements and making sure they end up in the log as well.
parent c2d52b34
...@@ -1877,7 +1877,8 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) ...@@ -1877,7 +1877,8 @@ void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->stmt_map.erase(stmt); thd->stmt_map.erase(stmt);
} }
else else
mysql_log.write(thd, COM_STMT_PREPARE, "[%lu] %s", stmt->id, packet); mysql_log.write(thd, COM_STMT_PREPARE, "[%lu] %.*b", stmt->id,
stmt->query_length, stmt->query);
/* check_prepared_statemnt sends the metadata packet in case of success */ /* check_prepared_statemnt sends the metadata packet in case of success */
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -2252,7 +2253,8 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length) ...@@ -2252,7 +2253,8 @@ void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR); my_pthread_setprio(pthread_self(), WAIT_PRIOR);
if (error == 0) if (error == 0)
mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query); mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %.*b", stmt->id,
thd->query_length, thd->query);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
......
...@@ -14912,22 +14912,31 @@ static void test_bug15613() ...@@ -14912,22 +14912,31 @@ static void test_bug15613()
/* /*
Bug#17667: An attacker has the opportunity to bypass query logging. Bug#17667: An attacker has the opportunity to bypass query logging.
Note! Also tests Bug#21813, where prepared statements are used to
run queries
*/ */
static void test_bug17667() static void test_bug17667()
{ {
int rc; int rc;
MYSQL_STMT *stmt;
enum query_type { QT_NORMAL, QT_PREPARED};
struct buffer_and_length { struct buffer_and_length {
enum query_type qt;
const char *buffer; const char *buffer;
const uint length; const uint length;
} statements[]= { } statements[]= {
{ "drop table if exists bug17667", 29 }, { QT_NORMAL, "drop table if exists bug17667", 29 },
{ "create table bug17667 (c varchar(20))", 37 }, { QT_NORMAL, "create table bug17667 (c varchar(20))", 37 },
{ "insert into bug17667 (c) values ('regular') /* NUL=\0 with comment */", 68 }, { QT_NORMAL, "insert into bug17667 (c) values ('regular') /* NUL=\0 with comment */", 68 },
{ "insert into bug17667 (c) values ('NUL=\0 in value')", 50 }, { QT_PREPARED,
{ "insert into bug17667 (c) values ('5 NULs=\0\0\0\0\0')", 48 }, "insert into bug17667 (c) values ('prepared') /* NUL=\0 with comment */", 69, },
{ "/* NUL=\0 with comment */ insert into bug17667 (c) values ('encore')", 67 }, { QT_NORMAL, "insert into bug17667 (c) values ('NUL=\0 in value')", 50 },
{ "drop table bug17667", 19 }, { QT_NORMAL, "insert into bug17667 (c) values ('5 NULs=\0\0\0\0\0')", 48 },
{ NULL, 0 } }; { QT_PREPARED, "insert into bug17667 (c) values ('6 NULs=\0\0\0\0\0\0')", 50 },
{ QT_NORMAL, "/* NUL=\0 with comment */ insert into bug17667 (c) values ('encore')", 67 },
{ QT_NORMAL, "drop table bug17667", 19 },
{ QT_NORMAL, NULL, 0 } };
struct buffer_and_length *statement_cursor; struct buffer_and_length *statement_cursor;
FILE *log_file; FILE *log_file;
...@@ -14937,10 +14946,37 @@ static void test_bug17667() ...@@ -14937,10 +14946,37 @@ static void test_bug17667()
for (statement_cursor= statements; statement_cursor->buffer != NULL; for (statement_cursor= statements; statement_cursor->buffer != NULL;
statement_cursor++) { statement_cursor++) {
if (statement_cursor->qt == QT_NORMAL)
{
/* Run statement as normal query */
rc= mysql_real_query(mysql, statement_cursor->buffer, rc= mysql_real_query(mysql, statement_cursor->buffer,
statement_cursor->length); statement_cursor->length);
myquery(rc); myquery(rc);
} }
else if (statement_cursor->qt == QT_PREPARED)
{
/*
Run as prepared statement
NOTE! All these queries should be in the log twice,
one time for prepare and one time for execute
*/
stmt= mysql_stmt_init(mysql);
rc= mysql_stmt_prepare(stmt, statement_cursor->buffer,
statement_cursor->length);
check_execute(stmt, rc);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
mysql_stmt_close(stmt);
}
else
{
assert(0==1);
}
}
/* Make sure the server has written the logs to disk before reading it */ /* Make sure the server has written the logs to disk before reading it */
rc= mysql_query(mysql, "flush logs"); rc= mysql_query(mysql, "flush logs");
...@@ -14957,9 +14993,17 @@ static void test_bug17667() ...@@ -14957,9 +14993,17 @@ static void test_bug17667()
for (statement_cursor= statements; statement_cursor->buffer != NULL; for (statement_cursor= statements; statement_cursor->buffer != NULL;
statement_cursor++) { statement_cursor++) {
int expected_hits= 1, hits= 0;
char line_buffer[MAX_TEST_QUERY_LENGTH*2]; char line_buffer[MAX_TEST_QUERY_LENGTH*2];
/* more than enough room for the query and some marginalia. */ /* more than enough room for the query and some marginalia. */
/* Prepared statments always occurs twice in log */
if (statement_cursor->qt == QT_PREPARED)
expected_hits++;
/* Loop until we found expected number of log entries */
do {
/* Loop until statement is found in log */
do { do {
memset(line_buffer, '/', MAX_TEST_QUERY_LENGTH*2); memset(line_buffer, '/', MAX_TEST_QUERY_LENGTH*2);
...@@ -14968,18 +15012,17 @@ static void test_bug17667() ...@@ -14968,18 +15012,17 @@ static void test_bug17667()
/* If fgets returned NULL, it indicates either error or EOF */ /* If fgets returned NULL, it indicates either error or EOF */
if (feof(log_file)) if (feof(log_file))
DIE("Found EOF before all statements where found"); DIE("Found EOF before all statements where found");
else
{
fprintf(stderr, "Got error %d while reading from file\n", fprintf(stderr, "Got error %d while reading from file\n",
ferror(log_file)); ferror(log_file));
DIE("Read error"); DIE("Read error");
} }
}
/* Print the line */
printf("%s", line_buffer);
} while (my_memmem(line_buffer, MAX_TEST_QUERY_LENGTH*2, } while (my_memmem(line_buffer, MAX_TEST_QUERY_LENGTH*2,
statement_cursor->buffer, statement_cursor->length) == NULL); statement_cursor->buffer,
statement_cursor->length) == NULL);
hits++;
} while (hits < expected_hits);
printf("Found statement starting with \"%s\"\n", printf("Found statement starting with \"%s\"\n",
statement_cursor->buffer); statement_cursor->buffer);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment