MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sql_audit.cc
1 /* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
16 #include "sql_priv.h"
17 #include "sql_audit.h"
18 
19 extern int initialize_audit_plugin(st_plugin_int *plugin);
20 extern int finalize_audit_plugin(st_plugin_int *plugin);
21 
22 #ifndef EMBEDDED_LIBRARY
23 
25 {
26  unsigned int event_class;
27  const void *event;
28 };
29 
30 unsigned long mysql_global_audit_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
31 
32 static mysql_mutex_t LOCK_audit_mask;
33 
34 static void event_class_dispatch(THD *thd, unsigned int event_class,
35  const void *event);
36 
37 
38 static inline
39 void set_audit_mask(unsigned long *mask, uint event_class)
40 {
41  mask[0]= 1;
42  mask[0]<<= event_class;
43 }
44 
45 static inline
46 void add_audit_mask(unsigned long *mask, const unsigned long *rhs)
47 {
48  mask[0]|= rhs[0];
49 }
50 
51 static inline
52 bool check_audit_mask(const unsigned long *lhs,
53  const unsigned long *rhs)
54 {
55  return !(lhs[0] & rhs[0]);
56 }
57 
58 
59 typedef void (*audit_handler_t)(THD *thd, uint event_subtype, va_list ap);
60 
71 static void general_class_handler(THD *thd, uint event_subtype, va_list ap)
72 {
74  event.event_subclass= event_subtype;
75  event.general_error_code= va_arg(ap, int);
76  event.general_thread_id= thd ? thd->thread_id : 0;
77  event.general_time= va_arg(ap, time_t);
78  event.general_user= va_arg(ap, const char *);
79  event.general_user_length= va_arg(ap, unsigned int);
80  event.general_command= va_arg(ap, const char *);
81  event.general_command_length= va_arg(ap, unsigned int);
82  event.general_query= va_arg(ap, const char *);
83  event.general_query_length= va_arg(ap, unsigned int);
84  event.general_charset= va_arg(ap, struct charset_info_st *);
85  event.general_rows= (unsigned long long) va_arg(ap, ha_rows);
86  event.general_sql_command= va_arg(ap, MYSQL_LEX_STRING);
87  event.general_host= va_arg(ap, MYSQL_LEX_STRING);
88  event.general_external_user= va_arg(ap, MYSQL_LEX_STRING);
89  event.general_ip= va_arg(ap, MYSQL_LEX_STRING);
90  event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
91 }
92 
93 
94 static void connection_class_handler(THD *thd, uint event_subclass, va_list ap)
95 {
97  event.event_subclass= event_subclass;
98  event.status= va_arg(ap, int);
99  event.thread_id= va_arg(ap, unsigned long);
100  event.user= va_arg(ap, const char *);
101  event.user_length= va_arg(ap, unsigned int);
102  event.priv_user= va_arg(ap, const char *);
103  event.priv_user_length= va_arg(ap, unsigned int);
104  event.external_user= va_arg(ap, const char *);
105  event.external_user_length= va_arg(ap, unsigned int);
106  event.proxy_user= va_arg(ap, const char *);
107  event.proxy_user_length= va_arg(ap, unsigned int);
108  event.host= va_arg(ap, const char *);
109  event.host_length= va_arg(ap, unsigned int);
110  event.ip= va_arg(ap, const char *);
111  event.ip_length= va_arg(ap, unsigned int);
112  event.database= va_arg(ap, const char *);
113  event.database_length= va_arg(ap, unsigned int);
114  event_class_dispatch(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
115 }
116 
117 
118 static audit_handler_t audit_handlers[] =
119 {
120  general_class_handler, connection_class_handler
121 };
122 
123 static const uint audit_handlers_count=
124  (sizeof(audit_handlers) / sizeof(audit_handler_t));
125 
126 
137 static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg)
138 {
139  uint event_class= *(uint*) arg;
140  unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
141  st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
142 
143  set_audit_mask(event_class_mask, event_class);
144 
145  /* Check if this plugin is interested in the event */
146  if (check_audit_mask(data->class_mask, event_class_mask))
147  return 0;
148 
149  /*
150  Check if this plugin may already be registered. This will fail to
151  acquire a newly installed plugin on a specific corner case where
152  one or more event classes already in use by the calling thread
153  are an event class of which the audit plugin has interest.
154  */
155  if (!check_audit_mask(data->class_mask, thd->audit_class_mask))
156  return 0;
157 
158  /* Check if we need to initialize the array of acquired plugins */
159  if (unlikely(!thd->audit_class_plugins.buffer))
160  {
161  /* specify some reasonable initialization defaults */
162  my_init_dynamic_array(&thd->audit_class_plugins,
163  sizeof(plugin_ref), 16, 16);
164  }
165 
166  /* lock the plugin and add it to the list */
167  plugin= my_plugin_lock(NULL, &plugin);
168  insert_dynamic(&thd->audit_class_plugins, &plugin);
169 
170  return 0;
171 }
172 
173 
183 void mysql_audit_acquire_plugins(THD *thd, uint event_class)
184 {
185  unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
186  DBUG_ENTER("mysql_audit_acquire_plugins");
187  set_audit_mask(event_class_mask, event_class);
188  if (thd && !check_audit_mask(mysql_global_audit_mask, event_class_mask) &&
189  check_audit_mask(thd->audit_class_mask, event_class_mask))
190  {
191  plugin_foreach(thd, acquire_plugins, MYSQL_AUDIT_PLUGIN, &event_class);
192  add_audit_mask(thd->audit_class_mask, event_class_mask);
193  }
194  DBUG_VOID_RETURN;
195 }
196 
197 
208 void mysql_audit_notify(THD *thd, uint event_class, uint event_subtype, ...)
209 {
210  va_list ap;
211  audit_handler_t *handlers= audit_handlers + event_class;
212  DBUG_ASSERT(event_class < audit_handlers_count);
213  mysql_audit_acquire_plugins(thd, event_class);
214  va_start(ap, event_subtype);
215  (*handlers)(thd, event_subtype, ap);
216  va_end(ap);
217 }
218 
219 
227 void mysql_audit_release(THD *thd)
228 {
229  plugin_ref *plugins, *plugins_last;
230 
231  if (!thd || !(thd->audit_class_plugins.elements))
232  return;
233 
234  plugins= (plugin_ref*) thd->audit_class_plugins.buffer;
235  plugins_last= plugins + thd->audit_class_plugins.elements;
236  for (; plugins < plugins_last; plugins++)
237  {
238  st_mysql_audit *data= plugin_data(*plugins, struct st_mysql_audit *);
239 
240  /* Check to see if the plugin has a release method */
241  if (!(data->release_thd))
242  continue;
243 
244  /* Tell the plugin to release its resources */
245  data->release_thd(thd);
246  }
247 
248  /* Now we actually unlock the plugins */
249  plugin_unlock_list(NULL, (plugin_ref*) thd->audit_class_plugins.buffer,
250  thd->audit_class_plugins.elements);
251 
252  /* Reset the state of thread values */
253  reset_dynamic(&thd->audit_class_plugins);
254  memset(thd->audit_class_mask, 0, sizeof(thd->audit_class_mask));
255 }
256 
257 
265 void mysql_audit_init_thd(THD *thd)
266 {
267  memset(&thd->audit_class_plugins, 0, sizeof(thd->audit_class_plugins));
268  memset(thd->audit_class_mask, 0, sizeof(thd->audit_class_mask));
269 }
270 
271 
282 void mysql_audit_free_thd(THD *thd)
283 {
284  mysql_audit_release(thd);
285  DBUG_ASSERT(thd->audit_class_plugins.elements == 0);
286  delete_dynamic(&thd->audit_class_plugins);
287 }
288 
289 #ifdef HAVE_PSI_INTERFACE
290 static PSI_mutex_key key_LOCK_audit_mask;
291 
292 static PSI_mutex_info all_audit_mutexes[]=
293 {
294  { &key_LOCK_audit_mask, "LOCK_audit_mask", PSI_FLAG_GLOBAL}
295 };
296 
297 static void init_audit_psi_keys(void)
298 {
299  const char* category= "sql";
300  int count;
301 
302  count= array_elements(all_audit_mutexes);
303  mysql_mutex_register(category, all_audit_mutexes, count);
304 }
305 #endif /* HAVE_PSI_INTERFACE */
306 
311 void mysql_audit_initialize()
312 {
313 #ifdef HAVE_PSI_INTERFACE
314  init_audit_psi_keys();
315 #endif
316 
317  mysql_mutex_init(key_LOCK_audit_mask, &LOCK_audit_mask, MY_MUTEX_INIT_FAST);
318  memset(mysql_global_audit_mask, 0, sizeof(mysql_global_audit_mask));
319 }
320 
321 
326 void mysql_audit_finalize()
327 {
328  mysql_mutex_destroy(&LOCK_audit_mask);
329 }
330 
331 
341 int initialize_audit_plugin(st_plugin_int *plugin)
342 {
343  st_mysql_audit *data= (st_mysql_audit*) plugin->plugin->info;
344 
345  if (!data->class_mask || !data->event_notify ||
346  !data->class_mask[0])
347  {
348  sql_print_error("Plugin '%s' has invalid data.",
349  plugin->name.str);
350  return 1;
351  }
352 
353  if (plugin->plugin->init && plugin->plugin->init(plugin))
354  {
355  sql_print_error("Plugin '%s' init function returned error.",
356  plugin->name.str);
357  return 1;
358  }
359 
360  /* Make the interface info more easily accessible */
361  plugin->data= plugin->plugin->info;
362 
363  /* Add the bits the plugin is interested in to the global mask */
364  mysql_mutex_lock(&LOCK_audit_mask);
365  add_audit_mask(mysql_global_audit_mask, data->class_mask);
366  mysql_mutex_unlock(&LOCK_audit_mask);
367 
368  return 0;
369 }
370 
371 
381 static my_bool calc_class_mask(THD *thd, plugin_ref plugin, void *arg)
382 {
383  st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
384  if ((data= plugin_data(plugin, struct st_mysql_audit *)))
385  add_audit_mask((unsigned long *) arg, data->class_mask);
386  return 0;
387 }
388 
389 
398 int finalize_audit_plugin(st_plugin_int *plugin)
399 {
400  unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
401 
402  if (plugin->plugin->deinit && plugin->plugin->deinit(NULL))
403  {
404  DBUG_PRINT("warning", ("Plugin '%s' deinit function returned error.",
405  plugin->name.str));
406  DBUG_EXECUTE("finalize_audit_plugin", return 1; );
407  }
408 
409  plugin->data= NULL;
410  memset(&event_class_mask, 0, sizeof(event_class_mask));
411 
412  /* Iterate through all the installed plugins to create new mask */
413 
414  /*
415  LOCK_audit_mask/LOCK_plugin order is not fixed, but serialized with table
416  lock on mysql.plugin.
417  */
418  mysql_mutex_lock(&LOCK_audit_mask);
419  plugin_foreach(current_thd, calc_class_mask, MYSQL_AUDIT_PLUGIN,
420  &event_class_mask);
421 
422  /* Set the global audit mask */
423  bmove(mysql_global_audit_mask, event_class_mask, sizeof(event_class_mask));
424  mysql_mutex_unlock(&LOCK_audit_mask);
425 
426  return 0;
427 }
428 
429 
440 static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg)
441 {
442  const struct st_mysql_event_generic *event_generic=
443  (const struct st_mysql_event_generic *) arg;
444  unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
445  st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
446 
447  set_audit_mask(event_class_mask, event_generic->event_class);
448 
449  /* Check to see if the plugin is interested in this event */
450  if (check_audit_mask(data->class_mask, event_class_mask))
451  return 0;
452 
453  /* Actually notify the plugin */
454  data->event_notify(thd, event_generic->event_class, event_generic->event);
455 
456  return 0;
457 }
458 
459 
467 static void event_class_dispatch(THD *thd, unsigned int event_class,
468  const void *event)
469 {
470  struct st_mysql_event_generic event_generic;
471  event_generic.event_class= event_class;
472  event_generic.event= event;
473  /*
474  Check if we are doing a slow global dispatch. This event occurs when
475  thd == NULL as it is not associated with any particular thread.
476  */
477  if (unlikely(!thd))
478  {
479  plugin_foreach(thd, plugins_dispatch, MYSQL_AUDIT_PLUGIN, &event_generic);
480  }
481  else
482  {
483  plugin_ref *plugins, *plugins_last;
484 
485  /* Use the cached set of audit plugins */
486  plugins= (plugin_ref*) thd->audit_class_plugins.buffer;
487  plugins_last= plugins + thd->audit_class_plugins.elements;
488 
489  for (; plugins < plugins_last; plugins++)
490  plugins_dispatch(thd, *plugins, &event_generic);
491  }
492 }
493 
494 
495 #else /* EMBEDDED_LIBRARY */
496 
497 
498 void mysql_audit_acquire_plugins(THD *thd, uint event_class)
499 {
500 }
501 
502 
503 void mysql_audit_initialize()
504 {
505 }
506 
507 
508 void mysql_audit_finalize()
509 {
510 }
511 
512 
513 int initialize_audit_plugin(st_plugin_int *plugin)
514 {
515  return 1;
516 }
517 
518 
519 int finalize_audit_plugin(st_plugin_int *plugin)
520 {
521  return 0;
522 }
523 
524 
525 void mysql_audit_release(THD *thd)
526 {
527 }
528 
529 
530 #endif /* EMBEDDED_LIBRARY */