diff options
author | Sergei Golubchik <serg@mariadb.org> | 2016-01-25 22:58:57 +0100 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2016-01-25 22:58:57 +0100 |
commit | f4faac4d6ad31acc96b9edf8c322f3031123dfe0 (patch) | |
tree | 8c7b112e3464149928db6b0bb7c7ddd9067f69c9 /plugin | |
parent | 2ff65ba53190ad897fc41f546fc4a7bdc80d9054 (diff) | |
parent | 666b9663f94c2a4f39d331b28fabd09f2dc43bf2 (diff) | |
download | mariadb-git-f4faac4d6ad31acc96b9edf8c322f3031123dfe0.tar.gz |
Merge branch '10.0' into 10.1
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/server_audit/CMakeLists.txt | 5 | ||||
-rw-r--r-- | plugin/server_audit/plugin_audit_v4.h | 561 | ||||
-rw-r--r-- | plugin/server_audit/server_audit.c | 868 | ||||
-rw-r--r-- | plugin/server_audit/test_audit_v4.c | 162 |
4 files changed, 1321 insertions, 275 deletions
diff --git a/plugin/server_audit/CMakeLists.txt b/plugin/server_audit/CMakeLists.txt index 527d0d702da..3de8c43c871 100644 --- a/plugin/server_audit/CMakeLists.txt +++ b/plugin/server_audit/CMakeLists.txt @@ -13,4 +13,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -MYSQL_ADD_PLUGIN(server_audit server_audit.c MODULE_ONLY) +SET(SERVER_AUDIT_SOURCES + server_audit.c test_audit_v4.c plugin_audit_v4.h) + + MYSQL_ADD_PLUGIN(server_audit ${SERVER_AUDIT_SOURCES} MODULE_ONLY) diff --git a/plugin/server_audit/plugin_audit_v4.h b/plugin/server_audit/plugin_audit_v4.h new file mode 100644 index 00000000000..5f8e43b3811 --- /dev/null +++ b/plugin/server_audit/plugin_audit_v4.h @@ -0,0 +1,561 @@ +/* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved. + + 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. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef _my_audit_h +#define _my_audit_h + +#ifndef PLUGIN_CONTEXT +#include "plugin.h" +#include "mysql/mysql_lex_string.h" +#ifndef MYSQL_ABI_CHECK +#include "m_string.h" +#endif +#include "my_command.h" +#include "my_sqlcommand.h" +#endif /*PLUGIN_CONTEXT*/ + +#define MYSQL_AUDIT_INTERFACE_VERSION 0x0401 + +/** + @enum mysql_event_class_t + + Audit event classes. +*/ +typedef enum +{ + MYSQL_AUDIT_GENERAL_CLASS = 0, + MYSQL_AUDIT_CONNECTION_CLASS = 1, + MYSQL_AUDIT_PARSE_CLASS = 2, + MYSQL_AUDIT_AUTHORIZATION_CLASS = 3, + MYSQL_AUDIT_TABLE_ACCESS_CLASS = 4, + MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS = 5, + MYSQL_AUDIT_SERVER_STARTUP_CLASS = 6, + MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS = 7, + MYSQL_AUDIT_COMMAND_CLASS = 8, + MYSQL_AUDIT_QUERY_CLASS = 9, + MYSQL_AUDIT_STORED_PROGRAM_CLASS = 10, + /* This item must be last in the list. */ + MYSQL_AUDIT_CLASS_MASK_SIZE +} mysql_event_class_t; + +/** + @struct st_mysql_audit + + The descriptor structure that is referred from st_mysql_plugin. +*/ +struct st_mysql_audit +{ + /** + Interface version. + */ + int interface_version; + + /** + Event occurs when the event class consumer is to be + disassociated from the specified THD.This would typically occur + before some operation which may require sleeping - such as when + waiting for the next query from the client. + */ + void (*release_thd)(MYSQL_THD); + + /** + Invoked whenever an event occurs which is of any + class for which the plugin has interest.The second argument + indicates the specific event class and the third argument is data + as required for that class. + */ + int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *); + + /** + An array of bits used to indicate what event classes + that this plugin wants to receive. + */ + unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; +}; + +/** + @typedef enum_sql_command_t + + SQL command type definition. +*/ +typedef enum enum_sql_command enum_sql_command_t; + +/** + @enum mysql_event_general_subclass_t + + Events for the MYSQL_AUDIT_GENERAL_CLASS event class. +*/ +typedef enum +{ + /** occurs before emitting to the general query log. */ + MYSQL_AUDIT_GENERAL_LOG = 1 << 0, + /** occurs before transmitting errors to the user. */ + MYSQL_AUDIT_GENERAL_ERROR = 1 << 1, + /** occurs after transmitting a resultset to the user. */ + MYSQL_AUDIT_GENERAL_RESULT = 1 << 2, + /** occurs after transmitting a resultset or errors */ + MYSQL_AUDIT_GENERAL_STATUS = 1 << 3 +} mysql_event_general_subclass_t; + +#define MYSQL_AUDIT_GENERAL_ALL (MYSQL_AUDIT_GENERAL_LOG | \ + MYSQL_AUDIT_GENERAL_ERROR | \ + MYSQL_AUDIT_GENERAL_RESULT | \ + MYSQL_AUDIT_GENERAL_STATUS) +/** + @struct mysql_event_general + + Structure for the MYSQL_AUDIT_GENERAL_CLASS event class. +*/ +struct mysql_event_general +{ + mysql_event_general_subclass_t event_subclass; + int general_error_code; + unsigned long general_thread_id; + MYSQL_LEX_CSTRING general_user; + MYSQL_LEX_CSTRING general_command; + MYSQL_LEX_CSTRING general_query; + struct charset_info_st *general_charset; + unsigned long long general_time; + unsigned long long general_rows; + MYSQL_LEX_CSTRING general_host; + MYSQL_LEX_CSTRING general_sql_command; + MYSQL_LEX_CSTRING general_external_user; + MYSQL_LEX_CSTRING general_ip; +}; + +/** + @enum mysql_event_connection_subclass_t + + Events for MYSQL_AUDIT_CONNECTION_CLASS event class. +*/ +typedef enum +{ + /** occurs after authentication phase is completed. */ + MYSQL_AUDIT_CONNECTION_CONNECT = 1 << 0, + /** occurs after connection is terminated. */ + MYSQL_AUDIT_CONNECTION_DISCONNECT = 1 << 1, + /** occurs after COM_CHANGE_USER RPC is completed. */ + MYSQL_AUDIT_CONNECTION_CHANGE_USER = 1 << 2, + /** occurs before authentication. */ + MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE = 1 << 3 +} mysql_event_connection_subclass_t; + +#define MYSQL_AUDIT_CONNECTION_ALL (MYSQL_AUDIT_CONNECTION_CONNECT | \ + MYSQL_AUDIT_CONNECTION_DISCONNECT | \ + MYSQL_AUDIT_CONNECTION_CHANGE_USER | \ + MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE) +/** + @struct mysql_event_connection + + Structure for the MYSQL_AUDIT_CONNECTION_CLASS event class. +*/ +struct mysql_event_connection +{ + /** Event subclass. */ + mysql_event_connection_subclass_t event_subclass; + /** Current status of the connection. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** User name of this connection. */ + MYSQL_LEX_CSTRING user; + /** Priv user name. */ + MYSQL_LEX_CSTRING priv_user; + /** External user name. */ + MYSQL_LEX_CSTRING external_user; + /** Proxy user used for this connection. */ + MYSQL_LEX_CSTRING proxy_user; + /** Connection host. */ + MYSQL_LEX_CSTRING host; + /** IP of the connection. */ + MYSQL_LEX_CSTRING ip; + /** Database name specified at connection time. */ + MYSQL_LEX_CSTRING database; + /** Connection type: + - 0 Undefined + - 1 TCP/IP + - 2 Socket + - 3 Named pipe + - 4 SSL + - 5 Shared memory + */ + int connection_type; +}; + +/** +@enum mysql_event_parse_subclass_t + +Events for MYSQL_AUDIT_PARSE_CLASS event class. +*/ +typedef enum +{ + /** occurs before the query parsing. */ + MYSQL_AUDIT_PARSE_PREPARSE = 1 << 0, + /** occurs after the query parsing. */ + MYSQL_AUDIT_PARSE_POSTPARSE = 1 << 1 +} mysql_event_parse_subclass_t; + +#define MYSQL_AUDIT_PARSE_ALL (MYSQL_AUDIT_PARSE_PREPARSE | \ + MYSQL_AUDIT_PARSE_POSTPARSE) + +typedef enum +{ + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_NONE = 0, + /// mysql_event_parse::flags Must be set by a plugin if the query is rewritten. + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_QUERY_REWRITTEN = 1 << 0, + /// mysql_event_parse::flags Is set by the server if the query is prepared statement. + MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_IS_PREPARED_STATEMENT = 1 << 1 +} mysql_event_parse_rewrite_plugin_flag; + +/** Data for the MYSQL_AUDIT_PARSE events */ +struct mysql_event_parse +{ + /** MYSQL_AUDIT_[PRE|POST]_PARSE event id */ + mysql_event_parse_subclass_t event_subclass; + + /** one of FLAG_REWRITE_PLUGIN_* */ + mysql_event_parse_rewrite_plugin_flag *flags; + + /** input: the original query text */ + MYSQL_LEX_CSTRING query; + + /** output: returns the null-terminated rewriten query allocated by my_malloc() */ + MYSQL_LEX_CSTRING *rewritten_query; +}; + +/** + @enum mysql_event_authorization_subclass_t + + Events for MYSQL_AUDIT_AUTHORIZATION_CLASS event class. +*/ +typedef enum +{ + MYSQL_AUDIT_AUTHORIZATION_USER = 1 << 0, + /** Occurs when database privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_DB = 1 << 1, + /** Occurs when table privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_TABLE = 1 << 2, + /** Occurs when column privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_COLUMN = 1 << 3, + /** Occurs when procedure privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_PROCEDURE = 1 << 4, + /** Occurs when proxy privilege is checked. */ + MYSQL_AUDIT_AUTHORIZATION_PROXY = 1 << 5 +} mysql_event_authorization_subclass_t; + +#define MYSQL_AUDIT_AUTHORIZATION_ALL (MYSQL_AUDIT_AUTHORIZATION_USER | \ + MYSQL_AUDIT_AUTHORIZATION_DB | \ + MYSQL_AUDIT_AUTHORIZATION_TABLE | \ + MYSQL_AUDIT_AUTHORIZATION_COLUMN | \ + MYSQL_AUDIT_AUTHORIZATION_PROCEDURE | \ + MYSQL_AUDIT_AUTHORIZATION_PROXY) +/** + @struct mysql_event_authorization + + Structure for MYSQL_AUDIT_AUTHORIZATION_CLASS event class. +*/ +struct mysql_event_authorization +{ + /** Event subclass. */ + mysql_event_authorization_subclass_t event_subclass; + /** Event status. */ + int status; + /** Connection id. */ + unsigned int connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query text. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** Database name. */ + MYSQL_LEX_CSTRING database; + /** Table name. */ + MYSQL_LEX_CSTRING table; + /** Other name associated with the event. */ + MYSQL_LEX_CSTRING object; + /** Requested authorization privileges. */ + unsigned long requested_privilege; + /** Currently granted authorization privileges. */ + unsigned long granted_privilege; +}; + +/** + @enum mysql_event_table_row_access_subclass_t + + Events for MYSQL_AUDIT_TABLE_ACCES_CLASS event class. +*/ +typedef enum +{ + /** Occurs when table data are read. */ + MYSQL_AUDIT_TABLE_ACCESS_READ = 1 << 0, + /** Occurs when table data are inserted. */ + MYSQL_AUDIT_TABLE_ACCESS_INSERT = 1 << 1, + /** Occurs when table data are updated. */ + MYSQL_AUDIT_TABLE_ACCESS_UPDATE = 1 << 2, + /** Occurs when table data are deleted. */ + MYSQL_AUDIT_TABLE_ACCESS_DELETE = 1 << 3 +} mysql_event_table_access_subclass_t; + +#define MYSQL_AUDIT_TABLE_ACCESS_ALL (MYSQL_AUDIT_TABLE_ACCESS_READ | \ + MYSQL_AUDIT_TABLE_ACCESS_INSERT | \ + MYSQL_AUDIT_TABLE_ACCESS_UPDATE | \ + MYSQL_AUDIT_TABLE_ACCESS_DELETE) + +/** + @struct mysql_event_table_row_access + + Structure for MYSQL_AUDIT_TABLE_ACCES_CLASS event class. +*/ +struct mysql_event_table_access +{ + /** Event subclass. */ + mysql_event_table_access_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** Database name. */ + MYSQL_LEX_CSTRING table_database; + /** Table name. */ + MYSQL_LEX_CSTRING table_name; +}; + +/** + @enum mysql_event_global_variable_subclass_t + + Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class. +*/ +typedef enum +{ + /** Occurs when global variable is retrieved. */ + MYSQL_AUDIT_GLOBAL_VARIABLE_GET = 1 << 0, + /** Occurs when global variable is set. */ + MYSQL_AUDIT_GLOBAL_VARIABLE_SET = 1 << 1 +} mysql_event_global_variable_subclass_t; + +#define MYSQL_AUDIT_GLOBAL_VARIABLE_ALL (MYSQL_AUDIT_GLOBAL_VARIABLE_GET | \ + MYSQL_AUDIT_GLOBAL_VARIABLE_SET) + +/** Events for MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS event class. */ +struct mysql_event_global_variable +{ + /** Event subclass. */ + mysql_event_global_variable_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** Variable name. */ + MYSQL_LEX_CSTRING variable_name; + /** Variable value. */ + MYSQL_LEX_CSTRING variable_value; +}; + +/** + @enum mysql_event_server_startup_subclass_t + + Events for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class. +*/ +typedef enum +{ + /** Occurs after all subsystem are initialized during system start. */ + MYSQL_AUDIT_SERVER_STARTUP_STARTUP = 1 << 0 +} mysql_event_server_startup_subclass_t; + +#define MYSQL_AUDIT_SERVER_STARTUP_ALL (MYSQL_AUDIT_SERVER_STARTUP_STARTUP) + +/** + @struct mysql_event_server_startup + + Structure for MYSQL_AUDIT_SERVER_STARTUP_CLASS event class. +*/ +struct mysql_event_server_startup +{ + /** Event subclass. */ + mysql_event_server_startup_subclass_t event_subclass; + /** Command line arguments. */ + const char **argv; + /** Command line arguments count. */ + unsigned int argc; +}; + +/** + @enum mysql_event_server_shutdown_subclass_t + + Events for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class. +*/ +typedef enum +{ + /** Occurs when global variable is set. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN = 1 << 0 +} mysql_event_server_shutdown_subclass_t; + +#define MYSQL_AUDIT_SERVER_SHUTDOWN_ALL (MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN) + +/** + @enum mysql_server_shutdown_reason_t + + Server shutdown reason. +*/ +typedef enum +{ + /** User requested shut down. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_SHUTDOWN, + /** The server aborts. */ + MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_ABORT +} mysql_server_shutdown_reason_t; + +/** + @struct mysql_event_server_shutdown + + Structure for MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS event class. +*/ +struct mysql_event_server_shutdown +{ + /** Shutdown event. */ + mysql_event_server_shutdown_subclass_t event_subclass; + /** Exit code associated with the shutdown event. */ + int exit_code; + /** Shutdown reason. */ + mysql_server_shutdown_reason_t reason; +}; + +/** + @enum mysql_event_command_subclass_t + + Events for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +typedef enum +{ + /** Command start event. */ + MYSQL_AUDIT_COMMAND_START = 1 << 0, + /** Command end event. */ + MYSQL_AUDIT_COMMAND_END = 1 << 1 +} mysql_event_command_subclass_t; + +#define MYSQL_AUDIT_COMMAND_ALL (MYSQL_AUDIT_COMMAND_START | \ + MYSQL_AUDIT_COMMAND_END) +/** + @typedef enum_server_command_t + + Server command type definition. +*/ +typedef enum enum_server_command enum_server_command_t; + +/** + @struct mysql_event_command + + Event for MYSQL_AUDIT_COMMAND_CLASS event class. + Events generated as a result of RPC command requests. +*/ +struct mysql_event_command +{ + /** Command event subclass. */ + mysql_event_command_subclass_t event_subclass; + /** Command event status. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** Command id. */ + enum_server_command_t command_id; +}; + +/** + @enum mysql_event_query_subclass_t + + Events for MYSQL_AUDIT_QUERY_CLASS event class. +*/ +typedef enum +{ + /** Query start event. */ + MYSQL_AUDIT_QUERY_START = 1 << 0, + /** Nested query start event. */ + MYSQL_AUDIT_QUERY_NESTED_START = 1 << 1, + /** Query post parse event. */ + MYSQL_AUDIT_QUERY_STATUS_END = 1 << 2, + /** Nested query status end event. */ + MYSQL_AUDIT_QUERY_NESTED_STATUS_END = 1 << 3 +} mysql_event_query_subclass_t; + +#define MYSQL_AUDIT_QUERY_ALL (MYSQL_AUDIT_QUERY_START | \ + MYSQL_AUDIT_QUERY_NESTED_START | \ + MYSQL_AUDIT_QUERY_STATUS_END | \ + MYSQL_AUDIT_QUERY_NESTED_STATUS_END) +/** + @struct mysql_event_command + + Event for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +struct mysql_event_query +{ + /** Event subclass. */ + mysql_event_query_subclass_t event_subclass; + /** Event status. */ + int status; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; +}; + +/** + @enum mysql_event_stored_program_subclass_t + + Events for MYSQL_AUDIT_STORED_PROGRAM_CLASS event class. +*/ +typedef enum +{ + /** Stored program execution event. */ + MYSQL_AUDIT_STORED_PROGRAM_EXECUTE = 1 << 0 +} mysql_event_stored_program_subclass_t; + +#define MYSQL_AUDIT_STORED_PROGRAM_ALL (MYSQL_AUDIT_STORED_PROGRAM_EXECUTE) + +/** + @struct mysql_event_command + +Event for MYSQL_AUDIT_COMMAND_CLASS event class. +*/ +struct mysql_event_stored_program +{ + /** Event subclass. */ + mysql_event_stored_program_subclass_t event_subclass; + /** Connection id. */ + unsigned long connection_id; + /** SQL command id. */ + enum_sql_command_t sql_command_id; + /** SQL query text. */ + MYSQL_LEX_CSTRING query; + /** SQL query charset. */ + const struct charset_info_st *query_charset; + /** The Database the procedure is defined in. */ + MYSQL_LEX_CSTRING database; + /** Name of the stored program. */ + MYSQL_LEX_CSTRING name; + /** Stored program parameters. */ + void *parameters; +}; + +#endif diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 1c91c66759d..d988f7e8317 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -14,13 +14,16 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define PLUGIN_VERSION 0x103 -#define PLUGIN_STR_VERSION "1.3.0" +#define PLUGIN_VERSION 0x104 +#define PLUGIN_STR_VERSION "1.4.0" + +#define _my_thread_var loc_thread_var #include <my_config.h> #include <stdio.h> #include <time.h> #include <string.h> +#include <fcntl.h> #ifndef _WIN32 #include <syslog.h> @@ -80,8 +83,7 @@ static void closelog() {} #endif /*MARIADB_ONLY*/ #include <my_base.h> -//#include <hash.h> -#include <my_dir.h> +//#include <my_dir.h> #include <typelib.h> #include <mysql/plugin.h> #include <mysql/plugin_audit.h> @@ -89,75 +91,176 @@ static void closelog() {} #define RTLD_DEFAULT NULL #endif -#undef my_init_dynamic_array_ci -#define init_dynamic_array2 loc_init_dynamic_array2 -#define my_init_dynamic_array_ci(A,B,C,D) loc_init_dynamic_array2(A,B,NULL,C,D) -#define my_hash_init2 loc_my_hash_init -#define my_hash_search loc_my_hash_search -#define my_hash_insert loc_my_hash_insert -#define my_hash_delete loc_my_hash_delete -#define my_hash_update loc_my_hash_update -#define my_hash_free loc_my_hash_free -#define my_hash_first loc_my_hash_first -#define my_hash_reset loc_my_hash_reset -#define my_hash_search_using_hash_value loc_my_hash_search_using_hash_value -#define my_hash_first_from_hash_value loc_my_hash_first_from_hash_value -#define my_hash_sort loc_my_hash_sort -#undef my_hash_first_from_hash_value -#define my_hash_first_from_hash_value loc_my_my_hash_first_from_hash_value -#define my_hash_next loc_my_hash_next -#define my_hash_element loc_my_hash_element -#define my_hash_replace loc_my_hash_replace -#define my_hash_iterate loc_my_hash_iterate - -#define alloc_dynamic loc_alloc_dynamic -#define pop_dynamic loc_pop_dynamic -#define delete_dynamic loc_delete_dynamic -void *loc_alloc_dynamic(DYNAMIC_ARRAY *array); -#ifdef my_strnncoll -#undef my_strnncoll -#define my_strnncoll(s, a, b, c, d) (my_strnncoll_binary((s), (a), (b), (c), (d), 0)) -#endif - -static int my_strnncoll_binary(CHARSET_INFO * cs __attribute__((unused)), - const uchar *s, size_t slen, - const uchar *t, size_t tlen, - my_bool t_is_prefix) -{ - size_t len= slen < tlen ? slen : tlen; - int cmp= memcmp(s,t,len); - return cmp ? cmp : (int)((t_is_prefix ? len : slen) - tlen); -} - -#include "../../mysys/array.c" -#include "../../mysys/hash.c" - #ifndef MARIADB_ONLY #undef MYSQL_SERVICE_LOGGER_INCLUDED #undef MYSQL_DYNAMIC_PLUGIN #define FLOGGER_NO_PSI /* How to access the pthread_mutex in mysql_mutex_t */ -#ifdef SAFE_MUTEX -#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex -#elif defined(MY_PTHREAD_FASTMUTEX) -#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex -#else +//#ifdef SAFE_MUTEX +//#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex +//#elif defined(MY_PTHREAD_FASTMUTEX) +//#define mysql_mutex_real_mutex(A) &(A)->m_mutex.mutex +//#else #define mysql_mutex_real_mutex(A) &(A)->m_mutex -#endif +//#endif -#define flogger_mutex_init(A,B,C) pthread_mutex_init(mysql_mutex_real_mutex(B), C) -#define flogger_mutex_destroy(A) pthread_mutex_destroy(mysql_mutex_real_mutex(A)) -#define flogger_mutex_lock(A) pthread_mutex_lock(mysql_mutex_real_mutex(A)) -#define flogger_mutex_unlock(A) pthread_mutex_unlock(mysql_mutex_real_mutex(A)) +#define flogger_mutex_init(A,B,C) do{}while(0) +#define flogger_mutex_destroy(A) do{}while(0) +#define flogger_mutex_lock(A) do{}while(0) +#define flogger_mutex_unlock(A) do{}while(0) static char **int_mysql_data_home; static char *default_home= (char *)"."; #define mysql_data_home (*int_mysql_data_home) +#define FLOGGER_SKIP_INCLUDES +#define my_open(A, B, C) loc_open(A, B) +#define my_close(A, B) loc_close(A) +#define my_rename(A, B, C) loc_rename(A, B) +#define my_tell(A, B) loc_tell(A) +#define my_write(A, B, C, D) loc_write(A, B, C) +#define my_malloc(A, B) malloc(A) +#define my_free(A) free(A) +#ifdef my_errno + #undef my_errno +#endif +static int loc_file_errno; +#define my_errno loc_file_errno +#ifdef my_vsnprintf + #undef my_vsnprintf +#endif +#define my_vsnprintf vsnprintf +#define logger_open loc_logger_open +#define logger_close loc_logger_close +#define logger_write loc_logger_write +#define logger_rotate loc_logger_rotate +#define logger_init_mutexts loc_logger_init_mutexts + + +static size_t loc_write(File Filedes, const uchar *Buffer, size_t Count) +{ + size_t writtenbytes; +#ifdef _WIN32 + writtenbytes= my_win_write(Filedes, Buffer, Count); +#else + writtenbytes= write(Filedes, Buffer, Count); +#endif + return writtenbytes; +} + + +static File loc_open(const char *FileName, int Flags) + /* Path-name of file */ + /* Read | write .. */ + /* Special flags */ +{ + File fd; +#if defined(_WIN32) + fd= my_win_open(FileName, Flags); +#elif !defined(NO_OPEN_3) + fd = open(FileName, Flags, my_umask); /* Normal unix */ +#else + fd = open((char *) FileName, Flags); +#endif + my_errno= errno; + return fd; +} + + +static int loc_close(File fd) +{ + int err; +#ifndef _WIN32 + do + { + err= close(fd); + } while (err == -1 && errno == EINTR); +#else + err= my_win_close(fd); +#endif + my_errno=errno; + return err; +} + + +static int loc_rename(const char *from, const char *to) +{ + int error = 0; + +#if defined(__WIN__) + if (!MoveFileEx(from, to, MOVEFILE_COPY_ALLOWED | + MOVEFILE_REPLACE_EXISTING)) + { + my_osmaperr(GetLastError()); +#elif defined(HAVE_RENAME) + if (rename(from,to)) + { +#else + if (link(from, to) || unlink(from)) + { +#endif + my_errno=errno; + error = -1; + } + return error; +} + + +static my_off_t loc_seek(File fd, my_off_t pos, int whence) +{ + os_off_t newpos= -1; +#ifdef _WIN32 + newpos= my_win_lseek(fd, pos, whence); +#else + newpos= lseek(fd, pos, whence); +#endif + if (newpos == (os_off_t) -1) + { + my_errno= errno; + return MY_FILEPOS_ERROR; + } + + return (my_off_t) newpos; +} + + +static my_off_t loc_tell(File fd) +{ + os_off_t pos; +#if defined (HAVE_TELL) && !defined (_WIN32) + pos= tell(fd); +#else + pos= loc_seek(fd, 0L, MY_SEEK_CUR); +#endif + if (pos == (os_off_t) -1) + { + my_errno= errno; + } + return (my_off_t) pos; +} + +#ifdef HAVE_PSI_INTERFACE +#undef HAVE_PSI_INTERFACE +#include <mysql/service_logger.h> +#include "../../mysys/file_logger.c" +#define HAVE_PSI_INTERFACE +#else +#include <mysql/service_logger.h> #include "../../mysys/file_logger.c" +#endif #endif /*!MARIADB_ONLY*/ +#undef flogger_mutex_init +#undef flogger_mutex_destroy +#undef flogger_mutex_lock +#undef flogger_mutex_unlock + +#define flogger_mutex_init(A,B,C) pthread_mutex_init(mysql_mutex_real_mutex(B), C) +#define flogger_mutex_destroy(A) pthread_mutex_destroy(mysql_mutex_real_mutex(A)) +#define flogger_mutex_lock(A) pthread_mutex_lock(mysql_mutex_real_mutex(A)) +#define flogger_mutex_unlock(A) pthread_mutex_unlock(mysql_mutex_real_mutex(A)) + #ifndef DBUG_OFF #define PLUGIN_DEBUG_VERSION "-debug" #else @@ -178,7 +281,11 @@ static char *default_home= (char *)"."; extern char server_version[]; static const char *serv_ver= NULL; static int started_mysql= 0; +static int mysql_57_started= 0; +static int debug_server_started= 0; +static int use_event_data_for_disconnect= 0; static int started_mariadb= 0; +static int maria_55_started= 0; static int maria_above_5= 0; static char *incl_users, *excl_users, *file_path, *syslog_info; @@ -203,6 +310,27 @@ static char servhost[256]; static size_t servhost_len; static char *syslog_ident; static char syslog_ident_buffer[128]= "mysql-server_auditing"; + +struct connection_info +{ + int header; + unsigned long thread_id; + unsigned long long query_id; + char db[256]; + int db_length; + char user[64]; + int user_length; + char host[64]; + int host_length; + char ip[64]; + int ip_length; + const char *query; + int query_length; + char query_buffer[1024]; + time_t query_time; + int log_always; +}; + #define DEFAULT_FILENAME_LEN 16 static char default_file_name[DEFAULT_FILENAME_LEN+1]= "server_audit.log"; @@ -256,7 +384,8 @@ static TYPELIB events_typelib= array_elements(event_names) - 1, "", event_names, NULL }; static MYSQL_SYSVAR_SET(events, events, PLUGIN_VAR_RQCMDARG, - "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE, QUERY_DDL, QUERY_DML.", + "Specifies the set of events to monitor. Can be CONNECT, QUERY, TABLE," + " QUERY_DDL, QUERY_DML, QUERY_DCL.", NULL, NULL, 0, &events_typelib); #define OUTPUT_SYSLOG 0 #define OUTPUT_FILE 1 @@ -297,6 +426,13 @@ static MYSQL_SYSVAR_UINT(query_log_limit, query_log_limit, PLUGIN_VAR_OPCMDARG, "Limit on the length of the query string in a record.", NULL, NULL, 1024, 0, 0x7FFFFFFF, 1); +char locinfo_ini_value[sizeof(struct connection_info)+4]; + +static MYSQL_THDVAR_STR(loc_info, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, + "Auxiliary info.", NULL, NULL, + locinfo_ini_value); + static const char *syslog_facility_names[]= { "LOG_USER", "LOG_MAIL", "LOG_DAEMON", "LOG_AUTH", @@ -376,6 +512,7 @@ static struct st_mysql_sys_var* vars[] = { MYSQL_SYSVAR(syslog_facility), MYSQL_SYSVAR(syslog_priority), MYSQL_SYSVAR(query_log_limit), + MYSQL_SYSVAR(loc_info), NULL }; @@ -386,6 +523,8 @@ static long log_write_failures= 0; static char current_log_buf[FN_REFLEN]= ""; static char last_error_buf[512]= ""; +extern void *mysql_v4_descriptor; + static struct st_mysql_show_var audit_status[]= { {"server_audit_active", (char *)&is_active, SHOW_BOOL}, @@ -425,7 +564,7 @@ static uchar *getkey_user(const char *entry, size_t *length, } -static void blank_user(uchar *user) +static void blank_user(char *user) { for (; *user && *user != ','; user++) *user= ' '; @@ -485,19 +624,98 @@ static void remove_blanks(char *user) } -static int user_hash_fill(HASH *h, char *users, - HASH *cmp_hash, int take_over_cmp) +struct user_name +{ + int name_len; + char *name; +}; + + +struct user_coll +{ + int n_users; + struct user_name *users; + int n_alloced; +}; + + +static void coll_init(struct user_coll *c) +{ + c->n_users= 0; + c->users= 0; + c->n_alloced= 0; +} + + +static void coll_free(struct user_coll *c) +{ + if (c->users) + { + free(c->users); + coll_init(c); + } +} + + +static int cmp_users(const void *ia, const void *ib) +{ + const struct user_name *a= (const struct user_name *) ia; + const struct user_name *b= (const struct user_name *) ib; + int dl= a->name_len - b->name_len; + if (dl != 0) + return dl; + + return strncmp(a->name, b->name, a->name_len); +} + + +static char *coll_search(struct user_coll *c, const char *n, int len) +{ + struct user_name un; + struct user_name *found; + un.name_len= len; + un.name= (char *) n; + found= (struct user_name*) bsearch(&un, c->users, c->n_users, + sizeof(c->users[0]), cmp_users); + return found ? found->name : 0; +} + + +static int coll_insert(struct user_coll *c, char *n, int len) +{ + if (c->n_users >= c->n_alloced) + { + c->n_alloced+= 128; + if (c->users == NULL) + c->users= malloc(c->n_alloced * sizeof(c->users[0])); + else + c->users= realloc(c->users, c->n_alloced * sizeof(c->users[0])); + + if (c->users == NULL) + return 1; + } + c->users[c->n_users].name= n; + c->users[c->n_users].name_len= len; + c->n_users++; + return 0; +} + + +static void coll_sort(struct user_coll *c) +{ + qsort(c->users, c->n_users, sizeof(c->users[0]), cmp_users); +} + + +static int user_coll_fill(struct user_coll *c, char *users, + struct user_coll *cmp_c, int take_over_cmp) { char *orig_users= users; - uchar *cmp_user= 0; + char *cmp_user= 0; size_t cmp_length; - int refill_cmp_hash= 0; + int refill_cmp_coll= 0; - if (my_hash_inited(h)) - my_hash_reset(h); - else - loc_my_hash_init(h, 0, &my_charset_bin, 0x100, 0, 0, - (my_hash_get_key) getkey_user, 0, 0, 0); + c->n_users= 0; while (*users) { @@ -506,11 +724,10 @@ static int user_hash_fill(HASH *h, char *users, if (!*users) return 0; - - if (cmp_hash) + (void) getkey_user(users, &cmp_length, FALSE); + if (cmp_c) { - (void) getkey_user(users, &cmp_length, FALSE); - cmp_user= my_hash_search(cmp_hash, (const uchar *) users, cmp_length); + cmp_user= coll_search(cmp_c, users, cmp_length); if (cmp_user && take_over_cmp) { @@ -520,7 +737,7 @@ static int user_hash_fill(HASH *h, char *users, MYF(ME_JUST_WARNING), (int) cmp_length, users); internal_stop_logging= 0; blank_user(cmp_user); - refill_cmp_hash= 1; + refill_cmp_coll= 1; } else if (cmp_user) { @@ -532,7 +749,7 @@ static int user_hash_fill(HASH *h, char *users, continue; } } - if (my_hash_insert(h, (const uchar *) users)) + if (coll_insert(c, users, cmp_length)) return 1; while (*users && *users != ',') users++; @@ -541,15 +758,17 @@ static int user_hash_fill(HASH *h, char *users, users++; } - if (refill_cmp_hash) + if (refill_cmp_coll) { remove_blanks(excl_users); - return user_hash_fill(cmp_hash, excl_users, 0, 0); + return user_coll_fill(cmp_c, excl_users, 0, 0); } if (users > orig_users && users[-1] == ',') users[-1]= 0; + coll_sort(c); + return 0; } @@ -678,48 +897,19 @@ static void error_header() static LOGGER_HANDLE *logfile; -static HASH incl_user_hash, excl_user_hash; +static struct user_coll incl_user_coll, excl_user_coll; static unsigned long long query_counter= 1; -struct connection_info -{ - unsigned long thread_id; - unsigned long long query_id; - char db[256]; - int db_length; - char user[64]; - int user_length; - char host[64]; - int host_length; - char ip[64]; - int ip_length; - const char *query; - int query_length; - char query_buffer[1024]; - time_t query_time; - int log_always; -}; - -static HASH connection_hash; - - -struct connection_info *alloc_connection() -{ - return malloc(ALIGN_SIZE(sizeof(struct connection_info))); -} - - -void free_connection(void* pconn) +static struct connection_info *get_loc_info(MYSQL_THD thd) { - (void) free(pconn); + return (struct connection_info *) THDVAR(thd, loc_info); } -static struct connection_info *find_connection(unsigned long id) +static int ci_needs_setup(const struct connection_info *ci) { - return (struct connection_info *) - my_hash_search(&connection_hash, (const uchar *) &id, sizeof(id)); + return ci->header != 0; } @@ -883,15 +1073,24 @@ static int stop_logging() return 0; } -static struct connection_info * - add_connection(const struct mysql_event_connection *event) + +static void setup_connection_simple(struct connection_info *ci) +{ + ci->db_length= 0; + ci->user_length= 0; + ci->host_length= 0; + ci->ip_length= 0; + ci->query_length= 0; + ci->header= 0; +} + + +static void setup_connection_connect(struct connection_info *cn, + const struct mysql_event_connection *event) { - struct connection_info *cn= alloc_connection(); - if (!cn) - return 0; - cn->thread_id= event->thread_id; cn->query_id= 0; cn->log_always= 0; + cn->thread_id= event->thread_id; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), event->database, event->database_length); get_str_n(cn->user, &cn->user_length, sizeof(cn->db), @@ -900,11 +1099,7 @@ static struct connection_info * event->host, event->host_length); get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), event->ip, event->ip_length); - - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + cn->header= 0; } @@ -927,46 +1122,43 @@ do { \ -static struct connection_info * - add_connection_initdb(const struct mysql_event_general *event) +static void setup_connection_initdb(struct connection_info *cn, + const struct mysql_event_general *event) { - struct connection_info *cn; size_t user_len, host_len, ip_len; char uh_buffer[512]; - if (get_user_host(event->general_user, event->general_user_length, - uh_buffer, sizeof(uh_buffer), - &user_len, &host_len, &ip_len) || - (cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->general_thread_id; cn->query_id= 0; cn->log_always= 0; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), event->general_query, event->general_query_length); - get_str_n(cn->user, &cn->user_length, sizeof(cn->db), - uh_buffer, user_len); - get_str_n(cn->host, &cn->host_length, sizeof(cn->host), - uh_buffer+user_len+1, host_len); - get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), - uh_buffer+user_len+1+host_len+1, ip_len); - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + if (get_user_host(event->general_user, event->general_user_length, + uh_buffer, sizeof(uh_buffer), + &user_len, &host_len, &ip_len)) + { + /* The user@host line is incorrect. */ + cn->user_length= 0; + cn->host_length= 0; + cn->ip_length= 0; + } + else + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->db), + uh_buffer, user_len); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + uh_buffer+user_len+1, host_len); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + uh_buffer+user_len+1+host_len+1, ip_len); + } + cn->header= 0; } -static struct connection_info * - add_connection_table(const struct mysql_event_table *event) +static void setup_connection_table(struct connection_info *cn, + const struct mysql_event_table *event) { - struct connection_info *cn; - - if ((cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->thread_id; cn->query_id= query_counter++; cn->log_always= 0; @@ -978,42 +1170,40 @@ static struct connection_info * event->host, SAFE_STRLEN(event->host)); get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), event->ip, SAFE_STRLEN(event->ip)); - - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - - return cn; + cn->header= 0; } -static struct connection_info * - add_connection_query(const struct mysql_event_general *event) +static void setup_connection_query(struct connection_info *cn, + const struct mysql_event_general *event) { - struct connection_info *cn; size_t user_len, host_len, ip_len; char uh_buffer[512]; - if (get_user_host(event->general_user, event->general_user_length, - uh_buffer, sizeof(uh_buffer), - &user_len, &host_len, &ip_len) || - (cn= alloc_connection()) == NULL) - return 0; - cn->thread_id= event->general_thread_id; cn->query_id= query_counter++; cn->log_always= 0; get_str_n(cn->db, &cn->db_length, sizeof(cn->db), "", 0); - get_str_n(cn->user, &cn->user_length, sizeof(cn->db), - uh_buffer, user_len); - get_str_n(cn->host, &cn->host_length, sizeof(cn->host), - uh_buffer+user_len+1, host_len); - get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), - uh_buffer+user_len+1+host_len+1, ip_len); - - if (my_hash_insert(&connection_hash, (const uchar *) cn)) - return 0; - return cn; + if (get_user_host(event->general_user, event->general_user_length, + uh_buffer, sizeof(uh_buffer), + &user_len, &host_len, &ip_len)) + { + /* The user@host line is incorrect. */ + cn->user_length= 0; + cn->host_length= 0; + cn->ip_length= 0; + } + else + { + get_str_n(cn->user, &cn->user_length, sizeof(cn->db), + uh_buffer, user_len); + get_str_n(cn->host, &cn->host_length, sizeof(cn->host), + uh_buffer+user_len+1, host_len); + get_str_n(cn->ip, &cn->ip_length, sizeof(cn->ip), + uh_buffer+user_len+1+host_len+1, ip_len); + } + cn->header= 0; } @@ -1105,6 +1295,27 @@ static int log_connection(const struct connection_info *cn, } +static int log_connection_event(const struct mysql_event_connection *event, + const char *type) +{ + time_t ctime; + size_t csize; + char message[1024]; + + (void) time(&ctime); + csize= log_header(message, sizeof(message)-1, &ctime, + servhost, servhost_len, + event->user, event->user_length, + event->host, event->host_length, + event->ip, event->ip_length, + event->thread_id, 0, type); + csize+= my_snprintf(message+csize, sizeof(message) - 1 - csize, + ",%.*s,,%d", event->database_length, event->database, event->status); + message[csize]= '\n'; + return write_log(message, csize + 1); +} + + static size_t escape_string(const char *str, unsigned int len, char *result, size_t result_len) { @@ -1250,11 +1461,11 @@ static int do_log_user(const char *name) return 0; len= strlen(name); - if (incl_user_hash.records) - return my_hash_search(&incl_user_hash, (const uchar *) name, len) != 0; + if (incl_user_coll.n_users) + return coll_search(&incl_user_coll, name, len) != 0; - if (excl_user_hash.records) - return my_hash_search(&excl_user_hash, (const uchar *) name, len) == 0; + if (excl_user_coll.n_users) + return coll_search(&excl_user_coll, name, len) == 0; return 1; } @@ -1592,14 +1803,14 @@ static void update_general_user(struct connection_info *cn, } +static struct connection_info ci_disconnect_buffer; + #define AA_FREE_CONNECTION 1 #define AA_CHANGE_USER 2 -static struct connection_info *update_connection_hash(unsigned int event_class, - const void *ev, - int *after_action) +static void update_connection_info(struct connection_info *cn, + unsigned int event_class, const void *ev, int *after_action) { - struct connection_info *cn= NULL; *after_action= 0; switch (event_class) { @@ -1612,13 +1823,17 @@ static struct connection_info *update_connection_hash(unsigned int event_class, { int init_db_command= event->general_command_length == 7 && strncmp(event->general_command, "Init DB", 7) == 0; - if ((cn= find_connection(event->general_thread_id))) + if (!ci_needs_setup(cn)) { if (init_db_command) { /* Change DB */ - get_str_n(cn->db, &cn->db_length, sizeof(cn->db), - event->general_query, event->general_query_length); + if (mysql_57_started) + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->database, event->database_length); + else + get_str_n(cn->db, &cn->db_length, sizeof(cn->db), + event->general_query, event->general_query_length); } cn->query_id= mode ? query_counter++ : event->query_id; cn->query= event->general_query; @@ -1627,18 +1842,19 @@ static struct connection_info *update_connection_hash(unsigned int event_class, update_general_user(cn, event); } else if (init_db_command) - cn= add_connection_initdb(event); + setup_connection_initdb(cn, event); else if (event_query_command(event)) - cn= add_connection_query(event); + setup_connection_query(cn, event); + else + setup_connection_simple(cn); break; } case MYSQL_AUDIT_GENERAL_STATUS: if (event_query_command(event)) { - if (!(cn= find_connection(event->general_thread_id)) && - !(cn= add_connection_query(event))) - return 0; + if (ci_needs_setup(cn)) + setup_connection_query(cn, event); if (mode == 0 && cn->db_length == 0 && event->database_length > 0) get_str_n(cn->db, &cn->db_length, sizeof(cn->db), @@ -1664,13 +1880,13 @@ static struct connection_info *update_connection_hash(unsigned int event_class, } break; case MYSQL_AUDIT_GENERAL_ERROR: - /* We need this because of a bug in the MariaDB */ - /* that it returns NULL query field for the */ - /* MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare. */ - /* As a result we get empty QUERY field for errors. */ - if (!(cn= find_connection(event->general_thread_id)) && - !(cn= add_connection_query(event))) - return 0; + /* + We need this because the MariaDB returns NULL query field for the + MYSQL_AUDIT_GENERAL_STATUS in the mysqld_stmt_prepare. + As a result we get empty QUERY field for errors. + */ + if (ci_needs_setup(cn)) + setup_connection_query(cn, event); cn->query_id= mode ? query_counter++ : event->query_id; get_str_n(cn->query_buffer, &cn->query_length, sizeof(cn->query_buffer), event->general_query, event->general_query_length); @@ -1685,9 +1901,9 @@ static struct connection_info *update_connection_hash(unsigned int event_class, { const struct mysql_event_table *event = (const struct mysql_event_table *) ev; - if (!(cn= find_connection(event->thread_id)) && - !(cn= add_connection_table(event))) - return 0; + if (ci_needs_setup(cn)) + setup_connection_table(cn, event); + if (cn->user_length == 0 && cn->host_length == 0 && cn->ip_length == 0) { get_str_n(cn->user, &cn->user_length, sizeof(cn->user), @@ -1713,17 +1929,10 @@ static struct connection_info *update_connection_hash(unsigned int event_class, switch (event->event_subclass) { case MYSQL_AUDIT_CONNECTION_CONNECT: - cn= add_connection(ev); - break; - case MYSQL_AUDIT_CONNECTION_DISCONNECT: - cn= find_connection(event->thread_id); - if (cn) - *after_action= AA_FREE_CONNECTION; + setup_connection_connect(cn, event); break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: - cn= find_connection(event->thread_id); - if (cn) - *after_action= AA_CHANGE_USER; + *after_action= AA_CHANGE_USER; break; default:; } @@ -1732,17 +1941,17 @@ static struct connection_info *update_connection_hash(unsigned int event_class, default: break; } - return cn; } +struct connection_info cn_error_buffer; + + #define FILTER(MASK) (events == 0 || (events & MASK)) -static void auditing(MYSQL_THD thd __attribute__((unused)), - unsigned int event_class, - const void *ev) +void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev) { - struct connection_info *cn; - int after_action; + struct connection_info *cn= 0; + int after_action= 0; /* That one is important as this function can be called with */ /* &lock_operations locked when the server logs an error reported */ @@ -1752,8 +1961,35 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), flogger_mutex_lock(&lock_operations); - if (!(cn= update_connection_hash(event_class, ev, &after_action))) - goto exit_func; + if (maria_55_started && debug_server_started && + event_class == MYSQL_AUDIT_GENERAL_CLASS) + { + /* + There's a bug in MariaDB 5.5 that prevents using thread local + variables in some cases. + The 'select * from notexisting_table;' query produces such case. + So just use the static buffer in this case. + */ + const struct mysql_event_general *event = + (const struct mysql_event_general *) ev; + + if (event->event_subclass == MYSQL_AUDIT_GENERAL_ERROR || + (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event->general_query_length == 0 && + cn_error_buffer.query_id == event->query_id)) + { + cn= &cn_error_buffer; + cn->header= 1; + } + else + cn= get_loc_info(thd); + } + else + { + cn= get_loc_info(thd); + } + + update_connection_info(cn, event_class, ev, &after_action); if (!logging) goto exit_func; @@ -1767,8 +2003,11 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), /* Only one subclass is logged. */ - if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS) + if (event->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event_query_command(event)) + { log_statement(cn, event, "QUERY"); + } } else if (event_class == MYSQL_AUDIT_TABLE_CLASS && FILTER(EVENT_TABLE) && cn) { @@ -1809,7 +2048,10 @@ static void auditing(MYSQL_THD thd __attribute__((unused)), log_connection(cn, event, event->status ? "FAILED_CONNECT": "CONNECT"); break; case MYSQL_AUDIT_CONNECTION_DISCONNECT: - log_connection(cn, event, "DISCONNECT"); + if (use_event_data_for_disconnect) + log_connection_event(event, "DISCONNECT"); + else + log_connection(&ci_disconnect_buffer, event, "DISCONNECT"); break; case MYSQL_AUDIT_CONNECTION_CHANGE_USER: log_connection(cn, event, "CHANGEUSER"); @@ -1824,10 +2066,6 @@ exit_func: if (after_action) { switch (after_action) { - case AA_FREE_CONNECTION: - my_hash_delete(&connection_hash, (uchar *) cn); - cn= 0; - break; case AA_CHANGE_USER: { const struct mysql_event_connection *event = @@ -1845,28 +2083,6 @@ exit_func: } -#ifdef DBUG_OFF - #ifdef __x86_64__ -static const int cmd_off= 4200; -static const int db_off= 120; -static const int db_len_off= 128; - #else -static const int cmd_off= 2668; -static const int db_off= 60; -static const int db_len_off= 64; - #endif /*x86_64*/ -#else - #ifdef __x86_64__ -static const int cmd_off= 4432; -static const int db_off= 120; -static const int db_len_off= 128; - #else -static const int cmd_off= 2808; -static const int db_off= 64; -static const int db_len_off= 68; - #endif /*x86_64*/ -#endif /*DBUG_OFF*/ - struct mysql_event_general_v8 { unsigned int event_class; @@ -1884,8 +2100,31 @@ struct mysql_event_general_v8 unsigned long long general_rows; }; + static void auditing_v8(MYSQL_THD thd, struct mysql_event_general_v8 *ev_v8) { +#ifdef DBUG_OFF + #ifdef __x86_64__ + static const int cmd_off= 4200; + static const int db_off= 120; + static const int db_len_off= 128; + #else + static const int cmd_off= 2668; + static const int db_off= 60; + static const int db_len_off= 64; + #endif /*x86_64*/ +#else + #ifdef __x86_64__ + static const int cmd_off= 4432; + static const int db_off= 120; + static const int db_len_off= 128; + #else + static const int cmd_off= 2808; + static const int db_off= 64; + static const int db_len_off= 68; + #endif /*x86_64*/ +#endif /*DBUG_OFF*/ + struct mysql_event_general event; if (ev_v8->event_class != MYSQL_AUDIT_GENERAL_CLASS) @@ -1944,6 +2183,41 @@ static void auditing_v13(MYSQL_THD thd, unsigned int *ev_v0) } +int get_db_mysql57(MYSQL_THD thd, char **name, int *len) +{ + int db_off; + int db_len_off; + if (debug_server_started) + { +#ifdef __x86_64__ + db_off= 608; + db_len_off= 616; +#else + db_off= 0; + db_len_off= 0; +#endif /*x86_64*/ + } + else + { +#ifdef __x86_64__ + db_off= 536; + db_len_off= 544; +#else + db_off= 0; + db_len_off= 0; +#endif /*x86_64*/ + } + +#ifdef __linux__ + *name= *(char **) (((char *) thd) + db_off); + *len= *((int *) (((char*) thd) + db_len_off)); + if (*name && (*name)[*len] != 0) + return 1; + return 0; +#else + return 1; +#endif +} /* As it's just too difficult to #include "sql_class.h", let's just copy the necessary part of the system_variables @@ -2070,8 +2344,8 @@ static int server_audit_init(void *p __attribute__((unused))) flogger_mutex_init(key_LOCK_operations, &lock_operations, MY_MUTEX_INIT_FAST); flogger_mutex_init(key_LOCK_operations, &lock_bigbuffer, MY_MUTEX_INIT_FAST); - my_hash_clear(&incl_user_hash); - my_hash_clear(&excl_user_hash); + coll_init(&incl_user_coll); + coll_init(&excl_user_coll); if (incl_users) { @@ -2089,9 +2363,6 @@ static int server_audit_init(void *p __attribute__((unused))) update_excl_users(NULL, NULL, NULL, &excl_users); } - loc_my_hash_init(&connection_hash, 0, &my_charset_bin, 0x100, 0, - sizeof(unsigned long), 0, 0, free_connection, 0); - error_header(); fprintf(stderr, "MariaDB Audit Plugin version %s%s STARTED.\n", PLUGIN_STR_VERSION, PLUGIN_DEBUG_VERSION); @@ -2115,6 +2386,16 @@ static int server_audit_init(void *p __attribute__((unused))) } } + ci_disconnect_buffer.header= 10; + ci_disconnect_buffer.thread_id= 0; + ci_disconnect_buffer.query_id= 0; + ci_disconnect_buffer.db_length= 0; + ci_disconnect_buffer.user_length= 0; + ci_disconnect_buffer.host_length= 0; + ci_disconnect_buffer.ip_length= 0; + ci_disconnect_buffer.query= empty_str; + ci_disconnect_buffer.query_length= 0; + if (logging) start_logging(); @@ -2133,13 +2414,8 @@ static int server_audit_init_mysql(void *p) static int server_audit_deinit(void *p __attribute__((unused))) { - if (my_hash_inited(&incl_user_hash)) - my_hash_free(&incl_user_hash); - - if (my_hash_inited(&excl_user_hash)) - my_hash_free(&excl_user_hash); - - my_hash_free(&connection_hash); + coll_free(&incl_user_coll); + coll_free(&excl_user_coll); if (output_type == OUTPUT_FILE && logfile) logger_close(logfile); @@ -2174,6 +2450,7 @@ static struct st_mysql_audit mysql_descriptor = { MYSQL_AUDIT_GENERAL_CLASSMASK | MYSQL_AUDIT_CONNECTION_CLASSMASK } }; + mysql_declare_plugin(server_audit) { MYSQL_AUDIT_PLUGIN, @@ -2224,22 +2501,21 @@ maria_declare_plugin_end; static void mark_always_logged(MYSQL_THD thd) { struct connection_info *cn; - if (thd && (cn= find_connection(thd_get_thread_id(thd)))) + if (thd && (cn= get_loc_info(thd))) cn->log_always= 1; } static void log_current_query(MYSQL_THD thd) { - unsigned long thd_id; struct connection_info *cn; - if (!thd || - !(cn= find_connection((thd_id= thd_get_thread_id(thd))))) + if (!thd) return; - if (FILTER(EVENT_QUERY) && do_log_user(cn->user)) + cn= get_loc_info(thd); + if (!ci_needs_setup(cn) && FILTER(EVENT_QUERY) && do_log_user(cn->user)) { - log_statement_ex(cn, cn->query_time, thd_id, cn->query, cn->query_length, - 0, "QUERY"); + log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd), + cn->query, cn->query_length, 0, "QUERY"); cn->log_always= 1; } } @@ -2251,7 +2527,8 @@ static void update_file_path(MYSQL_THD thd, { char *new_name= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; error_header(); fprintf(stderr, "Log file name was changed to '%s'.\n", new_name); @@ -2287,7 +2564,8 @@ static void update_file_path(MYSQL_THD thd, file_path= path_buffer; exit_func: internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2331,14 +2609,16 @@ static void update_incl_users(MYSQL_THD thd, void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); mark_always_logged(thd); strncpy(incl_user_buffer, new_users, sizeof(incl_user_buffer)); incl_users= incl_user_buffer; - user_hash_fill(&incl_user_hash, incl_users, &excl_user_hash, 1); + user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1); error_header(); fprintf(stderr, "server_audit_incl_users set to '%s'.\n", incl_users); - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2347,14 +2627,16 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); mark_always_logged(thd); strncpy(excl_user_buffer, new_users, sizeof(excl_user_buffer)); excl_users= excl_user_buffer; - user_hash_fill(&excl_user_hash, excl_users, &incl_user_hash, 0); + user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0); error_header(); fprintf(stderr, "server_audit_excl_users set to '%s'.\n", excl_users); - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2430,7 +2712,8 @@ static void update_logging(MYSQL_THD thd, if (new_logging == logging) return; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; if ((logging= new_logging)) { @@ -2447,7 +2730,8 @@ static void update_logging(MYSQL_THD thd, } internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2459,14 +2743,16 @@ static void update_mode(MYSQL_THD thd __attribute__((unused)), if (mode_readonly || new_mode == mode) return; - flogger_mutex_lock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_lock(&lock_operations); internal_stop_logging= 1; mark_always_logged(thd); error_header(); fprintf(stderr, "Logging mode was changed from %d to %d.\n", mode, new_mode); mode= new_mode; internal_stop_logging= 0; - flogger_mutex_unlock(&lock_operations); + if (!maria_55_started || !debug_server_started) + flogger_mutex_unlock(&lock_operations); } @@ -2490,6 +2776,13 @@ static void update_syslog_ident(MYSQL_THD thd __attribute__((unused)), } +struct st_my_thread_var *loc_thread_var(void) +{ + return 0; +} + + + #ifdef _WIN32 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { @@ -2507,9 +2800,18 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) goto exit; started_mariadb= strstr(serv_ver, "MariaDB") != 0; + debug_server_started= strstr(serv_ver, "debug") != 0; - if (!started_mariadb) + if (started_mariadb) { + if (serv_ver[0] == '1') + use_event_data_for_disconnect= 1; + else + maria_55_started= 1; + } + else + { + /* Started MySQL. */ if (serv_ver[0] == '5' && serv_ver[2] == '5') { int sc= serv_ver[4] - '0'; @@ -2526,7 +2828,25 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) mysql_descriptor.event_notify= (void *) auditing_v13; } } + else if (serv_ver[0] == '5' && serv_ver[2] == '6') + { + int sc= serv_ver[4] - '0'; + if (serv_ver[5] >= '0' && serv_ver[5] <= '9') + sc= sc * 10 + serv_ver[5] - '0'; + if (sc >= 24) + use_event_data_for_disconnect= 1; + } + else if (serv_ver[0] == '5' && serv_ver[2] == '7') + { + mysql_57_started= 1; + _mysql_plugin_declarations_[0].info= mysql_v4_descriptor; + use_event_data_for_disconnect= 1; + } } + + memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1); + locinfo_ini_value[sizeof(locinfo_ini_value)]= 0; + exit: #ifdef _WIN32 return 1; diff --git a/plugin/server_audit/test_audit_v4.c b/plugin/server_audit/test_audit_v4.c new file mode 100644 index 00000000000..ae7527f8449 --- /dev/null +++ b/plugin/server_audit/test_audit_v4.c @@ -0,0 +1,162 @@ +#define PLUGIN_CONTEXT + +#include <stdio.h> + +typedef void *MYSQL_THD; +struct st_mysql_const_lex_string +{ + const char *str; + size_t length; +}; +typedef struct st_mysql_const_lex_string MYSQL_LEX_CSTRING; +enum enum_sql_command{ SQLCOM_A, SQLCOM_B }; +enum enum_server_command{ SERVCOM_A, SERVCOM_B }; + +#include "plugin_audit_v4.h" + +extern void auditing(MYSQL_THD thd, unsigned int event_class, const void *ev); +extern int get_db_mysql57(MYSQL_THD thd, char **name, int *len); + + +struct mysql_event_general_302 +{ + unsigned int event_subclass; + int general_error_code; + unsigned long general_thread_id; + const char *general_user; + unsigned int general_user_length; + const char *general_command; + unsigned int general_command_length; + const char *general_query; + unsigned int general_query_length; + struct charset_info_st *general_charset; + unsigned long long general_time; + unsigned long long general_rows; + unsigned long long query_id; + char *database; + int database_length; +}; + + +static int auditing_v4(MYSQL_THD thd, mysql_event_class_t class, const void *ev) +{ + int *subclass= (int *)ev; + struct mysql_event_general_302 ev_302; + int subclass_v3, subclass_orig; + + if (class != MYSQL_AUDIT_GENERAL_CLASS && + class != MYSQL_AUDIT_CONNECTION_CLASS) + return 0; + + subclass_orig= *subclass; + + if (class == MYSQL_AUDIT_GENERAL_CLASS) + { + struct mysql_event_general *event= (struct mysql_event_general *) ev; + ev_302.general_error_code= event->general_error_code; + ev_302.general_thread_id= event->general_thread_id; + ev_302.general_user= event->general_user.str; + ev_302.general_user_length= event->general_user.length; + ev_302.general_command= event->general_command.str; + ev_302.general_command_length= event->general_command.length; + ev_302.general_query= event->general_query.str; + ev_302.general_query_length= event->general_query.length; + ev_302.general_charset= event->general_charset; + ev_302.general_time= event->general_time; + ev_302.general_rows= event->general_rows; + if (get_db_mysql57(thd, &ev_302.database, &ev_302.database_length)) + { + ev_302.database= 0; + ev_302.database_length= 0; + } + ev= &ev_302; + switch (subclass_orig) + { + case MYSQL_AUDIT_GENERAL_LOG: + subclass_v3= 0; + ev_302.event_subclass= 0; + break; + case MYSQL_AUDIT_GENERAL_ERROR: + subclass_v3= 1; + ev_302.event_subclass= 1; + break; + case MYSQL_AUDIT_GENERAL_RESULT: + subclass_v3= 2; + ev_302.event_subclass= 2; + break; + case MYSQL_AUDIT_GENERAL_STATUS: + { + subclass_v3= 3; + ev_302.event_subclass= 3; + break; + } + default: + return 0; + } + } + else /* if (class == MYSQL_AUDIT_CONNECTION_CLASS) */ + { + switch (subclass_orig) + { + case MYSQL_AUDIT_CONNECTION_CONNECT: + subclass_v3= 0; + break; + case MYSQL_AUDIT_CONNECTION_DISCONNECT: + subclass_v3= 1; + break; + default: + return 0; + } + } + + *subclass= subclass_v3; + + auditing(thd, (int) class, ev); + + *subclass= subclass_orig; + return 0; +} + + +static struct st_mysql_audit mysql_descriptor = +{ + MYSQL_AUDIT_INTERFACE_VERSION, + NULL, + auditing_v4, + { (unsigned long) MYSQL_AUDIT_GENERAL_ALL, + (unsigned long) MYSQL_AUDIT_CONNECTION_ALL, + (unsigned long) MYSQL_AUDIT_PARSE_ALL, + 0, /* This event class is currently not supported. */ + 0, /* This event class is currently not supported. */ + (unsigned long) MYSQL_AUDIT_GLOBAL_VARIABLE_ALL, + (unsigned long) MYSQL_AUDIT_SERVER_STARTUP_ALL, + (unsigned long) MYSQL_AUDIT_SERVER_SHUTDOWN_ALL, + (unsigned long) MYSQL_AUDIT_COMMAND_ALL, + (unsigned long) MYSQL_AUDIT_QUERY_ALL, + (unsigned long) MYSQL_AUDIT_STORED_PROGRAM_ALL } +#ifdef WHEN_MYSQL_BUG_FIXED + /* + By this moment MySQL just sends no notifications at all + when we request only those we actually need. + So we have to request everything and filter them inside the + handling function. + */ + { (unsigned long) MYSQL_AUDIT_GENERAL_ALL, + (unsigned long) (MYSQL_AUDIT_CONNECTION_CONNECT | + MYSQL_AUDIT_CONNECTION_DISCONNECT), + 0, + 0, /* This event class is currently not supported. */ + 0, /* This event class is currently not supported. */ + 0, + 0, + 0, + 0, + 0, + 0 + } +#endif /*WHEN_MYSQL_BUG_FIXED*/ +}; + + +void *mysql_v4_descriptor= &mysql_descriptor; + |