Skip to content

Commit

Permalink
Bug#22740362 ENCRYPTED INNODB DATA FILES ARE NOT PORTABLE BETWEEN 32-…
Browse files Browse the repository at this point in the history
…BIT AND 64-BIT

Problem:

Encrypted files are not portable across 32bit and 64bit platforms.

ie. encrypted tablespace files created by 32bit mysqld are not readable by 64bit mysqld and
encrypted tablespace files created by 64bit mysqld are not readable by 32bit mysqld

The reason is, when writing encryption header in page 0, after writing
4-byte master key, we moved by sizeof(ulint) bytes to write the next header member.
sizeof(ulint) is 4bytes on 32bit machines and 8bytes on 64bit machines.

In V2, UUID and rest of header is at different position for 32bit vs 64bit
In V1, tablespace key and rest of header is at different position for 32bit vs 64bit

So, the header can be decoded properly and the tablespaces cannot be read.

Fix:

For versions v1 & v2, after reading the 4-byte master key, we check if next
4-bytes are all 0x00. If it is, then it is created by 64bit server, then we
can move to the position after these bytes to read next field.
Introduce v3, which doesn't use ulint at all. There are no guesses in parsing
in v3.
Any rewrite of encryption header (like rotation etc), writes always in v3.
(even if the header is in v1 or v2).
Misc code cleanup (remove unused paramters, variables etc)

Reviewed-By: Ingo Struewing [email protected]
	 Satya Bodapati [email protected]
RB:17312
  • Loading branch information
Allen Lai committed Dec 25, 2017
1 parent 6cf1ccf commit f565013
Show file tree
Hide file tree
Showing 21 changed files with 464 additions and 165 deletions.
Binary file added mysql-test/std_data/table_encrypted_32.zip
Binary file not shown.
Binary file added mysql-test/std_data/table_encrypted_64.zip
Binary file not shown.
46 changes: 46 additions & 0 deletions mysql-test/suite/innodb/r/table_encrypt_portable_32.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB;
ALTER TABLE test.t1 DISCARD TABLESPACE;
ALTER TABLE test.t1 IMPORT TABLESPACE;
SELECT * FROM t1;
c1 c2
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
DROP TABLE t1;
46 changes: 46 additions & 0 deletions mysql-test/suite/innodb/r/table_encrypt_portable_64.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB;
ALTER TABLE test.t1 DISCARD TABLESPACE;
ALTER TABLE test.t1 IMPORT TABLESPACE;
SELECT * FROM t1;
c1 c2
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
0 aaaaa
1 bbbbb
2 ccccc
3 ddddd
4 eeeee
5 fffff
6 ggggg
7 hhhhh
8 iiiii
9 jjjjj
DROP TABLE t1;
2 changes: 1 addition & 1 deletion mysql-test/suite/innodb/r/undo_encrypt_basic.result
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ c1 c2
0 aaaaa
DROP TABLE t1,t2;
# Search for particular string of encryption metadata, should success since it's encrypted.
Pattern "lCB" found
Pattern "lCC" found
CREATE TABLE t1(c1 INT, c2 char(20)) ENGINE = InnoDB;
START TRANSACTION;
INSERT INTO t1 VALUES(0, "aaaaa");
Expand Down
6 changes: 3 additions & 3 deletions mysql-test/suite/innodb/r/undo_encrypt_bootstrap.result
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Run the bootstrap command with no keyring
# Run the bootstrap command with keyring
# Search for particular string of encryption metadata, should success since it's encrypted.
Pattern "lCB" found
Pattern "lCC" found
# Start the DB server with undo log encryption disabled and keyring plugin loaded. It should success.
INSTALL PLUGIN keyring_file SONAME 'keyring_file.so';
ERROR HY000: Function 'keyring_file' already exists
Expand All @@ -28,5 +28,5 @@ c1 c2
400 bbbbb
UNINSTALL PLUGIN keyring_file;
DROP TABLE tab1;
Pattern "lCB" found
Pattern "lCB" found
Pattern "lCC" found
Pattern "lCC" found
73 changes: 73 additions & 0 deletions mysql-test/suite/innodb/t/table_encrypt_portable_32.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# InnoDB transparent tablespace data encryption
# This test case will test portability of encrypted table support feature.
# This test try to port an encrypted table which created by 64bits server to 32bits server.
# The data files of source table is in mysql-test/std_data/table_encrypted_64.zip.
# For update the table_encrypted_64.zip, you have to run the test case table_encrypt_portable_64 in
# record mode.

--source include/have_innodb_max_16k.inc
--source include/have_debug.inc
--source include/have_32bit.inc
--source include/not_windows.inc

#########################
# how to record test result for 64bit test case table_encrypt_portable_64 content mismatch
# 1) change the record value from 0 to 1. it should be "--let $RECORD=1"
# 2) run the test, it will fail (this is expected). The zip files are updated
# 3) change the line to "--let $RECORD=0" and run test
#########################
--let $RECORD=0

let $restart_parameters = restart: --early-plugin-load="keyring_file=$KEYRING_PLUGIN" --loose-keyring_file_data=$MYSQL_TMP_DIR/mysecret_keyring $KEYRING_PLUGIN_OPT;
--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR $KEYRING_PLUGIN_OPT --plugin-dir=KEYRING_PLUGIN_PATH $KEYRING_PLUGIN keyring_file.so
--replace_regex /\.dll/.so/
--source include/restart_mysqld_no_echo.inc

let $MYSQLD_DATADIR = `SELECT @@datadir`;

let $source_db = test;

let $dest_db = test;

if ($RECORD == 1) {
# Create a table with encryption
CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB;
INSERT INTO t1 VALUES(0, "aaaaa");
INSERT INTO t1 VALUES(1, "bbbbb");
INSERT INTO t1 VALUES(2, "ccccc");
INSERT INTO t1 VALUES(3, "ddddd");
INSERT INTO t1 VALUES(4, "eeeee");
INSERT INTO t1 VALUES(5, "fffff");
INSERT INTO t1 VALUES(6, "ggggg");
INSERT INTO t1 VALUES(7, "hhhhh");
INSERT INTO t1 VALUES(8, "iiiii");
INSERT INTO t1 VALUES(9, "jjjjj");
INSERT INTO t1 select * from t1;
INSERT INTO t1 select * from t1;

eval FLUSH TABLES $source_db.t1 FOR EXPORT;

--exec zip -j $MYSQL_TMP_DIR/table_encrypted.zip $MYSQLD_DATADIR/$source_db/t1.* 2>&1 > /dev/null
--move_file $MYSQL_TMP_DIR/table_encrypted.zip $MYSQL_TEST_DIR/std_data/table_encrypted_32.zip

UNLOCK TABLES;

DROP TABLE t1;
}

if ($RECORD == 0) {
CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB;
eval ALTER TABLE $dest_db.t1 DISCARD TABLESPACE;

--exec unzip -qo $MYSQL_TEST_DIR/std_data/table_encrypted_64.zip -d $MYSQLD_DATADIR/$dest_db

eval ALTER TABLE $dest_db.t1 IMPORT TABLESPACE;

SELECT * FROM t1;

# Cleanup
DROP TABLE t1;

--remove_file $MYSQL_TMP_DIR/mysecret_keyring
}

73 changes: 73 additions & 0 deletions mysql-test/suite/innodb/t/table_encrypt_portable_64.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# InnoDB transparent tablespace data encryption
# This test case will test portability of encrypted table support features.
# This test try to port an encrypted table which created by 32bits server to 64bits server.
# The data files of source table is in mysql-test/std_data/table_encrypted_32.zip.
# For update the table_encrypted_32.zip, you have to run the test case table_encrypt_portable_32 in
# record mode.

--source include/have_innodb_max_16k.inc
--source include/have_debug.inc
--source include/have_64bit.inc
--source include/not_windows.inc

#########################
# how to record test result for 32bit test case table_encrypt_portable_32 content mismatch
# 1) change the record value from 0 to 1. it should be "--let $RECORD=1"
# 2) run the test, it will fail (this is expected). The zip files are updated
# 3) change the line to "--let $RECORD=0" and run test
#########################
--let $RECORD=0

let $restart_parameters = restart: --early-plugin-load="keyring_file=$KEYRING_PLUGIN" --loose-keyring_file_data=$MYSQL_TMP_DIR/mysecret_keyring $KEYRING_PLUGIN_OPT;
--replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR $KEYRING_PLUGIN_OPT --plugin-dir=KEYRING_PLUGIN_PATH $KEYRING_PLUGIN keyring_file.so
--replace_regex /\.dll/.so/
--source include/restart_mysqld_no_echo.inc

let $MYSQLD_DATADIR = `SELECT @@datadir`;

let $source_db = test;

let $dest_db = test;

if ($RECORD == 1) {
# Create a table with encryption
CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB;
INSERT INTO t1 VALUES(0, "aaaaa");
INSERT INTO t1 VALUES(1, "bbbbb");
INSERT INTO t1 VALUES(2, "ccccc");
INSERT INTO t1 VALUES(3, "ddddd");
INSERT INTO t1 VALUES(4, "eeeee");
INSERT INTO t1 VALUES(5, "fffff");
INSERT INTO t1 VALUES(6, "ggggg");
INSERT INTO t1 VALUES(7, "hhhhh");
INSERT INTO t1 VALUES(8, "iiiii");
INSERT INTO t1 VALUES(9, "jjjjj");
INSERT INTO t1 select * from t1;
INSERT INTO t1 select * from t1;

eval FLUSH TABLES $source_db.t1 FOR EXPORT;

--exec zip -j $MYSQL_TMP_DIR/table_encrypted.zip $MYSQLD_DATADIR/$source_db/t1.* 2>&1 > /dev/null
--move_file $MYSQL_TMP_DIR/table_encrypted.zip $MYSQL_TEST_DIR/std_data/table_encrypted_64.zip

UNLOCK TABLES;

DROP TABLE t1;
}

if ($RECORD == 0) {
CREATE TABLE t1(c1 INT, c2 char(20)) ENCRYPTION="Y" ENGINE = InnoDB;
eval ALTER TABLE $dest_db.t1 DISCARD TABLESPACE;

--exec unzip -qo $MYSQL_TEST_DIR/std_data/table_encrypted_32.zip -d $MYSQLD_DATADIR/$dest_db

eval ALTER TABLE $dest_db.t1 IMPORT TABLESPACE;

SELECT * FROM t1;

# Cleanup
DROP TABLE t1;

--remove_file $MYSQL_TMP_DIR/mysecret_keyring
}

2 changes: 1 addition & 1 deletion mysql-test/suite/innodb/t/undo_encrypt_basic.test
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ DROP TABLE t1,t2;

--echo # Search for particular string of encryption metadata, should success since it's encrypted.
let SEARCH_FILE= $MYSQLD_UNDO_DATADIR/undo_001;
let SEARCH_PATTERN= lCB;
let SEARCH_PATTERN= lCC;
--source include/search_pattern.inc

# Crash/recovery test.
Expand Down
6 changes: 3 additions & 3 deletions mysql-test/suite/innodb/t/undo_encrypt_bootstrap.test
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ let NEW_CMD = $MYSQLD --no-defaults --innodb_dedicated_server=OFF --initialize-i

--echo # Search for particular string of encryption metadata, should success since it's encrypted.
let SEARCH_FILE= $MYSQLD_UNDO_DATADIR/undo_001;
let SEARCH_PATTERN= lCB;
let SEARCH_PATTERN= lCC;
--source include/search_pattern.inc

#--echo # Start the DB server with undo log encryption disabled, and no keyring plugin. It should fail.
Expand Down Expand Up @@ -109,11 +109,11 @@ DROP TABLE tab1;
# stored.
--sleep 2
let SEARCH_FILE= $MYSQLD_UNDO_DATADIR/undo_001;
let SEARCH_PATTERN= lCB;
let SEARCH_PATTERN= lCC;
--source include/search_pattern.inc

let SEARCH_FILE= $MYSQLD_UNDO_DATADIR/undo_002;
let SEARCH_PATTERN= lCB;
let SEARCH_PATTERN= lCC;
--source include/search_pattern.inc

# restart the server with MTR default
Expand Down
10 changes: 4 additions & 6 deletions storage/innobase/fil/fil0fil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9482,7 +9482,7 @@ fil_set_encryption(
bool
Fil_system::encryption_rotate_in_a_shard(Fil_shard* shard)
{
byte encrypt_info[ENCRYPTION_INFO_SIZE_V2];
byte encrypt_info[ENCRYPTION_INFO_SIZE];

for (auto& elem : shard->m_spaces) {

Expand Down Expand Up @@ -9517,7 +9517,7 @@ Fil_system::encryption_rotate_in_a_shard(Fil_shard* shard)

mtr_x_lock_space(space, &mtr);

memset(encrypt_info, 0, ENCRYPTION_INFO_SIZE_V2);
memset(encrypt_info, 0, ENCRYPTION_INFO_SIZE);

if (!fsp_header_rotate_encryption(
space, encrypt_info, &mtr)) {
Expand Down Expand Up @@ -10726,8 +10726,7 @@ fil_tablespace_redo_encryption(

if (offset >= UNIV_PAGE_SIZE
|| len + offset > UNIV_PAGE_SIZE
|| (len != ENCRYPTION_INFO_SIZE_V1
&& len != ENCRYPTION_INFO_SIZE_V2)) {
|| len != ENCRYPTION_INFO_SIZE) {

recv_sys->found_corrupt_log = true;
return(nullptr);
Expand Down Expand Up @@ -10759,8 +10758,7 @@ fil_tablespace_redo_encryption(
}
#endif /* UNIV_HOTBACKUP */

ut_ad(len == ENCRYPTION_INFO_SIZE_V1
|| len == ENCRYPTION_INFO_SIZE_V2);
ut_ad(len == ENCRYPTION_INFO_SIZE);

ptr += len;

Expand Down
13 changes: 8 additions & 5 deletions storage/innobase/fsp/fsp0fsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,7 @@ fsp_header_get_encryption_offset(
left_size = page_size.physical() - FSP_HEADER_OFFSET - offset
- FIL_PAGE_DATA_END;

ut_ad(left_size >= ENCRYPTION_INFO_SIZE_V2);
ut_ad(left_size >= ENCRYPTION_INFO_SIZE);
#endif

return offset;
Expand All @@ -933,7 +933,7 @@ fsp_header_write_encryption(
buf_block_t* block;
ulint offset;
page_t* page;
ulint master_key_id;
uint32_t master_key_id;

const page_size_t page_size(space_flags);

Expand All @@ -959,13 +959,16 @@ fsp_header_write_encryption(
ENCRYPTION_MAGIC_SIZE) == 0
|| memcmp(page + offset,
ENCRYPTION_KEY_MAGIC_V2,
ENCRYPTION_MAGIC_SIZE) == 0
|| memcmp(page + offset,
ENCRYPTION_KEY_MAGIC_V3,
ENCRYPTION_MAGIC_SIZE) == 0);
return(true);
}

mlog_write_string(page + offset,
encrypt_info,
ENCRYPTION_INFO_SIZE_V2,
ENCRYPTION_INFO_SIZE,
mtr);

/* Write the new fsp flags into be update to the header if needed */
Expand Down Expand Up @@ -1086,7 +1089,7 @@ fsp_header_init(
info to the page 0. */
if (FSP_FLAGS_GET_ENCRYPTION(space->flags)) {
ulint offset = fsp_header_get_encryption_offset(page_size);
byte encryption_info[ENCRYPTION_INFO_SIZE_V2];
byte encryption_info[ENCRYPTION_INFO_SIZE];

if (offset == 0)
return(false);
Expand All @@ -1103,7 +1106,7 @@ fsp_header_init(

mlog_write_string(page + offset,
encryption_info,
ENCRYPTION_INFO_SIZE_V2,
ENCRYPTION_INFO_SIZE,
mtr);
}

Expand Down
Loading

0 comments on commit f565013

Please sign in to comment.