diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 7dcc07380..708fcbf5f 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -60,6 +60,9 @@ #define BINLOG_EVENT_HDR_LEN 19 #define BINLOG_EVENT_CRC_ALGO_TYPE 1 #define BINLOG_EVENT_CRC_SIZE 4 +#define BINLOG_EVENT_LEN_OFFSET 9 +#define BINLOG_ENCRYPTION_ALGORYTM_NAME_LEN 13 +#define BINLOG_FATAL_ERROR_READING 1236 /** * Binlog event types @@ -457,6 +460,16 @@ typedef struct int fde_len; /*< Length of fde_event */ } MASTER_RESPONSES; +/** + * The binlog encryption setup + */ +typedef struct binlog_encryption_setup +{ + bool enabled; + char encryption_algorithm[BINLOG_ENCRYPTION_ALGORYTM_NAME_LEN]; + char *key_management_filename; + uint8_t *keys; +} BINLOG_ENCRYPTION_SETUP; /** * The per instance data for the router. */ @@ -541,7 +554,8 @@ typedef struct router_instance char *ssl_version; /*< config TLS Version for Master SSL connection */ bool request_semi_sync; /*< Request Semi-Sync replication to master */ int master_semi_sync; /*< Semi-Sync replication status of master server */ - int encrypt_binlog; /*< Encrypt binlog files */ + BINLOG_ENCRYPTION_SETUP encryption; /*< Binlog encryption setup */ + void *encryption_ctx; /*< Encryption context */ struct router_instance *next; } ROUTER_INSTANCE; diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index f578fee2a..6b4bdfa0f 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -295,7 +295,12 @@ createInstance(SERVICE *service, char **options) /* Semi-Sync support */ inst->request_semi_sync = false; inst->master_semi_sync = 0; - inst->encrypt_binlog = 0; + + /* Binlog encryption */ + inst->encryption.enabled = 0; + + /* Encryption CTX */ + inst->encryption_ctx = NULL; /* Generate UUID for the router instance */ uuid_generate_time(defuuid); @@ -433,7 +438,7 @@ createInstance(SERVICE *service, char **options) } else if (strcmp(options[i], "encrypt_binlog") == 0) { - inst->encrypt_binlog = config_truth_value(value); + inst->encryption.enabled = config_truth_value(value); } else if (strcmp(options[i], "lowwater") == 0) { @@ -805,7 +810,7 @@ createInstance(SERVICE *service, char **options) } /* Log whether the binlog encryption option value is on */ - if (inst->encrypt_binlog) + if (inst->encryption.enabled) { MXS_NOTICE("%s: Service has binlog encryption set to ON", service->name); @@ -820,21 +825,34 @@ createInstance(SERVICE *service, char **options) MXS_NOTICE("Validating binlog file '%s' ...", inst->binlog_name); - if (inst->trx_safe && !blr_check_binlog(inst)) + if (!blr_check_binlog(inst)) { - /* Don't start replication, just return */ - return (ROUTER *)inst; + if (inst->trx_safe) + { + /* Don't start replication, just return */ + return (ROUTER *)inst; + } } - if (!inst->trx_safe) + /* Report current pos in binlog file and last seen transaction pos */ + MXS_INFO("Current binlog file is %s, safe pos %lu, current pos is %lu\n", + inst->binlog_name, inst->binlog_position, inst->current_pos); + + /* Don't start replication if binlog has START_ENCRYPTION_EVENT but binlog encryption is off */ + if (!inst->encryption.enabled && inst->encryption_ctx) { - MXS_INFO("Current binlog file is %s, current pos is %lu\n", - inst->binlog_name, inst->binlog_position); - } - else - { - MXS_INFO("Current binlog file is %s, safe pos %lu, current pos is %lu\n", - inst->binlog_name, inst->binlog_position, inst->current_pos); + MXS_ERROR("Found START_ENCRYPTION_EVENT but " + "binlog ecryption option is currently Off. Replication can't start right now. " + "Please restart maxScale with option set to On"); + + /* Force STOPPED state */ + inst->master_state = BLRM_SLAVE_STOPPED; + /* Set mysql_errno and error message */ + inst->m_errno = BINLOG_FATAL_ERROR_READING; + inst->m_errmsg = mxs_strdup("HY000 Binlog encryption is Off but binlog file has " + "the START_ENCRYPTION_EVENT"); + + return (ROUTER *)inst; } /* Start replication from master server */ diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index b07657031..391f1f3bf 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -140,6 +140,20 @@ typedef struct start_encryption_event uint8_t nonce[BLRM_NONCE_LENGTH]; } 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; + uint32_t binlog_key_version; + uint8_t nonce[BLRM_NONCE_LENGTH]; +} 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 @@ -435,7 +449,6 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, * Fill the gap with a self generated ignorable event * Binlog file position is incremented by blr_write_special_event() */ - if (hdr->next_pos && (hdr->next_pos > (file_offset + size))) { uint64_t hole_size = hdr->next_pos - file_offset - size; @@ -445,6 +458,43 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, } } + if (router->encryption_ctx != NULL) + { + BINLOG_ENCRYPTION_CTX *tmp_encryption_ctx = (BINLOG_ENCRYPTION_CTX *)(router->encryption_ctx); + uint8_t iv[BLRM_IV_LENGTH]; + uint64_t file_offset = router->current_pos; + char iv_hex[AES_BLOCK_SIZE * 2 + 1] = ""; + char nonce_hex[BLRM_NONCE_LENGTH * 2 + 1] = ""; + + /* Encryption IV is 12 bytes nonce + 4 bytes event position */ + memcpy(iv, tmp_encryption_ctx->nonce, BLRM_NONCE_LENGTH); + gw_mysql_set_byte4(iv + BLRM_NONCE_LENGTH, (unsigned long)file_offset); + /* Human readable versions */ + gw_bin2hex(iv_hex, iv, BLRM_IV_LENGTH); + gw_bin2hex(nonce_hex, tmp_encryption_ctx->nonce, BLRM_NONCE_LENGTH); + + MXS_DEBUG("Writing Encrypted event type %d, size %lu. IV is %s, nonce %s, enc scheme %d, key ver %u", + hdr->event_type, + (unsigned long)size, + iv_hex, + nonce_hex, + tmp_encryption_ctx->binlog_crypto_scheme, + tmp_encryption_ctx->binlog_key_version); + } + + /** + * 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) @@ -474,10 +524,14 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, spinlock_release(&router->binlog_lock); /* Check whether adding the Start Encryption event into current binlog */ - if (router->encrypt_binlog && write_begin_encryption) + if (router->encryption.enabled && write_begin_encryption) { - uint64_t event_size = router->master_chksum ? 40 : 36; + uint64_t event_size = sizeof(START_ENCRYPTION_EVENT); uint64_t file_offset = router->current_pos; + if (router->master_chksum) + { + event_size += BINLOG_EVENT_CRC_SIZE; + } if (!blr_write_special_event(router, file_offset, event_size, hdr, BLRM_START_ENCRYPTION)) { return 0; @@ -1298,6 +1352,48 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug) } } + if (start_encryption_seen) + { + uint8_t iv[AES_BLOCK_SIZE + 1] = ""; + char iv_hex[AES_BLOCK_SIZE * 2 + 1] = ""; + uint32_t event_size = EXTRACT32(hdbuf + BINLOG_EVENT_LEN_OFFSET); + + /** + * Events are encrypted. + * + * The routine doesn't decrypt them but follows + * next event based on the event_size (4 bytes) that is af offset + * of BINLOG_EVENT_LEN_OFFSET (9) and it's in clear. + * + * This version prints to DEBUG the encryption event IV. + */ + + /* Get binlog file "nonce" and other data from router encryption_ctx */ + BINLOG_ENCRYPTION_CTX *enc_ctx = router->encryption_ctx; + + /* Encryption IV is 12 bytes nonce + 4 bytes event position */ + memcpy(iv, enc_ctx->nonce, BLRM_NONCE_LENGTH); + gw_mysql_set_byte4(iv + BLRM_NONCE_LENGTH, (unsigned long)pos); + + /* Human readable version */ + gw_bin2hex(iv_hex, iv, BLRM_IV_LENGTH); + + MXS_DEBUG("** Encrypted Event @ %lu: the IV is %s, size is %lu, next pos is %lu\n", + (unsigned long)pos, + iv_hex, (unsigned long)event_size, + (unsigned long)(pos + event_size)); + + /* Next event pos is ps + event size */ + pos = pos + event_size; + + /* Update other offsets as well */ + router->binlog_position = pos; + router->current_safe_event = pos; + router->current_pos = pos; + + continue; + } + /* fill replication header struct */ hdr.timestamp = EXTRACT32(hdbuf); hdr.event_type = hdbuf[4]; @@ -1586,51 +1682,64 @@ 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) { - START_ENCRYPTION_EVENT ste_event = {}; - char nonce_hex[12 * 2 + 1] = ""; - /* The start encryption event data is 17 bytes long: - * Scheme = 1 - * Key Version: 4 - * nonce = 12 - */ + char nonce_hex[12 * 2 + 1] = ""; + BINLOG_ENCRYPTION_CTX *new_encryption_ctx = MXS_CALLOC(1, sizeof(BINLOG_ENCRYPTION_CTX)); + START_ENCRYPTION_EVENT ste_event = {}; - /* Fill the event content, after the event header */ - ste_event.binlog_crypto_scheme = ptr[0]; - ste_event.binlog_key_version = extract_field(ptr + 1, 32); - memcpy(ste_event.nonce, ptr + 1 + 4, BLRM_NONCE_LENGTH); + /* The start encryption event data is 17 bytes long: + * Scheme = 1 + * Key Version: 4 + * nonce = 12 + */ - if (debug) + /* Fill the event content, after the event header */ + ste_event.binlog_crypto_scheme = ptr[0]; + ste_event.binlog_key_version = extract_field(ptr + 1, 32); + memcpy(ste_event.nonce, ptr + 1 + 4, BLRM_NONCE_LENGTH); + + /* Fill the encryption_ctx */ + memcpy(new_encryption_ctx->nonce, ste_event.nonce, BLRM_NONCE_LENGTH); + new_encryption_ctx->binlog_crypto_scheme = ste_event.binlog_crypto_scheme; + memcpy(&new_encryption_ctx->binlog_key_version, + &ste_event.binlog_key_version, BLRM_KEY_VERSION_LENGTH); + + if (debug) + { + char *cksum_format = ", crc32 0x"; + char hex_checksum[BINLOG_EVENT_CRC_SIZE * 2 + strlen(cksum_format) + 1]; + uint8_t cksum_data[BINLOG_EVENT_CRC_SIZE]; + hex_checksum[0]='\0'; + + /* Hex representation of nonce */ + gw_bin2hex(nonce_hex, ste_event.nonce, BLRM_NONCE_LENGTH); + + /* Hex representation of checksum */ + cksum_data[3] = *(ptr + hdr.event_size - 4 - BINLOG_EVENT_HDR_LEN); + cksum_data[2] = *(ptr + hdr.event_size - 3 - BINLOG_EVENT_HDR_LEN); + cksum_data[1] = *(ptr + hdr.event_size - 2 - BINLOG_EVENT_HDR_LEN); + cksum_data[0] = *(ptr + hdr.event_size - 1 - BINLOG_EVENT_HDR_LEN); + + if (found_chksum) { - char *cksum_format = ", crc32 0x"; - char hex_checksum[BINLOG_EVENT_CRC_SIZE * 2 + strlen(cksum_format) + 1]; - uint8_t cksum_data[BINLOG_EVENT_CRC_SIZE]; - hex_checksum[0]='\0'; - - /* Hex representation of nonce */ - gw_bin2hex(nonce_hex, ste_event.nonce, BLRM_NONCE_LENGTH); - - /* Hex representation of checksum */ - cksum_data[3] = *(ptr + hdr.event_size - 4 - BINLOG_EVENT_HDR_LEN); - cksum_data[2] = *(ptr + hdr.event_size - 3 - BINLOG_EVENT_HDR_LEN); - cksum_data[1] = *(ptr + hdr.event_size - 2 - BINLOG_EVENT_HDR_LEN); - cksum_data[0] = *(ptr + hdr.event_size - 1 - BINLOG_EVENT_HDR_LEN); - - if (found_chksum) - { - strcpy(hex_checksum, cksum_format); - gw_bin2hex(hex_checksum + strlen(cksum_format) , cksum_data, BINLOG_EVENT_CRC_SIZE); - } - - MXS_DEBUG("- START_ENCRYPTION event @ %llu, size %lu, next pos is @ %lu, flags %u%s", - pos, (unsigned long)hdr.event_size, (unsigned long)hdr.next_pos, hdr.flags, - hex_checksum); - - MXS_DEBUG(" Encryption scheme: %u, key_version: %u," - " nonce: %s\n", ste_event.binlog_crypto_scheme, - ste_event.binlog_key_version, nonce_hex); + strcpy(hex_checksum, cksum_format); + gw_bin2hex(hex_checksum + strlen(cksum_format) , cksum_data, BINLOG_EVENT_CRC_SIZE); } - start_encryption_seen = 1; + MXS_DEBUG("- START_ENCRYPTION event @ %llu, size %lu, next pos is @ %lu, flags %u%s", + pos, (unsigned long)hdr.event_size, (unsigned long)hdr.next_pos, hdr.flags, + hex_checksum); + + MXS_DEBUG(" Encryption scheme: %u, key_version: %u," + " nonce: %s\n", ste_event.binlog_crypto_scheme, + ste_event.binlog_key_version, nonce_hex); + } + + start_encryption_seen = 1; + + /* Update the router encryption context */ + MXS_FREE(router->encryption_ctx); + router->encryption_ctx = NULL; + router->encryption_ctx = new_encryption_ctx; } /* set last event time, pos and type */ @@ -2375,6 +2484,7 @@ blr_create_start_encryption_event(ROUTER_INSTANCE *router, uint32_t event_pos, b { uint8_t *new_event; uint8_t event_size = sizeof(START_ENCRYPTION_EVENT); + BINLOG_ENCRYPTION_CTX *new_encryption_ctx = MXS_CALLOC(1, sizeof(BINLOG_ENCRYPTION_CTX)); /* Add 4 bytes to event size with crc32 */ if (do_checksum) @@ -2424,6 +2534,22 @@ blr_create_start_encryption_event(ROUTER_INSTANCE *router, uint32_t event_pos, b encode_value(new_event + event_size - BINLOG_EVENT_CRC_SIZE, chksum, 32); } + /* Update the encryption context */ + uint8_t *nonce_ptr = &(new_event[BINLOG_EVENT_HDR_LEN + 4 + 1]); + + spinlock_acquire(&router->binlog_lock); + + memcpy(new_encryption_ctx->nonce, nonce_ptr, BLRM_NONCE_LENGTH); + new_encryption_ctx->binlog_crypto_scheme = new_event[BINLOG_EVENT_HDR_LEN]; + memcpy(&new_encryption_ctx->binlog_key_version, + &new_event[BINLOG_EVENT_HDR_LEN + 1], BLRM_KEY_VERSION_LENGTH); + + MXS_FREE(router->encryption_ctx); + router->encryption_ctx = NULL; + router->encryption_ctx = new_encryption_ctx; + + spinlock_release(&router->binlog_lock); + return new_event; } diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index f59f4ca66..198a6f112 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -1250,8 +1250,9 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt) spinlock_release(&router->lock); #ifdef SHOW_EVENTS - printf("blr: len %lu, event type 0x%02x, flags 0x%04x, " + printf("blr @ %lu: len %lu, event type 0x%02x, flags 0x%04x, " "event size %d, event timestamp %lu\n", + router->current_pos, (unsigned long)len - 4, hdr.event_type, hdr.flags, diff --git a/server/modules/routing/binlog/maxbinlogcheck.c b/server/modules/routing/binlog/maxbinlogcheck.c index 4b2007358..699c3aa66 100644 --- a/server/modules/routing/binlog/maxbinlogcheck.c +++ b/server/modules/routing/binlog/maxbinlogcheck.c @@ -28,6 +28,12 @@ * Currently MariadDB 10 starting transactions * are detected checking GTID event * with flags = 0 + * 26/04/16 Massimiliano Pinto MariaDB 10.1 GTID flags are properly parsed + * 23/09/16 Massimiliano Pinto MariaDB 10.1 encrypted binlog compatible: + * the output shows the START_ENCRYPTION_EVENT and follows + * binlog positions without dectypting events. + * + * * @endverbatim */ @@ -55,7 +61,7 @@ static struct option long_options[] = {0, 0, 0, 0} }; -char *binlog_check_version = "1.1.0"; +char *binlog_check_version = "1.2.0"; int maxscale_uptime()