mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 14:33:32 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			478 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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 "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 */
 |