20 Commits

Author SHA1 Message Date
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
11 changed files with 1291 additions and 1222 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-2018 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:

View File

@@ -128,6 +128,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
@@ -101,6 +101,9 @@ typedef struct ThdOffsets {
OFFSET found_rows;
OFFSET sent_row_count;
OFFSET row_count_func;
OFFSET stmt_da;
OFFSET da_status;
OFFSET da_sql_errno;
} ThdOffsets;
/*
@@ -145,6 +148,9 @@ public:
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.
*/
@@ -178,6 +184,9 @@ private:
int m_port; // TCP port of remote side
uint m_errorCode;
bool m_setErrorCode;
protected:
ThdSesData(const ThdSesData&);
ThdSesData &operator =(const ThdSesData&);
@@ -218,6 +227,8 @@ public:
static QueryTableInf *getQueryCacheTableList1(THD *thd);
// 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)
@@ -276,7 +287,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
@@ -335,15 +348,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)
@@ -387,23 +394,19 @@ public:
static inline const char * pfs_connect_attrs(void * pfs)
{
if (! Audit_formatter::thd_offsets.pfs_connect_attrs)
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);
if (pfs_pointer == NULL)
{
return NULL;
}
return *pfs_pointer;
}
static inline uint pfs_connect_attrs_length(void * pfs)
{
if (! Audit_formatter::thd_offsets.pfs_connect_attrs_length)
if (! Audit_formatter::thd_offsets.pfs_connect_attrs_length || pfs == NULL)
{
//no offsets - return 0
return 0;
@@ -413,7 +416,7 @@ public:
static inline const CHARSET_INFO * pfs_connect_attrs_cs(void * pfs)
{
if (! Audit_formatter::thd_offsets.pfs_connect_attrs_cs)
if (! Audit_formatter::thd_offsets.pfs_connect_attrs_cs || pfs == NULL)
{
//no offsets - return null
return NULL;
@@ -475,29 +478,6 @@ static inline const CHARSET_INFO * pfs_connect_attrs_cs(void * pfs)
return sock;
}
static inline int thd_client_port(THD *thd)
{
if (! Audit_formatter::thd_offsets.net)
{
return -1;
}
NET *net = ((NET *) (((unsigned char *) thd)
+ Audit_formatter::thd_offsets.net));
// get the port for the remote end
int port = -1;
if (net->vio != NULL) // MySQL 5.7.17 - this can happen. :-(
{
struct sockaddr_in *in;
in = (struct sockaddr_in *) & net->vio->remote;
port = in->sin_port;
}
return port;
}
static inline ulonglong thd_found_rows(THD *thd)
{
if (Audit_formatter::thd_offsets.found_rows == 0)
@@ -537,6 +517,47 @@ static inline const CHARSET_INFO * pfs_connect_attrs_cs(void * pfs)
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)
{
@@ -899,7 +920,8 @@ class Audit_socket_handler: public Audit_io_handler {
public:
Audit_socket_handler() :
m_vio(NULL), m_connect_timeout(1)
m_vio(NULL), m_connect_timeout(1), m_write_timeout(0),
m_log_with_error_severity(false)
{
m_io_type = "socket";
}
@@ -922,6 +944,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&);
@@ -930,6 +954,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

@@ -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
@@ -44,6 +38,7 @@
#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,8 +48,6 @@
#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>

View File

@@ -59,7 +59,7 @@ fi
# In 5.6.15 and up, 5.7 and mariabdb 10.0.11 and up, mariadb 10.1
# m_session_connect_attrs_cs changed to m_session_connect_attrs_cs_number
if echo $MYVER | grep -P '^(5\.7|10\.1|5\.6\.(1[5-9]|[2-9][0-9])|10.0.(1[1-9]|[2-9][0-9]))' > /dev/null
if echo $MYVER | grep -P '^(5\.7|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
@@ -110,6 +110,26 @@ 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)' > /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
cat <<EOF > offsets.gdb
set logging on
set width 0
@@ -136,6 +156,9 @@ $LEX_SQL
$FOUND_ROWS
$SENT_ROW_COUNT
$ROW_COUNT_FUNC
$STMT_DA
$DA_STATUS
$DA_SQL_ERRNO
printf "}"
EOF

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

@@ -27,6 +27,11 @@
#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 {\
@@ -111,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)
@@ -465,14 +513,46 @@ int Audit_socket_handler::open(const char *io_dest, bool log_errors)
#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;
}
@@ -616,7 +696,7 @@ ssize_t Audit_json_formatter::start_msg_format(IWriter *writer)
yajl_add_string_val(gen, "msg-type", "header");
uint64 ts = 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);
@@ -848,7 +928,12 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
(strcasestr(cmd, "select") != NULL && thd_row_count_func(thd) > 0))
{
// m_row_count_func will be -1 for most selects but can be > 0, e.g. select into file
rows = thd_row_count_func(thd);
// 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
{
@@ -860,6 +945,12 @@ ssize_t Audit_json_formatter::event_format(ThdSesData *pThdData, IWriter *writer
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
@@ -989,7 +1080,7 @@ ThdSesData::ThdSesData(THD *pTHD, StatementSource source)
: m_pThd (pTHD), m_CmdName(NULL), m_UserName(NULL),
m_objIterType(OBJ_NONE), m_tables(NULL), m_firstTable(true),
m_tableInf(NULL), m_index(0), m_isSqlCmd(false),
m_port(-1), m_source(source)
m_port(-1), m_source(source), m_errorCode(0), m_setErrorCode(false)
{
m_CmdName = retrieve_command (m_pThd, m_isSqlCmd);
m_UserName = retrieve_user (m_pThd);
@@ -1002,6 +1093,15 @@ ThdSesData::ThdSesData(THD *pTHD, StatementSource source)
}
}
void ThdSesData::storeErrorCode()
{
uint code = 0;
if (Audit_formatter::thd_error_code(m_pThd, code))
{
setErrorCode(code);
}
}
bool ThdSesData::startGetObjects()
{
// reset vars as this may be called multiple times

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@
#include "md5.h"
#endif
static const char *PLUGIN_NAME = "AUDIT";
/*
@@ -103,12 +104,12 @@ static char password_masking_regex_buff[4096] = {0};
static const char default_pw_masking_regex[] =
// identified by [password] '***'
"identified"_COMMENT_SPACE_"by"_COMMENT_SPACE_"(?:password)?"_COMMENT_SPACE_ _QUOTED_PSW_
"identified" _COMMENT_SPACE_ "by" _COMMENT_SPACE_ "(?:password)?" _COMMENT_SPACE_ _QUOTED_PSW_
// password function
"|password"_COMMENT_SPACE_"\\("_COMMENT_SPACE_ _QUOTED_PSW_ _COMMENT_SPACE_"\\)"
"|password" _COMMENT_SPACE_ "\\(" _COMMENT_SPACE_ _QUOTED_PSW_ _COMMENT_SPACE_ "\\)"
// Used at: CHANGE MASTER TO MASTER_PASSWORD='new3cret', SET PASSWORD [FOR user] = 'hash', password 'user_pass';
"|password"_COMMENT_SPACE_"(?:for"_COMMENT_SPACE_"\\S+?)?"_COMMENT_SPACE_"="_COMMENT_SPACE_ _QUOTED_PSW_
"|password"_COMMENT_SPACE_ _QUOTED_PSW_
"|password" _COMMENT_SPACE_ "(?:for" _COMMENT_SPACE_ "\\S+?)?" _COMMENT_SPACE_ "=" _COMMENT_SPACE_ _QUOTED_PSW_
"|password" _COMMENT_SPACE_ _QUOTED_PSW_
// federated engine create table with connection. See: http://dev.mysql.com/doc/refman/5.5/en/federated-create-connection.html
// commented out as federated engine is disabled by default
// "|ENGINE"_COMMENT_SPACE_"="_COMMENT_SPACE_"FEDERATED"_COMMENT_SPACE_".*CONNECTION"_COMMENT_SPACE_"="_COMMENT_SPACE_"[\'|\"]\\S+?://\\S+?:(?<psw>.*)@\\S+[\'|\"]"
@@ -120,6 +121,9 @@ static char json_socket_name_buff[1024] = {0};
// Define default port in case user configured out port and socket in my.cnf (bug 1151389)
#define MYSQL_DEFAULT_PORT 3306
// Default value for write timeout in microseconds
#define DEFAULT_WRITE_TIMEOUT 1000 // milliseconds --> 1 second - MySQL API uses seconds
/**
* The trampoline functions we use. Will be set to point to allocated mem.
*/
@@ -129,12 +133,6 @@ static unsigned int trampoline_mysql_execute_size = 0;
#if MYSQL_VERSION_ID < 50600
static void (*trampoline_log_slow_statement)(THD *thd) = NULL;
static unsigned int trampoline_log_slow_statement_size = 0;
#endif
#if MYSQL_VERSION_ID < 50505
static int (*trampoline_check_user)(THD *thd, enum enum_server_command command, const char *passwd, uint passwd_len, const char *db, bool check_count) = NULL;
static unsigned int trampoline_check_user_size = 0;
#elif MYSQL_VERSION_ID < 50600
static bool (*trampoline_acl_authenticate)(THD *thd, uint connect_errors, uint com_change_user_pkt_len) = NULL;
static unsigned int trampoline_acl_authenticate_size = 0;
#endif
@@ -260,7 +258,7 @@ static void initializePeerCredentials(THD *pThd)
goto done;
}
// Is it a Unix Domain socket?
// Is it a socket?
if (fstat(sock, &sbuf) < 0)
{
goto done;
@@ -271,17 +269,9 @@ static void initializePeerCredentials(THD *pThd)
goto done;
}
// At this point, we know we have a Unix domain socket.
if (! json_formatter.m_write_socket_creds)
{
// We need to set this, so that we don't send the
// client port, but we don't bother getting the command
// name and user name, since they won't be sent.
THDVAR(pThd, peer_is_uds) = TRUE;
goto done;
}
// Do a SO_PEERCRED
// Note, this seems to work on any socket, but won't bring valid
// info except for a Unix Domain Socket. Sigh.
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) < 0)
{
goto done;
@@ -292,6 +282,21 @@ static void initializePeerCredentials(THD *pThd)
goto done;
}
// At this point, we know we have a socket but we don't know what type.
if (! json_formatter.m_write_socket_creds)
{
// We need to set this, so that we don't send the
// client port, but we don't bother getting the command
// name and user name, since they won't be sent.
THDVAR(pThd, peer_is_uds) = (cred.pid != 0);
goto done;
}
if (cred.pid == 0)
{
goto done;
}
if (peer == NULL)
{
goto done;
@@ -305,7 +310,14 @@ static void initializePeerCredentials(THD *pThd)
if (pwbuf == NULL)
{
// no name, send UID
snprintf(buf, sizeof(buf) - 1, "%lu", (unsigned long) cred.uid);
if (cred.uid >= 0)
{
snprintf(buf, sizeof(buf) - 1, "%lu", (unsigned long) cred.uid);
}
else
{
strcpy(buf, "[unknown]");
}
username = buf;
}
else
@@ -370,7 +382,7 @@ PeerInfo *retrieve_peerinfo(THD *thd)
{
PeerInfo *peer = (PeerInfo *) THDVAR(thd, peer_info);
if (THDVAR(thd, peer_is_uds) && peer != NULL);
if (THDVAR(thd, peer_is_uds) && peer != NULL)
{
return peer;
}
@@ -379,20 +391,25 @@ PeerInfo *retrieve_peerinfo(THD *thd)
return NULL;
}
static int check_array(const char *cmds[],const char *array, int length)
// cmds[] is a list of "commands" (names, commands, whatever) to
// check against the list stored in `array'. Although declared
// here as simple `char *', the `array' is actually a two-dimensional
// array where each element is `length' bytes long. (An array of
// strings.)
//
// We loop over the array and for each element see if it's also
// in `cmds'. If so, we return 1, otherwise we return 0.
//
// This should really be of type bool and return true/false.
static int check_array(const char *cmds[], const char *array, int length)
{
for (int k = 0; array[k * length] !='\0';k++)
{
const char *elem = array + (k * length);
for (int q = 0; cmds[q] != NULL; q++)
{
const char *cmd = cmds[q];
int j = 0;
while (array[k * length + j] != '\0' && cmd[j] != '\0'
&& array[k * length + j] == tolower(cmd[j]))
{
j++;
}
if (array[k * length + j] == '\0' && j != 0)
if (strcasecmp(cmd, elem) == 0)
{
return 1;
}
@@ -579,22 +596,16 @@ QueryTableInf *Audit_formatter::getQueryCacheTableList1(THD *thd)
}
static bool (*trampoline_check_table_access)(THD *thd, ulong want_access,TABLE_LIST *tables,
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID >= 50505
bool any_combination_of_privileges_will_do,
#endif
uint number, bool no_errors) = NULL;
static bool audit_check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables,
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID >= 50505
bool any_combination_of_privileges_will_do,
#endif
uint number, bool no_errors)
{
TABLE_LIST *pTables;
bool res = trampoline_check_table_access(thd, want_access, tables,
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID >= 50505
any_combination_of_privileges_will_do,
#endif
number, no_errors);
if (!res && tables)
{
@@ -647,8 +658,9 @@ static int audit_send_result_to_client(Query_cache *pthis, THD *thd, const LEX_
#endif
if (res)
{
ThdSesData thd_data(thd, ThdSesData::SOURCE_QUERY_CACHE);
audit(&thd_data);
ThdSesData thdData(thd, ThdSesData::SOURCE_QUERY_CACHE);
thdData.storeErrorCode();
audit(&thdData);
}
THDVAR(thd, query_cache_table_list) = 0;
return res;
@@ -679,7 +691,8 @@ static int audit_open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint f
&& (Audit_formatter::thd_inst_thread_id(thd)
|| Audit_formatter::thd_inst_query_id(thd)))
{
ThdSesData thd_data (thd);
ThdSesData thd_data(thd);
// This is before something is run, so no need to set exit status
audit(&thd_data);
}
return res;
@@ -694,10 +707,11 @@ static void audit_post_execute(THD * thd)
// query events are audited by mysql execute command
if (Audit_formatter::thd_inst_command(thd) != COM_QUERY)
{
ThdSesData ThdData(thd);
if (strcasestr(ThdData.getCmdName(), "show_fields") == NULL)
ThdSesData thdData(thd);
if (strcasestr(thdData.getCmdName(), "show_fields") == NULL)
{
audit(&ThdData);
thdData.storeErrorCode();
audit(&thdData);
}
}
}
@@ -761,6 +775,7 @@ static int audit_notify(THD *thd, mysql_event_class_t event_class,
case ER_ACCOUNT_HAS_BEEN_LOCKED:
#endif
ThdData.setCmdName("Failed Login");
ThdData.setErrorCode(event_general->general_error_code);
audit(&ThdData);
break;
default:
@@ -827,12 +842,6 @@ static int (*trampoline_end_connection)(THD *thd) = NULL;
static unsigned int trampoline_end_connection_size = 0;
#endif
#if MYSQL_VERSION_ID >= 50505
// in 5.5 builtins is named differently
#define mysqld_builtins mysql_mandatory_plugins
#endif
extern struct st_mysql_plugin *mysqld_builtins[];
void remove_hot_functions()
{
void * target_function = NULL;
@@ -842,14 +851,6 @@ void remove_hot_functions()
remove_hot_patch_function(target_function,
(void*) trampoline_log_slow_statement, trampoline_log_slow_statement_size, true);
trampoline_log_slow_statement_size = 0;
#endif
#if MYSQL_VERSION_ID < 50505
target_function = (void *) check_user;
remove_hot_patch_function(target_function,
(void*) trampoline_check_user, trampoline_check_user_size, true);
trampoline_check_user_size = 0;
#elif MYSQL_VERSION_ID < 50600
target_function = (void *) acl_authenticate;
remove_hot_patch_function(target_function,
(void*) trampoline_acl_authenticate, trampoline_acl_authenticate_size, true);
@@ -919,11 +920,13 @@ int is_remove_patches(ThdSesData *pThdData)
Sql_cmd_uninstall_plugin *up = Audit_formatter::lex_sql_cmd(pLex);
LEX_STRING Lex_comment = *(LEX_STRING*)(((unsigned char *) up) + Audit_formatter::thd_offsets.uninstall_cmd_comment);
#endif
if (strncasecmp(Lex_comment.str, "AUDIT", 5) == 0)
if (strncasecmp(Lex_comment.str, PLUGIN_NAME, strlen(PLUGIN_NAME)) == 0)
{
char msgBuffer[200];
if (! uninstall_plugin_enable)
{
my_message(ER_NOT_ALLOWED_COMMAND, "Uninstall AUDIT plugin disabled", MYF(0));
sprintf(msgBuffer, "Uninstall %s plugin disabled", PLUGIN_NAME);
my_message(ER_NOT_ALLOWED_COMMAND, msgBuffer, MYF(0));
return 2;
}
@@ -933,7 +936,8 @@ int is_remove_patches(ThdSesData *pThdData)
if (! called_once)
{
called_once = true;
my_message(WARN_PLUGIN_BUSY, "Uninstall AUDIT plugin must be called again to complete", MYF(0));
sprintf(msgBuffer, "Uninstall %s plugin must be called again to complete", PLUGIN_NAME);
my_message(WARN_PLUGIN_BUSY, msgBuffer, MYF(0));
return 2;
}
return 1;
@@ -971,10 +975,10 @@ static int audit_mysql_execute_command(THD *thd)
}
}
ThdSesData thd_data(thd);
const char *cmd = thd_data.getCmdName();
ThdSesData thdData(thd);
const char *cmd = thdData.getCmdName();
do_delay(& thd_data);
do_delay(& thdData);
if (before_after_mode == AUDIT_BEFORE || before_after_mode == AUDIT_BOTH)
{
@@ -984,7 +988,7 @@ static int audit_mysql_execute_command(THD *thd)
strcasestr(cmd, "truncate") != NULL ||
strcasestr(cmd, "rename") != NULL)
{
audit(&thd_data);
audit(&thdData);
}
}
@@ -999,7 +1003,7 @@ static int audit_mysql_execute_command(THD *thd)
}
else
{
switch (is_remove_patches(&thd_data))
switch (is_remove_patches(&thdData))
{
case 1:
// hot patch function were removed and we call the real execute (restored)
@@ -1019,10 +1023,11 @@ static int audit_mysql_execute_command(THD *thd)
}
}
thdData.storeErrorCode();
if (before_after_mode == AUDIT_AFTER || before_after_mode == AUDIT_BOTH)
{
audit(&thd_data);
audit(&thdData);
}
if (pThdPrintedList && pThdPrintedList->cur_index > 0)
@@ -1044,29 +1049,15 @@ static void audit_log_slow_statement(THD *thd)
trampoline_log_slow_statement(thd);
audit_post_execute(thd);
}
#endif
#if MYSQL_VERSION_ID < 50505
static int audit_check_user(THD *thd, enum enum_server_command command,
const char *passwd, uint passwd_len, const char *db,
bool check_count)
{
int res = trampoline_check_user(thd, command, passwd, passwd_len, db, check_count);
ThdSesData ThdData(thd);
audit(&ThdData);
return (res);
}
#elif MYSQL_VERSION_ID < 50600
// only for 5.5
// in 5.6: we use audit plugin event to get the login event
static bool audit_acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
{
bool res = trampoline_acl_authenticate(thd, connect_errors, com_change_user_pkt_len);
ThdSesData ThdData(thd);
audit(&ThdData);
ThdSesData thdData(thd);
thdData.storeErrorCode();
audit(&thdData);
return (res);
}
@@ -1239,9 +1230,17 @@ static bool calc_file_md5(const char *file_name, char *digest_str)
if ((fd = my_open(file_name, O_RDONLY, MYF(MY_WME))) < 0)
{
sql_print_error("%s Failed file open: [%s], errno: %d.",
log_prefix, file_name, errno);
return false;
sql_print_error("%s Failed file open: [%s], errno: %d. Retrying with /proc/%d/exe.",
log_prefix, file_name, errno, getpid());
char pidFilename[100];
sprintf(pidFilename, "/proc/%d/exe", getpid());
if ((fd = my_open(pidFilename, O_RDONLY, MYF(MY_WME))) < 0)
{
sql_print_error("%s Failed file open: [%s], errno: %d.",
log_prefix, pidFilename, errno);
return false;
}
}
my_MD5Context context;
@@ -1330,7 +1329,7 @@ static int setup_offsets()
if (parse_thd_offsets_string(offsets_string))
{
sql_print_information ("%s setup_offsets Audit_formatter::thd_offsets values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu", log_prefix,
sql_print_information ("%s setup_offsets Audit_formatter::thd_offsets values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu", log_prefix,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
Audit_formatter::thd_offsets.main_security_ctx,
@@ -1352,7 +1351,10 @@ static int setup_offsets()
Audit_formatter::thd_offsets.uninstall_cmd_comment,
Audit_formatter::thd_offsets.found_rows,
Audit_formatter::thd_offsets.sent_row_count,
Audit_formatter::thd_offsets.row_count_func
Audit_formatter::thd_offsets.row_count_func,
Audit_formatter::thd_offsets.stmt_da,
Audit_formatter::thd_offsets.da_status,
Audit_formatter::thd_offsets.da_sql_errno
);
if (! validate_offsets(&Audit_formatter::thd_offsets))
@@ -1467,11 +1469,23 @@ static int setup_offsets()
{
decoffsets.row_count_func -= dec;
}
if (decoffsets.stmt_da)
{
decoffsets.stmt_da -= dec;
}
if (decoffsets.da_status)
{
decoffsets.da_status -= dec;
}
if (decoffsets.da_sql_errno)
{
decoffsets.da_sql_errno -= dec;
}
if (validate_offsets(&decoffsets))
{
Audit_formatter::thd_offsets = decoffsets;
sql_print_information("%s Using decrement (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu",
sql_print_information("%s Using decrement (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu",
log_prefix, dec, offset->version, offset->md5digest,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
@@ -1493,7 +1507,10 @@ static int setup_offsets()
Audit_formatter::thd_offsets.uninstall_cmd_comment,
Audit_formatter::thd_offsets.found_rows,
Audit_formatter::thd_offsets.sent_row_count,
Audit_formatter::thd_offsets.row_count_func
Audit_formatter::thd_offsets.row_count_func,
Audit_formatter::thd_offsets.stmt_da,
Audit_formatter::thd_offsets.da_status,
Audit_formatter::thd_offsets.da_sql_errno
);
DBUG_RETURN(0);
@@ -1511,7 +1528,7 @@ static int setup_offsets()
if (validate_offsets(&incoffsets))
{
Audit_formatter::thd_offsets = incoffsets;
sql_print_information("%s Using increment (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu",
sql_print_information("%s Using increment (%zu) offsets from offset version: %s (%s) values: %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu %zu",
log_prefix, inc, offset->version, offset->md5digest,
Audit_formatter::thd_offsets.query_id,
Audit_formatter::thd_offsets.thread_id,
@@ -1533,7 +1550,10 @@ static int setup_offsets()
Audit_formatter::thd_offsets.uninstall_cmd_comment,
Audit_formatter::thd_offsets.found_rows,
Audit_formatter::thd_offsets.sent_row_count,
Audit_formatter::thd_offsets.row_count_func
Audit_formatter::thd_offsets.row_count_func,
Audit_formatter::thd_offsets.stmt_da,
Audit_formatter::thd_offsets.da_status,
Audit_formatter::thd_offsets.da_sql_errno
);
DBUG_RETURN(0);
}
@@ -1958,7 +1978,7 @@ static int audit_plugin_init(void *p)
interface_ver = interface_ver >> 8;
#endif
sql_print_information(
"%s starting up. Version: %s , Revision: %s (%s). AUDIT plugin interface version: %d (0x%x). MySQL Server version: %s.",
"%s starting up. Version: %s , Revision: %s (%s). MySQL AUDIT plugin interface version: %d (0x%x). MySQL Server version: %s.",
log_prefix, MYSQL_AUDIT_PLUGIN_VERSION,
MYSQL_AUDIT_PLUGIN_REVISION, arch, interface_ver, interface_ver,
server_version);
@@ -2134,15 +2154,7 @@ static int audit_plugin_init(void *p)
log_prefix);
}
#endif
#if MYSQL_VERSION_ID < 50505
if (do_hot_patch((void **)&trampoline_check_user, &trampoline_check_user_size,
(void *)check_user, (void *)audit_check_user, "check_user"))
{
DBUG_RETURN(1);
}
#elif MYSQL_VERSION_ID < 50600
if (do_hot_patch((void **)&trampoline_acl_authenticate, &trampoline_acl_authenticate_size,
(void *)acl_authenticate, (void *)audit_acl_authenticate, "acl_authenticate"))
{
@@ -2343,6 +2355,12 @@ static MYSQL_SYSVAR_STR(json_socket_name, json_socket_handler.m_io_dest,
"AUDIT plugin json log unix socket name",
NULL, json_socket_name_update, "");
static MYSQL_SYSVAR_ULONG(json_socket_write_timeout, json_socket_handler.m_write_timeout,
PLUGIN_VAR_RQCMDARG,
"AUDIT plugin json socket write timeout, in milliseconds (currently must be at least 1000)",
NULL, NULL, DEFAULT_WRITE_TIMEOUT,
0, UINT_MAX32, 0);
static MYSQL_SYSVAR_STR(offsets, offsets_string,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC,
"AUDIT plugin offsets. Comma separated list of offsets to use for extracting data",
@@ -2476,6 +2494,7 @@ static struct st_mysql_sys_var* audit_system_variables[] =
MYSQL_SYSVAR(peer_is_uds),
MYSQL_SYSVAR(peer_info),
MYSQL_SYSVAR(before_after),
MYSQL_SYSVAR(json_socket_write_timeout),
NULL
};
@@ -2485,7 +2504,7 @@ mysql_declare_plugin(audit_plugin)
{
plugin_type,
&audit_plugin,
"AUDIT",
PLUGIN_NAME,
"McAfee Inc",
"AUDIT plugin, creates a file mysql-audit.log to log activity",
PLUGIN_LICENSE_GPL,
@@ -2498,32 +2517,26 @@ mysql_declare_plugin(audit_plugin)
}
mysql_declare_plugin_end;
#if MYSQL_VERSION_ID < 50505
/**
* DLL constructor method.
* We set here the audit plugin version to the same as the first built in plugin.
* This is so we can have a single lib for all versions (needed in 5.1)
*/
extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
static inline void init_peer_info()
{
if (mysqld_builtins && mysqld_builtins[0])
{
audit_plugin.interface_version = *(int *) mysqld_builtins[0]->info;
sql_print_information("%s Set interface version to: %d (%d)",
log_prefix, audit_plugin.interface_version,
audit_plugin.interface_version >> 8);
}
else
{
sql_print_error(
"%s mysqld_builtins are null. Plugin will not load unless the mysql version is: %d. \n",
log_prefix, audit_plugin.interface_version >> 8);
}
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
}
#elif MYSQL_VERSION_ID < 50600
static inline void set_plugin_name_from_env()
{
char *plugin_name = getenv("MCAFEE_AUDIT_PLUGIN_NAME");
if (plugin_name != NULL)
{
PLUGIN_NAME = plugin_name;
_mysql_plugin_declarations_[0].name = plugin_name;
sql_print_information(
"%s using plugin name %s from environtment", log_prefix, plugin_name);
}
}
#if MYSQL_VERSION_ID < 50600
extern struct st_mysql_plugin *mysql_mandatory_plugins[];
extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
{
@@ -2532,8 +2545,8 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
log_prefix, audit_plugin.interface_version,
audit_plugin.interface_version >> 8);
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
init_peer_info();
set_plugin_name_from_env();
}
#elif !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID < 50709
// Interface version for MySQL 5.6 changed in 5.6.14.
@@ -2550,15 +2563,15 @@ extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
audit_plugin.interface_version = 0x0301;
}
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
init_peer_info();
set_plugin_name_from_env();
}
#else
// 5.7
extern "C" void __attribute__ ((constructor)) audit_plugin_so_init(void)
{
memset(peer_info_init_value, '0', sizeof(peer_info_init_value)-1);
peer_info_init_value[sizeof(peer_info_init_value) - 1] = '\0';
init_peer_info();
set_plugin_name_from_env();
}
#endif