37 Commits

Author SHA1 Message Date
Patrick Wade
cd928db022 Updating with the latest offsets for
Percona64 	5.7.31-34
MySQL64 	5.6.50, 5.7.32
MariaDB64	10.2.36, 10.2.35, 10.1.48
MySQL32		5.6.50, 5.7.32
MariaDB32	10.2.36, 10.2.35, 10.1.48
2021-02-23 16:47:11 +00:00
pwrpw
622366a459 Update README.md 2021-02-21 13:33:43 +00:00
wbarrettmcafee
3ec0c137c9 Updating with the latest offsets for
Percona64 	5.7.30-33, 5.7.29-32, 5.7.28-31, 5.7.27-30, 5.7.26-29

MySQL64 	8.0.22, 9.0.21, 8.0.20, 8.0.19, 8.0.18, 8.0.17, 8.0.16, 8.0.15, 8.0.14, 8.0.13, 8.0.12, 8.0.11,
		5.6.49, 5.7.31, 5.6.46, 5.6.47, 5.6.48, 5.7.30, 5.7.29, 5.7.28, 5.6.45, 5.7.27, 5.6.44, 5.7.26

MariaDB64	10.1.43, 10.2.27, 10.2.29, 5.5.66, 10.1.47, 10.2.34, 10.2.30, 10.2.33, 10.1.46, 10.2.31, 10.1.44,
		10.1.45, 10.2.32, 5.5.67, 5.5.68, 10.1.41, 10.2.26, 5.5.65, 10.2.25, 10.1.40, 10.1.39, 10.2.24,
		10.2.23, 5.5.64

MySQL32		5.6.49, 5.7.31, 5.6.46, 5.6.47, 5.6.48, 5.7.28, 5.7.29, 5.7.30, 5.6.45, 5.7.27, 5.6.44, 5.7.26

MariaDB32	10.1.43, 10.2.27, 10.2.29, 5.5.66, 10.1.47, 10.2.34, 10.2.30, 10.2.33, 10.1.46, 10.2.32, 10.1.44,
		10.1.45, 10.2.31, 5.5.67, 5.5.68, 10.1.41, 10.2.26, 5.5.65, 10.2.25, 10.1.40, 10.1.39, 10.2.24,
		10.2.23, 5.5.64
2020-12-03 15:15:07 +00:00
Twomey, Sean
a875a835a4 Update with the latest offsets for
MySQL 5.6.43, 5.7.25, 5.5.62, 5.6.42, 5.7.24
MariaDB 10.2.21, 10.2.20, 10.2.19, 10.1.37, 10.0.37, 5.5.62
Percona 5.7.24-27,  5.7.24-26,  5.7.23-25, 5.7.23-24
2019-03-12 16:56:15 +00:00
Twomey, Sean
2b130c7bd7 Remove file falsely added 2018-11-21 10:41:44 +00:00
Twomey, Sean
266447b8b7 update API Key 2018-11-14 15:24:56 +00:00
Twomey, Sean
416e6fa0aa Add offsets for the follow versions
MySQL 	5.5.60
MySQL 	5.5.61
MySQL 	5.6.40
MySQL 	5.6.41
MySQL 	5.7.22
MySQL 	5.7.23

MariaDB 	5.5.59
MariaDB 	5.5.60
MariaDB 	5.5.61

MariaDB 	10.0.35-36
MariaDB 	10.1.32-36
MariaDB 	10.2.14-18
Percona 	5.7.22-23
2018-11-12 16:41:44 +00:00
Aharon Robbins
edbed38f42 Update copyright year in README.md. 2018-03-25 14:19:12 +03:00
Aharon Robbins
72af9c00f4 Add offsets for Percona 5.7.21-20. 2018-02-21 09:19:48 +02:00
Aharon Robbins
637d30a568 Add offsets for MariaDB 10.2.13. 2018-02-15 11:43:35 +02:00
Aharon Robbins
a52594c59c Add offsets for MariaDB 10.1.31. 2018-02-08 10:52:38 +02:00
Aharon Robbins
cb2cbc605c Add offsets for MariaDB 10.0.34. 2018-01-31 13:21:41 +02:00
Aharon Robbins
52c611cef8 MySQL 5.5.59, 5.6.39, 5.7.21
MariaDB 5.5.59, 10.2.12
Percona 5.7.20-19
2018-01-22 14:23:28 +02:00
Tomer Hadri
6ea0b6bd15 Merge branch 'master' of https://github.com/mcafee/mysql-audit
Conflicts:
	src/audit_offsets.cc
2017-12-28 14:49:00 +02:00
Tomer Hadri
2d0a23c73d - Add support for Percona`s MySQL 5.7.19-17, 5.7.20-18
- MySQL 5.1 support EOL
2017-12-28 14:34:46 +02:00
Aharon Robbins
bca3715fb5 Add support for Percona 5.7.19-17 64-bit.
Add support for MySQL 5.5.58, 5.6.38, 5.7.20.
Add support for MariaDB 5.5.58, 10.0.33, 10.1.27-29, 10.2.8-11.
2017-12-05 12:27:22 +02:00
Tomer Hadri
d9786405af Add support for 5.5.58, 5.6.38, 5.7.20.
Include code from https://github.com/mcafee/mysql-audit/pull/179.
2017-10-22 11:48:27 +03:00
Aharon Robbins
286197fe6d Add offsets for MySQL 5.5.57, 5.6.37 and 5.7.19. 2017-09-13 12:42:55 +03:00
Aharon Robbins
40dc1e7ff7 Add offsets for MariaDB 10.0.32 and 10.1.26.
Fix Community Issues 171, 172, 173.
Add support for sending exit status of a command.
2017-08-31 11:16:19 +03:00
Aharon Robbins
969d0b481a Add offsets for MariaDB 5.5.57. 2017-07-20 17:46:45 +03:00
Aharon Robbins
e7d07fc1fe Add offsets for MariaDB 10.2.7. 2017-07-13 15:35:14 +03:00
Aharon Robbins
e6fd9d8b6f Update copyright years in README.md. 2017-07-12 10:57:14 +03:00
Aharon Robbins
8e56548468 1. Add support for MariaDB 10.2.0-6, 10.1.25.
2. Plugin can now be renamed.
3. Socket failed to connect message log as warning the first time.
4. Correctly retrieve the port for a remote connection.
5. Log negative user id number as [unknown] .
2017-07-06 15:29:30 +03:00
Aharon Robbins
e03dcc1cec Add offsets for MariaDB 10.1.24. 2017-06-06 10:43:56 +03:00
Aharon Robbins
8370847e9b Add offsets for MariaDB 10.0.31. 2017-05-24 10:49:45 +03:00
Aharon Robbins
4b78009cb3 Fix build for MySQL 5.5 and 5.1. 2017-05-21 15:19:16 +03:00
Aharon Robbins
f4a3411ccd Add offsets for MySQL 5.5.56.
Add offsets for MariaDB 5.5.56 and 10.1.23.
Add write timeout for sockets: audit_json_write_timeout variable.
2017-05-09 17:32:06 +03:00
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
15 changed files with 3129 additions and 1333 deletions

View File

@@ -42,7 +42,7 @@ Source code is available at: https://github.com/mcafee/mysql-audit
License
-------------------------------
Copyright (C) 2016 McAfee, Inc.
Copyright (C) 2011-2021 McAfee, LLC.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU
General Public License as published by the Free Software Foundation; version 2 of the License.

View File

@@ -52,6 +52,8 @@ first (see www.boost.org). In such a case, use:
Note: For MariaDB use: cmake . -DBUILD_CONFIG=mysql_release
Note: For Percona`s MySQL use: cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_CONFIG=mysql_release -DWITH_BOOST=boost_1_59_0 .
Note: to speed things up it is possible to build just the following directories:
libservices
extra
@@ -63,6 +65,8 @@ chmod +x bootstrap.sh
CXX='gcc -static-libgcc' CC='gcc -static-libgcc' ./configure --with-mysql=mysql-5.x.x --with-mysql-libservices=mysql-5.x.x/libservices/libmysqlservices.a
make
Note: For Percona`s MySQL define also PERCONA_BUILD=true variable, like "PERCONA_BUILD=true ./configure..."
==== Compiling with make =====
Go to top source dir and run:
@@ -77,3 +81,20 @@ Some documentation about configure command for mysql:
http://dev.mysql.com/doc/refman/5.1/en/source-configuration-options.html
==== MariaDB 10.2.10 ======
1. Firstly checkout the source code:-
- svn co https://beasource3.corp.nai.org/svn/projects/DBSec-MySQL audit_plugin_mysql
2. cd audit_plugin_mysql
3. unzip zip-sources/mariadb-10.2.10.zip
4. cd mariadb-10.2.10
5. CC=gcc CXX=g++ cmake . -DBUILD_CONFIG=mysql_release -DGNUTLS_INCLUDE_DIR=./zip-sources/mariadb-10.2.10/gnutls-3.3.24/64b/include -DGNUTLS_LIBRARY=./zip-sources/mariadb-10.2.10/gnutls-3.3.24/64b/lib
6. cd mariadb-10.2.10/libservices
7. make
8. cd ../extra
9. make
10. cd ../..
11. chmod +x bootstrap.sh
12. ./bootstrap.sh
13. CXX='gcc -static-libgcc' CC='gcc -static-libgcc' MYSQL_AUDIT_PLUGIN_VERSION=1.1.7 MYSQL_AUDIT_PLUGIN_REVISION=`svn info|grep ^Revision|awk -F ": " '{print $2}'` ./configure --enable-debug=no --with-mysql=mariadb-10.2.10 --with-mysql-libservices=mariadb-10.2.10/libservices/libmysqlservices.a
14. gmake <======== This will create the plugin "libaudit_plugin.so"

View File

@@ -33,7 +33,7 @@ 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/libbinlogevents/export -I$withval/libbinlogevents/include -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 -I$withval/extra/rapidjson/include"
AC_MSG_RESULT(["$withval"])
],
[

View File

@@ -96,8 +96,7 @@ AC_PATH_PROG(DIFF, diff, diff)
#we can add the following flags for better error catching: -Werror -Wimplicit
CPPFLAGS="$CPPFLAGS -Werror -Wall"
CFLAGS="$CFLAGS -Wimplicit"
# From MySQL: Disable exceptions as they seams to create problems with gcc and threads.
CXXFLAGS="-fno-implicit-templates -fno-exceptions -fno-rtti -Wno-reorder -Wno-strict-aliasing"
CXXFLAGS="-fno-implicit-templates -fno-strict-aliasing"
#add pthread libs
LIBS="$LIBS -lpthread"
@@ -128,6 +127,10 @@ CPPFLAGS="$CPPFLAGS -DMYSQL_AUDIT_PLUGIN_VERSION='\"$MYSQL_AUDIT_PLUGIN_VERSION\
CPPFLAGS="$CPPFLAGS -DMYSQL_AUDIT_PLUGIN_REVISION='\"$MYSQL_AUDIT_PLUGIN_REVISION\"'"
CPPFLAGS="$CPPFLAGS '-DMYSQL_AUDIT_PLUGIN_SYMBOL_VERSION()=extern const char audit_plugin_version_$MYSQL_AUDIT_PLUGIN_SYMBOL_VERSION'"
# Percona`s MySQL macro
if [[ "$PERCONA_BUILD" = "true" ]]; then
CPPFLAGS="$CPPFLAGS -DPERCONA_BUILD" # Percona`s build macro, used to distinguish between MySQL/MariaDB build VS Percona build
fi
#subst the relevant variables
AC_SUBST(CPPFLAGS)

View File

@@ -17,7 +17,7 @@
#include <pcre.h>
#define AUDIT_LOG_PREFIX "Audit Plugin:"
#define AUDIT_LOG_PREFIX "McAfee Audit Plugin:"
#define AUDIT_PROTOCOL_VERSION "1.0"
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
@@ -48,15 +48,28 @@ 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
#define MAX_OBJECT_CHAR_NUMBERS 131
#define MAX_USER_CHAR_NUMBERS 20
#define MAX_USER_CHAR_NUMBERS 32
#define MAX_NUM_OBJECT_ELEM 256
#define MAX_NUM_USER_ELEM 256
@@ -78,6 +91,20 @@ 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;
OFFSET stmt_da;
OFFSET da_status;
OFFSET da_sql_errno;
OFFSET view_tables;
} ThdOffsets;
/*
@@ -110,11 +137,21 @@ 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; }
void storeErrorCode();
void setErrorCode(uint code) { m_errorCode = code; m_setErrorCode = true; }
bool getErrorCode(uint & code) const { code = m_errorCode; return m_setErrorCode; }
/**
* Start fetching objects. Return true if there are objects available.
*/
@@ -141,6 +178,16 @@ private:
QueryTableInf *m_tableInf;
int m_index;
// Statement source
StatementSource m_source;
PeerInfo *m_peerInfo;
int m_port; // TCP port of remote side
uint m_errorCode;
bool m_setErrorCode;
protected:
ThdSesData(const ThdSesData&);
ThdSesData &operator =(const ThdSesData&);
@@ -178,9 +225,13 @@ public:
virtual ssize_t stop_msg_format(IWriter *writer) { return 0; }
static const char *retrieve_object_type(TABLE_LIST *pObj);
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 80000
static QueryTableInf *getQueryCacheTableList1(THD *thd);
#endif
// utility functions for fetching thd stuff
static int thd_client_port(THD *thd);
static inline my_thread_id thd_inst_thread_id(THD *thd)
{
return *(my_thread_id *) (((unsigned char *) thd)
@@ -239,7 +290,9 @@ public:
static inline const char *thd_inst_main_security_ctx_host(THD *thd)
{
Security_context *sctx = thd_inst_main_security_ctx(thd);
if (! Audit_formatter::thd_offsets.sec_ctx_ip) // check ip to understand if set as host is first and may actually be set to 0
// check ip to understand if set, as host is first in the struct and may actually be set to 0
// we expect to have offsets for both ip and host or for neither of them
if (! Audit_formatter::thd_offsets.sec_ctx_ip)
{
// 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
@@ -298,15 +351,9 @@ public:
return sctx->priv_user().str;
#endif
}
#if MYSQL_VERSION_ID < 50505
// in 5.1.x priv_user is a pointer
return *(const char **) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_priv_user);
#else
// in 5.5 and up priv_user is an array (char priv_user[USERNAME_LENGTH])
return (const char *) (((unsigned char *) sctx)
+ Audit_formatter::thd_offsets.sec_ctx_priv_user);
#endif
}
static inline int thd_inst_command(THD *thd)
@@ -319,6 +366,208 @@ 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 || pfs == NULL)
{
//no offsets - return null
return NULL;
}
const char **pfs_pointer = (const char **) (((unsigned char *) pfs) + Audit_formatter::thd_offsets.pfs_connect_attrs);
return *pfs_pointer;
}
static inline uint pfs_connect_attrs_length(void * pfs)
{
if (! Audit_formatter::thd_offsets.pfs_connect_attrs_length || pfs == NULL)
{
//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 || pfs == NULL)
{
//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) ) || (major == 8) // 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 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;
}
static inline bool thd_error_code(THD *thd, uint & code)
{
#if MYSQL_VERSION_ID >= 50534
if ( Audit_formatter::thd_offsets.stmt_da == 0 ||
Audit_formatter::thd_offsets.da_status == 0 ||
Audit_formatter::thd_offsets.da_sql_errno == 0 )
{
return false;
}
Diagnostics_area **stmt_da = ((Diagnostics_area **) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.stmt_da));
enum Diagnostics_area::enum_diagnostics_status *status =
((enum Diagnostics_area::enum_diagnostics_status *) (((unsigned char *) (*stmt_da))
+ Audit_formatter::thd_offsets.da_status));
uint *sql_errno = ((uint *) (((unsigned char *) (*stmt_da))
+ Audit_formatter::thd_offsets.da_sql_errno));
if (*status == Diagnostics_area::DA_OK ||
*status == Diagnostics_area::DA_EOF )
{
code = 0;
return true;
}
else if (*status == Diagnostics_area::DA_ERROR)
{
code = *sql_errno;
return true;
}
else // DA_EMPTY, DA_DISABLE
{
return false;
}
#else
return false;
#endif
}
#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)
@@ -333,7 +582,10 @@ public:
static inline bool table_is_view(TABLE_LIST *table)
{
return table->view_tables != 0;
if (!Audit_formatter::thd_offsets.view_tables)
return table->view_tables != 0;
List<TABLE_LIST> **view_tables = (List<TABLE_LIST> **)((char*)table + Audit_formatter::thd_offsets.view_tables);
return *view_tables;
}
};
@@ -346,11 +598,14 @@ public:
static const char *DEF_MSG_DELIMITER;
Audit_json_formatter()
: m_msg_delimiter(NULL),
m_write_start_msg(true),
m_password_mask_regex_preg(NULL),
: m_write_start_msg(true),
m_write_sess_connect_attrs(true),
m_write_client_capabilities(false),
m_write_socket_creds(true),
m_perform_password_masking(NULL),
m_msg_delimiter(NULL),
m_password_mask_regex_compiled(false),
m_perform_password_masking(NULL)
m_password_mask_regex_preg(NULL)
{
}
@@ -386,7 +641,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
@@ -440,8 +712,12 @@ public:
static void stop_all();
Audit_handler() :
m_initialized(false), m_enabled(false), m_print_offset_err(true),
m_formatter(NULL), m_failed(false), m_log_io_errors(true)
m_formatter()
,m_initialized()
,m_enabled()
,m_failed()
,m_log_io_errors(true)
,m_print_offset_err(true)
{
}
@@ -607,7 +883,10 @@ class Audit_file_handler: public Audit_io_handler {
public:
Audit_file_handler() :
m_sync_period(0), m_log_file(NULL), m_sync_counter(0), m_bufsize(0)
m_sync_period(0)
, m_bufsize(0)
, m_log_file(NULL)
, m_sync_counter(0)
{
m_io_type = "file";
}
@@ -654,7 +933,10 @@ class Audit_socket_handler: public Audit_io_handler {
public:
Audit_socket_handler() :
m_vio(NULL), m_connect_timeout(1)
m_connect_timeout(1)
, m_write_timeout()
, m_vio()
, m_log_with_error_severity()
{
m_io_type = "socket";
}
@@ -677,6 +959,8 @@ public:
void close();
int open(const char *io_dest, bool log_errors);
unsigned long m_write_timeout; // write timeout in microseconds
protected:
// override default assignment and copy to protect against creating additional instances
Audit_socket_handler & operator=(const Audit_socket_handler&);
@@ -685,6 +969,9 @@ protected:
// Vio we write to
// define as void* so we don't access members directly
void *m_vio;
// log using error severity only the second time same issue occurs
bool m_log_with_error_severity;
};
#endif /* AUDIT_HANDLER_H_ */

View File

@@ -4,7 +4,6 @@
* Created on: Jan 10, 2011
* Author: Guyl
*/
#ifndef HOT_PATCH_H_
#define HOT_PATCH_H_
@@ -14,9 +13,14 @@
#define GETPAGESIZE() sysconf (_SC_PAGE_SIZE)
int hot_patch_function(void* targetFunction, void* newFunction, void* trampolineFunction, unsigned int *trampolinesize, unsigned int *usedsize, bool log_info);
struct SavedCode {
char code [1024];
size_t size;
};
void remove_hot_patch_function(void* targetFunction, void* trampolineFunction, unsigned int trampolinesize, bool log_info);
int hot_patch_function(void* targetFunction, void* newFunction, void* trampolineFunction, unsigned int *trampolinesize, unsigned int *usedsize, bool log_info, SavedCode* saved_code);
void remove_hot_patch_function(void* targetFunction, void* trampolineFunction, unsigned int trampolinesize, bool log_info, SavedCode* saved_code);
//8KB NOP + 16
//can be used to define a block of memory to use for trampolines

View File

@@ -17,12 +17,6 @@
#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
@@ -39,11 +33,31 @@
#include <sql_parse.h>
#include <sql_class.h>
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 80019
#include <mysql/components/services/mysql_connection_attributes_iterator.h>
#include <mysql/components/my_service.h>
#include <mysql/service_plugin_registry.h>
#endif
#if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 80000
using my_bool = bool;
#if MYSQL_VERSION_ID < 80012
#define PLUGIN_VAR_NOSYSVAR 0x0400
#endif
#include <sql/item.h>
#include <sql/log.h>
#include <sql/log_event.h>
#include <sql/mysqld.h>
#include <sql/protocol.h>
#include <sql/sql_lex.h>
#else
#include <my_global.h>
typedef struct st_mysql_sys_var SYS_VAR;
#endif
#include <sql_connect.h>
#include <sql/sql_base.h>
#include <sql/sql_table.h>
#include <sql/sql_view.h>
#include <sql/sql_error.h>
// TODO: use mysql mutex instead of pthread
/*
@@ -53,12 +67,11 @@
#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 */
#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
@@ -79,10 +92,7 @@
# endif
#endif
// MariaDB doesn't have my_getsystime (returns 100 nano seconds) function. They replaced with my_hrtime_t my_hrtime() which returns microseconds
#if defined(MARIADB_BASE_VERSION)
#define my_getsystime() ((my_hrtime()).val * 10)
#if defined(MARIADB_BASE_VERSION)
// MariaDB has a kill service that overrides thd_killed as a macro. It also has thd_killed function defined for backwards compatibility, so we redefine it.
#undef thd_killed
extern "C" int thd_killed(const MYSQL_THD thd);
@@ -91,7 +101,87 @@ extern "C" int thd_killed(const MYSQL_THD thd);
#if MYSQL_VERSION_ID >= 100010
extern "C" char *thd_security_context(MYSQL_THD thd, char *buffer, unsigned int length, unsigned int max_query_len);
#endif
#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
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 80000
#include <dlfcn.h>
#endif
namespace compat {
/*************************/
/* my_getsystime */
/*************************/
#if defined(MARIADB_BASE_VERSION)
// MariaDB doesn't have my_getsystime (returns 100 nano seconds) function. They replaced with my_hrtime_t my_hrtime() which returns microseconds
static inline unsigned long long int my_getsystime() { return (my_hrtime()).val * 10; }
#elif MYSQL_VERSION_ID < 80000
static inline unsigned long long int my_getsystime() { return ::my_getsystime(); }
#else
static inline unsigned long long int my_getsystime() {
#ifdef HAVE_CLOCK_GETTIME
// Performance regression testing showed this to be preferable
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
return (static_cast<unsigned long long int>(tp.tv_sec) * 10000000 +
static_cast<unsigned long long int>(tp.tv_nsec) / 100);
#else
return std::chrono::duration_cast<
std::chrono::duration<std::int64_t, std::ratio<1, 10000000>>>(
UTC_clock::now().time_since_epoch())
.count();
#endif /* HAVE_CLOCK_GETTIME */
}
#endif
/*********************************************/
/* vio_socket_connect */
/*********************************************/
#if MYSQL_VERSION_ID >= 50600
#ifndef MYSQL_VIO
#define MYSQL_VIO Vio*
#endif
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 80000
static inline bool vio_socket_connect(MYSQL_VIO vio, struct sockaddr *addr, socklen_t len, int timeout)
{
return ::vio_socket_connect(vio, addr, len, timeout);
}
#else
/*********************************************/
/* */
/* resolve the symbols manualy to permit */
/* loading of the plugin in their absence */
/* */
/*********************************************/
extern bool (*_vio_socket_connect)(MYSQL_VIO vio, struct sockaddr *addr, socklen_t len, int timeout);
extern bool (*_vio_socket_connect_80016)(MYSQL_VIO vio, struct sockaddr *addr, socklen_t len, bool nonblocking, int timeout);
extern bool (*_vio_socket_connect_80020)(MYSQL_VIO vio, struct sockaddr *addr, socklen_t len, bool nonblocking, int timeout, bool *connect_done);
static inline bool vio_socket_connect(MYSQL_VIO vio, struct sockaddr *addr, socklen_t len, int timeout)
{
if (_vio_socket_connect) return _vio_socket_connect(vio, addr, len, timeout);
if (_vio_socket_connect_80016) return _vio_socket_connect_80016(vio, addr, len, false, timeout);
if (_vio_socket_connect_80020) return _vio_socket_connect_80020(vio, addr, len, false, timeout, nullptr);
return true;
}
static inline bool init()
{
void* handle = dlopen(NULL, RTLD_LAZY);
if (!handle)
return false;
_vio_socket_connect = (decltype(_vio_socket_connect))dlsym(handle, "_Z18vio_socket_connectP3VioP8sockaddrji");
_vio_socket_connect_80016 = (decltype(_vio_socket_connect_80016))dlsym(handle, "_Z18vio_socket_connectP3VioP8sockaddrjbi");
_vio_socket_connect_80020 = (decltype(_vio_socket_connect_80020))dlsym(handle, "_Z18vio_socket_connectP3VioP8sockaddrjbiPb");
dlclose(handle);
return _vio_socket_connect || _vio_socket_connect_80016 || _vio_socket_connect_80020;
}
#endif
#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,121 @@ 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|8\.|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|8\.)' > /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|8\.|10\.[1-2]|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|8\.0)' > /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
# Exit status info 5.5, 5.6, 5.7
DA_STATUS="print_offset Diagnostics_area m_status" # 5.5, 5.6, 5.7, mariadb 10.0, 10.1, 10.2
DA_SQL_ERRNO="print_offset Diagnostics_area m_sql_errno" # 5.5, 5.6, mariadb 10.0, 10.1, 10.2
STMT_DA="print_offset THD m_stmt_da" # 5.6, 5.7, mariadb 10.0, 10.1, 10.2
if echo $MYVER | grep -P '^(5\.7|8\.0)' > /dev/null
then
DA_SQL_ERRNO="print_offset Diagnostics_area m_mysql_errno"
elif echo $MYVER | grep -P '^(5\.6|10\.)' > /dev/null
then
: place holder
elif echo $MYVER | grep -P '^(5\.5)' > /dev/null
then
STMT_DA="print_offset THD stmt_da"
else
STMT_DA='printf ", 0"'
DA_STATUS='printf ", 0"'
DA_SQL_ERRNO='printf ", 0"'
fi
LEX_COMMENT=""
VIEW_TABLES=""
if echo $MYVER | grep -P '^(8\.0)' > /dev/null
then
LEX_COMMENT='printf ", 0"'
VIEW_TABLES="print_offset TABLE_LIST view_tables"
else
LEX_COMMENT="print_offset LEX comment"
VIEW_TABLES='printf ", 0"'
fi
cat <<EOF > offsets.gdb
set logging on
set width 0
define print_offset
printf ", %d", (size_t)&((\$arg0*)0)->\$arg1
end
@@ -59,30 +153,45 @@ print_offset THD $THREAD_ID
print_offset THD $SEC_CONTEXT
print_offset THD $COMMAND_MEMBER
print_offset THD lex
print_offset LEX comment
$LEX_COMMENT
print_offset Security_context $USER
print_offset Security_context $HOST
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
$STMT_DA
$DA_STATUS
$DA_SQL_ERRNO
$VIEW_TABLES
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 +200,6 @@ OFFSETS=`cat gdb.txt`
echo "//offsets for: $1 ($FULL_MYVER)"
echo "$OFFSETS,"
#clean up
# clean up
rm gdb.txt
rm offsets.gdb

54
plugin-name.txt Normal file
View File

@@ -0,0 +1,54 @@
Tue, Jun 27, 2017 10:48:38 AM
=============================
By default, the McAfee AUDIT plugin for MySQL* is named "AUDIT" and
that is the name you should use when installing the plugin with the SQL
"INSTALL PLUGIN" command.
It is the "AUDIT" name that provides the "audit_" prefix to the plugin's
various configuration variables.
In order to avoid conflict with other vendors' auditing plugins whose
names may start with "audit" (such as MySQL's "audit_log" plugin) it
is possible to change the name of the McAfee plugin. The steps are
as follows:
1. If you're currently using the McAfee plugin, unload it.
2. Edit the /usr/bin/mysqld_safe shell script (using the correct location
for your system). For MySQL 5.7.9, look for the eval_log_error() function.
Before the line that says:
eval "$cmd"
add a line like this:
export MCAFEE_AUDIT_PLUGIN_NAME=MCAFEE # use any name you want
You can use any name you like, "MCAFEE" is just an example.
For other MySQL versions, determine where the mysqld daemon is actually
started, and set the environment variable right before that.
3. After restarting MySQL, you will need to load the plugin using the
new name. From the MySQL client:
install plugin MCAFEE soname 'libaudit_plugin.so';
and/or from /etc/my.cnf:
[mysqld]
plugin-load=MCAFEE=libaudit_plugin.so
Once you've done that, you must remember that the names of ALL the
configuration variables will start with the lowercase version of the
name you've chosen. For example, "mcafee_json_log_file" instead of
"audit_json_log_file".
If you previously had various "audit_XXX" variables set in your
/etc/my.cnf file, you will need to rename them! Otherwise MySQL will
fail to start, with an error about unknown variables.
That's it! Good luck.
* Other trademarks and brands may be claimed as the property of others.

0
src/MySQLPlugin.map Executable file → Normal file
View File

View File

@@ -23,8 +23,15 @@
// for definition of sockaddr_un
#include <sys/un.h>
#include <stdio_ext.h>
#include <limits.h>
#include <unistd.h>
#include "static_assert.h"
#if MYSQL_VERSION_ID < 50600
// for 5.5 and 5.1
extern "C" void vio_timeout(Vio *vio,uint which, uint timeout);
#endif
// utility macro to log also with a date as a prefix
// FIXME: This is no longer used. Remove?
#define log_with_date(f, ...) do {\
@@ -109,6 +116,49 @@ const char *Audit_formatter::retrieve_object_type(TABLE_LIST *pObj)
return "TABLE";
}
// This routine used to pull the client port out of the thd->net->vio->remote
// object, but on MySQL 5.7 the port is zero. So we resort to getting the
// underlying fd and using getpeername(2) on it.
int Audit_formatter::thd_client_port(THD *thd)
{
int port = -1;
int sock = thd_client_fd(thd);
if (sock < 0)
{
return port; // shouldn't happen
}
struct sockaddr_storage addr;
socklen_t len = sizeof(addr);
// get port of the guy on the other end of our connection
if (getpeername(sock, (struct sockaddr *) & addr, & len) < 0)
{
return port;
}
if (addr.ss_family == AF_INET)
{
struct sockaddr_in *sin = (struct sockaddr_in *) & addr;
port = ntohs(sin->sin_port);
}
else
{
struct sockaddr_in6 *sin = (struct sockaddr_in6 *) & addr;
port = ntohs(sin->sin6_port);
}
if (port == 0) // shouldn't happen
{
port = -1;
}
return port;
}
void Audit_handler::stop_all()
{
for (size_t i = 0; i < MAX_AUDIT_HANDLERS_NUM; ++i)
@@ -178,7 +228,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 +259,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 +412,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);
}
@@ -457,19 +508,51 @@ int Audit_socket_handler::open(const char *io_dest, bool log_errors)
m_connect_timeout))
#else
// in 5.6 timeout is in ms
if (vio_socket_connect((Vio*)m_vio,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
if (compat::vio_socket_connect((Vio*)m_vio,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr),
m_connect_timeout * 1000))
#endif
{
if (log_errors)
{
sql_print_warning(
"%s unable to connect to socket: %s. err: %s.",
AUDIT_LOG_PREFIX, m_io_dest, strerror(errno));
// The next time this occurs, log as an error
m_log_with_error_severity = true;
}
// Only if issue persist also in second retry, report it by using 'error' severity.
else if (m_log_with_error_severity)
{
sql_print_error(
"%s unable to connect to socket: %s. err: %s.",
AUDIT_LOG_PREFIX, m_io_dest, strerror(errno));
m_log_with_error_severity = false;
}
close();
return -2;
}
// At this point, connected successfully.
// Ensure same behavior in case first time failed but second retry was successful
m_log_with_error_severity = false;
if (m_write_timeout > 0)
{
int timeout = m_write_timeout / 1000; // milliseconds to seconds, integer dvision
if (timeout == 0)
{
timeout = 1; // round up to 1 second
}
// we don't check the result of this call since in earlier
// versions it returns void
//
// 1 as the 2nd argument means write timeout
vio_timeout((Vio*)m_vio, 1, timeout);
}
return 0;
}
@@ -502,7 +585,7 @@ static void yajl_add_uint64(yajl_gen gen, const char *name, uint64 num)
{
const size_t max_int64_str_len = 21;
char buf[max_int64_str_len];
snprintf(buf, max_int64_str_len, "%llu", num);
snprintf(buf, max_int64_str_len, "%llu", (unsigned long long)num);
yajl_add_string_val(gen, name, buf);
}
@@ -539,8 +622,11 @@ static const char *retrieve_user(THD *thd)
// starting with MySQL version 5.1.41 thd_query_string is added
// And at 5.7 it changed
#if ! defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 50709
#if MYSQL_VERSION_ID >= 80000
extern LEX_CSTRING thd_query_unsafe(MYSQL_THD thd);
#else
extern "C" LEX_CSTRING thd_query_unsafe(MYSQL_THD thd);
#endif
static const char *thd_query_str(THD *thd, size_t *len)
{
@@ -557,7 +643,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)
@@ -611,15 +697,16 @@ ssize_t Audit_json_formatter::start_msg_format(IWriter *writer)
yajl_gen gen = yajl_gen_alloc(NULL);
yajl_gen_map_open(gen);
yajl_add_string_val(gen, "msg-type", "header");
uint64 ts = my_getsystime() / (10000);
uint64 ts = compat::my_getsystime() / (10000);
yajl_add_uint64(gen, "date", ts);
yajl_add_string_val(gen, "audit-version", MYSQL_AUDIT_PLUGIN_VERSION"-"MYSQL_AUDIT_PLUGIN_REVISION);
yajl_add_string_val(gen, "audit-version", MYSQL_AUDIT_PLUGIN_VERSION "-" MYSQL_AUDIT_PLUGIN_REVISION);
yajl_add_string_val(gen, "audit-protocol-version", AUDIT_PROTOCOL_VERSION);
yajl_add_string_val(gen, "hostname", glob_hostname);
yajl_add_string_val(gen, "mysql-version", server_version);
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 +758,157 @@ static const char *replace_in_string(THD *thd,
return new_str;
}
#ifdef HAVE_SESS_CONNECT_ATTRS
#include <storage/perfschema/pfs_instr.h>
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 80000
//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);
#else
// the function is not exported in MySQL 8
/**
Take a length encoded string
@arg ptr inout the input string array
@arg dest where to store the result
@arg dest_size max size of @c dest
@arg copied_len the actual length of the data copied
@arg start_ptr pointer to the start of input
@arg input_length the length of the incoming data
@arg from_cs character set in which @c ptr is encoded
@arg nchars_max maximum number of characters to read
@return status
@retval true parsing failed
@retval false parsing succeeded
*/
static bool parse_length_encoded_string(
const char **ptr
,char *dest
,uint dest_size
,uint *copied_len
,const char *start_ptr
,uint input_length
,bool /* unused */
,const CHARSET_INFO *from_cs
,uint nchars_max
)
{
ulong copy_length, data_length;
const char *well_formed_error_pos = NULL, *cannot_convert_error_pos = NULL,
*from_end_pos = NULL;
copy_length = data_length = net_field_length((uchar **)ptr);
/* we don't tolerate NULL as a length */
if (data_length == NULL_LENGTH) {
return true;
}
if (*ptr - start_ptr + data_length > input_length) {
return true;
}
/*
TODO: Migrate the data itself to UTF8MB4,
this is still UTF8MB3 printed in a UTF8MB4 column.
*/
copy_length = well_formed_copy_nchars(
&my_charset_utf8_bin
, dest
, dest_size
, from_cs
, *ptr
, data_length
, nchars_max
, &well_formed_error_pos
, &cannot_convert_error_pos
, &from_end_pos
);
*copied_len = copy_length;
(*ptr) += data_length;
return false;
}
#endif
/**
* 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)
{
sql_print_information("%s Failed to compute offsets connect_attrs. pfs [%p], connect_attrs [%p], connect_attrs_length [%d], connect_attrs_cs [%p]", AUDIT_LOG_PREFIX, pfs, 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
// sql_print_information("%s something went wrong or we are done 1", AUDIT_LOG_PREFIX);
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)
{
// sql_print_information("%s something went wrong or we are done 2", AUDIT_LOG_PREFIX);
break;
}
attr_value_length= copy_length;
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
return;
}
#endif
ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer)
{
THD *thd = pThdData->getTHD();
@@ -686,15 +924,96 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
// TODO: get the start date from THD (but it is not in millis. Need to think about how we handle this)
// for now simply use the current time.
// my_getsystime() time since epoc in 100 nanosec units. Need to devide by 1000*(1000/100) to reach millis
uint64 ts = my_getsystime() / (10000);
uint64 ts = compat::my_getsystime() / (10000);
yajl_add_uint64(gen, "date", ts);
yajl_add_uint64(gen, "thread-id", thdid);
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
// thd_row_count_func() returns signed valiue. Don't assign it to rows directly.
longlong row_count = thd_row_count_func(thd);
if (row_count > 0)
{
rows = row_count;
}
}
else
{
rows = thd_sent_row_count(thd);
}
if (rows != 0UL)
{
yajl_add_uint64(gen, "rows", rows);
}
uint code;
if (pThdData->getErrorCode(code))
{
yajl_add_uint64(gen, "status", code); // 0 - success, otherwise reports specific errno
}
yajl_add_string_val(gen, "cmd", cmd);
// get objects
@@ -820,13 +1139,39 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
return res;
}
ThdSesData::ThdSesData(THD *pTHD)
: 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)
ThdSesData::ThdSesData(THD *pTHD, StatementSource source)
: m_pThd (pTHD)
, m_CmdName()
, m_UserName()
, m_isSqlCmd()
, m_objIterType(OBJ_NONE)
, m_tables()
, m_firstTable(true)
, m_tableInf()
, m_index()
, m_source(source)
, m_port(-1)
, m_errorCode()
, m_setErrorCode()
{
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);
}
}
void ThdSesData::storeErrorCode()
{
uint code = 0;
if (Audit_formatter::thd_error_code(m_pThd, code))
{
setErrorCode(code);
}
}
bool ThdSesData::startGetObjects()
@@ -836,15 +1181,17 @@ bool ThdSesData::startGetObjects()
m_tables = NULL;
m_firstTable = true;
m_index = 0;
m_tableInf = Audit_formatter::getQueryCacheTableList1(getTHD());
int command = Audit_formatter::thd_inst_command(getTHD());
LEX *pLex = Audit_formatter::thd_lex(getTHD());
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID < 80000
// query cache case
m_tableInf = Audit_formatter::getQueryCacheTableList1(getTHD());
if (pLex && command == COM_QUERY && m_tableInf && m_tableInf->num_of_elem > 0)
{
m_objIterType = OBJ_QUERY_CACHE;
return true;
}
#endif
const char *cmd = getCmdName();
// commands which have single database object
if (strcmp(cmd,"Init DB") == 0
@@ -940,6 +1287,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

File diff suppressed because it is too large Load Diff

View File

@@ -228,7 +228,7 @@ static void WriteJump32(void *pAddress, ULONG_PTR JumpTo)
// Hooks a function
//
static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_PTR trampolineFunction,
unsigned int *trampolinesize, unsigned int *usedsize)
unsigned int *trampolinesize, unsigned int *usedsize, SavedCode* saved_code)
{
#define MAX_INSTRUCTIONS 100
uint8_t raw[MAX_INSTRUCTIONS];
@@ -293,6 +293,9 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_
ud_obj.operand[0].type == UD_OP_JIMM)
{
bool cannot_disassemble = true;
sql_print_information("ud_obj.mnemonic == UD_Ijmp: %d", ud_obj.mnemonic == UD_Ijmp);
sql_print_information("ud_obj.mnemonic == UD_Icall: %d", ud_obj.mnemonic == UD_Icall);
sql_print_information("ud_obj.operand[0].type == UD_OP_JIMM: %d", ud_obj.operand[0].type == UD_OP_JIMM);
#ifdef __i386__
const BYTE *pc = (const BYTE *)targetFunction + InstrSize;
@@ -324,7 +327,59 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_
cannot_disassemble = false;
}
}
sql_print_error("in __i386__");
#else
// If there is a relative jump or call in the to be overwritten chunk,
// construct an absolute jump/call in the trampoline.
#ifdef __x86_64__
sql_print_information("__x86_64__");
#endif
if (ud_obj.operand[0].type == UD_OP_JIMM && (ud_obj.mnemonic == UD_Ijmp || ud_obj.mnemonic == UD_Icall)) {
// jump or call
size_t rewrite_size = 0;
switch (ud_obj.mnemonic) {
case UD_Ijmp:
sql_print_information("rewriting relative jump as absolute");
memcpy((void*)(trampolineFunction + uCurrentSize), "\xff\x25\x00\x00\x00\x00", 6); // jmpq *0x0(%rip)
rewrite_size = 6;
break;
case UD_Icall:
sql_print_information("rewriting relative call as absolute");
memcpy((void*)(trampolineFunction + uCurrentSize), "\xff\x15\x02\x00\x00\x00", 6); // callq *0x2(%rip) -- call the function via the address stored at RIP+2
memcpy((void*)(trampolineFunction + uCurrentSize + 6), "\xeb\x08", 2); // jmp 0x08 -- jump over the function address (8 bytes forward)
rewrite_size = 8;
break;
default:
break;
}
// calculate the jump target from the instruction pointer and the immediate operand
unsigned long jump_target = ud_obj.pc;
switch (ud_obj.operand[0].size) {
case 8:
jump_target += ud_obj.operand[0].lval.sbyte;
break;
case 16:
jump_target += ud_obj.operand[0].lval.sword;
break;
case 32:
jump_target += ud_obj.operand[0].lval.sdword;
break;
}
memcpy((void*)(trampolineFunction + uCurrentSize + rewrite_size), &jump_target, 8);
rewrite_size += 8;
// update the indexes
uCurrentSize += rewrite_size;
InstrSize += ud_insn_len (&ud_obj);
// clear the flag
cannot_disassemble = false;
sql_print_information("target address: [0x%016lx]", jump_target);
sql_print_information("original instruction: [%s]", ud_insn_asm(&ud_obj));
}
#endif
if (cannot_disassemble)
{
@@ -381,8 +436,17 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_
return false;
}
WriteJump((BYTE*)trampolineFunction + uCurrentSize, targetFunction + InstrSize);
// Save the original code that is going to be overwitten by the jump.
// The code in the trampoline can be larger due to rewriting of RIP
// relative instructions and unsuitable for writting back on unhook.
memcpy(saved_code->code, (void*)targetFunction, InstrSize);
saved_code->size = InstrSize;
// jump from trampoline back to continue the original function
WriteJump((BYTE*)trampolineFunction + uCurrentSize, targetFunction + InstrSize);
*usedsize = uCurrentSize + JUMP_SIZE;
// jump from the begin of the original function to our function
#ifndef __x86_64__
WriteJump((void *) targetFunction, newFunction);
#else
@@ -411,7 +475,7 @@ static bool HookFunction(ULONG_PTR targetFunction, ULONG_PTR newFunction, ULONG_
//
static void UnhookFunction(ULONG_PTR Function, ULONG_PTR trampolineFunction, unsigned int trampolinesize)
static void UnhookFunction(ULONG_PTR Function, ULONG_PTR trampolineFunction, unsigned int trampolinesize, SavedCode* saved_code)
{
DATATYPE_ADDRESS FunctionPage = get_page_address((void*)Function);
if (unprotect((void*)FunctionPage, PAGE_SIZE) != 0)
@@ -421,7 +485,7 @@ static void UnhookFunction(ULONG_PTR Function, ULONG_PTR trampolineFunction, uns
log_prefix, (void *) FunctionPage);
return;
}
memcpy((void *) Function, (void*)trampolineFunction,trampolinesize);
memcpy((void *) Function, saved_code->code, saved_code->size);
protect((void*)FunctionPage, PAGE_SIZE);
}
@@ -442,12 +506,12 @@ static void UnhookFunction(ULONG_PTR Function, ULONG_PTR trampolineFunction, uns
* @Return 0 on success otherwise failure
* @See MS Detours paper: http:// research.microsoft.com/pubs/68568/huntusenixnt99.pdf for some background info.
*/
int hot_patch_function(void *targetFunction, void *newFunction, void *trampolineFunction, unsigned int *trampolinesize, unsigned int *usedsize, bool info_print)
int hot_patch_function(void *targetFunction, void *newFunction, void *trampolineFunction, unsigned int *trampolinesize, unsigned int *usedsize, bool info_print, SavedCode* saved_code)
{
DATATYPE_ADDRESS trampolinePage = get_page_address(trampolineFunction);
cond_info_print(info_print, "%s hot patching function: %p, trampolineFunction: %p trampolinePage: %p",log_prefix, (void *)targetFunction, (void *)trampolineFunction, (void *)trampolinePage);
if (HookFunction((ULONG_PTR) targetFunction, (ULONG_PTR) newFunction,
(ULONG_PTR) trampolineFunction, trampolinesize, usedsize))
(ULONG_PTR) trampolineFunction, trampolinesize, usedsize, saved_code))
{
return 0;
}
@@ -466,15 +530,18 @@ int hot_patch_function(void *targetFunction, void *newFunction, void *trampoline
* @param trampolineFunction a function which contains a jump back to the targetFunction.
* @param log_file if not null will log about progress of installing the plugin
*/
void remove_hot_patch_function(void *targetFunction, void *trampolineFunction, unsigned int trampolinesize, bool info_print)
void remove_hot_patch_function(void *targetFunction, void *trampolineFunction, unsigned int trampolinesize, bool info_print, SavedCode* saved_code)
{
if (trampolinesize == 0)
sql_print_information("trampolinesize: %d", trampolinesize);
sql_print_information("saved_code->size: %zd", saved_code->size);
if (trampolinesize == 0 || !saved_code->size)
{
// nothing todo. As hot patch was not set.
return;
cond_info_print(info_print, "%s not removing as hot patch was not set: %p",log_prefix, (void *)targetFunction);
return;
}
DATATYPE_ADDRESS targetPage = get_page_address(targetFunction);
cond_info_print(info_print, "%s removing hot patching function: %p targetPage: %p trampolineFunction: %p",log_prefix, (void *)targetFunction, (void *)targetPage, (void *)trampolineFunction);
UnhookFunction ((ULONG_PTR) targetFunction, (ULONG_PTR)trampolineFunction,trampolinesize);
UnhookFunction ((ULONG_PTR) targetFunction, (ULONG_PTR)trampolineFunction,trampolinesize, saved_code);
return;
}

View File

@@ -22,3 +22,6 @@ libudis86_la_SOURCES = itab.c \
itab.c itab.h: ../docs/x86optable.xml opgen.py
python ./opgen.py
# generate the generated sources prior to the compilation
BUILT_SOURCES = itab.c itab.h