mariadb/sql/sql_audit.cc

478 lines
11 KiB
C++

/* 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 "sql_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.event_subclass= event_subtype;
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 */