MDEV-8126 encryption for temp files
IO_CACHE tempfiles encryption
This commit is contained in:
parent
318c826e93
commit
d9340d6c8e
@ -67,7 +67,7 @@ typedef struct my_aio_result {
|
||||
#define MY_WME 16 /* Write message on error */
|
||||
#define MY_WAIT_IF_FULL 32 /* Wait and try again if disk full error */
|
||||
#define MY_IGNORE_BADFD 32 /* my_sync: ignore 'bad descriptor' errors */
|
||||
#define MY_UNUSED 64 /* Unused (was support for RAID) */
|
||||
#define MY_ENCRYPT 64 /* Encrypt IO_CACHE temporary files */
|
||||
#define MY_FULL_IO 512 /* For my_read - loop intil I/O is complete */
|
||||
#define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */
|
||||
#define MY_LINK_WARNING 32 /* my_redel() gives warning if links */
|
||||
|
@ -104,7 +104,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
|
||||
../sql/sql_explain.cc ../sql/sql_explain.h
|
||||
../sql/sql_analyze_stmt.cc ../sql/sql_analyze_stmt.h
|
||||
../sql/compat56.cc
|
||||
../sql/table_cache.cc
|
||||
../sql/table_cache.cc ../sql/mf_iocache_encr.cc
|
||||
../sql/item_inetfunc.cc
|
||||
../sql/wsrep_dummy.cc ../sql/encryption.cc
|
||||
${GEN_SOURCES}
|
||||
|
4
mysql-test/include/have_sequence.inc
Normal file
4
mysql-test/include/have_sequence.inc
Normal file
@ -0,0 +1,4 @@
|
||||
if (`SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'sequence' AND support IN ('YES', 'DEFAULT', 'ENABLED')`)
|
||||
{
|
||||
--skip Test requires Sequence engine
|
||||
}
|
1
mysql-test/include/have_sequence.opt
Normal file
1
mysql-test/include/have_sequence.opt
Normal file
@ -0,0 +1 @@
|
||||
--loose-sequence
|
@ -163,8 +163,11 @@ The following options may be given as the first argument:
|
||||
Precision of the result of '/' operator will be increased
|
||||
on that value
|
||||
--encrypt-tmp-disk-tables
|
||||
Encrypt tmp disk tables (created as part of query
|
||||
execution)
|
||||
Encrypt temporary on-disk tables (created as part of
|
||||
query execution)
|
||||
--encrypt-tmp-files Encrypt temporary files (created for filesort, binary log
|
||||
cache, etc)
|
||||
(Defaults to on; use --skip-encrypt-tmp-files to disable.)
|
||||
--enforce-storage-engine=name
|
||||
Force the use of a storage engine for new tables
|
||||
--event-scheduler[=name]
|
||||
@ -1149,6 +1152,7 @@ delayed-insert-timeout 300
|
||||
delayed-queue-size 1000
|
||||
div-precision-increment 4
|
||||
encrypt-tmp-disk-tables FALSE
|
||||
encrypt-tmp-files TRUE
|
||||
enforce-storage-engine (No default value)
|
||||
event-scheduler OFF
|
||||
expensive-subquery-limit 100
|
||||
|
46
mysql-test/suite/encryption/r/tempfiles.result
Normal file
46
mysql-test/suite/encryption/r/tempfiles.result
Normal file
@ -0,0 +1,46 @@
|
||||
CREATE TABLE t1(a INT);
|
||||
INSERT INTO t1 VALUES(1),(2);
|
||||
DELETE FROM t1 WHERE a=1;
|
||||
OPTIMIZE TABLE t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 optimize status OK
|
||||
CHECK TABLE t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 check status OK
|
||||
DROP TABLE t1;
|
||||
create table t1 (v varchar(10), c char(10), t text, key(v), key(c), key(t(10)));
|
||||
insert into t1 (v) select concat(char(ascii('a')+s2.seq),repeat(' ',s1.seq))
|
||||
from seq_0_to_9 as s1, seq_0_to_26 as s2;
|
||||
update t1 set c=v, t=v;
|
||||
select sql_big_result t,count(t) from t1 group by t limit 10;
|
||||
t count(t)
|
||||
a 10
|
||||
b 10
|
||||
c 10
|
||||
d 10
|
||||
e 10
|
||||
f 10
|
||||
g 10
|
||||
h 10
|
||||
i 10
|
||||
j 10
|
||||
drop table t1;
|
||||
set global binlog_cache_size=8192;
|
||||
create table t1 (a text) engine=innodb;
|
||||
start transaction;
|
||||
insert t1 select repeat(seq, 1000) from seq_1_to_15;
|
||||
commit;
|
||||
start transaction;
|
||||
insert t1 select repeat(seq, 1000) from seq_1_to_8;
|
||||
commit;
|
||||
drop table t1;
|
||||
create table t1 (a text) engine=innodb;
|
||||
start transaction;
|
||||
insert t1 select repeat(seq, 1000) from seq_1_to_15;
|
||||
savepoint foo;
|
||||
insert t1 select repeat(seq, 1000) from seq_16_to_30;
|
||||
rollback to savepoint foo;
|
||||
insert t1 select repeat(seq, 1000) from seq_31_to_40;
|
||||
commit;
|
||||
drop table t1;
|
||||
set global binlog_cache_size=default;
|
74
mysql-test/suite/encryption/t/tempfiles.test
Normal file
74
mysql-test/suite/encryption/t/tempfiles.test
Normal file
@ -0,0 +1,74 @@
|
||||
#
|
||||
# Various test cases for IO_CACHE tempfiles (file==-1) encryption
|
||||
#
|
||||
source include/have_example_key_management_plugin.inc;
|
||||
source include/have_sequence.inc;
|
||||
|
||||
# Row binlog format to fill binlog cache faster
|
||||
source include/have_binlog_format_row.inc;
|
||||
|
||||
# Nothing XtraDB specific in this test, it just needs *some* transactional
|
||||
# engine. But there's no need to run it twice for InnoDB and XtraDB.
|
||||
source include/have_xtradb.inc;
|
||||
|
||||
#
|
||||
# MyISAM messing around with IO_CACHE::file
|
||||
#
|
||||
CREATE TABLE t1(a INT);
|
||||
INSERT INTO t1 VALUES(1),(2);
|
||||
DELETE FROM t1 WHERE a=1;
|
||||
OPTIMIZE TABLE t1;
|
||||
CHECK TABLE t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# filesort, my_b_pread, seeks in READ_CACHE
|
||||
#
|
||||
create table t1 (v varchar(10), c char(10), t text, key(v), key(c), key(t(10)));
|
||||
insert into t1 (v) select concat(char(ascii('a')+s2.seq),repeat(' ',s1.seq))
|
||||
from seq_0_to_9 as s1, seq_0_to_26 as s2;
|
||||
update t1 set c=v, t=v;
|
||||
select sql_big_result t,count(t) from t1 group by t limit 10;
|
||||
drop table t1;
|
||||
|
||||
set global binlog_cache_size=8192;
|
||||
|
||||
connect con1, localhost, root;
|
||||
|
||||
#
|
||||
# Test the last half-filled block:
|
||||
# first write 3 blocks, then reinit the file and write one full and one
|
||||
# partial block. reading the second time must stop in the middle of the
|
||||
# second block, and NOT read till EOF.
|
||||
#
|
||||
create table t1 (a text) engine=innodb;
|
||||
start transaction;
|
||||
insert t1 select repeat(seq, 1000) from seq_1_to_15;
|
||||
commit;
|
||||
start transaction;
|
||||
insert t1 select repeat(seq, 1000) from seq_1_to_8;
|
||||
commit;
|
||||
drop table t1;
|
||||
|
||||
disconnect con1;
|
||||
connect con2, localhost, root;
|
||||
|
||||
#
|
||||
# Test reinit_io_cache(WRITE_CACHE) with non-zero seek_offset:
|
||||
# Start a transaction, write until the cache goes to disk,
|
||||
# create a savepoint, write more blocks to disk, rollback to savepoint.
|
||||
#
|
||||
create table t1 (a text) engine=innodb;
|
||||
start transaction;
|
||||
insert t1 select repeat(seq, 1000) from seq_1_to_15;
|
||||
savepoint foo;
|
||||
insert t1 select repeat(seq, 1000) from seq_16_to_30;
|
||||
rollback to savepoint foo;
|
||||
insert t1 select repeat(seq, 1000) from seq_31_to_40;
|
||||
commit;
|
||||
drop table t1;
|
||||
|
||||
disconnect con2;
|
||||
connection default;
|
||||
|
||||
set global binlog_cache_size=default;
|
@ -10,6 +10,7 @@ there should be *no* long test name listed below:
|
||||
select distinct variable_name as `there should be *no* variables listed below:` from t2
|
||||
left join t1 on variable_name=test_name where test_name is null;
|
||||
there should be *no* variables listed below:
|
||||
encrypt_tmp_files
|
||||
innodb_default_encryption_key_id
|
||||
max_digest_length
|
||||
strict_password_validation
|
||||
|
@ -688,13 +688,27 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME
|
||||
DEFAULT_VALUE OFF
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE BOOLEAN
|
||||
VARIABLE_COMMENT Encrypt tmp disk tables (created as part of query execution)
|
||||
VARIABLE_COMMENT Encrypt temporary on-disk tables (created as part of query execution)
|
||||
NUMERIC_MIN_VALUE NULL
|
||||
NUMERIC_MAX_VALUE NULL
|
||||
NUMERIC_BLOCK_SIZE NULL
|
||||
ENUM_VALUE_LIST OFF,ON
|
||||
READ_ONLY NO
|
||||
COMMAND_LINE_ARGUMENT OPTIONAL
|
||||
VARIABLE_NAME ENCRYPT_TMP_FILES
|
||||
SESSION_VALUE NULL
|
||||
GLOBAL_VALUE ON
|
||||
GLOBAL_VALUE_ORIGIN COMPILE-TIME
|
||||
DEFAULT_VALUE ON
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE BOOLEAN
|
||||
VARIABLE_COMMENT Encrypt temporary files (created for filesort, binary log cache, etc)
|
||||
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 ENFORCE_STORAGE_ENGINE
|
||||
SESSION_VALUE
|
||||
GLOBAL_VALUE NULL
|
||||
|
@ -702,13 +702,27 @@ GLOBAL_VALUE_ORIGIN COMPILE-TIME
|
||||
DEFAULT_VALUE OFF
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE BOOLEAN
|
||||
VARIABLE_COMMENT Encrypt tmp disk tables (created as part of query execution)
|
||||
VARIABLE_COMMENT Encrypt temporary on-disk tables (created as part of query execution)
|
||||
NUMERIC_MIN_VALUE NULL
|
||||
NUMERIC_MAX_VALUE NULL
|
||||
NUMERIC_BLOCK_SIZE NULL
|
||||
ENUM_VALUE_LIST OFF,ON
|
||||
READ_ONLY NO
|
||||
COMMAND_LINE_ARGUMENT OPTIONAL
|
||||
VARIABLE_NAME ENCRYPT_TMP_FILES
|
||||
SESSION_VALUE NULL
|
||||
GLOBAL_VALUE ON
|
||||
GLOBAL_VALUE_ORIGIN COMPILE-TIME
|
||||
DEFAULT_VALUE ON
|
||||
VARIABLE_SCOPE GLOBAL
|
||||
VARIABLE_TYPE BOOLEAN
|
||||
VARIABLE_COMMENT Encrypt temporary files (created for filesort, binary log cache, etc)
|
||||
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 ENFORCE_STORAGE_ENGINE
|
||||
SESSION_VALUE
|
||||
GLOBAL_VALUE NULL
|
||||
|
@ -70,6 +70,10 @@ static int _my_b_seq_read(IO_CACHE *info, uchar *Buffer, size_t Count);
|
||||
static int _my_b_cache_write(IO_CACHE *info, const uchar *Buffer, size_t Count);
|
||||
static int _my_b_cache_write_r(IO_CACHE *info, const uchar *Buffer, size_t Count);
|
||||
|
||||
int (*_my_b_encr_read)(IO_CACHE *info,uchar *Buffer,size_t Count)= 0;
|
||||
int (*_my_b_encr_write)(IO_CACHE *info,const uchar *Buffer,size_t Count)= 0;
|
||||
|
||||
|
||||
/*
|
||||
Setup internal pointers inside IO_CACHE
|
||||
|
||||
@ -114,18 +118,35 @@ init_functions(IO_CACHE* info)
|
||||
programs that link against mysys but know nothing about THD, such
|
||||
as myisamchk
|
||||
*/
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
break;
|
||||
case SEQ_READ_APPEND:
|
||||
info->read_function = _my_b_seq_read;
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
break;
|
||||
case READ_CACHE:
|
||||
if (info->myflags & MY_ENCRYPT)
|
||||
{
|
||||
DBUG_ASSERT(info->share == 0);
|
||||
info->read_function = _my_b_encr_read;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case WRITE_CACHE:
|
||||
if (info->myflags & MY_ENCRYPT)
|
||||
{
|
||||
info->write_function = _my_b_encr_write;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case READ_FIFO:
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
info->read_function = info->share ? _my_b_cache_read_r : _my_b_cache_read;
|
||||
info->write_function = info->share ? _my_b_cache_write_r : _my_b_cache_write;
|
||||
break;
|
||||
case TYPE_NOT_SET:
|
||||
DBUG_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
setup_io_cache(info);
|
||||
@ -175,6 +196,7 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
|
||||
|
||||
if (file >= 0)
|
||||
{
|
||||
DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
|
||||
pos= mysql_file_tell(file, MYF(0));
|
||||
if ((pos == (my_off_t) -1) && (my_errno == ESPIPE))
|
||||
{
|
||||
@ -191,6 +213,12 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
|
||||
else
|
||||
info->seek_not_done= MY_TEST(seek_offset != pos);
|
||||
}
|
||||
else
|
||||
if (type == WRITE_CACHE && _my_b_encr_read)
|
||||
{
|
||||
cache_myflags|= MY_ENCRYPT;
|
||||
DBUG_ASSERT(seek_offset == 0);
|
||||
}
|
||||
|
||||
info->disk_writes= 0;
|
||||
info->share=0;
|
||||
@ -200,6 +228,7 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
|
||||
min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
|
||||
if (type == READ_CACHE || type == SEQ_READ_APPEND)
|
||||
{ /* Assume file isn't growing */
|
||||
DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
|
||||
if (!(cache_myflags & MY_DONT_CHECK_FILESIZE))
|
||||
{
|
||||
/* Calculate end of file to avoid allocating oversized buffers */
|
||||
@ -235,6 +264,8 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
|
||||
buffer_block= cachesize;
|
||||
if (type == SEQ_READ_APPEND)
|
||||
buffer_block *= 2;
|
||||
else if (cache_myflags & MY_ENCRYPT)
|
||||
buffer_block= 2*(buffer_block + MY_AES_BLOCK_SIZE) + sizeof(IO_CACHE_CRYPT);
|
||||
if (cachesize == min_cache)
|
||||
flags|= (myf) MY_WME;
|
||||
|
||||
@ -288,6 +319,7 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
|
||||
if (use_async_io && ! my_disable_async_io)
|
||||
{
|
||||
DBUG_PRINT("info",("Using async io"));
|
||||
DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
|
||||
info->read_length/=2;
|
||||
info->read_function=_my_b_async_read;
|
||||
}
|
||||
@ -399,9 +431,23 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
|
||||
info->read_end=info->buffer; /* Nothing in cache */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info->myflags & MY_ENCRYPT)
|
||||
{
|
||||
info->write_end = info->write_buffer + info->buffer_length;
|
||||
if (seek_offset && info->file != -1)
|
||||
{
|
||||
info->read_end= info->buffer;
|
||||
_my_b_encr_read(info, 0, 0); /* prefill the buffer */
|
||||
info->write_pos= info->read_pos;
|
||||
info->pos_in_file+= info->buffer_length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info->write_end=(info->buffer + info->buffer_length -
|
||||
(seek_offset & (IO_SIZE-1)));
|
||||
}
|
||||
info->end_of_file= ~(my_off_t) 0;
|
||||
}
|
||||
}
|
||||
@ -414,6 +460,7 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
|
||||
((ulong) info->buffer_length <
|
||||
(ulong) (info->end_of_file - seek_offset)))
|
||||
{
|
||||
DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
|
||||
info->read_length=info->buffer_length/2;
|
||||
info->read_function=_my_b_async_read;
|
||||
}
|
||||
@ -514,7 +561,7 @@ int _my_b_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
|
||||
Otherwise info->error contains the number of bytes in Buffer.
|
||||
*/
|
||||
|
||||
static int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count)
|
||||
int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count)
|
||||
{
|
||||
size_t length, diff_length, left_length= 0, max_length;
|
||||
my_off_t pos_in_file;
|
||||
@ -1057,6 +1104,7 @@ static int _my_b_cache_read_r(IO_CACHE *cache, uchar *Buffer, size_t Count)
|
||||
size_t length, diff_length, left_length= 0;
|
||||
IO_CACHE_SHARE *cshare= cache->share;
|
||||
DBUG_ENTER("_my_b_cache_read_r");
|
||||
DBUG_ASSERT(!(cache->myflags & MY_ENCRYPT));
|
||||
|
||||
while (Count)
|
||||
{
|
||||
@ -1560,7 +1608,7 @@ int _my_b_get(IO_CACHE *info)
|
||||
-1 On error; my_errno contains error code.
|
||||
*/
|
||||
|
||||
static int _my_b_cache_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
|
||||
int _my_b_cache_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
|
||||
{
|
||||
if (Buffer != info->write_buffer)
|
||||
{
|
||||
@ -1611,6 +1659,7 @@ static int _my_b_cache_write_r(IO_CACHE *info, const uchar *Buffer, size_t Count
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
DBUG_ASSERT(info->share);
|
||||
copy_to_read_buffer(info, Buffer, old_pos_in_file);
|
||||
|
||||
@ -1633,6 +1682,7 @@ int my_b_append(IO_CACHE *info, const uchar *Buffer, size_t Count)
|
||||
day, we might need to add a call to copy_to_read_buffer().
|
||||
*/
|
||||
DBUG_ASSERT(!info->share);
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
|
||||
lock_append_buffer(info);
|
||||
rest_length= (size_t) (info->write_end - info->write_pos);
|
||||
@ -1699,6 +1749,7 @@ int my_block_write(IO_CACHE *info, const uchar *Buffer, size_t Count,
|
||||
day, we might need to add a call to copy_to_read_buffer().
|
||||
*/
|
||||
DBUG_ASSERT(!info->share);
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
|
||||
if (pos < info->pos_in_file)
|
||||
{
|
||||
|
@ -182,6 +182,13 @@ void my_b_seek(IO_CACHE *info,my_off_t pos)
|
||||
|
||||
int my_b_pread(IO_CACHE *info, uchar *Buffer, size_t Count, my_off_t pos)
|
||||
{
|
||||
if (info->myflags & MY_ENCRYPT)
|
||||
{
|
||||
my_b_seek(info, pos);
|
||||
return my_b_read(info, Buffer, Count);
|
||||
}
|
||||
|
||||
/* backward compatibility behavior. XXX remove it? */
|
||||
if (mysql_file_pread(info->file, Buffer, Count, pos, info->myflags | MY_NABP))
|
||||
return info->error= -1;
|
||||
return 0;
|
||||
|
@ -13,8 +13,14 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifndef MYSYS_PRIV_INCLUDED
|
||||
#define MYSYS_PRIV_INCLUDED
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <my_crypt.h>
|
||||
|
||||
C_MODE_START
|
||||
|
||||
#ifdef USE_SYSTEM_WRAPPERS
|
||||
#include "system_wrappers.h"
|
||||
@ -71,6 +77,16 @@ extern PSI_file_key key_file_proc_meminfo;
|
||||
extern PSI_file_key key_file_charset, key_file_cnf;
|
||||
#endif /* HAVE_PSI_INTERFACE */
|
||||
|
||||
typedef struct {
|
||||
ulonglong counter;
|
||||
uint block_length, last_block_length;
|
||||
uchar key[MY_AES_BLOCK_SIZE];
|
||||
ulonglong inbuf_counter;
|
||||
} IO_CACHE_CRYPT;
|
||||
|
||||
extern int (*_my_b_encr_read)(IO_CACHE *info,uchar *Buffer,size_t Count);
|
||||
extern int (*_my_b_encr_write)(IO_CACHE *info,const uchar *Buffer,size_t Count);
|
||||
|
||||
#ifdef SAFEMALLOC
|
||||
void *sf_malloc(size_t size, myf my_flags);
|
||||
void *sf_realloc(void *ptr, size_t size, myf my_flags);
|
||||
@ -116,3 +132,7 @@ extern File my_win_dup(File fd);
|
||||
extern File my_win_sopen(const char *path, int oflag, int shflag, int perm);
|
||||
extern File my_open_osfhandle(HANDLE handle, int oflag);
|
||||
#endif
|
||||
|
||||
C_MODE_END
|
||||
|
||||
#endif
|
||||
|
@ -130,9 +130,8 @@ SET (SQL_SOURCE
|
||||
opt_index_cond_pushdown.cc opt_subselect.cc
|
||||
opt_table_elimination.cc sql_expression_cache.cc
|
||||
gcalc_slicescan.cc gcalc_tools.cc
|
||||
threadpool_common.cc
|
||||
../sql-common/mysql_async.c
|
||||
my_apc.cc my_apc.h
|
||||
threadpool_common.cc ../sql-common/mysql_async.c
|
||||
my_apc.cc my_apc.h mf_iocache_encr.cc
|
||||
my_json_writer.cc my_json_writer.h
|
||||
rpl_gtid.cc rpl_parallel.cc
|
||||
${WSREP_SOURCES}
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "sql_plugin.h"
|
||||
#include <my_crypt.h>
|
||||
|
||||
void init_io_cache_encryption();
|
||||
|
||||
/* there can be only one encryption plugin enabled */
|
||||
static plugin_ref encryption_manager= 0;
|
||||
struct encryption_service_st encryption_handler;
|
||||
@ -79,6 +81,8 @@ int initialize_encryption_plugin(st_plugin_int *plugin)
|
||||
encryption_handler.encryption_key_get_latest_version_func=
|
||||
handle->get_latest_key_version; // must be the last
|
||||
|
||||
init_io_cache_encryption();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -100,6 +104,7 @@ int finalize_encryption_plugin(st_plugin_int *plugin)
|
||||
if (encryption_manager)
|
||||
plugin_unlock(NULL, encryption_manager);
|
||||
encryption_manager= 0;
|
||||
init_io_cache_encryption();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
254
sql/mf_iocache_encr.cc
Normal file
254
sql/mf_iocache_encr.cc
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
Copyright (c) 2015, 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-1301 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)
|
||||
{
|
||||
info->read_pos= info->read_end= info->buffer;
|
||||
info->pos_in_file= pos_in_file;
|
||||
info->error= 0;
|
||||
DBUG_RETURN(MY_TEST(Count));
|
||||
}
|
||||
|
||||
if (info->seek_not_done)
|
||||
{
|
||||
size_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;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
size_t copied;
|
||||
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 - (ebuffer - wbuffer);
|
||||
set_iv(iv, pos_in_file, crypt_data->inbuf_counter);
|
||||
|
||||
if (encryption_decrypt(ebuffer, elength, info->buffer, &length,
|
||||
crypt_data->key, sizeof(crypt_data->key),
|
||||
iv, sizeof(iv), 0, keyid, keyver))
|
||||
{
|
||||
my_errno= 1;
|
||||
DBUG_RETURN(info->error= -1);
|
||||
}
|
||||
|
||||
DBUG_ASSERT(length <= info->buffer_length);
|
||||
|
||||
copied= MY_MIN(Count, length - pos_offset);
|
||||
|
||||
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= 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 == 0);
|
||||
|
||||
if ((mysql_file_seek(info->file, 0, 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;
|
||||
|
||||
IF_DBUG(crypt_data->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_encrypt(Buffer, length, ebuffer, &elength,
|
||||
crypt_data->key, sizeof(crypt_data->key),
|
||||
iv, sizeof(iv), 0, keyid, keyver))
|
||||
{
|
||||
my_errno= 1;
|
||||
DBUG_RETURN(info->error= -1);
|
||||
}
|
||||
wlength= elength + 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 <= length + MY_AES_BLOCK_SIZE);
|
||||
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.
|
||||
*/
|
||||
void init_io_cache_encryption()
|
||||
{
|
||||
if (encrypt_tmp_files)
|
||||
{
|
||||
keyver= encryption_key_get_latest_version(keyid= 2);
|
||||
if (keyver == ENCRYPTION_KEY_VERSION_INVALID)
|
||||
keyver= encryption_key_get_latest_version(keyid= 1);
|
||||
}
|
||||
else
|
||||
keyver= ENCRYPTION_KEY_VERSION_INVALID;
|
||||
|
||||
if (keyver != ENCRYPTION_KEY_VERSION_INVALID)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
_my_b_encr_read= 0;
|
||||
_my_b_encr_write= 0;
|
||||
}
|
||||
}
|
||||
|
@ -629,7 +629,7 @@ char server_version[SERVER_VERSION_LENGTH];
|
||||
char *mysqld_unix_port, *opt_mysql_tmpdir;
|
||||
ulong thread_handling;
|
||||
|
||||
my_bool encrypt_tmp_disk_tables;
|
||||
my_bool encrypt_tmp_disk_tables, encrypt_tmp_files;
|
||||
|
||||
/** name of reference on left expression in rewritten IN subquery */
|
||||
const char *in_left_expr_name= "<left expr>";
|
||||
|
@ -254,7 +254,7 @@ extern ulong connection_errors_internal;
|
||||
extern ulong connection_errors_max_connection;
|
||||
extern ulong connection_errors_peer_addr;
|
||||
extern ulong log_warnings;
|
||||
extern my_bool encrypt_tmp_disk_tables;
|
||||
extern my_bool encrypt_tmp_disk_tables, encrypt_tmp_files;
|
||||
extern ulong encryption_algorithm;
|
||||
extern const char *encryption_algorithm_names[];
|
||||
|
||||
|
@ -5163,10 +5163,16 @@ static Sys_var_harows Sys_expensive_subquery_limit(
|
||||
|
||||
static Sys_var_mybool Sys_encrypt_tmp_disk_tables(
|
||||
"encrypt_tmp_disk_tables",
|
||||
"Encrypt tmp disk tables (created as part of query execution)",
|
||||
"Encrypt temporary on-disk tables (created as part of query execution)",
|
||||
GLOBAL_VAR(encrypt_tmp_disk_tables),
|
||||
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
|
||||
|
||||
static Sys_var_mybool Sys_encrypt_tmp_files(
|
||||
"encrypt_tmp_files",
|
||||
"Encrypt temporary files (created for filesort, binary log cache, etc)",
|
||||
READ_ONLY GLOBAL_VAR(encrypt_tmp_files),
|
||||
CMD_LINE(OPT_ARG), DEFAULT(TRUE));
|
||||
|
||||
static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
|
||||
{
|
||||
longlong previous_val= thd->variables.pseudo_slave_mode;
|
||||
|
@ -42,6 +42,7 @@ my_bool _ma_read_cache(MARIA_HA *handler, IO_CACHE *info, uchar *buff,
|
||||
my_off_t offset;
|
||||
uchar *in_buff_pos;
|
||||
DBUG_ENTER("_ma_read_cache");
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
|
||||
if (pos < info->pos_in_file)
|
||||
{
|
||||
|
@ -43,6 +43,7 @@ int _mi_read_cache(IO_CACHE *info, uchar *buff, my_off_t pos, uint length,
|
||||
my_off_t offset;
|
||||
uchar *in_buff_pos;
|
||||
DBUG_ENTER("_mi_read_cache");
|
||||
DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
|
||||
|
||||
if (pos < info->pos_in_file)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user