10 Commits

Author SHA1 Message Date
Aharon Robbins
cd9400da0e Add offsets for: MySQL 5.5.55, 5.6.36, 5.7.18 and MariaDB 5.5.55. 2017-04-26 11:45:04 +03:00
Aharon Robbins
0ff56ddfb4 Fix Issue #155 - slave shutdown crashes master.
Add offsets for MariaDB 10.0.30 and 10.1.22.
2017-03-15 15:21:08 +02:00
Arnold Robbins
570bfb0de6 Fix core dump if no connection attributes available.
Reported in https://github.com/mcafee/mysql-audit/issues/161.
2017-02-23 12:03:58 +02:00
Arnold Robbins
ba21262b81 * 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.
2017-02-13 16:22:15 +02:00
Arnold Robbins
4fbcae3a81 Add info about process on other end of UNIX Domain Socket and
also client port of remote connection for TCP Socket. New offset
required for this, so updated the offsets as well.
2017-01-02 11:52:28 +02:00
Arnold Robbins
d83c36627c Update offsets to add MariaDB 5.5.54.
Disable writing of client capabilities by default.
2016-12-26 12:06:51 +02:00
Arnold Robbins
5e8055249e Add offsets for MariaDB 10.1.20. 2016-12-20 12:32:03 +02:00
Arnold Robbins
c33a99baa1 Add offsets for MySQL 5.5.54 and 5.5.17. 2016-12-13 10:09:42 +02:00
Arnold Robbins
f29f9bb8ca Add offsets for MySQL 5.6.35. 2016-12-11 13:49:16 +02:00
Arnold Robbins
53b1db74e3 Add new offsets for client capabilities and session connect attributes.
Add two new variables: client_capabilities and sess_connect_attrs.
Update lists of compiled in "known" offsets.
2016-12-07 10:05:00 +02:00
6 changed files with 2133 additions and 1109 deletions

View File

@@ -48,10 +48,23 @@ typedef struct _THDPRINTED {
char is_thd_printed_queue[MAX_NUM_QUEUE_ELEM];
} THDPRINTED;
#define MAX_COMMAND_CHAR_NUMBERS 40
struct PeerInfo {
unsigned long pid;
enum { MAX_APP_NAME_LEN = 128, MAX_USER_NAME_LEN = 128 };
char appName[MAX_APP_NAME_LEN + 1];
char osUser[MAX_USER_NAME_LEN + 1]; // allow lots, in case from LDAP or some such
PeerInfo() : pid(0) {
memset(appName, 0, sizeof appName);
memset(osUser, 0, sizeof osUser);
}
};
PeerInfo *retrieve_peerinfo(THD *thd);
const char *retrieve_command(THD *thd, bool& is_sql_cmd);
typedef size_t OFFSET;
#define MAX_COMMAND_CHAR_NUMBERS 40
#define MAX_COM_STATUS_VARS_RECORDS 512
// mysql max identifier is 64 so 2*64 + . and null
@@ -78,6 +91,16 @@ typedef struct ThdOffsets {
OFFSET sec_ctx_priv_user;
OFFSET db;
OFFSET killed;
OFFSET client_capabilities;
OFFSET pfs_connect_attrs;
OFFSET pfs_connect_attrs_length;
OFFSET pfs_connect_attrs_cs;
OFFSET net;
OFFSET lex_m_sql_command;
OFFSET uninstall_cmd_comment;
OFFSET found_rows;
OFFSET sent_row_count;
OFFSET row_count_func;
} ThdOffsets;
/*
@@ -110,11 +133,18 @@ class ThdSesData {
public:
// enum indicating from where the object list came from
enum ObjectIterType { OBJ_NONE, OBJ_DB, OBJ_QUERY_CACHE, OBJ_TABLE_LIST };
ThdSesData(THD *pTHD);
THD *getTHD() { return m_pThd;}
const char *getCmdName() { return m_CmdName; }
// 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;}
const char *getCmdName() const { return m_CmdName; }
void setCmdName(const char *cmd) { m_CmdName = cmd; }
const char *getUserName() { return m_UserName; }
const unsigned long getPeerPid() const;
const char *getAppName() const;
const char *getOsUser() const;
const int getPort() const { return m_port; }
const StatementSource getStatementSource() const { return m_source; }
/**
* Start fetching objects. Return true if there are objects available.
*/
@@ -141,6 +171,13 @@ private:
QueryTableInf *m_tableInf;
int m_index;
// Statement source
StatementSource m_source;
PeerInfo *m_peerInfo;
int m_port; // TCP port of remote side
protected:
ThdSesData(const ThdSesData&);
ThdSesData &operator =(const ThdSesData&);
@@ -319,6 +356,194 @@ public:
return *(LEX **) (((unsigned char *) thd) + Audit_formatter::thd_offsets.lex);
}
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
//in mysql 5.7 capabilities flag moved to protocol. We use the capabilities offset to point to m_protocol
//and get from protocol the capabilities flag
static inline ulong thd_client_capabilities(THD *thd)
{
if (! Audit_formatter::thd_offsets.client_capabilities)
{
//no offsets - return 0
return 0;
}
Protocol * prot = *(Protocol **) (((unsigned char *) thd) + Audit_formatter::thd_offsets.client_capabilities);
if(!prot)
{
return 0;
}
return prot->get_client_capabilities();
}
#else
static inline ulong thd_client_capabilities(THD *thd)
{
if (! Audit_formatter::thd_offsets.client_capabilities)
{
//no offsets - return 0
return 0;
}
return *(ulong *) (((unsigned char *) thd) + Audit_formatter::thd_offsets.client_capabilities);
}
#endif
static inline const char * pfs_connect_attrs(void * pfs)
{
if (! Audit_formatter::thd_offsets.pfs_connect_attrs)
{
//no offsets - return null
return NULL;
}
const char **pfs_pointer = (const char **) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs);
if (pfs_pointer == NULL)
{
return NULL;
}
return *pfs_pointer;
}
static inline uint pfs_connect_attrs_length(void * pfs)
{
if (! Audit_formatter::thd_offsets.pfs_connect_attrs_length)
{
//no offsets - return 0
return 0;
}
return *(uint *) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs_length);
}
static inline const CHARSET_INFO * pfs_connect_attrs_cs(void * pfs)
{
if (! Audit_formatter::thd_offsets.pfs_connect_attrs_cs)
{
//no offsets - return null
return NULL;
}
#if (!defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50600) || (defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100010)
/**
* m_session_connect_attrs_cs changed to: m_session_connect_attrs_cs_number
* in 5.6.15 and up, 5.7 and mariadb 10.0.11 and up, and 10.1.
* see: storage/perfschema/table_session_connect.cc
* and: storage/perfschema/pfs_instr.h
*/
static bool first = true;
static int major, minor, patch;
if (first)
{
sscanf(server_version, "%d.%d.%d", & major, & minor, & patch);
// sql_print_information("Audit_plugin: extracted version: %d.%d.%d",
// major, minor, patch);
}
if ( ( major == 5 && ( (minor == 6 && patch >= 15) || minor >= 7) ) // MySQL
|| ( major == 10 && ( (minor == 0 && patch >= 11) || minor >= 1) ) ) // MariaDB
{
uint cs_number = *(uint *) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs_cs);
if (!cs_number)
{
return NULL;
}
return get_charset(cs_number, MYF(0));
}
else
#endif
{
return *(const CHARSET_INFO **) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs_cs);
}
}
static inline int thd_client_fd(THD *thd)
{
if (! Audit_formatter::thd_offsets.net)
{
return -1;
}
NET *net = ((NET *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.net));
// get the socket for the peer
int sock = -1;
if (net->vio != NULL) // MySQL 5.7.17 - this can happen. :-(
{
#if MYSQL_VERSION_ID < 50600
sock = net->vio->sd;
#else
sock = net->vio->mysql_socket.fd;
#endif
}
return sock;
}
static inline int thd_client_port(THD *thd)
{
if (! Audit_formatter::thd_offsets.net)
{
return -1;
}
NET *net = ((NET *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.net));
// get the port for the remote end
int port = -1;
if (net->vio != NULL) // MySQL 5.7.17 - this can happen. :-(
{
struct sockaddr_in *in;
in = (struct sockaddr_in *) & net->vio->remote;
port = in->sin_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
// and it may return an invalid value for view_db
static inline const char *table_get_db_name(TABLE_LIST *table)
@@ -348,6 +573,9 @@ public:
Audit_json_formatter()
: m_msg_delimiter(NULL),
m_write_start_msg(true),
m_write_sess_connect_attrs(true),
m_write_client_capabilities(false),
m_write_socket_creds(true),
m_password_mask_regex_preg(NULL),
m_password_mask_regex_compiled(false),
m_perform_password_masking(NULL)
@@ -386,7 +614,24 @@ public:
* Public so sysvar can update.
*/
my_bool m_write_start_msg;
/**
* include session oonnect attributes
* Public so sysvar can update
*/
my_bool m_write_sess_connect_attrs;
/**
* include client capabilities
* Public for sysvar
*/
my_bool m_write_client_capabilities;
/**
* include socket credentials from Unix Domain Socket
* Public for sysvar
*/
my_bool m_write_socket_creds;
/**
* Callback function to determine if password masking should be performed

View File

@@ -59,6 +59,7 @@
#if MYSQL_VERSION_ID >= 50709
#include <sql/log.h>
#if ! defined(MARIADB_BASE_VERSION)
#include <sql/sql_plugin.h>
#include <sql/auth/auth_common.h>
#endif
#endif
@@ -94,4 +95,9 @@ extern "C" char *thd_security_context(MYSQL_THD thd, char *buffer, unsigned int
#endif
//Define HAVE_SESS_CONNECT_ATTRS. We define it for mysql 5.6 and above
#if (!defined(MARIADB_BASE_VERSION)) && MYSQL_VERSION_ID >= 50600
#define HAVE_SESS_CONNECT_ATTRS 1
#endif
#endif // MYSQL_INCL_H

View File

@@ -1,25 +1,24 @@
#!/bin/sh
if [ $# = 0 ]; then
if [ $# = 0 ]
then
echo "Usage: $0 <mysqld executable> [optional mysqld symbols]"
echo "Will extract offsets from mysqld. Requires gdb, md5sum and mysqld symbols."
exit 1
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"
echo $FULL_MYVER | grep 'log' > /dev/null
if [ $? = 0 ]; then
MYVER=`echo "$MYVER" | grep -P -o '.+(?=-log)'`
if echo $FULL_MYVER | grep 'log' > /dev/null
then
MYVER=`echo "$MYVER" | grep -P -o '.+(?=-log)'`
fi
COMMAND_MEMBER=command
@@ -30,26 +29,90 @@ HOST=host
IP=ip
PRIV_USER=priv_user
DB=db
CLIENT_CAPS="print_offset THD client_capabilities"
#in 5.6 command member is named m_command
echo $MYVER | grep -P '^(5\.6|5\.7|10\.)' > /dev/null
if [ $? = 0 ]; then
# In 5.6 command member is named m_command
if echo $MYVER | grep -P '^(5\.6|5\.7|10\.)' > /dev/null
then
COMMAND_MEMBER=m_command
HAS_CONNECT_ATTRS=yes
fi
#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 [ $? = 0 ]; then
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
if echo $MYVER | grep -P '^(5\.7)' > /dev/null
then
THREAD_ID=m_thread_id
SEC_CONTEXT=m_main_security_ctx
USER=m_user
HOST=m_host
IP=m_ip
PRIV_USER=m_priv_user
DB=m_db
SEC_CONTEXT=m_main_security_ctx
USER=m_user
HOST=m_host
IP=m_ip
PRIV_USER=m_priv_user
DB=m_db
# client capabilities has moved out THD in 5.7. Set to 0
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
# 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
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
then
CONNECT_ATTRS_CS=m_session_connect_attrs_cs_number
fi
CONNECT_ATTRS=""
if [ -n "$HAS_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 $CONNECT_ATTRS_CS
"
else
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
cat <<EOF > offsets.gdb
set logging on
set width 0
define print_offset
printf ", %d", (size_t)&((\$arg0*)0)->\$arg1
end
@@ -66,23 +129,34 @@ print_offset Security_context $IP
print_offset Security_context $PRIV_USER
print_offset THD $DB
print_offset THD killed
$CLIENT_CAPS
$CONNECT_ATTRS
print_offset THD net
$LEX_SQL
$FOUND_ROWS
$SENT_ROW_COUNT
$ROW_COUNT_FUNC
printf "}"
EOF
SYMPARAM=""
if [ -n "$2" ]; then
if [ -n "$2" ]
then
SYMPARAM="-s $2 -e"
fi
which gdb > /dev/null 2>&1
if [ $? != 0 ]; then
if which gdb > /dev/null 2>&1
then
:
else
echo "ERROR: gdb not found. Make sure gdb is installed and on the path."
exit 3;
exit 3
fi
gdb -n -q -batch -x offsets.gdb $SYMPARAM $1 > /dev/null 2>&1
if [ $? != 0 ]; then
if gdb -n -q -batch -x offsets.gdb $SYMPARAM $1 > /dev/null 2>&1
then
:
else
echo "GDB failed!!!" > /dev/stderr
exit 2
fi
@@ -91,7 +165,6 @@ OFFSETS=`cat gdb.txt`
echo "//offsets for: $1 ($FULL_MYVER)"
echo "$OFFSETS,"
#clean up
# clean up
rm gdb.txt
rm offsets.gdb

View File

@@ -23,6 +23,8 @@
// for definition of sockaddr_un
#include <sys/un.h>
#include <stdio_ext.h>
#include <limits.h>
#include <unistd.h>
#include "static_assert.h"
// utility macro to log also with a date as a prefix
@@ -178,7 +180,7 @@ void Audit_handler::log_audit(ThdSesData *pThdData)
return;
}
// sanity check that offsets match
// we can also consider using secutiry context function to do some sanity checks
// we can also consider using security context function to do some sanity checks
// char buffer[2048];
// thd_security_context(thd, buffer, 2048, 2000);
// fprintf(log_file, "info from security context: %s\n", buffer);
@@ -209,11 +211,11 @@ void Audit_handler::log_audit(ThdSesData *pThdData)
{
pthread_mutex_lock(&LOCK_io);
//get the io lock. After acquiring the lock do another check that we really need to start (maybe another thread did this already)
if(!m_failed)
if (!m_failed)
{
do_log = true;
}
else if(m_retry_interval > 0 &&
else if (m_retry_interval > 0 &&
difftime(time(NULL), m_last_retry_sec_ts) > m_retry_interval)
{
do_log = handler_start_nolock();
@@ -362,7 +364,8 @@ bool Audit_io_handler::handler_start_internal()
return true;
}
bool Audit_io_handler::handler_log_audit(ThdSesData *pThdData) {
bool Audit_io_handler::handler_log_audit(ThdSesData *pThdData)
{
return (m_formatter->event_format(pThdData, this) >= 0);
}
@@ -557,7 +560,7 @@ static const char *thd_query_str(THD *thd, size_t *len)
#elif defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID > 50140
extern "C" {
MYSQL_LEX_STRING *thd_query_string(MYSQL_THD thd);
MYSQL_LEX_STRING *thd_query_string(MYSQL_THD thd);
}
static const char *thd_query_str(THD *thd, size_t *len)
@@ -620,6 +623,7 @@ ssize_t Audit_json_formatter::start_msg_format(IWriter *writer)
yajl_add_string_val(gen, "mysql-program", my_progname);
yajl_add_string_val(gen, "mysql-socket", mysqld_unix_port);
yajl_add_uint64(gen, "mysql-port", mysqld_port);
yajl_add_uint64(gen, "server_pid", getpid());
ssize_t res = -2;
yajl_gen_status stat = yajl_gen_map_close(gen); // close the object
@@ -671,6 +675,97 @@ static const char *replace_in_string(THD *thd,
return new_str;
}
#ifdef HAVE_SESS_CONNECT_ATTRS
#include <storage/perfschema/pfs_instr.h>
//declare the function: parse_length_encoded_string from: storage/perfschema/table_session_connect.cc
bool parse_length_encoded_string(const char **ptr,
char *dest, uint dest_size,
uint *copied_len,
const char *start_ptr, uint input_length,
bool copy_data,
const CHARSET_INFO *from_cs,
uint nchars_max);
/**
* Code based upon read_nth_attribute of storage/perfschema/table_session_connect.cc
* Only difference we do once loop and write out the attributes
*/
static void log_session_connect_attrs(yajl_gen gen, THD *thd)
{
PFS_thread * pfs = PFS_thread::get_current_thread();
const char * connect_attrs = Audit_formatter::pfs_connect_attrs(pfs);
const uint connect_attrs_length = Audit_formatter::pfs_connect_attrs_length(pfs);
const CHARSET_INFO *connect_attrs_cs = Audit_formatter::pfs_connect_attrs_cs(pfs);
//sanity max attributes
const uint max_idx = 32;
uint idx;
const char *ptr;
bool array_start = false;
if(!connect_attrs || !connect_attrs_length || !connect_attrs_cs)
{
//either offsets are wrong or not set
return;
}
for (ptr= connect_attrs, idx= 0;
(uint)(ptr - connect_attrs) < connect_attrs_length && idx <= max_idx;
idx++)
{
const uint MAX_COPY_CHARS_NAME = 32;
const uint MAX_COPY_CHARS_VAL = 256;
//time 6 (max udf8 char length)
char attr_name[MAX_COPY_CHARS_NAME*6];
char attr_value[MAX_COPY_CHARS_VAL *6];
uint copy_length, attr_name_length, attr_value_length;
/* always do copying */
bool fill_in_attr_name= true;
bool fill_in_attr_value= true;
/* read the key */
copy_length = 0;
if (parse_length_encoded_string(&ptr,
attr_name, array_elements(attr_name), &copy_length,
connect_attrs,
connect_attrs_length,
fill_in_attr_name,
connect_attrs_cs, MAX_COPY_CHARS_NAME) || !copy_length)
{
//something went wrong or we are done
break;
}
attr_name_length = copy_length;
/* read the value */
copy_length = 0;
if (parse_length_encoded_string(&ptr,
attr_value, array_elements(attr_value), &copy_length,
connect_attrs,
connect_attrs_length,
fill_in_attr_value,
connect_attrs_cs, MAX_COPY_CHARS_VAL) || !copy_length)
{
break;
}
attr_value_length= copy_length;
if(!array_start)
{
yajl_add_string(gen, "connect_attrs");
yajl_gen_map_open(gen);
array_start = true;
}
yajl_gen_string(gen, (const unsigned char*)attr_name, attr_name_length);
yajl_gen_string(gen, (const unsigned char*)attr_value, attr_value_length);
} //close for loop
if(array_start)
{
yajl_gen_map_close(gen);
}
return;
}
#endif
ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer)
{
THD *thd = pThdData->getTHD();
@@ -692,9 +787,79 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
yajl_add_uint64(gen, "query-id", qid);
yajl_add_string_val(gen, "user", pThdData->getUserName());
yajl_add_string_val(gen, "priv_user", Audit_formatter::thd_inst_main_security_ctx_priv_user(thd));
yajl_add_string_val(gen, "host", Audit_formatter::thd_inst_main_security_ctx_host(thd));
yajl_add_string_val(gen, "ip", Audit_formatter::thd_inst_main_security_ctx_ip(thd));
// For backwards compatibility, we always send "host".
// If there is no value, send the IP address
const char *host = Audit_formatter::thd_inst_main_security_ctx_host(thd);
if (host == NULL || *host == '\0')
{
host = Audit_formatter::thd_inst_main_security_ctx_ip(thd);
}
yajl_add_string_val(gen, "host", host);
if (m_write_client_capabilities)
{
ulong caps = Audit_formatter::thd_client_capabilities(thd);
if (caps)
{
yajl_add_uint64(gen, "capabilities", caps);
}
}
#ifdef HAVE_SESS_CONNECT_ATTRS
if (m_write_sess_connect_attrs)
{
log_session_connect_attrs(gen, thd);
}
#endif
if (pThdData->getPeerPid() != 0) // Unix Domain Socket
{
if (m_write_socket_creds)
{
yajl_add_uint64(gen, "pid", pThdData->getPeerPid());
if (pThdData->getOsUser() != NULL)
{
yajl_add_string_val(gen, "os_user", pThdData->getOsUser());
}
if (pThdData->getAppName() != NULL)
{
yajl_add_string_val(gen, "appname", pThdData->getAppName());
}
}
}
else if (pThdData->getPort() > 0) // TCP socket
{
yajl_add_uint64(gen, "client_port", pThdData->getPort());
}
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);
// get objects
@@ -820,13 +985,21 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
return res;
}
ThdSesData::ThdSesData(THD *pTHD)
ThdSesData::ThdSesData(THD *pTHD, StatementSource source)
: m_pThd (pTHD), m_CmdName(NULL), m_UserName(NULL),
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_source(source)
{
m_CmdName = retrieve_command (m_pThd, m_isSqlCmd);
m_UserName = retrieve_user (m_pThd);
m_peerInfo = retrieve_peerinfo(m_pThd);
if (m_peerInfo && m_peerInfo->pid == 0)
{
// not UDS, get remote port
m_port = Audit_formatter::thd_client_port(m_pThd);
}
}
bool ThdSesData::startGetObjects()
@@ -940,6 +1113,21 @@ bool ThdSesData::getNextObject(const char **db_name, const char **obj_name, cons
}
}
const unsigned long ThdSesData::getPeerPid() const
{
return (m_peerInfo != NULL ? m_peerInfo->pid : 0L);
}
const char *ThdSesData::getAppName() const
{
return (m_peerInfo != NULL ? m_peerInfo->appName : NULL);
}
const char *ThdSesData::getOsUser() const
{
return (m_peerInfo != NULL ? m_peerInfo->osUser : NULL);
}
pcre *Audit_json_formatter::regex_compile(const char *str)
{
const char *error;

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
#include "hot_patch.h"
#include <stdlib.h>
#include <ctype.h>
#include <pwd.h>
#include "audit_handler.h"
#include <string.h>
@@ -84,6 +85,14 @@ static int num_record_objs = 0;
static int num_whitelist_users = 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
static char password_masking_regex_check_buff[4096] = {0};
static char * password_masking_regex_string = NULL;
@@ -152,6 +161,46 @@ static MYSQL_THDVAR_ULONG(query_cache_table_list,
#endif
1);
static MYSQL_THDVAR_BOOL(set_peer_cred,
PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
"track if peer credential information is set",
NULL, NULL, 0);
static MYSQL_THDVAR_BOOL(peer_is_uds,
PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
"track if client is connected on Unix Domain Socket",
NULL, NULL, 0);
// We use a THDVAR_STR to store the PeerInfo struct for this thread.
// In order to get MySQL to allocate storage correctly, we have to
// fool it into thinking that the storage holds a C string. To do so,
// we create a char array of the right size and use that as the initial
// value. Then in the constructor routines, below, we initialize the
// contents to look like a C string with a bunch of '0' characters,
// terminated by a final '\0'.
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,
PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_READONLY |
PLUGIN_VAR_MEMALLOC,
"Info related to client process",
NULL, NULL, peer_info_init_value);
#endif
THDPRINTED *GetThdPrintedList(THD *thd)
{
THDPRINTED *pThdPrintedList= (THDPRINTED*) THDVAR(thd, is_thd_printed_list);
@@ -163,6 +212,173 @@ THDPRINTED *GetThdPrintedList(THD *thd)
return NULL;
}
static void initializePeerCredentials(THD *pThd)
{
int sock;
struct stat sbuf;
struct ucred cred;
socklen_t cred_len = sizeof(cred);
char buf[BUFSIZ];
struct passwd pwd, *pwbuf = NULL;
char *username = NULL;
int fd;
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
peer = (PeerInfo *) THDVAR(pThd, peer_info);
if (peer != NULL)
{
memset(peer, 0, sizeof(PeerInfo));
}
#endif
// get the NET structure
sock = Audit_formatter::thd_client_fd(pThd);
// These tests are not chained so that we can add
// debug prints if necessary.
if (sock < 0)
{
goto done;
}
// Is it a Unix Domain socket?
if (fstat(sock, &sbuf) < 0)
{
goto done;
}
if ((sbuf.st_mode & S_IFMT) != S_IFSOCK)
{
goto done;
}
// At this point, we know we have a Unix domain socket.
if (! json_formatter.m_write_socket_creds)
{
// We need to set this, so that we don't send the
// client port, but we don't bother getting the command
// name and user name, since they won't be sent.
THDVAR(pThd, peer_is_uds) = TRUE;
goto done;
}
// Do a SO_PEERCRED
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0)
{
goto done;
}
if (cred_len != sizeof(cred))
{
goto done;
}
if (peer == NULL)
{
goto done;
}
// Set PID
peer->pid = cred.pid;
// Get OS user name
getpwuid_r(cred.uid, & pwd, buf, sizeof(buf), & pwbuf);
if (pwbuf == NULL)
{
// no name, send UID
snprintf(buf, sizeof(buf) - 1, "%lu", (unsigned long) cred.uid);
username = buf;
}
else
{
username = pwd.pw_name;
}
strncpy(peer->osUser, username, PeerInfo::MAX_USER_NAME_LEN);
peer->osUser[PeerInfo::MAX_USER_NAME_LEN] = '\0';
// Get the progran name out of /proc
snprintf(buf, sizeof(buf) - 1, "/proc/%d/cmdline", cred.pid);
fd = open(buf, O_RDONLY);
if (fd >= 0)
{
char data[PATH_MAX+1];
ssize_t count = read(fd, data, sizeof(data) - 1);
if (count > 0)
{
data[count] = '\0'; // just in case
if (strlen(data) <= PeerInfo::MAX_APP_NAME_LEN)
{
strcpy(peer->appName, data);
}
else
{
// take last 128 characters, typically will be
// .../x/y/appname
char *cp = data + strlen(data) - PeerInfo::MAX_APP_NAME_LEN;
strncpy(peer->appName, cp, PeerInfo::MAX_APP_NAME_LEN);
peer->appName[PeerInfo::MAX_APP_NAME_LEN] = '\0';
}
}
close(fd);
}
// in case of errors:
if (peer->appName[0] == '\0')
{
snprintf(peer->appName, sizeof(peer->appName) - 1, "pid:%d", cred.pid);
}
// set that we have a UDS, so that THD vars will be used
THDVAR(pThd, peer_is_uds) = TRUE;
done:
THDVAR(pThd, set_peer_cred) = TRUE;
}
PeerInfo *retrieve_peerinfo(THD *thd)
{
if (! THDVAR(thd, set_peer_cred))
{
initializePeerCredentials(thd);
}
if (json_formatter.m_write_socket_creds)
{
PeerInfo *peer = (PeerInfo *) THDVAR(thd, peer_info);
if (THDVAR(thd, peer_is_uds) && peer != NULL);
{
return peer;
}
}
return NULL;
}
static int check_array(const char *cmds[],const char *array, int length)
{
for (int k = 0; array[k * length] !='\0';k++)
@@ -224,6 +440,23 @@ static my_bool check_do_password_masking(const char * cmd)
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)
{
THDPRINTED *pThdPrintedList = GetThdPrintedList(pThdData->getTHD());
@@ -302,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
// we audit as the test "test select" doesn't go through mysql_execute_command
@@ -320,20 +553,6 @@ static void audit(ThdSesData *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);
}
}
}
@@ -422,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
res = trampoline_send_result_to_client (pthis,thd, sql, query_length);
res = trampoline_send_result_to_client(pthis,thd, sql, query_length);
#else
res = trampoline_send_result_to_client (pthis, thd, sql_query);
res = trampoline_send_result_to_client(pthis, thd, sql_query);
#endif
if (res)
{
ThdSesData thd_data(thd);
ThdSesData thd_data(thd, ThdSesData::SOURCE_QUERY_CACHE);
audit(&thd_data);
}
THDVAR(thd, query_cache_table_list) = 0;
@@ -456,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);
#endif
// 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);
audit(&thd_data);
@@ -473,7 +694,7 @@ static void audit_post_execute(THD * thd)
// query events are audited by mysql execute command
if (Audit_formatter::thd_inst_command(thd) != COM_QUERY)
{
ThdSesData ThdData (thd);
ThdSesData ThdData(thd);
if (strcasestr(ThdData.getCmdName(), "show_fields") == NULL)
{
audit(&ThdData);
@@ -482,7 +703,6 @@ static void audit_post_execute(THD * thd)
}
/*
Plugin descriptor
*/
@@ -503,6 +723,15 @@ static int audit_notify(THD *thd, mysql_event_class_t event_class,
const void * event)
#endif
{
if (thd == NULL) // can happen in replication setup
{
#if ! defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
return 0; // return success, keep MySQL going
#else
return;
#endif
}
if (MYSQL_AUDIT_GENERAL_CLASS == event_class)
{
const struct mysql_event_general *event_general =
@@ -544,7 +773,12 @@ static int audit_notify(THD *thd, mysql_event_class_t event_class,
const struct mysql_event_connection *event_connection =
(const struct mysql_event_connection *) event;
// only audit for connect and change_user. disconnect is caught by general event
if (event_connection->event_subclass != MYSQL_AUDIT_CONNECTION_DISCONNECT)
if (event_connection->event_subclass != MYSQL_AUDIT_CONNECTION_DISCONNECT
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
// in pre-authenticate, user info etc is empty. don't log it
&& event_connection->event_subclass != MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE
#endif
)
{
ThdSesData ThdData(thd);
audit(&ThdData);
@@ -586,6 +820,13 @@ static struct st_mysql_audit audit_plugin =
// some extern definitions which are not in include files
extern void log_slow_statement(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
// in 5.5 builtins is named differently
#define mysqld_builtins mysql_mandatory_plugins
@@ -649,6 +890,15 @@ void remove_hot_functions()
target_function = (void*)
(int (*)(THD *thd, bool first_level)) &mysql_execute_command;
#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,
(void*) trampoline_mysql_execute_command,
trampoline_mysql_execute_size, true);
@@ -663,7 +913,12 @@ int is_remove_patches(ThdSesData *pThdData)
LEX *pLex = Audit_formatter::thd_lex(pThdData->getTHD());
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);
#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 (! uninstall_plugin_enable)
@@ -719,13 +974,18 @@ static int audit_mysql_execute_command(THD *thd)
ThdSesData thd_data(thd);
const char *cmd = thd_data.getCmdName();
if (strcasestr(cmd, "alter") != NULL ||
strcasestr(cmd, "drop") != NULL ||
strcasestr(cmd, "create") != NULL ||
strcasestr(cmd, "truncate") != NULL ||
strcasestr(cmd, "rename") != NULL)
do_delay(& thd_data);
if (before_after_mode == AUDIT_BEFORE || before_after_mode == AUDIT_BOTH)
{
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;
@@ -759,7 +1019,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)
{
@@ -808,6 +1072,24 @@ static bool audit_acl_authenticate(THD *thd, uint connect_errors, uint com_chang
}
#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)
{
char offset_str[2048] = {0};
@@ -874,6 +1156,14 @@ static bool parse_thd_offsets_string (char *poffsets_string)
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)
char buf[32*1024] = {0};
THD *thd = (THD *)buf;
@@ -1038,9 +1328,9 @@ 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", 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.thread_id,
Audit_formatter::thd_offsets.main_security_ctx,
@@ -1052,7 +1342,18 @@ static int setup_offsets()
Audit_formatter::thd_offsets.sec_ctx_ip,
Audit_formatter::thd_offsets.sec_ctx_priv_user,
Audit_formatter::thd_offsets.db,
Audit_formatter::thd_offsets.killed);
Audit_formatter::thd_offsets.killed,
Audit_formatter::thd_offsets.client_capabilities,
Audit_formatter::thd_offsets.pfs_connect_attrs,
Audit_formatter::thd_offsets.pfs_connect_attrs_length,
Audit_formatter::thd_offsets.pfs_connect_attrs_cs,
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))
{
@@ -1134,10 +1435,43 @@ static int setup_offsets()
decoffsets.thread_id -= dec;
decoffsets.main_security_ctx -= dec;
decoffsets.command -= dec;
if(decoffsets.killed)
{
decoffsets.killed -= dec;
}
if(decoffsets.client_capabilities)
{
decoffsets.client_capabilities -= dec;
}
if(decoffsets.net)
{
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))
{
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",
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,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
@@ -1148,7 +1482,19 @@ static int setup_offsets()
Audit_formatter::thd_offsets.sec_ctx_user,
Audit_formatter::thd_offsets.sec_ctx_host,
Audit_formatter::thd_offsets.sec_ctx_ip,
Audit_formatter::thd_offsets.sec_ctx_priv_user);
Audit_formatter::thd_offsets.sec_ctx_priv_user,
Audit_formatter::thd_offsets.killed,
Audit_formatter::thd_offsets.client_capabilities,
Audit_formatter::thd_offsets.pfs_connect_attrs,
Audit_formatter::thd_offsets.pfs_connect_attrs_length,
Audit_formatter::thd_offsets.pfs_connect_attrs_cs,
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);
}
@@ -1165,7 +1511,7 @@ static int setup_offsets()
if (validate_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",
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,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
@@ -1176,7 +1522,19 @@ static int setup_offsets()
Audit_formatter::thd_offsets.sec_ctx_user,
Audit_formatter::thd_offsets.sec_ctx_host,
Audit_formatter::thd_offsets.sec_ctx_ip,
Audit_formatter::thd_offsets.sec_ctx_priv_user);
Audit_formatter::thd_offsets.sec_ctx_priv_user,
Audit_formatter::thd_offsets.killed,
Audit_formatter::thd_offsets.client_capabilities,
Audit_formatter::thd_offsets.pfs_connect_attrs,
Audit_formatter::thd_offsets.pfs_connect_attrs_length,
Audit_formatter::thd_offsets.pfs_connect_attrs_cs,
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);
}
}
@@ -1207,11 +1565,14 @@ const char *retrieve_command(THD *thd, bool &is_sql_cmd)
}
const int sql_command = thd_sql_command(thd);
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
if (command == COM_QUERY && sql_command >= 0 && sql_command < SQLCOM_END)
#define MAX_SQL_COM_INDEX SQLCOM_END
#else
if (command != COM_STMT_PREPARE && sql_command >= 0 && sql_command < MAX_COM_STATUS_VARS_RECORDS)
#define MAX_SQL_COM_INDEX MAX_COM_STATUS_VARS_RECORDS
#endif
if (command != COM_STMT_PREPARE && sql_command >= 0 && sql_command < MAX_SQL_COM_INDEX)
{
is_sql_cmd = true;
cmd = com_status_vars_array[sql_command].name;
@@ -1345,7 +1706,7 @@ static int string_to_array(const void *save, void *array, int rows, int length)
__attribute__ ((noinline)) static void trampoline_dummy_func_for_mem()
{
TRAMPOLINE_NOP_DEF
TRAMPOLINE_NOP_DEF
}
// holds memory used for trampoline
@@ -1601,12 +1962,35 @@ static int audit_plugin_init(void *p)
log_prefix, MYSQL_AUDIT_PLUGIN_VERSION,
MYSQL_AUDIT_PLUGIN_REVISION, arch, interface_ver, interface_ver,
server_version);
// setup our offsets.
if (setup_offsets() != 0)
{
DBUG_RETURN(1);
}
if(!Audit_formatter::thd_offsets.client_capabilities)
{
sql_print_error(
"%s offsets for client_capabilities not defined. client_capabilities will not be logged.",
log_prefix);
}
#ifdef HAVE_SESS_CONNECT_ATTRS
if(!Audit_formatter::thd_offsets.pfs_connect_attrs ||
!Audit_formatter::thd_offsets.pfs_connect_attrs_length ||
!Audit_formatter::thd_offsets.pfs_connect_attrs_cs)
{
sql_print_error(
"%s offsets for sess_connect_attrs not defined. sess_connect_attrs will not be logged.",
log_prefix);
}
#endif
if (!Audit_formatter::thd_offsets.net)
{
sql_print_error("%s offset for NET structure not defined. peer attributes will not be logged.",
log_prefix);
}
if (delay_cmds_string != NULL)
{
delay_cmds_string_update(NULL, NULL, NULL, &delay_cmds_string);
@@ -1627,11 +2011,11 @@ static int audit_plugin_init(void *p)
{
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);
}
if (NULL != password_masking_regex_string)
if (password_masking_regex_string != NULL)
{
password_masking_regex_string_update(NULL, NULL, NULL, &password_masking_regex_string);
}
@@ -1671,8 +2055,9 @@ static int audit_plugin_init(void *p)
// align our trampoline mem on its own page
const unsigned long page_size = GETPAGESIZE();
const unsigned long std_page_size = 4096;
bool use_static_memory = (page_size <= std_page_size);
bool use_static_memory = (page_size <= std_page_size);
int mmap_flags = MAP_PRIVATE|MAP_ANONYMOUS;
trampoline_mem = NULL;
#ifdef __x86_64__
size_t func_in_mysqld = (size_t)log_slow_statement;
@@ -1680,33 +2065,47 @@ static int audit_plugin_init(void *p)
if (func_in_mysqld < INT_MAX && func_in_plugin > INT_MAX)
{
// See comment about IndirectJump in hot_patch.cc.
mmap_flags |= MAP_32BIT;
use_static_memory = false;
}
#endif
if (use_static_memory)
{
// use static executable memory we alocated via trampoline_dummy_func_for_mem
DATATYPE_ADDRESS addrs = (DATATYPE_ADDRESS)trampoline_dummy_func_for_mem + (page_size - 1);
trampoline_mem = (void*)(addrs & ~(page_size - 1));
sql_print_information(
"%s mem func addr: %p mem start addr: %p page size: %ld",
log_prefix, trampoline_dummy_func_for_mem, trampoline_mem, page_size);
}
else // big pages for some reason. allocate mem using mmap
{
trampoline_mem = mmap(NULL, page_size, PROT_READ|PROT_EXEC, mmap_flags, -1, 0);
//mmap_flags |= MAP_32BIT;
trampoline_mem = mmap(NULL, page_size, PROT_READ|PROT_EXEC, mmap_flags | MAP_32BIT, -1, 0);
if (MAP_FAILED == trampoline_mem)
{
sql_print_error("%s unable to mmap memory size: %lu, errno: %d. Aborting.",
log_prefix, page_size, errno);
DBUG_RETURN(1);
trampoline_mem = NULL;
sql_print_information("%s unable to mmap 32bit memory size: %lu, errno: %d. This may happen when 32bit address space is used up. Will try static and regular mmap...",
log_prefix, page_size, errno);
}
else
{
sql_print_information(
"%s mem via mmap: %p page size: %ld", log_prefix, trampoline_mem, page_size);
"%s mem via 32bit mmap: %p page size: %ld", log_prefix, trampoline_mem, page_size);
}
}
#endif
if (!trampoline_mem)
{
if (use_static_memory)
{
// use static executable memory we alocated via trampoline_dummy_func_for_mem
DATATYPE_ADDRESS addrs = (DATATYPE_ADDRESS)trampoline_dummy_func_for_mem + (page_size - 1);
trampoline_mem = (void*)(addrs & ~(page_size - 1));
sql_print_information(
"%s mem func addr: %p mem start addr: %p page size: %ld",
log_prefix, trampoline_dummy_func_for_mem, trampoline_mem, page_size);
}
else // big pages allocate mem using mmap
{
trampoline_mem = mmap(NULL, page_size, PROT_READ|PROT_EXEC, mmap_flags , -1, 0);
if (MAP_FAILED == trampoline_mem)
{
trampoline_mem = NULL;
sql_print_error("%s unable to mmap 32bit memory size: %lu, errno: %d. Abborting",
log_prefix, page_size, errno);
DBUG_RETURN(1);
}
else
{
sql_print_information(
"%s mem via mmap: %p page size: %ld", log_prefix, trampoline_mem, page_size);
}
}
}
trampoline_mem_free = trampoline_mem;
@@ -1785,10 +2184,21 @@ static int audit_plugin_init(void *p)
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)
{
DBUG_RETURN(1);
}
sql_print_information("%s Init completed successfully.", log_prefix);
DBUG_RETURN(0);
}
@@ -1872,6 +2282,20 @@ static void json_log_socket_enable(THD *thd, struct st_mysql_sys_var *var,
// setup sysvars which update directly the relevant plugins
static MYSQL_SYSVAR_BOOL(socket_creds, json_formatter.m_write_socket_creds,
PLUGIN_VAR_RQCMDARG,
"AUDIT log socket credentials from Unix Domain Socket. Enable|Disable. Default enabled.", NULL, NULL, 1);
static MYSQL_SYSVAR_BOOL(client_capabilities, json_formatter.m_write_client_capabilities,
PLUGIN_VAR_RQCMDARG,
"AUDIT log client capabilities. Enable|Disable. Default disabled.", NULL, NULL, 0);
#ifdef HAVE_SESS_CONNECT_ATTRS
static MYSQL_SYSVAR_BOOL(sess_connect_attrs, json_formatter.m_write_sess_connect_attrs,
PLUGIN_VAR_RQCMDARG,
"AUDIT log session connect attributes (see: performance_schema.session_connect_attrs table). Enable|Disable. Default enabled.", NULL, NULL, 1);
#endif
static MYSQL_SYSVAR_BOOL(header_msg, json_formatter.m_write_start_msg,
PLUGIN_VAR_RQCMDARG,
"AUDIT write header message at start of logging or file flush Enable|Disable. Default enabled.", NULL, NULL, 1);
@@ -1884,6 +2308,7 @@ static MYSQL_SYSVAR_STR(json_log_file, json_file_handler.m_io_dest,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
"AUDIT plugin json log file name",
NULL, NULL, "mysql-audit.json");
static MYSQL_SYSVAR_LONG(json_file_bufsize, json_file_handler.m_bufsize,
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.",
@@ -1969,7 +2394,7 @@ static MYSQL_SYSVAR_STR(delay_cmds, delay_cmds_string,
static MYSQL_SYSVAR_STR(whitelist_cmds, whitelist_cmds_string,
PLUGIN_VAR_RQCMDARG,
"AUDIT plugin whitelisted commands for which queries are not recorded, comma separated",
NULL, whitelist_cmds_string_update, "BEGIN,COMMIT");
NULL, whitelist_cmds_string_update, "BEGIN,COMMIT,PING");
static MYSQL_SYSVAR_STR(record_cmds, record_cmds_string,
PLUGIN_VAR_RQCMDARG,
"AUDIT plugin commands for which queries are recorded, comma separated. If set then only queries of these commands will be recorded.",
@@ -1979,7 +2404,7 @@ static MYSQL_SYSVAR_STR(password_masking_cmds, password_masking_cmds_string,
"AUDIT plugin commands to apply password masking regex to, comma separated",
NULL, password_masking_cmds_string_update,
// 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,
PLUGIN_VAR_RQCMDARG,
"AUDIT plugin whitelisted users whose queries are not recorded, comma separated",
@@ -1990,11 +2415,36 @@ 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.",
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
*/
static struct st_mysql_sys_var* audit_system_variables[] =
{
#ifdef HAVE_SESS_CONNECT_ATTRS
MYSQL_SYSVAR(sess_connect_attrs),
#endif
MYSQL_SYSVAR(socket_creds),
MYSQL_SYSVAR(client_capabilities),
MYSQL_SYSVAR(header_msg),
MYSQL_SYSVAR(force_record_logins),
MYSQL_SYSVAR(json_log_file),
@@ -2022,6 +2472,10 @@ static struct st_mysql_sys_var* audit_system_variables[] =
MYSQL_SYSVAR(record_objs),
MYSQL_SYSVAR(checksum),
MYSQL_SYSVAR(password_masking_regex),
MYSQL_SYSVAR(set_peer_cred),
MYSQL_SYSVAR(peer_is_uds),
MYSQL_SYSVAR(peer_info),
MYSQL_SYSVAR(before_after),
NULL
};
@@ -2065,6 +2519,9 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
"%s mysqld_builtins are null. Plugin will not load unless the mysql version is: %d. \n",
log_prefix, audit_plugin.interface_version >> 8);
}
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
}
#elif MYSQL_VERSION_ID < 50600
extern struct st_mysql_plugin *mysql_mandatory_plugins[];
@@ -2075,6 +2532,8 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
log_prefix, audit_plugin.interface_version,
audit_plugin.interface_version >> 8);
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
}
#elif !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID < 50709
// Interface version for MySQL 5.6 changed in 5.6.14.
@@ -2090,6 +2549,16 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
{
audit_plugin.interface_version = 0x0301;
}
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
}
#else
// 5.7
extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
{
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
}
#endif