Commit b85a0016 authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-8264 encryption for binlog

* Start_encryption_log_event
* --encrypt-binlog command line option

based on google patches.
parent 41d68cab
...@@ -2575,6 +2575,11 @@ void *sql_alloc(size_t size) ...@@ -2575,6 +2575,11 @@ void *sql_alloc(size_t size)
return alloc_root(&s_mem_root, size); return alloc_root(&s_mem_root, size);
} }
struct encryption_service_st encryption_handler=
{
0, 0, 0, 0, 0, 0, 0
};
/* /*
We must include this here as it's compiled with different options for We must include this here as it's compiled with different options for
the server the server
......
...@@ -10,19 +10,19 @@ ...@@ -10,19 +10,19 @@
# #
# Format_description_log_event length = # Format_description_log_event length =
# 19 /* event common header */ + # 19 /* event common header */ +
# 57 /* misc stuff in the Format description header */ + # 58 /* misc stuff in the Format description header */ +
# number of events + # number of events +
# 1 /* Checksum algorithm */ + # 1 /* Checksum algorithm */ +
# 4 /* CRC32 length */ # 4 /* CRC32 length */
# #
# With current number of events = 163, # With current number of events = 164,
# #
# binlog_start_pos = 4 + 19 + 57 + 163 + 1 + 4 = 248. # binlog_start_pos = 4 + 19 + 57 + 163 + 1 + 4 = 249.
# #
############################################################################## ##############################################################################
let $binlog_start_pos=248; let $binlog_start_pos=249;
--disable_query_log --disable_query_log
SET @binlog_start_pos=248; SET @binlog_start_pos=249;
--enable_query_log --enable_query_log
...@@ -4,7 +4,7 @@ if ($binlog_start) ...@@ -4,7 +4,7 @@ if ($binlog_start)
} }
if (!$binlog_start) if (!$binlog_start)
{ {
--let $_binlog_start=248 --let $_binlog_start=249
} }
if ($binlog_file) if ($binlog_file)
{ {
......
...@@ -176,6 +176,8 @@ The following options may be given as the first argument: ...@@ -176,6 +176,8 @@ The following options may be given as the first argument:
--div-precision-increment=# --div-precision-increment=#
Precision of the result of '/' operator will be increased Precision of the result of '/' operator will be increased
on that value on that value
--encrypt-binlog Encrypt binary logs (including relay logs)
(Defaults to on; use --skip-encrypt-binlog to disable.)
--encrypt-tmp-disk-tables --encrypt-tmp-disk-tables
Encrypt temporary on-disk tables (created as part of Encrypt temporary on-disk tables (created as part of
query execution) query execution)
...@@ -1170,6 +1172,7 @@ delayed-insert-limit 100 ...@@ -1170,6 +1172,7 @@ delayed-insert-limit 100
delayed-insert-timeout 300 delayed-insert-timeout 300
delayed-queue-size 1000 delayed-queue-size 1000
div-precision-increment 4 div-precision-increment 4
encrypt-binlog TRUE
encrypt-tmp-disk-tables FALSE encrypt-tmp-disk-tables FALSE
encrypt-tmp-files TRUE encrypt-tmp-files TRUE
enforce-storage-engine (No default value) enforce-storage-engine (No default value)
......
...@@ -71,7 +71,7 @@ insert into t1 values (1) /* will not be applied on slave due to simulation */; ...@@ -71,7 +71,7 @@ insert into t1 values (1) /* will not be applied on slave due to simulation */;
set @@global.debug_dbug='d,simulate_slave_unaware_checksum'; set @@global.debug_dbug='d,simulate_slave_unaware_checksum';
start slave; start slave;
include/wait_for_slave_io_error.inc [errno=1236] include/wait_for_slave_io_error.inc [errno=1236]
Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'Slave can not handle replication events with the checksum that master is configured to log; the first event 'master-bin.000009' at 367, the last event read from 'master-bin.000010' at 4, the last byte read from 'master-bin.000010' at 248.'' Last_IO_Error = 'Got fatal error 1236 from master when reading data from binary log: 'Slave can not handle replication events with the checksum that master is configured to log; the first event 'master-bin.000009' at 368, the last event read from 'master-bin.000010' at 4, the last byte read from 'master-bin.000010' at 249.''
select count(*) as zero from t1; select count(*) as zero from t1;
zero zero
0 0
......
...@@ -695,6 +695,20 @@ NUMERIC_BLOCK_SIZE 1 ...@@ -695,6 +695,20 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME ENCRYPT_BINLOG
SESSION_VALUE NULL
GLOBAL_VALUE ON
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE ON
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Encrypt binary logs (including relay logs)
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 ENCRYPT_TMP_DISK_TABLES VARIABLE_NAME ENCRYPT_TMP_DISK_TABLES
SESSION_VALUE NULL SESSION_VALUE NULL
GLOBAL_VALUE OFF GLOBAL_VALUE OFF
......
...@@ -709,6 +709,20 @@ NUMERIC_BLOCK_SIZE 1 ...@@ -709,6 +709,20 @@ NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL ENUM_VALUE_LIST NULL
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME ENCRYPT_BINLOG
SESSION_VALUE NULL
GLOBAL_VALUE ON
GLOBAL_VALUE_ORIGIN COMPILE-TIME
DEFAULT_VALUE ON
VARIABLE_SCOPE GLOBAL
VARIABLE_TYPE BOOLEAN
VARIABLE_COMMENT Encrypt binary logs (including relay logs)
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 ENCRYPT_TMP_DISK_TABLES VARIABLE_NAME ENCRYPT_TMP_DISK_TABLES
SESSION_VALUE NULL SESSION_VALUE NULL
GLOBAL_VALUE OFF GLOBAL_VALUE OFF
......
...@@ -3457,6 +3457,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name, ...@@ -3457,6 +3457,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
else else
s.checksum_alg= (enum_binlog_checksum_alg)binlog_checksum_options; s.checksum_alg= (enum_binlog_checksum_alg)binlog_checksum_options;
crypto.scheme = 0;
DBUG_ASSERT(s.checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF); DBUG_ASSERT(s.checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF);
if (!s.is_valid()) if (!s.is_valid())
goto err; goto err;
...@@ -3465,6 +3466,26 @@ bool MYSQL_BIN_LOG::open(const char *log_name, ...@@ -3465,6 +3466,26 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
goto err; goto err;
bytes_written+= s.data_written; bytes_written+= s.data_written;
if (encrypt_binlog)
{
uint key_version= encryption_key_get_latest_version(ENCRYPTION_KEY_SYSTEM_DATA);
if (key_version != ENCRYPTION_KEY_VERSION_INVALID &&
key_version != ENCRYPTION_KEY_NOT_ENCRYPTED)
{
if (my_random_bytes(crypto.nonce, sizeof(crypto.nonce)))
goto err;
Start_encryption_log_event sele(1, key_version, crypto.nonce);
sele.checksum_alg= s.checksum_alg;
if (write_event(&sele))
goto err;
// Start_encryption_log_event is written, enable the encryption
if (crypto.init(sele.crypto_scheme, key_version))
goto err;
}
}
if (!is_relay_log) if (!is_relay_log)
{ {
char buf[FN_REFLEN]; char buf[FN_REFLEN];
...@@ -5107,7 +5128,10 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) ...@@ -5107,7 +5128,10 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
bool MYSQL_BIN_LOG::write_event(Log_event *ev, IO_CACHE *file) bool MYSQL_BIN_LOG::write_event(Log_event *ev, IO_CACHE *file)
{ {
Log_event_writer writer(file); Log_event_writer writer(file, &crypto);
if (crypto.scheme && file == &log_file)
writer.ctx= alloca(crypto.ctx_size);
return writer.write(ev); return writer.write(ev);
} }
...@@ -5145,32 +5169,62 @@ bool MYSQL_BIN_LOG::append_no_lock(Log_event* ev) ...@@ -5145,32 +5169,62 @@ bool MYSQL_BIN_LOG::append_no_lock(Log_event* ev)
DBUG_RETURN(error); DBUG_RETURN(error);
} }
bool MYSQL_BIN_LOG::write_event_buffer(uchar* buf, uint len)
bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
{ {
bool error= 0; bool error= 1;
DBUG_ENTER("MYSQL_BIN_LOG::appendv"); uchar *ebuf= 0;
va_list(args); DBUG_ENTER("MYSQL_BIN_LOG::write_event_buffer");
va_start(args,len);
DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
mysql_mutex_assert_owner(&LOCK_log); mysql_mutex_assert_owner(&LOCK_log);
do
if (crypto.scheme != 0)
{ {
if (my_b_append(&log_file,(uchar*) buf,len)) DBUG_ASSERT(crypto.scheme == 1);
{
error= 1; uint elen;
uchar iv[BINLOG_IV_LENGTH];
ebuf= (uchar*)my_safe_alloca(len);
if (!ebuf)
goto err; goto err;
}
bytes_written += len; crypto.set_iv(iv, my_b_append_tell(&log_file));
} while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint)));
/*
we want to encrypt everything, excluding the event length:
massage the data before the encryption
*/
memcpy(buf + EVENT_LEN_OFFSET, buf, 4);
if (encryption_crypt(buf + 4, len - 4,
ebuf + 4, &elen,
crypto.key, crypto.key_length, iv, sizeof(iv),
ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD,
ENCRYPTION_KEY_SYSTEM_DATA, crypto.key_version))
goto err;
DBUG_ASSERT(elen == len - 4);
/* massage the data after the encryption */
memcpy(ebuf, ebuf + EVENT_LEN_OFFSET, 4);
int4store(ebuf + EVENT_LEN_OFFSET, len);
buf= ebuf;
}
if (my_b_append(&log_file, buf, len))
goto err;
bytes_written+= len;
error= 0;
DBUG_PRINT("info",("max_size: %lu",max_size)); DBUG_PRINT("info",("max_size: %lu",max_size));
if (flush_and_sync(0)) if (flush_and_sync(0))
goto err; goto err;
if (my_b_append_tell(&log_file) > max_size) if (my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking(); error= new_file_without_locking();
err: err:
my_safe_afree(ebuf, len);
if (!error) if (!error)
signal_update(); signal_update();
DBUG_RETURN(error); DBUG_RETURN(error);
...@@ -6533,8 +6587,9 @@ class CacheWriter: public Log_event_writer ...@@ -6533,8 +6587,9 @@ class CacheWriter: public Log_event_writer
public: public:
ulong remains; ulong remains;
CacheWriter(THD *thd_arg, IO_CACHE *file_arg, bool do_checksum) CacheWriter(THD *thd_arg, IO_CACHE *file_arg, bool do_checksum,
: Log_event_writer(file_arg), remains(0), thd(thd_arg), first(true) Binlog_crypt_data *cr)
: Log_event_writer(file_arg, cr), remains(0), thd(thd_arg), first(true)
{ checksum_len= do_checksum ? BINLOG_CHECKSUM_LEN : 0; } { checksum_len= do_checksum ? BINLOG_CHECKSUM_LEN : 0; }
~CacheWriter() ~CacheWriter()
...@@ -6585,7 +6640,10 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache) ...@@ -6585,7 +6640,10 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
long val; long val;
ulong end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t ulong end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t
uchar header[LOG_EVENT_HEADER_LEN]; uchar header[LOG_EVENT_HEADER_LEN];
CacheWriter writer(thd, &log_file, binlog_checksum_options); CacheWriter writer(thd, &log_file, binlog_checksum_options, &crypto);
if (crypto.scheme)
writer.ctx= alloca(crypto.ctx_size);
// while there is just one alg the following must hold: // while there is just one alg the following must hold:
DBUG_ASSERT(binlog_checksum_options == BINLOG_CHECKSUM_ALG_OFF || DBUG_ASSERT(binlog_checksum_options == BINLOG_CHECKSUM_ALG_OFF ||
...@@ -9670,6 +9728,13 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, ...@@ -9670,6 +9728,13 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
break; break;
#endif #endif
case START_ENCRYPTION_EVENT:
{
if (fdle->start_decryption((Start_encryption_log_event*) ev))
goto err2;
}
break;
default: default:
/* Nothing. */ /* Nothing. */
break; break;
...@@ -9746,6 +9811,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, ...@@ -9746,6 +9811,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
sql_print_error("Error reading binlog files during recovery. Aborting."); sql_print_error("Error reading binlog files during recovery. Aborting.");
goto err2; goto err2;
} }
fdle->reset_crypto();
} }
if (do_xa) if (do_xa)
......
...@@ -527,6 +527,9 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG ...@@ -527,6 +527,9 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
ulonglong group_commit_trigger_count, group_commit_trigger_timeout; ulonglong group_commit_trigger_count, group_commit_trigger_timeout;
ulonglong group_commit_trigger_lock_wait; ulonglong group_commit_trigger_lock_wait;
/* binlog encryption data */
struct Binlog_crypt_data crypto;
/* pointer to the sync period variable, for binlog this will be /* pointer to the sync period variable, for binlog this will be
sync_binlog_period, for relay log this will be sync_binlog_period, for relay log this will be
sync_relay_log_period sync_relay_log_period
...@@ -740,11 +743,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG ...@@ -740,11 +743,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
bool write_event(Log_event *ev, IO_CACHE *file); bool write_event(Log_event *ev, IO_CACHE *file);
bool write_event(Log_event *ev) { return write_event(ev, &log_file); } bool write_event(Log_event *ev) { return write_event(ev, &log_file); }
/* bool write_event_buffer(uchar* buf,uint len);
v stands for vector
invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
*/
bool appendv(const char* buf,uint len,...);
bool append(Log_event* ev); bool append(Log_event* ev);
bool append_no_lock(Log_event* ev); bool append_no_lock(Log_event* ev);
......
This diff is collapsed.
...@@ -81,6 +81,7 @@ class String; ...@@ -81,6 +81,7 @@ class String;
#define LOG_READ_TRUNC -6 #define LOG_READ_TRUNC -6
#define LOG_READ_TOO_LARGE -7 #define LOG_READ_TOO_LARGE -7
#define LOG_READ_CHECKSUM_FAILURE -8 #define LOG_READ_CHECKSUM_FAILURE -8
#define LOG_READ_DECRYPT -9
#define LOG_EVENT_OFFSET 4 #define LOG_EVENT_OFFSET 4
...@@ -211,6 +212,7 @@ class String; ...@@ -211,6 +212,7 @@ class String;
#define BINLOG_CHECKPOINT_HEADER_LEN 4 #define BINLOG_CHECKPOINT_HEADER_LEN 4
#define GTID_HEADER_LEN 19 #define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4 #define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
/* /*
Max number of possible extra bytes in a replication event compared to a Max number of possible extra bytes in a replication event compared to a
...@@ -666,6 +668,8 @@ enum Log_event_type ...@@ -666,6 +668,8 @@ enum Log_event_type
*/ */
GTID_LIST_EVENT= 163, GTID_LIST_EVENT= 163,
START_ENCRYPTION_EVENT= 164,
/* Add new MariaDB events here - right above this comment! */ /* Add new MariaDB events here - right above this comment! */
ENUM_END_EVENT /* end marker */ ENUM_END_EVENT /* end marker */
...@@ -790,12 +794,13 @@ typedef struct st_print_event_info ...@@ -790,12 +794,13 @@ typedef struct st_print_event_info
/** /**
This class encapsulates writing of Log_event objects to IO_CACHE. This class encapsulates writing of Log_event objects to IO_CACHE.
Automatically calculates the checksum if necessary. Automatically calculates the checksum and encrypts the data, if necessary.
*/ */
class Log_event_writer class Log_event_writer
{ {
public: public:
ulonglong bytes_written; ulonglong bytes_written;
void *ctx; ///< Encryption context or 0 if no encryption is needed
uint checksum_len; uint checksum_len;
int write(Log_event *ev); int write(Log_event *ev);
int write_header(uchar *pos, size_t len); int write_header(uchar *pos, size_t len);
...@@ -803,7 +808,9 @@ class Log_event_writer ...@@ -803,7 +808,9 @@ class Log_event_writer
int write_footer(); int write_footer();
my_off_t pos() { return my_b_safe_tell(file); } my_off_t pos() { return my_b_safe_tell(file); }
Log_event_writer(IO_CACHE *file_arg) : bytes_written(0), file(file_arg) { } Log_event_writer(IO_CACHE *file_arg, Binlog_crypt_data *cr= 0)
: bytes_written(0), ctx(0),
file(file_arg), crypto(cr) { }
private: private:
IO_CACHE *file; IO_CACHE *file;
...@@ -811,8 +818,17 @@ Log_event_writer(IO_CACHE *file_arg) : bytes_written(0), file(file_arg) { } ...@@ -811,8 +818,17 @@ Log_event_writer(IO_CACHE *file_arg) : bytes_written(0), file(file_arg) { }
Placeholder for event checksum while writing to binlog. Placeholder for event checksum while writing to binlog.
*/ */
ha_checksum crc; ha_checksum crc;
/**
Encryption data (key, nonce). Only used if ctx != 0.
*/
Binlog_crypt_data *crypto;
/**
Event length to be written into the next encrypted block
*/
uint event_len;
int write_internal(const uchar *pos, size_t len); int write_internal(const uchar *pos, size_t len);
int encrypt_and_write(const uchar *pos, size_t len);
int maybe_write_event_len(uchar *pos, size_t len);
}; };
/** /**
...@@ -1163,6 +1179,7 @@ class Log_event ...@@ -1163,6 +1179,7 @@ class Log_event
@retval LOG_READ_TOO_LARGE event too large @retval LOG_READ_TOO_LARGE event too large
*/ */
static int read_log_event(IO_CACHE* file, String* packet, static int read_log_event(IO_CACHE* file, String* packet,
const Format_description_log_event *fdle,
enum enum_binlog_checksum_alg checksum_alg_arg); enum enum_binlog_checksum_alg checksum_alg_arg);
/* /*
The value is set by caller of FD constructor and The value is set by caller of FD constructor and
...@@ -1289,7 +1306,6 @@ class Log_event ...@@ -1289,7 +1306,6 @@ class Log_event
const char* get_type_str(); const char* get_type_str();
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
public:
/** /**
Apply the event to the database. Apply the event to the database.
...@@ -1381,6 +1397,7 @@ class Log_event ...@@ -1381,6 +1397,7 @@ class Log_event
case HEARTBEAT_LOG_EVENT: case HEARTBEAT_LOG_EVENT:
case BINLOG_CHECKPOINT_EVENT: case BINLOG_CHECKPOINT_EVENT:
case GTID_LIST_EVENT: case GTID_LIST_EVENT:
case START_ENCRYPTION_EVENT:
return false; return false;
default: default:
...@@ -2457,6 +2474,73 @@ class Start_log_event_v3: public Log_event ...@@ -2457,6 +2474,73 @@ class Start_log_event_v3: public Log_event
#endif #endif
}; };
/**
@class Start_encryption_log_event
Start_encryption_log_event marks the beginning of encrypted data (all events
after this event are encrypted).
It contains the cryptographic scheme used for the encryption as well as any
data required to decrypt (except the actual key).
For binlog cryptoscheme 1: key version, and nonce for iv generation.
*/
class Start_encryption_log_event : public Log_event
{
public:
#ifdef MYSQL_SERVER
Start_encryption_log_event(uint crypto_scheme_arg, uint key_version_arg,
const uchar* nonce_arg)
: crypto_scheme(crypto_scheme_arg), key_version(key_version_arg)
{
cache_type = EVENT_NO_CACHE;
DBUG_ASSERT(crypto_scheme == 1);
memcpy(nonce, nonce_arg, BINLOG_NONCE_LENGTH);
}
bool write_data_body()
{
uchar scheme_buf= crypto_scheme;
uchar key_version_buf[BINLOG_KEY_VERSION_LENGTH];
int4store(key_version_buf, key_version);
return write_data(&scheme_buf, sizeof(scheme_buf)) ||
write_data(key_version_buf, sizeof(key_version_buf)) ||
write_data(nonce, BINLOG_NONCE_LENGTH);
}
#else
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
Start_encryption_log_event(
const char* buf, uint event_len,
const Format_description_log_event* description_event);
bool is_valid() const { return crypto_scheme == 1; }
Log_event_type get_type_code() { return START_ENCRYPTION_EVENT; }
int get_data_size()
{
return BINLOG_CRYPTO_SCHEME_LENGTH + BINLOG_KEY_VERSION_LENGTH +
BINLOG_NONCE_LENGTH;
}
uint crypto_scheme;
uint key_version;
uchar nonce[BINLOG_NONCE_LENGTH];
protected:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_apply_event(rpl_group_info* rgi);
virtual int do_update_pos(rpl_group_info *rgi);
virtual enum_skip_reason do_shall_skip(rpl_group_info* rgi)
{
return Log_event::EVENT_SKIP_NOT;
}
#endif
};
/** /**
@class Format_description_log_event @class Format_description_log_event
...@@ -2534,6 +2618,17 @@ class Format_description_log_event: public Start_log_event_v3 ...@@ -2534,6 +2618,17 @@ class Format_description_log_event: public Start_log_event_v3
return FORMAT_DESCRIPTION_HEADER_LEN; return FORMAT_DESCRIPTION_HEADER_LEN;
} }
Binlog_crypt_data crypto_data;
bool start_decryption(Start_encryption_log_event* sele);
void copy_crypto_data(const Format_description_log_event* o)
{
crypto_data= o->crypto_data;
}
void reset_crypto()
{
crypto_data.scheme= 0;
}
void calc_server_version_split(); void calc_server_version_split();
static bool is_version_before_checksum(const master_version_split *version_split); static bool is_version_before_checksum(const master_version_split *version_split);
protected: protected:
......
...@@ -629,6 +629,7 @@ char server_version[SERVER_VERSION_LENGTH]; ...@@ -629,6 +629,7 @@ char server_version[SERVER_VERSION_LENGTH];
char *mysqld_unix_port, *opt_mysql_tmpdir; char *mysqld_unix_port, *opt_mysql_tmpdir;
ulong thread_handling; ulong thread_handling;
my_bool encrypt_binlog;
my_bool encrypt_tmp_disk_tables, encrypt_tmp_files; my_bool encrypt_tmp_disk_tables, encrypt_tmp_files;
/** name of reference on left expression in rewritten IN subquery */ /** name of reference on left expression in rewritten IN subquery */
......
...@@ -255,6 +255,7 @@ extern ulong connection_errors_internal; ...@@ -255,6 +255,7 @@ extern ulong connection_errors_internal;
extern ulong connection_errors_max_connection; extern ulong connection_errors_max_connection;
extern ulong connection_errors_peer_addr; extern ulong connection_errors_peer_addr;
extern ulong log_warnings; extern ulong log_warnings;
extern my_bool encrypt_binlog;
extern my_bool encrypt_tmp_disk_tables, encrypt_tmp_files; extern my_bool encrypt_tmp_disk_tables, encrypt_tmp_files;
extern ulong encryption_algorithm; extern ulong encryption_algorithm;
extern const char *encryption_algorithm_names[]; extern const char *encryption_algorithm_names[];
......
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
#ifndef RPL_CONSTANTS_H #ifndef RPL_CONSTANTS_H
#define RPL_CONSTANTS_H #define RPL_CONSTANTS_H
#include <my_sys.h>
#include <my_crypt.h>
/** /**
Enumeration of the incidents that can occur for the server. Enumeration of the incidents that can occur for the server.
*/ */
...@@ -78,4 +81,32 @@ enum enum_binlog_checksum_alg { ...@@ -78,4 +81,32 @@ enum enum_binlog_checksum_alg {
// or events from checksum-unaware servers // or events from checksum-unaware servers
}; };
#define BINLOG_CRYPTO_SCHEME_LENGTH 1
#define BINLOG_KEY_VERSION_LENGTH 4
#define BINLOG_IV_LENGTH MY_AES_BLOCK_SIZE
#define BINLOG_IV_OFFS_LENGTH 4
#define BINLOG_NONCE_LENGTH (BINLOG_IV_LENGTH - BINLOG_IV_OFFS_LENGTH)
struct Binlog_crypt_data {
uint scheme;
uint key_version, key_length, ctx_size;
uchar key[MY_AES_MAX_KEY_LENGTH];
uchar nonce[BINLOG_NONCE_LENGTH];
int init(uint sch, uint kv)
{
scheme= sch;
ctx_size= encryption_ctx_size(ENCRYPTION_KEY_SYSTEM_DATA, kv);
key_version= kv;
key_length= sizeof(key);
return encryption_key_get(ENCRYPTION_KEY_SYSTEM_DATA, kv, key, &key_length);
}
void set_iv(uchar* iv, uint32 offs) const
{
memcpy(iv, nonce, BINLOG_NONCE_LENGTH);
int4store(iv + BINLOG_NONCE_LENGTH, offs);
}
};
#endif /* RPL_CONSTANTS_H */ #endif /* RPL_CONSTANTS_H */
...@@ -570,16 +570,26 @@ retry_event_group(rpl_group_info *rgi, rpl_parallel_thread *rpt, ...@@ -570,16 +570,26 @@ retry_event_group(rpl_group_info *rgi, rpl_parallel_thread *rpt,
err= 1; err= 1;
goto check_retry; goto check_retry;
} }
description_event->reset_crypto();
/* Loop to try again on the new log file. */ /* Loop to try again on the new log file. */
} }
event_type= ev->get_type_code(); event_type= ev->get_type_code();
if (event_type == FORMAT_DESCRIPTION_EVENT) if (event_type == FORMAT_DESCRIPTION_EVENT)
{ {
Format_description_log_event *newde= (Format_description_log_event*)ev;
newde->copy_crypto_data(description_event);
delete description_event; delete description_event;
description_event= (Format_description_log_event *)ev; description_event= newde;
continue; continue;
} else if (!Log_event::is_group_event(event_type)) }
else if (event_type == START_ENCRYPTION_EVENT)
{
description_event->start_decryption((Start_encryption_log_event*)ev);
delete ev;
continue;
}
else if (!Log_event::is_group_event(event_type))
{ {
delete ev; delete ev;
continue; continue;
......
...@@ -547,9 +547,12 @@ read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos, ...@@ -547,9 +547,12 @@ read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos,
typ= ev->get_type_code(); typ= ev->get_type_code();
if (typ == FORMAT_DESCRIPTION_EVENT) if (typ == FORMAT_DESCRIPTION_EVENT)
{ {
Format_description_log_event *old= fdev;
DBUG_PRINT("info",("found Format_description_log_event")); DBUG_PRINT("info",("found Format_description_log_event"));
delete fdev;
fdev= (Format_description_log_event*) ev; fdev= (Format_description_log_event*) ev;
fdev->copy_crypto_data(old);
delete old;
/* /*
As ev was returned by read_log_event, it has passed is_valid(), so As ev was returned by read_log_event, it has passed is_valid(), so
my_malloc() in ctor worked, no need to check again. my_malloc() in ctor worked, no need to check again.
...@@ -571,6 +574,17 @@ read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos, ...@@ -571,6 +574,17 @@ read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos,
or Format_desc. or Format_desc.
*/ */
} }
else if (typ == START_ENCRYPTION_EVENT)
{
if (fdev->start_decryption((Start_encryption_log_event*) ev))
{
*errmsg= "Unable to set up decryption of binlog.";
delete ev;
delete fdev;
return NULL;
}
delete ev;
}
else else
{ {
DBUG_PRINT("info",("found event of another type=%d", typ)); DBUG_PRINT("info",("found event of another type=%d", typ));
......
...@@ -5607,6 +5607,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) ...@@ -5607,6 +5607,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE; error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
goto err; goto err;
} }
tmp->copy_crypto_data(mi->rli.relay_log.description_event_for_queue);
delete mi->rli.relay_log.description_event_for_queue; delete mi->rli.relay_log.description_event_for_queue;
mi->rli.relay_log.description_event_for_queue= tmp; mi->rli.relay_log.description_event_for_queue= tmp;
if (tmp->checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF) if (tmp->checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF)
...@@ -6028,7 +6029,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len) ...@@ -6028,7 +6029,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
} }
else else
{ {
if (likely(!(rli->relay_log.appendv(buf,event_len,0)))) if (likely(!rli->relay_log.write_event_buffer((uchar*)buf, event_len)))
{ {
mi->master_log_pos+= inc_pos; mi->master_log_pos+= inc_pos;
DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos)); DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
...@@ -6778,6 +6779,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) ...@@ -6778,6 +6779,7 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
mysql_file_close(rli->cur_log_fd, MYF(MY_WME)); mysql_file_close(rli->cur_log_fd, MYF(MY_WME));
rli->cur_log_fd = -1; rli->cur_log_fd = -1;
rli->last_inuse_relaylog->completed= true; rli->last_inuse_relaylog->completed= true;
rli->relay_log.description_event_for_exec->reset_crypto();
if (relay_log_purge) if (relay_log_purge)
{ {
......
...@@ -644,6 +644,9 @@ void set_read_error(binlog_send_info *info, int error) ...@@ -644,6 +644,9 @@ void set_read_error(binlog_send_info *info, int error)
case LOG_READ_CHECKSUM_FAILURE: case LOG_READ_CHECKSUM_FAILURE:
info->errmsg= "event read from binlog did not pass crc check"; info->errmsg= "event read from binlog did not pass crc check";
break; break;
case LOG_READ_DECRYPT:
info->errmsg= "event decryption failure";
break;
default: default:
info->errmsg= "unknown error reading log event on the master"; info->errmsg= "unknown error reading log event on the master";
break; break;
...@@ -918,9 +921,14 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list) ...@@ -918,9 +921,14 @@ get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
typ= ev->get_type_code(); typ= ev->get_type_code();
if (typ == GTID_LIST_EVENT) if (typ == GTID_LIST_EVENT)
break; /* Done, found it */ break; /* Done, found it */
if (typ == START_ENCRYPTION_EVENT)
{
if (fdle->start_decryption((Start_encryption_log_event*) ev))
errormsg= "Could not set up decryption for binlog.";
}
delete ev; delete ev;
if (typ == ROTATE_EVENT || typ == STOP_EVENT || if (typ == ROTATE_EVENT || typ == STOP_EVENT ||
typ == FORMAT_DESCRIPTION_EVENT) typ == FORMAT_DESCRIPTION_EVENT || typ == START_ENCRYPTION_EVENT)
continue; /* Continue looking */ continue; /* Continue looking */
/* We did not find any Gtid_list_log_event, must be old binlog. */ /* We did not find any Gtid_list_log_event, must be old binlog. */
...@@ -1437,7 +1445,7 @@ gtid_state_from_pos(const char *name, uint32 offset, ...@@ -1437,7 +1445,7 @@ gtid_state_from_pos(const char *name, uint32 offset,
break; break;
packet.length(0); packet.length(0);
err= Log_event::read_log_event(&cache, &packet, err= Log_event::read_log_event(&cache, &packet, fdev,
opt_master_verify_checksum ? current_checksum_alg opt_master_verify_checksum ? current_checksum_alg
: BINLOG_CHECKSUM_ALG_OFF); : BINLOG_CHECKSUM_ALG_OFF);
if (err) if (err)
...@@ -1474,6 +1482,20 @@ gtid_state_from_pos(const char *name, uint32 offset, ...@@ -1474,6 +1482,20 @@ gtid_state_from_pos(const char *name, uint32 offset,
delete fdev; delete fdev;
fdev= tmp; fdev= tmp;
} }
else if (typ == START_ENCRYPTION_EVENT)
{
uint sele_len = packet.length();
if (current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
{
sele_len -= BINLOG_CHECKSUM_LEN;
}
Start_encryption_log_event sele(packet.ptr(), sele_len, fdev);
if (fdev->start_decryption(&sele))
{
errormsg= "Could not start decryption of binlog.";
goto end;
}
}
else if (typ != FORMAT_DESCRIPTION_EVENT && !found_format_description_event) else if (typ != FORMAT_DESCRIPTION_EVENT && !found_format_description_event)
{ {
errormsg= "Did not find format description log event while searching " errormsg= "Did not find format description log event while searching "
...@@ -2216,9 +2238,10 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log, ...@@ -2216,9 +2238,10 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
the binlog the binlog
*/ */
info->last_pos= my_b_tell(log); info->last_pos= my_b_tell(log);
error= Log_event::read_log_event(log, packet, error= Log_event::read_log_event(log, packet, info->fdev,
opt_master_verify_checksum opt_master_verify_checksum
? info->current_checksum_alg : BINLOG_CHECKSUM_ALG_OFF); ? info->current_checksum_alg
: BINLOG_CHECKSUM_ALG_OFF);
linfo->pos= my_b_tell(log); linfo->pos= my_b_tell(log);
if (error) if (error)
...@@ -2268,10 +2291,13 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log, ...@@ -2268,10 +2291,13 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
return 1; return 1;
} }
uint ev_len= packet->length() - ev_offset;
if (info->current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
ev_len-= BINLOG_CHECKSUM_LEN;
Format_description_log_event *tmp; Format_description_log_event *tmp;
if (!(tmp= new Format_description_log_event(packet->ptr()+ev_offset, if (!(tmp= new Format_description_log_event(packet->ptr() + ev_offset,
packet->length()-ev_offset, ev_len, info->fdev)))
info->fdev)))
{ {
info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG; info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG;
info->errmsg= "Corrupt Format_description event found " info->errmsg= "Corrupt Format_description event found "
...@@ -2338,6 +2364,57 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log, ...@@ -2338,6 +2364,57 @@ static int send_format_descriptor_event(binlog_send_info *info, IO_CACHE *log,
return 1; return 1;
} }
/*
Read the following Start_encryption_log_event but don't send it to slave.
Slave doesn't need to know whether master's binlog is encrypted,
and if it'll want to encrypt its logs, it should generate its own
random nonce, not use the one from the master.
*/
packet->length(0);
info->last_pos= linfo->pos;
error= Log_event::read_log_event(log, packet, info->fdev,
opt_master_verify_checksum
? info->current_checksum_alg
: BINLOG_CHECKSUM_ALG_OFF);
linfo->pos= my_b_tell(log);
if (error)
{
set_read_error(info, error);
return 1;
}
event_type= (Log_event_type)((uchar)(*packet)[LOG_EVENT_OFFSET]);
if (event_type == START_ENCRYPTION_EVENT)
{
Start_encryption_log_event *sele= (Start_encryption_log_event *)
Log_event::read_log_event(packet->ptr(), packet->length(), &info->errmsg,
info->fdev, BINLOG_CHECKSUM_ALG_OFF);
if (!sele)
{
info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG;
return 1;
}
if (info->fdev->start_decryption(sele))
{
info->error= ER_MASTER_FATAL_ERROR_READING_BINLOG;
info->errmsg= "Could not decrypt binlog: encryption key error";
return 1;
}
delete sele;
}
else if (start_pos == BIN_LOG_HEADER_SIZE)
{
/*
not Start_encryption_log_event - seek back. But only if
send_one_binlog_file() isn't going to seek anyway
*/
my_b_seek(log, info->last_pos);
linfo->pos= info->last_pos;
}
/** all done */ /** all done */
return 0; return 0;
} }
...@@ -2547,7 +2624,7 @@ static int send_events(binlog_send_info *info, IO_CACHE* log, LOG_INFO* linfo, ...@@ -2547,7 +2624,7 @@ static int send_events(binlog_send_info *info, IO_CACHE* log, LOG_INFO* linfo,
return 1; return 1;
info->last_pos= linfo->pos; info->last_pos= linfo->pos;
error= Log_event::read_log_event(log, packet, error= Log_event::read_log_event(log, packet, info->fdev,
opt_master_verify_checksum ? info->current_checksum_alg opt_master_verify_checksum ? info->current_checksum_alg
: BINLOG_CHECKSUM_ALG_OFF); : BINLOG_CHECKSUM_ALG_OFF);
linfo->pos= my_b_tell(log); linfo->pos= my_b_tell(log);
...@@ -2598,8 +2675,9 @@ static int send_events(binlog_send_info *info, IO_CACHE* log, LOG_INFO* linfo, ...@@ -2598,8 +2675,9 @@ static int send_events(binlog_send_info *info, IO_CACHE* log, LOG_INFO* linfo,
}); });
#endif #endif
if ((info->errmsg= send_event_to_slave(info, event_type, log, if (event_type != START_ENCRYPTION_EVENT &&
ev_offset, &info->error_gtid))) ((info->errmsg= send_event_to_slave(info, event_type, log,
ev_offset, &info->error_gtid))))
return 1; return 1;
if (unlikely(info->send_fake_gtid_list) && if (unlikely(info->send_fake_gtid_list) &&
...@@ -3876,38 +3954,52 @@ bool mysql_show_binlog_events(THD* thd) ...@@ -3876,38 +3954,52 @@ bool mysql_show_binlog_events(THD* thd)
Read the first event in case it's a Format_description_log_event, to Read the first event in case it's a Format_description_log_event, to
know the format. If there's no such event, we are 3.23 or 4.x. This know the format. If there's no such event, we are 3.23 or 4.x. This
code, like before, can't read 3.23 binlogs. code, like before, can't read 3.23 binlogs.
Also read the second event, in case it's a Start_encryption_log_event.
This code will fail on a mixed relay log (one which has Format_desc then This code will fail on a mixed relay log (one which has Format_desc then
Rotate then Format_desc). Rotate then Format_desc).
*/ */
ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event,
opt_master_verify_checksum); my_off_t scan_pos = BIN_LOG_HEADER_SIZE;
if (ev) while (scan_pos < pos)
{ {
ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event,
opt_master_verify_checksum);
scan_pos = my_b_tell(&log);
if (ev == NULL || !ev->is_valid())
{
mysql_mutex_unlock(log_lock);
errmsg = "Wrong offset or I/O error";
goto err;
}
if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT) if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
{ {
delete description_event; delete description_event;
description_event= (Format_description_log_event*) ev; description_event= (Format_description_log_event*) ev;
} }
else else
{
if (ev->get_type_code() == START_ENCRYPTION_EVENT)
{
if (description_event->start_decryption((Start_encryption_log_event*) ev))
{
delete ev;
mysql_mutex_unlock(log_lock);
errmsg = "Could not initialize decryption of binlog.";
goto err;
}
}
delete ev; delete ev;
break;
}
} }
my_b_seek(&log, pos); my_b_seek(&log, pos);
if (!description_event->is_valid())
{
errmsg="Invalid Format_description event; could be out of memory";
goto err;
}
for (event_count = 0; for (event_count = 0;
(ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0, (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0,
description_event, description_event,
opt_master_verify_checksum)); ) opt_master_verify_checksum)); )
{ {
if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
description_event->checksum_alg= ev->checksum_alg;
if (event_count >= limit_start && if (event_count >= limit_start &&
ev->net_send(thd, protocol, linfo.log_file_name, pos)) ev->net_send(thd, protocol, linfo.log_file_name, pos))
{ {
...@@ -3917,8 +4009,30 @@ bool mysql_show_binlog_events(THD* thd) ...@@ -3917,8 +4009,30 @@ bool mysql_show_binlog_events(THD* thd)
goto err; goto err;
} }
if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
{
Format_description_log_event* new_fdle=
(Format_description_log_event*) ev;
new_fdle->copy_crypto_data(description_event);
delete description_event;
description_event= new_fdle;
}
else
{
if (ev->get_type_code() == START_ENCRYPTION_EVENT)
{
if (description_event->start_decryption((Start_encryption_log_event*) ev))
{
errmsg = "Error starting decryption";
delete ev;
mysql_mutex_unlock(log_lock);
goto err;
}
}
delete ev;
}
pos = my_b_tell(&log); pos = my_b_tell(&log);
delete ev;
if (++event_count >= limit_end) if (++event_count >= limit_end)
break; break;
......
...@@ -467,6 +467,10 @@ class String ...@@ -467,6 +467,10 @@ class String
} }
return false; return false;
} }
bool append_hex(const uchar *src, uint32 srclen)
{
return append_hex((const char*)src, srclen);
}
bool fill(uint32 max_length,char fill); bool fill(uint32 max_length,char fill);
void strip_sp(); void strip_sp();
friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs); friend int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
......
...@@ -1132,7 +1132,6 @@ static Sys_var_mybool Sys_log_bin( ...@@ -1132,7 +1132,6 @@ static Sys_var_mybool Sys_log_bin(
"log_bin", "Whether the binary log is enabled", "log_bin", "Whether the binary log is enabled",
READ_ONLY GLOBAL_VAR(opt_bin_log), NO_CMD_LINE, DEFAULT(FALSE)); READ_ONLY GLOBAL_VAR(opt_bin_log), NO_CMD_LINE, DEFAULT(FALSE));
static Sys_var_mybool Sys_trust_function_creators( static Sys_var_mybool Sys_trust_function_creators(
"log_bin_trust_function_creators", "log_bin_trust_function_creators",
"If set to FALSE (the default), then when --log-bin is used, creation " "If set to FALSE (the default), then when --log-bin is used, creation "
...@@ -5215,6 +5214,11 @@ static Sys_var_mybool Sys_encrypt_tmp_files( ...@@ -5215,6 +5214,11 @@ static Sys_var_mybool Sys_encrypt_tmp_files(
READ_ONLY GLOBAL_VAR(encrypt_tmp_files), READ_ONLY GLOBAL_VAR(encrypt_tmp_files),
CMD_LINE(OPT_ARG), DEFAULT(TRUE)); CMD_LINE(OPT_ARG), DEFAULT(TRUE));
static Sys_var_mybool Sys_binlog_encryption(
"encrypt_binlog", "Encrypt binary logs (including relay logs)",
READ_ONLY GLOBAL_VAR(encrypt_binlog), CMD_LINE(OPT_ARG),
DEFAULT(TRUE));
static const char *binlog_row_image_names[]= {"MINIMAL", "NOBLOB", "FULL", NullS}; static const char *binlog_row_image_names[]= {"MINIMAL", "NOBLOB", "FULL", NullS};
static Sys_var_enum Sys_binlog_row_image( static Sys_var_enum Sys_binlog_row_image(
"binlog_row_image", "binlog_row_image",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment