Skip to content
Permalink
Browse files

MDEV-17228 Encrypted temporary tables are not encrypted

- Introduce a new variable called innodb_encrypt_temporary_tables which is
a boolean variable. It decides whether to encrypt the temporary tablespace.
- Encrypts the temporary tablespace based on full checksum format.
- Introduced a new counter to track encrypted and decrypted temporary
tablespace pages.
- Warnings issued if temporary table creation has conflict value with
innodb_encrypt_temporary_tables
- Added a new test case which reads and writes the pages from/to temporary
tablespace.
  • Loading branch information...
Thirunarayanan committed Jun 27, 2019
1 parent e4a0dbf commit 723a4b1d78274b10222b03088ff75884b78c2ced
@@ -3,6 +3,7 @@ show variables like 'innodb_encrypt%';
Variable_name Value
innodb_encrypt_log ON
innodb_encrypt_tables ON
innodb_encrypt_temporary_tables OFF
innodb_encryption_rotate_key_age 2
innodb_encryption_rotation_iops 100
innodb_encryption_threads 4
@@ -22,6 +22,8 @@ key (col_int_key),
key (col_char_key)
) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=1;
CREATE TEMPORARY TABLE t LIKE t0;
Warnings:
Warning 1478 Ignoring encryption parameter during temporary table creation.
INSERT INTO t VALUES
(NULL,1,1,'private','secret'),(NULL,2,2,'sacred','success'),
(NULL,3,3,'story','secure'),(NULL,4,4,'security','sacrament');
@@ -0,0 +1,19 @@
SELECT variable_value into @old_encrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_encrypted';
SELECT variable_value into @old_decrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_decrypted';
CREATE TEMPORARY TABLE t1(f1 CHAR(200), f2 CHAR(200)) ENGINE=InnoDB;
INSERT INTO t1 (f1,f2) SELECT '', '' FROM seq_1_to_8192;
CREATE TEMPORARY TABLE t2(f1 CHAR(100), f2 CHAR(200), f3 CHAR(200))ENGINE=InnoDB;
INSERT INTO t2 (f1,f2,f3) SELECT '', '', '' FROM seq_1_to_8192;
SELECT COUNT(*) FROM t1;
COUNT(*)
8192
SELECT variable_value > @old_encrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_encrypted';
variable_value > @old_encrypted
1
SELECT variable_value > @old_decrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_decrypted';
variable_value > @old_decrypted
1
@@ -3,6 +3,7 @@ SHOW VARIABLES LIKE 'innodb_encrypt%';
Variable_name Value
innodb_encrypt_log ON
innodb_encrypt_tables ON
innodb_encrypt_temporary_tables OFF
innodb_encryption_rotate_key_age 15
innodb_encryption_rotation_iops 100
innodb_encryption_threads 4
@@ -57,6 +58,7 @@ SHOW VARIABLES LIKE 'innodb_encrypt%';
Variable_name Value
innodb_encrypt_log ON
innodb_encrypt_tables OFF
innodb_encrypt_temporary_tables OFF
innodb_encryption_rotate_key_age 15
innodb_encryption_rotation_iops 100
innodb_encryption_threads 0
@@ -0,0 +1,2 @@
--innodb_buffer_pool_size=5M
--innodb_encrypt_temporary_tables=1
@@ -0,0 +1,23 @@
--source include/have_sequence.inc
--source include/have_innodb.inc
--source include/have_file_key_management_plugin.inc

SELECT variable_value into @old_encrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_encrypted';

SELECT variable_value into @old_decrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_decrypted';

CREATE TEMPORARY TABLE t1(f1 CHAR(200), f2 CHAR(200)) ENGINE=InnoDB;
INSERT INTO t1 (f1,f2) SELECT '', '' FROM seq_1_to_8192;

CREATE TEMPORARY TABLE t2(f1 CHAR(100), f2 CHAR(200), f3 CHAR(200))ENGINE=InnoDB;
INSERT INTO t2 (f1,f2,f3) SELECT '', '', '' FROM seq_1_to_8192;

SELECT COUNT(*) FROM t1;
SELECT variable_value > @old_encrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_encrypted';

SELECT variable_value > @old_decrypted FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_decrypted';
--source include/restart_mysqld.inc
@@ -1028,6 +1028,20 @@ NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST OFF,ON,FORCE
READ_ONLY NO
COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME INNODB_ENCRYPT_TEMPORARY_TABLES
SESSION_VALUE NULL
GLOBAL_VALUE OFF
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE OFF
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Enrypt the temporary table data.
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST OFF,ON
READ_ONLY YES
COMMAND_LINE_ARGUMENT OPTIONAL
VARIABLE_NAME INNODB_FAKE_CHANGES
SESSION_VALUE OFF
GLOBAL_VALUE OFF
@@ -60,6 +60,7 @@ Created 11/5/1995 Heikki Tuuri
#include "dict0dict.h"
#include "log0recv.h"
#include "srv0mon.h"
#include "log0crypt.h"
#endif /* !UNIV_INNOCHECKSUM */
#include "page0zip.h"
#include "sync0sync.h"
@@ -476,6 +477,45 @@ buf_pool_register_chunk(
chunk->blocks->frame, chunk));
}

/** Decrypt a page for temporary tablespace.
@param[in,out] tmp_frame Temporary buffer
@param[in] src_frame Page to decrypt
@return true if temporary tablespace decrypted, false if not */
static bool buf_tmp_page_decrypt(byte* tmp_frame, byte* src_frame)
{
if (buf_page_is_zeroes(src_frame, srv_page_size)) {
return true;
}

/* read space & lsn */
uint header_len = FIL_PAGE_DATA;

/* Copy FIL page header, it is not encrypted */
memcpy(tmp_frame, src_frame, header_len);

/* Calculate the offset where decryption starts */
const byte* src = src_frame + header_len;
byte* dst = tmp_frame + header_len;
uint srclen = uint(srv_page_size)
- header_len - FIL_PAGE_DATA_END;
ulint offset = mach_read_from_4(src_frame + FIL_PAGE_OFFSET);

if (!log_tmp_block_decrypt(src, srclen, dst,
(offset * srv_page_size))) {
return false;
}

memcpy(tmp_frame + srv_page_size - FIL_PAGE_DATA_END,
src_frame + srv_page_size - FIL_PAGE_DATA_END,
FIL_PAGE_DATA_END);

memcpy(src_frame, tmp_frame, srv_page_size);
srv_stats.pages_decrypted.inc();
srv_stats.n_temp_blocks_decrypted.inc();

return true; /* page was decrypted */
}

/** Decrypt a page.
@param[in,out] bpage Page control block
@param[in,out] space tablespace
@@ -495,6 +535,22 @@ static bool buf_page_decrypt_after_read(buf_page_t* bpage, fil_space_t* space)
return (true);
}

if (space->purpose == FIL_TYPE_TEMPORARY
&& innodb_encrypt_temporary_tables) {
buf_tmp_buffer_t* slot = buf_pool_reserve_tmp_slot(buf_pool);
buf_tmp_reserve_crypt_buf(slot);

if (!buf_tmp_page_decrypt(slot->crypt_buf, dst_frame)) {
slot->release();
ib::error() << "Encrypted page " << bpage->id
<< " in file " << space->chain.start->name;
return false;
}

slot->release();
return true;
}

/* Page is encrypted if encryption information is found from
tablespace and page contains used key_version. This is true
also for pages first compressed and then encrypted. */
@@ -923,6 +979,22 @@ static uint32_t buf_page_check_crc32(const byte* page, uint32_t checksum)
# define buf_page_check_crc32(page, checksum) buf_calc_page_crc32(page)
#endif /* INNODB_BUG_ENDIAN_CRC32 */

/** Check if a page is all zeroes.
@param[in] read_buf database page
@param[in] page_size page frame size
@return whether the page is all zeroes */
bool buf_page_is_zeroes(const void* read_buf, size_t page_size)
{
const ulint* b = reinterpret_cast<const ulint*>(read_buf);
const ulint* const e = b + page_size / sizeof *b;
do {
if (*b++) {
return false;
}
} while (b != e);
return true;
}

/** Check if a page is corrupt.
@param[in] check_lsn whether the LSN should be checked
@param[in] read_buf database page
@@ -7281,6 +7353,44 @@ operator<<(
return(out);
}

/** Encrypt a buffer of temporary tablespace
@param[in] offset Page offset
@param[in] src_frame Page to encrypt
@param[in,out] dst_frame Output buffer
@return encrypted buffer or NULL */
static byte* buf_tmp_page_encrypt(
ulint offset,
byte* src_frame,
byte* dst_frame)
{
uint header_len = FIL_PAGE_DATA;
/* FIL page header is not encrypted */
memcpy(dst_frame, src_frame, header_len);

/* Calculate the start offset in a page */
uint unencrypted_bytes = header_len + FIL_PAGE_DATA_END;
uint srclen = srv_page_size - unencrypted_bytes;
const byte* src = src_frame + header_len;
byte* dst = dst_frame + header_len;

if (!log_tmp_block_encrypt(src, srclen, dst, (offset * srv_page_size),
true)) {
return NULL;
}

memcpy(dst_frame + srv_page_size - FIL_PAGE_DATA_END,
src_frame + srv_page_size - FIL_PAGE_DATA_END,
FIL_PAGE_DATA_END);

/* Handle post encryption checksum */
mach_write_to_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION + 4,
buf_calc_page_crc32(dst_frame));

srv_stats.pages_encrypted.inc();
srv_stats.n_temp_blocks_encrypted.inc();
return dst_frame;
}

/** Encryption and page_compression hook that is called just before
a page is written to disk.
@param[in,out] space tablespace
@@ -7314,13 +7424,21 @@ buf_page_encrypt_before_write(

fil_space_crypt_t* crypt_data = space->crypt_data;

const bool encrypted = crypt_data
&& !crypt_data->not_encrypted()
&& crypt_data->type != CRYPT_SCHEME_UNENCRYPTED
&& (!crypt_data->is_default_encryption()
|| srv_encrypt_tables);
bool encrypted, page_compressed;

if (space->purpose == FIL_TYPE_TEMPORARY) {
ut_ad(!crypt_data);
encrypted = innodb_encrypt_temporary_tables;
page_compressed = false;
} else {
encrypted = crypt_data
&& !crypt_data->not_encrypted()
&& crypt_data->type != CRYPT_SCHEME_UNENCRYPTED
&& (!crypt_data->is_default_encryption()
|| srv_encrypt_tables);

bool page_compressed = FSP_FLAGS_HAS_PAGE_COMPRESSION(space->flags);
page_compressed = FSP_FLAGS_HAS_PAGE_COMPRESSION(space->flags);
}

if (!encrypted && !page_compressed) {
/* No need to encrypt or page compress the page.
@@ -7341,18 +7459,25 @@ buf_page_encrypt_before_write(

if (!page_compressed) {
not_compressed:
/* Encrypt page content */
byte* tmp = fil_space_encrypt(space,
bpage->id.page_no(),
bpage->newest_modification,
src_frame,
dst_frame);
byte* tmp;
if (space->purpose == FIL_TYPE_TEMPORARY) {
/* Encrypt temporary tablespace page content */
tmp = buf_tmp_page_encrypt(bpage->id.page_no(),
src_frame, dst_frame);
} else {
/* Encrypt page content */
tmp = fil_space_encrypt(
space, bpage->id.page_no(),
bpage->newest_modification,
src_frame, dst_frame);
}

bpage->real_size = UNIV_PAGE_SIZE;
slot->out_buf = dst_frame = tmp;

ut_d(fil_page_type_validate(tmp));
} else {
ut_ad(space->purpose != FIL_TYPE_TEMPORARY);
/* First we compress the page content */
buf_tmp_reserve_compression_buf(slot);
byte* tmp = slot->comp_buf;
@@ -338,20 +338,6 @@ buf_dblwr_create()
goto start_again;
}

/** Check if a page is all zeroes.
@param[in] read_buf database page
@param[in] page_size page frame size
@return whether the page is all zeroes */
static bool buf_page_is_zeroes(const byte* read_buf, size_t page_size)
{
for (ulint i = 0; i < page_size; i++) {
if (read_buf[i] != 0) {
return false;
}
}
return true;
}

/**
At database startup initializes the doublewrite buffer memory structure if
we already have a doublewrite buffer created in the data files. If we are
@@ -1194,6 +1194,12 @@ static SHOW_VAR innodb_status_variables[]= {
{"encryption_n_rowlog_blocks_decrypted",
(char*)&export_vars.innodb_n_rowlog_blocks_decrypted,
SHOW_LONGLONG},
{"encryption_n_temp_blocks_encrypted",
(char*)&export_vars.innodb_n_temp_blocks_encrypted,
SHOW_LONGLONG},
{"encryption_n_temp_blocks_decrypted",
(char*)&export_vars.innodb_n_temp_blocks_decrypted,
SHOW_LONGLONG},

/* scrubing */
{"scrub_background_page_reorganizations",
@@ -3931,7 +3937,8 @@ innobase_init(
}
#endif

if ((srv_encrypt_tables || srv_encrypt_log)
if ((srv_encrypt_tables || srv_encrypt_log
|| innodb_encrypt_temporary_tables)
&& !encryption_key_id_exists(FIL_DEFAULT_ENCRYPTION_KEY)) {
sql_print_error("InnoDB: cannot enable encryption, "
"encryption plugin is not available");
@@ -11270,7 +11277,18 @@ create_table_info_t::create_table_def()
Given that temp table lifetime is limited to connection/server lifetime
on re-start we don't need to restore temp-table and so no entry is
needed in SYSTEM tables. */
if (dict_table_is_temporary(table)) {
if (table->is_temporary()) {

if ((options->encryption == 1
&& !innodb_encrypt_temporary_tables)
|| (options->encryption == 2
&& innodb_encrypt_temporary_tables)) {
push_warning_printf(m_thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
"Ignoring encryption parameter during "
"temporary table creation.");
}

/* Get a new table ID */
dict_table_assign_new_id(table, m_trx);
table->space = SRV_TMP_SPACE_ID;
"DEPRECATED. This setting has no effect.",
NULL, innodb_instrument_semaphores_update, FALSE);

static MYSQL_SYSVAR_BOOL(encrypt_temporary_tables, innodb_encrypt_temporary_tables,
PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
"Enrypt the temporary table data.",
NULL, NULL, false);

#include "ha_xtradb.h"

static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(instrument_semaphores),
MYSQL_SYSVAR(buf_dump_status_frequency),
MYSQL_SYSVAR(background_thread),
MYSQL_SYSVAR(encrypt_temporary_tables),

/* XtraDB compatibility system variables */
#define HA_XTRADB_SYSVARS

0 comments on commit 723a4b1

Please sign in to comment.
You can’t perform that action at this time.