diff options
author | Sergey Vojtovich <svoj@sun.com> | 2010-01-19 17:30:55 +0400 |
---|---|---|
committer | Sergey Vojtovich <svoj@sun.com> | 2010-01-19 17:30:55 +0400 |
commit | 667b5944a7a28dcefdd725d360b6631bd74aedc8 (patch) | |
tree | 667b5c726df9ea2c6435bb9a13fad521165d32ec /sql | |
parent | a6165accb3279fe64b672fa0eeaa30ef12cf6557 (diff) | |
parent | 9130563708cbdbb488e5361af79eb9f4b9dab0ea (diff) | |
download | mariadb-git-667b5944a7a28dcefdd725d360b6631bd74aedc8.tar.gz |
Merge backport of WL#3771 with mysql-next-mr.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 2 | ||||
-rw-r--r-- | sql/Makefile.am | 4 | ||||
-rw-r--r-- | sql/event_queue.cc | 8 | ||||
-rw-r--r-- | sql/ha_ndbcluster.cc | 12 | ||||
-rw-r--r-- | sql/log.cc | 17 | ||||
-rw-r--r-- | sql/mysqld.cc | 37 | ||||
-rw-r--r-- | sql/sql_audit.cc | 477 | ||||
-rw-r--r-- | sql/sql_audit.h | 131 | ||||
-rw-r--r-- | sql/sql_class.cc | 5 | ||||
-rw-r--r-- | sql/sql_class.h | 15 | ||||
-rw-r--r-- | sql/sql_connect.cc | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 3 | ||||
-rw-r--r-- | sql/sql_parse.cc | 4 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 58 |
14 files changed, 712 insertions, 63 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 82a94b64365..99b1a3dc206 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -73,7 +73,7 @@ SET (SQL_SOURCE event_queue.cc event_db_repository.cc sql_tablespace.cc events.cc ../sql-common/my_user.c partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc - rpl_rli.cc rpl_mi.cc sql_servers.cc + rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc sql_connect.cc scheduler.cc sql_profile.cc event_parse_data.cc sql_signal.cc rpl_handler.cc sys_vars.cc diff --git a/sql/Makefile.am b/sql/Makefile.am index 5f8bc8ef112..e22de4931db 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -111,6 +111,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ sql_plugin.h authors.h event_parse_data.h \ event_data_objects.h event_scheduler.h \ sql_partition.h partition_info.h partition_element.h \ + sql_audit.h \ contributors.h sql_servers.h sql_signal.h records.h \ sql_prepare.h rpl_handler.h replication.h sys_vars.h @@ -157,7 +158,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ event_queue.cc event_db_repository.cc events.cc \ sql_plugin.cc sql_binlog.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc \ - sql_servers.cc event_parse_data.cc sql_signal.cc \ + sql_servers.cc sql_audit.cc \ + event_parse_data.cc sql_signal.cc \ rpl_handler.cc nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c diff --git a/sql/event_queue.cc b/sql/event_queue.cc index cf63ba937e0..225029040b0 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -16,6 +16,7 @@ #include "mysql_priv.h" #include "event_queue.h" #include "event_data_objects.h" +#include "sql_audit.h" /** @addtogroup Event_Scheduler @@ -581,6 +582,9 @@ Event_queue::get_top_for_execution_if_time(THD *thd, /* There are no events in the queue */ next_activation_at= 0; + /* Release any held audit resources before waiting */ + mysql_audit_release(thd); + /* Wait on condition until signaled. Release LOCK_queue while waiting. */ cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__); @@ -600,6 +604,10 @@ Event_queue::get_top_for_execution_if_time(THD *thd, */ struct timespec top_time; set_timespec(top_time, next_activation_at - thd->query_start()); + + /* Release any held audit resources before waiting */ + mysql_audit_release(thd); + cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__); continue; diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index b254d5d387c..63716e8960e 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -7466,7 +7466,6 @@ static int connect_callback() } extern int ndb_dictionary_is_mysqld; -extern mysql_mutex_t LOCK_plugin; #ifdef HAVE_PSI_INTERFACE @@ -7559,13 +7558,6 @@ static int ndbcluster_init(void *p) init_ndbcluster_psi_keys(); #endif - /* - Below we create new THD's. They'll need LOCK_plugin, but it's taken now by - plugin initialization code. Release it to avoid deadlocks. It's safe, as - there're no threads that may concurrently access plugin control structures. - */ - mysql_mutex_unlock(&LOCK_plugin); - mysql_mutex_init(key_ndbcluster_mutex, &ndbcluster_mutex, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_ndb_util_thread, @@ -7722,8 +7714,6 @@ static int ndbcluster_init(void *p) goto ndbcluster_init_error; } - mysql_mutex_lock(&LOCK_plugin); - ndbcluster_inited= 1; DBUG_RETURN(FALSE); @@ -7736,8 +7726,6 @@ ndbcluster_init_error: g_ndb_cluster_connection= NULL; ndbcluster_hton->state= SHOW_OPTION_DISABLED; // If we couldn't use handler - mysql_mutex_lock(&LOCK_plugin); - DBUG_RETURN(TRUE); } diff --git a/sql/log.cc b/sql/log.cc index 7776b6bfbdc..81f8b01ba3c 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -28,6 +28,7 @@ #include "sql_repl.h" #include "rpl_filter.h" #include "rpl_rli.h" +#include "sql_audit.h" #include <my_dir.h> #include <stdarg.h> @@ -42,7 +43,6 @@ /* max size of the log message */ #define MAX_LOG_BUFFER_SIZE 1024 -#define MAX_USER_HOST_SIZE 512 #define MAX_TIME_SIZE 32 #define MY_OFF_T_UNDEF (~(my_off_t)0UL) @@ -1069,7 +1069,6 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, bool error= FALSE; Log_event_handler **current_handler= general_log_handler_list; char user_host_buff[MAX_USER_HOST_SIZE + 1]; - Security_context *sctx= thd->security_ctx; uint user_host_len= 0; time_t current_time; @@ -1081,14 +1080,16 @@ bool LOGGER::general_log_write(THD *thd, enum enum_server_command command, unlock(); return 0; } - user_host_len= strxnmov(user_host_buff, MAX_USER_HOST_SIZE, - sctx->priv_user ? sctx->priv_user : "", "[", - sctx->user ? sctx->user : "", "] @ ", - sctx->host ? sctx->host : "", " [", - sctx->ip ? sctx->ip : "", "]", NullS) - - user_host_buff; + user_host_len= make_user_name(thd, user_host_buff); current_time= my_time(0); + + mysql_audit_general_log(thd, current_time, + user_host_buff, user_host_len, + command_name[(uint) command].str, + command_name[(uint) command].length, + query, query_length); + while (*current_handler) error|= (*current_handler++)-> log_general(thd, current_time, user_host_buff, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e424735af1e..f7b86653908 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -26,6 +26,7 @@ #include "mysqld_suffix.h" #include "mysys_err.h" #include "events.h" +#include "sql_audit.h" #include "probes_mysql.h" #include "debug_sync.h" @@ -954,6 +955,7 @@ static void close_server_sock(); static void clean_up_mutexes(void); static void wait_for_signal_thread_to_end(void); static void create_pid_file(); +static void mysqld_exit(int exit_code) __attribute__((noreturn)); static void end_ssl(); #endif @@ -1364,6 +1366,7 @@ void unireg_end(void) #endif } + extern "C" void unireg_abort(int exit_code) { DBUG_ENTER("unireg_abort"); @@ -1374,15 +1377,25 @@ extern "C" void unireg_abort(int exit_code) sql_print_error("Aborting\n"); clean_up(!opt_help && (exit_code || !opt_bootstrap)); /* purecov: inspected */ DBUG_PRINT("quit",("done with cleanup in unireg_abort")); + mysqld_exit(exit_code); +} + +static void mysqld_exit(int exit_code) +{ + /* + Important note: we wait for the signal thread to end, + but if a kill -15 signal was sent, the signal thread did + spawn the kill_server_thread thread, which is running concurrently. + */ wait_for_signal_thread_to_end(); + mysql_audit_finalize(); clean_up_mutexes(); clean_up_error_log_mutex(); my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(exit_code); /* purecov: inspected */ } -#endif /*EMBEDDED_LIBRARY*/ - +#endif /* !EMBEDDED_LIBRARY */ void clean_up(bool print_message) { @@ -3023,6 +3036,8 @@ void my_message_sql(uint error, const char *str, myf MyFlags) error= ER_UNKNOWN_ERROR; } + mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_ERROR, error, str); + if (thd) { if (MyFlags & ME_FATALERROR) @@ -4568,6 +4583,9 @@ int main(int argc, char **argv) thr_kill_signal= SIGINT; #endif + /* Initialize audit interface globals. Audit plugins are inited later. */ + mysql_audit_initialize(); + /* Perform basic logger initialization logger. Should be called after MY_INIT, as it initializes mutexes. Log tables are inited later. @@ -4860,14 +4878,6 @@ int main(int argc, char **argv) } #endif clean_up(1); - /* - Important note: we wait for the signal thread to end, - but if a kill -15 signal was sent, the signal thread did - spawn the kill_server_thread thread, which is running concurrently. - */ - wait_for_signal_thread_to_end(); - clean_up_mutexes(); - clean_up_error_log_mutex(); #ifdef HAVE_PSI_INTERFACE /* Disable the instrumentation, to avoid recording events @@ -4880,13 +4890,10 @@ int main(int argc, char **argv) } shutdown_performance_schema(); #endif - my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); - - exit(0); - return(0); /* purecov: deadcode */ + mysqld_exit(0); } -#endif /* EMBEDDED_LIBRARY */ +#endif /* !EMBEDDED_LIBRARY */ /**************************************************************************** diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc new file mode 100644 index 00000000000..5190cba64de --- /dev/null +++ b/sql/sql_audit.cc @@ -0,0 +1,477 @@ +/* Copyright (C) 2007 MySQL AB, 2008-2009 Sun Microsystems, Inc + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysql_priv.h" +#include "sql_audit.h" + +extern int initialize_audit_plugin(st_plugin_int *plugin); +extern int finalize_audit_plugin(st_plugin_int *plugin); + +#ifndef EMBEDDED_LIBRARY + +unsigned long mysql_global_audit_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + +static mysql_mutex_t LOCK_audit_mask; + +static void event_class_dispatch(THD *thd, const struct mysql_event *event); + + +static inline +void set_audit_mask(unsigned long *mask, uint event_class) +{ + mask[0]= 1; + mask[0]<<= event_class; +} + +static inline +void add_audit_mask(unsigned long *mask, const unsigned long *rhs) +{ + mask[0]|= rhs[0]; +} + +static inline +bool check_audit_mask(const unsigned long *lhs, + const unsigned long *rhs) +{ + return !(lhs[0] & rhs[0]); +} + + +typedef void (*audit_handler_t)(THD *thd, uint event_subtype, va_list ap); + +/** + MYSQL_AUDIT_GENERAL_CLASS handler + + @param[in] thd + @param[in] event_subtype + @param[in] error_code + @param[in] ap + +*/ + +static void general_class_handler(THD *thd, uint event_subtype, va_list ap) +{ + mysql_event_general event; + event.event_class= MYSQL_AUDIT_GENERAL_CLASS; + event.general_error_code= va_arg(ap, int); + event.general_thread_id= thd ? thd->thread_id : 0; + event.general_time= va_arg(ap, time_t); + event.general_user= va_arg(ap, const char *); + event.general_user_length= va_arg(ap, unsigned int); + event.general_command= va_arg(ap, const char *); + event.general_command_length= va_arg(ap, unsigned int); + event.general_query= va_arg(ap, const char *); + event.general_query_length= va_arg(ap, unsigned int); + event.general_charset= va_arg(ap, struct charset_info_st *); + event.general_rows= (unsigned long long) va_arg(ap, ha_rows); + event_class_dispatch(thd, (const mysql_event*) &event); +} + + +static audit_handler_t audit_handlers[] = +{ + general_class_handler +}; + +static const uint audit_handlers_count= + (sizeof(audit_handlers) / sizeof(audit_handler_t)); + + +/** + Acquire and lock any additional audit plugins as required + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE Always +*/ + +static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg) +{ + uint event_class= *(uint*) arg; + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *); + + set_audit_mask(event_class_mask, event_class); + + /* Check if this plugin is interested in the event */ + if (check_audit_mask(data->class_mask, event_class_mask)) + return 0; + + /* + Check if this plugin may already be registered. This will fail to + acquire a newly installed plugin on a specific corner case where + one or more event classes already in use by the calling thread + are an event class of which the audit plugin has interest. + */ + if (!check_audit_mask(data->class_mask, thd->audit_class_mask)) + return 0; + + /* Check if we need to initialize the array of acquired plugins */ + if (unlikely(!thd->audit_class_plugins.buffer)) + { + /* specify some reasonable initialization defaults */ + my_init_dynamic_array(&thd->audit_class_plugins, + sizeof(plugin_ref), 16, 16); + } + + /* lock the plugin and add it to the list */ + plugin= my_plugin_lock(NULL, &plugin); + insert_dynamic(&thd->audit_class_plugins, (uchar*) &plugin); + + return 0; +} + + +/** + Notify the audit system of an event + + @param[in] thd + @param[in] event_class + @param[in] event_subtype + @param[in] error_code + +*/ + +void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...) +{ + va_list ap; + audit_handler_t *handlers= audit_handlers + event_class; + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + + DBUG_ASSERT(event_class < audit_handlers_count); + + set_audit_mask(event_class_mask, event_class); + /* + Check to see if we have acquired the audit plugins for the + required audit event classes. + */ + if (thd && check_audit_mask(thd->audit_class_mask, event_class_mask)) + { + plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, &event_class); + add_audit_mask(thd->audit_class_mask, event_class_mask); + } + + va_start(ap, event_subtype); + (*handlers)(thd, event_subtype, ap); + va_end(ap); +} + + +/** + Release any resources associated with the current thd. + + @param[in] thd + +*/ + +void mysql_audit_release(THD *thd) +{ + plugin_ref *plugins, *plugins_last; + + if (!thd || !(thd->audit_class_plugins.elements)) + return; + + plugins= (plugin_ref*) thd->audit_class_plugins.buffer; + plugins_last= plugins + thd->audit_class_plugins.elements; + for (; plugins < plugins_last; plugins++) + { + st_mysql_audit *data= plugin_data(*plugins, struct st_mysql_audit *); + + /* Check to see if the plugin has a release method */ + if (!(data->release_thd)) + continue; + + /* Tell the plugin to release its resources */ + data->release_thd(thd); + } + + /* Now we actually unlock the plugins */ + plugin_unlock_list(NULL, (plugin_ref*) thd->audit_class_plugins.buffer, + thd->audit_class_plugins.elements); + + /* Reset the state of thread values */ + reset_dynamic(&thd->audit_class_plugins); + bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask)); +} + + +/** + Initialize thd variables used by Audit + + @param[in] thd + +*/ + +void mysql_audit_init_thd(THD *thd) +{ + bzero(&thd->audit_class_plugins, sizeof(thd->audit_class_plugins)); + bzero(thd->audit_class_mask, sizeof(thd->audit_class_mask)); +} + + +/** + Free thd variables used by Audit + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE Always +*/ + +void mysql_audit_free_thd(THD *thd) +{ + mysql_audit_release(thd); + DBUG_ASSERT(thd->audit_class_plugins.elements == 0); + delete_dynamic(&thd->audit_class_plugins); +} + +#ifdef HAVE_PSI_INTERFACE +static PSI_mutex_key key_LOCK_audit_mask; + +static PSI_mutex_info all_audit_mutexes[]= +{ + { &key_LOCK_audit_mask, "LOCK_audit_mask", PSI_FLAG_GLOBAL} +}; + +static void init_audit_psi_keys(void) +{ + const char* category= "sql"; + int count; + + if (PSI_server == NULL) + return; + + count= array_elements(all_audit_mutexes); + PSI_server->register_mutex(category, all_audit_mutexes, count); +} +#endif /* HAVE_PSI_INTERFACE */ + +/** + Initialize Audit global variables +*/ + +void mysql_audit_initialize() +{ +#ifdef HAVE_PSI_INTERFACE + init_audit_psi_keys(); +#endif + + mysql_mutex_init(key_LOCK_audit_mask, &LOCK_audit_mask, MY_MUTEX_INIT_FAST); + bzero(mysql_global_audit_mask, sizeof(mysql_global_audit_mask)); +} + + +/** + Finalize Audit global variables +*/ + +void mysql_audit_finalize() +{ + mysql_mutex_destroy(&LOCK_audit_mask); +} + + +/** + Initialize an Audit plug-in + + @param[in] plugin + + @retval FALSE OK + @retval TRUE There was an error. +*/ + +int initialize_audit_plugin(st_plugin_int *plugin) +{ + st_mysql_audit *data= (st_mysql_audit*) plugin->plugin->info; + + if (!data->class_mask || !data->event_notify || + !data->class_mask[0]) + { + sql_print_error("Plugin '%s' has invalid data.", + plugin->name.str); + return 1; + } + + if (plugin->plugin->init && plugin->plugin->init(NULL)) + { + sql_print_error("Plugin '%s' init function returned error.", + plugin->name.str); + return 1; + } + + /* Make the interface info more easily accessible */ + plugin->data= plugin->plugin->info; + + /* Add the bits the plugin is interested in to the global mask */ + mysql_mutex_lock(&LOCK_audit_mask); + add_audit_mask(mysql_global_audit_mask, data->class_mask); + mysql_mutex_unlock(&LOCK_audit_mask); + + return 0; +} + + +/** + Performs a bitwise OR of the installed plugins event class masks + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE always +*/ +static my_bool calc_class_mask(THD *thd, plugin_ref plugin, void *arg) +{ + st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *); + if ((data= plugin_data(plugin, struct st_mysql_audit *))) + add_audit_mask((unsigned long *) arg, data->class_mask); + return 0; +} + + +/** + Finalize an Audit plug-in + + @param[in] plugin + + @retval FALSE OK + @retval TRUE There was an error. +*/ +int finalize_audit_plugin(st_plugin_int *plugin) +{ + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + + if (plugin->plugin->deinit && plugin->plugin->deinit(NULL)) + { + DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.", + plugin->name.str)); + DBUG_EXECUTE("finalize_audit_plugin", return 1; ); + } + + plugin->data= NULL; + bzero(&event_class_mask, sizeof(event_class_mask)); + + /* Iterate through all the installed plugins to create new mask */ + + /* + LOCK_audit_mask/LOCK_plugin order is not fixed, but serialized with table + lock on mysql.plugin. + */ + mysql_mutex_lock(&LOCK_audit_mask); + plugin_foreach(current_thd, calc_class_mask, MYSQL_AUDIT_PLUGIN, + &event_class_mask); + + /* Set the global audit mask */ + bmove(mysql_global_audit_mask, event_class_mask, sizeof(event_class_mask)); + mysql_mutex_unlock(&LOCK_audit_mask); + + return 0; +} + + +/** + Dispatches an event by invoking the plugin's event_notify method. + + @param[in] thd + @param[in] plugin + @param[in] arg + + @retval FALSE always +*/ + +static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg) +{ + const struct mysql_event *event= (const struct mysql_event *) arg; + unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; + st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *); + + set_audit_mask(event_class_mask, event->event_class); + + /* Check to see if the plugin is interested in this event */ + if (check_audit_mask(data->class_mask, event_class_mask)) + return 0; + + /* Actually notify the plugin */ + data->event_notify(thd, event); + + return 0; +} + + +/** + Distributes an audit event to plug-ins + + @param[in] thd + @param[in] event +*/ + +static void event_class_dispatch(THD *thd, const struct mysql_event *event) +{ + /* + Check if we are doing a slow global dispatch. This event occurs when + thd == NULL as it is not associated with any particular thread. + */ + if (unlikely(!thd)) + { + plugin_foreach(thd, plugins_dispatch, MYSQL_AUDIT_PLUGIN, (void*) event); + } + else + { + plugin_ref *plugins, *plugins_last; + + /* Use the cached set of audit plugins */ + plugins= (plugin_ref*) thd->audit_class_plugins.buffer; + plugins_last= plugins + thd->audit_class_plugins.elements; + + for (; plugins < plugins_last; plugins++) + plugins_dispatch(thd, *plugins, (void*) event); + } +} + + +#else /* EMBEDDED_LIBRARY */ + + +void mysql_audit_initialize() +{ +} + + +void mysql_audit_finalize() +{ +} + + +int initialize_audit_plugin(st_plugin_int *plugin) +{ + return 1; +} + + +int finalize_audit_plugin(st_plugin_int *plugin) +{ + return 0; +} + + +void mysql_audit_release(THD *thd) +{ +} + + +#endif /* EMBEDDED_LIBRARY */ diff --git a/sql/sql_audit.h b/sql/sql_audit.h new file mode 100644 index 00000000000..c25011d0d59 --- /dev/null +++ b/sql/sql_audit.h @@ -0,0 +1,131 @@ +#ifndef SQL_AUDIT_INCLUDED +#define SQL_AUDIT_INCLUDED + +/* Copyright (C) 2007 MySQL AB + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include <mysql/plugin_audit.h> + +extern unsigned long mysql_global_audit_mask[]; + + +extern void mysql_audit_initialize(); +extern void mysql_audit_finalize(); + + +extern void mysql_audit_init_thd(THD *thd); +extern void mysql_audit_free_thd(THD *thd); + + +extern void mysql_audit_notify(THD *thd, uint event_class, + uint event_subtype, ...); +extern void mysql_audit_release(THD *thd); + +#define MAX_USER_HOST_SIZE 512 +static inline uint make_user_name(THD *thd, char *buf) +{ + Security_context *sctx= thd->security_ctx; + return strxnmov(buf, MAX_USER_HOST_SIZE, + sctx->priv_user ? sctx->priv_user : "", "[", + sctx->user ? sctx->user : "", "] @ ", + sctx->host ? sctx->host : "", " [", + sctx->ip ? sctx->ip : "", "]", NullS) - buf; +} + +/** + Call audit plugins of GENERAL audit class, MYSQL_AUDIT_GENERAL_LOG subtype. + + @param[in] thd + @param[in] time time that event occurred + @param[in] user User name + @param[in] userlen User name length + @param[in] cmd Command name + @param[in] cmdlen Command name length + @param[in] query Query string + @param[in] querylen Query string length +*/ + +static inline +void mysql_audit_general_log(THD *thd, time_t time, + const char *user, uint userlen, + const char *cmd, uint cmdlen, + const char *query, uint querylen) +{ +#ifndef EMBEDDED_LIBRARY + if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK) + { + CHARSET_INFO *clientcs= thd ? thd->variables.character_set_client + : global_system_variables.character_set_client; + + mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, MYSQL_AUDIT_GENERAL_LOG, + 0, time, user, userlen, cmd, cmdlen, + query, querylen, clientcs, 0); + } +#endif +} + +/** + Call audit plugins of GENERAL audit class. + event_subtype should be set to one of: + MYSQL_AUDIT_GENERAL_ERROR + MYSQL_AUDIT_GENERAL_RESULT + + @param[in] thd + @param[in] event_subtype Type of general audit event. + @param[in] error_code Error code + @param[in] msg Message +*/ +static inline +void mysql_audit_general(THD *thd, uint event_subtype, + int error_code, const char *msg) +{ +#ifndef EMBEDDED_LIBRARY + if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK) + { + time_t time= my_time(0); + uint msglen= msg ? strlen(msg) : 0; + const char *query, *user; + uint querylen, userlen; + char user_buff[MAX_USER_HOST_SIZE]; + CHARSET_INFO *clientcs; + ha_rows rows; + + if (thd) + { + query= thd->query(); + querylen= thd->query_length(); + user= user_buff; + userlen= make_user_name(thd, user_buff); + clientcs= thd->variables.character_set_client; + rows= thd->warning_info->current_row_for_warning(); + } + else + { + query= user= 0; + querylen= userlen= 0; + clientcs= global_system_variables.character_set_client; + rows= 0; + } + + mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype, + error_code, time, user, userlen, msg, msglen, + query, querylen, clientcs, rows); + } +#endif +} + + +#endif /* SQL_AUDIT_INCLUDED */ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 2a82b0058da..60c1438b5cf 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -32,6 +32,7 @@ #include "slave.h" #include <my_bitmap.h> #include "log_event.h" +#include "sql_audit.h" #include <m_ctype.h> #include <sys/stat.h> #include <thr_alarm.h> @@ -524,6 +525,7 @@ THD::THD() dbug_sentry=THD_SENTRY_MAGIC; #endif #ifndef EMBEDDED_LIBRARY + mysql_audit_init_thd(this); net.vio=0; #endif client_capabilities= 0; // minimalistic client @@ -1041,6 +1043,7 @@ THD::~THD() cleanup(); ha_close_connection(this); + mysql_audit_release(this); plugin_thdvar_cleanup(this); DBUG_PRINT("info", ("freeing security context")); @@ -1058,6 +1061,8 @@ THD::~THD() delete rli_fake; rli_fake= NULL; } + + mysql_audit_free_thd(this); #endif free_root(&main_mem_root, MYF(0)); diff --git a/sql/sql_class.h b/sql/sql_class.h index b3cc1fc68c0..f1437f520f5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -23,6 +23,7 @@ #pragma interface /* gcc class implementation */ #endif +#include <mysql/plugin_audit.h> #include "log.h" #include "rpl_tblmap.h" @@ -1784,6 +1785,20 @@ public: partition_info *work_part_info; #endif +#ifndef EMBEDDED_LIBRARY + /** + Array of active audit plugins which have been used by this THD. + This list is later iterated to invoke release_thd() on those + plugins. + */ + DYNAMIC_ARRAY audit_class_plugins; + /** + Array of bits indicating which audit classes have already been + added to the list of audit plugins which are currently in use. + */ + unsigned long audit_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE]; +#endif + #if defined(ENABLED_DEBUG_SYNC) /* Debug Sync facility. See debug_sync.cc. */ struct st_debug_sync_control *debug_sync_control; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index ccc176624ed..9758b2b4e4d 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -19,6 +19,7 @@ */ #include "mysql_priv.h" +#include "sql_audit.h" #include "probes_mysql.h" #ifdef HAVE_OPENSSL @@ -1169,6 +1170,7 @@ void do_handle_one_connection(THD *thd_arg) while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION)) { + mysql_audit_release(thd); if (do_command(thd)) break; } diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 748052b2985..b2871e6ed74 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -61,6 +61,7 @@ #include "sql_show.h" #include "slave.h" #include "rpl_mi.h" +#include "sql_audit.h" #ifndef EMBEDDED_LIBRARY static bool delayed_get_table(THD *thd, TABLE_LIST *table_list); @@ -2459,6 +2460,7 @@ pthread_handler_t handle_delayed_insert(void *arg) while (!thd->killed) { int error; + mysql_audit_release(thd); #if defined(HAVE_BROKEN_COND_TIMEDWAIT) error= mysql_cond_wait(&di->cond, &di->mutex); #else @@ -2540,6 +2542,7 @@ pthread_handler_t handle_delayed_insert(void *arg) mysql_unlock_tables(thd, lock); ha_autocommit_or_rollback(thd, 0); di->group_count=0; + mysql_audit_release(thd); mysql_mutex_lock(&di->mutex); } if (di->tables_in_use) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d8e82c3e0d1..a544781d9ec 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -28,6 +28,7 @@ #include "sp_cache.h" #include "events.h" #include "sql_trigger.h" +#include "sql_audit.h" #include "sql_prepare.h" #include "probes_mysql.h" #include "set_var.h" @@ -1493,6 +1494,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Free tables */ close_thread_tables(thd); + if (!thd->is_error() && !thd->killed_errno()) + mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0); + log_slow_statement(thd); thd_proc_info(thd, "cleaning up"); diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 0a7864631ae..442c342fd31 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -16,6 +16,7 @@ #include "sys_vars_shared.h" #include <my_pthread.h> #include <my_getopt.h> +#include <mysql/plugin_audit.h> #define REPORT_TO_LOG 1 #define REPORT_TO_USER 2 @@ -48,12 +49,16 @@ const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]= { C_STRING_WITH_LEN("FTPARSER") }, { C_STRING_WITH_LEN("DAEMON") }, { C_STRING_WITH_LEN("INFORMATION SCHEMA") }, + { C_STRING_WITH_LEN("AUDIT") }, { C_STRING_WITH_LEN("REPLICATION") }, }; extern int initialize_schema_table(st_plugin_int *plugin); extern int finalize_schema_table(st_plugin_int *plugin); +extern int initialize_audit_plugin(st_plugin_int *plugin); +extern int finalize_audit_plugin(st_plugin_int *plugin); + /* The number of elements in both plugin_type_initialize and plugin_type_deinitialize should equal to the number of plugins @@ -61,12 +66,14 @@ extern int finalize_schema_table(st_plugin_int *plugin); */ plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= { - 0,ha_initialize_handlerton,0,0,initialize_schema_table + 0,ha_initialize_handlerton,0,0,initialize_schema_table, + initialize_audit_plugin }; plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= { - 0,ha_finalize_handlerton,0,0,finalize_schema_table + 0,ha_finalize_handlerton,0,0,finalize_schema_table, + finalize_audit_plugin }; #ifdef HAVE_DLOPEN @@ -88,7 +95,8 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= MYSQL_FTPARSER_INTERFACE_VERSION, MYSQL_DAEMON_INTERFACE_VERSION, MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION, - MYSQL_REPLICATION_INTERFACE_VERSION, + MYSQL_AUDIT_INTERFACE_VERSION, + MYSQL_REPLICATION_INTERFACE_VERSION }; static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= { @@ -97,7 +105,8 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= MYSQL_FTPARSER_INTERFACE_VERSION, MYSQL_DAEMON_INTERFACE_VERSION, MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION, - MYSQL_REPLICATION_INTERFACE_VERSION, + MYSQL_AUDIT_INTERFACE_VERSION, + MYSQL_REPLICATION_INTERFACE_VERSION }; /* support for Services */ @@ -1025,6 +1034,10 @@ static int plugin_initialize(struct st_plugin_int *plugin) DBUG_ENTER("plugin_initialize"); mysql_mutex_assert_owner(&LOCK_plugin); + uint state= plugin->state; + DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED); + + mysql_mutex_unlock(&LOCK_plugin); if (plugin_type_initialize[plugin->plugin->type]) { if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) @@ -1043,8 +1056,7 @@ static int plugin_initialize(struct st_plugin_int *plugin) goto err; } } - - plugin->state= PLUGIN_IS_READY; + state= PLUGIN_IS_READY; // plugin->init() succeeded if (plugin->plugin->status_vars) { @@ -1063,7 +1075,8 @@ static int plugin_initialize(struct st_plugin_int *plugin) if (add_status_vars(array)) // add_status_vars makes a copy goto err; #else - add_status_vars(plugin->plugin->status_vars); // add_status_vars makes a copy + if (add_status_vars(plugin->plugin->status_vars)) + goto err; #endif /* FIX_LATER */ } @@ -1086,10 +1099,13 @@ static int plugin_initialize(struct st_plugin_int *plugin) ret= 0; err: + mysql_mutex_lock(&LOCK_plugin); + plugin->state= state; + /* maintain the obsolete @@have_innodb variable */ if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB")) - have_innodb= plugin->state & PLUGIN_IS_READY ? SHOW_OPTION_YES - : SHOW_OPTION_DISABLED; + have_innodb= state & PLUGIN_IS_READY ? SHOW_OPTION_YES + : SHOW_OPTION_DISABLED; DBUG_RETURN(ret); } @@ -1422,26 +1438,22 @@ end: */ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) { + THD thd; TABLE_LIST tables; TABLE *table; READ_RECORD read_record_info; int error; - THD *new_thd; + THD *new_thd= &thd; #ifdef EMBEDDED_LIBRARY bool table_exists; #endif /* EMBEDDED_LIBRARY */ DBUG_ENTER("plugin_load"); - if (!(new_thd= new THD)) - { - sql_print_error("Can't allocate memory for plugin structures"); - delete new_thd; - DBUG_VOID_RETURN; - } new_thd->thread_stack= (char*) &tables; new_thd->store_globals(); new_thd->db= my_strdup("mysql", MYF(0)); new_thd->db_length= 5; + bzero((char*) &thd.net, sizeof(thd.net)); bzero((uchar*)&tables, sizeof(tables)); tables.alias= tables.table_name= (char*)"plugin"; tables.lock_type= TL_READ; @@ -1499,7 +1511,6 @@ static void plugin_load(MEM_ROOT *tmp_root, int *argc, char **argv) new_thd->version--; // Force close to free memory end: close_thread_tables(new_thd); - delete new_thd; /* Remember that we don't have a THD */ my_pthread_setspecific_ptr(THR_THD, 0); DBUG_VOID_RETURN; @@ -1763,7 +1774,6 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl } else { - DBUG_ASSERT(tmp->state == PLUGIN_IS_UNINITIALIZED); if (plugin_initialize(tmp)) { my_error(ER_CANT_INITIALIZE_UDF, MYF(0), name->str, @@ -2176,7 +2186,7 @@ static int check_func_set(THD *thd, struct st_mysql_sys_var *var, { if (value->val_int(value, (long long *)&result)) goto err; - if (unlikely((result >= (ULL(1) << typelib->count)) && + if (unlikely((result >= (1ULL << typelib->count)) && (typelib->count < sizeof(long)*8))) goto err; } @@ -2261,10 +2271,6 @@ sys_var *find_sys_var(THD *thd, const char *str, uint length) mysql_rwlock_unlock(&LOCK_system_variables_hash); mysql_mutex_unlock(&LOCK_plugin); - /* - If the variable exists but the plugin it is associated with is not ready - then the intern_plugin_lock did not raise an error, so we do it here. - */ if (!var) my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str); DBUG_RETURN(var); @@ -2727,7 +2733,7 @@ TYPELIB* sys_var_pluginvar::plugin_var_typelib(void) default: return NULL; } - return NULL; + return NULL; /* Keep compiler happy */ } @@ -2888,7 +2894,7 @@ static void plugin_opt_set_limits(struct my_option *options, options->typelib= ((sysvar_set_t*) opt)->typelib; options->def_value= ((sysvar_set_t*) opt)->def_val; options->min_value= options->block_size= 0; - options->max_value= (ULL(1) << options->typelib->count) - 1; + options->max_value= (1ULL << options->typelib->count) - 1; break; case PLUGIN_VAR_BOOL: options->var_type= GET_BOOL; @@ -2930,7 +2936,7 @@ static void plugin_opt_set_limits(struct my_option *options, options->typelib= ((thdvar_set_t*) opt)->typelib; options->def_value= ((thdvar_set_t*) opt)->def_val; options->min_value= options->block_size= 0; - options->max_value= (ULL(1) << options->typelib->count) - 1; + options->max_value= (1ULL << options->typelib->count) - 1; break; case PLUGIN_VAR_BOOL | PLUGIN_VAR_THDLOCAL: options->var_type= GET_BOOL; |