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 
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char * argv[])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:
65   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 
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char * argv[])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 
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_ONLY,
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 */
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 
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;
152   MySQLLogSystem(LogLevel logLevel) :
153     Base(logLevel)
154   {
155   }
156   virtual LogLevel GetLogLevel(void) const override
157   {
158     return (LogLevel)log_level;
159   }
160   virtual ~MySQLLogSystem()
161   {
162   }
163 
164   virtual void Flush(void) override
165   {
166   }
167 
168 protected:
169   virtual void ProcessFormattedStatement(Aws::String&& statement) override
170   {
171 #ifdef _WIN32
172     /*
173       On Windows, we can't use C runtime functions to write to stdout,
174       because we compile with static C runtime, so plugin has a stdout
175       different from server. Thus we're using WriteFile().
176     */
177     DWORD nSize= (DWORD)statement.size();
178     DWORD nWritten;
179     const char *s= statement.c_str();
180     HANDLE h= GetStdHandle(STD_OUTPUT_HANDLE);
181 
182     WriteFile(h, s, nSize, &nWritten, NULL);
183 #else
184     printf("%s", statement.c_str());
185 #endif
186   }
187 };
188 
189 /*  Get list of files in current directory */
190 static vector<string> traverse_current_directory()
191 {
192   vector<string> v;
193 #ifdef _WIN32
194   WIN32_FIND_DATA find_data;
195   HANDLE h= FindFirstFile("*.*", &find_data);
196   if (h == INVALID_HANDLE_VALUE)
197     return v;
198   do
199   {
200     v.push_back(find_data.cFileName);
201   }
202   while (FindNextFile(h, &find_data));
203   FindClose(h);
204 #else
205   DIR *dir = opendir(".");
206   if (!dir)
207     return v;
208   struct dirent *e;
209   while ((e= readdir(dir)))
210     v.push_back(e->d_name);
211   closedir(dir);
212 #endif
213   return v;
214 }
215 
216 Aws::SDKOptions sdkOptions;
217 
218 static int aws_init()
219 {
220 
221 #ifdef HAVE_WOLFSSL
222   sdkOptions.cryptoOptions.initAndCleanupOpenSSL = true;
223 #else
224   /* Server initialized OpenSSL already, thus AWS must skip it */
225   sdkOptions.cryptoOptions.initAndCleanupOpenSSL = false;
226 #endif
227 
228   Aws::InitAPI(sdkOptions);
229   InitializeAWSLogging(Aws::MakeShared<MySQLLogSystem>("aws_key_management_plugin", (Aws::Utils::Logging::LogLevel) log_level));
230 
231   Aws::Client::ClientConfiguration clientConfiguration;
232   if (region && region[0])
233   {
234     clientConfiguration.region = region;
235   }
236   if (request_timeout)
237   {
238      clientConfiguration.requestTimeoutMs= request_timeout;
239      clientConfiguration.connectTimeoutMs= request_timeout;
240   }
241   client = new KMSClient(clientConfiguration);
242   if (!client)
243   {
244     my_printf_error(ER_UNKNOWN_ERROR, "Can't initialize KMS client", ME_ERROR_LOG_ONLY | ME_WARNING);
245     return -1;
246   }
247   return 0;
248 }
249 
250 static int init()
251 {
252 #if WITH_AWS_MOCK
253   if(mock)
254     return 0;
255 #endif
256   return aws_init();
257 }
258 
259 /*
260   Plugin initialization.
261 
262   Create KMS client and scan datadir to find out which keys and versions
263   are present.
264 */
265 static int plugin_init(void *p)
266 {
267   if (init())
268     return -1;
269 
270   vector<string> files= traverse_current_directory();
271   for (size_t i=0; i < files.size(); i++)
272   {
273 
274     KEY_INFO info;
275     if (extract_id_and_version(files[i].c_str(), &info.key_id, &info.key_version) == 0)
276     {
277       key_info_cache[KEY_ID_AND_VERSION(info.key_id, info.key_version)]= info;
278       latest_version_cache[info.key_id]= max(info.key_version, latest_version_cache[info.key_id]);
279     }
280   }
281  return 0;
282 }
283 
284 
285 static void aws_shutdown()
286 {
287   delete client;
288   ShutdownAWSLogging();
289   Aws::ShutdownAPI(sdkOptions);
290 }
291 
292 
293 static void shutdown()
294 {
295 #if WITH_AWS_MOCK
296   if(mock)
297     return;
298 #endif
299   aws_shutdown();
300 }
301 
302 
303 static int plugin_deinit(void *p)
304 {
305   latest_version_cache.clear();
306   key_info_cache.clear();
307   shutdown();
308   return 0;
309 }
310 
311 /*  Generate filename to store the ciphered key */
312 static void format_keyfile_name(char *buf, size_t size, uint key_id, uint version)
313 {
314   snprintf(buf, size, "aws-kms-key.%u.%u", key_id, version);
315 }
316 
317 /* Extract key id and version from file name */
318 static int extract_id_and_version(const char *name, uint *id, uint *ver)
319 {
320   int len;
321   int n= sscanf(name, "aws-kms-key.%u.%u%n", id, ver, &len);
322   if (n == 2 && *id > 0 && *ver > 0 && len == (int)strlen(name))
323     return 0;
324   return 1;
325 }
326 
327 /*
328   Decrypt key stored in aws-kms-key.<id>.<version>
329   Cache the decrypted key.
330 */
331 static int load_key(KEY_INFO *info)
332 {
333   int ret;
334   char path[256];
335 
336   format_keyfile_name(path, sizeof(path), info->key_id, info->key_version);
337   ret= read_and_decrypt_key(path, info);
338   if (ret)
339     info->load_failed= true;
340 
341   latest_version_cache[info->key_id]= max(latest_version_cache[info->key_id], info->key_version);
342   key_info_cache[KEY_ID_AND_VERSION(info->key_id, info->key_version)]= *info;
343 
344   if (!ret)
345   {
346     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: loaded key %u, version %u, key length %u bit", ME_ERROR_LOG_ONLY | ME_NOTE,
347       info->key_id, info->key_version,(uint)info->length*8);
348   }
349   else
350   {
351     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: key %u, version %u could not be decrypted", ME_ERROR_LOG_ONLY | ME_WARNING,
352       info->key_id, info->key_version);
353   }
354   return ret;
355 }
356 
357 
358 /*
359   Get latest version for the key.
360 
361   If key is not decrypted yet, this function also decrypt the key
362   and error will be returned if decryption fails.
363 
364   The reason for that is that Innodb crashes
365    in case errors are returned by get_key(),
366 
367   A new key will be created if it does not exist, provided there is
368   valid master_key_id.
369 */
370 static unsigned int get_latest_key_version(unsigned int key_id)
371 {
372   unsigned int ret;
373   mtx.lock();
374   ret= get_latest_key_version_nolock(key_id);
375   mtx.unlock();
376   return ret;
377 }
378 
379 static unsigned int get_latest_key_version_nolock(unsigned int key_id)
380 {
381   KEY_INFO info;
382   uint ver;
383 
384   ver= latest_version_cache[key_id];
385   if (ver > 0)
386   {
387     info= key_info_cache[KEY_ID_AND_VERSION(key_id, ver)];
388   }
389   if (info.load_failed)
390   {
391     /* Decryption failed previously, don't retry */
392     return(ENCRYPTION_KEY_VERSION_INVALID);
393   }
394   else if (ver > 0)
395   {
396     /* Key exists already, return it*/
397     if (info.length > 0)
398       return(ver);
399   }
400   else // (ver == 0)
401   {
402     /* Generate a new key, version 1 */
403     if (generate_and_save_datakey(key_id, 1) != 0)
404       return(ENCRYPTION_KEY_VERSION_INVALID);
405     info.key_id= key_id;
406     info.key_version= 1;
407     info.length= 0;
408   }
409 
410   if (load_key(&info))
411     return(ENCRYPTION_KEY_VERSION_INVALID);
412   return(info.key_version);
413 }
414 
415 /* Decrypt Byte buffer with AWS. */
416 static int aws_decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output)
417 {
418   DecryptRequest request;
419   request.SetCiphertextBlob(input);
420   DecryptOutcome outcome = client->Decrypt(request);
421   if (!outcome.IsSuccess())
422   {
423     print_kms_error("Decrypt", outcome.GetError());
424     return -1;
425   }
426   *output= outcome.GetResult().GetPlaintext();
427   return 0;
428 }
429 
430 
431 static int decrypt(Aws::Utils::ByteBuffer input, Aws::Utils::ByteBuffer* output)
432 {
433 #if WITH_AWS_MOCK
434   if(mock)
435     return mock_decrypt(input,output);
436 #endif
437   return aws_decrypt(input, output);
438 }
439 
440 /*
441   Decrypt a file with KMS
442 */
443 static  int read_and_decrypt_key(const char *path, KEY_INFO *info)
444 {
445 
446   /* Read file content into memory */
447   ifstream ifs(path, ios::binary | ios::ate);
448   if (!ifs.good())
449   {
450     my_printf_error(ER_UNKNOWN_ERROR, "can't open file %s", ME_ERROR_LOG_ONLY, path);
451     return(-1);
452   }
453   size_t pos = (size_t)ifs.tellg();
454   if (!pos || pos == SIZE_T_MAX)
455   {
456     my_printf_error(ER_UNKNOWN_ERROR, "invalid key file %s", ME_ERROR_LOG_ONLY, path);
457     return(-1);
458   }
459   std::vector<char>  contents(pos);
460   ifs.seekg(0, ios::beg);
461   ifs.read(&contents[0], pos);
462 
463   /* Decrypt data the with AWS */
464 
465   Aws::Utils::ByteBuffer input((unsigned char *)contents.data(), pos);
466   Aws::Utils::ByteBuffer plaintext;
467 
468   if (decrypt(input, &plaintext))
469   {
470       return -1;
471   }
472 
473   size_t len = plaintext.GetLength();
474 
475   if (len > sizeof(info->data))
476   {
477     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: encoding key too large for %s", ME_ERROR_LOG_ONLY, path);
478     return(ENCRYPTION_KEY_BUFFER_TOO_SMALL);
479   }
480   memcpy(info->data, plaintext.GetUnderlyingData(), len);
481   info->length= (unsigned int)len;
482   return(0);
483 }
484 
485 
486 int aws_generate_encrypted_key(Aws::Utils::ByteBuffer *result)
487 {
488   if (!master_key_id[0])
489   {
490     my_printf_error(ER_UNKNOWN_ERROR,
491       "Can't generate encryption key, because 'aws_key_management_master_key_id' parameter is not set",
492       MYF(0));
493     return(-1);
494   }
495   GenerateDataKeyWithoutPlaintextRequest request;
496   request.SetKeyId(master_key_id);
497   request.SetKeySpec(DataKeySpecMapper::GetDataKeySpecForName(key_spec_names[key_spec]));
498 
499   GenerateDataKeyWithoutPlaintextOutcome outcome;
500   outcome= client->GenerateDataKeyWithoutPlaintext(request);
501   if (!outcome.IsSuccess())
502   {
503     print_kms_error("GenerateDataKeyWithoutPlaintext", outcome.GetError());
504     return(-1);
505   }
506  *result =  outcome.GetResult().GetCiphertextBlob();
507  return 0;
508 }
509 
510 
511 static int generate_encrypted_key(Aws::Utils::ByteBuffer *output)
512 {
513 #if WITH_AWS_MOCK
514    if(mock)
515      return mock_generate_encrypted_key(output);
516 #endif
517    return aws_generate_encrypted_key(output);
518 }
519 
520 /* Generate a new datakey and store it a file */
521 static int generate_and_save_datakey(uint keyid, uint version)
522 {
523   Aws::Utils::ByteBuffer byteBuffer;
524 
525   if (generate_encrypted_key(&byteBuffer))
526      return -1;
527 
528   string out;
529   char filename[20];
530   format_keyfile_name(filename, sizeof(filename), keyid, version);
531   int fd= open(filename, O_WRONLY |O_CREAT|O_BINARY, IF_WIN(_S_IREAD, S_IRUSR| S_IRGRP| S_IROTH));
532   if (fd < 0)
533   {
534     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: Can't create file %s", ME_ERROR_LOG_ONLY, filename);
535     return(-1);
536   }
537   unsigned int len= (unsigned int)byteBuffer.GetLength();
538   if (write(fd, byteBuffer.GetUnderlyingData(), len) != len)
539   {
540     my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: can't write to %s", ME_ERROR_LOG_ONLY, filename);
541     close(fd);
542     unlink(filename);
543     return(-1);
544   }
545   close(fd);
546   my_printf_error(ER_UNKNOWN_ERROR, "AWS KMS plugin: generated encrypted datakey for key id=%u, version=%u", ME_ERROR_LOG_ONLY | ME_NOTE,
547     keyid, version);
548   return(0);
549 }
550 
551 /* Key rotation  for a single key */
552 static int rotate_single_key(uint key_id)
553 {
554   uint ver;
555   ver= latest_version_cache[key_id];
556 
557   if (!ver)
558   {
559     my_printf_error(ER_UNKNOWN_ERROR, "key %u does not exist", MYF(ME_WARNING), key_id);
560     return -1;
561   }
562   else if (generate_and_save_datakey(key_id, ver + 1))
563   {
564     my_printf_error(ER_UNKNOWN_ERROR, "Could not generate datakey for key id= %u, ver= %u",
565       MYF(ME_WARNING), key_id, ver);
566     return -1;
567   }
568   else
569   {
570     KEY_INFO info;
571     info.key_id= key_id;
572     info.key_version = ver + 1;
573     if (load_key(&info))
574     {
575       my_printf_error(ER_UNKNOWN_ERROR, "Could not load datakey for key id= %u, ver= %u",
576         MYF(ME_WARNING), key_id, ver);
577       return -1;
578     }
579   }
580   return 0;
581 }
582 
583 /* Key rotation for all key ids */
584 static int rotate_all_keys()
585 {
586   int ret= 0;
587   for (map<uint, uint>::iterator it= latest_version_cache.begin(); it != latest_version_cache.end(); it++)
588   {
589     ret= rotate_single_key(it->first);
590     if (ret)
591       break;
592   }
593   return ret;
594 }
595 
596 static void update_rotate(MYSQL_THD, struct st_mysql_sys_var *, void *, const void *val)
597 {
598   if (!master_key_id[0])
599   {
600     my_printf_error(ER_UNKNOWN_ERROR,
601       "aws_key_management_master_key_id must be set to generate new data keys", MYF(ME_WARNING));
602     return;
603   }
604   mtx.lock();
605   rotate_key= *(int *)val;
606   switch (rotate_key)
607   {
608   case 0:
609     break;
610   case -1:
611     rotate_all_keys();
612     break;
613   default:
614     rotate_single_key(rotate_key);
615     break;
616   }
617   rotate_key= 0;
618   mtx.unlock();
619 }
620 
621 static unsigned int get_key(
622   unsigned int key_id,
623   unsigned int version,
624   unsigned char* dstbuf,
625   unsigned int* buflen)
626 {
627   KEY_INFO info;
628 
629   mtx.lock();
630   info= key_info_cache[KEY_ID_AND_VERSION(key_id, version)];
631   if (info.length == 0 && !info.load_failed)
632   {
633     info.key_id= key_id;
634     info.key_version= version;
635     load_key(&info);
636   }
637   mtx.unlock();
638   if (info.load_failed)
639     return(ENCRYPTION_KEY_VERSION_INVALID);
640   if (*buflen < info.length)
641   {
642     *buflen= info.length;
643     return(ENCRYPTION_KEY_BUFFER_TOO_SMALL);
644   }
645   *buflen= info.length;
646   memcpy(dstbuf, info.data, info.length);
647   return(0);
648 }
649 
650 
651 /* Plugin defs */
652 struct st_mariadb_encryption aws_key_management_plugin= {
653   MariaDB_ENCRYPTION_INTERFACE_VERSION,
654   get_latest_key_version,
655   get_key,
656   // use default encrypt/decrypt functions
657   0, 0, 0, 0, 0
658 };
659 
660 
661 static TYPELIB key_spec_typelib =
662 {
663   array_elements(key_spec_names) - 1, "",
664   key_spec_names, NULL
665 };
666 
667 const char *log_level_names[] =
668 {
669   "Off",
670   "Fatal",
671   "Error",
672   "Warn",
673   "Info",
674   "Debug",
675   "Trace",
676   0
677 };
678 
679 static TYPELIB log_level_typelib =
680 {
681   array_elements(log_level_names) - 1, "",
682   log_level_names, NULL
683 };
684 
685 static MYSQL_SYSVAR_STR(master_key_id, master_key_id,
686   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
687   "Key id for master encryption key. Used to create new datakeys. If not set, no new keys will be created",
688   NULL, NULL, "");
689 
690 static MYSQL_SYSVAR_ENUM(key_spec, key_spec,
691   PLUGIN_VAR_RQCMDARG,
692   "Encryption algorithm used to create new keys.",
693   NULL, NULL, 0, &key_spec_typelib);
694 
695 
696 static MYSQL_SYSVAR_ENUM(log_level, log_level,
697   PLUGIN_VAR_RQCMDARG,
698   "Logging for AWS API",
699   NULL, NULL, 0, &log_level_typelib);
700 
701 
702 static MYSQL_SYSVAR_INT(rotate_key, rotate_key,
703   PLUGIN_VAR_RQCMDARG,
704   "Set this variable to key id to perform rotation of the key. Specify -1 to rotate all keys",
705   NULL, update_rotate, 0, -1, INT_MAX, 1);
706 
707 
708 static MYSQL_SYSVAR_INT(request_timeout, request_timeout,
709   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
710   "Timeout in milliseconds for create HTTPS connection or execute AWS request. Specify 0 to use SDK default.",
711   NULL, NULL, 0, 0, INT_MAX, 1);
712 
713 static MYSQL_SYSVAR_STR(region, region,
714   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
715   "AWS region. For example us-east-1, or eu-central-1. If no value provided, SDK default is used.",
716   NULL, NULL, "");
717 
718 #if WITH_AWS_MOCK
719 static MYSQL_SYSVAR_BOOL(mock, mock,
720   PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
721  "Mock AWS KMS calls (for testing).",
722   NULL,  NULL, 0);
723 #endif
724 
725 static struct st_mysql_sys_var* settings[]= {
726   MYSQL_SYSVAR(master_key_id),
727   MYSQL_SYSVAR(key_spec),
728   MYSQL_SYSVAR(rotate_key),
729   MYSQL_SYSVAR(log_level),
730   MYSQL_SYSVAR(request_timeout),
731   MYSQL_SYSVAR(region),
732 #if WITH_AWS_MOCK
733   MYSQL_SYSVAR(mock),
734 #endif
735   NULL
736 };
737 
738 /*
739   Plugin library descriptor
740 */
741 maria_declare_plugin(aws_key_management)
742 {
743   MariaDB_ENCRYPTION_PLUGIN,
744     &aws_key_management_plugin,
745     "aws_key_management",
746     "MariaDB Corporation",
747     "AWS key management plugin",
748     PLUGIN_LICENSE_GPL,
749     plugin_init,
750     plugin_deinit,
751     0x0100,
752     NULL,
753     settings,
754     "1.0",
755     MariaDB_PLUGIN_MATURITY_STABLE
756 }
757 maria_declare_plugin_end;
758