diff options
-rw-r--r-- | storage/innobase/buf/buf0buf.cc | 42 | ||||
-rw-r--r-- | storage/innobase/include/log0crypt.h | 70 | ||||
-rw-r--r-- | storage/innobase/log/log0crypt.cc | 96 | ||||
-rw-r--r-- | storage/innobase/log/log0recv.cc | 50 | ||||
-rw-r--r-- | storage/xtradb/buf/buf0buf.cc | 42 | ||||
-rw-r--r-- | storage/xtradb/include/log0crypt.h | 70 | ||||
-rw-r--r-- | storage/xtradb/log/log0crypt.cc | 96 | ||||
-rw-r--r-- | storage/xtradb/log/log0recv.cc | 50 |
8 files changed, 472 insertions, 44 deletions
diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 12115fde7f4..9fa0591c95c 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -4273,6 +4273,46 @@ buf_mark_space_corrupt( } /********************************************************************//** +Check if page is maybe compressed, encrypted or both when we encounter +corrupted page. Note that we can't be 100% sure if page is corrupted +or decrypt/decompress just failed. +*/ +static +void +buf_page_check_corrupt( +/*===================*/ + const buf_page_t* bpage) /*!< in/out: buffer page read from disk */ +{ + ulint zip_size = buf_page_get_zip_size(bpage); + byte* dst_frame = (zip_size) ? bpage->zip.data : + ((buf_block_t*) bpage)->frame; + unsigned key_version = + mach_read_from_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); + bool page_compressed = fil_page_is_compressed(dst_frame); + bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame); + ulint space_id = mach_read_from_4( + dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); + fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); + fil_space_t* space = fil_space_found_by_id(space_id); + + if (key_version != 0 || + (crypt_data && crypt_data->type != CRYPT_SCHEME_UNENCRYPTED) || + page_compressed || page_compressed_encrypted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Maybe corruption: Block space_id %lu in file %s maybe corrupted\n" + "Reason could be that key_version in page %u \n" + "or in crypt_data %p could not be found,\n" + "key management plugin is not found or\n" + "used encryption algorithm or method does not match.\n" + "Page compressed %d, compressed and encrypted %d.\n", + space_id, space ? space->name : "NULL", + key_version, + crypt_data, + page_compressed, page_compressed_encrypted); + } +} + +/********************************************************************//** Completes an asynchronous read or write request of a file page to or from the buffer pool. @return true if successful */ @@ -4438,6 +4478,8 @@ corrupt: && buf_mark_space_corrupt(bpage)) { return(false); } else { + buf_page_check_corrupt(bpage); + fputs("InnoDB: Ending processing" " because of" " a corrupt database page.\n", diff --git a/storage/innobase/include/log0crypt.h b/storage/innobase/include/log0crypt.h index b04f16d2a29..7e737853465 100644 --- a/storage/innobase/include/log0crypt.h +++ b/storage/innobase/include/log0crypt.h @@ -1,8 +1,27 @@ +/***************************************************************************** + +Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. +Copyright (C) 2014, 2015, MariaDB Corporation. All Rights Reserved. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*****************************************************************************/ /**************************************************//** @file include/log0crypt.h Innodb log encrypt/decrypt Created 11/25/2013 Minli Zhu +Modified Jan Lindström jan.lindstrom@mariadb.com *******************************************************/ #ifndef log0crypt_h #define log0crypt_h @@ -22,7 +41,7 @@ UNIV_INTERN void log_crypt_set_ver_and_key( /*======================*/ - ib_uint64_t next_checkpoint_no); + ib_uint64_t next_checkpoint_no);/*!< in: next checkpoint no */ /*********************************************************************//** @@ -43,17 +62,17 @@ UNIV_INTERN bool log_crypt_read_checkpoint_buf( /*===========================*/ - const byte* buf); /*!< in: checkpoint buffer */ + const byte* buf); /*!< in: checkpoint buffer */ /******************************************************** 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 */ +/*=====================*/ + 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 */ /******************************************************** Decrypt a specified log segment after they are read from a log file to a buffer. @@ -61,8 +80,41 @@ Decrypt a specified log segment after they are read from a log file to a buffer. UNIV_INTERN void log_decrypt_after_read( -/*==========================*/ - byte* frame, /*!< in/out: log segment */ - const ulint size); /*!< in: log segment size */ +/*===================*/ + byte* frame, /*!< in/out: log segment */ + const ulint size); /*!< in: log segment size */ + +/* Error codes for crypt info */ +typedef enum { + LOG_UNENCRYPTED = 0, + LOG_CRYPT_KEY_NOT_FOUND = 1, + LOG_DECRYPT_MAYBE_FAILED = 2 +} log_crypt_err_t; + +/******************************************************** +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 */ + +/******************************************************** +Print crypt error message to error log */ +UNIV_INTERN +void +log_crypt_print_error( +/*==================*/ + log_crypt_err_t err_info); /*!< out: error info */ #endif // log0crypt.h diff --git a/storage/innobase/log/log0crypt.cc b/storage/innobase/log/log0crypt.cc index 79f4ba35b69..6d0a2d6a1f8 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( @@ -239,14 +245,23 @@ init_crypt_key( 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))); @@ -332,7 +347,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 */ @@ -369,7 +384,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 */ { @@ -499,3 +514,74 @@ log_crypt_read_checkpoint_buf( 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 */ + } +} diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 8e16e74ba1c..bb39d9804f6 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, MariaDB Corporation. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -36,6 +36,8 @@ Created 9/20/1997 Heikki Tuuri #include "log0recv.ic" #endif +#include "log0crypt.h" + #include "mem0mem.h" #include "buf0buf.h" #include "buf0flu.h" @@ -2704,8 +2706,9 @@ recv_scan_log_recs( lsn_t* contiguous_lsn, /*!< in/out: it is known that all log groups contain contiguous log data up to this lsn */ - lsn_t* group_scanned_lsn)/*!< out: scanning succeeded up to + lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to this lsn */ + dberr_t* err) /*!< out: error code or DB_SUCCESS */ { const byte* log_block; ulint no; @@ -2724,6 +2727,7 @@ recv_scan_log_recs( log_block = buf; scanned_lsn = start_lsn; more_data = FALSE; + *err = DB_SUCCESS; do { no = log_block_get_hdr_no(log_block); @@ -2735,6 +2739,7 @@ recv_scan_log_recs( */ if (no != log_block_convert_lsn_to_no(scanned_lsn) || !log_block_checksum_is_ok_or_old_format(log_block)) { + log_crypt_err_t log_crypt_err; if (no == log_block_convert_lsn_to_no(scanned_lsn) && !log_block_checksum_is_ok_or_old_format( @@ -2756,6 +2761,14 @@ recv_scan_log_recs( finished = TRUE; + if (log_crypt_block_maybe_encrypted(log_block, + &log_crypt_err)) { + /* Log block maybe encrypted */ + log_crypt_print_error(log_crypt_err); + *err = DB_ERROR; + return (TRUE); + } + /* Crash if we encounter a garbage log block */ if (!srv_force_recovery) { fputs("InnoDB: Set innodb_force_recovery" @@ -2944,14 +2957,16 @@ recv_group_scan_log_recs( lsn_t* contiguous_lsn, /*!< in/out: it is known that all log groups contain contiguous log data up to this lsn */ - lsn_t* group_scanned_lsn)/*!< out: scanning succeeded up to + lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to this lsn */ + dberr_t* err) /*!< out: error code or DB_SUCCESS */ { ibool finished; lsn_t start_lsn; lsn_t end_lsn; finished = FALSE; + *err = DB_SUCCESS; start_lsn = *contiguous_lsn; @@ -2966,7 +2981,13 @@ recv_group_scan_log_recs( - (recv_n_pool_free_frames * srv_buf_pool_instances)) * UNIV_PAGE_SIZE, TRUE, log_sys->buf, RECV_SCAN_SIZE, - start_lsn, contiguous_lsn, group_scanned_lsn); + start_lsn, contiguous_lsn, group_scanned_lsn, + err); + + if (*err != DB_SUCCESS) { + break; + } + start_lsn = end_lsn; } @@ -3174,6 +3195,7 @@ recv_recovery_from_checkpoint_start_func( up_to_date_group = max_cp_group; } else { ulint capacity; + dberr_t err; /* Try to recover the remaining part from logs: first from the logs of the archived group */ @@ -3193,8 +3215,9 @@ recv_recovery_from_checkpoint_start_func( } recv_group_scan_log_recs(group, &contiguous_lsn, - &group_scanned_lsn); - if (recv_sys->scanned_lsn < checkpoint_lsn) { + &group_scanned_lsn, &err); + + if (err != DB_SUCCESS || recv_sys->scanned_lsn < checkpoint_lsn) { mutex_exit(&(log_sys->mutex)); @@ -3226,9 +3249,15 @@ recv_recovery_from_checkpoint_start_func( #ifdef UNIV_LOG_ARCHIVE lsn_t old_scanned_lsn = recv_sys->scanned_lsn; #endif /* UNIV_LOG_ARCHIVE */ + dberr_t err; recv_group_scan_log_recs(group, &contiguous_lsn, - &group_scanned_lsn); + &group_scanned_lsn, &err); + + if (err != DB_SUCCESS) { + return (err); + } + group->scanned_lsn = group_scanned_lsn; #ifdef UNIV_LOG_ARCHIVE @@ -3707,6 +3736,7 @@ log_group_recover_from_archive_file( os_offset_t file_size; int input_char; char name[10000]; + dberr_t err; ut_a(0); @@ -3851,7 +3881,11 @@ ask_again: (buf_pool_get_n_pages() - (recv_n_pool_free_frames * srv_buf_pool_instances)) * UNIV_PAGE_SIZE, TRUE, buf, len, start_lsn, - &dummy_lsn, &scanned_lsn); + &dummy_lsn, &scanned_lsn, &err); + + if (err != DB_SUCCESS) { + return (FALSE); + } if (scanned_lsn == file_end_lsn) { diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc index cf5c6fd2766..6fc9752dd7a 100644 --- a/storage/xtradb/buf/buf0buf.cc +++ b/storage/xtradb/buf/buf0buf.cc @@ -4321,6 +4321,46 @@ buf_mark_space_corrupt( } /********************************************************************//** +Check if page is maybe compressed, encrypted or both when we encounter +corrupted page. Note that we can't be 100% sure if page is corrupted +or decrypt/decompress just failed. +*/ +static +void +buf_page_check_corrupt( +/*===================*/ + const buf_page_t* bpage) /*!< in/out: buffer page read from disk */ +{ + ulint zip_size = buf_page_get_zip_size(bpage); + byte* dst_frame = (zip_size) ? bpage->zip.data : + ((buf_block_t*) bpage)->frame; + unsigned key_version = + mach_read_from_4(dst_frame + FIL_PAGE_FILE_FLUSH_LSN_OR_KEY_VERSION); + bool page_compressed = fil_page_is_compressed(dst_frame); + bool page_compressed_encrypted = fil_page_is_compressed_encrypted(dst_frame); + ulint space_id = mach_read_from_4( + dst_frame + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID); + fil_space_crypt_t* crypt_data = fil_space_get_crypt_data(space_id); + fil_space_t* space = fil_space_found_by_id(space_id); + + if (key_version != 0 || + (crypt_data && crypt_data->type != CRYPT_SCHEME_UNENCRYPTED) || + page_compressed || page_compressed_encrypted) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Maybe corruption: Block space_id %lu in file %s maybe corrupted\n" + "Reason could be that key_version in page %u \n" + "or in crypt_data %p could not be found,\n" + "key management plugin is not found or\n" + "used encryption algorithm or method does not match.\n" + "Page compressed %d, compressed and encrypted %d.\n", + space_id, space ? space->name : "NULL", + key_version, + crypt_data, + page_compressed, page_compressed_encrypted); + } +} + +/********************************************************************//** Completes an asynchronous read or write request of a file page to or from the buffer pool. @return true if successful */ @@ -4505,6 +4545,8 @@ corrupt: && buf_mark_space_corrupt(bpage)) { return(false); } else { + buf_page_check_corrupt(bpage); + fputs("InnoDB: Ending processing" " because of" " a corrupt database page.\n", diff --git a/storage/xtradb/include/log0crypt.h b/storage/xtradb/include/log0crypt.h index b04f16d2a29..7e737853465 100644 --- a/storage/xtradb/include/log0crypt.h +++ b/storage/xtradb/include/log0crypt.h @@ -1,8 +1,27 @@ +/***************************************************************************** + +Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. +Copyright (C) 2014, 2015, MariaDB Corporation. All Rights Reserved. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*****************************************************************************/ /**************************************************//** @file include/log0crypt.h Innodb log encrypt/decrypt Created 11/25/2013 Minli Zhu +Modified Jan Lindström jan.lindstrom@mariadb.com *******************************************************/ #ifndef log0crypt_h #define log0crypt_h @@ -22,7 +41,7 @@ UNIV_INTERN void log_crypt_set_ver_and_key( /*======================*/ - ib_uint64_t next_checkpoint_no); + ib_uint64_t next_checkpoint_no);/*!< in: next checkpoint no */ /*********************************************************************//** @@ -43,17 +62,17 @@ UNIV_INTERN bool log_crypt_read_checkpoint_buf( /*===========================*/ - const byte* buf); /*!< in: checkpoint buffer */ + const byte* buf); /*!< in: checkpoint buffer */ /******************************************************** 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 */ +/*=====================*/ + 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 */ /******************************************************** Decrypt a specified log segment after they are read from a log file to a buffer. @@ -61,8 +80,41 @@ Decrypt a specified log segment after they are read from a log file to a buffer. UNIV_INTERN void log_decrypt_after_read( -/*==========================*/ - byte* frame, /*!< in/out: log segment */ - const ulint size); /*!< in: log segment size */ +/*===================*/ + byte* frame, /*!< in/out: log segment */ + const ulint size); /*!< in: log segment size */ + +/* Error codes for crypt info */ +typedef enum { + LOG_UNENCRYPTED = 0, + LOG_CRYPT_KEY_NOT_FOUND = 1, + LOG_DECRYPT_MAYBE_FAILED = 2 +} log_crypt_err_t; + +/******************************************************** +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 */ + +/******************************************************** +Print crypt error message to error log */ +UNIV_INTERN +void +log_crypt_print_error( +/*==================*/ + log_crypt_err_t err_info); /*!< out: error info */ #endif // log0crypt.h diff --git a/storage/xtradb/log/log0crypt.cc b/storage/xtradb/log/log0crypt.cc index 23bde798de1..38b82d6ceb4 100644 --- a/storage/xtradb/log/log0crypt.cc +++ b/storage/xtradb/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( @@ -239,14 +245,23 @@ init_crypt_key( 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))); @@ -332,7 +347,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 */ @@ -369,7 +384,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 */ { @@ -499,3 +514,74 @@ log_crypt_read_checkpoint_buf( 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 */ + } +} diff --git a/storage/xtradb/log/log0recv.cc b/storage/xtradb/log/log0recv.cc index 93ed384c0c7..08a9b2b3863 100644 --- a/storage/xtradb/log/log0recv.cc +++ b/storage/xtradb/log/log0recv.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2015, MariaDB Corporation. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -36,6 +36,8 @@ Created 9/20/1997 Heikki Tuuri #include "log0recv.ic" #endif +#include "log0crypt.h" + #include "config.h" #ifdef HAVE_ALLOCA_H #include "alloca.h" @@ -2774,8 +2776,9 @@ recv_scan_log_recs( lsn_t* contiguous_lsn, /*!< in/out: it is known that all log groups contain contiguous log data up to this lsn */ - lsn_t* group_scanned_lsn)/*!< out: scanning succeeded up to + lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to this lsn */ + dberr_t* err) /*!< out: error code or DB_SUCCESS */ { const byte* log_block; ulint no; @@ -2794,6 +2797,7 @@ recv_scan_log_recs( log_block = buf; scanned_lsn = start_lsn; more_data = FALSE; + *err = DB_SUCCESS; do { no = log_block_get_hdr_no(log_block); @@ -2805,6 +2809,7 @@ recv_scan_log_recs( */ if (no != log_block_convert_lsn_to_no(scanned_lsn) || !log_block_checksum_is_ok_or_old_format(log_block)) { + log_crypt_err_t log_crypt_err; if (no == log_block_convert_lsn_to_no(scanned_lsn) && !log_block_checksum_is_ok_or_old_format( @@ -2826,6 +2831,14 @@ recv_scan_log_recs( finished = TRUE; + if (log_crypt_block_maybe_encrypted(log_block, + &log_crypt_err)) { + /* Log block maybe encrypted */ + log_crypt_print_error(log_crypt_err); + *err = DB_ERROR; + return (TRUE); + } + /* Crash if we encounter a garbage log block */ if (!srv_force_recovery) { fputs("InnoDB: Set innodb_force_recovery" @@ -3014,14 +3027,16 @@ recv_group_scan_log_recs( lsn_t* contiguous_lsn, /*!< in/out: it is known that all log groups contain contiguous log data up to this lsn */ - lsn_t* group_scanned_lsn)/*!< out: scanning succeeded up to + lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to this lsn */ + dberr_t* err) /*!< out: error code or DB_SUCCESS */ { ibool finished; lsn_t start_lsn; lsn_t end_lsn; finished = FALSE; + *err = DB_SUCCESS; start_lsn = *contiguous_lsn; @@ -3036,7 +3051,13 @@ recv_group_scan_log_recs( - (recv_n_pool_free_frames * srv_buf_pool_instances)) * UNIV_PAGE_SIZE, TRUE, log_sys->buf, RECV_SCAN_SIZE, - start_lsn, contiguous_lsn, group_scanned_lsn); + start_lsn, contiguous_lsn, group_scanned_lsn, + err); + + if (*err != DB_SUCCESS) { + break; + } + start_lsn = end_lsn; } @@ -3267,6 +3288,7 @@ recv_recovery_from_checkpoint_start_func( up_to_date_group = max_cp_group; } else { ulint capacity; + dberr_t err; /* Try to recover the remaining part from logs: first from the logs of the archived group */ @@ -3286,8 +3308,9 @@ recv_recovery_from_checkpoint_start_func( } recv_group_scan_log_recs(group, &contiguous_lsn, - &group_scanned_lsn); - if (recv_sys->scanned_lsn < checkpoint_lsn) { + &group_scanned_lsn, &err); + + if (err != DB_SUCCESS || recv_sys->scanned_lsn < checkpoint_lsn) { mutex_exit(&(log_sys->mutex)); @@ -3319,9 +3342,15 @@ recv_recovery_from_checkpoint_start_func( #ifdef UNIV_LOG_ARCHIVE lsn_t old_scanned_lsn = recv_sys->scanned_lsn; #endif /* UNIV_LOG_ARCHIVE */ + dberr_t err; recv_group_scan_log_recs(group, &contiguous_lsn, - &group_scanned_lsn); + &group_scanned_lsn, &err); + + if (err != DB_SUCCESS) { + return (err); + } + group->scanned_lsn = group_scanned_lsn; #ifdef UNIV_LOG_ARCHIVE @@ -3801,6 +3830,7 @@ log_group_recover_from_archive_file( os_offset_t file_size; int input_char; char name[OS_FILE_MAX_PATH]; + dberr_t err; ut_a(0); @@ -3946,7 +3976,11 @@ ask_again: (buf_pool_get_n_pages() - (recv_n_pool_free_frames * srv_buf_pool_instances)) * UNIV_PAGE_SIZE, TRUE, buf, len, start_lsn, - &dummy_lsn, &scanned_lsn); + &dummy_lsn, &scanned_lsn, &err); + + if (err != DB_SUCCESS) { + return(FALSE); + } if (scanned_lsn == file_end_lsn) { |