diff options
Diffstat (limited to 'storage/innobase/log/log0crypt.cc')
-rw-r--r-- | storage/innobase/log/log0crypt.cc | 170 |
1 files changed, 131 insertions, 39 deletions
diff --git a/storage/innobase/log/log0crypt.cc b/storage/innobase/log/log0crypt.cc index cd84dbe6e6d..8c4fd623fc6 100644 --- a/storage/innobase/log/log0crypt.cc +++ b/storage/innobase/log/log0crypt.cc @@ -86,6 +86,9 @@ log_block_get_start_lsn( return start_lsn; } +/*********************************************************************//** +Get crypt info from checkpoint. +@return a crypt info or NULL if not present. */ static const crypt_info_t* get_crypt_info( @@ -107,6 +110,9 @@ get_crypt_info( return NULL; } +/*********************************************************************//** +Get crypt info from log block +@return a crypt info or NULL if not present. */ static const crypt_info_t* get_crypt_info( @@ -125,15 +131,16 @@ log_blocks_crypt( const byte* block, /*!< in: blocks before encrypt/decrypt*/ ulint size, /*!< in: size of block */ byte* dst_block, /*!< out: blocks after encrypt/decrypt */ - bool is_encrypt) /*!< in: encrypt or decrypt*/ + int what) /*!< in: encrypt or decrypt*/ { byte *log_block = (byte*)block; Crypt_result rc = MY_AES_OK; uint dst_len; byte aes_ctr_counter[MY_AES_BLOCK_SIZE]; + byte is_encrypt= what == ENCRYPTION_FLAG_ENCRYPT; lsn_t lsn = is_encrypt ? log_sys->lsn : srv_start_lsn; - const int src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE; + const uint src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE; for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE) { ulint log_block_no = log_block_get_hdr_no(log_block); lsn_t log_block_start_lsn = log_block_get_start_lsn( @@ -168,21 +175,13 @@ log_blocks_crypt( bzero(aes_ctr_counter + 15, 1); int rc; - if (is_encrypt) { - rc = encryption_encrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, - dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, - (unsigned char*)(info->crypt_key), 16, - aes_ctr_counter, MY_AES_BLOCK_SIZE, 1, - LOG_DEFAULT_ENCRYPTION_KEY, - info->key_version); - } else { - rc = encryption_decrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, - dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, - (unsigned char*)(info->crypt_key), 16, - aes_ctr_counter, MY_AES_BLOCK_SIZE, 1, - LOG_DEFAULT_ENCRYPTION_KEY, - info->key_version); - } + rc = encryption_crypt(log_block + LOG_BLOCK_HDR_SIZE, src_len, + dst_block + LOG_BLOCK_HDR_SIZE, &dst_len, + (unsigned char*)(info->crypt_key), 16, + aes_ctr_counter, MY_AES_BLOCK_SIZE, + what | ENCRYPTION_FLAG_NOPAD, + LOG_DEFAULT_ENCRYPTION_KEY, + info->key_version); ut_a(rc == MY_AES_OK); ut_a(dst_len == src_len); @@ -195,9 +194,10 @@ next: } /*********************************************************************//** -Generate crypt key from crypt msg. */ +Generate crypt key from crypt msg. +@return true if successfull, false if not. */ static -void +bool init_crypt_key( /*===========*/ crypt_info_t* info) /*< in/out: crypt info */ @@ -206,7 +206,7 @@ init_crypt_key( memset(info->crypt_key, 0, sizeof(info->crypt_key)); memset(info->crypt_msg, 0, sizeof(info->crypt_msg)); memset(info->crypt_nonce, 0, sizeof(info->crypt_nonce)); - return; + return true; } byte mysqld_key[MY_AES_BLOCK_SIZE] = {0}; @@ -216,42 +216,59 @@ init_crypt_key( { ib_logf(IB_LOG_LEVEL_ERROR, "Redo log crypto: getting mysqld crypto key " - "from key version failed."); - ut_error; + "from key version failed. Reason could be that requested" + " key_version %u is not found or required encryption " + " key management is not found.", info->key_version); + return false; } uint dst_len; - int rc= my_aes_encrypt_ecb(info->crypt_msg, sizeof(info->crypt_msg), //src, srclen - info->crypt_key, &dst_len, //dst, &dstlen - (unsigned char*)&mysqld_key, sizeof(mysqld_key), - NULL, 0, 1); + int rc= my_aes_crypt(MY_AES_ECB, ENCRYPTION_FLAG_NOPAD|ENCRYPTION_FLAG_ENCRYPT, + info->crypt_msg, sizeof(info->crypt_msg), //src, srclen + info->crypt_key, &dst_len, //dst, &dstlen + (unsigned char*)&mysqld_key, sizeof(mysqld_key), + NULL, 0); if (rc != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) { fprintf(stderr, "\nInnodb redo log crypto: getting redo log crypto key " "failed.\n"); - ut_error; + return false; } + + return true; } -static bool mysort(const crypt_info_t& i, - const crypt_info_t& j) +/*********************************************************************//** +Compare function for checkpoint numbers +@return true if first checkpoint is larger than second one */ +static +bool +mysort(const crypt_info_t& i, + const crypt_info_t& j) { return i.checkpoint_no > j.checkpoint_no; } +/*********************************************************************//** +Add crypt info to set if it is not already present +@return true if successfull, false if not- */ static -bool add_crypt_info(crypt_info_t* info) +bool +add_crypt_info(crypt_info_t* info) { /* so that no one is searching array while we modify it */ ut_ad(mutex_own(&(log_sys->mutex))); if (get_crypt_info(info->checkpoint_no) != NULL) { // already present... + return true; + } + + if (!init_crypt_key(info)) { return false; } - init_crypt_key(info); crypt_info.push_back(*info); /* a log block only stores 4-bytes of checkpoint no */ @@ -273,7 +290,7 @@ log_blocks_encrypt( const ulint size, /*!< in: size of blocks, must be multiple of a log block */ byte* dst_block) /*!< out: blocks after encryption */ { - return log_blocks_crypt(block, size, dst_block, true); + return log_blocks_crypt(block, size, dst_block, ENCRYPTION_FLAG_ENCRYPT); } /*********************************************************************//** @@ -324,7 +341,7 @@ Encrypt one or more log block before it is flushed to disk */ UNIV_INTERN void log_encrypt_before_write( -/*===========================*/ +/*=====================*/ ib_uint64_t next_checkpoint_no, /*!< in: log group to be flushed */ byte* block, /*!< in/out: pointer to a log block */ const ulint size) /*!< in: size of log blocks */ @@ -343,7 +360,7 @@ log_encrypt_before_write( byte* dst_frame = (byte*)malloc(size); //encrypt log blocks content - Crypt_result result = log_blocks_crypt(block, size, dst_frame, true); + Crypt_result result = log_blocks_crypt(block, size, dst_frame, ENCRYPTION_FLAG_ENCRYPT); if (result == MY_AES_OK) { ut_ad(block[0] == dst_frame[0]); @@ -361,7 +378,7 @@ Decrypt a specified log segment after they are read from a log file to a buffer. */ void log_decrypt_after_read( -/*==========================*/ +/*===================*/ byte* frame, /*!< in/out: log segment */ const ulint size) /*!< in: log segment size */ { @@ -369,7 +386,7 @@ log_decrypt_after_read( byte* dst_frame = (byte*)malloc(size); // decrypt log blocks content - Crypt_result result = log_blocks_crypt(frame, size, dst_frame, false); + Crypt_result result = log_blocks_crypt(frame, size, dst_frame, ENCRYPTION_FLAG_DECRYPT); if (result == MY_AES_OK) { memcpy(frame, dst_frame, size); @@ -450,7 +467,7 @@ Read the crypto (version, msg and iv) info, which has been used for log blocks with lsn <= this checkpoint's lsn, from a log header's checkpoint buf. */ UNIV_INTERN -void +bool log_crypt_read_checkpoint_buf( /*===========================*/ const byte* buf) { /*!< in: checkpoint buffer */ @@ -459,7 +476,7 @@ log_crypt_read_checkpoint_buf( byte scheme = buf[0]; if (scheme != redo_log_purpose_byte) { - return; + return true; } buf++; size_t n = buf[0]; @@ -471,7 +488,10 @@ log_crypt_read_checkpoint_buf( info.key_version = mach_read_from_4(buf + 4); memcpy(info.crypt_msg, buf + 8, MY_AES_BLOCK_SIZE); memcpy(info.crypt_nonce, buf + 24, MY_AES_BLOCK_SIZE); - add_crypt_info(&info); + + if (!add_crypt_info(&info)) { + return false; + } buf += LOG_CRYPT_ENTRY_SIZE; } @@ -485,5 +505,77 @@ log_crypt_read_checkpoint_buf( } fprintf(stderr, "\n"); #endif + return true; } +/******************************************************** +Check is the checkpoint information encrypted. This check +is based on fact has log group crypt info and based +on this crypt info was the key version different from +unencrypted key version. There is no realible way to +distinguish encrypted log block from corrupted log block, +but if log block corruption is found this function is +used to find out if log block is maybe encrypted but +encryption key, key management plugin or encryption +algorithm does not match. +@return TRUE, if log block may be encrypted */ +UNIV_INTERN +ibool +log_crypt_block_maybe_encrypted( +/*============================*/ + const byte* log_block, /*!< in: log block */ + log_crypt_err_t* err_info) /*!< out: error info */ +{ + ibool maybe_encrypted = FALSE; + const crypt_info_t* crypt_info; + + *err_info = LOG_UNENCRYPTED; + crypt_info = get_crypt_info(log_block); + + if (crypt_info && + crypt_info->key_version != UNENCRYPTED_KEY_VER) { + byte mysqld_key[MY_AES_BLOCK_SIZE] = {0}; + uint keylen= sizeof(mysqld_key); + + /* Log block contains crypt info and based on key + version block could be encrypted. */ + *err_info = LOG_DECRYPT_MAYBE_FAILED; + maybe_encrypted = TRUE; + + if (encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, + crypt_info->key_version, mysqld_key, &keylen)) { + *err_info = LOG_CRYPT_KEY_NOT_FOUND; + } + } + + return (maybe_encrypted); +} + +/******************************************************** +Print crypt error message to error log */ +UNIV_INTERN +void +log_crypt_print_error( +/*==================*/ + log_crypt_err_t err_info) /*!< out: error info */ +{ + switch(err_info) { + case LOG_CRYPT_KEY_NOT_FOUND: + ib_logf(IB_LOG_LEVEL_ERROR, + "Redo log crypto: getting mysqld crypto key " + "from key version failed. Reason could be that " + "requested key version is not found or required " + "encryption key management plugin is not found."); + break; + case LOG_DECRYPT_MAYBE_FAILED: + ib_logf(IB_LOG_LEVEL_ERROR, + "Redo log crypto: failed to decrypt log block. " + "Reason could be that requested key version is " + "not found, required encryption key management " + "plugin is not found or configured encryption " + "algorithm and/or method does not match."); + break; + default: + ut_error; /* Real bug */ + } +} |