summaryrefslogtreecommitdiffstats
path: root/plugin/file_key_management/parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/file_key_management/parser.cc')
-rw-r--r--plugin/file_key_management/parser.cc411
1 files changed, 411 insertions, 0 deletions
diff --git a/plugin/file_key_management/parser.cc b/plugin/file_key_management/parser.cc
new file mode 100644
index 00000000..57e0139a
--- /dev/null
+++ b/plugin/file_key_management/parser.cc
@@ -0,0 +1,411 @@
+/* Copyright (C) 2014 eperi GmbH.
+ Copyright (C) 2015 MariaDB Corporation
+
+ 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-1335 USA */
+
+/******************************************************************//**
+ @file Parser.cc
+ A class to parse the key file
+
+How it works...
+The location and usage can be configured via the configuration file.
+Example
+
+[mysqld]
+...
+file_key_management_filename = /home/mdb/keys.enc
+file_key_management_filekey = secret
+...
+
+The keys are read from a file.
+The filename is set up via the file_key_management_filename
+configuration value.
+file_key_management_filename is used to configure the absolute
+path to this file.
+
+Examples:
+file_key_management_filename = \\\\unc\\keys.enc (windows share)
+file_key_management_filename = e:/tmp/keys.enc (windows path)
+file_key_management_filename = /tmp/keys.enc (linux path)
+
+The key file contains AES keys as hex-encoded strings.
+Supported are keys of size 128, 192 or 256 bits.
+Example:
+1;F5502320F8429037B8DAEF761B189D12
+2;770A8A65DA156D24EE2A093277530142770A8A65DA156D24EE2A093277530142
+
+1 is the key identifier which can be used for table creation,
+it is followed by a AES key
+
+The key file could be encrypted and the key to decrypt the file can
+be given with the optional file_key_management_filekey
+parameter.
+
+The file key can also be located if FILE: is prepended to the
+key. Then the following part is interpreted as absolute path to the
+file containing the file key (which must be a text - not binary - string).
+
+Example:
+
+file_key_management_filekey = FILE:y:/secret256.enc
+
+If the key file can not be read at server startup, for example if the
+file key is not present, the plugin will not start
+access to encrypted tables will not be possible.
+
+Open SSL command line utility can be used to create an encrypted key file.
+Example:
+openssl enc -aes-256-cbc -md sha1 -k "secret" -in keys.txt -out keys.enc
+***********************************************************************/
+
+#include <my_global.h>
+#include "parser.h"
+#include <m_string.h>
+#include <mysys_err.h>
+
+#define FILE_PREFIX "FILE:"
+#define MAX_KEY_FILE_SIZE 1024*1024
+#define MAX_SECRET_SIZE 256
+
+/*
+ The values below are what one gets after
+ openssl enc -aes-256-cbc -md sha1 -k "secret" -in keys.txt -out keys.enc
+*/
+#define OpenSSL_prefix "Salted__"
+#define OpenSSL_prefix_len (sizeof(OpenSSL_prefix) - 1)
+#define OpenSSL_salt_len 8
+#define OpenSSL_key_len 32
+#define OpenSSL_iv_len 16
+
+/**
+ Calculate key and iv from a given salt and secret as in the
+ openssl command-line tool
+
+ @param salt [in] the given salt as extracted from the encrypted file
+ @param secret [in] the given secret as String, provided by the user
+ @param key [out] 32 Bytes of key are written to this pointer
+ @param iv [out] 16 Bytes of iv are written to this pointer
+*/
+
+void Parser::bytes_to_key(const unsigned char *salt, const char *input,
+ unsigned char *key, unsigned char *iv)
+{
+ unsigned char digest[MY_SHA1_HASH_SIZE];
+ int key_left = OpenSSL_key_len;
+ int iv_left = OpenSSL_iv_len;
+ const size_t ilen= strlen(input);
+ const size_t slen= OpenSSL_salt_len; // either this or explicit (size_t) casts below
+
+ my_sha1_multi(digest, input, ilen, salt, slen, NullS);
+
+ while (iv_left)
+ {
+ int left= MY_SHA1_HASH_SIZE;
+ if (key_left)
+ {
+ int store = MY_MIN(key_left, MY_SHA1_HASH_SIZE);
+ memcpy(&key[OpenSSL_key_len - key_left], digest, store);
+
+ key_left -= store;
+ left -= store;
+ }
+
+ if (iv_left && left)
+ {
+ int store= MY_MIN(iv_left, left);
+ memcpy(&iv[OpenSSL_iv_len - iv_left], &digest[MY_SHA1_HASH_SIZE - left], store);
+
+ iv_left -= store;
+ }
+
+ if (iv_left)
+ my_sha1_multi(digest, digest, MY_SHA1_HASH_SIZE,
+ input, ilen, salt, slen, NullS);
+ }
+}
+
+
+bool Parser::parse(std::map<uint,keyentry> *keys)
+{
+ const char *secret= filekey;
+ char buf[MAX_SECRET_SIZE + 1];
+
+ //If secret starts with FILE: interpret the secret as a filename.
+ if (strncmp(filekey, FILE_PREFIX,sizeof(FILE_PREFIX) -1) == 0)
+ {
+ if (read_filekey(filekey + sizeof(FILE_PREFIX) - 1, buf))
+ return 1;
+ secret= buf;
+ }
+
+ return parse_file(keys, secret);
+}
+
+
+/*
+ secret is limited to MAX_SECRET_SIZE characters
+*/
+
+bool Parser::read_filekey(const char *filekey, char *secret)
+{
+ int f= open(filekey, O_RDONLY|O_BINARY);
+ if (f == -1)
+ {
+ my_error(EE_FILENOTFOUND, ME_ERROR_LOG, filekey, errno);
+ return 1;
+ }
+
+ int len= read(f, secret, MAX_SECRET_SIZE + 1);
+ if (len <= 0)
+ {
+ my_error(EE_READ, ME_ERROR_LOG, filekey, errno);
+ close(f);
+ return 1;
+ }
+ close(f);
+
+ while (secret[len - 1] == '\r' || secret[len - 1] == '\n') len--;
+ if (len > MAX_SECRET_SIZE)
+ {
+ my_printf_error(EE_READ,
+ "Cannot read %s, the filekey is too long, "
+ "max secret size is %dB ",
+ ME_ERROR_LOG, filekey, MAX_SECRET_SIZE);
+ return 1;
+ }
+ secret[len]= '\0';
+ return 0;
+}
+
+
+/**
+ Get the keys from the key file <filename> and decrypt it with the
+ key <secret>. Store the keys with id smaller then <maxKeyId> in an
+ array of structs keyentry.
+
+ @return 0 when ok, 1 for an error
+ */
+
+bool Parser::parse_file(std::map<uint,keyentry> *keys, const char *secret)
+{
+ char *buffer= read_and_decrypt_file(secret);
+
+ if (!buffer)
+ return 1;
+
+ keyentry key;
+ char *line=buffer;
+
+ while (*line)
+ {
+ line_number++;
+ switch (parse_line(&line, &key)) {
+ case 1: // comment
+ break;
+ case -1: // error
+ free(buffer);
+ return 1;
+ case 0:
+ (*keys)[key.id] = key;
+ break;
+ }
+ }
+
+ free(buffer);
+ if (keys->size() == 0 || (*keys)[1].id == 0)
+ {
+ report_error("System key id 1 is missing", 0);
+ return 1;
+ }
+
+ return 0;
+}
+
+void Parser::report_error(const char *reason, size_t position)
+{
+ my_printf_error(EE_READ, "%s at %s line %u, column %zu",
+ ME_ERROR_LOG, reason, filename, line_number, position + 1);
+}
+
+/*
+ return 0 - new key
+ 1 - comment
+ -1 - error
+*/
+int Parser::parse_line(char **line_ptr, keyentry *key)
+{
+ int res= 1;
+ char *p= *line_ptr;
+ while (isspace(*p) && *p != '\n') p++;
+ if (*p != '#' && *p != '\n')
+ {
+ if (!isdigit(*p))
+ {
+ report_error("Syntax error", p - *line_ptr);
+ return -1;
+ }
+
+ longlong id = 0;
+ while (isdigit(*p))
+ {
+ id = id * 10 + *p - '0';
+ if (id > UINT_MAX32)
+ {
+ report_error("Invalid key id", p - *line_ptr);
+ return -1;
+ }
+ p++;
+ }
+
+ if (id < 1)
+ {
+ report_error("Invalid key id", p - *line_ptr);
+ return -1;
+ }
+
+ if (*p != ';')
+ {
+ report_error("Syntax error", p - *line_ptr);
+ return -1;
+ }
+
+ p++;
+ key->id= (unsigned int)id;
+ key->length=0;
+ while (isxdigit(p[0]) && isxdigit(p[1]) && key->length < sizeof(key->key))
+ {
+ key->key[key->length++] = from_hex(p[0]) * 16 + from_hex(p[1]);
+ p+=2;
+ }
+ if (isxdigit(*p) ||
+ (key->length != 16 && key->length != 24 && key->length != 32))
+ {
+ report_error("Invalid key", p - *line_ptr);
+ return -1;
+ }
+
+ res= 0;
+ }
+ while (*p && *p != '\n') p++;
+ *line_ptr= *p == '\n' ? p + 1 : p;
+ return res;
+}
+
+/**
+ Decrypt the key file 'filename' if it is encrypted with the key
+ 'secret'. Store the content of the decrypted file in 'buffer'. The
+ buffer has to be freed in the calling function.
+ */
+#ifdef _WIN32
+#define lseek _lseeki64
+#endif
+
+char* Parser::read_and_decrypt_file(const char *secret)
+{
+ int f;
+ if (!filename || !filename[0])
+ {
+ my_printf_error(EE_CANT_OPEN_STREAM, "file-key-management-filename is not set",
+ ME_ERROR_LOG);
+ goto err0;
+ }
+
+ f= open(filename, O_RDONLY|O_BINARY, 0);
+ if (f < 0)
+ {
+ my_error(EE_FILENOTFOUND, ME_ERROR_LOG, filename, errno);
+ goto err0;
+ }
+
+ my_off_t file_size;
+ file_size= lseek(f, 0, SEEK_END);
+
+ if (file_size == MY_FILEPOS_ERROR || (my_off_t)lseek(f, 0, SEEK_SET) == MY_FILEPOS_ERROR)
+ {
+ my_error(EE_CANT_SEEK, MYF(0), filename, errno);
+ goto err1;
+ }
+
+ if (file_size > MAX_KEY_FILE_SIZE)
+ {
+ my_error(EE_READ, MYF(0), filename, EFBIG);
+ goto err1;
+ }
+
+ //Read file into buffer
+ uchar *buffer;
+ buffer= (uchar*)malloc((size_t)file_size + 1);
+ if (!buffer)
+ {
+ my_error(EE_OUTOFMEMORY, ME_ERROR_LOG| ME_FATAL, file_size);
+ goto err1;
+ }
+
+ if (read(f, buffer, (int)file_size) != (int)file_size)
+ {
+ my_printf_error(EE_READ,
+ "read from %s failed, errno %d",
+ MYF(ME_ERROR_LOG|ME_FATAL), filename, errno);
+ goto err2;
+ }
+
+// Check for file encryption
+ uchar *decrypted;
+ if (file_size > OpenSSL_prefix_len && strncmp((char*)buffer, OpenSSL_prefix, OpenSSL_prefix_len) == 0)
+ {
+ uchar key[OpenSSL_key_len];
+ uchar iv[OpenSSL_iv_len];
+
+ decrypted= (uchar*)malloc((size_t)file_size);
+ if (!decrypted)
+ {
+ my_error(EE_OUTOFMEMORY, ME_ERROR_LOG | ME_FATAL, file_size);
+ goto err2;
+ }
+ bytes_to_key(buffer + OpenSSL_prefix_len, secret, key, iv);
+ uint32 d_size;
+ if (my_aes_crypt(MY_AES_CBC, ENCRYPTION_FLAG_DECRYPT,
+ buffer + OpenSSL_prefix_len + OpenSSL_salt_len,
+ (unsigned int)file_size - OpenSSL_prefix_len - OpenSSL_salt_len,
+ decrypted, &d_size, key, OpenSSL_key_len,
+ iv, OpenSSL_iv_len))
+
+ {
+ my_printf_error(EE_READ, "Cannot decrypt %s. Wrong key?", ME_ERROR_LOG, filename);
+ goto err3;
+ }
+
+ free(buffer);
+ buffer= decrypted;
+ file_size= d_size;
+ }
+ else if (*secret)
+ {
+ my_printf_error(EE_READ, "Cannot decrypt %s. Not encrypted", ME_ERROR_LOG, filename);
+ goto err2;
+ }
+
+ buffer[file_size]= '\0';
+ close(f);
+ return (char*) buffer;
+
+err3:
+ free(decrypted);
+err2:
+ free(buffer);
+err1:
+ close(f);
+err0:
+ return NULL;
+}