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.
pull/179/head
Arnold Robbins 2016-12-07 10:05:00 +02:00
parent 278dd0edac
commit 53b1db74e3
6 changed files with 1340 additions and 1031 deletions

View File

@ -78,6 +78,10 @@ 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;
} ThdOffsets;
/*
@ -318,7 +322,100 @@ 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;
}
return *(const char **) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs);
}
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);
}
}
// 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 +445,8 @@ public:
Audit_json_formatter()
: m_msg_delimiter(NULL),
m_write_start_msg(true),
m_write_sess_connect_attrs(true),
m_write_client_capabilities(true),
m_password_mask_regex_preg(NULL),
m_password_mask_regex_compiled(false),
m_perform_password_masking(NULL)
@ -386,6 +485,18 @@ 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;
/**

View File

@ -94,4 +94,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

@ -30,12 +30,15 @@ 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
COMMAND_MEMBER=m_command
HAS_CONNECT_ATTRS=yes
fi
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
echo $MYVER | grep -P '^(5\.7)' > /dev/null
if [ $? = 0 ]; then
@ -46,10 +49,31 @@ if [ $? = 0 ]; then
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'
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
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
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
cat <<EOF > offsets.gdb
set logging on
set width 0
define print_offset
printf ", %d", (size_t)&((\$arg0*)0)->\$arg1
end
@ -66,6 +90,8 @@ print_offset Security_context $IP
print_offset Security_context $PRIV_USER
print_offset THD $DB
print_offset THD killed
$CLIENT_CAPS
$CONNECT_ATTRS
printf "}"
EOF

View File

@ -178,7 +178,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 +209,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 +362,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);
}
@ -671,6 +672,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,8 +784,31 @@ 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));
// Don't send host unless there's a real value
const char *host = Audit_formatter::thd_inst_main_security_ctx_host(thd);
if (host != NULL && *host != '\0')
{
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
const char *cmd = pThdData->getCmdName();
yajl_add_string_val(gen, "cmd", cmd);

File diff suppressed because it is too large Load Diff

View File

@ -544,7 +544,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);
@ -1040,7 +1045,7 @@ static int setup_offsets()
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", log_prefix,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
Audit_formatter::thd_offsets.main_security_ctx,
@ -1052,7 +1057,12 @@ 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
);
if (! validate_offsets(&Audit_formatter::thd_offsets))
{
@ -1134,10 +1144,18 @@ 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 (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",
log_prefix, dec, offset->version, offset->md5digest,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
@ -1148,7 +1166,9 @@ 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);
DBUG_RETURN(0);
}
@ -1607,6 +1627,22 @@ static int audit_plugin_init(void *p)
{
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 (delay_cmds_string != NULL)
{
delay_cmds_string_update(NULL, NULL, NULL, &delay_cmds_string);
@ -1671,8 +1707,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 +1717,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;
@ -1872,6 +1923,16 @@ 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(client_capabilities, json_formatter.m_write_client_capabilities,
PLUGIN_VAR_RQCMDARG,
"AUDIT log client capabilities. Enable|Disable. Default enabled.", NULL, NULL, 1);
#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);
@ -1995,6 +2056,10 @@ static MYSQL_SYSVAR_STR(record_objs, record_objs_string,
*/
static struct st_mysql_sys_var* audit_system_variables[] =
{
#ifdef HAVE_SESS_CONNECT_ATTRS
MYSQL_SYSVAR(sess_connect_attrs),
#endif
MYSQL_SYSVAR(client_capabilities),
MYSQL_SYSVAR(header_msg),
MYSQL_SYSVAR(force_record_logins),
MYSQL_SYSVAR(json_log_file),