Commit 8ae5408c authored by Julius Goryavsky's avatar Julius Goryavsky

MDEV-28442: Hashicorp: refactoring to wrap static variables into a class

This commit contains changes to refactor the the Hashicorp plugin code
which hides all variables previously declared as "static" and which are
not user-visible parameters into a special class that contains all the
plugin's dynamic data. This was done primarily to significantly simplify
the code of the initialization and deinitialization functions, which
previously contained a large number of gotos and complex branching
conditions to control memory deallocation.
parent e571174e
...@@ -43,22 +43,6 @@ ...@@ -43,22 +43,6 @@
#define PLUGIN_ERROR_HEADER "hashicorp: " #define PLUGIN_ERROR_HEADER "hashicorp: "
static clock_t cache_max_time;
static clock_t cache_max_ver_time;
/*
Convert milliseconds to timer ticks with rounding
to nearest integer:
*/
static clock_t ms_to_ticks (long ms)
{
long long ticks_1000 = ms * (long long) CLOCKS_PER_SEC;
clock_t ticks = (clock_t) (ticks_1000 / 1000);
return ticks + ((clock_t) (ticks_1000 % 1000) >= 500);
}
static std::mutex mtx;
/* Key version information structure: */ /* Key version information structure: */
typedef struct VER_INFO typedef struct VER_INFO
{ {
...@@ -88,16 +72,114 @@ typedef struct KEY_INFO ...@@ -88,16 +72,114 @@ typedef struct KEY_INFO
/* Cache for the latest version, per key id: */ /* Cache for the latest version, per key id: */
typedef std::unordered_map<unsigned int, VER_INFO> VER_MAP; typedef std::unordered_map<unsigned int, VER_INFO> VER_MAP;
static VER_MAP latest_version_cache;
/* Cache for key information: */ /* Cache for key information: */
typedef std::unordered_map<unsigned long long, KEY_INFO> KEY_MAP; typedef std::unordered_map<unsigned long long, KEY_INFO> KEY_MAP;
static KEY_MAP key_info_cache;
#define KEY_ID_AND_VERSION(key_id, version) \ #define KEY_ID_AND_VERSION(key_id, version) \
((unsigned long long)key_id << 32 | version) ((unsigned long long)key_id << 32 | version)
static void cache_add (const KEY_INFO& info, bool update_version) class HCData
{
private:
struct curl_slist *slist;
char *vault_url_data;
size_t vault_url_len;
char *local_token;
char *token_header;
bool curl_inited;
public:
HCData()
:slist(NULL),
vault_url_data(NULL),
vault_url_len(0),
local_token(NULL),
token_header(NULL),
curl_inited(false)
{}
unsigned int get_latest_version (unsigned int key_id);
unsigned int get_key_from_vault (unsigned int key_id,
unsigned int key_version,
unsigned char *dstbuf,
unsigned int *buflen);
int init ();
void deinit ()
{
if (slist)
{
curl_slist_free_all(slist);
slist = NULL;
}
if (curl_inited)
{
curl_global_cleanup();
curl_inited = false;
}
vault_url_len = 0;
if (vault_url_data)
{
free(vault_url_data);
vault_url_data = NULL;
}
if (token_header)
{
free(token_header);
token_header = NULL;
}
if (local_token)
{
free(local_token);
local_token = NULL;
}
}
void cache_clean ()
{
latest_version_cache.clear();
key_info_cache.clear();
}
private:
std::mutex mtx;
VER_MAP latest_version_cache;
KEY_MAP key_info_cache;
private:
void cache_add (const KEY_INFO& info, bool update_version);
unsigned int cache_get (unsigned int key_id, unsigned int key_version,
unsigned char* data, unsigned int* buflen,
bool with_timeouts);
unsigned int cache_check_version (unsigned int key_id);
unsigned int cache_get_version (unsigned int key_id);
int curl_run (const char *url, std::string *response,
bool soft_timeout) const;
int check_version (const char *mount_url) const;
void *alloc (size_t nbytes) const
{
void *res = (char *) malloc(nbytes);
if (!res)
{
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Memory allocation error", 0);
}
return res;
}
};
static HCData data;
static clock_t cache_max_time;
static clock_t cache_max_ver_time;
/*
Convert milliseconds to timer ticks with rounding
to nearest integer:
*/
static clock_t ms_to_ticks (long ms)
{
long long ticks_1000 = ms * (long long) CLOCKS_PER_SEC;
clock_t ticks = (clock_t) (ticks_1000 / 1000);
return ticks + ((clock_t) (ticks_1000 % 1000) >= 500);
}
void HCData::cache_add (const KEY_INFO& info, bool update_version)
{ {
unsigned int key_id = info.key_id; unsigned int key_id = info.key_id;
unsigned int key_version = info.key_version; unsigned int key_version = info.key_version;
...@@ -120,10 +202,10 @@ static void cache_add (const KEY_INFO& info, bool update_version) ...@@ -120,10 +202,10 @@ static void cache_add (const KEY_INFO& info, bool update_version)
mtx.unlock(); mtx.unlock();
} }
static unsigned int unsigned int
cache_get (unsigned int key_id, unsigned int key_version, HCData::cache_get (unsigned int key_id, unsigned int key_version,
unsigned char* data, unsigned int* buflen, unsigned char* data, unsigned int* buflen,
bool with_timeouts) bool with_timeouts)
{ {
unsigned int version = key_version; unsigned int version = key_version;
clock_t current_time = clock(); clock_t current_time = clock();
...@@ -223,7 +305,7 @@ static unsigned int ...@@ -223,7 +305,7 @@ static unsigned int
return 0; return 0;
} }
static unsigned int cache_get_version (unsigned int key_id) unsigned int HCData::cache_get_version (unsigned int key_id)
{ {
unsigned int version; unsigned int version;
mtx.lock(); mtx.lock();
...@@ -248,7 +330,7 @@ static unsigned int cache_get_version (unsigned int key_id) ...@@ -248,7 +330,7 @@ static unsigned int cache_get_version (unsigned int key_id)
return version; return version;
} }
static unsigned int cache_check_version (unsigned int key_id) unsigned int HCData::cache_check_version (unsigned int key_id)
{ {
unsigned int version; unsigned int version;
clock_t timestamp; clock_t timestamp;
...@@ -446,9 +528,8 @@ static CURLcode ...@@ -446,9 +528,8 @@ static CURLcode
return curl_res; return curl_res;
} }
static struct curl_slist *list; int HCData::curl_run (const char *url, std::string *response,
bool soft_timeout) const
static int curl_run (char *url, std::string *response, bool soft_timeout)
{ {
char curl_errbuf[CURL_ERROR_SIZE]; char curl_errbuf[CURL_ERROR_SIZE];
std::ostringstream read_data_stream; std::ostringstream read_data_stream;
...@@ -470,7 +551,7 @@ static int curl_run (char *url, std::string *response, bool soft_timeout) ...@@ -470,7 +551,7 @@ static int curl_run (char *url, std::string *response, bool soft_timeout)
(curl_res= curl_easy_setopt(curl, CURLOPT_WRITEDATA, (curl_res= curl_easy_setopt(curl, CURLOPT_WRITEDATA,
&read_data_stream)) != &read_data_stream)) !=
CURLE_OK || CURLE_OK ||
(curl_res= curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list)) != (curl_res= curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist)) !=
CURLE_OK || CURLE_OK ||
/* /*
The options CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST are The options CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST are
...@@ -704,10 +785,7 @@ static int get_key_data (const char *js, int js_len, ...@@ -704,10 +785,7 @@ static int get_key_data (const char *js, int js_len,
return 0; return 0;
} }
static char *vault_url_data; unsigned int HCData::get_latest_version (unsigned int key_id)
static size_t vault_url_len;
static unsigned int get_latest_version (unsigned int key_id)
{ {
unsigned int version; unsigned int version;
#if HASICORP_DEBUG_LOGGING #if HASICORP_DEBUG_LOGGING
...@@ -784,10 +862,10 @@ static unsigned int get_latest_version (unsigned int key_id) ...@@ -784,10 +862,10 @@ static unsigned int get_latest_version (unsigned int key_id)
return version; return version;
} }
static unsigned int get_key_from_vault (unsigned int key_id, unsigned int HCData::get_key_from_vault (unsigned int key_id,
unsigned int key_version, unsigned int key_version,
unsigned char *dstbuf, unsigned char *dstbuf,
unsigned int *buflen) unsigned int *buflen)
{ {
#if HASICORP_DEBUG_LOGGING #if HASICORP_DEBUG_LOGGING
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
...@@ -900,6 +978,19 @@ static unsigned int get_key_from_vault (unsigned int key_id, ...@@ -900,6 +978,19 @@ static unsigned int get_key_from_vault (unsigned int key_id,
return 0; return 0;
} }
static unsigned int get_latest_version (unsigned int key_id)
{
return data.get_latest_version(key_id);
}
static unsigned int get_key_from_vault (unsigned int key_id,
unsigned int key_version,
unsigned char *dstbuf,
unsigned int *buflen)
{
return data.get_key_from_vault(key_id, key_version, dstbuf, buflen);
}
struct st_mariadb_encryption hashicorp_key_management_plugin= { struct st_mariadb_encryption hashicorp_key_management_plugin= {
MariaDB_ENCRYPTION_INTERFACE_VERSION, MariaDB_ENCRYPTION_INTERFACE_VERSION,
get_latest_version, get_latest_version,
...@@ -909,7 +1000,7 @@ struct st_mariadb_encryption hashicorp_key_management_plugin= { ...@@ -909,7 +1000,7 @@ struct st_mariadb_encryption hashicorp_key_management_plugin= {
#ifdef _MSC_VER #ifdef _MSC_VER
static int setenv(const char *name, const char *value, int overwrite) static int setenv (const char *name, const char *value, int overwrite)
{ {
if (!overwrite) if (!overwrite)
{ {
...@@ -932,10 +1023,7 @@ static int setenv(const char *name, const char *value, int overwrite) ...@@ -932,10 +1023,7 @@ static int setenv(const char *name, const char *value, int overwrite)
#define MAX_URL_SIZE 32768 #define MAX_URL_SIZE 32768
static char *local_token; int HCData::init ()
static char *token_header;
static int hashicorp_key_management_plugin_init(void *p)
{ {
const static char *x_vault_token = "X-Vault-Token:"; const static char *x_vault_token = "X-Vault-Token:";
const static size_t x_vault_token_len = strlen(x_vault_token); const static size_t x_vault_token_len = strlen(x_vault_token);
...@@ -956,11 +1044,8 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -956,11 +1044,8 @@ static int hashicorp_key_management_plugin_init(void *p)
of the current process). Therefore, we need to copy the token of the current process). Therefore, we need to copy the token
value to the working buffer: value to the working buffer:
*/ */
token = (char *) malloc(token_len + 1); if (!(token = (char *) alloc(token_len + 1)))
if (token == NULL)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Memory allocation error", 0);
return 1; return 1;
} }
memcpy(token, token_env, token_len + 1); memcpy(token, token_env, token_len + 1);
...@@ -1006,17 +1091,8 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1006,17 +1091,8 @@ static int hashicorp_key_management_plugin_init(void *p)
ME_ERROR_LOG_ONLY | ME_NOTE, token, (int) token_len); ME_ERROR_LOG_ONLY | ME_NOTE, token, (int) token_len);
#endif #endif
size_t buf_len = x_vault_token_len + token_len + 1; size_t buf_len = x_vault_token_len + token_len + 1;
token_header = (char *) malloc(buf_len); if (!(token_header = (char *) alloc(buf_len)))
if (token_header == NULL)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Memory allocation error", 0);
Failure2:
if (local_token)
{
free(local_token);
local_token = NULL;
}
return 1; return 1;
} }
snprintf(token_header, buf_len, "%s%s", x_vault_token, token); snprintf(token_header, buf_len, "%s%s", x_vault_token, token);
...@@ -1030,11 +1106,7 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1030,11 +1106,7 @@ static int hashicorp_key_management_plugin_init(void *p)
"the path inside the URL must start with " "the path inside the URL must start with "
"the \"/v1/\" prefix, while the supplied " "the \"/v1/\" prefix, while the supplied "
"URL value is: \"%s\"", 0, vault_url); "URL value is: \"%s\"", 0, vault_url);
Failure: return 1;
vault_url_len = 0;
free(token_header);
token_header = NULL;
goto Failure2;
} }
size_t prefix_len = (size_t) (suffix - vault_url); size_t prefix_len = (size_t) (suffix - vault_url);
if (prefix_len == 0) if (prefix_len == 0)
...@@ -1043,7 +1115,7 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1043,7 +1115,7 @@ static int hashicorp_key_management_plugin_init(void *p)
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Supplied URL does not contain a hostname: \"%s\"", "Supplied URL does not contain a hostname: \"%s\"",
0, vault_url); 0, vault_url);
goto Failure; return 1;
} }
/* Check if the suffix consists only of the slash: */ /* Check if the suffix consists only of the slash: */
size_t suffix_len = strlen(suffix + 1) + 1; size_t suffix_len = strlen(suffix + 1) + 1;
...@@ -1116,7 +1188,7 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1116,7 +1188,7 @@ static int hashicorp_key_management_plugin_init(void *p)
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Supplied URL does not contain a secret name: \"%s\"", "Supplied URL does not contain a secret name: \"%s\"",
0, vault_url); 0, vault_url);
goto Failure; return 1;
} }
suffix += 3; suffix += 3;
/* Checking for a slash at the end of the "/v1/" sequence: */ /* Checking for a slash at the end of the "/v1/" sequence: */
...@@ -1148,18 +1220,15 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1148,18 +1220,15 @@ static int hashicorp_key_management_plugin_init(void *p)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Maximum allowed vault URL length exceeded", 0); "Maximum allowed vault URL length exceeded", 0);
goto Failure; return 1;
} }
/* /*
In advance, we create a buffer containing the URL for vault In advance, we create a buffer containing the URL for vault
+ the "/data/" suffix (7 characters): + the "/data/" suffix (7 characters):
*/ */
vault_url_data = (char *) malloc(vault_url_len + 7); if (!(vault_url_data = (char *) alloc(vault_url_len + 7)))
if (vault_url_data == NULL)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER return 1;
"Memory allocation error", 0);
goto Failure;
} }
memcpy(vault_url_data, vault_url, vault_url_len); memcpy(vault_url_data, vault_url, vault_url_len);
memcpy(vault_url_data + vault_url_len, "/data/", 7); memcpy(vault_url_data + vault_url_len, "/data/", 7);
...@@ -1174,19 +1243,15 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1174,19 +1243,15 @@ static int hashicorp_key_management_plugin_init(void *p)
"curl returned this error code: %u " "curl returned this error code: %u "
"with the following error message: %s", "with the following error message: %s",
0, curl_res, curl_easy_strerror(curl_res)); 0, curl_res, curl_easy_strerror(curl_res));
curl_error: return 1;
free(vault_url_data);
vault_url_data = NULL;
goto Failure;
} }
list = curl_slist_append(list, token_header); curl_inited = true;
if (list == NULL) slist = curl_slist_append(slist, token_header);
if (slist == NULL)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"curl: unable to construct slist", 0); "curl: unable to construct slist", 0);
Failure3: return 1;
curl_global_cleanup();
goto curl_error;
} }
/* /*
If we do not need to check the key-value storage version, If we do not need to check the key-value storage version,
...@@ -1198,15 +1263,12 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1198,15 +1263,12 @@ static int hashicorp_key_management_plugin_init(void *p)
/* /*
Let's construct a URL to check the version of the key-value storage: Let's construct a URL to check the version of the key-value storage:
*/ */
char *mount_url = (char *) malloc(vault_url_len + 11 + 6); char *mount_url = (char *) alloc(vault_url_len + 11 + 6);
if (mount_url == NULL) if (mount_url == NULL)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Memory allocation error", 0); "Memory allocation error", 0);
Failure4: return 1;
curl_slist_free_all(list);
list = NULL;
goto Failure3;
} }
/* /*
The prefix length must be recalculated, as it may have The prefix length must be recalculated, as it may have
...@@ -1222,6 +1284,13 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1222,6 +1284,13 @@ static int hashicorp_key_management_plugin_init(void *p)
"storage mount url: [%s]", "storage mount url: [%s]",
ME_ERROR_LOG_ONLY | ME_NOTE, mount_url); ME_ERROR_LOG_ONLY | ME_NOTE, mount_url);
#endif #endif
int rc = check_version(mount_url);
free(mount_url);
return rc;
}
int HCData::check_version (const char *mount_url) const
{
std::string response_str; std::string response_str;
int rc = curl_run(mount_url, &response_str, false); int rc = curl_run(mount_url, &response_str, false);
if (rc != OPERATION_OK) if (rc != OPERATION_OK)
...@@ -1230,8 +1299,7 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1230,8 +1299,7 @@ static int hashicorp_key_management_plugin_init(void *p)
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Unable to get storage options for \"%s\"", "Unable to get storage options for \"%s\"",
0, mount_url); 0, mount_url);
free(mount_url); return 1;
goto Failure4;
} }
const char *response = response_str.c_str(); const char *response = response_str.c_str();
size_t response_len = response_str.size(); size_t response_len = response_str.size();
...@@ -1243,7 +1311,6 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1243,7 +1311,6 @@ static int hashicorp_key_management_plugin_init(void *p)
{ {
goto storage_error; goto storage_error;
} }
free(mount_url);
const char *js; const char *js;
int js_len; int js_len;
if (json_get_object_key(response, response + response_len, "options", if (json_get_object_key(response, response + response_len, "options",
...@@ -1252,7 +1319,7 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1252,7 +1319,7 @@ static int hashicorp_key_management_plugin_init(void *p)
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Unable to get storage options (http response is: %s)", "Unable to get storage options (http response is: %s)",
0, response); 0, response);
goto Failure4; return 1;
} }
const char *ver; const char *ver;
int ver_len; int ver_len;
...@@ -1263,7 +1330,7 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1263,7 +1330,7 @@ static int hashicorp_key_management_plugin_init(void *p)
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Unable to get storage version (http response is: %s)", "Unable to get storage version (http response is: %s)",
0, response); 0, response);
goto Failure4; return 1;
} }
unsigned long version = strtoul(ver, NULL, 10); unsigned long version = strtoul(ver, NULL, 10);
if (version > UINT_MAX || (version == ULONG_MAX && errno)) if (version > UINT_MAX || (version == ULONG_MAX && errno))
...@@ -1271,44 +1338,32 @@ static int hashicorp_key_management_plugin_init(void *p) ...@@ -1271,44 +1338,32 @@ static int hashicorp_key_management_plugin_init(void *p)
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Integer conversion error (for version number) " "Integer conversion error (for version number) "
"(http response is: %s)", 0, response); "(http response is: %s)", 0, response);
goto Failure4; return 1;
} }
if (version < 2) if (version < 2)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Key-value storage must be version " "Key-value storage must be version "
"number 2 or later", 0); "number 2 or later", 0);
goto Failure4; return 1;
} }
return 0; return 0;
} }
static int hashicorp_key_management_plugin_deinit(void *p) static int hashicorp_key_management_plugin_init(void *p)
{ {
if (list) int rc = data.init();
{ if (rc)
curl_slist_free_all(list);
list = NULL;
}
latest_version_cache.clear();
key_info_cache.clear();
curl_global_cleanup();
if (vault_url_data)
{
free(vault_url_data);
vault_url_data = NULL;
vault_url_len = 0;
}
if (token_header)
{
free(token_header);
token_header = NULL;
}
if (local_token)
{ {
free(local_token); data.deinit();
local_token = NULL;
} }
return rc;
}
static int hashicorp_key_management_plugin_deinit(void *p)
{
data.cache_clean();
data.deinit();
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment