/* 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 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 "keyring.h" #include "buffered_file_io.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::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)), struct st_mysql_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); boost::movelib::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(MY_ERROR_LEVEL, "keyring_file_data cannot be set to new value" " as the keyring file cannot be created/accessed in the provided path"); 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 struct st_mysql_sys_var *keyring_file_system_variables[]= { MYSQL_SYSVAR(data), MYSQL_SYSVAR(open_mode), NULL }; static int keyring_init(MYSQL_PLUGIN plugin_info) { try { #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(plugin_info)); if (create_keyring_dir_if_does_not_exist(keyring_file_data_value)) { logger->log(MY_ERROR_LEVEL, "Could not create keyring directory " "The keyring_file will stay unusable until correct path to the keyring " "directory gets provided"); return TRUE; } keys.reset(new Keys_container(logger.get())); IKeyring_io *keyring_io= new Buffered_file_io(logger.get()); if (keys->init(keyring_io, keyring_file_data_value)) { is_keys_container_initialized = FALSE; logger->log(MY_ERROR_LEVEL, "keyring_file initialization failure. Please check" " if the keyring_file_data points to readable keyring file or keyring file" " can be created in the specified location. " "The keyring_file will stay unusable until correct path to the keyring file " "gets provided"); return TRUE; } is_keys_container_initialized = TRUE; return FALSE; } catch (...) { if (logger != NULL) logger->log(MY_ERROR_LEVEL, "keyring_file initialization failure due to internal" " exception inside the plugin"); return TRUE; } } 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 keys.reset(); logger.reset(); keyring_file_data.reset(); mysql_rwlock_destroy(&LOCK_keyring); return 0; } my_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"); } my_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"); } my_bool mysql_key_remove(const char *key_id, const char *user_id) { return mysql_key_remove(key_id, user_id, "keyring_file"); } my_bool mysql_key_generate(const char *key_id, const char *key_type, const char *user_id, size_t key_len) { try { boost::movelib::unique_ptr key_candidate(new keyring::Key(key_id, key_type, user_id, NULL, 0)); boost::movelib::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(MY_ERROR_LEVEL, "Failed to generate a key due to internal exception inside keyring_file plugin"); 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_file"); } 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) */ keyring_deinit, /* deinit function (when unloaded) */ 0x0100, /* version */ NULL, /* status variables */ keyring_file_system_variables, /* system variables */ NULL, 0, } mysql_declare_plugin_end;