1 /*
2   Copyright (c) 2016 MariaDB Corporation
3 
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; version 2 of the License.
7 
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   GNU General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License
14   along with this program; if not, write to the Free Software
15   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16 
17 
18 #include <my_global.h>
19 #include <typelib.h>
20 #include <mysql/plugin_encryption.h>
21 #include <my_crypt.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <mysqld_error.h>
26 #include <my_sys.h>
27 #include <map>
28 #include <algorithm>
29 #include <string>
30 #include <vector>
31 #include <iterator>
32 #include <sstream>
33 #include <fstream>
34 
35 #ifndef _WIN32
36 #include <dirent.h>
37 #endif
38 
39 #include <aws/core/Aws.h>
40 #include <aws/core/client/AWSError.h>
41 #include <aws/core/utils/logging/AWSLogging.h>
42 #include <aws/core/utils/logging/ConsoleLogSystem.h>
43 #include <aws/kms/KMSClient.h>
44 #include <aws/kms/model/DecryptRequest.h>
45 #include <aws/kms/model/DecryptResult.h>
46 #include <aws/kms/model/GenerateDataKeyWithoutPlaintextRequest.h>
47 #include <aws/kms/model/GenerateDataKeyWithoutPlaintextResult.h>
48 #include <aws/core/utils/Outcome.h>
49 
50 using namespace std;
51 using namespace Aws::KMS;
52 using namespace Aws::KMS::Model;
53 using namespace Aws::Utils::Logging;
54 
55 
56 /* Plaintext key info struct */
57 struct KEY_INFO
58 {
59   unsigned int  key_id;
60   unsigned int  key_version;
61   unsigned int  length;
62   unsigned char data[MY_AES_MAX_KEY_LENGTH];
63   bool          load_failed; /* if true, do not attempt to reload?*/
64 public:
KEY_INFOKEY_INFO65   KEY_INFO() : key_id(0), key_version(0), length(0), load_failed(false){};
66 };
67 #define KEY_ID_AND_VERSION(key_id,version) ((longlong)key_id << 32 | version)
68 
69 /* Cache for the latest version, per key id */
70 static std::map<uint, uint> latest_version_cache;
71 
72 /* Cache for plaintext keys */
73 static std::map<ulonglong, KEY_INFO>  key_info_cache;
74 
75 static const char *key_spec_names[]={ "AES_128", "AES_256", 0 };
76 
77 /* Plugin variables */
78 static char* master_key_id;
79 static char* region;
80 static unsigned long key_spec;
81 static unsigned long log_level;
82 static int rotate_key;
83 static int request_timeout;
84 
85 #ifndef DBUG_OFF
86 #define WITH_AWS_MOCK 1
87 #else
88 #define WITH_AWS_MOCK 0
89 #endif
90 
91 #if WITH_AWS_MOCK
92 static char mock;
93 #endif
94 
95 
96 /* AWS functionality*/
97 static int read_and_decrypt_key(const char *path, KEY_INFO *info);
98 static int generate_and_save_datakey(uint key_id, uint version);
99 
100 static int extract_id_and_version(const char *name, uint *id, uint *ver);
101 static unsigned int get_latest_key_version(unsigned int key_id);
102 static unsigned int get_latest_key_version_nolock(unsigned int key_id);
103 static int load_key(KEY_INFO *info);
104 static std::mutex mtx;
105 
106 
107 static Aws::KMS::KMSClient *client;
108 
print_kms_error(const char * func,const Aws::Client::AWSError<Aws::KMS::KMSErrors> & err)109 static void print_kms_error(const char *func, const Aws::Client::AWSError<Aws::KMS::KMSErrors>& err)
110 {
111   my_printf_error(ER_UNKNOWN_ERROR,
112     "AWS KMS plugin : KMS Client API '%s' failed : %s - %s",
113     ME_ERROR_LOG,
114     func, err.GetExceptionName().c_str(), err.GetMessage().c_str());
115 }
116 
117 #if WITH_AWS_MOCK
118 /*
119   Mock routines to test plugin without actual AWS KMS interaction
120   we only need to mock 2 functions - generating encrypted key, and decrypt
121 
122   This mock functions do no-op encryption, i.e encrypt and decrypt of
123   a buffer return the buffer itself.
124 */
125 
126 /*
127   Generate random "encrypted" key. We do not encrypt anything in mock mode.
128 */
mock_generate_encrypted_key(Aws::Utils::ByteBuffer * result)129 static int mock_generate_encrypted_key(Aws::Utils::ByteBuffer *result)
130 {
131   size_t len = key_spec == 0?16 : 32;
132   *result = Aws::Utils::ByteBuffer(len);
133   my_random_bytes(result->GetUnderlyingData(), (int)len);
134   return 0;
135 }
136 
137 
mock_decrypt(Aws::Utils::ByteBuffer input,Aws::Utils::ByteBuffer * output)138 static int mock_decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output)
139 {
140   /* We do not encrypt or decrypt in mock mode.*/
141   *output = input;
142   return 0;
143 }
144 #endif
145 
146 /* Redirect AWS trace to error log */
147 class  MySQLLogSystem : public Aws::Utils::Logging::FormattedLogSystem
148 {
149 public:
150 
151   using Base = FormattedLogSystem;
MySQLLogSystem(LogLevel logLevel)152   MySQLLogSystem(LogLevel logLevel) :
153     Base(logLevel)
154   {
155   }
GetLogLevel(void) const156   virtual LogLevel GetLogLevel(void) const override
157   {
158     return (LogLevel)log_level;
159   }
~MySQLLogSystem()160   virtual ~MySQLLogSystem()
161   {
162   }
163 
164 protected:
ProcessFormattedStatement(Aws::String && statement)165   virtual void ProcessFormattedStatement(Aws::String&& statement) override
166   {
167 #ifdef _WIN32
168     /*
169       On Windows, we can't use C runtime functions to write to stdout,
170       because we compile with static C runtime, so plugin has a stdout
171       different from server. Thus we're using WriteFile().
172     */
173     DWORD nSize= (DWORD)statement.size();
174     DWORD nWritten;
175     const char *s= statement.c_str();
176     HANDLE h= GetStdHandle(STD_OUTPUT_HANDLE);
177 
178     WriteFile(h, s, nSize, &nWritten, NULL);
179 #else
180     printf("%s", statement.c_str());
181 #endif
182   }
183 };
184 
185 /*  Get list of files in current directory */
traverse_current_directory()186 static vector<string> traverse_current_directory()
187 {
188   vector<string> v;
189 #ifdef _WIN32
190   WIN32_FIND_DATA find_data;
191   HANDLE h= FindFirstFile("*.*", &find_data);
192   if (h == INVALID_HANDLE_VALUE)
193     return v;
194   do
195   {
196     v.push_back(find_data.cFileName);
197   }
198   while (FindNextFile(h, &find_data));
199   FindClose(h);
200 #else
201   DIR *dir = opendir(".");
202   if (!dir)
203     return v;
204   struct dirent *e;
205   while ((e= readdir(dir)))
206     v.push_back(e->d_name);
207   closedir(dir);
208 #endif
209   return v;
210 }
211 
212 Aws::SDKOptions sdkOptions;
213 
aws_init()214 static int aws_init()
215 {
216 
217 #ifdef HAVE_YASSL
218   sdkOptions.cryptoOptions.initAndCleanupOpenSSL = true;
219 #else
220   /* Server initialized OpenSSL already, thus AWS must skip it */
221   sdkOptions.cryptoOptions.initAndCleanupOpenSSL = false;
222 #endif
223 
224   Aws::InitAPI(sdkOptions);
225   InitializeAWSLogging(Aws::MakeShared<MySQLLogSystem>("aws_key_management_plugin", (Aws::Utils::Logging::LogLevel) log_level));
226 
227   Aws::Client::ClientConfiguration clientConfiguration;
228   if (region && region[0])
229   {
230     clientConfiguration.region = region;
231   }
232   if (request_timeout)
233   {
234      clientConfiguration.requestTimeoutMs= request_timeout;
235      clientConfiguration.connectTimeoutMs= request_timeout;
236   }
237   client = new KMSClient(clientConfiguration);
238   if (!client)
239   {
240     my_printf_error(ER_UNKNOWN_ERROR, "Can not initialize KMS client", ME_ERROR_LOG | ME_WARNING);
241     return -1;
242   }
243   return 0;
244 }
245 
init()246 static int init()
247 {
248 #if WITH_AWS_MOCK
249   if(mock)
250     return 0;
251 #endif
252   return aws_init();
253 }
254 
255 /*
256   Plugin initialization.
257 
258   Create KMS client and scan datadir to find out which keys and versions
259   are present.
260 */
plugin_init(void * p)261 static int plugin_init(void *p)
262 {
263   if (init())
264     return -1;
265 
266   vector<string> files= traverse_current_directory();
267   for (size_t i=0; i < files.size(); i++)
268   {
269 
270     KEY_INFO info;
271     if (extract_id_and_version(files[i].c_str(), &info.key_id, &info.key_version) == 0)
272     {
273       key_info_cache[KEY_ID_AND_VERSION(info.key_id, info.key_version)]= info;
274       latest_version_cache[info.key_id]= max(info.key_version, latest_version_cache[info.key_id]);
275     }
276   }
277  return 0;
278 }
279 
280 
aws_shutdown()281 static void aws_shutdown()
282 {
283   delete client;
284   ShutdownAWSLogging();
285   Aws::ShutdownAPI(sdkOptions);
286 }
287 
288 
shutdown()289 static void shutdown()
290 {
291 #if WITH_AWS_MOCK
292   if(mock)
293     return;
294 #endif
295   aws_shutdown();
296 }
297 
298 
plugin_deinit(void * p)299 static int plugin_deinit(void *p)
300 {
301   latest_version_cache.clear();
302   key_info_cache.clear();
303   shutdown();
304   return 0;
305 }
306 
307 /*  Generate filename to store the ciphered key */
format_keyfile_name(char * buf,size_t size,uint key_id,uint version)308 static void format_keyfile_name(char *buf, size_t size, uint key_id, uint version)
309 {
310   snprintf(buf, size, "aws-kms-key.%u.%u", key_id, version);
311 }
312 
313 /* Extract key id and version from file name */
extract_id_and_version(const char * name,uint * id,uint * ver)314 static int extract_id_and_version(const char *name, uint *id, uint *ver)
315 {
316   int len;
317   int n= sscanf(name, "aws-kms-key.%u.%u%n", id, ver, &len);
318   if (n == 2 && *id > 0 && *ver > 0 && len == (int)strlen(name))
319     return 0;
320   return 1;
321 }
322 
323 /*
324   Decrypt key stored in aws-kms-key.<id>.<version>
325   Cache the decrypted key.
326 */
load_key(KEY_INFO * info)327 static int load_key(KEY_INFO *info)
328 {
329   int ret;
330   char path[256];
331 
332   format_keyfile_name(path, sizeof(path), info->key_id, info->key_version);
333   ret= read_and_decrypt_key(path, info);
334   if (ret)
335     info->load_failed= true;
336 
337   latest_version_cache[info->key_id]= max(latest_version_cache[info->key_id], info->key_version);
338   key_info_cache[KEY_ID_AND_VERSION(info->key_id, info->key_version)]= *info;
339 
340   if (!ret)
341   {
342     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: loaded key %u, version %u, key length %u bit", ME_ERROR_LOG | ME_NOTE,
343       info->key_id, info->key_version,(uint)info->length*8);
344   }
345   else
346   {
347     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: key %u, version %u could not be decrypted", ME_ERROR_LOG | ME_WARNING,
348       info->key_id, info->key_version);
349   }
350   return ret;
351 }
352 
353 
354 /*
355   Get latest version for the key.
356 
357   If key is not decrypted yet, this function also decrypt the key
358   and error will be returned if decryption fails.
359 
360   The reason for that is that Innodb crashes
361    in case errors are returned by get_key(),
362 
363   A new key will be created if it does not exist, provided there is
364   valid master_key_id.
365 */
get_latest_key_version(unsigned int key_id)366 static unsigned int get_latest_key_version(unsigned int key_id)
367 {
368   unsigned int ret;
369   mtx.lock();
370   ret= get_latest_key_version_nolock(key_id);
371   mtx.unlock();
372   return ret;
373 }
374 
get_latest_key_version_nolock(unsigned int key_id)375 static unsigned int get_latest_key_version_nolock(unsigned int key_id)
376 {
377   KEY_INFO info;
378   uint ver;
379 
380   ver= latest_version_cache[key_id];
381   if (ver > 0)
382   {
383     info= key_info_cache[KEY_ID_AND_VERSION(key_id, ver)];
384   }
385   if (info.load_failed)
386   {
387     /* Decryption failed previously, don't retry */
388     return(ENCRYPTION_KEY_VERSION_INVALID);
389   }
390   else if (ver > 0)
391   {
392     /* Key exists already, return it*/
393     if (info.length > 0)
394       return(ver);
395   }
396   else // (ver == 0)
397   {
398     /* Generate a new key, version 1 */
399     if (generate_and_save_datakey(key_id, 1) != 0)
400       return(ENCRYPTION_KEY_VERSION_INVALID);
401     info.key_id= key_id;
402     info.key_version= 1;
403     info.length= 0;
404   }
405 
406   if (load_key(&info))
407     return(ENCRYPTION_KEY_VERSION_INVALID);
408   return(info.key_version);
409 }
410 
411 /* Decrypt Byte buffer with AWS. */
aws_decrypt(Aws::Utils::ByteBuffer input,Aws::Utils::ByteBuffer * output)412 static int aws_decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output)
413 {
414   DecryptRequest request;
415   request.SetCiphertextBlob(input);
416   DecryptOutcome outcome = client->Decrypt(request);
417   if (!outcome.IsSuccess())
418   {
419     print_kms_error("Decrypt", outcome.GetError());
420     return -1;
421   }
422   *output= outcome.GetResult().GetPlaintext();
423   return 0;
424 }
425 
426 
decrypt(Aws::Utils::ByteBuffer input,Aws::Utils::ByteBuffer * output)427 static int decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output)
428 {
429 #if WITH_AWS_MOCK
430   if(mock)
431     return mock_decrypt(input,output);
432 #endif
433   return aws_decrypt(input, output);
434 }
435 
436 /*
437   Decrypt a file with KMS
438 */
read_and_decrypt_key(const char * path,KEY_INFO * info)439 static  int read_and_decrypt_key(const char *path, KEY_INFO *info)
440 {
441 
442   /* Read file content into memory */
443   ifstream ifs(path, ios::binary | ios::ate);
444   if (!ifs.good())
445   {
446     my_printf_error(ER_UNKNOWN_ERROR, "can't open file %s", ME_ERROR_LOG, path);
447     return(-1);
448   }
449   size_t pos = (size_t)ifs.tellg();
450   if (!pos || pos == SIZE_T_MAX)
451   {
452     my_printf_error(ER_UNKNOWN_ERROR, "invalid key file %s", ME_ERROR_LOG, path);
453     return(-1);
454   }
455   std::vector<char>  contents(pos);
456   ifs.seekg(0, ios::beg);
457   ifs.read(&contents[0], pos);
458 
459   /* Decrypt data the with AWS */
460 
461   Aws::Utils::ByteBuffer input((unsigned char *)contents.data(), pos);
462   Aws::Utils::ByteBuffer plaintext;
463 
464   if (decrypt(input, &plaintext))
465   {
466       return -1;
467   }
468 
469   size_t len = plaintext.GetLength();
470 
471   if (len > sizeof(info->data))
472   {
473     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: encoding key too large for %s", ME_ERROR_LOG, path);
474     return(ENCRYPTION_KEY_BUFFER_TOO_SMALL);
475   }
476   memcpy(info->data, plaintext.GetUnderlyingData(), len);
477   info->length= (unsigned int)len;
478   return(0);
479 }
480 
481 
aws_generate_encrypted_key(Aws::Utils::ByteBuffer * result)482 int aws_generate_encrypted_key(Aws::Utils::ByteBuffer *result)
483 {
484   if (!master_key_id[0])
485   {
486     my_printf_error(ER_UNKNOWN_ERROR,
487       "Can't generate encryption key, because 'aws_key_management_master_key_id' parameter is not set",
488       MYF(0));
489     return(-1);
490   }
491   GenerateDataKeyWithoutPlaintextRequest request;
492   request.SetKeyId(master_key_id);
493   request.SetKeySpec(DataKeySpecMapper::GetDataKeySpecForName(key_spec_names[key_spec]));
494 
495   GenerateDataKeyWithoutPlaintextOutcome outcome;
496   outcome= client->GenerateDataKeyWithoutPlaintext(request);
497   if (!outcome.IsSuccess())
498   {
499     print_kms_error("GenerateDataKeyWithoutPlaintext", outcome.GetError());
500     return(-1);
501   }
502  *result =  outcome.GetResult().GetCiphertextBlob();
503  return 0;
504 }
505 
506 
generate_encrypted_key(Aws::Utils::ByteBuffer * output)507 static int generate_encrypted_key(Aws::Utils::ByteBuffer *output)
508 {
509 #if WITH_AWS_MOCK
510    if(mock)
511      return mock_generate_encrypted_key(output);
512 #endif
513    return aws_generate_encrypted_key(output);
514 }
515 
516 /* Generate a new datakey and store it a file */
generate_and_save_datakey(uint keyid,uint version)517 static int generate_and_save_datakey(uint keyid, uint version)
518 {
519   Aws::Utils::ByteBuffer byteBuffer;
520 
521   if (generate_encrypted_key(&byteBuffer))
522      return -1;
523 
524   string out;
525   char filename[20];
526   format_keyfile_name(filename, sizeof(filename), keyid, version);
527   int fd= open(filename, O_WRONLY |O_CREAT|O_BINARY, IF_WIN(_S_IREAD, S_IRUSR| S_IRGRP| S_IROTH));
528   if (fd < 0)
529   {
530     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: Can't create file %s", ME_ERROR_LOG, filename);
531     return(-1);
532   }
533   unsigned int len= (unsigned int)byteBuffer.GetLength();
534   if (write(fd, byteBuffer.GetUnderlyingData(), len) != len)
535   {
536     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: can't write to %s", ME_ERROR_LOG, filename);
537     close(fd);
538     unlink(filename);
539     return(-1);
540   }
541   close(fd);
542   my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: generated encrypted datakey for key id=%u, version=%u", ME_ERROR_LOG | ME_NOTE,
543     keyid, version);
544   return(0);
545 }
546 
547 /* Key rotation  for a single key */
rotate_single_key(uint key_id)548 static int rotate_single_key(uint key_id)
549 {
550   uint ver;
551   ver= latest_version_cache[key_id];
552 
553   if (!ver)
554   {
555     my_printf_error(ER_UNKNOWN_ERROR, "key %u does not exist", MYF(ME_JUST_WARNING), key_id);
556     return -1;
557   }
558   else if (generate_and_save_datakey(key_id, ver + 1))
559   {
560     my_printf_error(ER_UNKNOWN_ERROR, "Could not generate datakey for key id= %u, ver= %u",
561       MYF(ME_JUST_WARNING), key_id, ver);
562     return -1;
563   }
564   else
565   {
566     KEY_INFO info;
567     info.key_id= key_id;
568     info.key_version = ver + 1;
569     if (load_key(&info))
570     {
571       my_printf_error(ER_UNKNOWN_ERROR, "Could not load datakey for key id= %u, ver= %u",
572         MYF(ME_JUST_WARNING), key_id, ver);
573       return -1;
574     }
575   }
576   return 0;
577 }
578 
579 /* Key rotation for all key ids */
rotate_all_keys()580 static int rotate_all_keys()
581 {
582   int ret= 0;
583   for (map<uint, uint>::iterator it= latest_version_cache.begin(); it != latest_version_cache.end(); it++)
584   {
585     ret= rotate_single_key(it->first);
586     if (ret)
587       break;
588   }
589   return ret;
590 }
591 
update_rotate(MYSQL_THD,struct st_mysql_sys_var *,void *,const void * val)592 static void update_rotate(MYSQL_THD, struct st_mysql_sys_var *, void *, const void *val)
593 {
594   if (!master_key_id[0])
595   {
596     my_printf_error(ER_UNKNOWN_ERROR,
597       "aws_key_management_master_key_id must be set to generate new data keys", MYF(ME_JUST_WARNING));
598     return;
599   }
600   mtx.lock();
601   rotate_key= *(int *)val;
602   switch (rotate_key)
603   {
604   case 0:
605     break;
606   case -1:
607     rotate_all_keys();
608     break;
609   default:
610     rotate_single_key(rotate_key);
611     break;
612   }
613   rotate_key= 0;
614   mtx.unlock();
615 }
616 
get_key(unsigned int key_id,unsigned int version,unsigned char * dstbuf,unsigned int * buflen)617 static unsigned int get_key(
618   unsigned int key_id,
619   unsigned int version,
620   unsigned char* dstbuf,
621   unsigned int* buflen)
622 {
623   KEY_INFO info;
624 
625   mtx.lock();
626   info= key_info_cache[KEY_ID_AND_VERSION(key_id, version)];
627   if (info.length == 0 && !info.load_failed)
628   {
629     info.key_id= key_id;
630     info.key_version= version;
631     load_key(&info);
632   }
633   mtx.unlock();
634   if (info.load_failed)
635     return(ENCRYPTION_KEY_VERSION_INVALID);
636   if (*buflen < info.length)
637   {
638     *buflen= info.length;
639     return(ENCRYPTION_KEY_BUFFER_TOO_SMALL);
640   }
641   *buflen= info.length;
642   memcpy(dstbuf, info.data, info.length);
643   return(0);
644 }
645 
646 
647 /* Plugin defs */
648 struct st_mariadb_encryption aws_key_management_plugin= {
649   MariaDB_ENCRYPTION_INTERFACE_VERSION,
650   get_latest_key_version,
651   get_key,
652   // use default encrypt/decrypt functions
653   0, 0, 0, 0, 0
654 };
655 
656 
657 static TYPELIB key_spec_typelib =
658 {
659   array_elements(key_spec_names) - 1, "",
660   key_spec_names, NULL
661 };
662 
663 const char *log_level_names[] =
664 {
665   "Off",
666   "Fatal",
667   "Error",
668   "Warn",
669   "Info",
670   "Debug",
671   "Trace",
672   0
673 };
674 
675 static TYPELIB log_level_typelib =
676 {
677   array_elements(log_level_names) - 1, "",
678   log_level_names, NULL
679 };
680 
681 static MYSQL_SYSVAR_STR(master_key_id, master_key_id,
682   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
683   "Key id for master encryption key. Used to create new datakeys. If not set, no new keys will be created",
684   NULL, NULL, "");
685 
686 static MYSQL_SYSVAR_ENUM(key_spec, key_spec,
687   PLUGIN_VAR_RQCMDARG,
688   "Encryption algorithm used to create new keys.",
689   NULL, NULL, 0, &key_spec_typelib);
690 
691 
692 static MYSQL_SYSVAR_ENUM(log_level, log_level,
693   PLUGIN_VAR_RQCMDARG,
694   "Logging for AWS API",
695   NULL, NULL, 0, &log_level_typelib);
696 
697 
698 static MYSQL_SYSVAR_INT(rotate_key, rotate_key,
699   PLUGIN_VAR_RQCMDARG,
700   "Set this variable to key id to perform rotation of the key. Specify -1 to rotate all keys",
701   NULL, update_rotate, 0, -1, INT_MAX, 1);
702 
703 
704 static MYSQL_SYSVAR_INT(request_timeout, request_timeout,
705   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
706   "Timeout in milliseconds for create HTTPS connection or execute AWS request. Specify 0 to use SDK default.",
707   NULL, NULL, 0, 0, INT_MAX, 1);
708 
709 static MYSQL_SYSVAR_STR(region, region,
710   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
711   "AWS region. For example us-east-1, or eu-central-1. If no value provided, SDK default is used.",
712   NULL, NULL, "");
713 
714 #if WITH_AWS_MOCK
715 static MYSQL_SYSVAR_BOOL(mock, mock,
716   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
717  "Mock AWS KMS calls (for testing).",
718   NULL,  NULL, 0);
719 #endif
720 
721 static struct st_mysql_sys_var* settings[]= {
722   MYSQL_SYSVAR(master_key_id),
723   MYSQL_SYSVAR(key_spec),
724   MYSQL_SYSVAR(rotate_key),
725   MYSQL_SYSVAR(log_level),
726   MYSQL_SYSVAR(request_timeout),
727   MYSQL_SYSVAR(region),
728 #if WITH_AWS_MOCK
729   MYSQL_SYSVAR(mock),
730 #endif
731   NULL
732 };
733 
734 /*
735   Plugin library descriptor
736 */
maria_declare_plugin(aws_key_management)737 maria_declare_plugin(aws_key_management)
738 {
739   MariaDB_ENCRYPTION_PLUGIN,
740     &aws_key_management_plugin,
741     "aws_key_management",
742     "MariaDB Corporation",
743     "AWS key management plugin",
744     PLUGIN_LICENSE_GPL,
745     plugin_init,
746     plugin_deinit,
747     0x0100,
748     NULL,
749     settings,
750     "1.0",
751     MariaDB_PLUGIN_MATURITY_STABLE
752 }
753 maria_declare_plugin_end;
754