mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-03 20:36:16 +01:00 
			
		
		
		
	my_b_encr_write(): Initialize also block_length, and at the same time last_block_length, so that all 128 bits can be initialized with fewer writes. This fixes an error that was caught in the test encryption.tempfiles_encrypted. test_my_safe_print_str(): Skip a test that would attempt to display uninitialized data in the test unit.stacktrace. Previously, our CI did not build unit tests with MemorySanitizer. handle_delayed_insert(): Remove a redundant call to pthread_exit(0), which would for some reason cause MemorySanitizer in clang-19 to report a stack overflow in a RelWithDebInfo build. This fixes a failure of several tests. Reviewed by: Vladislav Vaintroub
		
			
				
	
	
		
			275 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
   Copyright (c) 2015, 2020, MariaDB
 | 
						|
 | 
						|
   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 */
 | 
						|
 | 
						|
/*************************************************************************
 | 
						|
  Limitation of encrypted IO_CACHEs
 | 
						|
  1. Designed to support temporary files only (open_cached_file, fd=-1)
 | 
						|
  2. Created with WRITE_CACHE, later can be reinit_io_cache'ed to
 | 
						|
     READ_CACHE and WRITE_CACHE in any order arbitrary number of times.
 | 
						|
  3. no seeks for writes, but reinit_io_cache(WRITE_CACHE, seek_offset)
 | 
						|
     is allowed (there's a special hack in reinit_io_cache() for that)
 | 
						|
*/
 | 
						|
 | 
						|
#include "../mysys/mysys_priv.h"
 | 
						|
#include "log.h"
 | 
						|
#include "mysqld.h"
 | 
						|
#include "sql_class.h"
 | 
						|
 | 
						|
static uint keyid, keyver;
 | 
						|
 | 
						|
#define set_iv(IV, N1, N2)                                              \
 | 
						|
  do {                                                                  \
 | 
						|
    compile_time_assert(sizeof(IV) >= sizeof(N1) + sizeof(N2));         \
 | 
						|
    memcpy(IV, &(N1), sizeof(N1));                                      \
 | 
						|
    memcpy(IV + sizeof(N1), &(N2), sizeof(N2));                         \
 | 
						|
  } while(0)
 | 
						|
 | 
						|
static int my_b_encr_read(IO_CACHE *info, uchar *Buffer, size_t Count)
 | 
						|
{
 | 
						|
  my_off_t pos_in_file= info->pos_in_file + (info->read_end - info->buffer);
 | 
						|
  my_off_t old_pos_in_file= pos_in_file, pos_offset= 0;
 | 
						|
  IO_CACHE_CRYPT *crypt_data=
 | 
						|
    (IO_CACHE_CRYPT *)(info->buffer + info->buffer_length + MY_AES_BLOCK_SIZE);
 | 
						|
  uchar *wbuffer= (uchar*)&(crypt_data->inbuf_counter);
 | 
						|
  uchar *ebuffer= (uchar*)(crypt_data + 1);
 | 
						|
  DBUG_ENTER("my_b_encr_read");
 | 
						|
 | 
						|
  if (pos_in_file == info->end_of_file)
 | 
						|
  {
 | 
						|
    /*  reading past EOF should not empty the cache */
 | 
						|
    info->read_pos= info->read_end;
 | 
						|
    info->error= 0;
 | 
						|
    DBUG_RETURN(MY_TEST(Count));
 | 
						|
  }
 | 
						|
 | 
						|
  if (info->seek_not_done)
 | 
						|
  {
 | 
						|
    my_off_t wpos;
 | 
						|
 | 
						|
    pos_offset= pos_in_file % info->buffer_length;
 | 
						|
    pos_in_file-= pos_offset;
 | 
						|
 | 
						|
    wpos= pos_in_file / info->buffer_length * crypt_data->block_length;
 | 
						|
 | 
						|
    if ((mysql_file_seek(info->file, wpos, MY_SEEK_SET, MYF(0))
 | 
						|
        == MY_FILEPOS_ERROR))
 | 
						|
    {
 | 
						|
      info->error= -1;
 | 
						|
      DBUG_RETURN(1);
 | 
						|
    }
 | 
						|
    info->seek_not_done= 0;
 | 
						|
    if (info->next_file_user)
 | 
						|
    {
 | 
						|
      IO_CACHE *c;
 | 
						|
      for (c= info->next_file_user;
 | 
						|
           c!= info;
 | 
						|
           c= c->next_file_user)
 | 
						|
      {
 | 
						|
        c->seek_not_done= 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  do
 | 
						|
  {
 | 
						|
    uint elength, wlength, length;
 | 
						|
    uchar iv[MY_AES_BLOCK_SIZE]= {0};
 | 
						|
 | 
						|
    DBUG_ASSERT(pos_in_file % info->buffer_length == 0);
 | 
						|
 | 
						|
    if (info->end_of_file - pos_in_file >= info->buffer_length)
 | 
						|
      wlength= crypt_data->block_length;
 | 
						|
    else
 | 
						|
      wlength= crypt_data->last_block_length;
 | 
						|
 | 
						|
    if (mysql_file_read(info->file, wbuffer, wlength, info->myflags | MY_NABP))
 | 
						|
    {
 | 
						|
      info->error= -1;
 | 
						|
      DBUG_RETURN(1);
 | 
						|
    }
 | 
						|
 | 
						|
    elength= wlength - (uint)(ebuffer - wbuffer);
 | 
						|
    set_iv(iv, pos_in_file, crypt_data->inbuf_counter);
 | 
						|
 | 
						|
    if (encryption_crypt(ebuffer, elength, info->buffer, &length,
 | 
						|
                         crypt_data->key, sizeof(crypt_data->key),
 | 
						|
                         iv, sizeof(iv), ENCRYPTION_FLAG_DECRYPT,
 | 
						|
                         keyid, keyver))
 | 
						|
    {
 | 
						|
      my_errno= 1;
 | 
						|
      DBUG_RETURN(info->error= -1);
 | 
						|
    }
 | 
						|
 | 
						|
    DBUG_ASSERT(length <= info->buffer_length);
 | 
						|
 | 
						|
    size_t copied= MY_MIN(Count, (size_t)(length - pos_offset));
 | 
						|
    if (copied)
 | 
						|
    {
 | 
						|
      memcpy(Buffer, info->buffer + pos_offset, copied);
 | 
						|
      Count-= copied;
 | 
						|
      Buffer+= copied;
 | 
						|
    }
 | 
						|
 | 
						|
    info->read_pos= info->buffer + pos_offset + copied;
 | 
						|
    info->read_end= info->buffer + length;
 | 
						|
    info->pos_in_file= pos_in_file;
 | 
						|
    pos_in_file+= length;
 | 
						|
    pos_offset= 0;
 | 
						|
 | 
						|
    if (wlength < crypt_data->block_length && pos_in_file < info->end_of_file)
 | 
						|
    {
 | 
						|
      info->error= (int)(pos_in_file - old_pos_in_file);
 | 
						|
      DBUG_RETURN(1);
 | 
						|
    }
 | 
						|
  } while (Count);
 | 
						|
 | 
						|
  DBUG_RETURN(0);
 | 
						|
}
 | 
						|
 | 
						|
static int my_b_encr_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
 | 
						|
{
 | 
						|
  IO_CACHE_CRYPT *crypt_data=
 | 
						|
    (IO_CACHE_CRYPT *)(info->buffer + info->buffer_length + MY_AES_BLOCK_SIZE);
 | 
						|
  uchar *wbuffer= (uchar*)&(crypt_data->inbuf_counter);
 | 
						|
  uchar *ebuffer= (uchar*)(crypt_data + 1);
 | 
						|
  DBUG_ENTER("my_b_encr_write");
 | 
						|
 | 
						|
  if (Buffer != info->write_buffer)
 | 
						|
  {
 | 
						|
    Count-= Count % info->buffer_length;
 | 
						|
    if (!Count)
 | 
						|
      DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (info->seek_not_done)
 | 
						|
  {
 | 
						|
    DBUG_ASSERT(info->pos_in_file % info->buffer_length == 0);
 | 
						|
    my_off_t wpos= info->pos_in_file / info->buffer_length * crypt_data->block_length;
 | 
						|
 | 
						|
    if ((mysql_file_seek(info->file, wpos, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR))
 | 
						|
    {
 | 
						|
      info->error= -1;
 | 
						|
      DBUG_RETURN(1);
 | 
						|
    }
 | 
						|
    info->seek_not_done= 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (info->pos_in_file == 0)
 | 
						|
  {
 | 
						|
    if (my_random_bytes(crypt_data->key, sizeof(crypt_data->key)))
 | 
						|
    {
 | 
						|
      my_errno= 1;
 | 
						|
      DBUG_RETURN(info->error= -1);
 | 
						|
    }
 | 
						|
    crypt_data->counter= 0;
 | 
						|
    crypt_data->block_length= 0;
 | 
						|
    crypt_data->last_block_length= 0;
 | 
						|
  }
 | 
						|
 | 
						|
  do
 | 
						|
  {
 | 
						|
    size_t length= MY_MIN(info->buffer_length, Count);
 | 
						|
    uint elength, wlength;
 | 
						|
    uchar iv[MY_AES_BLOCK_SIZE]= {0};
 | 
						|
 | 
						|
    crypt_data->inbuf_counter= crypt_data->counter;
 | 
						|
    set_iv(iv, info->pos_in_file, crypt_data->inbuf_counter);
 | 
						|
 | 
						|
    if (encryption_crypt(Buffer, (uint)length, ebuffer, &elength,
 | 
						|
                         crypt_data->key, (uint) sizeof(crypt_data->key),
 | 
						|
                         iv, (uint) sizeof(iv), ENCRYPTION_FLAG_ENCRYPT,
 | 
						|
                         keyid, keyver))
 | 
						|
    {
 | 
						|
      my_errno= 1;
 | 
						|
      DBUG_RETURN(info->error= -1);
 | 
						|
    }
 | 
						|
    wlength= elength + (uint)(ebuffer - wbuffer);
 | 
						|
 | 
						|
    if (length == info->buffer_length)
 | 
						|
    {
 | 
						|
      /*
 | 
						|
        block_length should be always the same. that is, encrypting
 | 
						|
        buffer_length bytes should *always* produce block_length bytes
 | 
						|
      */
 | 
						|
      DBUG_ASSERT(crypt_data->block_length == 0 || crypt_data->block_length == wlength);
 | 
						|
      DBUG_ASSERT(elength <= encryption_encrypted_length((uint)length, keyid, keyver));
 | 
						|
      crypt_data->block_length= wlength;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /* if we write a partial block, it *must* be the last write */
 | 
						|
      IF_DBUG(info->write_function= 0,);
 | 
						|
      crypt_data->last_block_length= wlength;
 | 
						|
    }
 | 
						|
 | 
						|
    if (mysql_file_write(info->file, wbuffer, wlength, info->myflags | MY_NABP))
 | 
						|
      DBUG_RETURN(info->error= -1);
 | 
						|
 | 
						|
    Buffer+= length;
 | 
						|
    Count-= length;
 | 
						|
    info->pos_in_file+= length;
 | 
						|
    crypt_data->counter++;
 | 
						|
  } while (Count);
 | 
						|
  DBUG_RETURN(0);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  determine what key id and key version to use for IO_CACHE temp files
 | 
						|
 | 
						|
  First, try key id 2, if it doesn't exist, use key id 1.
 | 
						|
 | 
						|
  (key id 1 is the default system key id, used pretty much everywhere, it must
 | 
						|
  exist. key id 2 is for tempfiles, it can be used, for example, to set a
 | 
						|
  faster encryption algorithm for temporary files)
 | 
						|
 | 
						|
  This looks like it might have a bug: if an encryption plugin is unloaded when
 | 
						|
  there's an open IO_CACHE, that IO_CACHE will become unreadable after reinit.
 | 
						|
  But in fact it is safe, as an encryption plugin can only be unloaded on
 | 
						|
  server shutdown.
 | 
						|
 | 
						|
  Note that encrypt_tmp_files variable is read-only.
 | 
						|
*/
 | 
						|
int init_io_cache_encryption()
 | 
						|
{
 | 
						|
  if (encrypt_tmp_files)
 | 
						|
  {
 | 
						|
    keyid= ENCRYPTION_KEY_TEMPORARY_DATA;
 | 
						|
    keyver= encryption_key_get_latest_version(keyid);
 | 
						|
    if (keyver == ENCRYPTION_KEY_VERSION_INVALID)
 | 
						|
    {
 | 
						|
      keyid= ENCRYPTION_KEY_SYSTEM_DATA;
 | 
						|
      keyver= encryption_key_get_latest_version(keyid);
 | 
						|
    }
 | 
						|
    if (keyver == ENCRYPTION_KEY_VERSION_INVALID)
 | 
						|
    {
 | 
						|
      sql_print_error("Failed to enable encryption of temporary files");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (keyver != ENCRYPTION_KEY_NOT_ENCRYPTED)
 | 
						|
    {
 | 
						|
      sql_print_information("Using encryption key id %d for temporary files", keyid);
 | 
						|
      _my_b_encr_read= my_b_encr_read;
 | 
						|
      _my_b_encr_write= my_b_encr_write;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  _my_b_encr_read= 0;
 | 
						|
  _my_b_encr_write= 0;
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 |