Binlog server encrypts binary logs with AES key and AES_CTR algorithm

First implementation of Binlog server binary encryption

Limitations:
AES_CTR only is supported
events larger than 16MBytes are not encrypted.

Maxscale binary log files can be read by a MaraDB 10.1 server which set
same AES key and algorithm
This commit is contained in:
MassimilianoPinto
2016-11-28 11:35:06 +01:00
parent 5dee14059b
commit bf07eb0582
5 changed files with 854 additions and 203 deletions

View File

@ -54,6 +54,8 @@
* 26/08/2016 Massimiliano Pinto Addition of Start Encription Event description
* 29/08/2016 Massimiliano Pinto Addition of encrypt_binlog option
* 08/11/2016 Massimiliano Pinto Added destroyInstance()
* 24/11/2016 Massimiliano Pinto Addition of encryption options:
* encryption_algorithm and encryption_key_file
*
* @endverbatim
*/
@ -113,6 +115,8 @@ int blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug);
void blr_master_close(ROUTER_INSTANCE *);
void blr_free_ssl_data(ROUTER_INSTANCE *inst);
static void destroyInstance(ROUTER *instance);
bool blr_parse_key(char *line, ROUTER_INSTANCE *router);
bool blr_get_encryption_key(ROUTER_INSTANCE *router);
/** The module object definition */
static ROUTER_OBJECT MyObject =
@ -297,6 +301,8 @@ createInstance(SERVICE *service, char **options)
/* Binlog encryption */
inst->encryption.enabled = 0;
inst->encryption.encryption_algorithm = BINLOG_DEFAULT_ENC_ALGO;
inst->encryption.key_management_filename = NULL;
/* Encryption CTX */
inst->encryption_ctx = NULL;
@ -439,6 +445,27 @@ createInstance(SERVICE *service, char **options)
{
inst->encryption.enabled = config_truth_value(value);
}
else if (strcmp(options[i], "encryption_algorithm") == 0)
{
int ret = blr_check_encryption_algorithm(value);
if (ret > -1)
{
inst->encryption.encryption_algorithm = ret;
}
else
{
MXS_ERROR("Service %s, invalid encryption_algorithm '%s'. "
"Supported algorithms: %s",
service->name, value, blr_encryption_algorithm_list());
free_instance(inst);
return NULL;
}
}
else if (strcmp(options[i], "encryption_key_file") == 0)
{
inst->encryption.key_management_filename = MXS_STRDUP_A(value);
}
else if (strcmp(options[i], "lowwater") == 0)
{
inst->low_water = atoi(value);
@ -571,6 +598,14 @@ createInstance(SERVICE *service, char **options)
return NULL;
}
/* Get the Encryption key */
if (inst->encryption.enabled && !blr_get_encryption_key(inst))
{
free_instance(inst);
return NULL;
}
/**
* If binlogdir is not found create it
* On failure don't start the instance
@ -790,8 +825,11 @@ createInstance(SERVICE *service, char **options)
/* Log whether the binlog encryption option value is on */
if (inst->encryption.enabled)
{
MXS_NOTICE("%s: Service has binlog encryption set to ON",
service->name);
MXS_NOTICE("%s: Service has binlog encryption set to ON, algorithm: %s,"
" KEY len %lu bits",
service->name,
blr_get_encryption_algorithm(inst->encryption.encryption_algorithm),
8 * inst->encryption.key_len);
}
/**
@ -2393,3 +2431,122 @@ destroyInstance(ROUTER *instance)
spinlock_release(&inst->lock);
}
/**
* Return the the value from hexadecimal digit
*
* @param c Then hex char
* @return The numeric value
*/
unsigned int from_hex(char c)
{
return c <= '9' ? c - '0' : tolower(c) - 'a' + 10;
}
/**
* Parse a buffer of HEX data
*
* An encryption Key and its len are stored
* in router->encryption struct
*
* @param router The router instance
* @param buffer A buffer of bytes, in hex format
* @return true on success and false on error
*/
bool blr_parse_key(char *buffer, ROUTER_INSTANCE *router)
{
char *p = buffer;
int length = 0;
uint8_t *key = (uint8_t *)router->encryption.key_value;
while (isspace(*p) && *p != '\n')
{
p++;
}
while (isxdigit(p[0]) && isxdigit(p[1]) && length <= BINLOG_AES_MAX_KEY_LEN)
{
key[length++] = from_hex(p[0]) * 16 + from_hex(p[1]);
p+= 2;
}
if (isxdigit(*p) ||
(length != 16 && length != 24 && length != 32))
{
MXS_ERROR("Found invalid Encryption Key at index %lu. File %s",
p - buffer,
router->encryption.key_management_filename);
return false;
}
router->encryption.key_len = length;
return true;
}
/**
* Read the encryption key form a file
*
* The key must be written in HEX format
*
* @param router The router instance
* @return false on error and true on success
*/
bool blr_get_encryption_key(ROUTER_INSTANCE *router)
{
int ret = false;
if (router->encryption.key_management_filename == NULL)
{
MXS_ERROR("Service %s, encryption key is not set. "
"Please specify key filename with 'encryption_key_file'",
router->service->name);
return ret;
}
else
{
int size = BINLOG_MAX_KEYFILE_LINE_LEN;
char *buffer = MXS_CALLOC(1, size * sizeof(char));
if (buffer)
{
int file = open(router->encryption.key_management_filename, O_RDONLY);
if (file)
{
int n;
if ((n = read(file, buffer, size)) > 0)
{
if (buffer[n - 1 ] == '\n')
{
buffer[n - 1] = '\0';
n--;
}
memset(router->encryption.key_value, '\0', sizeof(router->encryption.key_value));
/* Parse buffer for key */
if (blr_parse_key(buffer, router))
{
/* Success */
router->encryption.key_id = BINLOG_SYSTEM_DATA_CRYPTO_SCHEME;
ret = true;
}
}
close(file);
}
else
{
char errbuf[MXS_STRERROR_BUFLEN];
MXS_ERROR("%s, Failed to open KEY file '%s': %s",
router->service->name,
router->encryption.key_management_filename,
strerror_r(errno, errbuf, sizeof(errbuf)));
}
MXS_FREE(buffer);
return ret;
}
else
{
MXS_OOM_MESSAGE("Failed to allocate enough memory while reading KEY file.");
return ret;
}
}
}

View File

@ -68,8 +68,26 @@ MXS_BEGIN_DECLS
#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
#define BINLOG_FATAL_ERROR_READING 1236
/* Binlog Encryption */
#define BINLOG_ENC_ALGO_NAME_LEN 13
#define BINLOG_FLAG_ENCRYPT 1
#define BINLOG_FLAG_DECRYPT 0
#define BINLOG_AES_MAX_KEY_LEN 32
#define BINLOG_MAX_CRYPTO_SCHEME 2
#define BINLOG_SYSTEM_DATA_CRYPTO_SCHEME 1
#define BINLOG_MAX_KEYFILE_LINE_LEN 130
/* Supported Encryption algorithms */
enum blr_aes_mode
{
BLR_AES_CBC,
BLR_AES_CTR
};
/* Default encryption alogorithm is AES_CTR */
#define BINLOG_DEFAULT_ENC_ALGO BLR_AES_CTR
/**
* Binlog event types
@ -474,9 +492,11 @@ typedef struct
typedef struct binlog_encryption_setup
{
bool enabled;
char encryption_algorithm[BINLOG_ENCRYPTION_ALGORYTHM_NAME_LEN];
int encryption_algorithm;
char *key_management_filename;
uint8_t *keys;
uint8_t key_value[BINLOG_AES_MAX_KEY_LEN];
unsigned long key_len;
uint8_t key_id;
} BINLOG_ENCRYPTION_SETUP;
/**
@ -755,6 +775,11 @@ extern bool blr_send_event(blr_thread_role_t role,
REP_HEADER *hdr,
uint8_t *buf);
extern const char *blr_get_encryption_algorithm(int);
extern int blr_check_encryption_algorithm(char *);
extern const char *blr_encryption_algorithm_list(void);
extern bool blr_get_encryption_key(ROUTER_INSTANCE *);
MXS_END_DECLS
#endif

View File

@ -39,6 +39,11 @@
* detected from master binlog stream
* 19/09/2016 Massimiliano Pinto START_ENCRYPTION_EVENT is detected by maxbinlocheck.
* 21/09/2016 Massimiliano Pinto Addition of START_ENCRYPTION_EVENT: new event is written
* 25/11/2016 Massimiliano Pinto Binlog files can be encrypted with specified AES key
* and AES algorithm (Only AES_CTR right now).
* Events are decrypted before being sent to slaves.
* Events larger than 16MBytes are currently not suitable
* for ecryption/decryption.
*
* @endverbatim
*/
@ -65,6 +70,52 @@
#include <inttypes.h>
#include <maxscale/secrets.h>
/**
* AES_CTR handling
*
* @param klen The AES Key len
* @return The EVP_AES_CTR routine for key len
*/
static inline const EVP_CIPHER *aes_ctr(unsigned int klen)
{
switch (klen)
{
case 16: return EVP_aes_128_ctr();
case 24: return EVP_aes_192_ctr();
case 32: return EVP_aes_256_ctr();
default: return 0;
}
}
/**
* AES_CBC handling
*
* @param klen The AES Key len
* @return The EVP_AES_CBC routine for key len
*/
static inline const EVP_CIPHER *aes_cbc(uint klen)
{
switch (klen)
{
case 16: return EVP_aes_128_cbc();
case 24: return EVP_aes_192_cbc();
case 32: return EVP_aes_256_cbc();
default: return 0;
}
}
/**
* Array of functions for supported algorithms
*/
const EVP_CIPHER *(*ciphers[])(unsigned int) =
{
aes_cbc,
aes_ctr
};
static const char *blr_encryption_algorithm_names[BINLOG_MAX_CRYPTO_SCHEME] = {"aes_cbc", "aes_ctr"};
static const char blr_encryption_algorithm_list_names[] = "aes_cbc, aes_ctr";
static int blr_file_create(ROUTER_INSTANCE *router, char *file);
static void blr_log_header(int priority, char *msg, uint8_t *ptr);
void blr_cache_read_master_data(ROUTER_INSTANCE *router);
@ -99,6 +150,17 @@ static int blr_write_special_event(ROUTER_INSTANCE *router,
static uint8_t *blr_create_start_encryption_event(ROUTER_INSTANCE *router,
uint32_t event_pos,
bool do_checksum);
GWBUF *blr_prepare_encrypted_event(ROUTER_INSTANCE *router,
uint8_t *event,
uint32_t event_size,
uint32_t pos,
const uint8_t *nonce,
int action);
GWBUF *blr_aes_crypt(ROUTER_INSTANCE *router,
uint8_t *event,
uint32_t event_size,
uint8_t *iv,
int action);
/** MaxScale generated events */
typedef enum
@ -408,15 +470,15 @@ blr_file_append(ROUTER_INSTANCE *router, char *file)
int
blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size, uint8_t *buf)
{
int n;
bool write_begin_encryption = false;
int n = 0;
bool write_start_encryption_event = 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)
{
write_begin_encryption = true;
write_start_encryption_event = true;
}
/**
@ -432,67 +494,45 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size,
{
return 0;
}
n = hole_size;
}
if (router->encryption.enabled && router->encryption_ctx != NULL && !write_begin_encryption)
if (router->encryption.enabled && 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);
/**
* 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++)
GWBUF *encrypted;
uint8_t *encr_ptr;
if ((encrypted = blr_prepare_encrypted_event(router,
buf,
size,
router->current_pos,
NULL,
BINLOG_FLAG_ENCRYPT)) == NULL)
{
buf_ptr[i]= buf_ptr[i] ^ iv[i];
return 0;
}
memmove(buf, buf + BINLOG_EVENT_LEN_OFFSET, 4);
memcpy(buf + BINLOG_EVENT_LEN_OFFSET, &event_size, 4);
encr_ptr = GWBUF_DATA(encrypted);
n = pwrite(router->binlog_fd, encr_ptr, size, router->last_written);
gwbuf_free(encrypted);
encrypted = NULL;
} else {
/* Write current received event form master */
n = pwrite(router->binlog_fd, buf, size, router->last_written);
}
/* Write current received event form master */
if ((n = pwrite(router->binlog_fd, buf, size,
router->last_written)) != size)
/* Check write operation result*/
if (n != size)
{
char err_msg[MXS_STRERROR_BUFLEN];
MXS_ERROR("%s: Failed to write binlog record at %lu of %s, %s. "
"Truncating to previous record.",
router->service->name, router->last_written,
router->service->name, router->binlog_position,
router->binlog_name,
strerror_r(errno, err_msg, sizeof(err_msg)));
/* Remove any partial event that was written */
if (ftruncate(router->binlog_fd, router->last_written))
if (ftruncate(router->binlog_fd, router->binlog_position))
{
MXS_ERROR("%s: Failed to truncate binlog record at %lu of %s, %s. ",
router->service->name, router->last_written,
@ -510,7 +550,7 @@ 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->encryption.enabled && write_begin_encryption)
if (router->encryption.enabled && write_start_encryption_event)
{
uint64_t event_size = sizeof(START_ENCRYPTION_EVENT);
uint64_t file_offset = router->current_pos;
@ -522,9 +562,14 @@ blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint32_t size,
{
return 0;
}
/* At this point the router->encryption_ctx is set:
* Encryption of new events can start
*/
write_start_encryption_event = false;
write_begin_encryption = false;
n = event_size;
}
return n;
}
@ -725,7 +770,7 @@ blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned long pos, REP_HE
switch (n)
{
case 0:
MXS_DEBUG("Reached end of binlog file '%s' at %lu.",
MXS_INFO("Reached end of binlog file '%s' at %lu.",
file->binlogname, pos);
/* set ok indicator */
@ -756,119 +801,10 @@ 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 (enc_ctx && pos >= enc_ctx->first_enc_event_pos)
/* If enc_ctx is NULL check position */
if (enc_ctx == NULL)
{
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, 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]);
hdr->event_size = extract_field(&hdbuf[9], 32);
hdr->next_pos = EXTRACT32(&hdbuf[13]);
hdr->flags = EXTRACT16(&hdbuf[17]);
/* event pos & size checks */
if (hdr->event_size == 0 || ((hdr->next_pos != (pos + hdr->event_size)) &&
(hdr->event_type != ROTATE_EVENT)))
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Client requested master to start replication from invalid "
"position %lu in binlog file '%s'", pos,
file->binlogname);
return NULL;
}
/* event type checks */
if (router->mariadb10_compat)
{
if (hdr->event_type > MAX_EVENT_TYPE_MARIADB10)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Invalid MariaDB 10 event type 0x%x at %lu in binlog file '%s'",
hdr->event_type, pos, file->binlogname);
return NULL;
}
}
else
{
if (hdr->event_type > MAX_EVENT_TYPE)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Invalid event type 0x%x at %lu in binlog file '%s'", hdr->event_type,
pos, file->binlogname);
return NULL;
}
}
if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT)
{
MXS_ERROR("Next position in header appears to be incorrect "
"rereading event header at pos %lu in file %s, "
"file size is %lu. Master will write %lu in %s next.",
pos, file->binlogname, filelen, router->binlog_position,
router->binlog_name);
if ((n = pread(file->fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN)
{
switch (n)
{
case 0:
MXS_DEBUG("Reached end of binlog file at %lu.",
pos);
/* set ok indicator */
hdr->ok = SLAVE_POS_READ_OK;
break;
case -1:
{
char err_msg[MXS_STRERROR_BUFLEN];
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Failed to reread header in binlog file '%s'; (%s), event at %lu",
file->binlogname, strerror_r(errno, err_msg, sizeof(err_msg)), pos);
if (errno == EBADF)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Bad file descriptor rereading header for binlog file '%s', "
"refcount %d, descriptor %d, event at %lu",
file->binlogname, file->refcnt, file->fd, pos);
}
}
break;
default:
snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data rereading log event header; "
"expected %d bytes but read %d, position %lu in binlog file '%s'",
BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname);
break;
}
return NULL;
}
hdr->timestamp = EXTRACT32(hdbuf);
hdr->event_type = hdbuf[4];
hdr->serverid = EXTRACT32(&hdbuf[5]);
@ -876,18 +812,116 @@ blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned long pos, REP_HE
hdr->next_pos = EXTRACT32(&hdbuf[13]);
hdr->flags = EXTRACT16(&hdbuf[17]);
if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT)
/* event pos & size checks */
if (hdr->event_size == 0 || ((hdr->next_pos != (pos + hdr->event_size)) &&
(hdr->event_type != ROTATE_EVENT)))
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Next event position still incorrect after rereading, "
"event at %lu in binlog file '%s'", pos, file->binlogname);
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Client requested master to start replication from invalid "
"position %lu in binlog file '%s'", pos,
file->binlogname);
return NULL;
}
/* event type checks */
if (router->mariadb10_compat)
{
if (hdr->event_type > MAX_EVENT_TYPE_MARIADB10)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Invalid MariaDB 10 event type 0x%x at %lu in binlog file '%s'",
hdr->event_type, pos, file->binlogname);
return NULL;
}
}
else
{
MXS_ERROR("Next position corrected by "
"rereading");
if (hdr->event_type > MAX_EVENT_TYPE)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Invalid event type 0x%x at %lu in binlog file '%s'", hdr->event_type,
pos, file->binlogname);
return NULL;
}
}
/* Try to read again the binlog event */
if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT)
{
MXS_ERROR("Next position in header appears to be incorrect "
"rereading event header at pos %lu in file %s, "
"file size is %lu. Master will write %lu in %s next.",
pos, file->binlogname, filelen, router->binlog_position,
router->binlog_name);
if ((n = pread(file->fd, hdbuf, BINLOG_EVENT_HDR_LEN, pos)) != BINLOG_EVENT_HDR_LEN)
{
switch (n)
{
case 0:
MXS_INFO("Reached end of binlog file at %lu.",
pos);
/* set ok indicator */
hdr->ok = SLAVE_POS_READ_OK;
break;
case -1:
{
char err_msg[MXS_STRERROR_BUFLEN];
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Failed to reread header in binlog file '%s'; (%s), event at %lu",
file->binlogname, strerror_r(errno, err_msg, sizeof(err_msg)), pos);
if (errno == EBADF)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
"Bad file descriptor rereading header for binlog file '%s', "
"refcount %d, descriptor %d, event at %lu",
file->binlogname, file->refcnt, file->fd, pos);
}
}
break;
default:
snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Bogus data rereading log event header; "
"expected %d bytes but read %d, position %lu in binlog file '%s'",
BINLOG_EVENT_HDR_LEN, n, pos, file->binlogname);
break;
}
return NULL;
}
/* Fill replication header struct */
hdr->timestamp = EXTRACT32(hdbuf);
hdr->event_type = hdbuf[4];
hdr->serverid = EXTRACT32(&hdbuf[5]);
hdr->event_size = extract_field(&hdbuf[9], 32);
hdr->next_pos = EXTRACT32(&hdbuf[13]);
hdr->flags = EXTRACT16(&hdbuf[17]);
if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN, "Next event position still incorrect after rereading, "
"event at %lu in binlog file '%s'", pos, file->binlogname);
return NULL;
}
else
{
MXS_ERROR("Next position corrected by "
"rereading");
}
}
}
else
{
/**
* The encryption context is set at this point.
*
* Only the event size is in "clear", use it.
*/
hdr->event_size = extract_field(&hdbuf[9], 32);
}
/* Allocate memory for the binlog event */
if ((result = gwbuf_alloc(hdr->event_size)) == NULL)
{
snprintf(errmsg, BINLOG_ERROR_MSG_LEN,
@ -898,12 +932,23 @@ blr_read_binlog(ROUTER_INSTANCE *router, BLFILE *file, unsigned long pos, REP_HE
data = GWBUF_DATA(result);
memcpy(data, hdbuf, BINLOG_EVENT_HDR_LEN); // Copy the header in
memcpy(data, hdbuf, BINLOG_EVENT_HDR_LEN); // Copy the header in the buffer
if ((n = pread(file->fd, &data[BINLOG_EVENT_HDR_LEN], hdr->event_size - BINLOG_EVENT_HDR_LEN,
pos + BINLOG_EVENT_HDR_LEN))
!= hdr->event_size - BINLOG_EVENT_HDR_LEN) // Read the balance
{
if (n == 0)
{
MXS_INFO("Reached end of binlog file at %lu while reading remaining bytes.",
pos);
/* set ok indicator */
hdr->ok = SLAVE_POS_READ_OK;
return NULL;
}
if (n == -1)
{
char err_msg[MXS_STRERROR_BUFLEN];
@ -935,6 +980,40 @@ 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 (enc_ctx && pos >= enc_ctx->first_enc_event_pos)
{
GWBUF *decrypted_event;
uint8_t *decrypt_ptr;
/* prepare and decrypt the event */
if ((decrypted_event = blr_prepare_encrypted_event(router,
data,
hdr->event_size,
pos,
enc_ctx->nonce,
BINLOG_FLAG_DECRYPT)) == NULL)
{
gwbuf_free(result);
return NULL;
}
decrypt_ptr = GWBUF_DATA(decrypted_event);
/* Fill replication header struct */
hdr->timestamp = EXTRACT32(decrypt_ptr);
hdr->event_type = decrypt_ptr[4];
hdr->serverid = EXTRACT32(&decrypt_ptr[5]);
hdr->event_size = extract_field(&decrypt_ptr[9], 32);
hdr->next_pos = EXTRACT32(&decrypt_ptr[13]);
hdr->flags = EXTRACT16(&decrypt_ptr[17]);
/* Free data read from disk */
gwbuf_free(result);
/* Set the decrypted event as result*/
result = decrypted_event;
}
/* set OK indicator */
hdr->ok = SLAVE_POS_READ_OK;
@ -1174,7 +1253,8 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
struct stat statb;
uint8_t hdbuf[BINLOG_EVENT_HDR_LEN];
uint8_t *data;
GWBUF *result;
GWBUF *result = NULL;
GWBUF *decrypted_event = NULL;
unsigned long long pos = 4;
unsigned long long last_known_commit = 4;
@ -1400,16 +1480,10 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
iv_hex, (unsigned long)event_size,
(unsigned long)(pos + event_size));
/* Next event pos is ps + event size */
pos = pos + event_size;
hdr.event_size = event_size;
/* Update other offsets as well */
router->binlog_position = pos;
router->current_safe_event = pos;
router->current_pos = pos;
continue;
}
else {
/* fill replication header struct */
hdr.timestamp = EXTRACT32(hdbuf);
@ -1499,6 +1573,8 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
return 1;
}
}
/* Allocate a GWBUF for the event */
if ((result = gwbuf_alloc(hdr.event_size)) == NULL)
{
@ -1599,8 +1675,53 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
fde_seen = 0;
}
/* get event content after event header */
ptr = data + BINLOG_EVENT_HDR_LEN;
/* decrypt events */
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);
uint8_t *decrypt_ptr;
/**
* Events are encrypted.
*/
if ((decrypted_event = blr_prepare_encrypted_event(router,
data,
hdr.event_size,
pos,
NULL,
BINLOG_FLAG_DECRYPT)) == NULL)
{
gwbuf_free(result);
return 1;
}
decrypt_ptr = GWBUF_DATA(decrypted_event);
/* fill replication header struct */
hdr.timestamp = EXTRACT32(decrypt_ptr);
hdr.event_type = decrypt_ptr[4];
hdr.serverid = EXTRACT32(&decrypt_ptr[5]);
hdr.event_size = extract_field(&decrypt_ptr[9], 32);
hdr.next_pos = pos + event_size;
hdr.flags = EXTRACT16(&decrypt_ptr[17]);
MXS_DEBUG("Event time %lu\nEvent Type %u (%s)\nServer Id %lu\nNextPos %lu/%lu\nFlags %u",
(unsigned long)hdr.timestamp, hdr.event_type,
blr_get_event_description(router, hdr.event_type),
(unsigned long)hdr.serverid, (unsigned long)EXTRACT32(&decrypt_ptr[13]),
(unsigned long)hdr.next_pos, hdr.flags);
/* get event content after event header */
ptr = decrypt_ptr + BINLOG_EVENT_HDR_LEN;
}
else
{
/* get event content after event header */
ptr = data + BINLOG_EVENT_HDR_LEN;
}
/* check for FORMAT DESCRIPTION EVENT */
if (hdr.event_type == FORMAT_DESCRIPTION_EVENT)
@ -1702,6 +1823,7 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
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)
{
return 1;
@ -1726,7 +1848,7 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
if (debug)
{
char *cksum_format = ", crc32 0x";
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';
@ -1744,6 +1866,10 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
{
strcpy(hex_checksum, cksum_format);
gw_bin2hex(hex_checksum + strlen(cksum_format) , cksum_data, BINLOG_EVENT_CRC_SIZE);
for (char *p = hex_checksum + strlen(cksum_format) ; *p; ++p)
{
*p = tolower(*p);
}
}
MXS_DEBUG("- START_ENCRYPTION event @ %llu, size %lu, next pos is @ %lu, flags %u%s",
@ -1755,6 +1881,12 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
ste_event.binlog_key_version, nonce_hex);
}
if (router->encryption.key_len == 0)
{
MXS_ERROR("*** The binlog is encrypted. No KEY/Algo found for decryption. ***");
return 1;
}
start_encryption_seen = 1;
/* Update the router encryption context */
@ -1807,7 +1939,7 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
{
if (hdr.event_type == MARIADB10_GTID_EVENT)
{
uint64_t n_sequence; /* 8 bytes */
uint64_t n_sequence;/* 8 bytes */
uint32_t domainid; /* 4 bytes */
unsigned int flags; /* 1 byte */
n_sequence = extract_field(ptr, 64);
@ -1960,6 +2092,7 @@ blr_read_events_all_events(ROUTER_INSTANCE *router, int fix, int debug)
}
gwbuf_free(result);
gwbuf_free(decrypted_event);
/* pos and next_pos sanity checks */
if (hdr.next_pos > 0 && hdr.next_pos < pos)
@ -2417,6 +2550,23 @@ blr_write_special_event(ROUTER_INSTANCE *router, uint32_t file_offset, uint32_t
{
return 0;
}
if (router->encryption.enabled && router->encryption_ctx != NULL)
{
GWBUF *encrypted;
uint8_t *encr_ptr;
if ((encrypted = blr_prepare_encrypted_event(router,
new_event,
event_size,
router->current_pos,
NULL,
BINLOG_FLAG_ENCRYPT)) == NULL)
{
return 0;
}
memcpy(new_event, GWBUF_DATA(encrypted), event_size);
gwbuf_free(encrypted);
}
break;
case BLRM_START_ENCRYPTION:
new_event_desc = "MARIADB10_START_ENCRYPTION";
@ -2448,8 +2598,8 @@ blr_write_special_event(ROUTER_INSTANCE *router, uint32_t file_offset, uint32_t
break;
}
// Write the event
if ((n = pwrite(router->binlog_fd, new_event, event_size, file_offset)) != event_size)
/* Write the event */
if ((n = pwrite(router->binlog_fd, new_event, event_size, router->last_written)) != event_size)
{
char err_msg[MXS_STRERROR_BUFLEN];
MXS_ERROR("%s: Failed to write %s special binlog record at %lu of %s, %s. "
@ -2459,7 +2609,7 @@ blr_write_special_event(ROUTER_INSTANCE *router, uint32_t file_offset, uint32_t
strerror_r(errno, err_msg, sizeof(err_msg)));
/* Remove any partial event that was written */
if (ftruncate(router->binlog_fd, router->last_written))
if (ftruncate(router->binlog_fd, router->binlog_position))
{
MXS_ERROR("%s: Failed to truncate %s special binlog record at %lu of %s, %s. ",
router->service->name, new_event_desc, (unsigned long)file_offset,
@ -2504,6 +2654,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));
if (new_encryption_ctx == NULL)
{
return NULL;
@ -2524,10 +2675,11 @@ blr_create_start_encryption_event(ROUTER_INSTANCE *router, uint32_t event_pos, b
// Populate Event header 19 bytes
encode_value(&new_event[0], time(NULL), 32); // now
new_event[4] = MARIADB10_START_ENCRYPTION_EVENT; // type is BEGIN_ENCRYPTION_EVENT
/* Set binlog server instance server id */
encode_value(&new_event[5], router->serverid, 32); // serverid of maxscale
encode_value(&new_event[9], event_size, 32); // event size
encode_value(&new_event[13], event_pos + event_size, 32); // next_pos
encode_value(&new_event[17], LOG_EVENT_IGNORABLE_F, 16); // flag is LOG_EVENT_IGNORABLE_F OR 0 ?
encode_value(&new_event[17], 0, 16); // flag is 0 ?
/**
* Now add the event content, after 19 bytes of header
@ -2567,6 +2719,7 @@ blr_create_start_encryption_event(ROUTER_INSTANCE *router, uint32_t event_pos, b
memcpy(&new_encryption_ctx->binlog_key_version,
&new_event[BINLOG_EVENT_HDR_LEN + 1], BLRM_KEY_VERSION_LENGTH);
/* Set the router encryption context for current binlog file */
MXS_FREE(router->encryption_ctx);
router->encryption_ctx = new_encryption_ctx;
@ -2574,3 +2727,220 @@ blr_create_start_encryption_event(ROUTER_INSTANCE *router, uint32_t event_pos, b
return new_event;
}
/**
* Encrypt/Decrypt an array of bytes
*
* Note: The output buffer is 4 bytes larger than input
* Encrypted bytes start at offset 4
*
* @param router The router instance
* @param buffer The buffer to encrypt/decrypt
* @param size The buffer size
* @param iv The AES initialisation Vector
* @action Crypt action: 1 encrypt, 1 decrypt
* @return A new allocated, encrypted, GWBUF buffer
*
*/
GWBUF *blr_aes_crypt(ROUTER_INSTANCE *router, uint8_t *buffer, uint32_t size, uint8_t *iv, int action)
{
EVP_CIPHER_CTX *ctx;
ctx = EVP_CIPHER_CTX_new();
uint8_t key[BINLOG_AES_MAX_KEY_LEN];
int outlen;
int flen;
uint32_t encrypted_size = size + 4;
int total_len;
GWBUF *outbuf = gwbuf_alloc(encrypted_size);
uint8_t *out_ptr;
if (outbuf == NULL)
{
return NULL;
}
out_ptr = GWBUF_DATA(outbuf);
if (router->encryption.key_len == 0)
{
MXS_ERROR("The encrytion key len is 0");
return NULL;
}
else
{
memcpy(key, router->encryption.key_value, router->encryption.key_len);
}
EVP_CIPHER_CTX_init(ctx);
/* Set the encryption algorithm accordingly to key_len and encryption mode */
EVP_CipherInit_ex(ctx,
ciphers[router->encryption.encryption_algorithm](router->encryption.key_len),
NULL, key, iv, action);
/* No padding */
EVP_CIPHER_CTX_set_padding(ctx, 0);
if(!EVP_CipherUpdate(ctx, out_ptr + 4, &outlen, buffer, size))
{
MXS_ERROR("Error in EVP_CipherUpdate");
EVP_CIPHER_CTX_free(ctx);
MXS_FREE(outbuf);
return NULL;
}
if (!EVP_CipherFinal_ex(ctx, (out_ptr + 4 + outlen), (int*)&flen))
{
MXS_ERROR("Error in EVP_CipherFinal_ex");
EVP_CIPHER_CTX_free(ctx);
MXS_FREE(outbuf);
return NULL;
}
EVP_CIPHER_CTX_free(ctx);
return outbuf;
}
/**
* The routine prepares a binlg event for encryption and ecrypts it
*
* @param router The ruter instance
* @buf The binlog event
* @size The event size (CRC32 four bytes included)
* @pos The position of the event in binlog file
* @nonce The binlog nonce 12 bytes as in START_ENCRYPTION_EVENT
* of requested or current binlog file
* If nonce is NULL the one from current binlog file is used.
* @action Encryption action: 1 Encryp, 0 Decryot
* @return A GWBUF buffer or NULL omn error
*/
GWBUF *blr_prepare_encrypted_event(ROUTER_INSTANCE *router, uint8_t *buf, uint32_t size, uint32_t pos, const uint8_t *nonce, int action)
{
uint8_t iv[BLRM_IV_LENGTH];
uint32_t file_offset = pos;
uint8_t event_size[4];
const uint8_t *nonce_ptr = nonce;
GWBUF *encrypted;
uint8_t *enc_ptr;
/* If nonce is NULL use the router current binlog file */
if (nonce_ptr == NULL)
{
BINLOG_ENCRYPTION_CTX *encryption_ctx = (BINLOG_ENCRYPTION_CTX *)(router->encryption_ctx);
nonce_ptr = encryption_ctx->nonce;
}
/* Encryption IV is 12 bytes nonce + 4 bytes event position */
memcpy(iv, nonce_ptr, BLRM_NONCE_LENGTH);
gw_mysql_set_byte4(iv + BLRM_NONCE_LENGTH, (unsigned long)file_offset);
/**
* Encrypt binlog event, steps:
*
* 1: Save event size (buf + 9, 4 bytes)
* 2: move first 4 bytes of buf to buf + 9
* 3: encrypt buf starting from buf + 4 (so it will be event_size - 4)
* 4: move encrypted_data + 9 (4 bytes) to encrypted_data[0]
* 5: Copy saved_event_size 4 bytes into encrypted_data + 9
*/
/* (1): Save event size (buf + 9, 4 bytes) */
memcpy(&event_size, buf + BINLOG_EVENT_LEN_OFFSET, 4);
/* (2): move first 4 bytes of buf to buf + 9 */
memmove(buf + BINLOG_EVENT_LEN_OFFSET, buf, 4);
#ifdef SS_DEBUG
char iv_hex[AES_BLOCK_SIZE * 2 + 1] = "";
char nonce_hex[BLRM_NONCE_LENGTH * 2 + 1] = "";
/* Human readable debug */
gw_bin2hex(iv_hex, iv, BLRM_IV_LENGTH);
gw_bin2hex(nonce_hex, nonce_ptr, BLRM_NONCE_LENGTH);
MXS_DEBUG("** Decrypting Event @ %lu: the IV is %s, size is %lu, next pos is %lu",
(unsigned long)pos,
iv_hex, (unsigned long)size,
(unsigned long)(pos + size));
#endif
/**
* (3): encrypt the event stored in buf starting from (buf + 4):
* with len (event_size - 4)
*
* NOTE: the encrypted_data buffer returned by blr_aes_encrypt() contains:
* (size - 4) encrypted bytes + (4) bytes event size in clear
*
* The encrypted buffer has same size of the original event (size variable)
*/
if ((encrypted = blr_aes_crypt(router, buf + 4, size - 4, iv, action)) == NULL)
{
return NULL;
}
enc_ptr = GWBUF_DATA(encrypted);
/* (4): move encrypted_data + 9 (4 bytes) to encrypted_data[0] */
memmove(enc_ptr, enc_ptr + BINLOG_EVENT_LEN_OFFSET, 4);
/* (5): Copy saved_event_size 4 bytes into encrypted_data + 9 */
memcpy(enc_ptr + BINLOG_EVENT_LEN_OFFSET, &event_size, 4);
return encrypted;
}
/**
* Return the encryption algorithm string
*
* @param algo The algorithm value
* @return A static string or NULL
*/
const char *blr_get_encryption_algorithm(int algo)
{
if (algo < 0 || algo >= BINLOG_MAX_CRYPTO_SCHEME)
{
return NULL;
}
else
{
return blr_encryption_algorithm_names[algo];
}
}
/**
* Return the encryption algorithm value
*
* @param name The alogorithm string
* @return The numeric value or -1 on error
*/
int blr_check_encryption_algorithm(char *name)
{
if (name)
{
if (strcasecmp(name, "aes_cbc") == 0)
{
return BLR_AES_CBC;
}
if (strcasecmp(name, "aes_ctr") == 0)
{
return BLR_AES_CTR;
}
}
return -1;
}
/**
* Return a string with a list of supported algorithms
*
* @return The algorith list as char *
*/
const char *blr_encryption_algorithm_list(void)
{
return blr_encryption_algorithm_list_names;
}

View File

@ -50,6 +50,8 @@
* 22/07/2016 Massimiliano Pinto Added semi_sync replication support
* 24/08/2016 Massimiliano Pinto Added slave notification and blr_distribute_binlog_record removed
* 01/09/2016 Massimiliano Pinto Added support for ANNOTATE_ROWS_EVENT in COM_BINLOG_DUMP
* 11/11/2016 Massimiliano Pinto Encryption context is freed and set to null a new binlog file
* is being created due to ROTATE event.
*
* @endverbatim
*/
@ -951,7 +953,7 @@ blr_make_binlog_dump(ROUTER_INSTANCE *router)
/* With mariadb10 always ask for annotate rows events */
if (router->mariadb10_compat)
{
// set flag for annotate rows event
/* set flag for annotate rows event request */
encode_value(&data[9], BLR_REQUEST_ANNOTATE_ROWS_EVENT, 16);
}
else
@ -1256,13 +1258,14 @@ blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt)
spinlock_release(&router->lock);
#ifdef SHOW_EVENTS
printf("blr @ %lu: len %lu, event type 0x%02x, flags 0x%04x, "
"event size %d, event timestamp %lu\n",
"event size %d, event timestamp %lu, next pos %lu\n",
router->current_pos,
(unsigned long)len - 4,
hdr.event_type,
hdr.flags,
hdr.event_size,
(unsigned long)hdr.timestamp);
(unsigned long)hdr.timestamp,
(unsigned long)hdr.next_pos);
#endif
}
}
@ -1861,9 +1864,11 @@ blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *ptr, REP_HEADER *hdr)
strcpy(router->prevbinlog, router->binlog_name);
int rotated = 1;
int remove_encrytion_ctx = 0;
if (strncmp(router->binlog_name, file, slen) != 0)
{
remove_encrytion_ctx = 1;
router->stats.n_rotates++;
if (blr_file_rotate(router, file, pos) == 0)
{
@ -1872,6 +1877,13 @@ blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *ptr, REP_HEADER *hdr)
}
spinlock_acquire(&router->binlog_lock);
router->rotating = 0;
/* remove current binlog encryption context */
if (remove_encrytion_ctx == 1)
{
MXS_FREE(router->encryption_ctx);
router->encryption_ctx = NULL;
}
spinlock_release(&router->binlog_lock);
return rotated;
}
@ -2420,12 +2432,12 @@ blr_write_data_into_binlog(ROUTER_INSTANCE *router, uint32_t data_len, uint8_t *
char err_msg[MXS_STRERROR_BUFLEN];
MXS_ERROR("%s: Failed to write binlog record at %lu of %s, %s. "
"Truncating to previous record.",
router->service->name, router->last_written,
router->service->name, router->binlog_position,
router->binlog_name,
strerror_r(errno, err_msg, sizeof(err_msg)));
/* Remove any partial event that was written */
if (ftruncate(router->binlog_fd, router->last_written))
if (ftruncate(router->binlog_fd, router->binlog_position))
{
MXS_ERROR("%s: Failed to truncate binlog record at %lu of %s, %s. ",
router->service->name, router->last_written,

View File

@ -32,6 +32,8 @@
* 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.
* 25/11/16 Massimiliano Pinto MariaDB 10.1 encrypted files can be checked
* with Key and Algo options
*
*
* @endverbatim
@ -50,6 +52,7 @@
static void printVersion(const char *progname);
static void printUsage(const char *progname);
static int set_encryption_options(ROUTER_INSTANCE *inst, char *key_file, char *aes_algo);
static struct option long_options[] =
{
@ -57,11 +60,13 @@ static struct option long_options[] =
{"version", no_argument, 0, 'V'},
{"fix", no_argument, 0, 'f'},
{"mariadb10", no_argument, 0, 'M'},
{"key_file", required_argument, 0, 'K'},
{"aes_algo", required_argument, 0, 'A'},
{"help", no_argument, 0, '?'},
{0, 0, 0, 0}
};
char *binlog_check_version = "1.2.0";
char *binlog_check_version = "2.0.1";
int
maxscale_uptime()
@ -75,9 +80,12 @@ int main(int argc, char **argv)
int debug_out = 0;
int fix_file = 0;
int mariadb10_compat = 0;
char *key_file = NULL;
char *aes_algo = NULL;
char c;
while ((c = getopt_long(argc, argv, "dVfM?", long_options, &option_index)) >= 0)
while ((c = getopt_long(argc, argv, "dVfMK:A:?", long_options, &option_index)) >= 0)
{
switch (c)
{
@ -94,6 +102,12 @@ int main(int argc, char **argv)
case 'M':
mariadb10_compat = 1;
break;
case 'K':
key_file = optarg;
break;
case 'A':
aes_algo = optarg;
break;
case '?':
printUsage(*argv);
exit(optopt ? EXIT_FAILURE : EXIT_SUCCESS);
@ -148,7 +162,8 @@ int main(int argc, char **argv)
printf("ERROR: Failed to open binlog file %s: %s.\n",
path, strerror(errno));
MXS_FREE(inst);
exit(EXIT_FAILURE);
mxs_log_flush_sync();
mxs_log_finish();
}
inst->binlog_fd = fd;
@ -169,6 +184,15 @@ int main(int argc, char **argv)
filelen = statb.st_size;
}
/* If encryption options are in use check and use them */
if (set_encryption_options(inst, key_file, aes_algo))
{
MXS_FREE(inst);
mxs_log_flush_sync();
mxs_log_finish();
exit(EXIT_FAILURE);
}
MXS_NOTICE("Checking %s (%s), size %lu bytes", path, inst->binlog_name, filelen);
/* read binary log */
@ -210,6 +234,69 @@ printUsage(const char *progname)
printf(" -d|--debug Print debug messages\n");
printf(" -M|--mariadb10 MariaDB 10 binlog compatibility\n");
printf(" -V|--version print version information and exit\n");
printf(" -K|--key_file AES Key file for MariaDB 10.1 binlog file decryption\n");
printf(" -A|--aes_algo AES Algorithm for MariaDB 10.1 binlog file decryption (default=AES_CTR, AES_CBC)\n");
printf(" -?|--help Print this help text\n");
}
/**
* Check and set the encryption options
*
* @param inst The current binlog instance
* @param key_file The AES Key filename
* @param aes_algo The AES algorithm
* @return 1 on failure, 0 on success
*/
static int set_encryption_options(ROUTER_INSTANCE *inst, char *key_file, char *aes_algo)
{
if (aes_algo && !key_file)
{
MXS_ERROR("AES algorithm set but no KEY file specified, exiting.");
return 1;
}
/* Get the encryption KEY */
if (key_file)
{
inst->encryption.key_management_filename = key_file;
if (!blr_get_encryption_key(inst))
{
return 1;
}
else
{
/* Check aes algorithm */
if (aes_algo)
{
int ret = blr_check_encryption_algorithm(aes_algo);
if (ret > -1)
{
inst->encryption.encryption_algorithm = ret;
}
else
{
MXS_ERROR("Invalid encryption_algorithm '%s'. "
"Supported algorithms: %s",
aes_algo,
blr_encryption_algorithm_list());
return 1;
}
}
else
{
inst->encryption.encryption_algorithm = BINLOG_DEFAULT_ENC_ALGO;
}
MXS_NOTICE("Decrypting binlog file with algorithm: %s,"
" KEY len %lu bits",
blr_get_encryption_algorithm(inst->encryption.encryption_algorithm),
8 * inst->encryption.key_len);
return 0;
}
}
else
{
return 0;
}
}