Skip to content

Commit

Permalink
MDEV-9559: Server without encryption configs crashes if selecting fro…
Browse files Browse the repository at this point in the history
…m an implicitly encrypted table

There was two problems. Firstly, if page in ibuf is encrypted but
decrypt failed we should not allow InnoDB to start because
this means that system tablespace is encrypted and not usable.
Secondly, if page decrypt is detected we should return false
from buf_page_decrypt_after_read.
  • Loading branch information
Jan Lindström committed Feb 17, 2016
1 parent 1ac64b7 commit 36ca65b
Show file tree
Hide file tree
Showing 16 changed files with 301 additions and 91 deletions.
32 changes: 32 additions & 0 deletions mysql-test/suite/encryption/r/innodb-encryption-disable.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
SET GLOBAL innodb_file_format = `Barracuda`;
SET GLOBAL innodb_file_per_table = ON;
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
call mtr.add_suppression("Couldn't load plugins from 'file_key_management*");
call mtr.add_suppression("InnoDB: Tablespace id.* is encrypted but encryption service or used key_id .* is not available. Can't continue opening tablespace.");
create table t5 (
`intcol1` int(32) DEFAULT NULL,
`intcol2` int(32) DEFAULT NULL,
`charcol1` varchar(128) DEFAULT NULL,
`charcol2` varchar(128) DEFAULT NULL,
`charcol3` varchar(128) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into t5 values (1,2,'maria','db','encryption');
CREATE TABLE `t1` (
`intcol1` int(32) DEFAULT NULL,
`intcol2` int(32) DEFAULT NULL,
`charcol1` varchar(128) DEFAULT NULL,
`charcol2` varchar(128) DEFAULT NULL,
`charcol3` varchar(128) DEFAULT NULL
) ENGINE=InnoDB;
insert into t1 values (1,2,'maria','db','encryption');
alter table t1 encrypted='yes' `encryption_key_id`=1;
select * from t1;
ERROR HY000: Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
select * from t5;
ERROR HY000: Got error 192 'Table encrypted but decryption failed. This could be because correct encryption management plugin is not loaded, used encryption key is not available or encryption method does not match.' from InnoDB
drop table t1;
drop table t5;
92 changes: 92 additions & 0 deletions mysql-test/suite/encryption/t/innodb-encryption-disable.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
-- source include/have_innodb.inc
# embedded does not support restart
-- source include/not_embedded.inc
-- source include/not_valgrind.inc
# Avoid CrashReporter popup on Mac
-- source include/not_crashrep.inc
-- source filekeys_plugin_exists.inc

--disable_query_log
let $innodb_file_format_orig = `SELECT @@innodb_file_format`;
let $innodb_file_per_table_orig = `SELECT @@innodb_file_per_table`;
--enable_query_log

SET GLOBAL innodb_file_format = `Barracuda`;
SET GLOBAL innodb_file_per_table = ON;

#
# MDEV-9559: Server without encryption configs crashes if selecting from an implicitly encrypted table
#
call mtr.add_suppression("InnoDB: Block in space_id .* in file test/.* encrypted");
call mtr.add_suppression("InnoDB: However key management plugin or used key_id 1 is not found or used encryption algorithm or method does not match.");
call mtr.add_suppression("InnoDB: Marking tablespace as missing. You may drop this table or install correct key management plugin and key file.");
call mtr.add_suppression(".*InnoDB: Cannot open table test/.* from the internal data dictionary of InnoDB though the .frm file for the table exists. See .* for how you can resolve the problem.");
call mtr.add_suppression("InnoDB: .ibd file is missing for table test/.*");
# Suppression for builds where file_key_management plugin is linked statically
call mtr.add_suppression("Couldn't load plugins from 'file_key_management*");
call mtr.add_suppression("InnoDB: Tablespace id.* is encrypted but encryption service or used key_id .* is not available. Can't continue opening tablespace.");

--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--shutdown_server
--source include/wait_until_disconnected.inc

--write_file $MYSQLTEST_VARDIR/keys1.txt
1;770A8A65DA156D24EE2A093277530142
4;770A8A65DA156D24EE2A093277530143
EOF

--exec echo "restart:--innodb-encrypt-tables --plugin-load-add=file_key_management.so --file-key-management --file-key-management-filename=$MYSQLTEST_VARDIR/keys1.txt" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc

create table t5 (
`intcol1` int(32) DEFAULT NULL,
`intcol2` int(32) DEFAULT NULL,
`charcol1` varchar(128) DEFAULT NULL,
`charcol2` varchar(128) DEFAULT NULL,
`charcol3` varchar(128) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

insert into t5 values (1,2,'maria','db','encryption');

CREATE TABLE `t1` (
`intcol1` int(32) DEFAULT NULL,
`intcol2` int(32) DEFAULT NULL,
`charcol1` varchar(128) DEFAULT NULL,
`charcol2` varchar(128) DEFAULT NULL,
`charcol3` varchar(128) DEFAULT NULL
) ENGINE=InnoDB;

insert into t1 values (1,2,'maria','db','encryption');
alter table t1 encrypted='yes' `encryption_key_id`=1;

--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--shutdown_server
--source include/wait_until_disconnected.inc

--exec echo "restart:--innodb-encrypt-tables=OFF" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc

--error 1296
select * from t1;
--error 1296
select * from t5;

--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--shutdown_server
--source include/wait_until_disconnected.inc

--exec echo "restart:--innodb-encrypt-tables --plugin-load-add=file_key_management.so --file-key-management --file-key-management-filename=$MYSQLTEST_VARDIR/keys1.txt" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
--enable_reconnect
--source include/wait_until_connected_again.inc

drop table t1;
drop table t5;

--disable_query_log
EVAL SET GLOBAL innodb_file_per_table = $innodb_file_per_table_orig;
EVAL SET GLOBAL innodb_file_format = $innodb_file_format_orig;
--enable_query_log

--remove_file $MYSQLTEST_VARDIR/keys1.txt
4 changes: 4 additions & 0 deletions storage/innobase/btr/btr0btr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,10 @@ btr_root_get(
buf_block_t* root = btr_root_block_get(index, RW_X_LATCH,
mtr);

if (root && root->page.encrypted == true) {
root = NULL;
}

return(root ? buf_block_get_frame(root) : NULL);
}

Expand Down
45 changes: 30 additions & 15 deletions storage/innobase/buf/buf0buf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2013, 2015, MariaDB Corporation. All Rights Reserved.
Copyright (c) 2013, 2016, MariaDB Corporation. All Rights Reserved.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
Expand Down Expand Up @@ -4674,10 +4674,10 @@ buf_page_io_complete(

ib_push_warning((void *)NULL, DB_DECRYPTION_FAILED,
"Table in tablespace %lu encrypted."
"However key management plugin or used key_id %lu is not found or"
"However key management plugin or used key_id %u is not found or"
" used encryption algorithm or method does not match."
" Can't continue opening the table.",
bpage->key_version);
(ulint)bpage->space, bpage->key_version);

if (bpage->space > TRX_SYS_SPACE) {
if (corrupted) {
Expand All @@ -4701,10 +4701,19 @@ buf_page_io_complete(
}

if (uncompressed && !recv_no_ibuf_operations) {
ibuf_merge_or_delete_for_page(
(buf_block_t*) bpage, bpage->space,
bpage->offset, buf_page_get_zip_size(bpage),
TRUE);
if (bpage && bpage->encrypted) {
fprintf(stderr,
"InnoDB: Warning: Table in tablespace %lu encrypted."
"However key management plugin or used key_id %u is not found or"
" used encryption algorithm or method does not match."
" Can't continue opening the table.\n",
(ulint)bpage->space, bpage->key_version);
} else {
ibuf_merge_or_delete_for_page(
(buf_block_t*) bpage, bpage->space,
bpage->offset, buf_page_get_zip_size(bpage),
TRUE);
}
}
} else {
/* io_type == BUF_IO_WRITE */
Expand Down Expand Up @@ -6156,6 +6165,7 @@ buf_page_decrypt_after_read(
bool page_compressed = fil_page_is_compressed(dst_frame);
bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame);
buf_pool_t* buf_pool = buf_pool_from_bpage(bpage);
bool success = true;

/* If page is encrypted read post-encryption checksum */
if (!page_compressed_encrypted && key_version != 0) {
Expand Down Expand Up @@ -6214,16 +6224,21 @@ buf_page_decrypt_after_read(
}

/* decrypt using crypt_buf to dst_frame */
fil_space_decrypt(bpage->space,
slot->crypt_buf,
size,
dst_frame);
byte* res = fil_space_decrypt(bpage->space,
slot->crypt_buf,
size,
dst_frame);

if (!res) {
bpage->encrypted = true;
success = false;
}
#ifdef UNIV_DEBUG
fil_page_type_validate(dst_frame);
#endif
}

if (page_compressed_encrypted) {
if (page_compressed_encrypted && success) {
if (!slot) {
slot = buf_pool_reserve_tmp_slot(buf_pool, page_compressed);
}
Expand All @@ -6236,11 +6251,11 @@ buf_page_decrypt_after_read(
dst_frame,
size,
&bpage->write_size);
}

#ifdef UNIV_DEBUG
fil_page_type_validate(dst_frame);
fil_page_type_validate(dst_frame);
#endif
}

/* Mark this slot as free */
if (slot) {
Expand All @@ -6250,5 +6265,5 @@ buf_page_decrypt_after_read(

bpage->key_version = key_version;

return (TRUE);
return (success);
}
33 changes: 18 additions & 15 deletions storage/innobase/dict/dict0boot.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 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
Expand Down Expand Up @@ -450,27 +451,29 @@ dict_boot(void)

/* Initialize the insert buffer table and index for each tablespace */

ibuf_init_at_db_start();

dberr_t err = DB_SUCCESS;

if (srv_read_only_mode && !ibuf_is_empty()) {
err = ibuf_init_at_db_start();

ib_logf(IB_LOG_LEVEL_ERROR,
"Change buffer must be empty when --innodb-read-only "
"is set!");
if (err == DB_SUCCESS) {
if (srv_read_only_mode && !ibuf_is_empty()) {

err = DB_ERROR;
} else {
/* Load definitions of other indexes on system tables */
ib_logf(IB_LOG_LEVEL_ERROR,
"Change buffer must be empty when --innodb-read-only "
"is set!");

dict_load_sys_table(dict_sys->sys_tables);
dict_load_sys_table(dict_sys->sys_columns);
dict_load_sys_table(dict_sys->sys_indexes);
dict_load_sys_table(dict_sys->sys_fields);
}
err = DB_ERROR;
} else {
/* Load definitions of other indexes on system tables */

mutex_exit(&(dict_sys->mutex));
dict_load_sys_table(dict_sys->sys_tables);
dict_load_sys_table(dict_sys->sys_columns);
dict_load_sys_table(dict_sys->sys_indexes);
dict_load_sys_table(dict_sys->sys_fields);
}

mutex_exit(&(dict_sys->mutex));
}

return(err);
}
Expand Down
15 changes: 10 additions & 5 deletions storage/innobase/fil/fil0crypt.cc
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ fil_space_decrypt(
byte* src_frame) /*!< in/out: page buffer */
{
dberr_t err = DB_SUCCESS;
byte* res = NULL;

bool encrypted = fil_space_decrypt(
fil_space_get_crypt_data(space),
Expand All @@ -807,13 +808,17 @@ fil_space_decrypt(
src_frame,
&err);

if (encrypted) {
/* Copy the decrypted page back to page buffer, not
really any other options. */
memcpy(src_frame, tmp_frame, page_size);
if (err == DB_SUCCESS) {
if (encrypted) {
/* Copy the decrypted page back to page buffer, not
really any other options. */
memcpy(src_frame, tmp_frame, page_size);
}

res = src_frame;
}

return src_frame;
return res;
}

/******************************************************************
Expand Down
23 changes: 18 additions & 5 deletions storage/innobase/ibuf/ibuf0ibuf.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2014, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, 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
Expand Down Expand Up @@ -383,12 +384,18 @@ ibuf_header_page_get(
buf_block_t* block;

ut_ad(!ibuf_inside(mtr));
page_t* page = NULL;

block = buf_page_get(
IBUF_SPACE_ID, 0, FSP_IBUF_HEADER_PAGE_NO, RW_X_LATCH, mtr);
buf_block_dbg_add_level(block, SYNC_IBUF_HEADER);

return(buf_block_get_frame(block));
if (!block->page.encrypted) {
buf_block_dbg_add_level(block, SYNC_IBUF_HEADER);

page = buf_block_get_frame(block);
}

return page;
}

/******************************************************************//**
Expand Down Expand Up @@ -500,9 +507,10 @@ ibuf_size_update(

/******************************************************************//**
Creates the insert buffer data structure at a database startup and initializes
the data structures for the insert buffer. */
the data structures for the insert buffer.
@return DB_SUCCESS or failure */
UNIV_INTERN
void
dberr_t
ibuf_init_at_db_start(void)
/*=======================*/
{
Expand All @@ -513,7 +521,7 @@ ibuf_init_at_db_start(void)
dict_index_t* index;
ulint n_used;
page_t* header_page;
dberr_t error;
dberr_t error= DB_SUCCESS;

ibuf = static_cast<ibuf_t*>(mem_zalloc(sizeof(ibuf_t)));

Expand Down Expand Up @@ -543,6 +551,10 @@ ibuf_init_at_db_start(void)

header_page = ibuf_header_page_get(&mtr);

if (!header_page) {
return (DB_DECRYPTION_FAILED);
}

fseg_n_reserved_pages(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER,
&n_used, &mtr);
ibuf_enter(&mtr);
Expand Down Expand Up @@ -593,6 +605,7 @@ ibuf_init_at_db_start(void)
ut_a(error == DB_SUCCESS);

ibuf->index = dict_table_get_first_index(table);
return (error);
}

/*********************************************************************//**
Expand Down
Loading

0 comments on commit 36ca65b

Please sign in to comment.