completely rewrote file_key_management plugin
numerous issues fixed: * buffer overflows * error conditions aren't checked (crash if file doesn't exist) * accessing random unallocated memory * hard-coded password * arbitrary hard-coded key id limit * incomprehensible error messages (for key_id == 0 it reported "The key could not be initialized", for syntax errors the message was "Wrong match of the keyID, see the template", for a key id larger than hard-coded limit the message was "No asked key", and there was an error "Is comment" for a comment). * tons of small mallocs, many are freed few lines down in the code * malloc(N) and new char[N] are used both, even in the same function * redundant memory copies * pcre - "I can solve it with regular expressions" - with incorrect regexes * parser context stored in a singleton * keys are stored as strings and are strlen-ed and hex2bin-ed on every get_key() request * lots of useless code (e.g. sprintf instead of a pointer assignment, checking of the file length to read a part of it in a fixed buffer, multiplying by sizeof(char) in many places, etc) * this list is not exhaustive
This commit is contained in:
parent
9bda4bc52a
commit
e02749aaf5
@ -1,6 +1,6 @@
|
||||
1;F5502320F8429037B8DAEF761B189D12;770A8A65DA156D24EE2A093277530142
|
||||
2;35B2FF0795FB84BBD666DB8430CA214E;4D92199549E0F2EF009B4160F3582E5528A11A45017F3EF8
|
||||
3;7E892875A52C59A3B588306B13C31FBD;B374A26A71490437AA024E4FADD5B497FDFF1A8EA6FF12F6FB65AF2720B59CCF
|
||||
4;021B0663D4DD7B54E2EBC852677E40BD;18420B5CBA31CCDFFE9716E91EB61374D05914F3ADE23E03
|
||||
5;9BF92CEA026CE732DA80821122A8CE97;966050D7777350B6FD5CCB3E5F648DA45C63BEFB6DEDDFA13443F156B7D35C84
|
||||
6;BC44D4AFD2D9FCD82A679E4DC6700D06;B5EA210C8C09EF20DB95EC584714A89F
|
||||
1;770A8A65DA156D24EE2A093277530142
|
||||
2;4D92199549E0F2EF009B4160F3582E5528A11A45017F3EF8
|
||||
3;B374A26A71490437AA024E4FADD5B497FDFF1A8EA6FF12F6FB65AF2720B59CCF
|
||||
4;18420B5CBA31CCDFFE9716E91EB61374D05914F3ADE23E03
|
||||
5;966050D7777350B6FD5CCB3E5F648DA45C63BEFB6DEDDFA13443F156B7D35C84
|
||||
6;B5EA210C8C09EF20DB95EC584714A89F
|
||||
|
@ -1,6 +1,4 @@
|
||||
SET(FILE_KEY_MANAGEMENT_PLUGIN_SOURCES file_key_management_plugin.cc EncKeys.cc KeySingleton.cc)
|
||||
SET(FILE_KEY_MANAGEMENT_PLUGIN_SOURCES file_key_management_plugin.cc parser.cc)
|
||||
|
||||
IF(NOT SSL_SOURCES)
|
||||
MYSQL_ADD_PLUGIN(FILE_KEY_MANAGEMENT ${FILE_KEY_MANAGEMENT_PLUGIN_SOURCES}
|
||||
LINK_LIBRARIES pcre)
|
||||
ENDIF()
|
||||
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/sql)
|
||||
MYSQL_ADD_PLUGIN(FILE_KEY_MANAGEMENT ${FILE_KEY_MANAGEMENT_PLUGIN_SOURCES})
|
||||
|
@ -1,477 +0,0 @@
|
||||
/* Copyright (C) 2014 eperi GmbH.
|
||||
|
||||
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-1301 USA */
|
||||
|
||||
/******************************************************************//**
|
||||
@file EncKeys.cc
|
||||
A class to keep keys for encryption/decryption.
|
||||
|
||||
How it works...
|
||||
The location and usage can be configured via the configuration file.
|
||||
Example
|
||||
|
||||
[mysqld]
|
||||
...
|
||||
file_key_management_plugin_filename = /home/mdb/keys.enc
|
||||
file_key_management_plugin_filekey = secret
|
||||
file_key_management_plugin_encryption_method = aes_cbc
|
||||
|
||||
...
|
||||
|
||||
Optional configuration value
|
||||
file_key_management_plugin_encryption_method determines the method
|
||||
used for encryption.
|
||||
Supported are aes_cbc, aes_ecb or aes_ctr. aes_cbc is default.
|
||||
The plug-in sets the default aes encryption/decryption method to the given method.
|
||||
|
||||
The keys are read from a file.
|
||||
The filename is set up via the file_key_management_plugin_filename
|
||||
configuration value.
|
||||
file_key_management_plugin_filename is used to configure the absolute
|
||||
path to this file.
|
||||
|
||||
Examples:
|
||||
file_key_management_plugin_filename = \\\\unc\\keys.enc (windows share)
|
||||
file_key_management_plugin_filename = e:/tmp/keys.enc (windows path)
|
||||
file_key_management_plugin_filename = /tmp/keys.enc (linux path)
|
||||
|
||||
The key file contains AES keys and initialization vectors as
|
||||
hex-encoded Strings.
|
||||
Supported are keys of size 128, 192 or 256 bits. IV consists of 16 bytes.
|
||||
Example:
|
||||
1;F5502320F8429037B8DAEF761B189D12;770A8A65DA156D24EE2A093277530142
|
||||
|
||||
1 is the key identifier which can be used for table creation, a 16
|
||||
byte IV follows, and finally a 16 byte AES key.
|
||||
255 entries are supported.
|
||||
|
||||
The key file should be encrypted and the key to decrypt the file can
|
||||
be given with the optional file_key_management_plugin_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. This file can optionally be encrypted,
|
||||
currently with a fix key.
|
||||
|
||||
Example:
|
||||
|
||||
file_key_management_plugin_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, page_encryption feature is not availabe and
|
||||
access to page_encryption tables is not possible.
|
||||
|
||||
Open SSL command line utility can be used to create an encrypted key file.
|
||||
Examples:
|
||||
openssl enc –aes-256-cbc –md sha1 –k secret –in keys.txt –out keys.enc
|
||||
openssl enc –aes-256-cbc –md sha1 –k <initialPwd> –in secret –out secret.enc
|
||||
|
||||
Created 09/15/2014
|
||||
***********************************************************************/
|
||||
|
||||
#ifdef __WIN__
|
||||
#define PCRE_STATIC 1
|
||||
#endif
|
||||
|
||||
#include <my_global.h>
|
||||
#include <sql_class.h> /* For sql_print_error */
|
||||
#include "EncKeys.h"
|
||||
#include <my_aes.h>
|
||||
#include <memory.h>
|
||||
#include <my_sys.h>
|
||||
#include <pcre.h>
|
||||
#include <string.h>
|
||||
#include <my_sys.h>
|
||||
|
||||
const char* EncKeys::strMAGIC= "Salted__";
|
||||
const int EncKeys::magicSize= 8;//strlen(strMAGIC); // 8 byte
|
||||
const char* EncKeys::newLine= "\n";
|
||||
|
||||
const char* EncKeys::errorNoKeyId= "KeyID %u not found or with error. Check the key and the log file.\n";
|
||||
const char* EncKeys::errorInMatches= "Wrong match of the keyID in line %u, see the template.\n";
|
||||
const char* EncKeys::errorExceedKeyFileSize= "The size of the key file %s exceeds "
|
||||
"the maximum allowed of %u bytes.\n";
|
||||
const char* EncKeys::errorExceedKeySize= "The key size exceeds the maximum allowed size of %u in line %u.\n";
|
||||
const char* EncKeys::errorEqualDoubleKey= "More than one identical key with keyID %u found"
|
||||
" in lines %u and %u.\nDelete one of them in the key file.\n";
|
||||
const char* EncKeys::errorUnequalDoubleKey= "More than one not identical key with keyID %u found"
|
||||
" in lines %u and %u.\nChoose the right one and delete the other in the key file.\n"
|
||||
"I'll take the key from line %u\n";
|
||||
#define errorNoInitializedKey "The key could not be initialized.\n"
|
||||
const char* EncKeys::errorNotImplemented= "Initializing keys through key server is not"
|
||||
" yet implemented.\nYou can not read encrypted tables or columns\n\n";
|
||||
const char* EncKeys::errorOpenFile= "Could not open %s for reading. You can not read encrypted tables or columns.\n\n";
|
||||
const char* EncKeys::errorReadingFile= "Could not read from %s. You can not read encrypted tables or columns\n\n";
|
||||
const char* EncKeys::errorFileSize= "Could not get the file size from %s. You can not read encrypted tables or columns\n\n";
|
||||
const char* EncKeys::errorFalseFileKey= "Wrong encryption / decryption key for keyfile '%s'.\n";
|
||||
|
||||
/* read this from a secret source in some later version */
|
||||
const char* EncKeys::initialPwd= "lg28s9ac5ffa537fd8798875c98e190df289da7e047c05";
|
||||
|
||||
EncKeys::EncKeys()
|
||||
{
|
||||
countKeys= keyLineInKeyFile= 0;
|
||||
for (int ii= 0; ii < MAX_KEYS; ii++) {
|
||||
keys[ii].id= 0;
|
||||
keys[ii].iv= keys[ii].key= NULL;
|
||||
}
|
||||
oneKey= NULL;
|
||||
}
|
||||
|
||||
|
||||
EncKeys::~EncKeys()
|
||||
{
|
||||
for (int ii= MAX_KEYS - 1; ii >= 0 ; ii--) {
|
||||
delete[] keys[ii].iv;
|
||||
keys[ii].iv= NULL;
|
||||
delete[] keys[ii].key;
|
||||
keys[ii].key= NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool EncKeys::initKeys(const char *filename, const char *filekey)
|
||||
{
|
||||
if (filename==NULL)
|
||||
return false;
|
||||
|
||||
const char *MAGIC= "FILE:";
|
||||
const short MAGIC_LEN= 5;
|
||||
|
||||
char *secret= (char*) malloc(MAX_SECRET_SIZE +1 * sizeof(char));
|
||||
|
||||
if (filekey != NULL)
|
||||
{
|
||||
//If secret starts with FILE: interpret the secret as filename.
|
||||
if(memcmp(MAGIC, filekey, MAGIC_LEN) == 0)
|
||||
{
|
||||
int fk_len= strlen(filekey);
|
||||
char *secretfile= (char*)malloc((1 + fk_len - MAGIC_LEN)* sizeof(char));
|
||||
memcpy(secretfile, filekey+MAGIC_LEN, fk_len - MAGIC_LEN);
|
||||
secretfile[fk_len-MAGIC_LEN]= '\0';
|
||||
parseSecret(secretfile, secret);
|
||||
free(secretfile);
|
||||
} else
|
||||
{
|
||||
sprintf(secret, "%s", filekey);
|
||||
}
|
||||
}
|
||||
|
||||
int ret= parseFile((const char *)filename, 254, secret);
|
||||
free(secret);
|
||||
return (ret==NO_ERROR_KEY_FILE_PARSE_OK);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
secret is limited to MAX_SECRET_SIZE characters
|
||||
*/
|
||||
|
||||
void EncKeys::parseSecret(const char *secretfile, char *secret)
|
||||
{
|
||||
size_t maxSize= (MAX_SECRET_SIZE +16 + magicSize*2) ;
|
||||
char* buf= (char*)malloc((maxSize) * sizeof(char));
|
||||
char* _initPwd= (char*)malloc((strlen(initialPwd)+1) * sizeof(char));
|
||||
FILE *fp= fopen(secretfile, "rb");
|
||||
fseek(fp, 0L, SEEK_END);
|
||||
long file_size= ftell(fp);
|
||||
rewind(fp);
|
||||
size_t bytes_to_read= ((maxSize >= (size_t) file_size) ? (size_t) file_size :
|
||||
maxSize);
|
||||
bytes_to_read= fread(buf, 1, bytes_to_read, fp);
|
||||
if (memcmp(buf, strMAGIC, magicSize))
|
||||
{
|
||||
bytes_to_read= (bytes_to_read>MAX_SECRET_SIZE) ? MAX_SECRET_SIZE :
|
||||
bytes_to_read;
|
||||
memcpy(secret, buf, bytes_to_read);
|
||||
secret[bytes_to_read]= '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char salt[magicSize];
|
||||
unsigned char *key= new unsigned char[keySize32];
|
||||
unsigned char *iv= new unsigned char[ivSize16];
|
||||
memcpy(&salt, buf + magicSize, magicSize);
|
||||
memcpy(_initPwd, initialPwd, strlen(initialPwd));
|
||||
_initPwd[strlen(initialPwd)]= '\0';
|
||||
my_bytes_to_key((unsigned char *) salt, _initPwd, key, iv);
|
||||
uint32 d_size= 0;
|
||||
my_aes_decrypt_dynamic_type func= get_aes_decrypt_func(MY_AES_ALGORITHM_CBC);
|
||||
int re= (* func)((const uchar*)buf + 2 * magicSize,
|
||||
bytes_to_read - 2 * magicSize,
|
||||
(uchar*)secret, &d_size, (const uchar*)key, keySize32,
|
||||
iv, ivSize16, 0);
|
||||
if (re)
|
||||
d_size= 0;
|
||||
if (d_size>EncKeys::MAX_SECRET_SIZE)
|
||||
{
|
||||
d_size= EncKeys::MAX_SECRET_SIZE;
|
||||
}
|
||||
secret[d_size]= '\0';
|
||||
delete[] key;
|
||||
delete[] iv;
|
||||
}
|
||||
free(buf);
|
||||
free(_initPwd);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a struct keyentry with the asked 'id' or NULL.
|
||||
*/
|
||||
keyentry *EncKeys::getKeys(int id)
|
||||
{
|
||||
if (KEY_MIN <= id && KEY_MAX >= id && (&keys[id - 1])->iv)
|
||||
{
|
||||
return &keys[id - 1];
|
||||
}
|
||||
#ifndef DBUG_OFF
|
||||
else
|
||||
{
|
||||
sql_print_error(errorNoKeyId, id);
|
||||
}
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
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. Returns NO_ERROR_PARSE_OK or an
|
||||
appropriate error code.
|
||||
*/
|
||||
|
||||
int EncKeys::parseFile(const char* filename, const uint32 maxKeyId,
|
||||
const char *secret)
|
||||
{
|
||||
int errorCode= 0;
|
||||
char *buffer= decryptFile(filename, secret, &errorCode);
|
||||
|
||||
if (errorCode != NO_ERROR_PARSE_OK)
|
||||
return errorCode;
|
||||
errorCode= NO_ERROR_KEY_FILE_PARSE_OK;
|
||||
|
||||
char *line= strtok(buffer, newLine);
|
||||
while (NULL != line)
|
||||
{
|
||||
keyLineInKeyFile++;
|
||||
switch (parseLine(line, maxKeyId)) {
|
||||
case NO_ERROR_PARSE_OK:
|
||||
keys[oneKey->id - 1]= *oneKey;
|
||||
delete(oneKey);
|
||||
countKeys++;
|
||||
break;
|
||||
case ERROR_ID_TOO_BIG:
|
||||
sql_print_error(errorExceedKeySize, KEY_MAX,
|
||||
keyLineInKeyFile);
|
||||
sql_print_error(" ---> %s\n", line);
|
||||
errorCode= ERROR_KEY_FILE_EXCEEDS_MAX_NUMBERS_OF_KEYS;
|
||||
break;
|
||||
case ERROR_NOINITIALIZEDKEY:
|
||||
sql_print_error(errorNoInitializedKey);
|
||||
sql_print_error(" ----> %s\n", line);
|
||||
errorCode= ERROR_KEY_FILE_PARSE_NULL;
|
||||
break;
|
||||
case ERROR_WRONG_NUMBER_OF_MATCHES:
|
||||
sql_print_error(errorInMatches, keyLineInKeyFile);
|
||||
sql_print_error(" -----> %s\n", line);
|
||||
errorCode= ERROR_KEY_FILE_PARSE_NULL;
|
||||
break;
|
||||
case NO_ERROR_KEY_GREATER_THAN_ASKED:
|
||||
sql_print_error("No asked key in line %u: %s\n",
|
||||
keyLineInKeyFile, line);
|
||||
break;
|
||||
case NO_ERROR_ISCOMMENT:
|
||||
sql_print_error("Is comment in line %u: %s\n",
|
||||
keyLineInKeyFile, line);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
line= strtok(NULL, newLine);
|
||||
}
|
||||
|
||||
free(line);
|
||||
line= NULL;
|
||||
delete[] buffer;
|
||||
buffer= NULL;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
|
||||
int EncKeys::parseLine(const char *line, const uint32 maxKeyId)
|
||||
{
|
||||
int ret= NO_ERROR_PARSE_OK;
|
||||
if (isComment(line))
|
||||
ret= NO_ERROR_ISCOMMENT;
|
||||
else
|
||||
{
|
||||
const char *error_p= NULL;
|
||||
int offset;
|
||||
pcre *pattern= pcre_compile(
|
||||
"([0-9]+);([0-9,a-f,A-F]{32});([0-9,a-f,A-F]{64}|[0-9,a-f,A-F]{48}|[0-9,a-f,A-F]{32})",
|
||||
0, &error_p, &offset, NULL);
|
||||
if (NULL != error_p)
|
||||
sql_print_error("Error: %s\nOffset: %d\n", error_p, offset);
|
||||
|
||||
int m_len= (int) strlen(line), ovector[MAX_OFFSETS_IN_PCRE_PATTERNS];
|
||||
int rc= pcre_exec(pattern, NULL, line, m_len, 0, 0, ovector,
|
||||
MAX_OFFSETS_IN_PCRE_PATTERNS);
|
||||
pcre_free(pattern);
|
||||
if (4 == rc)
|
||||
{
|
||||
char lin[MAX_KEY_LINE_SIZE + 1];
|
||||
strncpy(lin, line, MAX_KEY_LINE_SIZE);
|
||||
lin[MAX_KEY_LINE_SIZE]= '\0';
|
||||
char *substring_start= lin + ovector[2];
|
||||
int substr_length= ovector[3] - ovector[2];
|
||||
if (3 < substr_length)
|
||||
ret= ERROR_ID_TOO_BIG;
|
||||
else
|
||||
{
|
||||
char buffer[4];
|
||||
sprintf(buffer, "%.*s", substr_length, substring_start);
|
||||
uint32 id= atoi(buffer);
|
||||
if (0 == id) ret= ERROR_NOINITIALIZEDKEY;
|
||||
else if (KEY_MAX < id) ret= ERROR_ID_TOO_BIG;
|
||||
else if (maxKeyId < id) ret= NO_ERROR_KEY_GREATER_THAN_ASKED;
|
||||
else
|
||||
{
|
||||
oneKey= new keyentry;
|
||||
oneKey->id= id;
|
||||
substring_start= lin + ovector[4];
|
||||
substr_length= ovector[5] - ovector[4];
|
||||
oneKey->iv= new char[substr_length + 1];
|
||||
sprintf(oneKey->iv, "%.*s", substr_length, substring_start);
|
||||
substring_start= lin + ovector[6];
|
||||
substr_length= ovector[7] - ovector[6];
|
||||
oneKey->key= new char[substr_length + 1];
|
||||
sprintf(oneKey->key, "%.*s", substr_length, substring_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ret= ERROR_WRONG_NUMBER_OF_MATCHES;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
|
||||
char* EncKeys::decryptFile(const char* filename, const char *secret,
|
||||
int *errorCode)
|
||||
{
|
||||
*errorCode= NO_ERROR_PARSE_OK;
|
||||
FILE *fp= fopen(filename, "rb");
|
||||
if (NULL == fp)
|
||||
{
|
||||
sql_print_error(errorOpenFile, filename);
|
||||
*errorCode= ERROR_OPEN_FILE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0L, SEEK_END))
|
||||
{
|
||||
*errorCode= ERROR_READING_FILE;
|
||||
return NULL;
|
||||
}
|
||||
long file_size= ftell(fp); // get the file size
|
||||
if (MAX_KEY_FILE_SIZE < file_size)
|
||||
{
|
||||
sql_print_error(errorExceedKeyFileSize, filename, MAX_KEY_FILE_SIZE);
|
||||
*errorCode= ERROR_KEY_FILE_TOO_BIG;
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
else if (-1L == file_size)
|
||||
{
|
||||
sql_print_error(errorFileSize, filename);
|
||||
*errorCode= ERROR_READING_FILE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rewind(fp);
|
||||
//Read file into buffer
|
||||
uchar *buffer= new uchar[file_size + 1];
|
||||
file_size= fread(buffer, 1, file_size, fp);
|
||||
buffer[file_size]= '\0';
|
||||
fclose(fp);
|
||||
//Check for file encryption
|
||||
if (0 == memcmp(buffer, strMAGIC, magicSize))
|
||||
{
|
||||
//If file is encrypted, decrypt it first.
|
||||
unsigned char salt[magicSize];
|
||||
unsigned char *key= new unsigned char[keySize32];
|
||||
unsigned char *iv= new unsigned char[ivSize16];
|
||||
uchar *decrypted= new uchar[file_size];
|
||||
memcpy(&salt, buffer + magicSize, magicSize);
|
||||
my_bytes_to_key((unsigned char *) salt, secret, key, iv);
|
||||
uint32 d_size= 0;
|
||||
my_aes_decrypt_dynamic_type func= get_aes_decrypt_func(MY_AES_ALGORITHM_CBC);
|
||||
int res= (* func)((const uchar*)buffer + 2 * magicSize,
|
||||
file_size - 2 * magicSize,
|
||||
decrypted, &d_size, (const uchar*) key, keySize32,
|
||||
iv, ivSize16, 0);
|
||||
if(0 != res)
|
||||
{
|
||||
*errorCode= ERROR_FALSE_FILE_KEY;
|
||||
delete[] buffer; buffer= NULL;
|
||||
sql_print_error(errorFalseFileKey, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(buffer, decrypted, d_size);
|
||||
buffer[d_size]= '\0';
|
||||
}
|
||||
|
||||
delete[] decrypted; decrypted= NULL;
|
||||
delete[] key; key= NULL;
|
||||
delete[] iv; iv= NULL;
|
||||
}
|
||||
return (char*) buffer;
|
||||
}
|
||||
|
||||
bool EncKeys::isComment(const char *line)
|
||||
{
|
||||
const char *error_p;
|
||||
int offset, m_len= (int) strlen(line),
|
||||
ovector[MAX_OFFSETS_IN_PCRE_PATTERNS];
|
||||
pcre *pattern= pcre_compile("\\s*#.*", 0, &error_p, &offset, NULL);
|
||||
int rc= pcre_exec(pattern, NULL, line, m_len, 0, 0, ovector,
|
||||
MAX_OFFSETS_IN_PCRE_PATTERNS);
|
||||
pcre_free(pattern);
|
||||
return (rc >= 0);
|
||||
}
|
||||
|
||||
|
||||
void EncKeys::printKeyEntry(uint32 id)
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
keyentry *entry= getKeys(id);
|
||||
if (NULL == entry)
|
||||
{
|
||||
sql_print_error("No such keyID: %u\n",id);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql_print_error("Key: id: %3u\tiv:%lu bytes\tkey:%lu bytes\n",
|
||||
entry->id, strlen(entry->iv)/2, strlen(entry->key)/2);
|
||||
}
|
||||
#endif /* DBUG_OFF */
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/* Copyright (C) 2014 eperi GmbH.
|
||||
|
||||
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-1301 USA */
|
||||
|
||||
/******************************************************************//**
|
||||
@file EncKeys.h
|
||||
A structure and class to keep keys for encryption/decryption.
|
||||
|
||||
Created 09/15/2014
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef ENCKEYS_H_
|
||||
#define ENCKEYS_H_
|
||||
|
||||
#include <my_global.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
Calculate key and iv from a given salt and secret as it is handled in openssl
|
||||
encrypted files via console
|
||||
|
||||
SYNOPSIS
|
||||
my_bytes_to_key()
|
||||
|
||||
@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 my_bytes_to_key(const uchar *salt,
|
||||
const char *secret, uchar *key,
|
||||
uchar *iv);
|
||||
|
||||
/**
|
||||
Decode Hexencoded String to uint8[].
|
||||
|
||||
SYNOPSIS
|
||||
my_aes_hex2uint()
|
||||
@param iv [in] Pointer to hexadecimal encoded IV String
|
||||
@param dest [out] Pointer to output uint8 array. Memory needs to be
|
||||
allocated by caller
|
||||
@param iv_length [in] Size of destination array.
|
||||
*/
|
||||
|
||||
void my_aes_hex2uint(const char *in, uchar *out, int dest_length);
|
||||
|
||||
struct keyentry {
|
||||
uint32 id;
|
||||
char *iv;
|
||||
char *key;
|
||||
};
|
||||
|
||||
class EncKeys
|
||||
{
|
||||
private:
|
||||
static const char *strMAGIC, *newLine;
|
||||
static const int magicSize;
|
||||
|
||||
enum constants { MAX_OFFSETS_IN_PCRE_PATTERNS = 30};
|
||||
enum keyAttributes { KEY_MIN = 1, KEY_MAX = 255, MAX_KEYS = 255,
|
||||
MAX_IVLEN = 256, MAX_KEYLEN = 512, ivSize16 = 16, keySize32 = 32 };
|
||||
enum keyInitType { KEYINITTYPE_FILE = 1, KEYINITTYPE_SERVER = 2 };
|
||||
enum errorAttributes { MAX_KEY_LINE_SIZE = 3 * MAX_KEYLEN, MAX_KEY_FILE_SIZE = 1048576 };
|
||||
enum errorCodesLine { NO_ERROR_PARSE_OK = 0, NO_ERROR_ISCOMMENT = 10, NO_ERROR_KEY_GREATER_THAN_ASKED = 20,
|
||||
ERROR_NOINITIALIZEDKEY = 30, ERROR_ID_TOO_BIG = 40, ERROR_WRONG_NUMBER_OF_MATCHES = 50,
|
||||
ERROR_EQUAL_DOUBLE_KEY = 60, ERROR_UNEQUAL_DOUBLE_KEY = 70 };
|
||||
|
||||
static const char *errorNoKeyId, *errorInMatches, *errorExceedKeyFileSize,
|
||||
*errorExceedKeySize, *errorEqualDoubleKey, *errorUnequalDoubleKey,
|
||||
*errorNoInitializedKey, *errorFalseFileKey,
|
||||
*errorNotImplemented, *errorOpenFile, *errorReadingFile, *errorFileSize;
|
||||
|
||||
static const char* initialPwd;
|
||||
uint32 countKeys, keyLineInKeyFile;
|
||||
keyentry keys[MAX_KEYS], *oneKey;
|
||||
|
||||
void printKeyEntry( uint32 id);
|
||||
bool isComment( const char *line);
|
||||
char * decryptFile( const char* filename, const char *secret, int *errorCode);
|
||||
int parseFile( const char* filename, const uint32 maxKeyId, const char *secret);
|
||||
int parseLine( const char *line, const uint32 maxKeyId);
|
||||
|
||||
public:
|
||||
static const size_t MAX_SECRET_SIZE = 256;
|
||||
|
||||
enum errorCodesFile { NO_ERROR_KEY_FILE_PARSE_OK = 0, ERROR_KEY_FILE_PARSE_NULL = 110,
|
||||
ERROR_KEY_FILE_TOO_BIG = 120, ERROR_KEY_FILE_EXCEEDS_MAX_NUMBERS_OF_KEYS = 130,
|
||||
ERROR_OPEN_FILE = 140, ERROR_READING_FILE = 150, ERROR_FALSE_FILE_KEY = 160,
|
||||
ERROR_KEYINITTYPE_SERVER_NOT_IMPLEMENTED = 170, ERROR_ENCRYPTION_SECRET_NULL = 180 };
|
||||
EncKeys();
|
||||
virtual ~EncKeys();
|
||||
bool initKeys( const char *filename, const char *filekey);
|
||||
keyentry *getKeys( int id);
|
||||
/* made public for unit testing */
|
||||
static void parseSecret( const char *filename, char *secret );
|
||||
|
||||
};
|
||||
|
||||
#endif /* ENCKEYS_H_ */
|
@ -1,64 +0,0 @@
|
||||
/* Copyright (C) 2014 eperi GmbH.
|
||||
|
||||
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-1301 USA */
|
||||
|
||||
/******************************************************************//**
|
||||
@file KeySingleton.cc
|
||||
Implementation of single pattern to keep keys for encrypting/decrypting pages.
|
||||
|
||||
Created 09/13/2014
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
#include <my_global.h>
|
||||
#include <sql_class.h>
|
||||
#include "KeySingleton.h"
|
||||
|
||||
bool KeySingleton::instanceInited = false;
|
||||
KeySingleton KeySingleton::theInstance;
|
||||
EncKeys KeySingleton::encKeys;
|
||||
|
||||
KeySingleton & KeySingleton::getInstance()
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
if( !instanceInited)
|
||||
{
|
||||
sql_print_error("Encryption / decryption keys were not initialized. "
|
||||
"You can not read encrypted tables or columns\n");
|
||||
}
|
||||
#endif /* DBUG_OFF */
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
KeySingleton & KeySingleton::getInstance(const char *filename,
|
||||
const char *filekey)
|
||||
{
|
||||
if (!instanceInited)
|
||||
{
|
||||
if (!(instanceInited = encKeys.initKeys(filename, filekey)))
|
||||
sql_print_error("Could not initialize any of the encryption / "
|
||||
"decryption keys. You can not read encrypted tables");
|
||||
}
|
||||
return theInstance;
|
||||
}
|
||||
|
||||
keyentry *KeySingleton::getKeys(int id)
|
||||
{
|
||||
return encKeys.getKeys(id);
|
||||
}
|
||||
|
||||
bool KeySingleton::hasKey(int id)
|
||||
{
|
||||
return encKeys.getKeys(id) != NULL;
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/* Copyright (C) 2014 eperi GmbH.
|
||||
|
||||
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-1301 USA */
|
||||
|
||||
/******************************************************************//**
|
||||
@file KeySingletonPattern.h
|
||||
Implementation of single pattern to keep keys for encrypting/decrypting pages.
|
||||
|
||||
Created 09/13/2014
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
#ifndef KEYSINGLETON_H_
|
||||
#define KEYSINGLETON_H_
|
||||
|
||||
#include "EncKeys.h"
|
||||
|
||||
|
||||
class KeySingleton
|
||||
{
|
||||
private:
|
||||
static bool instanceInited;
|
||||
static KeySingleton theInstance;
|
||||
static EncKeys encKeys;
|
||||
|
||||
// No new instance or object possible
|
||||
KeySingleton() {}
|
||||
|
||||
// No new instance possible through copy constructor
|
||||
KeySingleton( const KeySingleton&) {}
|
||||
|
||||
// No new instance possible through copy
|
||||
KeySingleton & operator = (const KeySingleton&);
|
||||
|
||||
public:
|
||||
virtual ~KeySingleton() {encKeys.~EncKeys();}
|
||||
static KeySingleton& getInstance();
|
||||
// Init the instance for only one time
|
||||
static KeySingleton& getInstance(const char *filename, const char *filekey);
|
||||
keyentry *getKeys(int id);
|
||||
bool hasKey(int id);
|
||||
static bool isAvailable() {
|
||||
return instanceInited;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* KEYSINGLETON_H_ */
|
@ -14,27 +14,23 @@
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
|
||||
#include <my_global.h>
|
||||
#include "parser.h"
|
||||
#include <mysql_version.h>
|
||||
#include <mysql/plugin_encryption_key_management.h>
|
||||
#include <my_aes.h>
|
||||
#include "sql_class.h"
|
||||
#include "KeySingleton.h"
|
||||
#include "EncKeys.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Encryption for tables and columns */
|
||||
static char* filename = NULL;
|
||||
static char* filekey = NULL;
|
||||
static char* filename;
|
||||
static char* filekey;
|
||||
|
||||
static MYSQL_SYSVAR_STR(filename, filename,
|
||||
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
|
||||
"Path and name of the key file.",
|
||||
NULL, NULL, NULL);
|
||||
NULL, NULL, "");
|
||||
|
||||
static MYSQL_SYSVAR_STR(filekey, filekey,
|
||||
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
|
||||
"Key to encrypt / decrypt the keyfile.",
|
||||
NULL, NULL, NULL);
|
||||
NULL, NULL, "");
|
||||
|
||||
static struct st_mysql_sys_var* settings[] = {
|
||||
MYSQL_SYSVAR(filename),
|
||||
@ -42,103 +38,24 @@ static struct st_mysql_sys_var* settings[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
Decode Hexencoded String to uint8[].
|
||||
Dynamic_array<keyentry> keys(static_cast<uint>(0));
|
||||
|
||||
SYNOPSIS
|
||||
my_aes_hex2uint()
|
||||
@param iv [in] Pointer to hexadecimal encoded IV String
|
||||
@param dest [out] Pointer to output uint8 array. Memory allocated by caller
|
||||
@param iv_length [in] Size of destination array.
|
||||
*/
|
||||
|
||||
void my_aes_hex2uint(const char* in, unsigned char *out, int dest_length)
|
||||
static keyentry *get_key(unsigned int key_id)
|
||||
{
|
||||
const char *pos= in;
|
||||
int count;
|
||||
for (count = 0; count < dest_length; count++)
|
||||
keyentry *a= keys.front(), *b= keys.back() + 1, *c;
|
||||
while (b - a > 1)
|
||||
{
|
||||
uchar res;
|
||||
sscanf(pos, "%2hhx", &res);
|
||||
out[count] = res;
|
||||
pos += 2 * sizeof(char);
|
||||
c= a + (b - a)/2;
|
||||
if (c->id == key_id)
|
||||
return c;
|
||||
else if (c->id < key_id)
|
||||
a= c;
|
||||
else
|
||||
b= c;
|
||||
}
|
||||
return a->id == key_id ? a : 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Calculate key and iv from a given salt and secret as it is handled
|
||||
in openssl encrypted files via console
|
||||
|
||||
SYNOPSIS
|
||||
my_bytes_to_key()
|
||||
@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 my_bytes_to_key(const unsigned char *salt, const char *secret, unsigned char *key,
|
||||
unsigned char *iv)
|
||||
{
|
||||
#ifdef HAVE_YASSL
|
||||
/* the yassl function has no support for SHA1. Reason unknown. */
|
||||
int keyLen = 32;
|
||||
int ivLen = 16;
|
||||
int EVP_SALT_SZ = 8;
|
||||
const int SHA_LEN = 20;
|
||||
yaSSL::SHA myMD;
|
||||
uint digestSz = myMD.get_digestSize();
|
||||
unsigned char digest[SHA_LEN]; // max size
|
||||
int sz = strlen(secret);
|
||||
int count = 1;
|
||||
int keyLeft = keyLen;
|
||||
int ivLeft = ivLen;
|
||||
int keyOutput = 0;
|
||||
|
||||
while (keyOutput < (keyLen + ivLen))
|
||||
{
|
||||
int digestLeft = digestSz;
|
||||
if (keyOutput) // first time D_0 is empty
|
||||
myMD.update(digest, digestSz);
|
||||
myMD.update((yaSSL::byte* )secret, sz);
|
||||
if (salt)
|
||||
myMD.update(salt, EVP_SALT_SZ);
|
||||
myMD.get_digest(digest);
|
||||
for (int j = 1; j < count; j++)
|
||||
{
|
||||
myMD.update(digest, digestSz);
|
||||
myMD.get_digest(digest);
|
||||
}
|
||||
|
||||
if (keyLeft)
|
||||
{
|
||||
int store = MY_MIN(keyLeft, static_cast<int>(digestSz));
|
||||
memcpy(&key[keyLen - keyLeft], digest, store);
|
||||
|
||||
keyOutput += store;
|
||||
keyLeft -= store;
|
||||
digestLeft -= store;
|
||||
}
|
||||
|
||||
if (ivLeft && digestLeft)
|
||||
{
|
||||
int store = MY_MIN(ivLeft, digestLeft);
|
||||
memcpy(&iv[ivLen - ivLeft], &digest[digestSz - digestLeft], store);
|
||||
|
||||
keyOutput += store;
|
||||
ivLeft -= store;
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_OPENSSL)
|
||||
const EVP_CIPHER *type = EVP_aes_256_cbc();
|
||||
const EVP_MD *digest = EVP_sha1();
|
||||
EVP_BytesToKey(type, digest, salt, (uchar*) secret, strlen(secret), 1, key, iv);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This method is using with the id 0 if exists.
|
||||
This method is used by innobase/xtradb for the key
|
||||
@ -147,91 +64,45 @@ void my_bytes_to_key(const unsigned char *salt, const char *secret, unsigned cha
|
||||
|
||||
static unsigned int get_highest_key_used_in_key_file()
|
||||
{
|
||||
if (KeySingleton::getInstance().hasKey(0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return CRYPT_KEY_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int has_key_from_key_file(unsigned int keyID)
|
||||
static unsigned int has_key_from_key_file(unsigned int key_id)
|
||||
{
|
||||
keyentry* entry = KeySingleton::getInstance().getKeys(keyID);
|
||||
keyentry* entry = get_key(key_id);
|
||||
|
||||
return entry != NULL;
|
||||
}
|
||||
|
||||
static unsigned int get_key_size_from_key_file(unsigned int keyID)
|
||||
static unsigned int get_key_size_from_key_file(unsigned int key_id)
|
||||
{
|
||||
keyentry* entry = KeySingleton::getInstance().getKeys(keyID);
|
||||
keyentry* entry = get_key(key_id);
|
||||
|
||||
if (entry != NULL)
|
||||
{
|
||||
char* keyString = entry->key;
|
||||
size_t key_len = strlen(keyString)/2;
|
||||
|
||||
return key_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CRYPT_KEY_UNKNOWN;
|
||||
}
|
||||
return entry ? entry->length : CRYPT_KEY_UNKNOWN;
|
||||
}
|
||||
|
||||
static int get_key_from_key_file(unsigned int keyID, unsigned char* dstbuf,
|
||||
static int get_key_from_key_file(unsigned int key_id, unsigned char* dstbuf,
|
||||
unsigned buflen)
|
||||
{
|
||||
keyentry* entry = KeySingleton::getInstance().getKeys((int)keyID);
|
||||
keyentry* entry = get_key(key_id);
|
||||
|
||||
if (entry != NULL)
|
||||
{
|
||||
char* keyString = entry->key;
|
||||
size_t key_len = strlen(keyString)/2;
|
||||
|
||||
if (buflen < key_len)
|
||||
{
|
||||
if (buflen < entry->length)
|
||||
return CRYPT_BUFFER_TO_SMALL;
|
||||
}
|
||||
|
||||
my_aes_hex2uint(keyString, (unsigned char*)dstbuf, key_len);
|
||||
memcpy(dstbuf, entry->key, entry->length);
|
||||
|
||||
return CRYPT_KEY_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CRYPT_KEY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static int file_key_management_plugin_init(void *p)
|
||||
{
|
||||
/* init */
|
||||
|
||||
if (current_aes_dynamic_method == MY_AES_ALGORITHM_NONE)
|
||||
{
|
||||
sql_print_error("No encryption method choosen with --encryption-algorithm. "
|
||||
"file_key_management disabled");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (filename == NULL || strcmp("", filename) == 0)
|
||||
{
|
||||
sql_print_error("Parameter file_key_management_filename is required");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
KeySingleton::getInstance(filename, filekey);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int file_key_management_plugin_deinit(void *p)
|
||||
{
|
||||
KeySingleton::getInstance().~KeySingleton();
|
||||
|
||||
return 0;
|
||||
Parser parser(filename, filekey);
|
||||
return parser.parse(&keys);
|
||||
}
|
||||
|
||||
struct st_mariadb_encryption_key_management file_key_management_plugin= {
|
||||
@ -253,12 +124,12 @@ maria_declare_plugin(file_key_management)
|
||||
"Denis Endro eperi GmbH",
|
||||
"File-based key management plugin",
|
||||
PLUGIN_LICENSE_GPL,
|
||||
file_key_management_plugin_init, /* Plugin Init */
|
||||
file_key_management_plugin_deinit, /* Plugin Deinit */
|
||||
file_key_management_plugin_init,
|
||||
NULL,
|
||||
0x0100 /* 1.0 */,
|
||||
NULL, /* status variables */
|
||||
settings,
|
||||
"1.0",
|
||||
MariaDB_PLUGIN_MATURITY_UNKNOWN
|
||||
MariaDB_PLUGIN_MATURITY_ALPHA
|
||||
}
|
||||
maria_declare_plugin_end;
|
||||
|
365
plugin/file_key_management/parser.cc
Normal file
365
plugin/file_key_management/parser.cc
Normal file
@ -0,0 +1,365 @@
|
||||
/* 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-1301 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 "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
|
||||
|
||||
Note, that in openssl this whole function can be reduced to
|
||||
|
||||
#include <openssl/evp.h>
|
||||
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt,
|
||||
secret, strlen(secret), 1, key, iv);
|
||||
|
||||
but alas! we want to support yassl too
|
||||
*/
|
||||
|
||||
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(Dynamic_array<keyentry> *keys)
|
||||
{
|
||||
const char *secret= filekey;
|
||||
char buf[MAX_SECRET_SIZE + 1];
|
||||
|
||||
//If secret starts with FILE: interpret the secret as a filename.
|
||||
if (is_prefix(filekey, FILE_PREFIX))
|
||||
{
|
||||
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= my_open(filekey, O_RDONLY, MYF(MY_WME));
|
||||
if (f == -1)
|
||||
return 1;
|
||||
int len= my_read(f, (uchar*)secret, MAX_SECRET_SIZE, MYF(MY_WME));
|
||||
my_close(f, MYF(MY_WME));
|
||||
if (len <= 0)
|
||||
return 1;
|
||||
secret[len]= '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sort_keys(const keyentry *k1, const keyentry *k2)
|
||||
{
|
||||
return k1->id < k2->id ? -1 : k1->id > k2->id;
|
||||
}
|
||||
|
||||
/**
|
||||
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(Dynamic_array<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
|
||||
my_free(buffer);
|
||||
return 1;
|
||||
case 0:
|
||||
if (keys->push(key))
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
keys->sort(sort_keys);
|
||||
|
||||
my_free(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Parser::report_error(const char *reason, uint position)
|
||||
{
|
||||
my_printf_error(EE_READ, "%s at %s line %u, column %u",
|
||||
MYF(ME_NOREFRESH), 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')
|
||||
{
|
||||
int error;
|
||||
p+= 100; // the number will surely end here (on a non-digit or with an overflow)
|
||||
longlong id= my_strtoll10(p - 100, &p, &error);
|
||||
if (error)
|
||||
{
|
||||
report_error("Syntax error", p - *line_ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (id < 1 || id > UINT_MAX32)
|
||||
{
|
||||
report_error("Invalid key id", p - *line_ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*p != ';')
|
||||
{
|
||||
report_error("Syntax error", p - *line_ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
p++;
|
||||
key->id= 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.
|
||||
*/
|
||||
|
||||
char* Parser::read_and_decrypt_file(const char *secret)
|
||||
{
|
||||
int f= my_open(filename, O_RDONLY, MYF(MY_WME));
|
||||
if (f < 0)
|
||||
goto err0;
|
||||
|
||||
my_off_t file_size;
|
||||
file_size= my_seek(f, 0, SEEK_END, MYF(MY_WME));
|
||||
|
||||
if (file_size == MY_FILEPOS_ERROR)
|
||||
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*)my_malloc(file_size + 1, MYF(MY_WME));
|
||||
if (!buffer)
|
||||
goto err1;
|
||||
|
||||
if (my_pread(f, buffer, file_size, 0, MYF(MY_WME)) != file_size)
|
||||
goto err2;
|
||||
|
||||
// Check for file encryption
|
||||
uchar *decrypted;
|
||||
if (is_prefix((char*)buffer, OpenSSL_prefix))
|
||||
{
|
||||
uchar key[OpenSSL_key_len];
|
||||
uchar iv[OpenSSL_iv_len];
|
||||
|
||||
decrypted= (uchar*)my_malloc(file_size, MYF(MY_WME));
|
||||
if (!decrypted)
|
||||
goto err2;
|
||||
|
||||
bytes_to_key(buffer + OpenSSL_prefix_len, secret, key, iv);
|
||||
uint32 d_size;
|
||||
if (my_aes_decrypt_cbc(buffer + OpenSSL_prefix_len + OpenSSL_salt_len,
|
||||
file_size - OpenSSL_prefix_len - OpenSSL_salt_len,
|
||||
decrypted, &d_size, key, OpenSSL_key_len,
|
||||
iv, OpenSSL_iv_len, 0))
|
||||
|
||||
{
|
||||
my_printf_error(EE_READ, "Cannot decrypt %s. Wrong key?", MYF(ME_NOREFRESH), filename);
|
||||
goto err3;
|
||||
}
|
||||
|
||||
my_free(buffer);
|
||||
buffer= decrypted;
|
||||
file_size= d_size;
|
||||
}
|
||||
else if (*secret)
|
||||
{
|
||||
my_printf_error(EE_READ, "Cannot decrypt %s. Not encrypted", MYF(ME_NOREFRESH), filename);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
buffer[file_size]= '\0';
|
||||
my_close(f, MYF(MY_WME));
|
||||
return (char*) buffer;
|
||||
|
||||
err3:
|
||||
my_free(decrypted);
|
||||
err2:
|
||||
my_free(buffer);
|
||||
err1:
|
||||
my_close(f, MYF(MY_WME));
|
||||
err0:
|
||||
return NULL;
|
||||
}
|
||||
|
54
plugin/file_key_management/parser.h
Normal file
54
plugin/file_key_management/parser.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* Copyright (C) 2014 eperi GmbH.
|
||||
|
||||
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-1301 USA */
|
||||
|
||||
/******************************************************************//**
|
||||
@file Parser.h
|
||||
A structure and class to keep keys for encryption/decryption.
|
||||
|
||||
Created 09/15/2014
|
||||
***********************************************************************/
|
||||
|
||||
#include <my_crypt.h>
|
||||
#include <ctype.h>
|
||||
#include <sql_array.h>
|
||||
|
||||
struct keyentry {
|
||||
unsigned int id;
|
||||
unsigned char key[MY_AES_MAX_KEY_LENGTH];
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
class Parser
|
||||
{
|
||||
const char *filename;
|
||||
const char *filekey;
|
||||
unsigned int line_number;
|
||||
|
||||
unsigned int from_hex(char c)
|
||||
{ return c <= '9' ? c - '0' : tolower(c) - 'a' + 10; }
|
||||
|
||||
void bytes_to_key(const unsigned char *salt, const char *secret,
|
||||
unsigned char *key, unsigned char *iv);
|
||||
bool read_filekey(const char *filekey, char *secret);
|
||||
bool parse_file(Dynamic_array<keyentry> *keys, const char *secret);
|
||||
void report_error(const char *reason, unsigned int position);
|
||||
int parse_line(char **line_ptr, keyentry *key);
|
||||
char* read_and_decrypt_file(const char *secret);
|
||||
|
||||
public:
|
||||
Parser(const char* fn, const char *fk) :
|
||||
filename(fn), filekey(fk), line_number(0) { }
|
||||
bool parse(Dynamic_array<keyentry> *keys);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user