diff --git a/compiling.txt b/compiling.txt index 0eeeefa..4742b6e 100644 --- a/compiling.txt +++ b/compiling.txt @@ -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: diff --git a/config/ac_mysql.m4 b/config/ac_mysql.m4 index 477b32e..8e548ad 100644 --- a/config/ac_mysql.m4 +++ b/config/ac_mysql.m4 @@ -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 .... && 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"]) ], [ diff --git a/include/audit_handler.h b/include/audit_handler.h index 5e26213..20aa329 100644 --- a/include/audit_handler.h +++ b/include/audit_handler.h @@ -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; -#endif + // 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 diff --git a/include/mysql_inc.h b/include/mysql_inc.h index b60eafd..89d3adc 100644 --- a/include/mysql_inc.h +++ b/include/mysql_inc.h @@ -14,17 +14,27 @@ #include #include + +#if MYSQL_VERSION_ID < 50505 +#include +#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 #include +#endif + #include + #if MYSQL_VERSION_ID >= 50600 -//in 5.6 we use the audit plugin interface +// From 5.6 we use the audit plugin interface #include #endif + #include #include #include @@ -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 +#if MYSQL_VERSION_ID >= 50709 +#include +#if ! defined(MARIADB_BASE_VERSION) +#include +#endif #endif #include diff --git a/src/audit_handler.cc b/src/audit_handler.cc index 3114b1c..cd3709d 100644 --- a/src/audit_handler.cc +++ b/src/audit_handler.cc @@ -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(); @@ -598,16 +652,35 @@ ssize_t Audit_json_formatter::event_format(ThdSesData* pThdData, IWriter * write const CHARSET_INFO *col_connection; #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())) { diff --git a/src/audit_offsets.cc b/src/audit_offsets.cc old mode 100644 new mode 100755 index a0e027e..6ad286c --- a/src/audit_offsets.cc +++ b/src/audit_offsets.cc @@ -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) diff --git a/src/audit_plugin.cc b/src/audit_plugin.cc index f7514b6..224117b 100644 --- a/src/audit_plugin.cc +++ b/src/audit_plugin.cc @@ -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_ "[\'|\"](?.*?)(?.*)@\\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(save), array_elements( NAME ## _buff) - 1);\ + /* handle "set global audit_xxx = null;" */ \ + char *const* save_p = static_cast(save);\ + if (save_p != NULL && *save_p != NULL)\ + {\ + strncpy( NAME ##_buff , *static_cast(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(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(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); - } - password_masking_regex_string = password_masking_regex_buff; - password_masking_regex_compile(); + str_val = *save_p; + } + + // 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(save); + const char * str_val = NULL; + char *const* save_p = static_cast(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_ "[\'|\"](?.*?)(?.*)@\\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";