summaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
authorVladislav Vaintroub <wlad@mariadb.com>2017-08-28 18:28:07 +0000
committerVladislav Vaintroub <wlad@mariadb.com>2017-08-28 18:28:07 +0000
commitdda40b930498b70bb5546f857b27744039a5649d (patch)
treefc68ae13dcbd984ac7f662dadfcb6d74ffa7fa61 /plugin
parent11352d52cd35f89b1d573405fca92932e112cba9 (diff)
downloadmariadb-git-dda40b930498b70bb5546f857b27744039a5649d.tar.gz
AWS Key Management : Introduce "mock" variable, available in debug build.
If this variable is set, skip actual AWS calls, and fake/mock both generation and encryption of the keys. The advantage of having a mock mode is that more aws_key_management tests can be enabled on buildbot.
Diffstat (limited to 'plugin')
-rw-r--r--plugin/aws_key_management/CMakeLists.txt1
-rw-r--r--plugin/aws_key_management/aws_key_management_plugin.cc202
2 files changed, 164 insertions, 39 deletions
diff --git a/plugin/aws_key_management/CMakeLists.txt b/plugin/aws_key_management/CMakeLists.txt
index 00e788ffbd5..9221b54ef7e 100644
--- a/plugin/aws_key_management/CMakeLists.txt
+++ b/plugin/aws_key_management/CMakeLists.txt
@@ -20,6 +20,7 @@ MACRO(SKIP_AWS_PLUGIN msg)
ENDIF()
RETURN()
ENDMACRO()
+SET(CMAKE_CXX_STANDARD 11)
MYSQL_ADD_PLUGIN(aws_key_management aws_key_management_plugin.cc DISABLED
COMPONENT aws-key-management)
diff --git a/plugin/aws_key_management/aws_key_management_plugin.cc b/plugin/aws_key_management/aws_key_management_plugin.cc
index 365c5caf198..60ca6fd1ff3 100644
--- a/plugin/aws_key_management/aws_key_management_plugin.cc
+++ b/plugin/aws_key_management/aws_key_management_plugin.cc
@@ -82,9 +82,20 @@ static unsigned long log_level;
static int rotate_key;
static int request_timeout;
+#ifndef DBUG_OFF
+#define WITH_AWS_MOCK 1
+#else
+#define WITH_AWS_MOCK 0
+#endif
+
+#if WITH_AWS_MOCK
+static char mock;
+#endif
+
+
/* AWS functionality*/
-static int aws_decrypt_key(const char *path, KEY_INFO *info);
-static int aws_generate_datakey(uint key_id, uint version);
+static int read_and_decrypt_key(const char *path, KEY_INFO *info);
+static int generate_and_save_datakey(uint key_id, uint version);
static int extract_id_and_version(const char *name, uint *id, uint *ver);
static unsigned int get_latest_key_version(unsigned int key_id);
@@ -95,6 +106,35 @@ static std::mutex mtx;
static Aws::KMS::KMSClient *client;
+#if WITH_AWS_MOCK
+/*
+ Mock routines to test plugin without actual AWS KMS interaction
+ we only need to mock 2 functions - generating encrypted key, and decrypt
+
+ This mock functions do no-op encryption, i.e encrypt and decrypt of
+ a buffer return the buffer itself.
+*/
+
+/*
+ Generate random "encrypted" key. We do not encrypt anything in mock mode.
+*/
+static int mock_generate_encrypted_key(Aws::Utils::ByteBuffer *result)
+{
+ size_t len = key_spec == 0?16 : 32;
+ *result = Aws::Utils::ByteBuffer(len);
+ my_random_bytes(result->GetUnderlyingData(), (int)len);
+ return 0;
+}
+
+
+static int mock_decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output, Aws::String *errmsg)
+{
+ /* We do not encrypt or decrypt in mock mode.*/
+ *output = input;
+ return 0;
+}
+#endif
+
/* Redirect AWS trace to error log */
class MySQLLogSystem : public Aws::Utils::Logging::FormattedLogSystem
{
@@ -163,13 +203,7 @@ static vector<string> traverse_current_directory()
Aws::SDKOptions sdkOptions;
-/*
- Plugin initialization.
-
- Create KMS client and scan datadir to find out which keys and versions
- are present.
-*/
-static int plugin_init(void *p)
+static int aws_init()
{
#ifdef HAVE_YASSL
@@ -198,7 +232,29 @@ static int plugin_init(void *p)
my_printf_error(ER_UNKNOWN_ERROR, "Can not initialize KMS client", ME_ERROR_LOG | ME_WARNING);
return -1;
}
-
+ return 0;
+}
+
+static int init()
+{
+#if WITH_AWS_MOCK
+ if(mock)
+ return 0;
+#endif
+ return aws_init();
+}
+
+/*
+ Plugin initialization.
+
+ Create KMS client and scan datadir to find out which keys and versions
+ are present.
+*/
+static int plugin_init(void *p)
+{
+ if (init())
+ return -1;
+
vector<string> files= traverse_current_directory();
for (size_t i=0; i < files.size(); i++)
{
@@ -214,14 +270,29 @@ static int plugin_init(void *p)
}
-static int plugin_deinit(void *p)
+static void aws_shutdown()
{
- latest_version_cache.clear();
- key_info_cache.clear();
delete client;
ShutdownAWSLogging();
-
Aws::ShutdownAPI(sdkOptions);
+}
+
+
+static void shutdown()
+{
+#if WITH_AWS_MOCK
+ if(mock)
+ return;
+#endif
+ aws_shutdown();
+}
+
+
+static int plugin_deinit(void *p)
+{
+ latest_version_cache.clear();
+ key_info_cache.clear();
+ shutdown();
return 0;
}
@@ -251,7 +322,7 @@ static int load_key(KEY_INFO *info)
char path[256];
format_keyfile_name(path, sizeof(path), info->key_id, info->key_version);
- ret= aws_decrypt_key(path, info);
+ ret= read_and_decrypt_key(path, info);
if (ret)
info->load_failed= true;
@@ -317,14 +388,7 @@ static unsigned int get_latest_key_version_nolock(unsigned int key_id)
else // (ver == 0)
{
/* Generate a new key, version 1 */
- if (!master_key_id[0])
- {
- my_printf_error(ER_UNKNOWN_ERROR,
- "Can't generate encryption key %u, because 'aws_key_management_master_key_id' parameter is not set",
- MYF(0), key_id);
- return(ENCRYPTION_KEY_VERSION_INVALID);
- }
- if (aws_generate_datakey(key_id, 1) != 0)
+ if (generate_and_save_datakey(key_id, 1) != 0)
return(ENCRYPTION_KEY_VERSION_INVALID);
info.key_id= key_id;
info.key_version= 1;
@@ -336,11 +400,35 @@ static unsigned int get_latest_key_version_nolock(unsigned int key_id)
return(info.key_version);
}
+/* Decrypt Byte buffer with AWS. */
+static int aws_decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output, Aws::String *errmsg)
+{
+ DecryptRequest request;
+ request.SetCiphertextBlob(input);
+ DecryptOutcome outcome = client->Decrypt(request);
+ if (!outcome.IsSuccess())
+ {
+ *errmsg = outcome.GetError().GetMessage();
+ return -1;
+ }
+ *output= outcome.GetResult().GetPlaintext();
+ return 0;
+}
+
+
+static int decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output, Aws::String *errmsg)
+{
+#if WITH_AWS_MOCK
+ if(mock)
+ return mock_decrypt(input,output, errmsg);
+#endif
+ return aws_decrypt(input, output, errmsg);
+}
/*
Decrypt a file with KMS
*/
-static int aws_decrypt_key(const char *path, KEY_INFO *info)
+static int read_and_decrypt_key(const char *path, KEY_INFO *info)
{
/* Read file content into memory */
@@ -361,20 +449,21 @@ static int aws_decrypt_key(const char *path, KEY_INFO *info)
ifs.read(&contents[0], pos);
/* Decrypt data the with AWS */
- DecryptRequest request;
- Aws::Utils::ByteBuffer byteBuffer((unsigned char *)contents.data(), pos);
- request.SetCiphertextBlob(byteBuffer);
- DecryptOutcome outcome = client->Decrypt(request);
- if (!outcome.IsSuccess())
+
+ Aws::Utils::ByteBuffer input((unsigned char *)contents.data(), pos);
+ Aws::Utils::ByteBuffer plaintext;
+ Aws::String errmsg;
+
+ if (decrypt(input, &plaintext, &errmsg))
{
- my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: Decrypt failed for %s : %s", ME_ERROR_LOG, path,
- outcome.GetError().GetMessage().c_str());
- return(-1);
+ my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: Decrypt failed for %s : %s", ME_ERROR_LOG, path,
+ errmsg.c_str());
+ return -1;
}
- Aws::Utils::ByteBuffer plaintext = outcome.GetResult().GetPlaintext();
+
size_t len = plaintext.GetLength();
- if (len > (int)sizeof(info->data))
+ if (len > sizeof(info->data))
{
my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: encoding key too large for %s", ME_ERROR_LOG, path);
return(ENCRYPTION_KEY_BUFFER_TOO_SMALL);
@@ -385,9 +474,15 @@ static int aws_decrypt_key(const char *path, KEY_INFO *info)
}
-/* Generate a new datakey and store it a file */
-static int aws_generate_datakey(uint keyid, uint version)
+int aws_generate_encrypted_key(Aws::Utils::ByteBuffer *result)
{
+ if (!master_key_id[0])
+ {
+ my_printf_error(ER_UNKNOWN_ERROR,
+ "Can't generate encryption key, because 'aws_key_management_master_key_id' parameter is not set",
+ MYF(0));
+ return(-1);
+ }
GenerateDataKeyWithoutPlaintextRequest request;
request.SetKeyId(master_key_id);
request.SetKeySpec(DataKeySpecMapper::GetDataKeySpecForName(key_spec_names[key_spec]));
@@ -401,11 +496,30 @@ static int aws_generate_datakey(uint keyid, uint version)
outcome.GetError().GetMessage().c_str());
return(-1);
}
+ *result = outcome.GetResult().GetCiphertextBlob();
+ return 0;
+}
+
+
+static int generate_encrypted_key(Aws::Utils::ByteBuffer *output)
+{
+#if WITH_AWS_MOCK
+ if(mock)
+ return mock_generate_encrypted_key(output);
+#endif
+ return aws_generate_encrypted_key(output);
+}
+
+/* Generate a new datakey and store it a file */
+static int generate_and_save_datakey(uint keyid, uint version)
+{
+ Aws::Utils::ByteBuffer byteBuffer;
+
+ if (generate_encrypted_key(&byteBuffer))
+ return -1;
string out;
char filename[20];
- Aws::Utils::ByteBuffer byteBuffer = outcome.GetResult().GetCiphertextBlob();
-
format_keyfile_name(filename, sizeof(filename), keyid, version);
int fd= open(filename, O_WRONLY |O_CREAT|O_BINARY, IF_WIN(_S_IREAD, S_IRUSR| S_IRGRP| S_IROTH));
if (fd < 0)
@@ -438,7 +552,7 @@ static int rotate_single_key(uint key_id)
my_printf_error(ER_UNKNOWN_ERROR, "key %u does not exist", MYF(ME_JUST_WARNING), key_id);
return -1;
}
- else if (aws_generate_datakey(key_id, ver + 1))
+ else if (generate_and_save_datakey(key_id, ver + 1))
{
my_printf_error(ER_UNKNOWN_ERROR, "Could not generate datakey for key id= %u, ver= %u",
MYF(ME_JUST_WARNING), key_id, ver);
@@ -594,6 +708,13 @@ static MYSQL_SYSVAR_STR(region, region,
"AWS region. For example us-east-1, or eu-central-1. If no value provided, SDK default is used.",
NULL, NULL, "");
+#if WITH_AWS_MOCK
+static MYSQL_SYSVAR_BOOL(mock, mock,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
+ "Mock AWS KMS calls (for testing).",
+ NULL, NULL, 0);
+#endif
+
static struct st_mysql_sys_var* settings[]= {
MYSQL_SYSVAR(master_key_id),
MYSQL_SYSVAR(key_spec),
@@ -601,6 +722,9 @@ static struct st_mysql_sys_var* settings[]= {
MYSQL_SYSVAR(log_level),
MYSQL_SYSVAR(request_timeout),
MYSQL_SYSVAR(region),
+#if WITH_AWS_MOCK
+ MYSQL_SYSVAR(mock),
+#endif
NULL
};