853 lines
28 KiB
C++
853 lines
28 KiB
C++
/* Copyright (c) 2010, 2019, 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, version 2.0,
|
|
as published by the Free Software Foundation.
|
|
|
|
This program is also distributed with certain software (including
|
|
but not limited to OpenSSL) that is licensed under separate terms,
|
|
as designated in a particular file or component or in included license
|
|
documentation. The authors of MySQL hereby grant you an additional
|
|
permission to link the program and your derivative works with the
|
|
separately licensed software that they have included with MySQL.
|
|
|
|
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, version 2.0, 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-1301 USA */
|
|
|
|
#include <mysql/plugin.h>
|
|
#include <mysql/plugin_audit.h>
|
|
#include <mysqld_error.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "lex_string.h"
|
|
#include "m_ctype.h"
|
|
#include "my_compiler.h"
|
|
#include "my_inttypes.h"
|
|
#include "my_macros.h"
|
|
#include "my_sys.h"
|
|
#include "mysql/psi/mysql_mutex.h"
|
|
#include "thr_mutex.h"
|
|
|
|
/** Event strings. */
|
|
LEX_CSTRING event_names[][6] = {
|
|
/** MYSQL_AUDIT_GENERAL_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_LOG")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_ERROR")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_RESULT")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_GENERAL_STATUS")},
|
|
},
|
|
/** MYSQL_AUDIT_CONNECTION_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_CONNECT")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_DISCONNECT")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_CHANGE_USER")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE")},
|
|
},
|
|
/** MYSQL_AUDIT_PARSE_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_PARSE_PREPARSE")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_PARSE_POSTPARSE")},
|
|
},
|
|
/** MYSQL_AUDIT_AUTHORIZATION_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_USER")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_DB")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_TABLE")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_COLUMN")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_PROCEDURE")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHORIZATION_PROXY")},
|
|
},
|
|
/** MYSQL_AUDIT_TABLE_ROW_ACCES_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_READ")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_INSERT")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_UPDATE")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_TABLE_ACCESS_DELETE")},
|
|
},
|
|
/** MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_GLOBAL_VARIABLE_GET")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_GLOBAL_VARIABLE_SET")},
|
|
},
|
|
/** MYSQL_AUDIT_SERVER_STARTUP_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_SERVER_STARTUP_STARTUP")},
|
|
},
|
|
/** MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN")},
|
|
},
|
|
/** MYSQL_AUDIT_COMMAND_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_COMMAND_START")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_COMMAND_END")},
|
|
},
|
|
/** MYSQL_AUDIT_QUERY_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_QUERY_START")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_QUERY_NESTED_START")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_QUERY_STATUS_END")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_QUERY_NESTED_STATUS_END")},
|
|
},
|
|
/** MYSQL_AUDIT_STORED_PROGRAM_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_STORED_PROGRAM_EXECUTE")},
|
|
},
|
|
/** MYSQL_AUDIT_AUTHENTICATION_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHENTICATION_FLUSH")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHENTICATION_AUTHID_CREATE")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHENTICATION_CREDENTIAL_CHANGE")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHENTICATION_AUTHID_RENAME")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_AUTHENTICATION_AUTHID_DROP")},
|
|
},
|
|
/** MYSQL_AUDIT_MESSAGE_CLASS */
|
|
{
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_MESSAGE_INTERNAL")},
|
|
{STRING_WITH_LEN("MYSQL_AUDIT_MESSAGE_USER")},
|
|
}};
|
|
|
|
static volatile int number_of_calls;
|
|
|
|
/*
|
|
Plugin has been installed.
|
|
*/
|
|
static bool g_plugin_installed = false;
|
|
|
|
/*
|
|
Record buffer mutex.
|
|
*/
|
|
static mysql_mutex_t g_record_buffer_mutex;
|
|
|
|
/*
|
|
Event recording buffer.
|
|
*/
|
|
static char *g_record_buffer;
|
|
|
|
#define AUDIT_NULL_VAR(x) static volatile int number_of_calls_##x;
|
|
#include "plugin/audit_null/audit_null_variables.h"
|
|
|
|
#undef AUDIT_NULL_VAR
|
|
|
|
/*
|
|
Plugin status variables for SHOW STATUS
|
|
*/
|
|
|
|
static SHOW_VAR simple_status[] = {
|
|
{"Audit_null_called",
|
|
const_cast<char *>(reinterpret_cast<volatile char *>(&number_of_calls)),
|
|
SHOW_INT, SHOW_SCOPE_GLOBAL},
|
|
|
|
#define AUDIT_NULL_VAR(x) \
|
|
{"Audit_null_" #x, \
|
|
const_cast<char *>( \
|
|
reinterpret_cast<volatile char *>(&number_of_calls_##x)), \
|
|
SHOW_INT, SHOW_SCOPE_GLOBAL},
|
|
#include "plugin/audit_null/audit_null_variables.h"
|
|
|
|
#undef AUDIT_NULL_VAR
|
|
|
|
{0, 0, SHOW_UNDEF, SHOW_SCOPE_GLOBAL}};
|
|
|
|
/*
|
|
Define plugin variables.
|
|
*/
|
|
|
|
static MYSQL_THDVAR_STR(abort_message,
|
|
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
|
|
"Custom message for event abort.", NULL, NULL, NULL);
|
|
|
|
static MYSQL_THDVAR_INT(abort_value, PLUGIN_VAR_RQCMDARG, "Event abort value.",
|
|
NULL, NULL, 1, -1, 150, 0);
|
|
|
|
static MYSQL_THDVAR_STR(event_order_check,
|
|
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
|
|
"Event order check string", NULL, NULL, NULL);
|
|
|
|
static MYSQL_THDVAR_UINT(event_order_check_consume_ignore_count,
|
|
PLUGIN_VAR_RQCMDARG,
|
|
"Do not consume event order string specified "
|
|
"number of times.",
|
|
NULL, NULL, 0, 0, UINT_MAX, 1);
|
|
|
|
static MYSQL_THDVAR_INT(event_order_started, PLUGIN_VAR_RQCMDARG,
|
|
"Plugin is in the event order check.", NULL, NULL, 0, 0,
|
|
1, 0);
|
|
|
|
static MYSQL_THDVAR_INT(event_order_check_exact, PLUGIN_VAR_RQCMDARG,
|
|
"Plugin checks exact event order.", NULL, NULL, 1, 0, 1,
|
|
0);
|
|
|
|
static MYSQL_THDVAR_STR(event_record_def,
|
|
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
|
|
"Event recording definition", NULL, NULL, NULL);
|
|
|
|
static MYSQL_THDVAR_STR(event_record,
|
|
PLUGIN_VAR_READONLY | PLUGIN_VAR_RQCMDARG |
|
|
PLUGIN_VAR_MEMALLOC,
|
|
"Event recording", NULL, NULL, NULL);
|
|
/*
|
|
Initialize the plugin at server start or plugin installation.
|
|
|
|
SYNOPSIS
|
|
audit_null_plugin_init()
|
|
|
|
DESCRIPTION
|
|
Does nothing.
|
|
|
|
RETURN VALUE
|
|
0 success
|
|
1 failure (cannot happen)
|
|
*/
|
|
|
|
static int audit_null_plugin_init(void *arg MY_ATTRIBUTE((unused))) {
|
|
SHOW_VAR *var;
|
|
|
|
for (var = simple_status; var->value != 0; var++) {
|
|
*((int *)var->value) = 0;
|
|
}
|
|
|
|
mysql_mutex_init(PSI_NOT_INSTRUMENTED, &g_record_buffer_mutex,
|
|
MY_MUTEX_INIT_FAST);
|
|
|
|
g_record_buffer = NULL;
|
|
g_plugin_installed = true;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
Terminate the plugin at server shutdown or plugin deinstallation.
|
|
|
|
SYNOPSIS
|
|
audit_null_plugin_deinit()
|
|
Does nothing.
|
|
|
|
RETURN VALUE
|
|
0 success
|
|
1 failure (cannot happen)
|
|
|
|
*/
|
|
|
|
static int audit_null_plugin_deinit(void *arg MY_ATTRIBUTE((unused))) {
|
|
if (g_plugin_installed == true) {
|
|
my_free((void *)(g_record_buffer));
|
|
|
|
g_record_buffer = NULL;
|
|
|
|
mysql_mutex_destroy(&g_record_buffer_mutex);
|
|
|
|
g_plugin_installed = false;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
@brief Converts event_class and event_subclass into a string.
|
|
|
|
@param [in] event_class Event class value.
|
|
@param [in] event_subclass Event subclass value.
|
|
|
|
@retval Event name.
|
|
*/
|
|
static LEX_CSTRING event_to_str(unsigned int event_class,
|
|
unsigned long event_subclass) {
|
|
int count;
|
|
for (count = 0; event_subclass; count++, event_subclass >>= 1)
|
|
;
|
|
|
|
return event_names[event_class][count - 1];
|
|
}
|
|
|
|
/**
|
|
@brief Read token delimited by a semicolon from a string.
|
|
|
|
@param [in,out] str Pointer to a string containing text.
|
|
Pointer is moved to a new token after the function ends.
|
|
|
|
@retval Token retrieved from a string.
|
|
*/
|
|
static LEX_CSTRING get_token(char **str) {
|
|
LEX_CSTRING ret = {NULL, 0};
|
|
|
|
if (*str != NULL) {
|
|
ret.str = *str;
|
|
|
|
if (*ret.str != '\0') {
|
|
/* Find a param delimiter. */
|
|
while (**str && **str != ';') (*str)++;
|
|
|
|
ret.length = *str - ret.str;
|
|
|
|
/* Skip a delimiter. */
|
|
if (**str == ';') (*str)++;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static char *add_event(const char *var, LEX_CSTRING event, const char *data,
|
|
size_t data_length) {
|
|
LEX_CSTRING str;
|
|
size_t size;
|
|
char *buffer;
|
|
|
|
lex_cstring_set(&str, var);
|
|
|
|
size = str.length + event.length + data_length + 4;
|
|
buffer = (char *)my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_FAE));
|
|
|
|
snprintf(buffer, size, "%s%s%s;%s;", var, str.length == 0 ? "" : "\n",
|
|
event.str, data);
|
|
|
|
buffer[size - (str.length == 0 ? 2 : 1)] = '\0';
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static void process_event_record(MYSQL_THD thd, LEX_CSTRING event_name,
|
|
const char *data, size_t data_length) {
|
|
char *record_str = THDVAR(thd, event_record_def);
|
|
LEX_CSTRING record_begin = get_token(&record_str);
|
|
LEX_CSTRING record_end = get_token(&record_str);
|
|
|
|
if (record_str == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (record_end.length == 0) {
|
|
/* We are already in the consuming phase. Add a new event name into
|
|
a record variable */
|
|
|
|
const char *buffer = THDVAR(thd, event_record);
|
|
char *new_buffer = NULL;
|
|
|
|
/* Add event. */
|
|
mysql_mutex_lock(&g_record_buffer_mutex);
|
|
|
|
/* Only one THD is capable of adding events into the buffer. */
|
|
if (buffer && (buffer == g_record_buffer)) {
|
|
new_buffer = add_event(buffer, event_name, data, data_length);
|
|
g_record_buffer = new_buffer;
|
|
my_free(const_cast<char *>(buffer));
|
|
}
|
|
|
|
mysql_mutex_unlock(&g_record_buffer_mutex);
|
|
|
|
THDVAR(thd, event_record) = new_buffer;
|
|
|
|
if (!my_charset_latin1.coll->strnncoll(
|
|
&my_charset_latin1, (const uchar *)record_begin.str,
|
|
record_begin.length, (const uchar *)event_name.str,
|
|
event_name.length, false)) {
|
|
/* Do not expect any more events. */
|
|
THDVAR(thd, event_record_def) = 0;
|
|
}
|
|
} else {
|
|
const char *buffer;
|
|
|
|
/* We have not started recording of events yet. */
|
|
if (my_charset_latin1.coll->strnncoll(
|
|
&my_charset_latin1, (const uchar *)record_begin.str,
|
|
record_begin.length, (const uchar *)event_name.str,
|
|
event_name.length, false)) {
|
|
/* Event not matching. */
|
|
return;
|
|
}
|
|
|
|
buffer = THDVAR(thd, event_record);
|
|
|
|
mysql_mutex_lock(&g_record_buffer_mutex);
|
|
|
|
if (buffer == g_record_buffer) {
|
|
my_free(const_cast<char *>(buffer));
|
|
|
|
g_record_buffer = add_event("", event_name, data, data_length);
|
|
|
|
THDVAR(thd, event_record) = g_record_buffer;
|
|
}
|
|
|
|
mysql_mutex_unlock(&g_record_buffer_mutex);
|
|
|
|
/* Add event. */
|
|
|
|
record_str = THDVAR(thd, event_record_def);
|
|
|
|
/* Remove starting event. */
|
|
memmove(record_str, record_end.str, record_end.length + 1);
|
|
}
|
|
}
|
|
|
|
static int process_command(MYSQL_THD thd, LEX_CSTRING event_command,
|
|
bool consume_event) {
|
|
LEX_CSTRING abort_ret_command = {STRING_WITH_LEN("ABORT_RET")};
|
|
|
|
if (!my_charset_latin1.coll->strnncoll(
|
|
&my_charset_latin1, (const uchar *)event_command.str,
|
|
event_command.length, (const uchar *)abort_ret_command.str,
|
|
abort_ret_command.length, 0)) {
|
|
int ret_code = (int)THDVAR(thd, abort_value);
|
|
const char *err_message = (const char *)THDVAR(thd, abort_message);
|
|
LEX_CSTRING status = {STRING_WITH_LEN("EVENT-ORDER-ABORT")};
|
|
|
|
char *order_str = THDVAR(thd, event_order_check);
|
|
|
|
/* Do not replace order string yet. */
|
|
if (consume_event) {
|
|
memmove(order_str, status.str, status.length + 1);
|
|
|
|
THDVAR(thd, abort_value) = 1;
|
|
THDVAR(thd, abort_message) = 0;
|
|
}
|
|
|
|
if (err_message) {
|
|
my_message(ER_AUDIT_API_ABORT, err_message, MYF(0));
|
|
THDVAR(thd, event_order_check) = order_str;
|
|
}
|
|
|
|
return ret_code;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
@brief Plugin function handler.
|
|
|
|
@param [in] thd Connection context.
|
|
@param [in] event_class Event class value.
|
|
@param [in] event Event data.
|
|
|
|
@retval Value indicating, whether the server should abort continuation
|
|
of the current operation.
|
|
*/
|
|
static int audit_null_notify(MYSQL_THD thd, mysql_event_class_t event_class,
|
|
const void *event) {
|
|
char buffer[2000] = {
|
|
0,
|
|
};
|
|
int buffer_data = 0;
|
|
unsigned long event_subclass =
|
|
static_cast<unsigned long>(*static_cast<const int *>(event));
|
|
char *order_str = THDVAR(thd, event_order_check);
|
|
int event_order_started = (int)THDVAR(thd, event_order_started);
|
|
int exact_check = (int)THDVAR(thd, event_order_check_exact);
|
|
LEX_CSTRING event_name = event_to_str(event_class, event_subclass);
|
|
LEX_CSTRING event_token = get_token(&order_str);
|
|
LEX_CSTRING event_data = get_token(&order_str);
|
|
LEX_CSTRING event_command = get_token(&order_str);
|
|
bool consume_event = true;
|
|
|
|
/* prone to races, oh well */
|
|
number_of_calls++;
|
|
|
|
if (event_class == MYSQL_AUDIT_GENERAL_CLASS) {
|
|
const struct mysql_event_general *event_general =
|
|
(const struct mysql_event_general *)event;
|
|
|
|
switch (event_general->event_subclass) {
|
|
case MYSQL_AUDIT_GENERAL_LOG:
|
|
number_of_calls_general_log++;
|
|
break;
|
|
case MYSQL_AUDIT_GENERAL_ERROR:
|
|
number_of_calls_general_error++;
|
|
break;
|
|
case MYSQL_AUDIT_GENERAL_RESULT:
|
|
number_of_calls_general_result++;
|
|
break;
|
|
case MYSQL_AUDIT_GENERAL_STATUS:
|
|
number_of_calls_general_status++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) {
|
|
const struct mysql_event_connection *event_connection =
|
|
(const struct mysql_event_connection *)event;
|
|
|
|
switch (event_connection->event_subclass) {
|
|
case MYSQL_AUDIT_CONNECTION_CONNECT:
|
|
number_of_calls_connection_connect++;
|
|
break;
|
|
case MYSQL_AUDIT_CONNECTION_DISCONNECT:
|
|
number_of_calls_connection_disconnect++;
|
|
break;
|
|
case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
|
|
number_of_calls_connection_change_user++;
|
|
break;
|
|
case MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE:
|
|
number_of_calls_connection_pre_authenticate++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event_class == MYSQL_AUDIT_PARSE_CLASS) {
|
|
const struct mysql_event_parse *event_parse =
|
|
(const struct mysql_event_parse *)event;
|
|
|
|
switch (event_parse->event_subclass) {
|
|
case MYSQL_AUDIT_PARSE_PREPARSE:
|
|
number_of_calls_parse_preparse++;
|
|
break;
|
|
case MYSQL_AUDIT_PARSE_POSTPARSE:
|
|
number_of_calls_parse_postparse++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#if 0
|
|
/**
|
|
Currently events not active.
|
|
|
|
else if (event_class == MYSQL_AUDIT_AUTHORIZATION_CLASS)
|
|
{
|
|
const struct mysql_event_authorization *event_grant =
|
|
(const struct mysql_event_authorization *)event;
|
|
|
|
buffer_data= sprintf(buffer, "db=\"%s\" table=\"%s\" object=\"%s\" "
|
|
"requested=\"0x%08x\" granted=\"0x%08x\"",
|
|
event_grant->database.str ? event_grant->database.str : "<NULL>",
|
|
event_grant->table.str ? event_grant->table.str : "<NULL>",
|
|
event_grant->object.str ? event_grant->object.str : "<NULL>",
|
|
event_grant->requested_privilege,
|
|
event_grant->granted_privilege);
|
|
|
|
switch (event_grant->event_subclass)
|
|
{
|
|
case MYSQL_AUDIT_AUTHORIZATION_USER:
|
|
number_of_calls_authorization_user++;
|
|
break;
|
|
case MYSQL_AUDIT_AUTHORIZATION_DB:
|
|
number_of_calls_authorization_db++;
|
|
break;
|
|
case MYSQL_AUDIT_AUTHORIZATION_TABLE:
|
|
number_of_calls_authorization_table++;
|
|
break;
|
|
case MYSQL_AUDIT_AUTHORIZATION_COLUMN:
|
|
number_of_calls_authorization_column++;
|
|
break;
|
|
case MYSQL_AUDIT_AUTHORIZATION_PROCEDURE:
|
|
number_of_calls_authorization_procedure++;
|
|
break;
|
|
case MYSQL_AUDIT_AUTHORIZATION_PROXY:
|
|
number_of_calls_authorization_proxy++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
#endif
|
|
else if (event_class == MYSQL_AUDIT_SERVER_STARTUP_CLASS) {
|
|
/* const struct mysql_event_server_startup *event_startup=
|
|
(const struct mysql_event_server_startup *) event; */
|
|
number_of_calls_server_startup++;
|
|
} else if (event_class == MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS) {
|
|
/* const struct mysql_event_server_shutdown *event_startup=
|
|
(const struct mysql_event_server_shutdown *) event; */
|
|
number_of_calls_server_shutdown++;
|
|
} else if (event_class == MYSQL_AUDIT_COMMAND_CLASS) {
|
|
const struct mysql_event_command *event_command =
|
|
(const struct mysql_event_command *)event;
|
|
|
|
buffer_data =
|
|
sprintf(buffer, "command_id=\"%d\"", event_command->command_id);
|
|
|
|
switch (event_command->event_subclass) {
|
|
case MYSQL_AUDIT_COMMAND_START:
|
|
number_of_calls_command_start++;
|
|
break;
|
|
case MYSQL_AUDIT_COMMAND_END:
|
|
number_of_calls_command_end++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event_class == MYSQL_AUDIT_QUERY_CLASS) {
|
|
const struct mysql_event_query *event_query =
|
|
(const struct mysql_event_query *)event;
|
|
|
|
buffer_data = sprintf(buffer, "sql_command_id=\"%d\"",
|
|
(int)event_query->sql_command_id);
|
|
|
|
switch (event_query->event_subclass) {
|
|
case MYSQL_AUDIT_QUERY_START:
|
|
number_of_calls_query_start++;
|
|
break;
|
|
case MYSQL_AUDIT_QUERY_NESTED_START:
|
|
number_of_calls_query_nested_start++;
|
|
break;
|
|
case MYSQL_AUDIT_QUERY_STATUS_END:
|
|
number_of_calls_query_status_end++;
|
|
break;
|
|
case MYSQL_AUDIT_QUERY_NESTED_STATUS_END:
|
|
number_of_calls_query_nested_status_end++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event_class == MYSQL_AUDIT_TABLE_ACCESS_CLASS) {
|
|
const struct mysql_event_table_access *event_table =
|
|
(const struct mysql_event_table_access *)event;
|
|
|
|
buffer_data =
|
|
sprintf(buffer, "db=\"%s\" table=\"%s\"",
|
|
event_table->table_database.str, event_table->table_name.str);
|
|
|
|
switch (event_table->event_subclass) {
|
|
case MYSQL_AUDIT_TABLE_ACCESS_INSERT:
|
|
number_of_calls_table_access_insert++;
|
|
break;
|
|
case MYSQL_AUDIT_TABLE_ACCESS_DELETE:
|
|
number_of_calls_table_access_delete++;
|
|
break;
|
|
case MYSQL_AUDIT_TABLE_ACCESS_UPDATE:
|
|
number_of_calls_table_access_update++;
|
|
break;
|
|
case MYSQL_AUDIT_TABLE_ACCESS_READ:
|
|
number_of_calls_table_access_read++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event_class == MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS) {
|
|
const struct mysql_event_global_variable *event_gvar =
|
|
(const struct mysql_event_global_variable *)event;
|
|
|
|
/* Copy the variable content into the buffer. We do not guarantee that the
|
|
variable value will fit into buffer. The buffer should be large enough
|
|
to be used for the test purposes. */
|
|
buffer_data = sprintf(buffer, "name=\"%.*s\"",
|
|
MY_MIN((int)event_gvar->variable_name.length,
|
|
(int)(sizeof(buffer) - 8)),
|
|
event_gvar->variable_name.str);
|
|
|
|
buffer_data += sprintf(buffer + buffer_data, " value=\"%.*s\"",
|
|
MY_MIN((int)event_gvar->variable_value.length,
|
|
(int)(sizeof(buffer) - 16)),
|
|
event_gvar->variable_value.str);
|
|
buffer[buffer_data] = '\0';
|
|
|
|
switch (event_gvar->event_subclass) {
|
|
case MYSQL_AUDIT_GLOBAL_VARIABLE_GET:
|
|
number_of_calls_global_variable_get++;
|
|
break;
|
|
case MYSQL_AUDIT_GLOBAL_VARIABLE_SET:
|
|
number_of_calls_global_variable_set++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event_class == MYSQL_AUDIT_MESSAGE_CLASS) {
|
|
const struct mysql_event_message *evt =
|
|
reinterpret_cast<const struct mysql_event_message *>(event);
|
|
|
|
buffer_data +=
|
|
snprintf(buffer, sizeof(buffer) - 1,
|
|
"component=\"%.*s\" producer=\"%.*s\" message=\"%.*s\"",
|
|
static_cast<int>(evt->component.length), evt->component.str,
|
|
static_cast<int>(evt->producer.length), evt->producer.str,
|
|
static_cast<int>(evt->message.length), evt->message.str);
|
|
|
|
for (size_t i = 0; i < evt->key_value_map_length; ++i) {
|
|
if (evt->key_value_map[i].value_type ==
|
|
MYSQL_AUDIT_MESSAGE_VALUE_TYPE_STR &&
|
|
evt->key_value_map[i].value.str.str == nullptr)
|
|
buffer_data +=
|
|
snprintf(buffer + buffer_data, sizeof(buffer) - buffer_data - 1,
|
|
" key[%zu]=\"%.*s\" value[%zu]=null", i,
|
|
static_cast<int>(evt->key_value_map[i].key.length),
|
|
evt->key_value_map[i].key.str, i);
|
|
else if (evt->key_value_map[i].value_type ==
|
|
MYSQL_AUDIT_MESSAGE_VALUE_TYPE_STR)
|
|
buffer_data +=
|
|
snprintf(buffer + buffer_data, sizeof(buffer) - buffer_data - 1,
|
|
" key[%zu]=\"%.*s\" value[%zu]=\"%.*s\"", i,
|
|
static_cast<int>(evt->key_value_map[i].key.length),
|
|
evt->key_value_map[i].key.str, i,
|
|
static_cast<int>(evt->key_value_map[i].value.str.length),
|
|
evt->key_value_map[i].value.str.str);
|
|
else if (evt->key_value_map[i].value_type ==
|
|
MYSQL_AUDIT_MESSAGE_VALUE_TYPE_NUM)
|
|
buffer_data += snprintf(
|
|
buffer + buffer_data, sizeof(buffer) - buffer_data - 1,
|
|
" key[%zu]=\"%.*s\" value[%zu]=%lld", i,
|
|
static_cast<int>(evt->key_value_map[i].key.length),
|
|
evt->key_value_map[i].key.str, i, evt->key_value_map[i].value.num);
|
|
}
|
|
|
|
buffer[buffer_data] = '\0';
|
|
|
|
switch (evt->event_subclass) {
|
|
case MYSQL_AUDIT_MESSAGE_INTERNAL:
|
|
number_of_calls_message_internal++;
|
|
break;
|
|
case MYSQL_AUDIT_MESSAGE_USER:
|
|
number_of_calls_message_user++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
process_event_record(thd, event_name, buffer, buffer_data);
|
|
|
|
if (my_charset_latin1.coll->strnncoll(
|
|
&my_charset_latin1, (const uchar *)event_name.str, event_name.length,
|
|
(const uchar *)event_token.str, event_token.length, 0)) {
|
|
/* Clear event command. */
|
|
event_command.str = NULL;
|
|
event_command.length = 0;
|
|
|
|
if (exact_check == 1 && event_order_started == 1) {
|
|
if (!(event_class == MYSQL_AUDIT_GENERAL_CLASS &&
|
|
event_subclass == MYSQL_AUDIT_GENERAL_ERROR)) {
|
|
strxnmov(buffer, sizeof(buffer), event_name.str, " instead of ",
|
|
event_token.str, NullS);
|
|
my_message(ER_AUDIT_API_ABORT, buffer, MYF(0));
|
|
}
|
|
|
|
THDVAR(thd, event_order_started) = 0;
|
|
THDVAR(thd, event_order_check) = 0;
|
|
|
|
return 1;
|
|
}
|
|
} else {
|
|
LEX_CSTRING ignore = {STRING_WITH_LEN("<IGNORE>")};
|
|
|
|
/* When we are not in the event order check, check if the specified
|
|
data corresponds to the actual event data. */
|
|
if (my_charset_latin1.coll->strnncoll(
|
|
&my_charset_latin1, (const uchar *)event_data.str,
|
|
event_data.length, (const uchar *)ignore.str, ignore.length, 0) &&
|
|
my_charset_latin1.coll->strnncoll(
|
|
&my_charset_latin1, (const uchar *)event_data.str,
|
|
event_data.length, (const uchar *)buffer, (size_t)buffer_data, 0)) {
|
|
if (exact_check == 1 && event_order_started == 1) {
|
|
char invalid_data_buffer[sizeof(buffer)] = {
|
|
0,
|
|
};
|
|
LEX_CSTRING status = {STRING_WITH_LEN("EVENT-ORDER-INVALID-DATA")};
|
|
|
|
char *order_str = THDVAR(thd, event_order_check);
|
|
|
|
memmove(order_str, status.str, status.length + 1);
|
|
|
|
strxnmov(invalid_data_buffer, sizeof(invalid_data_buffer),
|
|
"Invalid data for '", event_name.str, "' -> ", buffer, NullS);
|
|
my_message(ER_AUDIT_API_ABORT, invalid_data_buffer, MYF(0));
|
|
|
|
THDVAR(thd, event_order_started) = 0;
|
|
THDVAR(thd, event_order_check) = order_str;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Clear event command. */
|
|
event_command.str = NULL;
|
|
event_command.length = 0;
|
|
} else {
|
|
LEX_STRING order_cstr;
|
|
ulong consume = THDVAR(thd, event_order_check_consume_ignore_count);
|
|
lex_string_set(&order_cstr, THDVAR(thd, event_order_check));
|
|
|
|
THDVAR(thd, event_order_started) = 1;
|
|
|
|
if (consume) {
|
|
/*
|
|
Do not consume event this time. Just decrease value and wait until
|
|
the next event is matched.
|
|
*/
|
|
THDVAR(thd, event_order_check_consume_ignore_count) = consume - 1;
|
|
consume_event = false;
|
|
} else {
|
|
/* Consume matched event. */
|
|
memmove(order_cstr.str, order_str,
|
|
order_cstr.length - (order_str - order_cstr.str) + 1);
|
|
|
|
/* Count new length. */
|
|
lex_string_set(&order_cstr, order_cstr.str);
|
|
|
|
if (order_cstr.length == 0) {
|
|
LEX_CSTRING status = {STRING_WITH_LEN("EVENT-ORDER-OK")};
|
|
|
|
memmove(order_cstr.str, status.str, status.length + 1);
|
|
|
|
/* event_order_started contains message. Do not verify it. */
|
|
THDVAR(thd, event_order_started) = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return process_command(thd, event_command, consume_event);
|
|
}
|
|
|
|
/*
|
|
Plugin type-specific descriptor
|
|
*/
|
|
|
|
static struct st_mysql_audit audit_null_descriptor = {
|
|
MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */
|
|
NULL, /* release_thd function */
|
|
audit_null_notify, /* notify function */
|
|
{(unsigned long)MYSQL_AUDIT_GENERAL_ALL,
|
|
(unsigned long)MYSQL_AUDIT_CONNECTION_ALL,
|
|
(unsigned long)MYSQL_AUDIT_PARSE_ALL,
|
|
0, /* This event class is currently not supported. */
|
|
(unsigned long)MYSQL_AUDIT_TABLE_ACCESS_ALL,
|
|
(unsigned long)MYSQL_AUDIT_GLOBAL_VARIABLE_ALL,
|
|
(unsigned long)MYSQL_AUDIT_SERVER_STARTUP_ALL,
|
|
(unsigned long)MYSQL_AUDIT_SERVER_SHUTDOWN_ALL,
|
|
(unsigned long)MYSQL_AUDIT_COMMAND_ALL,
|
|
(unsigned long)MYSQL_AUDIT_QUERY_ALL,
|
|
(unsigned long)MYSQL_AUDIT_STORED_PROGRAM_ALL,
|
|
(unsigned long)MYSQL_AUDIT_AUTHENTICATION_ALL,
|
|
(unsigned long)MYSQL_AUDIT_MESSAGE_ALL}};
|
|
|
|
static SYS_VAR *system_variables[] = {
|
|
|
|
MYSQL_SYSVAR(abort_message),
|
|
MYSQL_SYSVAR(abort_value),
|
|
|
|
MYSQL_SYSVAR(event_order_check),
|
|
MYSQL_SYSVAR(event_order_check_consume_ignore_count),
|
|
MYSQL_SYSVAR(event_order_started),
|
|
MYSQL_SYSVAR(event_order_check_exact),
|
|
|
|
MYSQL_SYSVAR(event_record_def),
|
|
MYSQL_SYSVAR(event_record),
|
|
NULL};
|
|
|
|
/*
|
|
Plugin library descriptor
|
|
*/
|
|
|
|
mysql_declare_plugin(audit_null){
|
|
MYSQL_AUDIT_PLUGIN, /* type */
|
|
&audit_null_descriptor, /* descriptor */
|
|
"NULL_AUDIT", /* name */
|
|
"Oracle Corp", /* author */
|
|
"Simple NULL Audit", /* description */
|
|
PLUGIN_LICENSE_GPL,
|
|
audit_null_plugin_init, /* init function (when loaded) */
|
|
NULL, /* check uninstall function */
|
|
audit_null_plugin_deinit, /* deinit function (when unloaded) */
|
|
0x0003, /* version */
|
|
simple_status, /* status variables */
|
|
system_variables, /* system variables */
|
|
NULL,
|
|
0,
|
|
} mysql_declare_plugin_end;
|