diff --git a/include/my_bitmap.h b/include/my_bitmap.h index d0bf4d3fc822..99db6710f8ae 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -70,7 +70,7 @@ extern uint bitmap_bits_set(const MY_BITMAP *map); extern void bitmap_free(MY_BITMAP *map); extern void bitmap_set_above(MY_BITMAP *map, uint from_byte, bool use_bit); extern void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size); -extern void bitmap_intersect(MY_BITMAP *to, const MY_BITMAP *from); +extern void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2); @@ -135,4 +135,4 @@ static inline void bitmap_set_all(MY_BITMAP *map) { memset(map->bitmap, 0xFF, 4 * no_words_in_map(map)); } -#endif // MY_BITMAP_INCLUDED +#endif /* MY_BITMAP_INCLUDED */ diff --git a/include/my_sys.h b/include/my_sys.h index e7bdd13a7cca..b8f8968b35ea 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -602,6 +602,9 @@ extern size_t my_fwrite(FILE *stream, const uchar *Buffer, size_t Count, myf MyFlags); extern my_off_t my_fseek(FILE *stream, my_off_t pos, int whence); extern my_off_t my_ftell(FILE *stream); +#if !defined(HAVE_MEMSET_S) +void memset_s(void *dest, size_t dest_max, int c, size_t n); +#endif /* implemented in my_syslog.c */ diff --git a/mysql-test/include/plugin.defs b/mysql-test/include/plugin.defs index cd384e161952..74540576893e 100644 --- a/mysql-test/include/plugin.defs +++ b/mysql-test/include/plugin.defs @@ -175,3 +175,13 @@ test_services_command_services plugin_output_directory no TEST_SERVICES_CO # component test_status_var_reader component_test_status_var_reader plugin_output_directory no TEST_STATUS_VAR_READER + +# Percona additions +auth_socket plugin_output_directory no SOCKET_AUTH +audit_log plugin_output_directory no AUDIT_LOG audit_log +ha_tokudb plugin_output_directory no TOKUDB tokudb,tokudb_trx,tokudb_locks,tokudb_lock_waits,tokudb_fractal_tree_info,tokudb_background_job_status,tokudb_file_map +tokudb_backup plugin_output_directory no TOKUDB_BACKUP tokudb_backup +ha_rocksdb plugin_output_directory no ROCKSDB rocksdb,rocksdb_cfstats,rocksdb_dbstats,rocksdb_perf_context,rocksdb_perf_context_global,rocksdb_cf_options,rocksdb_compaction_history,rocksdb_compaction_stats,rocksdb_active_compaction_stats,rocksdb_global_info,rocksdb_ddl,rocksdb_index_file_map,rocksdb_locks,rocksdb_trx,rocksdb_deadlock,rocksdb_sst_props +auth_pam plugin_output_directory no AUTH_PAM +auth_pam_compat plugin_output_directory no AUTH_PAM_COMPAT +keyring_vault plugin_output_directory no KEYRING_VAULT_PLUGIN keyring_vault diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 65c601a00e4a..5e36e0ece5c9 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -26,6 +26,8 @@ SET(MY_TIME_SOURCES my_time.cc my_systime.cc) ADD_CONVENIENCE_LIBRARY(mytime ${MY_TIME_SOURCES}) +INCLUDE_DIRECTORIES(SYSTEM ${BOOST_PATCHES_DIR} ${BOOST_INCLUDE_DIR}) + SET(MYSYS_SOURCES array.cc charset.cc diff --git a/mysys/my_malloc.cc b/mysys/my_malloc.cc index 09fee686d907..51d75868a6a7 100644 --- a/mysys/my_malloc.cc +++ b/mysys/my_malloc.cc @@ -497,3 +497,16 @@ char *my_strndup(PSI_memory_key key, const char *from, size_t length, } return ptr; } + +#if !defined(HAVE_MEMSET_S) +void memset_s(void *dest, size_t dest_max, int c, size_t n) { +#if defined(WIN32) + SecureZeroMemory(dest, n); +#else + volatile unsigned char *p = static_cast(dest); + while (dest_max-- && n--) { + *p++ = c; + } +#endif +} +#endif diff --git a/plugin/audit_log/CMakeLists.txt b/plugin/audit_log/CMakeLists.txt new file mode 100644 index 000000000000..c7aa10ed53aa --- /dev/null +++ b/plugin/audit_log/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2014 Percona LLC 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-1301 USA + +MYSQL_ADD_PLUGIN(audit_log audit_log.cc file_logger.cc buffer.cc audit_file.cc + audit_syslog.cc filter.cc + MODULE_ONLY MODULE_OUTPUT_NAME "audit_log") + +IF(UNIX) + IF(INSTALL_MYSQLTESTDIR) + INSTALL(DIRECTORY tests/mtr/ DESTINATION ${INSTALL_MYSQLTESTDIR}/suite/audit_log COMPONENT Test) + ENDIF() +ENDIF() diff --git a/plugin/audit_log/audit_file.cc b/plugin/audit_log/audit_file.cc new file mode 100644 index 000000000000..ee8ae42439b6 --- /dev/null +++ b/plugin/audit_log/audit_file.cc @@ -0,0 +1,183 @@ +/* Copyright (c) 2014 Percona LLC 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-1301 USA */ + +#include "audit_handler.h" +#include "audit_log.h" +#include "buffer.h" +#include "my_dbug.h" +#include "my_sys.h" +#include "mysql/service_mysql_alloc.h" + +struct audit_handler_file_data_t { + size_t struct_size; + LOGGER_HANDLE *logger; + logger_prolog_func_t header; + logger_epilog_func_t footer; + bool sync_on_write; + bool use_buffer; + audit_log_buffer_t *buffer; +}; + +static int audit_handler_file_write(audit_handler_t *handler, const char *buf, + size_t len); +static int audit_handler_file_flush(audit_handler_t *handler) noexcept; +static int audit_handler_file_close(audit_handler_t *handler) noexcept; +static int audit_handler_file_write_nobuf(LOGGER_HANDLE *logger, + const char *buf, size_t len, + log_record_state_t state) noexcept; +static int audit_handler_file_write_buf(audit_log_buffer_t *buffer, + const char *buf, size_t len); +static void audit_handler_file_set_option(audit_handler_t *handler, + audit_handler_option_t opt, + void *val) noexcept; + +static int write_callback(void *data, const char *buf, size_t len, + log_record_state_t state) noexcept { + audit_handler_t *handler = (audit_handler_t *)data; + audit_handler_file_data_t *hdata = (audit_handler_file_data_t *)handler->data; + + assert(hdata->struct_size == sizeof(audit_handler_file_data_t)); + + return audit_handler_file_write_nobuf(hdata->logger, buf, len, state); +} + +audit_handler_t *audit_handler_file_open( + audit_handler_file_config_t *opts) noexcept { + audit_handler_t *handler = (audit_handler_t *)my_malloc( + key_memory_audit_log_handler, + sizeof(audit_handler_t) + sizeof(audit_handler_file_data_t), MY_ZEROFILL); + if (handler != nullptr) { + audit_handler_file_data_t *data = + (audit_handler_file_data_t *)(handler + 1); + data->struct_size = sizeof(audit_handler_file_data_t); + data->footer = opts->footer; + data->header = opts->header; + data->sync_on_write = opts->sync_on_write; + data->use_buffer = opts->use_buffer; + if (data->use_buffer) { + data->buffer = audit_log_buffer_init( + opts->buffer_size, opts->can_drop_data, write_callback, handler); + if (data->buffer == nullptr) goto error; + } + data->logger = logger_open(opts->name, opts->rotate_on_size, + opts->rotate_on_size ? opts->rotations : 0, + !opts->use_buffer, opts->header); + if (data->logger == nullptr) { + goto error; + } + handler->data = data; + handler->write = audit_handler_file_write; + handler->flush = audit_handler_file_flush; + handler->close = audit_handler_file_close; + handler->set_option = audit_handler_file_set_option; + goto success; + error: + if (data->buffer) { + audit_log_buffer_shutdown(data->buffer); + } + my_free(handler); + handler = nullptr; + } +success: + return handler; +} + +static int audit_handler_file_write_nobuf(LOGGER_HANDLE *logger, + const char *buf, size_t len, + log_record_state_t state) noexcept { + return logger_write(logger, buf, len, state); +} + +static int audit_handler_file_write_buf(audit_log_buffer_t *buffer, + const char *buf, size_t len) { + return audit_log_buffer_write(buffer, buf, len); +} + +static int audit_handler_file_write(audit_handler_t *handler, const char *buf, + size_t len) { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + int res; + + assert(data->struct_size == sizeof(audit_handler_file_data_t)); + + if (data->use_buffer) { + assert(data->buffer); + res = audit_handler_file_write_buf(data->buffer, buf, len); + } else { + assert(data->logger); + res = audit_handler_file_write_nobuf(data->logger, buf, len, + log_record_state_t::COMPLETE); + + if (data->sync_on_write) { + logger_sync(data->logger); + } + } + + return res; +} + +static int audit_handler_file_flush(audit_handler_t *handler) noexcept { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + LOGGER_HANDLE *logger; + int res; + + assert(data->struct_size == sizeof(audit_handler_file_data_t)); + + logger = data->logger; + + if (data->use_buffer) audit_log_buffer_pause(data->buffer); + + res = logger_reopen(logger, data->header, data->footer); + + if (data->use_buffer) audit_log_buffer_resume(data->buffer); + + return res; +} + +static int audit_handler_file_close(audit_handler_t *handler) noexcept { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + int res; + LOGGER_HANDLE *logger; + + assert(data->struct_size == sizeof(audit_handler_file_data_t)); + + logger = data->logger; + + if (data->use_buffer) { + audit_log_buffer_shutdown(data->buffer); + } + + res = logger_close(logger, data->footer); + + my_free(handler); + + return res; +} + +static void audit_handler_file_set_option(audit_handler_t *handler, + audit_handler_option_t opt, + void *val) noexcept { + audit_handler_file_data_t *data = (audit_handler_file_data_t *)handler->data; + + switch (opt) { + case audit_handler_option_t::ROTATE_ON_SIZE: + logger_set_size_limit(data->logger, *(ulonglong *)(val)); + break; + case audit_handler_option_t::ROTATIONS: + logger_set_rotations(data->logger, *(ulonglong *)(val)); + break; + } +} diff --git a/plugin/audit_log/audit_handler.h b/plugin/audit_log/audit_handler.h new file mode 100644 index 000000000000..a5bd77336a01 --- /dev/null +++ b/plugin/audit_log/audit_handler.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2014 Percona LLC 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-1301 USA */ + +#ifndef AUDIT_HANDLER_INCLUDED +#define AUDIT_HANDLER_INCLUDED + +#include "logger.h" + +struct audit_handler_file_config_t; +struct audit_handler_syslog_config_t; +struct audit_handler_buffered_t; +typedef void *audit_handler_data_t; + +enum class audit_handler_option_t { ROTATE_ON_SIZE, ROTATIONS }; + +struct audit_handler_t { + int (*write)(audit_handler_t *, const char *, size_t); + int (*flush)(audit_handler_t *); + int (*close)(audit_handler_t *); + void (*set_option)(audit_handler_t *, audit_handler_option_t, void *); + audit_handler_data_t data; +}; + +struct audit_handler_file_config_t { + const char *name; + size_t rotate_on_size; + size_t rotations; + bool sync_on_write; + bool use_buffer; + size_t buffer_size; + bool can_drop_data; + logger_prolog_func_t header; + logger_epilog_func_t footer; +}; + +struct audit_handler_syslog_config_t { + const char *ident; + int facility; + int priority; + logger_prolog_func_t header; + logger_epilog_func_t footer; +}; + +static inline int audit_handler_write(audit_handler_t *handler, const char *buf, + size_t len) { + if (handler != nullptr && handler->write != nullptr) { + return handler->write(handler, buf, len); + } + return len; +} + +static inline int audit_handler_flush(audit_handler_t *handler) { + if (handler != nullptr && handler->flush != nullptr) { + return handler->flush(handler); + } + return 0; +} + +static inline int audit_handler_close(audit_handler_t *handler) { + if (handler != nullptr && handler->close != nullptr) { + return handler->close(handler); + } + return 0; +} + +static inline void audit_handler_set_option(audit_handler_t *handler, + audit_handler_option_t opt, + void *val) { + if (handler != nullptr && handler->set_option != nullptr) { + handler->set_option(handler, opt, val); + } +} + +audit_handler_t *audit_handler_file_open( + audit_handler_file_config_t *opts) noexcept; +audit_handler_t *audit_handler_syslog_open( + audit_handler_syslog_config_t *opts) noexcept; + +#endif diff --git a/plugin/audit_log/audit_log.cc b/plugin/audit_log/audit_log.cc new file mode 100644 index 000000000000..fa8506ef0993 --- /dev/null +++ b/plugin/audit_log/audit_log.cc @@ -0,0 +1,1716 @@ +/* Copyright (c) 2014-2016 Percona LLC 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-1301 USA */ + +#include +#include +#include +#include +#include + +#include "m_ctype.h" +#include "my_sys.h" +#include "mysql/components/services/component_sys_var_service.h" +#include "mysql/plugin.h" +#include "mysql/plugin_audit.h" +#include "mysql/psi/mysql_memory.h" +#include "mysql/service_security_context.h" +#include "mysql_com.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include "sql/mysqld.h" +#include "typelib.h" + +#include "audit_handler.h" +#include "audit_log.h" +#include "buffer.h" +#include "filter.h" +#include "logger.h" + +#define PLUGIN_VERSION 0x0002 + +enum audit_log_policy_t { ALL, NONE, LOGINS, QUERIES }; +enum audit_log_strategy_t { + ASYNCHRONOUS, + PERFORMANCE, + SEMISYNCHRONOUS, + SYNCHRONOUS +}; +enum audit_log_format_t { OLD, NEW, JSON, CSV }; +enum audit_log_handler_t { HANDLER_FILE, HANDLER_SYSLOG }; + +typedef void (*escape_buf_func_t)(const char *, size_t *, char *, size_t *); + +static audit_handler_t *log_handler = nullptr; +static std::atomic record_id{0}; +static time_t log_file_time = 0; +static char *audit_log_file; +static const char default_audit_log_file[] = "audit.log"; +static ulong audit_log_policy = audit_log_policy_t::ALL; +static ulong audit_log_strategy = audit_log_strategy_t::ASYNCHRONOUS; +static ulonglong audit_log_buffer_size = 1048576; +static ulonglong audit_log_rotate_on_size = 0; +static ulonglong audit_log_rotations = 0; +static bool audit_log_flush = false; +static ulong audit_log_format = audit_log_format_t::OLD; +static ulong audit_log_handler = audit_log_handler_t::HANDLER_FILE; +static char *audit_log_syslog_ident; +static const char default_audit_log_syslog_ident[] = "percona-audit"; +static ulong audit_log_syslog_facility = 0; +static ulong audit_log_syslog_priority = 0; +static char *audit_log_exclude_accounts = nullptr; +static char *audit_log_include_accounts = nullptr; +static char *audit_log_exclude_databases = nullptr; +static char *audit_log_include_databases = nullptr; +static char *audit_log_exclude_commands = nullptr; +static char *audit_log_include_commands = nullptr; + +PSI_memory_key key_memory_audit_log_logger_handle; +PSI_memory_key key_memory_audit_log_handler; +PSI_memory_key key_memory_audit_log_buffer; +PSI_memory_key key_memory_audit_log_accounts; +PSI_memory_key key_memory_audit_log_databases; +PSI_memory_key key_memory_audit_log_commands; + +static PSI_memory_info all_audit_log_memory[] = { + {&key_memory_audit_log_logger_handle, "audit_log_logger_handle", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_handler, "audit_log_handler", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_buffer, "audit_log_buffer", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_accounts, "audit_log_accounts", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_databases, "audit_log_databases", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_audit_log_commands, "audit_log_commands", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, +}; + +static const int audit_log_syslog_facility_codes[] = { + LOG_USER, LOG_AUTHPRIV, LOG_CRON, LOG_DAEMON, LOG_FTP, LOG_KERN, + LOG_LPR, LOG_MAIL, LOG_NEWS, +#if (defined LOG_SECURITY) + LOG_SECURITY, +#endif + LOG_SYSLOG, LOG_AUTH, LOG_UUCP, LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, + LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7, 0}; + +static const char *audit_log_syslog_facility_names[] = { + "LOG_USER", "LOG_AUTHPRIV", "LOG_CRON", "LOG_DAEMON", + "LOG_FTP", "LOG_KERN", "LOG_LPR", "LOG_MAIL", + "LOG_NEWS", +#if (defined LOG_SECURITY) + "LOG_SECURITY", +#endif + "LOG_SYSLOG", "LOG_AUTH", "LOG_UUCP", "LOG_LOCAL0", + "LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4", + "LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7", 0}; + +static const int audit_log_syslog_priority_codes[] = { + LOG_INFO, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, + LOG_NOTICE, LOG_EMERG, LOG_DEBUG, 0}; + +static const char *audit_log_syslog_priority_names[] = { + "LOG_INFO", "LOG_ALERT", "LOG_CRIT", "LOG_ERR", "LOG_WARNING", + "LOG_NOTICE", "LOG_EMERG", "LOG_DEBUG", 0}; + +static MYSQL_PLUGIN plugin_ptr; + +static void init_record_id(off_t size) noexcept { + record_id.store(size, std::memory_order_relaxed); +} + +static ulonglong next_record_id() noexcept { + return record_id.fetch_add(1, std::memory_order_relaxed) + 1; +} + +static const constexpr auto MAX_RECORD_ID_SIZE = 50; +static const constexpr auto MAX_TIMESTAMP_SIZE = 25; + +static char *make_timestamp(char *buf, size_t buf_len, time_t t) noexcept { + tm tm; + + memset(&tm, 0, sizeof(tm)); + strftime(buf, buf_len, "%FT%TZ", gmtime_r(&t, &tm)); + + return buf; +} + +static char *make_record_id(char *buf, size_t buf_len) noexcept { + tm tm; + size_t len; + + memset(&tm, 0, sizeof(tm)); + len = snprintf(buf, buf_len, "%llu_", next_record_id()); + + strftime(buf + len, buf_len - len, "%FT%T", gmtime_r(&log_file_time, &tm)); + + return buf; +} + +struct escape_rule_t { + char character; + size_t length; + const char *replacement; +}; + +static void escape_buf(const char *in, size_t *inlen, char *out, size_t *outlen, + const escape_rule_t *control_escape_rules, + const escape_rule_t *other_escape_rules) noexcept { + char *outstart = out; + const char *base = in; + char *outend = out + *outlen; + + const char *inend = in + (*inlen); + + while ((in < inend) && (out < outend)) { + const escape_rule_t *replace_rule = nullptr; + if ((unsigned char)(*in) < 32) { + if (control_escape_rules[(unsigned int)*in].character) { + replace_rule = &control_escape_rules[(unsigned int)*in]; + } + } else { + const escape_rule_t *rule = nullptr; + for (rule = other_escape_rules; rule->character; rule++) { + if (*in == rule->character) { + replace_rule = rule; + break; + } + } + } + if (replace_rule) { + if ((outend - out) < (ptrdiff_t)replace_rule->length) break; + memcpy(out, replace_rule->replacement, replace_rule->length); + out += replace_rule->length; + } else { + *out++ = *in; + } + ++in; + } + *outlen = out - outstart; + *inlen = in - base; +} + +static void xml_escape(const char *in, size_t *inlen, char *out, + size_t *outlen) noexcept { + // Although most control sequences aren't supported in XML 1.0, we are better + // off printing them anyway instead of the original control characters + static const escape_rule_t control_rules[] = { + {0, 1, "?"}, {1, 4, ""}, {2, 4, ""}, + {3, 4, ""}, {4, 4, ""}, {5, 4, ""}, + {6, 4, ""}, {7, 4, ""}, {8, 4, ""}, + {'\t', 4, " "}, {'\n', 5, " "}, {11, 5, " "}, + {12, 5, " "}, {'\r', 5, " "}, {14, 5, ""}, + {15, 5, ""}, {16, 5, ""}, {17, 5, ""}, + {18, 5, ""}, {19, 5, ""}, {20, 5, ""}, + {21, 5, ""}, {22, 5, ""}, {23, 5, ""}, + {24, 5, ""}, {25, 5, ""}, {26, 5, ""}, + {27, 5, ""}, {28, 5, ""}, {29, 5, ""}, + {30, 5, ""}, {31, 5, ""}, + }; + static const escape_rule_t other_rules[] = {{'<', 4, "<"}, + {'>', 4, ">"}, + {'&', 5, "&"}, + {'"', 6, """}, + {0, 0, nullptr}}; + + escape_buf(in, inlen, out, outlen, control_rules, other_rules); +} + +static void json_escape(const char *in, size_t *inlen, char *out, + size_t *outlen) noexcept { + static const escape_rule_t control_rules[] = { + {0, 6, "\\u0000"}, {1, 6, "\\u0001"}, {2, 6, "\\u0002"}, + {3, 6, "\\u0003"}, {4, 6, "\\u0004"}, {5, 6, "\\u0005"}, + {6, 6, "\\u0006"}, {7, 6, "\\u0007"}, {'\b', 2, "\\b"}, + {'\t', 2, "\\t"}, {'\n', 2, "\\n"}, {11, 6, "\\u000B"}, + {'\f', 2, "\\f"}, {'\r', 2, "\\r"}, {14, 6, "\\u000E"}, + {15, 6, "\\u000F"}, {16, 6, "\\u0010"}, {17, 6, "\\u0011"}, + {18, 6, "\\u0012"}, {19, 6, "\\u0013"}, {20, 6, "\\u0014"}, + {21, 6, "\\u0015"}, {22, 6, "\\u0016"}, {23, 6, "\\u0017"}, + {24, 6, "\\u0018"}, {25, 6, "\\u0019"}, {26, 6, "\\u001A"}, + {27, 6, "\\u001B"}, {28, 6, "\\u001C"}, {29, 6, "\\u001D"}, + {30, 6, "\\u001E"}, {31, 6, "\\u001F"}, + }; + + static const escape_rule_t other_rules[] = { + {'\\', 2, "\\\\"}, {'"', 2, "\\\""}, {'/', 2, "\\/"}, {0, 0, NULL}}; + + escape_buf(in, inlen, out, outlen, control_rules, other_rules); +} + +static void csv_escape(const char *in, size_t *inlen, char *out, + size_t *outlen) noexcept { + // We do not have any standard control escape rules for CSVs + static const escape_rule_t control_rules[] = { + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, {0, 0, nullptr}, + }; + + static const escape_rule_t other_rules[] = {{'"', 2, "\"\""}, + {0, 0, nullptr}}; + + escape_buf(in, inlen, out, outlen, control_rules, other_rules); +} + +static const escape_buf_func_t format_escape_func[] = {xml_escape, xml_escape, + json_escape, csv_escape}; + +/* + Calculate the size of the otput bufer needed to escape the string. + + @param[in] in Input string + @param[in] len Length of the input string + + @return + size of the otput bufer including trailing zero +*/ +static size_t calculate_escape_string_buf_len(const char *in, + size_t len) noexcept { + char tmp[128]; + size_t full_outlen = 0; + + while (len > 0) { + size_t tmp_size = sizeof(tmp); + size_t inlen = len; + format_escape_func[static_cast(audit_log_format)](in, &inlen, tmp, + &tmp_size); + in += inlen; + len -= inlen; + full_outlen += tmp_size; + } + return full_outlen + 1; +} + +/* + Escape string according to audit_log_format. + + @param[in] in Input string + @param[in] inlen Length of the input string + @param[in] out Output buffer + @param[in] outlen Length of the output buffer + @param[out] endptr A pointer to the character after the + last escaped character in the output + buffer + @param[out] full_outlen Length of the output buffer that would + be needed to store complete non-truncated + escaped input buffer + + @return + pointer to the beginning of the output buffer +*/ +static char *escape_string(const char *in, size_t inlen, char *out, + size_t outlen, char **endptr, + size_t *full_outlen) noexcept { + if (outlen == 0) { + if (endptr) *endptr = out; + if (full_outlen) *full_outlen += calculate_escape_string_buf_len(in, inlen); + } else if (in != nullptr) { + size_t inlen_res = inlen; + --outlen; + format_escape_func[static_cast(audit_log_format)](in, &inlen_res, out, + &outlen); + out[outlen] = 0; + if (endptr) *endptr = out + outlen + 1; + if (full_outlen) { + *full_outlen += outlen; + *full_outlen += + calculate_escape_string_buf_len(in + inlen_res, inlen - inlen_res); + } + } else { + *out = 0; + if (endptr) *endptr = out + 1; + if (full_outlen) ++(*full_outlen); + } + return out; +} + +static void my_plugin_perror(void) noexcept { + char errbuf[MYSYS_STRERROR_SIZE]; + my_strerror(errbuf, sizeof(errbuf), errno); + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Error: %s", errbuf); +} + +static void audit_log_write(const char *buf, size_t len) { + static bool write_error = false; + + if (audit_handler_write(log_handler, buf, len) < 0) { + if (!write_error) { + write_error = true; + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Error writing to file %s.", audit_log_file); + my_plugin_perror(); + } + } else { + write_error = false; + } +} + +static char *make_argv(char *buf, size_t len, int argc, char **argv) noexcept { + size_t left = len; + + buf[0] = 0; + while (argc > 0 && left > 0) { + const int ret = + snprintf(buf + len - left, left, "%s%c", *argv, argc > 1 ? ' ' : 0); + assert(ret > 0); + if (ret < 0 || static_cast(ret) >= left) break; + left -= ret; + argc--; + argv++; + } + + return buf; +} + +static char *audit_log_audit_record(char *buf, size_t buflen, const char *name, + time_t t, size_t *outlen) noexcept { + char id_str[MAX_RECORD_ID_SIZE]; + char timestamp[MAX_TIMESTAMP_SIZE]; + char arg_buf[512]; + static const char *format_string[] = { + "\n", + + "\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " " MACHINE_TYPE "-" SYSTEM_TYPE + "\n" + "\n", + + "{\"audit_record\":{\"name\":\"%s\",\"record\":\"%s\"," + "\"timestamp\":\"%s\",\"mysql_version\":\"%s\"," + "\"startup_optionsi\":\"%s\"," + "\"os_version\":\"" MACHINE_TYPE "-" SYSTEM_TYPE "\"}}\n", + + "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"," + "\"" MACHINE_TYPE "-" SYSTEM_TYPE "\"\n"}; + + *outlen = snprintf( + buf, buflen, format_string[static_cast(audit_log_format)], name, + make_record_id(id_str, sizeof(id_str)), + make_timestamp(timestamp, sizeof(timestamp), t), server_version, + make_argv(arg_buf, sizeof(arg_buf), orig_argc - 1, orig_argv + 1)); + + /* make sure that record is not truncated */ + assert(buf + *outlen <= buf + buflen); + + return buf; +} + +static char *audit_log_general_record(char *buf, size_t buflen, + const char *name, time_t t, int status, + const mysql_event_general &event, + const char *default_db, + size_t *outlen) noexcept { + char id_str[MAX_RECORD_ID_SIZE]; + char timestamp[MAX_TIMESTAMP_SIZE]; + char *query, *user, *host, *external_user, *ip, *db; + char *endptr = buf, *endbuf = buf + buflen; + size_t full_outlen = 0, buflen_estimated; + size_t query_length; + + static const char *format_string[] = { + "\n", + + "\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %lu\n" + " %d\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + "\n", + + "{\"audit_record\":" + "{\"name\":\"%s\"," + "\"record\":\"%s\"," + "\"timestamp\":\"%s\"," + "\"command_class\":\"%s\"," + "\"connection_id\":\"%lu\"," + "\"status\":%d," + "\"sqltext\":\"%s\"," + "\"user\":\"%s\"," + "\"host\":\"%s\"," + "\"os_user\":\"%s\"," + "\"ip\":\"%s\"," + "\"db\":\"%s\"}}\n", + + ("\"%s\",\"%s\",\"%s\",\"%s\",\"%lu\",%d,\"%s\",\"%s\"," + "\"%s\",\"%s\",\"%s\",\"%s\"\n")}; + + query_length = + my_charset_utf8mb4_general_ci.mbmaxlen * event.general_query.length; + + if (query_length < (size_t)(endbuf - endptr)) { + uint errors; + query_length = + my_convert(endptr, query_length, &my_charset_utf8mb4_general_ci, + event.general_query.str, event.general_query.length, + event.general_charset, &errors); + query = endptr; + endptr += query_length; + + full_outlen += query_length; + + query = escape_string(query, query_length, endptr, endbuf - endptr, &endptr, + &full_outlen); + } else { + endptr = endbuf; + query = escape_string(event.general_query.str, event.general_query.length, + endptr, endbuf - endptr, &endptr, &full_outlen); + full_outlen += full_outlen * my_charset_utf8mb4_general_ci.mbmaxlen; + } + + user = escape_string(event.general_user.str, event.general_user.length, + endptr, endbuf - endptr, &endptr, &full_outlen); + host = escape_string(event.general_host.str, event.general_host.length, + endptr, endbuf - endptr, &endptr, &full_outlen); + external_user = escape_string(event.general_external_user.str, + event.general_external_user.length, endptr, + endbuf - endptr, &endptr, &full_outlen); + ip = escape_string(event.general_ip.str, event.general_ip.length, endptr, + endbuf - endptr, &endptr, &full_outlen); + db = escape_string(default_db, strlen(default_db), endptr, endbuf - endptr, + &endptr, &full_outlen); + + buflen_estimated = full_outlen * 2 + + strlen(format_string[static_cast(audit_log_format)]) + + strlen(name) + event.general_sql_command.length + + 20 + /* general_thread_id */ + 20 + /* status */ + MAX_RECORD_ID_SIZE + MAX_TIMESTAMP_SIZE; + if (buflen_estimated > buflen) { + *outlen = buflen_estimated; + return NULL; + } + + *outlen = snprintf(endptr, endbuf - endptr, + format_string[static_cast(audit_log_format)], name, + make_record_id(id_str, sizeof(id_str)), + make_timestamp(timestamp, sizeof(timestamp), t), + event.general_sql_command.str, event.general_thread_id, + status, query, user, host, external_user, ip, db); + + /* make sure that record is not truncated */ + assert(endptr + *outlen <= buf + buflen); + + return endptr; +} + +static char *audit_log_connection_record(char *buf, size_t buflen, + const char *name, time_t t, + const mysql_event_connection &event, + size_t *outlen) noexcept { + char id_str[MAX_RECORD_ID_SIZE]; + char timestamp[MAX_TIMESTAMP_SIZE]; + char *user, *priv_user, *external_user, *proxy_user, *host, *ip, *database; + char *endptr = buf, *endbuf = buf + buflen; + + static const char *format_string[] = { + "\n", + + "\n" + " %s\n" + " %s\n" + " %s\n" + " %lu\n" + " %d\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + "\n", + + "{\"audit_record\":" + "{\"name\":\"%s\"," + "\"record\":\"%s\"," + "\"timestamp\":\"%s\"," + "\"connection_id\":\"%lu\"," + "\"status\":%d," + "\"user\":\"%s\"," + "\"priv_user\":\"%s\"," + "\"os_login\":\"%s\"," + "\"proxy_user\":\"%s\"," + "\"host\":\"%s\"," + "\"ip\":\"%s\"," + "\"db\":\"%s\"}}\n", + + ("\"%s\",\"%s\",\"%s\",\"%lu\",%d,\"%s\",\"%s\",\"%s\"," + "\"%s\",\"%s\",\"%s\",\"%s\"\n")}; + + user = escape_string(event.user.str, event.user.length, endptr, + endbuf - endptr, &endptr, nullptr); + priv_user = escape_string(event.priv_user.str, event.priv_user.length, endptr, + endbuf - endptr, &endptr, nullptr); + external_user = + escape_string(event.external_user.str, event.external_user.length, endptr, + endbuf - endptr, &endptr, nullptr); + proxy_user = escape_string(event.proxy_user.str, event.proxy_user.length, + endptr, endbuf - endptr, &endptr, nullptr); + host = escape_string(event.host.str, event.host.length, endptr, + endbuf - endptr, &endptr, nullptr); + ip = escape_string(event.ip.str, event.ip.length, endptr, endbuf - endptr, + &endptr, nullptr); + database = escape_string(event.database.str, event.database.length, endptr, + endbuf - endptr, &endptr, nullptr); + + assert((endptr - buf) * 2 + + strlen(format_string[static_cast(audit_log_format)]) + + strlen(name) + MAX_RECORD_ID_SIZE + MAX_TIMESTAMP_SIZE + + 20 + /* event.thread_id */ + 20 /* event.status */ + < buflen); + + *outlen = snprintf(endptr, endbuf - endptr, + format_string[static_cast(audit_log_format)], name, + make_record_id(id_str, sizeof(id_str)), + make_timestamp(timestamp, sizeof(timestamp), t), + event.connection_id, event.status, user, priv_user, + external_user, proxy_user, host, ip, database); + + /* make sure that record is not truncated */ + assert(endptr + *outlen <= buf + buflen); + + return endptr; +} + +static size_t audit_log_header(MY_STAT *stat, char *buf, + size_t buflen) noexcept { + static const char *const format_string[5] = { + "\n" + "\n", + "\n" + "\n", + "", ""}; + + assert(strcmp(system_charset_info->csname, "utf8mb3") == 0); + + log_file_time = stat->st_mtime; + + init_record_id(stat->st_size); + + if (buf == nullptr) { + return 0; + } + + return snprintf(buf, buflen, "%s", format_string[audit_log_format]); +} + +static size_t audit_log_footer(char *buf, size_t buflen) noexcept { + static const char *const format_string[] = {"\n", "\n", "", + ""}; + + if (buf == nullptr) { + return 0; + } + + return snprintf(buf, buflen, "%s", format_string[audit_log_format]); +} + +static int init_new_log_file() noexcept { + if (audit_log_handler == audit_log_handler_t::HANDLER_FILE) { + audit_handler_file_config_t opts; + opts.name = audit_log_file; + opts.rotate_on_size = audit_log_rotate_on_size; + opts.rotations = audit_log_rotations; + opts.sync_on_write = + audit_log_strategy == audit_log_strategy_t::SYNCHRONOUS; + opts.use_buffer = + audit_log_strategy < audit_log_strategy_t::SEMISYNCHRONOUS; + opts.buffer_size = audit_log_buffer_size; + opts.can_drop_data = + audit_log_strategy == audit_log_strategy_t::PERFORMANCE; + opts.header = audit_log_header; + opts.footer = audit_log_footer; + + log_handler = audit_handler_file_open(&opts); + if (log_handler == nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Cannot open file %s.", + audit_log_file); + my_plugin_perror(); + return (1); + } + } else { + audit_handler_syslog_config_t opts; + opts.facility = audit_log_syslog_facility_codes[audit_log_syslog_facility]; + opts.ident = audit_log_syslog_ident; + opts.priority = audit_log_syslog_priority_codes[audit_log_syslog_priority]; + opts.header = audit_log_header; + opts.footer = audit_log_footer; + + log_handler = audit_handler_syslog_open(&opts); + if (log_handler == nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Cannot open syslog."); + my_plugin_perror(); + return (1); + } + } + + return (0); +} + +static int reopen_log_file() { + if (audit_handler_flush(log_handler)) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Cannot open file %s.", + audit_log_file); + my_plugin_perror(); + return (1); + } + + return (0); +} + +struct query_stack_frame { + /* number of included databases */ + int databases_included; + /* number of excluded databases */ + int databases_excluded; + /* number of accessed databases */ + int databases_accessed; + /* query */ + const char *query; +}; + +struct query_stack { + size_t size; + size_t top; + query_stack_frame *frames; +}; + +/* + Struct to store various THD specific data + */ +struct audit_log_thd_local { + /* size of allocated large buffer for record formatting */ + size_t record_buffer_size; + /* large buffer for record formatting */ + char *record_buffer; + /* skip session logging */ + bool skip_session; + /* skip logging for the next query */ + bool skip_query; + /* default database */ + char db[NAME_LEN + 1]; + /* default database candidate */ + char init_db_query[NAME_LEN + 1]; + /* call stack */ + query_stack stack; +}; + +/* + Return pointer to THD specific data. + */ +static audit_log_thd_local *get_thd_local(MYSQL_THD thd) noexcept; + +/* + Allocate and return buffer of given size. + */ +static char *get_record_buffer(MYSQL_THD thd, size_t size) noexcept; + +/* + Allocate and return given number of stack frames. + */ +static query_stack_frame *realloc_stack_frames(MYSQL_THD thd, + size_t size) noexcept; + +static int audit_log_plugin_init(MYSQL_PLUGIN plugin_info) { + char buf[1024]; + size_t len; + int count; + + plugin_ptr = plugin_info; + + count = array_elements(all_audit_log_memory); + mysql_memory_register(AUDIT_LOG_PSI_CATEGORY, all_audit_log_memory, count); + logger_init_mutexes(); + + audit_log_filter_init(); + + if (audit_log_exclude_accounts != nullptr && + audit_log_include_accounts != nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Both 'audit_log_exclude_accounts' and " + "'audit_log_include_accounts' are not NULL\n"); + goto validation_error; + } + + if (audit_log_exclude_commands != nullptr && + audit_log_include_commands != nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Both 'audit_log_exclude_commands' and " + "'audit_log_include_commands' are not NULL\n"); + goto validation_error; + } + + if (audit_log_exclude_databases != nullptr && + audit_log_include_databases != nullptr) { + my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, + "Both 'audit_log_exclude_databases' and " + "'audit_log_include_databases' are not NULL\n"); + goto validation_error; + } + + if (audit_log_exclude_accounts != nullptr) { + audit_log_exclude_accounts = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_exclude_accounts, MYF(MY_FAE)); + audit_log_set_exclude_accounts(audit_log_exclude_accounts); + } + if (audit_log_include_accounts != nullptr) { + audit_log_include_accounts = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_include_accounts, MYF(MY_FAE)); + audit_log_set_include_accounts(audit_log_include_accounts); + } + if (audit_log_exclude_commands != nullptr) { + audit_log_exclude_commands = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_exclude_commands, MYF(MY_FAE)); + audit_log_set_exclude_commands(audit_log_exclude_commands); + } + if (audit_log_include_commands != nullptr) { + audit_log_include_commands = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_include_commands, MYF(MY_FAE)); + audit_log_set_include_commands(audit_log_include_commands); + } + if (audit_log_exclude_databases != nullptr) { + audit_log_exclude_databases = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_exclude_databases, MYF(MY_FAE)); + audit_log_set_exclude_databases(audit_log_exclude_databases); + } + if (audit_log_include_databases != nullptr) { + audit_log_include_databases = my_strdup( + PSI_NOT_INSTRUMENTED, audit_log_include_databases, MYF(MY_FAE)); + audit_log_set_include_databases(audit_log_include_databases); + } + + if (init_new_log_file()) return (1); + + if (audit_log_audit_record(buf, sizeof(buf), "Audit", time(nullptr), &len)) + audit_log_write(buf, len); + + return 0; + +validation_error: + + audit_log_exclude_accounts = audit_log_include_accounts = nullptr; + audit_log_exclude_commands = audit_log_include_commands = nullptr; + audit_log_exclude_databases = audit_log_include_databases = nullptr; + + return 1; +} + +static int audit_log_plugin_deinit(void *arg [[maybe_unused]]) { + char buf[1024]; + size_t len; + + if (audit_log_audit_record(buf, sizeof(buf), "NoAudit", time(nullptr), &len)) + audit_log_write(buf, len); + + audit_handler_close(log_handler); + + audit_log_filter_destroy(); + + my_free(audit_log_include_accounts); + my_free(audit_log_exclude_accounts); + + my_free(audit_log_include_databases); + my_free(audit_log_exclude_databases); + + my_free(audit_log_include_commands); + my_free(audit_log_exclude_commands); + + return (0); +} + +static bool is_event_class_allowed_by_policy(mysql_event_class_t event_class, + ulong policy) noexcept { + static const unsigned int class_mask[] = { + /* ALL */ + (1 << MYSQL_AUDIT_GENERAL_CLASS) | (1 << MYSQL_AUDIT_CONNECTION_CLASS), + 0, /* NONE */ + (1 << MYSQL_AUDIT_CONNECTION_CLASS), /* LOGINS */ + (1 << MYSQL_AUDIT_GENERAL_CLASS), /* QUERIES */ + }; + + return (class_mask[policy] & (1 << event_class)) != 0; +} + +static const char *next_word(const char *str, size_t *len, + const CHARSET_INFO *charset) noexcept { + while (*str && my_isspace(charset, *str)) { + if (*str == '/' && str[1] == '*' && str[2] == '!') + str += 3; + else if (*str == '/' && str[1] == '*') { + while (*str && !(*str == '*' && str[1] == '/')) str++; + } else + str++; + } + + *len = 0; + while (str[*len] && my_isvar(charset, str[*len])) (*len)++; + + if (*len == 0 && *str == '`') { + (*len)++; + while (str[*len]) { + if (str[*len] == '`' && str[*len + 1] == '`') + (*len)++; + else if (str[*len] == '`') + break; + (*len)++; + } + (*len)++; + } + + return str; +} + +static bool audit_log_update_thd_local(MYSQL_THD thd, + audit_log_thd_local *local, + unsigned int event_class, + const void *event) { + assert(audit_log_include_accounts == nullptr || + audit_log_exclude_accounts == nullptr); + + assert(audit_log_include_databases == nullptr || + audit_log_exclude_databases == nullptr); + + assert(audit_log_include_commands == nullptr || + audit_log_exclude_commands == nullptr); + + if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) { + const mysql_event_connection *event_connection = + (const mysql_event_connection *)event; + LEX_STRING priv_user, priv_host; + MYSQL_SECURITY_CONTEXT ctx; + + if (thd_get_security_context(thd, &ctx)) { + my_message(ER_AUDIT_API_ABORT, "Error: can not get security context", + MYF(0)); + return false; + } + + if (security_context_get_option(ctx, "priv_user", &priv_user)) { + my_message(ER_AUDIT_API_ABORT, + "Error: can not get priv_user from " + "security context", + MYF(0)); + return false; + } + + if (security_context_get_option(ctx, "priv_host", &priv_host)) { + my_message(ER_AUDIT_API_ABORT, + "Error: can not get priv_host from " + "security context", + MYF(0)); + return false; + } + + local->skip_session = false; + if (audit_log_include_accounts != nullptr && + !audit_log_check_account_included(priv_user.str, priv_user.length, + priv_host.str, priv_host.length)) + local->skip_session = true; + if (audit_log_exclude_accounts != nullptr && + audit_log_check_account_excluded(priv_user.str, priv_user.length, + priv_host.str, priv_host.length)) + local->skip_session = true; + + if (event_connection->status == 0) { + /* track default DB change */ + assert(event_connection->database.length <= sizeof(local->db)); + if (event_connection->database.str != nullptr) + memcpy(local->db, event_connection->database.str, + event_connection->database.length); + local->db[event_connection->database.length] = 0; + } + } else if (event_class == MYSQL_AUDIT_GENERAL_CLASS) { + const mysql_event_general *event_general = + (const mysql_event_general *)event; + + if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_STATUS) { + local->skip_query = false; + + if (local->stack.frames[local->stack.top].query == + event_general->general_query.str) { + local->skip_query |= + audit_log_include_databases && + local->stack.frames[local->stack.top].databases_accessed > 0 && + local->stack.frames[local->stack.top].databases_included == 0; + + local->skip_query |= + audit_log_exclude_databases && + local->stack.frames[local->stack.top].databases_accessed > 0 && + local->stack.frames[local->stack.top].databases_excluded == + local->stack.frames[local->stack.top].databases_accessed; + + local->stack.frames[local->stack.top].databases_included = 0; + local->stack.frames[local->stack.top].databases_accessed = 0; + local->stack.frames[local->stack.top].databases_excluded = 0; + local->stack.frames[local->stack.top].query = nullptr; + + if (local->stack.top > 0) --local->stack.top; + } + + local->skip_query |= audit_log_include_commands && + !audit_log_check_command_included( + event_general->general_sql_command.str, + event_general->general_sql_command.length); + + local->skip_query |= audit_log_exclude_commands && + audit_log_check_command_excluded( + event_general->general_sql_command.str, + event_general->general_sql_command.length); + + if (!local->skip_query && + ((event_general->general_command.length == 4 && + strncmp(event_general->general_command.str, "Quit", 4) == 0) || + (event_general->general_command.length == 11 && + strncmp(event_general->general_command.str, "Change user", 11) == + 0))) + local->skip_query = true; + } + + if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_LOG && + event_general->general_command.length == 7 && + strncmp(event_general->general_command.str, "Init DB", 7) == 0 && + event_general->general_query.str != nullptr && + strpbrk("\n\r\t ", event_general->general_query.str) == nullptr) { + /* Database is about to be changed. Server doesn't provide database + name in STATUS event, so remember it now. */ + + assert(event_general->general_query.length <= sizeof(local->db)); + memcpy(local->db, event_general->general_query.str, + event_general->general_query.length); + local->db[event_general->general_query.length] = 0; + } + if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_STATUS && + event_general->general_sql_command.length == 9 && + strncmp(event_general->general_sql_command.str, "change_db", 9) == 0 && + event_general->general_command.length == 5 && + strncmp(event_general->general_command.str, "Query", 5) == 0 && + event_general->general_error_code == 0) { + /* it's "use dbname" query */ + + size_t len; + const char *word; + + word = next_word(event_general->general_query.str, &len, + event_general->general_charset); + if (strncasecmp("use", word, len) == 0) { + uint errors; + + word = next_word(word + len, &len, event_general->general_charset); + if (*word == '`') { + word++; + len -= 2; + } + len = my_convert(local->db, sizeof(local->db) - 1, system_charset_info, + word, len, event_general->general_charset, &errors); + local->db[len] = 0; + } + } + } else if (event_class == MYSQL_AUDIT_TABLE_ACCESS_CLASS) { + const mysql_event_table_access *event_table = + (const mysql_event_table_access *)event; + + if (local->stack.frames[local->stack.top].query != event_table->query.str && + local->stack.frames[local->stack.top].query != nullptr) { + if (++local->stack.top >= local->stack.size) + realloc_stack_frames(thd, local->stack.size * 2); + } + local->stack.frames[local->stack.top].query = event_table->query.str; + + ++local->stack.frames[local->stack.top].databases_accessed; + + if (audit_log_include_databases != nullptr && + audit_log_check_database_included(event_table->table_database.str, + event_table->table_database.length)) + ++local->stack.frames[local->stack.top].databases_included; + + if (audit_log_exclude_databases != nullptr && + audit_log_check_database_excluded(event_table->table_database.str, + event_table->table_database.length)) + ++local->stack.frames[local->stack.top].databases_excluded; + } + return true; +} + +static int audit_log_notify(MYSQL_THD thd, mysql_event_class_t event_class, + const void *event) { + audit_log_thd_local *local = get_thd_local(thd); + if (!audit_log_update_thd_local(thd, local, event_class, event)) return 1; + + if (!is_event_class_allowed_by_policy(event_class, audit_log_policy)) + return 0; + + if (local->skip_session) return 0; + + char buf[4096]; + char *log_rec; + size_t len, buflen; + if (event_class == MYSQL_AUDIT_GENERAL_CLASS) { + const mysql_event_general *event_general = + (const mysql_event_general *)event; + switch (event_general->event_subclass) { + case MYSQL_AUDIT_GENERAL_STATUS: { + if (local->skip_query) break; + + /* use allocated buffer if available */ + char *allocated_buf = get_record_buffer(thd, 0); + if (allocated_buf != nullptr) { + log_rec = allocated_buf; + buflen = local->record_buffer_size; + } else { + log_rec = buf; + buflen = sizeof(buf); + } + log_rec = audit_log_general_record( + log_rec, buflen, event_general->general_command.str, + event_general->general_time, event_general->general_error_code, + *event_general, local->db, &len); + if (len > buflen) { + buflen = len * 4; + log_rec = audit_log_general_record( + get_record_buffer(thd, buflen), buflen, + event_general->general_command.str, event_general->general_time, + event_general->general_error_code, *event_general, local->db, + &len); + } + if (log_rec) audit_log_write(log_rec, len); + break; + } + case MYSQL_AUDIT_GENERAL_LOG: + case MYSQL_AUDIT_GENERAL_ERROR: + case MYSQL_AUDIT_GENERAL_RESULT: + break; + } + } else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) { + const mysql_event_connection *event_connection = + (const mysql_event_connection *)event; + switch (event_connection->event_subclass) { + case MYSQL_AUDIT_CONNECTION_CONNECT: + log_rec = + audit_log_connection_record(buf, sizeof(buf), "Connect", + time(nullptr), *event_connection, &len); + break; + case MYSQL_AUDIT_CONNECTION_DISCONNECT: + log_rec = audit_log_connection_record( + buf, sizeof(buf), "Quit", time(nullptr), *event_connection, &len); + break; + case MYSQL_AUDIT_CONNECTION_CHANGE_USER: + log_rec = + audit_log_connection_record(buf, sizeof(buf), "Change user", + time(nullptr), *event_connection, &len); + break; + default: + log_rec = nullptr; + len = 0; + break; + } + if (log_rec) audit_log_write(log_rec, len); + } + return 0; +} + +/* + * Plugin system vars + */ + +static MYSQL_SYSVAR_STR(file, audit_log_file, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | + PLUGIN_VAR_MEMALLOC, + "The name of the log file.", nullptr, nullptr, + default_audit_log_file); + +static const char *audit_log_policy_names[] = {"ALL", "NONE", "LOGINS", + "QUERIES", nullptr}; + +static TYPELIB audit_log_policy_typelib = { + array_elements(audit_log_policy_names) - 1, "audit_log_policy_typelib", + audit_log_policy_names, nullptr}; + +static MYSQL_SYSVAR_ENUM( + policy, audit_log_policy, PLUGIN_VAR_RQCMDARG, + "The policy controlling the information written by the audit log " + "plugin to its log file.", + NULL, NULL, audit_log_policy_t::ALL, &audit_log_policy_typelib); + +static const char *audit_log_strategy_names[] = { + "ASYNCHRONOUS", "PERFORMANCE", "SEMISYNCHRONOUS", "SYNCHRONOUS", nullptr}; +static TYPELIB audit_log_strategy_typelib = { + array_elements(audit_log_strategy_names) - 1, "audit_log_strategy_typelib", + audit_log_strategy_names, nullptr}; + +static MYSQL_SYSVAR_ENUM(strategy, audit_log_strategy, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The logging method used by the audit log plugin, " + "if FILE handler is used.", + nullptr, nullptr, ASYNCHRONOUS, + &audit_log_strategy_typelib); + +static const char *audit_log_format_names[] = {"OLD", "NEW", "JSON", "CSV", + nullptr}; +static TYPELIB audit_log_format_typelib = { + array_elements(audit_log_format_names) - 1, "audit_log_format_typelib", + audit_log_format_names, nullptr}; + +static MYSQL_SYSVAR_ENUM(format, audit_log_format, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The audit log file format.", nullptr, nullptr, + ASYNCHRONOUS, &audit_log_format_typelib); + +static const char *audit_log_handler_names[] = {"FILE", "SYSLOG", nullptr}; +static TYPELIB audit_log_handler_typelib = { + array_elements(audit_log_handler_names) - 1, "audit_log_handler_typelib", + audit_log_handler_names, nullptr}; + +static MYSQL_SYSVAR_ENUM(handler, audit_log_handler, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The audit log handler.", nullptr, nullptr, + HANDLER_FILE, &audit_log_handler_typelib); + +static MYSQL_SYSVAR_ULONGLONG( + buffer_size, audit_log_buffer_size, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "The size of the buffer for asynchronous logging, " + "if FILE handler is used.", + NULL, NULL, 1048576UL, 4096UL, ULLONG_MAX, 4096UL); + +static void audit_log_rotate_on_size_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) noexcept { + ulonglong new_val = *(const ulonglong *)(save); + + audit_handler_set_option(log_handler, audit_handler_option_t::ROTATE_ON_SIZE, + &new_val); + + audit_log_rotate_on_size = new_val; +} + +static MYSQL_SYSVAR_ULONGLONG( + rotate_on_size, audit_log_rotate_on_size, PLUGIN_VAR_RQCMDARG, + "Maximum size of the log to start the rotation, if FILE handler is used.", + NULL, audit_log_rotate_on_size_update, 0UL, 0UL, ULLONG_MAX, 4096UL); + +static void audit_log_rotations_update(MYSQL_THD thd [[maybe_unused]], + SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], + const void *save) noexcept { + ulonglong new_val = *(const ulonglong *)(save); + + audit_handler_set_option(log_handler, audit_handler_option_t::ROTATIONS, + &new_val); + + audit_log_rotations = new_val; +} + +static MYSQL_SYSVAR_ULONGLONG( + rotations, audit_log_rotations, PLUGIN_VAR_RQCMDARG, + "Maximum number of rotations to keep, if FILE handler is used.", NULL, + audit_log_rotations_update, 0UL, 0UL, 999UL, 1UL); + +static void audit_log_flush_update(MYSQL_THD thd [[maybe_unused]], + SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], + const void *save) { + char new_val = *(const char *)(save); + + if (new_val != audit_log_flush && new_val) { + audit_log_flush = true; + reopen_log_file(); + audit_log_flush = false; + } +} + +static MYSQL_SYSVAR_BOOL(flush, audit_log_flush, PLUGIN_VAR_OPCMDARG, + "Flush the log file.", nullptr, audit_log_flush_update, + false); + +static MYSQL_SYSVAR_STR( + syslog_ident, audit_log_syslog_ident, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC, + "The string that will be prepended to each log message, " + "if SYSLOG handler is used.", + nullptr, nullptr, default_audit_log_syslog_ident); + +static TYPELIB audit_log_syslog_facility_typelib = { + array_elements(audit_log_syslog_facility_names) - 1, + "audit_log_syslog_facility_typelib", audit_log_syslog_facility_names, + nullptr}; + +static MYSQL_SYSVAR_ENUM( + syslog_facility, audit_log_syslog_facility, PLUGIN_VAR_RQCMDARG, + "The syslog facility to assign to messages, if SYSLOG handler is used.", + nullptr, nullptr, 0, &audit_log_syslog_facility_typelib); + +static TYPELIB audit_log_syslog_priority_typelib = { + array_elements(audit_log_syslog_priority_names) - 1, + "audit_log_syslog_priority_typelib", audit_log_syslog_priority_names, + nullptr}; + +static MYSQL_SYSVAR_ENUM( + syslog_priority, audit_log_syslog_priority, PLUGIN_VAR_RQCMDARG, + "Priority to be assigned to all messages written to syslog.", nullptr, + nullptr, 0, &audit_log_syslog_priority_typelib); + +static MYSQL_THDVAR_STR(record_buffer, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "Buffer for query formatting.", nullptr, nullptr, ""); + +static MYSQL_THDVAR_STR(query_stack, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "Query stack.", nullptr, nullptr, ""); + +static const char *val_strmake(MYSQL_THD thd, + struct st_mysql_value *mysql_val) { + char buf[STRING_BUFFER_USUAL_SIZE]; + int len = sizeof(buf); + const char *val = mysql_val->val_str(mysql_val, buf, &len); + + if (val != NULL) val = thd_strmake(thd, val, len); + + return val; +} + +static int audit_log_exclude_accounts_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_include_accounts) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_exclude_accounts_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_include_accounts == nullptr); + + my_free(audit_log_exclude_accounts); + audit_log_exclude_accounts = nullptr; + + if (new_val != nullptr) { + audit_log_exclude_accounts = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_exclude_accounts(audit_log_exclude_accounts); + } else { + audit_log_set_exclude_accounts(""); + } +} + +static MYSQL_SYSVAR_STR(exclude_accounts, audit_log_exclude_accounts, + PLUGIN_VAR_RQCMDARG, + "Comma separated list of accounts " + "for which events should not be logged.", + audit_log_exclude_accounts_validate, + audit_log_exclude_accounts_update, nullptr); + +static int audit_log_include_accounts_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_exclude_accounts) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_include_accounts_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_exclude_accounts == nullptr); + + my_free(audit_log_include_accounts); + audit_log_include_accounts = nullptr; + + if (new_val != nullptr) { + audit_log_include_accounts = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_include_accounts(audit_log_include_accounts); + } else { + audit_log_set_include_accounts(""); + } +} + +static MYSQL_SYSVAR_STR( + include_accounts, audit_log_include_accounts, PLUGIN_VAR_RQCMDARG, + "Comma separated list of accounts for which events should be logged.", + audit_log_include_accounts_validate, audit_log_include_accounts_update, + nullptr); + +static int audit_log_exclude_databases_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_include_databases) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_exclude_databases_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_include_databases == nullptr); + + my_free(audit_log_exclude_databases); + audit_log_exclude_databases = nullptr; + + if (new_val != nullptr) { + audit_log_exclude_databases = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_exclude_databases(audit_log_exclude_databases); + } else { + audit_log_set_exclude_databases(""); + } +} + +static MYSQL_SYSVAR_STR(exclude_databases, audit_log_exclude_databases, + PLUGIN_VAR_RQCMDARG, + "Comma separated list of databases " + "for which events should not be logged.", + audit_log_exclude_databases_validate, + audit_log_exclude_databases_update, nullptr); + +static int audit_log_include_databases_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_exclude_databases) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_include_databases_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_exclude_databases == nullptr); + + my_free(audit_log_include_databases); + audit_log_include_databases = nullptr; + + if (new_val != nullptr) { + audit_log_include_databases = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_include_databases(audit_log_include_databases); + } else { + audit_log_set_include_databases(""); + } +} + +static MYSQL_SYSVAR_STR( + include_databases, audit_log_include_databases, PLUGIN_VAR_RQCMDARG, + "Comma separated list of databases for which events should be logged.", + audit_log_include_databases_validate, audit_log_include_databases_update, + nullptr); + +static int audit_log_exclude_commands_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_include_commands) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_exclude_commands_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_include_commands == nullptr); + + my_free(audit_log_exclude_commands); + audit_log_exclude_commands = nullptr; + + if (new_val != nullptr) { + audit_log_exclude_commands = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_exclude_commands(audit_log_exclude_commands); + } else { + audit_log_set_exclude_commands(""); + } +} + +static MYSQL_SYSVAR_STR(exclude_commands, audit_log_exclude_commands, + PLUGIN_VAR_RQCMDARG, + "Comma separated list of commands " + "for which events should not be logged.", + audit_log_exclude_commands_validate, + audit_log_exclude_commands_update, nullptr); + +static int audit_log_include_commands_validate(MYSQL_THD thd, + SYS_VAR *var [[maybe_unused]], + void *save, + st_mysql_value *value) { + if (audit_log_exclude_commands) return 1; + + *(const char **)(save) = val_strmake(thd, value); + + return 0; +} + +static void audit_log_include_commands_update( + MYSQL_THD thd [[maybe_unused]], SYS_VAR *var [[maybe_unused]], + void *var_ptr [[maybe_unused]], const void *save) { + const char *new_val = *(const char * const*)(save); + + assert(audit_log_exclude_commands == nullptr); + + my_free(audit_log_include_commands); + audit_log_include_commands = nullptr; + + if (new_val != nullptr) { + audit_log_include_commands = + my_strdup(PSI_NOT_INSTRUMENTED, new_val, MYF(MY_FAE)); + audit_log_set_include_commands(audit_log_include_commands); + } else { + audit_log_set_include_commands(""); + } +} + +static MYSQL_SYSVAR_STR( + include_commands, audit_log_include_commands, PLUGIN_VAR_RQCMDARG, + "Comma separated list of commands for which events should be logged.", + audit_log_include_commands_validate, audit_log_include_commands_update, + nullptr); + +static MYSQL_THDVAR_STR(local, + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | + PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT, + "Local store.", nullptr, nullptr, ""); + +static MYSQL_THDVAR_ULONG(local_ptr, + PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR | + PLUGIN_VAR_NOCMDOPT, + "Local store ptr.", nullptr, nullptr, 0, 0, ULONG_MAX, + 0); + +static SYS_VAR *audit_log_system_variables[] = {MYSQL_SYSVAR(file), + MYSQL_SYSVAR(policy), + MYSQL_SYSVAR(strategy), + MYSQL_SYSVAR(format), + MYSQL_SYSVAR(buffer_size), + MYSQL_SYSVAR(rotate_on_size), + MYSQL_SYSVAR(rotations), + MYSQL_SYSVAR(flush), + MYSQL_SYSVAR(handler), + MYSQL_SYSVAR(syslog_ident), + MYSQL_SYSVAR(syslog_priority), + MYSQL_SYSVAR(syslog_facility), + MYSQL_SYSVAR(record_buffer), + MYSQL_SYSVAR(query_stack), + MYSQL_SYSVAR(exclude_accounts), + MYSQL_SYSVAR(include_accounts), + MYSQL_SYSVAR(exclude_databases), + MYSQL_SYSVAR(include_databases), + MYSQL_SYSVAR(exclude_commands), + MYSQL_SYSVAR(include_commands), + MYSQL_SYSVAR(local), + MYSQL_SYSVAR(local_ptr), + NULL}; + +static char thd_local_init_buf[sizeof(audit_log_thd_local)]; + +void MY_ATTRIBUTE((constructor)) audit_log_so_init() noexcept { + memset(thd_local_init_buf, 1, sizeof(thd_local_init_buf) - 1); + thd_local_init_buf[sizeof(thd_local_init_buf) - 1] = 0; +} + +/* + Return pointer to THD specific data. + */ +static audit_log_thd_local *get_thd_local(MYSQL_THD thd) noexcept { + audit_log_thd_local *local = (audit_log_thd_local *)THDVAR(thd, local_ptr); + + static_assert(sizeof(THDVAR(thd, local_ptr)) >= sizeof(void *), + "THD::local_ptr must be at least pointer-big"); + + if (unlikely(local == nullptr)) { + THDVAR_SET(thd, local, thd_local_init_buf); + local = (audit_log_thd_local *)THDVAR(thd, local); + memset(local, 0, sizeof(audit_log_thd_local)); + THDVAR(thd, local_ptr) = (ulong)local; + + realloc_stack_frames(thd, 4); + } + return local; +} + +/* + Allocate and return buffer of given size. + */ +static char *get_record_buffer(MYSQL_THD thd, size_t size) noexcept { + audit_log_thd_local *local = get_thd_local(thd); + char *buf = local->record_buffer; + + if (local->record_buffer_size < size) { + local->record_buffer_size = size; + + buf = (char *)my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_FAE)); + memset(buf, 1, size - 1); + buf[size - 1] = 0; + + THDVAR_SET(thd, record_buffer, buf); + + my_free(buf); + + buf = (char *)THDVAR(thd, record_buffer); + local->record_buffer = buf; + } + + return buf; +} + +/* + Allocate and return given number of stack frames. + */ +static query_stack_frame *realloc_stack_frames(MYSQL_THD thd, + size_t size) noexcept { + audit_log_thd_local *local = get_thd_local(thd); + query_stack_frame *stack = (query_stack_frame *)THDVAR(thd, query_stack); + + if (local->stack.size < size) { + char *buf = (char *)my_malloc( + PSI_NOT_INSTRUMENTED, + (local->stack.size + size) * sizeof(query_stack_frame), MYF(MY_FAE)); + memset(buf + local->stack.size * sizeof(query_stack_frame), 1, + size * sizeof(query_stack_frame) - 1); + buf[(local->stack.size + size) * sizeof(query_stack_frame) - 1] = 0; + if (local->stack.size > 0) + memcpy(buf, stack, local->stack.size * sizeof(query_stack_frame)); + THDVAR_SET(thd, query_stack, + buf + local->stack.size * sizeof(query_stack_frame)); + stack = (query_stack_frame *)THDVAR(thd, query_stack); + memset(stack, 0, size * sizeof(query_stack_frame)); + if (local->stack.size > 0) + memcpy(stack, buf, local->stack.size * sizeof(query_stack_frame)); + local->stack.frames = stack; + local->stack.size = size; + my_free(buf); + } + + return stack; +} + +/* + Plugin type-specific descriptor +*/ +static st_mysql_audit audit_log_descriptor = { + MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */ + nullptr, /* release_thd function */ + audit_log_notify, /* notify function */ + {MYSQL_AUDIT_GENERAL_ALL, MYSQL_AUDIT_CONNECTION_ALL, 0, 0, + MYSQL_AUDIT_TABLE_ACCESS_ALL, 0, 0, 0, 0, 0} /* class mask */ +}; + +/* + Plugin status variables for SHOW STATUS +*/ + +static SHOW_VAR audit_log_status_variables[] = { + {NullS, NullS, SHOW_LONG, SHOW_SCOPE_GLOBAL}}; + +/* + Plugin library descriptor +*/ + +mysql_declare_plugin(audit_log){ + MYSQL_AUDIT_PLUGIN, /* type */ + &audit_log_descriptor, /* descriptor */ + "audit_log", /* name */ + "Percona LLC and/or its affiliates.", /* author */ + "Audit log", /* description */ + PLUGIN_LICENSE_GPL, + audit_log_plugin_init, /* init function (when loaded) */ + nullptr, + audit_log_plugin_deinit, /* deinit function (when unloaded) */ + PLUGIN_VERSION, /* version */ + audit_log_status_variables, /* status variables */ + audit_log_system_variables, /* system variables */ + nullptr, + 0, +} mysql_declare_plugin_end; diff --git a/plugin/audit_log/audit_log.h b/plugin/audit_log/audit_log.h new file mode 100644 index 000000000000..c952b6751cb8 --- /dev/null +++ b/plugin/audit_log/audit_log.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2015-2016 Percona LLC 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-1301 USA */ + +#ifndef AUDIT_LOG_H_INCLUDED +#define AUDIT_LOG_H_INCLUDED + +#include "mysql/psi/psi_memory.h" + +extern PSI_memory_key key_memory_audit_log_logger_handle; +extern PSI_memory_key key_memory_audit_log_handler; +extern PSI_memory_key key_memory_audit_log_buffer; +extern PSI_memory_key key_memory_audit_log_accounts; +extern PSI_memory_key key_memory_audit_log_databases; +extern PSI_memory_key key_memory_audit_log_commands; + +static const constexpr auto AUDIT_LOG_PSI_CATEGORY = "audit_log"; + +#endif /* AUDIT_LOG_H_INCLUDED */ diff --git a/plugin/audit_log/audit_syslog.cc b/plugin/audit_log/audit_syslog.cc new file mode 100644 index 000000000000..8693aa46ec17 --- /dev/null +++ b/plugin/audit_log/audit_syslog.cc @@ -0,0 +1,89 @@ +/* Copyright (c) 2014 Percona LLC 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-1301 USA */ + +#include +#include +#include "audit_handler.h" +#include "audit_log.h" +#include "my_dbug.h" +#include "my_sys.h" +#include "mysql/service_mysql_alloc.h" + +struct audit_handler_syslog_data_t { + size_t struct_size; + int priority; + logger_prolog_func_t header; + logger_epilog_func_t footer; +}; + +int audit_handler_syslog_write(audit_handler_t *handler, const char *buf, + size_t len); +int audit_handler_syslog_flush(audit_handler_t *handler); +int audit_handler_syslog_close(audit_handler_t *handler); + +audit_handler_t *audit_handler_syslog_open( + audit_handler_syslog_config_t *opts) noexcept { + audit_handler_t *handler = (audit_handler_t *)my_malloc( + key_memory_audit_log_handler, + sizeof(audit_handler_t) + sizeof(audit_handler_syslog_data_t), + MY_ZEROFILL); + if (handler != nullptr) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)(handler + 1); + + data->struct_size = sizeof(audit_handler_syslog_data_t); + data->priority = opts->priority; + data->header = opts->header; + data->footer = opts->footer; + openlog(opts->ident, 0, opts->facility); + MY_STAT stat_arg; + memset(&stat_arg, 0, sizeof(stat_arg)); + opts->header(&stat_arg, nullptr, 0); + handler->data = data; + handler->write = audit_handler_syslog_write; + handler->flush = audit_handler_syslog_flush; + handler->close = audit_handler_syslog_close; + } + return handler; +} + +int audit_handler_syslog_write(audit_handler_t *handler, const char *buf, + size_t len) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)handler->data; + assert(data->struct_size == sizeof(audit_handler_syslog_data_t)); + syslog(data->priority, "%s", buf); + return len; +} + +int audit_handler_syslog_flush(audit_handler_t *handler) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)handler->data; + MY_STAT stat_arg; + memset(&stat_arg, 0, sizeof(stat_arg)); + data->header(&stat_arg, nullptr, 0); + data->footer(nullptr, 0); + return 0; +} + +int audit_handler_syslog_close(audit_handler_t *handler) { + audit_handler_syslog_data_t *data = + (audit_handler_syslog_data_t *)handler->data; + data->footer(nullptr, 0); + closelog(); + my_free(handler); + return 0; +} diff --git a/plugin/audit_log/buffer.cc b/plugin/audit_log/buffer.cc new file mode 100644 index 000000000000..01fc9b0d4bf6 --- /dev/null +++ b/plugin/audit_log/buffer.cc @@ -0,0 +1,198 @@ +/* Copyright (c) 2014 Percona LLC 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-1301 USA */ + +#include "buffer.h" + +#include "audit_log.h" +#include "my_dbug.h" +#include "my_sys.h" +#include "my_systime.h" +#include "my_thread.h" +#include "mysql/psi/mysql_cond.h" +#include "mysql/psi/mysql_mutex.h" +#include "mysql/psi/psi_cond.h" +#include "mysql/psi/psi_mutex.h" +#include "mysql/service_mysql_alloc.h" +#include "template_utils.h" +#include "thr_mutex.h" + +struct audit_log_buffer_t { + char *buf; + size_t size; + size_t write_pos; + size_t flush_pos; + pthread_t flush_worker_thread; + bool stop; + bool drop_if_full; + void *write_func_data; + audit_log_write_func write_func; + mysql_mutex_t mutex; + mysql_cond_t flushed_cond; + mysql_cond_t written_cond; + log_record_state_t state; +}; + +#if defined(HAVE_PSI_INTERFACE) +/* These belong to the service initialization */ +static PSI_mutex_key key_log_mutex; +static PSI_mutex_info mutex_key_list[] = { + {&key_log_mutex, "audit_log_buffer::mutex", PSI_FLAG_SINGLETON, 0, + PSI_DOCUMENT_ME}}; + +static PSI_cond_key key_log_written_cond, key_log_flushed_cond; +static PSI_cond_info cond_key_list[] = { + {&key_log_written_cond, "audit_log_buffer::written_cond", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, + {&key_log_flushed_cond, "audit_log_buffer::flushed_cond", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}}; + +#endif + +static void audit_log_flush(audit_log_buffer_t *log) { + mysql_mutex_lock(&log->mutex); + while (log->flush_pos == log->write_pos) { + if (log->stop) { + mysql_mutex_unlock(&log->mutex); + return; + } + timespec abstime; + set_timespec(&abstime, 1); + mysql_cond_timedwait(&log->written_cond, &log->mutex, &abstime); + } + + if (log->flush_pos >= log->write_pos % log->size) { + log->state = log_record_state_t::INCOMPLETE; + mysql_mutex_unlock(&log->mutex); + log->write_func(log->write_func_data, log->buf + log->flush_pos, + log->size - log->flush_pos, log_record_state_t::INCOMPLETE); + mysql_mutex_lock(&log->mutex); + log->flush_pos = 0; + log->write_pos %= log->size; + } else { + const size_t flushlen = log->write_pos - log->flush_pos; + mysql_mutex_unlock(&log->mutex); + log->write_func(log->write_func_data, log->buf + log->flush_pos, flushlen, + log_record_state_t::COMPLETE); + mysql_mutex_lock(&log->mutex); + log->flush_pos += flushlen; + log->state = log_record_state_t::COMPLETE; + } + assert(log->write_pos >= log->flush_pos); + mysql_cond_broadcast(&log->flushed_cond); + mysql_mutex_unlock(&log->mutex); +} + +static void *audit_log_flush_worker(void *arg) { + audit_log_buffer_t *log = (audit_log_buffer_t *)arg; + + my_thread_init(); + while (!(log->stop && log->flush_pos == log->write_pos)) { + audit_log_flush(log); + } + my_thread_end(); + + return nullptr; +} + +audit_log_buffer_t *audit_log_buffer_init(size_t size, bool drop_if_full, + audit_log_write_func write_func, + void *data) noexcept { + audit_log_buffer_t *log = (audit_log_buffer_t *)my_malloc( + key_memory_audit_log_buffer, sizeof(audit_log_buffer_t) + size, + MY_ZEROFILL); + +#ifdef HAVE_PSI_INTERFACE + mysql_mutex_register(AUDIT_LOG_PSI_CATEGORY, mutex_key_list, + array_elements(mutex_key_list)); + mysql_cond_register(AUDIT_LOG_PSI_CATEGORY, cond_key_list, + array_elements(cond_key_list)); +#endif /* HAVE_PSI_INTERFACE */ + + if (log != nullptr) { + log->buf = ((char *)log + sizeof(audit_log_buffer_t)); + log->drop_if_full = drop_if_full; + log->write_func = write_func; + log->write_func_data = data; + log->size = size; + log->state = log_record_state_t::COMPLETE; + + mysql_mutex_init(key_log_mutex, &log->mutex, MY_MUTEX_INIT_FAST); + mysql_cond_init(key_log_flushed_cond, &log->flushed_cond); + mysql_cond_init(key_log_written_cond, &log->written_cond); + pthread_create(&log->flush_worker_thread, nullptr, audit_log_flush_worker, + log); + } + + return log; +} + +void audit_log_buffer_shutdown(audit_log_buffer_t *log) noexcept { + log->stop = true; + + pthread_join(log->flush_worker_thread, nullptr); + mysql_cond_destroy(&log->flushed_cond); + mysql_cond_destroy(&log->written_cond); + mysql_mutex_destroy(&log->mutex); + + my_free(log); +} + +void audit_log_buffer_pause(audit_log_buffer_t *log) noexcept { + mysql_mutex_lock(&log->mutex); + while (log->state == log_record_state_t::INCOMPLETE) { + mysql_cond_wait(&log->flushed_cond, &log->mutex); + } +} + +void audit_log_buffer_resume(audit_log_buffer_t *log) noexcept { + mysql_mutex_unlock(&log->mutex); +} + +int audit_log_buffer_write(audit_log_buffer_t *log, const char *buf, + size_t len) { + if (len > log->size) { + if (!log->drop_if_full) { + /* pause flushing thread and write out one record bypassing the buffer */ + audit_log_buffer_pause(log); + log->write_func(log->write_func_data, buf, len, + log_record_state_t::COMPLETE); + audit_log_buffer_resume(log); + } + return (0); + } + + mysql_mutex_lock(&log->mutex); +loop: + if (log->write_pos + len <= log->flush_pos + log->size) { + const size_t wrlen = + std::min(len, log->size - (log->write_pos % log->size)); + memcpy(log->buf + (log->write_pos % log->size), buf, wrlen); + if (wrlen < len) memcpy(log->buf, buf + wrlen, len - wrlen); + log->write_pos = log->write_pos + len; + assert(log->write_pos >= log->flush_pos); + } else { + if (!log->drop_if_full) { + mysql_cond_wait(&log->flushed_cond, &log->mutex); + goto loop; + } + } + if (log->write_pos > log->flush_pos + log->size / 2) { + mysql_cond_signal(&log->written_cond); + } + mysql_mutex_unlock(&log->mutex); + + return (0); +} diff --git a/plugin/audit_log/buffer.h b/plugin/audit_log/buffer.h new file mode 100644 index 000000000000..a59115b3af68 --- /dev/null +++ b/plugin/audit_log/buffer.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2014 Percona LLC 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-1301 USA */ + +#ifndef AUDIT_LOG_BUFFER_INCLUDED +#define AUDIT_LOG_BUFFER_INCLUDED + +#include +#include "logger.h" + +struct audit_log_buffer_t; + +typedef int (*audit_log_write_func)(void *data, const char *buf, size_t len, + log_record_state_t state); + +audit_log_buffer_t *audit_log_buffer_init(size_t size, bool drop_if_full, + audit_log_write_func write_func, + void *data) noexcept; +void audit_log_buffer_shutdown(audit_log_buffer_t *log) noexcept; +int audit_log_buffer_write(audit_log_buffer_t *log, const char *buf, + size_t len); +void audit_log_buffer_pause(audit_log_buffer_t *log) noexcept; +void audit_log_buffer_resume(audit_log_buffer_t *log) noexcept; + +#endif diff --git a/plugin/audit_log/file_logger.cc b/plugin/audit_log/file_logger.cc new file mode 100644 index 000000000000..5c6d15b06138 --- /dev/null +++ b/plugin/audit_log/file_logger.cc @@ -0,0 +1,296 @@ +/* Copyright (C) 2012 Monty Program Ab + + 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 Street, Fifth Floor, Boston, MA 02110-1301, + USA */ + +#include +#include "my_dir.h" +#include "my_sys.h" +#include "mysql/plugin.h" +#include "sql/mysqld.h" + +#include "audit_log.h" +#include "logger.h" + +#ifndef FLOGGER_NO_PSI +#define flogger_mutex_init(A, B, C) \ + if ((B)->thread_safe) mysql_mutex_init(A, &((B)->lock), C) + +#define flogger_mutex_destroy(A) \ + if ((A)->thread_safe) mysql_mutex_destroy(&((A)->lock)) + +#define flogger_mutex_lock(A) \ + if ((A)->thread_safe) mysql_mutex_lock(&((A)->lock)) + +#define flogger_mutex_unlock(A) \ + if ((A)->thread_safe) mysql_mutex_unlock(&((A)->lock)) +#else +#define flogger_mutex_init(A, B, C) \ + if ((B)->thread_safe) pthread_mutex_init(&((B)->lock.m_mutex), C) + +#define flogger_mutex_destroy(A) \ + if ((A)->thread_safe) pthread_mutex_destroy(&((A)->lock.m_mutex)) + +#define flogger_mutex_lock(A) \ + if ((A)->thread_safe) pthread_mutex_lock(&((A)->lock.m_mutex)) + +#define flogger_mutex_unlock(A) \ + if ((A)->thread_safe) pthread_mutex_unlock(&((A)->lock.m_mutex)) +#endif /*!FLOGGER_NO_PSI*/ + +#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) +/* These belong to the service initialization */ +static PSI_mutex_key key_LOCK_logger_service; +static PSI_mutex_info mutex_list[] = { + {&key_LOCK_logger_service, "file_logger::lock", PSI_FLAG_SINGLETON, + PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}}; +#else +#define key_LOCK_logger_service nullptr +#endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/ + +struct LOGGER_HANDLE { + File file; + char path[FN_REFLEN]; + unsigned long long size_limit; + unsigned int rotations; + size_t path_len; + mysql_mutex_t lock; + bool thread_safe; +}; + +#define LOG_FLAGS (O_APPEND | O_CREAT | O_WRONLY) + +static constexpr unsigned int n_dig(unsigned int i) noexcept { + return (i == 0) ? 0 : ((i < 10) ? 1 : ((i < 100) ? 2 : 3)); +} + +LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned int rotations, bool thread_safe, + logger_prolog_func_t header) noexcept { + /* + I don't think we ever need more rotations, + but if it's so, the rotation procedure should be adapted to it. + */ + if (rotations > 999) return 0; + + LOGGER_HANDLE new_log; + new_log.rotations = rotations; + new_log.size_limit = size_limit; + new_log.path_len = strlen( + fn_format(new_log.path, path, mysql_data_home, "", MY_UNPACK_FILENAME)); + new_log.thread_safe = thread_safe; + + if (new_log.path_len + n_dig(rotations) + 1 > FN_REFLEN) { + errno = ENAMETOOLONG; + /* File path too long */ + return 0; + } + + if ((new_log.file = my_open(new_log.path, LOG_FLAGS, 0666)) < 0) { + errno = my_errno(); + /* Check errno for the cause */ + return 0; + } + + MY_STAT stat_arg; + if (my_fstat(new_log.file, &stat_arg)) { + errno = my_errno(); + my_close(new_log.file, MYF(0)); + new_log.file = -1; + return 0; + } + + LOGGER_HANDLE *l_perm; + if (!(l_perm = (LOGGER_HANDLE *)my_malloc(key_memory_audit_log_logger_handle, + sizeof(LOGGER_HANDLE), MYF(0)))) { + my_close(new_log.file, MYF(0)); + new_log.file = -1; + return 0; /* End of memory */ + } + *l_perm = new_log; + + flogger_mutex_init(key_LOCK_logger_service, l_perm, MY_MUTEX_INIT_FAST); + + char buf[128]; + size_t len; + len = header(&stat_arg, buf, sizeof(buf)); + my_write(l_perm->file, (uchar *)buf, len, MYF(0)); + + return l_perm; +} + +int logger_close(LOGGER_HANDLE *log, logger_epilog_func_t footer) noexcept { + File file = log->file; + char buf[128]; + const size_t len = footer(buf, sizeof(buf)); + my_write(file, (uchar *)buf, len, MYF(0)); + + flogger_mutex_destroy(log); + my_free(log); + int result; + if ((result = my_close(file, MYF(0)))) errno = my_errno(); + return result; +} + +int logger_reopen(LOGGER_HANDLE *log, logger_prolog_func_t header, + logger_epilog_func_t footer) noexcept { + flogger_mutex_lock(log); + + char buf[128]; + size_t len = footer(buf, sizeof(buf)); + my_write(log->file, (uchar *)buf, len, MYF(0)); + + int result = 0; + if ((result = my_close(log->file, MYF(0)))) { + errno = my_errno(); + goto error; + } + + if ((log->file = my_open(log->path, LOG_FLAGS, MYF(0))) < 0) { + errno = my_errno(); + result = 1; + goto error; + } + + MY_STAT stat_arg; + if ((result = my_fstat(log->file, &stat_arg))) { + errno = my_errno(); + goto error; + } + + len = header(&stat_arg, buf, sizeof(buf)); + my_write(log->file, (uchar *)buf, len, MYF(0)); + +error: + flogger_mutex_unlock(log); + + return result; +} + +static char *logname(const LOGGER_HANDLE &log, char *buf, size_t buf_len, + unsigned int n_log) noexcept { + snprintf(buf + log.path_len, buf_len, ".%0*u", n_dig(log.rotations), n_log); + return buf; +} + +static int do_rotate(LOGGER_HANDLE *log) { + if (log->rotations == 0) return 0; + + char namebuf[FN_REFLEN]; + memcpy(namebuf, log->path, log->path_len); + + int result; + char *buf_new = logname(*log, namebuf, sizeof(namebuf), log->rotations); + char *buf_old = log->path; + for (auto i = log->rotations - 1; i > 0; i--) { + logname(*log, buf_old, FN_REFLEN, i); + if (!access(buf_old, F_OK) && + (result = my_rename(buf_old, buf_new, MYF(0)))) + goto exit; + char *tmp = buf_old; + buf_old = buf_new; + buf_new = tmp; + } + if ((result = my_close(log->file, MYF(0)))) goto exit; + namebuf[log->path_len] = 0; + result = my_rename(namebuf, logname(*log, log->path, FN_REFLEN, 1), MYF(0)); + log->file = my_open(namebuf, LOG_FLAGS, MYF(0)); +exit: + errno = my_errno(); + return log->file < 0 || result; +} + +int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, va_list ap) noexcept { + char cvtbuf[1024]; + size_t n_bytes; + + int result; + flogger_mutex_lock(log); + if (log->rotations > 0) { + my_off_t filesize; + if ((filesize = my_tell(log->file, MYF(0))) == (my_off_t)-1 || + ((unsigned long long)filesize >= log->size_limit && do_rotate(log))) { + result = -1; + errno = my_errno(); + goto exit; /* Log rotation needed but failed */ + } + } + + n_bytes = vsnprintf(cvtbuf, sizeof(cvtbuf), fmt, ap); + if (n_bytes >= sizeof(cvtbuf)) n_bytes = sizeof(cvtbuf) - 1; + + result = my_write(log->file, (uchar *)cvtbuf, n_bytes, MYF(0)); + +exit: + flogger_mutex_unlock(log); + return result; +} + +int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size, + log_record_state_t state) noexcept { + flogger_mutex_lock(log); + + int result = my_write(log->file, (const uchar *)buffer, size, MYF(0)); + + if (state == log_record_state_t::COMPLETE && log->rotations > 0) { + my_off_t filesize; + if ((filesize = my_tell(log->file, MYF(0))) == (my_off_t)-1 || + ((unsigned long long)filesize >= log->size_limit && do_rotate(log))) { + result = -1; + errno = my_errno(); + } + } + + flogger_mutex_unlock(log); + return result; +} + +int logger_rotate(LOGGER_HANDLE *log) { + flogger_mutex_lock(log); + const int result = do_rotate(log); + flogger_mutex_unlock(log); + return result; +} + +#ifndef __clang__ +MY_ATTRIBUTE((format(gnu_printf, 2, 3))) +#endif +int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...) noexcept { + va_list args; + va_start(args, fmt); + const int result = logger_vprintf(log, fmt, args); + va_end(args); + return result; +} + +void logger_init_mutexes() noexcept { +#if defined(HAVE_PSI_INTERFACE) && !defined(FLOGGER_NO_PSI) && \ + !defined(FLOGGER_NO_THREADSAFE) + mysql_mutex_register(AUDIT_LOG_PSI_CATEGORY, mutex_list, + array_elements(mutex_list)); +#endif /*HAVE_PSI_INTERFACE && !FLOGGER_NO_PSI*/ +} + +int logger_sync(LOGGER_HANDLE *log) noexcept { + return my_sync(log->file, MYF(0)); +} + +void logger_set_size_limit(LOGGER_HANDLE *log, + unsigned long long size_limit) noexcept { + log->size_limit = size_limit; +} + +void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations) noexcept { + log->rotations = rotations; +} diff --git a/plugin/audit_log/filter.cc b/plugin/audit_log/filter.cc new file mode 100644 index 000000000000..746f446f11c7 --- /dev/null +++ b/plugin/audit_log/filter.cc @@ -0,0 +1,389 @@ +/* Copyright (c) 2016 Percona LLC 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-1301 USA */ + +#include "filter.h" + +#include +#include "audit_log.h" +#include "m_ctype.h" +#include "map_helpers.h" +#include "my_hostname.h" +#include "my_sys.h" +#include "my_user.h" +#include "mysql/psi/mysql_rwlock.h" +#include "mysql_com.h" + +static std::string make_account_string(const char *user, size_t user_length, + const char *host, size_t host_length) { + std::string result(user, user_length); + result.reserve(user_length + host_length + 2); + result.append(1, '@'); + result.append(host, host_length); + result.append(1, '\0'); + return result; +} + +static std::string make_command_string(const char *name, size_t length) { + std::string result(name, length); + std::transform(result.begin(), result.end(), result.begin(), ::tolower); + result.shrink_to_fit(); + return result; +} + +using account_set_t = collation_unordered_set; + +static account_set_t *include_accounts; +static account_set_t *exclude_accounts; + +using db_set_t = collation_unordered_set; + +static db_set_t *include_databases; +static db_set_t *exclude_databases; + +using command_set_t = malloc_unordered_set; + +static command_set_t *include_commands; +static command_set_t *exclude_commands; + +#if defined(HAVE_PSI_INTERFACE) + +static PSI_rwlock_key key_LOCK_account_list; +static PSI_rwlock_key key_LOCK_database_list; +static PSI_rwlock_key key_LOCK_command_list; +static PSI_rwlock_info all_rwlock_list[] = { + {&key_LOCK_account_list, "audit_log_filter::account_list", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, + {&key_LOCK_database_list, "audit_log_filter::database_list", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, + {&key_LOCK_account_list, "audit_log_filter::command_list", + PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}}; + +#endif + +static mysql_rwlock_t LOCK_account_list; +static mysql_rwlock_t LOCK_database_list; +static mysql_rwlock_t LOCK_command_list; + +/* + Allocate memory and initialize new command +*/ + +/* + Remove enclosing quotes from string if any. +*/ +static void unquote_string(char *string, size_t *string_length) noexcept { + if (string[0] == '\'' && string[*string_length - 1] == '\'') { + *string_length -= 2; + memmove(string, string + 1, *string_length); + string[*string_length] = 0; + } +} + +/* + Parse comma-separated list of accounts and add it into account list. + Empty user name is allowed. +*/ +static void account_list_from_string(account_set_t *account_set, + const char *string) { + account_set->clear(); + + char *string_copy = my_strdup(PSI_NOT_INSTRUMENTED, string, MYF(MY_FAE)); + const size_t string_length = strlen(string_copy); + char *entry = string_copy; + while (entry - string_copy < static_cast(string_length)) { + size_t entry_length = 0; + bool quote = false; + + while (*entry == ' ') entry++; + + entry_length = 0; + while ( + ((entry[entry_length] != ' ' && entry[entry_length] != ',') || quote) && + entry[entry_length] != 0) { + if (entry[entry_length] == '\'') quote = !quote; + entry_length++; + } + + entry[entry_length] = 0; + + char user[USERNAME_LENGTH + 1], host[HOSTNAME_LENGTH + 1]; + size_t user_length, host_length; + parse_user(entry, entry_length, user, &user_length, host, &host_length); + unquote_string(user, &user_length); + unquote_string(host, &host_length); + my_casedn_str(system_charset_info, host); + + account_set->emplace( + make_account_string(user, user_length, host, host_length)); + + entry += entry_length + 1; + } + + my_free(string_copy); +} + +static void database_list_from_string(db_set_t *db_set, const char *string) { + const char *entry = string; + + db_set->clear(); + + while (*entry) { + while (*entry == ' ') entry++; + + size_t entry_length = 0; + bool quote = false; + char name[NAME_LEN + 1]; + size_t name_length = 0; + while ( + ((entry[entry_length] != ' ' && entry[entry_length] != ',') || quote) && + entry[entry_length] != 0) { + if (quote && entry[entry_length] == '`' && + entry[entry_length + 1] == '`') { + name[name_length++] = '`'; + entry_length += 1; + } else if (entry[entry_length] == '`') + quote = !quote; + else if (name_length < sizeof(name)) + name[name_length++] = entry[entry_length]; + entry_length++; + } + + if (name_length > 0) { + name[name_length] = 0; + db_set->emplace(name, name_length); + } + + entry += entry_length; + + if (*entry == ',') entry++; + } +} + +/* + Parse comma-separated list of command and add it into command hash. +*/ +static void command_list_from_string(command_set_t *command_set, + const char *string) { + std::string lcase_str(string); + std::transform(lcase_str.begin(), lcase_str.end(), lcase_str.begin(), + ::tolower); + + command_set->clear(); + + auto it = lcase_str.cbegin(); + while (it != lcase_str.cend()) { + std::string::size_type len = 0; + while (it != lcase_str.cend() && (*it == ' ' || *it == ',')) it++; + while (it + len != lcase_str.cend() && it[len] != ' ' && it[len] != ',') + len++; + if (len > 0) { + command_set->emplace(&(*it), len); + it += len; + } + } +} + +/* public interface */ + +void audit_log_filter_init() { +#ifdef HAVE_PSI_INTERFACE + mysql_rwlock_register(AUDIT_LOG_PSI_CATEGORY, all_rwlock_list, + array_elements(all_rwlock_list)); +#endif /* HAVE_PSI_INTERFACE */ + mysql_rwlock_init(key_LOCK_account_list, &LOCK_account_list); + mysql_rwlock_init(key_LOCK_database_list, &LOCK_database_list); + mysql_rwlock_init(key_LOCK_command_list, &LOCK_command_list); + + include_accounts = + new account_set_t(&my_charset_bin, key_memory_audit_log_accounts); + + exclude_accounts = + new account_set_t(&my_charset_bin, key_memory_audit_log_accounts); + + include_databases = + new db_set_t(&my_charset_bin, key_memory_audit_log_databases); + + exclude_databases = + new db_set_t(&my_charset_bin, key_memory_audit_log_databases); + + include_commands = new command_set_t(key_memory_audit_log_commands); + + exclude_commands = new command_set_t(key_memory_audit_log_commands); +} + +void audit_log_filter_destroy() noexcept { + delete include_accounts; + delete exclude_accounts; + delete include_databases; + delete exclude_databases; + delete include_commands; + delete exclude_commands; + mysql_rwlock_destroy(&LOCK_account_list); + mysql_rwlock_destroy(&LOCK_database_list); + mysql_rwlock_destroy(&LOCK_account_list); + mysql_rwlock_destroy(&LOCK_command_list); +} + +/* + Parse and store the list of included accounts. +*/ +void audit_log_set_include_accounts(const char *val) { + mysql_rwlock_wrlock(&LOCK_account_list); + account_list_from_string(include_accounts, val); + mysql_rwlock_unlock(&LOCK_account_list); +} + +/* + Parse and store the list of excluded accounts. +*/ +void audit_log_set_exclude_accounts(const char *val) { + mysql_rwlock_wrlock(&LOCK_account_list); + account_list_from_string(exclude_accounts, val); + mysql_rwlock_unlock(&LOCK_account_list); +} + +/* + Check if account has to be included. +*/ +bool audit_log_check_account_included(const char *user, size_t user_length, + const char *host, size_t host_length) { + const std::string acc{ + make_account_string(user, user_length, host, host_length)}; + + mysql_rwlock_rdlock(&LOCK_account_list); + const auto &it = include_accounts->find(acc); + const bool res = it != include_accounts->cend(); + mysql_rwlock_unlock(&LOCK_account_list); + + return res; +} + +/* + Check if account has to be excluded. +*/ +bool audit_log_check_account_excluded(const char *user, size_t user_length, + const char *host, size_t host_length) { + const std::string acc{ + make_account_string(user, user_length, host, host_length)}; + + mysql_rwlock_rdlock(&LOCK_account_list); + const auto &it = exclude_accounts->find(acc); + const bool res = it != exclude_accounts->cend(); + ; + mysql_rwlock_unlock(&LOCK_account_list); + + return res; +} + +/* + Parse and store the list of included databases. +*/ +void audit_log_set_include_databases(const char *val) { + mysql_rwlock_wrlock(&LOCK_database_list); + database_list_from_string(include_databases, val); + mysql_rwlock_unlock(&LOCK_database_list); +} + +/* + Parse and store the list of excluded databases. +*/ +void audit_log_set_exclude_databases(const char *val) { + mysql_rwlock_wrlock(&LOCK_database_list); + database_list_from_string(exclude_databases, val); + mysql_rwlock_unlock(&LOCK_database_list); +} + +/* + Check if database has to be included. +*/ +bool audit_log_check_database_included(const char *name, size_t length) { + if (length == 0) return false; + + const std::string db(name, length); + + mysql_rwlock_rdlock(&LOCK_database_list); + const auto &it = include_databases->find(db); + const bool res = it != include_databases->cend(); + mysql_rwlock_unlock(&LOCK_database_list); + + return res; +} + +/* + Check if database has to be excluded. +*/ +bool audit_log_check_database_excluded(const char *name, size_t length) { + if (length == 0) return false; + + const std::string db(name, length); + + mysql_rwlock_rdlock(&LOCK_database_list); + const auto &it = exclude_databases->find(db); + const bool res = it != exclude_databases->cend(); + mysql_rwlock_unlock(&LOCK_database_list); + + return res; +} + +/* + Parse and store the list of included commands. +*/ +void audit_log_set_include_commands(const char *val) { + mysql_rwlock_wrlock(&LOCK_command_list); + command_list_from_string(include_commands, val); + mysql_rwlock_unlock(&LOCK_command_list); +} + +/* + Parse and store the list of excluded commands. +*/ +void audit_log_set_exclude_commands(const char *val) { + mysql_rwlock_wrlock(&LOCK_command_list); + command_list_from_string(exclude_commands, val); + mysql_rwlock_unlock(&LOCK_command_list); +} + +/* + Check if command has to be included. +*/ +bool audit_log_check_command_included(const char *name, size_t length) { + if (length == 0) return false; + + const std::string cmd{make_command_string(name, length)}; + + mysql_rwlock_rdlock(&LOCK_command_list); + const auto &it = include_commands->find(cmd); + const bool res = it != include_commands->cend(); + mysql_rwlock_unlock(&LOCK_command_list); + + return res; +} + +/* + Check if command has to be excluded. +*/ +bool audit_log_check_command_excluded(const char *name, size_t length) { + if (length == 0) return false; + + const std::string cmd{make_command_string(name, length)}; + + mysql_rwlock_rdlock(&LOCK_command_list); + const auto &it = exclude_commands->find(cmd); + const bool res = it != exclude_commands->cend(); + mysql_rwlock_unlock(&LOCK_command_list); + + return res; +} diff --git a/plugin/audit_log/filter.h b/plugin/audit_log/filter.h new file mode 100644 index 000000000000..1622ef2f6d32 --- /dev/null +++ b/plugin/audit_log/filter.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2016 Percona LLC 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-1301 USA */ + +#ifndef AUDIT_LOG_FILTER_INCLUDED +#define AUDIT_LOG_FILTER_INCLUDED + +#include + +void audit_log_set_include_accounts(const char *val); +void audit_log_set_exclude_accounts(const char *val); +bool audit_log_check_account_included(const char *user, size_t user_length, + const char *host, size_t host_length); +bool audit_log_check_account_excluded(const char *user, size_t user_length, + const char *host, size_t host_length); + +void audit_log_set_include_databases(const char *val); +void audit_log_set_exclude_databases(const char *val); +bool audit_log_check_database_included(const char *name, size_t length); +bool audit_log_check_database_excluded(const char *name, size_t length); + +void audit_log_set_include_commands(const char *val); +void audit_log_set_exclude_commands(const char *val); +bool audit_log_check_command_included(const char *command, size_t length); +bool audit_log_check_command_excluded(const char *command, size_t length); + +void audit_log_filter_init(); +void audit_log_filter_destroy() noexcept; + +#endif diff --git a/plugin/audit_log/logger.h b/plugin/audit_log/logger.h new file mode 100644 index 000000000000..fd1503b1da80 --- /dev/null +++ b/plugin/audit_log/logger.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2012 Monty Program Ab + + 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 Street, Fifth Floor, Boston, MA 02110-1301, + USA */ + +#ifndef MYSQL_SERVICE_LOGGER_INCLUDED +#define MYSQL_SERVICE_LOGGER_INCLUDED + +/** + @file + logger service + + Log file with rotation implementation. + + This service implements logging with possible rotation + of the log files. Interface intentionally tries to be similar to FILE* + related functions. + + So that one can open the log with logger_open(), specifying + the limit on the logfile size and the rotations number. + + Then it's possible to write messages to the log with + logger_printf or logger_vprintf functions. + + As the size of the logfile grows over the specified limit, + it is renamed to 'logfile.1'. The former 'logfile.1' becomes + 'logfile.2', etc. The file 'logfile.rotations' is removed. + That's how the rotation works. + + The rotation can be forced with the logger_rotate() call. + + Finally the log should be closed with logger_close(). + +@notes: + Implementation checks the size of the log file before it starts new + printf into it. So the size of the file gets over the limit when it rotates. + + The access is secured with the mutex, so the log is threadsafe. +*/ + +#include +#include +#include "my_compiler.h" +#include "my_dir.h" + +struct LOGGER_HANDLE; + +typedef size_t (*logger_prolog_func_t)(MY_STAT *, char *buf, size_t buflen); +typedef size_t (*logger_epilog_func_t)(char *buf, size_t buflen); + +enum class log_record_state_t { COMPLETE, INCOMPLETE }; + +void logger_init_mutexes() noexcept; +LOGGER_HANDLE *logger_open(const char *path, unsigned long long size_limit, + unsigned int rotations, bool thread_safe, + logger_prolog_func_t header) noexcept; +int logger_close(LOGGER_HANDLE *log, logger_epilog_func_t footer) noexcept; +#ifndef __clang__ +MY_ATTRIBUTE((format(gnu_printf, 2, 0))) +#endif +int logger_vprintf(LOGGER_HANDLE *log, const char *fmt, + va_list argptr) noexcept; +#ifndef __clang__ +MY_ATTRIBUTE((format(gnu_printf, 2, 3))) +#endif +int logger_printf(LOGGER_HANDLE *log, const char *fmt, ...) noexcept; +int logger_write(LOGGER_HANDLE *log, const char *buffer, size_t size, + log_record_state_t state) noexcept; +int logger_rotate(LOGGER_HANDLE *log); +int logger_sync(LOGGER_HANDLE *log) noexcept; +int logger_reopen(LOGGER_HANDLE *log, logger_prolog_func_t header, + logger_epilog_func_t footer) noexcept; +void logger_set_size_limit(LOGGER_HANDLE *log, + unsigned long long size_limit) noexcept; +void logger_set_rotations(LOGGER_HANDLE *log, unsigned int rotations) noexcept; + +#endif /*MYSQL_SERVICE_LOGGER_INCLUDED*/ diff --git a/plugin/audit_log/tests/mtr/audit_log_charset-master.opt b/plugin/audit_log/tests/mtr/audit_log_charset-master.opt new file mode 100644 index 000000000000..ad7f91c65bd5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_charset-master.opt @@ -0,0 +1,6 @@ +$AUDIT_LOG_OPT +$AUDIT_LOG_LOAD +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_charset.result b/plugin/audit_log/tests/mtr/audit_log_charset.result new file mode 100644 index 000000000000..56221f2d92dc --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_charset.result @@ -0,0 +1,74 @@ +SET GLOBAL audit_log_flush=ON; +set names tis620; +SET NAMES utf8mb4; +INSERT IGNORE INTO t VALUES ('𦉘𦟌𦧲'); +Warnings: +Warning 1366 Incorrect string value: '\xF0\xA6\x89\x98\xF0\xA6...' for column 'txt' at row 1 +CREATE DATABASE 𦉘𦟌𦧲; +Warnings: +Warning 1300 Cannot convert string '\xF0\xA6\x89\x98\xF0\xA6...' from utf8mb4 to utf8mb3 +SHOW DATABASES; +Database +??? +information_schema +mtr +mysql +performance_schema +sys +test +ฐานข้อมูล +use 𦉘𦟌𦧲; +Warnings: +Warning 1300 Cannot convert string '\xF0\xA6\x89\x98\xF0\xA6...' from utf8mb4 to utf8mb3 +use ฐานข้อมูล; +SET NAMES utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +SELECT * FROM t WHERE txt LIKE 'ขุนนาง%'; +txt +ขุนนางใช่พ่อแม่ หินแง่ใช่ตายาย +use test; +SELECT * FROM ฐานข้อมูล.t LIMIT 1; +txt +ขุนนางใช่พ่อแม่ หินแง่ใช่ตายาย +use ฐานข้อมูล; +DROP DATABASE ฐานข้อมูล; +DROP DATABASE `???`; +use test; +SET @@character_set_client=cp1256; +CREATE t \217\355ݏ\355ݏ\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT CHARSET=ujis engine=TokuDB; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 't \217\355ف?\355ف?\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT ' at line 1 +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"set names tis620","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT x'b0d2b9a2e9cdc1d9c5'","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT x'a2d8b9b9d2a7e3aae8bee8cde1c1e820cbd4b9e1a7e8e3aae8b5d2c2d2c2'","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT x'a1a7e0a1c7d5c2b9a1d3e0a1c7d5c2b9'","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT REPEAT('กงเกวียนกำเกวียน ', 400)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 'ฐานข้อมูล'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE `ฐานข้อมูล`","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"use `ฐานข้อมูล`","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","create_table","",0,"CREATE TABLE t (txt TEXT) charset='utf8'","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT INTO t VALUES ('ขุนนางใช่พ่อแม่ หินแง่ใช่ตายาย')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT INTO t VALUES ('กงเกวียนกำเกวียน')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT INTO t VALUES ('กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน กงเกวียนกำเกวียน ')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","insert","",0,"INSERT IGNORE INTO t VALUES ('𦉘𦟌𦧲')","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","create_db","",0,"CREATE DATABASE 𦉘𦟌𦧲","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_databases","",0,"SHOW DATABASES","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","change_db","",0,"use 𦉘𦟌𦧲","root[root] @ localhost []","localhost","","","???" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","???" +"Query","","","change_db","",0,"use ฐานข้อมูล","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","select","",0,"SELECT * FROM t WHERE txt LIKE 'ขุนนาง%'","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","change_db","",0,"use test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM ฐานข้อมูล.t LIMIT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"use ฐานข้อมูล","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","drop_db","",0,"DROP DATABASE ฐานข้อมูล","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","drop_db","",0,"DROP DATABASE `???`","root[root] @ localhost []","localhost","","","ฐานข้อมูล" +"Query","","","change_db","",0,"use test","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET @@character_set_client=cp1256","root[root] @ localhost []","localhost","","","test" +"Query","","","error","",1064,"CREATE t \217\355ف?\355ف?\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT CHARSET=ujis engine=TokuDB","root[root] @ localhost []","localhost","","","test" +=================================================================== diff --git a/plugin/audit_log/tests/mtr/audit_log_charset.test b/plugin/audit_log/tests/mtr/audit_log_charset.test new file mode 100644 index 000000000000..a549832c0203 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_charset.test @@ -0,0 +1,64 @@ +# test encodings suppport by audit plugin + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +set names tis620; + +--disable_query_log +--disable_result_log + +let $db=`SELECT x'b0d2b9a2e9cdc1d9c5'`; +let $text1=`SELECT x'a2d8b9b9d2a7e3aae8bee8cde1c1e820cbd4b9e1a7e8e3aae8b5d2c2d2c2'`; +let $text2=`SELECT x'a1a7e0a1c7d5c2b9a1d3e0a1c7d5c2b9'`; +let $very_long_text=`SELECT REPEAT('$text2 ', 400)`; + +eval SELECT '$db'; + +eval CREATE DATABASE `$db`; + +eval use `$db`; + +CREATE TABLE t (txt TEXT) charset='utf8'; + +eval INSERT INTO t VALUES ('$text1'); +eval INSERT INTO t VALUES ('$text2'); +eval INSERT INTO t VALUES ('$very_long_text'); + +--enable_query_log +--enable_result_log + +SET NAMES utf8mb4; + +INSERT IGNORE INTO t VALUES ('𦉘𦟌𦧲'); +CREATE DATABASE 𦉘𦟌𦧲; + +SHOW DATABASES; + +use 𦉘𦟌𦧲; + +use ฐานข้อมูล; + +SET NAMES utf8; + +SELECT * FROM t WHERE txt LIKE 'ขุนนาง%'; + +use test; + +SELECT * FROM ฐานข้อมูล.t LIMIT 1; + +use ฐานข้อมูล; + +DROP DATABASE ฐานข้อมูล; +DROP DATABASE `???`; + +use test; + +SET @@character_set_client=cp1256; +--error ER_PARSE_ERROR +CREATE t \217\355ݏ\355ݏ\355\335(\217\260\241\217\260\241\217\260\241 char) DEFAULT CHARSET=ujis engine=TokuDB; + +--source audit_log_echo.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_csv-master.opt b/plugin/audit_log/tests/mtr/audit_log_csv-master.opt new file mode 100644 index 000000000000..fb919dedf07b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_csv-master.opt @@ -0,0 +1,3 @@ +--audit_log_file=test_audit.log +--audit-log-format=CSV +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_csv.result b/plugin/audit_log/tests/mtr/audit_log_csv.result new file mode 100644 index 000000000000..c94a9338321b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_csv.result @@ -0,0 +1,96 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_csv.test b/plugin/audit_log/tests/mtr/audit_log_csv.test new file mode 100644 index 000000000000..9cec7df34492 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_csv.test @@ -0,0 +1,23 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_csv.log +set global audit_log_flush= ON; +perl; + eval "use Text::CSV; 1" or exit 0; + my $p = Text::CSV->new({ binary => 1, sep_char => ',' }); + open my $file, $ENV{'MYSQLD_DATADIR'} . '/test_audit_csv.log' or die "Could not open log: $!"; + while ($p->getline($file)) {}; + if (not $p->eof) { + die "CSV parse error " . $p->error_diag(); + } + close $file; +EOF +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_csv.log diff --git a/plugin/audit_log/tests/mtr/audit_log_default_db-master.opt b/plugin/audit_log/tests/mtr/audit_log_default_db-master.opt new file mode 100644 index 000000000000..033178764ec5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_default_db-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_default_db.result b/plugin/audit_log/tests/mtr/audit_log_default_db.result new file mode 100644 index 000000000000..6c274bad24ac --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_default_db.result @@ -0,0 +1,102 @@ +set names utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +CREATE USER 'user1'@'%' IDENTIFIED BY '111'; +CREATE USER 'user2'@'%' IDENTIFIED BY '111'; +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE `ąžąžąžą`; +CREATE TABLE db1.t (a VARCHAR(100)); +CREATE TABLE db2.t (a VARCHAR(100)); +CREATE TABLE ąžąžąžą.t (a VARCHAR(100)) charset=utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +INSERT INTO db1.t VALUES ('db1'); +INSERT INTO db2.t VALUES ('db2'); +INSERT INTO ąžąžąžą.t VALUES ('ąžąžąžą'); +GRANT ALL PRIVILEGES ON db1.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON db2.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON db2.* TO 'user2'@'%'; +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user2'@'%'; +UNINSTALL PLUGIN audit_log; +Warnings: +Warning 1620 Plugin is busy and will be uninstalled on shutdown +INSTALL PLUGIN audit_log SONAME 'audit_log.so'; +SELECT * FROM t; +a +db2 +use `db1`; +ERROR 42000: Access denied for user 'user2'@'%' to database 'db1' +connect(localhost,user2,111,db1,MASTER_MYPORT,MASTER_MYSOCK); +ERROR 42000: Access denied for user 'user2'@'%' to database 'db1' +connect(localhost,user2,112,db2,MASTER_MYPORT,MASTER_MYSOCK); +ERROR 28000: Access denied for user 'user2'@'localhost' (using password: YES) +connect(localhost,user3,111,db2,MASTER_MYPORT,MASTER_MYSOCK); +ERROR 28000: Access denied for user 'user3'@'localhost' (using password: YES) +set names utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +SELECT * FROM t; +a +db2 +use `db1`; +ERROR 42000: Access denied for user 'user2'@'%' to database 'db1' +SELECT * FROM t; +a +db2 +set names utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +SELECT * FROM t; +a +db1 +use `db2`; +SELECT * FROM t; +a +db2 +use ąžąžąžą; +SELECT * FROM t; +a +ąžąžąžą +a +db1 +set global audit_log_flush= ON; +=================================================================== +"Query","","","install_plugin","",0,"INSTALL PLUGIN audit_log SONAME 'audit_log.so'","root[root] @ localhost []","localhost","","","" +"Quit","","","",0,"root","root","","","localhost","","test" +"Connect","","","",0,"user1","user1","","","localhost","","db2" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"use `db1`","user1[user1] @ localhost []","localhost","","","db1" +"Change user","","","",1044,"user2","user2","","","localhost","","" +"Quit","","","",0,"user1","user1","","","localhost","","db1" +"Connect","","","",1044,"user2","user2","","","localhost","","" +"Connect","","","",1045,"user2","user2","","","localhost","","" +"Connect","","","",1045,"user3","","","","localhost","","" +"Connect","","","",0,"user2","user2","","","localhost","","db2" +"Query","","","set_option","",0,"set names utf8","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","show_warnings","",0,"SHOW WARNINGS","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","select","",0,"SELECT * FROM t","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","change_db","",1044,"use `db1`","user2[user2] @ localhost []","localhost","","","db2" +"Query","","","select","",0,"SELECT * FROM t","user2[user2] @ localhost []","localhost","","","db2" +"Change user","","","",0,"user1","user1","","","localhost","","db1" +"Query","","","set_option","",0,"set names utf8","user1[user1] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","user1[user1] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"use `db2`","user1[user1] @ localhost []","localhost","","","db2" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"use ąžąžąžą","user1[user1] @ localhost []","localhost","","","ąžąžąžą" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","ąžąžąžą" +"Quit","","","",0,"user1","user1","","","localhost","","ąžąžąžą" +"Connect","","","",0,"user1","user1","","","localhost","","test" +"Query","","","select","",0,"select @@version_comment limit 1","user1[user1] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT DATABASE()","user1[user1] @ localhost []","localhost","","","test" +"Init DB","","","error","",0,"","user1[user1] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM t","user1[user1] @ localhost []","localhost","","","test" +"Quit","","","",0,"user1","user1","","","localhost","","db1" +=================================================================== +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ąžąžąžą; +DROP USER user1; +DROP USER user2; diff --git a/plugin/audit_log/tests/mtr/audit_log_default_db.test b/plugin/audit_log/tests/mtr/audit_log_default_db.test new file mode 100644 index 000000000000..e17e298e67da --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_default_db.test @@ -0,0 +1,100 @@ +# test correctness of default_db field + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +set names utf8; + +CREATE USER 'user1'@'%' IDENTIFIED BY '111'; +CREATE USER 'user2'@'%' IDENTIFIED BY '111'; + +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE `ąžąžąžą`; + +CREATE TABLE db1.t (a VARCHAR(100)); +CREATE TABLE db2.t (a VARCHAR(100)); +CREATE TABLE ąžąžąžą.t (a VARCHAR(100)) charset=utf8; +INSERT INTO db1.t VALUES ('db1'); +INSERT INTO db2.t VALUES ('db2'); +INSERT INTO ąžąžąžą.t VALUES ('ąžąžąžą'); + +GRANT ALL PRIVILEGES ON db1.* TO 'user1'@'%'; + +GRANT ALL PRIVILEGES ON db2.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON db2.* TO 'user2'@'%'; + +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user1'@'%'; +GRANT ALL PRIVILEGES ON ąžąžąžą.* TO 'user2'@'%'; + +# truncate audit log +UNINSTALL PLUGIN audit_log; +--source include/disconnect_connections.inc +let $wait_condition= + SELECT count(*) = 0 FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'audit_log'; +--source include/wait_condition.inc +--remove_file $log_file + +--source include/count_sessions.inc +connect (root,localhost,root,,,); +connection root; +INSTALL PLUGIN audit_log SONAME 'audit_log.so'; +disconnect root; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,user1,111,db2,); +connection test; +SELECT * FROM t; + +use `db1`; +--error ER_DBACCESS_DENIED_ERROR +change_user user2,111,db1,do_not_reconnect_on_fail; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc +--replace_result $MASTER_MYPORT MASTER_MYPORT $MASTER_MYSOCK MASTER_MYSOCK +--error ER_DBACCESS_DENIED_ERROR +connect (test,localhost,user2,111,db1,); +connection default; +--source include/wait_until_count_sessions.inc +--replace_result $MASTER_MYPORT MASTER_MYPORT $MASTER_MYSOCK MASTER_MYSOCK +--error ER_ACCESS_DENIED_ERROR +connect (test,localhost,user2,112,db2,); +connection default; +--source include/wait_until_count_sessions.inc +--replace_result $MASTER_MYPORT MASTER_MYPORT $MASTER_MYSOCK MASTER_MYSOCK +--error ER_ACCESS_DENIED_ERROR +connect (test,localhost,user3,111,db2,); +connection default; +--source include/wait_until_count_sessions.inc +connect (test,localhost,user2,111,db2,); +connection test; +set names utf8; +SELECT * FROM t; +--error ER_DBACCESS_DENIED_ERROR +use `db1`; +SELECT * FROM t; +change_user user1,111,db1; +set names utf8; +SELECT * FROM t; +use `db2`; +SELECT * FROM t; +use ąžąžąžą; +SELECT * FROM t; +disconnect test; +connection default; + +--source include/wait_until_count_sessions.inc +--exec $MYSQL --user=user1 --password=111 test -e "use db1; SELECT * FROM t;" +--source include/wait_until_count_sessions.inc + +--source audit_log_echo.inc + +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ąžąžąžą; + +DROP USER user1; +DROP USER user2; + diff --git a/plugin/audit_log/tests/mtr/audit_log_echo.inc b/plugin/audit_log/tests/mtr/audit_log_echo.inc new file mode 100644 index 000000000000..81d75b85af1f --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_echo.inc @@ -0,0 +1,47 @@ +# Echo the contents of the audit log (in CSV format) +# log_file is the name of the log file + +--move_file $log_file $log_file.copy +set global audit_log_flush= ON; +let log_file=$log_file; +perl; + print "===================================================================\n"; + open my $file, $ENV{'log_file'} . '.copy' or die "Can not open log: $!"; + while ($line = <$file>) { + if ($line =~ /SET NAMES/) { + # change_user does automatic reconnect and messing up 'SET NAMES' around + next; + } + if ($line =~ /Threads_connected/ || $line =~ /SELECT \d <= \d/ + || /SELECT.*FROM.*INFORMATION_SCHEMA.PLUGINS/) { + # part of wait_until_count_sessions.inc and wait_condition.inc scripts + next; + } + if ($line =~ /^"Audit"/) { + # skip opening log record and disconnect record + next; + } + if ($line =~ /SELECT count\(\*\)=1 FROM t WHERE a=7/) { + # skip checking for event shot + next; + } + if ($line =~ /INSERT INTO t VALUES \(7\)/) { + # don't show the last statement of the event + next; + } + if ($line =~ /SELECT 600 \* 10/) { + # skip checking for event shot + next; + } + $line =~ s/"([a-zA-Z_ ]*)","([0-9]+)_[0-9_ :T-]*","[0-9_ :A-Z-]*"/"$1","",""/; + $line =~ s/"(Connect|Quit|Change user)","","","[0-9]+"/"$1","","",""/; + $line =~ s/"([A-Za-z ]+)","","","([a-z_]+)","[0-9]+"/"$1","","","$2",""/; + if ($line =~ /SET GLOBAL .*_accounts/) { + print "*************************************************************\n"; + } + print "$line"; + }; + close $file; + print "===================================================================\n"; +EOF +--remove_file $log_file.copy diff --git a/plugin/audit_log/tests/mtr/audit_log_events.inc b/plugin/audit_log/tests/mtr/audit_log_events.inc new file mode 100644 index 000000000000..980252ade274 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_events.inc @@ -0,0 +1,102 @@ +# produce some events for audit log + +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +--error ER_TABLE_EXISTS_ERROR +CREATE TABLE t1 + (c1 INT, + c2 CHAR(20)); +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +-- if ($test_control_chars) { +INSERT INTO `t1` VALUES (4,NULL); +# can't add the zero ascii character, as that's a syntax error in MySQL +INSERT INTO `t1` VALUES (6,''); +INSERT INTO `t1` VALUES (7,''); +INSERT INTO `t1` VALUES (8,''); +INSERT INTO `t1` VALUES (9,''); +INSERT INTO `t1` VALUES (10,''); +INSERT INTO `t1` VALUES (11,''); +INSERT INTO `t1` VALUES (12,''); +INSERT INTO `t1` VALUES (13,''); +INSERT INTO `t1` VALUES (14,' '); +INSERT INTO `t1` VALUES (15,' +'); +INSERT INTO `t1` VALUES (16,' '); +INSERT INTO `t1` VALUES (17,' '); +INSERT INTO `t1` VALUES (18,' '); +INSERT INTO `t1` VALUES (19,''); +INSERT INTO `t1` VALUES (20,''); +INSERT INTO `t1` VALUES (21,''); +INSERT INTO `t1` VALUES (22,''); +INSERT INTO `t1` VALUES (23,''); +INSERT INTO `t1` VALUES (24,''); +INSERT INTO `t1` VALUES (25,''); +INSERT INTO `t1` VALUES (26,''); +INSERT INTO `t1` VALUES (27,''); +INSERT INTO `t1` VALUES (28,''); +INSERT INTO `t1` VALUES (29,''); +INSERT INTO `t1` VALUES (30,''); +INSERT INTO `t1` VALUES (31,''); +INSERT INTO `t1` VALUES (32,''); +INSERT INTO `t1` VALUES (33,''); +INSERT INTO `t1` VALUES (34,''); +INSERT INTO `t1` VALUES (35,''); +INSERT INTO `t1` VALUES (36,''); +-- } +SELECT * FROM t1; +--error ER_NO_SUCH_TABLE +SELECT * FROM t2; +DROP TABLE t1; + +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +SHOW STATUS LIKE 'audit_log%'; + +DEALLOCATE PREPARE stmt1; + +show variables like 'audit_log%'; +--source include/count_sessions.inc +connect (con1,localhost,root,,mysql); +connection default; +disconnect con1; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_ACCESS_DENIED_ERROR +connect (con1,localhost,no_such_user,,mysql); +connection default; +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +# query is longer than 4k +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +alter table t1 rename renamed_t1; +--error ER_NO_SUCH_TABLE +select * from t_doesnt_exist; +--error 1064 +syntax_error_query; +drop table renamed_t1, t2; +show variables like 'audit_log%'; +create database sa_db; +connect (con1,localhost,root,,test); +connection con1; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +connection default; +# IDENTIFIED BY will be logged as IDENTIFIED ... AS, containing control chars +if ($test_control_chars) { + create user 'jeffrey'@'localhost' IDENTIFIED BY 'mypass'; + drop user 'jeffrey'@'localhost'; +} +select '&;&&&""""<><<>>>>'; +if ($test_control_chars) { + let $str=`SELECT x'2009080c0a0d2f225c5c'`; + eval select '$str'; +} +disconnect con1; +--source include/wait_until_count_sessions.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands-master.opt b/plugin/audit_log/tests/mtr/audit_log_filter_commands-master.opt new file mode 100644 index 000000000000..ad7f91c65bd5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands-master.opt @@ -0,0 +1,6 @@ +$AUDIT_LOG_OPT +$AUDIT_LOG_LOAD +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands.result b/plugin/audit_log/tests/mtr/audit_log_filter_commands.result new file mode 100644 index 000000000000..e1cf0e3c38ce --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands.result @@ -0,0 +1,217 @@ +SET GLOBAL audit_log_include_commands= 'create_table,create_procedure,create_trigger,insert'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +create_table,create_procedure,create_trigger,insert NULL +SET GLOBAL audit_log_exclude_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +ERROR 42000: Variable 'audit_log_exclude_commands' can't be set to the value of 'alter_db_upgrade,change_db,drop_table,drop_db' +SET GLOBAL audit_log_exclude_commands= NULL; +ERROR 42000: Variable 'audit_log_exclude_commands' can't be set to the value of 'NULL' +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +create_table,create_procedure,create_trigger,insert NULL +SET GLOBAL audit_log_include_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +alter_db_upgrade,change_db,drop_table,drop_db NULL +SET GLOBAL audit_log_include_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands + NULL +SET GLOBAL audit_log_exclude_commands= 'insert,call_procedure,call_procedure,set_option,assign_to_keycache'; +ERROR 42000: Variable 'audit_log_exclude_commands' can't be set to the value of 'insert,call_procedure,call_procedure,set_option,assign_to_keycache' +SET GLOBAL audit_log_include_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL NULL +SET GLOBAL audit_log_exclude_commands= "insert,call_procedure,call_procedure,set_option,assign_to_keycache"; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL insert,call_procedure,call_procedure,set_option,assign_to_keycache +SET GLOBAL audit_log_include_commands= 'change_db,drop_table@localhost'; +ERROR 42000: Variable 'audit_log_include_commands' can't be set to the value of 'change_db,drop_table@localhost' +SET GLOBAL audit_log_include_commands= NULL; +ERROR 42000: Variable 'audit_log_include_commands' can't be set to the value of 'NULL' +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL insert,call_procedure,call_procedure,set_option,assign_to_keycache +SET GLOBAL audit_log_exclude_commands= 'change_db,drop_table'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL change_db,drop_table +SET GLOBAL audit_log_exclude_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL +SET GLOBAL audit_log_include_commands= 'change_db'; +ERROR 42000: Variable 'audit_log_include_commands' can't be set to the value of 'change_db' +SET GLOBAL audit_log_exclude_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +@@audit_log_include_commands @@audit_log_exclude_commands +NULL NULL +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,CREATE_TABLE,CREATE_TABLE,CREATE_PROCEDURE,CREATE_TRIGger,insert,insert,insert,call_procedure,call_procedure,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,assign_to_keycache,alter_db,alter_db_upgrade'; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_include_commands= NULL; +SET GLOBAL audit_log_exclude_commands= 'set_option,create_db,change_db,create_table,create_table,create_prOCEDURE,CREATE_TRIGGER,INSERT,INSERT,INSERT,CALL_PROCEDURE,CALL_PROCEDUre,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; +CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END// +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); +INSERT INTO t1 VALUES (5,5); +CALL p1(); +SET GLOBAL keycache1.key_buffer_size=128*1024; +Warnings: +Warning 1287 keycache1.key_buffer_size syntax is deprecated and will be removed in a future release +CACHE INDEX t1 IN keycache1; +Table Op Msg_type Msg_text +db1.t1 assign_to_keycache status OK +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE db1 DEFAULT CHARACTER SET latin1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t1 VALUES (5,5)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO t1 (a, b) VALUES (1, 1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET GLOBAL keycache1.key_buffer_size=128*1024","root[root] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Query","","","assign_to_keycache","",0,"CACHE INDEX t1 IN keycache1","root[root] @ localhost []","localhost","","","db1" +"Query","","","alter_db","",0,"ALTER DATABASE db1 DEFAULT CHARACTER SET utf8","root[root] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_db","",0,"DROP DATABASE db1","root[root] @ localhost []","localhost","","","test" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,CREATE_TABLE,CREATE_TABLE,CREATE_PROCEDURE,CREATE_TRIGger,insert,insert,insert,call_procedure,call_procedure,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE db1 DEFAULT CHARACTER SET latin1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO t1 (a, b) VALUES (1, 1); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t1 VALUES (5,5)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t2 (a, b) VALUES (new.a, new.b)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO t1 (a, b) VALUES (1, 1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET GLOBAL keycache1.key_buffer_size=128*1024","root[root] @ localhost []","localhost","","","db1" +"Query","","","assign_to_keycache","",0,"CACHE INDEX t1 IN keycache1","root[root] @ localhost []","localhost","","","db1" +"Query","","","alter_db","",0,"ALTER DATABASE db1 DEFAULT CHARACTER SET utf8","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_db","",0,"DROP DATABASE db1","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,assign_to_keycache,alter_db,alter_db_upgrade'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_db","",0,"CREATE DATABASE db1 DEFAULT CHARACTER SET latin1","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET GLOBAL keycache1.key_buffer_size=128*1024","root[root] @ localhost []","localhost","","","db1" +"Query","","","assign_to_keycache","",0,"CACHE INDEX t1 IN keycache1","root[root] @ localhost []","localhost","","","db1" +"Query","","","alter_db","",0,"ALTER DATABASE db1 DEFAULT CHARACTER SET utf8","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Query","","","show_warnings","",0,"SHOW WARNINGS","root[root] @ localhost []","localhost","","","db1" +"Ping","","","error","",0,"","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_commands= NULL","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_commands= NULL","root[root] @ localhost []","localhost","","","test" +=================================================================== diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands.test b/plugin/audit_log/tests/mtr/audit_log_filter_commands.test new file mode 100644 index 000000000000..efbb73524d3c --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands.test @@ -0,0 +1,71 @@ +# test filtering by sql_command + +# test set/unset filters + +SET GLOBAL audit_log_include_commands= 'create_table,create_procedure,create_trigger,insert'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_include_commands= 'alter_db_upgrade,change_db,drop_table,drop_db'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_include_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_commands= 'insert,call_procedure,call_procedure,set_option,assign_to_keycache'; +SET GLOBAL audit_log_include_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; + +SET GLOBAL audit_log_exclude_commands= "insert,call_procedure,call_procedure,set_option,assign_to_keycache"; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_commands= 'change_db,drop_table@localhost'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_exclude_commands= 'change_db,drop_table'; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +SET GLOBAL audit_log_exclude_commands= ''; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_commands= 'change_db'; +SET GLOBAL audit_log_exclude_commands= NULL; +SELECT @@audit_log_include_commands, @@audit_log_exclude_commands; + +# test filtering + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +SET GLOBAL audit_log_flush=ON; +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +# log everything +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; + +--source audit_log_filter_commands_events.inc + +# log everything once again +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,CREATE_TABLE,CREATE_TABLE,CREATE_PROCEDURE,CREATE_TRIGger,insert,insert,insert,call_procedure,call_procedure,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; + +--source audit_log_filter_commands_events.inc + +# log some of the commands +SET GLOBAL audit_log_include_commands= 'set_option,creaTE_DB,CHANGE_DB,assign_to_keycache,alter_db,alter_db_upgrade'; + +--source audit_log_filter_commands_events.inc + +# log nothing +SET GLOBAL audit_log_include_commands= NULL; +SET GLOBAL audit_log_exclude_commands= 'set_option,create_db,change_db,create_table,create_table,create_prOCEDURE,CREATE_TRIGGER,INSERT,INSERT,INSERT,CALL_PROCEDURE,CALL_PROCEDUre,set_option,assign_to_keycache,alter_db,alter_db_upgrade,change_db,drop_table,drop_db'; + +--source audit_log_filter_commands_events.inc + +SET GLOBAL audit_log_exclude_commands= NULL; +SET GLOBAL audit_log_include_commands= NULL; + +--source audit_log_echo.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_commands_events.inc b/plugin/audit_log/tests/mtr/audit_log_filter_commands_events.inc new file mode 100644 index 000000000000..362d4adbc050 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_commands_events.inc @@ -0,0 +1,28 @@ + +PING; +CREATE DATABASE db1 DEFAULT CHARACTER SET latin1; +USE db1; +CREATE TABLE t1 (a INT, b INT, KEY(b)) engine=MyISAM; +CREATE TABLE t2 (a INT, b INT, KEY(b)) engine=InnoDB; + +DELIMITER //; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t1 (a, b) VALUES (1, 1); +END// +DELIMITER ;// + +CREATE TRIGGER trigger1 BEFORE INSERT ON t1 FOR EACH ROW INSERT INTO t2 (a, b) VALUES (new.a, new.b); + +INSERT INTO t1 VALUES (5,5); +CALL p1(); + +SET GLOBAL keycache1.key_buffer_size=128*1024; +CACHE INDEX t1 IN keycache1; +ALTER DATABASE db1 DEFAULT CHARACTER SET utf8; + +USE test; +DROP TABLE db1.t1; +DROP DATABASE db1; + +PING; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db-master.opt b/plugin/audit_log/tests/mtr/audit_log_filter_db-master.opt new file mode 100644 index 000000000000..0adac3307e34 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db-master.opt @@ -0,0 +1,5 @@ +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS +--thread_stack=16777216 \ No newline at end of file diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db.result b/plugin/audit_log/tests/mtr/audit_log_filter_db.result new file mode 100644 index 000000000000..767e93f862f7 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db.result @@ -0,0 +1,1582 @@ +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE ```db3"`; +CREATE DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +SHOW DATABASES; +Database +`db3" +db1 +db2 +information_schema +mtr +mysql +performance_schema +some_very_long,database_na'me`some_very_long_database_n"ame____q +sys +test +SET GLOBAL audit_log_include_databases= '`some_very_long,database_na\'me``some_very_long_database_n"ame____q`,```db1"`,db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +`some_very_long,database_na'me``some_very_long_database_n"ame____q`,```db1"`,db3 NULL +SET GLOBAL audit_log_exclude_databases= 'db2'; +ERROR 42000: Variable 'audit_log_exclude_databases' can't be set to the value of 'db2' +SET GLOBAL audit_log_exclude_databases= NULL; +ERROR 42000: Variable 'audit_log_exclude_databases' can't be set to the value of 'NULL' +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +`some_very_long,database_na'me``some_very_long_database_n"ame____q`,```db1"`,db3 NULL +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +db1, db2, db3 NULL +SET GLOBAL audit_log_include_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases + NULL +SET GLOBAL audit_log_exclude_databases= 'db1'; +ERROR 42000: Variable 'audit_log_exclude_databases' can't be set to the value of 'db1' +SET GLOBAL audit_log_include_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL NULL +SET GLOBAL audit_log_exclude_databases= 'db2,`db3 `'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL db2,`db3 ` +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +ERROR 42000: Variable 'audit_log_include_databases' can't be set to the value of 'db1, db2, db3' +SET GLOBAL audit_log_include_databases= NULL; +ERROR 42000: Variable 'audit_log_include_databases' can't be set to the value of 'NULL' +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL db2,`db3 ` +SET GLOBAL audit_log_exclude_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL db1, db2, db3 +SET GLOBAL audit_log_exclude_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL +SET GLOBAL audit_log_include_databases= 'db2'; +ERROR 42000: Variable 'audit_log_include_databases' can't be set to the value of 'db2' +SET GLOBAL audit_log_exclude_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +@@audit_log_include_databases @@audit_log_exclude_databases +NULL NULL +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_include_databases= 'db1,```db3"`'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_include_databases= 'db2,```db3"`'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_include_databases= NULL; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_exclude_databases= 'db1,`some_very_long,database_na\'me``some_very_long_database_n"ame____q`'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_exclude_databases= 'db1,db2'; +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); +CREATE TABLE db1.words (id INT, word TEXT); +USE db1; +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END// +CALL p1(); +CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); +CREATE TABLE a0 (a INT); +CREATE TABLE a1 (a INT); +CREATE TABLE a2 (a INT); +CREATE TABLE a3 (a INT); +CREATE TABLE a4 (a INT); +CREATE TABLE a5 (a INT); +CREATE TABLE a6 (a INT); +CREATE TABLE a7 (a INT); +CREATE TABLE a8 (a INT); +CREATE TABLE a9 (a INT); +CREATE TABLE a10 (a INT); +CREATE TABLE a11 (a INT); +CREATE TABLE a12 (a INT); +CREATE TABLE a13 (a INT); +CREATE TABLE a14 (a INT); +CREATE TABLE a15 (a INT); +CREATE TABLE a16 (a INT); +CREATE TABLE a17 (a INT); +CREATE TABLE a18 (a INT); +CREATE TABLE a19 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a); +CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a); +CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a); +CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a); +CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a); +CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a); +CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a); +CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a); +CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a); +CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a); +CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a); +CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a); +CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a); +CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a); +CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a); +CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a); +CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a); +CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a); +CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a); +CREATE TABLE b0 (a INT); +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +INSERT INTO a19 VALUES (1); +INSERT INTO b0 VALUES (1); +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +a id word a +1 NULL NULL 2 +2 2 two NULL +3 3 three NULL +77 NULL NULL NULL +7 NULL NULL NULL +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +s a +3 3 +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +s a +3 3 +SELECT * FROM vmat; +s +3 +SELECT a FROM vjoin; +a +3 +SELECT s FROM vjoin; +s +3 +SELECT a,s FROM vjoin; +a s +3 3 +SELECT s FROM vmat; +s +3 +SELECT a FROM vup; +a +207 +210 +101 +102 +103 +1 +2 +3 +6 +1 +2 +1 +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; +USE test; +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); +a +1 +2 +SELECT 1; +1 +1 +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +DROP TABLE db1.a0; +DROP TABLE db1.a1; +DROP TABLE db1.a2; +DROP TABLE db1.a3; +DROP TABLE db1.a4; +DROP TABLE db1.a5; +DROP TABLE db1.a6; +DROP TABLE db1.a7; +DROP TABLE db1.a8; +DROP TABLE db1.a9; +DROP TABLE db1.a10; +DROP TABLE db1.a11; +DROP TABLE db1.a12; +DROP TABLE db1.a13; +DROP TABLE db1.a14; +DROP TABLE db1.a15; +DROP TABLE db1.a16; +DROP TABLE db1.a17; +DROP TABLE db1.a18; +DROP TABLE db1.a19; +DROP TABLE db1.b0; +SET GLOBAL audit_log_exclude_databases= NULL; +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_databases= 'db1,```db3""`'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"INSERT INTO t VALUES (77)","root[root] @ localhost [localhost]","localhost","","localhost","" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three')","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a19 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO b0 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_databases= 'db2,```db3""`'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (207)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (200 + a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3), (6)","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO vup VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vup","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vup SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_databases= NULL","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"INSERT INTO t VALUES (77)","root[root] @ localhost [localhost]","localhost","","localhost","" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (207)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (200 + a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three')","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO a19 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO b0 VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3), (6)","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO vup VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vmat","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vup","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vup SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_databases= 'db1,`some_very_long,database_na\'me``some_very_long_database_n""ame____q`'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (207)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"INSERT INTO db2.t VALUES (200 + a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO trig VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","delete_multi","",0,"DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3), (6)","root[root] @ localhost []","localhost","","","db2" +"Query","","","insert_select","",0,"INSERT INTO t SELECT * FROM db1.t","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"INSERT INTO vup VALUES (1)","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a,s FROM vjoin","root[root] @ localhost []","localhost","","","db1" +"Query","","","select","",0,"SELECT a FROM vup","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vup SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","update","",0,"UPDATE vjoin SET a=a+1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_databases= 'db1,db2'","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.trig (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db2.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE ```db3""`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t (a INT)","root[root] @ localhost []","localhost","","","test" +"Query","","","create_table","",0,"CREATE TABLE db1.words (id INT, word TEXT)","root[root] @ localhost []","localhost","","","test" +"Query","","","change_db","",0,"USE db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vup AS SELECT * FROM db2.t","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_view","",0,"CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p1() +BEGIN +INSERT INTO db2.t VALUES (207); +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p1()","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_procedure","",0,"CREATE PROCEDURE p2(a INT) +BEGIN +INSERT INTO db2.t VALUES (200 + a); +IF a = 0 THEN +CALL p2(a - 1); +END IF; +END","root[root] @ localhost []","localhost","","","db1" +"Query","","","set_option","",0,"SET max_sp_recursion_depth = 20","root[root] @ localhost []","localhost","","","db1" +"Query","","","call_procedure","",0,"CALL p2(10)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a1 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a2 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a3 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a4 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a5 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a6 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a7 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a8 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a9 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a10 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a11 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a12 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a13 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a14 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a15 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a16 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a17 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a18 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE a19 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr1 BEFORE INSERT ON a1 FOR EACH ROW INSERT INTO a0 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr2 BEFORE INSERT ON a2 FOR EACH ROW INSERT INTO a1 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr3 BEFORE INSERT ON a3 FOR EACH ROW INSERT INTO a2 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr4 BEFORE INSERT ON a4 FOR EACH ROW INSERT INTO a3 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr5 BEFORE INSERT ON a5 FOR EACH ROW INSERT INTO a4 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr6 BEFORE INSERT ON a6 FOR EACH ROW INSERT INTO a5 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr7 BEFORE INSERT ON a7 FOR EACH ROW INSERT INTO a6 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr8 BEFORE INSERT ON a8 FOR EACH ROW INSERT INTO a7 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr9 BEFORE INSERT ON a9 FOR EACH ROW INSERT INTO a8 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr10 BEFORE INSERT ON a10 FOR EACH ROW INSERT INTO a9 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr11 BEFORE INSERT ON a11 FOR EACH ROW INSERT INTO a10 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr12 BEFORE INSERT ON a12 FOR EACH ROW INSERT INTO a11 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr13 BEFORE INSERT ON a13 FOR EACH ROW INSERT INTO a12 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr14 BEFORE INSERT ON a14 FOR EACH ROW INSERT INTO a13 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr15 BEFORE INSERT ON a15 FOR EACH ROW INSERT INTO a14 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr16 BEFORE INSERT ON a16 FOR EACH ROW INSERT INTO a15 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr17 BEFORE INSERT ON a17 FOR EACH ROW INSERT INTO a16 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr18 BEFORE INSERT ON a18 FOR EACH ROW INSERT INTO a17 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr19 BEFORE INSERT ON a19 FOR EACH ROW INSERT INTO a18 VALUES (new.a)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_table","",0,"CREATE TABLE b0 (a INT)","root[root] @ localhost []","localhost","","","db1" +"Query","","","create_trigger","",0,"CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @tmp=1","root[root] @ localhost []","localhost","","","db1" +"Query","","","insert","",0,"SET @abc='cba'","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE db2","root[root] @ localhost []","localhost","","","db2" +"Query","","","change_db","",0,"USE ```db3""`","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","``db3""" +"Query","","","change_db","",0,"USE `some_very_long,database_na'me``some_very_long_database_n""ame____q`","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","insert","",0,"INSERT INTO t VALUES (1), (2), (3)","root[root] @ localhost []","localhost","","","some_very_long,database_na'me``some_very_long_database_n""ame____q" +"Query","","","change_db","",0,"use db1","root[root] @ localhost []","localhost","","","db1" +"Query","","","change_db","",0,"USE test","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t)","root[root] @ localhost []","localhost","","","test" +"Query","","","select","",0,"SELECT 1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.words","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.trig","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db2.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE ```db3""`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE `some_very_long,database_na'me``some_very_long_database_n""ame____q`.t","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_procedure","",0,"DROP PROCEDURE db1.p2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vmat","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vup","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_view","",0,"DROP VIEW db1.vjoin","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a0","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a1","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a2","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a3","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a4","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a5","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a6","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a7","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a8","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a9","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a10","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a11","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a12","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a13","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a14","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a15","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a16","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a17","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a18","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.a19","root[root] @ localhost []","localhost","","","test" +"Query","","","drop_table","",0,"DROP TABLE db1.b0","root[root] @ localhost []","localhost","","","test" +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_databases= NULL","root[root] @ localhost []","localhost","","","test" +=================================================================== +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ```db3"`; +DROP DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db.test b/plugin/audit_log/tests/mtr/audit_log_filter_db.test new file mode 100644 index 000000000000..39c24cbbb098 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db.test @@ -0,0 +1,85 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +# setup some databases + +CREATE DATABASE db1; +CREATE DATABASE db2; +CREATE DATABASE ```db3"`; +CREATE DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; + +SHOW DATABASES; + +# test set/unset filters + +SET GLOBAL audit_log_include_databases= '`some_very_long,database_na\'me``some_very_long_database_n"ame____q`,```db1"`,db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_databases= 'db2'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_include_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_databases= 'db1'; +SET GLOBAL audit_log_include_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; + +SET GLOBAL audit_log_exclude_databases= 'db2,`db3 `'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_databases= 'db1, db2, db3'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_exclude_databases= 'db1, db2, db3'; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +SET GLOBAL audit_log_exclude_databases= ''; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_databases= 'db2'; +SET GLOBAL audit_log_exclude_databases= NULL; +SELECT @@audit_log_include_databases, @@audit_log_exclude_databases; + +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +# testing include +SET GLOBAL audit_log_include_databases= 'db1,```db3"`'; + +--source audit_log_filter_db_events.inc + +SET GLOBAL audit_log_include_databases= 'db2,```db3"`'; + +--source audit_log_filter_db_events.inc + +# log everything +SET GLOBAL audit_log_include_databases= NULL; + +--source audit_log_filter_db_events.inc + +# testing exclude +SET GLOBAL audit_log_exclude_databases= 'db1,`some_very_long,database_na\'me``some_very_long_database_n"ame____q`'; + +--source audit_log_filter_db_events.inc + +SET GLOBAL audit_log_exclude_databases= 'db1,db2'; + +--source audit_log_filter_db_events.inc + +SET GLOBAL audit_log_exclude_databases= NULL; + +--source audit_log_echo.inc + +# cleanup databases +DROP DATABASE db1; +DROP DATABASE db2; +DROP DATABASE ```db3"`; +DROP DATABASE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_db_events.inc b/plugin/audit_log/tests/mtr/audit_log_filter_db_events.inc new file mode 100644 index 000000000000..7bf2d4dbfb76 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_db_events.inc @@ -0,0 +1,119 @@ +CREATE TABLE db1.t (a INT); +CREATE TABLE db1.trig (a INT); +CREATE TABLE db2.t (a INT); +CREATE TABLE ```db3"`.t (a INT); +CREATE TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t (a INT); + +CREATE TABLE db1.words (id INT, word TEXT); + +USE db1; + +CREATE VIEW vmat AS SELECT SUM(a) AS s FROM db1.t; +CREATE VIEW vup AS SELECT * FROM db2.t; +CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; + +DELIMITER //; +CREATE EVENT ev1 ON SCHEDULE AT CURRENT_TIMESTAMP DO BEGIN INSERT INTO t VALUES (77); INSERT INTO t VALUES (7); END// +DELIMITER ;// +let $wait_condition=SELECT count(*)=1 FROM t WHERE a=7; +let $wait_timeout=600; +--source include/wait_condition.inc +CREATE TRIGGER ins_tring BEFORE INSERT ON db1.trig FOR EACH ROW INSERT INTO db2.t VALUES (new.a + 100); +INSERT INTO t VALUES (1), (2), (3); +DELIMITER //; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO db2.t VALUES (207); +END// +DELIMITER ;// +CALL p1(); +DELIMITER //; +CREATE PROCEDURE p2(a INT) +BEGIN + INSERT INTO db2.t VALUES (200 + a); + IF a = 0 THEN + CALL p2(a - 1); + END IF; +END// +DELIMITER ;// +SET max_sp_recursion_depth = 20; +CALL p2(10); +INSERT INTO trig VALUES (1), (2), (3); +INSERT INTO words VALUES (0, 'one'), (2, 'two'), (3, 'three'); + +let $i=0; +while ($i < 20) +{ +eval CREATE TABLE a$i (a INT); +inc $i; +} + +let $i=1; +let $j=0; +while ($i < 20) +{ +eval CREATE TRIGGER tr$i BEFORE INSERT ON a$i FOR EACH ROW INSERT INTO a$j VALUES (new.a); +inc $i; +inc $j; +} + +CREATE TABLE b0 (a INT); +DELIMITER //; +CREATE TRIGGER tr_b_0 BEFORE INSERT ON b0 FOR EACH ROW BEGIN SET @tmp=1; SET @abc='cba'; END// +DELIMITER ;// + +INSERT INTO a19 VALUES (1); + +INSERT INTO b0 VALUES (1); + +SELECT * FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN t q ON t.a = q.a / 2 WHERE t.a > 2; +DELETE t, q, words FROM t LEFT JOIN words ON t.a = words.id LEFT JOIN db2.t q ON t.a = q.a / 2 WHERE t.a > 2; + +USE db2; +INSERT INTO t VALUES (1), (2), (3), (6); +INSERT INTO t SELECT * FROM db1.t; + +USE ```db3"`; +INSERT INTO t VALUES (1), (2), (3); + +USE `some_very_long,database_na'me``some_very_long_database_n"ame____q`; +INSERT INTO t VALUES (1), (2), (3); + +use db1; +INSERT INTO vup VALUES (1); +SELECT * FROM vjoin; +SELECT * FROM vmat JOIN vup ON vmat.s=vup.a; +SELECT * FROM vmat; +SELECT a FROM vjoin; +SELECT s FROM vjoin; +SELECT a,s FROM vjoin; +SELECT s FROM vmat; +SELECT a FROM vup; +UPDATE vup SET a=a+1; +UPDATE vjoin SET a=a+1; + +USE test; + +SELECT * FROM db1.t WHERE a IN (SELECT a FROM `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t); + +SELECT 1; + +DROP TABLE db1.t; +DROP TABLE db1.words; +DROP TABLE db1.trig; +DROP TABLE db2.t; +DROP TABLE ```db3"`.t; +DROP TABLE `some_very_long,database_na'me``some_very_long_database_n"ame____q`.t; +DROP PROCEDURE db1.p1; +DROP PROCEDURE db1.p2; +DROP VIEW db1.vmat; +DROP VIEW db1.vup; +DROP VIEW db1.vjoin; +let $i=0; +while ($i < 20) +{ +eval DROP TABLE db1.a$i; +inc $i; +} +DROP TABLE db1.b0; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_events.inc b/plugin/audit_log/tests/mtr/audit_log_filter_events.inc new file mode 100644 index 000000000000..9103f954a691 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_events.inc @@ -0,0 +1,43 @@ +--source include/count_sessions.inc + +connect (test,127.0.0.1,user1,password1,,$MASTER_PORT,); +connection test; +SELECT 'user1'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,user22,password1,,); +connection test; +SELECT 'user22'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,22user,password1,,); +connection test; +SELECT '22user'; +change_user user22,password1; +SELECT 'user22'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,127.0.0.1,admin,password1,,$MASTER_PORT,); +connection test; +SELECT 'admin'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connect (test,localhost,"us,er1",password1,,); +connection test; +SELECT 'us,er1'; +disconnect test; +connection default; +--source include/wait_until_count_sessions.inc + +connection default; + +connection default; +--source include/wait_until_count_sessions.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_users-master.opt b/plugin/audit_log/tests/mtr/audit_log_filter_users-master.opt new file mode 100644 index 000000000000..033178764ec5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_users-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=ALL +--audit-log-format=CSV +--audit_log_strategy=SYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_users.result b/plugin/audit_log/tests/mtr/audit_log_filter_users.result new file mode 100644 index 000000000000..50f350175f67 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_users.result @@ -0,0 +1,213 @@ +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +user1@localhost,, user22@127.0.0.1,admin@% NULL +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_exclude_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_exclude_accounts= NULL; +ERROR 42000: Variable 'audit_log_exclude_accounts' can't be set to the value of 'NULL' +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +user1@localhost,, user22@127.0.0.1,admin@% NULL +SET GLOBAL audit_log_include_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +user1@localhost, user2@localhost, user3@localhost NULL +SET GLOBAL audit_log_include_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts + NULL +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_exclude_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_include_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL NULL +SET GLOBAL audit_log_exclude_accounts= "'us,er1'@'localhost',, user22@127.0.0.1,admin@%"; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL 'us,er1'@'localhost',, user22@127.0.0.1,admin@% +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_include_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_include_accounts= NULL; +ERROR 42000: Variable 'audit_log_include_accounts' can't be set to the value of 'NULL' +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL 'us,er1'@'localhost',, user22@127.0.0.1,admin@% +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL user1@localhost, user2@localhost, user3@localhost +SET GLOBAL audit_log_exclude_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +ERROR 42000: Variable 'audit_log_include_accounts' can't be set to the value of '22useer@localhost' +SET GLOBAL audit_log_exclude_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +@@audit_log_include_accounts @@audit_log_exclude_accounts +NULL NULL +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_include_accounts= ''; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%,veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooongusername@veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooonghostname'; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_include_accounts= NULL; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_exclude_accounts= NULL; +SET GLOBAL audit_log_include_accounts= '22user@LocalHost,User22@%'; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_include_accounts= NULL; +set global audit_log_flush= ON; +=================================================================== +"Query","","","set_option","",0,"SET GLOBAL audit_log_flush=ON","root[root] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= ''","root[root] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%,veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooongusername@veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooonghostname'","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'admin'","admin[admin] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= NULL","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'user1'","user1[user1] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Connect","","","",0,"user22","user22","","","localhost","","test" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","test" +"Quit","","","",0,"user22","user22","","","localhost","","test" +"Connect","","","",0,"22user","22user","","","localhost","","test" +"Query","","","select","",0,"SELECT '22user'","22user[22user] @ localhost []","localhost","","","test" +"Change user","","","",0,"user22","user22","","","localhost","","" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","" +"Quit","","","",0,"user22","user22","","","localhost","","" +"Connect","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'admin'","admin[admin] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"admin","admin","","","localhost","127.0.0.1","test" +"Connect","","","",0,"us,er1","us,er1","","","localhost","","test" +"Query","","","select","",0,"SELECT 'us,er1'","us,er1[us,er1] @ localhost []","localhost","","","test" +"Quit","","","",0,"us,er1","us,er1","","","localhost","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'user1'","user1[user1] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Connect","","","",0,"user22","user22","","","localhost","","test" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","test" +"Quit","","","",0,"user22","user22","","","localhost","","test" +"Connect","","","",0,"22user","22user","","","localhost","","test" +"Query","","","select","",0,"SELECT '22user'","22user[22user] @ localhost []","localhost","","","test" +"Change user","","","",0,"user22","user22","","","localhost","","" +"Query","","","select","",0,"SELECT 'user22'","user22[user22] @ localhost []","localhost","","","" +"Quit","","","",0,"user22","user22","","","localhost","","" +"Connect","","","",0,"us,er1","us,er1","","","localhost","","test" +"Query","","","select","",0,"SELECT 'us,er1'","us,er1[us,er1] @ localhost []","localhost","","","test" +"Quit","","","",0,"us,er1","us,er1","","","localhost","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_exclude_accounts= NULL","root[root] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= '22user@LocalHost,User22@%'","root[root] @ localhost []","localhost","","","test" +"Connect","","","",0,"22user","22user","","","localhost","","test" +"Query","","","select","",0,"SELECT '22user'","22user[22user] @ localhost []","localhost","","","test" +************************************************************* +"Query","","","set_option","",0,"SET GLOBAL audit_log_include_accounts= NULL","root[root] @ localhost []","localhost","","","test" +=================================================================== +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/audit_log_filter_users.test b/plugin/audit_log/tests/mtr/audit_log_filter_users.test new file mode 100644 index 000000000000..f8b915c76a99 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_filter_users.test @@ -0,0 +1,86 @@ +# setup some user accounts + +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; + +# test set/unset filters + +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_include_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_include_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_exclude_accounts= '22useer@localhost'; +SET GLOBAL audit_log_include_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; + +SET GLOBAL audit_log_exclude_accounts= "'us,er1'@'localhost',, user22@127.0.0.1,admin@%"; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost, user2@localhost, user3@localhost'; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +SET GLOBAL audit_log_exclude_accounts= ''; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL audit_log_include_accounts= '22useer@localhost'; +SET GLOBAL audit_log_exclude_accounts= NULL; +SELECT @@audit_log_include_accounts, @@audit_log_exclude_accounts; + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + +SET GLOBAL audit_log_flush=ON; +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +# log nothing +SET GLOBAL audit_log_include_accounts= ''; + +--source audit_log_filter_events.inc + +# testing include +SET GLOBAL audit_log_include_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%,veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooongusername@veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryloooooooooooooonghostname'; + +--source audit_log_filter_events.inc + +# log everything +SET GLOBAL audit_log_include_accounts= NULL; + +--source audit_log_filter_events.inc + +# testing exclude +SET GLOBAL audit_log_exclude_accounts= 'user1@localhost,, user22@127.0.0.1,admin@%'; + +--source audit_log_filter_events.inc + +SET GLOBAL audit_log_exclude_accounts= NULL; + +# testing case sensitivity +SET GLOBAL audit_log_include_accounts= '22user@LocalHost,User22@%'; + +--source audit_log_filter_events.inc + +SET GLOBAL audit_log_include_accounts= NULL; + +--source audit_log_echo.inc + +# cleanup users +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/audit_log_install_bug1435606-master.opt b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606-master.opt new file mode 100644 index 000000000000..af01e3de44d7 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606-master.opt @@ -0,0 +1 @@ +--audit_log_file=/path/does/not/exist/audit.log diff --git a/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.result b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.result new file mode 100644 index 000000000000..773641dd1599 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.result @@ -0,0 +1,7 @@ +call mtr.add_suppression("Plugin 'audit_log' init function returned error"); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); +SELECT COUNT(*) AS should_be_0 FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'audit_log'; +should_be_0 +0 diff --git a/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.test b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.test new file mode 100644 index 000000000000..2d205893c188 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_install_bug1435606.test @@ -0,0 +1,9 @@ +# Bug1435606: server crashes if audit log plugin cannot create file + +call mtr.add_suppression("Plugin 'audit_log' init function returned error"); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); + +# Should have failed to install +SELECT COUNT(*) AS should_be_0 FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = 'audit_log'; diff --git a/plugin/audit_log/tests/mtr/audit_log_json-master.opt b/plugin/audit_log/tests/mtr/audit_log_json-master.opt new file mode 100644 index 000000000000..0e988b238f8b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_json-master.opt @@ -0,0 +1,3 @@ +--audit_log_file=test_audit.log +--audit-log-format=JSON +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_json.result b/plugin/audit_log/tests/mtr/audit_log_json.result new file mode 100644 index 000000000000..1abd6a550728 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_json.result @@ -0,0 +1,169 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +INSERT INTO `t1` VALUES (4,NULL); +INSERT INTO `t1` VALUES (6,''); +INSERT INTO `t1` VALUES (7,''); +INSERT INTO `t1` VALUES (8,''); +INSERT INTO `t1` VALUES (9,''); +INSERT INTO `t1` VALUES (10,''); +INSERT INTO `t1` VALUES (11,''); +INSERT INTO `t1` VALUES (12,''); +INSERT INTO `t1` VALUES (13,''); +INSERT INTO `t1` VALUES (14,' '); +INSERT INTO `t1` VALUES (15,' +'); +INSERT INTO `t1` VALUES (16,' '); +INSERT INTO `t1` VALUES (17,' '); +INSERT INTO `t1` VALUES (18,' '); +INSERT INTO `t1` VALUES (19,''); +INSERT INTO `t1` VALUES (20,''); +INSERT INTO `t1` VALUES (21,''); +INSERT INTO `t1` VALUES (22,''); +INSERT INTO `t1` VALUES (23,''); +INSERT INTO `t1` VALUES (24,''); +INSERT INTO `t1` VALUES (25,''); +INSERT INTO `t1` VALUES (26,''); +INSERT INTO `t1` VALUES (27,''); +INSERT INTO `t1` VALUES (28,''); +INSERT INTO `t1` VALUES (29,''); +INSERT INTO `t1` VALUES (30,''); +INSERT INTO `t1` VALUES (31,''); +INSERT INTO `t1` VALUES (32,''); +INSERT INTO `t1` VALUES (33,''); +INSERT INTO `t1` VALUES (34,''); +INSERT INTO `t1` VALUES (35,''); +INSERT INTO `t1` VALUES (36,''); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +4 NULL +6  +7  +8  +9  +10  +11  +12  +13  +14 +15 + +16 +17 +18 +19  +20  +21  +22  +23  +24  +25  +26  +27  +28  +29  +30  +31  +32  +33  +34  +35  +36  +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format JSON +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format JSON +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +create user 'jeffrey'@'localhost' IDENTIFIED BY 'mypass'; +drop user 'jeffrey'@'localhost'; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +select '  + /"\\'; +/"\ +  + /"\ +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_json.test b/plugin/audit_log/tests/mtr/audit_log_json.test new file mode 100644 index 000000000000..f1760f22a147 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_json.test @@ -0,0 +1,45 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--let $test_control_chars=1; +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_json.log +set global audit_log_flush= ON; +perl; + eval "use JSON qw(decode_json); 1" or exit 0; + open my $file, $ENV{'MYSQLD_DATADIR'} . '/test_audit_json.log' or die "Could not open log: $!"; + my $found_1st_control_char = 0; + my $last_control_char = 0; + my $control_char_count = 0; + while (my $line = <$file>) { + my $json = decode_json($line); + my $entry_type = $json->{audit_record}->{name}; + if($entry_type eq "Query") { + my $query = $json->{audit_record}->{sqltext}; + my @query_chars = sort($query =~ /./sg); + my $minimum_character = ord($query_chars[0]); + if ($minimum_character == 1) { + $found_1st_control_char = 1; + } + if ($found_1st_control_char && $control_char_count < 31) { + $control_char_count = $control_char_count + 1; + my $expected = $last_control_char + 1; + if ($expected != $minimum_character) { + print "Incorrect control character in output: Expected $expected, got $minimum_character\n"; + exit l; + } + $last_control_char = $minimum_character; + } + } + } + if ($control_char_count != 31) { + print "Missing control characters from the output. Expected 31, got $control_char_count\n"; + exit 2; + } + close $file; +EOF diff --git a/plugin/audit_log/tests/mtr/audit_log_long_records-master.opt b/plugin/audit_log/tests/mtr/audit_log_long_records-master.opt new file mode 100644 index 000000000000..1a976d9f6076 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_long_records-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_buffer_size=8192 +--audit_log_strategy=ASYNCHRONOUS +--audit_log_format=OLD diff --git a/plugin/audit_log/tests/mtr/audit_log_long_records.result b/plugin/audit_log/tests/mtr/audit_log_long_records.result new file mode 100644 index 000000000000..62cad62a6523 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_long_records.result @@ -0,0 +1,3 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_long_records.test b/plugin/audit_log/tests/mtr/audit_log_long_records.test new file mode 100644 index 000000000000..220cd7d44950 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_long_records.test @@ -0,0 +1,40 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +let $i= 2; +let $xx= xx; +while ($i < 3500) +{ +let $xx= x$xx; +inc $i; +} +--disable_query_log +--disable_result_log +while ($i < 8500) +{ +eval SELECT '$xx'; +let $xx= x$xx; +inc $i; +} +while ($i < 9000) +{ +eval SELECT '$xx'; +let $xx= xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx$xx; +inc $i; +} +--enable_query_log +--enable_result_log + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_long.log +SET GLOBAL audit_log_flush= ON; +perl; + eval "use XML::Parser; 1" or exit 0; + $p = new XML::Parser; + $p->parsefile($ENV{'MYSQLD_DATADIR'} . '/test_audit_long.log'); +EOF +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_long.log diff --git a/plugin/audit_log/tests/mtr/audit_log_many_connections.result b/plugin/audit_log/tests/mtr/audit_log_many_connections.result new file mode 100644 index 000000000000..395d3a6bffae --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_many_connections.result @@ -0,0 +1,7 @@ +SET @saved_max_connections = @@global.max_connections; +SET GLOBAL max_connections = 2; +connect(localhost,root,,test,MYSQL_PORT,MYSQL_SOCK); +ERROR HY000: Too many connections +connect(localhost,root,,test,MYSQL_PORT,MYSQL_SOCK); +ERROR HY000: Too many connections +SET GLOBAL max_connections= @saved_max_connections; diff --git a/plugin/audit_log/tests/mtr/audit_log_many_connections.test b/plugin/audit_log/tests/mtr/audit_log_many_connections.test new file mode 100644 index 000000000000..8fcd7350e2b6 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_many_connections.test @@ -0,0 +1,31 @@ +# +# Bug #1633988: Assertion `thd == _current_thd()' failed +# +# Test audit log handling of too many connections error +# + +--disable_query_log +call mtr.add_suppression("Too many connections"); +--enable_query_log + +--source include/count_sessions.inc + +SET @saved_max_connections = @@global.max_connections; +SET GLOBAL max_connections = 2; + +--connect (con1, localhost, root) +--connect (con2, localhost, root) +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +--error ER_CON_COUNT_ERROR +--connect (con3, localhost, root) +--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK +--error ER_CON_COUNT_ERROR +--connect (con4, localhost, root) + +connection default; +SET GLOBAL max_connections= @saved_max_connections; + +--disconnect con2 +--disconnect con1 + +--source include/wait_until_count_sessions.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_new-master.opt b/plugin/audit_log/tests/mtr/audit_log_new-master.opt new file mode 100644 index 000000000000..d4fdcdc41943 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_new-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=LOGINS +--audit-log-format=NEW +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_new.result b/plugin/audit_log/tests/mtr/audit_log_new.result new file mode 100644 index 000000000000..301af4f4c887 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_new.result @@ -0,0 +1,96 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format NEW +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy LOGINS +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format NEW +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy LOGINS +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy SEMISYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_new.test b/plugin/audit_log/tests/mtr/audit_log_new.test new file mode 100644 index 000000000000..fafd6881f2dc --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_new.test @@ -0,0 +1,18 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_new.log +set global audit_log_flush= ON; +perl; + eval "use XML::Parser; 1" or exit 0; + $p = new XML::Parser; + $p->parsefile($ENV{'MYSQLD_DATADIR'} . '/test_audit_new.log'); +EOF +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_new.log diff --git a/plugin/audit_log/tests/mtr/audit_log_old-master.opt b/plugin/audit_log/tests/mtr/audit_log_old-master.opt new file mode 100644 index 000000000000..2524574eb92a --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_old-master.opt @@ -0,0 +1,3 @@ +--audit_log_file=test_audit.log +--audit_log_buffer_size=4096 +--audit_log_strategy=ASYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_old.result b/plugin/audit_log/tests/mtr/audit_log_old.result new file mode 100644 index 000000000000..d9923de6069c --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_old.result @@ -0,0 +1,96 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 4096 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format OLD +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 4096 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file test_audit.log +audit_log_flush OFF +audit_log_format OLD +audit_log_handler FILE +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_USER +audit_log_syslog_ident percona-audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> +set global audit_log_flush= ON; diff --git a/plugin/audit_log/tests/mtr/audit_log_old.test b/plugin/audit_log/tests/mtr/audit_log_old.test new file mode 100644 index 000000000000..ad312e99e595 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_old.test @@ -0,0 +1,18 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source audit_log_events.inc + +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_old.log +set global audit_log_flush= ON; +perl; + eval "use XML::Parser; 1" or exit 0; + $p = new XML::Parser; + $p->parsefile($ENV{'MYSQLD_DATADIR'} . '/test_audit_old.log'); +EOF +# --remove_file $MYSQLD_DATADIR/test_audit.log +# --remove_file $MYSQLD_DATADIR/test_audit_old.log diff --git a/plugin/audit_log/tests/mtr/audit_log_rotate-master.opt b/plugin/audit_log/tests/mtr/audit_log_rotate-master.opt new file mode 100644 index 000000000000..36b97341b9e8 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_rotate-master.opt @@ -0,0 +1,6 @@ +--audit_log_file=test_audit.log +--audit_log_format=JSON +--audit_log_strategy=SEMISYNCHRONOUS +--audit_log_rotate_on_size=4096 +--audit_log_buffer_size=5000 +--audit_log_rotations=10 diff --git a/plugin/audit_log/tests/mtr/audit_log_rotate.result b/plugin/audit_log/tests/mtr/audit_log_rotate.result new file mode 100644 index 000000000000..c66c32f360eb --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_rotate.result @@ -0,0 +1,14 @@ +# +# Rotate with "rotations" and "size" given as startup options +# +success +# +# PS-4950: Invalid audit log file size when audit_log_rotations is changed during runtime +# +SET @audit_log_rotations_orig = @@audit_log_rotations; +SET @audit_log_rotate_on_size_orig = @@audit_log_rotate_on_size; +SET GLOBAL audit_log_rotations = 3; +SET GLOBAL audit_log_rotate_on_size = 4096; +SET GLOBAL audit_log_rotations = @audit_log_rotations_orig; +SET GLOBAL audit_log_rotate_on_size = @audit_log_rotate_on_size_orig; +success diff --git a/plugin/audit_log/tests/mtr/audit_log_rotate.test b/plugin/audit_log/tests/mtr/audit_log_rotate.test new file mode 100644 index 000000000000..c8bc53e9a1d2 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_rotate.test @@ -0,0 +1,67 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +--echo # +--echo # Rotate with "rotations" and "size" given as startup options +--echo # + +--disable_result_log +--disable_query_log +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--enable_query_log +--enable_result_log + +perl; + eval "use JSON qw(decode_json); 1" or exit 0; + my @files = glob ($ENV{'MYSQLD_DATADIR'} . "/test_audit.log.[0-9][0-9]"); + foreach (@files) { + open my $file, $_ or die "Could not open log: $!"; + while (my $line = <$file>) { + decode_json($line); + } + close $file; + } + die "Rotation doesn't work!" unless scalar(@files) > 1 +EOF + +--echo success + +--echo # +--echo # PS-4950: Invalid audit log file size when audit_log_rotations is changed during runtime +--echo # + +SET @audit_log_rotations_orig = @@audit_log_rotations; +SET @audit_log_rotate_on_size_orig = @@audit_log_rotate_on_size; +SET GLOBAL audit_log_rotations = 3; +SET GLOBAL audit_log_rotate_on_size = 4096; + +--remove_files_wildcard $MYSQLD_DATADIR test_audit.log.* + +--disable_result_log +--disable_query_log +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--source audit_log_events.inc +--enable_query_log +--enable_result_log + +perl; + my @files = glob ($ENV{'MYSQLD_DATADIR'} . "/test_audit.log.[0-9][0-9]"); + foreach (@files) { + my $size = -s $_; + print $_; + die "Files are too small!" unless $size >= 4096; + } + die "Too many rotations!" unless scalar(@files) <= 3; +EOF + +SET GLOBAL audit_log_rotations = @audit_log_rotations_orig; +SET GLOBAL audit_log_rotate_on_size = @audit_log_rotate_on_size_orig; + +--remove_files_wildcard $MYSQLD_DATADIR test_audit.log* + +--echo success diff --git a/plugin/audit_log/tests/mtr/audit_log_startup.result b/plugin/audit_log/tests/mtr/audit_log_startup.result new file mode 100644 index 000000000000..c49a551253e5 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_startup.result @@ -0,0 +1,29 @@ +call mtr.add_suppression("Plugin 'audit_log' init function returned error."); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed."); +call mtr.add_suppression("Plugin audit_log reported: 'Both"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); +# restart: --audit_log_exclude_accounts=user@localhost +SET GLOBAL audit_log_exclude_accounts='info@localhost'; +# restart: --audit_log_include_accounts=user@localhost +SET GLOBAL audit_log_include_accounts='info@localhost'; +# restart: --audit_log_exclude_commands=alter_table +SET GLOBAL audit_log_exclude_commands='insert'; +# restart: --audit_log_include_commands=alter_table +SET GLOBAL audit_log_include_commands='insert'; +# restart: --audit_log_exclude_databases=test +SET GLOBAL audit_log_exclude_databases='mysql'; +# restart: --audit_log_include_databases=test +SET GLOBAL audit_log_include_databases='mysql'; +# restart: --audit_log_exclude_accounts=user@localhost --audit_log_include_accounts=user@localhost +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value +# restart: --audit_log_exclude_commands=delete --audit_log_include_commands=delete +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value +# restart: --audit_log_exclude_databases=test --audit_log_include_databases=test +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value +# restart: --audit_log_exclude_accounts=user@localhost --audit_log_file=./directory/file +SHOW VARIABLES LIKE 'audit_log%'; +Variable_name Value diff --git a/plugin/audit_log/tests/mtr/audit_log_startup.test b/plugin/audit_log/tests/mtr/audit_log_startup.test new file mode 100644 index 000000000000..f11302b23a5b --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_startup.test @@ -0,0 +1,70 @@ +# Bug 1641910: Trying to set audit_log_exclude_accounts crashes server. + +call mtr.add_suppression("Plugin 'audit_log' init function returned error."); +call mtr.add_suppression("Plugin 'audit_log' registration as a AUDIT failed."); +call mtr.add_suppression("Plugin audit_log reported: 'Both"); +call mtr.add_suppression("Plugin audit_log reported: 'Cannot open file"); +call mtr.add_suppression("Plugin audit_log reported: 'Error: No such file or directory'"); + +--let $restart_parameters="restart: --audit_log_exclude_accounts='user@localhost'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_exclude_accounts='info@localhost'; + + +--let $restart_parameters="restart: --audit_log_include_accounts='user@localhost'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_include_accounts='info@localhost'; + + +--let $restart_parameters="restart: --audit_log_exclude_commands='alter_table'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_exclude_commands='insert'; + + +--let $restart_parameters="restart: --audit_log_include_commands='alter_table'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_include_commands='insert'; + + +--let $restart_parameters="restart: --audit_log_exclude_databases='test'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_exclude_databases='mysql'; + + +--let $restart_parameters="restart: --audit_log_include_databases='test'" +--source include/restart_mysqld.inc + +SET GLOBAL audit_log_include_databases='mysql'; + + +--let $restart_parameters="restart: --audit_log_exclude_accounts='user@localhost' --audit_log_include_accounts='user@localhost'" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; + + +--let $restart_parameters="restart: --audit_log_exclude_commands='delete' --audit_log_include_commands='delete'" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; + + +--let $restart_parameters="restart: --audit_log_exclude_databases='test' --audit_log_include_databases='test'" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; + + +--let $restart_parameters="restart: --audit_log_exclude_accounts='user@localhost' --audit_log_file=./directory/file" +--source include/restart_mysqld.inc + +# there should be no audit_log% variables shown +SHOW VARIABLES LIKE 'audit_log%'; diff --git a/plugin/audit_log/tests/mtr/audit_log_syslog-master.opt b/plugin/audit_log/tests/mtr/audit_log_syslog-master.opt new file mode 100644 index 000000000000..eb692e1c268f --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_syslog-master.opt @@ -0,0 +1,5 @@ +--audit-log-format=CSV +--audit_log_handler=SYSLOG +--audit_log_syslog_ident=test_audit +--audit_log_syslog_facility=LOG_AUTH +--audit_log_syslog_priority=LOG_INFO diff --git a/plugin/audit_log/tests/mtr/audit_log_syslog.result b/plugin/audit_log/tests/mtr/audit_log_syslog.result new file mode 100644 index 000000000000..4515f8770e8e --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_syslog.result @@ -0,0 +1,93 @@ +CREATE TABLE t1 (c1 INT, c2 CHAR(20)); +CREATE TABLE t1 +(c1 INT, +c2 CHAR(20)); +ERROR 42S01: Table 't1' already exists +INSERT INTO t1 VALUES (1,'a'),(2,'b'),(3,'c'); +SELECT * FROM t1; +c1 c2 +1 a +2 b +3 c +SELECT * FROM t2; +ERROR 42S02: Table 'test.t2' doesn't exist +DROP TABLE t1; +PREPARE stmt1 FROM 'SELECT 1'; +EXECUTE stmt1; +1 +1 +SHOW STATUS LIKE 'audit_log%'; +Variable_name Value +DEALLOCATE PREPARE stmt1; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler SYSLOG +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_AUTH +audit_log_syslog_ident test_audit +audit_log_syslog_priority LOG_INFO +connect(localhost,no_such_user,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'no_such_user'@'localhost' (using password: NO) +create table t1 (id int); +create table t2 (id int); +insert into t1 values (1), (2); +insert into t2 values (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2), (1), (2); +select * from t1; +id +1 +2 +alter table t1 rename renamed_t1; +select * from t_doesnt_exist; +ERROR 42S02: Table 'test.t_doesnt_exist' doesn't exist +syntax_error_query; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syntax_error_query' at line 1 +drop table renamed_t1, t2; +show variables like 'audit_log%'; +Variable_name Value +audit_log_buffer_size 1048576 +audit_log_exclude_accounts +audit_log_exclude_commands +audit_log_exclude_databases +audit_log_file audit.log +audit_log_flush OFF +audit_log_format CSV +audit_log_handler SYSLOG +audit_log_include_accounts +audit_log_include_commands +audit_log_include_databases +audit_log_policy ALL +audit_log_rotate_on_size 0 +audit_log_rotations 0 +audit_log_strategy ASYNCHRONOUS +audit_log_syslog_facility LOG_AUTH +audit_log_syslog_ident test_audit +audit_log_syslog_priority LOG_INFO +create database sa_db; +create table t1 (id2 int); +insert into t1 values (1), (2); +select * from t1; +id2 +1 +2 +drop table t1; +use sa_db; +create table sa_t1(id int); +insert into sa_t1 values (1), (2); +drop table sa_t1; +drop database sa_db; +select '&;&&&""""<><<>>>>'; +&;&&&""""<><<>>>> +&;&&&""""<><<>>>> diff --git a/plugin/audit_log/tests/mtr/audit_log_syslog.test b/plugin/audit_log/tests/mtr/audit_log_syslog.test new file mode 100644 index 000000000000..20cded3282a3 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_syslog.test @@ -0,0 +1 @@ +--source audit_log_events.inc diff --git a/plugin/audit_log/tests/mtr/audit_log_threadpool-master.opt b/plugin/audit_log/tests/mtr/audit_log_threadpool-master.opt new file mode 100644 index 000000000000..7aae3717b96d --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_threadpool-master.opt @@ -0,0 +1,3 @@ +--audit_log_strategy=SYNCHRONOUS +--audit_log_file=test_audit_threadpool.log +--thread_handling=pool-of-threads diff --git a/plugin/audit_log/tests/mtr/audit_log_threadpool.result b/plugin/audit_log/tests/mtr/audit_log_threadpool.result new file mode 100644 index 000000000000..f78eb60b346c --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_threadpool.result @@ -0,0 +1,10 @@ +set global audit_log_flush= ON; +set global audit_log_flush= ON; +select 1; +1 +1 +select '1more'; +1more +1more +set global audit_log_flush= ON; +1 connects, 2 disconnects diff --git a/plugin/audit_log/tests/mtr/audit_log_threadpool.test b/plugin/audit_log/tests/mtr/audit_log_threadpool.test new file mode 100644 index 000000000000..2f3c4faae709 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_threadpool.test @@ -0,0 +1,38 @@ +let $MYSQLD_DATADIR= `select @@datadir`; +let MYSQLD_DATADIR= $MYSQLD_DATADIR; + +set global audit_log_flush= ON; +--remove_file $MYSQLD_DATADIR/test_audit_threadpool.log +set global audit_log_flush= ON; + +--source include/count_sessions.inc +connect (con1,localhost,root,,mysql); +select 1; +connection default; +disconnect con1; +--source include/wait_until_count_sessions.inc + +connection default; +select '1more'; + +--move_file $MYSQLD_DATADIR/test_audit_threadpool.log $MYSQLD_DATADIR/test_audit_threadpool_done.log +set global audit_log_flush= ON; + +perl; + use strict; + use warnings; + + my $logfile = $ENV{'MYSQLD_DATADIR'} . '/test_audit_threadpool_done.log'; + + open(FILE, $logfile) or die "cannot open $logfile!"; + my @buf = ; + close(FILE); + + my $connect_count = grep(/Connect/, @buf); + my $disconnect_count = grep(/Quit/, @buf); + + print "$connect_count connects, $disconnect_count disconnects\n"; +EOF + +--remove_file $MYSQLD_DATADIR/test_audit_threadpool.log +--remove_file $MYSQLD_DATADIR/test_audit_threadpool_done.log diff --git a/plugin/audit_log/tests/mtr/audit_log_xml_escape-master.opt b/plugin/audit_log/tests/mtr/audit_log_xml_escape-master.opt new file mode 100644 index 000000000000..5db58a723e3d --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_xml_escape-master.opt @@ -0,0 +1,4 @@ +--audit_log_file=test_audit.log +--audit_log_policy=QUERIES +--audit-log-format=NEW +--audit_log_strategy=SEMISYNCHRONOUS diff --git a/plugin/audit_log/tests/mtr/audit_log_xml_escape.result b/plugin/audit_log/tests/mtr/audit_log_xml_escape.result new file mode 100644 index 000000000000..868241b4a393 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_xml_escape.result @@ -0,0 +1,10 @@ +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +CREATE TABLE a (id int) ENGINE = InnoDB; +INSERT INTO a +VALUES (1); +INSERT INTO a VALUES (2); +INSERT INTO a VALUES (3); +set global audit_log_flush= ON; +Escape rules Ok +DROP TABLE a; diff --git a/plugin/audit_log/tests/mtr/audit_log_xml_escape.test b/plugin/audit_log/tests/mtr/audit_log_xml_escape.test new file mode 100644 index 000000000000..d1a115611ea8 --- /dev/null +++ b/plugin/audit_log/tests/mtr/audit_log_xml_escape.test @@ -0,0 +1,62 @@ +--let $MYSQL_TMP_DIR = `select @@tmpdir` +--let $MYSQLD_DATADIR = `select @@datadir` +--let $TMP_QUERY_FILE = $MYSQL_TMP_DIR/tmp_query + +--let MYSQLD_DATADIR = $MYSQLD_DATADIR +--let TMP_QUERY_FILE = $TMP_QUERY_FILE + +--perl + use strict; + use warnings; + + my $query_file = $ENV{'TMP_QUERY_FILE'} or die "TMP_QUERY_FILE not set"; + open(my $fh, ">", $query_file) or die "Can't open < $query_file: $!"; + + print $fh "CREATE TABLE a (id int) ENGINE = InnoDB;\n"; + print $fh "INSERT INTO a \nVALUES (1);\n"; + print $fh "INSERT INTO a \tVALUES (2);\n"; + print $fh "INSERT INTO a \rVALUES (3);\n"; + + close($fh); +EOF + +SET GLOBAL audit_log_flush=ON; +--remove_file $MYSQLD_DATADIR/test_audit.log +SET GLOBAL audit_log_flush=ON; + +--source $TMP_QUERY_FILE +--move_file $MYSQLD_DATADIR/test_audit.log $MYSQLD_DATADIR/test_audit_new.log +set global audit_log_flush= ON; + +perl; + use strict; + use warnings; + + my $log_file = "$ENV{MYSQLD_DATADIR}/test_audit_new.log"; + open(my $fh, "<", $log_file) or die "Can't open < $log_file: $!"; + + my $escape_seq_count = 0; + my $expected_escape_seq_count = 3; + + while (my $line = <$fh>) { + if ($line =~ m/INSERT\sINTO\sa\s VALUES\s\(1\)/ + || $line =~ m/INSERT\sINTO\sa\s VALUES\s\(2\)/ + || $line =~ m/INSERT\sINTO\sa\s VALUES\s\(3\)/) { + ++$escape_seq_count; + } + } + + if ($escape_seq_count == $expected_escape_seq_count) { + print "Escape rules Ok\n"; + } + else { + print "Found $escape_seq_count escape sequences, expected $expected_escape_seq_count\n"; + } + + close($fh); +EOF + +--remove_file $MYSQLD_DATADIR/test_audit.log +--remove_file $MYSQLD_DATADIR/test_audit_new.log + +DROP TABLE a; diff --git a/plugin/audit_log/tests/mtr/percona_bug_ps3867.result b/plugin/audit_log/tests/mtr/percona_bug_ps3867.result new file mode 100644 index 000000000000..ee83ce6f8a44 --- /dev/null +++ b/plugin/audit_log/tests/mtr/percona_bug_ps3867.result @@ -0,0 +1,37 @@ +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; +SET GLOBAL audit_log_flush=ON; +SET GLOBAL audit_log_flush=ON; +SELECT 'user1'; +user1 +user1 +SELECT 'user22'; +user22 +user22 +SELECT '22user'; +22user +22user +SELECT 'user22'; +user22 +user22 +SELECT 'admin'; +admin +admin +SELECT 'us,er1'; +us,er1 +us,er1 +SET GLOBAL audit_log_flush=ON; +set global audit_log_flush= ON; +=================================================================== +"Connect","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +"Query","","","select","",0,"SELECT 'user1'","user1[user1] @ localhost [127.0.0.1]","localhost","","127.0.0.1","test" +"Quit","","","",0,"user1","user1","","","localhost","127.0.0.1","test" +=================================================================== +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/percona_bug_ps3867.test b/plugin/audit_log/tests/mtr/percona_bug_ps3867.test new file mode 100644 index 000000000000..5a9d8a2876c7 --- /dev/null +++ b/plugin/audit_log/tests/mtr/percona_bug_ps3867.test @@ -0,0 +1,35 @@ +# PS-3867: audit_log_include_accunts was incorrect when set on the command line + +# setup some user accounts + +CREATE USER 'user1'@'127.0.0.1' IDENTIFIED BY 'password1'; +CREATE USER 'user22'@'%' IDENTIFIED BY 'password1'; +CREATE USER '22user'@'LOCALHOST' IDENTIFIED BY 'password1'; +CREATE USER 'admin'@'%' IDENTIFIED BY 'password1'; +CREATE USER 'us,er1'@'localhost' IDENTIFIED BY 'password1'; + +let $MYSQLD_DATADIR= `select @@datadir`; +let $log_file=$MYSQLD_DATADIR/test_audit.log; + + +--disable_result_log +--let $restart_parameters="restart: $AUDIT_LOG_OPT $AUDIT_LOG_LOAD --audit_log_include_accounts='user1@127.0.0.1' --audit_log_file=test_audit.log --audit_log_policy=ALL --audit-log-format=CSV --audit_log_strategy=SYNCHRONOUS" +--source include/restart_mysqld.inc +--enable_result_log + +SET GLOBAL audit_log_flush=ON; +--remove_file $log_file +SET GLOBAL audit_log_flush=ON; + +--source audit_log_filter_events.inc + +SET GLOBAL audit_log_flush=ON; + +--source audit_log_echo.inc + +# cleanup users +DROP USER 'user1'@'127.0.0.1'; +DROP USER 'user22'@'%'; +DROP USER '22user'@'localhost'; +DROP USER 'admin'@'%'; +DROP USER 'us,er1'@'localhost'; diff --git a/plugin/audit_log/tests/mtr/suite.opt b/plugin/audit_log/tests/mtr/suite.opt new file mode 100644 index 000000000000..60755382b7b3 --- /dev/null +++ b/plugin/audit_log/tests/mtr/suite.opt @@ -0,0 +1,2 @@ +$AUDIT_LOG_OPT +$AUDIT_LOG_LOAD diff --git a/plugin/daemon_example/daemon_example.cc b/plugin/daemon_example/daemon_example.cc index 852ef5eac79c..1d7b3844136b 100644 --- a/plugin/daemon_example/daemon_example.cc +++ b/plugin/daemon_example/daemon_example.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include "m_string.h" // strlen #include "my_dbug.h" @@ -61,6 +62,7 @@ static void init_deamon_example_psi_keys() { struct mysql_heartbeat_context { my_thread_handle heartbeat_thread; File heartbeat_file; + std::atomic_bool done; }; static void *mysql_heartbeat(void *p) { @@ -70,7 +72,7 @@ static void *mysql_heartbeat(void *p) { time_t result; struct tm tm_tmp; - while (true) { + while (!con->done.load()) { sleep(5); result = time(nullptr); @@ -123,6 +125,7 @@ static int daemon_example_plugin_init(void *p) { MY_REPLACE_EXT | MY_UNPACK_FILENAME); unlink(heartbeat_filename); con->heartbeat_file = my_open(heartbeat_filename, O_CREAT | O_RDWR, MYF(0)); + con->done.store(false); /* No threads exist at this point in time, so this is thread safe. @@ -171,7 +174,7 @@ static int daemon_example_plugin_deinit(void *p) { struct tm tm_tmp; void *dummy_retval; - my_thread_cancel(&con->heartbeat_thread); + con->done.store(true); localtime_r(&result, &tm_tmp); snprintf(buffer, sizeof(buffer), diff --git a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc index 775a1fe43d45..5e6793030d7c 100644 --- a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc +++ b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/network/xcom_network_provider_ssl_native_lib.cc @@ -42,6 +42,11 @@ #include "openssl/engine.h" +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include +#include +#endif + #include "xcom/task_debug.h" #include "xcom/x_platform.h" diff --git a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc index 9c0ba4eea5f8..c05743b75b8d 100644 --- a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc +++ b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc @@ -1449,7 +1449,7 @@ static bool_t local_server_is_setup() { } static void init_time_queue(); -static int paxos_timer_task(task_arg arg MY_ATTRIBUTE((unused))); +static int paxos_timer_task(task_arg arg [[maybe_unused]]); int xcom_taskmain2(xcom_port listen_port) { init_xcom_transport(listen_port); @@ -3146,7 +3146,7 @@ node_no found_active_leaders(site_def *site) { /* Check if this message belongs to a channel that should be ignored */ static inline int ignore_message(synode_no x, site_def *site, - char const *dbg MY_ATTRIBUTE((unused))) { + char const *dbg [[maybe_unused]]) { int retval = !is_active_leader(x.node, site); IFDBG(D_BASE, STRLIT(dbg); STRLIT(" "); FN; SYCEXP(x); NUMEXP(retval)); return retval; @@ -8660,7 +8660,7 @@ int xcom_client_get_leaders(connection_descriptor *fd, uint32_t group_id, #if 0 /* Called when leader changes */ -static MY_ATTRIBUTE((unused)) xcom_election_cb election_cb = NULL; +static xcom_election_cb election_cb [[maybe_unused]] = NULL; void set_xcom_election_cb(xcom_election_cb x) { election_cb = x; } #endif @@ -8737,7 +8737,7 @@ static void paxos_timer_advance() { } /* Fire any expired timer for a Paxos machine */ -static int paxos_timer_task(task_arg arg MY_ATTRIBUTE((unused))) { +static int paxos_timer_task(task_arg arg [[maybe_unused]]) { DECL_ENV double start; ENV_INIT diff --git a/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc b/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc index adbd46fe5d79..178e6f952aac 100644 --- a/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc +++ b/plugin/innodb_memcached/daemon_memcached/daemon/memcached_mysql.cc @@ -100,8 +100,8 @@ static SYS_VAR *daemon_memcached_sys_var[] = { THD *thd_get_current_thd(); // from sql_class.cc static void emit_deprecation_message() { - push_deprecated_warn_no_replacement(thd_get_current_thd(), - "InnoDB Memcached Plugin"); + push_deprecated_warn_no_replacement(thd_get_current_thd(), + "InnoDB Memcached Plugin"); } static int daemon_memcached_plugin_deinit(void *p) @@ -159,49 +159,45 @@ static int daemon_memcached_plugin_init(void *p) pthread_attr_t attr; struct st_plugin_int* plugin = (struct st_plugin_int *)p; - emit_deprecation_message(); + emit_deprecation_message(); - con = (mysql_memcached_context*) my_malloc(PSI_INSTRUMENT_ME, + con = (mysql_memcached_context *)my_malloc(PSI_INSTRUMENT_ME, sizeof(*con), MYF(0)); - if (mci_engine_library) { - char* lib_path = (mci_eng_lib_path) - ? mci_eng_lib_path : opt_plugin_dir; - int lib_len = strlen(lib_path) - + strlen(mci_engine_library) - + strlen(FN_DIRSEP) + 1; - - con->memcached_conf.m_engine_library = (char*) my_malloc( - PSI_INSTRUMENT_ME, - lib_len, MYF(0)); - - strxmov(con->memcached_conf.m_engine_library, lib_path, - FN_DIRSEP, mci_engine_library, NullS); - } else { - con->memcached_conf.m_engine_library = NULL; - } + if (mci_engine_library) { + char *lib_path = + (mci_eng_lib_path) ? mci_eng_lib_path : opt_plugin_dir; + int lib_len = strlen(lib_path) + strlen(mci_engine_library) + + strlen(FN_DIRSEP) + 1; - con->memcached_conf.m_mem_option = mci_memcached_option; - con->memcached_conf.m_innodb_api_cb = plugin->data; - con->memcached_conf.m_r_batch_size = mci_r_batch_size; - con->memcached_conf.m_w_batch_size = mci_w_batch_size; - con->memcached_conf.m_enable_binlog = mci_enable_binlog; - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - - /* now create the thread */ - if (pthread_create(&con->memcached_thread, &attr, - daemon_memcached_main, - (void *)&con->memcached_conf) != 0) - { - fprintf(stderr,"Could not create memcached daemon thread!\n"); - exit(0); - } + con->memcached_conf.m_engine_library = + (char *)my_malloc(PSI_INSTRUMENT_ME, lib_len, MYF(0)); - plugin->data= (void *)con; + strxmov(con->memcached_conf.m_engine_library, lib_path, FN_DIRSEP, + mci_engine_library, NullS); + } else { + con->memcached_conf.m_engine_library = NULL; + } - return(0); + con->memcached_conf.m_mem_option = mci_memcached_option; + con->memcached_conf.m_innodb_api_cb = plugin->data; + con->memcached_conf.m_r_batch_size = mci_r_batch_size; + con->memcached_conf.m_w_batch_size = mci_w_batch_size; + con->memcached_conf.m_enable_binlog = mci_enable_binlog; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + /* now create the thread */ + if (pthread_create(&con->memcached_thread, &attr, daemon_memcached_main, + (void *)&con->memcached_conf) != 0) { + fprintf(stderr, "Could not create memcached daemon thread!\n"); + exit(0); + } + + plugin->data = (void *)con; + + return (0); } struct st_mysql_daemon daemon_memcached_plugin = diff --git a/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c b/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c index 3dc3e1d47005..a127c3735707 100644 --- a/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c +++ b/plugin/innodb_memcached/daemon_memcached/daemon/topkeys.c @@ -122,29 +122,6 @@ topkey_item_t *topkeys_item_get_or_create(topkeys_t *tk, const void *key, size_t return item; } -static inline void append_stat(const void *cookie, - const char *name, - size_t namelen, - const char *key, - size_t nkey, - int value, - ADD_STAT add_stats) { - char key_str[128]; - char val_str[128]; - int klen, vlen; - - klen = sizeof(key_str) - namelen - 2; - if (nkey < klen) { - klen = nkey; - } - memcpy(key_str, key, klen); - key_str[klen] = '.'; - memcpy(&key_str[klen+1], name, namelen + 1); - klen += namelen + 1; - vlen = snprintf(val_str, sizeof(val_str) - 1, "%d", value); - add_stats(key_str, klen, val_str, vlen, cookie); -} - struct tk_context { const void *cookie; ADD_STAT add_stat; diff --git a/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt b/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt index c7d27cf3ad1d..fa7b43fc3b57 100644 --- a/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt +++ b/plugin/innodb_memcached/innodb_memcache/CMakeLists.txt @@ -33,6 +33,9 @@ INCLUDE_DIRECTORIES( IF(CMAKE_C_FLAGS MATCHES "-Werror") STRING(REGEX REPLACE "-Werror( |$)" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") ENDIF(CMAKE_C_FLAGS MATCHES "-Werror") +IF(CMAKE_CXX_FLAGS MATCHES "-Werror") + STRING(REGEX REPLACE "-Werror( |$)" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +ENDIF(CMAKE_CXX_FLAGS MATCHES "-Werror") # Set extra flags for the C/CXX compiler IF(MY_COMPILER_IS_GNU_OR_CLANG) diff --git a/plugin/keyring/buffered_file_io.cc b/plugin/keyring/buffered_file_io.cc index b6d1e9b0fe51..76bd4b4e5912 100644 --- a/plugin/keyring/buffered_file_io.cc +++ b/plugin/keyring/buffered_file_io.cc @@ -299,7 +299,7 @@ bool Buffered_file_io::check_if_keyring_file_can_be_opened_or_created() { @retval true - there was an error with initializing keyring file @retval false - keyring file has been initialized successfully */ -bool Buffered_file_io::init(std::string *keyring_filename) { +bool Buffered_file_io::init(const std::string *keyring_filename) { // file name can't be empty assert(keyring_filename->empty() == false); diff --git a/plugin/keyring/buffered_file_io.h b/plugin/keyring/buffered_file_io.h index 2a206558b801..5556db756c2a 100644 --- a/plugin/keyring/buffered_file_io.h +++ b/plugin/keyring/buffered_file_io.h @@ -50,7 +50,7 @@ class Buffered_file_io : public IKeyring_io { // ================= IKeyring_io implementation ================= // - bool init(std::string *keyring_filename) override; + bool init(const std::string *keyring_filename) override; bool flush_to_backup(ISerialized_object *serialized_object) override; bool flush_to_storage(ISerialized_object *serialized_object) override; ISerializer *get_serializer() override; diff --git a/plugin/keyring/common/i_keyring_io.h b/plugin/keyring/common/i_keyring_io.h index 041c43be3957..b0628f488c7e 100644 --- a/plugin/keyring/common/i_keyring_io.h +++ b/plugin/keyring/common/i_keyring_io.h @@ -30,7 +30,7 @@ namespace keyring { class IKeyring_io : public Keyring_alloc { public: - virtual bool init(std::string *keyring_storage_url) = 0; + virtual bool init(const std::string *keyring_storage_url) = 0; virtual bool flush_to_backup(ISerialized_object *serialized_object) = 0; virtual bool flush_to_storage(ISerialized_object *serialized_object) = 0; diff --git a/plugin/keyring/common/i_keyring_key.h b/plugin/keyring/common/i_keyring_key.h index e4a9e15dadc1..c982825199e7 100644 --- a/plugin/keyring/common/i_keyring_key.h +++ b/plugin/keyring/common/i_keyring_key.h @@ -49,6 +49,7 @@ struct IKey : public Keyring_alloc { virtual size_t get_key_data_size() = 0; virtual size_t get_key_pod_size() const = 0; virtual uchar *release_key_data() = 0; + virtual void xor_data(uchar *data, size_t data_len) = 0; virtual void xor_data() = 0; virtual void set_key_data(uchar *key_data, size_t key_data_size) = 0; virtual void set_key_type(const std::string *key_type) = 0; diff --git a/plugin/keyring/common/keyring_key.cc b/plugin/keyring/common/keyring_key.cc index e95467f0c38e..939ffb6c03e4 100644 --- a/plugin/keyring/common/keyring_key.cc +++ b/plugin/keyring/common/keyring_key.cc @@ -189,12 +189,16 @@ size_t Key::get_key_pod_size() const { return key_pod_size_aligned; } -void Key::xor_data() { - if (key == nullptr) return; +void Key::xor_data(uchar *data, size_t data_len) { static const char *obfuscate_str = "*305=Ljt0*!@$Hnm(*-9-w;:"; - for (size_t i = 0, l = 0; i < key_len; + for (size_t i = 0, l = 0; i < data_len; ++i, l = ((l + 1) % strlen(obfuscate_str))) - key.get()[i] ^= obfuscate_str[l]; + data[i] ^= obfuscate_str[l]; +} + +void Key::xor_data() { + if (key == nullptr) return; + xor_data(key.get(), key_len); } bool Key::is_key_id_valid() { return key_id.length() > 0; } diff --git a/plugin/keyring/common/keyring_key.h b/plugin/keyring/common/keyring_key.h index 92a1ad905bba..0ff44cc29947 100644 --- a/plugin/keyring/common/keyring_key.h +++ b/plugin/keyring/common/keyring_key.h @@ -52,6 +52,7 @@ struct Key : IKey { size_t get_key_data_size() override; size_t get_key_pod_size() const override; uchar *release_key_data() override; + void xor_data(uchar *data, size_t data_len) override; void xor_data() override; void set_key_data(uchar *key_data, size_t key_data_size) override; void set_key_type(const std::string *key_type) override; @@ -68,7 +69,7 @@ struct Key : IKey { const void *a_key, size_t a_key_len); void clear_key_data(); - void create_key_signature() const; + virtual void create_key_signature() const; bool load_string_from_buffer(const uchar *buffer, size_t *buffer_position, size_t key_pod_size, std::string *string, size_t string_length); diff --git a/plugin/keyring/common/keyring_memory.h b/plugin/keyring/common/keyring_memory.h index e6a1262fb77d..da6c14502ca1 100644 --- a/plugin/keyring/common/keyring_memory.h +++ b/plugin/keyring/common/keyring_memory.h @@ -23,10 +23,13 @@ #ifndef MYSQL_KEYRING_MEMORY_H #define MYSQL_KEYRING_MEMORY_H -#include +#include #include #include +#include "my_sys.h" +#include "mysql/service_mysql_alloc.h" + namespace keyring { extern PSI_memory_key key_memory_KEYRING; @@ -48,6 +51,43 @@ class Keyring_alloc { static void operator delete(void *ptr, std::size_t) { my_free(ptr); } static void operator delete[](void *ptr, std::size_t) { my_free(ptr); } }; + +template +class Secure_allocator { + public: + using value_type = T; + + Secure_allocator() noexcept {} + + template + Secure_allocator(const Secure_allocator &) noexcept {} + + T *allocate(size_t n) { + if (n == 0) + return nullptr; + else if (n > INT_MAX) + throw std::bad_alloc(); + return keyring_malloc(n * sizeof(T)); + } + + void deallocate(T *p, size_t n) noexcept { + memset_s(p, n, 0, n); + my_free(p); + } +}; + +template +bool operator==(const Secure_allocator &, + const Secure_allocator &) noexcept { + return true; +} + +template +bool operator!=(const Secure_allocator &, + const Secure_allocator &) noexcept { + return false; +} + } // namespace keyring #endif // MYSQL_KEYRING_MEMORY_H diff --git a/plugin/keyring/common/secure_string.h b/plugin/keyring/common/secure_string.h new file mode 100644 index 000000000000..35fa7f6d366c --- /dev/null +++ b/plugin/keyring/common/secure_string.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_KEYRING_SECURE_STRING +#define MYSQL_KEYRING_SECURE_STRING + +#include +#include "keyring_memory.h" + +namespace keyring { +typedef std::basic_string, Secure_allocator> + Secure_string; +typedef std::basic_ostringstream, + Secure_allocator> + Secure_ostringstream; +typedef std::basic_istringstream, + Secure_allocator> + Secure_istringstream; +} // namespace keyring + +#endif // MYSQL_KEYRING_SECURE_STRING diff --git a/plugin/keyring/keyring_file.version b/plugin/keyring/keyring_file.version new file mode 100644 index 000000000000..023b6b555b21 --- /dev/null +++ b/plugin/keyring/keyring_file.version @@ -0,0 +1,8 @@ +KEYRING_FILE_VERSION_1.0 { + global: + _mysql_*; + mysql_malloc_service; + my_plugin_log_service; + security_context_service; + local: *; +}; diff --git a/plugin/keyring_vault/CMakeLists.txt b/plugin/keyring_vault/CMakeLists.txt new file mode 100644 index 000000000000..1b18d006f270 --- /dev/null +++ b/plugin/keyring_vault/CMakeLists.txt @@ -0,0 +1,64 @@ + +# keyring_vault mtr suite is a default suite and must be installed always, +# regardless if the keyring_vault plugin is built and installed or not. +IF(WITH_KEYRING_VAULT_TEST) + ADD_SUBDIRECTORY(keyring_vault-test) +ENDIF() + +IF(INSTALL_MYSQLTESTDIR) + INSTALL(DIRECTORY tests/mtr/ DESTINATION ${INSTALL_MYSQLTESTDIR}/suite/keyring_vault COMPONENT Test) +ENDIF() + +IF (NOT DEFINED WITH_KEYRING_VAULT) + SET (WITH_KEYRING_VAULT 1) +ENDIF() +IF (NOT WITH_KEYRING_VAULT) + MESSAGE (STATUS "Not building keyring_vault plugin") + RETURN() +ENDIF() + +INCLUDE(CheckFunctionExists) +INCLUDE(curl) + +SET(CMAKE_EXTRA_INCLUDE_FILES string.h) +SET(CMAKE_EXTRA_INCLUDE_FILES) + +MACRO (CHECK_IF_LIB_FOUND lib_name project_name status_mode) + SET (lib_found_variable_name "${lib_name}_FOUND") + IF (NOT DEFINED ${lib_found_variable_name} OR NOT ${${lib_found_variable_name}}) + message(${status_mode} "Not building ${project_name}, could not find library: ${lib_name}") + RETURN() + ENDIF() +ENDMACRO() + +CHECK_IF_LIB_FOUND(CURL "keyring_vault" FATAL_ERROR) +MESSAGE (STATUS "Building keyring_vault plugin") + +ADD_DEFINITIONS(-DLOG_COMPONENT_TAG="keyring_vault") + +INCLUDE_DIRECTORIES(SYSTEM ${BOOST_PATCHES_DIR} ${BOOST_INCLUDE_DIR} ${CURL_INCLUDE_DIRS}) + +MYSQL_ADD_PLUGIN(keyring_vault + ${CMAKE_SOURCE_DIR}/plugin/keyring/common/keyring_key.cc + ${CMAKE_SOURCE_DIR}/plugin/keyring/common/keys_container.cc + ${CMAKE_SOURCE_DIR}/plugin/keyring/common/keys_iterator.cc + ${CMAKE_SOURCE_DIR}/plugin/keyring/common/keyring_impl.cc + vault_io.cc + vault_key.cc + vault_base64.cc + vault_parser.cc + vault_keys_list.cc + vault_keys_container.cc + vault_curl.cc + vault_credentials_parser.cc + vault_credentials.cc + vault_keyring.cc + LINK_LIBRARIES ${CURL_LIBRARY} ${SSL_LIBRARIES} + MODULE_ONLY + MODULE_OUTPUT_NAME "keyring_vault") + +# We limit symbols exported on Linux to only those required by server. +IF(LINK_FLAG_NO_UNDEFINED) + GET_PROPERTY(keyring_vault_link_flags TARGET keyring_vault PROPERTY LINK_FLAGS) + SET_PROPERTY(TARGET keyring_vault PROPERTY LINK_FLAGS "${keyring_vault_link_flags} -Wl,--version-script=${CMAKE_SOURCE_DIR}/plugin/keyring_vault/keyring_vault.version") +ENDIF() diff --git a/plugin/keyring_vault/i_vault_curl.h b/plugin/keyring_vault/i_vault_curl.h new file mode 100644 index 000000000000..130f56707d26 --- /dev/null +++ b/plugin/keyring_vault/i_vault_curl.h @@ -0,0 +1,48 @@ + +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_I_VAULT_CURL +#define MYSQL_I_VAULT_CURL + +#include "plugin/keyring/common/i_keyring_key.h" +#include "plugin/keyring/common/secure_string.h" +#include "vault_credentials.h" +#include "vault_key.h" + +namespace keyring { + +class IVault_curl : public Keyring_alloc { + public: + virtual bool init(const Vault_credentials &vault_credentials) = 0; + + virtual bool list_keys(Secure_string *response) = 0; + virtual bool write_key(const Vault_key &key, Secure_string *response) = 0; + virtual bool read_key(const Vault_key &key, Secure_string *response) = 0; + virtual bool delete_key(const Vault_key &key, Secure_string *response) = 0; +#ifndef EXTRA_CODE_FOR_UNIT_TESTING + virtual void set_timeout(uint timeout) noexcept = 0; +#else + // GMock 1.8 doesn't support noexcept mocking at all + virtual void set_timeout(uint timeout) = 0; +#endif + + virtual ~IVault_curl() {} +}; + +} // namespace keyring + +#endif // MYSQL_I_VAULT_CURL_H diff --git a/plugin/keyring_vault/i_vault_io.h b/plugin/keyring_vault/i_vault_io.h new file mode 100644 index 000000000000..59346e6fee7b --- /dev/null +++ b/plugin/keyring_vault/i_vault_io.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef I_VAULTIO_INCLUDED +#define I_VAULTIO_INCLUDED + +#include "plugin/keyring/common/i_keyring_io.h" + +namespace keyring { + +class IVault_io : public IKeyring_io { + public: + virtual bool retrieve_key_type_and_data(IKey *key) = 0; + virtual void set_curl_timeout(uint timeout) = 0; + + virtual ~IVault_io() {} +}; + +} // namespace keyring + +#endif // I_VAULTIO_INCLUDED diff --git a/plugin/keyring_vault/i_vault_parser.h b/plugin/keyring_vault/i_vault_parser.h new file mode 100644 index 000000000000..1f6f8d5dcbf1 --- /dev/null +++ b/plugin/keyring_vault/i_vault_parser.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_I_VAULT_PARSER_H +#define MYSQL_I_VAULT_PARSER_H + +#include "plugin/keyring/common/i_keyring_key.h" +#include "plugin/keyring/common/logger.h" +#include "plugin/keyring/common/secure_string.h" +#include "vault_keys_list.h" + +namespace keyring { +class IVault_parser { + public: + struct KeyParameters { + Secure_string key_id; + Secure_string user_id; + Secure_string &operator[](uint i) { + assert(i <= 1); + return i == 0 ? key_id : user_id; + } + }; + + virtual bool parse_keys(const Secure_string &payload, + Vault_keys_list *keys) = 0; + virtual bool parse_key_data(const Secure_string &payload, IKey *key) = 0; + virtual bool parse_key_signature(const Secure_string &key_signature, + KeyParameters *key_parameters) = 0; + virtual bool parse_errors(const Secure_string &payload, + Secure_string *errors) = 0; + virtual ~IVault_parser() {} +}; +} // namespace keyring + +#endif // MYSQL_I_VAULT_PARSER_H diff --git a/plugin/keyring_vault/keyring_vault-test/CMakeLists.txt b/plugin/keyring_vault/keyring_vault-test/CMakeLists.txt new file mode 100644 index 000000000000..e62488b6f5dd --- /dev/null +++ b/plugin/keyring_vault/keyring_vault-test/CMakeLists.txt @@ -0,0 +1,23 @@ +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/plugin/keyring_vault + ${CMAKE_SOURCE_DIR}/unittest/gunit/keyring_vault) + +MYSQL_ADD_EXECUTABLE(keyring_vault-test + ./keyring_vault-test.cc + ${CMAKE_SOURCE_DIR}/plugin/keyring/common/keyring_key.cc + ${CMAKE_SOURCE_DIR}/plugin/keyring/common/keys_container.cc + ${CMAKE_SOURCE_DIR}/plugin/keyring/common/keyring_impl.cc + ../vault_io.cc + ../vault_key.cc + ../vault_base64.cc + ../vault_parser.cc + ../vault_keys_list.cc + ../vault_keys_container.cc + ../vault_curl.cc + ../vault_credentials_parser.cc + ../vault_credentials.cc + ADD_TEST keyring_vault-test) + +TARGET_LINK_LIBRARIES(keyring_vault-test strings regex mysys sql) +TARGET_LINK_LIBRARIES(keyring_vault-test binlog rpl master slave) +TARGET_LINK_LIBRARIES(keyring_vault-test ${CURL_LIBRARY} ${SSL_LIBRARIES}) diff --git a/plugin/keyring_vault/keyring_vault-test/keyring_vault-test.cc b/plugin/keyring_vault/keyring_vault-test/keyring_vault-test.cc new file mode 100644 index 000000000000..86e29327125e --- /dev/null +++ b/plugin/keyring_vault/keyring_vault-test/keyring_vault-test.cc @@ -0,0 +1,372 @@ +#include +#include +#include +#include +#include +#include "../vault_keyring.cc" +#include "generate_credential_file.h" +#include "vault_keys_container_ex.h" + +static bool random_keys = false; +static bool verbose; +static bool generate_random_keys_data = false; +static int number_of_keys_added = 0; +static int number_of_keys_fetched = 0; +static int number_of_keys_removed = 0; +static int number_of_keys_generated = 0; +static int max_generated_key_length = 0; +static int number_of_keys_to_generate = 0; + +static mysql_mutex_t LOCK_verbose; +static mysql_mutex_t LOCK_keys_in_keyring; +struct Key_in_keyring { + Key_in_keyring(const char *key_id, const char *user_id) + : key_id(key_id), + user_id(user_id), + key_signature(this->key_id + this->user_id) {} + + Key_in_keyring() {} + + bool operator<(const Key_in_keyring &rhs_key) const { + return this->key_signature < rhs_key.key_signature; + } + + std::string key_id; + std::string user_id; + std::string key_signature; +}; +static std::set keys_in_keyring; + +static void *generate(void *arg) { + my_thread_init(); + + int number_of_keys_to_generate = *static_cast(arg); + + for (uint i = 0; + my_atomic_load32(&number_of_keys_generated) < number_of_keys_to_generate; + i = (i + 1) % number_of_keys_to_generate) { + char key_id[12]; // Key#1000000\0 + char key_type[16]; // KeyType#1000000\0 + char user[13]; // User#1000000\0 + size_t key_len = rand() % 100; + + int key_nr = random_keys ? rand() % number_of_keys_to_generate : i; + sprintf(key_id, "Key#%d", key_nr); + strcpy(key_type, "AES"); + sprintf(user, "User#%d", key_nr); + Key_in_keyring key_in_keyring(key_id, user); + + bool result = false; + + if ((result = !mysql_key_generate(reinterpret_cast(key_id), + reinterpret_cast(key_type), + reinterpret_cast(user), + key_len))) { + my_atomic_add32(&number_of_keys_generated, 1); + mysql_mutex_lock(&LOCK_keys_in_keyring); + keys_in_keyring.insert(key_in_keyring); + mysql_mutex_unlock(&LOCK_keys_in_keyring); + } + + if (verbose) { + mysql_mutex_lock(&LOCK_verbose); + std::cout << "Key generated " << key_id << ' ' << key_type << ' ' << user + << ' '; + std::cout << (result ? "successful" : "failed") << std::endl; + mysql_mutex_unlock(&LOCK_verbose); + } + } + my_thread_end(); + return NULL; +} + +static void *store(void *arg) { + my_thread_init(); + int number_of_keys_to_store = *static_cast(arg); + + for (uint i = 0; + my_atomic_load32(&number_of_keys_added) < number_of_keys_to_store; + i = (i + 1) % number_of_keys_to_store) { + char key_id[12]; // Key#1000000\0 + char key_type[16]; // KeyType#1000000\0 + char user[13]; // User#1000000\0 + uchar key_stack[] = "KEeeeeeeeEEEEEeeeeEEEEEEEEEEEEY!"; + uchar *key; + size_t key_len; // = rand() % 100; + if (generate_random_keys_data) { + key_len = rand() % max_generated_key_length; + key = static_cast( + my_malloc(keyring::key_memory_KEYRING, key_len, MYF(0))); + assert(key != NULL); + assert(!my_rand_buffer(key, key_len)); + } else { + key = key_stack; + key_len = strlen(reinterpret_cast(key)) + 1; + } + + int key_nr = random_keys ? rand() % number_of_keys_to_store : i; + sprintf(key_id, "Key#%d", key_nr); + strcpy(key_type, "AES"); + sprintf(user, "User#%d", key_nr); + Key_in_keyring key_in_keyring(key_id, user); + + bool result = false; + + if ((result = !mysql_key_store(reinterpret_cast(key_id), + reinterpret_cast(key_type), + reinterpret_cast(user), key, + key_len))) { + my_atomic_add32(&number_of_keys_added, 1); + mysql_mutex_lock(&LOCK_keys_in_keyring); + keys_in_keyring.insert(key_in_keyring); + mysql_mutex_unlock(&LOCK_keys_in_keyring); + } + if (generate_random_keys_data) my_free(key); + + if (verbose) { + mysql_mutex_lock(&LOCK_verbose); + std::cout << "Key stored " << key_id << ' ' << key_type << ' ' << user + << ' '; + std::cout << (result ? "successful" : "failed") << std::endl; + mysql_mutex_unlock(&LOCK_verbose); + } + } + my_thread_end(); + return NULL; +} + +static void *fetch(void *arg) { + my_thread_init(); + int number_of_keys_to_fetch = *(static_cast(arg)); + + for (uint i = 0; + my_atomic_load32(&number_of_keys_fetched) < number_of_keys_to_fetch; + i = (i + 1) % number_of_keys_to_fetch) { + char key_id[12]; // Key#1000000\0 + char *key_type = NULL; + char user[13]; // User#1000000\0 + char key[] = "KEeeeeeeeEEEEEeeeeEEEEEEEEEEEEY!"; + + int key_nr = random_keys ? rand() % number_of_keys_to_fetch : i; + sprintf(key_id, "Key#%d", key_nr); + sprintf(user, "User#%d", key_nr); + + void *key_data = NULL; + size_t key_len = 0; + + bool result = true; + + if ((result = !mysql_key_fetch( + reinterpret_cast(key_id), &key_type, + reinterpret_cast(user), &key_data, &key_len)) && + key_data != NULL) { + my_atomic_add32(&number_of_keys_fetched, 1); + if (!generate_random_keys_data && number_of_keys_to_generate == 0) { + assert(key_len == strlen(key) + 1); + assert(strcmp(reinterpret_cast( + reinterpret_cast(key_data)), + key) == 0); + } + my_free(key_data); + } + + if (verbose) { + mysql_mutex_lock(&LOCK_verbose); + std::cout << "Key fetched " << key_id << ' '; + if (key_type != NULL) std::cout << key_type << ' '; + std::cout << user << ' '; + std::cout << (result ? "successful" : "failed") << std::endl; + mysql_mutex_unlock(&LOCK_verbose); + } + if (key_type != NULL) my_free(key_type); + } + my_thread_end(); + return NULL; +} + +static void *remove(void *arg) { + my_thread_init(); + int number_of_keys_to_remove = *(static_cast(arg)); + + for (uint i = 0; + my_atomic_load32(&number_of_keys_removed) < number_of_keys_to_remove; + i = (i + 1) % number_of_keys_to_remove) { + char key_id[12]; // Key#1000000\0 + char user[13]; // User#1000000\0 + + int key_nr = random_keys ? rand() % number_of_keys_to_remove : i; + sprintf(key_id, "Key#%d", key_nr); + sprintf(user, "User#%d", key_nr); + + bool result = true; + + if ((result = !mysql_key_remove(reinterpret_cast(key_id), + reinterpret_cast(user)))) + my_atomic_add32(&number_of_keys_removed, 1); + + if (verbose) { + mysql_mutex_lock(&LOCK_verbose); + std::cout << "Key removed " << key_id << ' ' << user << ' '; + std::cout << (result ? "successful" : "failed") << std::endl; + mysql_mutex_unlock(&LOCK_verbose); + } + } + my_thread_end(); + return NULL; +} + +static bool keyring_vault_init_for_test() { +#ifdef HAVE_PSI_INTERFACE + keyring_init_psi_keys(); +#endif + if (init_keyring_locks()) return true; + + if (init_curl()) return true; + + st_plugin_int plugin_info; + plugin_info.name.str = const_cast("keyring_vault"); + plugin_info.name.length = strlen("keyring_vault"); + + logger.reset(new keyring::Logger(&plugin_info)); + // We are using Vault_keys_container_ex which allows removing all keys created + // in keyring + // Its behaviour is exactly the same as Vault_keys_container + keys.reset(new keyring::Vault_keys_container_ex(logger.get())); + std::unique_ptr vault_curl(new Vault_curl(logger.get(), curl)); + std::unique_ptr vault_parser(new Vault_parser(logger.get())); + IKeyring_io *keyring_io = + new Vault_io(logger.get(), vault_curl.release(), vault_parser.release()); + is_keys_container_initialized = + !keys->init(keyring_io, keyring_vault_config_file); + return !is_keys_container_initialized; +} + +int main(int argc, char **argv) { + my_thread_global_init(); + mysql_mutex_init(0, &LOCK_verbose, MY_MUTEX_INIT_FAST); + mysql_mutex_init(0, &LOCK_keys_in_keyring, MY_MUTEX_INIT_FAST); + + keyring::system_charset_info = &my_charset_utf8_general_ci; + srand(time(NULL)); + my_thread_handle *otid; + unsigned long long i; + void *tret; + + std::string credential_file_url = "./keyring_vault.conf"; + if (generate_credential_file(credential_file_url)) { + std::cerr << "Could not generate default keyring configuration file" + << std::endl; + return 1; + } + + const char *default_args[] = {credential_file_url.c_str(), + "100", + "10", + "30", + "10", + "200", + "100", + "20", + "10", + "0", + "1"}; + bool argument_passed = argc == 12; + + if (!argument_passed) { + std::cerr << "Usage: keyring_vault_test " + " " + " " + " " + " " + " " + << std::endl + << "Using default values" << std::endl; + } + + my_init(); + + keyring_vault_config_file = static_cast(my_malloc( + PSI_NOT_INSTRUMENTED, + strlen(argument_passed ? argv[1] : default_args[0]) + 1, MYF(0))); + strcpy(keyring_vault_config_file, + (argument_passed ? argv[1] : default_args[0])); + const unsigned long long threads_store_number = + atoll(argument_passed ? argv[2] : default_args[1]); + const unsigned long long threads_remove_number = + atoll(argument_passed ? argv[3] : default_args[2]); + const unsigned long long threads_fetch_number = + atoll(argument_passed ? argv[4] : default_args[3]); + const unsigned long long threads_generate_key_number = + atoll(argument_passed ? argv[5] : default_args[4]); + const int number_of_keys_to_store = + atoi(argument_passed ? argv[6] : default_args[5]); + const int number_of_keys_to_fetch = + atoi(argument_passed ? argv[7] : default_args[6]); + number_of_keys_to_generate = + atoi(argument_passed ? argv[8] : default_args[7]); + max_generated_key_length = atoi(argument_passed ? argv[9] : default_args[8]); + random_keys = atoi(argument_passed ? argv[10] : default_args[9]); + verbose = atoi(argument_passed ? argv[11] : default_args[10]); + const int number_of_keys_to_remove = + number_of_keys_to_store + number_of_keys_to_generate; + + unsigned long long threads_number = + threads_store_number + threads_fetch_number + threads_remove_number + + threads_generate_key_number; + + if (!(otid = static_cast(my_malloc( + PSI_NOT_INSTRUMENTED, threads_number * sizeof(*otid), MYF(0))))) + return 7; + + if (keyring_vault_init_for_test()) { + fprintf(stderr, "Could not initialize keyring_vault."); + return 1; + } + + for (i = 0; i < threads_store_number; i++) + if (mysql_thread_create(PSI_NOT_INSTRUMENTED, &otid[i], NULL, store, + const_cast(&number_of_keys_to_store))) + return 2; + + for (i = 0; i < threads_fetch_number; i++) + if (mysql_thread_create(PSI_NOT_INSTRUMENTED, + &otid[threads_store_number + i], NULL, fetch, + const_cast(&number_of_keys_to_fetch))) + return 3; + + for (i = 0; i < threads_remove_number; i++) + if (mysql_thread_create( + PSI_NOT_INSTRUMENTED, + &otid[threads_store_number + threads_fetch_number + i], NULL, + remove, const_cast(&number_of_keys_to_remove))) + return 4; + + for (i = 0; i < threads_generate_key_number; i++) + if (mysql_thread_create(PSI_NOT_INSTRUMENTED, + &otid[threads_store_number + threads_fetch_number + + threads_remove_number + i], + NULL, generate, + static_cast(&number_of_keys_to_generate))) + return 6; + + for (i = 0; i < threads_number; i++) my_thread_join(&otid[i], &tret); + + // To be sure that all keys added in this test are removed - we try to remove + // all keys for which + // store/generate functions returned success + // keys_in_keyring is growing only set - as we do not want to add + // synchronization over keyring service calls. + for (std::set::const_iterator iter = keys_in_keyring.begin(); + iter != keys_in_keyring.end(); ++iter) + mysql_key_remove(iter->key_id.c_str(), iter->user_id.c_str()); + + my_free(keyring_vault_config_file); + my_free(otid); + mysql_mutex_destroy(&LOCK_verbose); + mysql_mutex_destroy(&LOCK_keys_in_keyring); + + keyring_vault_deinit(NULL); + my_end(0); + + return 0; +} diff --git a/plugin/keyring_vault/keyring_vault-test/vault_keys_container_ex.h b/plugin/keyring_vault/keyring_vault-test/vault_keys_container_ex.h new file mode 100644 index 000000000000..a0a54670f0b0 --- /dev/null +++ b/plugin/keyring_vault/keyring_vault-test/vault_keys_container_ex.h @@ -0,0 +1,24 @@ +#ifndef MYSQL_VAULT_KEYS_CONTAINER_EX_H +#define MYSQL_VAULT_KEYS_CONTAINER_EX_H + +#include + +#include "vault_keys_container.h" + +namespace keyring { + +class Vault_keys_container_ex : public Vault_keys_container { + public: + Vault_keys_container_ex(ILogger *logger_) : Vault_keys_container(logger_) {} + + void remove_all_keys() { + for (ulong idx = 0; idx < keys_hash->records; idx++) { + IKey *key = reinterpret_cast(my_hash_element(keys_hash, idx)); + remove_key(key); + } + } +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_KEYS_CONTAINER_EX_H diff --git a/plugin/keyring_vault/keyring_vault.version b/plugin/keyring_vault/keyring_vault.version new file mode 100644 index 000000000000..9e751fa12caa --- /dev/null +++ b/plugin/keyring_vault/keyring_vault.version @@ -0,0 +1,8 @@ +KEYRING_VAULT_VERSION_1.0 { + global: + _mysql_*; + mysql_malloc_service; + my_plugin_log_service; + security_context_service; + local: *; +}; diff --git a/plugin/keyring_vault/tests/mtr/generate_conf_file.inc b/plugin/keyring_vault/tests/mtr/generate_conf_file.inc new file mode 100644 index 000000000000..7255daf25224 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/generate_conf_file.inc @@ -0,0 +1,22 @@ +let KEYRING_CONF_FILE_TO_GENERATE=$KEYRING_CONF_FILE_TO_GENERATE; +let KEYRING_CONF_TEMPLATE_FILE=$KEYRING_CONF_TEMPLATE_FILE; +let MYSQL_TEST_DIR=$MYSQL_TEST_DIR; +let SERVER_UUID= query_get_value(SELECT @@SERVER_UUID, @@SERVER_UUID, 1); + +--perl + use strict; + my $mysql_test_dir= $ENV{MYSQL_TEST_DIR} or die "Need MYSQL_TEST_DIR"; + my $server_uuid= $ENV{SERVER_UUID} or die "Server uuid not set"; + my $keyring_conf_template_file= $ENV{KEYRING_CONF_TEMPLATE_FILE} or die "Need KEYRING_CONF_TEMPLATE_FILE"; + my $keyring_conf_file_to_generate= $ENV{'KEYRING_CONF_FILE_TO_GENERATE'} or die("KEYRING_CONF_FILE_TO_GENERATE not set\n"); + open CONF_FILE, ">", "$keyring_conf_file_to_generate" or die "Could not open configuration file: ${keyring_conf_file_to_generate}.\n"; + open CONF_TEMPLATE_FILE, "<", "$keyring_conf_template_file" or die "Could not open configuration template file: ${keyring_conf_template_file}.\n"; + while (my $row = ) + { + $row =~ s/MYSQL_TEST_DIR/$mysql_test_dir/g; + $row =~ s/SECRET_MOUNT_POINT_TAG/$server_uuid/g; + print CONF_FILE $row; + } + close(CONF_FILE); + close(CONF_TEMPLATE_FILE); +EOF diff --git a/plugin/keyring_vault/tests/mtr/generate_default_conf_files.inc b/plugin/keyring_vault/tests/mtr/generate_default_conf_files.inc new file mode 100644 index 000000000000..1061cd2fe326 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/generate_default_conf_files.inc @@ -0,0 +1,15 @@ +--let $KEYRING_CONF_FILE_1=$MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +--let $KEYRING_CONF_FILE_2=$MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf +--let $KEYRING_INVALID_TOKEN_CONF_FILE=$MYSQLTEST_VARDIR/keyring_vault_invalid_token.conf + +--let $KEYRING_CONF_TEMPLATE_FILE=$MYSQL_TEST_DIR/std_data/keyring_vault_confs/keyring_vault_mtr_template1.conf +--let $KEYRING_CONF_FILE_TO_GENERATE=$KEYRING_CONF_FILE_1 +--source generate_conf_file.inc + +--let $KEYRING_CONF_TEMPLATE_FILE=$MYSQL_TEST_DIR/std_data/keyring_vault_confs/keyring_vault_mtr_template2.conf +--let $KEYRING_CONF_FILE_TO_GENERATE=$KEYRING_CONF_FILE_2 +--source generate_conf_file.inc + +--let $KEYRING_CONF_TEMPLATE_FILE=$MYSQL_TEST_DIR/std_data/keyring_vault_confs/keyring_vault_mtr_invalid_token_template.conf +--let $KEYRING_CONF_FILE_TO_GENERATE=$KEYRING_INVALID_TOKEN_CONF_FILE +--source generate_conf_file.inc diff --git a/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption-master.opt b/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption-master.opt new file mode 100644 index 000000000000..5d9bbb435d6e --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption-master.opt @@ -0,0 +1,3 @@ +$KEYRING_VAULT_PLUGIN_OPT +$KEYRING_VAULT_PLUGIN_LOAD +--innodb-sort-buffer-size=64k diff --git a/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption.result b/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption.result new file mode 100644 index 000000000000..a6f50bda4d0c --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption.result @@ -0,0 +1,132 @@ +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); +SET @@global.keyring_vault_config="MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf"; +# restart: +CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY, a VARCHAR(255)) ENGINE=InnoDB ENCRYPTION='y'; +CREATE TABLE t2 (id INT NOT NULL PRIMARY KEY, a VARCHAR(255)) ENGINE=InnoDB; +CREATE TABLE t3 (id INT, a VARCHAR(255)) ENGINE=InnoDB ENCRYPTION='y'; +CREATE TABLE t4 (id INT, a VARCHAR(255)) ENGINE=InnoDB; +CREATE TABLE t5 (id INT NOT NULL PRIMARY KEY, a TEXT(500), b VARCHAR(255), FULLTEXT(b)) ENGINE=InnoDB ENCRYPTION='y'; +CREATE TABLE t6 (id INT, a TEXT(500), b VARCHAR(255), FULLTEXT(b)) ENGINE=InnoDB; +CREATE TABLE t7 (id INT NOT NULL PRIMARY KEY, a VARCHAR(255)) ENGINE=InnoDB ROW_FORMAT=COMPRESSED ENCRYPTION='y'; +CREATE PROCEDURE innodb_insert_proc (repeat_count INT) +BEGIN +DECLARE current_num INT; +SET current_num = 0; +WHILE current_num < repeat_count DO +INSERT INTO t1 VALUES (current_num, REPEAT('foobar', 42)); +INSERT INTO t2 VALUES (current_num, REPEAT('temp', 42)); +INSERT INTO t3 VALUES (current_num, REPEAT('barfoo', 42)); +INSERT INTO t4 VALUES (current_num, REPEAT('repeat', 42)); +INSERT INTO t5 VALUES (current_num, SUBSTRING('A BC DEF GHIJ KLM NOPQRS TUV WXYZ 012 3456789', RAND() * 36 + 1, 100), REPEAT('author new', 22)); +INSERT INTO t6 VALUES (current_num, SUBSTRING('A BC DEF GHIJ KLM NOPQRS TUV WXYZ 012 3456789', RAND() * 36 + 1, 100), REPEAT('mangled old', 22)); +INSERT INTO t7 VALUES (current_num, REPEAT('mysql', 42)); +SET current_num = current_num + 1; +END WHILE; +END// +COMMIT; +SET autocommit=0; +CALL innodb_insert_proc(15000); +COMMIT; +SET autocommit=1; +ALTER TABLE t1 ADD COLUMN b INT DEFAULT 2; +ALTER TABLE t2 ADD COLUMN b INT DEFAULT 2; +ALTER TABLE t7 ADD COLUMN b INT DEFAULT 2; +ALTER TABLE t1 ADD KEY a(a), ADD KEY b(b); +ALTER TABLE t2 ADD KEY a(a), ADD KEY b(b); +ALTER TABLE t3 ADD COLUMN c INT DEFAULT 5; +ALTER TABLE t4 ADD COLUMN c INT DEFAULT 5; +ALTER TABLE t3 ADD KEY (a), ADD KEY c(c); +ALTER TABLE t4 ADD KEY (a), ADD KEY c(c); +ALTER TABLE t5 ADD FULLTEXT(a); +ALTER TABLE t6 ADD FULLTEXT(a); +ALTER TABLE t7 ADD KEY a(a), ADD KEY b(b); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int NOT NULL, + `a` varchar(255) DEFAULT NULL, + `b` int DEFAULT '2', + PRIMARY KEY (`id`), + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='y' +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `id` int NOT NULL, + `a` varchar(255) DEFAULT NULL, + `b` int DEFAULT '2', + PRIMARY KEY (`id`), + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `id` int DEFAULT NULL, + `a` varchar(255) DEFAULT NULL, + `c` int DEFAULT '5', + KEY `a` (`a`), + KEY `c` (`c`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='y' +SHOW CREATE TABLE t4; +Table Create Table +t4 CREATE TABLE `t4` ( + `id` int DEFAULT NULL, + `a` varchar(255) DEFAULT NULL, + `c` int DEFAULT '5', + KEY `a` (`a`), + KEY `c` (`c`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t5; +Table Create Table +t5 CREATE TABLE `t5` ( + `id` int NOT NULL, + `a` text, + `b` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT KEY `b` (`b`), + FULLTEXT KEY `a` (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='y' +SHOW CREATE TABLE t6; +Table Create Table +t6 CREATE TABLE `t6` ( + `id` int DEFAULT NULL, + `a` text, + `b` varchar(255) DEFAULT NULL, + FULLTEXT KEY `b` (`b`), + FULLTEXT KEY `a` (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +SHOW CREATE TABLE t7; +Table Create Table +t7 CREATE TABLE `t7` ( + `id` int NOT NULL, + `a` varchar(255) DEFAULT NULL, + `b` int DEFAULT '2', + PRIMARY KEY (`id`), + KEY `a` (`a`), + KEY `b` (`b`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED ENCRYPTION='y' +# Restarting server +SELECT COUNT(1) FROM t2; +COUNT(1) +15000 +SELECT COUNT(1) FROM t3; +COUNT(1) +15000 +SELECT COUNT(1) FROM t4; +COUNT(1) +15000 +SELECT COUNT(1) FROM t5; +COUNT(1) +15000 +SELECT COUNT(1) FROM t6; +COUNT(1) +15000 +SELECT COUNT(1) FROM t7; +COUNT(1) +15000 +DROP PROCEDURE innodb_insert_proc; +DROP TABLE t1, t2, t3, t4, t5, t6, t7; diff --git a/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption.test b/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption.test new file mode 100644 index 000000000000..212ba52afdb2 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/innodb_online_alter_encryption.test @@ -0,0 +1,25 @@ +--source include/have_keyring_vault_plugin.inc + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +--let $keyring_restart_param=restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT --innodb-sort-buffer-size=64k --innodb_encrypt_online_alter_logs=ON + +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config="$KEYRING_CONF_FILE_1"; + +--source include/innodb_online_alter_encryption.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption-master.opt b/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption-master.opt new file mode 100644 index 000000000000..cdcb72ec4604 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption-master.opt @@ -0,0 +1,4 @@ +$KEYRING_VAULT_PLUGIN_OPT +$KEYRING_VAULT_PLUGIN_LOAD +--innodb-sort-buffer-size=64k +--innodb_encrypt_online_alter_logs=ON diff --git a/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption.result b/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption.result new file mode 100644 index 000000000000..a1d7b4435210 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption.result @@ -0,0 +1,36 @@ +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); +SET @@global.keyring_vault_config="MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf"; +# restart: +CREATE TABLE t1(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, credit_card VARCHAR(200), private VARCHAR(50)) ENGINE=InnoDB ENCRYPTION='y'; +SET DEBUG_SYNC= 'row_merge_after_scan SIGNAL opened WAIT_FOR rotated'; +ALTER TABLE t1 ADD INDEX secret (credit_card), ALGORITHM=INPLACE, LOCK=NONE; +SET DEBUG_SYNC= 'now WAIT_FOR opened'; +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SET DEBUG_SYNC= 'now SIGNAL rotated'; +CREATE TABLE t2(id int) ENGINE=InnoDB; +SET DEBUG_SYNC= 'row_merge_after_scan SIGNAL opened WAIT_FOR updated'; +ALTER TABLE t1 ADD INDEX secret2 (private), ALGORITHM=INPLACE, LOCK=NONE; +SET DEBUG_SYNC= 'now WAIT_FOR opened'; +INSERT INTO t1(credit_card) SELECT credit_card FROM t1; +INSERT INTO t1(credit_card) SELECT credit_card FROM t1; +INSERT INTO t1(credit_card) SELECT credit_card FROM t1; +SET DEBUG_SYNC= 'now SIGNAL updated'; +DROP TABLE t2; +SELECT variable_value > 0 FROM performance_schema.global_status WHERE variable_name = 'innodb_num_pages_encrypted'; +variable_value > 0 +SELECT variable_value > 0 FROM performance_schema.global_status WHERE variable_name = 'innodb_encryption_n_merge_blocks_encrypted'; +variable_value > 0 +1 +SELECT variable_value > 0 FROM performance_schema.global_status WHERE variable_name = 'innodb_encryption_n_merge_blocks_decrypted'; +variable_value > 0 +1 +SELECT variable_value > 0 FROM performance_schema.global_status WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_encrypted'; +variable_value > 0 +1 +SELECT variable_value > 0 FROM performance_schema.global_status WHERE variable_name = 'innodb_encryption_n_rowlog_blocks_decrypted'; +variable_value > 0 +1 +DROP TABLE t1; +SET DEBUG_SYNC= 'RESET'; diff --git a/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption.test b/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption.test new file mode 100644 index 000000000000..7655df774329 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/innodb_row_log_encryption.test @@ -0,0 +1,25 @@ +--source include/have_keyring_vault_plugin.inc + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +--let $keyring_restart_param=restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT --innodb-sort-buffer-size=64k --innodb_encrypt_online_alter_logs=ON + +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config="$KEYRING_CONF_FILE_1"; + +--source include/innodb_row_log_encryption.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/install_keyring_vault-master.opt b/plugin/keyring_vault/tests/mtr/install_keyring_vault-master.opt new file mode 100644 index 000000000000..f896e2bda971 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/install_keyring_vault-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT diff --git a/plugin/keyring_vault/tests/mtr/install_keyring_vault.result b/plugin/keyring_vault/tests/mtr/install_keyring_vault.result new file mode 100644 index 000000000000..1ea51b266794 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/install_keyring_vault.result @@ -0,0 +1,12 @@ +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; +ERROR HY000: Unknown system variable 'keyring_vault_config' +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +Warnings: +Warning 29 File '' not found (Errcode: 2 - No such file or directory) +Warning 42000 keyring_vault initialization failure. Please check the server log. +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; +UNINSTALL PLUGIN keyring_vault; diff --git a/plugin/keyring_vault/tests/mtr/install_keyring_vault.test b/plugin/keyring_vault/tests/mtr/install_keyring_vault.test new file mode 100644 index 000000000000..d60cce54ab5a --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/install_keyring_vault.test @@ -0,0 +1,22 @@ +--source include/have_keyring_vault_plugin.inc + +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error ER_UNKNOWN_SYSTEM_VARIABLE +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1'; + +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_2'; +#cleanup +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1'; +UNINSTALL PLUGIN keyring_vault; diff --git a/plugin/keyring_vault/tests/mtr/is_vault_server_up.inc b/plugin/keyring_vault/tests/mtr/is_vault_server_up.inc new file mode 100644 index 000000000000..3ec5bbbb9889 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/is_vault_server_up.inc @@ -0,0 +1,94 @@ +# In order to check whether Vault server is accessible we try to get list +# of server backends. This list should be short. + +let KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1; +let SERVER_UUID= query_get_value(SELECT @@SERVER_UUID, @@SERVER_UUID, 1); +if (!$CURL_TIMEOUT) +{ + --let CURL_TIMEOUT=4 +} +--perl + use strict; + use IO::File; + my $curl_timeout= $ENV{CURL_TIMEOUT} or die "Need CURL_TIMEOUT"; + my $keyring_conf_file= $ENV{'KEYRING_CONF_FILE'} or die("KEYRING_CONF_FILE not set\n"); + my $server_uuid= $ENV{'SERVER_UUID'} or die("SERVER_UUID not set\n"); + my $token; + my $vault_url; + my $secret_mount_point; + my $vault_ca; + my $CONF_FILE; + open(CONF_FILE, "$keyring_conf_file") or die("Could not open configuration file.\n"); + while (my $row = ) + { + if ($row =~ m/token[ ]*=[ ]*(.*)/) + { + $token=$1; + } + elsif ($row =~ m/vault_url[ ]*=[ ]*(.*)/) + { + $vault_url=$1; + } + elsif ($row =~ m/secret_mount_point[ ]*= [ ]*(.*)/) + { + $secret_mount_point=$1; + } + elsif ($row =~ m/vault_ca[ ]*= [ ]*(.*)/) + { + $vault_ca=$1; + } + } + close(CONF_FILE); + + my $vardir= $ENV{MYSQLTEST_VARDIR} or die "Need MYSQLTEST_VARDIR"; + + if ($token eq "" || $vault_url eq "" || $secret_mount_point eq "") + { + die("Could not read vault credentials from configuration file.\n"); + } + + my $vault_ca_cert_opt= ""; + if ($vault_ca) + { + $vault_ca_cert_opt= "--cacert $vault_ca"; + } + + system(qq#curl -H "X-Vault-Token: $token" --max-time $curl_timeout $vault_ca_cert_opt $vault_url/v1/sys/mounts > $vardir/tmp/curl_result#); + + my $curl_conn_successful = 1; + my $curl_response = 0; + + if (!-s "$vardir/tmp/curl_result") + { + # result file is empty, thus connection could not be established + $curl_conn_successful = 0; + } + else + { + # Vault server has returned errors + open my $file, '<', "$vardir/tmp/curl_result"; + $curl_response = <$file>; + if (index($curl_response, "\"errors\":[\"") != -1) + { + $curl_conn_successful = 0; + } + close $file; + } + unlink "$vardir/tmp/curl_result"; + my $file_name = "$vardir/tmp/mount_list_result.inc"; + my $F = IO::File->new($file_name, 'w') or die "Could not open '$file_name' for writing"; + if (!$curl_conn_successful) + { + if ($curl_response) + { + print $F "--skip Cannot connect to Hashicorp Vault due to : $curl_response"; + } + else + { + print $F "--skip Seems that Hashicorp Vault testing server is down"; + } + } + $F->close(); +EOF + +--source $MYSQLTEST_VARDIR/tmp/mount_list_result.inc diff --git a/plugin/keyring_vault/tests/mtr/key_rotation_qa-master.opt b/plugin/keyring_vault/tests/mtr/key_rotation_qa-master.opt new file mode 100644 index 000000000000..f896e2bda971 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/key_rotation_qa-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT diff --git a/plugin/keyring_vault/tests/mtr/key_rotation_qa.result b/plugin/keyring_vault/tests/mtr/key_rotation_qa.result new file mode 100644 index 000000000000..5c77adcc01c1 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/key_rotation_qa.result @@ -0,0 +1,142 @@ +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[Error\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[Error\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.so'."); +call mtr.add_suppression("\\[Error\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.dll'."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption information in datafile"); +call mtr.add_suppression("\\[Error\\] InnoDB: Operating system error number 2 in a file operation."); +call mtr.add_suppression("\\[Error\\] InnoDB: The error means the system cannot find the path specified."); +call mtr.add_suppression("\\[Error\\] InnoDB: Could not find a valid tablespace file for"); +call mtr.add_suppression("\\[Error\\] InnoDB: If you are installing InnoDB, remember that you must create directories yourself, InnoDB does not create them."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Warning\\] InnoDB: Ignoring tablespace `test/.*` because it could not be opened."); +call mtr.add_suppression("\\[Error\\] InnoDB: Failed to find tablespace for table"); +call mtr.add_suppression("\\[Warning\\] InnoDB: Can't read encryption key from file"); +call mtr.add_suppression("\\[Warning\\] InnoDB: Cannot open table .* from the internal data dictionary of InnoDB though the .frm file for the table exists.*"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Failed to decrpt encryption information, please check key file is not changed!"); +ALTER INSTANCE ROTATE MYISAM MASTER KEY; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'MYISAM MASTER KEY' at line 1 +ALTER INSTANCE ROTATE INNODB; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 +ALTER INSTANCE STORE INNDB; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'STORE INNDB' at line 1 +ALTER INSTANCE ROTATE INNODB SLAVE KEY; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SLAVE KEY' at line 1 +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. + +# restart: +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +ERROR HY000: Function 'keyring_vault' already exists + +SHOW GLOBAL variables LIKE 'early-plugin-load'; +Variable_name Value +SET @@global.early-plugin-load="keyring_vault=keyring_vault.so"; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-plugin-load="keyring_vault=keyring_vault.so"' at line 1 + +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME keyring_vault +PLUGIN_VERSION 1.0 +PLUGIN_STATUS ACTIVE +ALTER INSTANCE ROTATE INNODB MASTER KEY; +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +ALTER INSTANCE ROTATE INNODB MASTER KEY; +CREATE TABLE t2(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t2 SELECT * FROM t1; +SELECT COUNT(*) FROM t2; +COUNT(*) +3 +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +UNINSTALL PLUGIN keyring_vault; +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +# Try to access encrypted table. +SELECT COUNT(*) FROM t1; +COUNT(*) +3 +SELECT * FROM t2; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +CREATE TABLE t10(c1 INT, c2 char(20)) ENGINE = InnoDB; +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME keyring_vault +PLUGIN_VERSION 1.0 +PLUGIN_STATUS ACTIVE +CREATE TABLE t6(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +SELECT * FROM t2; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +UNINSTALL PLUGIN keyring_vault; +# restart: --early-plugin-load=keyring_vault=keyring_vault.so --keyring_vault_config=MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +SELECT * FROM t2; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +# restart: --early-plugin-load=keyring_vault=keyring_vault.so --keyring_vault_config=MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf +SELECT * FROM t1; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +CREATE TABLE t12(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t12 VALUES(2, "ccccchfc"); +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT * FROM t12; +c1 c2 +2 ccccchfc +# restart: +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME keyring_vault +PLUGIN_VERSION 1.0 +PLUGIN_STATUS ACTIVE +CREATE TABLE t11(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +SELECT * FROM t2; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +UNINSTALL PLUGIN keyring_vault; +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +SELECT * FROM t11; +c1 c2 +SELECT COUNT(*) FROM t1; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +CREATE TABLE t4(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +CREATE TABLE t5(c1 INT, c2 char(20)) ENGINE = InnoDB; +INSERT INTO t5 VALUES(100, "without_enc"); +DROP TABLE t11; + +SELECT * FROM t5; +c1 c2 +100 without_enc +DROP TABLE t1,t2,t5,t12,t10; +#End: diff --git a/plugin/keyring_vault/tests/mtr/key_rotation_qa.test b/plugin/keyring_vault/tests/mtr/key_rotation_qa.test new file mode 100644 index 000000000000..41562c2a2583 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/key_rotation_qa.test @@ -0,0 +1,198 @@ +# Wl8821 Testing +# This test will will check parser support for +# ALTER INSTANCE ROTATE MASTER KEY statement +# and key rotation testing. + +--source include/have_keyring_vault_plugin.inc +--source include/no_valgrind_without_big.inc +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[Error\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[Error\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.so'."); +call mtr.add_suppression("\\[Error\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.dll'."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption information in datafile"); +call mtr.add_suppression("\\[Error\\] InnoDB: Operating system error number 2 in a file operation."); +call mtr.add_suppression("\\[Error\\] InnoDB: The error means the system cannot find the path specified."); +call mtr.add_suppression("\\[Error\\] InnoDB: Could not find a valid tablespace file for"); +call mtr.add_suppression("\\[Error\\] InnoDB: If you are installing InnoDB, remember that you must create directories yourself, InnoDB does not create them."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Warning\\] InnoDB: Ignoring tablespace `test/.*` because it could not be opened."); +call mtr.add_suppression("\\[Error\\] InnoDB: Failed to find tablespace for table"); +call mtr.add_suppression("\\[Warning\\] InnoDB: Can't read encryption key from file"); +call mtr.add_suppression("\\[Warning\\] InnoDB: Cannot open table .* from the internal data dictionary of InnoDB though the .frm file for the table exists.*"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Failed to decrpt encryption information, please check key file is not changed!"); + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc + +# Invalid syntax +--error ER_PARSE_ERROR +ALTER INSTANCE ROTATE MYISAM MASTER KEY; +--error ER_PARSE_ERROR +ALTER INSTANCE ROTATE INNODB; +--error ER_PARSE_ERROR +ALTER INSTANCE STORE INNDB; +--error ER_PARSE_ERROR +ALTER INSTANCE ROTATE INNODB SLAVE KEY; + +# When keyring_vault plugin not loaded. +--error ER_CANNOT_FIND_KEY_IN_KEYRING +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +--echo +# Restarting mysql server with keyring_vault plugin. +--let $restart_parameters="restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT" +--let $restart_hide_args= 1 +--source include/restart_mysqld.inc + +--error ER_UDF_EXISTS +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; + +--echo +SHOW GLOBAL variables LIKE 'early-plugin-load'; +# Command-Line option. +--error ER_PARSE_ERROR +SET @@global.early-plugin-load="keyring_vault=keyring_vault.so"; + +--echo +# Check keyring_vault plugin. +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; + +# Roating master key when keyring vault does not have master key for current server. +# It should genereate new master key. +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +# Creating table with encryption. +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +SELECT * FROM t1; + +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +CREATE TABLE t2(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t2 SELECT * FROM t1; +SELECT COUNT(*) FROM t2; + +SELECT * FROM t1; + +# Uninstalling keyring_vault plugin. +UNINSTALL PLUGIN keyring_vault; +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; + +--echo # Try to access encrypted table. +# Cached the tablespace key in memory. +SELECT COUNT(*) FROM t1; +SELECT * FROM t2; + +CREATE TABLE t10(c1 INT, c2 char(20)) ENGINE = InnoDB; + +# Install keyring_vault plugin with different Vault. This means that InnoDB Master key will not be accessible +# and thus it will not be possible to create new encrypted tables + +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_2'; +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; + +--error ER_CANNOT_FIND_KEY_IN_KEYRING +CREATE TABLE t6(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; + +--error ER_CANNOT_FIND_KEY_IN_KEYRING +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +# Although InnoDB Master key is not accessible it should still be possible to select from encrypted tables +# for which tablespace key was already cached by InnoDB +SELECT * FROM t2; + +UNINSTALL PLUGIN keyring_vault; + +# After restart encrypted table/s should be accessible +# when using same Vault server. +let $restart_parameters = restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --keyring_vault_config=$KEYRING_CONF_FILE_1; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR $KEYRING_PLUGIN keyring_vault.so +--source include/restart_mysqld.inc +SELECT * FROM t1; +SELECT * FROM t2; + +# After restart old encrypted tables will not be not accessible, +# since we changed Vault we are connecting to +let $restart_parameters = restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --keyring_vault_config=$KEYRING_CONF_FILE_2; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR $KEYRING_PLUGIN keyring_vault.so +--source include/restart_mysqld.inc +--error ER_CANNOT_FIND_KEY_IN_KEYRING +SELECT * FROM t1; + +CREATE TABLE t12(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t12 VALUES(2, "ccccchfc"); + +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +SELECT * FROM t12; + +# Test keyring_file with early-plugin-load and plugin-load. +--let $restart_parameters="restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --keyring_vault_config=$KEYRING_CONF_FILE_2" +--let $restart_hide_args= 1 +--source include/restart_mysqld.inc + +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; + +CREATE TABLE t11(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; + +# Table will not be accesiable,since we have changed the Vault +--error ER_CANNOT_FIND_KEY_IN_KEYRING +SELECT * FROM t2; + +# Uninstalling keyring_vault plugin. +UNINSTALL PLUGIN keyring_vault; +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; + +SELECT * FROM t11; +# Try to access encrypted table, Which are created before restart. +--error ER_CANNOT_FIND_KEY_IN_KEYRING +SELECT COUNT(*) FROM t1; + +# Creating table after uninstalling keyring_vault plugin. +--error ER_CANNOT_FIND_KEY_IN_KEYRING +CREATE TABLE t4(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; + +# ALTER INSTANCE ROTATE MASTER KEY statement,after UNINSTALL. +--error ER_CANNOT_FIND_KEY_IN_KEYRING +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +# Creating table without encryption. +CREATE TABLE t5(c1 INT, c2 char(20)) ENGINE = InnoDB; +INSERT INTO t5 VALUES(100, "without_enc"); + +# Encrypted table DROP when keyring_vault plugin missing/removed. +DROP TABLE t11; + +--echo +## Try to access non-encrypted table. +SELECT * FROM t5; + +# Cleanup +DROP TABLE t1,t2,t5,t12,t10; + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc +--echo #End: diff --git a/plugin/keyring_vault/tests/mtr/keyring_udf-master.opt b/plugin/keyring_vault/tests/mtr/keyring_udf-master.opt new file mode 100644 index 000000000000..03f78d672822 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_udf-master.opt @@ -0,0 +1,2 @@ +$KEYRING_VAULT_PLUGIN_OPT +$KEYRING_UDF_OPT diff --git a/plugin/keyring_vault/tests/mtr/keyring_udf.result b/plugin/keyring_vault/tests/mtr/keyring_udf.result new file mode 100644 index 000000000000..9ae7be9dcdab --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_udf.result @@ -0,0 +1,635 @@ +call mtr.add_suppression("Error while fetching key: key_id cannot be empty"); +call mtr.add_suppression("Error while removing key: key_id cannot be empty"); +call mtr.add_suppression("Error while generating key: invalid key_type"); +call mtr.add_suppression("Error while generating key: key_id cannot be empty"); +call mtr.add_suppression("Error while storing key: invalid key_type"); +call mtr.add_suppression("Error while storing key: key_id cannot be empty"); +# Check what happens when we have not yet loaded keyring_udf or keyring_vault +create function keyring_key_store returns integer soname 'keyring_udf.so'; +create function keyring_key_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_type_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_length_fetch returns integer soname 'keyring_udf.so'; +create function keyring_key_remove returns integer soname 'keyring_udf.so'; +create function keyring_key_generate returns integer soname 'keyring_udf.so'; +select * from mysql.func where name like '%keyring%' order by name; +name ret dl type +keyring_key_fetch 0 keyring_udf.so function +keyring_key_generate 2 keyring_udf.so function +keyring_key_length_fetch 2 keyring_udf.so function +keyring_key_remove 2 keyring_udf.so function +keyring_key_store 2 keyring_udf.so function +keyring_key_type_fetch 0 keyring_udf.so function +select keyring_key_generate('Rob_AES_128','AES',16); +ERROR HY000: Can't initialize function 'keyring_key_generate'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_store('Rob_AES_128','AES',"0123456789012345"); +ERROR HY000: Can't initialize function 'keyring_key_store'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_fetch('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_type_fetch('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_type_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_length_fetch('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_length_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_remove('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_remove'; This function requires keyring_udf plugin which is not installed. Please install +drop function keyring_key_store; +drop function keyring_key_fetch; +drop function keyring_key_type_fetch; +drop function keyring_key_length_fetch; +drop function keyring_key_remove; +drop function keyring_key_generate; +# Re-starting mysql server with keyring_vault plugin. +# restart: +# Check what happens when we have not yet loaded keyring_udf +create function keyring_key_store returns integer soname 'keyring_udf.so'; +create function keyring_key_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_type_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_length_fetch returns integer soname 'keyring_udf.so'; +create function keyring_key_remove returns integer soname 'keyring_udf.so'; +create function keyring_key_generate returns integer soname 'keyring_udf.so'; +select * from mysql.func where name like '%keyring%' order by name; +name ret dl type +keyring_key_fetch 0 keyring_udf.so function +keyring_key_generate 2 keyring_udf.so function +keyring_key_length_fetch 2 keyring_udf.so function +keyring_key_remove 2 keyring_udf.so function +keyring_key_store 2 keyring_udf.so function +keyring_key_type_fetch 0 keyring_udf.so function +select keyring_key_generate('Rob_AES_128','AES',16); +ERROR HY000: Can't initialize function 'keyring_key_generate'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_store('Rob_AES_128','AES',"0123456789012345"); +ERROR HY000: Can't initialize function 'keyring_key_store'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_fetch('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_type_fetch('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_type_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_length_fetch('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_length_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_remove('Rob_AES_128'); +ERROR HY000: Can't initialize function 'keyring_key_remove'; This function requires keyring_udf plugin which is not installed. Please install +drop function keyring_key_store; +drop function keyring_key_fetch; +drop function keyring_key_type_fetch; +drop function keyring_key_length_fetch; +drop function keyring_key_remove; +drop function keyring_key_generate; +INSTALL PLUGIN keyring_udf SONAME 'keyring_udf.so'; +# Check if keys are available after server restart +create function keyring_key_store returns integer soname 'keyring_udf.so'; +create function keyring_key_generate returns integer soname 'keyring_udf.so'; +create function keyring_key_remove returns integer soname 'keyring_udf.so'; +create function keyring_key_fetch returns string soname 'keyring_udf.so'; +select keyring_key_generate('key_id','AES',16); +keyring_key_generate('key_id','AES',16) +1 +select keyring_key_store('key_id','AES',"0123456789012345"); +keyring_key_store('key_id','AES',"0123456789012345") +1 +# Check if checking keyring_vault configuration file works fine +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +select keyring_key_generate('key_id','AES',16); +keyring_key_generate('key_id','AES',16) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +16 +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +16 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +16 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +drop function keyring_key_fetch; +drop function keyring_key_remove; +drop function keyring_key_generate; +# Re-starting mysql server with keyring_vault plugin. +# restart: +create function keyring_key_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_type_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_length_fetch returns integer soname 'keyring_udf.so'; +create function keyring_key_remove returns integer soname 'keyring_udf.so'; +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +16 +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +16 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +drop function keyring_key_fetch; +drop function keyring_key_type_fetch; +drop function keyring_key_length_fetch; +drop function keyring_key_remove; +drop function keyring_key_store; +# Test cases for keyring_udf and keyring_vault plugins loaded +create function keyring_key_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_type_fetch returns string soname 'keyring_udf.so'; +create function keyring_key_length_fetch returns integer soname 'keyring_udf.so'; +create function keyring_key_remove returns integer soname 'keyring_udf.so'; +create function keyring_key_generate returns integer soname 'keyring_udf.so'; +select keyring_key_fetch('Key_1','AES'); +ERROR HY000: Can't initialize function 'keyring_key_fetch'; Mismatch in number of arguments to the function. +select keyring_key_type_fetch('Key_1','AES'); +ERROR HY000: Can't initialize function 'keyring_key_type_fetch'; Mismatch in number of arguments to the function. +select keyring_key_length_fetch('Key_1','AES'); +ERROR HY000: Can't initialize function 'keyring_key_length_fetch'; Mismatch in number of arguments to the function. +select keyring_key_remove('Key_1','AES'); +ERROR HY000: Can't initialize function 'keyring_key_remove'; Mismatch in number of arguments to the function. +select keyring_key_generate('Key_1'); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch in number of arguments to the function. +select keyring_key_generate('Key_1','AES'); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch in number of arguments to the function. +select keyring_key_generate('Key_1',''); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch in number of arguments to the function. +select keyring_key_fetch('Key_1',NULL); +ERROR HY000: Can't initialize function 'keyring_key_fetch'; Mismatch in number of arguments to the function. +select keyring_key_fetch(1); +ERROR HY000: Can't initialize function 'keyring_key_fetch'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_fetch(NULL); +ERROR HY000: Can't initialize function 'keyring_key_fetch'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_type_fetch(1); +ERROR HY000: Can't initialize function 'keyring_key_type_fetch'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_type_fetch(NULL); +ERROR HY000: Can't initialize function 'keyring_key_type_fetch'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_length_fetch(1); +ERROR HY000: Can't initialize function 'keyring_key_length_fetch'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_length_fetch(NULL); +ERROR HY000: Can't initialize function 'keyring_key_length_fetch'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_generate('Key_1','AES','123'); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. An integer argument is expected for key length. +select keyring_key_generate(NULL,'AES','123'); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_generate('Key_1',NULL,'123'); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. A string argument is expected for key type. +select keyring_key_generate('Key_1','AES',NULL); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. An integer argument is expected for key length. +select keyring_key_generate(NULL,NULL,NULL); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_generate(1,'AES',123); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_generate('Key_1',123,'123'); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. A string argument is expected for key type. +select keyring_key_generate('Key_invalid_key_type',123,123); +ERROR HY000: Can't initialize function 'keyring_key_generate'; Mismatch encountered. A string argument is expected for key type. +select keyring_key_remove(1); +ERROR HY000: Can't initialize function 'keyring_key_remove'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_remove(NULL); +ERROR HY000: Can't initialize function 'keyring_key_remove'; Mismatch encountered. A string argument is expected for key id. +select PLUGIN_NAME,PLUGIN_AUTHOR from information_schema.plugins where PLUGIN_NAME like '%keyring%'; +PLUGIN_NAME PLUGIN_AUTHOR +keyring_vault Percona +keyring_udf Oracle Corporation +select keyring_key_generate('key_id','AES',16); +keyring_key_generate('key_id','AES',16) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +16 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +16 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id', 'AES', 24); +keyring_key_generate('key_id', 'AES', 24) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +24 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +24 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','AES',32); +keyring_key_generate('key_id','AES',32) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +32 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +32 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','RSA',128); +keyring_key_generate('key_id','RSA',128) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +128 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +RSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +128 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','RSA',256); +keyring_key_generate('key_id','RSA',256) +1 +select keyring_key_fetch('key_id') into @x; +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +RSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +256 +select LENGTH(@x); +LENGTH(@x) +256 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','RSA',512); +keyring_key_generate('key_id','RSA',512) +1 +select keyring_key_fetch('key_id') into @x; +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +RSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +512 +select LENGTH(@x); +LENGTH(@x) +512 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','DSA',128); +keyring_key_generate('key_id','DSA',128) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +128 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +DSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +128 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','DSA',256); +keyring_key_generate('key_id','DSA',256) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +256 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +DSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +256 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','DSA',384); +keyring_key_generate('key_id','DSA',384) +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +384 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +DSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +384 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_fetch('key') into @x; +select @x; +@x +NULL +select keyring_key_type_fetch('key') into @x; +select @x; +@x +NULL +select keyring_key_length_fetch('key') into @x; +select @x; +@x +NULL +#Testing with AES_ENCRYPT/AES_DECRYPT +select keyring_key_generate('$key_id','AES',16); +keyring_key_generate('$key_id','AES',16) +1 +select AES_ENCRYPT('secret message', keyring_key_fetch('$key_id')) into @cipher; +select AES_DECRYPT(@cipher, keyring_key_fetch('$key_id')); +AES_DECRYPT(@cipher, keyring_key_fetch('$key_id')) +secret message +select keyring_key_remove('$key_id'); +keyring_key_remove('$key_id') +1 +select keyring_key_fetch('Rob1'); +keyring_key_fetch('Rob1') +NULL +select keyring_key_type_fetch('Rob1'); +keyring_key_type_fetch('Rob1') +NULL +select keyring_key_length_fetch('Rob1'); +keyring_key_length_fetch('Rob1') +NULL +select keyring_key_fetch('') into @x; +ERROR HY000: Function 'keyring_key_fetch' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select keyring_key_length_fetch('') into @x; +ERROR HY000: Function 'keyring_key_length_fetch' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select keyring_key_type_fetch('') into @x; +ERROR HY000: Function 'keyring_key_type_fetch' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select keyring_key_remove('Rob_not_existing') into @x; +ERROR HY000: Function 'keyring_key_remove' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select keyring_key_remove('') into @x; +ERROR HY000: Function 'keyring_key_remove' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select keyring_key_generate('Wrong_type','xxx', 16) into @x; +ERROR HY000: Function 'keyring_key_generate' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select keyring_key_generate('','AES', 16) into @x; +ERROR HY000: Function 'keyring_key_generate' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +# Testing privileges +select keyring_key_generate('roots_key_id', 'AES',16); +keyring_key_generate('roots_key_id', 'AES',16) +1 +create definer=root@localhost procedure shared_key() select keyring_key_fetch('$roots_key_id'); +CREATE USER user_execute_test@localhost; +select keyring_key_generate('Rob_DSA_no_privilege','DSA',384); +ERROR HY000: Can't initialize function 'keyring_key_generate'; The user is not privileged to execute this function. User needs to have EXECUTE +GRANT EXECUTE ON *.* TO 'user_execute_test'@'localhost'; +select keyring_key_generate('key_id','DSA',384); +keyring_key_generate('key_id','DSA',384) +1 +select keyring_key_generate('key_id','DSA',384); +keyring_key_generate('key_id','DSA',384) +1 +#Check if user you can access another user's keys +select keyring_key_remove('roots_key_id'); +ERROR HY000: Function 'keyring_key_remove' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +#Check that the user is able to access the key via procedure +call shared_key(); +keyring_key_fetch('$roots_key_id') +# +DROP PROCEDURE shared_key; +#Check if a user can create a key with id that already exist but belongs +#to another user. +select keyring_key_generate('key_id','DSA',384); +keyring_key_generate('key_id','DSA',384) +1 +#Check if user with larger privileges can access another user's key +select keyring_key_remove('key_id'); +ERROR HY000: Function 'keyring_key_remove' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +#Cleanup +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_remove('roots_key_id'); +keyring_key_remove('roots_key_id') +1 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +DROP USER 'user_execute_test'@'localhost'; +create function keyring_key_store returns integer soname 'keyring_udf.so'; +select keyring_key_store('Key_1'); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch in number of arguments to the function. +select keyring_key_store('Key_1','AES'); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch in number of arguments to the function. +select keyring_key_store('Key_1','AES',123); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key. +select keyring_key_store(NULL,'AES',123); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_store('Key_1',NULL,123); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key type. +select keyring_key_store('Key_1','AES',NULL); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key. +select keyring_key_store(NULL,NULL,NULL); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_store(1234,NULL,'53247@#$%^'); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_store(1,'AES','123'); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key id. +select keyring_key_store('Key_1',123,'123'); +ERROR HY000: Can't initialize function 'keyring_key_store'; Mismatch encountered. A string argument is expected for key type. +select keyring_key_store('key_id','AES',"0123456789012345"); +keyring_key_store('key_id','AES',"0123456789012345") +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +16 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +16 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_store('key_id','AES',"012345678901234567890%@3"); +keyring_key_store('key_id','AES',"012345678901234567890%@3") +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +24 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +24 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_store('key_id','AES',"01234567890123456789012345678901"); +keyring_key_store('key_id','AES',"01234567890123456789012345678901") +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +32 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +32 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_store('key_id','RSA',"01234567890123456789012345678901234567890123456789012345678901230123456789012345678901234567890123456789012345678901234567890123"); +keyring_key_store('key_id','RSA',"01234567890123456789012345678901234567890123456789012345678901230123456789012345678901234567890123456789012345678901234567890123") +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +128 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +RSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +128 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_store('key_id','DSA',"01234567890123456789012345678901234567890123456789012345678901230123456789012345678901234567890123456789012345678901234567890123"); +keyring_key_store('key_id','DSA',"01234567890123456789012345678901234567890123456789012345678901230123456789012345678901234567890123456789012345678901234567890123") +1 +select keyring_key_fetch('key_id') into @x; +select LENGTH(@x); +LENGTH(@x) +128 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +DSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +128 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_store('Wrong_type','xxx', '0123456789012345') into @x; +ERROR HY000: Function 'keyring_key_store' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select keyring_key_store('','AES', '0123456789012345') into @x; +ERROR HY000: Function 'keyring_key_store' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select @x; +@x +NULL +select repeat('abcdefgh', 2049) into @x; +select keyring_key_store('long_key', 'AES', @x); +ERROR HY000: Function 'keyring_key_store' failed because key length is too long. +select repeat('abcdefgh', 17) into @x; +select keyring_key_generate('key_id','AES',64); +keyring_key_generate('key_id','AES',64) +1 +set @x= keyring_key_fetch('key_id'); +select LENGTH(@x); +LENGTH(@x) +64 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +AES +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +64 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +select keyring_key_generate('key_id','DSA',512); +keyring_key_generate('key_id','DSA',512) +1 +set @x= keyring_key_fetch('key_id'); +select LENGTH(@x); +LENGTH(@x) +512 +select keyring_key_type_fetch('key_id'); +keyring_key_type_fetch('key_id') +DSA +select keyring_key_length_fetch('key_id'); +keyring_key_length_fetch('key_id') +512 +select keyring_key_remove('key_id'); +keyring_key_remove('key_id') +1 +UNINSTALL PLUGIN keyring_vault; +# Test cases for keyring_udf loaded and keyring_vault plugin not loaded +#The keyring plugin was uninstalled, the keyring_udf functions should fail with +#appropriate error message +select keyring_key_store('Rob_uninstalled','AES','123'); +ERROR HY000: Function 'keyring_key_store' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select keyring_key_fetch('Rob_uninstalled'); +ERROR HY000: Function 'keyring_key_fetch' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select keyring_key_type_fetch('Rob_uninstalled'); +ERROR HY000: Function 'keyring_key_type_fetch' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select keyring_key_length_fetch('Rob_uninstalled'); +ERROR HY000: Function 'keyring_key_length_fetch' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select keyring_key_remove('Rob_uninstalled'); +ERROR HY000: Function 'keyring_key_remove' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +select keyring_key_generate('Rob_uninstalled_generate','DSA',128); +ERROR HY000: Function 'keyring_key_generate' failed because underlying keyring service returned an error. Please check if a keyring plugin is installed and that provided arguments are valid for the keyring you are using. +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +UNINSTALL PLUGIN keyring_udf; +# Test cases for keyring_udf not loaded and keyring_vault plugin loaded +select keyring_key_store('Rob_uninstalled','AES','123'); +ERROR HY000: Can't initialize function 'keyring_key_store'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_fetch('Rob_uninstalled'); +ERROR HY000: Can't initialize function 'keyring_key_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_type_fetch('Rob_uninstalled'); +ERROR HY000: Can't initialize function 'keyring_key_type_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_length_fetch('Rob_uninstalled'); +ERROR HY000: Can't initialize function 'keyring_key_length_fetch'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_remove('Rob_uninstalled'); +ERROR HY000: Can't initialize function 'keyring_key_remove'; This function requires keyring_udf plugin which is not installed. Please install +select keyring_key_generate('Rob_uninstalled_generate','DSA',128); +ERROR HY000: Can't initialize function 'keyring_key_generate'; This function requires keyring_udf plugin which is not installed. Please install +drop function keyring_key_fetch; +drop function keyring_key_remove; +drop function keyring_key_generate; +drop function keyring_key_type_fetch; +drop function keyring_key_length_fetch; diff --git a/plugin/keyring_vault/tests/mtr/keyring_udf.test b/plugin/keyring_vault/tests/mtr/keyring_udf.test new file mode 100644 index 000000000000..af4d27710323 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_udf.test @@ -0,0 +1,177 @@ +--source include/have_keyring_vault_plugin.inc +--source include/have_keyring_udf_plugin.inc + +call mtr.add_suppression("Error while fetching key: key_id cannot be empty"); +call mtr.add_suppression("Error while removing key: key_id cannot be empty"); +call mtr.add_suppression("Error while generating key: invalid key_type"); +call mtr.add_suppression("Error while generating key: key_id cannot be empty"); +call mtr.add_suppression("Error while storing key: invalid key_type"); +call mtr.add_suppression("Error while storing key: key_id cannot be empty"); + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc + +let server_uuid = query_get_value(SELECT @@SERVER_UUID, @@SERVER_UUID, 1); + +--echo # Check what happens when we have not yet loaded keyring_udf or keyring_vault +--source include/keyring_udf_missing_plugin.inc + +# We need both plugins - keyring_vault and keyring_udf in one place - to be able to load both plugins +if ($KEYRING_UDF_DIR != $KEYRING_PLUGIN_DIR) +{ + --replace_regex /\.dll/.so/ + --error 0,1 + --remove_file $KEYRING_UDF_DIR/$KEYRING_VAULT_PLUGIN + --replace_regex /\.dll/.so/ + --copy_file $KEYRING_VAULT_PLUGIN_DIR/$KEYRING_VAULT_PLUGIN $KEYRING_UDF_DIR/$KEYRING_VAULT_PLUGIN +} +--echo # Re-starting mysql server with keyring_vault plugin. +--let $restart_hide_args=1 +--let $restart_parameters="restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1" +--source include/restart_mysqld.inc + +--echo # Check what happens when we have not yet loaded keyring_udf +--source include/keyring_udf_missing_plugin.inc + +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_udf SONAME '$KEYRING_UDF'; +--echo # Check if keys are available after server restart +--replace_regex /\.dll/.so/ +eval create function keyring_key_store returns integer soname '$KEYRING_UDF'; +--replace_regex /\.dll/.so/ +eval create function keyring_key_generate returns integer soname '$KEYRING_UDF'; +--replace_regex /\.dll/.so/ +eval create function keyring_key_remove returns integer soname '$KEYRING_UDF'; +--replace_regex /\.dll/.so/ +eval create function keyring_key_fetch returns string soname '$KEYRING_UDF'; + +let key_id = `select CONCAT('Rob_AES_128', '$server_uuid')`; +--replace_result $key_id key_id +eval select keyring_key_generate('$key_id','AES',16); +let key_id = `select CONCAT('Rob_AES_128_store', '$server_uuid')`; +--replace_result $key_id key_id +eval select keyring_key_store('$key_id','AES',"0123456789012345"); + +--echo # Check if checking keyring_vault configuration file works fine + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_2'; +--replace_result $key_id key_id +eval select keyring_key_generate('$key_id','AES',16); +--replace_result $key_id key_id +eval select keyring_key_fetch('$key_id') into @x; +select LENGTH(@x); + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1'; +--replace_result $key_id key_id +eval select keyring_key_fetch('$key_id') into @x; +select LENGTH(@x); +--replace_result $key_id key_id +eval select keyring_key_remove('$key_id'); + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_2'; +--replace_result $key_id key_id +eval select keyring_key_fetch('$key_id') into @x; +select LENGTH(@x); +--replace_result $key_id key_id +eval select keyring_key_remove('$key_id'); + +drop function keyring_key_fetch; +drop function keyring_key_remove; +drop function keyring_key_generate; + +--echo # Re-starting mysql server with keyring_vault plugin. +--let $restart_hide_args=1 +--let $restart_parameters="restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1" +--source include/restart_mysqld.inc + +--replace_regex /\.dll/.so/ +eval create function keyring_key_fetch returns string soname '$KEYRING_UDF'; +--replace_regex /\.dll/.so/ +eval create function keyring_key_type_fetch returns string soname '$KEYRING_UDF'; +--replace_regex /\.dll/.so/ +eval create function keyring_key_length_fetch returns integer soname '$KEYRING_UDF'; +--replace_regex /\.dll/.so/ +eval create function keyring_key_remove returns integer soname '$KEYRING_UDF'; + +let key_id = `select CONCAT('Rob_AES_128', '$server_uuid')`; +--replace_result $key_id key_id +eval select keyring_key_fetch('$key_id') into @x; +select LENGTH(@x); +--replace_result $key_id key_id +eval select keyring_key_length_fetch('$key_id'); +--replace_result $key_id key_id +eval select keyring_key_type_fetch('$key_id'); +--replace_result $key_id key_id +eval select keyring_key_remove('$key_id'); + +drop function keyring_key_fetch; +drop function keyring_key_type_fetch; +drop function keyring_key_length_fetch; +drop function keyring_key_remove; +drop function keyring_key_store; + +--echo # Test cases for keyring_udf and keyring_vault plugins loaded +--source include/keyring_udf_keyring_plugin_loaded.inc +--source include/keyring_udf_keyring_plugin_loaded_store_operations.inc + +# Tests that are not available in main keyring_udf_keyring_plugin_loaded +# Generate AES_512 +let key_id = `select CONCAT('Rob_AES_512', '$server_uuid')`; +--replace_result $key_id key_id +eval select keyring_key_generate('$key_id','AES',64); +--replace_result $key_id key_id +eval set @x= keyring_key_fetch('$key_id'); +eval select LENGTH(@x); +--replace_result $key_id key_id +eval select keyring_key_type_fetch('$key_id'); +--replace_result $key_id key_id +eval select keyring_key_length_fetch('$key_id'); +--replace_result $key_id key_id +eval select keyring_key_remove('$key_id'); +# Generate DES 512 +let key_id = `select CONCAT('Rob_DSA_4096', '$server_uuid')`; +--replace_result $key_id key_id +eval select keyring_key_generate('$key_id','DSA',512); +--replace_result $key_id key_id +eval set @x= keyring_key_fetch('$key_id'); +select LENGTH(@x); +--replace_result $key_id key_id +eval select keyring_key_type_fetch('$key_id'); +--replace_result $key_id key_id +eval select keyring_key_length_fetch('$key_id'); +--replace_result $key_id key_id +eval select keyring_key_remove('$key_id'); + +UNINSTALL PLUGIN keyring_vault; +--echo # Test cases for keyring_udf loaded and keyring_vault plugin not loaded +--source include/keyring_udf_keyring_plugin_not_loaded.inc + +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; + +UNINSTALL PLUGIN keyring_udf; +--echo # Test cases for keyring_udf not loaded and keyring_vault plugin loaded +--source include/keyring_udf_keyring_udf_plugin_not_loaded.inc + +drop function keyring_key_fetch; +drop function keyring_key_remove; +drop function keyring_key_generate; +drop function keyring_key_type_fetch; +drop function keyring_key_length_fetch; + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_config.result b/plugin/keyring_vault/tests/mtr/keyring_vault_config.result new file mode 100644 index 000000000000..10aafc191921 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_config.result @@ -0,0 +1,18 @@ +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '../../../../../bad_dir/bad_keyring_vault.conf' not found"); +# restart:--plugin_load=keyring_vault=keyring_vault.so --loose-keyring_vault_config=MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf KEYRING_VAULT_PLUGIN_OPT +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +SET @@global.keyring_vault_config= 'MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf +SET @@global.keyring_vault_config= '../../../../../bad_dir/bad_keyring_vault.conf'; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of '../../../../../bad_dir/bad_keyring_vault.conf' +SET @@global.keyring_vault_config=NULL; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of 'NULL' +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf +SET @@global.keyring_vault_config= 'MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_config.test b/plugin/keyring_vault/tests/mtr/keyring_vault_config.test new file mode 100644 index 000000000000..a9002409941a --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_config.test @@ -0,0 +1,28 @@ +--source include/have_keyring_vault_plugin.inc + +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '../../../../../bad_dir/bad_keyring_vault.conf' not found"); + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR $KEYRING_PLUGIN keyring_vault.so $KEYRING_VAULT_PLUGIN_OPT KEYRING_VAULT_PLUGIN_OPT +--let $restart_parameters="restart:$KEYRING_VAULT_PLUGIN_LOAD --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT" +--source include/restart_mysqld.inc + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +SELECT @@global.keyring_vault_config; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config= '$KEYRING_CONF_FILE_2'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +SELECT @@global.keyring_vault_config; +--Error ER_WRONG_VALUE_FOR_VAR +SET @@global.keyring_vault_config= '../../../../../bad_dir/bad_keyring_vault.conf'; +--Error ER_WRONG_VALUE_FOR_VAR +SET @@global.keyring_vault_config=NULL; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +SELECT @@global.keyring_vault_config; + +# cleanup +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config= '$KEYRING_CONF_FILE_1'; diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa-master.opt b/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa-master.opt new file mode 100644 index 000000000000..f896e2bda971 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa.result b/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa.result new file mode 100644 index 000000000000..4490a6d403e4 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa.result @@ -0,0 +1,58 @@ +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'Could not read secret_mount_point from the configuration file.'"); +call mtr.add_suppression("\\[Error\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[Error\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.so'."); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not retrieve list of keys from Vault. Vault has returned the following error\\(s\\): \\[\"permission denied\"\\]'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Error while loading keyring content. The keyring might be malformed'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '.*' not found"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not read file with credentials.'"); +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +Warnings: +Warning 29 File '' not found (Errcode: 2 - No such file or directory) +Warning 42000 keyring_vault initialization failure. Please check the server log. +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME keyring_vault +PLUGIN_VERSION 1.0 +PLUGIN_STATUS ACTIVE + +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf +SET @@global.keyring_vault_config=''; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of '' +SET @@global.keyring_vault_config='#^$^@E&('; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of '#^$^@E&(' +SET @@global.keyring_vault_config=1; +ERROR 42000: Incorrect argument type to variable 'keyring_vault_config' +SET @@global.keyring_vault_config='/'; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of '/' +SET @@session.keyring_vault_config='MYSQL_TMP_DIR/new_keyring_vault_config'; +ERROR HY000: Variable 'keyring_vault_config' is a GLOBAL variable and should be set with SET GLOBAL +SET @@local.keyring_vault_config='MYSQL_TMP_DIR/new_keyring_vault_config'; +ERROR HY000: Variable 'keyring_vault_config' is a GLOBAL variable and should be set with SET GLOBAL +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/keyring_vault_invalid_token.conf'; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of 'MYSQLTEST_VARDIR/keyring_vault_invalid_token.conf' +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +SET @@global.keyring_vault_config='MYSQL_test_invalid/dir/'; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of 'MYSQL_test_invalid/dir/' +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf + +UNINSTALL PLUGIN keyring_vault; +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +DROP TABLE t1; + +#End: diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa.test b/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa.test new file mode 100644 index 000000000000..6d77c9da7c8d --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_config_qa.test @@ -0,0 +1,92 @@ +# Keyring_vault plugin and keyring_vault_config variable testing. + +--source include/have_keyring_vault_plugin.inc + +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'Could not read secret_mount_point from the configuration file.'"); +call mtr.add_suppression("\\[Error\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[Error\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.so'."); + +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); + +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not retrieve list of keys from Vault. Vault has returned the following error\\(s\\): \\[\"permission denied\"\\]'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Error while loading keyring content. The keyring might be malformed'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '.*' not found"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not read file with credentials.'"); + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +# Installing keyring plugin. +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; +# Check keyring plugin +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +--echo +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +SELECT @@global.keyring_vault_config; +# Creating table with encryption. +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; + +# Setting keyring_vault_config value to file. +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_2'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +SELECT @@global.keyring_vault_config; + +# Invalid values +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.keyring_vault_config=''; +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.keyring_vault_config='#^$^@E&('; +--error ER_WRONG_TYPE_FOR_VAR +SET @@global.keyring_vault_config=1; +#This statement generates Could not read secret_mount_point error +#As location can be opened for reading +--error ER_WRONG_VALUE_FOR_VAR +SET @@global.keyring_vault_config='/'; +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--error ER_GLOBAL_VARIABLE +eval SET @@session.keyring_vault_config='$MYSQL_TMP_DIR/new_keyring_vault_config'; +--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--error ER_GLOBAL_VARIABLE +eval SET @@local.keyring_vault_config='$MYSQL_TMP_DIR/new_keyring_vault_config'; + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error ER_WRONG_VALUE_FOR_VAR +eval SET @@global.keyring_vault_config='$KEYRING_INVALID_TOKEN_CONF_FILE'; + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +SELECT @@global.keyring_vault_config; + +# Setting keyring_vault_config value to dir. +--Error ER_WRONG_VALUE_FOR_VAR +SET @@global.keyring_vault_config='MYSQL_test_invalid/dir/'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +SELECT @@global.keyring_vault_config; +--echo + +UNINSTALL PLUGIN keyring_vault; +# Check keyring plugin +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; + +# Cleanup +DROP TABLE t1; + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--echo +--echo #End: diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait-master.opt b/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait-master.opt new file mode 100644 index 000000000000..5b919d3093ba --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait-master.opt @@ -0,0 +1 @@ +--thread-handling=pool-of-threads diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait.result b/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait.result new file mode 100644 index 000000000000..ae826396aac4 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait.result @@ -0,0 +1,11 @@ +# restart: --early-plugin-load=keyring_vault=keyring_vault.so --keyring_vault_config=MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf KEYRING_VAULT_PLUGIN_OPT +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE=InnoDB; +INSERT INTO t1 VALUES(1,"aaaaa"); +UNINSTALL PLUGIN keyring_vault; +SET SESSION debug="+d,vault_network_lag"; +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +SELECT * FROM t1; +c1 c2 +1 aaaaa +SET SESSION debug="-d,vault_network_lag"; +DROP TABLE t1; diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait.test b/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait.test new file mode 100644 index 000000000000..64aef6d381c5 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_thd_wait.test @@ -0,0 +1,36 @@ +--source include/have_keyring_vault_plugin.inc +--source include/have_debug.inc + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +let $restart_parameters = restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR $KEYRING_PLUGIN keyring_vault.so $KEYRING_VAULT_PLUGIN_OPT KEYRING_VAULT_PLUGIN_OPT +--source include/restart_mysqld.inc + +# Create encrypted table to be sure there is Innodb Master Key in keyring, +# which will need to be fetched from keyring on keyring_vault initialization +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE=InnoDB; +INSERT INTO t1 VALUES(1,"aaaaa"); + +UNINSTALL PLUGIN keyring_vault; + +SET SESSION debug="+d,vault_network_lag"; +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; + +SELECT * FROM t1; + +# cleanup +SET SESSION debug="-d,vault_network_lag"; +DROP TABLE t1; + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_timeout-master.opt b/plugin/keyring_vault/tests/mtr/keyring_vault_timeout-master.opt new file mode 100644 index 000000000000..f896e2bda971 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_timeout-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_timeout.result b/plugin/keyring_vault/tests/mtr/keyring_vault_timeout.result new file mode 100644 index 000000000000..50d31838a38e --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_timeout.result @@ -0,0 +1,21 @@ +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'CURL returned this error code: 28 with error message : Connection timed out after"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not retrieve list of keys from Vault.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Error while loading keyring content. The keyring might be malformed'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'CURL returned this error code: 28 with error message : connect\\(\\) timed out!'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +Warnings: +Warning 29 File '' not found (Errcode: 2 - No such file or directory) +Warning 42000 keyring_vault initialization failure. Please check the server log. +include/assert.inc [Default vaule of keyring_vault_timeout should be 15] +SET @@GLOBAL.keyring_vault_timeout=15; +SET @@GLOBAL.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault_incorrect_server.conf'; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of 'MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault_incorrect_server.conf' +include/assert.inc [Connection time must be bigger than keyring_vault_timeout] +SET @@GLOBAL.keyring_vault_timeout=5; +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault_incorrect_server.conf'; +ERROR 42000: Variable 'keyring_vault_config' can't be set to the value of 'MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault_incorrect_server.conf' +include/assert.inc [Connection time must be bigger than keyring_vault_timeout] +UNINSTALL PLUGIN keyring_vault; diff --git a/plugin/keyring_vault/tests/mtr/keyring_vault_timeout.test b/plugin/keyring_vault/tests/mtr/keyring_vault_timeout.test new file mode 100644 index 000000000000..c1f9b4bbfec6 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/keyring_vault_timeout.test @@ -0,0 +1,65 @@ +--source include/have_keyring_vault_plugin.inc + +# PS-298: keyring_vault's timeout should be configurable + +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'CURL returned this error code: 28 with error message : Connection timed out after"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not retrieve list of keys from Vault.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Error while loading keyring content. The keyring might be malformed'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'CURL returned this error code: 28 with error message : connect\\(\\) timed out!'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); + +--let $KEYRING_CONF_FILE_1=$MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault_incorrect_server.conf +--let $KEYRING_CONF_TEMPLATE_FILE=$MYSQL_TEST_DIR/std_data/keyring_vault_confs/keyring_vault_incorrect_server.conf +--let $KEYRING_CONF_FILE_TO_GENERATE=$KEYRING_CONF_FILE_1 +--source generate_conf_file.inc + +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; + +--let $assert_text= Default vaule of keyring_vault_timeout should be 15 +--let $assert_cond= "[SELECT @@keyring_vault_timeout = 15]" = 1 +--source include/assert.inc + +SET @@GLOBAL.keyring_vault_timeout=15; + +let $connection_time_start= `SELECT UNIX_TIMESTAMP()`; + +# Here, we are trying to set keyring_vault_config variable to existing, but not accessible address. +# As the connection is not possible we should receive connection timeout - according the to value of +# keyring_vault_timeout variable. + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error ER_WRONG_VALUE_FOR_VAR +eval SET @@GLOBAL.keyring_vault_config='$KEYRING_CONF_FILE_1'; + +let $connection_time_end= `SELECT UNIX_TIMESTAMP()`; +let $connection_time= `SELECT $connection_time_end - $connection_time_start`; + +# Here, we check if connection timeouted correctly - we should receive timeout after +# keyring_vault_timeout seconds. We do not check the upper bound as we cannot +# estimate how much time connection can take in build system (Jenkins). + +let $timeout= `SELECT @@GLOBAL.keyring_vault_timeout`; +let $assert_text= Connection time must be bigger than keyring_vault_timeout; +let $assert_cond= $timeout <= $connection_time; +source include/assert.inc; + +SET @@GLOBAL.keyring_vault_timeout=5; + +let $connection_time_start= `SELECT UNIX_TIMESTAMP()`; + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +--error ER_WRONG_VALUE_FOR_VAR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1'; + +let $connection_time_end= `SELECT UNIX_TIMESTAMP()`; +let $connection_time= `SELECT $connection_time_end - $connection_time_start`; + +let $timeout= `SELECT @@GLOBAL.keyring_vault_timeout`; +let $assert_text= Connection time must be bigger than keyring_vault_timeout; +let $assert_cond= $timeout <= $connection_time; +source include/assert.inc; + +UNINSTALL PLUGIN keyring_vault; diff --git a/plugin/keyring_vault/tests/mtr/mount_point_service.inc b/plugin/keyring_vault/tests/mtr/mount_point_service.inc new file mode 100644 index 000000000000..1f72a05f96e5 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/mount_point_service.inc @@ -0,0 +1,68 @@ +# Creates or deletes secret mount point specified in keyring_vault configuration file +# Mount point is : +# - created when MOUNT_POINT_SERVICE_OP is set to CREATE +# - deleted when MOUNT_POINT_SERVICE_OP is set to DELETE + +# The sourcing test needs to set $KEYRING_CONF_FILE variable to +# the location of keyring_vault configuration file and +# MOUNT_POINT_SERVICE_OP variable to CREATE or DELETE +let KEYRING_CONF_FILE=$KEYRING_CONF_FILE; +let SERVER_UUID= query_get_value(SELECT @@SERVER_UUID, @@SERVER_UUID, 1); +let MOUNT_POINT_SERVICE_OP=$MOUNT_POINT_SERVICE_OP; + +--perl + use strict; + use MIME::Base64 qw( decode_base64 ); + my $keyring_conf_file= $ENV{'KEYRING_CONF_FILE'} or die("KEYRING_CONF_FILE not set\n"); + my $server_uuid= $ENV{'SERVER_UUID'} or die("SERVER_UUID not set\n"); + my $mount_point_service_op=$ENV{'MOUNT_POINT_SERVICE_OP'}; + my $token; + my $vault_url; + my $secret_mount_point; + my $vault_ca; + my $CONF_FILE; + open(CONF_FILE, "$keyring_conf_file") or die("Could not open configuration file.\n"); + while (my $row = ) + { + if ($row =~ m/token[ ]*=[ ]*(.*)/) + { + $token=$1; + } + elsif ($row =~ m/vault_url[ ]*=[ ]*(.*)/) + { + $vault_url=$1; + } + elsif ($row =~ m/secret_mount_point[ ]*= [ ]*(.*)/) + { + $secret_mount_point=$1; + } + elsif ($row =~ m/vault_ca[ ]*= [ ]*(.*)/) + { + $vault_ca=$1; + } + } + close(CONF_FILE); + if ($token eq "" || $vault_url eq "" || $secret_mount_point eq "") + { + die("Could not read vault credentials from configuration file.\n"); + } + + my $vault_ca_cert_opt= ""; + if ($vault_ca) + { + $vault_ca_cert_opt= "--cacert $vault_ca"; + } + + if ($mount_point_service_op eq 'CREATE') + { + system(qq#curl -H "X-Vault-Token: $token" $vault_ca_cert_opt --data '{"type":"generic"}' --request POST $vault_url/v1/sys/mounts/$secret_mount_point#); + } + elsif ($mount_point_service_op eq 'DELETE') + { + system(qq#curl -H "X-Vault-Token: $token" $vault_ca_cert_opt -X DELETE $vault_url/v1/sys/mounts/$secret_mount_point#); + } + else + { + die("Mount point should be either created or deleted. The resulting operation is no-op"); + } +EOF diff --git a/plugin/keyring_vault/tests/mtr/plugin.defs b/plugin/keyring_vault/tests/mtr/plugin.defs new file mode 100644 index 000000000000..fbaff1d3aa86 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/plugin.defs @@ -0,0 +1,35 @@ +# Definition file for plugins. +# +# [,...] +# +# The following variables will be set for a plugin, where PLUGVAR +# represents the variable name given as the 3rd item +# +# PLUGVAR: name of plugin file including extension .so or .dll +# PLUGVAR_DIR: name of directory where plugin was found +# PLUGVAR_OPT: mysqld option --plugin_dir=.... +# PLUGVAR_LOAD: option --plugin_load=.... if the 4th element is present +# PLUGVAR_LOAD_ADD: option --plugin_load_add=.... if the 4th element is present +# PLUGVAR_LOAD_PATH: option --plugin_load=.... with library full path +# if the 4th element is present +# PLUGVAR_LOAD_ADD_PATH: option --plugin_load_add=.... with library full path +# if the 4th element is present +# +# If a listed plugin is not found, the corresponding variables will be +# set to empty, they will not be unset. +# +# The PLUGVAR variable is not quoted, so you must remember to quote it +# when using it in an INSTALL PLUGIN command. +# +# The envorinment variables can be used in tests. If adding a new plugin, +# you are free to pick your variable name, but please keep it upper +# case for consistency. +# +# The _LOAD variable will have a form +# +# --plugin_load==;=..... +# +# with name1, name2 etc from the comma separated list of plugin names +# in the optional 4th argument. + +keyring_vault plugin/keyring_vault KEYRING_VAULT_PLUGIN keyring_vault diff --git a/plugin/keyring_vault/tests/mtr/rpl_key_rotation-master.opt b/plugin/keyring_vault/tests/mtr/rpl_key_rotation-master.opt new file mode 100644 index 000000000000..3b441dfbebf0 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/rpl_key_rotation-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT $KEYRING_VAULT_PLUGIN_LOAD diff --git a/plugin/keyring_vault/tests/mtr/rpl_key_rotation-slave.opt b/plugin/keyring_vault/tests/mtr/rpl_key_rotation-slave.opt new file mode 100644 index 000000000000..3b441dfbebf0 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/rpl_key_rotation-slave.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT $KEYRING_VAULT_PLUGIN_LOAD diff --git a/plugin/keyring_vault/tests/mtr/rpl_key_rotation.result b/plugin/keyring_vault/tests/mtr/rpl_key_rotation.result new file mode 100644 index 000000000000..eb65f307b16b --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/rpl_key_rotation.result @@ -0,0 +1,120 @@ +include/master-slave.inc +Warnings: +Note #### Sending passwords in plain text without SSL/TLS is extremely insecure. +Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information. +[connection master] +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("Error 'Can't find master key from keyring, please check keyring plugin is loaded."); +call mtr.add_suppression("\\[Warning\\] Slave: Can't find master key from keyring, please check keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("The slave coordinator and worker threads are stopped"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +[On Master] +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; + +ALTER INSTANCE ROTATE INNODB MASTER KEY; +CREATE TABLE t1(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +ALTER INSTANCE ROTATE INNODB MASTER KEY; +CREATE TABLE t2(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t2 SELECT * FROM t1; +SELECT COUNT(*) FROM t2; +COUNT(*) +3 +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +include/sync_slave_sql_with_master.inc +[On Slave] +SELECT COUNT(*) FROM t2; +COUNT(*) +3 +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +[On Master] +CREATE TABLE t3(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t3 VALUES(0, "aaaaa"); + +include/sync_slave_sql_with_master.inc +[On Slave] +SELECT * FROM t3; +c1 c2 +0 aaaaa +SELECT COUNT(*) FROM t2; +COUNT(*) +3 +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +ALTER INSTANCE ROTATE INNODB MASTER KEY; +UNINSTALL PLUGIN keyring_vault; +# Checking keyring plugin after uninstall. +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +include/stop_slave_sql.inc +[On Master] +CREATE TABLE t4(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t4 VALUES(1, "rpltest"); +include/save_master_pos.inc +[On Slave] +START SLAVE SQL_THREAD; +include/wait_for_slave_sql_error.inc [errno=3185] +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +Warnings: +Warning 29 File '' not found (Errcode: 2 - No such file or directory) +Warning 42000 keyring_vault initialization failure. Please check the server log. +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault2.conf'; +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME keyring_vault +PLUGIN_VERSION 1.0 +PLUGIN_STATUS ACTIVE +include/start_slave_sql.inc +include/sync_slave_sql.inc +SELECT * FROM t4; +c1 c2 +1 rpltest +[On Master] +# Uninstalling keyring_vault plugin on master. +UNINSTALL PLUGIN keyring_vault; +# Checking keyring plugin after uninstall. +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +SELECT * FROM t1; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc + +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# Installing keyring_vault plugin on master. +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +Warnings: +Warning 29 File '' not found (Errcode: 2 - No such file or directory) +Warning 42000 keyring_vault initialization failure. Please check the server log. +# Cleanup +DROP TABLE t1,t2,t3,t4; +include/sync_slave_sql_with_master.inc +include/rpl_end.inc +UNINSTALL PLUGIN keyring_vault; +# restart:--plugin_load=keyring_vault=keyring_vault.so KEYRING_VAULT_PLUGIN_OPT diff --git a/plugin/keyring_vault/tests/mtr/rpl_key_rotation.test b/plugin/keyring_vault/tests/mtr/rpl_key_rotation.test new file mode 100644 index 000000000000..37a9d5c439b4 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/rpl_key_rotation.test @@ -0,0 +1,145 @@ +# WL#8821 +# Testing ALTER INSTANCE ROTATE INNODB MASTER KEY with replication +# and keyring installing/uninstalling scenarios on master/slave. +--source include/have_keyring_vault_plugin.inc +--source include/master-slave.inc + +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("Error 'Can't find master key from keyring, please check keyring plugin is loaded."); +call mtr.add_suppression("\\[Warning\\] Slave: Can't find master key from keyring, please check keyring plugin is loaded."); +call mtr.add_suppression("\\[Error\\] InnoDB: Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("The slave coordinator and worker threads are stopped"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc + +--connection slave +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_2'; + +--connection master +--echo [On Master] +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1'; + +--echo +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +# Creating table with encryption. +CREATE TABLE t1(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +SELECT * FROM t1; + +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +CREATE TABLE t2(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t2 SELECT * FROM t1; +SELECT COUNT(*) FROM t2; + +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +SELECT * FROM t1; + +--source include/sync_slave_sql_with_master.inc +--echo [On Slave] + +SELECT COUNT(*) FROM t2; +SELECT * FROM t1; + +--connection master +--echo [On Master] +CREATE TABLE t3(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t3 VALUES(0, "aaaaa"); + +--echo +--source include/sync_slave_sql_with_master.inc +--echo [On Slave] +SELECT * FROM t3; +SELECT COUNT(*) FROM t2; +SELECT * FROM t1; + +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +# Uninstalling keyring_vault plugin on slave. +UNINSTALL PLUGIN keyring_vault; +--echo # Checking keyring plugin after uninstall. +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +--source include/stop_slave_sql.inc + +--connection master +--echo [On Master] +CREATE TABLE t4(c1 INT PRIMARY KEY, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t4 VALUES(1, "rpltest"); +--source include/save_master_pos.inc + +--connection slave +--echo [On Slave] +START SLAVE SQL_THREAD; +--let $slave_sql_errno= convert_error(ER_CANNOT_FIND_KEY_IN_KEYRING) +--source include/wait_for_slave_sql_error.inc + +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_2'; +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +--source include/start_slave_sql.inc +--source include/sync_slave_sql.inc +SELECT * FROM t4; + +--connection master +--echo [On Master] +--echo # Uninstalling keyring_vault plugin on master. +UNINSTALL PLUGIN keyring_vault; +--echo # Checking keyring plugin after uninstall. +query_vertical SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS +FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +SELECT * FROM t1; +--echo +# Try to rotate master key when keyring_vault plugin uninstalled. +--error ER_CANNOT_FIND_KEY_IN_KEYRING +ALTER INSTANCE ROTATE INNODB MASTER KEY; + +--echo # Installing keyring_vault plugin on master. +--replace_regex /\.dll/.so/ +eval INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN'; +--echo # Cleanup +DROP TABLE t1,t2,t3,t4; + +--source include/sync_slave_sql_with_master.inc +--source include/rpl_end.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--connection master +--source mount_point_service.inc +--connection slave +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--connection master +--source mount_point_service.inc +--connection slave +--source mount_point_service.inc + +#reset keyring_vault_config variable to null +UNINSTALL PLUGIN keyring_vault; +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR $KEYRING_PLUGIN keyring_vault.so $KEYRING_VAULT_PLUGIN_OPT KEYRING_VAULT_PLUGIN_OPT +--let $restart_parameters="restart:$KEYRING_VAULT_PLUGIN_LOAD $KEYRING_VAULT_PLUGIN_OPT" +--source include/restart_mysqld.inc +# End: diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_1.result b/plugin/keyring_vault/tests/mtr/table_encrypt_1.result new file mode 100644 index 000000000000..3bcd0529acea --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_1.result @@ -0,0 +1,276 @@ +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Failed to find tablespace for table `\.\.*`\.`\.\.*` in the cache."); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Operating system error number 2 in a file operation."); +call mtr.add_suppression("\\[ERROR\\] InnoDB: The error means the system cannot find the path specified."); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Could not find a valid tablespace file for"); +call mtr.add_suppression("ibd can't be decrypted , please confirm the keyfile is match and keyring plugin is loaded."); +call mtr.add_suppression("\\[Warning\\] InnoDB: Ignoring tablespace .* because it could not be opened"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: If you are installing InnoDB, remember that you must create directories yourself, InnoDB does not create them."); +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +CREATE TABLE t1(c1 INT, c2 char(20)) ENGINE = InnoDB; +ALTER TABLE t1 ENCRYPTION="Y", algorithm=copy; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# restart: +DROP TABLE IF EXISTS t1; +SET GLOBAL innodb_file_per_table = 0; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +0 +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: InnoDB: Tablespace `innodb_system` cannot contain an ENCRYPTED table. +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE t1(c int) ENCRYPTION="Y" tablespace innodb_system; +ERROR HY000: InnoDB: Tablespace `innodb_system` cannot contain an ENCRYPTED table. +CREATE TABLE t1(c int) ENCRYPTION="N" tablespace innodb_system; +DROP TABLE t1; +CREATE TEMPORARY TABLE t1(c int) ENCRYPTION="Y"; +ERROR HY000: InnoDB: Unsupported encryption option for temporary tables. +CREATE TEMPORARY TABLE t1(c int) ENCRYPTION="N"; +DROP TABLE t1; +CREATE TABLE t1(c int) ENCRYPTION="R" ENGINE = InnoDB; +ERROR HY000: Invalid encryption option. +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int DEFAULT NULL, + `c2` char(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +INSERT INTO t1 VALUES(3, "ddddd"); +INSERT INTO t1 VALUES(4, "eeeee"); +INSERT INTO t1 VALUES(5, "fffff"); +INSERT INTO t1 VALUES(6, "ggggg"); +INSERT INTO t1 VALUES(7, "hhhhh"); +INSERT INTO t1 VALUES(8, "iiiii"); +INSERT INTO t1 VALUES(9, "jjjjj"); +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +SELECT * FROM t1 ORDER BY c1 LIMIT 10; +c1 c2 +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +# restart +SELECT * FROM t1 ORDER BY c1 LIMIT 10; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# restart: +SELECT * FROM t1 ORDER BY c1 LIMIT 10; +c1 c2 +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +0 aaaaa +DROP TABLE t1; +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +INSERT INTO t1 VALUES(3, "ddddd"); +INSERT INTO t1 VALUES(4, "eeeee"); +INSERT INTO t1 VALUES(5, "fffff"); +INSERT INTO t1 VALUES(6, "ggggg"); +INSERT INTO t1 VALUES(7, "hhhhh"); +INSERT INTO t1 VALUES(8, "iiiii"); +INSERT INTO t1 VALUES(9, "jjjjj"); +# restart: +SELECT * FROM t1 ORDER BY c1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +ALTER TABLE t1 ENCRYPTION="N", algorithm=inplace; +ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Cannot alter encryption attribute by inplace algorithm.. Try ALGORITHM=COPY. +ALTER TABLE t1 TABLESPACE=`innodb_system`; +ERROR HY000: InnoDB: Tablespace `innodb_system` cannot contain an ENCRYPTED table. +ALTER TABLE t1 ENCRYPTION="N", algorithm=copy; +SELECT * FROM t1 ORDER BY c1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +DROP TABLE t1; +CREATE TABLE t1 (c1 int) ENCRYPTION='N'; +ALTER TABLE t1 ENCRYPTION='P',algorithm=copy; +ERROR HY000: Invalid encryption option. +ALTER TABLE t1 ADD KEY k1 (c1) ,algorithm=inplace; +ALTER TABLE t1 ENCRYPTION='Y',algorithm=inplace; +ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Cannot alter encryption attribute by inplace algorithm.. Try ALGORITHM=COPY. +drop table t1; +CREATE TABLE t1(c1 INT PRIMARY KEY) COMPRESSION = "ZLIB" ENCRYPTION = "Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0), (1), (2), (3), (4), (5), (6), (7), (8), (9); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int NOT NULL, + PRIMARY KEY (`c1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMPRESSION='ZLIB' ENCRYPTION='Y' +FLUSH TABLES t1 WITH READ LOCK; +UNLOCK TABLES; +SELECT * FROM t1 ORDER BY c1 LIMIT 10; +c1 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +# restart: +SELECT * FROM t1 ORDER BY c1; +c1 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +DROP TABLE t1; +CREATE TABLE t1(c1 int null) ENCRYPTION='Y' ROW_FORMAT=compressed; +INSERT INTO t1 VALUES(0), (1), (2), (3), (4), (5), (6), (7), (8), (9); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED ENCRYPTION='Y' +FLUSH TABLES t1 WITH READ LOCK; +UNLOCK TABLES; +SELECT * FROM t1 ORDER BY c1 LIMIT 10; +c1 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +# restart: +SELECT * FROM t1 ORDER BY c1; +c1 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +DROP TABLE t1; +CREATE TABLE t1(c1 INT PRIMARY KEY, g geometry not null, spatial index(g)) ENCRYPTION = "Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0, POINT(0, 0)); +INSERT INTO t1 VALUES(1, POINT(1, 1)); +INSERT INTO t1 VALUES(2, POINT(2, 2)); +INSERT INTO t1 VALUES(3, POINT(3, 3)); +INSERT INTO t1 VALUES(4, POINT(4, 4)); +INSERT INTO t1 VALUES(5, POINT(5, 5)); +INSERT INTO t1 VALUES(6, POINT(6, 6)); +INSERT INTO t1 VALUES(7, POINT(7, 7)); +INSERT INTO t1 VALUES(8, POINT(8, 8)); +INSERT INTO t1 VALUES(9, POINT(9, 9)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int NOT NULL, + `g` geometry NOT NULL, + PRIMARY KEY (`c1`), + SPATIAL KEY `g` (`g`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +FLUSH TABLES t1 WITH READ LOCK; +UNLOCK TABLES; +SELECT c1, ST_AsText(g) FROM t1 ORDER BY c1 LIMIT 10; +c1 ST_AsText(g) +0 POINT(0 0) +1 POINT(1 1) +2 POINT(2 2) +3 POINT(3 3) +4 POINT(4 4) +5 POINT(5 5) +6 POINT(6 6) +7 POINT(7 7) +8 POINT(8 8) +9 POINT(9 9) +# restart: +SELECT c1, ST_AsText(g) FROM t1 ORDER BY c1 LIMIT 10; +c1 ST_AsText(g) +0 POINT(0 0) +1 POINT(1 1) +2 POINT(2 2) +3 POINT(3 3) +4 POINT(4 4) +5 POINT(5 5) +6 POINT(6 6) +7 POINT(7 7) +8 POINT(8 8) +9 POINT(9 9) +DROP TABLE t1; +SET GLOBAL innodb_file_per_table=OFF; +CREATE TABLE t1 (c1 int); +ALTER TABLE t1 COMPRESSION='zlib'; +ALTER TABLE t1 ENCRYPTION='Y',ALGORITHM=COPY; +ERROR HY000: InnoDB: Tablespace `innodb_system` cannot contain an ENCRYPTED table. +DROP TABLE t1; +SET GLOBAL innodb_file_per_table=ON; +CREATE TABLE t1 (id int unsigned NOT NULL auto_increment PRIMARY KEY, val varchar(20) NOT NULL) ENGINE=InnoDB; +INSERT INTO t1 (val) VALUES ('Sydney'), ('Melbourne'), ('Brisbane'), ('Perth'), ('Adelaide'); +ALTER TABLE t1 ENCRYPTION = 'Y'; +ALTER TABLE t1 ADD COLUMN is_capital char(1) NOT NULL DEFAULT 'N' AFTER val; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `val` varchar(20) NOT NULL, + `is_capital` char(1) NOT NULL DEFAULT 'N', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1 ENCRYPTION='Y' +Pattern "Sydney" not found +SET GLOBAL innodb_file_per_table=1; +DROP TABLE t1; diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_1.test b/plugin/keyring_vault/tests/mtr/table_encrypt_1.test new file mode 100644 index 000000000000..2785f069705d --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_1.test @@ -0,0 +1,18 @@ +--source include/have_keyring_vault_plugin.inc + +--let $KEYRING_CONF_FILE_1=$MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +--let $keyring_restart_param=restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT +--source include/table_encrypt_1.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_2-master.opt b/plugin/keyring_vault/tests/mtr/table_encrypt_2-master.opt new file mode 100644 index 000000000000..1f8fd2247925 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_2-master.opt @@ -0,0 +1,2 @@ +$KEYRING_VAULT_PLUGIN_OPT +$KEYRING_VAULT_PLUGIN_LOAD diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_2.result b/plugin/keyring_vault/tests/mtr/table_encrypt_2.result new file mode 100644 index 000000000000..db5a599d903a --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_2.result @@ -0,0 +1,142 @@ +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); +SET @@global.keyring_vault_config="MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf"; +CREATE TABLE t1(c1 int) ENGINE=InnoDB ENCRYPTION="Y"; +DROP TABLE t1; +# restart: +DROP TABLE IF EXISTS t1; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int DEFAULT NULL, + `c2` char(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +INSERT INTO t1 VALUES(3, "ddddd"); +INSERT INTO t1 VALUES(4, "eeeee"); +INSERT INTO t1 VALUES(5, "fffff"); +INSERT INTO t1 VALUES(6, "ggggg"); +INSERT INTO t1 VALUES(7, "hhhhh"); +INSERT INTO t1 VALUES(8, "iiiii"); +INSERT INTO t1 VALUES(9, "jjjjj"); +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +SELECT * FROM t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +# restart: +SELECT * FROM t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +ALTER INSTANCE ROTATE INNODB MASTER KEY; +DROP TABLE t1; +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +INSERT INTO t1 VALUES(3, "ddddd"); +INSERT INTO t1 VALUES(4, "eeeee"); +INSERT INTO t1 VALUES(5, "fffff"); +INSERT INTO t1 VALUES(6, "ggggg"); +INSERT INTO t1 VALUES(7, "hhhhh"); +INSERT INTO t1 VALUES(8, "iiiii"); +INSERT INTO t1 VALUES(9, "jjjjj"); +# restart: +SELECT * FROM t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +DROP TABLE t1; +# restart: +SET block_encryption_mode = 'aes-256-cbc'; +DROP DATABASE IF EXISTS tde_db; +CREATE DATABASE tde_db; +CREATE TABLE tde_db.t1(c1 INT PRIMARY KEY, c2 char(50)) ENCRYPTION = 'Y' ENGINE = InnoDB; +INSERT INTO tde_db.t1 VALUES(0, 'abc'); +INSERT INTO tde_db.t1 VALUES(1, 'xyz'); +INSERT INTO tde_db.t1 VALUES(2, null); +INSERT INTO tde_db.t1 VALUES(3, null); +SELECT * FROM tde_db.t1 LIMIT 10; +c1 c2 +0 abc +1 xyz +2 NULL +3 NULL +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT * FROM tde_db.t1 LIMIT 10; +c1 c2 +0 abc +1 xyz +2 NULL +3 NULL +# Mysqldump output + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `tde_db` /*!40100 DEFAULT CHARACTER SET latin1 */; + +USE `tde_db`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `t1` ( + `c1` int NOT NULL, + `c2` char(50) DEFAULT NULL, + PRIMARY KEY (`c1`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y'; +/*!40101 SET character_set_client = @saved_cs_client */; +INSERT INTO `t1` VALUES (0,'abc'),(1,'xyz'),(2,NULL),(3,NULL); +# Redirecting mysqlpump output to MYSQL_TMP_DIR/mysqlpump_encrypt.sql +DROP DATABASE tde_db; +# Loading tables from mysqlpump_encrypt.sql +SELECT * FROM tde_db.t1 LIMIT 10; +c1 c2 +0 abc +1 xyz +2 NULL +3 NULL +INSERT INTO tde_db.t1 VALUES(4, null); +SELECT * FROM tde_db.t1 LIMIT 10; +c1 c2 +0 abc +1 xyz +2 NULL +3 NULL +4 NULL +DROP DATABASE tde_db; +SET GLOBAL innodb_file_per_table=1; diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_2.test b/plugin/keyring_vault/tests/mtr/table_encrypt_2.test new file mode 100644 index 000000000000..b33bc5eed1e8 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_2.test @@ -0,0 +1,34 @@ +# InnoDB transparent tablespace data encryption +# This test case will test basic encryption support features. + +--source include/have_keyring_vault_plugin.inc +--source include/no_valgrind_without_big.inc + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc + +--let $keyring1_restart_param=restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT +--let $keyring2_restart_param=restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_2 $KEYRING_VAULT_PLUGIN_OPT + +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); + +--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR +eval SET @@global.keyring_vault_config="$KEYRING_CONF_FILE_1"; + +--source include/table_encrypt_2.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_3.result b/plugin/keyring_vault/tests/mtr/table_encrypt_3.result new file mode 100644 index 000000000000..0cdd16181822 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_3.result @@ -0,0 +1,1415 @@ +call mtr.add_suppression("\\[ERROR\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("InnoDB: The error means that another program is using InnoDB's files"); +call mtr.add_suppression("InnoDB: The error means that another program is using InnoDB's files"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Operating system error number 32 in a file operation."); +# Create encrypt table before loading keyring plugin +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +DROP TABLE IF EXISTS t_encrypt; +CREATE TABLE t_encrypt(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +UNINSTALL PLUGIN keyring_vault; +ERROR 42000: PLUGIN keyring_vault does not exist +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# Starting server with keyring plugin +# restart: +DROP DATABASE IF EXISTS tde_db; +DROP TABLE IF EXISTS tde_db.t_encrypt; +CREATE DATABASE tde_db; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t_encrypt(c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO tde_db.t_encrypt(c4,c7) VALUES('{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +INSERT INTO tde_db.t_encrypt(c4,c7) select c4,c7 from tde_db.t_encrypt; +SELECT c4,c5,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c4 c5 ST_AsText(c7) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +SELECT c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c4 c5 c6 ST_AsText(c7) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +# In connection 1 +LOCK TABLES tde_db.t_encrypt WRITE; +# In connection default +SHOW OPEN TABLES LIKE 't_encrypt'; +Database Table In_use Name_locked +tde_db t_encrypt 1 0 +"ALTER INSTANCE.." do not conflict with "LOCK TABLE .." COMMAND +ALTER INSTANCE ROTATE INNODB MASTER KEY; +# In connection 1 +INSERT INTO tde_db.t_encrypt(c4,c7) VALUES('{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c4 c5 c6 ST_AsText(c7) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +UNLOCK TABLES; +# In connection default +SELECT c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c4 c5 c6 ST_AsText(c7) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# restart: +SELECT c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c4 c5 c6 ST_AsText(c7) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +{"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +DROP DATABASE tde_db; +SET GLOBAL innodb_file_per_table=1; +DROP DATABASE IF EXISTS tde_db; +CREATE DATABASE tde_db; +USE tde_db; +DROP TABLE IF EXISTS tde_db.t_encrypt; +DROP TABLE IF EXISTS tde_db.t_encrypt_1; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t_encrypt(c1 INT, c2 char(20), c3 BLOB) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE TABLE tde_db.t_encrypt_1(c1 INT, c2 char(20)) ENCRYPTION="Yes" ENGINE = InnoDB; +ERROR HY000: Invalid encryption option. +CREATE TABLE tde_db.t_encrypt_1(c1 INT, c2 char(20)) ENCRYPTION="y" ENGINE = InnoDB; +DROP TABLE tde_db.t_encrypt_1; +CREATE TABLE tde_db.t_encrypt_1(c1 INT, c2 char(20),c3 BLOB) ENGINE = InnoDB; +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c1` int(11) DEFAULT NULL, + `c2` char(20) DEFAULT NULL, + `c3` blob +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO tde_db.t_encrypt VALUES(0, "aaaaa",repeat('A', 20000)); +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +#Insert into non encrypted table +INSERT INTO tde_db.t_encrypt_1 SELECT * FROM tde_db.t_encrypt; +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt_1 LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +SELECT COUNT(*) FROM tde_db.t_encrypt_1; +COUNT(*) +64 +# Starting server with keyring plugin restart with keying +# restart: +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +64 +#check non encrypted table +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt_1 LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +SELECT COUNT(*) FROM tde_db.t_encrypt_1; +COUNT(*) +64 +DROP TABLE tde_db.t_encrypt; +DROP TABLE tde_db.t_encrypt_1; +SET GLOBAL innodb_file_per_table=1; +DROP DATABASE IF EXISTS tde_db; +DROP TABLE IF EXISTS tde_db.t_encrypt; +CREATE DATABASE tde_db; +USE tde_db; +# File per table is set 0. Encryption not possible. +SET GLOBAL innodb_file_per_table = 0; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +0 +CREATE TABLE tde_db.t_encrypt(c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: InnoDB: Tablespace `innodb_system` cannot contain an ENCRYPTED table. +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLESPACE s_alt1 ADD DATAFILE 's_alt1.ibd'; +CREATE TABLE tde_db.t_encrypt (a int, b text) ENCRYPTION="Y" TABLESPACE=`s_alt1` ENGINE=InnoDB; +ERROR HY000: InnoDB: Tablespace `s_alt1` cannot contain an ENCRYPTED table. +DROP TABLESPACE s_alt1; +CREATE TEMPORARY TABLE tde_db.t_encrypt (a int, b text) ENCRYPTION="Y" ENGINE=InnoDB; +ERROR HY000: InnoDB: Unsupported encryption option for temporary tables. +CREATE TABLE tde_db.t_encrypt_myisam (a int, b text) ENCRYPTION="Y" ENGINE=MyISAM; +ERROR HY000: Table storage engine for 't_encrypt_myisam' doesn't have this option +CREATE PROCEDURE tde_db.row_format_t_encrypt(row_form VARCHAR(1000)) +begin +declare i int default 1; +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1062 SET has_error = 1; +DROP TABLE IF EXISTS tde_db.t_encrypt; +SET @sql_text = CONCAT('CREATE TABLE tde_db.t_encrypt ('," c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,c3 VARCHAR(255), c4 JSON ,c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED,c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL,c7 POINT NOT NULL,spatial INDEX idx2 (c7) ) ", ' ENCRYPTION="Y" ', row_form ,' ENGINE=InnoDB'); +PREPARE stmt FROM @sql_text; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +SHOW CREATE TABLE tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES (REPEAT('a',200),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt; +SELECT c2,c4,c5,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +SELECT c2,c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +DELETE FROM tde_db.t_encrypt WHERE c2 > 10; +UPDATE tde_db.t_encrypt SET c2 = 100 WHERE c2=1; +SELECT c2,c4,c5,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +SELECT c2,c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +SHOW CREATE TABLE tde_db.t_encrypt; +end| +call tde_db.row_format_t_encrypt(" ROW_FORMAT=DYNAMIC "); +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC ENCRYPTION='Y' +COUNT(*) +32 +c2 c4 c5 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +c2 c4 c5 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC ENCRYPTION='Y' +# restart with keying +# restart: +SELECT c2,c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +8 +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +16 +call tde_db.row_format_t_encrypt(" ROW_FORMAT=COMPACT "); +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT ENCRYPTION='Y' +COUNT(*) +32 +c2 c4 c5 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +c2 c4 c5 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT ENCRYPTION='Y' +# restart with keying +# restart: +SELECT c2,c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +8 +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +16 +call tde_db.row_format_t_encrypt(" ROW_FORMAT=REDUNDANT "); +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT ENCRYPTION='Y' +COUNT(*) +32 +c2 c4 c5 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +c2 c4 c5 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=latin1 ROW_FORMAT=REDUNDANT ENCRYPTION='Y' +# restart with keying +# restart: +SELECT c2,c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +8 +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +16 +call tde_db.row_format_t_encrypt(" ROW_FORMAT=COMPRESSED " ); +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED ENCRYPTION='Y' +COUNT(*) +32 +c2 c4 c5 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +c2 c4 c5 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED ENCRYPTION='Y' +# restart with keying +# restart: +SELECT c2,c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +8 +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +16 +call tde_db.row_format_t_encrypt(" ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4 "); +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4 ENCRYPTION='Y' +COUNT(*) +32 +c2 c4 c5 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +c2 c4 c5 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4 ENCRYPTION='Y' +# restart with keying +# restart: +SELECT c2,c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 ST_AsText(c7) +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +8 +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +16 +# Create partition table +DROP TABLE tde_db.t_encrypt; +CREATE TABLE tde_db.t_encrypt (c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,c3 VARCHAR(255), c4 JSON ,c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED,c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL ) ENCRYPTION="Y" ENGINE=InnoDB PARTITION BY RANGE (c2) (PARTITION p1 VALUES LESS THAN (4),PARTITION p2 VALUES LESS THAN (8),PARTITION p3 VALUES LESS THAN (1000)) ; +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + PRIMARY KEY (`c2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +/*!50100 PARTITION BY RANGE (c2) +(PARTITION p1 VALUES LESS THAN (4) ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN (8) ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN (1000) ENGINE = InnoDB) */ +INSERT INTO tde_db.t_encrypt(c3,c4) VALUES (REPEAT('a',200),'{ "key_a": 1, "key_b": 2, "key_c": 3 }'); +INSERT INTO tde_db.t_encrypt(c3,c4) SELECT c3,c4 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4) SELECT c3,c4 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4) SELECT c3,c4 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4) SELECT c3,c4 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4) SELECT c3,c4 FROM tde_db.t_encrypt; +SELECT c2,c4,c5 FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +5 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +10 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +SELECT c2,c4,c5,c6 FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 +1 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +5 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +10 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +DELETE FROM tde_db.t_encrypt WHERE c2 > 10; +UPDATE tde_db.t_encrypt SET c2 = 100 WHERE c2=1; +SELECT c2,c4,c5 FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +5 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +10 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +SELECT c2,c4,c5,c6 FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +4 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +5 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +6 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +7 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +10 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +ALTER TABLE tde_db.t_encrypt TRUNCATE PARTITION p2; +SELECT c2,c4,c5 FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +10 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 +SELECT c2,c4,c5,c6 FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +10 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL AUTO_INCREMENT, + `c3` varchar(255) DEFAULT NULL, + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + PRIMARY KEY (`c2`) +) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=latin1 ENCRYPTION='Y' +/*!50100 PARTITION BY RANGE (c2) +(PARTITION p1 VALUES LESS THAN (4) ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN (8) ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN (1000) ENGINE = InnoDB) */ +# restart with keying +# restart: +SELECT c2,c4,c5,c6 FROM tde_db.t_encrypt LIMIT 10; +c2 c4 c5 c6 +2 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +3 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +8 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +9 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +10 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +100 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 +DROP TABLE tde_db.t_encrypt; +DROP DATABASE tde_db; +SET GLOBAL innodb_file_per_table=1; +DROP DATABASE IF EXISTS tde_db; +DROP TABLE IF EXISTS tde_db. t_encrypt; +CREATE DATABASE tde_db; +USE tde_db; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t_encrypt(c2 INT NOT NULL PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE PROCEDURE tde_db.populate_t_encrypt() +begin +declare i int default 1; +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1062 SET has_error = 1; +while (i <= 2000) DO +insert into tde_db.t_encrypt(c2,c3,c4,c7) VALUES(i,CONCAT(REPEAT('a',200),LPAD(CAST(i AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.populate_t_encrypt_small() +begin +declare i int default 1; +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1062 SET has_error = 1; +while (i <= 500) DO +insert into tde_db.t_encrypt(c2,c3,c4,c7) VALUES(i,CONCAT(REPEAT('a',200),LPAD(CAST(i AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.read_t_encrypt() +begin +declare i int default 1; +while (i <= 30) DO +SELECT * FROM (SELECT * FROM tde_db.t_encrypt ORDER BY RAND() LIMIT 1) AS A WHERE A.c2 < 0 ; +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.rotate_master_key() +begin +declare i int default 1; +declare has_error int default 0; +while (i <= 500) DO +ALTER INSTANCE ROTATE INNODB MASTER KEY; +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.create_encrypt_table(encrypt VARCHAR(5)) +begin +declare i int default 1; +declare has_error int default 0; +while (i <= 50) DO +SET @sql_text = CONCAT('CREATE TABLE ',CONCAT('tde_db.t_encrypt_',encrypt,'_',i),' (c1 INT) ENCRYPTION="',encrypt,'"' ,' ENGINE=InnoDB'); +PREPARE stmt FROM @sql_text; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +set i = i + 1; +end while; +end| +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int(11) NOT NULL, + `c3` char(255) DEFAULT 'No text', + `c4` json DEFAULT NULL, + `c5` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_a')) STORED, + `c6` int(11) GENERATED ALWAYS AS (json_extract(`c4`,'$.key_b')) VIRTUAL, + `c7` point NOT NULL, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +# In connection con1 - Running insert +call tde_db.populate_t_encrypt(); +# In connection con2 - Running insert +call tde_db.populate_t_encrypt_small(); +# In connection con3 : Running select +call tde_db.read_t_encrypt(); +# In connection con4 : Running select +call tde_db.read_t_encrypt(); +# In connection con5 - Running "alter instance" +call tde_db.rotate_master_key(); +# In connection con6 - Running "alter instance" +call tde_db.rotate_master_key(); +# In connection con7 - Running "create table" +call tde_db.create_encrypt_table("Y"); +# In connection con8 - Running "create table" +call tde_db.create_encrypt_table("N"); +# In connection con1 +# In connection con2 +# In connection con3 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +# In connection con4 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +c2 c3 c4 c5 c6 c7 +# In connection con5 +# In connection con6 +# In connection con7 +# In connection con8 +USE tde_db; +SELECT c2,right(c3,20),c4,c5,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0002 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0003 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0004 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0005 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0006 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0007 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0008 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0009 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0010 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0002 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0003 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0004 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0005 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0006 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0007 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0008 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0009 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0010 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +2000 +SELECT c2,right(c3,20),c4,c5,ST_AsText(c7) FROM tde_db.t_encrypt WHERE c2%200 = 0; +c2 right(c3,20) c4 c5 ST_AsText(c7) +200 aaaaaaaaaaaaaaaa0200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +400 aaaaaaaaaaaaaaaa0400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +600 aaaaaaaaaaaaaaaa0600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +800 aaaaaaaaaaaaaaaa0800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1000 aaaaaaaaaaaaaaaa1000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1200 aaaaaaaaaaaaaaaa1200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1400 aaaaaaaaaaaaaaaa1400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1600 aaaaaaaaaaaaaaaa1600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1800 aaaaaaaaaaaaaaaa1800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2000 aaaaaaaaaaaaaaaa2000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt WHERE c2%200 = 0; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +200 aaaaaaaaaaaaaaaa0200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +400 aaaaaaaaaaaaaaaa0400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +600 aaaaaaaaaaaaaaaa0600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +800 aaaaaaaaaaaaaaaa0800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1000 aaaaaaaaaaaaaaaa1000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1200 aaaaaaaaaaaaaaaa1200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1400 aaaaaaaaaaaaaaaa1400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1600 aaaaaaaaaaaaaaaa1600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1800 aaaaaaaaaaaaaaaa1800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2000 aaaaaaaaaaaaaaaa2000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# restart with keying +# restart: +SELECT c2,right(c3,20),c4,c5,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0002 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0003 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0004 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0005 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0006 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0007 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0008 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0009 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0010 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0002 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0003 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0004 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0005 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0006 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0007 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0008 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0009 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0010 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +2000 +SELECT c2,right(c3,20),c4,c5,ST_AsText(c7) FROM tde_db.t_encrypt WHERE c2%200 = 0; +c2 right(c3,20) c4 c5 ST_AsText(c7) +200 aaaaaaaaaaaaaaaa0200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +400 aaaaaaaaaaaaaaaa0400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +600 aaaaaaaaaaaaaaaa0600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +800 aaaaaaaaaaaaaaaa0800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1000 aaaaaaaaaaaaaaaa1000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1200 aaaaaaaaaaaaaaaa1200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1400 aaaaaaaaaaaaaaaa1400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1600 aaaaaaaaaaaaaaaa1600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +1800 aaaaaaaaaaaaaaaa1800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +2000 aaaaaaaaaaaaaaaa2000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 POINT(383293632 1754448) +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt WHERE c2%200 = 0; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +200 aaaaaaaaaaaaaaaa0200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +400 aaaaaaaaaaaaaaaa0400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +600 aaaaaaaaaaaaaaaa0600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +800 aaaaaaaaaaaaaaaa0800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1000 aaaaaaaaaaaaaaaa1000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1200 aaaaaaaaaaaaaaaa1200 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1400 aaaaaaaaaaaaaaaa1400 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1600 aaaaaaaaaaaaaaaa1600 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +1800 aaaaaaaaaaaaaaaa1800 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2000 aaaaaaaaaaaaaaaa2000 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +DROP DATABASE tde_db; +SET GLOBAL innodb_file_per_table=1; +DROP DATABASE IF EXISTS tde_db; +CREATE DATABASE tde_db; +USE tde_db; +DROP TABLE IF EXISTS tde_db.t_encrypt; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t_encrypt(c1 INT, c2 char(20), c3 BLOB) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c1` int(11) DEFAULT NULL, + `c2` char(20) DEFAULT NULL, + `c3` blob +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO tde_db.t_encrypt VALUES(0, "aaaaa",repeat('A', 20000)); +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt select * from tde_db.t_encrypt; +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +CREATE USER encryptprivuser@localhost IDENTIFIED BY 'auth'; +GRANT ALL PRIVILEGES ON *.* to encryptprivuser@localhost; +FLUSH PRIVILEGES; +CREATE USER encryptnonprivuser@localhost IDENTIFIED BY 'noauth'; +GRANT SELECT ON *.* to encryptnonprivuser@localhost; +FLUSH PRIVILEGES; +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +# In connection 1 +SELECT CURRENT_USER(); +CURRENT_USER() +encryptprivuser@localhost +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +# In connection 2 +SELECT CURRENT_USER(); +CURRENT_USER() +encryptnonprivuser@localhost +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +CREATE TABLE tde_db.t_encrypt_np(c1 INT, c2 char(20), c3 BLOB) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR 42000: CREATE command denied to user 'encryptnonprivuser'@'localhost' for table 't_encrypt_np' +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +# In connection default +# Starting server with keyring plugin +# restart: +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +0 aaaaa AAAAAAAAAAAAAAAAAAAA +DROP TABLE tde_db.t_encrypt; +DROP USER encryptnonprivuser@localhost; +DROP USER encryptprivuser@localhost; +SET GLOBAL innodb_file_per_table=1; +DROP DATABASE IF EXISTS tde_db; +CREATE DATABASE tde_db; +USE tde_db; +DROP TABLE IF EXISTS tde_db.t_encrypt; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t_encrypt(c1 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, c2 char(100), c3 BLOB , FULLTEXT INDEX `idx1` (c2)) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE TABLE tde_db.t_encrypt1(c11 INT , c22 char(100), c33 BLOB , FULLTEXT INDEX `idx1` (c22)) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c1` int(11) NOT NULL AUTO_INCREMENT, + `c2` char(100) DEFAULT NULL, + `c3` blob, + PRIMARY KEY (`c1`), + FULLTEXT KEY `idx1` (`c2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +CREATE TABLE tde_db.t_encrypt2 (f1 INT PRIMARY KEY, f2 CHAR(100), +FOREIGN KEY (f1) REFERENCES tde_db.t_encrypt(c1) ON UPDATE CASCADE) ENCRYPTION="Y" ENGINE=InnoDB; +CREATE TRIGGER tde_db.trigger_encrypt_table AFTER INSERT ON tde_db.t_encrypt +FOR EACH ROW +begin +INSERT INTO tde_db.t_encrypt1 SET c11 = NEW.c1*-1, c22 = NEW.c2 , c33 = NEW.c3; +end| +INSERT INTO tde_db.t_encrypt(c2,c3) VALUES("transparanet tablespace encryption",repeat('A', 200)); +INSERT INTO tde_db.t_encrypt(c2,c3) VALUES("general tablespace option",repeat('A', 200)); +INSERT INTO tde_db.t_encrypt(c2,c3) VALUES("page level encryption",repeat('A', 200)); +INSERT INTO tde_db.t_encrypt2(f1,f2) VALUES(1,"transparanet tablespace encryption"); +INSERT INTO tde_db.t_encrypt2(f1,f2) VALUES(2,"general tablespace option"); +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +1 transparanet tablespace encryption AAAAAAAAAAAAAAAAAAAA +2 general tablespace option AAAAAAAAAAAAAAAAAAAA +3 page level encryption AAAAAAAAAAAAAAAAAAAA +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt WHERE MATCH c2 AGAINST ('tablespace'); +c1 c2 right(c3, 20) +1 transparanet tablespace encryption AAAAAAAAAAAAAAAAAAAA +2 general tablespace option AAAAAAAAAAAAAAAAAAAA +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt WHERE MATCH c2 AGAINST ('tablespace' IN BOOLEAN MODE); +c1 c2 right(c3, 20) +1 transparanet tablespace encryption AAAAAAAAAAAAAAAAAAAA +2 general tablespace option AAAAAAAAAAAAAAAAAAAA +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt WHERE MATCH c2 AGAINST ('+tablespace -encryption' IN BOOLEAN MODE); +c1 c2 right(c3, 20) +2 general tablespace option AAAAAAAAAAAAAAAAAAAA +ALTER TABLE tde_db.t_encrypt DROP INDEX idx1; +SELECT c1,c2,right(c3, 20) FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) +1 transparanet tablespace encryption AAAAAAAAAAAAAAAAAAAA +2 general tablespace option AAAAAAAAAAAAAAAAAAAA +3 page level encryption AAAAAAAAAAAAAAAAAAAA +ALTER TABLE tde_db.t_encrypt ADD COLUMN c4 CHAR(20) DEFAULT 'text'; +SELECT c1,c2,right(c3, 20),c4 FROM tde_db.t_encrypt LIMIT 10; +c1 c2 right(c3, 20) c4 +1 transparanet tablespace encryption AAAAAAAAAAAAAAAAAAAA text +2 general tablespace option AAAAAAAAAAAAAAAAAAAA text +3 page level encryption AAAAAAAAAAAAAAAAAAAA text +CREATE VIEW tde_db.t_encrypt_view AS SELECT c1,c2 FROM tde_db.t_encrypt; +SELECT c2 FROM tde_db.t_encrypt_view LIMIT 10; +c2 +transparanet tablespace encryption +general tablespace option +page level encryption +SELECT A.c2,B.c2,right(B.c3,20) FROM tde_db.t_encrypt_view A , tde_db.t_encrypt B WHERE A.c2 = B.c2; +c2 c2 right(B.c3,20) +transparanet tablespace encryption transparanet tablespace encryption AAAAAAAAAAAAAAAAAAAA +general tablespace option general tablespace option AAAAAAAAAAAAAAAAAAAA +page level encryption page level encryption AAAAAAAAAAAAAAAAAAAA +DROP VIEW tde_db.t_encrypt_view; +SELECT c11,c22,right(c33, 20) FROM tde_db.t_encrypt1 LIMIT 10; +c11 c22 right(c33, 20) +-1 transparanet tablespace encryption AAAAAAAAAAAAAAAAAAAA +-2 general tablespace option AAAAAAAAAAAAAAAAAAAA +-3 page level encryption AAAAAAAAAAAAAAAAAAAA +INSERT INTO tde_db.t_encrypt2(f1,f2) VALUES(2,"general tablespace option"); +ERROR 23000: Duplicate entry '2' for key 'PRIMARY' +INSERT INTO tde_db.t_encrypt2(f1,f2) VALUES(8,"general tablespace option"); +ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`tde_db`.`t_encrypt2`, CONSTRAINT `t_encrypt2_ibfk_1` FOREIGN KEY (`f1`) REFERENCES `t_encrypt` (`c1`) ON UPDATE CASCADE) +SELECT f1,f2 FROM tde_db.t_encrypt2; +f1 f2 +1 transparanet tablespace encryption +2 general tablespace option +UPDATE tde_db.t_encrypt SET c1=10 WHERE c1=1; +SELECT f1,f2 FROM tde_db.t_encrypt2; +f1 f2 +2 general tablespace option +10 transparanet tablespace encryption +DROP DATABASE tde_db; +SET GLOBAL innodb_file_per_table=1; +DROP DATABASE IF EXISTS tde_db; +CREATE DATABASE tde_db; +USE tde_db; +DROP TABLE IF EXISTS tde_db.t_encrypt; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t_encrypt (c2 INT NOT NULL AUTO_INCREMENT ,c3 VARCHAR(255), c4 JSON ,c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED,c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL,c7 POINT NOT NULL,spatial INDEX idx2 (c7) , PRIMARY KEY (c2,c3(100))) ENCRYPTION="Y" ENGINE=InnoDB; +CREATE PROCEDURE tde_db.txn_t_encrypt() +BEGIN +declare i int default 0; +declare rowcnt int default 0; +START TRANSACTION; +WHILE (i <= 2000) DO +SET i = i + 1; +SET rowcnt = rowcnt + 1; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES (CONCAT(REPEAT('a',10),REPEAT(i,10)),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +IF (rowcnt = 3) THEN +UPDATE tde_db.t_encrypt SET c4 = '{ "key_a": 21, "key_b": 22, "key_c": 23 }' WHERE c2 = i-1 ; +DELETE FROM tde_db.t_encrypt WHERE c2 = i; +SAVEPOINT A; +END IF; +IF (rowcnt = 5) THEN +UPDATE tde_db.t_encrypt SET c4 = '{ "key_a": 41, "key_b": 42, "key_c": 43 }' WHERE c2 = i-1 ; +DELETE FROM tde_db.t_encrypt WHERE c2 = i; +SAVEPOINT B; +END IF; +IF (rowcnt = 10) THEN +ROLLBACK TO SAVEPOINT A; +COMMIT; +SET rowcnt = 0; +START TRANSACTION; +END IF; +END WHILE; +COMMIT; +end| +call tde_db.txn_t_encrypt(); +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +401 +SELECT c2,RIGHT(c3,20),c4 FROM tde_db.t_encrypt LIMIT 10; +c2 RIGHT(c3,20) c4 +1 aaaaaaaaaa1111111111 {"key_a": 1, "key_b": 2, "key_c": 3} +2 aaaaaaaaaa2222222222 {"key_a": 21, "key_b": 22, "key_c": 23} +11 11111111111111111111 {"key_a": 1, "key_b": 2, "key_c": 3} +12 12121212121212121212 {"key_a": 21, "key_b": 22, "key_c": 23} +21 21212121212121212121 {"key_a": 1, "key_b": 2, "key_c": 3} +22 22222222222222222222 {"key_a": 21, "key_b": 22, "key_c": 23} +31 31313131313131313131 {"key_a": 1, "key_b": 2, "key_c": 3} +32 32323232323232323232 {"key_a": 21, "key_b": 22, "key_c": 23} +41 41414141414141414141 {"key_a": 1, "key_b": 2, "key_c": 3} +42 42424242424242424242 {"key_a": 21, "key_b": 22, "key_c": 23} +SELECT c2,RIGHT(c3,20),c4 FROM tde_db.t_encrypt WHERE c2 > 500 AND c2 < 600; +c2 RIGHT(c3,20) c4 +501 01501501501501501501 {"key_a": 1, "key_b": 2, "key_c": 3} +502 02502502502502502502 {"key_a": 21, "key_b": 22, "key_c": 23} +511 11511511511511511511 {"key_a": 1, "key_b": 2, "key_c": 3} +512 12512512512512512512 {"key_a": 21, "key_b": 22, "key_c": 23} +521 21521521521521521521 {"key_a": 1, "key_b": 2, "key_c": 3} +522 22522522522522522522 {"key_a": 21, "key_b": 22, "key_c": 23} +531 31531531531531531531 {"key_a": 1, "key_b": 2, "key_c": 3} +532 32532532532532532532 {"key_a": 21, "key_b": 22, "key_c": 23} +541 41541541541541541541 {"key_a": 1, "key_b": 2, "key_c": 3} +542 42542542542542542542 {"key_a": 21, "key_b": 22, "key_c": 23} +551 51551551551551551551 {"key_a": 1, "key_b": 2, "key_c": 3} +552 52552552552552552552 {"key_a": 21, "key_b": 22, "key_c": 23} +561 61561561561561561561 {"key_a": 1, "key_b": 2, "key_c": 3} +562 62562562562562562562 {"key_a": 21, "key_b": 22, "key_c": 23} +571 71571571571571571571 {"key_a": 1, "key_b": 2, "key_c": 3} +572 72572572572572572572 {"key_a": 21, "key_b": 22, "key_c": 23} +581 81581581581581581581 {"key_a": 1, "key_b": 2, "key_c": 3} +582 82582582582582582582 {"key_a": 21, "key_b": 22, "key_c": 23} +591 91591591591591591591 {"key_a": 1, "key_b": 2, "key_c": 3} +592 92592592592592592592 {"key_a": 21, "key_b": 22, "key_c": 23} +SELECT c2,RIGHT(c3,20),c4 FROM tde_db.t_encrypt ORDER BY c2 DESC LIMIT 10; +c2 RIGHT(c3,20) c4 +2001 20012001200120012001 {"key_a": 1, "key_b": 2, "key_c": 3} +1992 19921992199219921992 {"key_a": 21, "key_b": 22, "key_c": 23} +1991 19911991199119911991 {"key_a": 1, "key_b": 2, "key_c": 3} +1982 19821982198219821982 {"key_a": 21, "key_b": 22, "key_c": 23} +1981 19811981198119811981 {"key_a": 1, "key_b": 2, "key_c": 3} +1972 19721972197219721972 {"key_a": 21, "key_b": 22, "key_c": 23} +1971 19711971197119711971 {"key_a": 1, "key_b": 2, "key_c": 3} +1962 19621962196219621962 {"key_a": 21, "key_b": 22, "key_c": 23} +1961 19611961196119611961 {"key_a": 1, "key_b": 2, "key_c": 3} +1952 19521952195219521952 {"key_a": 21, "key_b": 22, "key_c": 23} +# Starting server with keyring plugin +# restart: +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +401 +SELECT c2,RIGHT(c3,20),c4 FROM tde_db.t_encrypt LIMIT 10; +c2 RIGHT(c3,20) c4 +1 aaaaaaaaaa1111111111 {"key_a": 1, "key_b": 2, "key_c": 3} +2 aaaaaaaaaa2222222222 {"key_a": 21, "key_b": 22, "key_c": 23} +11 11111111111111111111 {"key_a": 1, "key_b": 2, "key_c": 3} +12 12121212121212121212 {"key_a": 21, "key_b": 22, "key_c": 23} +21 21212121212121212121 {"key_a": 1, "key_b": 2, "key_c": 3} +22 22222222222222222222 {"key_a": 21, "key_b": 22, "key_c": 23} +31 31313131313131313131 {"key_a": 1, "key_b": 2, "key_c": 3} +32 32323232323232323232 {"key_a": 21, "key_b": 22, "key_c": 23} +41 41414141414141414141 {"key_a": 1, "key_b": 2, "key_c": 3} +42 42424242424242424242 {"key_a": 21, "key_b": 22, "key_c": 23} +SELECT c2,RIGHT(c3,20),c4 FROM tde_db.t_encrypt WHERE c2 > 500 AND c2 < 600; +c2 RIGHT(c3,20) c4 +501 01501501501501501501 {"key_a": 1, "key_b": 2, "key_c": 3} +502 02502502502502502502 {"key_a": 21, "key_b": 22, "key_c": 23} +511 11511511511511511511 {"key_a": 1, "key_b": 2, "key_c": 3} +512 12512512512512512512 {"key_a": 21, "key_b": 22, "key_c": 23} +521 21521521521521521521 {"key_a": 1, "key_b": 2, "key_c": 3} +522 22522522522522522522 {"key_a": 21, "key_b": 22, "key_c": 23} +531 31531531531531531531 {"key_a": 1, "key_b": 2, "key_c": 3} +532 32532532532532532532 {"key_a": 21, "key_b": 22, "key_c": 23} +541 41541541541541541541 {"key_a": 1, "key_b": 2, "key_c": 3} +542 42542542542542542542 {"key_a": 21, "key_b": 22, "key_c": 23} +551 51551551551551551551 {"key_a": 1, "key_b": 2, "key_c": 3} +552 52552552552552552552 {"key_a": 21, "key_b": 22, "key_c": 23} +561 61561561561561561561 {"key_a": 1, "key_b": 2, "key_c": 3} +562 62562562562562562562 {"key_a": 21, "key_b": 22, "key_c": 23} +571 71571571571571571571 {"key_a": 1, "key_b": 2, "key_c": 3} +572 72572572572572572572 {"key_a": 21, "key_b": 22, "key_c": 23} +581 81581581581581581581 {"key_a": 1, "key_b": 2, "key_c": 3} +582 82582582582582582582 {"key_a": 21, "key_b": 22, "key_c": 23} +591 91591591591591591591 {"key_a": 1, "key_b": 2, "key_c": 3} +592 92592592592592592592 {"key_a": 21, "key_b": 22, "key_c": 23} +SELECT c2,RIGHT(c3,20),c4 FROM tde_db.t_encrypt ORDER BY c2 DESC LIMIT 10; +c2 RIGHT(c3,20) c4 +2001 20012001200120012001 {"key_a": 1, "key_b": 2, "key_c": 3} +1992 19921992199219921992 {"key_a": 21, "key_b": 22, "key_c": 23} +1991 19911991199119911991 {"key_a": 1, "key_b": 2, "key_c": 3} +1982 19821982198219821982 {"key_a": 21, "key_b": 22, "key_c": 23} +1981 19811981198119811981 {"key_a": 1, "key_b": 2, "key_c": 3} +1972 19721972197219721972 {"key_a": 21, "key_b": 22, "key_c": 23} +1971 19711971197119711971 {"key_a": 1, "key_b": 2, "key_c": 3} +1962 19621962196219621962 {"key_a": 21, "key_b": 22, "key_c": 23} +1961 19611961196119611961 {"key_a": 1, "key_b": 2, "key_c": 3} +1952 19521952195219521952 {"key_a": 21, "key_b": 22, "key_c": 23} +DROP DATABASE tde_db; +# +# Bug#24404091 FAILING ASSERTION: !SRV_READ_ONLY_MODE || +# M_IMPL.M_LOG_MODE == MTR_LOG_NO_REDO +# +# restart: +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: InnoDB is in read only mode. +# restart: +SET GLOBAL innodb_file_per_table=1; diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_3.test b/plugin/keyring_vault/tests/mtr/table_encrypt_3.test new file mode 100644 index 000000000000..cb518276d4a6 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_3.test @@ -0,0 +1,18 @@ +--source include/big_test.inc +--source include/have_keyring_vault_plugin.inc +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +--let $keyring_plugin_name=keyring_vault +--let $keyring_restart_param=restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT +--source include/table_encrypt_3.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_4.result b/plugin/keyring_vault/tests/mtr/table_encrypt_4.result new file mode 100644 index 000000000000..cf48651588fc --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_4.result @@ -0,0 +1,153 @@ +# restart: +DROP TABLE IF EXISTS t1; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +# Create a table with encryption +CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int DEFAULT NULL, + `c2` char(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO t1 VALUES(0, "aaaaa"); +INSERT INTO t1 VALUES(1, "bbbbb"); +INSERT INTO t1 VALUES(2, "ccccc"); +INSERT INTO t1 VALUES(3, "ddddd"); +INSERT INTO t1 VALUES(4, "eeeee"); +INSERT INTO t1 VALUES(5, "fffff"); +INSERT INTO t1 VALUES(6, "ggggg"); +INSERT INTO t1 VALUES(7, "hhhhh"); +INSERT INTO t1 VALUES(8, "iiiii"); +INSERT INTO t1 VALUES(9, "jjjjj"); +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +INSERT INTO t1 select * from t1; +SELECT * FROM t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +# Test export/import encrypted tablespace. +FLUSH TABLES test.t1 FOR EXPORT; +UNLOCK TABLES; +ALTER TABLE test.t1 DISCARD TABLESPACE; +ALTER TABLE test.t1 IMPORT TABLESPACE; +CHECK TABLE test.t1; +Table Op Msg_type Msg_text +test.t1 check status OK +SHOW CREATE TABLE test.t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int DEFAULT NULL, + `c2` char(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +SELECT * FROM test.t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +# restart: +SELECT * FROM test.t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +ALTER TABLE test.t1 DISCARD TABLESPACE; +# Try import in another session. +ALTER TABLE test.t1 IMPORT TABLESPACE; +SELECT * FROM test.t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +ALTER TABLE test.t1 DISCARD TABLESPACE; +# Import without .cfg file is posible +# copying .cfp and .ibd file +ALTER TABLE test.t1 IMPORT TABLESPACE; +Warnings: +Warning 1810 InnoDB: IO Read error: (2, No such file or directory) Error opening './test/t1.cfg', will attempt to import without schema verification +SELECT * FROM test.t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT * FROM test.t1 LIMIT 10; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +ALTER TABLE test.t1 DISCARD TABLESPACE; +# Import without .cfp file +# copying .cfg and .ibd file +ALTER TABLE test.t1 IMPORT TABLESPACE; +ERROR HY000: Schema mismatch (Table is in an encrypted tablespace, but can't find the encryption meta-data file in importing) +ALTER INSTANCE ROTATE INNODB MASTER KEY; +# Import without .idb file +# copying .cfp and .cfg file +ALTER TABLE test.t1 IMPORT TABLESPACE; +ERROR HY000: Internal error: Cannot reset LSNs in table `test`.`t1` : Tablespace not found +ALTER INSTANCE ROTATE INNODB MASTER KEY; +# Schema mismatch dest table without encryption - fix result +DROP TABLE t1; +CREATE TABLE test.t1(c1 INT, c2 char(20)) ENGINE = InnoDB; +ALTER TABLE test.t1 DISCARD TABLESPACE; +ALTER TABLE test.t1 IMPORT TABLESPACE; +ERROR HY000: Schema mismatch (Encryption attribute is no matched) +# Import got expected error. +DROP TABLE t1; +CREATE TABLE test.t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +ALTER TABLE test.t1 DISCARD TABLESPACE; +SET SESSION DEBUG="+d, fsp_header_rotate_encryption_failure"; +ALTER TABLE test.t1 IMPORT TABLESPACE; +ERROR HY000: Got error 168 from storage engine +Warnings: +Note 1051 Unknown table 'test.t1' diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_4.test b/plugin/keyring_vault/tests/mtr/table_encrypt_4.test new file mode 100644 index 000000000000..d8a45114f568 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_4.test @@ -0,0 +1,16 @@ +--source include/have_keyring_vault_plugin.inc +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc + +--let $keyring_restart_param=restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_2 $KEYRING_VAULT_PLUGIN_OPT +--source include/table_encrypt_4.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_5-master.opt b/plugin/keyring_vault/tests/mtr/table_encrypt_5-master.opt new file mode 100644 index 000000000000..f896e2bda971 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_5-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_5.result b/plugin/keyring_vault/tests/mtr/table_encrypt_5.result new file mode 100644 index 000000000000..fcc43a70fc50 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_5.result @@ -0,0 +1,1053 @@ +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("ibd can't be decrypted , please confirm the keyfile is match and keyring plugin is loaded"); +call mtr.add_suppression("Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("InnoDB: Operating system error number"); +call mtr.add_suppression("The error means the system cannot find the path specified"); +call mtr.add_suppression("InnoDB: If you are installing InnoDB, remember that you must create directories yourself, InnoDB does not create them"); +call mtr.add_suppression("Could not find a valid tablespace file"); +call mtr.add_suppression("InnoDB: Ignoring tablespace"); +call mtr.add_suppression("InnoDB: Failed to find tablespace for table"); +call mtr.add_suppression("InnoDB: Cannot open table tde_db/t_encrypt.* from the internal data dictionary of InnoDB though the .frm file for the table exists"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Table tde_db/t_encrypt.* in the InnoDB data dictionary has tablespace id .*, but tablespace with that id or name does not exist"); +call mtr.add_suppression("\\[Warning\\] InnoDB: Please refer to .* for how to resolve the issue"); +call mtr.add_suppression("Error while loading keyring content. The keyring might be malformed"); +# Starting server with keyring plugin +# restart: +DROP DATABASE IF EXISTS tde_db; +DROP TABLE IF EXISTS tde_db. t_encrypt; +CREATE DATABASE tde_db; +USE tde_db; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE PROCEDURE tde_db.init_setup() +begin +/* Create encrypt table with encryption */ +CREATE TABLE tde_db.t_encrypt(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +/* Create NON encrypt table with encryption */ +CREATE TABLE tde_db.t_non_encrypt(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +/* insert into encrypt table */ +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_encrypt */'; +SELECT COUNT(*) FROM tde_db.t_encrypt; +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +/* insert into non encrypt table */ +INSERT INTO tde_db.t_non_encrypt(c2,c3,c4,c7) SELECT c2,c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_non_encrypt */'; +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +ALTER INSTANCE ROTATE INNODB MASTER KEY; +CREATE TABLE tde_db.t_encrypt_2(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE TABLE tde_db.t_non_encrypt_2(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +/* insert into encrypt table 2 */ +INSERT INTO tde_db.t_encrypt_2(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_encrypt_2 */'; +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt_2 LIMIT 10; +/* insert into NON encrypt table 2 */ +INSERT INTO tde_db.t_non_encrypt_2(c2,c3,c4,c7) SELECT c2,c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_non_encrypt_2 */'; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt_2 LIMIT 10; +end| +#----------------------------------------------------------------------- +# init tables +call tde_db.init_setup(); +/* select tde_db.t_encrypt */ +/* select tde_db.t_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt */ +/* select tde_db.t_non_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_encrypt_2 */ +/* select tde_db.t_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt_2 */ +/* select tde_db.t_non_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# plugin already installed error +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +ERROR HY000: Function 'keyring_vault' already exists +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +# Uninstall is possible when server started with --early-plugin-load +UNINSTALL PLUGIN keyring_vault; +# variable not accessible after uninstall +SELECT @@global.keyring_vault_config; +ERROR HY000: Unknown system variable 'keyring_vault_config' +# Select non encrypt table : Pass +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +# Select encrypt table : No Error (after uninstall plugin -master key is cached) +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt_2 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +64 +# Error on "ALTER INSTANCE ..." after UNINSTALL PLUGIN +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# new encrypt table creation is blocked after uninstall +CREATE TABLE tde_db.t_encrypt_3(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# new non encrypt table +CREATE TABLE tde_db.t_non_encrypt_3(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +DROP TABLE tde_db.t_encrypt , tde_db.t_encrypt_2 ; +DROP TABLE tde_db.t_non_encrypt , tde_db.t_non_encrypt_2 , tde_db.t_non_encrypt_3; +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME PLUGIN_VERSION PLUGIN_STATUS +#----------------------------------------------------------------------- +# Test 1 : Restart with same keyring option , all tables accesible +# restart with --early-plugin-load +# restart: +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME PLUGIN_VERSION PLUGIN_STATUS +keyring_vault 1.0 ACTIVE +# init tables +call tde_db.init_setup(); +/* select tde_db.t_encrypt */ +/* select tde_db.t_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt */ +/* select tde_db.t_non_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_encrypt_2 */ +/* select tde_db.t_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt_2 */ +/* select tde_db.t_non_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# restart with same --early-plugin-load and keyring_file_data option +# restart: +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt_2 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt_2 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# insert into old encrypt tables +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +64 +INSERT INTO tde_db.t_encrypt_2(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +128 +# insert into old non encrypt tables +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +INSERT INTO tde_db.t_non_encrypt_2(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +128 +# update into old encrypt tables +UPDATE tde_db.t_encrypt_2 SET c2 = 1000 WHERE c2 = 1; +SELECT COUNT(*) FROM tde_db.t_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +1 +# update into old non encrypt tables +UPDATE tde_db.t_non_encrypt_2 SET c2 = 1000 WHERE c2 = 1; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +1 +# delete into old encrypt tables +DELETE FROM tde_db.t_encrypt_2 WHERE c2 = 1000 ; +SELECT COUNT(*) FROM tde_db.t_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +0 +# delete into old non encrypt tables +DELETE FROM tde_db.t_non_encrypt_2 WHERE c2 = 1000 ; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +0 +# new table +CREATE TABLE tde_db.t_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE TABLE tde_db.t_non_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +INSERT INTO tde_db.t_encrypt_4(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_non_encrypt_4(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt_4; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt_4 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_non_encrypt_4; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt_4 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +127 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +127 +SELECT COUNT(*) FROM tde_db.t_encrypt_4; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_4; +COUNT(*) +64 +DROP TABLE tde_db.t_encrypt , tde_db.t_encrypt_2 , tde_db.t_encrypt_4; +DROP TABLE tde_db.t_non_encrypt , tde_db.t_non_encrypt_2 , tde_db.t_non_encrypt_4; +#----------------------------------------------------------------------- +# Test 2 : Restart without keyring option - old encrypt table not +# accessible but rest are. +# restart with --early-plugin-load to load initial data +# restart: +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME PLUGIN_VERSION PLUGIN_STATUS +keyring_vault 1.0 ACTIVE +# init tables +call tde_db.init_setup(); +/* select tde_db.t_encrypt */ +/* select tde_db.t_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt */ +/* select tde_db.t_non_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_encrypt_2 */ +/* select tde_db.t_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt_2 */ +/* select tde_db.t_non_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# restart without --early-plugin-load and keyring_file_data +# restart: +# encrypt table not accessible +SELECT COUNT(*) FROM tde_db.t_encrypt; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# NON encrypt table are accessible +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +# encrypt table not possible +CREATE TABLE tde_db.t_non_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# non encrypt table possible +CREATE TABLE tde_db.t_non_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +INSERT INTO tde_db.t_non_encrypt_4(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_non_encrypt; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_4; +COUNT(*) +64 +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# encrypt table not accessible +SELECT COUNT(*) FROM tde_db.t_encrypt; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +DROP TABLE tde_db.t_encrypt , tde_db.t_encrypt_2; +DROP TABLE tde_db.t_non_encrypt , tde_db.t_non_encrypt_2 ,tde_db.t_non_encrypt_4; +DROP DATABASE tde_db; +# Initial setup +# Starting server with keyring plugin +# restart: +DROP DATABASE IF EXISTS tde_db; +CREATE DATABASE tde_db; +USE tde_db; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE PROCEDURE tde_db.init_setup() +begin +/* Create encrypt table with encryption */ +CREATE TABLE tde_db.t_encrypt(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +/* Create NON encrypt table with encryption */ +CREATE TABLE tde_db.t_non_encrypt(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +/* insert into encrypt table */ +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_encrypt */'; +SELECT COUNT(*) FROM tde_db.t_encrypt; +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +/* insert into non encrypt table */ +INSERT INTO tde_db.t_non_encrypt(c2,c3,c4,c7) SELECT c2,c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_non_encrypt */'; +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +ALTER INSTANCE ROTATE INNODB MASTER KEY; +CREATE TABLE tde_db.t_encrypt_2(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE TABLE tde_db.t_non_encrypt_2(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +/* insert into encrypt table 2 */ +INSERT INTO tde_db.t_encrypt_2(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_encrypt_2 */'; +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt_2 LIMIT 10; +/* insert into NON encrypt table 2 */ +INSERT INTO tde_db.t_non_encrypt_2(c2,c3,c4,c7) SELECT c2,c3,c4,c7 FROM tde_db.t_encrypt; +SELECT '/* select tde_db.t_non_encrypt_2 */'; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +SELECT c2 ,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt_2 LIMIT 10; +end| +# Test 3 : Restart without keyring option but load plugin using command +# Access all tables +#----------------------------------------------------------------------- +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME PLUGIN_VERSION PLUGIN_STATUS +keyring_vault 1.0 ACTIVE +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +# init tables +call tde_db.init_setup(); +/* select tde_db.t_encrypt */ +/* select tde_db.t_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt */ +/* select tde_db.t_non_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_encrypt_2 */ +/* select tde_db.t_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt_2 */ +/* select tde_db.t_non_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# restart without --early-plugin-load and keyring_file_data +# AND explicitly install plugin to access old table +# restart: +# Install keyring plugin +INSTALL PLUGIN keyring_vault SONAME 'keyring_vault.so'; +# Set keyring_file_data as old file so as to access old tables +SET @@global.keyring_vault_config='MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf'; +SELECT @@global.keyring_vault_config; +@@global.keyring_vault_config +MYSQLTEST_VARDIR/std_data/keyring_vault_confs/keyring_vault1.conf +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt_2 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt_2 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# insert into old encrypt tables +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +64 +INSERT INTO tde_db.t_encrypt_2(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +128 +# insert into old non encrypt tables +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +INSERT INTO tde_db.t_non_encrypt_2(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +128 +# update into old encrypt tables +UPDATE tde_db.t_encrypt_2 SET c2 = 1000 WHERE c2 = 1; +SELECT COUNT(*) FROM tde_db.t_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +1 +# update into old non encrypt tables +UPDATE tde_db.t_non_encrypt_2 SET c2 = 1000 WHERE c2 = 1; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +1 +# delete into old encrypt tables +DELETE FROM tde_db.t_encrypt_2 WHERE c2 = 1000 ; +SELECT COUNT(*) FROM tde_db.t_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +0 +# delete into old non encrypt tables +DELETE FROM tde_db.t_non_encrypt_2 WHERE c2 = 1000 ; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2 WHERE c2 = 1000 ; +COUNT(*) +0 +# new table +CREATE TABLE tde_db.t_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE TABLE tde_db.t_non_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +INSERT INTO tde_db.t_encrypt_4(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +INSERT INTO tde_db.t_non_encrypt_4(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt_4; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt_4 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +SELECT COUNT(*) FROM tde_db.t_non_encrypt_4; +COUNT(*) +64 +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt_4 LIMIT 10; +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT COUNT(*) FROM tde_db.t_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +COUNT(*) +127 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +127 +SELECT COUNT(*) FROM tde_db.t_encrypt_4; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_4; +COUNT(*) +64 +DROP TABLE tde_db.t_encrypt , tde_db.t_encrypt_2 , tde_db.t_encrypt_4; +DROP TABLE tde_db.t_non_encrypt , tde_db.t_non_encrypt_2 , tde_db.t_non_encrypt_4; +UNINSTALL PLUGIN keyring_vault; +CREATE TABLE tde_db.t_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +#----------------------------------------------------------------------- +# Test 4 : Restart with new keyring_data_file +# Old encrypt table not accessible , non encrypt tables accessible +# And creation of new encrypt,non encrypt table is also posible +# restart with --early-plugin-load to load initial data +# restart: +SELECT PLUGIN_NAME,PLUGIN_VERSION,PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE plugin_name='keyring_vault'; +PLUGIN_NAME PLUGIN_VERSION PLUGIN_STATUS +keyring_vault 1.0 ACTIVE +# init tables +call tde_db.init_setup(); +/* select tde_db.t_encrypt */ +/* select tde_db.t_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt */ +/* select tde_db.t_non_encrypt */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_encrypt_2 */ +/* select tde_db.t_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +5 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +10 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +/* select tde_db.t_non_encrypt_2 */ +/* select tde_db.t_non_encrypt_2 */ +COUNT(*) +64 +c2 right(c3,20) c4 c5 c6 ST_AsText(c7) +1 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +2 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +3 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +4 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +6 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +7 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +8 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +9 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +13 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +14 aaaaaaaaaaaaaaaa0001 {"key_a": 1, "key_b": 2, "key_c": 3} 1 2 POINT(383293632 1754448) +# restart with with different keyring_file_data file +# restart: +# encrypt table not accessible +SELECT COUNT(*) FROM tde_db.t_encrypt; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. +# NON encrypt table are accessible +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +# new encrypt table is possible +CREATE TABLE tde_db.t_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +# non encrypt table possible +CREATE TABLE tde_db.t_non_encrypt_4(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +INSERT INTO tde_db.t_encrypt_4(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_non_encrypt; +SELECT COUNT(*) FROM tde_db.t_encrypt_4; +COUNT(*) +64 +INSERT INTO tde_db.t_non_encrypt_4(c3,c4,c7) SELECT c3,c4,c7 FROM tde_db.t_non_encrypt; +SELECT COUNT(*) FROM tde_db.t_non_encrypt_4; +COUNT(*) +64 +ALTER INSTANCE ROTATE INNODB MASTER KEY; +# old encrypt table not accessible +SELECT COUNT(*) FROM tde_db.t_encrypt; +Got one of the listed errors +SELECT COUNT(*) FROM tde_db.t_encrypt_2; +Got one of the listed errors +# NON encrypt old table are accessible +SELECT COUNT(*) FROM tde_db.t_non_encrypt; +COUNT(*) +64 +SELECT COUNT(*) FROM tde_db.t_non_encrypt_2; +COUNT(*) +64 +# new encrypt table accessible +SELECT COUNT(*) FROM tde_db.t_encrypt_4; +COUNT(*) +64 +# new NON encrypt table accessible +SELECT COUNT(*) FROM tde_db.t_non_encrypt_4; +COUNT(*) +64 +DROP TABLE tde_db.t_encrypt , tde_db.t_encrypt_2 ,tde_db.t_encrypt_4; +DROP TABLE tde_db.t_non_encrypt , tde_db.t_non_encrypt_2 ,tde_db.t_non_encrypt_4; +DROP DATABASE tde_db; +# Starting server without keyring +# restart: +SET GLOBAL innodb_file_per_table=1; diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_5.test b/plugin/keyring_vault/tests/mtr/table_encrypt_5.test new file mode 100644 index 000000000000..7e12a0ff6267 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_5.test @@ -0,0 +1,29 @@ +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); + +--source include/have_keyring_vault_plugin.inc +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc + +--let $keyring_plugin_name=keyring_vault +--let $keyring1_restart_param= restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_1 $KEYRING_VAULT_PLUGIN_OPT +--let $keyring2_restart_param= restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE_2 $KEYRING_VAULT_PLUGIN_OPT +--let $install_keyring_statement=INSTALL PLUGIN keyring_vault SONAME '$KEYRING_VAULT_PLUGIN' +--let $select_keyring_variable_statement=SELECT @@global.keyring_vault_config +--let $set_keyring_variable_to_keyring1=SET @@global.keyring_vault_config='$KEYRING_CONF_FILE_1' +--source include/table_encrypt_5.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_2 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_debug.result b/plugin/keyring_vault/tests/mtr/table_encrypt_debug.result new file mode 100644 index 000000000000..0183e6a57494 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_debug.result @@ -0,0 +1,118 @@ +DROP DATABASE IF EXISTS tde_db; +CREATE DATABASE tde_db; +USE tde_db; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE tde_db.t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int DEFAULT NULL, + `c2` char(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO tde_db.t1 VALUES(0, "aaaaa"); +INSERT INTO tde_db.t1 VALUES(1, "bbbbb"); +INSERT INTO tde_db.t1 VALUES(2, "ccccc"); +INSERT INTO tde_db.t1 VALUES(3, "ddddd"); +INSERT INTO tde_db.t1 VALUES(4, "eeeee"); +INSERT INTO tde_db.t1 VALUES(5, "fffff"); +INSERT INTO tde_db.t1 VALUES(6, "ggggg"); +INSERT INTO tde_db.t1 VALUES(7, "hhhhh"); +INSERT INTO tde_db.t1 VALUES(8, "iiiii"); +INSERT INTO tde_db.t1 VALUES(9, "jjjjj"); +SET SESSION debug="+d,ib_crash_during_rotation_for_encryption"; +CREATE TABLE tde_db.t2(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +SHOW CREATE TABLE tde_db.t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `c1` int DEFAULT NULL, + `c2` char(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +INSERT INTO tde_db.t2 VALUES(0, "aaaaa"); +INSERT INTO tde_db.t2 VALUES(1, "bbbbb"); +ALTER INSTANCE ROTATE INNODB MASTER KEY; +ERROR HY000: Lost connection to MySQL server during query +SET SESSION debug="-d,ib_crash_during_rotation_for_encryption"; +SELECT * FROM tde_db.t1 ORDER BY c1 LIMIT 5; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +INSERT INTO tde_db.t1 VALUES(12, "mmmmm"); +SELECT * FROM tde_db.t1 ORDER BY c1 ; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +12 mmmmm +SELECT * FROM tde_db.t2 ORDER BY c1 ; +c1 c2 +0 aaaaa +1 bbbbb +ALTER INSTANCE ROTATE INNODB MASTER KEY; +SELECT * FROM tde_db.t1 ORDER BY c1 ; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +12 mmmmm +DROP TABLE tde_db.t2; +SET SESSION debug="+d,ib_crash_during_create_for_encryption"; +CREATE TABLE tde_db.t2(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB; +ERROR HY000: Lost connection to MySQL server during query +SET SESSION debug="-d,ib_crash_during_create_for_encryption"; +SELECT * FROM tde_db.t1 ORDER BY c1 LIMIT 5; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +INSERT INTO tde_db.t1 VALUES(13, "nnnnn"); +SELECT * FROM tde_db.t1 ORDER BY c1 ; +c1 c2 +0 aaaaa +1 bbbbb +2 ccccc +3 ddddd +4 eeeee +5 fffff +6 ggggg +7 hhhhh +8 iiiii +9 jjjjj +12 mmmmm +13 nnnnn +SELECT * FROM tde_db.t2; +ERROR 42S02: Table 'tde_db.t2' doesn't exist +FLUSH TABLE tde_db.t1 FOR EXPORT; +UNLOCK TABLES; +ALTER TABLE tde_db.t1 DISCARD TABLESPACE; +SET SESSION debug="+d,ib_crash_during_decrypt_page"; +ALTER TABLE tde_db.t1 IMPORT TABLESPACE; +ERROR HY000: Lost connection to MySQL server during query +SET SESSION debug="-d,ib_crash_during_decrypt_page"; +INSERT INTO tde_db.t1 VALUES(11, "lllll"); +ERROR HY000: Tablespace has been discarded for table 't1' +SELECT * FROM tde_db.t1 ORDER BY c1 ; +ERROR HY000: Tablespace has been discarded for table 't1' +DROP TABLE tde_db.t1; +SET GLOBAL innodb_file_per_table=1; diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_debug.test b/plugin/keyring_vault/tests/mtr/table_encrypt_debug.test new file mode 100644 index 000000000000..a9547c678828 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_debug.test @@ -0,0 +1,17 @@ +--source include/have_keyring_vault_plugin.inc +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--let $keyring_restart_param=restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE $KEYRING_VAULT_PLUGIN_OPT +--source include/table_encrypt_debug.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_kill.result b/plugin/keyring_vault/tests/mtr/table_encrypt_kill.result new file mode 100644 index 000000000000..b4bda226030c --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_kill.result @@ -0,0 +1,197 @@ +call mtr.add_suppression("\\[ERROR\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("ibd can't be decrypted , please confirm the keyfile is match and keyring plugin is loaded"); +call mtr.add_suppression("Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("does not exist in the InnoDB internal data dictionary though MySQL is trying to drop it"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +call mtr.add_suppression("ibd can't be decrypted , please confirm the keyfile is match and keyring plugin is loaded"); +call mtr.add_suppression("Can't generate new master key for tablespace encryption, please check the keyring plugin is loaded."); +call mtr.add_suppression("InnoDB: Operating system error number"); +call mtr.add_suppression("The error means the system cannot find the path specified"); +call mtr.add_suppression("InnoDB: If you are installing InnoDB, remember that you must create directories yourself, InnoDB does not create them"); +call mtr.add_suppression("Could not find a valid tablespace file"); +call mtr.add_suppression("InnoDB: Ignoring tablespace"); +call mtr.add_suppression("InnoDB: Failed to find tablespace for table"); +call mtr.add_suppression("InnoDB: Cannot open table tde_db/t_encrypt.* from the internal data dictionary of InnoDB though the .frm file for the table exists"); +call mtr.add_suppression("\\[ERROR\\] InnoDB: Table tde_db/t_encrypt.* in the InnoDB data dictionary has tablespace id .*, but tablespace with that id or name does not exist"); +call mtr.add_suppression("\\[Warning\\] InnoDB: Please refer to .* for how to resolve the issue"); +call mtr.add_suppression("Cannot create keyring directory"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_file reported: 'Could not create keyring directory"); +# Starting server with keyring plugin +DROP DATABASE IF EXISTS tde_db; +DROP TABLE IF EXISTS tde_db. t_encrypt; +CREATE DATABASE tde_db; +USE tde_db; +SET GLOBAL innodb_file_per_table = 1; +SELECT @@innodb_file_per_table; +@@innodb_file_per_table +1 +CREATE TABLE tde_db.t_encrypt(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENCRYPTION="Y" ENGINE = InnoDB; +CREATE TABLE tde_db.t_non_encrypt(c2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, +c3 CHAR(255) Default 'No text', +c4 JSON , +c5 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_a')) STORED, +c6 INT GENERATED ALWAYS AS (JSON_EXTRACT(c4,'$.key_b')) VIRTUAL, +c7 POINT NOT NULL, +spatial INDEX idx2 (c7) +) ENGINE = InnoDB; +CREATE PROCEDURE tde_db.populate_t_encrypt() +begin +declare i int default 1; +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1062 SET has_error = 1; +while (i <= 5000) DO +insert into tde_db.t_encrypt(c2,c3,c4,c7) VALUES(i,CONCAT(REPEAT('a',200),LPAD(CAST(i AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.populate_t_non_encrypt() +begin +declare i int default 1; +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1062 SET has_error = 1; +while (i <= 5000) DO +insert into tde_db.t_non_encrypt(c2,c3,c4,c7) VALUES(i,CONCAT(REPEAT('a',200),LPAD(CAST(i AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.update_t_encrypt() +begin +declare i int default 1; +declare ustr varchar(1000); +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1062 SET has_error = 1; +while (i <= 5000) DO +SET @sql_text = CONCAT (' UPDATE tde_db.t_encrypt SET c3 =' , 'CONCAT(REPEAT(a,200),LPAD(CAST(',i, 'AS CHAR),4,0) ORDER BY RAND() LIMIT 1'); +PREPARE stmt FROM @sql_text; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.delete_t_encrypt() +begin +declare i int default 1; +declare ustr varchar(1000); +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1062 SET has_error = 1; +while (i <= 5000) DO +SET @sql_text = CONCAT (' DELETE FROM tde_db.t_encrypt LIMIT 1'); +PREPARE stmt FROM @sql_text; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.read_t_encrypt() +begin +declare i int default 1; +while (i <= 5000) DO +SELECT * FROM (SELECT * FROM tde_db.t_encrypt ORDER BY RAND() LIMIT 1) AS A WHERE A.c2 < 0 ; +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.alter_t_encrypt() +begin +declare i int default 1; +declare has_error int default 0; +while (i <= 5000) DO +ALTER INSTANCE ROTATE INNODB MASTER KEY; +set i = i + 1; +end while; +end| +CREATE PROCEDURE tde_db.create_t_encrypt(encrypt VARCHAR(5), tcnt INT) +begin +declare i int default 1; +declare has_error int default 0; +DECLARE CONTINUE HANDLER FOR 1050 SET has_error = 1; +SET i = tcnt ; +while (i <= 5000) DO +SET @sql_text = CONCAT('CREATE TABLE ',CONCAT('tde_db.t_encrypt_',encrypt,'_',i),' (c1 INT) ENCRYPTION="',encrypt,'"' ,' ENGINE=InnoDB'); +PREPARE stmt FROM @sql_text; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +set i = i + 1; +end while; +end| +SHOW CREATE TABLE tde_db.t_encrypt; +Table Create Table +t_encrypt CREATE TABLE `t_encrypt` ( + `c2` int NOT NULL AUTO_INCREMENT, + `c3` char(255) DEFAULT 'No text', + `c4` json DEFAULT NULL, + `c5` int GENERATED ALWAYS AS (json_extract(`c4`,_utf8mb4'$.key_a')) STORED, + `c6` int GENERATED ALWAYS AS (json_extract(`c4`,_utf8mb4'$.key_b')) VIRTUAL, + `c7` point NOT NULL /*!80003 SRID 0 */, + PRIMARY KEY (`c2`), + SPATIAL KEY `idx2` (`c7`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ENCRYPTION='Y' +# Case1: insert on encrypt and non encrypt table in parallel during kill +# In connection con1 - Running insert on encrypt table +call tde_db.populate_t_encrypt(); +# In connection con2 - Running insert on encrypt table +call tde_db.populate_t_encrypt(); +# In connection con3 - Running insert into non encrypt table +call tde_db.populate_t_non_encrypt(); +# kill and restart the server +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +INSERT INTO tde_db.t_non_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT 1; +1 +1 +# Case2: insert/update/delete on encrypt in parallel during kill +# In connection con1 - Running insert on encrypt table +call tde_db.populate_t_encrypt(); +# In connection con2 - Running update on encrypt table +call tde_db.update_t_encrypt(); +# In connection con3 - Running delete on encrypt table +call tde_db.delete_t_encrypt(); +# kill and restart the server +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +INSERT INTO tde_db.t_non_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT 1; +1 +1 +# Case3: select on encrypt in parallel during kill +# In connection con1 - Running select on encrypt table +call tde_db.read_t_encrypt(); +# In connection con2 - Running select on encrypt table +call tde_db.read_t_encrypt(); +# In connection con3 - Running select on encrypt table +call tde_db.read_t_encrypt(); +# kill and restart the server +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +INSERT INTO tde_db.t_non_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT 1; +1 +1 +# Case3: select and insert on encrypt in parallel during kill +# In connection con1 - Running insert on encrypt table +call tde_db.populate_t_encrypt(); +# In connection con2 - Running select on encrypt table +call tde_db.read_t_encrypt(); +# In connection con3 - Running select on encrypt table +call tde_db.create_t_encrypt("Y",1); +# kill and restart the server +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_encrypt LIMIT 10; +INSERT INTO tde_db.t_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT c2,right(c3,20),c4,c5,c6,ST_AsText(c7) FROM tde_db.t_non_encrypt LIMIT 10; +INSERT INTO tde_db.t_non_encrypt(c3,c4,c7) VALUES(CONCAT(REPEAT('a',200),LPAD(CAST(1 AS CHAR),4,'0')),'{ "key_a": 1, "key_b": 2, "key_c": 3 }',ST_GeomFromText('POINT(383293632 1754448)')); +SELECT 1; +1 +1 +# restart server without keyring +# restart: +SET GLOBAL innodb_file_per_table=1; diff --git a/plugin/keyring_vault/tests/mtr/table_encrypt_kill.test b/plugin/keyring_vault/tests/mtr/table_encrypt_kill.test new file mode 100644 index 000000000000..d75511cede5c --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/table_encrypt_kill.test @@ -0,0 +1,17 @@ +--source include/have_keyring_vault_plugin.inc +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--let $keyring_restart_param=restart: --early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --loose-keyring_vault_config=$KEYRING_CONF_FILE $KEYRING_VAULT_PLUGIN_OPT +--source include/table_encrypt_kill.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault-master.opt b/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault-master.opt new file mode 100644 index 000000000000..1f8fd2247925 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault-master.opt @@ -0,0 +1,2 @@ +$KEYRING_VAULT_PLUGIN_OPT +$KEYRING_VAULT_PLUGIN_LOAD diff --git a/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault.result b/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault.result new file mode 100644 index 000000000000..e43e28b1059f --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault.result @@ -0,0 +1,68 @@ +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); +call mtr.add_suppression("\\[Error\\] InnoDB: Encryption can't find master key, please check the keyring plugin is loaded."); +# restart: +CREATE TEMPORARY TABLE t01 (a TEXT) ENGINE=InnoDB; +INSERT INTO t01 VALUES ('Lorem ipsum dolor sit amet, consectetur adipiscing elit'); +Pattern found. +CREATE TEMPORARY TABLE t04 (a TEXT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED; +INSERT INTO t04 VALUES ('Praesent tristique eros a tempus fringilla'); +Pattern found. +DROP TABLE t04; +SET GLOBAL innodb_temp_tablespace_encrypt = ON; +CREATE TEMPORARY TABLE t02 (a INT) ENGINE=InnoDB ENCRYPTION='Y'; +ERROR HY000: InnoDB: Unsupported encryption option for temporary tables. +CREATE TEMPORARY TABLE t03 (a TEXT) ENGINE=InnoDB ENCRYPTION='N'; +INSERT INTO t03 VALUES ('Curabitur laoreet, velit non interdum venenatis'); +Pattern not found. +CREATE TEMPORARY TABLE t04 (a TEXT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED; +INSERT INTO t04 VALUES ('Praesent tristique eros a tempus fringilla'); +Pattern not found. +CREATE TEMPORARY TABLE t05 (a INT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED ENCRYPTION='y'; +ERROR HY000: InnoDB: Unsupported encryption option for temporary tables. +CREATE TEMPORARY TABLE t06 (a INT) ENGINE=InnoDB ROW_FORMAT=COMPRESSED ENCRYPTION='n'; +INSERT INTO t06 VALUES (1), (2), (3); +SET GLOBAL innodb_temp_tablespace_encrypt = OFF; +CREATE TEMPORARY TABLE t07 (a INT) ENGINE=InnoDB; +INSERT INTO t07 VALUES (1), (2), (3); +SET GLOBAL innodb_temp_tablespace_encrypt = ON; +CREATE TABLE t10 (a INT AUTO_INCREMENT PRIMARY KEY, b INT); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) VALUES (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)), (FLOOR(RAND() * 10000)); +INSERT INTO t10 (b) SELECT b FROM t10; +INSERT INTO t10 (b) SELECT b FROM t10; +INSERT INTO t10 (b) SELECT b FROM t10; +INSERT INTO t10 (b) SELECT b FROM t10; +CREATE INDEX t10_b ON t10 (b); +DROP INDEX t10_b ON t10; +CREATE INDEX t10_b ON t10 (b) ALGORITHM=COPY; +DROP INDEX t10_b ON t10 ALGORITHM=COPY; +# restart: +CREATE TEMPORARY TABLE t01 (a INT) ENGINE=InnoDB; +INSERT INTO t01 VALUES (1), (2), (3); +# Kill and restart: +CREATE TEMPORARY TABLE t01 (a INT) ENGINE=InnoDB; +INSERT INTO t01 VALUES (1), (2), (3); +# restart: +CREATE TEMPORARY TABLE t01 (a INT) ENGINE=InnoDB; +INSERT INTO t01 VALUES (1), (2), (3); +# restart: +CREATE TEMPORARY TABLE t01 (a INT) ENGINE=InnoDB; +INSERT INTO t01 VALUES (1), (2), (3); +DROP TABLE t10; +# restart: --innodb-temp-tablespace-encrypt +CREATE TABLE t1(a INT key) ENGINE = MEMORY; +INSERT INTO t1 VALUES (11061); +INSERT INTO t1 VALUES (3); +SET big_tables=1; +SELECT * FROM t1 WHERE a IN(SELECT MAX(a) FROM t1); +ERROR HY000: Can't find master key from keyring, please check keyring plugin is loaded. diff --git a/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault.test b/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault.test new file mode 100644 index 000000000000..39d23f036845 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/temp_table_encrypt_keyring_vault.test @@ -0,0 +1,24 @@ +# test temporary tables encryption with keyring_file plugin + +--source include/have_keyring_vault_plugin.inc + +--source generate_default_conf_files.inc +--source is_vault_server_up.inc + +# Create mount points +--let MOUNT_POINT_SERVICE_OP=CREATE +--let KEYRING_CONF_FILE=$KEYRING_CONF_FILE_1 +--source mount_point_service.inc + +--let $keyring_restart_param=restart:--early-plugin-load="keyring_vault=$KEYRING_VAULT_PLUGIN" --keyring_vault_config=$KEYRING_CONF_FILE $KEYRING_VAULT_PLUGIN_OPT + +call mtr.add_suppression("\\[ERROR\\] Function 'keyring_vault' already exists"); +call mtr.add_suppression("\\[ERROR\\] Couldn't load plugin named 'keyring_vault' with soname 'keyring_vault.*'."); +call mtr.add_suppression("Plugin keyring_vault reported"); + +--source suite/innodb/include/temp_table_encrypt.inc + +# Delete mount points +--let MOUNT_POINT_SERVICE_OP=DELETE +--let $KEYRING_CONF_FILE=$KEYRING_CONF_FILE +--source mount_point_service.inc diff --git a/plugin/keyring_vault/tests/mtr/timeout_basic-master.opt b/plugin/keyring_vault/tests/mtr/timeout_basic-master.opt new file mode 100644 index 000000000000..3b441dfbebf0 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/timeout_basic-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT $KEYRING_VAULT_PLUGIN_LOAD diff --git a/plugin/keyring_vault/tests/mtr/timeout_basic.result b/plugin/keyring_vault/tests/mtr/timeout_basic.result new file mode 100644 index 000000000000..6612f332aa47 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/timeout_basic.result @@ -0,0 +1,88 @@ +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); +SET @start_global_value = @@GLOBAL.keyring_vault_timeout; +SET @@GLOBAL.keyring_vault_timeout = 100; +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; +SELECT @@GLOBAL.keyring_vault_timeout; +@@GLOBAL.keyring_vault_timeout +15 +SELECT @@session.keyring_vault_timeout; +ERROR HY000: Variable 'keyring_vault_timeout' is a GLOBAL variable +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; +include/assert.inc [Default vaule of keyring_vault_timeout should be 15] +# Disable keyring_vault's timeout +SET @@GLOBAL.keyring_vault_timeout= 0; +include/assert.inc [keyring_vault timeout should be disabled (i.e. equal to 0)] +# Turn keyring_vault's timeout and set it to minimal value, i.e. 1s +SET @@GLOBAL.keyring_vault_timeout= 1; +include/assert.inc [keyring_vault timeout should be enabled and set to min (i.e. equal to 1)] +# Set keyring_vault's timeout and set it to maximum value, i.e. 86400s (24 hours) +SET @@GLOBAL.keyring_vault_timeout= 86400; +include/assert.inc [keyring_vault timeout should be set to max (i.e. equal to 86400)] +# Set keyring_vault's timeout to DEFAULT, i.e. 15s +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; +SET @@GLOBAL.keyring_vault_timeout = -1; +Warnings: +Warning 1292 Truncated incorrect keyring_vault_timeout value: '-1' +include/assert.inc [keyring_vault timeout should be 0] +SET @@GLOBAL.keyring_vault_timeout = -1024; +Warnings: +Warning 1292 Truncated incorrect keyring_vault_timeout value: '-1024' +include/assert.inc [keyring_vault timeout should be 0] +# Set to max + 1 +SET @@GLOBAL.keyring_vault_timeout= 86400; +SELECT @@GLOBAL.keyring_vault_timeout; +@@GLOBAL.keyring_vault_timeout +86400 +include/assert.inc [keyring_vault timeout should be set to max (i.e. equal to 86400)] +SET @@GLOBAL.keyring_vault_timeout= 123456789031; +Warnings: +Warning 1292 Truncated incorrect keyring_vault_timeout value: '123456789031' +include/assert.inc [keyring_vault timeout should be set to max (i.e. equal to 86400)] +# Set keyring_vault's timeout to DEFAULT, i.e. 15s +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; +SET @@GLOBAL.keyring_vault_timeout = ' '; +ERROR 42000: Incorrect argument type to variable 'keyring_vault_timeout' +# keyring_vault_timeout should not be changed +include/assert.inc [keyring_vault timeout should be set to default (i.e. equal to 15)] +SET @@GLOBAL.keyring_vault_timeout = ON; +ERROR 42000: Incorrect argument type to variable 'keyring_vault_timeout' +# keyring_vault_timeout should not be changed +include/assert.inc [keyring_vault timeout should be set to default (i.e. equal to 15)] +SET @@GLOBAL.keyring_vault_timeout = OFF; +ERROR 42000: Incorrect argument type to variable 'keyring_vault_timeout' +# keyring_vault_timeout should not be changed +include/assert.inc [keyring_vault timeout should be set to default (i.e. equal to 15)] +SET @@GLOBAL.keyring_vault_timeout = 65530.34; +ERROR 42000: Incorrect argument type to variable 'keyring_vault_timeout' +# keyring_vault_timeout should not be changed +include/assert.inc [keyring_vault timeout should be set to default (i.e. equal to 15)] +SET @@GLOBAL.keyring_vault_timeout ="Test"; +ERROR 42000: Incorrect argument type to variable 'keyring_vault_timeout' +# keyring_vault_timeout should not be changed +include/assert.inc [keyring_vault timeout should be set to default (i.e. equal to 15)] +SET @@GLOBAL.keyring_vault_timeout = True; +# keyring_vault_timeout should be casted to 1 +include/assert.inc [keyring_vault_timeout should be casted to 1] +SET @@GLOBAL.keyring_vault_timeout = False; +# keyring_vault_timeout should be casted to 0 +include/assert.inc [keyring_vault_timeout should be casted to 0] +# Set keyring_vault's timeout to DEFAULT, i.e. 15s +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; +SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME='keyring_vault_timeout'; +VARIABLE_VALUE +15 +include/assert.inc [keyring_vault_timeout should match value in GLOBAL Table] +SET @@GLOBAL.keyring_vault_timeout = 100; +SET keyring_vault_timeout = 1027; +ERROR HY000: Variable 'keyring_vault_timeout' is a GLOBAL variable and should be set with SET GLOBAL +SET @@GLOBAL.keyring_vault_timeout = 1027; +SELECT @@keyring_vault_timeout; +@@keyring_vault_timeout +1027 +SELECT GLOBAL.keyring_vault_config; +ERROR 42S02: Unknown table 'GLOBAL' in field list +SELECT keyring_vault_timeout = @@GLOBAL.keyring_vault_timeout; +ERROR 42S22: Unknown column 'keyring_vault_timeout' in 'field list' +SET @@GLOBAL.keyring_vault_timeout = @start_global_value; diff --git a/plugin/keyring_vault/tests/mtr/timeout_basic.test b/plugin/keyring_vault/tests/mtr/timeout_basic.test new file mode 100644 index 000000000000..998340c59def --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/timeout_basic.test @@ -0,0 +1,184 @@ +############################################################################### +# # +# Variable Name: keyring_vault_timeout # +# Scope: GLOBAL # +# Access Type: Dynamic # +# Data Type: numeric # +# Default Value: 15 # +# Range: 0 - 86400 # +# # +############################################################################### + +--source include/have_keyring_vault_plugin.inc +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '' not found"); + +############################################################# +# Save initial value # +############################################################# + +SET @start_global_value = @@GLOBAL.keyring_vault_timeout; + +############################################################ +# Display the DEFAULT value of kering_vault__timeout # +############################################################ + +SET @@GLOBAL.keyring_vault_timeout = 100; +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; +SELECT @@GLOBAL.keyring_vault_timeout; + +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SELECT @@session.keyring_vault_timeout; + +############################################################ +# Check the DEFAULT value of keyring_vault_timeout # +############################################################ + +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; + +--let $assert_text= Default vaule of keyring_vault_timeout should be 15 +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 15]" = 1 +--source include/assert.inc + +############################################################################### +# Change the value of keyring_vault_timeout to a valid value for GLOBAL Scope # +############################################################################### + +--echo # Disable keyring_vault's timeout +SET @@GLOBAL.keyring_vault_timeout= 0; +--let $assert_text= keyring_vault timeout should be disabled (i.e. equal to 0) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 0]" = 1 +--source include/assert.inc + +--echo # Turn keyring_vault's timeout and set it to minimal value, i.e. 1s +SET @@GLOBAL.keyring_vault_timeout= 1; +--let $assert_text= keyring_vault timeout should be enabled and set to min (i.e. equal to 1) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 1]" = 1 +--source include/assert.inc + +--echo # Set keyring_vault's timeout and set it to maximum value, i.e. 86400s (24 hours) +SET @@GLOBAL.keyring_vault_timeout= 86400; +--let $assert_text= keyring_vault timeout should be set to max (i.e. equal to 86400) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 86400]" = 1 +--source include/assert.inc + +--echo # Set keyring_vault's timeout to DEFAULT, i.e. 15s +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; + +################################################################# +# Change the value of keyring_vault_timeout to an invalid value # +################################################################# + +SET @@GLOBAL.keyring_vault_timeout = -1; +--let $assert_text= keyring_vault timeout should be 0 +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 0]" = 1 +--source include/assert.inc +SET @@GLOBAL.keyring_vault_timeout = -1024; +--let $assert_text= keyring_vault timeout should be 0 +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 0]" = 1 +--source include/assert.inc +--echo # Set to max + 1 +SET @@GLOBAL.keyring_vault_timeout= 86400; +SELECT @@GLOBAL.keyring_vault_timeout; +--let $assert_text= keyring_vault timeout should be set to max (i.e. equal to 86400) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 86400]" = 1 +--source include/assert.inc + +SET @@GLOBAL.keyring_vault_timeout= 123456789031; +--let $assert_text= keyring_vault timeout should be set to max (i.e. equal to 86400) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 86400]" = 1 +--source include/assert.inc + +--echo # Set keyring_vault's timeout to DEFAULT, i.e. 15s +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; + +--Error ER_WRONG_TYPE_FOR_VAR +SET @@GLOBAL.keyring_vault_timeout = ' '; +--echo # keyring_vault_timeout should not be changed +--let $assert_text= keyring_vault timeout should be set to default (i.e. equal to 15) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 15]" = 1 +--source include/assert.inc + +--Error ER_WRONG_TYPE_FOR_VAR +SET @@GLOBAL.keyring_vault_timeout = ON; + +--echo # keyring_vault_timeout should not be changed +--let $assert_text= keyring_vault timeout should be set to default (i.e. equal to 15) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 15]" = 1 +--source include/assert.inc + +--Error ER_WRONG_TYPE_FOR_VAR +SET @@GLOBAL.keyring_vault_timeout = OFF; +--echo # keyring_vault_timeout should not be changed +--let $assert_text= keyring_vault timeout should be set to default (i.e. equal to 15) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 15]" = 1 +--source include/assert.inc + +--Error ER_WRONG_TYPE_FOR_VAR +SET @@GLOBAL.keyring_vault_timeout = 65530.34; +--echo # keyring_vault_timeout should not be changed +--let $assert_text= keyring_vault timeout should be set to default (i.e. equal to 15) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 15]" = 1 +--source include/assert.inc + +--Error ER_WRONG_TYPE_FOR_VAR +SET @@GLOBAL.keyring_vault_timeout ="Test"; +--echo # keyring_vault_timeout should not be changed +--let $assert_text= keyring_vault timeout should be set to default (i.e. equal to 15) +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 15]" = 1 +--source include/assert.inc + +SET @@GLOBAL.keyring_vault_timeout = True; +--echo # keyring_vault_timeout should be casted to 1 +--let $assert_text= keyring_vault_timeout should be casted to 1 +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 1]" = 1 +--source include/assert.inc + +SET @@GLOBAL.keyring_vault_timeout = False; +--echo # keyring_vault_timeout should be casted to 0 +--let $assert_text= keyring_vault_timeout should be casted to 0 +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = 0]" = 1 +--source include/assert.inc + +--echo # Set keyring_vault's timeout to DEFAULT, i.e. 15s +SET @@GLOBAL.keyring_vault_timeout = DEFAULT; + +#################################################################### +# Check if the value in GLOBAL Table matches value in variable # +#################################################################### +SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME='keyring_vault_timeout'; +--disable_warnings +--let $assert_text= keyring_vault_timeout should match value in GLOBAL Table +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME=\'keyring_vault_timeout\']" = 1 +--source include/assert.inc +--enable_warnings + +######################################################################## +# Check if accessing variable with GLOBAL and without SCOPE # +# points to same global variable # +######################################################################## + +SET @@GLOBAL.keyring_vault_timeout = 100; +--let $assert_text= keyring_vault_timeout with GLOBAL and without SCOPE should point to same global variable +--let $assert_cond= "[SELECT @@GLOBAL.keyring_vault_timeout = @@keyring_vault_timeout]" = 1 + +############################################################################# +# Check if keyring_vault_timeout can be accessed with and without @@ sign # +############################################################################# + +--error ER_GLOBAL_VARIABLE +SET keyring_vault_timeout = 1027; +SET @@GLOBAL.keyring_vault_timeout = 1027; +SELECT @@keyring_vault_timeout; + +--Error ER_UNKNOWN_TABLE +SELECT GLOBAL.keyring_vault_config; + +--Error ER_BAD_FIELD_ERROR +SELECT keyring_vault_timeout = @@GLOBAL.keyring_vault_timeout; + +#################################### +# Restore initial value # +#################################### +SET @@GLOBAL.keyring_vault_timeout = @start_global_value; diff --git a/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config-master.opt b/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config-master.opt new file mode 100644 index 000000000000..20465603d82b --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config-master.opt @@ -0,0 +1 @@ +$KEYRING_VAULT_PLUGIN_OPT $KEYRING_VAULT_PLUGIN_LOAD --loose-keyring_vault_config=../bad_dir/../../bad_keyring diff --git a/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config.result b/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config.result new file mode 100644 index 000000000000..6a94f562417d --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config.result @@ -0,0 +1,3 @@ +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '../bad_dir/../../bad_keyring' not found"); diff --git a/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config.test b/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config.test new file mode 100644 index 000000000000..ed50a67cc470 --- /dev/null +++ b/plugin/keyring_vault/tests/mtr/wrong_keyring_vault_config.test @@ -0,0 +1,10 @@ +--source include/have_keyring_vault_plugin.inc + +# This test checks behavior of keyring_vault plugin when it is initialized on server startup and +# incorrect path to configuration file is assigned to keyring_vault_config variable. The path is +# set in master.opt file. + +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'Could not open file with credentials.'"); +call mtr.add_suppression("\\[Error\\] Plugin keyring_vault reported: 'keyring_vault initialization failure."); +call mtr.add_suppression("\\[ERROR\\] Plugin keyring_vault reported: 'File '../bad_dir/../../bad_keyring' not found"); + diff --git a/plugin/keyring_vault/vault_base64.cc b/plugin/keyring_vault/vault_base64.cc new file mode 100644 index 000000000000..c234b6cdaf66 --- /dev/null +++ b/plugin/keyring_vault/vault_base64.cc @@ -0,0 +1,78 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#include "vault_base64.h" +#include +#include +#include "base64.h" +#include "boost/scoped_array.hpp" + +namespace keyring { +bool Vault_base64::encode(const void *src, size_t src_len, + Secure_string *encoded, Format format) { + uint64 memory_needed = base64_needed_encoded_length(src_len); + boost::scoped_array base64_encoded_text(new char[memory_needed]); + // Using scoped_array instead of vector or string as those containers do not + // provide access to underlying data when they are empty. Calling reserve on + // those containers does not help. + if (::base64_encode(src, src_len, base64_encoded_text.get()) != 0) { + memset_s(base64_encoded_text.get(), memory_needed, 0, memory_needed); + return true; + } + if (format == Format::SINGLE_LINE) { + char *new_end = + std::remove(base64_encoded_text.get(), + base64_encoded_text.get() + memory_needed, '\n'); + memory_needed = new_end - base64_encoded_text.get(); + } + // base64 encode below returns data with NULL terminating string - which we do + // not care about + encoded->assign(base64_encoded_text.get(), memory_needed - 1); + memset_s(base64_encoded_text.get(), memory_needed, 0, memory_needed); + + return false; +} + +bool Vault_base64::decode(const Secure_string &src, Secure_string *dst) { + char *data; + uint64 data_length; + if (decode(src, &data, &data_length)) return true; + dst->assign(data, data_length); + memset_s(data, data_length, 0, data_length); + delete[] data; + return false; +} + +bool Vault_base64::decode(const Secure_string &src, char **dst, + uint64 *dst_length) { + uint64 base64_length_of_memory_needed_for_decode = + base64_needed_decoded_length(src.length()); + std::unique_ptr data( + new char[base64_length_of_memory_needed_for_decode]); + + int64 decoded_length = + ::base64_decode(src.c_str(), src.length(), data.get(), NULL, 0); + if (decoded_length <= 0) { + memset_s(data.get(), base64_length_of_memory_needed_for_decode, 0, + base64_length_of_memory_needed_for_decode); + return true; + } + *dst = data.release(); + *dst_length = decoded_length; + + return false; +} +} // namespace keyring diff --git a/plugin/keyring_vault/vault_base64.h b/plugin/keyring_vault/vault_base64.h new file mode 100644 index 000000000000..165584a03aff --- /dev/null +++ b/plugin/keyring_vault/vault_base64.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_VAULT_BASE64_H +#define MYSQL_VAULT_BASE64_H + +#include "plugin/keyring/common/secure_string.h" + +namespace keyring { +class Vault_base64 final { + public: + enum class Format { SINGLE_LINE, MULTI_LINE }; + static bool encode(const void *src, size_t src_len, Secure_string *encoded, + Format format); + static bool decode(const Secure_string &src, Secure_string *dst); + // It is caller responsibility to delete memory allocated with delete[] + static bool decode(const Secure_string &src, char **dst, uint64 *dst_length); +}; +} // namespace keyring + +#endif // MYSQL_VAULT_BASE64_H diff --git a/plugin/keyring_vault/vault_credentials.cc b/plugin/keyring_vault/vault_credentials.cc new file mode 100644 index 000000000000..ed4f354ec823 --- /dev/null +++ b/plugin/keyring_vault/vault_credentials.cc @@ -0,0 +1,32 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#include "vault_credentials.h" + +namespace keyring { + +static Secure_string empty_value; + +const Secure_string &get_credential(const Vault_credentials &credentials, + const Secure_string &key) { + Vault_credentials::const_iterator it = credentials.find(key); + if (it == credentials.end()) + return empty_value; + else + return it->second; +} + +} // namespace keyring diff --git a/plugin/keyring_vault/vault_credentials.h b/plugin/keyring_vault/vault_credentials.h new file mode 100644 index 000000000000..e0428bc4bae7 --- /dev/null +++ b/plugin/keyring_vault/vault_credentials.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_VAULT_CREDENTIALS +#define MYSQL_VAULT_CREDENTIALS + +#include +#include "plugin/keyring/common/secure_string.h" + +namespace keyring { +typedef std::map Vault_credentials; +const Secure_string &get_credential(const Vault_credentials &credentials, + const Secure_string &key); +} // namespace keyring + +#endif // MYSQL_VAULT_CREDENTIALS diff --git a/plugin/keyring_vault/vault_credentials_parser.cc b/plugin/keyring_vault/vault_credentials_parser.cc new file mode 100644 index 000000000000..9fa4e857d728 --- /dev/null +++ b/plugin/keyring_vault/vault_credentials_parser.cc @@ -0,0 +1,134 @@ +#include "vault_credentials_parser.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "plugin/keyring/file_io.h" + +namespace keyring { +const size_t Vault_credentials_parser::max_file_size = 10000; + +void Vault_credentials_parser::reset_vault_credentials( + Vault_credentials *vault_credentials) noexcept { + for (Vault_credentials::iterator iter = vault_credentials->begin(); + iter != vault_credentials->end(); ++iter) + iter->second.clear(); +} + +bool Vault_credentials_parser::is_valid_option( + const Secure_string &option) const noexcept { + return vault_credentials_in_progress.count(option) != 0; +} + +bool Vault_credentials_parser::parse_line( + uint line_number, const Secure_string &line, + Vault_credentials *vault_credentials) { + if (line.empty()) return false; + + size_t eq_sign_pos = line.find('='); + std::ostringstream err_ss; + + if (eq_sign_pos == std::string::npos) { + err_ss << "Could not parse credential file. Cannot find equal sign (=) in " + "line: "; + err_ss << line_number << '.'; + logger->log(MY_ERROR_LEVEL, err_ss.str().c_str()); + return true; + } + Secure_string option = line.substr(0, eq_sign_pos); + boost::trim(option); + + if (!is_valid_option(option)) { + err_ss << "Could not parse credential file. Unknown option \"" << option + << "\" in line: "; + err_ss << line_number << '.'; + return true; + } + Secure_string *value = &(*vault_credentials)[option]; + + if (!value->empty()) // repeated option in file + { + err_ss << "Could not parse credential file. Seems that value for option " + << option; + err_ss << " has been specified more than once in line: " << line_number + << '.'; + logger->log(MY_ERROR_LEVEL, err_ss.str().c_str()); + return true; + } + *value = line.substr(eq_sign_pos + 1, line.size() - (eq_sign_pos + 1)); + boost::trim(*value); + + if (value->empty()) { + err_ss << "Could not parse credential file. Seems there is no value " + "specified "; + err_ss << "for option " << option << " in line: " << line_number << '.'; + + logger->log(MY_ERROR_LEVEL, err_ss.str().c_str()); + return true; + } + return false; +} + +bool Vault_credentials_parser::parse(const std::string &file_url, + Vault_credentials *vault_credentials) { + reset_vault_credentials(&vault_credentials_in_progress); + + File_io file_io(logger); + File file = file_io.open(PSI_NOT_INSTRUMENTED, file_url.c_str(), O_RDONLY, + MYF(MY_WME)); + + if (file < 0) { + logger->log(MY_ERROR_LEVEL, "Could not open file with credentials."); + return true; + } + BOOST_SCOPE_EXIT(&file_io, &file) { file_io.close(file, MYF(MY_WME)); } + BOOST_SCOPE_EXIT_END + + if (file_io.seek(file, 0, MY_SEEK_END, MYF(MY_WME)) == MY_FILEPOS_ERROR) { + logger->log(MY_ERROR_LEVEL, "Could not read file with credentials."); + return true; + } + my_off_t file_size = file_io.tell(file, MYF(MY_WME)); + if (file_size == 0) { + logger->log(MY_ERROR_LEVEL, "Empty file with credentials."); + return true; + } + if (file_size > max_file_size || + file_io.seek(file, 0, MY_SEEK_SET, MYF(MY_WME)) == MY_FILEPOS_ERROR) { + logger->log(MY_ERROR_LEVEL, "Could not read file with credentials."); + return true; + } + std::unique_ptr buffer(new uchar[file_size]); + if (file_io.read(file, buffer.get(), file_size, MYF(MY_WME)) != file_size) { + logger->log(MY_ERROR_LEVEL, "Could not read file with credentials."); + return true; + } + Secure_string file_content(reinterpret_cast(buffer.get()), file_size); + Secure_istringstream credentials_ss(file_content); + uint line_number = 1; + Secure_string line; + while (!getline(credentials_ss, line).fail()) + if (parse_line(line_number, line, &vault_credentials_in_progress)) { + line_number++; + return true; + } + + for (Vault_credentials::const_iterator iter = + vault_credentials_in_progress.begin(); + iter != vault_credentials_in_progress.end(); ++iter) { + if (iter->second.empty() && optional_value.count(iter->first) == 0) { + std::ostringstream err_ss; + err_ss << "Could not read " << iter->first + << " from the configuration file."; + logger->log(MY_ERROR_LEVEL, err_ss.str().c_str()); + return true; + } + } + *vault_credentials = vault_credentials_in_progress; + return false; +} +} // namespace keyring diff --git a/plugin/keyring_vault/vault_credentials_parser.h b/plugin/keyring_vault/vault_credentials_parser.h new file mode 100644 index 000000000000..6e5e3fedfc01 --- /dev/null +++ b/plugin/keyring_vault/vault_credentials_parser.h @@ -0,0 +1,40 @@ +#ifndef MYSQL_VAULT_CREDENTIALS_PARSER_H +#define MYSQL_VAULT_CREDENTIALS_PARSER_H + +#include +#include +#include "plugin/keyring/common/logger.h" +#include "vault_credentials.h" + +namespace keyring { +class Vault_credentials_parser final { + public: + Vault_credentials_parser(ILogger *logger) : logger(logger) { + vault_credentials_in_progress.emplace(std::make_pair("vault_url", "")); + vault_credentials_in_progress.emplace( + std::make_pair("secret_mount_point", "")); + vault_credentials_in_progress.emplace(std::make_pair("vault_ca", "")); + vault_credentials_in_progress.emplace(std::make_pair("token", "")); + + optional_value.emplace("vault_ca"); + } + + bool parse(const std::string &file_url, Vault_credentials *vault_credentials); + + private: + void reset_vault_credentials(Vault_credentials *vault_credentials) noexcept; + + bool parse_line(uint line_number, const Secure_string &line, + Vault_credentials *vault_credentials); + + bool is_valid_option(const Secure_string &option) const noexcept; + Vault_credentials vault_credentials_in_progress; + std::set optional_value; + + ILogger *logger; + static const size_t max_file_size; +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_CREDENTIALS_PARSER_H diff --git a/plugin/keyring_vault/vault_curl.cc b/plugin/keyring_vault/vault_curl.cc new file mode 100644 index 000000000000..ad9e86c0beb4 --- /dev/null +++ b/plugin/keyring_vault/vault_curl.cc @@ -0,0 +1,350 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#include "vault_curl.h" +#include +#include +#include +#include "my_rdtsc.h" +#include "mysql/service_thd_wait.h" +#include "plugin/keyring/common/secure_string.h" +#include "sql/current_thd.h" +#include "sql/mysqld.h" +#include "sql/sql_error.h" +#include "vault_base64.h" + +namespace keyring { + +static const constexpr size_t max_response_size = 32000000; +static MY_TIMER_INFO curl_timer_info; +static ulonglong last_ping_time; +static bool was_thd_wait_started = false; +#ifndef NDEBUG +static const constexpr ulonglong slow_connection_threshold = 100; // [ms] +#endif + +class Thd_wait_end_guard { + public: + ~Thd_wait_end_guard() { + DBUG_EXECUTE_IF("vault_network_lag", { was_thd_wait_started = false; }); + assert(!was_thd_wait_started); + if (was_thd_wait_started) { + // This should never be called as thd_wait_end should be called at the end + // of CURL I/O operation. However, in production the call to thd_wait_end + // cannot be missed. Thus we limit our trust to CURL lib and make sure + // thd_wait_end was called. + thd_wait_end(current_thd); + was_thd_wait_started = false; + } + } +}; + +class Curl_session_guard : private boost::noncopyable { + public: + Curl_session_guard(CURL *curl) noexcept : curl(curl) {} + ~Curl_session_guard() { + if (curl != nullptr) curl_easy_cleanup(curl); + } + + private: + CURL *curl; +}; + +static size_t write_response_memory(void *contents, size_t size, size_t nmemb, + void *userp) noexcept { + size_t realsize = size * nmemb; + if (size != 0 && realsize / size != nmemb) return 0; // overflow + Secure_ostringstream *read_data = static_cast(userp); + size_t ss_pos = read_data->tellp(); + read_data->seekp(0, std::ios::end); + size_t number_of_read_bytes = read_data->tellp(); + read_data->seekp(ss_pos); + + if (number_of_read_bytes + realsize > max_response_size) + return 0; // response size limit exceeded + + read_data->write(static_cast(contents), realsize); + if (!read_data->good()) return 0; + return realsize; +} + +int progress_callback(void *clientp MY_ATTRIBUTE((unused)), double dltotal, + double dlnow, double ultotal MY_ATTRIBUTE((unused)), + double ulnow MY_ATTRIBUTE((unused))) noexcept { + ulonglong curr_ping_time = my_timer_milliseconds(); + + DBUG_EXECUTE_IF("vault_network_lag", { + curr_ping_time = last_ping_time + slow_connection_threshold + 10; + dltotal = 1; + dlnow = 0; + }); + + BOOST_SCOPE_EXIT(&curr_ping_time, &last_ping_time) { + last_ping_time = curr_ping_time; + } + BOOST_SCOPE_EXIT_END + + //***To keep compiler happy, remove when PS-244 gets resolved + (void) dltotal; + (void)dlnow; + //**** + + // The calls to threadpool are disabled till bug PS-244 gets resolved. + /* <--Uncomment when PS-244 gets resolved + if (!was_thd_wait_started) + { + if ((dlnow < dltotal || ulnow < ultotal) && last_ping_time - curr_ping_time + > slow_connection_threshold) + { + // there is a good chance that connection is slow, thus we can let know + the threadpool that there is time + // to start new thread(s) + thd_wait_begin(current_thd, THD_WAIT_NET); + was_thd_wait_started = true; + } + } + else if ((dlnow == dltotal && ulnow == ultotal) || last_ping_time - + curr_ping_time <= slow_connection_threshold) + { + // connection has speed up or we have finished transfering + thd_wait_end(current_thd); + was_thd_wait_started = false; + }*/ // <--Uncomment when PS-244 gets resolved + return 0; +} + +std::string Vault_curl::get_error_from_curl(CURLcode curl_code) { + size_t len = strlen(curl_errbuf); + std::ostringstream ss; + if (curl_code != CURLE_OK) { + ss << "CURL returned this error code: " << curl_code; + ss << " with error message : "; + if (len) + ss << curl_errbuf; + else + ss << curl_easy_strerror(curl_code); + } + return ss.str(); +} + +bool Vault_curl::init(const Vault_credentials &vault_credentials) { + this->token_header = + "X-Vault-Token:" + get_credential(vault_credentials, "token"); + this->vault_url = get_credential(vault_credentials, "vault_url") + "/v1/" + + get_credential(vault_credentials, "secret_mount_point"); + this->vault_ca = get_credential(vault_credentials, "vault_ca"); + if (this->vault_ca.empty()) { + logger->log( + MY_WARNING_LEVEL, + "There is no vault_ca specified in keyring_vault's configuration file. " + "Please make sure that Vault's CA certificate is trusted by the " + "machine from " + "which you intend to connect to Vault."); + } + my_timer_init(&curl_timer_info); + return false; +} + +bool Vault_curl::setup_curl_session(CURL *curl) { + CURLcode curl_res = CURLE_OK; + read_data_ss.str(""); + read_data_ss.clear(); + curl_errbuf[0] = '\0'; + if (list != nullptr) { + curl_slist_free_all(list); + list = nullptr; + } + + last_ping_time = my_timer_milliseconds(); + + if ((list = curl_slist_append(list, token_header.c_str())) == nullptr || + (list = curl_slist_append(list, "Content-Type: application/json")) == + nullptr || + (curl_res = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf)) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + write_response_memory)) != CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, + static_cast(&read_data_ss))) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list)) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1)) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L)) != + CURLE_OK || + (!vault_ca.empty() && + (curl_res = curl_easy_setopt(curl, CURLOPT_CAINFO, vault_ca.c_str())) != + CURLE_OK) || + (curl_res = curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL)) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, + progress_callback)) != CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L)) != CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout)) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout)) != + CURLE_OK) { + logger->log(MY_ERROR_LEVEL, get_error_from_curl(curl_res).c_str()); + return true; + } + return false; +} + +bool Vault_curl::list_keys(Secure_string *response) { + CURLcode curl_res = CURLE_OK; + long http_code = 0; + + Thd_wait_end_guard thd_wait_end_guard; + (void)thd_wait_end_guard; // silence unused variable error + + CURL *curl = curl_easy_init(); + if (curl == nullptr) { + logger->log(MY_ERROR_LEVEL, "Cannot initialize curl session"); + return true; + } + Curl_session_guard curl_session_guard(curl); + + if (setup_curl_session(curl) || + (curl_res = curl_easy_setopt(curl, CURLOPT_URL, + (vault_url + "?list=true").c_str())) != + CURLE_OK || + (curl_res = curl_easy_perform(curl)) != CURLE_OK || + (curl_res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, + &http_code)) != CURLE_OK) { + logger->log(MY_ERROR_LEVEL, get_error_from_curl(curl_res).c_str()); + return true; + } + if (http_code == 404) { + *response = ""; // no keys found + return false; + } + *response = read_data_ss.str(); + return http_code / 100 != 2; // 2** are success return codes +} + +bool Vault_curl::encode_key_signature(const Vault_key &key, + Secure_string *encoded_key_signature) { + if (Vault_base64::encode( + key.get_key_signature()->c_str(), key.get_key_signature()->length(), + encoded_key_signature, Vault_base64::Format::SINGLE_LINE)) { + logger->log(MY_ERROR_LEVEL, "Could not encode key's signature in base64"); + return true; + } + return false; +} + +bool Vault_curl::get_key_url(const Vault_key &key, Secure_string *key_url) { + Secure_string encoded_key_signature; + if (encode_key_signature(key, &encoded_key_signature)) return true; + *key_url = vault_url + '/' + encoded_key_signature.c_str(); + return false; +} + +bool Vault_curl::write_key(const Vault_key &key, Secure_string *response) { + Secure_string encoded_key_data; + if (Vault_base64::encode(reinterpret_cast(key.get_key_data()), + key.get_key_data_size(), &encoded_key_data, + Vault_base64::Format::SINGLE_LINE)) { + logger->log(MY_ERROR_LEVEL, "Could not encode a key in base64"); + return true; + } + CURLcode curl_res = CURLE_OK; + Secure_string postdata = "{\"type\":\""; + postdata += key.get_key_type_as_string()->c_str(); + postdata += "\",\""; + postdata += "value\":\"" + encoded_key_data + "\"}"; + + Secure_string key_url; + if (get_key_url(key, &key_url)) return true; + + Thd_wait_end_guard thd_wait_end_guard; + (void)thd_wait_end_guard; // silence unused variable error + + CURL *curl = curl_easy_init(); + if (curl == nullptr) { + logger->log(MY_ERROR_LEVEL, "Cannot initialize curl session"); + return true; + } + Curl_session_guard curl_session_guard(curl); + + if (setup_curl_session(curl) || + (curl_res = curl_easy_setopt(curl, CURLOPT_URL, key_url.c_str())) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, + postdata.c_str())) != CURLE_OK || + (curl_res = curl_easy_perform(curl)) != CURLE_OK) { + logger->log(MY_ERROR_LEVEL, get_error_from_curl(curl_res).c_str()); + return true; + } + *response = read_data_ss.str(); + return false; +} + +bool Vault_curl::read_key(const Vault_key &key, Secure_string *response) { + Secure_string key_url; + if (get_key_url(key, &key_url)) return true; + CURLcode curl_res = CURLE_OK; + + Thd_wait_end_guard thd_wait_end_guard; + (void)thd_wait_end_guard; // silence unused variable error + + CURL *curl = curl_easy_init(); + if (curl == NULL) { + logger->log(MY_ERROR_LEVEL, "Cannot initialize curl session"); + return true; + } + Curl_session_guard curl_session_guard(curl); + + if (setup_curl_session(curl) || + (curl_res = curl_easy_setopt(curl, CURLOPT_URL, key_url.c_str())) != + CURLE_OK || + (curl_res = curl_easy_perform(curl)) != CURLE_OK) { + logger->log(MY_ERROR_LEVEL, get_error_from_curl(curl_res).c_str()); + return true; + } + *response = read_data_ss.str(); + return false; +} + +bool Vault_curl::delete_key(const Vault_key &key, Secure_string *response) { + Secure_string key_url; + if (get_key_url(key, &key_url)) return true; + CURLcode curl_res = CURLE_OK; + + Thd_wait_end_guard thd_wait_end_guard; + (void)thd_wait_end_guard; // silence unused variable error + CURL *curl = curl_easy_init(); + if (curl == NULL) { + logger->log(MY_ERROR_LEVEL, "Cannot initialize curl session"); + return true; + } + Curl_session_guard curl_session_guard(curl); + + if (setup_curl_session(curl) || + (curl_res = curl_easy_setopt(curl, CURLOPT_URL, key_url.c_str())) != + CURLE_OK || + (curl_res = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE")) != + CURLE_OK || + (curl_res = curl_easy_perform(curl)) != CURLE_OK) { + logger->log(MY_ERROR_LEVEL, get_error_from_curl(curl_res).c_str()); + return true; + } + *response = read_data_ss.str(); + return false; +} + +} // namespace keyring diff --git a/plugin/keyring_vault/vault_curl.h b/plugin/keyring_vault/vault_curl.h new file mode 100644 index 000000000000..34796a9bd446 --- /dev/null +++ b/plugin/keyring_vault/vault_curl.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_VAULT_CURL_H +#define MYSQL_VAULT_CURL_H + +#include +#include +#include +#include "i_vault_curl.h" +#include "plugin/keyring/common/i_keyring_key.h" +#include "plugin/keyring/common/logger.h" +#include "plugin/keyring/common/secure_string.h" +#include "vault_credentials.h" +#include "vault_key.h" + +namespace keyring { + +class Vault_curl final : public IVault_curl, private boost::noncopyable { + public: + Vault_curl(ILogger *logger, uint timeout) noexcept + : logger(logger), list(nullptr), timeout(timeout) {} + + ~Vault_curl() { + if (list != nullptr) curl_slist_free_all(list); + } + + bool init(const Vault_credentials &vault_credentials) override; + bool list_keys(Secure_string *response) override; + bool write_key(const Vault_key &key, Secure_string *response) override; + bool read_key(const Vault_key &key, Secure_string *response) override; + bool delete_key(const Vault_key &key, Secure_string *response) override; + void set_timeout(uint timeout) noexcept override { this->timeout = timeout; } + + private: + bool setup_curl_session(CURL *curl); + std::string get_error_from_curl(CURLcode curl_code); + bool encode_key_signature(const Vault_key &key, + Secure_string *encoded_key_signature); + bool get_key_url(const Vault_key &key, Secure_string *key_url); + + ILogger *logger; + Secure_string token_header; + Secure_string vault_url; + char curl_errbuf[CURL_ERROR_SIZE]; // error from CURL + Secure_ostringstream read_data_ss; + struct curl_slist *list; + Secure_string vault_ca; + uint timeout; +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_CURL_H diff --git a/plugin/keyring_vault/vault_io.cc b/plugin/keyring_vault/vault_io.cc new file mode 100644 index 000000000000..0818ea4b995d --- /dev/null +++ b/plugin/keyring_vault/vault_io.cc @@ -0,0 +1,118 @@ +#include "vault_io.h" +#include +#include +#include "vault_credentials_parser.h" +#include "vault_keys_list.h" + +namespace keyring { + +bool Vault_io::init(const std::string *keyring_storage_url) { + Vault_credentials_parser vault_credentials_parser(logger); + Vault_credentials vault_credentials; + return vault_credentials_parser.parse(*keyring_storage_url, + &vault_credentials) || + vault_curl->init(vault_credentials); +} + +Vault_io::~Vault_io() { + delete vault_curl; + delete vault_parser; +} + +Secure_string Vault_io::get_errors_from_response( + const Secure_string &json_response) { + if (json_response.empty()) return Secure_string(); + + Secure_string errors_from_response, err_msg; + if (vault_parser->parse_errors(json_response, &errors_from_response)) + err_msg = " Error while parsing error messages"; + else if (errors_from_response.size()) + err_msg = + " Vault has returned the following error(s): " + errors_from_response; + return err_msg; +} + +bool Vault_io::get_serialized_object(ISerialized_object **serialized_object) { + static Secure_string err_msg("Could not retrieve list of keys from Vault."); + *serialized_object = nullptr; + Secure_string json_response; + + if (vault_curl->list_keys(&json_response)) { + logger->log(MY_ERROR_LEVEL, + (err_msg + get_errors_from_response(json_response)).c_str()); + return true; + } + if (json_response.empty()) // no keys + { + *serialized_object = nullptr; + return false; + } + + std::unique_ptr keys(new Vault_keys_list()); + if (vault_parser->parse_keys(json_response, keys.get())) { + logger->log(MY_ERROR_LEVEL, err_msg.c_str()); + return true; + } + if (keys->size() == 0) keys.reset(nullptr); + + *serialized_object = keys.release(); + return false; +} + +bool Vault_io::retrieve_key_type_and_data(IKey *key) { + Secure_string json_response; + if (vault_curl->read_key(static_cast(*key), + &json_response) || + vault_parser->parse_key_data(json_response, key)) { + logger->log(MY_ERROR_LEVEL, ("Could not read key from Vault." + + get_errors_from_response(json_response)) + .c_str()); + return true; + } + return false; +} + +ISerializer *Vault_io::get_serializer() { return &vault_key_serializer; } + +bool Vault_io::write_key(const Vault_key &key) { + Secure_string json_response, errors_from_response; + if (vault_curl->write_key(key, &json_response) || + !((errors_from_response = get_errors_from_response(json_response)) + .empty())) { + errors_from_response.insert(0, "Could not write key to Vault."); + logger->log(MY_ERROR_LEVEL, errors_from_response.c_str()); + return true; + } + return false; +} + +bool Vault_io::delete_key(const Vault_key &key) { + Secure_string json_response, errors_from_response; + if (vault_curl->delete_key(key, &json_response) || + !((errors_from_response = get_errors_from_response(json_response)) + .empty())) { + logger->log( + MY_ERROR_LEVEL, + ("Could not delete key from Vault." + errors_from_response).c_str()); + return true; + } + return false; +} + +bool Vault_io::flush_to_storage(ISerialized_object *serialized_object) { + assert(serialized_object->has_next_key()); + IKey *vault_key_raw = nullptr; + + if (serialized_object->get_next_key(&vault_key_raw) || + vault_key_raw == NULL) { + delete vault_key_raw; + return true; + } + std::unique_ptr vault_key(vault_key_raw); + + return serialized_object->get_key_operation() == STORE_KEY + ? write_key(static_cast(*vault_key)) + : delete_key(static_cast(*vault_key)); +} + +} // namespace keyring diff --git a/plugin/keyring_vault/vault_io.h b/plugin/keyring_vault/vault_io.h new file mode 100644 index 000000000000..8ee54b0a7513 --- /dev/null +++ b/plugin/keyring_vault/vault_io.h @@ -0,0 +1,49 @@ +#ifndef MYSQL_VAULT_IO_H +#define MYSQL_VAULT_IO_H + +#include +#include "i_vault_io.h" +#include "plugin/keyring/common/logger.h" +#include "vault_curl.h" +#include "vault_key_serializer.h" +#include "vault_parser.h" + +namespace keyring { + +class Vault_io final : public IVault_io, private boost::noncopyable { + public: + Vault_io(ILogger *logger, IVault_curl *vault_curl, + IVault_parser *vault_parser) noexcept + : logger(logger), vault_curl(vault_curl), vault_parser(vault_parser) {} + + ~Vault_io(); + + bool retrieve_key_type_and_data(IKey *key) override; + + bool init(const std::string *keyring_storage_url) override; + bool flush_to_backup( + ISerialized_object *serialized_object [[maybe_unused]]) override { + return false; // we do not have backup storage in vault + } + bool flush_to_storage(ISerialized_object *serialized_object) override; + + ISerializer *get_serializer() override; + bool get_serialized_object(ISerialized_object **serialized_object) override; + bool has_next_serialized_object() override { return false; } + void set_curl_timeout(uint timeout) noexcept override; + + private: + bool write_key(const Vault_key &key); + bool delete_key(const Vault_key &key); + + Secure_string get_errors_from_response(const Secure_string &json_response); + + ILogger *logger; + IVault_curl *vault_curl; + IVault_parser *vault_parser; + Vault_key_serializer vault_key_serializer; +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_IO_H diff --git a/plugin/keyring_vault/vault_key.cc b/plugin/keyring_vault/vault_key.cc new file mode 100644 index 000000000000..b50af9081e6d --- /dev/null +++ b/plugin/keyring_vault/vault_key.cc @@ -0,0 +1,49 @@ +#include "vault_key.h" +#include +#include + +namespace keyring { + +bool Vault_key::get_next_key(IKey **key_out) { + if (was_key_retrieved) { + *key_out = nullptr; + return true; + } + *key_out = new Vault_key(*this); + was_key_retrieved = true; + return false; +} + +bool Vault_key::has_next_key() { return !was_key_retrieved; } + +void Vault_key::xor_data() { /* We do not xor data in keyring_vault */ +} + +uchar *Vault_key::get_key_data() const { return key.get(); } + +size_t Vault_key::get_key_data_size() const { return key_len; } + +Key_type Vault_key::get_key_type() const { + if (strcasecmp(key_type.c_str(), "AES") == 0) return Key_type::aes; + if (strcasecmp(key_type.c_str(), "DSA") == 0) return Key_type::dsa; + if (strcasecmp(key_type.c_str(), "SECRET") == 0) return Key_type::secret; + return Key_type::unknown; +} + +std::string *Vault_key::get_key_type_as_string() { return &this->key_type; } + +const std::string *Vault_key::get_key_type_as_string() const { + return &this->key_type; +} + +void Vault_key::create_key_signature() const { + if (key_id.empty()) return; + std::ostringstream key_signature_ss; + key_signature_ss << key_id.length() << '_'; + key_signature_ss << key_id; + key_signature_ss << user_id.length() << '_'; + key_signature_ss << user_id; + key_signature = key_signature_ss.str(); +} + +} // namespace keyring diff --git a/plugin/keyring_vault/vault_key.h b/plugin/keyring_vault/vault_key.h new file mode 100644 index 000000000000..daa9ff046614 --- /dev/null +++ b/plugin/keyring_vault/vault_key.h @@ -0,0 +1,44 @@ +#ifndef MYSQL_VAULT_KEY_H +#define MYSQL_VAULT_KEY_H + +#include "plugin/keyring/common/i_serialized_object.h" +#include "plugin/keyring/common/keyring_key.h" + +namespace keyring { + +struct Vault_key : public Key, public ISerialized_object { + Vault_key(const char *a_key_id, const char *a_key_type, const char *a_user_id, + const void *a_key, size_t a_key_len) noexcept + : Key(a_key_id, a_key_type, a_user_id, a_key, a_key_len), + was_key_retrieved(false) {} + + Vault_key(const Vault_key &vault_key) noexcept + : Key(vault_key.key_id.c_str(), vault_key.key_type.c_str(), + vault_key.user_id.c_str(), vault_key.key.get(), vault_key.key_len), + was_key_retrieved(false) { + this->key_operation = vault_key.key_operation; + } + Vault_key() {} + + using Key::get_key_data; + uchar *get_key_data() const; + using Key::get_key_data_size; + size_t get_key_data_size() const; + using Key::get_key_type; + Key_type get_key_type() const override; + std::string *get_key_type_as_string() override; + const std::string *get_key_type_as_string() const; + + bool get_next_key(IKey **key_out) override; + bool has_next_key() override; + void create_key_signature() const override; + void xor_data(uchar *, size_t) override; + void xor_data() override; + + protected: + bool was_key_retrieved; +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_KEY_H diff --git a/plugin/keyring_vault/vault_key_serializer.h b/plugin/keyring_vault/vault_key_serializer.h new file mode 100644 index 000000000000..21d7f9999c6d --- /dev/null +++ b/plugin/keyring_vault/vault_key_serializer.h @@ -0,0 +1,26 @@ +#ifndef MYSQL_VAULT_KEY_SERIALIZER_H +#define MYSQL_VAULT_KEY_SERIALIZER_H + +#include "my_dbug.h" +#include "plugin/keyring/common/i_serializer.h" +#include "vault_key.h" + +namespace keyring { + +class Vault_key_serializer final : public ISerializer { + public: + ISerialized_object *serialize( + const collation_unordered_map> + &keys_hash [[maybe_unused]], + IKey *key, const Key_operation operation) override { + Vault_key *vault_key = dynamic_cast(key); + assert(vault_key != nullptr); + vault_key->set_key_operation(operation); + + return new Vault_key(*vault_key); + } +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_KEY_SERIALIZER_H diff --git a/plugin/keyring_vault/vault_keyring.cc b/plugin/keyring_vault/vault_keyring.cc new file mode 100644 index 000000000000..9728cfdd8b39 --- /dev/null +++ b/plugin/keyring_vault/vault_keyring.cc @@ -0,0 +1,271 @@ +#include +#include "my_config.h" +#include "mysql/components/services/log_builtins.h" +#include "mysql/plugin_keyring.h" +#include "plugin/keyring/common/keyring.h" +#include "sql/sql_class.h" +#include "vault_io.h" +#include "vault_keys_container.h" +#include "vault_parser.h" + +using keyring::IVault_curl; +using keyring::IVault_parser; +using keyring::Keys_iterator; +using keyring::Logger; +using keyring::Vault_curl; +using keyring::Vault_io; +using keyring::Vault_keys_container; +using keyring::Vault_parser; + +mysql_rwlock_t LOCK_keyring; + +static bool reset_curl() noexcept { + curl_global_cleanup(); + return curl_global_init(CURL_GLOBAL_ALL) != 0; +} + +static void handle_std_bad_alloc_exception(const std::string &message_prefix) { + assert(0); + const std::string error_message = + message_prefix + " due to memory allocation failure"; + if (logger != nullptr) logger->log(MY_ERROR_LEVEL, error_message.c_str()); +} + +static void handle_unknown_exception(const std::string &message_prefix) { + assert(0); + const std::string error_message = message_prefix + + " due to internal " + "exception inside the keyring_vault plugin"; + if (logger != nullptr) logger->log(MY_ERROR_LEVEL, error_message.c_str()); +} + +static char *keyring_vault_config_file = nullptr; +static uint keyring_vault_timeout = 0; + +int check_keyring_file_data(MYSQL_THD thd [[maybe_unused]], + SYS_VAR *var [[maybe_unused]], void *save, + st_mysql_value *value) { + char buff[FN_REFLEN + 1]; + const char *keyring_filename; + int len = sizeof(buff); + std::unique_ptr new_keys( + new Vault_keys_container(logger.get())); + + (*(const char **)save) = nullptr; + keyring_filename = value->val_str(value, buff, &len); + if (keyring_filename == nullptr) return 1; + PolyLock_rwlock keyring_rwlock(&LOCK_keyring); + AutoWLock keyring_auto_wlokc(&keyring_rwlock); + + try { + if (reset_curl()) { + logger->log(MY_ERROR_LEVEL, "Cannot set keyring_vault_config_file"); + return 1; + } + std::unique_ptr vault_curl( + new Vault_curl(logger.get(), keyring_vault_timeout)); + std::unique_ptr vault_parser(new Vault_parser(logger.get())); + IKeyring_io *keyring_io( + new Vault_io(logger.get(), vault_curl.get(), vault_parser.get())); + vault_curl.release(); + vault_parser.release(); + if (new_keys->init(keyring_io, keyring_filename)) return 1; + *reinterpret_cast(save) = new_keys.release(); + } catch (const std::bad_alloc &e) { + handle_std_bad_alloc_exception("Cannot set keyring_vault_config_file"); + return 1; + } catch (...) { + handle_unknown_exception("Cannot set keyring_vault_config_file"); + return 1; + } + return (0); +} + +static MYSQL_SYSVAR_STR( + config, /* name */ + keyring_vault_config_file, /* value */ + PLUGIN_VAR_RQCMDARG, /* flags */ + "The path to the keyring_vault configuration file", /* comment */ + check_keyring_file_data, /* check() */ + update_keyring_file_data, /* update() */ + "" /* default */ +); + +static void update_keyring_vault_timeout(MYSQL_THD thd [[maybe_unused]], + SYS_VAR *var [[maybe_unused]], + void *ptr, const void *val) noexcept { + assert(dynamic_cast(keys.get()) != nullptr); + *reinterpret_cast(ptr) = *reinterpret_cast(val); + dynamic_cast(keys.get()) + ->set_curl_timeout(*static_cast(val)); +} + +static MYSQL_SYSVAR_UINT( + timeout, /* name */ + keyring_vault_timeout, /* value */ + PLUGIN_VAR_OPCMDARG, /* flags */ + "The keyring_vault - Vault server connection timeout", /* comment */ + nullptr, /* check() */ + update_keyring_vault_timeout, /* update() */ + 15, /* default */ + 0, /* min */ + 86400, /* max - 24h */ + 0 /* blocksize */ +); + +static SYS_VAR *keyring_vault_system_variables[] = { + MYSQL_SYSVAR(config), MYSQL_SYSVAR(timeout), nullptr}; + +static SERVICE_TYPE(registry) *reg_srv = nullptr; +SERVICE_TYPE(log_builtins) *log_bi = nullptr; +SERVICE_TYPE(log_builtins_string) *log_bs = nullptr; + +static int keyring_vault_init(MYSQL_PLUGIN plugin_info [[maybe_unused]]) { + if (init_logging_service_for_plugin(®_srv, &log_bi, &log_bs)) return 1; + try { +#ifdef HAVE_PSI_INTERFACE + keyring_init_psi_keys(); +#endif + if (init_keyring_locks()) return 1; + + if (curl_global_init(CURL_GLOBAL_ALL) != 0) return 1; + + logger.reset(new Logger()); + keys.reset(new Vault_keys_container(logger.get())); + std::unique_ptr vault_curl( + new Vault_curl(logger.get(), keyring_vault_timeout)); + std::unique_ptr vault_parser(new Vault_parser(logger.get())); + IKeyring_io *keyring_io = + new Vault_io(logger.get(), vault_curl.get(), vault_parser.get()); + vault_curl.release(); + vault_parser.release(); + if (keys->init(keyring_io, keyring_vault_config_file)) { + is_keys_container_initialized = false; + logger->log( + MY_ERROR_LEVEL, + "keyring_vault initialization failure. Please check that" + " the keyring_vault_config_file points to readable keyring_vault " + "configuration" + " file. Please also make sure Vault is running and accessible." + " The keyring_vault will stay unusable until correct configuration " + "file gets" + " provided."); + + if (current_thd != nullptr) + push_warning(current_thd, Sql_condition::SL_WARNING, 42000, + "keyring_vault initialization failure. Please check the " + "server log."); + return 0; + } + is_keys_container_initialized = true; + return 0; + } catch (const std::bad_alloc &e) { + handle_std_bad_alloc_exception("keyring_vault initialization failure"); + curl_global_cleanup(); + return 1; + } catch (...) { + handle_unknown_exception("keyring_vault initialization failure"); + curl_global_cleanup(); + return 1; + } +} + +int keyring_vault_deinit(void *arg [[maybe_unused]]) noexcept { + keys.reset(); + logger.reset(); + delete_keyring_file_data(); + mysql_rwlock_destroy(&LOCK_keyring); + + curl_global_cleanup(); + return 0; +} + +bool mysql_key_fetch(const char *key_id, char **key_type, const char *user_id, + void **key, size_t *key_len) { + return mysql_key_fetch(key_id, key_type, user_id, key, + key_len, "keyring_vault"); +} + +bool mysql_key_store(const char *key_id, const char *key_type, + const char *user_id, const void *key, size_t key_len) { + return mysql_key_store(key_id, key_type, user_id, key, + key_len, "keyring_vault"); +} + +bool mysql_key_remove(const char *key_id, const char *user_id) { + return mysql_key_remove(key_id, user_id, "keyring_vault"); +} + +bool mysql_key_generate(const char *key_id, const char *key_type, + const char *user_id, size_t key_len) noexcept { + try { + std::unique_ptr key_candidate( + new keyring::Vault_key(key_id, key_type, user_id, NULL, 0)); + + std::unique_ptr key(new uchar[key_len]); + if (key.get() == nullptr) return true; + memset(key.get(), 0, key_len); + if (!is_keys_container_initialized || + check_key_for_writing(key_candidate.get(), "generating") || + my_rand_buffer(key.get(), key_len)) + return true; + + return mysql_key_store(key_id, key_type, user_id, key.get(), key_len); + } catch (const std::bad_alloc &e) { + handle_std_bad_alloc_exception("Failed to generate a key"); + return true; + } catch (...) { + // We want to make sure that no exception leaves keyring_vault plugin and + // goes into the server. That is why there are try..catch blocks in all + // keyring_vault service methods. + handle_unknown_exception("Failed to generate a key"); + return true; + } +} + +static void mysql_key_iterator_init(void **key_iterator) { + *key_iterator = new Keys_iterator(logger.get()); + mysql_key_iterator_init( + static_cast(*key_iterator), "keyring_vault"); +} + +static void mysql_key_iterator_deinit(void *key_iterator) { + mysql_key_iterator_deinit( + static_cast(key_iterator), "keyring_vault"); + delete static_cast(key_iterator); +} + +static bool mysql_key_iterator_get_key(void *key_iterator, char *key_id, + char *user_id) { + return mysql_key_iterator_get_key( + static_cast(key_iterator), key_id, user_id, + "keyring_vault"); +} + +/* Plugin type-specific descriptor */ +static struct st_mysql_keyring keyring_descriptor = { + MYSQL_KEYRING_INTERFACE_VERSION, + mysql_key_store, + mysql_key_fetch, + mysql_key_remove, + mysql_key_generate, + mysql_key_iterator_init, + mysql_key_iterator_deinit, + mysql_key_iterator_get_key}; + +mysql_declare_plugin(keyring_vault){ + MYSQL_KEYRING_PLUGIN, /* type */ + &keyring_descriptor, /* descriptor */ + "keyring_vault", /* name */ + "Percona", /* author */ + "store/fetch authentication data to/from Vault server", /* description */ + PLUGIN_LICENSE_GPL, + keyring_vault_init, /* init function (when loaded) */ + nullptr, + keyring_vault_deinit, /* deinit function (when unloaded) */ + 0x0100, /* version */ + nullptr, /* status variables */ + keyring_vault_system_variables, /* system variables */ + nullptr, + PLUGIN_OPT_ALLOW_EARLY, +} mysql_declare_plugin_end; diff --git a/plugin/keyring_vault/vault_keys_container.cc b/plugin/keyring_vault/vault_keys_container.cc new file mode 100644 index 000000000000..d2730a43302f --- /dev/null +++ b/plugin/keyring_vault/vault_keys_container.cc @@ -0,0 +1,28 @@ +#include "vault_keys_container.h" + +namespace keyring { +bool Vault_keys_container::init(IKeyring_io *keyring_io_value, + std::string keyring_storage_url_value) { + vault_io = dynamic_cast(keyring_io_value); + assert(vault_io != nullptr); + return Keys_container::init(keyring_io_value, keyring_storage_url_value); +} + +IKey *Vault_keys_container::fetch_key(IKey *key) { + assert(key->get_key_data() == nullptr); + assert(key->get_key_type_as_string()->empty()); + + IKey *fetched_key = get_key_from_hash(key); + + if (fetched_key == nullptr) return nullptr; + + if (fetched_key->get_key_type_as_string()->empty() && + vault_io->retrieve_key_type_and_data( + fetched_key)) // key is fetched for the first time + return nullptr; + + return Keys_container::fetch_key(key); +} + +bool Vault_keys_container::flush_to_backup() { return false; } +} // namespace keyring diff --git a/plugin/keyring_vault/vault_keys_container.h b/plugin/keyring_vault/vault_keys_container.h new file mode 100644 index 000000000000..78da022c2b90 --- /dev/null +++ b/plugin/keyring_vault/vault_keys_container.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2018, 2021 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_VAULT_KEYS_CONTAINER_H +#define MYSQL_VAULT_KEYS_CONTAINER_H + +#include +#include "i_vault_io.h" +#include "plugin/keyring/common/keys_container.h" + +namespace keyring { + +class Vault_keys_container final : public Keys_container, + private boost::noncopyable { + public: + Vault_keys_container(ILogger *logger_value) noexcept + : Keys_container(logger_value) {} + + bool init(IKeyring_io *keyring_io, std::string keyring_storage_url) override; + IKey *fetch_key(IKey *key) override; + virtual void set_curl_timeout(uint timeout); + + private: + bool flush_to_backup() override; + IVault_io *vault_io; +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_KEYS_CONTAINER_H diff --git a/plugin/keyring_vault/vault_keys_list.cc b/plugin/keyring_vault/vault_keys_list.cc new file mode 100644 index 000000000000..9cca4078e852 --- /dev/null +++ b/plugin/keyring_vault/vault_keys_list.cc @@ -0,0 +1,27 @@ +#include "vault_keys_list.h" + +namespace keyring { + +// The caller takes ownership of the key, thus it is +// his resposibility to free the key +bool Vault_keys_list::get_next_key(IKey **key) { + *key = nullptr; + if (size() == 0) return true; + *key = keys.front(); + keys.pop_front(); + return false; +} + +bool Vault_keys_list::has_next_key() { return size() != 0; } + +size_t Vault_keys_list::size() const { return keys.size(); } + +Vault_keys_list::~Vault_keys_list() { + // remove not fetched keys + for (Keys_list::iterator iter = keys.begin(); iter != keys.end(); ++iter) + delete *iter; +} + +void Vault_keys_list::push_back(IKey *key) { keys.push_back(key); } + +} // namespace keyring diff --git a/plugin/keyring_vault/vault_keys_list.h b/plugin/keyring_vault/vault_keys_list.h new file mode 100644 index 000000000000..9f321bd0a94d --- /dev/null +++ b/plugin/keyring_vault/vault_keys_list.h @@ -0,0 +1,27 @@ +#ifndef MYSQL_VAULT_KEYS_H +#define MYSQL_VAULT_KEYS_H + +#include +#include +#include "plugin/keyring/common/i_serialized_object.h" + +namespace keyring { + +class Vault_keys_list final : public ISerialized_object, + private boost::noncopyable { + public: + bool get_next_key(IKey **key) override; + bool has_next_key() override; + void push_back(IKey *key); + size_t size() const; + + ~Vault_keys_list() override; + + private: + typedef std::list Keys_list; + Keys_list keys; +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_KEYS_H diff --git a/plugin/keyring_vault/vault_parser.cc b/plugin/keyring_vault/vault_parser.cc new file mode 100644 index 000000000000..8ef3e5dc677d --- /dev/null +++ b/plugin/keyring_vault/vault_parser.cc @@ -0,0 +1,195 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#include "vault_parser.h" +#include +#include +#include +#include "vault_base64.h" +#include "vault_key.h" + +namespace keyring { + +bool Vault_parser::retrieve_tag_value(const Secure_string &payload, + const Secure_string &tag, + const char opening_bracket, + const char closing_bracket, + Secure_string *value) { + size_t opening_bracket_pos, closing_bracket_pos, tag_pos = payload.find(tag); + if (tag_pos == Secure_string::npos) { + value->clear(); + return false; + } + + if ((opening_bracket_pos = (payload.find(opening_bracket, tag_pos))) == + Secure_string::npos || + (closing_bracket_pos = (payload.find( + closing_bracket, opening_bracket_pos))) == Secure_string::npos) { + std::ostringstream err_ss("Could not parse tag "); + err_ss << tag << " from Vault's response."; + logger->log(MY_ERROR_LEVEL, err_ss.str().c_str()); + return true; + } + + *value = payload.substr(opening_bracket_pos, + closing_bracket_pos - opening_bracket_pos + 1); + value->erase(std::remove(value->begin(), value->end(), '\n'), value->end()); + return false; +} + +bool Vault_parser::retrieve_list(const Secure_string &payload, + const Secure_string &list_name, + Secure_string *list) { + return retrieve_tag_value(payload, list_name, '[', ']', list); +} + +bool Vault_parser::retrieve_map(const Secure_string &payload, + const Secure_string &map_name, + Secure_string *map) { + return retrieve_tag_value(payload, map_name, '{', '}', map); +} + +bool Vault_parser::retrieve_tokens_from_list(const Secure_string &list, + Tokens *tokens) { + std::size_t token_start = 0, token_end = 0; + while ((token_start = list.find('\"', token_end)) != Secure_string::npos && + token_start < list.size()) { + if ((token_end = list.find('\"', token_start + 1)) == Secure_string::npos) { + tokens->clear(); + return true; + } + tokens->push_back( + list.substr(token_start + 1, token_end - token_start - 1)); + ++token_end; + } + return false; +} + +const size_t Vault_parser::start_tag_length = strlen(":\""); + +bool Vault_parser::retrieve_value_from_map(const Secure_string &map, + const Secure_string &key, + Secure_string *value) { + size_t key_tag_pos = Secure_string::npos, + value_start_pos = Secure_string::npos, + value_end_pos = Secure_string::npos; + bool was_error = false; + + if ((key_tag_pos = map.find(key)) != Secure_string::npos && + (value_start_pos = map.find(":\"", key_tag_pos)) != Secure_string::npos && + (value_end_pos = map.find("\"", value_start_pos + start_tag_length)) != + Secure_string::npos) { + value_start_pos += start_tag_length; + assert(value_end_pos > 0); + value_end_pos--; // due to closing " + *value = map.substr(value_start_pos, (value_end_pos - value_start_pos + 1)); + } else + was_error = true; + + if (was_error || value->empty()) { + std::ostringstream err_ss; + err_ss << "Could not parse " << key << " tag for a key."; + logger->log(MY_ERROR_LEVEL, err_ss.str().c_str()); + return true; + } + return false; +} + +bool Vault_parser::parse_errors(const Secure_string &payload, + Secure_string *errors) { + return retrieve_list(payload, "errors", errors); +} + +bool Vault_parser::parse_keys(const Secure_string &payload, + Vault_keys_list *keys) { + /* payload is built as follows: + * (...)"data":{"keys":["keysignature","keysignature"]}(...) + * We need to retrieve keys signatures from it + */ + Tokens key_tokens; + Secure_string keys_list; + if (retrieve_list(payload, "keys", &keys_list) || keys_list.empty() || + retrieve_tokens_from_list(keys_list, &key_tokens)) { + logger->log(MY_ERROR_LEVEL, + "Could not parse keys tag with keys list from Vault."); + return true; + } + KeyParameters key_parameters; + for (Tokens::const_iterator iter = key_tokens.begin(); + iter != key_tokens.end(); ++iter) { + if (parse_key_signature(*iter, &key_parameters)) { + logger->log(MY_WARNING_LEVEL, + "Could not parse key's signature, skipping the key."); + continue; // found incorrect key, skipping it + } + keys->push_back(new Vault_key(key_parameters.key_id.c_str(), NULL, + key_parameters.user_id.c_str(), NULL, 0)); + } + return false; +} + +bool Vault_parser::parse_key_signature( + const Secure_string &base64_key_signature, KeyParameters *key_parameters) { + // key_signature= lengthof(key_id)||_||key_id||lengthof(user_id)||_||user_id + static const Secure_string digits("0123456789"); + Secure_string key_signature; + if (Vault_base64::decode(base64_key_signature, &key_signature)) { + logger->log(MY_WARNING_LEVEL, "Could not decode base64 key's signature"); + return true; + } + + size_t next_pos_to_start_from = 0; + for (int i = 0; i < 2; ++i) { + size_t key_id_pos = + key_signature.find_first_not_of(digits, next_pos_to_start_from); + if (key_id_pos == Secure_string::npos || key_signature[key_id_pos] != '_') + return true; + ++key_id_pos; + Secure_string key_id_length = + key_signature.substr(next_pos_to_start_from, key_id_pos); + int key_l = atoi(key_id_length.c_str()); + if (key_l < 0 || key_l + key_id_pos > key_signature.length()) return true; + (*key_parameters)[i] = key_signature.substr(key_id_pos, key_l); + next_pos_to_start_from = key_id_pos + key_l; + } + return false; +} + +bool Vault_parser::parse_key_data(const Secure_string &payload, IKey *key) { + Secure_string map, type, value; + if (retrieve_map(payload, "data", &map) || + retrieve_value_from_map(map, "type", &type) || + retrieve_value_from_map(map, "value", &value)) + return true; + + char *decoded_key_data; + uint64 decoded_key_data_length; + if (Vault_base64::decode(value, &decoded_key_data, + &decoded_key_data_length)) { + logger->log(MY_ERROR_LEVEL, "Could not decode base64 key's value"); + return true; + } + + key->set_key_data( + const_cast(reinterpret_cast(decoded_key_data)), + decoded_key_data_length); + std::string key_type(type.c_str(), type.length()); + key->set_key_type(&key_type); + + return false; +} + +} // namespace keyring diff --git a/plugin/keyring_vault/vault_parser.h b/plugin/keyring_vault/vault_parser.h new file mode 100644 index 000000000000..c3b42c40b468 --- /dev/null +++ b/plugin/keyring_vault/vault_parser.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#ifndef MYSQL_VAULT_PARSER_H +#define MYSQL_VAULT_PARSER_H + +#include "i_vault_parser.h" +#include "plugin/keyring/common/logger.h" +#include "plugin/keyring/common/secure_string.h" + +namespace keyring { + +class Vault_parser final : public IVault_parser { + public: + Vault_parser(ILogger *logger) : logger(logger) {} + + bool parse_keys(const Secure_string &payload, Vault_keys_list *keys) override; + bool parse_key_data(const Secure_string &payload, IKey *key) override; + bool parse_key_signature(const Secure_string &base64_key_signature, + KeyParameters *key_parameters) override; + bool parse_errors(const Secure_string &payload, + Secure_string *errors) override; + + private: + typedef std::vector Tokens; + + bool retrieve_tag_value(const Secure_string &payload, + const Secure_string &tag, const char opening_bracket, + const char closing_bracket, Secure_string *value); + bool retrieve_list(const Secure_string &payload, + const Secure_string &list_name, Secure_string *list); + bool retrieve_map(const Secure_string &payload, const Secure_string &map_name, + Secure_string *map); + bool retrieve_tokens_from_list(const Secure_string &list, Tokens *tokens); + bool retrieve_value_from_map(const Secure_string &map, + const Secure_string &key, Secure_string *value); + + ILogger *logger; + const static size_t start_tag_length; +}; + +} // namespace keyring + +#endif // MYSQL_VAULT_PARSER_H diff --git a/plugin/percona-pam-for-mysql/CMakeLists.txt b/plugin/percona-pam-for-mysql/CMakeLists.txt new file mode 100644 index 000000000000..7c7bb8de2808 --- /dev/null +++ b/plugin/percona-pam-for-mysql/CMakeLists.txt @@ -0,0 +1,42 @@ +# (C) 2011-2013 Percona LLC and/or its affiliates +# +# 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +# +IF(WITH_PAM) +INCLUDE (CheckLibraryExists) +CHECK_LIBRARY_EXISTS(pam pam_authenticate "" HAVE_PAM) +IF(NOT HAVE_PAM) + MESSAGE(FATAL_ERROR "Required PAM dev library not found. Please install PAM development files!") +ENDIF(NOT HAVE_PAM) +CHECK_SYMBOL_EXISTS(getpwnam_r "pwd.h" HAVE_GETPWNAM_R) +CHECK_SYMBOL_EXISTS(getgrgid_r "grp.h" HAVE_GETGRGID_R) +CHECK_INCLUDE_FILES (security/pam_misc.h HAVE_SECURITY_PAM_MISC_H) +CHECK_INCLUDE_FILES (security/openpam.h HAVE_SECURITY_OPENPAM_H) +CHECK_INCLUDE_FILES (dlfcn.h HAVE_DLFCN_H) +ADD_DEFINITIONS(-Dget_tty_password=dialog_mysql_get_tty_password) +IF(HAVE_PAM AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R AND HAVE_DLFCN_H) + SET(AUTH_PAM_COMMON_SOURCES + src/auth_pam_common.cc src/lib_auth_pam_client.c src/lib_auth_pam_client.h + src/auth_mapping.h src/auth_mapping.cc src/groups.cc src/groups.h) + SET(AUTH_PAM_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam.cc) + SET(AUTH_PAM_COMPAT_SOURCES ${AUTH_PAM_COMMON_SOURCES} src/auth_pam_compat.cc) + MYSQL_ADD_PLUGIN(auth_pam ${AUTH_PAM_SOURCES} LINK_LIBRARIES pam MODULE_ONLY) + MYSQL_ADD_PLUGIN(auth_pam_compat ${AUTH_PAM_COMPAT_SOURCES} LINK_LIBRARIES pam MODULE_ONLY) + MYSQL_ADD_PLUGIN(dialog + src/dialog.cc + ../../sql-common/get_password.cc + LINK_LIBRARIES perconaserverclient + MODULE_ONLY) +ENDIF(HAVE_PAM AND HAVE_GETPWNAM_R AND HAVE_GETGRGID_R AND HAVE_DLFCN_H) +ENDIF(WITH_PAM) diff --git a/plugin/percona-pam-for-mysql/doc/make.bat b/plugin/percona-pam-for-mysql/doc/make.bat new file mode 100644 index 000000000000..4bc6b71e63ac --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PerconaXtraBackup.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PerconaXtraBackup.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/plugin/percona-pam-for-mysql/doc/source/_static/percona-pam-plugin-logo.jpg b/plugin/percona-pam-for-mysql/doc/source/_static/percona-pam-plugin-logo.jpg new file mode 100644 index 000000000000..6c9f9d668bba Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/_static/percona-pam-plugin-logo.jpg differ diff --git a/plugin/percona-pam-for-mysql/doc/source/_static/percona_favicon.ico b/plugin/percona-pam-for-mysql/doc/source/_static/percona_favicon.ico new file mode 100644 index 000000000000..f426064d6f50 Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/_static/percona_favicon.ico differ diff --git a/plugin/percona-pam-for-mysql/doc/source/conf.py b/plugin/percona-pam-for-mysql/doc/source/conf.py new file mode 100644 index 000000000000..31287b000008 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/conf.py @@ -0,0 +1,245 @@ +# -*- coding: utf-8 -*- +# +# Percona PAM authentication plugin documentation build configuration file, created by +# sphinx-quickstart on Mon Jun 27 22:27:15 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. + +extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo', + 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', + 'sphinx.ext.extlinks'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Percona PAM authentication plugin for MySQL' +copyright = u'2012, Percona Inc' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +rst_prolog = ''' +.. |check| replace:: ``|[[---CHECK---]]|`` + +.. |xtrabackup| replace:: :program:`xtrabackup` + +.. |innobackupex| replace:: :program:`innobackupex` + +.. |XtraBackup| replace:: *XtraBackup* + +.. |Percona Server| replace:: *Percona Server* + +.. |Percona| replace:: *Percona* + +.. |MySQL| replace:: *MySQL* + +.. |Drizzle| replace:: *Drizzle* + +.. |MariaDB| replace:: *MariaDB* + + +''' + +extlinks = {'bug': ('https://bugs.launchpad.net/percona-pam-for-mysql/+bug/%s', + '#')} + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'percona-theme' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +html_theme_path = ['.', './percona-theme'] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = 'Percona PAM authenticatino plugin for MySQL Documentation' + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = 'PAM Plugin Docs' + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'percona-pam-plugin-logo.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'percona_favicon.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PerconaPAMForMySQL' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'PerconaPAMForMySQL.tex', u'Percona PAM Authentication Plugin for MySQL Documentation', + u'Percona Inc', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ +# ('index', 'perconapamplugin', u'Percona PAM Authentication Plugin for MySQL Documentation', +# [u'Percona Inc'], 1) +] diff --git a/plugin/percona-pam-for-mysql/doc/source/faq.rst b/plugin/percona-pam-for-mysql/doc/source/faq.rst new file mode 100644 index 000000000000..0188c77d35a2 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/faq.rst @@ -0,0 +1,51 @@ +============================ + Frequently Asked Questions +============================ + +Is there a Windows version? +=========================== + +No, Windows does not support PAM, so there will not be a Windows version. + +Can I use it with MySQL? +======================== + +Yes. + +Can I use it with Percona Server? +================================= + +Yes. + + +Is it Free and Open Source Software? +==================================== + +Yes. + + +Can I use the PAM plugin to authenticate against /etc/shadow? +============================================================= + +Yes, you need to add the mysql user to the shadow group. Because PAM libraries, such as 'pam_unix.so', need to access /etc/shadow. + +For example this is how you can do it in *Ubuntu*: :: + + root@lucid64:/var/lib/mysql# getent group shadow + shadow:x:42:mysql + + root@lucid64:/var/lib/mysql# ls -alhs /etc/shadow + 4.0K -rw-r----- 1 root shadow 912 Dec 21 10:39 /etc/shadow + +After you restart mysqld for changes to take effect, pam_unix authentication will work. + +The other option is to run mysqld as root. This should be used for testing only or as a last resort method. + + +I'm getting the: "ERROR 2059 (HY000): Authentication plugin 'auth_pam' cannot be loaded" +======================================================================================== + +This means that the default client :option:`plugin-dir` setting doesn't work or it isn't set up properly. You'll need to add the location of the plugin folder to your client configuration: :: + + [client] + plugin_dir='/usr/lib/mysql/plugin' diff --git a/plugin/percona-pam-for-mysql/doc/source/glossary.rst b/plugin/percona-pam-for-mysql/doc/source/glossary.rst new file mode 100644 index 000000000000..70834a733210 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/glossary.rst @@ -0,0 +1,8 @@ +========== + Glossary +========== + +.. glossary:: + + PAM + Pluggable Authentication Module - allows integrating multiple authentication mechanisms which can be written independently of the underlying authentication scheme that's being used in the application. diff --git a/plugin/percona-pam-for-mysql/doc/source/index.rst b/plugin/percona-pam-for-mysql/doc/source/index.rst new file mode 100644 index 000000000000..b249b9fceff4 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/index.rst @@ -0,0 +1,51 @@ +============================================================ + Percona PAM authentication plugin For MySQL - Documentation +============================================================ + +Percona PAM authentication plugin for MySQL. + +Introduction +============ + +.. toctree:: + :maxdepth: 1 + :glob: + + intro + +Installation +============ + +.. toctree:: + :maxdepth: 2 + :glob: + + installation + +User's Manual +============= + +.. toctree:: + :maxdepth: 2 + :glob: + + manual + +Miscellaneous +============= + +.. toctree:: + :maxdepth: 1 + :glob: + + faq + release-notes + glossary + +Indices and tables +================== + +* :ref:`genindex` + +* :ref:`search` + diff --git a/plugin/percona-pam-for-mysql/doc/source/installation.rst b/plugin/percona-pam-for-mysql/doc/source/installation.rst new file mode 100644 index 000000000000..30aec3add935 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/installation.rst @@ -0,0 +1,50 @@ +======================================================= + Installing Percona PAM Authentication Plugin for MySQL +======================================================= + +.. toctree:: + :hidden: + +Compiling from Source +===================== + +You will need both the PAM headers and the MySQL 5.5 headers and corresponding `mysql_config` binary available on your system. + +If you are not using one of the pre-built binary packages, you will need to compile the plugin from source. You can either use a source tarball or the source repository. + +For getting a copy of the latest development bzr tree: :: + + $ bzr branch lp:percona-pam-for-mysql + +If you are building from bzr, you will need to generate the configure script: :: + + $ ./bootstrap + +You do not need to run `bootstrap` if you are using a source tarball. + +You then need to build the plugin: :: + + $ ./configure + $ make + +To install, you can simply run (as root or using sudo or similar): :: + + $ make install + +Installing server-side plugin +============================= + +The shared library that holds the plugin, auth_pam.so, needs to be stored in the plugindir directory of mysql. You can get this value via the command: :: + + $ mysql_config --plugindir + +Make sure that after installed, the library has got the appropiate permissions (file execution is required). + +Most packages should do this for you, so this is likely only required with the binary tarballs. + +In order to load the plugin into the working server, issue the following command: :: + + mysql> INSTALL PLUGIN auth_pam SONAME 'auth_pam.so'; + + +You can now create a PAM configuration for the MySQL server and create users that are authenticated by PAM. diff --git a/plugin/percona-pam-for-mysql/doc/source/intro.rst b/plugin/percona-pam-for-mysql/doc/source/intro.rst new file mode 100644 index 000000000000..600e2f57a6a8 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/intro.rst @@ -0,0 +1,23 @@ +================================================== + About Percona PAM Authentication Plugin for MySQL +================================================== + +Percona PAM Authentication Plugin is a free and Open Source implementation of the MySQL's authentication plugin. This plugin acts as a mediator between the MySQL server, the MySQL client, and the PAM stack. The server plugin requests authentication from the PAM stack, forwards any requests and messages from the PAM stack over the wire to the client (in cleartext) and reads back any replies for the PAM stack. + + PAM plugin uses dialog as its client side plugin. Dialog plugin can be loaded to any client application that uses libmysqlclient library (or compatible client library such as libperconaserverclient). + +Here are some of the benefits that Percona dialog plugin offers over the default one: + + * It correctly recognizes whether PAM wants input to be echoed or not, while the default one always echoes the input on the user's console. + * It can use the password which is passed to |MySQL| client via "-p" parameter. + * Dialog client `installation bug `_ has been fixed. + * This plugin works on |MySQL| and |Percona Server|. + +Percona offers two versions of this plugin: + + * Full PAM plugin called *auth_pam*. This plugin uses *dialog.so*. It fully supports the PAM protocol with arbitrary communication between client and server. + * Oracle-compatible PAM called *auth_pam_compat*. This plugin uses *mysql_clear_password* which is a part of Oracle MySQL client. It also has some limitations, such as, it supports only one password input. You must use "-p" option in order to pass the password to auth_pam_compat. + +These two versions of plugins are physically different. To choose which one you want used, you must use *IDENTIFIED WITH 'auth_pam'* for auth_pam, and *IDENTIFIED WITH 'auth_pam_compat'* for auth_pam_compat. + + diff --git a/plugin/percona-pam-for-mysql/doc/source/manual.rst b/plugin/percona-pam-for-mysql/doc/source/manual.rst new file mode 100644 index 000000000000..f96e0883f2d6 --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/manual.rst @@ -0,0 +1,45 @@ +.. _user-manual: + +========================================================== + *Percona PAM authentication plugin for MySQL* User Manual +========================================================== + +.. toctree:: + :maxdepth: 1 + :hidden: + +Configuring PAM for MySQL +========================= + +You will need to configure PAM on your system for how it should authenticate for MySQL. A simple setup can be to use the standard UNIX authentication method. + +*NOTE:* Using pam_unix means the MySQL Server needs to read the `/etc/shadow` file, which usually means it has to be run as `root` - usually not a recommended configuration. + +A sample `/etc/pam.d/mysqld` file: :: + + auth required pam_unix.so + account required pam_unix.so + +For added information in the system log, you can expand it to be: :: + + auth required pam_warn.so + auth required pam_unix.so audit + account required pam_unix.so audit + + +Creating A User +=============== + +You will need to execute `CREATE USER` with specifying the PAM plugin. For example: :: + + CREATE USER 'username'@'host' IDENTIFIED WITH auth_pam; + +This creates a user `username` that can connect from `host` and will be authenticated using the PAM plugin. If you are using the `pam_unix` method in PAM (or similar) you will need to have an account for `username` existing on the system. + +Supplementary groups support +============================ + +|Percona Server| has implemented PAM plugin support for supplementary groups. Supplementary or secondary groups are extra groups a specific user is member of. For example user ``joe`` might be a member of groups: ``joe`` (his primary group) and secondary groups ``developers`` and ``dba``. A complete list of groups and users belonging to them can be checked with ``cat /etc/group`` command. + +This feature enables using secondary groups in the mapping part of the authentication string, like "``mysql, developers=joe, dba=mark``". Previously only primary groups could have been specified there. If user is a member of both ``developers`` and ``dba``, PAM plugin will map it to the ``joe`` because ``developers`` matches first. + diff --git a/plugin/percona-pam-for-mysql/doc/source/percona-pam-plugin-logo.png b/plugin/percona-pam-for-mysql/doc/source/percona-pam-plugin-logo.png new file mode 100644 index 000000000000..32afd1cab4fa Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/percona-pam-plugin-logo.png differ diff --git a/plugin/percona-pam-for-mysql/doc/source/percona_favicon.ico b/plugin/percona-pam-for-mysql/doc/source/percona_favicon.ico new file mode 100644 index 000000000000..f426064d6f50 Binary files /dev/null and b/plugin/percona-pam-for-mysql/doc/source/percona_favicon.ico differ diff --git a/plugin/percona-pam-for-mysql/doc/source/release-notes.rst b/plugin/percona-pam-for-mysql/doc/source/release-notes.rst new file mode 100644 index 000000000000..e025381cf6db --- /dev/null +++ b/plugin/percona-pam-for-mysql/doc/source/release-notes.rst @@ -0,0 +1,19 @@ +============================================================ + |Percona| PAM authentication plugin for MySQL Release Notes +============================================================ + +General availability (GA) +========================= + +Percona is pleased to announce that Percona's PAM Authentication plugin for MySQL has been merged to |Percona Server| `5.5.24-26.0 `_ on June 1st, 2012. With this release PAM Authentication plugin became GA. Further information on how to install and use the plugin can be found in the |Percona Server| documentation. + +Preview Release +=============== + +Percona is pleased to announce availability of an early access version of Percona's PAM Authentication plugin for MySQL on December 5th, 2011. This plugin supports |MySQL|-5.5.x, |Percona Server| 5.5.x and MariaDB 5.2.x. The PAM Authentication plugin can be used for: + * |MySQL| authentication using operating system users (``pam_unix``) + * |MySQL| authentication from LDAP server (pam_ldap) + * authentication against RSA SecurID server + * any other authentication methods that provides access via PAM + +Percona PAM Authentication Plugin for MySQL is fully open source, free of charge and can be used on an unlimited amount of servers. diff --git a/plugin/percona-pam-for-mysql/src/auth_mapping.cc b/plugin/percona-pam-for-mysql/src/auth_mapping.cc new file mode 100644 index 000000000000..b5215674bad7 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_mapping.cc @@ -0,0 +1,227 @@ +/* +(C) 2012, 2016 Percona LLC and/or its affiliates + +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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include "auth_mapping.h" +#include +#include +#include +#include "groups.h" + +#include "auth_pam_common.h" + +/** Token representation: + token type, string repr, length of token */ +struct token { + enum class ctype { id, comma, eq, eof } type; + const char *token; + size_t token_len; +}; + +/** Iterator in key-value mapping: + position and length of key, + position and length of value, + current position in string */ +struct mapping_iter { + const char *key; + size_t key_len; + const char *value; + size_t value_len; + const char *ptr; +}; + +/** Get next token from buf. Returns new buf position. */ +static const char *get_token(struct token *token, const char *buf) { + const char *ptr = buf; + + while (*ptr && isspace(*ptr)) ++ptr; + + token->token = ptr; + switch (*ptr) { + case '\0': + token->type = token::ctype::eof; + break; + case ',': + token->token_len = 1; + token->type = token::ctype::comma; + ++ptr; + break; + case '=': + token->token_len = 1; + token->type = token::ctype::eq; + ++ptr; + break; + case '"': + token->token_len = 0; + ++ptr; + token->token = ptr; + while (*ptr && *ptr != '"') { + ++token->token_len; + ++ptr; + } + token->type = token::ctype::id; + if (*ptr) ++ptr; + break; + default: + token->token_len = 0; + while (*ptr && !isspace(*ptr) && *ptr != ',' && *ptr != '=') { + ++token->token_len; + ++ptr; + } + token->type = token::ctype::id; + } + + return ptr; +} + +/** Create iterator through mapping string. + Initially iterator set to position before first + key-value pair. On success non-NULL pointer returned, otherwise NULL */ +struct mapping_iter *mapping_iter_new(const char *mapping_string) { + struct mapping_iter *it = static_cast( + my_malloc(key_memory_pam_mapping_iter, sizeof(struct mapping_iter), 0)); + if (it != nullptr) { + it->key = nullptr; + it->value = nullptr; + /* eat up service name and move to (, key = value)* part */ + struct token token; + it->ptr = get_token(&token, mapping_string); + } + return it; +} + +/** Move iterator to next key-value pair. + On success pointer to key position in string returned, + otherwise NULL */ +const char *mapping_iter_next(struct mapping_iter *it) { + struct token token[4] = {{token::ctype::id, 0, 0}}; + + /* read next 4 tokens */ + it->ptr = get_token( + token + 3, + get_token(token + 2, get_token(token + 1, get_token(token, it->ptr)))); + + /* was it ", id = id"? */ + if (!((token[0].type == token::ctype::comma) && + (token[1].type == token::ctype::id) && + (token[2].type == token::ctype::eq) && + (token[3].type == token::ctype::id))) { + /* we got something inconsistent */ + return nullptr; + } + + /* set key */ + it->key = token[1].token; + it->key_len = token[1].token_len; + + /* set value */ + it->value = token[3].token; + it->value_len = token[3].token_len; + + return it->key; +} + +/** Finish iteration and release iterator */ +void mapping_iter_free(struct mapping_iter *it) { my_free(it); } + +/** Get mapped value for given user name. + Value is looked up by using all user groups as a key. + Auth string is iterated only once, while groups are iterated + for every key-value pair. This is mean than auth string order + is dominant. + + Example: + + given: + user "foo" is the member of "wheel", "staff" and "bar". + auth string is "mysql, root=user1, bar=user2, staff=user3" + + result is "user2". + + On success value_buf returned, otherwise NULL */ +char *mapping_lookup_user(const char *user_name, char *value_buf, + size_t value_buf_len, const char *mapping_string) { + /* Iterate through the key-value list stored in auth_string and + find key (which is interpreted as group name) in the list of groups + for specified user. If match is found, store appropriate value in + the authenticated_as field. */ + struct mapping_iter *keyval_it = mapping_iter_new(mapping_string); + if (keyval_it == nullptr) return nullptr; + + struct groups_iter *group_it = groups_iter_new(user_name); + if (group_it == nullptr) { + mapping_iter_free(keyval_it); + return nullptr; + } + + const char *key; + const char *group; + while ((key = mapping_iter_next(keyval_it)) != nullptr) { + while ((group = groups_iter_next(group_it)) != nullptr) { + if (keyval_it->key_len == strlen(group) && + strncmp(key, group, keyval_it->key_len) == 0) { + /* match is found */ + memcpy(value_buf, keyval_it->value, + std::min(value_buf_len, keyval_it->value_len)); + value_buf[std::min(value_buf_len, keyval_it->value_len)] = '\0'; + groups_iter_free(group_it); + mapping_iter_free(keyval_it); + return value_buf; + } + } + groups_iter_reset(group_it); + } + + groups_iter_free(group_it); + mapping_iter_free(keyval_it); + + return nullptr; +} + +/** Get key in current iterator pos. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_key(struct mapping_iter *it, char *buf, size_t buf_len) { + if (it->key == nullptr) return nullptr; + memcpy(buf, it->key, std::min(buf_len, it->key_len)); + buf[std::min(buf_len, it->key_len)] = '\0'; + return buf; +} + +/** Get value in current iterator pos. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_value(struct mapping_iter *it, char *buf, + size_t buf_len) { + if (it->value == nullptr) return nullptr; + memcpy(buf, it->value, std::min(buf_len, it->value_len)); + buf[std::min(buf_len, it->value_len)] = '\0'; + return buf; +} + +/** Get value by key. On success pointer to service_name + returned, otherwise NULL */ +char *mapping_get_service_name(char *buf, size_t buf_len, + const char *mapping_string) { + struct token token; + + get_token(&token, mapping_string); + if (token.type == token::ctype::id) { + memcpy(buf, token.token, std::min(buf_len, token.token_len)); + buf[std::min(buf_len, token.token_len)] = '\0'; + return buf; + } + + return nullptr; +} diff --git a/plugin/percona-pam-for-mysql/src/auth_mapping.h b/plugin/percona-pam-for-mysql/src/auth_mapping.h new file mode 100644 index 000000000000..7a98e30bcb0a --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_mapping.h @@ -0,0 +1,73 @@ +#ifndef AUTH_MAPPING_INCLUDED +#define AUTH_MAPPING_INCLUDED +/* + (C) 2012, 2013 Percona LLC and/or its affiliates + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA + +*/ + +/** + @file + + PAM authentication for MySQL, interface for user mapping. + +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Mapping iterator. It's not exposed outsude */ +struct mapping_iter; + +/** Create iterator through mapping string. + Initially iterator set to position before first + key-value pair. On success non-NULL pointer returned, otherwise NULL */ +struct mapping_iter *mapping_iter_new(const char *mapping_string); + +/** Move iterator to next key-value pair. + On success pointer to key position in string returned, + otherwise NULL */ +const char *mapping_iter_next(struct mapping_iter *it); + +/** Finish iteration and release iterator */ +void mapping_iter_free(struct mapping_iter *it); + +/** Get key at current iterator position. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_key(struct mapping_iter *it, char *buf, size_t buf_len); + +/** Get value at current iterator position. On success buf returned, + otherwise NULL */ +char *mapping_iter_get_value(struct mapping_iter *it, char *buf, + size_t buf_len); + +/** Get value by given key. On success value_buf returned, + otherwise NULL */ +char *mapping_lookup_user(const char *key, char *value_buf, + size_t value_buf_len, const char *mapping_string); + +/** Get service name for auth_string. On success buf returned, + otherwise NULL */ +char *mapping_get_service_name(char *buf, size_t buf_len, + const char *mapping_string); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugin/percona-pam-for-mysql/src/auth_pam.cc b/plugin/percona-pam-for-mysql/src/auth_pam.cc new file mode 100644 index 000000000000..5fabfe161466 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam.cc @@ -0,0 +1,179 @@ +/* +(C) 2012, 2015 Percona LLC and/or its affiliates + +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 Street, Fifth Floor, Boston, MA 02110-1301, USA + +*/ + +/** + @file + + PAM authentication for MySQL, server-side plugin for the + production use. + + A general-purpose PAM authentication plugin for MySQL. Acts as a mediator + between the MySQL server, the MySQL client, and the PAM backend. Dialog plugin + used as client plugin. + + The server plugin requests authentication from the PAM backend, forwards any + requests and messages from the PAM backend over the wire to the client (in + cleartext) and reads back any replies for the backend. + + This plugin does not encrypt the communication channel in any way. If this is + required, a SSL connection should be used. + + To install this plugin, copy the .so file to the plugin directory and do + + INSTALL PLUGIN auth_pam SONAME 'auth_pam.so'; + + To use this plugin for one particular user, specify it at user's creation time + (TODO: tested with localhost only): + + CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam; + + Alternatively UPDATE the mysql.user table to set the plugin value for an + existing user. + + Also it is possible to use this plugin to authenticate anonymous users: + + CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam; + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "auth_pam_common.h" +#include "my_sys.h" +#include "mysql/psi/mysql_memory.h" + +MYSQL_PLUGIN auth_pam_plugin_info; + +/** The maximum length of buffered PAM messages, i.e. any messages up to the + next PAM reply-requiring message. 10K should be more than enough by order + of magnitude. */ +static const constexpr auto max_pam_buffered_msg_len = 10240; + +static PSI_memory_key key_memory_pam_msg_buf; + +static PSI_memory_info pam_auth_memory[] = { + {&key_memory_pam_msg_buf, "auth_pam_msg_buf", PSI_FLAG_ONLY_GLOBAL_STAT, + PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, +}; + +struct pam_msg_buf { + unsigned char buf[max_pam_buffered_msg_len]; + unsigned char *ptr; +}; + +static char pam_msg_style_to_char(int pam_msg_style) { + /* Magic byte for the dialog plugin, '\2' is defined as ORDINARY_QUESTION + and '\4' as PASSWORD_QUESTION there. */ + return (pam_msg_style == PAM_PROMPT_ECHO_ON) ? '\2' : '\4'; +} + +int auth_pam_client_talk_init(void **talk_data) { + struct pam_msg_buf *msg_buf = static_cast(my_malloc( + key_memory_pam_msg_buf, sizeof(struct pam_msg_buf), MY_ZEROFILL)); + *talk_data = (void *)msg_buf; + if (msg_buf != nullptr) { + msg_buf->ptr = msg_buf->buf + 1; + return PAM_SUCCESS; + } + return PAM_BUF_ERR; +} + +void auth_pam_client_talk_finalize(void *talk_data) { my_free(talk_data); } + +int auth_pam_talk_perform(const struct pam_message *msg, + struct pam_response *resp, struct pam_conv_data *data, + void *talk_data) { + struct pam_msg_buf *msg_buf = (struct pam_msg_buf *)talk_data; + + /* Append the PAM message or prompt to the unsent message buffer */ + if (msg->msg) { + unsigned char *last_buf_pos = msg_buf->buf + max_pam_buffered_msg_len - 1; + if (msg_buf->ptr + strlen(msg->msg) >= last_buf_pos) { + /* Cannot happen: the PAM message buffer too small. */ + MY_ASSERT_UNREACHABLE(); + return PAM_CONV_ERR; + } + memcpy(msg_buf->ptr, msg->msg, strlen(msg->msg)); + msg_buf->ptr += strlen(msg->msg); + *(msg_buf->ptr)++ = '\n'; + } + + if (msg->msg_style == PAM_PROMPT_ECHO_OFF || + msg->msg_style == PAM_PROMPT_ECHO_ON) { + int pkt_len; + unsigned char *pkt; + + msg_buf->buf[0] = pam_msg_style_to_char(msg->msg_style); + + /* Write the message. */ + if (data->vio->write_packet(data->vio, msg_buf->buf, + msg_buf->ptr - msg_buf->buf - 1)) + return PAM_CONV_ERR; + + /* Read the answer */ + if ((pkt_len = data->vio->read_packet(data->vio, &pkt)) < 0) + return PAM_CONV_ERR; + + resp->resp = static_cast(malloc(pkt_len + 1)); + if (resp->resp == nullptr) return PAM_BUF_ERR; + + strncpy(resp->resp, (char *)pkt, pkt_len); + resp->resp[pkt_len] = '\0'; + + if (msg->msg_style == PAM_PROMPT_ECHO_OFF) + data->info->password_used = PASSWORD_USED_YES; + + msg_buf->ptr = msg_buf->buf + 1; + } + + return PAM_SUCCESS; +} + +static int auth_pam_init(MYSQL_PLUGIN plugin_info) { + int count; + auth_pam_common_init("auth_pam"); + count = array_elements(pam_auth_memory); + mysql_memory_register("auth_pam", pam_auth_memory, count); + auth_pam_plugin_info = plugin_info; + return 0; +} + +static struct st_mysql_auth pam_auth_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "dialog", + &authenticate_user_with_pam_server, + &auth_pam_generate_auth_string_hash, + &auth_pam_validate_auth_string_hash, + &auth_pam_set_salt, + 0UL, + nullptr}; + +mysql_declare_plugin(auth_pam) { + MYSQL_AUTHENTICATION_PLUGIN, &pam_auth_handler, "auth_pam", "Percona, Inc.", + "PAM authentication plugin", PLUGIN_LICENSE_GPL, auth_pam_init, nullptr, + nullptr, 0x0001, nullptr, nullptr, nullptr +#if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 + , + 0 +#endif +} +mysql_declare_plugin_end; diff --git a/plugin/percona-pam-for-mysql/src/auth_pam_common.cc b/plugin/percona-pam-for-mysql/src/auth_pam_common.cc new file mode 100644 index 000000000000..11067fd9d3a7 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam_common.cc @@ -0,0 +1,208 @@ +/* +(C) 2011-2015 Percona LLC and/or its affiliates + +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 Street, Fifth Floor, Boston, MA 02110-1301, USA + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "auth_mapping.h" +#include "auth_pam_common.h" +#include "groups.h" +#include "my_sys.h" +#include "mysql/psi/mysql_memory.h" + +/* The server plugin */ + +PSI_memory_key key_memory_pam_mapping_iter; +PSI_memory_key key_memory_pam_group_iter; + +static PSI_memory_info common_pam_memory[] = { + {&key_memory_pam_mapping_iter, "auth_pam_mapping_iterator", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, + {&key_memory_pam_group_iter, "auth_pam_group_iterator", + PSI_FLAG_ONLY_GLOBAL_STAT, PSI_VOLATILITY_UNKNOWN, PSI_DOCUMENT_ME}, +}; + +/** The MySQL service name for PAM configuration */ +static const char *service_name_default = "mysqld"; + +void auth_pam_common_init(const char *psi_category) { + int count = array_elements(common_pam_memory); + mysql_memory_register(psi_category, common_pam_memory, count); +} + +static bool valid_pam_msg_style(int pam_msg_style) { + switch (pam_msg_style) { + case PAM_PROMPT_ECHO_OFF: + case PAM_PROMPT_ECHO_ON: + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + return true; + default: + return false; + } +} + +/** The maximum length of service name. It shouldn't be too long as it's + filename in pam.d directory also */ +static const constexpr auto max_pam_service_name_len = 64; + +static void free_pam_response(struct pam_response **resp, int n) { + for (int i = 0; i < n; i++) { + free((*resp)[i].resp); + } + free(*resp); + *resp = nullptr; +} + +static int vio_server_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) { + struct pam_conv_data *data = (struct pam_conv_data *)appdata_ptr; + + if (data == nullptr) { + MY_ASSERT_UNREACHABLE(); + return PAM_CONV_ERR; + } + + *resp = (struct pam_response *)calloc(sizeof(struct pam_response), num_msg); + if (*resp == nullptr) return PAM_BUF_ERR; + + void *talk_data; + int error = auth_pam_client_talk_init(&talk_data); + if (error != PAM_SUCCESS) { + free_pam_response(resp, 0); + return error; + } + + for (int i = 0; i < num_msg; i++) { + if (!valid_pam_msg_style(msg[i]->msg_style)) { + auth_pam_client_talk_finalize(talk_data); + free_pam_response(resp, i); + return PAM_CONV_ERR; + } + + error = auth_pam_talk_perform(msg[i], &(*resp)[i], data, talk_data); + if (error != PAM_SUCCESS) { + auth_pam_client_talk_finalize(talk_data); + free_pam_response(resp, i); + return error; + } + } + auth_pam_client_talk_finalize(talk_data); + return PAM_SUCCESS; +} + +int authenticate_user_with_pam_server(MYSQL_PLUGIN_VIO *vio, + MYSQL_SERVER_AUTH_INFO *info) { + /* Set service name as specified in auth_string. If no auth_string + provided or parsing error occurs, then keep default value */ + char service_name[max_pam_service_name_len]; + strcpy(service_name, service_name_default); + if (info->auth_string) + mapping_get_service_name(service_name, sizeof(service_name), + info->auth_string); + + info->password_used = PASSWORD_USED_NO_MENTION; + + pam_handle_t *pam_handle; + struct pam_conv_data data = {vio, info}; + struct pam_conv conv_func_info = {&vio_server_conv, &data}; + int error = + pam_start(service_name, info->user_name, &conv_func_info, &pam_handle); + if (error != PAM_SUCCESS) return CR_ERROR; + + error = pam_set_item(pam_handle, PAM_RUSER, info->user_name); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + error = pam_set_item(pam_handle, PAM_RHOST, info->host_or_ip); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + error = pam_authenticate(pam_handle, 0); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + error = pam_acct_mgmt(pam_handle, 0); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + /* Get the authenticated user name from PAM */ + char *pam_mapped_user_name; + error = pam_get_item(pam_handle, PAM_USER, + const_cast( + reinterpret_cast(&pam_mapped_user_name))); + if (error != PAM_SUCCESS) { + pam_end(pam_handle, error); + return CR_ERROR; + } + + /* Check if user name from PAM is the same as provided for MySQL. If + different, use the new user name for MySQL authorization and as + CURRENT_USER() value. */ + if (strcmp(info->user_name, pam_mapped_user_name)) { + strncpy(info->authenticated_as, pam_mapped_user_name, + MYSQL_USERNAME_LENGTH); + info->authenticated_as[MYSQL_USERNAME_LENGTH] = '\0'; + } + + if (info->auth_string) { + mapping_lookup_user(pam_mapped_user_name, info->authenticated_as, + MYSQL_USERNAME_LENGTH, info->auth_string); + } + + error = pam_end(pam_handle, error); + if (error != PAM_SUCCESS) return CR_ERROR; + + return CR_OK; +} + +int auth_pam_generate_auth_string_hash(char *outbuf, unsigned int *buflen, + const char *inbuf, + unsigned int inbuflen) { + /* + fail if buffer specified by server cannot be copied to output buffer + */ + if (*buflen < inbuflen) return 1; /* error */ + strncpy(outbuf, inbuf, inbuflen); + *buflen = strlen(inbuf); + return 0; /* success */ +} + +int auth_pam_validate_auth_string_hash(char *const buf __attribute__((unused)), + unsigned int len + __attribute__((unused))) { + return 0; /* success */ +} + +int auth_pam_set_salt(const char *password __attribute__((unused)), + unsigned int password_len __attribute__((unused)), + unsigned char *salt __attribute__((unused)), + unsigned char *salt_len) { + *salt_len = 0; + return 0; /* success */ +} diff --git a/plugin/percona-pam-for-mysql/src/auth_pam_common.h b/plugin/percona-pam-for-mysql/src/auth_pam_common.h new file mode 100644 index 000000000000..9976adebe238 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam_common.h @@ -0,0 +1,82 @@ +#ifndef AUTH_PAM_COMMON_INCLUDED +#define AUTH_PAM_COMMON_INCLUDED +/* + (C) 2012 Percona 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, common definitions for side plugins. + + For the general description, see the top comment in auth_pam_common.c. +*/ + +#include +#include +#ifdef HAVE_SECURITY_PAM_MISC_H +#include +#elif defined(HAVE_SECURITY_OPENPAM_H) +#include +#endif + +#include "mysql/client_plugin.h" +#include "mysql/plugin.h" +#include "mysql/plugin_auth.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct pam_conv_data { + MYSQL_PLUGIN_VIO *vio; + MYSQL_SERVER_AUTH_INFO *info; +}; + +extern MYSQL_PLUGIN auth_pam_plugin_info; + +extern PSI_memory_key key_memory_pam_mapping_iter; +extern PSI_memory_key key_memory_pam_group_iter; + +void auth_pam_common_init(const char *psi_category); + +/** Define following three functions for your specific client plugin */ + +int auth_pam_client_talk_init(void **talk_data); + +int auth_pam_talk_perform(const struct pam_message *msg, + struct pam_response *resp, struct pam_conv_data *data, + void *talk_data); + +void auth_pam_client_talk_finalize(void *talk_data); + +int authenticate_user_with_pam_server(MYSQL_PLUGIN_VIO *vio, + MYSQL_SERVER_AUTH_INFO *info); + +int auth_pam_generate_auth_string_hash(char *outbuf, unsigned int *buflen, + const char *inbuf, + unsigned int inbuflen); + +int auth_pam_validate_auth_string_hash(char *const buf, unsigned int len); + +int auth_pam_set_salt(const char *password, unsigned int password_len, + unsigned char *salt, unsigned char *salt_len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugin/percona-pam-for-mysql/src/auth_pam_compat.cc b/plugin/percona-pam-for-mysql/src/auth_pam_compat.cc new file mode 100644 index 000000000000..c855f5f8f49c --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/auth_pam_compat.cc @@ -0,0 +1,129 @@ +/* +(C) 2012, 2015 Percona LLC and/or its affiliates + +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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, server-side plugin for the + production use. + + Oracle MySQL-compatible plugin. Acts as a mediator + between the MySQL server, the MySQL client, and the PAM backend. + + The server plugin requests authentication from the PAM backend, and reads one + phrase from client plugin. mysql_clear_password plugin used as client plugin. + + This plugin does not encrypt the communication channel in any way. If this is + required, a SSL connection should be used. + + To install this plugin, copy the .so file to the plugin directory and do + + INSTALL PLUGIN auth_pam SONAME 'auth_pam_compat.so'; + + To use this plugin for one particular user, specify it at user's creation time + (TODO: tested with localhost only): + + CREATE USER 'username'@'hostname' IDENTIFIED WITH auth_pam_compat; + + Alternatively UPDATE the mysql.user table to set the plugin value for an + existing user. + + Also it is possible to use this plugin to authenticate anonymous users: + + CREATE USER ''@'hostname' IDENTIFIED WITH auth_pam_compat; + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "auth_pam_common.h" +#include "my_sys.h" + +MYSQL_PLUGIN auth_pam_plugin_info; + +int auth_pam_client_talk_init(void **talk_data) { + int *num_talks = static_cast( + my_malloc(PSI_NOT_INSTRUMENTED, sizeof(int), MY_ZEROFILL)); + *talk_data = (void *)num_talks; + return (num_talks != nullptr) ? PAM_SUCCESS : PAM_BUF_ERR; +} + +void auth_pam_client_talk_finalize(void *talk_data) { my_free(talk_data); } + +int auth_pam_talk_perform(const struct pam_message *msg, + struct pam_response *resp, struct pam_conv_data *data, + void *talk_data) { + if (msg->msg_style == PAM_PROMPT_ECHO_OFF || + msg->msg_style == PAM_PROMPT_ECHO_ON) { + /* mysql_clear_password plugin has support for only single phrase */ + int *num_talks = (int *)talk_data; + if (*num_talks > 1) return PAM_CONV_ERR; + + /* Read the answer */ + unsigned char *pkt; + int pkt_len = data->vio->read_packet(data->vio, &pkt); + if (pkt_len < 0) return PAM_CONV_ERR; + + resp->resp = static_cast(malloc(pkt_len + 1)); + if (resp->resp == nullptr) return PAM_BUF_ERR; + + strncpy(resp->resp, (char *)pkt, pkt_len); + resp->resp[pkt_len] = '\0'; + + /** + we could only guess whether password was used or not + normally we would set PASSWORD_USED_NO_MENTION but + because of http://bugs.mysql.com/bug.php?id=72536 + we set PASSWORD_USED_YES. + */ + data->info->password_used = PASSWORD_USED_YES; + ++(*num_talks); + } + + return PAM_SUCCESS; +} + +static int auth_pam_compat_init(MYSQL_PLUGIN plugin_info) { + auth_pam_common_init("auth_pam_compat"); + auth_pam_plugin_info = plugin_info; + return 0; +} + +static struct st_mysql_auth pam_auth_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "mysql_clear_password", + &authenticate_user_with_pam_server, + &auth_pam_generate_auth_string_hash, + &auth_pam_validate_auth_string_hash, + &auth_pam_set_salt, + 0UL, + nullptr}; + +mysql_declare_plugin(auth_pam) { + MYSQL_AUTHENTICATION_PLUGIN, &pam_auth_handler, "auth_pam_compat", + "Percona, Inc.", "PAM authentication plugin", PLUGIN_LICENSE_GPL, + auth_pam_compat_init, nullptr, nullptr, 0x0001, nullptr, nullptr, nullptr +#if MYSQL_PLUGIN_INTERFACE_VERSION >= 0x103 + , + 0 +#endif +} +mysql_declare_plugin_end; diff --git a/plugin/percona-pam-for-mysql/src/dialog.cc b/plugin/percona-pam-for-mysql/src/dialog.cc new file mode 100644 index 000000000000..e676ab046d19 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/dialog.cc @@ -0,0 +1,327 @@ +/* Copyright (c) 2010, 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-1301 USA */ + +/** + @file + + dialog client authentication plugin with examples + + dialog is a general purpose client authentication plugin, it simply + asks the user the question, as provided by the server and reports + the answer back to the server. No encryption is involved, + the answers are sent in clear text. + + Two examples are provided: two_questions server plugin, that asks + the password and an "Are you sure?" question with a reply "yes, of course". + It demonstrates the usage of "password" (input is hidden) and "ordinary" + (input can be echoed) questions, and how to mark the last question, + to avoid an extra roundtrip. + + And three_attempts plugin that gives the user three attempts to enter + a correct password. It shows the situation when a number of questions + is not known in advance. +*/ +#if defined(WIN32) && !defined(RTLD_DEFAULT) +#define RTLD_DEFAULT GetModuleHandle(NULL) +#endif + +#include +#include +#include +#include "mysql.h" +#include "mysql/client_plugin.h" +#include "mysql/plugin_auth.h" + +#ifdef HAVE_DLFCN_H +#include +#endif + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE /* for RTLD_DEFAULT */ +#endif + +/** + first byte of the question string is the question "type". + It can be an "ordinary" or a "password" question. + The last bit set marks a last question in the authentication exchange. +*/ +#define ORDINARY_QUESTION "\2" +#define LAST_QUESTION "\3" +#define PASSWORD_QUESTION "\4" +#define LAST_PASSWORD "\5" + +/********************* SERVER SIDE ****************************************/ + +/** + dialog demo with two questions, one password and one, the last, ordinary. +*/ +static int two_questions(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { + /* send a password question */ + if (vio->write_packet( + vio, + (const unsigned char *)PASSWORD_QUESTION "Password, please:", 18)) + return CR_ERROR; + + /* read the answer */ + unsigned char *pkt; + int pkt_len = vio->read_packet(vio, &pkt); + if (pkt_len < 0) return CR_ERROR; + + info->password_used = PASSWORD_USED_YES; + + /* fail if the password is wrong */ + if (strcmp((const char *)pkt, info->auth_string)) return CR_ERROR; + + /* send the last, ordinary, question */ + if (vio->write_packet( + vio, (const unsigned char *)LAST_QUESTION "Are you sure ?", 15)) + return CR_ERROR; + + /* read the answer */ + if ((pkt_len = vio->read_packet(vio, &pkt)) < 0) return CR_ERROR; + + /* check the reply */ + return strcmp((const char *)pkt, "yes, of course") ? CR_ERROR : CR_OK; +} + +static struct st_mysql_auth two_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "dialog", /* requires dialog client plugin */ + two_questions, + nullptr, + nullptr, + nullptr, + 0UL, + nullptr}; + +/* dialog demo where the number of questions is not known in advance */ +static int three_attempts(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { + for (int i = 0; i < 3; i++) { + /* send the prompt */ + if (vio->write_packet( + vio, + (const unsigned char *)PASSWORD_QUESTION "Password, please:", 18)) + return CR_ERROR; + + /* read the password */ + unsigned char *pkt; + int pkt_len = vio->read_packet(vio, &pkt); + if (pkt_len < 0) return CR_ERROR; + + info->password_used = PASSWORD_USED_YES; + + /* + finish, if the password is correct. + note, that we did not mark the prompt packet as "last" + */ + if (strcmp((const char *)pkt, info->auth_string) == 0) return CR_OK; + } + + return CR_ERROR; +} + +static struct st_mysql_auth three_handler = { + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + "dialog", /* requires dialog client plugin */ + three_attempts, + nullptr, + nullptr, + nullptr, + 0UL, + nullptr}; + +mysql_declare_plugin(dialog){ + MYSQL_AUTHENTICATION_PLUGIN, + &two_handler, + "two_questions", + "Sergei Golubchik", + "Dialog plugin demo 1", + PLUGIN_LICENSE_GPL, + nullptr, + nullptr, + nullptr, + 0x0100, + nullptr, + nullptr, + nullptr, + 0, +}, + { + MYSQL_AUTHENTICATION_PLUGIN, + &three_handler, + "three_attempts", + "Sergei Golubchik", + "Dialog plugin demo 2", + PLUGIN_LICENSE_GPL, + nullptr, + nullptr, + nullptr, + 0x0100, + nullptr, + nullptr, + nullptr, + 0, + } mysql_declare_plugin_end; + +/********************* CLIENT SIDE ***************************************/ +/* + This plugin performs a dialog with the user, asking questions and + reading answers. Depending on the client it may be desirable to do it + using GUI, or console, with or without curses, or read answers + from a smartcard, for example. + + To support all this variety, the dialog plugin has a callback function + "authentication_dialog_ask". If the client has a function of this name + dialog plugin will use it for communication with the user. Otherwise + a default fgets() based implementation will be used. +*/ + +/** + type of the mysql_authentication_dialog_ask function + + @param mysql mysql + @param type type of the input + 1 - ordinary string input + 2 - password string + @param prompt prompt + @param buf a buffer to store the use input + @param buf_len the length of the buffer + + @retval a pointer to the user input string. + It may be equal to 'buf' or to 'mysql->password'. + In all other cases it is assumed to be an allocated + string, and the "dialog" plugin will free() it. +*/ +typedef char *(*mysql_authentication_dialog_ask_t)(MYSQL *mysql, int type, + const char *prompt, + char *buf, int buf_len); + +static mysql_authentication_dialog_ask_t ask; + +static char *builtin_ask(MYSQL *mysql [[maybe_unused]], + int type [[maybe_unused]], const char *prompt, + char *buf, int buf_len) { + if (type == 2) /* password */ + { + char *const password = get_tty_password(prompt); + strncpy(buf, password, buf_len - 1); + buf[buf_len - 1] = 0; + free(password); + } else { + if (!fgets(buf, buf_len - 1, stdin)) + buf[0] = 0; + else { + const int len = strlen(buf); + if (len && buf[len - 1] == '\n') buf[len - 1] = 0; + } + } + + return buf; +} + +/** + The main function of the dialog plugin. + + Read the prompt, ask the question, send the reply, repeat until + the server is satisfied. + + @note + 1. this plugin shows how a client authentication plugin + may read a MySQL protocol OK packet internally - which is important + where a number of packets is not known in advance. + 2. the first byte of the prompt is special. it is not + shown to the user, but signals whether it is the last question + (prompt[0] & 1 == 1) or not last (prompt[0] & 1 == 0), + and whether the input is a password (not echoed). + 3. the prompt is expected to be sent zero-terminated +*/ +static int perform_dialog(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { + unsigned char cmd = 0; + char reply_buf[1024]; + bool first = true; + + do { + /* read the prompt */ + unsigned char *pkt; + int pkt_len = vio->read_packet(vio, &pkt); + if (pkt_len < 0) return CR_ERROR; + + char *reply; + if (pkt == nullptr && first) { + /* + in mysql_change_user() the client sends the first packet, so + the first vio->read_packet() does nothing (pkt == 0). + + We send the "password", assuming the client knows what it's doing. + (in other words, the dialog plugin should be only set as a default + authentication plugin on the client if the first question + asks for a password - which will be sent in clear text, by the way) + */ + reply = mysql->passwd; + } else { + cmd = *pkt++; + + /* is it MySQL protocol packet ? */ + if (cmd == 0 || cmd == 254) + return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */ + + /* + asking for a password in the first packet mean mysql->password, if it's + set otherwise we ask the user and read the reply + */ + if ((cmd >> 1) == 2 && first && mysql->passwd[0]) + reply = mysql->passwd; + else + reply = ask(mysql, cmd >> 1, (const char *)pkt, reply_buf, + sizeof(reply_buf)); + if (!reply) return CR_ERROR; + } + /* send the reply to the server */ + const int res = + vio->write_packet(vio, (const unsigned char *)reply, strlen(reply) + 1); + + if (reply != mysql->passwd && reply != reply_buf) free(reply); + + if (res) return CR_ERROR; + + /* repeat unless it was the last question */ + first = false; + } while ((cmd & 1) != 1); + + /* the job of reading the ok/error packet is left to the server */ + return CR_OK; +} + +/** + initialization function of the dialog plugin + + Pick up the client's authentication_dialog_ask() function, if exists, + or fall back to the default implementation. +*/ + +static int init_dialog(char *unused1 [[maybe_unused]], + size_t unused2 [[maybe_unused]], + int unused3 [[maybe_unused]], + va_list unused4 [[maybe_unused]]) { + void *sym = dlsym(RTLD_DEFAULT, "mysql_authentication_dialog_ask"); + ask = sym ? (mysql_authentication_dialog_ask_t)sym : builtin_ask; + return 0; +} + +mysql_declare_client_plugin(AUTHENTICATION) "dialog", "Sergei Golubchik", + "Dialog Client Authentication Plugin", {0, 1, 0}, "GPL", + nullptr, init_dialog, nullptr, nullptr, nullptr, perform_dialog, + nullptr, mysql_end_client_plugin; diff --git a/plugin/percona-pam-for-mysql/src/groups.cc b/plugin/percona-pam-for-mysql/src/groups.cc new file mode 100644 index 000000000000..27b9f1f8114d --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/groups.cc @@ -0,0 +1,146 @@ +/* +(C) 2013, 2016 Percona LLC and/or its affiliates + +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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +#include +#include +#include +#include +#include +#include + +#include "auth_pam_common.h" + +#include "my_sys.h" + +static int gr_buf_size = 0; + +#ifdef __APPLE__ +using my_gid_t = int; +#else +using my_gid_t = gid_t; +#endif + +/** Groups iterator. It's not exposed outsude */ +struct groups_iter { + char *buf; + my_gid_t *groups; + int current_group; + int ngroups; + int buf_size; +}; + +/** Create iterator through user groups. + Initially iterator set to position before first + group. On success non-NULL pointer returned, otherwise NULL */ +struct groups_iter *groups_iter_new(const char *user_name) { + if (gr_buf_size <= 0) { + long gr_size_max, pw_size_max; + gr_size_max = sysconf(_SC_GETGR_R_SIZE_MAX); + pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX); + gr_buf_size = gr_size_max > pw_size_max ? gr_size_max : pw_size_max; + } + + struct groups_iter *const it = (struct groups_iter *)my_malloc( + key_memory_pam_group_iter, sizeof(struct groups_iter), + MYF(MY_FAE | MY_ZEROFILL)); + + it->buf_size = gr_buf_size; + if (it->buf_size <= 0) it->buf_size = 1024; + + it->buf = + (char *)my_malloc(key_memory_pam_group_iter, it->buf_size, MYF(MY_FAE)); + + struct passwd pwd, *pwd_result; + int error; + while ((error = getpwnam_r(user_name, &pwd, it->buf, it->buf_size, + &pwd_result)) == ERANGE) { + it->buf_size = it->buf_size * 2; + it->buf = (char *)my_realloc(key_memory_pam_group_iter, it->buf, + it->buf_size, MYF(MY_FAE)); + } + if (error != 0 || pwd_result == nullptr) { + my_plugin_log_message(&auth_pam_plugin_info, MY_ERROR_LEVEL, + "Unable to obtain the passwd entry for the user " + "'%s'.", + user_name); + my_free(it->buf); + my_free(it); + return nullptr; + } + + gr_buf_size = it->buf_size; + + it->ngroups = 1024; + it->groups = static_cast(my_malloc( + key_memory_pam_group_iter, it->ngroups * sizeof(gid_t), MYF(MY_FAE))); + error = getgrouplist(user_name, pwd_result->pw_gid, it->groups, &it->ngroups); + if (error == -1) { + it->groups = static_cast( + my_realloc(key_memory_pam_group_iter, it->groups, + it->ngroups * sizeof(gid_t), MYF(MY_FAE))); + error = + getgrouplist(user_name, pwd_result->pw_gid, it->groups, &it->ngroups); + if (error == -1) { + my_plugin_log_message(&auth_pam_plugin_info, MY_ERROR_LEVEL, + "Unable to obtain the group access list for " + "the user '%s'.", + user_name); + my_free(it->buf); + my_free(it->groups); + my_free(it); + return nullptr; + } + } + + return it; +} + +/** Move iterator to next group. + On success group name is returned, + otherwise NULL */ +const char *groups_iter_next(struct groups_iter *it) { + if (it->current_group >= it->ngroups) return nullptr; + + int error; + struct group grp, *grp_result; + while ((error = getgrgid_r(it->groups[it->current_group], &grp, it->buf, + it->buf_size, &grp_result)) == ERANGE) { + it->buf_size = it->buf_size * 2; + it->buf = (char *)my_realloc(key_memory_pam_group_iter, it->buf, + it->buf_size, MYF(MY_FAE)); + } + if (error != 0 || grp_result == nullptr) { + my_plugin_log_message(&auth_pam_plugin_info, MY_ERROR_LEVEL, + "Unable to obtain the group record for the group " + "id %d.", + (int)it->groups[it->current_group]); + return nullptr; + } + ++it->current_group; + + return grp_result->gr_name; +} + +/** Make iterator to point to the beginning again */ +void groups_iter_reset(struct groups_iter *it) { it->current_group = 0; } + +/** Finish iteration and release iterator */ +void groups_iter_free(struct groups_iter *it) { + my_free(it->buf); + my_free(it->groups); + my_free(it); +} diff --git a/plugin/percona-pam-for-mysql/src/groups.h b/plugin/percona-pam-for-mysql/src/groups.h new file mode 100644 index 000000000000..30b937468aeb --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/groups.h @@ -0,0 +1,45 @@ +#ifndef AUTH_PAM_GROUPS_INCLUDED +#define AUTH_PAM_GROUPS_INCLUDED +/* + (C) 2013 Percona LLC and/or its affiliates + + 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 Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, interface for groups enumeration. + +*/ + +struct groups_iter; + +/** Create iterator through user groups. + Initially iterator set to position before first + group. On success non-NULL pointer returned, otherwise NULL */ +struct groups_iter *groups_iter_new(const char *user_name); + +/** Move iterator to next group. + On success group name is returned, + otherwise NULL */ +const char *groups_iter_next(struct groups_iter *it); + +/** Make iterator to point to beginning again */ +void groups_iter_reset(struct groups_iter *it); + +/** Finish iteration and release iterator */ +void groups_iter_free(struct groups_iter *it); + +#endif diff --git a/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.c b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.c new file mode 100644 index 000000000000..caed7563391a --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.c @@ -0,0 +1,72 @@ +/* + (C) 2011 Percona 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, common code for client-side plugins. + + For the general description, see the top comment in auth_pam.c. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "lib_auth_pam_client.h" + +#include +#include + +#define MY_ASSERT_UNREACHABLE() assert(0) + +int authenticate_user_with_pam_client_common( + MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql __attribute__((unused)), + prompt_fn echoless_prompt_fn, prompt_fn echo_prompt_fn, + info_fn show_error_fn, info_fn show_info_fn) { + do { + char *buf; + int pkt_len; + + if ((pkt_len = vio->read_packet(vio, (unsigned char **)&buf)) < 0) + return CR_ERROR; + + /* The first byte is the message type, followed by the message itself. */ + + if (buf[0] == '\2' || buf[0] == '\3') { + /* '\2' - PAM_PROMPT_ECHO_OFF, '\3' - PAM_PROMPT_ECHO_ON */ + char *reply = (buf[0] == '\2') ? echoless_prompt_fn(&buf[1]) + : echo_prompt_fn(&buf[1]); + if (!reply) return CR_ERROR; + if (vio->write_packet(vio, (unsigned char *)reply, strlen(reply) + 1)) { + free(reply); + return CR_ERROR; + } + free(reply); + } else if (buf[0] == '\4') /* PAM_ERROR_MSG */ + show_error_fn(&buf[1]); + else if (buf[0] == '\5') /* PAM_TEXT_INFO */ + show_info_fn(&buf[1]); + else if (buf[0] == '\0') /* end-of-authorization */ + return CR_OK; + else + return CR_ERROR; /* Unknown! */ + } while (1); + + /* Should not come here */ + MY_ASSERT_UNREACHABLE(); + return CR_ERROR; +} diff --git a/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.h b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.h new file mode 100644 index 000000000000..6aa1c7b27a41 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/lib_auth_pam_client.h @@ -0,0 +1,77 @@ +#ifndef LIB_AUTH_PAM_CLIENT_INCLUDED +#define LIB_AUTH_PAM_CLIENT_INCLUDED +/* + (C) 2011 Percona 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, common definitions for client-side plugins. + + For the general description, see the top comment in auth_pam.c. +*/ + +#define STDCALL + +#include "mysql/client_plugin.h" + +/** + Callback type for functions that prompt the user for (echoed or silent) input + and return it. Should returns a pointer to malloc-allocated string, the + caller is responsible for freeing it. Should return NULL in the case of a + memory allocation or I/O error. */ +typedef char *(*prompt_fn)(const char *); + +/** + Callback type for functions that show user some info (error or notification). +*/ +typedef void (*info_fn)(const char *); + +struct st_mysql; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Client-side PAM auth plugin implementation. + + Communicates with the server-side plugin and does user interaction using the + provided callbacks. + + @param vio TODO + @param mysql TODO + @param echoless_prompt_fn callback to use to prompt the user for non-echoed + input (e.g. password) + @param echo_prompt_fn callback to use to prompt the user for echoed input + (e.g. user name) + @param show_error_fn callback to use to show the user an error message + @param show_info_fn callback to use to show the user an informational message + + @return Authentication conversation status + @retval CR_OK the authentication dialog is completed successfully + @retval CR_ERROR the authentication dialog is aborted due to error +*/ +int authenticate_user_with_pam_client_common( + MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql, prompt_fn echoless_prompt_fn, + prompt_fn echo_prompt_fn, info_fn show_error_fn, info_fn show_info_fn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/plugin/percona-pam-for-mysql/src/test_auth_pam_client.c b/plugin/percona-pam-for-mysql/src/test_auth_pam_client.c new file mode 100644 index 000000000000..875068e30eb7 --- /dev/null +++ b/plugin/percona-pam-for-mysql/src/test_auth_pam_client.c @@ -0,0 +1,69 @@ +/* + (C) 2011 Percona 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +/** + @file + + PAM authentication for MySQL, the test version of the client-side plugin. +*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#define STDCALL + +#include "mysql/client_plugin.h" +#include "mysql/plugin_auth.h" + +#include "lib_auth_pam_client.h" + +const char *echo_off_reply_1 = "aaaaaaa"; +const char *echo_off_reply_2 = "AAAAAAA"; + +const char *echo_on_reply_1 = "bbbbbbbbbb"; +const char *echo_on_reply_2 = "BBBBBBBBBB"; + +/* Returns alternating echo_off_reply_1 and echo_off_reply_2 */ +static char *test_prompt_echo_off(const char *prompt __attribute__((unused))) { + static unsigned call_no = 0; + return strdup((call_no++ % 2) == 0 ? echo_off_reply_1 : echo_off_reply_2); +} + +/* Returns alternating echo_on_reply_1 and echo_on_reply_2 */ +static char *test_prompt_echo_on(const char *prompt __attribute__((unused))) { + static unsigned call_no = 0; + return strdup((call_no++ % 2) == 0 ? echo_on_reply_1 : echo_on_reply_2); +} + +/* Pretend we have shown the message to the user */ +static void test_show_anything(const char *message __attribute__((unused))) {} + +static int test_pam_auth_client(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql) { + return authenticate_user_with_pam_client_common( + vio, mysql, &test_prompt_echo_off, &test_prompt_echo_on, + &test_show_anything, &test_show_anything); +} + +mysql_declare_client_plugin(AUTHENTICATION) "auth_pam_test", "Percona, Inc.", + "Test version of the client PAM authentication plugin. " + "DO NOT USE IN PRODUCTION.", + {0, 1, 0}, "GPL", NULL, NULL, /* init */ + NULL, /* deinit */ + NULL, /* options */ + &test_pam_auth_client mysql_end_client_plugin; diff --git a/plugin/percona-pam-for-mysql/test/dbqp/percona_tests/percona_pam/pam_mapping_test.py b/plugin/percona-pam-for-mysql/test/dbqp/percona_tests/percona_pam/pam_mapping_test.py new file mode 100755 index 000000000000..03f84afe5209 --- /dev/null +++ b/plugin/percona-pam-for-mysql/test/dbqp/percona_tests/percona_pam/pam_mapping_test.py @@ -0,0 +1,118 @@ +#! /usr/bin/env python +# -*- mode: python; indent-tabs-mode: nil; -*- +# vim:expandtab:shiftwidth=2:tabstop=2:smarttab: +# +# Copyright (C) 2013 Percona LLC and/or its affiliates +# +# +# 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; either version 2 of the License, or +# (at your option) any later version. +# +# 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-1301 USA + +import os +import time +import shutil +import signal +import subprocess +import re +import grp + +from lib.util.mysqlBaseTestCase import mysqlBaseTestCase +from lib.util.mysql_methods import execute_cmd + + +server_requirements = [[]] +servers = [] +server_manager = None +test_executor = None +pamcfg = '/etc/pam.d/mysqld' + +def group_exists(groupname): + try: + grp.getgrnam(groupname)[0] + except KeyError: + return False + return True + +class basicTest(mysqlBaseTestCase): + + def test_pam_basic(self): + percent_string = '%' + opt_matrix_req = ['pam_plugin_dir'] + self.servers = servers + logging = test_executor.logging + master_server = servers[0] + output_path = os.path.join(master_server.vardir, 'pam.out') + test_executor.matrix_manager.matrix_check_req(opt_matrix_req) + # This is a master + if test_executor.matrix_manager.option_matrix['pam_user']: + pam_user = test_executor.matrix_manager.option_matrix['pam_user'] + else: + pam_user = 'pamuser' + + groups = ['grp%d' % (n) for n in xrange(3)] + users = ['user1%d' % (n) for n in xrange(3)] + + for grp in groups: + if not group_exists(grp): + subprocess.call(["groupadd", grp]) + + # Create UNIX system account + if not test_executor.system_manager.user_exists(pam_user): + subprocess.call(["useradd", pam_user, "-g", groups[0], "-G", ",".join(groups[1:]) ]) + else: + subprocess.call(["usermod", "-g", groups[0], "-G", ",".join(groups[1:]), pam_user ]) + + # Create PAM config + if (os.path.isfile(pamcfg)): + os.remove(pamcfg) + + pamcfg_fh = open("/etc/pam.d/mysqld", "wb") + pamcfg_fh.write("auth\trequired\tpam_permit.so\n") + pamcfg_fh.write("account\trequired\tpam_permit.so\n") + pamcfg_fh.close(); + + master_server.stop() + + # setup plugin, users, privileges + groups.reverse() + groups = [ "grp21", "grp22" ] + groups + users = [ "usr21", "usr22" ] + users + queries = [ "INSTALL PLUGIN auth_pam SONAME 'auth_pam.so';" ] + \ + [ "CREATE USER '%s'@'localhost';" % (user) for user in users ] + \ + [ "CREATE USER ''@'' IDENTIFIED WITH auth_pam AS 'mysqld, %s';" \ + % ( ",".join([ user + "=" + group for user, group in zip(groups, users) ] ) ) ] + \ + [ "GRANT PROXY ON '%s'@'localhost' TO ''@'';" % (user) for user in users ] + \ + [ "SELECT user, host, authentication_string FROM mysql.user;", \ + "FLUSH PRIVILEGES;", "SHOW VARIABLES LIKE 'plugin%'" ] + + master_server.server_options.append('--plugin-dir=%s' %(test_executor.matrix_manager.option_matrix['pam_plugin_dir'])) + + master_server.start() + self.assertEqual( master_server.status, 1, msg = 'Server failed to restart') + + cmd = "%s --protocol=tcp --port=%d -uroot -e \"%s\"" %(master_server.mysql_client + , master_server.master_port + , "\n".join(queries) ) + retcode, output = execute_cmd(cmd, output_path, None, True) + + query = "SELECT CONCAT(USER(), CURRENT_USER(), @@PROXY_USER) as res;" + expected_result = "res%s@localhostuser10@localhost''@''" % (pam_user) + cmd = "%s --plugin-dir=/usr/lib/mysql/plugin/ --protocol=tcp --port=%d --user=%s --password=\'\' -e \"%s\" test" %(master_server.mysql_client + , master_server.master_port + , pam_user + , query ) + retcode, output = execute_cmd(cmd, output_path, None, True) + output = re.sub(r'\s+', '', output) + self.assertEqual(retcode, 0, msg = output) + self.assertEqual(output, expected_result, msg = "%s || %s" %(output, expected_result)) diff --git a/plugin/percona-udf/CMakeLists.txt b/plugin/percona-udf/CMakeLists.txt new file mode 100644 index 000000000000..cc98e8f1d380 --- /dev/null +++ b/plugin/percona-udf/CMakeLists.txt @@ -0,0 +1,3 @@ +MYSQL_ADD_PLUGIN(libfnv1a_udf fnv1a_udf.cc MODULE_ONLY) +MYSQL_ADD_PLUGIN(libfnv_udf fnv_udf.cc MODULE_ONLY) +MYSQL_ADD_PLUGIN(libmurmur_udf murmur_udf.cc MODULE_ONLY) diff --git a/plugin/percona-udf/fnv1a_udf.cc b/plugin/percona-udf/fnv1a_udf.cc new file mode 100644 index 000000000000..386ef579bfa4 --- /dev/null +++ b/plugin/percona-udf/fnv1a_udf.cc @@ -0,0 +1,196 @@ +/* This file implements a 64-bit FNV-1a hash UDF (user-defined function) for + * MySQL. The function accepts any number of arguments and returns a 64-bit + * unsigned integer. MySQL actually interprets the result as a signed integer, + * but you should ignore that. I chose not to return the number as a + * hexadecimal string because using an integer makes it possible to use it + * efficiently with BIT_XOR(). + * + * The function never returns NULL, even when you give it NULL arguments. + * + * To compile and install, execute the following commands. The function name + * fnv1a_64 in the mysql command is case-sensitive! (Of course, when you + * actually call the function, it is case-insensitive just like any other SQL + * function). + * + * gcc -fPIC -Wall -I/usr/include/mysql -shared -o fnv1a_udf.so fnv1a_udf.cc + * cp fnv1a_udf.so /lib * OR: * cp fnv1a_udf.so /usr/lib + * mysql mysql -e "CREATE FUNCTION fnv1a_64 RETURNS INTEGER SONAME + * 'fnv1a_udf.so'" + * + * For MySQL version 4.1 or older you must add the following flag to the gcc + * command above: -DNO_DECIMAL_RESULT + * Otherwise you will get an error like: + * fnv1a_udf.cc:167: `DECIMAL_RESULT' undeclared (first use this function) + * (See http://code.google.com/p/maatkit/issues/detail?id=89) + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'fnv1a_udf.so' (errno: 22 fnv1a_udf.so: cannot open shared object file: No + * such file or directory)" then you may need to copy the .so file to another + * location in your system. Look at your environment's $LD_LIBRARY_PATH + * variable for clues. If none is set, you may need to set this variable to + * something like /lib. + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'libfnv1a_udf.so' (errno: 22 /lib/libfnv1a_udf.so: undefined symbol: + * __gxx_personality_v0)" then you may need to use g++ instead of gcc. + * + * Try both /lib and /usr/lib before changing LD_LIBRARY_PATH. + * + * On Mac OSX, use -dynamiclib instead of -shared and add -lstdc++ to the + * compile flags. + * + * Once installed successfully, you should be able to call the function. Here's + * a faster alternative to MD5 hashing, with the added ability to hash multiple + * arguments in a single call: + * + * mysql> SELECT FNV1A_64('hello', 'world'); + * + * Here's a way to reduce an entire table to a single order-independent hash: + * + * mysql> SELECT BIT_XOR(CAST(FNV1A_64(col1, col2, col3) AS UNSIGNED)) FROM tbl; + * + */ + +/* The following header is from hash_64a.c: + * + * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code + * + * @(#) $Revision: 5.1 $ + * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $ + * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $ + * + *** + * + * Fowler/Noll/Vo hash + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an EMail message + * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + * for more details as well as other forms of the FNV hash. + * + *** + * + * To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the + * Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str(). + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +#include +#include +#include "my_sys.h" +#include "mysql.h" + +/* On the first call, use this as the initial_value. */ +#define FNV1A_64_INIT 0xcbf29ce484222325ULL +/* Default for NULLs, just so the result is never NULL. */ +#define HASH_NULL_DEFAULT 0x0a0b0c0d +/* Magic number for the hashing. */ +#define FNV_64_PRIME 0x100000001b3ULL + +/* Prototypes */ + +extern "C" { +ulonglong hash64a(const void *buf, size_t len, ulonglong hval); +bool fnv1a_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +ulonglong fnv1a_64(UDF_INIT *initid, UDF_ARGS *args, char *is_null, + char *error); +} + +/* Implementations */ + +ulonglong hash64a(const void *buf, size_t len, ulonglong hval) { + const unsigned char *bp = (const unsigned char *)buf; + const unsigned char *be = bp + len; + + /* FNV-1a hash each octet of the buffer */ + for (; bp != be; ++bp) { + /* xor the bottom with the current octet */ + hval ^= (ulonglong)*bp; + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + hval *= FNV_64_PRIME; + } + + return hval; +} + +bool fnv1a_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == 0) { + strcpy(message, "FNV1A_64 requires at least one argument"); + return true; + } + initid->maybe_null = 0; /* The result will never be NULL */ + return false; +} + +ulonglong fnv1a_64(UDF_INIT *initid [[maybe_unused]], UDF_ARGS *args, + char *is_null [[maybe_unused]], + char *error [[maybe_unused]]) { + uint null_default = HASH_NULL_DEFAULT; + ulonglong result = FNV1A_64_INIT; + uint i; + + for (i = 0; i < args->arg_count; ++i) { + if (args->args[i] != NULL) { + switch (args->arg_type[i]) { + case STRING_RESULT: +#ifdef NO_DECIMAL_RESULT +#else + case DECIMAL_RESULT: +#endif + result = + hash64a((const void *)args->args[i], args->lengths[i], result); + break; + case REAL_RESULT: { + double real_val; + real_val = *((double *)args->args[i]); + result = hash64a((const void *)&real_val, sizeof(double), result); + } break; + case INT_RESULT: { + long long int_val; + int_val = *((long long *)args->args[i]); + result = hash64a((const void *)&int_val, sizeof(ulonglong), result); + } break; + default: + break; + } + } else { + result = + hash64a((const void *)&null_default, sizeof(null_default), result); + } + } + return result; +} diff --git a/plugin/percona-udf/fnv_udf.cc b/plugin/percona-udf/fnv_udf.cc new file mode 100644 index 000000000000..2d367432d8f9 --- /dev/null +++ b/plugin/percona-udf/fnv_udf.cc @@ -0,0 +1,199 @@ +/* This file implements a 64-bit FNV-1 hash UDF (user-defined function) for + * MySQL. The function accepts any number of arguments and returns a 64-bit + * unsigned integer. MySQL actually interprets the result as a signed integer, + * but you should ignore that. I chose not to return the number as a + * hexadecimal string because using an integer makes it possible to use it + * efficiently with BIT_XOR(). + * + * The function never returns NULL, even when you give it NULL arguments. + * + * To compile and install, execute the following commands. The function name + * fnv_64 in the mysql command is case-sensitive! (Of course, when you actually + * call the function, it is case-insensitive just like any other SQL function). + * + * gcc -fPIC -Wall -I/usr/include/mysql -shared -o fnv_udf.so fnv_udf.cc + * cp fnv_udf.so /lib * OR: * cp fnv_udf.so /usr/lib + * mysql mysql -e "CREATE FUNCTION fnv_64 RETURNS INTEGER SONAME 'fnv_udf.so'" + * + * For MySQL version 4.1 or older you must add the following flag to the gcc + * command above: -DNO_DECIMAL_RESULT + * Otherwise you will get an error like: + * fnv_udf.cc:167: `DECIMAL_RESULT' undeclared (first use this function) + * (See http://code.google.com/p/maatkit/issues/detail?id=89) + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'fnv_udf.so' (errno: 22 fnv_udf.so: cannot open shared object file: No such + * file or directory)" then you may need to copy the .so file to another + * location in your system. Look at your environment's $LD_LIBRARY_PATH + * variable for clues. If none is set, you may need to set this variable to + * something like /lib. + * + * If you get the error "ERROR 1126 (HY000): Can't open shared library + * 'libfnv_udf.so' (errno: 22 /lib/libfnv_udf.so: undefined symbol: + * __gxx_personality_v0)" then you may need to use g++ instead of gcc. + * + * Try both /lib and /usr/lib before changing LD_LIBRARY_PATH. + * + * On Mac OSX, use -dynamiclib instead of -shared and add -lstdc++ to the + * compile flags. + * + * Once installed successfully, you should be able to call the function. Here's + * a faster alternative to MD5 hashing, with the added ability to hash multiple + * arguments in a single call: + * + * mysql> SELECT FNV_64('hello', 'world'); + * + * Here's a way to reduce an entire table to a single order-independent hash: + * + * mysql> SELECT BIT_XOR(CAST(FNV_64(col1, col2, col3) AS UNSIGNED)) FROM tbl1; + * + */ + +/* The following header is from hash_64.c: + * + * hash_64 - 64 bit Fowler/Noll/Vo-0 hash code + * + * @(#) $Revision: 1.8 $ + * @(#) $Id: hash_64.c,v 1.8 2003/10/03 20:37:04 chongo Exp $ + * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64.c,v $ + * + *** + * + * Fowler/Noll/Vo hash + * + * The basis of this hash algorithm was taken from an idea sent + * as reviewer comments to the IEEE POSIX P1003.2 committee by: + * + * Phong Vo (http://www.research.att.com/info/kpv/) + * Glenn Fowler (http://www.research.att.com/~gsf/) + * + * In a subsequent ballot round: + * + * Landon Curt Noll (http://www.isthe.com/chongo/) + * + * improved on their algorithm. Some people tried this hash + * and found that it worked rather well. In an EMail message + * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. + * + * FNV hashes are designed to be fast while maintaining a low + * collision rate. The FNV speed allows one to quickly hash lots + * of data while maintaining a reasonable collision rate. See: + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + * for more details as well as other forms of the FNV hash. + * + *** + * + * NOTE: The FNV-0 historic hash is not recommended. One should use + * the FNV-1 hash instead. + * + * To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the + * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str(). + * + * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the + * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str(). + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +#include +#include +#include +#include + +/* On the first call, use this as the initial_value. */ +#define HASH_64_INIT 0x84222325cbf29ce4ULL +/* Default for NULLs, just so the result is never NULL. */ +#define HASH_NULL_DEFAULT 0x0a0b0c0d +/* Magic number for the hashing. */ +#define FNV_64_PRIME 0x100000001b3ULL + +/* Prototypes */ + +extern "C" { +ulonglong hash64(const void *buf, size_t len, ulonglong hval); +bool fnv_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +ulonglong fnv_64(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); +} + +/* Implementations */ + +ulonglong hash64(const void *buf, size_t len, ulonglong hval) { + const unsigned char *bp = (const unsigned char *)buf; + const unsigned char *be = bp + len; + + /* FNV-1 hash each octet of the buffer */ + for (; bp != be; ++bp) { + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + hval *= FNV_64_PRIME; + /* xor the bottom with the current octet */ + hval ^= (ulonglong)*bp; + } + + return hval; +} + +bool fnv_64_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == 0) { + strcpy(message, "FNV_64 requires at least one argument"); + return true; + } + initid->maybe_null = 0; /* The result will never be NULL */ + return false; +} + +ulonglong fnv_64(UDF_INIT *initid [[maybe_unused]], UDF_ARGS *args, + char *is_null [[maybe_unused]], + char *error [[maybe_unused]]) { + uint null_default = HASH_NULL_DEFAULT; + ulonglong result = HASH_64_INIT; + uint i; + + for (i = 0; i < args->arg_count; ++i) { + if (args->args[i] != NULL) { + switch (args->arg_type[i]) { + case STRING_RESULT: +#ifdef NO_DECIMAL_RESULT +#else + case DECIMAL_RESULT: +#endif + result = + hash64((const void *)args->args[i], args->lengths[i], result); + break; + case REAL_RESULT: { + double real_val; + real_val = *((double *)args->args[i]); + result = hash64((const void *)&real_val, sizeof(double), result); + } break; + case INT_RESULT: { + long long int_val; + int_val = *((long long *)args->args[i]); + result = hash64((const void *)&int_val, sizeof(ulonglong), result); + } break; + default: + break; + } + } else { + result = + hash64((const void *)&null_default, sizeof(null_default), result); + } + } + return result; +} diff --git a/plugin/percona-udf/murmur_udf.cc b/plugin/percona-udf/murmur_udf.cc new file mode 100644 index 000000000000..6ce9dfb0d3eb --- /dev/null +++ b/plugin/percona-udf/murmur_udf.cc @@ -0,0 +1,178 @@ +/* License: This code is in the public domain. + * + * See http://murmurhash.googlepages.com/ for more about the Murmur hash. The + * Murmur hash is by Austin Appleby. + * + * This file implements a 64-bit Murmur-2 hash UDF (user-defined function) for + * MySQL. The function accepts any number of arguments and returns a 64-bit + * unsigned integer. MySQL actually interprets the result as a signed integer, + * but you should ignore that. I chose not to return the number as a + * hexadecimal string because using an integer makes it possible to use it + * efficiently with BIT_XOR(). + * + * The function never returns NULL, even when you give it NULL arguments. + * + * To compile and install, execute the following commands. The function name + * murmur_hash in the mysql command is case-sensitive! (Of course, when you + * actually call the function, it is case-insensitive just like any other SQL + * function). + * + * g++ -fPIC -Wall -I/usr/include/mysql -shared -o murmur_udf.so murmur_udf.cc + * cp murmur_udf.so /lib + * mysql mysql -e "CREATE FUNCTION murmur_hash RETURNS INTEGER SONAME + * 'murmur_udf.so'" + * + * Once installed successfully, you should be able to call the function. Here's + * a faster alternative to MD5 hashing, with the added ability to hash multiple + * arguments in a single call: + * + * mysql> SELECT MURMUR_HASH('hello', 'world'); + * + * Here's a way to reduce an entire table to a single order-independent hash: + * + * mysql> SELECT BIT_XOR(CAST(MURMUR_HASH(col1, col2, col3) AS UNSIGNED)) FROM + * tbl1; + * + * Note - This code makes a few assumptions about how your machine behaves - + * 1. We can read a 4-byte value from any address without crashing + * 2. sizeof(int) == 4 + * + * And it has a few limitations: + * 1. It will not work incrementally. + * 2. It will not produce the same results on little-endian and big-endian + * machines. + */ + +#include +#include +#include "my_sys.h" +#include "mysql.h" + +/* On the first call, use this as the initial_value. */ +#define HASH_64_INIT 0x84222325cbf29ce4ULL +/* Default for NULLs, just so the result is never NULL. */ +#define HASH_NULL_DEFAULT 0x0a0b0c0d + +/* Prototypes */ + +extern "C" { +ulonglong MurmurHash2(const void *key, int len, unsigned int seed); +bool murmur_hash_init(UDF_INIT *initid, UDF_ARGS *args, char *message); +ulonglong murmur_hash(UDF_INIT *initid, UDF_ARGS *args, char *is_null, + char *error); +} + +/* Implementations */ + +/* + * MurmurHash2, by Austin Appleby + * This is the 64 bit version of MurmurHash2 for 32-bit platforms. + */ + +ulonglong MurmurHash2(const void *key, int len, unsigned int seed) { + const unsigned int m = 0x5bd1e995; + const int r = 24; + + unsigned int h1 = seed ^ len; + unsigned int h2 = 0; + + const unsigned int *data = (const unsigned int *)key; + + while (len >= 8) { + unsigned int k1 = *data++; + k1 *= m; + k1 ^= k1 >> r; + k1 *= m; + h1 *= m; + h1 ^= k1; + len -= 4; + + unsigned int k2 = *data++; + k2 *= m; + k2 ^= k2 >> r; + k2 *= m; + h2 *= m; + h2 ^= k2; + len -= 4; + } + + if (len >= 4) { + unsigned int k1 = *data++; + k1 *= m; + k1 ^= k1 >> r; + k1 *= m; + h1 *= m; + h1 ^= k1; + len -= 4; + } + + switch (len) { + case 3: + h2 ^= ((const unsigned char *)data)[2] << 16; + [[fallthrough]]; + case 2: + h2 ^= ((const unsigned char *)data)[1] << 8; + [[fallthrough]]; + case 1: + h2 ^= ((const unsigned char *)data)[0]; + h2 *= m; + }; + + h1 ^= h2 >> 18; + h1 *= m; + h2 ^= h1 >> 22; + h2 *= m; + h1 ^= h2 >> 17; + h1 *= m; + + ulonglong h = h1; + + h = (h << 32) | h2; + return h; +} + +bool murmur_hash_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { + if (args->arg_count == 0) { + strcpy(message, "MURMUR_HASH requires at least one argument"); + return true; + } + initid->maybe_null = 0; /* The result will never be NULL */ + return false; +} + +ulonglong murmur_hash(UDF_INIT *initid [[maybe_unused]], UDF_ARGS *args, + char *is_null [[maybe_unused]], + char *error [[maybe_unused]]) { + uint null_default = HASH_NULL_DEFAULT; + ulonglong result = HASH_64_INIT; + uint i; + + for (i = 0; i < args->arg_count; ++i) { + if (args->args[i] != NULL) { + switch (args->arg_type[i]) { + case STRING_RESULT: + case DECIMAL_RESULT: + result = MurmurHash2((const void *)args->args[i], args->lengths[i], + result); + break; + case REAL_RESULT: { + double real_val; + real_val = *((double *)args->args[i]); + result = MurmurHash2((const void *)&real_val, sizeof(double), result); + } break; + case INT_RESULT: { + long long int_val; + int_val = *((long long *)args->args[i]); + result = + MurmurHash2((const void *)&int_val, sizeof(ulonglong), result); + } break; + default: + break; + } + } else { + result = MurmurHash2((const void *)&null_default, sizeof(null_default), + result); + } + } + return result; +} diff --git a/plugin/semisync/semisync_replica.cc b/plugin/semisync/semisync_replica.cc index 89c00381a8bc..709931305bc1 100644 --- a/plugin/semisync/semisync_replica.cc +++ b/plugin/semisync/semisync_replica.cc @@ -110,7 +110,7 @@ int ReplSemiSyncSlave::slaveReply(MYSQL *mysql, const char *binlog_filename, function_enter(kWho); DBUG_EXECUTE_IF("rpl_semisync_before_send_ack", { - const char act[] = "now SIGNAL sending_ack WAIT_FOR continue"; + const char act[] = "now WAIT_FOR continue"; assert(opt_debug_sync_timeout > 0); assert(!debug_sync_set_action(current_thd, STRING_WITH_LEN(act))); };); diff --git a/unittest/gunit/keyring/CMakeLists.txt b/unittest/gunit/keyring/CMakeLists.txt index 7fe6113a4232..f17950878b84 100644 --- a/unittest/gunit/keyring/CMakeLists.txt +++ b/unittest/gunit/keyring/CMakeLists.txt @@ -22,7 +22,6 @@ SET(TESTS keyring-api - keys_container buffered_file_io converter file_io diff --git a/unittest/gunit/keyring/keyring-api-t.cc b/unittest/gunit/keyring/keyring-api-t.cc index 5eb67cae647d..a8ddce5c0e16 100644 --- a/unittest/gunit/keyring/keyring-api-t.cc +++ b/unittest/gunit/keyring/keyring-api-t.cc @@ -245,23 +245,6 @@ TEST_F(Keyring_api_test, StoreTwiceTheSameDifferentTypes) { 1); } -TEST_F(Keyring_api_test, KeyGenerate) { - EXPECT_EQ(mysql_key_generate("Robert_key", "AES", "Robert", 128), 0); - char *key_type; - size_t key_len; - void *key; - EXPECT_EQ(mysql_key_fetch("Robert_key", &key_type, "Robert", &key, &key_len), - 0); - EXPECT_STREQ("AES", key_type); - EXPECT_EQ(key_len, (size_t)128); - // Try accessing the last byte of key - char ch = ((char *)key)[key_len - 1]; - // Just to get rid of unused variable compiler error - (void)ch; - my_free(key); - my_free(key_type); -} - TEST_F(Keyring_api_test, InitWithDifferentKeyringFile) { EXPECT_EQ( mysql_key_store("Robert_key", "AES", "Robert", sample_key_data.c_str(), @@ -329,6 +312,9 @@ TEST_F(Keyring_api_test, InitWithDifferentKeyringFile) { my_free(key); key = nullptr; remove("./new_keyring"); + // backup will stay as adding percona_binlog key will be unsuccessful - we + // have already added it in keyring + remove("./new_keyring.backup"); } TEST_F(Keyring_api_test, NullUser) { diff --git a/unittest/gunit/keyring/keys_container-t.cc b/unittest/gunit/keyring/keys_container-t.cc deleted file mode 100644 index 3e00460cdd67..000000000000 --- a/unittest/gunit/keyring/keys_container-t.cc +++ /dev/null @@ -1,1482 +0,0 @@ -/* Copyright (c) 2016, 2022, Oracle and/or its affiliates. - - 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 -#include -#include -#include - -#include "my_inttypes.h" -#include "plugin/keyring/buffered_file_io.h" -#include "plugin/keyring/common/i_serialized_object.h" -#include "plugin/keyring/common/keys_container.h" -#include "unittest/gunit/keyring/buffered_file_io_10.h" -#include "unittest/gunit/keyring/mock_logger.h" - -namespace keyring__keys_container_unittest { -using namespace keyring; -using ::testing::_; -using ::testing::DoAll; -using ::testing::InSequence; -using ::testing::Return; -using ::testing::SetArgPointee; -using ::testing::StrEq; - -bool check_if_file_exists_and_TAG_is_correct(const char *file_name) { - char tag[4]; - std::fstream file; - - file.open(file_name, std::fstream::in | std::fstream::binary); - if (!file.is_open()) return false; - file.seekg(0, file.end); - if (file.tellg() < (3 + 32)) // tag + sha256 - return false; // File do not contains tag - file.seekg(-(3 + 32), file.end); - if (file.good() == false) return false; - file.read(tag, 3); - size_t chars_read = file.gcount(); - if (file.good() == false || chars_read < 3) return false; - tag[3] = '\0'; - file.close(); - return strcmp(tag, "EOF") == 0; -} - -class Keys_container_test : public ::testing::Test { - public: - Keys_container_test() : file_name("./keyring") {} - - protected: - void SetUp() override { - sample_key_data = "Robi"; - sample_key = new Key("Roberts_key", "AES", "Robert", - sample_key_data.c_str(), sample_key_data.length() + 1); - - remove(file_name.c_str()); - remove("./keyring.backup"); - - logger = new Mock_logger(); - keys_container = new Keys_container(logger); - } - void TearDown() override { - remove(file_name.c_str()); - delete keys_container; - delete logger; - } - void create_keyring_file(const char *file_name, const char *keyring_buffer); - void generate_keyring_file_with_correct_ver_1_0_structure( - const char *file_name); - void generate_keyring_file_with_correct_ver_2_0_structure( - const char *file_name); - void generate_keyring_file_with_incorrect_file_version(const char *file_name); - void generate_keyring_file_with_incorrect_TAG(const char *file_name); - - protected: - Keys_container *keys_container; - ILogger *logger; - Key *sample_key; - std::string sample_key_data; - std::string file_name; -}; - -void Keys_container_test::create_keyring_file(const char *file_name, - const char *keyring_buffer) { - std::fstream file; - file.open(file_name, - std::fstream::out | std::fstream::binary | std::fstream::trunc); - ASSERT_TRUE(file.is_open()); - file.write(keyring_buffer, strlen(keyring_buffer)); - file.close(); -} - -void Keys_container_test::generate_keyring_file_with_correct_ver_1_0_structure( - const char *file_name) { - static const char *keyring_buffer = "Keyring file version:1.0EOF"; - create_keyring_file(file_name, keyring_buffer); -} - -void Keys_container_test::generate_keyring_file_with_correct_ver_2_0_structure( - const char *file_name) { - static const char *keyring_buffer = - "Keyring file version:2.0EOF" - "01234567890123456789012345678901"; - create_keyring_file(file_name, keyring_buffer); -} - -void Keys_container_test::generate_keyring_file_with_incorrect_file_version( - const char *file_name) { - static const char *keyring_buffer = "Keyring file version:3.0EOF"; - create_keyring_file(file_name, keyring_buffer); -} - -void Keys_container_test::generate_keyring_file_with_incorrect_TAG( - const char *file_name) { - static const char *keyring_buffer = "Keyring file version:2.0EF"; - create_keyring_file(file_name, keyring_buffer); -} - -TEST_F(Keys_container_test, InitWithFileWithCorrect_1_0_Struct) { - const char *keyring_correct_struct = "./keyring_correct_1_0_struct"; - remove(keyring_correct_struct); - std::vector allowedFileVersionsToInit; - // this keyring will work with keyring files in the following versions: - allowedFileVersionsToInit.push_back(keyring::keyring_file_version_2_0); - allowedFileVersionsToInit.push_back(keyring::keyring_file_version_1_0); - generate_keyring_file_with_correct_ver_1_0_structure(keyring_correct_struct); - IKeyring_io *keyring_io = - new Buffered_file_io(logger, &allowedFileVersionsToInit); - EXPECT_EQ(keys_container->init(keyring_io, keyring_correct_struct), 0); - remove(keyring_correct_struct); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, InitWithFileWithCorrect_2_0_Struct) { - const char *keyring_correct_struct = "./keyring_correct_2_0_struct"; - remove(keyring_correct_struct); - generate_keyring_file_with_correct_ver_2_0_structure(keyring_correct_struct); - Buffered_file_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, keyring_correct_struct), 0); - remove(keyring_correct_struct); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, InitWithFileWithIncorrectKeyringVersion) { - const char *keyring_incorrect_version = "./keyring_incorrect_version"; - remove(keyring_incorrect_version); - generate_keyring_file_with_incorrect_file_version(keyring_incorrect_version); - Buffered_file_io *keyring_io = new Buffered_file_io(logger); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Error while loading keyring content." - " The keyring might be malformed"))); - EXPECT_EQ(keys_container->init(keyring_io, keyring_incorrect_version), 1); - remove(keyring_incorrect_version); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, InitWithFileWithIncorrectTAG) { - const char *keyring_incorrect_tag = "./keyring_incorrect_tag"; - remove(keyring_incorrect_tag); - generate_keyring_file_with_incorrect_TAG(keyring_incorrect_tag); - Buffered_file_io *keyring_io = new Buffered_file_io(logger); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Error while loading keyring content. The " - "keyring might be malformed"))); - EXPECT_EQ(keys_container->init(keyring_io, keyring_incorrect_tag), 1); - remove(keyring_incorrect_tag); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, StoreFetchRemove) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - Key key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - - ASSERT_TRUE(fetched_key != nullptr); - std::string expected_key_signature = "Roberts_keyRobert"; - EXPECT_STREQ(fetched_key->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key->get_key_signature()->length(), - expected_key_signature.length()); - uchar *key_data_fetched = fetched_key->get_key_data(); - size_t key_data_fetched_size = fetched_key->get_key_data_size(); - EXPECT_STREQ(sample_key_data.c_str(), - reinterpret_cast(key_data_fetched)); - EXPECT_STREQ("AES", fetched_key->get_key_type_as_string()->c_str()); - ASSERT_TRUE(sample_key_data.length() + 1 == key_data_fetched_size); - - keys_container->remove_key(&key_id); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - my_free(fetched_key->release_key_data()); -} - -TEST_F(Keys_container_test, FetchNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - Key key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - ASSERT_TRUE(fetched_key == nullptr); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, RemoveNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - Key key_id("Roberts_key", "AES", "Robert", nullptr, 0); - ASSERT_TRUE(keys_container->remove_key(&key_id) == true); - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_test, StoreFetchNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - Key key_id("NotRoberts_key", nullptr, "NotRobert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - ASSERT_TRUE(fetched_key == nullptr); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); -} - -TEST_F(Keys_container_test, StoreRemoveNotExisting) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - Key key_id("NotRoberts_key", "AES", "NotRobert", nullptr, 0); - // Failed to remove key - ASSERT_TRUE(keys_container->remove_key(&key_id) == true); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); -} - -TEST_F(Keys_container_test, StoreStoreStoreFetchRemove) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - std::string key_data1("Robi1"); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - - EXPECT_EQ(keys_container->store_key(key1), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - std::string key_data2("Robi2"); - Key *key2 = new Key("Roberts_key2", "AES", "Robert", key_data2.c_str(), - key_data2.length() + 1); - - EXPECT_EQ(keys_container->store_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 3); - - std::string key_data3("Robi3"); - Key *key3 = new Key("Roberts_key3", "AES", "Robert", key_data3.c_str(), - key_data3.length() + 1); - - EXPECT_EQ(keys_container->store_key(key3), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 4); - - Key key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key2_id); - - ASSERT_TRUE(fetched_key != nullptr); - std::string expected_key_signature = "Roberts_key2Robert"; - EXPECT_STREQ(fetched_key->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key->get_key_signature()->length(), - expected_key_signature.length()); - uchar *key_data_fetched = fetched_key->get_key_data(); - size_t key_data_fetched_size = fetched_key->get_key_data_size(); - EXPECT_STREQ(key_data2.c_str(), - reinterpret_cast(key_data_fetched)); - ASSERT_TRUE(key_data2.length() + 1 == key_data_fetched_size); - - Key key3_id("Roberts_key3", "AES", "Robert", nullptr, 0); - keys_container->remove_key(&key3_id); - ASSERT_TRUE(keys_container->get_number_of_keys() == 3); - - my_free(fetched_key->release_key_data()); -} - -TEST_F(Keys_container_test, StoreTwiceTheSame) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - EXPECT_EQ(keys_container->store_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); -} - -class Buffered_file_io_20 : public Buffered_file_io { - public: - Buffered_file_io_20(ILogger *logger) : Buffered_file_io(logger) {} - void set_memory_needed_for_buffer(size_t memory_needed) { - memory_needed_for_buffer = memory_needed; - } -}; - -TEST_F(Keys_container_test, StoreKeyInVer10StoreKeyInVer20FetchKeyInVer20) { - size_t memory_needed_for_buffer; - { - Buffered_file_io_10 keyring_io_10(logger); - EXPECT_EQ(keys_container->init(&keyring_io_10, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - memory_needed_for_buffer = keyring_io_10.get_memory_needed_for_buffer(); - } - Buffered_file_io_20 *keyring_io_20 = new Buffered_file_io_20(logger); - EXPECT_EQ(keyring_io_20->init(&file_name), 0); - keyring_io_20->set_memory_needed_for_buffer(memory_needed_for_buffer); - keys_container->set_keyring_io(keyring_io_20); - - std::string key_data1("Robi1"); - Key key_1_id("Roberts_key1", nullptr, "Robert", nullptr, 0); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - EXPECT_EQ(keys_container->store_key(key1), 0); - - Key key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetched_key = keys_container->fetch_key(&key_id); - ASSERT_TRUE(fetched_key != nullptr); - std::string expected_key_signature = "Roberts_keyRobert"; - EXPECT_STREQ(fetched_key->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key->get_key_signature()->length(), - expected_key_signature.length()); - uchar *key_data_fetched = fetched_key->get_key_data(); - size_t key_data_fetched_size = fetched_key->get_key_data_size(); - EXPECT_STREQ(sample_key_data.c_str(), - reinterpret_cast(key_data_fetched)); - ASSERT_TRUE(sample_key_data.length() + 1 == key_data_fetched_size); - - keys_container->remove_key(&key_id); - - IKey *fetched_key_1 = keys_container->fetch_key(&key_1_id); - ASSERT_TRUE(fetched_key_1 != nullptr); - expected_key_signature = "Roberts_key1Robert"; - EXPECT_STREQ(fetched_key_1->get_key_signature()->c_str(), - expected_key_signature.c_str()); - EXPECT_EQ(fetched_key_1->get_key_signature()->length(), - expected_key_signature.length()); - key_data_fetched = fetched_key_1->get_key_data(); - key_data_fetched_size = fetched_key_1->get_key_data_size(); - EXPECT_STREQ(key_data1.c_str(), - reinterpret_cast(key_data_fetched)); - ASSERT_TRUE(key_data1.length() + 1 == key_data_fetched_size); - - keys_container->remove_key(&key_1_id); - - my_free(fetched_key->release_key_data()); - my_free(fetched_key_1->release_key_data()); -} - -TEST_F(Keys_container_test, - CheckIfKeyIsNotDumpedIntoKeyringFileIfKeyringFileDoesnotExist) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - remove("./keyring"); - std::string key_data1("Robi1"); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - EXPECT_CALL( - *((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - // it should not be possible to store_key if the keyring file does not exist - EXPECT_EQ(keys_container->store_key(key1), 1); - delete key1; - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), 0); -} - -TEST_F(Keys_container_test, - CheckIfKeyIsNotDumpedIntoKeyringFileIfKeyringFileHasInvalidDigest) { - IKeyring_io *keyring_io = new Buffered_file_io(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - - std::fstream keyring_file("./keyring"); - ASSERT_TRUE(keyring_file.is_open()); - keyring_file.seekp(-3, std::ios_base::end); - keyring_file.write("a", 1); // changed digest - keyring_file.close(); - EXPECT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring")); - - std::string key_data1("Robi1"); - Key *key1 = new Key("Roberts_key1", "AES", "Robert", key_data1.c_str(), - key_data1.length() + 1); - EXPECT_CALL( - *((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - - // it should not be possible to store_key if the keyring file was changed - EXPECT_EQ(keys_container->store_key(key1), 1); - delete key1; - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); -} - -class Buffered_file_io_dont_remove_backup : public Buffered_file_io { - public: - Buffered_file_io_dont_remove_backup(ILogger *logger) - : Buffered_file_io(logger) {} - - bool remove_backup(myf) { return false; } -}; - -class Keys_container_test_dont_close : public ::testing::Test { - public: - Keys_container_test_dont_close() : file_name("./keyring") {} - - protected: - void SetUp() override { - sample_key_data = "Robi"; - sample_key = new Key("Roberts_key", "AES", "Robert", - sample_key_data.c_str(), sample_key_data.length() + 1); - std::string sample_key_data2 = "xobi2"; - sample_key2 = - new Key("Roberts_key2", "AES", "Robert", sample_key_data2.c_str(), - sample_key_data2.length() + 1); - - // Remove Keyring files just to be save - remove(file_name.c_str()); - remove("./keyring.backup"); - remove("./keyring.backup.backup"); - } - void TearDown() override { remove(file_name.c_str()); } - void generate_malformed_keyring_file_without_tag(const char *file_name); - - protected: - Key *sample_key; - Key *sample_key2; - std::string sample_key_data; - std::string file_name; -}; - -void Keys_container_test_dont_close:: - generate_malformed_keyring_file_without_tag(const char *file_name) { - static const char *malformed_keyring_buffer = - "Key1AESRobertKEYDATA" - "Key2AESZibiDATAKey3DATA...crashing"; - - std::fstream file; - file.open(file_name, - std::fstream::out | std::fstream::binary | std::fstream::trunc); - ASSERT_TRUE(file.is_open()); - file.write(malformed_keyring_buffer, strlen(malformed_keyring_buffer)); - file.close(); -} - -TEST_F(Keys_container_test_dont_close, - CheckIfCorrectBackupFileIsCreatedAfterStoringOneKey) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - - // Check if backup file is empty - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - ASSERT_TRUE(keys_container->init(keyring_io, "./keyring.backup") == 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - remove("./keyring.backup"); - remove("./keyring.backup.backup"); // leftover from initializing keyring with - // backup file - remove(file_name.c_str()); - delete keys_container; - delete logger; - delete sample_key2; // unused in this test -} - -TEST_F(Keys_container_test_dont_close, - CheckIfCorrectBackupFileIsCreatedAfterStoringTwoKeys) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - delete keys_container; - delete logger; - - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - delete keys_container; - delete logger; - // Check that backup file contains sample_key only - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io_2, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey != nullptr); - - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "Robi", - fetchedKey->get_key_data_size()) == 0); - - remove("./keyring.backup"); - remove("./keyring.backup.backup"); // leftover from initializing keyring with - // backup file - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - CheckIfCorrectBackupFileIsCreatedBeforeRemovingKey) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - ASSERT_TRUE(keys_container->init(keyring_io_dont_remove_backup, file_name) == - 0); - Key sample_key_id("Roberts_key", "AES", "Robert", nullptr, 0); - EXPECT_EQ(keys_container->remove_key(&sample_key_id), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - delete keys_container; - delete logger; - // Check that backup file contains sample_key and sample_key2 - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io_2, "./keyring.backup"), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - Key sample_key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key2_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_key2Robert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "xobi2", - fetchedKey->get_key_data_size()) == 0); - - remove("./keyring.backup"); - remove("./keyring.backup.backup"); // leftover from initializing keyring with - // backup file - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - CheckIfBackupFileIsNotCreatedForFetching) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - // check if the backup file was not created - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - remove("./keyring.backup"); - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - KeyringfileIsMalformedCheckIfBackupIsLoaded) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - // Now we have correct backup file - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - logger = new Mock_logger(); - IKeyring_io *keyring_io_dont_remove_backup = - new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - - // this key will not be in backup file thus we do not care about it - Key *sample_key3 = - new Key("Roberts_key3", "ZZZZ", "MaybeRobert", "DATA", strlen("DATA")); - - EXPECT_EQ(keys_container->init(keyring_io_dont_remove_backup, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key3), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 3); - // Now we have correct backup file - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - remove("./keyring"); - generate_malformed_keyring_file_without_tag("./keyring"); - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - - ASSERT_TRUE(keys_container->init(keyring_io_2, file_name) == 0); - // Check that keyring from backup was loaded as the keyring file is corrupted - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - Key sample_key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key2_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_key2Robert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "xobi2", - fetchedKey->get_key_data_size()) == 0); - IKey *fetchedKey2 = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey2 != nullptr); - ASSERT_TRUE(*fetchedKey2->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey2->get_key_data(), "Robi", - fetchedKey2->get_key_data_size()) == 0); - - // check if the backup file was removed - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - remove("./keyring.backup"); - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); - my_free(fetchedKey2->release_key_data()); -} - -TEST_F(Keys_container_test_dont_close, - BackupfileIsMalformedCheckItIsIgnoredAndDeleted) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io(logger); - Keys_container *keys_container = new Keys_container(logger); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - // successfully stored the key - backup file does not exist - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - delete keys_container; - delete logger; - generate_malformed_keyring_file_without_tag("./keyring.backup"); - logger = new Mock_logger(); - IKeyring_io *keyring_io_2 = new Buffered_file_io(logger); - keys_container = new Keys_container(logger); - - // Check that backup file was ignored (as backup file is malformed) - EXPECT_CALL(*((Mock_logger *)logger), - log(ERROR_LEVEL, StrEq("Incorrect Keyring file"))); - EXPECT_CALL(*((Mock_logger *)logger), - log(WARNING_LEVEL, - StrEq("Found malformed keyring backup file - removing it"))); - EXPECT_EQ(keys_container->init(keyring_io_2, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - Key sample_key2_id("Roberts_key2", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key2_id); - ASSERT_TRUE(fetchedKey != nullptr); - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_key2Robert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "xobi2", - fetchedKey->get_key_data_size()) == 0); - IKey *fetchedKey2 = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey2 != nullptr); - ASSERT_TRUE(*fetchedKey2->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey2->get_key_data(), "Robi", - fetchedKey2->get_key_data_size()) == 0); - - // check if the backup file was removed - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), false); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); - my_free(fetchedKey2->release_key_data()); -} - -TEST_F( - Keys_container_test_dont_close, - CheckIfBackupIsCreatedAfterEachOperationAndIsUsedWhenKeyringDoesNotExist) { - ILogger *logger = new Mock_logger(); - IKeyring_io *keyring_io = new Buffered_file_io_dont_remove_backup(logger); - Keys_container *keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == true); - - remove("./keyring.backup"); - rename("keyring", "keyring.backup"); - ASSERT_TRUE(check_if_file_exists_and_TAG_is_correct("./keyring") == false); - // Now keyring file should be recreated based on keyring.backup - delete keys_container; - keyring_io = new Buffered_file_io_dont_remove_backup(logger); - keys_container = new Keys_container(logger); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - EXPECT_EQ(keys_container->store_key(sample_key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring.backup"), true); - EXPECT_EQ(check_if_file_exists_and_TAG_is_correct("./keyring"), true); - - Key sample_key_id("Roberts_key", nullptr, "Robert", nullptr, 0); - IKey *fetchedKey = keys_container->fetch_key(&sample_key_id); - ASSERT_TRUE(fetchedKey != nullptr); - - ASSERT_TRUE(*fetchedKey->get_key_signature() == "Roberts_keyRobert"); - ASSERT_TRUE(memcmp(fetchedKey->get_key_data(), "Robi", - fetchedKey->get_key_data_size()) == 0); - - remove("./keyring.backup"); - remove(file_name.c_str()); - delete keys_container; - delete logger; - my_free(fetchedKey->release_key_data()); - // fetchedKey->release_key_data(); -} - -class Mock_keyring_io : public IKeyring_io { - public: - MOCK_METHOD1(init, bool(std::string *keyring_filename)); - MOCK_METHOD1(flush_to_backup, bool(ISerialized_object *serialized_object)); - MOCK_METHOD1(flush_to_storage, bool(ISerialized_object *serialized_object)); - MOCK_METHOD0(get_serializer, ISerializer *()); - MOCK_METHOD1(get_serialized_object, - bool(ISerialized_object **serialized_object)); - MOCK_METHOD0(has_next_serialized_object, bool()); -}; - -class Mock_serialized_object : public ISerialized_object { - public: - MOCK_METHOD1(get_next_key, bool(IKey **key)); - MOCK_METHOD0(has_next_key, bool()); - MOCK_METHOD0(get_key_operation, Key_operation()); - MOCK_METHOD1(set_key_operation, void(Key_operation)); -}; - -class Mock_serializer : public ISerializer { - public: - MOCK_METHOD3( - serialize, - ISerialized_object *( - const collation_unordered_map> &, - IKey *, Key_operation)); -}; - -class Keys_container_with_mocked_io_test : public ::testing::Test { - protected: - void SetUp() override { - std::string sample_key_data("Robi"); - sample_key = new Key("Roberts_key", "AES", "Robert", - sample_key_data.c_str(), sample_key_data.length() + 1); - - file_name = "/home/rob/write_key"; - } - void TearDown() override { - remove(file_name.c_str()); - delete keys_container; - } - - protected: - Keys_container *keys_container; - Mock_keyring_io *keyring_io; - Key *sample_key; - char *sample_key_data; - std::string file_name; - - void expect_calls_on_init(); - void expect_calls_on_store_sample_key(); -}; - -void Keys_container_with_mocked_io_test::expect_calls_on_init() { - Mock_serialized_object *mock_serialized_object = new Mock_serialized_object; - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - EXPECT_CALL(*keyring_io, get_serialized_object(_)) - .WillOnce(DoAll(SetArgPointee<0>(mock_serialized_object), Return(false))); - EXPECT_CALL(*mock_serialized_object, has_next_key()) - .WillOnce(Return(false)); // no keys to read - EXPECT_CALL(*keyring_io, has_next_serialized_object()) - .WillOnce(Return(false)); -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromIODuringInitOnGettingSerializedObject) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - EXPECT_CALL(*keyring_io, get_serialized_object(_)).WillOnce(Return(true)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Error while loading keyring content. The " - "keyring might be malformed"))); - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - delete logger; - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromIODuringInitInvalidKeyAndMockedSerializedObject) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - - IKey *invalid_key = new Key(); - std::string invalid_key_type("ZZZ"); - invalid_key->set_key_type(&invalid_key_type); - - Mock_serialized_object *mock_serialized_object = new Mock_serialized_object; - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - { - InSequence dummy; - EXPECT_CALL(*keyring_io, get_serialized_object(_)) - .WillOnce( - DoAll(SetArgPointee<0>(mock_serialized_object), Return(false))); - EXPECT_CALL(*mock_serialized_object, has_next_key()).WillOnce(Return(true)); - EXPECT_CALL(*mock_serialized_object, get_next_key(_)) - .WillOnce(DoAll(SetArgPointee<0>(sample_key), Return(false))); - EXPECT_CALL(*mock_serialized_object, has_next_key()).WillOnce(Return(true)); - EXPECT_CALL(*mock_serialized_object, get_next_key(_)) - .WillOnce(DoAll(SetArgPointee<0>(invalid_key), Return(false))); - - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Error while loading keyring content. " - "The keyring might be malformed"))); - } - - EXPECT_EQ(keys_container->init(keyring_io, file_name), 1); - ASSERT_EQ(0u, keys_container->get_number_of_keys()); - delete logger; -} - -TEST_F(Keys_container_with_mocked_io_test, ErrorFromIODuringInitInvalidKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - - IKey *invalid_key = new Key(); - std::string invalid_key_type("ZZZ"); - invalid_key->set_key_type(&invalid_key_type); - - Buffer *buffer = new Buffer(sample_key->get_key_pod_size() + - invalid_key->get_key_pod_size()); - sample_key->store_in_buffer(buffer->data, &(buffer->position)); - invalid_key->store_in_buffer(buffer->data, &(buffer->position)); - buffer->position = 0; // rewind buffer - - EXPECT_CALL(*keyring_io, init(Pointee(StrEq(file_name)))) - .WillOnce(Return(0)); // init successful - { - InSequence dummy; - EXPECT_CALL(*keyring_io, get_serialized_object(_)) - .WillOnce(DoAll(SetArgPointee<0>(buffer), Return(false))); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Error while loading keyring content. " - "The keyring might be malformed"))); - } - EXPECT_EQ(keys_container->init(keyring_io, file_name), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - delete logger; - delete invalid_key; - delete sample_key; // unused in this test -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToBackupWhenStoringKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - { - InSequence dummy; - - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL( - *logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - } - EXPECT_EQ(keys_container->store_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete sample_key; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToKeyringWhenStoringKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring"))); - } - EXPECT_EQ(keys_container->store_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete sample_key; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToBackupWhenRemovingKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - { - InSequence dummy; - - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL( - *logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring's backup"))); - } - EXPECT_EQ(keys_container->remove_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromSerializerOnFlushToKeyringWhenRemovingKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring"))); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, StoreAndRemoveKey) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - // recreate serialized objects - empty_serialized_object = new Buffer; - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_storage(empty_serialized_object)); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, - ErrorFromIOWhileRemovingKeyAfterAdding2Keys) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - std::string key_data2("Robi2"); - Key *key2 = new Key("Roberts_key2", "AES", "Robert", key_data2.c_str(), - key_data2.length() + 1); - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - Buffer *serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, key2, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key_and_key2)); - } - EXPECT_EQ(keys_container->store_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key_and_key2)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return((ISerialized_object *)nullptr)); - EXPECT_CALL(*logger, - log(ERROR_LEVEL, StrEq("Could not flush keys to keyring"))); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 1); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - delete logger; - delete mock_serializer; -} - -TEST_F(Keys_container_with_mocked_io_test, Store2KeysAndRemoveThem) { - keyring_io = new Mock_keyring_io(); - Mock_logger *logger = new Mock_logger(); - keys_container = new Keys_container(logger); - expect_calls_on_init(); - EXPECT_EQ(keys_container->init(keyring_io, file_name), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - Mock_serializer *mock_serializer = new Mock_serializer; - - ISerialized_object *empty_serialized_object = new Buffer; - Buffer *serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_backup(empty_serialized_object)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key)); - } - EXPECT_EQ(keys_container->store_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - std::string key_data2("Robi2"); - Key *key2 = new Key("Roberts_key2", "AES", "Robert", key_data2.c_str(), - key_data2.length() + 1); - - serialized_object_with_sample_key = - new Buffer(sample_key->get_key_pod_size()); - sample_key->store_in_buffer(serialized_object_with_sample_key->data, - &(serialized_object_with_sample_key->position)); - serialized_object_with_sample_key->position = 0; // rewind buffer - - Buffer *serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, key2, STORE_KEY)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_storage(serialized_object_with_sample_key_and_key2)); - } - EXPECT_EQ(keys_container->store_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 2); - - serialized_object_with_sample_key_and_key2 = - new Buffer(sample_key->get_key_pod_size() + key2->get_key_pod_size()); - sample_key->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - key2->store_in_buffer( - serialized_object_with_sample_key_and_key2->data, - &(serialized_object_with_sample_key_and_key2->position)); - serialized_object_with_sample_key_and_key2->position = 0; // rewind buffer - - Buffer *serialized_object_with_key2 = new Buffer(key2->get_key_pod_size()); - key2->store_in_buffer(serialized_object_with_key2->data, - &(serialized_object_with_key2->position)); - serialized_object_with_key2->position = 0; // rewind buffer - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_sample_key_and_key2)); - EXPECT_CALL(*keyring_io, - flush_to_backup(serialized_object_with_sample_key_and_key2)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, sample_key, REMOVE_KEY)) - .WillOnce(Return(serialized_object_with_key2)); - EXPECT_CALL(*keyring_io, flush_to_storage(serialized_object_with_key2)); - } - - EXPECT_EQ(keys_container->remove_key(sample_key), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 1); - - serialized_object_with_key2 = new Buffer(key2->get_key_pod_size()); - key2->store_in_buffer(serialized_object_with_key2->data, - &(serialized_object_with_key2->position)); - serialized_object_with_key2->position = 0; // rewind buffer - - empty_serialized_object = new Buffer; - - { - InSequence dummy; - // flush to backup - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, NULL, NONE)) - .WillOnce(Return(serialized_object_with_key2)); - EXPECT_CALL(*keyring_io, flush_to_backup(serialized_object_with_key2)); - // flush to keyring - EXPECT_CALL(*keyring_io, get_serializer()) - .WillOnce(Return(mock_serializer)); - EXPECT_CALL(*mock_serializer, serialize(_, key2, REMOVE_KEY)) - .WillOnce(Return(empty_serialized_object)); - EXPECT_CALL(*keyring_io, flush_to_storage(empty_serialized_object)); - } - - EXPECT_EQ(keys_container->remove_key(key2), 0); - ASSERT_TRUE(keys_container->get_number_of_keys() == 0); - - delete logger; - delete mock_serializer; -} - -} // namespace keyring__keys_container_unittest diff --git a/unittest/gunit/keyring/system_keys_container-t.cc b/unittest/gunit/keyring/system_keys_container-t.cc new file mode 100644 index 000000000000..1b1b247cb968 --- /dev/null +++ b/unittest/gunit/keyring/system_keys_container-t.cc @@ -0,0 +1,697 @@ +/* Copyright (c) 2018 Percona LLC 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-1301 USA */ + +#include +#include +#include +#include "mock_logger.h" +#include "mysql/service_mysql_keyring.h" +#include "plugin/keyring/common/keyring_key.h" +#include "plugin/keyring/common/system_keys_container.h" +#include "system_key.h" + +namespace keyring__system_keys_container_unittest { +using namespace keyring; +using keyring::Key; +using ::testing::StrEq; + +class System_keys_container_test : public ::testing::Test { + public: + virtual void SetUp() { + logger = new Mock_logger; + sys_keys_container = new System_keys_container(logger); + } + virtual void TearDown() { + delete sys_keys_container; + delete logger; + } + + protected: + ILogger *logger; + System_keys_container *sys_keys_container; +}; + +TEST_F(System_keys_container_test, StoreFetchPBkeyStoreFetchSystemKey) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + std::string key_data_with_version = "0:" + key_data1; + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:0"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + std::string key_data2("system_key_data_2"); + Key *key_innodb_sk = new Key("percona_innodb123:0", "AES", nullptr, + key_data2.c_str(), key_data2.length() + 1); + key_innodb_sk->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key_innodb_sk); + + Key innodb_key_id("percona_innodb123", nullptr, nullptr, nullptr, 0); + + system_key = sys_keys_container->get_latest_key_if_system_key_without_version( + &innodb_key_id); + + ASSERT_TRUE(system_key != nullptr); + + key_data_with_version = "0:" + key_data2; + Key key_2(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key_2.xor_data(); + key_data_fetched = key_2.get_key_data(); + key_data_fetched_size = key_2.get_key_data_size(); + EXPECT_STREQ(key_2.get_key_id()->c_str(), "percona_innodb123:0"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key_2.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key_innodb_sk; +} + +TEST_F(System_keys_container_test, + StoreKey1StoreKey1FetchStoreKey2StoreKey2Fetch) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "1:" + key_data2; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + std::string key_data3("1234XXXYYYZZZZ5335"); + Key *key1_sk = new Key("percona_system_key:0", "AES", nullptr, + key_data3.c_str(), key_data3.length() + 1); + key1_sk->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1_sk); + + std::string key_data4("CCCSADSDa___DFsdfk0001___"); + Key *key2_sk = new Key("percona_system_key:1", "AES", nullptr, + key_data4.c_str(), key_data4.length() + 1); + key2_sk->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2_sk); + + Key system_key_id("percona_system_key", nullptr, nullptr, nullptr, 0); + + system_key = sys_keys_container->get_latest_key_if_system_key_without_version( + &system_key_id); + + Key key_sk(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key_sk.xor_data(); + key_data_fetched = key_sk.get_key_data(); + key_data_fetched_size = key_sk.get_key_data_size(); + key_data_with_version = "1:" + key_data4; + EXPECT_STREQ(key_sk.get_key_id()->c_str(), "percona_system_key:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key_sk.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key1_sk; + delete key2_sk; +} + +TEST_F(System_keys_container_test, StoreStoreStoreFetch) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_key:0:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_key:0:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_key:0:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + key3->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key key_id("percona_key:0", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key != nullptr); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "2:" + key_data3; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_key:0:2"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key3; +} + +TEST_F(System_keys_container_test, StoreKeyWithTheSameIdTwice) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:0", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "0:" + key_data1; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:0"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; +} + +TEST_F(System_keys_container_test, + StoreKeyWithTheSameIdTwiceAndThenWithDifferentOne) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:0", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_binlog:1", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + key3->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "1:" + key_data3; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key3; +} + +TEST_F(System_keys_container_test, StoreKey1StoreKey1StoreKey2FetchKey1) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_key:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + key3->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + Key key(system_key->get_key_id()->c_str(), + system_key->get_key_type_as_string()->c_str(), + system_key->get_user_id()->c_str(), system_key->get_key_data(), + system_key->get_key_data_size()); + key.xor_data(); + uchar *key_data_fetched = key.get_key_data(); + size_t key_data_fetched_size = key.get_key_data_size(); + std::string key_data_with_version = "1:" + key_data2; + EXPECT_STREQ(key.get_key_id()->c_str(), "percona_binlog:1"); + EXPECT_STREQ(key_data_with_version.c_str(), + reinterpret_cast(key_data_fetched)); + EXPECT_STREQ("AES", key.get_key_type_as_string()->c_str()); + ASSERT_TRUE(key_data_with_version.length() + 1 == key_data_fetched_size); + + delete key1; + delete key2; + delete key3; +} + +TEST_F(System_keys_container_test, IfSystemKey) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key1), true); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_key231dqwldk__23_:1", "AES", nullptr, + key_data2.c_str(), key_data2.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key2), true); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_binlog:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key3), true); + + std::string key_data("system_key_data"); + Key *key_without_version = new Key("percona_binlog", "AES", nullptr, + key_data.c_str(), key_data.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(key_without_version), true); + + std::string not_system_key_data("not_system_key_data"); + Key *not_system_key = + new Key("unicorn_binlog", "AES", nullptr, not_system_key_data.c_str(), + not_system_key_data.length() + 1); + + EXPECT_EQ(sys_keys_container->is_system_key(not_system_key), false); + + delete key1; + delete key2; + delete key3; + delete key_without_version; + delete not_system_key; +} + +TEST_F(System_keys_container_test, GetKeyWithRotatedId) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("percona_binlog:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *percona_binlog_key = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key), + false); + EXPECT_STREQ(percona_binlog_key->get_key_id()->c_str(), "percona_binlog:1"); + + std::string key_data2("system_key_data_2"); + Key *key2 = new Key("percona_binlog:1", "AES", nullptr, key_data2.c_str(), + key_data2.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key *percona_binlog_key2 = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key2), + false); + EXPECT_STREQ(percona_binlog_key2->get_key_id()->c_str(), "percona_binlog:2"); + + std::string key_data3("system_key_data_3"); + Key *key3 = new Key("percona_binlog:2", "AES", nullptr, key_data3.c_str(), + key_data3.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key3); + + Key *percona_binlog_key3 = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key3), + false); + EXPECT_STREQ(percona_binlog_key3->get_key_id()->c_str(), "percona_binlog:3"); + + Key *key1_sk = new Key("percona_key:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key1_sk); + + Key *percona_key = new Key("percona_key", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_key), + false); + EXPECT_STREQ(percona_key->get_key_id()->c_str(), "percona_key:1"); + + delete key1; + delete key2; + delete key3; + delete key1_sk; + delete percona_binlog_key; + delete percona_binlog_key2; + delete percona_binlog_key3; + delete percona_key; +} + +TEST_F(System_keys_container_test, RotateOnNotSystemKey) { + std::string key_data1("system_key_data_1"); + Key *key1 = new Key("not_system_key:0", "AES", nullptr, key_data1.c_str(), + key_data1.length() + 1); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *key_1_id = new Key("not_system_key:0", "AES", nullptr, "sys_key", 8); + EXPECT_EQ( + sys_keys_container->rotate_key_id_if_system_key_without_version(key_1_id), + false); + EXPECT_STREQ(key_1_id->get_key_id()->c_str(), "not_system_key:0"); + + delete key1; + delete key_1_id; +} + +TEST_F(System_keys_container_test, RotateToMaxKeyId) { + std::string key_data1("system_key_data"); + std::ostringstream correct_percona_binlog_key_id_ss; + correct_percona_binlog_key_id_ss << "percona_binlog:"; + correct_percona_binlog_key_id_ss << (UINT_MAX - 1); + std::string correct_percona_binlog_key_id = + correct_percona_binlog_key_id_ss.str(); + + Key *key1 = new Key(correct_percona_binlog_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *percona_binlog_key = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key), + false); + + std::ostringstream max_percona_binlog_key_id_ss; + max_percona_binlog_key_id_ss << "percona_binlog:"; + max_percona_binlog_key_id_ss << UINT_MAX; + std::string max_percona_binlog_key_id = max_percona_binlog_key_id_ss.str(); + + EXPECT_STREQ(percona_binlog_key->get_key_id()->c_str(), + max_percona_binlog_key_id.c_str()); + + std::ostringstream correct_percona_key_id_ss; + correct_percona_key_id_ss << "percona_key:"; + correct_percona_key_id_ss << (UINT_MAX - 1); + std::string correct_percona_key_id = correct_percona_key_id_ss.str(); + + Key *key2 = new Key(correct_percona_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key *percona_key = new Key("percona_key", "AES", nullptr, "sys_key", 8); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_key), + false); + + std::ostringstream max_percona_key_id_ss; + max_percona_key_id_ss << "percona_key:"; + max_percona_key_id_ss << UINT_MAX; + std::string max_percona_key_id = max_percona_key_id_ss.str(); + + EXPECT_STREQ(percona_key->get_key_id()->c_str(), max_percona_key_id.c_str()); + + delete key1; + delete key2; + delete percona_binlog_key; + delete percona_key; +} + +TEST_F(System_keys_container_test, RotateFromMaxKeyId) { + std::string key_data1("system_key_data"); + std::ostringstream max_percona_binlog_key_id_ss; + max_percona_binlog_key_id_ss << "percona_binlog:"; + max_percona_binlog_key_id_ss << UINT_MAX; + std::string max_percona_binlog_key_id = max_percona_binlog_key_id_ss.str(); + + Key *key1 = new Key(max_percona_binlog_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key *percona_binlog_key = + new Key("percona_binlog", "AES", nullptr, "sys_key", 8); + + EXPECT_CALL( + *((Mock_logger *)logger), + log(MY_ERROR_LEVEL, StrEq("System key cannot be rotated anymore, the " + "maximum key version has been reached."))); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_binlog_key), + true); + + EXPECT_STREQ(percona_binlog_key->get_key_id()->c_str(), "percona_binlog"); + + std::ostringstream max_percona_key_id_ss; + max_percona_key_id_ss << "percona_key:"; + max_percona_key_id_ss << UINT_MAX; + std::string max_percona_key_id = max_percona_key_id_ss.str(); + + Key *key2 = new Key(max_percona_key_id.c_str(), "AES", nullptr, + key_data1.c_str(), key_data1.length() + 1); + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key *percona_key = new Key("percona_key", "AES", nullptr, "sys_key", 8); + + EXPECT_CALL( + *((Mock_logger *)logger), + log(MY_ERROR_LEVEL, StrEq("System key cannot be rotated anymore, the " + "maximum key version has been reached."))); + EXPECT_EQ(sys_keys_container->rotate_key_id_if_system_key_without_version( + percona_key), + true); + + EXPECT_STREQ(percona_key->get_key_id()->c_str(), "percona_key"); + + delete key1; + delete key2; + delete percona_binlog_key; + delete percona_key; +} + +TEST_F(System_keys_container_test, FetchFromEmptyContainer) { + Key key_id("percona_binlog", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key == nullptr); +} + +TEST_F(System_keys_container_test, StoreKeyWithOnlySemicolon) { + std::string key_data1("system_key_data_1"); + Key *key1 = + new Key(":", "AES", nullptr, key_data1.c_str(), key_data1.length() + 1); + key1->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key1); + + Key key_id(":", nullptr, nullptr, nullptr, 0); + + IKey *system_key = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key == nullptr); + + Key *key2 = + new Key(":0", "AES", nullptr, key_data1.c_str(), key_data1.length() + 1); + key2->xor_data(); + + sys_keys_container->store_or_update_if_system_key_with_version(key2); + + Key key2_id(":0", nullptr, nullptr, nullptr, 0); + + IKey *system_key2 = + sys_keys_container->get_latest_key_if_system_key_without_version(&key_id); + + ASSERT_TRUE(system_key2 == nullptr); + + delete key1; + delete key2; +} + +TEST_F(System_keys_container_test, ParseSystemKey) { + std::string system_key("12:0123456789012345"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(return_val != 0); + EXPECT_EQ(key_data_length, static_cast(16)); + EXPECT_EQ(key_version, static_cast(12)); + EXPECT_TRUE(memcmp(key_data, "0123456789012345", key_data_length) == 0); + EXPECT_EQ(return_val, key_data); + my_free(key_data); +} + +TEST_F(System_keys_container_test, ParseNotSystemKey) { + std::string key("0123456789012345"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = + parse_system_key(reinterpret_cast(key.c_str()), + key.length(), &key_version, &key_data, &key_data_length); + ASSERT_TRUE(key_data == 0); + ASSERT_TRUE(return_val == 0); + EXPECT_EQ(key_data_length, static_cast(0)); + EXPECT_EQ(key_version, static_cast(0)); +} + +TEST_F(System_keys_container_test, ParseSystemKeyWithTwoColons) { + std::string system_key("0:0123456:789012345"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(return_val != 0); + EXPECT_EQ(key_data_length, static_cast(17)); + EXPECT_EQ(key_version, static_cast(0)); + EXPECT_TRUE(memcmp(key_data, "0123456:789012345", key_data_length) == 0); + EXPECT_EQ(return_val, key_data); + my_free(key_data); +} + +TEST_F(System_keys_container_test, ParseSystemKeyWithAlphaVersion) { + std::string system_key("A:key01234567890123"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(key_data == 0); + ASSERT_TRUE(return_val == 0); + EXPECT_EQ(key_data_length, static_cast(0)); + EXPECT_EQ(key_version, static_cast(0)); +} + +TEST_F(System_keys_container_test, ParseSystemKeyWithAlphaKey) { + std::string system_key("234:key01234567890123"); + uchar *key_data = nullptr; + uint key_version = 0; + size_t key_data_length = 0; + uchar *return_val = parse_system_key( + reinterpret_cast(system_key.c_str()), system_key.length(), + &key_version, &key_data, &key_data_length); + ASSERT_TRUE(return_val != 0); + EXPECT_EQ(key_data_length, static_cast(17)); + EXPECT_EQ(key_version, static_cast(234)); + EXPECT_TRUE(memcmp(key_data, "key01234567890123", key_data_length) == 0); + EXPECT_EQ(return_val, key_data); + my_free(key_data); +} + +} // namespace keyring__system_keys_container_unittest