mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 12:56:14 +01:00 
			
		
		
		
	Most things where wrong in the test suite. The one thing that was a bug was that table_map_id was in some places defined as ulong and in other places as ulonglong. On Linux 64 bit this is not a problem as ulong == ulonglong, but on windows this caused failures. Fixed by ensuring that all instances of table_map_id are ulonglong.
		
			
				
	
	
		
			411 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			411 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* Copyright (C) 2014 eperi GmbH.
 | 
						|
   Copyright (C) 2015 MariaDB Corporation
 | 
						|
 | 
						|
 This program is free software; you can redistribute it and/or modify
 | 
						|
 it under the terms of the GNU General Public License as published by
 | 
						|
 the Free Software Foundation; version 2 of the License.
 | 
						|
 | 
						|
 This program is distributed in the hope that it will be useful,
 | 
						|
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 GNU General Public License for more details.
 | 
						|
 | 
						|
 You should have received a copy of the GNU General Public License
 | 
						|
 along with this program; if not, write to the Free Software
 | 
						|
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | 
						|
 | 
						|
/******************************************************************//**
 | 
						|
 @file Parser.cc
 | 
						|
 A class to parse the key file
 | 
						|
 | 
						|
How it works...
 | 
						|
The location and usage can be configured via the configuration file.
 | 
						|
Example
 | 
						|
 | 
						|
[mysqld]
 | 
						|
...
 | 
						|
file_key_management_filename = /home/mdb/keys.enc
 | 
						|
file_key_management_filekey = secret
 | 
						|
...
 | 
						|
 | 
						|
The keys are read from a file.
 | 
						|
The filename is set up via the file_key_management_filename
 | 
						|
configuration value.
 | 
						|
file_key_management_filename is used to configure the absolute
 | 
						|
path to this file.
 | 
						|
 | 
						|
Examples:
 | 
						|
file_key_management_filename = \\\\unc\\keys.enc        (windows share)
 | 
						|
file_key_management_filename = e:/tmp/keys.enc          (windows path)
 | 
						|
file_key_management_filename = /tmp/keys.enc            (linux path)
 | 
						|
 | 
						|
The key file contains AES keys as hex-encoded strings.
 | 
						|
Supported are keys of size 128, 192 or 256 bits.
 | 
						|
Example:
 | 
						|
1;F5502320F8429037B8DAEF761B189D12
 | 
						|
2;770A8A65DA156D24EE2A093277530142770A8A65DA156D24EE2A093277530142
 | 
						|
 | 
						|
1 is the key identifier which can be used for table creation,
 | 
						|
it is followed by a AES key
 | 
						|
 | 
						|
The key file could be encrypted and the key to decrypt the file can
 | 
						|
be given with the optional file_key_management_filekey
 | 
						|
parameter.
 | 
						|
 | 
						|
The file key can also be located if FILE: is prepended to the
 | 
						|
key. Then the following part is interpreted as absolute path to the
 | 
						|
file containing the file key (which must be a text - not binary - string).
 | 
						|
 | 
						|
Example:
 | 
						|
 | 
						|
file_key_management_filekey = FILE:y:/secret256.enc
 | 
						|
 | 
						|
If the key file can not be read at server startup, for example if the
 | 
						|
file key is not present, the plugin will not start
 | 
						|
access to encrypted tables will not be possible.
 | 
						|
 | 
						|
Open SSL command line utility can be used to create an encrypted key file.
 | 
						|
Example:
 | 
						|
openssl enc -aes-256-cbc -md sha1 -k "secret" -in keys.txt -out keys.enc
 | 
						|
***********************************************************************/
 | 
						|
 | 
						|
#include <my_global.h>
 | 
						|
#include "parser.h"
 | 
						|
#include <m_string.h>
 | 
						|
#include <mysys_err.h>
 | 
						|
 | 
						|
#define FILE_PREFIX "FILE:"
 | 
						|
#define MAX_KEY_FILE_SIZE 1024*1024
 | 
						|
#define MAX_SECRET_SIZE 256
 | 
						|
 | 
						|
/*
 | 
						|
  The values below are what one gets after
 | 
						|
  openssl enc -aes-256-cbc -md sha1 -k "secret" -in keys.txt -out keys.enc
 | 
						|
*/
 | 
						|
#define OpenSSL_prefix     "Salted__"
 | 
						|
#define OpenSSL_prefix_len (sizeof(OpenSSL_prefix) - 1)
 | 
						|
#define OpenSSL_salt_len   8
 | 
						|
#define OpenSSL_key_len    32
 | 
						|
#define OpenSSL_iv_len     16
 | 
						|
 | 
						|
/**
 | 
						|
   Calculate key and iv from a given salt and secret as in the
 | 
						|
   openssl command-line tool
 | 
						|
 | 
						|
   @param salt   [in]  the given salt as extracted from the encrypted file
 | 
						|
   @param secret [in]  the given secret as String, provided by the user
 | 
						|
   @param key    [out] 32 Bytes of key are written to this pointer
 | 
						|
   @param iv     [out] 16 Bytes of iv are written to this pointer
 | 
						|
*/
 | 
						|
 | 
						|
void Parser::bytes_to_key(const unsigned char *salt, const char *input,
 | 
						|
                          unsigned char *key, unsigned char *iv)
 | 
						|
{
 | 
						|
  unsigned char digest[MY_SHA1_HASH_SIZE];
 | 
						|
  int key_left   = OpenSSL_key_len;
 | 
						|
  int iv_left    = OpenSSL_iv_len;
 | 
						|
  const size_t ilen= strlen(input);
 | 
						|
  const size_t slen= OpenSSL_salt_len; // either this or explicit (size_t) casts below
 | 
						|
 | 
						|
  my_sha1_multi(digest, input, ilen, salt, slen, NullS);
 | 
						|
 | 
						|
  while (iv_left)
 | 
						|
  {
 | 
						|
    int left= MY_SHA1_HASH_SIZE;
 | 
						|
    if (key_left)
 | 
						|
    {
 | 
						|
      int store = MY_MIN(key_left, MY_SHA1_HASH_SIZE);
 | 
						|
      memcpy(&key[OpenSSL_key_len - key_left], digest, store);
 | 
						|
 | 
						|
      key_left -= store;
 | 
						|
      left     -= store;
 | 
						|
    }
 | 
						|
 | 
						|
    if (iv_left && left)
 | 
						|
    {
 | 
						|
      int store= MY_MIN(iv_left, left);
 | 
						|
      memcpy(&iv[OpenSSL_iv_len - iv_left], &digest[MY_SHA1_HASH_SIZE - left], store);
 | 
						|
 | 
						|
      iv_left    -= store;
 | 
						|
    }
 | 
						|
 | 
						|
    if (iv_left)
 | 
						|
      my_sha1_multi(digest, digest, MY_SHA1_HASH_SIZE,
 | 
						|
                            input, ilen, salt, slen, NullS);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool Parser::parse(std::map<uint,keyentry> *keys)
 | 
						|
{
 | 
						|
  const char *secret= filekey;
 | 
						|
  char buf[MAX_SECRET_SIZE + 1];
 | 
						|
 | 
						|
  //If secret starts with FILE: interpret the secret as a filename.
 | 
						|
  if (strncmp(filekey, FILE_PREFIX,sizeof(FILE_PREFIX) -1) == 0)
 | 
						|
  {
 | 
						|
    if (read_filekey(filekey + sizeof(FILE_PREFIX) - 1, buf))
 | 
						|
      return 1;
 | 
						|
    secret= buf;
 | 
						|
  }
 | 
						|
 | 
						|
  return parse_file(keys, secret);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  secret is limited to MAX_SECRET_SIZE characters
 | 
						|
*/
 | 
						|
 | 
						|
bool Parser::read_filekey(const char *filekey, char *secret)
 | 
						|
{
 | 
						|
  int f= open(filekey, O_RDONLY|O_BINARY);
 | 
						|
  if (f == -1)
 | 
						|
  {
 | 
						|
    my_error(EE_FILENOTFOUND, ME_ERROR_LOG, filekey, errno);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  int len= read(f, secret, MAX_SECRET_SIZE + 1);
 | 
						|
  if (len <= 0)
 | 
						|
  {
 | 
						|
    my_error(EE_READ, ME_ERROR_LOG, filekey, errno);
 | 
						|
    close(f);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  close(f);
 | 
						|
 | 
						|
  while (secret[len - 1] == '\r' || secret[len - 1] == '\n') len--;
 | 
						|
  if (len > MAX_SECRET_SIZE)
 | 
						|
  {
 | 
						|
    my_printf_error(EE_READ,
 | 
						|
                    "Cannot read %s, the filekey is too long, "
 | 
						|
                    "max secret size is %dB ",
 | 
						|
                    ME_ERROR_LOG, filekey, MAX_SECRET_SIZE);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
  secret[len]= '\0';
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
   Get the keys from the key file <filename> and decrypt it with the
 | 
						|
   key <secret>.  Store the keys with id smaller then <maxKeyId> in an
 | 
						|
   array of structs keyentry.
 | 
						|
   
 | 
						|
   @return 0 when ok, 1 for an error
 | 
						|
 */
 | 
						|
 | 
						|
bool Parser::parse_file(std::map<uint,keyentry> *keys, const char *secret)
 | 
						|
{
 | 
						|
  char *buffer= read_and_decrypt_file(secret);
 | 
						|
 | 
						|
  if (!buffer)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  keyentry key;
 | 
						|
  char *line=buffer;
 | 
						|
 | 
						|
  while (*line)
 | 
						|
  {
 | 
						|
    line_number++;
 | 
						|
    switch (parse_line(&line, &key)) {
 | 
						|
    case 1: // comment
 | 
						|
      break;
 | 
						|
    case -1: // error
 | 
						|
      free(buffer);
 | 
						|
      return 1;
 | 
						|
    case 0:
 | 
						|
      (*keys)[key.id] = key;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  free(buffer);
 | 
						|
  if (keys->size() == 0 || (*keys)[1].id == 0)
 | 
						|
  {
 | 
						|
    report_error("System key id 1 is missing", 0);
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
void Parser::report_error(const char *reason, size_t position)
 | 
						|
{
 | 
						|
  my_printf_error(EE_READ, "%s at %s line %u, column %zu",
 | 
						|
    ME_ERROR_LOG, reason, filename, line_number, position + 1);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  return 0 - new key
 | 
						|
         1 - comment
 | 
						|
        -1 - error
 | 
						|
*/
 | 
						|
int Parser::parse_line(char **line_ptr, keyentry *key)
 | 
						|
{
 | 
						|
  int res= 1;
 | 
						|
  char *p= *line_ptr;
 | 
						|
  while (isspace(*p) && *p != '\n') p++;
 | 
						|
  if (*p != '#' && *p != '\n')
 | 
						|
  {
 | 
						|
    if (!isdigit(*p))
 | 
						|
    {
 | 
						|
      report_error("Syntax error", p - *line_ptr);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    longlong id = 0;
 | 
						|
    while (isdigit(*p))
 | 
						|
    {
 | 
						|
      id = id * 10 + *p - '0';
 | 
						|
      if (id > (longlong) UINT_MAX32)
 | 
						|
      {
 | 
						|
        report_error("Invalid key id", p - *line_ptr);
 | 
						|
        return -1;
 | 
						|
      }
 | 
						|
      p++;
 | 
						|
    }
 | 
						|
 | 
						|
    if (id < 1)
 | 
						|
    {
 | 
						|
      report_error("Invalid key id", p - *line_ptr);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (*p != ';')
 | 
						|
    {
 | 
						|
      report_error("Syntax error", p - *line_ptr);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    p++;
 | 
						|
    key->id= (unsigned int)id;
 | 
						|
    key->length=0;
 | 
						|
    while (isxdigit(p[0]) && isxdigit(p[1]) && key->length < sizeof(key->key))
 | 
						|
    {
 | 
						|
      key->key[key->length++] = from_hex(p[0]) * 16 + from_hex(p[1]);
 | 
						|
      p+=2;
 | 
						|
    }
 | 
						|
    if (isxdigit(*p) ||
 | 
						|
        (key->length != 16 && key->length != 24 && key->length != 32))
 | 
						|
    {
 | 
						|
      report_error("Invalid key", p - *line_ptr);
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    res= 0;
 | 
						|
  }
 | 
						|
  while (*p && *p != '\n') p++;
 | 
						|
  *line_ptr= *p == '\n' ? p + 1 : p;
 | 
						|
  return res;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
   Decrypt the key file 'filename' if it is encrypted with the key
 | 
						|
   'secret'.  Store the content of the decrypted file in 'buffer'. The
 | 
						|
   buffer has to be freed in the calling function.
 | 
						|
 */
 | 
						|
#ifdef _WIN32
 | 
						|
#define lseek _lseeki64
 | 
						|
#endif
 | 
						|
 | 
						|
char* Parser::read_and_decrypt_file(const char *secret)
 | 
						|
{
 | 
						|
  int f;
 | 
						|
  if (!filename || !filename[0])
 | 
						|
  {
 | 
						|
    my_printf_error(EE_CANT_OPEN_STREAM, "file-key-management-filename is not set",
 | 
						|
                    ME_ERROR_LOG);
 | 
						|
    goto err0;
 | 
						|
  }
 | 
						|
 | 
						|
  f= open(filename, O_RDONLY|O_BINARY, 0);
 | 
						|
  if (f < 0)
 | 
						|
  {
 | 
						|
    my_error(EE_FILENOTFOUND, ME_ERROR_LOG, filename, errno);
 | 
						|
    goto err0;
 | 
						|
  }
 | 
						|
 | 
						|
  my_off_t file_size;
 | 
						|
  file_size= lseek(f, 0, SEEK_END);
 | 
						|
 | 
						|
  if (file_size == MY_FILEPOS_ERROR || (my_off_t)lseek(f, 0, SEEK_SET) == MY_FILEPOS_ERROR)
 | 
						|
  {
 | 
						|
    my_error(EE_CANT_SEEK, MYF(0), filename, errno);
 | 
						|
    goto err1;
 | 
						|
  }
 | 
						|
 | 
						|
  if (file_size > MAX_KEY_FILE_SIZE)
 | 
						|
  {
 | 
						|
    my_error(EE_READ, MYF(0), filename, EFBIG);
 | 
						|
    goto err1;
 | 
						|
  }
 | 
						|
 | 
						|
  //Read file into buffer
 | 
						|
  uchar *buffer;
 | 
						|
  buffer= (uchar*)malloc((size_t)file_size + 1);
 | 
						|
  if (!buffer)
 | 
						|
  {
 | 
						|
    my_error(EE_OUTOFMEMORY, ME_ERROR_LOG| ME_FATAL, file_size);
 | 
						|
    goto err1;
 | 
						|
  }
 | 
						|
 | 
						|
  if (read(f, buffer, (int)file_size) != (int)file_size)
 | 
						|
  {
 | 
						|
    my_printf_error(EE_READ,
 | 
						|
      "read from %s failed, errno %d",
 | 
						|
      MYF(ME_ERROR_LOG|ME_FATAL), filename, errno);
 | 
						|
    goto err2;
 | 
						|
  }
 | 
						|
 | 
						|
// Check for file encryption
 | 
						|
  uchar *decrypted;
 | 
						|
  if (file_size > OpenSSL_prefix_len && strncmp((char*)buffer, OpenSSL_prefix, OpenSSL_prefix_len) == 0)
 | 
						|
  {
 | 
						|
    uchar key[OpenSSL_key_len];
 | 
						|
    uchar iv[OpenSSL_iv_len];
 | 
						|
 | 
						|
    decrypted= (uchar*)malloc((size_t)file_size);
 | 
						|
    if (!decrypted)
 | 
						|
    {
 | 
						|
      my_error(EE_OUTOFMEMORY, ME_ERROR_LOG | ME_FATAL, file_size);
 | 
						|
      goto err2;
 | 
						|
    }
 | 
						|
    bytes_to_key(buffer + OpenSSL_prefix_len, secret, key, iv);
 | 
						|
    uint32 d_size;
 | 
						|
    if (my_aes_crypt(MY_AES_CBC, ENCRYPTION_FLAG_DECRYPT,
 | 
						|
                     buffer + OpenSSL_prefix_len + OpenSSL_salt_len,
 | 
						|
                     (unsigned int)file_size - OpenSSL_prefix_len - OpenSSL_salt_len,
 | 
						|
                     decrypted, &d_size, key, OpenSSL_key_len,
 | 
						|
                     iv, OpenSSL_iv_len))
 | 
						|
 | 
						|
    {
 | 
						|
      my_printf_error(EE_READ, "Cannot decrypt %s. Wrong key?", ME_ERROR_LOG, filename);
 | 
						|
      goto err3;
 | 
						|
    }
 | 
						|
 | 
						|
    free(buffer);
 | 
						|
    buffer= decrypted;
 | 
						|
    file_size= d_size;
 | 
						|
  }
 | 
						|
  else if (*secret)
 | 
						|
  {
 | 
						|
    my_printf_error(EE_READ, "Cannot decrypt %s. Not encrypted", ME_ERROR_LOG, filename);
 | 
						|
    goto err2;
 | 
						|
  }
 | 
						|
 | 
						|
  buffer[file_size]= '\0';
 | 
						|
  close(f);
 | 
						|
  return (char*) buffer;
 | 
						|
 | 
						|
err3:
 | 
						|
  free(decrypted);
 | 
						|
err2:
 | 
						|
  free(buffer);
 | 
						|
err1:
 | 
						|
  close(f);
 | 
						|
err0:
 | 
						|
  return NULL;
 | 
						|
}
 |