mariadb/sql/sql_audit.cc
Monty a7e352b54d Changed database, tablename and alias to be LEX_CSTRING
This was done in, among other things:
- thd->db and thd->db_length
- TABLE_LIST tablename, db, alias and schema_name
- Audit plugin database name
- lex->db
- All db and table names in Alter_table_ctx
- st_select_lex db

Other things:
- Changed a lot of functions to take const LEX_CSTRING* as argument
  for db, table_name and alias. See init_one_table() as an example.
- Changed some function arguments from LEX_CSTRING to const LEX_CSTRING
- Changed some lists from LEX_STRING to LEX_CSTRING
- threads_mysql.result changed because process list_db wasn't always
  correctly updated
- New append_identifier() function that takes LEX_CSTRING* as arguments
- Added new element tmp_buff to Alter_table_ctx to separate temp name
  handling from temporary space
- Ensure we store the length after my_casedn_str() of table/db names
- Removed not used version of rename_table_in_stat_tables()
- Changed Natural_join_column::table_name and db_name() to never return
  NULL (used for print)
- thd->get_db() now returns db as a printable string (thd->db.str or "")
2018-01-30 21:33:55 +02:00

471 lines
11 KiB
C++

/* Copyright (c) 2007, 2013, 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 */
#include "mariadb.h"
#include "sql_priv.h"
#include "mysqld.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
struct st_mysql_event_generic
{
unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
unsigned int event_class;
const void *event;
};
unsigned long mysql_global_audit_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
static mysql_mutex_t LOCK_audit_mask;
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]);
}
/**
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)
{
ulong *event_class_mask= (ulong*) arg;
st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
/* 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, MYF(0));
}
/* 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;
}
/**
@brief Acquire audit plugins
@param[in] thd MySQL thread handle
@param[in] event_class Audit event class
@details Ensure that audit plugins interested in given event
class are locked by current thread.
*/
void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
{
DBUG_ENTER("mysql_audit_acquire_plugins");
DBUG_ASSERT(thd);
DBUG_ASSERT(!check_audit_mask(mysql_global_audit_mask, event_class_mask));
if (check_audit_mask(thd->audit_class_mask, event_class_mask))
{
plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, event_class_mask);
add_audit_mask(thd->audit_class_mask, event_class_mask);
}
DBUG_VOID_RETURN;
}
/**
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->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);
/*
Pre-acquire the newly inslalled audit plugin for events that
may potentially occur further during INSTALL PLUGIN.
When audit event is triggered, audit subsystem acquires interested
plugins by walking through plugin list. Evidently plugin list
iterator protects plugin list by acquiring LOCK_plugin, see
plugin_foreach_with_mask().
On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin
rather for a long time.
When audit event is triggered during [UN]INSTALL PLUGIN, plugin
list iterator acquires the same lock (within the same thread)
second time.
This hack should be removed when LOCK_plugin is fixed so it
protects only what it supposed to protect.
See also mysql_install_plugin() and mysql_uninstall_plugin()
*/
THD *thd= current_thd;
if (thd)
{
acquire_plugins(thd, plugin_int_to_ref(plugin), data->class_mask);
add_audit_mask(thd->audit_class_mask, data->class_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 st_mysql_event_generic *event_generic=
(const struct st_mysql_event_generic *) arg;
st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
/* Check to see if the plugin is interested in this event */
if (!check_audit_mask(data->class_mask, event_generic->event_class_mask))
data->event_notify(thd, event_generic->event_class, event_generic->event);
return 0;
}
/**
Distributes an audit event to plug-ins
@param[in] thd
@param[in] event_class
@param[in] event
*/
void mysql_audit_notify(THD *thd, uint event_class, const void *event)
{
struct st_mysql_event_generic event_generic;
event_generic.event_class= event_class;
event_generic.event= event;
set_audit_mask(event_generic.event_class_mask, event_class);
/*
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, &event_generic);
}
else
{
plugin_ref *plugins, *plugins_last;
mysql_audit_acquire_plugins(thd, event_generic.event_class_mask);
/* 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, &event_generic);
}
}
#else /* EMBEDDED_LIBRARY */
void mysql_audit_acquire_plugins(THD *thd, ulong *event_class_mask)
{
}
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)
{
}
void mysql_audit_init_thd(THD *thd)
{
}
void mysql_audit_free_thd(THD *thd)
{
}
#endif /* EMBEDDED_LIBRARY */