Add support for MySQL 5.7.{9,10,11}.

Add support for MariaDB 10.1.{10,11}.
Fix segfault for set var to null (issue 133).
If password masking regex doesn't compile, revert to default regex.
pull/141/head
Arnold Robbins 2016-02-07 15:59:41 +02:00
parent 685b20e20c
commit d9129cc112
7 changed files with 392 additions and 94 deletions

View File

@ -35,16 +35,21 @@ This will create configure script. Then run:
CXX='gcc -static-libgcc' CC='gcc -static-libgcc' ./configure --with-mysql=mysql-5.1.40
==== MySQL 5.5 / 5.6 ======
==== MySQL 5.5 / 5.6 / 5.7 ======
Extract MySQL 5.5 or 5.6 source code
Extract MySQL 5.5, 5.6, or 5.7 source code
go to mysql-src dir and run:
cd mysql-5.5.x or mysql-5.6.x
cd mysql-5.5.x or mysql-5.6.x or mysql-5.7.x
cmake .
make
Note: MySQL 5.7 requires Boost 1.59. You may have to install that
first (see www.boost.org). In such a case, use:
cmake -DWITH_BOOST=/path/to/boost_1_59_0 .
Note: For MariaDB use: cmake . -DBUILD_CONFIG=mysql_release
Note: to speed things up it is possible to build just the following directories:

View File

@ -27,8 +27,13 @@ AC_DEFUN([MYSQL_SRC_TEST], [
AC_MSG_ERROR([Failed to find required header file $file in $withval, check the path and make sure you've run './configure ..<options>.. && cd include && make' in MySQL 5.1 sources dir or 'cmake . && make' in MySQL 5.5 sources dir.])
fi
done
dnl binary_log_types.h included by mysql_com.h included by mysql_inc.h -
dnl is found in libbinlogevents/export.
dnl
dnl table_id.h included from table.h included by mysql_inc.h is
dnl in libbinlogevents/include.
AC_DEFINE([MYSQL_SRC], [1], [Source directory for MySQL])
MYSQL_INC="-I$withval/sql -I$withval/include -I$withval/regex -I$withval"
MYSQL_INC="-I$withval/sql -I$withval/libbinlogevents/export -I$withval/libbinlogevents/include -I$withval/include -I$withval/regex -I$withval"
AC_MSG_RESULT(["$withval"])
],
[

View File

@ -20,6 +20,18 @@
#define AUDIT_LOG_PREFIX "Audit Plugin:"
#define AUDIT_PROTOCOL_VERSION "1.0"
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
// For locking we use the native lock routines provided by MySQL.
// The data types and functions for native locking changed at 5.7.x.
// Try to hide this with macros.
#define rw_lock_t native_rw_lock_t
#define rw_rdlock native_rw_rdlock
#define rw_wrlock native_rw_wrlock
#define rw_unlock native_rw_unlock
#define rwlock_destroy native_rw_destroy
#define my_rwlock_init(lock, unused) native_rw_init(lock)
#endif
class THD;
#define MAX_NUM_QUERY_TABLE_ELEM 100
@ -190,7 +202,11 @@ public:
{
if(!Audit_formatter::thd_offsets.db) //no offsets use compiled in header
{
return thd->db;
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
return thd->db;
#else
return thd->db().str;
#endif
}
return *(const char **) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.db);
@ -211,7 +227,11 @@ public:
Security_context * sctx = thd_inst_main_security_ctx(thd);
if(!Audit_formatter::thd_offsets.sec_ctx_user) //no offsets use compiled in header
{
return sctx->user;
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
return sctx->user;
#else
return sctx->user().str;
#endif
}
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_user);
@ -224,11 +244,20 @@ public:
{
//interface changed in 5.5.34 and 5.6.14 and up host changed to get_host()
//see: http://bazaar.launchpad.net/~mysql/mysql-server/5.5/revision/4407.1.1/sql/sql_class.h
#if !defined(MARIADB_BASE_VERSION) && ( ( MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) || (MYSQL_VERSION_ID >= 50614) )
return sctx->get_host()->ptr();
#else
#if defined(MARIADB_BASE_VERSION)
return sctx->host;
#else
// MySQL
#if MYSQL_VERSION_ID < 50534 || (MYSQL_VERSION_ID >= 50600 && MYSQL_VERSION_ID < 50614)
return sctx->host;
#elif (MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) \
|| (MYSQL_VERSION_ID >= 50614 && MYSQL_VERSION_ID < 50709)
return sctx->get_host()->ptr();
#else
// interface changed again in 5.7
return sctx->host().str;
#endif
#endif // ! defined(MARIADB_BASE_VERSION)
}
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_host);
@ -240,11 +269,20 @@ public:
if(!Audit_formatter::thd_offsets.sec_ctx_ip) //no offsets use compiled in header
{
//interface changed in 5.5.34 and 5.6.14 and up host changed to get_ip()
#if !defined(MARIADB_BASE_VERSION) && ( (MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) || (MYSQL_VERSION_ID >= 50614) )
#if defined(MARIADB_BASE_VERSION)
return sctx->ip;
#else
// MySQL
#if MYSQL_VERSION_ID < 50534 || (MYSQL_VERSION_ID >= 50600 && MYSQL_VERSION_ID < 50614)
return sctx->ip;
#elif (MYSQL_VERSION_ID >= 50534 && MYSQL_VERSION_ID < 50600) \
|| (MYSQL_VERSION_ID >= 50614 && MYSQL_VERSION_ID < 50709)
return sctx->get_ip()->ptr();
#else
return sctx->ip;
// interface changed again in 5.7
return sctx->ip().str;
#endif
#endif // ! defined(MARIADB_BASE_VERSION)
}
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_ip);
@ -255,7 +293,11 @@ public:
Security_context * sctx = thd_inst_main_security_ctx(thd);
if(!Audit_formatter::thd_offsets.sec_ctx_priv_user) //no offsets use compiled in header
{
return sctx->priv_user;
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
return sctx->priv_user;
#else
return sctx->priv_user().str;
#endif
}
#if MYSQL_VERSION_ID < 50505
//in 5.1.x priv_user is a pointer

View File

@ -14,17 +14,27 @@
#include <my_config.h>
#include <mysql_version.h>
#if MYSQL_VERSION_ID < 50505
#include <mysql_priv.h>
#else
//version 5.5.x doesn't contain mysql_priv.h . We need to add the includes provided by it.
#if MYSQL_VERSION_ID >= 50505
// These two are not present in 5.7.9
#if MYSQL_VERSION_ID < 50709
#include <my_pthread.h>
#include <sql_priv.h>
#endif
#include <mysql/plugin.h>
#if MYSQL_VERSION_ID >= 50600
//in 5.6 we use the audit plugin interface
// From 5.6 we use the audit plugin interface
#include <mysql/plugin_audit.h>
#endif
#include <sql_parse.h>
#include <sql_class.h>
#include <my_global.h>
@ -41,9 +51,14 @@
#define pthread_mutex_destroy mysql_mutex_destroy
#define pthread_mutex_t mysql_mutex_t
*/
#endif /* ! if MYSQL_VERSION_ID >= 50505 */
#endif /* ! if MYSQL_VERSION_ID < 50505 */
#else
#include <mysql_priv.h>
#if MYSQL_VERSION_ID >= 50709
#include <sql/log.h>
#if ! defined(MARIADB_BASE_VERSION)
#include <sql/auth/auth_common.h>
#endif
#endif
#include <violite.h>

View File

@ -48,7 +48,9 @@ ThdOffsets Audit_formatter::thd_offsets = { 0 };
Audit_handler * Audit_handler::m_audit_handler_list[Audit_handler::MAX_AUDIT_HANDLERS_NUM];
const char * Audit_json_formatter::DEF_MSG_DELIMITER = "\\n";
#if MYSQL_VERSION_ID < 50709
#define C_STRING_WITH_LEN(X) ((char *) (X)), ((size_t) (sizeof(X) - 1))
#endif
const char * Audit_formatter::retrieve_object_type (TABLE_LIST *pObj)
@ -465,7 +467,25 @@ static const char * retrieve_user (THD * thd)
//will return a pointer to the query and set len with the length of the query
//starting with MySQL version 5.1.41 thd_query_string is added
#if MYSQL_VERSION_ID > 50140
//And at 5.7 it changed
#if ! defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
extern "C" LEX_CSTRING thd_query_unsafe(MYSQL_THD thd);
static const char * thd_query_str(THD * thd, size_t * len)
{
const LEX_CSTRING str = thd_query_unsafe(thd);
if(str.length > 0)
{
*len = str.length;
return str.str;
}
*len = 0;
return NULL;
}
#elif defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID > 50140
extern "C" {
MYSQL_LEX_STRING *thd_query_string(MYSQL_THD thd);
}
@ -548,6 +568,40 @@ ssize_t Audit_json_formatter::start_msg_format(IWriter * writer)
}
// This routine replaces clear text with the string in `replace', leaving the rest of the string intact.
//
// thd - MySQL thread, used for allocating memory
// str - pointer to start of original string
// str_len - length thereof
// cleartext_start - start of cleartext to replace
// cleartext_len - length of cleartext
// replace - \0 terminated string with replacement text
static const char *replace_in_string(THD *thd,
const char *str, size_t str_len,
size_t cleartext_start, size_t cleartext_len,
const char *replace)
{
size_t to_alloc = str_len + strlen(replace) + 1;
char *new_str = (char *) thd_alloc(thd, to_alloc);
memset(new_str, '\0', to_alloc);
// point to text after clear text
const char *trailing = str + cleartext_start + cleartext_len;
// how much text after clear text to copy in
size_t final_to_move = ((str + str_len) - trailing);
char *pos = new_str;
memcpy(pos, str, cleartext_start); // copy front of string
pos += cleartext_start;
memcpy(pos, replace, strlen(replace)); // copy replacement text
pos += strlen(replace);
memcpy(pos, trailing, final_to_move); // copy trailing part of string
return new_str;
}
ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * writer)
{
THD * thd = pThdData->getTHD();
@ -599,15 +653,34 @@ ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * write
#endif
col_connection = Item::default_charset();
String sQuery (query,qlen,col_connection) ;
if (strcmp (col_connection->csname,"utf8")!=0) {
pThdData->getTHD()->convert_string (&sQuery,col_connection,&my_charset_utf8_general_ci);
// See comment below as to why we don't use String class directly, or call
// pThdData->getTHD()->convert_string (&sQuery,col_connection,&my_charset_utf8_general_ci);
const char *query_text = query;
size_t query_len = qlen;
if (strcmp(col_connection->csname, "utf8") != 0) {
// max UTF-8 bytes per char is 4.
size_t to_amount = (qlen * 4) + 1;
char* to = (char *) thd_alloc(thd, to_amount);
uint errors = 0;
size_t len = copy_and_convert(to, to_amount,
&my_charset_utf8_general_ci,
query, qlen,
col_connection, & errors);
to[len] = '\0';
query = to;
qlen = len;
}
if(m_perform_password_masking && m_password_mask_regex_compiled && m_password_mask_regex_preg && m_perform_password_masking(cmd))
{
//do password masking
int matches[90] = {0};
if(pcre_exec(m_password_mask_regex_preg, NULL, sQuery.ptr(), sQuery.length(), 0, 0, matches, array_elements(matches)) >= 0)
if(pcre_exec(m_password_mask_regex_preg, NULL, query_text, query_len, 0, 0, matches, array_elements(matches)) >= 0)
{
//search for the first substring that matches with the name psw
char *first = NULL, *last = NULL;
@ -619,17 +692,24 @@ ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * write
//first 2 bytes give us the number
int n = (((int)(entry)[0]) << 8) | (entry)[1];
if (n > 0 && n < (int)array_elements(matches) && matches[n*2] >= 0)
{ //we have a match
sQuery.copy(); //make sure string is alloced before doing replace
const char * pass_replace = "***";
sQuery.replace(matches[n*2], matches[(n*2) + 1] - matches[n*2], pass_replace, strlen(pass_replace));
{
// We have a match.
// Starting with MySQL 5.7, we cannot use the String::replace() function.
// Doing so causes a crash in the string's destructor. It appears that the
// interfaces in MySQL have changed fairly drastically. So we just do the
// replacement ourselves.
const char *pass_replace = "***";
const char *updated = replace_in_string(thd, query_text, query_len, matches[n*2], matches[(n*2) + 1] - matches[n*2], pass_replace);
query_text = updated;
query_len = strlen(query_text);
break;
}
}
}
}
}
yajl_add_string_val(gen, "query", sQuery.ptr(), sQuery.length());
yajl_add_string_val(gen, "query", query_text, query_len);
}
else
{
@ -691,7 +771,9 @@ bool ThdSesData::startGetObjects()
}
const char *cmd = getCmdName();
//commands which have single database object
if (strcmp (cmd,"Init DB") ==0 || strcmp (cmd, "SHOW TABLES")== 0 || strcmp (cmd, "SHOW TABLE")==0)
if (strcmp (cmd,"Init DB") ==0
|| strcmp (cmd, "SHOW TABLES")== 0
|| strcmp (cmd, "SHOW TABLE")==0)
{
if(Audit_formatter::thd_db(getTHD()))
{

28
src/audit_offsets.cc Normal file → Executable file
View File

@ -21,6 +21,16 @@
//64 bit offsets
const ThdOffsets thd_offsets_arr[] =
{
//offsets for: /mysqlrpm/5.5.48/usr/sbin/mysqld (5.5.48)
{"5.5.48","69b209f0c08027a501b1cb7a20e6e582", 6144, 6192, 3816, 4312, 88, 2592, 96, 0, 32, 104, 120, 6264},
//offsets for: /mysqlrpm/5.6.29/usr/sbin/mysqld (5.6.29)
{"5.6.29","badcd2b5b459d5fd6e4fb6a4bd3a1f91", 6992, 7040, 4000, 4520, 72, 2704, 96, 0, 32, 104, 136, 7128},
//offsets for: /mysqlrpm/5.7.11/usr/sbin/mysqld (5.7.11)
{"5.7.11","68ebf5245b47e43235466a2820cf5d46", 7792, 7840, 3616, 4768, 456, 360, 0, 32, 64, 160, 536, 7956},
//offsets for: /mysqlrpm/5.7.10/usr/sbin/mysqld (5.7.10)
{"5.7.10","8e9c0636d9263cea97498dd99e7841b6", 7784, 7832, 3608, 4760, 456, 360, 0, 32, 64, 160, 536, 7948},
//offsets for: /mysqlrpm/5.7.9/usr/sbin/mysqld (5.7.9)
{"5.7.9","1cb6f2ea6f98c2788d51a196afa153d9", 7784, 7832, 3608, 4760, 456, 360, 0, 32, 64, 160, 536, 7948},
//offsets for: /mysqlrpm/5.5.47/usr/sbin/mysqld (5.5.47)
{"5.5.47","a35964c285630302290dc1ad31bddd93", 6144, 6192, 3816, 4312, 88, 2592, 96, 0, 32, 104, 120, 6264},
//offsets for: /mysqlrpm/5.6.28/usr/sbin/mysqld (5.6.28)
@ -419,6 +429,16 @@ const ThdOffsets thd_offsets_arr[] =
//32 bit offsets
const ThdOffsets thd_offsets_arr[] =
{
//offsets for: /mysqlrpm/5.5.48/usr/sbin/mysqld (5.5.48)
{"5.5.48","731f6399029830b0b9ffa151541ed474", 3872, 3900, 2368, 2748, 44, 1656, 60, 0, 20, 64, 60, 3956},
//offsets for: /mysqlrpm/5.6.29/usr/sbin/mysqld (5.6.29)
{"5.6.29","c1e32ee3937f2ad5fc9df94d1e0aa649", 4676, 4704, 2660, 3052, 36, 1748, 60, 0, 20, 64, 72, 4776},
//offsets for: /mysqlrpm/5.7.11/usr/sbin/mysqld (5.7.11)
{"5.7.11","014a96aac6006bf942dd2047aad0ccd1", 5064, 5092, 2200, 3020, 296, 200, 0, 20, 40, 100, 340, 5180},
//offsets for: /mysqlrpm/5.7.10/usr/sbin/mysqld (5.7.10)
{"5.7.10","e6389a154b82cfaac4a89f1b2a995365", 5060, 5088, 2196, 3016, 296, 200, 0, 20, 40, 100, 340, 5172},
//offsets for: /mysqlrpm/5.7.9/usr/sbin/mysqld (5.7.9)
{"5.7.9","95a6843300e1fef377d298210a4e2525", 5060, 5088, 2196, 3016, 296, 200, 0, 20, 40, 100, 340, 5172},
//offsets for: /mysqlrpm/5.5.47/usr/sbin/mysqld (5.5.47)
{"5.5.47","669f76493658cd2758af28a1c391391a", 3872, 3900, 2368, 2748, 44, 1656, 60, 0, 20, 64, 60, 3956},
//offsets for: /mysqlrpm/5.6.28/usr/sbin/mysqld (5.6.28)
@ -797,6 +817,10 @@ const ThdOffsets thd_offsets_arr[] =
//64 bit offsets
const ThdOffsets thd_offsets_arr[] =
{
//offsets for: /mariadb/10.1.11/bin/mysqld (10.1.11-MariaDB)
{"10.1.11-MariaDB","91dc9f2e78433f0efc975174263a4206", 13592, 13656, 6368, 7976, 88, 2976, 8, 0, 16, 24, 152, 13748},
//offsets for: /mariadb/10.1.10/bin/mysqld (10.1.10-MariaDB)
{"10.1.10-MariaDB","d6dd898de1ac04fc742bf525f8ce37bf", 13576, 13640, 6352, 7960, 88, 2960, 8, 0, 16, 24, 152, 13732},
//offsets for: /mariadb/10.0.23/bin/mysqld (10.0.23-MariaDB)
{"10.0.23-MariaDB","1cc0611b29e35a3a64eba2ff9bc3663c", 13416, 13480, 6192, 7800, 88, 2976, 8, 0, 16, 24, 152, 13572},
//offsets for: /mariadb/10.0.22/bin/mysqld (10.0.22-MariaDB)
@ -877,6 +901,10 @@ const ThdOffsets thd_offsets_arr[] =
//32 bit offsets
const ThdOffsets thd_offsets_arr[] =
{
//offsets for: /mariadb/10.1.11/bin/mysqld (10.1.11-MariaDB)
{"10.1.11-MariaDB","bc5982b6028c0ef5d50ff7f8465bc6e5", 8472, 8508, 3816, 5276, 44, 1892, 4, 0, 8, 12, 84, 8584},
//offsets for: /mariadb/10.1.10/bin/mysqld (10.1.10-MariaDB)
{"10.1.10-MariaDB","102b27c47031aecdf8ffaf881c841e28", 8464, 8500, 3808, 5268, 44, 1884, 4, 0, 8, 12, 84, 8576},
//offsets for: /mariadb/10.0.23/bin/mysqld (10.0.23-MariaDB)
{"10.0.23-MariaDB","487c1be817fcf314df0edc3f688fdc80", 8336, 8372, 3680, 5140, 44, 1892, 4, 0, 8, 12, 84, 8444},
//offsets for: /mariadb/10.0.22/bin/mysqld (10.0.22-MariaDB)

View File

@ -87,6 +87,23 @@ static char password_masking_regex_check_buff[4096] = {0};
static char * password_masking_regex_string = NULL;
static char password_masking_regex_buff[4096] = {0};
#define _COMMENT_SPACE_ "(?:/\\*.*?\\*/|\\s)*?"
#define _QUOTED_PSW_ "[\'|\"](?<psw>.*?)(?<!\\\\)[\'|\"]"
static const char default_pw_masking_regex[] =
//identified by [password] '***'
"identified"_COMMENT_SPACE_"by"_COMMENT_SPACE_"(?:password)?"_COMMENT_SPACE_ _QUOTED_PSW_
//password function
"|password"_COMMENT_SPACE_"\\("_COMMENT_SPACE_ _QUOTED_PSW_ _COMMENT_SPACE_"\\)"
//Used at: CHANGE MASTER TO MASTER_PASSWORD='new3cret', SET PASSWORD [FOR user] = 'hash', password 'user_pass';
"|password"_COMMENT_SPACE_"(?:for"_COMMENT_SPACE_"\\S+?)?"_COMMENT_SPACE_"="_COMMENT_SPACE_ _QUOTED_PSW_
"|password"_COMMENT_SPACE_ _QUOTED_PSW_
//federated engine create table with connection. See: http://dev.mysql.com/doc/refman/5.5/en/federated-create-connection.html
//commented out as federated engine is disabled by default
//"|ENGINE"_COMMENT_SPACE_"="_COMMENT_SPACE_"FEDERATED"_COMMENT_SPACE_".*CONNECTION"_COMMENT_SPACE_"="_COMMENT_SPACE_"[\'|\"]\\S+?://\\S+?:(?<psw>.*)@\\S+[\'|\"]"
;
//socket name
static char json_socket_name_buff[1024] = {0};
@ -197,18 +214,6 @@ static my_bool check_do_password_masking(const char * cmd)
return false;
}
//utility compiles the regex. if fails zero outs password_masking_regex_string
static void password_masking_regex_compile()
{
int res = json_formatter.compile_password_masking_regex(password_masking_regex_string);
if(res)
{ password_masking_regex_buff[0] = '\0';
password_masking_regex_string = password_masking_regex_buff;
}
sql_print_information("%s Compile password_masking_regex res: [%d]", log_prefix, res);
}
static void audit(ThdSesData *pThdData)
{
THDPRINTED *pThdPrintedList = GetThdPrintedList (pThdData->getTHD());
@ -302,14 +307,18 @@ static void audit(ThdSesData *pThdData)
}
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
static int (*trampoline_send_result_to_client)(Query_cache *pthis, THD *thd, char *sql, uint query_length) = NULL;
#else
static int (*trampoline_send_result_to_client)(Query_cache *pthis, THD *thd, const LEX_CSTRING& sql_query) = NULL;
#endif
#if MYSQL_VERSION_ID > 50505
static bool (*trampoline_open_tables)(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy) = NULL;
#elif defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
static bool (*trampoline_open_tables)(THD *thd, const DDL_options_st &options, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy) = NULL;
#elif MYSQL_VERSION_ID > 50505
static bool (*trampoline_open_tables)(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy) = NULL;
#else
static int (*trampoline_open_tables)(THD *thd, TABLE_LIST **start, uint *counter, uint flags) = NULL;
#endif
@ -355,7 +364,11 @@ static bool audit_check_table_access(THD *thd, ulong want_access,TABLE_LIST *tab
static unsigned int trampoline_check_table_access_size = 0;
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
static int audit_send_result_to_client(Query_cache *pthis, THD *thd, char *sql, uint query_length)
#else
static int audit_send_result_to_client(Query_cache *pthis, THD *thd, const LEX_CSTRING& sql_query)
#endif
{
int res;
void *pList = thd_alloc (thd, sizeof (QueryTableInf));
@ -366,7 +379,11 @@ static int audit_send_result_to_client(Query_cache *pthis, THD *thd, char *sql,
memset (pList,0,sizeof (QueryTableInf));
THDVAR(thd, query_cache_table_list) =(ulong)pList;
}
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
res = trampoline_send_result_to_client (pthis,thd, sql, query_length);
#else
res = trampoline_send_result_to_client (pthis, thd, sql_query);
#endif
if (res)
{
ThdSesData thd_data (thd);
@ -377,19 +394,19 @@ static int audit_send_result_to_client(Query_cache *pthis, THD *thd, char *sql,
}
static unsigned int trampoline_send_result_to_client_size =0;
#if MYSQL_VERSION_ID > 50505
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
static bool audit_open_tables(THD *thd, const DDL_options_st &options, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)
{
bool res;
res = trampoline_open_tables (thd, options, start, counter, flags, prelocking_strategy);
#elif MYSQL_VERSION_ID > 50505
static bool audit_open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)
{
bool res;
res = trampoline_open_tables (thd, start, counter, flags, prelocking_strategy);
#elif defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
static bool audit_open_tables(THD *thd, const DDL_options_st &options, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)
{
bool res;
res = trampoline_open_tables (thd, options, start, counter, flags, prelocking_strategy);
#else
static int audit_open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{
@ -440,8 +457,13 @@ static struct st_mysql_daemon audit_plugin =
#else
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
static void audit_notify(THD *thd, unsigned int event_class,
const void * event)
#else
static int audit_notify(THD *thd, mysql_event_class_t event_class,
const void * event)
#endif
{
if (MYSQL_AUDIT_GENERAL_CLASS == event_class)
{
@ -463,6 +485,9 @@ static void audit_notify(THD *thd, unsigned int event_class,
audit (&ThdData);
}
}
#if ! defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
return 0; // Zero means success, MySQL continues processing the event.
#endif
}
static int plugin_type = MYSQL_AUDIT_PLUGIN;
@ -471,8 +496,25 @@ static struct st_mysql_audit audit_plugin=
MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */
NULL, /* release_thd function */
audit_notify, /* notify function */
#if ! defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
// As of 5.7.9, this is an array of bitmaps of events that we're interested in.
{ (unsigned long) MYSQL_AUDIT_GENERAL_ALL,
(unsigned long) MYSQL_AUDIT_CONNECTION_ALL,
0,
0,
0,
0,
0,
0,
(unsigned long) MYSQL_AUDIT_COMMAND_ALL,
0,
0
}
#else
{ (unsigned long) MYSQL_AUDIT_GENERAL_CLASSMASK |
MYSQL_AUDIT_CONNECTION_CLASSMASK } /* class mask */
#endif
};
#endif
@ -509,12 +551,12 @@ void remove_hot_functions ()
trampoline_acl_authenticate_size=0;
#endif
#if MYSQL_VERSION_ID > 50505
target_function = (void *)*(bool (*)(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)) &open_tables;
#elif defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
target_function = (void *)*(bool (*)(THD *thd, const DDL_options_st &options, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)) &open_tables;
#elif MYSQL_VERSION_ID > 50505
target_function = (void *)*(bool (*)(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)) &open_tables;
#else
target_function = (void *)*(int (*)(THD *thd, TABLE_LIST **start, uint *counter, uint flags)) &open_tables;
#endif
@ -522,7 +564,11 @@ void remove_hot_functions ()
(void*) trampoline_open_tables, trampoline_open_tables_size, true);
trampoline_open_tables_size=0;
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
int (Query_cache::*pf_send_result_to_client)(THD *,char *, uint) = &Query_cache::send_result_to_client;
#else
int (Query_cache::*pf_send_result_to_client)(THD *,const LEX_CSTRING&) = &Query_cache::send_result_to_client;
#endif
target_function = *(void **) &pf_send_result_to_client;
remove_hot_patch_function(target_function,
(void*) trampoline_send_result_to_client, trampoline_send_result_to_client_size, true);
@ -532,7 +578,14 @@ void remove_hot_functions ()
(void*) trampoline_check_table_access,
trampoline_check_table_access_size, true);
trampoline_check_table_access_size=0;
remove_hot_patch_function((void*)mysql_execute_command,
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
target_function = (void*) mysql_execute_command;
#else
target_function = (void*)
(int (*)(THD *thd, bool first_level)) &mysql_execute_command;
#endif
remove_hot_patch_function(target_function,
(void*) trampoline_mysql_execute_command,
trampoline_mysql_execute_size, true);
trampoline_mysql_execute_size=0;
@ -618,7 +671,11 @@ static int audit_mysql_execute_command(THD *thd)
{
case 1:
//hot patch function were removed and we call the real execute (restored)
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
res = mysql_execute_command(thd);
#else
res = mysql_execute_command(thd, false);
#endif
break;
case 2:
//denied uninstall plugin
@ -746,7 +803,8 @@ static bool validate_offsets(const ThdOffsets * offset)
{
sql_print_error(
"%s Offsets: %s (%s) match thread validation check fails with value: %lu. Skipping offest.",
log_prefix, offset->version, offset->md5digest, res);
log_prefix, offset->version, offset->md5digest,
(unsigned long) res);
return false;
}
//extended validation via security_context method
@ -759,7 +817,11 @@ static bool validate_offsets(const ThdOffsets * offset)
char user_test_val[] = "aud_tusr";
if(!offset->sec_ctx_user) //use compiled header
{
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
sctx->user = user_test_val;
#else
sctx->set_user_ptr(user_test_val, strlen(user_test_val));
#endif
}
else
{
@ -1044,9 +1106,13 @@ const char * retrieve_command (THD * thd, bool & is_sql_cmd)
return "select";
}
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)
#else
if (sql_command >=0 && sql_command < MAX_COM_STATUS_VARS_RECORDS )
#endif
{
is_sql_cmd = true;
is_sql_cmd = true;
cmd = com_status_vars_array[sql_command].name;
}
if(!cmd)
@ -1204,9 +1270,18 @@ static int do_hot_patch(void ** trampoline_func_pp, unsigned int * trampoline_si
static void NAME ## _string_update(THD *thd, struct st_mysql_sys_var *var, void *tgt, const void *save)\
{\
num_ ## NAME = string_to_array(save, NAME ## _array, array_elements( NAME ## _array), sizeof( NAME ## _array[0]));\
strncpy( NAME ##_buff , *static_cast<char*const*>(save), array_elements( NAME ## _buff) - 1);\
/* handle "set global audit_xxx = null;" */ \
char *const* save_p = static_cast<char*const*>(save);\
if (save_p != NULL && *save_p != NULL)\
{\
strncpy( NAME ##_buff , *static_cast<char*const*>(save), array_elements( NAME ## _buff) - 1);\
}\
else\
{\
NAME ## _buff[0] = '\0';\
}\
NAME ## _string = NAME ##_buff;\
sql_print_information("%s Set " #NAME " num: %d, value: %s", log_prefix, num_ ## NAME, NAME ## _string);\
sql_print_information("%s Set " #NAME " num: %d, value: %s", log_prefix, num_ ## NAME, NAME ## _string);\
}
DECLARE_STRING_ARR_UPDATE_FUNC(delay_cmds)
@ -1218,15 +1293,47 @@ DECLARE_STRING_ARR_UPDATE_FUNC(record_objs)
static void password_masking_regex_string_update(THD *thd, struct st_mysql_sys_var *var, void *tgt, const void *save)
{
const char * str_val = *static_cast<char*const*>(save);
const char * str = str_val;
//copy str to buffer only if str is not pointing to buff
if(str != password_masking_regex_buff)
const char * str_val = "";
char *const* save_p = static_cast<char*const*>(save);
// if got a null pointer or empty string, use ""
if (save_p != NULL && *save_p != NULL)
{
strncpy( password_masking_regex_buff , str, array_elements( password_masking_regex_buff) - 1);
str_val = *save_p;
}
password_masking_regex_string = password_masking_regex_buff;
password_masking_regex_compile();
// if a string value supplied, check that it compiles
if (*str_val)
{
int res = json_formatter.compile_password_masking_regex(str_val);
if (res != 0) // fails compilation
{
// copy in default pw
strncpy(password_masking_regex_buff, default_pw_masking_regex, array_elements(password_masking_regex_buff) - 1);
password_masking_regex_string = password_masking_regex_buff;
// compile it
json_formatter.compile_password_masking_regex(password_masking_regex_string);
}
else
{
// all ok, save it
if (str_val != password_masking_regex_buff)
{
strncpy(password_masking_regex_buff, str_val, array_elements(password_masking_regex_buff) - 1);
}
password_masking_regex_string = password_masking_regex_buff;
// it was already compiled, don't need to do it again
}
sql_print_information("%s Compile password_masking_regex res: [%d]", log_prefix, res);
}
else
{
// clear out password
password_masking_regex_buff[0] = '\0';
password_masking_regex_string = password_masking_regex_buff;
json_formatter.compile_password_masking_regex(password_masking_regex_string);
}
sql_print_information("%s Set password_masking_regex value: [%s]", log_prefix, str_val);
}
@ -1244,7 +1351,14 @@ static void replace_char(char * str, const char tofind, const char rplc)
static void json_socket_name_update(THD *thd, struct st_mysql_sys_var *var, void *tgt, const void *save)
{
const char * str_val = *static_cast<char*const*>(save);
const char * str_val = NULL;
char *const* save_p = static_cast<char*const*>(save);
if (save_p != NULL && *save_p != NULL)
{
str_val = *save_p;
}
const char * str = str_val;
const size_t buff_len = array_elements( json_socket_name_buff) -1;
//copy str to buffer only if str is not pointing to buff
@ -1450,8 +1564,14 @@ static void record_objs_string_update_extended(THD *thd, struct st_mysql_sys_var
//hot patch stuff
void * target_function = NULL;
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
target_function = (void*) mysql_execute_command;
#else
target_function = (void*)
(int (*)(THD *thd, bool first_level)) &mysql_execute_command;
#endif
if(do_hot_patch((void **)&trampoline_mysql_execute_command, &trampoline_mysql_execute_size,
(void *)mysql_execute_command, (void *)audit_mysql_execute_command, "mysql_execute_command"))
target_function, (void *)audit_mysql_execute_command, "mysql_execute_command"))
{
DBUG_RETURN(1);
}
@ -1480,7 +1600,11 @@ static void record_objs_string_update_extended(THD *thd, struct st_mysql_sys_var
DBUG_RETURN(1);
}
#endif
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 50709
int (Query_cache::*pf_send_result_to_client)(THD *,char *, uint) = &Query_cache::send_result_to_client;
#else
int (Query_cache::*pf_send_result_to_client)(THD *,const LEX_CSTRING&) = &Query_cache::send_result_to_client;
#endif
target_function = *(void **) &pf_send_result_to_client;
if(do_hot_patch((void **)&trampoline_send_result_to_client, &trampoline_send_result_to_client_size,
(void *)target_function, (void *)audit_send_result_to_client, "send_result_to_client"))
@ -1494,12 +1618,12 @@ static void record_objs_string_update_extended(THD *thd, struct st_mysql_sys_var
DBUG_RETURN(1);
}
#if MYSQL_VERSION_ID > 50505
target_function = (void *)*(bool (*)(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)) &open_tables;
#elif defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 100108
target_function = (void *)*(bool (*)(THD *thd, const DDL_options_st &options, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)) &open_tables;
#elif MYSQL_VERSION_ID > 50505
target_function = (void *)*(bool (*)(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)) &open_tables;
#else
target_function = (void *)*(int (*)(THD *thd, TABLE_LIST **start, uint *counter, uint flags)) &open_tables;
#endif
@ -1545,10 +1669,18 @@ static struct st_mysql_show_var audit_status[] =
{
{ "Audit_version",
(char *) MYSQL_AUDIT_PLUGIN_VERSION "-" MYSQL_AUDIT_PLUGIN_REVISION,
SHOW_CHAR },
SHOW_CHAR
#if ! defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
, SHOW_SCOPE_GLOBAL
#endif
},
{ "Audit_protocol_version",
(char *) AUDIT_PROTOCOL_VERSION,
SHOW_CHAR },
SHOW_CHAR
#if ! defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
, SHOW_SCOPE_GLOBAL
#endif
},
//{"called", (char *)&number_of_calls, SHOW_LONG},
{ 0, 0, (enum_mysql_show_type) 0 } };
@ -1648,23 +1780,11 @@ static MYSQL_SYSVAR_STR(checksum, checksum_string,
"AUDIT plugin checksum. Checksum for mysqld corresponding to offsets",
NULL, NULL, "");
#define _COMMENT_SPACE_ "(?:/\\*.*?\\*/|\\s)*?"
#define _QUOTED_PSW_ "[\'|\"](?<psw>.*?)(?<!\\\\)[\'|\"]"
static MYSQL_SYSVAR_STR(password_masking_regex, password_masking_regex_string,
PLUGIN_VAR_RQCMDARG ,
"AUDIT plugin regex to use for password masking",
password_masking_regex_check, password_masking_regex_string_update,
//identified by [password] '***'
"identified"_COMMENT_SPACE_"by"_COMMENT_SPACE_"(?:password)?"_COMMENT_SPACE_ _QUOTED_PSW_
//password function
"|password"_COMMENT_SPACE_"\\("_COMMENT_SPACE_ _QUOTED_PSW_ _COMMENT_SPACE_"\\)"
//Used at: CHANGE MASTER TO MASTER_PASSWORD='new3cret', SET PASSWORD [FOR user] = 'hash', password 'user_pass';
"|password"_COMMENT_SPACE_"(?:for"_COMMENT_SPACE_"\\S+?)?"_COMMENT_SPACE_"="_COMMENT_SPACE_ _QUOTED_PSW_
"|password"_COMMENT_SPACE_ _QUOTED_PSW_
//federated engine create table with connection. See: http://dev.mysql.com/doc/refman/5.5/en/federated-create-connection.html
//commented out as federated engine is disabled by default
//"|ENGINE"_COMMENT_SPACE_"="_COMMENT_SPACE_"FEDERATED"_COMMENT_SPACE_".*CONNECTION"_COMMENT_SPACE_"="_COMMENT_SPACE_"[\'|\"]\\S+?://\\S+?:(?<psw>.*)@\\S+[\'|\"]"
default_pw_masking_regex
);
static MYSQL_SYSVAR_BOOL(uninstall_plugin, uninstall_plugin_enable,
@ -1701,7 +1821,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, NULL);
NULL, whitelist_cmds_string_update, "BEGIN,COMMIT");
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.",
@ -1810,8 +1930,9 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
audit_plugin.interface_version >> 8);
}
#elif !defined(MARIADB_BASE_VERSION)
//interface version for MySQL 5.6 changed in 5.6.14
#elif !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID < 50709
// Interface version for MySQL 5.6 changed in 5.6.14.
// This is not needed for 5.7.
extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
{
const char * ver_5_6_13 = "5.6.13";