Binlog files have 15 bytes of header XORed with IV

Events saved and read have 15 bytes of header XORed with IV.

Partial events are still not handled.

Next implementation will encrypt the whole event instead of 15 bytes XOR
This commit is contained in:
MassimilianoPinto
2016-10-03 09:39:22 +02:00
parent 6e69c783f3
commit d9bcf6a775
4 changed files with 344 additions and 68 deletions

View File

@ -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;
}