diff options
author | Sergei Golubchik <serg@mariadb.org> | 2015-03-28 13:25:25 +0100 |
---|---|---|
committer | Sergei Golubchik <serg@mariadb.org> | 2015-04-08 10:58:50 +0200 |
commit | e02749aaf5559384ef130e53aaaf02a33e5bcac3 (patch) | |
tree | 8a55d98b53ef46545e54949696c2c6340c4ff5bf /plugin | |
parent | 9bda4bc52a1c36ec61589e9006edc3f8ac5fec0b (diff) | |
download | mariadb-git-e02749aaf5559384ef130e53aaaf02a33e5bcac3.tar.gz |
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
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/file_key_management/CMakeLists.txt | 8 | ||||
-rw-r--r-- | plugin/file_key_management/EncKeys.cc | 477 | ||||
-rw-r--r-- | plugin/file_key_management/EncKeys.h | 112 | ||||
-rw-r--r-- | plugin/file_key_management/KeySingleton.cc | 64 | ||||
-rw-r--r-- | plugin/file_key_management/KeySingleton.h | 58 | ||||
-rw-r--r-- | plugin/file_key_management/file_key_management_plugin.cc | 195 | ||||
-rw-r--r-- | plugin/file_key_management/parser.cc | 365 | ||||
-rw-r--r-- | plugin/file_key_management/parser.h | 54 |
8 files changed, 455 insertions, 878 deletions
diff --git a/plugin/file_key_management/CMakeLists.txt b/plugin/file_key_management/CMakeLists.txt index 15c0357f47b..c2f13fb9f63 100644 --- a/plugin/file_key_management/CMakeLists.txt +++ b/plugin/file_key_management/CMakeLists.txt @@ -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}) diff --git a/plugin/file_key_management/EncKeys.cc b/plugin/file_key_management/EncKeys.cc deleted file mode 100644 index 77e28878832..00000000000 --- a/plugin/file_key_management/EncKeys.cc +++ /dev/null @@ -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 */ -} diff --git a/plugin/file_key_management/EncKeys.h b/plugin/file_key_management/EncKeys.h deleted file mode 100644 index 8d57237f375..00000000000 --- a/plugin/file_key_management/EncKeys.h +++ /dev/null @@ -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_ */ diff --git a/plugin/file_key_management/KeySingleton.cc b/plugin/file_key_management/KeySingleton.cc deleted file mode 100644 index 936a7cf71a9..00000000000 --- a/plugin/file_key_management/KeySingleton.cc +++ /dev/null @@ -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; -} diff --git a/plugin/file_key_management/KeySingleton.h b/plugin/file_key_management/KeySingleton.h deleted file mode 100644 index 37246c1b802..00000000000 --- a/plugin/file_key_management/KeySingleton.h +++ /dev/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_ */ diff --git a/plugin/file_key_management/file_key_management_plugin.cc b/plugin/file_key_management/file_key_management_plugin.cc index 806d50938dd..125f5b6e91d 100644 --- a/plugin/file_key_management/file_key_management_plugin.cc +++ b/plugin/file_key_management/file_key_management_plugin.cc @@ -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[]. - - 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. - */ +Dynamic_array<keyentry> keys(static_cast<uint>(0)); -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; diff --git a/plugin/file_key_management/parser.cc b/plugin/file_key_management/parser.cc new file mode 100644 index 00000000000..1a2d48c2471 --- /dev/null +++ b/plugin/file_key_management/parser.cc @@ -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; +} + diff --git a/plugin/file_key_management/parser.h b/plugin/file_key_management/parser.h new file mode 100644 index 00000000000..c8349db70a0 --- /dev/null +++ b/plugin/file_key_management/parser.h @@ -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); +}; |