summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt5
-rw-r--r--sql/encryption.cc5
-rw-r--r--sql/mf_iocache_encr.cc254
-rw-r--r--sql/mysqld.cc2
-rw-r--r--sql/mysqld.h2
-rw-r--r--sql/sys_vars.cc8
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;