* Add 'UPDATE' to list of commands checked for password masking.

* Fix 'uninstall plugin' to work on MySQL 5.7.
* Add ability to send rows affected by each command, includes new
  'audit_before_after' configuration variable.
* More offsets for more released versions.
pull/179/head
Arnold Robbins 2017-02-13 16:22:15 +02:00
parent 4fbcae3a81
commit ba21262b81
6 changed files with 1005 additions and 696 deletions

View File

@ -96,6 +96,11 @@ typedef struct ThdOffsets {
OFFSET pfs_connect_attrs_length; OFFSET pfs_connect_attrs_length;
OFFSET pfs_connect_attrs_cs; OFFSET pfs_connect_attrs_cs;
OFFSET net; OFFSET net;
OFFSET lex_m_sql_command;
OFFSET uninstall_cmd_comment;
OFFSET found_rows;
OFFSET sent_row_count;
OFFSET row_count_func;
} ThdOffsets; } ThdOffsets;
/* /*
@ -128,7 +133,9 @@ class ThdSesData {
public: public:
// enum indicating from where the object list came from // enum indicating from where the object list came from
enum ObjectIterType { OBJ_NONE, OBJ_DB, OBJ_QUERY_CACHE, OBJ_TABLE_LIST }; enum ObjectIterType { OBJ_NONE, OBJ_DB, OBJ_QUERY_CACHE, OBJ_TABLE_LIST };
ThdSesData(THD *pTHD); // enum indicating source of statement
typedef enum { SOURCE_GENERAL, SOURCE_QUERY_CACHE } StatementSource;
ThdSesData(THD *pTHD, StatementSource source = SOURCE_GENERAL);
THD *getTHD() const { return m_pThd;} THD *getTHD() const { return m_pThd;}
const char *getCmdName() const { return m_CmdName; } const char *getCmdName() const { return m_CmdName; }
void setCmdName(const char *cmd) { m_CmdName = cmd; } void setCmdName(const char *cmd) { m_CmdName = cmd; }
@ -137,6 +144,7 @@ public:
const char *getAppName() const; const char *getAppName() const;
const char *getOsUser() const; const char *getOsUser() const;
const int getPort() const { return m_port; } const int getPort() const { return m_port; }
const StatementSource getStatementSource() const { return m_source; }
/** /**
* Start fetching objects. Return true if there are objects available. * Start fetching objects. Return true if there are objects available.
*/ */
@ -163,6 +171,9 @@ private:
QueryTableInf *m_tableInf; QueryTableInf *m_tableInf;
int m_index; int m_index;
// Statement source
StatementSource m_source;
PeerInfo *m_peerInfo; PeerInfo *m_peerInfo;
int m_port; // TCP port of remote side int m_port; // TCP port of remote side
@ -374,8 +385,6 @@ public:
} }
#endif #endif
static inline const char * pfs_connect_attrs(void * pfs) static inline const char * pfs_connect_attrs(void * pfs)
{ {
if (! Audit_formatter::thd_offsets.pfs_connect_attrs) if (! Audit_formatter::thd_offsets.pfs_connect_attrs)
@ -437,6 +446,7 @@ static inline const CHARSET_INFO * pfs_connect_attrs_cs(void * pfs)
return *(const CHARSET_INFO **) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs_cs); return *(const CHARSET_INFO **) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs_cs);
} }
} }
static inline int thd_client_fd(THD *thd) static inline int thd_client_fd(THD *thd)
{ {
if (! Audit_formatter::thd_offsets.net) if (! Audit_formatter::thd_offsets.net)
@ -482,6 +492,52 @@ static inline const CHARSET_INFO * pfs_connect_attrs_cs(void * pfs)
return port; return port;
} }
static inline ulonglong thd_found_rows(THD *thd)
{
if (Audit_formatter::thd_offsets.found_rows == 0)
{
return 0;
}
ulonglong *rows = ((ulonglong *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.found_rows));
return *rows;
}
static inline unsigned long thd_sent_row_count(THD *thd)
{
if (Audit_formatter::thd_offsets.sent_row_count == 0)
{
return 0;
}
ha_rows *rows = ((ha_rows *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.sent_row_count));
return (unsigned long) *rows;
}
static inline longlong thd_row_count_func(THD *thd)
{
if (Audit_formatter::thd_offsets.row_count_func == 0)
{
return -1;
}
longlong *rows = ((longlong *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.row_count_func));
return *rows;
}
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
static inline Sql_cmd_uninstall_plugin* lex_sql_cmd(LEX *lex)
{
return *(Sql_cmd_uninstall_plugin **) (((unsigned char *) lex) + Audit_formatter::thd_offsets.lex_m_sql_command);
}
#endif
// we don't use get_db_name() as when we call it view may be not null // we don't use get_db_name() as when we call it view may be not null
// and it may return an invalid value for view_db // and it may return an invalid value for view_db
static inline const char *table_get_db_name(TABLE_LIST *table) static inline const char *table_get_db_name(TABLE_LIST *table)

View File

@ -59,6 +59,7 @@
#if MYSQL_VERSION_ID >= 50709 #if MYSQL_VERSION_ID >= 50709
#include <sql/log.h> #include <sql/log.h>
#if ! defined(MARIADB_BASE_VERSION) #if ! defined(MARIADB_BASE_VERSION)
#include <sql/sql_plugin.h>
#include <sql/auth/auth_common.h> #include <sql/auth/auth_common.h>
#endif #endif
#endif #endif

View File

@ -1,25 +1,24 @@
#!/bin/sh #!/bin/sh
if [ $# = 0 ]; then if [ $# = 0 ]
then
echo "Usage: $0 <mysqld executable> [optional mysqld symbols]" echo "Usage: $0 <mysqld executable> [optional mysqld symbols]"
echo "Will extract offsets from mysqld. Requires gdb, md5sum and mysqld symbols." echo "Will extract offsets from mysqld. Requires gdb, md5sum and mysqld symbols."
exit 1 exit 1
fi fi
#extract the version of mysqld # Extract the version of mysqld
FULL_MYVER=`$1 --version | grep -P -o 'Ver\s+[\w\.-]+'| awk '{print $2}'` FULL_MYVER=`$1 --version | grep -P -o 'Ver\s+[\w\.-]+'| awk '{ print $2 }'`
#extract the md5 digest # Extract the md5 digest
MYMD5=`md5sum -b $1 | awk -v Field=1 '{print $1}'` MYMD5=`md5sum -b $1 | awk -v Field=1 '{ print $1 }'`
MYVER="$FULL_MYVER" MYVER="$FULL_MYVER"
echo $FULL_MYVER | grep 'log' > /dev/null if echo $FULL_MYVER | grep 'log' > /dev/null
then
MYVER=`echo "$MYVER" | grep -P -o '.+(?=-log)'`
if [ $? = 0 ]; then
MYVER=`echo "$MYVER" | grep -P -o '.+(?=-log)'`
fi fi
COMMAND_MEMBER=command COMMAND_MEMBER=command
@ -32,43 +31,83 @@ PRIV_USER=priv_user
DB=db DB=db
CLIENT_CAPS="print_offset THD client_capabilities" CLIENT_CAPS="print_offset THD client_capabilities"
#in 5.6 command member is named m_command # In 5.6 command member is named m_command
echo $MYVER | grep -P '^(5\.6|5\.7|10\.)' > /dev/null if echo $MYVER | grep -P '^(5\.6|5\.7|10\.)' > /dev/null
if [ $? = 0 ]; then then
COMMAND_MEMBER=m_command COMMAND_MEMBER=m_command
HAS_CONNECT_ATTRS=yes HAS_CONNECT_ATTRS=yes
fi fi
CONNECT_ATTRS_CS=m_session_connect_attrs_cs CONNECT_ATTRS_CS=m_session_connect_attrs_cs
#in 5.7 thread_id changed to m_thread_id. main_security_ctx changed to m_main_security_ctx # In 5.7 thread_id changed to m_thread_id. main_security_ctx changed to m_main_security_ctx
echo $MYVER | grep -P '^(5\.7)' > /dev/null if echo $MYVER | grep -P '^(5\.7)' > /dev/null
if [ $? = 0 ]; then then
THREAD_ID=m_thread_id THREAD_ID=m_thread_id
SEC_CONTEXT=m_main_security_ctx SEC_CONTEXT=m_main_security_ctx
USER=m_user USER=m_user
HOST=m_host HOST=m_host
IP=m_ip IP=m_ip
PRIV_USER=m_priv_user PRIV_USER=m_priv_user
DB=m_db DB=m_db
#client capabilities has moved out THD in 5.7. Set to 0 # client capabilities has moved out THD in 5.7. Set to 0
CLIENT_CAPS='print_offset THD m_protocol' CLIENT_CAPS='print_offset THD m_protocol'
# comment which holds plugin name for uninstall moved into
# a separate object
HAS_LEX_SQL_CMD=yes
fi fi
#in 5.6.15 and up, 5.7 and mariabdb 10.0.11 and up, mariadb 10.1 # In 5.6.15 and up, 5.7 and mariabdb 10.0.11 and up, mariadb 10.1
#m_session_connect_attrs_cs changed to m_session_connect_attrs_cs_number # m_session_connect_attrs_cs changed to m_session_connect_attrs_cs_number
echo $MYVER | grep -P '^(5\.7|10\.1|5\.6\.(1[5-9]|[2-9][0-9])|10.0.(1[1-9]|[2-9][0-9]))' > /dev/null if echo $MYVER | grep -P '^(5\.7|10\.1|5\.6\.(1[5-9]|[2-9][0-9])|10.0.(1[1-9]|[2-9][0-9]))' > /dev/null
if [ $? = 0 ]; then then
CONNECT_ATTRS_CS=m_session_connect_attrs_cs_number CONNECT_ATTRS_CS=m_session_connect_attrs_cs_number
fi fi
CONNECT_ATTRS="" CONNECT_ATTRS=""
if [ -n "$HAS_CONNECT_ATTRS" ]; then if [ -n "$HAS_CONNECT_ATTRS" ]
CONNECT_ATTRS="print_offset PFS_thread m_session_connect_attrs then
CONNECT_ATTRS="print_offset PFS_thread m_session_connect_attrs
print_offset PFS_thread m_session_connect_attrs_length print_offset PFS_thread m_session_connect_attrs_length
print_offset PFS_thread $CONNECT_ATTRS_CS print_offset PFS_thread $CONNECT_ATTRS_CS
" "
else else
CONNECT_ATTRS='printf ", 0, 0, 0"' CONNECT_ATTRS='printf ", 0, 0, 0"'
fi
if echo $MYVER | grep -P '^5\.7' > /dev/null
then
if echo $MYVER | grep -P '^5\.7\.8' > /dev/null
then
FOUND_ROWS="print_offset THD limit_found_rows"
else
FOUND_ROWS="print_offset THD previous_found_rows"
fi
else
FOUND_ROWS="print_offset THD limit_found_rows"
fi
if echo $MYVER | grep -P '^5\.[15]' > /dev/null
then
SENT_ROW_COUNT='print_offset THD sent_row_count'
else
SENT_ROW_COUNT="print_offset THD m_sent_row_count"
fi
if echo $MYVER | grep -P '^5\.1' > /dev/null
then
ROW_COUNT_FUNC='print_offset THD row_count_func'
else
ROW_COUNT_FUNC='print_offset THD m_row_count_func'
fi
LEX_SQL=""
if [ -n "$HAS_LEX_SQL_CMD" ]
then
LEX_SQL="print_offset LEX m_sql_cmd
print_offset Sql_cmd_uninstall_plugin m_comment"
else
LEX_SQL='printf ", 0, 0"'
fi fi
cat <<EOF > offsets.gdb cat <<EOF > offsets.gdb
@ -93,23 +132,31 @@ print_offset THD killed
$CLIENT_CAPS $CLIENT_CAPS
$CONNECT_ATTRS $CONNECT_ATTRS
print_offset THD net print_offset THD net
$LEX_SQL
$FOUND_ROWS
$SENT_ROW_COUNT
$ROW_COUNT_FUNC
printf "}" printf "}"
EOF EOF
SYMPARAM="" SYMPARAM=""
if [ -n "$2" ]; then if [ -n "$2" ]
then
SYMPARAM="-s $2 -e" SYMPARAM="-s $2 -e"
fi fi
which gdb > /dev/null 2>&1 if which gdb > /dev/null 2>&1
if [ $? != 0 ]; then then
:
else
echo "ERROR: gdb not found. Make sure gdb is installed and on the path." echo "ERROR: gdb not found. Make sure gdb is installed and on the path."
exit 3; exit 3
fi fi
gdb -n -q -batch -x offsets.gdb $SYMPARAM $1 > /dev/null 2>&1 if gdb -n -q -batch -x offsets.gdb $SYMPARAM $1 > /dev/null 2>&1
then
if [ $? != 0 ]; then :
else
echo "GDB failed!!!" > /dev/stderr echo "GDB failed!!!" > /dev/stderr
exit 2 exit 2
fi fi
@ -118,7 +165,6 @@ OFFSETS=`cat gdb.txt`
echo "//offsets for: $1 ($FULL_MYVER)" echo "//offsets for: $1 ($FULL_MYVER)"
echo "$OFFSETS," echo "$OFFSETS,"
#clean up # clean up
rm gdb.txt rm gdb.txt
rm offsets.gdb rm offsets.gdb

View File

@ -835,6 +835,31 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
} }
const char *cmd = pThdData->getCmdName(); const char *cmd = pThdData->getCmdName();
ulonglong rows = 0;
if (pThdData->getStatementSource() == ThdSesData::SOURCE_QUERY_CACHE)
{
// from the query cache
rows = thd_found_rows(thd);
}
else if (strcasestr(cmd, "insert") != NULL ||
strcasestr(cmd, "update") != NULL ||
strcasestr(cmd, "delete") != NULL ||
(strcasestr(cmd, "select") != NULL && thd_row_count_func(thd) > 0))
{
// m_row_count_func will be -1 for most selects but can be > 0, e.g. select into file
rows = thd_row_count_func(thd);
}
else
{
rows = thd_sent_row_count(thd);
}
if (rows != 0UL)
{
yajl_add_uint64(gen, "rows", rows);
}
yajl_add_string_val(gen, "cmd", cmd); yajl_add_string_val(gen, "cmd", cmd);
// get objects // get objects
@ -960,11 +985,11 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
return res; return res;
} }
ThdSesData::ThdSesData(THD *pTHD) ThdSesData::ThdSesData(THD *pTHD, StatementSource source)
: m_pThd (pTHD), m_CmdName(NULL), m_UserName(NULL), : m_pThd (pTHD), m_CmdName(NULL), m_UserName(NULL),
m_objIterType(OBJ_NONE), m_tables(NULL), m_firstTable(true), m_objIterType(OBJ_NONE), m_tables(NULL), m_firstTable(true),
m_tableInf(NULL), m_index(0), m_isSqlCmd(false), m_tableInf(NULL), m_index(0), m_isSqlCmd(false),
m_port(-1) m_port(-1), m_source(source)
{ {
m_CmdName = retrieve_command (m_pThd, m_isSqlCmd); m_CmdName = retrieve_command (m_pThd, m_isSqlCmd);
m_UserName = retrieve_user (m_pThd); m_UserName = retrieve_user (m_pThd);

File diff suppressed because it is too large Load Diff

View File

@ -85,6 +85,14 @@ static int num_record_objs = 0;
static int num_whitelist_users = 0; static int num_whitelist_users = 0;
static SHOW_VAR com_status_vars_array [MAX_COM_STATUS_VARS_RECORDS] = {{0}}; static SHOW_VAR com_status_vars_array [MAX_COM_STATUS_VARS_RECORDS] = {{0}};
enum before_after_enum {
AUDIT_AFTER = 0, // default
AUDIT_BEFORE,
AUDIT_BOTH
};
static ulong before_after_mode = AUDIT_AFTER;
// regex stuff // regex stuff
static char password_masking_regex_check_buff[4096] = {0}; static char password_masking_regex_check_buff[4096] = {0};
static char * password_masking_regex_string = NULL; static char * password_masking_regex_string = NULL;
@ -173,10 +181,24 @@ static MYSQL_THDVAR_BOOL(peer_is_uds,
// terminated by a final '\0'. // terminated by a final '\0'.
char peer_info_init_value[sizeof(struct PeerInfo) + 4]; char peer_info_init_value[sizeof(struct PeerInfo) + 4];
#if defined(MARIADB_BASE_VERSION)
static MYSQL_THDVAR_ULONG(peer_info,
PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
"Pointer to peer info data",
NULL, NULL, 0, 0,
#ifdef __x86_64__
0xffffffffffffff,
#else
0xffffffff,
#endif
1);
#else // NOT mariadb
static MYSQL_THDVAR_STR(peer_info, static MYSQL_THDVAR_STR(peer_info,
PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_READONLY | /* PLUGIN_VAR_NOCMDOPT | */ PLUGIN_VAR_MEMALLOC, PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_READONLY |
PLUGIN_VAR_MEMALLOC,
"Info related to client process", "Info related to client process",
NULL, NULL, peer_info_init_value); NULL, NULL, peer_info_init_value);
#endif
THDPRINTED *GetThdPrintedList(THD *thd) THDPRINTED *GetThdPrintedList(THD *thd)
@ -202,12 +224,32 @@ static void initializePeerCredentials(THD *pThd)
int fd; int fd;
PeerInfo *peer; PeerInfo *peer;
#if defined(MARIADB_BASE_VERSION)
if (THDVAR(pThd, peer_info) == 0)
{
peer = (PeerInfo *) malloc(sizeof(PeerInfo));
if (peer)
{
memset(peer, 0, sizeof(PeerInfo));
THDVAR(pThd, peer_info) = (ulong) peer;
}
else
{
goto done;
}
}
else
{
peer = (PeerInfo *) THDVAR(pThd, peer_info);
}
#else
// zero out thread local copy of PeerInfo // zero out thread local copy of PeerInfo
peer = (PeerInfo *) THDVAR(pThd, peer_info); peer = (PeerInfo *) THDVAR(pThd, peer_info);
if (peer != NULL) if (peer != NULL)
{ {
memset(peer, 0, sizeof(PeerInfo)); memset(peer, 0, sizeof(PeerInfo));
} }
#endif
// get the NET structure // get the NET structure
sock = Audit_formatter::thd_client_fd(pThd); sock = Audit_formatter::thd_client_fd(pThd);
@ -398,6 +440,23 @@ static my_bool check_do_password_masking(const char * cmd)
return false; return false;
} }
static void do_delay(ThdSesData *pThdData)
{
if (delay_ms_val > 0)
{
const char *cmd = pThdData->getCmdName();
const char *cmds[2];
cmds[0] = cmd;
cmds[1] = NULL;
int delay = check_array(cmds, (char *) delay_cmds_array, MAX_COMMAND_CHAR_NUMBERS);
if (delay)
{
// Audit_file_handler::print_sleep(thd,delay_ms_val);
my_sleep(delay_ms_val *1000);
}
}
}
static void audit(ThdSesData *pThdData) static void audit(ThdSesData *pThdData)
{ {
THDPRINTED *pThdPrintedList = GetThdPrintedList(pThdData->getTHD()); THDPRINTED *pThdPrintedList = GetThdPrintedList(pThdData->getTHD());
@ -476,7 +535,7 @@ static void audit(ThdSesData *pThdData)
} }
} }
if (pThdPrintedList && pThdPrintedList->cur_index < MAX_NUM_QUEUE_ELEM) if (before_after_mode != AUDIT_BOTH && pThdPrintedList && pThdPrintedList->cur_index < MAX_NUM_QUEUE_ELEM)
{ {
// audit the event if we haven't done so yet or in the case of prepare_sql // audit the event if we haven't done so yet or in the case of prepare_sql
// we audit as the test "test select" doesn't go through mysql_execute_command // we audit as the test "test select" doesn't go through mysql_execute_command
@ -494,20 +553,6 @@ static void audit(ThdSesData *pThdData)
{ {
Audit_handler::log_audit_all(pThdData); Audit_handler::log_audit_all(pThdData);
} }
if (delay_ms_val > 0)
{
const char * cmd = pThdData->getCmdName();
const char *cmds[2];
cmds[0] = cmd;
cmds[1] = NULL;
int delay = check_array(cmds, (char *) delay_cmds_array, MAX_COMMAND_CHAR_NUMBERS);
if (delay)
{
// Audit_file_handler::print_sleep(thd,delay_ms_val);
my_sleep (delay_ms_val *1000);
}
}
} }
@ -596,13 +641,13 @@ static int audit_send_result_to_client(Query_cache *pthis, THD *thd, const LEX_
} }
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709 #if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
res = trampoline_send_result_to_client (pthis,thd, sql, query_length); res = trampoline_send_result_to_client(pthis,thd, sql, query_length);
#else #else
res = trampoline_send_result_to_client (pthis, thd, sql_query); res = trampoline_send_result_to_client(pthis, thd, sql_query);
#endif #endif
if (res) if (res)
{ {
ThdSesData thd_data(thd); ThdSesData thd_data(thd, ThdSesData::SOURCE_QUERY_CACHE);
audit(&thd_data); audit(&thd_data);
} }
THDVAR(thd, query_cache_table_list) = 0; THDVAR(thd, query_cache_table_list) = 0;
@ -630,7 +675,9 @@ static int audit_open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint f
res = trampoline_open_tables (thd, start, counter, flags); res = trampoline_open_tables (thd, start, counter, flags);
#endif #endif
// only log if thread id or query id is non 0 (otherwise this is comming from startup activity) // only log if thread id or query id is non 0 (otherwise this is comming from startup activity)
if (Audit_formatter::thd_inst_thread_id(thd) || Audit_formatter::thd_inst_query_id(thd)) if ( (before_after_mode == AUDIT_BEFORE || before_after_mode == AUDIT_BOTH)
&& (Audit_formatter::thd_inst_thread_id(thd)
|| Audit_formatter::thd_inst_query_id(thd)))
{ {
ThdSesData thd_data (thd); ThdSesData thd_data (thd);
audit(&thd_data); audit(&thd_data);
@ -647,7 +694,7 @@ static void audit_post_execute(THD * thd)
// query events are audited by mysql execute command // query events are audited by mysql execute command
if (Audit_formatter::thd_inst_command(thd) != COM_QUERY) if (Audit_formatter::thd_inst_command(thd) != COM_QUERY)
{ {
ThdSesData ThdData (thd); ThdSesData ThdData(thd);
if (strcasestr(ThdData.getCmdName(), "show_fields") == NULL) if (strcasestr(ThdData.getCmdName(), "show_fields") == NULL)
{ {
audit(&ThdData); audit(&ThdData);
@ -656,7 +703,6 @@ static void audit_post_execute(THD * thd)
} }
/* /*
Plugin descriptor Plugin descriptor
*/ */
@ -723,7 +769,7 @@ static int audit_notify(THD *thd, mysql_event_class_t event_class,
// in pre-authenticate, user info etc is empty. don't log it // in pre-authenticate, user info etc is empty. don't log it
&& event_connection->event_subclass != MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE && event_connection->event_subclass != MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE
#endif #endif
) )
{ {
ThdSesData ThdData(thd); ThdSesData ThdData(thd);
audit(&ThdData); audit(&ThdData);
@ -765,6 +811,13 @@ static struct st_mysql_audit audit_plugin =
// some extern definitions which are not in include files // some extern definitions which are not in include files
extern void log_slow_statement(THD *thd); extern void log_slow_statement(THD *thd);
extern int mysql_execute_command(THD *thd); extern int mysql_execute_command(THD *thd);
#if defined(MARIADB_BASE_VERSION)
extern void end_connection(THD *thd);
static int (*trampoline_end_connection)(THD *thd) = NULL;
static unsigned int trampoline_end_connection_size = 0;
#endif
#if MYSQL_VERSION_ID >= 50505 #if MYSQL_VERSION_ID >= 50505
// in 5.5 builtins is named differently // in 5.5 builtins is named differently
#define mysqld_builtins mysql_mandatory_plugins #define mysqld_builtins mysql_mandatory_plugins
@ -828,6 +881,15 @@ void remove_hot_functions()
target_function = (void*) target_function = (void*)
(int (*)(THD *thd, bool first_level)) &mysql_execute_command; (int (*)(THD *thd, bool first_level)) &mysql_execute_command;
#endif #endif
#if defined(MARIADB_BASE_VERSION)
target_function = (void*) end_connection;
remove_hot_patch_function(target_function,
(void*) trampoline_end_connection,
trampoline_end_connection_size, true);
trampoline_end_connection_size = 0;
#endif
remove_hot_patch_function(target_function, remove_hot_patch_function(target_function,
(void*) trampoline_mysql_execute_command, (void*) trampoline_mysql_execute_command,
trampoline_mysql_execute_size, true); trampoline_mysql_execute_size, true);
@ -842,7 +904,12 @@ int is_remove_patches(ThdSesData *pThdData)
LEX *pLex = Audit_formatter::thd_lex(pThdData->getTHD()); LEX *pLex = Audit_formatter::thd_lex(pThdData->getTHD());
if (pThdData->getTHD() && pLex != NULL && strncasecmp(cmd, sUninstallPlugin, strlen(sUninstallPlugin)) == 0) if (pThdData->getTHD() && pLex != NULL && strncasecmp(cmd, sUninstallPlugin, strlen(sUninstallPlugin)) == 0)
{ {
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
LEX_STRING Lex_comment = *(LEX_STRING*)(((unsigned char *) pLex) + Audit_formatter::thd_offsets.lex_comment); LEX_STRING Lex_comment = *(LEX_STRING*)(((unsigned char *) pLex) + Audit_formatter::thd_offsets.lex_comment);
#else
Sql_cmd_uninstall_plugin *up = Audit_formatter::lex_sql_cmd(pLex);
LEX_STRING Lex_comment = *(LEX_STRING*)(((unsigned char *) up) + Audit_formatter::thd_offsets.uninstall_cmd_comment);
#endif
if (strncasecmp(Lex_comment.str, "AUDIT", 5) == 0) if (strncasecmp(Lex_comment.str, "AUDIT", 5) == 0)
{ {
if (! uninstall_plugin_enable) if (! uninstall_plugin_enable)
@ -898,13 +965,18 @@ static int audit_mysql_execute_command(THD *thd)
ThdSesData thd_data(thd); ThdSesData thd_data(thd);
const char *cmd = thd_data.getCmdName(); const char *cmd = thd_data.getCmdName();
if (strcasestr(cmd, "alter") != NULL || do_delay(& thd_data);
strcasestr(cmd, "drop") != NULL ||
strcasestr(cmd, "create") != NULL || if (before_after_mode == AUDIT_BEFORE || before_after_mode == AUDIT_BOTH)
strcasestr(cmd, "truncate") != NULL ||
strcasestr(cmd, "rename") != NULL)
{ {
audit(&thd_data); if (strcasestr(cmd, "alter") != NULL ||
strcasestr(cmd, "drop") != NULL ||
strcasestr(cmd, "create") != NULL ||
strcasestr(cmd, "truncate") != NULL ||
strcasestr(cmd, "rename") != NULL)
{
audit(&thd_data);
}
} }
int res; int res;
@ -938,7 +1010,11 @@ static int audit_mysql_execute_command(THD *thd)
} }
} }
audit(&thd_data);
if (before_after_mode == AUDIT_AFTER || before_after_mode == AUDIT_BOTH)
{
audit(&thd_data);
}
if (pThdPrintedList && pThdPrintedList->cur_index > 0) if (pThdPrintedList && pThdPrintedList->cur_index > 0)
{ {
@ -987,6 +1063,24 @@ static bool audit_acl_authenticate(THD *thd, uint connect_errors, uint com_chang
} }
#endif #endif
#if defined(MARIADB_BASE_VERSION)
static void audit_end_connection(THD *thd)
{
#if MYSQL_VERSION_ID < 50600
ThdSesData thd_data(thd);
thd_data.setCmdName("Quit");
audit(&thd_data);
#endif
PeerInfo *peer = (PeerInfo *) THDVAR(thd, peer_info);
if (peer)
{
free(peer);
THDVAR(thd, peer_info) = 0;
}
trampoline_end_connection(thd);
}
#endif
static bool parse_thd_offsets_string (char *poffsets_string) static bool parse_thd_offsets_string (char *poffsets_string)
{ {
char offset_str[2048] = {0}; char offset_str[2048] = {0};
@ -1053,6 +1147,14 @@ static bool parse_thd_offsets_string (char *poffsets_string)
static bool validate_offsets(const ThdOffsets *offset) static bool validate_offsets(const ThdOffsets *offset)
{ {
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
if (offset->lex_m_sql_command == 0 || offset->uninstall_cmd_comment == 0)
{
sql_print_error("%s Offsets: missing offsets for checking 'uninstall plugin'", log_prefix);
return false;
}
#endif
// check that offsets are actually correct. We use a buff of memory as a dummy THD (32K is high enough) // check that offsets are actually correct. We use a buff of memory as a dummy THD (32K is high enough)
char buf[32*1024] = {0}; char buf[32*1024] = {0};
THD *thd = (THD *)buf; THD *thd = (THD *)buf;
@ -1219,7 +1321,7 @@ static int setup_offsets()
if (parse_thd_offsets_string(offsets_string)) if (parse_thd_offsets_string(offsets_string))
{ {
sql_print_information ("%s setup_offsets Audit_formatter::thd_offsets values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu", log_prefix, sql_print_information ("%s setup_offsets Audit_formatter::thd_offsets values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu", log_prefix,
Audit_formatter::thd_offsets.query_id, Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id, Audit_formatter::thd_offsets.thread_id,
Audit_formatter::thd_offsets.main_security_ctx, Audit_formatter::thd_offsets.main_security_ctx,
@ -1236,7 +1338,12 @@ static int setup_offsets()
Audit_formatter::thd_offsets.pfs_connect_attrs, Audit_formatter::thd_offsets.pfs_connect_attrs,
Audit_formatter::thd_offsets.pfs_connect_attrs_length, Audit_formatter::thd_offsets.pfs_connect_attrs_length,
Audit_formatter::thd_offsets.pfs_connect_attrs_cs, Audit_formatter::thd_offsets.pfs_connect_attrs_cs,
Audit_formatter::thd_offsets.net Audit_formatter::thd_offsets.net,
Audit_formatter::thd_offsets.lex_m_sql_command,
Audit_formatter::thd_offsets.uninstall_cmd_comment,
Audit_formatter::thd_offsets.found_rows,
Audit_formatter::thd_offsets.sent_row_count,
Audit_formatter::thd_offsets.row_count_func
); );
if (! validate_offsets(&Audit_formatter::thd_offsets)) if (! validate_offsets(&Audit_formatter::thd_offsets))
@ -1331,10 +1438,31 @@ static int setup_offsets()
{ {
decoffsets.net -= dec; decoffsets.net -= dec;
} }
if (decoffsets.lex_m_sql_command)
{
decoffsets.lex_m_sql_command -= dec;
}
if (decoffsets.uninstall_cmd_comment)
{
decoffsets.uninstall_cmd_comment -= dec;
}
if (decoffsets.found_rows)
{
decoffsets.found_rows -= dec;
}
if (decoffsets.sent_row_count)
{
decoffsets.sent_row_count -= dec;
}
if (decoffsets.row_count_func)
{
decoffsets.row_count_func -= dec;
}
if (validate_offsets(&decoffsets)) if (validate_offsets(&decoffsets))
{ {
Audit_formatter::thd_offsets = decoffsets; Audit_formatter::thd_offsets = decoffsets;
sql_print_information("%s Using decrement (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu", sql_print_information("%s Using decrement (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu",
log_prefix, dec, offset->version, offset->md5digest, log_prefix, dec, offset->version, offset->md5digest,
Audit_formatter::thd_offsets.query_id, Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id, Audit_formatter::thd_offsets.thread_id,
@ -1351,7 +1479,13 @@ static int setup_offsets()
Audit_formatter::thd_offsets.pfs_connect_attrs, Audit_formatter::thd_offsets.pfs_connect_attrs,
Audit_formatter::thd_offsets.pfs_connect_attrs_length, Audit_formatter::thd_offsets.pfs_connect_attrs_length,
Audit_formatter::thd_offsets.pfs_connect_attrs_cs, Audit_formatter::thd_offsets.pfs_connect_attrs_cs,
Audit_formatter::thd_offsets.net); Audit_formatter::thd_offsets.net,
Audit_formatter::thd_offsets.lex_m_sql_command,
Audit_formatter::thd_offsets.uninstall_cmd_comment,
Audit_formatter::thd_offsets.found_rows,
Audit_formatter::thd_offsets.sent_row_count,
Audit_formatter::thd_offsets.row_count_func
);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@ -1368,7 +1502,7 @@ static int setup_offsets()
if (validate_offsets(&incoffsets)) if (validate_offsets(&incoffsets))
{ {
Audit_formatter::thd_offsets = incoffsets; Audit_formatter::thd_offsets = incoffsets;
sql_print_information("%s Using increment (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu", sql_print_information("%s Using increment (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu",
log_prefix, inc, offset->version, offset->md5digest, log_prefix, inc, offset->version, offset->md5digest,
Audit_formatter::thd_offsets.query_id, Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id, Audit_formatter::thd_offsets.thread_id,
@ -1385,7 +1519,13 @@ static int setup_offsets()
Audit_formatter::thd_offsets.pfs_connect_attrs, Audit_formatter::thd_offsets.pfs_connect_attrs,
Audit_formatter::thd_offsets.pfs_connect_attrs_length, Audit_formatter::thd_offsets.pfs_connect_attrs_length,
Audit_formatter::thd_offsets.pfs_connect_attrs_cs, Audit_formatter::thd_offsets.pfs_connect_attrs_cs,
Audit_formatter::thd_offsets.net); Audit_formatter::thd_offsets.net,
Audit_formatter::thd_offsets.lex_m_sql_command,
Audit_formatter::thd_offsets.uninstall_cmd_comment,
Audit_formatter::thd_offsets.found_rows,
Audit_formatter::thd_offsets.sent_row_count,
Audit_formatter::thd_offsets.row_count_func
);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
} }
@ -1862,11 +2002,11 @@ static int audit_plugin_init(void *p)
{ {
record_objs_string_update_extended(NULL, NULL, NULL, &record_objs_string); record_objs_string_update_extended(NULL, NULL, NULL, &record_objs_string);
} }
if (NULL != password_masking_cmds_string) if (password_masking_cmds_string != NULL)
{ {
password_masking_cmds_string_update(NULL, NULL, NULL, &password_masking_cmds_string); password_masking_cmds_string_update(NULL, NULL, NULL, &password_masking_cmds_string);
} }
if (NULL != password_masking_regex_string) if (password_masking_regex_string != NULL)
{ {
password_masking_regex_string_update(NULL, NULL, NULL, &password_masking_regex_string); password_masking_regex_string_update(NULL, NULL, NULL, &password_masking_regex_string);
} }
@ -2035,6 +2175,15 @@ static int audit_plugin_init(void *p)
DBUG_RETURN(1); DBUG_RETURN(1);
} }
#if defined(MARIADB_BASE_VERSION)
target_function = (void*) end_connection;
if (do_hot_patch((void **)&trampoline_end_connection, &trampoline_end_connection_size,
(void *)target_function, (void *)audit_end_connection, "end_connection"))
{
DBUG_RETURN(1);
}
#endif
if (set_com_status_vars_array () != 0) if (set_com_status_vars_array () != 0)
{ {
DBUG_RETURN(1); DBUG_RETURN(1);
@ -2150,6 +2299,7 @@ static MYSQL_SYSVAR_STR(json_log_file, json_file_handler.m_io_dest,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
"AUDIT plugin json log file name", "AUDIT plugin json log file name",
NULL, NULL, "mysql-audit.json"); NULL, NULL, "mysql-audit.json");
static MYSQL_SYSVAR_LONG(json_file_bufsize, json_file_handler.m_bufsize, static MYSQL_SYSVAR_LONG(json_file_bufsize, json_file_handler.m_bufsize,
PLUGIN_VAR_RQCMDARG, PLUGIN_VAR_RQCMDARG,
"AUDIT plugin json log file buffer size. Buffer size in bytes (larger size may improve performance). 0 = use default size, 1 = no buffering. If changed during runtime need to perform a flush for the new value to take affect.", "AUDIT plugin json log file buffer size. Buffer size in bytes (larger size may improve performance). 0 = use default size, 1 = no buffering. If changed during runtime need to perform a flush for the new value to take affect.",
@ -2245,7 +2395,7 @@ static MYSQL_SYSVAR_STR(password_masking_cmds, password_masking_cmds_string,
"AUDIT plugin commands to apply password masking regex to, comma separated", "AUDIT plugin commands to apply password masking regex to, comma separated",
NULL, password_masking_cmds_string_update, NULL, password_masking_cmds_string_update,
// set password is recorded as set_option // set password is recorded as set_option
"CREATE_USER,GRANT,SET_OPTION,SLAVE_START,CREATE_SERVER,ALTER_SERVER,CHANGE_MASTER"); "CREATE_USER,GRANT,SET_OPTION,SLAVE_START,CREATE_SERVER,ALTER_SERVER,CHANGE_MASTER,UPDATE");
static MYSQL_SYSVAR_STR(whitelist_users, whitelist_users_string, static MYSQL_SYSVAR_STR(whitelist_users, whitelist_users_string,
PLUGIN_VAR_RQCMDARG, PLUGIN_VAR_RQCMDARG,
"AUDIT plugin whitelisted users whose queries are not recorded, comma separated", "AUDIT plugin whitelisted users whose queries are not recorded, comma separated",
@ -2256,6 +2406,26 @@ static MYSQL_SYSVAR_STR(record_objs, record_objs_string,
"AUDIT plugin objects to record, comma separated. If set then only queries containing these objects will be recorded.", "AUDIT plugin objects to record, comma separated. If set then only queries containing these objects will be recorded.",
NULL, record_objs_string_update_extended, NULL); NULL, record_objs_string_update_extended, NULL);
static const char *before_after_names[] =
{
"after", "before", "both", NullS
};
TYPELIB before_after_typelib =
{
array_elements(before_after_names) - 1,
"before_after_typelib",
before_after_names,
NULL
};
static MYSQL_SYSVAR_ENUM(before_after, before_after_mode,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
"AUDIT plugin log statements before execution, after, or both. Default is 'after'",
NULL, NULL, 0,
& before_after_typelib);
/* /*
* Plugin system vars * Plugin system vars
*/ */
@ -2296,6 +2466,7 @@ static struct st_mysql_sys_var* audit_system_variables[] =
MYSQL_SYSVAR(set_peer_cred), MYSQL_SYSVAR(set_peer_cred),
MYSQL_SYSVAR(peer_is_uds), MYSQL_SYSVAR(peer_is_uds),
MYSQL_SYSVAR(peer_info), MYSQL_SYSVAR(peer_info),
MYSQL_SYSVAR(before_after),
NULL NULL
}; };