mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-27 00:48:30 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			484 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			484 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-1335  USA */
 | |
| 
 | |
| #include "mariadb.h"
 | |
| #include "sql_priv.h"
 | |
| #include "mysqld.h"
 | |
| #include "sql_audit.h"
 | |
| 
 | |
| extern int initialize_audit_plugin(void *plugin);
 | |
| extern int finalize_audit_plugin(void *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(PSI_INSTRUMENT_ME, &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);
 | |
|   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);
 | |
|     thd->audit_plugin_version= global_plugin_version;
 | |
|   }
 | |
|   DBUG_VOID_RETURN;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check if there were changes in the state of plugins
 | |
|   so we need to do the mysql_audit_release asap.
 | |
| 
 | |
|   @param[in] thd
 | |
| 
 | |
| */
 | |
| 
 | |
| my_bool mysql_audit_release_required(THD *thd)
 | |
| {
 | |
|   return thd && (thd->audit_plugin_version != global_plugin_version);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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));
 | |
|   thd->audit_plugin_version= -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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(void *plugin_)
 | |
| {
 | |
|   st_plugin_int *plugin= static_cast<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 installed 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(void *plugin_)
 | |
| {
 | |
|   st_plugin_int *plugin= static_cast<st_plugin_int *>(plugin_);
 | |
|   int deinit_status= 0;
 | |
|   unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
 | |
|   
 | |
|   if (plugin->plugin->deinit)
 | |
|     deinit_status= plugin->plugin->deinit(NULL);
 | |
|   
 | |
|   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 deinit_status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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(void *plugin)
 | |
| {
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| int finalize_audit_plugin(void *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 */
 | 
