1 /* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22 
23 #include <stdio.h>
24 #include <m_ctype.h>
25 #include <mysql/plugin.h>
26 #include <mysql/plugin_audit.h>
27 #include <my_sys.h>
28 #include <mysqld_error.h>
29 #include "my_compiler.h"
30 
31 /** Event strings. */
32 LEX_CSTRING event_names[][6] = {
33   /** MYSQL_AUDIT_GENERAL_CLASS */
34     { { C_STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_LOG") },
35       { C_STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_ERROR") },
36       { C_STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_RESULT") },
37       { C_STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_STATUS") },
38     },
39     /** MYSQL_AUDIT_CONNECTION_CLASS */
40     { { C_STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_CONNECT") },
41       { C_STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_DISCONNECT") },
42       { C_STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_CHANGE_USER") },
43       { C_STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE") },
44     },
45     /** MYSQL_AUDIT_PARSE_CLASS */
46     { { C_STRING_WITH_LEN("MYSQL_AUDIT_PARSE_PREPARSE") },
47       { C_STRING_WITH_LEN("MYSQL_AUDIT_PARSE_POSTPARSE") },
48     },
49     /** MYSQL_AUDIT_AUTHORIZATION_CLASS */
50     { { C_STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_USER") },
51       { C_STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_DB") },
52       { C_STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_TABLE") },
53       { C_STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_COLUMN") },
54       { C_STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_PROCEDURE") },
55       { C_STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_PROXY") },
56     },
57     /** MYSQL_AUDIT_TABLE_ROW_ACCES_CLASS */
58     {
59       { C_STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_READ") },
60       { C_STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_INSERT") },
61       { C_STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_UPDATE") },
62       { C_STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_DELETE") },
63     },
64     /** MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS */
65     {
66       { C_STRING_WITH_LEN("MYSQL_AUDIT_GLOBAL_VARIABLE_GET") },
67       { C_STRING_WITH_LEN("MYSQL_AUDIT_GLOBAL_VARIABLE_SET") },
68     },
69     /** MYSQL_AUDIT_SERVER_STARTUP_CLASS */
70     {
71       { C_STRING_WITH_LEN("MYSQL_AUDIT_SERVER_STARTUP_STARTUP") },
72     },
73     /** MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS */
74     {
75       { C_STRING_WITH_LEN("MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN") },
76     },
77     /** MYSQL_AUDIT_COMMAND_CLASS */
78     {
79       { C_STRING_WITH_LEN("MYSQL_AUDIT_COMMAND_START") },
80       { C_STRING_WITH_LEN("MYSQL_AUDIT_COMMAND_END") },
81     },
82     /** MYSQL_AUDIT_QUERY_CLASS */
83     {
84       { C_STRING_WITH_LEN("MYSQL_AUDIT_QUERY_START") },
85       { C_STRING_WITH_LEN("MYSQL_AUDIT_QUERY_NESTED_START") },
86       { C_STRING_WITH_LEN("MYSQL_AUDIT_QUERY_STATUS_END") },
87       { C_STRING_WITH_LEN("MYSQL_AUDIT_QUERY_NESTED_STATUS_END") },
88     },
89     /** MYSQL_AUDIT_STORED_PROGRAM_CLASS */
90     {
91       { C_STRING_WITH_LEN("MYSQL_AUDIT_STORED_PROGRAM_EXECUTE") },
92     }
93 };
94 
95 static volatile int number_of_calls;
96 
97 /*
98   Plugin has been installed.
99 */
100 static my_bool g_plugin_installed= FALSE;
101 
102 /*
103   Record buffer mutex.
104 */
105 static mysql_mutex_t g_record_buffer_mutex;
106 
107 /*
108   Event recording buffer.
109 */
110 static char *g_record_buffer;
111 
112 #define AUDIT_NULL_VAR(x) static volatile int number_of_calls_ ## x;
113 #include "audit_null_variables.h"
114 #undef AUDIT_NULL_VAR
115 
116 /*
117   Plugin status variables for SHOW STATUS
118 */
119 
120 static struct st_mysql_show_var simple_status[] =
121 {
122   { "Audit_null_called",
123     (char *)&number_of_calls,
124     SHOW_INT, SHOW_SCOPE_GLOBAL },
125 
126 #define AUDIT_NULL_VAR(x) { "Audit_null_" #x, (char*)&number_of_calls_ ## x, \
127                             SHOW_INT, SHOW_SCOPE_GLOBAL },
128 #include "audit_null_variables.h"
129 #undef AUDIT_NULL_VAR
130 
131   { 0, 0, 0, SHOW_SCOPE_GLOBAL }
132 };
133 
134 /*
135   Define plugin variables.
136 */
137 
138 static MYSQL_THDVAR_STR(abort_message,
139                         PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
140                         "Custom message for event abort.", NULL, NULL, NULL);
141 
142 static MYSQL_THDVAR_INT(abort_value,
143                         PLUGIN_VAR_RQCMDARG,
144                         "Event abort value.",
145                         NULL, NULL, 1, -1, 150, 0);
146 
147 static MYSQL_THDVAR_STR(event_order_check,
148                         PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
149                         "Event order check string", NULL, NULL, NULL);
150 
151 static MYSQL_THDVAR_UINT(event_order_check_consume_ignore_count,
152                          PLUGIN_VAR_RQCMDARG,
153                          "Do not consume event order string specified "
154                          "number of times.",
155                          NULL, NULL, 0, 0, UINT_MAX, 1);
156 
157 static MYSQL_THDVAR_INT(event_order_started,
158                         PLUGIN_VAR_RQCMDARG,
159                         "Plugin is in the event order check.",
160                         NULL, NULL, 0, 0, 1, 0);
161 
162 static MYSQL_THDVAR_INT(event_order_check_exact,
163                         PLUGIN_VAR_RQCMDARG,
164                         "Plugin checks exact event order.",
165                         NULL, NULL, 1, 0, 1, 0);
166 
167 static MYSQL_THDVAR_STR(event_record_def,
168                         PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
169                         "Event recording definition", NULL, NULL, NULL);
170 
171 static MYSQL_THDVAR_STR(event_record,
172                         PLUGIN_VAR_READONLY |
173                         PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
174                         "Event recording", NULL, NULL, NULL);
175 /*
176   Initialize the plugin at server start or plugin installation.
177 
178   SYNOPSIS
179     audit_null_plugin_init()
180 
181   DESCRIPTION
182     Does nothing.
183 
184   RETURN VALUE
185     0                    success
186     1                    failure (cannot happen)
187 */
188 
audit_null_plugin_init(void * arg MY_ATTRIBUTE ((unused)))189 static int audit_null_plugin_init(void *arg MY_ATTRIBUTE((unused)))
190 {
191   struct st_mysql_show_var *var;
192 
193   for (var= simple_status; var->value != 0; var++)
194   {
195     *((int*)var->value) = 0;
196   }
197 
198   mysql_mutex_init(PSI_NOT_INSTRUMENTED,
199                    &g_record_buffer_mutex,
200                    MY_MUTEX_INIT_FAST);
201 
202   g_record_buffer= NULL;
203   g_plugin_installed= TRUE;
204 
205   return(0);
206 }
207 
208 
209 /*
210   Terminate the plugin at server shutdown or plugin deinstallation.
211 
212   SYNOPSIS
213     audit_null_plugin_deinit()
214     Does nothing.
215 
216   RETURN VALUE
217     0                    success
218     1                    failure (cannot happen)
219 
220 */
221 
audit_null_plugin_deinit(void * arg MY_ATTRIBUTE ((unused)))222 static int audit_null_plugin_deinit(void *arg MY_ATTRIBUTE((unused)))
223 {
224   if (g_plugin_installed == TRUE)
225   {
226     my_free((void *)(g_record_buffer));
227 
228     g_record_buffer= NULL;
229 
230     mysql_mutex_destroy(&g_record_buffer_mutex);
231 
232     g_plugin_installed= FALSE;
233   }
234 
235   return(0);
236 }
237 
238 /**
239   @brief Converts event_class and event_subclass into a string.
240 
241   @param event_class[in]    Event class value.
242   @param event_subclass[in] Event subclass value.
243 
244   @retval Event name.
245 */
event_to_str(unsigned int event_class,unsigned long event_subclass)246 static LEX_CSTRING event_to_str(unsigned int event_class,
247                                 unsigned long event_subclass)
248 {
249   int count;
250   for (count= 0; event_subclass; count++, event_subclass >>= 1);
251 
252   return event_names[event_class][count - 1];
253 }
254 
255 /**
256   @brief Read token delimited by a semicolon from a string.
257 
258   @param str[in,out] Pointer to a string containing text.
259                      Pointer is moved to a new token after the function ends.
260 
261   @retval Token retrieved from a string.
262 */
get_token(const char ** str)263 static LEX_CSTRING get_token(const char **str)
264 {
265   LEX_CSTRING ret = { NULL, 0 };
266 
267   if (*str != NULL)
268   {
269     ret.str= *str;
270 
271     if (*ret.str != '\0')
272     {
273       /* Find a param delimiter. */
274       while (**str && **str != ';') (*str)++;
275 
276       ret.length= *str - ret.str;
277 
278       /* Skip a delimiter. */
279       if (**str == ';') (*str)++;
280     }
281   }
282 
283   return ret;
284 }
285 
add_event(const char * var,LEX_CSTRING event,const char * data,size_t data_length)286 static char *add_event(const char *var, LEX_CSTRING event,
287                        const char *data, size_t data_length)
288 {
289   LEX_CSTRING str;
290   size_t size;
291   char *buffer;
292 
293   lex_cstring_set(&str, var);
294 
295   size= str.length + event.length + data_length + 4;
296   buffer= (char *)my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_FAE));
297 
298   my_snprintf(buffer, size, "%s%s%s;%s;", var, str.length == 0 ? "" : "\n",
299               event.str, data);
300 
301   buffer[size - (str.length == 0 ? 2 : 1)] = '\0';
302 
303   return buffer;
304 }
305 
process_event_record(MYSQL_THD thd,LEX_CSTRING event_name,const char * data,size_t data_length)306 static void process_event_record(MYSQL_THD thd, LEX_CSTRING event_name,
307                                  const char *data, size_t data_length)
308 {
309   const char *record_str = (const char *)THDVAR(thd, event_record_def);
310   LEX_CSTRING record_begin = get_token(&record_str);
311   LEX_CSTRING record_end = get_token(&record_str);
312 
313   if (record_str == NULL)
314   {
315     return;
316   }
317 
318   if (record_end.length == 0)
319   {
320     /* We are already in the consuming phase. Add a new event name into
321        a record variable */
322 
323     const char *buffer= THDVAR(thd, event_record);
324     char *new_buffer= NULL;
325 
326     /* Add event. */
327     mysql_mutex_lock(&g_record_buffer_mutex);
328 
329     /* Only one THD is capable of adding events into the buffer. */
330     if (buffer && (buffer == g_record_buffer))
331     {
332       new_buffer= add_event(buffer, event_name, data, data_length);
333       g_record_buffer= new_buffer;
334       my_free((void *)(buffer));
335     }
336 
337     mysql_mutex_unlock(&g_record_buffer_mutex);
338 
339     THDVAR(thd, event_record)= new_buffer;
340 
341     if (!my_charset_latin1.coll->strnncoll(&my_charset_latin1,
342                                            (const uchar *)record_begin.str,
343                                            record_begin.length,
344                                            (const uchar *)event_name.str,
345                                            event_name.length, FALSE))
346     {
347       /* Do not expect any more events. */
348       THDVAR(thd, event_record_def)= 0;
349     }
350   }
351   else
352   {
353     const char *buffer;
354 
355     /* We have not started recording of events yet. */
356     if (my_charset_latin1.coll->strnncoll(&my_charset_latin1,
357                                           (const uchar *)record_begin.str,
358                                           record_begin.length,
359                                           (const uchar *)event_name.str,
360                                           event_name.length, FALSE))
361     {
362       /* Event not matching. */
363       return;
364     }
365 
366     buffer= THDVAR(thd, event_record);
367 
368     mysql_mutex_lock(&g_record_buffer_mutex);
369 
370     if (buffer == g_record_buffer)
371     {
372       my_free((void *)(buffer));
373 
374       g_record_buffer= add_event("", event_name, data, data_length);
375 
376       THDVAR(thd, event_record)= g_record_buffer;
377     }
378 
379     mysql_mutex_unlock(&g_record_buffer_mutex);
380 
381     /* Add event. */
382 
383     record_str = (const char *)THDVAR(thd, event_record_def);
384 
385     /* Remove starting event. */
386     memmove((char *)record_str, (void *)record_end.str, record_end.length + 1);
387   }
388 }
389 
process_command(MYSQL_THD thd,LEX_CSTRING event_command,my_bool consume_event)390 static int process_command(MYSQL_THD thd, LEX_CSTRING event_command,
391                            my_bool consume_event)
392 {
393   LEX_CSTRING abort_ret_command= { C_STRING_WITH_LEN("ABORT_RET") };
394 
395   if (!my_charset_latin1.coll->strnncoll(&my_charset_latin1,
396                                          (const uchar *)event_command.str,
397                                          event_command.length,
398                                          (const uchar *)abort_ret_command.str,
399                                          abort_ret_command.length, 0))
400   {
401     int ret_code = (int)THDVAR(thd, abort_value);
402     const char *err_message = (const char *)THDVAR(thd, abort_message);
403     LEX_CSTRING status = { C_STRING_WITH_LEN("EVENT-ORDER-ABORT") };
404     LEX_CSTRING order_cstr;
405 
406     lex_cstring_set(&order_cstr,
407                     (const char *)THDVAR(thd, event_order_check));
408 
409     /* Do not replace order string yet. */
410     if (consume_event)
411     {
412       memmove((char *) order_cstr.str,
413               (void *) status.str, status.length + 1);
414 
415       THDVAR(thd, abort_value)= 1;
416       THDVAR(thd, abort_message)= 0;
417     }
418 
419     if (err_message)
420     {
421       my_message(ER_AUDIT_API_ABORT, err_message, MYF(0));
422       THDVAR(thd, event_order_check)= (char *)order_cstr.str;
423     }
424 
425     return ret_code;
426   }
427 
428   return 0;
429 }
430 
431 /**
432   @brief Plugin function handler.
433 
434   @param thd[in]         Connection context.
435   @param event_class[in] Event class value.
436   @param event[in]       Event data.
437 
438   @retval Value indicating, whether the server should abort continuation
439           of the current oparation.
440 */
audit_null_notify(MYSQL_THD thd,mysql_event_class_t event_class,const void * event)441 static int audit_null_notify(MYSQL_THD thd,
442                              mysql_event_class_t event_class,
443                              const void *event)
444 {
445   char buffer[2000]= { 0, };
446   int buffer_data= 0;
447   unsigned long event_subclass= (unsigned long)*(int *)event;
448   const char *order_str= (const char *)THDVAR(thd, event_order_check);
449   int event_order_started= (int)THDVAR(thd, event_order_started);
450   int exact_check= (int)THDVAR(thd, event_order_check_exact);
451   LEX_CSTRING event_name= event_to_str(event_class, event_subclass);
452   LEX_CSTRING event_token= get_token(&order_str);
453   LEX_CSTRING event_data= get_token(&order_str);
454   LEX_CSTRING event_command= get_token(&order_str);
455   my_bool consume_event= TRUE;
456 
457   /* prone to races, oh well */
458   number_of_calls++;
459 
460   if (event_class == MYSQL_AUDIT_GENERAL_CLASS)
461   {
462     const struct mysql_event_general *event_general=
463                                     (const struct mysql_event_general *)event;
464 
465     switch (event_general->event_subclass)
466     {
467     case MYSQL_AUDIT_GENERAL_LOG:
468       number_of_calls_general_log++;
469       break;
470     case MYSQL_AUDIT_GENERAL_ERROR:
471       number_of_calls_general_error++;
472       break;
473     case MYSQL_AUDIT_GENERAL_RESULT:
474       number_of_calls_general_result++;
475       break;
476     case MYSQL_AUDIT_GENERAL_STATUS:
477       number_of_calls_general_status++;
478       break;
479     default:
480       break;
481     }
482   }
483   else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
484   {
485     const struct mysql_event_connection *event_connection=
486                                 (const struct mysql_event_connection *) event;
487 
488     switch (event_connection->event_subclass)
489     {
490     case MYSQL_AUDIT_CONNECTION_CONNECT:
491       number_of_calls_connection_connect++;
492       break;
493     case MYSQL_AUDIT_CONNECTION_DISCONNECT:
494       number_of_calls_connection_disconnect++;
495       break;
496     case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
497       number_of_calls_connection_change_user++;
498       break;
499     case MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE:
500       number_of_calls_connection_pre_authenticate++;
501         break;
502     default:
503       break;
504     }
505   }
506   else if (event_class == MYSQL_AUDIT_PARSE_CLASS)
507   {
508     const struct mysql_event_parse *event_parse =
509                                       (const struct mysql_event_parse *)event;
510 
511     switch (event_parse->event_subclass)
512     {
513     case MYSQL_AUDIT_PARSE_PREPARSE:
514       number_of_calls_parse_preparse++;
515       break;
516     case MYSQL_AUDIT_PARSE_POSTPARSE:
517       number_of_calls_parse_postparse++;
518       break;
519     default:
520       break;
521     }
522   }
523   /**
524     Currently events not active.
525 
526   else if (event_class == MYSQL_AUDIT_AUTHORIZATION_CLASS)
527   {
528     const struct mysql_event_authorization *event_grant =
529                              (const struct mysql_event_authorization *)event;
530 
531     buffer_data= sprintf(buffer, "db=\"%s\" table=\"%s\" object=\"%s\" "
532                          "requested=\"0x%08x\" granted=\"0x%08x\"",
533                          event_grant->database.str ? event_grant->database.str : "<NULL>",
534                          event_grant->table.str ? event_grant->table.str : "<NULL>",
535                          event_grant->object.str ? event_grant->object.str : "<NULL>",
536                          event_grant->requested_privilege,
537                          event_grant->granted_privilege);
538 
539     switch (event_grant->event_subclass)
540     {
541     case MYSQL_AUDIT_AUTHORIZATION_USER:
542       number_of_calls_authorization_user++;
543       break;
544     case MYSQL_AUDIT_AUTHORIZATION_DB:
545       number_of_calls_authorization_db++;
546       break;
547     case MYSQL_AUDIT_AUTHORIZATION_TABLE:
548       number_of_calls_authorization_table++;
549       break;
550     case MYSQL_AUDIT_AUTHORIZATION_COLUMN:
551       number_of_calls_authorization_column++;
552       break;
553     case MYSQL_AUDIT_AUTHORIZATION_PROCEDURE:
554       number_of_calls_authorization_procedure++;
555       break;
556     case MYSQL_AUDIT_AUTHORIZATION_PROXY:
557       number_of_calls_authorization_proxy++;
558       break;
559     default:
560       break;
561     }
562   }
563   */
564   else if (event_class == MYSQL_AUDIT_SERVER_STARTUP_CLASS)
565   {
566     /* const struct mysql_event_server_startup *event_startup=
567        (const struct mysql_event_server_startup *) event; */
568     number_of_calls_server_startup++;
569   }
570   else if (event_class == MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS)
571   {
572     /* const struct mysql_event_server_shutdown *event_startup=
573        (const struct mysql_event_server_shutdown *) event; */
574     number_of_calls_server_shutdown++;
575   }
576   else if (event_class == MYSQL_AUDIT_COMMAND_CLASS)
577   {
578     const struct mysql_event_command *event_command=
579                                     (const struct mysql_event_command *)event;
580 
581     buffer_data= sprintf(buffer, "command_id=\"%d\"", event_command->command_id);
582 
583     switch (event_command->event_subclass)
584     {
585     case MYSQL_AUDIT_COMMAND_START:
586       number_of_calls_command_start++;
587       break;
588     case MYSQL_AUDIT_COMMAND_END:
589       number_of_calls_command_end++;
590       break;
591     default:
592       break;
593     }
594   }
595   else if (event_class == MYSQL_AUDIT_QUERY_CLASS)
596   {
597     const struct mysql_event_query *event_query=
598                                       (const struct mysql_event_query *)event;
599 
600     buffer_data= sprintf(buffer, "sql_command_id=\"%d\"",
601                          (int) event_query->sql_command_id);
602 
603     switch (event_query->event_subclass)
604     {
605     case MYSQL_AUDIT_QUERY_START:
606       number_of_calls_query_start++;
607       break;
608     case MYSQL_AUDIT_QUERY_NESTED_START:
609       number_of_calls_query_nested_start++;
610       break;
611     case MYSQL_AUDIT_QUERY_STATUS_END:
612       number_of_calls_query_status_end++;
613       break;
614     case MYSQL_AUDIT_QUERY_NESTED_STATUS_END:
615       number_of_calls_query_nested_status_end++;
616       break;
617     default:
618       break;
619     }
620   }
621   else if (event_class == MYSQL_AUDIT_TABLE_ACCESS_CLASS)
622   {
623     const struct mysql_event_table_access *event_table=
624                                (const struct mysql_event_table_access *)event;
625 
626     buffer_data= sprintf(buffer, "db=\"%s\" table=\"%s\"",
627                          event_table->table_database.str,
628                          event_table->table_name.str);
629 
630     switch (event_table->event_subclass)
631     {
632     case MYSQL_AUDIT_TABLE_ACCESS_INSERT:
633       number_of_calls_table_access_insert++;
634       break;
635     case MYSQL_AUDIT_TABLE_ACCESS_DELETE:
636       number_of_calls_table_access_delete++;
637       break;
638     case MYSQL_AUDIT_TABLE_ACCESS_UPDATE:
639       number_of_calls_table_access_update++;
640       break;
641     case MYSQL_AUDIT_TABLE_ACCESS_READ:
642       number_of_calls_table_access_read++;
643       break;
644     default:
645       break;
646     }
647   }
648   else if (event_class == MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS)
649   {
650     const struct mysql_event_global_variable *event_gvar =
651                             (const struct mysql_event_global_variable *)event;
652 
653     /* Copy the variable content into the buffer. We do not guarantee that the
654        variable value will fit into buffer. The buffer should be large enough
655        to be used for the test purposes. */
656     buffer_data= sprintf(buffer, "name=\"%.*s\"",
657                          MY_MIN((int) event_gvar->variable_name.length,
658                                 (int) (sizeof(buffer) - 8)),
659                           event_gvar->variable_name.str);
660 
661     buffer_data+= sprintf(buffer + buffer_data, " value=\"%.*s\"",
662                          MY_MIN((int) event_gvar->variable_value.length,
663                                 (int) (sizeof(buffer) - 16)),
664                           event_gvar->variable_value.str);
665     buffer[buffer_data]= '\0';
666 
667     switch (event_gvar->event_subclass)
668     {
669     case MYSQL_AUDIT_GLOBAL_VARIABLE_GET:
670       number_of_calls_global_variable_get++;
671       break;
672     case MYSQL_AUDIT_GLOBAL_VARIABLE_SET:
673       number_of_calls_global_variable_set++;
674       break;
675     default:
676       break;
677     }
678   }
679 
680   process_event_record(thd, event_name, buffer, buffer_data);
681 
682   if (my_charset_latin1.coll->strnncoll(&my_charset_latin1,
683                                         (const uchar *)event_name.str,
684                                         event_name.length,
685                                         (const uchar *)event_token.str,
686                                         event_token.length, 0))
687   {
688     /* Clear event command. */
689     event_command.str= NULL;
690     event_command.length= 0;
691 
692     if (exact_check == 1 && event_order_started == 1)
693     {
694       if (!(event_class == MYSQL_AUDIT_GENERAL_CLASS &&
695             event_subclass == MYSQL_AUDIT_GENERAL_ERROR))
696       {
697         strxnmov(buffer, sizeof(buffer), event_name.str, " instead of ",
698                  event_token.str, NullS);
699         my_message(ER_AUDIT_API_ABORT, buffer, MYF(0));
700       }
701 
702       THDVAR(thd, event_order_started)= 0;
703       THDVAR(thd, event_order_check)= 0;
704 
705       return 1;
706     }
707   }
708   else
709   {
710     LEX_CSTRING ignore= { C_STRING_WITH_LEN("<IGNORE>") };
711 
712     /* When we are not in the event order check, check if the specified
713        data corresponds to the actual event data. */
714     if (my_charset_latin1.coll->strnncoll(&my_charset_latin1,
715                                           (const uchar *)event_data.str,
716                                           event_data.length,
717                                           (const uchar *) ignore.str,
718                                           ignore.length, 0) &&
719         my_charset_latin1.coll->strnncoll(&my_charset_latin1,
720                                           (const uchar *) event_data.str,
721                                           event_data.length,
722                                           (const uchar *)buffer,
723                                           (size_t)buffer_data, 0))
724     {
725       if (exact_check == 1 && event_order_started == 1)
726       {
727         char invalid_data_buffer[sizeof(buffer)]= { 0, };
728         LEX_CSTRING status= { C_STRING_WITH_LEN("EVENT-ORDER-INVALID-DATA") };
729         LEX_CSTRING order_cstr;
730 
731         lex_cstring_set(&order_cstr,
732                         (const char *)THDVAR(thd, event_order_check));
733 
734         memmove((char *)order_cstr.str,
735                 (void *)status.str, status.length + 1);
736 
737         strxnmov(invalid_data_buffer, sizeof(invalid_data_buffer),
738                  "Invalid data for '", event_name.str, "' -> ", buffer, NullS);
739         my_message(ER_AUDIT_API_ABORT, invalid_data_buffer, MYF(0));
740 
741         THDVAR(thd, event_order_started)= 0;
742         THDVAR(thd, event_order_check)= (char *)order_cstr.str;
743 
744         return 1;
745       }
746 
747       /* Clear event command. */
748       event_command.str= NULL;
749       event_command.length= 0;
750     }
751     else
752     {
753       LEX_CSTRING order_cstr;
754       ulong consume= THDVAR(thd, event_order_check_consume_ignore_count);
755       lex_cstring_set(&order_cstr,
756                       (const char *)THDVAR(thd, event_order_check));
757 
758       THDVAR(thd, event_order_started)= 1;
759 
760       if (consume)
761       {
762         /*
763           Do not consume event this time. Just decrease value and wait until
764           the next event is matched.
765         */
766         THDVAR(thd, event_order_check_consume_ignore_count)= consume - 1;
767         consume_event= FALSE;
768       }
769       else
770       {
771         /* Consume matched event. */
772         memmove((char*)order_cstr.str, (void*)order_str,
773           order_cstr.length - (order_str - order_cstr.str) + 1);
774 
775         /* Count new length. */
776         lex_cstring_set(&order_cstr, order_cstr.str);
777 
778         if (order_cstr.length == 0)
779         {
780           LEX_CSTRING status = { C_STRING_WITH_LEN("EVENT-ORDER-OK") };
781 
782           memmove((char *)order_cstr.str,
783                   (void *)status.str, status.length + 1);
784 
785           /* event_order_started contains message. Do not verify it. */
786           THDVAR(thd, event_order_started)= 0;
787         }
788       }
789     }
790   }
791 
792   return process_command(thd, event_command, consume_event);
793 }
794 
795 /*
796   Plugin type-specific descriptor
797 */
798 
799 static struct st_mysql_audit audit_null_descriptor=
800 {
801   MYSQL_AUDIT_INTERFACE_VERSION,                    /* interface version    */
802   NULL,                                             /* release_thd function */
803   audit_null_notify,                                /* notify function      */
804   { (unsigned long) MYSQL_AUDIT_GENERAL_ALL,
805     (unsigned long) MYSQL_AUDIT_CONNECTION_ALL,
806     (unsigned long) MYSQL_AUDIT_PARSE_ALL,
807     0, /* This event class is currently not supported. */
808     (unsigned long) MYSQL_AUDIT_TABLE_ACCESS_ALL,
809     (unsigned long) MYSQL_AUDIT_GLOBAL_VARIABLE_ALL,
810     (unsigned long) MYSQL_AUDIT_SERVER_STARTUP_ALL,
811     (unsigned long) MYSQL_AUDIT_SERVER_SHUTDOWN_ALL,
812     (unsigned long) MYSQL_AUDIT_COMMAND_ALL,
813     (unsigned long) MYSQL_AUDIT_QUERY_ALL,
814     (unsigned long) MYSQL_AUDIT_STORED_PROGRAM_ALL }
815 };
816 
817 static struct st_mysql_sys_var* system_variables[] = {
818 
819   MYSQL_SYSVAR(abort_message),
820   MYSQL_SYSVAR(abort_value),
821 
822   MYSQL_SYSVAR(event_order_check),
823   MYSQL_SYSVAR(event_order_check_consume_ignore_count),
824   MYSQL_SYSVAR(event_order_started),
825   MYSQL_SYSVAR(event_order_check_exact),
826 
827   MYSQL_SYSVAR(event_record_def),
828   MYSQL_SYSVAR(event_record),
829   NULL
830 };
831 
832 /*
833   Plugin library descriptor
834 */
835 
mysql_declare_plugin(audit_null)836 mysql_declare_plugin(audit_null)
837 {
838   MYSQL_AUDIT_PLUGIN,         /* type                            */
839   &audit_null_descriptor,     /* descriptor                      */
840   "NULL_AUDIT",               /* name                            */
841   "Oracle Corp",              /* author                          */
842   "Simple NULL Audit",        /* description                     */
843   PLUGIN_LICENSE_GPL,
844   audit_null_plugin_init,     /* init function (when loaded)     */
845   audit_null_plugin_deinit,   /* deinit function (when unloaded) */
846   0x0003,                     /* version                         */
847   simple_status,              /* status variables                */
848   system_variables,           /* system variables                */
849   NULL,
850   0,
851 }
852 mysql_declare_plugin_end;
853