diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/CMakeLists.txt | 5 | ||||
-rw-r--r-- | sql/encryption.cc | 5 | ||||
-rw-r--r-- | sql/mf_iocache_encr.cc | 254 | ||||
-rw-r--r-- | sql/mysqld.cc | 2 | ||||
-rw-r--r-- | sql/mysqld.h | 2 | ||||
-rw-r--r-- | sql/sys_vars.cc | 8 |
6 files changed, 270 insertions, 6 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 522e8076275..2eb65837992 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -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} diff --git a/sql/encryption.cc b/sql/encryption.cc index b108eb6a25c..520eb8b898f 100644 --- a/sql/encryption.cc +++ b/sql/encryption.cc @@ -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; } diff --git a/sql/mf_iocache_encr.cc b/sql/mf_iocache_encr.cc new file mode 100644 index 00000000000..0a72492d743 --- /dev/null +++ b/sql/mf_iocache_encr.cc @@ -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; + } +} + diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 37ae81f896b..2bde88189fb 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -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>"; diff --git a/sql/mysqld.h b/sql/mysqld.h index e1c39431015..ebf55d8fd8a 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -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[]; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 09c743434f5..88e5f1b392b 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -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; |