diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index f62edbff2..dac910159 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -49,6 +49,7 @@ #include #include #include +#include #define BINLOG_FNAMELEN 255 #define BLR_PROTOCOL "MySQLBackend" @@ -60,6 +61,7 @@ #define BINLOG_EVENT_HDR_LEN 19 #define BINLOG_EVENT_CRC_ALGO_TYPE 1 #define BINLOG_EVENT_CRC_SIZE 4 +/* BINLOG_EVENT_LEN_OFFSET points to event_size in event_header */ #define BINLOG_EVENT_LEN_OFFSET 9 #define BINLOG_ENCRYPTION_ALGORYTHM_NAME_LEN 13 #define BINLOG_FATAL_ERROR_READING 1236 @@ -315,6 +317,7 @@ typedef struct blfile int refcnt; /*< Reference count for file */ BLCACHE *cache; /*< Record cache for this file */ SPINLOCK lock; /*< The file lock */ + void *encryption_ctx; /*< The encryption context */ struct blfile *next; /*< Next file in list */ } BLFILE; @@ -402,6 +405,7 @@ typedef struct router_slave THREAD lsi_sender_tid; /*< Who sent */ char lsi_binlog_name[BINLOG_FNAMELEN + 1]; /*< Which binlog file */ uint32_t lsi_binlog_pos; /*< What position */ + void *encryption_ctx; /*< Encryption context */ #if defined(SS_DEBUG) skygw_chk_t rses_chk_tail; #endif @@ -470,6 +474,7 @@ typedef struct binlog_encryption_setup char *key_management_filename; uint8_t *keys; } BINLOG_ENCRYPTION_SETUP; + /** * The per instance data for the router. */ @@ -559,6 +564,52 @@ typedef struct router_instance struct router_instance *next; } ROUTER_INSTANCE; +/** + * Binlog encryption context of slave binlog file + */ + +typedef struct slave_encryption_ctx +{ + uint8_t binlog_crypto_scheme; /**< Encryption scheme */ + uint32_t binlog_key_version; /**< Encryption key version */ + uint8_t nonce[AES_BLOCK_SIZE]; /**< nonce (random bytes) of current binlog. + * These bytes + the binlog event current pos + * form the encrryption IV for the event */ + char *log_file; /**< The log file the client has requested */ + uint32_t first_enc_event_pos; /**< The position of first encrypted event + * It's the first event afte Start_encryption_event + * Which is after FDE */ +} SLAVE_ENCRYPTION_CTX; + +/** + * Binlog encryption context of binlog file + */ + +typedef struct binlog_encryption_ctx +{ + uint8_t binlog_crypto_scheme; /**< Encryption scheme */ + uint32_t binlog_key_version; /**< Encryption key version */ + uint8_t nonce[AES_BLOCK_SIZE]; /**< nonce (random bytes) of current binlog. + * These bytes + the binlog event current pos + * form the encrryption IV for the event */ + char *binlog_file; /**< Current binlog file being encrypted */ +} BINLOG_ENCRYPTION_CTX; + +/** + * Defines and offsets for binlog encryption + * + * BLRM_FDE_EVENT_TYPES_OFFSET is the offset in FDE event content that points to + * the number of events the master server supports. + */ +#define BLRM_FDE_EVENT_TYPES_OFFSET (2 + 50 + 4 + 1) +#define BLRM_CRYPTO_SCHEME_LENGTH 1 +#define BLRM_KEY_VERSION_LENGTH 4 +#define BLRM_IV_LENGTH AES_BLOCK_SIZE +#define BLRM_IV_OFFS_LENGTH 4 +#define BLRM_NONCE_LENGTH (BLRM_IV_LENGTH - BLRM_IV_OFFS_LENGTH) + + + /** * State machine for the master to MaxScale replication */ diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index dbc45ce6b..ee0da8900 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -941,10 +941,11 @@ newSession(ROUTER *instance, SESSION *session) slave->mariadb10_compat = false; slave->heartbeat = 0; slave->lastEventReceived = 0; + slave->encryption_ctx = NULL; /** - * Add this session to the list of active sessions. - */ + * Add this session to the list of active sessions. + */ spinlock_acquire(&inst->lock); slave->next = inst->slaves; inst->slaves = slave; @@ -1021,6 +1022,10 @@ static void freeSession(ROUTER* router_instance, { MXS_FREE(slave->passwd); } + if (slave->encryption_ctx) + { + MXS_FREE(slave->encryption_ctx); + } MXS_FREE(slave); } diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index dcc7a056e..04961e804 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -77,6 +77,7 @@ extern uint32_t extract_field(uint8_t *src, int bits); static void blr_format_event_size(double *event_size, char *label); extern int MaxScaleUptime(); extern void encode_value(unsigned char *data, unsigned int value, int len); +extern void blr_extract_header(register uint8_t *ptr, register REP_HEADER *hdr); typedef struct binlog_event_desc { @@ -108,19 +109,6 @@ typedef enum BLRM_START_ENCRYPTION /*< Start Encryption event */ } generated_event_t; -/** - * The offset in FDE event content that points to the number of events - * the master server supports. - */ - -/* Defines and offsets for binlog encryption */ -#define BLRM_FDE_EVENT_TYPES_OFFSET (2 + 50 + 4 + 1) -#define BLRM_CRYPTO_SCHEME_LENGTH 1 -#define BLRM_KEY_VERSION_LENGTH 4 -#define BLRM_IV_LENGTH AES_BLOCK_SIZE -#define BLRM_IV_OFFS_LENGTH 4 -#define BLRM_NONCE_LENGTH (BLRM_IV_LENGTH - BLRM_IV_OFFS_LENGTH) - /** * MariaDB 10.1.7 Start Encryption event content * @@ -142,22 +130,6 @@ typedef struct start_encryption_event * form the encrryption IV for the event */ } START_ENCRYPTION_EVENT; -/** - * Binlog encryption context of current binlog file - * - * nonce for current binlog - * key version - * crypto_scheme - */ -typedef struct binlog_encryption_ctx -{ - uint8_t binlog_crypto_scheme; /**< Encryption scheme */ - uint32_t binlog_key_version; /**< Encryption key version */ - uint8_t nonce[BLRM_NONCE_LENGTH]; /**< nonce (random bytes) of current binlog. - * These bytes + the binlog event current pos - * form the encrryption IV for the event */ -} BINLOG_ENCRYPTION_CTX; - /** * Initialise the binlog file for this instance. MaxScale will look * for all the binlogs that it has on local disk, determine the next @@ -441,6 +413,7 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, int n; bool write_begin_encryption = false; uint64_t file_offset = router->current_pos; + uint32_t event_size[4]; /* Track whether FORMAT_DESCRIPTION_EVENT has been received */ if (hdr->event_type == FORMAT_DESCRIPTION_EVENT) @@ -462,7 +435,7 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, } } - if (router->encryption_ctx != NULL) + if (router->encryption.enabled && router->encryption_ctx != NULL && !write_begin_encryption) { BINLOG_ENCRYPTION_CTX *tmp_encryption_ctx = (BINLOG_ENCRYPTION_CTX *)(router->encryption_ctx); uint8_t iv[BLRM_IV_LENGTH]; @@ -484,21 +457,31 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, nonce_hex, tmp_encryption_ctx->binlog_crypto_scheme, tmp_encryption_ctx->binlog_key_version); + + /** + * Encrypt binlog event: + * + * Save event size (buf + 9, 4 bytes) + * move first 4 bytes of buf to buf + 9 ... + * encrypt buf starting from buf + 4 (so it will be event_size - 4) + * move encrypted_data + 9, (4 bytes), to encrypted_data[0] + * write saved_event_size 4 bytes into encrypted_data + 9 + * write encrypted_data + */ + + memcpy(&event_size, buf + BINLOG_EVENT_LEN_OFFSET , 4); + memmove(buf + BINLOG_EVENT_LEN_OFFSET, buf, 4); + uint8_t *buf_ptr = buf + 4; + /* 16 bytes after buf + 4 are owerwritten by XORed with IV */ + /* Only 15 bytes are involved */ + for (int i=0; i < (AES_BLOCK_SIZE - 1); i++) + { + buf_ptr[i]= buf_ptr[i] ^ iv[i]; + } + memmove(buf, buf + BINLOG_EVENT_LEN_OFFSET, 4); + memcpy(buf + BINLOG_EVENT_LEN_OFFSET, &event_size, 4); } - - /** - * TODO: - * - * save event size (buf + 9, 4 bytes) - * move first 4 bytes of buf to buf + 9 ... - * encrypt buf starting from buf + 4 (so it will be event_size - 4) - * move encrypted_data + 9, (4 bytesi), to encrypted_data[0] - * write saved_event_size 4 bytes into encrypted_data + 9 - * write encrypted_data - * - * First task is the data move only in current 'buf', no encryption at all. - */ - + /* Write current received event from master */ if ((n = pwrite(router->binlog_fd, buf, size, router->last_written)) != size) @@ -622,6 +605,8 @@ blr_open_binlog(ROUTER_INSTANCE *router, char *binlog) return NULL; } + file->encryption_ctx = NULL; + file->next = router->files; router->files = file; spinlock_release(&router->fileslock); @@ -648,6 +633,7 @@ blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned long pos, REP_HE int n; unsigned long filelen = 0; struct stat statb; + SLAVE_ENCRYPTION_CTX *file_enc_ctx = NULL; memset(hdbuf, '\0', BINLOG_EVENT_HDR_LEN); @@ -732,6 +718,10 @@ blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned long pos, REP_HE return NULL; } + + /* Get encryption_ctx */ + file_enc_ctx = file->encryption_ctx; + spinlock_release(&file->lock); spinlock_release(&router->binlog_lock); @@ -772,6 +762,35 @@ blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned long pos, REP_HE return NULL; } + /* Check whether we need to decrypt the current event */ + if (file_enc_ctx && pos >= file_enc_ctx->first_enc_event_pos) + { + uint8_t *event_ptr = hdbuf; + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t event_size[4]; + + /* Encryption IV is 12 bytes nonce + 4 bytes event position */ + memcpy(&iv, file_enc_ctx->nonce, BLRM_NONCE_LENGTH); + gw_mysql_set_byte4(iv + BLRM_NONCE_LENGTH, (unsigned long)pos); + + /* Save event size */ + memcpy(&event_size, event_ptr + BINLOG_EVENT_LEN_OFFSET , 4); + + MXS_INFO("Decoding encrypted event @ pos %lu, size %lu", + (unsigned long)pos, (unsigned long)extract_field(event_size, 32)); + + memmove(event_ptr + BINLOG_EVENT_LEN_OFFSET, event_ptr, 4); + uint8_t *buf_ptr = event_ptr + 4; + /* 16 bytes after buf + 4 are owerwritten by XORed with IV */ + // 15 for now + for (int i=0; i < (AES_BLOCK_SIZE - 1); i++) + { + buf_ptr[i]= buf_ptr[i] ^ iv[i]; + } + memmove(event_ptr, event_ptr + BINLOG_EVENT_LEN_OFFSET, 4); + memcpy(event_ptr + BINLOG_EVENT_LEN_OFFSET, &event_size, 4); + } + hdr->timestamp = EXTRACT32(hdbuf); hdr->event_type = hdbuf[4]; hdr->serverid = EXTRACT32(&hdbuf[5]); @@ -971,6 +990,7 @@ blr_close_binlog(ROUTER_INSTANCE *router, BLFILE *file) { close(file->fd); file->fd = -1; + file->encryption_ctx = NULL; MXS_FREE(file); } } @@ -1686,7 +1706,7 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug) /* Detect possible Start Encryption Event */ if (hdr.event_type == MARIADB10_START_ENCRYPTION_EVENT) { - char nonce_hex[12 * 2 + 1] = ""; + char nonce_hex[AES_BLOCK_SIZE * 2 + 1] = ""; START_ENCRYPTION_EVENT ste_event = {}; BINLOG_ENCRYPTION_CTX *new_encryption_ctx = MXS_CALLOC(1, sizeof(BINLOG_ENCRYPTION_CTX)); if (new_encryption_ctx == NULL) diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 8031e65de..915d48a81 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 MariaDB Corporation Ab + * Copyright (c) 201nMariaDB Corporation Ab * * Use of this software is governed by the Business Source License included * in the LICENSE.TXT file and at www.mariadb.com/bsl. @@ -97,6 +97,7 @@ extern int blr_file_new_binlog(ROUTER_INSTANCE *router, char *file); extern int blr_file_write_master_config(ROUTER_INSTANCE *router, char *error); extern char *blr_extract_column(GWBUF *buf, int col); extern uint32_t extract_field(uint8_t *src, int bits); +void blr_extract_header(register uint8_t *ptr, register REP_HEADER *hdr); int blr_file_get_next_binlogname(ROUTER_INSTANCE *router); static void encode_value(unsigned char *data, unsigned int value, int len); static int blr_slave_query(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue); @@ -109,7 +110,7 @@ int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large); uint8_t *blr_build_header(GWBUF *pkt, REP_HEADER *hdr); int blr_slave_callback(DCB *dcb, DCB_REASON reason, void *data); static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, BLFILE** filep); -static void blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); +static uint32_t blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *fde); static int blr_slave_send_maxscale_version(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_slave_send_server_id(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_slave_send_maxscale_variables(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); @@ -164,6 +165,8 @@ static int blr_slave_send_columndef_with_status_schema(ROUTER_INSTANCE *router, static void blr_send_slave_heartbeat(void *inst); static int blr_slave_send_heartbeat(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); static int blr_set_master_ssl(ROUTER_INSTANCE *router, CHANGE_MASTER_OPTIONS config, char *error_message); +static int blr_slave_read_ste(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, uint32_t fde_end_pos); +static GWBUF *blr_slave_read_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); void poll_fake_write_event(DCB *dcb); @@ -1965,6 +1968,7 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue int len, rval, binlognamelen; REP_HEADER hdr; uint32_t chksum; + uint32_t fde_end_pos; ptr = GWBUF_DATA(queue); len = extract_field(ptr, 24); @@ -2093,15 +2097,40 @@ blr_slave_binlog_dump(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue slave->lastReply = time(0); } + GWBUF *fde = blr_slave_read_fde(router, slave); + if (fde == NULL) + { + // ERROR + return 1; + } + + /* FDE ends at pos 4 + FDE size */ + fde_end_pos = 4 + GWBUF_LENGTH(fde); + /* Send the FORMAT_DESCRIPTION_EVENT */ if (slave->binlog_pos != 4) { - blr_slave_send_fde(router, slave); + blr_slave_send_fde(router, slave, fde); } /* set lastEventReceived */ slave->lastEventReceived = FORMAT_DESCRIPTION_EVENT; + /** + * Check for START_ENCRYPTION_EVENT (after FDE) if + * client request pos is greater than 4 + * + * TODO: If router has binlog encryption take it + * otherwise error + * If no encryption and event found return error + * + * If event is found the contest is set into slave struct + */ + if (slave->binlog_pos != 4) + { + blr_slave_read_ste(router, slave, fde_end_pos); + } + slave->dcb->low_water = router->low_water; slave->dcb->high_water = router->high_water; @@ -2298,22 +2327,82 @@ blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large) #endif int events_before = slave->stats.n_events; + /* Set file encryption context from slave pointer */ + spinlock_acquire(&slave->catch_lock); + if (slave->encryption_ctx) + { + file->encryption_ctx = slave->encryption_ctx; + } + else + { + file->encryption_ctx = NULL; + } + spinlock_release(&slave->catch_lock); + while (burst-- && burst_size > 0 && (record = blr_read_binlog(router, file, slave->binlog_pos, &hdr, read_errmsg)) != NULL) { char binlog_name[BINLOG_FNAMELEN + 1]; uint32_t binlog_pos; + uint32_t event_size; strcpy(binlog_name, slave->binlogfile); binlog_pos = slave->binlog_pos; /* Don't sent special events generated by MaxScale */ - if (hdr.event_type == MARIADB10_START_ENCRYPTION_EVENT || hdr.event_type == IGNORABLE_EVENT || (hdr.flags & LOG_EVENT_IGNORABLE_F)) + if (hdr.event_type == MARIADB10_START_ENCRYPTION_EVENT || + hdr.event_type == IGNORABLE_EVENT || + (hdr.flags & LOG_EVENT_IGNORABLE_F)) { + /* In case of file rotation or pos = 4 the events are sent from position 4. + * new FDE at pos 4 is read. + * We need to check whether the first event after FDE + * is the MARIADB10_START_ENCRYPTION_EVENT of the new file. + * + * Read it if slave->encryption_ctx is NULL and set the slave->encryption_ctx accordingly + */ + spinlock_acquire(&slave->catch_lock); + + if (hdr.event_type == MARIADB10_START_ENCRYPTION_EVENT && !slave->encryption_ctx) + { + /* read it, set slave & file context */ + uint8_t *record_ptr = GWBUF_DATA(record); + SLAVE_ENCRYPTION_CTX *new_encryption_ctx = MXS_CALLOC(1, sizeof(SLAVE_ENCRYPTION_CTX)); + record_ptr += BINLOG_EVENT_HDR_LEN; + new_encryption_ctx->binlog_crypto_scheme = record_ptr[0]; + memcpy(&new_encryption_ctx->binlog_key_version, record_ptr + 1, BLRM_KEY_VERSION_LENGTH); + memcpy(new_encryption_ctx->nonce, record_ptr + 1 + BLRM_KEY_VERSION_LENGTH, BLRM_NONCE_LENGTH); + + /* Save current first_enc_event_pos */ + if (slave->encryption_ctx) + { + SLAVE_ENCRYPTION_CTX *slave_enc_ctx = (SLAVE_ENCRYPTION_CTX *)slave->encryption_ctx; + new_encryption_ctx->first_enc_event_pos = slave_enc_ctx->first_enc_event_pos; + } + + /* set the encryption ctx into slave */ + slave->encryption_ctx = new_encryption_ctx; + + MXS_INFO("Start Encryption event found while reading. Binlog %s is encrypted. First event at %lu", + slave->binlogfile, + (unsigned long) hdr.next_pos); + } + + MXS_INFO("Found ignorable event [%s] of size %lu while reading binlog %s at %lu", + blr_get_event_description(router, hdr.event_type), + (unsigned long)hdr.event_size, + slave->binlogfile, + (unsigned long) slave->binlog_pos); + + /* set next pos */ slave->binlog_pos = hdr.next_pos; + + spinlock_release(&slave->catch_lock); + gwbuf_free(record); record = NULL; - continue; + + break; } if (hdr.event_type == ROTATE_EVENT) @@ -2325,6 +2414,11 @@ blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large) MXS_ERROR("blr_close_binlog took %lu maxscale beats", hkheartbeat - beat1); } blr_slave_rotate(router, slave, GWBUF_DATA(record)); + + /* reset the encryption context */ + MXS_FREE(slave->encryption_ctx); + slave->encryption_ctx = NULL; + beat1 = hkheartbeat; #ifdef BLFILE_IN_SLAVE if ((slave->file = blr_open_binlog(router, slave->binlogfile)) == NULL) @@ -2558,6 +2652,10 @@ blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large) slave->binlogfile, (unsigned long)slave->binlog_pos, router->binlog_name, router->binlog_position); + /* Reset encryption context */ + MXS_FREE(slave->encryption_ctx); + slave->encryption_ctx = NULL; + #ifdef BLFILE_IN_SLAVE if (blr_slave_fake_rotate(router, slave, &slave->file)) #else @@ -2763,13 +2861,14 @@ blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, BLFILE** fil } /** - * Send a "fake" format description event to the newly connected slave + * Read the format description event FDE from current slave logfile * * @param router The router instance * @param slave The slave to send the event to + * @return The read FDE event on success or NULL on error */ -static void -blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) +static GWBUF * +blr_slave_read_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) { BLFILE *file; REP_HEADER hdr; @@ -2784,7 +2883,7 @@ blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) { - return; + return NULL; } if ((record = blr_read_binlog(router, file, 4, &hdr, err_msg)) == NULL) { @@ -2799,20 +2898,56 @@ blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) } blr_close_binlog(router, file); - return; + return NULL; } blr_close_binlog(router, file); + + return record; +} + +/** + * Send a "fake" format description event to the newly connected slave + * + * @param router The router instance + * @param slave The slave to send the event to + * @return The FDE event size on success or 0 on error + */ +static uint32_t +blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *fde) +{ + BLFILE *file; + REP_HEADER hdr; + GWBUF *head; + uint8_t *ptr; + uint32_t chksum; + char err_msg[BINLOG_ERROR_MSG_LEN + 1]; + uint32_t event_size; + uint8_t *event_ptr; + + if (fde == NULL) + { + return 0; + } + + err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; + event_ptr = GWBUF_DATA(fde); head = gwbuf_alloc(5); ptr = GWBUF_DATA(head); - encode_value(ptr, hdr.event_size + 1, 24); // Payload length + + event_size = GWBUF_LENGTH(fde); + + /* Set payload to event_size + 1 (the ok/err byte) */ + encode_value(ptr, event_size + 1, 32); ptr += 3; *ptr++ = slave->seqno++; - *ptr++ = 0; // OK - head = gwbuf_append(head, record); - ptr = GWBUF_DATA(record); - encode_value(ptr, time(0), 32); // Overwrite timestamp - ptr += 13; - encode_value(ptr, 0, 32); // Set next position to 0 + *ptr++ = 0; // OK/ERR byte + head = gwbuf_append(head, fde); + event_ptr = GWBUF_DATA(fde); + encode_value(event_ptr, time(0), 32); // Overwrite timestamp + event_ptr += 13; // 4 time + 1 type + 4 server_id + 4 event_size + /* event_ptr points to position of the next event */ + encode_value(event_ptr, 0, 32); // Set next position to 0 + /* * Since we have changed the timestamp we must recalculate the CRC * @@ -2820,16 +2955,15 @@ blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) * calculate a new checksum * and write it into the header */ - ptr = GWBUF_DATA(record) + hdr.event_size - 4; + ptr = GWBUF_DATA(fde) + event_size - BINLOG_EVENT_CRC_SIZE; chksum = crc32(0L, NULL, 0); - chksum = crc32(chksum, GWBUF_DATA(record), hdr.event_size - 4); + chksum = crc32(chksum, GWBUF_DATA(fde), event_size - BINLOG_EVENT_CRC_SIZE); encode_value(ptr, chksum, 32); - slave->dcb->func.write(slave->dcb, head); + return slave->dcb->func.write(slave->dcb, head); } - /** * Send the field count packet in a response packet sequence. * @@ -5577,3 +5711,69 @@ bool blr_notify_waiting_slave(ROUTER_SLAVE *slave) return ret; } + +/** + * Read START_ENCRYPTION_EVENT, after FDE + */ +static int +blr_slave_read_ste(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, uint32_t fde_end_pos) +{ + BLFILE *file; + REP_HEADER hdr; + GWBUF *record, *head; + uint8_t *ptr; + uint32_t chksum; + char err_msg[BINLOG_ERROR_MSG_LEN + 1]; + + err_msg[BINLOG_ERROR_MSG_LEN] = '\0'; + + memset(&hdr, 0, BINLOG_EVENT_HDR_LEN); + + if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) + { + return 0; + } + if ((record = blr_read_binlog(router, file, fde_end_pos, &hdr, err_msg)) == NULL) + { + if (hdr.ok != SLAVE_POS_READ_OK) + { + MXS_ERROR("Slave %s:%i, server-id %d, binlog '%s', blr_read_binlog failure: %s", + slave->dcb->remote, + ntohs((slave->dcb->ipv4).sin_port), + slave->serverid, + slave->binlogfile, + err_msg); + } + + blr_close_binlog(router, file); + return 0; + } + + blr_close_binlog(router, file); + + /* check for START_ENCRYPTION_EVENT */ + if (hdr.event_type == MARIADB10_START_ENCRYPTION_EVENT) + { + uint8_t *record_ptr = GWBUF_DATA(record); + SLAVE_ENCRYPTION_CTX *new_encryption_ctx = MXS_CALLOC(1, sizeof(SLAVE_ENCRYPTION_CTX)); + record_ptr += BINLOG_EVENT_HDR_LEN; + new_encryption_ctx->binlog_crypto_scheme = record_ptr[0]; // 1 Byte + memcpy(&new_encryption_ctx->binlog_key_version, record_ptr + 1, BLRM_KEY_VERSION_LENGTH); + memcpy(new_encryption_ctx->nonce, record_ptr + 1 + BLRM_KEY_VERSION_LENGTH, BLRM_NONCE_LENGTH); + /* Set the pos of first encrypted event */ + new_encryption_ctx->first_enc_event_pos = fde_end_pos + hdr.event_size; + + spinlock_acquire(&slave->catch_lock); + /* set the encryption ctx into slave */ + MXS_FREE(slave->encryption_ctx); + slave->encryption_ctx = new_encryption_ctx; + spinlock_release(&slave->catch_lock); + + MXS_INFO("Start Encryption event found. Binlog %s is encrypted. First event at %lu", + slave->binlogfile, + (unsigned long)fde_end_pos + hdr.event_size); + return 1; + } + + return 0; +}