/* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "my_config.h" #include #include #include #include #include #include #include "my_compiler.h" #include "my_inttypes.h" #include "my_io.h" #include "my_psi_config.h" #include "mysqld_error.h" #include "plugin/keyring/buffered_file_io.h" #include "plugin/keyring/common/keyring.h" #ifdef _WIN32 #define MYSQL_DEFAULT_KEYRINGFILE MYSQL_KEYRINGDIR "\\keyring" #else #define MYSQL_DEFAULT_KEYRINGFILE MYSQL_KEYRINGDIR "/keyring" #endif using keyring::Buffered_file_io; using keyring::Key; using keyring::Keys_container; using keyring::Keys_iterator; using keyring::Logger; mysql_rwlock_t LOCK_keyring; int check_keyring_file_data(MYSQL_THD thd MY_ATTRIBUTE((unused)), SYS_VAR *var MY_ATTRIBUTE((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 Keys_container(logger.get())); (*(const char **)save) = NULL; keyring_filename = value->val_str(value, buff, &len); mysql_rwlock_wrlock(&LOCK_keyring); if (create_keyring_dir_if_does_not_exist(keyring_filename)) { mysql_rwlock_unlock(&LOCK_keyring); logger->log(ERROR_LEVEL, ER_KEYRING_FAILED_TO_SET_KEYRING_FILE_DATA); return 1; } try { IKeyring_io *keyring_io(new Buffered_file_io(logger.get())); if (new_keys->init(keyring_io, keyring_filename)) { mysql_rwlock_unlock(&LOCK_keyring); return 1; } *reinterpret_cast(save) = new_keys.get(); new_keys.release(); mysql_rwlock_unlock(&LOCK_keyring); } catch (...) { mysql_rwlock_unlock(&LOCK_keyring); return 1; } return (0); } static char *keyring_file_data_value = NULL; static MYSQL_SYSVAR_STR( data, /* name */ keyring_file_data_value, /* value */ PLUGIN_VAR_RQCMDARG, /* flags */ "The path to the keyring file. Must be specified", /* comment */ check_keyring_file_data, /* check() */ update_keyring_file_data, /* update() */ MYSQL_DEFAULT_KEYRINGFILE /* default */ ); static MYSQL_SYSVAR_BOOL(open_mode, keyring_open_mode, PLUGIN_VAR_INVISIBLE | PLUGIN_VAR_RQCMDARG, "Mode in which keyring file should be opened", NULL, NULL, true); static SYS_VAR *keyring_file_system_variables[] = { MYSQL_SYSVAR(data), MYSQL_SYSVAR(open_mode), NULL}; 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_init(MYSQL_PLUGIN plugin_info MY_ATTRIBUTE((unused))) { if (init_logging_service_for_plugin(®_srv, &log_bi, &log_bs)) return true; try { SSL_library_init(); // always returns 1 ERR_load_BIO_strings(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #ifdef HAVE_PSI_INTERFACE keyring_init_psi_keys(); #endif DBUG_EXECUTE_IF("simulate_keyring_init_error", return true;); if (init_keyring_locks()) return true; logger.reset(new Logger()); if (create_keyring_dir_if_does_not_exist(keyring_file_data_value)) { logger->log(ERROR_LEVEL, ER_KEYRING_FAILED_TO_CREATE_KEYRING_DIR); return true; } keys.reset(new Keys_container(logger.get())); 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); IKeyring_io *keyring_io = new Buffered_file_io(logger.get(), &allowedFileVersionsToInit); if (keys->init(keyring_io, keyring_file_data_value)) { is_keys_container_initialized = false; logger->log(ERROR_LEVEL, ER_KEYRING_FILE_INIT_FAILED); return true; } is_keys_container_initialized = true; return false; } catch (...) { if (logger != NULL) logger->log(ERROR_LEVEL, ER_KEYRING_INTERNAL_EXCEPTION_FAILED_FILE_INIT); deinit_logging_service_for_plugin(®_srv, &log_bi, &log_bs); return true; } } static int keyring_deinit(void *arg MY_ATTRIBUTE((unused))) { // not taking a lock here as the calls to keyring_deinit are serialized by // the plugin framework #if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_remove_thread_state(0); #endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */ ERR_free_strings(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); keys.reset(); logger.reset(); keyring_file_data.reset(); mysql_rwlock_destroy(&LOCK_keyring); deinit_logging_service_for_plugin(®_srv, &log_bi, &log_bs); return 0; } static 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_file"); } static 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_file"); } static bool mysql_key_remove(const char *key_id, const char *user_id) { return mysql_key_remove(key_id, user_id, "keyring_file"); } static bool mysql_key_generate(const char *key_id, const char *key_type, const char *user_id, size_t key_len) { try { std::unique_ptr key_candidate( new Key(key_id, key_type, user_id, NULL, 0)); std::unique_ptr key(new uchar[key_len]); if (key.get() == NULL) return true; memset(key.get(), 0, key_len); if (is_keys_container_initialized == false || 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) == true; } catch (...) { if (logger != NULL) logger->log(ERROR_LEVEL, ER_KEYRING_FAILED_TO_GENERATE_KEY); return true; } } static void mysql_key_iterator_init(void **key_iterator) { *key_iterator = new Keys_iterator(logger.get()); if (mysql_key_iterator_init( static_cast(*key_iterator), "keyring_file") == true) { delete static_cast(*key_iterator); *key_iterator = nullptr; } } static void mysql_key_iterator_deinit(void *key_iterator) { mysql_key_iterator_deinit( static_cast(key_iterator), "keyring_file"); 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_file"); } /* 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_file){ MYSQL_KEYRING_PLUGIN, /* type */ &keyring_descriptor, /* descriptor */ "keyring_file", /* name */ "Oracle Corporation", /* author */ "store/fetch authentication data to/from a flat file", /* description */ PLUGIN_LICENSE_GPL, keyring_init, /* init function (when loaded) */ NULL, /* check uninstall function */ keyring_deinit, /* deinit function (when unloaded) */ 0x0100, /* version */ NULL, /* status variables */ keyring_file_system_variables, /* system variables */ NULL, PLUGIN_OPT_ALLOW_EARLY, } mysql_declare_plugin_end;