262 lines
7.6 KiB
C++
262 lines
7.6 KiB
C++
/* 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 <my_global.h>
|
|
#include <sstream>
|
|
#include "keyring.h"
|
|
|
|
namespace keyring
|
|
{
|
|
/* Always defined. */
|
|
PSI_memory_key key_memory_KEYRING;
|
|
PSI_rwlock_key key_LOCK_keyring;
|
|
}
|
|
|
|
extern mysql_rwlock_t LOCK_keyring;
|
|
|
|
boost::movelib::unique_ptr<IKeys_container> keys(NULL);
|
|
volatile my_bool is_keys_container_initialized= FALSE;
|
|
boost::movelib::unique_ptr<ILogger> logger(NULL);
|
|
boost::movelib::unique_ptr<char[]> keyring_file_data(NULL);
|
|
my_bool keyring_open_mode= FALSE; // 0 - Read|Write|Create; 1 - Read only
|
|
|
|
#ifdef HAVE_PSI_INTERFACE
|
|
static PSI_rwlock_info all_keyring_rwlocks[]=
|
|
{
|
|
{&keyring::key_LOCK_keyring, "LOCK_keyring", 0}
|
|
};
|
|
|
|
static PSI_memory_info all_keyring_memory[]=
|
|
{
|
|
{&keyring::key_memory_KEYRING, "KEYRING", 0}
|
|
};
|
|
|
|
void keyring_init_psi_keys(void)
|
|
{
|
|
const char *category = "keyring";
|
|
int count;
|
|
|
|
count= array_elements(all_keyring_memory);
|
|
mysql_memory_register(category, all_keyring_memory, count);
|
|
|
|
count= array_elements(all_keyring_rwlocks);
|
|
mysql_rwlock_register(category, all_keyring_rwlocks, count);
|
|
}
|
|
#endif //HAVE_PSI_INTERFACE
|
|
|
|
int init_keyring_locks()
|
|
{
|
|
return mysql_rwlock_init(keyring::key_LOCK_keyring, &LOCK_keyring);
|
|
}
|
|
|
|
my_bool is_key_length_and_type_valid(const char *key_type, size_t key_len)
|
|
{
|
|
my_bool is_key_len_valid= FALSE;
|
|
my_bool is_type_valid= TRUE;
|
|
|
|
if(strcmp(key_type, "AES") == 0)
|
|
is_key_len_valid= (key_len == 16 || key_len == 24 || key_len == 32);
|
|
else if (strcmp(key_type, "RSA") == 0)
|
|
is_key_len_valid= (key_len == 128 || key_len == 256 || key_len == 512);
|
|
else if (strcmp(key_type, "DSA") == 0)
|
|
is_key_len_valid= (key_len == 128 || key_len == 256 || key_len == 384);
|
|
else
|
|
{
|
|
is_type_valid= FALSE;
|
|
logger->log(MY_ERROR_LEVEL, "Invalid key type");
|
|
}
|
|
|
|
if (is_type_valid == TRUE && is_key_len_valid == FALSE)
|
|
logger->log(MY_ERROR_LEVEL, "Invalid key length for given block cipher");
|
|
|
|
return is_type_valid && is_key_len_valid;
|
|
}
|
|
|
|
void log_operation_error(const char *failed_operation, const char *plugin_name)
|
|
{
|
|
if (logger != NULL)
|
|
{
|
|
std::ostringstream err_msg;
|
|
err_msg << "Failed to " << failed_operation << " due to internal exception inside "
|
|
<< plugin_name << " plugin";
|
|
logger->log(MY_ERROR_LEVEL, err_msg.str().c_str());
|
|
}
|
|
}
|
|
|
|
my_bool create_keyring_dir_if_does_not_exist(const char *keyring_file_path)
|
|
{
|
|
if (!keyring_file_path || strlen(keyring_file_path) == 0)
|
|
return TRUE;
|
|
char keyring_dir[FN_REFLEN];
|
|
size_t keyring_dir_length;
|
|
dirname_part(keyring_dir, keyring_file_path, &keyring_dir_length);
|
|
if (keyring_dir_length > 1 && (keyring_dir[keyring_dir_length-1] == FN_LIBCHAR))
|
|
{
|
|
keyring_dir[keyring_dir_length-1]= '\0';
|
|
--keyring_dir_length;
|
|
}
|
|
int flags=
|
|
#ifdef _WIN32
|
|
0
|
|
#else
|
|
S_IRWXU | S_IRGRP | S_IXGRP
|
|
#endif
|
|
;
|
|
/*
|
|
If keyring_dir_length is 0, it means file
|
|
is being created current working directory
|
|
*/
|
|
if (strlen(keyring_dir) != 0)
|
|
my_mkdir(keyring_dir, flags, MYF(0));
|
|
return FALSE;
|
|
}
|
|
|
|
void update_keyring_file_data(MYSQL_THD thd MY_ATTRIBUTE((unused)),
|
|
struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
|
|
void *var_ptr MY_ATTRIBUTE((unused)),
|
|
const void *save_ptr)
|
|
{
|
|
mysql_rwlock_wrlock(&LOCK_keyring);
|
|
IKeys_container *new_keys= *reinterpret_cast<IKeys_container**>(const_cast<void*>(save_ptr));
|
|
keys.reset(new_keys);
|
|
keyring_file_data.reset(new char[new_keys->get_keyring_storage_url().length()+1]);
|
|
memcpy(keyring_file_data.get(), new_keys->get_keyring_storage_url().c_str(),
|
|
new_keys->get_keyring_storage_url().length()+1);
|
|
*reinterpret_cast<char **>(var_ptr)= keyring_file_data.get();
|
|
is_keys_container_initialized= TRUE;
|
|
mysql_rwlock_unlock(&LOCK_keyring);
|
|
}
|
|
|
|
my_bool mysql_key_fetch(boost::movelib::unique_ptr<IKey> key_to_fetch, char **key_type,
|
|
void **key, size_t *key_len)
|
|
{
|
|
if (is_keys_container_initialized == FALSE)
|
|
return TRUE;
|
|
|
|
if (key_to_fetch->is_key_id_valid() == FALSE)
|
|
{
|
|
logger->log(MY_ERROR_LEVEL,
|
|
"Error while fetching key: key_id cannot be empty");
|
|
return TRUE;
|
|
}
|
|
mysql_rwlock_rdlock(&LOCK_keyring);
|
|
IKey *fetched_key = keys->fetch_key(key_to_fetch.get());
|
|
mysql_rwlock_unlock(&LOCK_keyring);
|
|
if (fetched_key)
|
|
{
|
|
*key_len = fetched_key->get_key_data_size();
|
|
fetched_key->xor_data();
|
|
*key= static_cast<void*>(fetched_key->release_key_data());
|
|
*key_type= my_strdup(keyring::key_memory_KEYRING,
|
|
fetched_key->get_key_type()->c_str(),
|
|
MYF(MY_WME));
|
|
}
|
|
else
|
|
*key = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
my_bool check_key_for_writing(IKey* key, std::string error_for)
|
|
{
|
|
std::string error_msg= "Error while ";
|
|
error_msg+= error_for;
|
|
if (key->is_key_type_valid() == FALSE)
|
|
{
|
|
error_msg+= " key: invalid key_type";
|
|
logger->log(MY_ERROR_LEVEL, error_msg.c_str());
|
|
return TRUE;
|
|
}
|
|
if (key->is_key_id_valid() == FALSE)
|
|
{
|
|
error_msg+= " key: key_id cannot be empty";
|
|
logger->log(MY_ERROR_LEVEL, error_msg.c_str());
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
my_bool mysql_key_store(boost::movelib::unique_ptr<IKey> key_to_store)
|
|
{
|
|
if (is_keys_container_initialized == FALSE)
|
|
return TRUE;
|
|
|
|
if (check_key_for_writing(key_to_store.get(), "storing"))
|
|
return TRUE;
|
|
|
|
if (key_to_store->get_key_data_size() > 0)
|
|
key_to_store->xor_data();
|
|
mysql_rwlock_wrlock(&LOCK_keyring);
|
|
if (keys->store_key(key_to_store.get()))
|
|
{
|
|
mysql_rwlock_unlock(&LOCK_keyring);
|
|
return TRUE;
|
|
}
|
|
mysql_rwlock_unlock(&LOCK_keyring);
|
|
|
|
key_to_store.release();
|
|
return FALSE;
|
|
}
|
|
|
|
my_bool mysql_key_remove(boost::movelib::unique_ptr<IKey> key_to_remove)
|
|
{
|
|
bool retval= false;
|
|
if (is_keys_container_initialized == FALSE)
|
|
return TRUE;
|
|
if (key_to_remove->is_key_id_valid() == FALSE)
|
|
{
|
|
logger->log(MY_ERROR_LEVEL,
|
|
"Error while removing key: key_id cannot be empty");
|
|
return TRUE;
|
|
}
|
|
mysql_rwlock_wrlock(&LOCK_keyring);
|
|
retval= keys->remove_key(key_to_remove.get());
|
|
mysql_rwlock_unlock(&LOCK_keyring);
|
|
return retval;
|
|
}
|
|
|
|
my_bool mysql_keyring_iterator_init(Keys_iterator *key_iterator)
|
|
{
|
|
mysql_rwlock_rdlock(&LOCK_keyring);
|
|
key_iterator->init();
|
|
mysql_rwlock_unlock(&LOCK_keyring);
|
|
return false;
|
|
}
|
|
|
|
void mysql_keyring_iterator_deinit(Keys_iterator *key_iterator)
|
|
{
|
|
key_iterator->deinit();
|
|
}
|
|
|
|
bool mysql_keyring_iterator_get_key(Keys_iterator *key_iterator,
|
|
char *key_id, char *user_id)
|
|
{
|
|
keyring::Key_metadata *key_loaded= NULL;
|
|
bool error= key_iterator->get_key(&key_loaded);
|
|
if (error == FALSE && key_loaded != NULL)
|
|
{
|
|
if (key_id)
|
|
strcpy(key_id, key_loaded->id->c_str());
|
|
if (user_id)
|
|
strcpy(user_id, key_loaded->user->c_str());
|
|
delete key_loaded;
|
|
}
|
|
else if (error == FALSE && key_loaded == NULL)
|
|
{
|
|
/* no keys exists or all keys are read */
|
|
return TRUE;
|
|
}
|
|
return error;
|
|
}
|