| 1 | /***************************************************************************** |
| 2 | Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. |
| 3 | Copyright (c) 2015, 2017, MariaDB Corporation. |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify it under |
| 6 | the terms of the GNU General Public License as published by the Free Software |
| 7 | Foundation; version 2 of the License. |
| 8 | |
| 9 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 11 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License along with |
| 14 | this program; if not, write to the Free Software Foundation, Inc., |
| 15 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
| 16 | |
| 17 | *****************************************************************************/ |
| 18 | |
| 19 | /**************************************************//** |
| 20 | @file include/fil0crypt.h |
| 21 | The low-level file system encryption support functions |
| 22 | |
| 23 | Created 04/01/2015 Jan Lindström |
| 24 | *******************************************************/ |
| 25 | |
| 26 | #ifndef fil0crypt_h |
| 27 | #define fil0crypt_h |
| 28 | |
| 29 | #ifndef UNIV_INNOCHECKSUM |
| 30 | #include "os0event.h" |
| 31 | #include "my_crypt.h" |
| 32 | #include "fil0fil.h" |
| 33 | #endif /*! UNIV_INNOCHECKSUM */ |
| 34 | |
| 35 | /** |
| 36 | * Magic pattern in start of crypt data on page 0 |
| 37 | */ |
| 38 | #define MAGIC_SZ 6 |
| 39 | |
| 40 | static const unsigned char CRYPT_MAGIC[MAGIC_SZ] = { |
| 41 | 's', 0xE, 0xC, 'R', 'E', 't' }; |
| 42 | |
| 43 | /* This key will be used if nothing else is given */ |
| 44 | #define FIL_DEFAULT_ENCRYPTION_KEY ENCRYPTION_KEY_SYSTEM_DATA |
| 45 | |
| 46 | extern os_event_t fil_crypt_threads_event; |
| 47 | |
| 48 | /** |
| 49 | * CRYPT_SCHEME_UNENCRYPTED |
| 50 | * |
| 51 | * Used as intermediate state when convering a space from unencrypted |
| 52 | * to encrypted |
| 53 | */ |
| 54 | /** |
| 55 | * CRYPT_SCHEME_1 |
| 56 | * |
| 57 | * xxx is AES_CTR or AES_CBC (or another block cypher with the same key and iv lengths) |
| 58 | * L = AES_ECB(KEY, IV) |
| 59 | * CRYPT(PAGE) = xxx(KEY=L, IV=C, PAGE) |
| 60 | */ |
| 61 | |
| 62 | #define CRYPT_SCHEME_1 1 |
| 63 | #define CRYPT_SCHEME_1_IV_LEN 16 |
| 64 | #define CRYPT_SCHEME_UNENCRYPTED 0 |
| 65 | |
| 66 | /* Cached L or key for given key_version */ |
| 67 | struct key_struct |
| 68 | { |
| 69 | uint key_version; /*!< Version of the key */ |
| 70 | uint key_length; /*!< Key length */ |
| 71 | unsigned char key[MY_AES_MAX_KEY_LENGTH]; /*!< Cached key |
| 72 | (that is L in CRYPT_SCHEME_1) */ |
| 73 | }; |
| 74 | |
| 75 | /** is encryption enabled */ |
| 76 | extern ulong srv_encrypt_tables; |
| 77 | |
| 78 | /** Mutex helper for crypt_data->scheme |
| 79 | @param[in, out] schme encryption scheme |
| 80 | @param[in] exit should we exit or enter mutex ? */ |
| 81 | void |
| 82 | crypt_data_scheme_locker( |
| 83 | st_encryption_scheme* scheme, |
| 84 | int exit); |
| 85 | |
| 86 | struct fil_space_rotate_state_t |
| 87 | { |
| 88 | time_t start_time; /*!< time when rotation started */ |
| 89 | ulint active_threads; /*!< active threads in space */ |
| 90 | ulint next_offset; /*!< next "free" offset */ |
| 91 | ulint max_offset; /*!< max offset needing to be rotated */ |
| 92 | uint min_key_version_found; /*!< min key version found but not |
| 93 | rotated */ |
| 94 | lsn_t end_lsn; /*!< max lsn created when rotating this |
| 95 | space */ |
| 96 | bool starting; /*!< initial write of IV */ |
| 97 | bool flushing; /*!< space is being flushed at end of rotate */ |
| 98 | struct { |
| 99 | bool is_active; /*!< is scrubbing active in this space */ |
| 100 | time_t last_scrub_completed; /*!< when was last scrub |
| 101 | completed */ |
| 102 | } scrubbing; |
| 103 | }; |
| 104 | |
| 105 | #ifndef UNIV_INNOCHECKSUM |
| 106 | |
| 107 | struct fil_space_crypt_t : st_encryption_scheme |
| 108 | { |
| 109 | public: |
| 110 | /** Constructor. Does not initialize the members! |
| 111 | The object is expected to be placed in a buffer that |
| 112 | has been zero-initialized. */ |
| 113 | fil_space_crypt_t( |
| 114 | uint new_type, |
| 115 | uint new_min_key_version, |
| 116 | uint new_key_id, |
| 117 | fil_encryption_t new_encryption) |
| 118 | : st_encryption_scheme(), |
| 119 | min_key_version(new_min_key_version), |
| 120 | page0_offset(0), |
| 121 | encryption(new_encryption), |
| 122 | key_found(0), |
| 123 | rotate_state() |
| 124 | { |
| 125 | key_id = new_key_id; |
| 126 | my_random_bytes(iv, sizeof(iv)); |
| 127 | mutex_create(LATCH_ID_FIL_CRYPT_DATA_MUTEX, &mutex); |
| 128 | locker = crypt_data_scheme_locker; |
| 129 | type = new_type; |
| 130 | |
| 131 | if (new_encryption == FIL_ENCRYPTION_OFF || |
| 132 | (!srv_encrypt_tables && |
| 133 | new_encryption == FIL_ENCRYPTION_DEFAULT)) { |
| 134 | type = CRYPT_SCHEME_UNENCRYPTED; |
| 135 | } else { |
| 136 | type = CRYPT_SCHEME_1; |
| 137 | min_key_version = key_get_latest_version(); |
| 138 | } |
| 139 | |
| 140 | key_found = min_key_version; |
| 141 | } |
| 142 | |
| 143 | /** Destructor */ |
| 144 | ~fil_space_crypt_t() |
| 145 | { |
| 146 | mutex_free(&mutex); |
| 147 | } |
| 148 | |
| 149 | /** Get latest key version from encryption plugin |
| 150 | @retval key_version or |
| 151 | @retval ENCRYPTION_KEY_VERSION_INVALID if used key_id |
| 152 | is not found from encryption plugin. */ |
| 153 | uint key_get_latest_version(void); |
| 154 | |
| 155 | /** Returns true if key was found from encryption plugin |
| 156 | and false if not. */ |
| 157 | bool is_key_found() const { |
| 158 | return key_found != ENCRYPTION_KEY_VERSION_INVALID; |
| 159 | } |
| 160 | |
| 161 | /** Returns true if tablespace should be encrypted */ |
| 162 | bool should_encrypt() const { |
| 163 | return ((encryption == FIL_ENCRYPTION_ON) || |
| 164 | (srv_encrypt_tables && |
| 165 | encryption == FIL_ENCRYPTION_DEFAULT)); |
| 166 | } |
| 167 | |
| 168 | /** Return true if tablespace is encrypted. */ |
| 169 | bool is_encrypted() const { |
| 170 | return (encryption != FIL_ENCRYPTION_OFF); |
| 171 | } |
| 172 | |
| 173 | /** Return true if default tablespace encryption is used, */ |
| 174 | bool is_default_encryption() const { |
| 175 | return (encryption == FIL_ENCRYPTION_DEFAULT); |
| 176 | } |
| 177 | |
| 178 | /** Return true if tablespace is not encrypted. */ |
| 179 | bool not_encrypted() const { |
| 180 | return (encryption == FIL_ENCRYPTION_OFF); |
| 181 | } |
| 182 | |
| 183 | /** Write crypt data to a page (0) |
| 184 | @param[in] space tablespace |
| 185 | @param[in,out] page0 first page of the tablespace |
| 186 | @param[in,out] mtr mini-transaction */ |
| 187 | void write_page0(const fil_space_t* space, byte* page0, mtr_t* mtr); |
| 188 | |
| 189 | uint min_key_version; // min key version for this space |
| 190 | ulint page0_offset; // byte offset on page 0 for crypt data |
| 191 | fil_encryption_t encryption; // Encryption setup |
| 192 | |
| 193 | ib_mutex_t mutex; // mutex protecting following variables |
| 194 | |
| 195 | /** Return code from encryption_key_get_latest_version. |
| 196 | If ENCRYPTION_KEY_VERSION_INVALID encryption plugin |
| 197 | could not find the key and there is no need to call |
| 198 | get_latest_key_version again as keys are read only |
| 199 | at startup. */ |
| 200 | uint key_found; |
| 201 | |
| 202 | fil_space_rotate_state_t rotate_state; |
| 203 | }; |
| 204 | |
| 205 | /** Status info about encryption */ |
| 206 | struct fil_space_crypt_status_t { |
| 207 | ulint space; /*!< tablespace id */ |
| 208 | ulint scheme; /*!< encryption scheme */ |
| 209 | uint min_key_version; /*!< min key version */ |
| 210 | uint current_key_version;/*!< current key version */ |
| 211 | uint keyserver_requests;/*!< no of key requests to key server */ |
| 212 | uint key_id; /*!< current key_id */ |
| 213 | bool rotating; /*!< is key rotation ongoing */ |
| 214 | bool flushing; /*!< is flush at end of rotation ongoing */ |
| 215 | ulint rotate_next_page_number; /*!< next page if key rotating */ |
| 216 | ulint rotate_max_page_number; /*!< max page if key rotating */ |
| 217 | }; |
| 218 | |
| 219 | /** Statistics about encryption key rotation */ |
| 220 | struct fil_crypt_stat_t { |
| 221 | ulint pages_read_from_cache; |
| 222 | ulint pages_read_from_disk; |
| 223 | ulint pages_modified; |
| 224 | ulint pages_flushed; |
| 225 | ulint estimated_iops; |
| 226 | }; |
| 227 | |
| 228 | /** Status info about scrubbing */ |
| 229 | struct fil_space_scrub_status_t { |
| 230 | ulint space; /*!< tablespace id */ |
| 231 | bool compressed; /*!< is space compressed */ |
| 232 | time_t last_scrub_completed; /*!< when was last scrub completed */ |
| 233 | bool scrubbing; /*!< is scrubbing ongoing */ |
| 234 | time_t current_scrub_started; /*!< when started current scrubbing */ |
| 235 | ulint current_scrub_active_threads; /*!< current scrub active threads */ |
| 236 | ulint current_scrub_page_number; /*!< current scrub page no */ |
| 237 | ulint current_scrub_max_page_number; /*!< current scrub max page no */ |
| 238 | }; |
| 239 | |
| 240 | /********************************************************************* |
| 241 | Init space crypt */ |
| 242 | UNIV_INTERN |
| 243 | void |
| 244 | fil_space_crypt_init(); |
| 245 | |
| 246 | /********************************************************************* |
| 247 | Cleanup space crypt */ |
| 248 | UNIV_INTERN |
| 249 | void |
| 250 | fil_space_crypt_cleanup(); |
| 251 | |
| 252 | /** |
| 253 | Create a fil_space_crypt_t object |
| 254 | @param[in] encrypt_mode FIL_ENCRYPTION_DEFAULT or |
| 255 | FIL_ENCRYPTION_ON or |
| 256 | FIL_ENCRYPTION_OFF |
| 257 | |
| 258 | @param[in] key_id Encryption key id |
| 259 | @return crypt object */ |
| 260 | UNIV_INTERN |
| 261 | fil_space_crypt_t* |
| 262 | fil_space_create_crypt_data( |
| 263 | fil_encryption_t encrypt_mode, |
| 264 | uint key_id) |
| 265 | MY_ATTRIBUTE((warn_unused_result)); |
| 266 | |
| 267 | /****************************************************************** |
| 268 | Merge fil_space_crypt_t object |
| 269 | @param[in,out] dst Destination cryp data |
| 270 | @param[in] src Source crypt data */ |
| 271 | UNIV_INTERN |
| 272 | void |
| 273 | fil_space_merge_crypt_data( |
| 274 | fil_space_crypt_t* dst, |
| 275 | const fil_space_crypt_t* src); |
| 276 | |
| 277 | /** Initialize encryption parameters from a tablespace header page. |
| 278 | @param[in] page_size page size of the tablespace |
| 279 | @param[in] page first page of the tablespace |
| 280 | @return crypt data from page 0 |
| 281 | @retval NULL if not present or not valid */ |
| 282 | UNIV_INTERN |
| 283 | fil_space_crypt_t* |
| 284 | fil_space_read_crypt_data(const page_size_t& page_size, const byte* page) |
| 285 | MY_ATTRIBUTE((nonnull, warn_unused_result)); |
| 286 | |
| 287 | /** |
| 288 | Free a crypt data object |
| 289 | @param[in,out] crypt_data crypt data to be freed */ |
| 290 | UNIV_INTERN |
| 291 | void |
| 292 | fil_space_destroy_crypt_data( |
| 293 | fil_space_crypt_t **crypt_data); |
| 294 | |
| 295 | /****************************************************************** |
| 296 | Parse a MLOG_FILE_WRITE_CRYPT_DATA log entry |
| 297 | @param[in] ptr Log entry start |
| 298 | @param[in] end_ptr Log entry end |
| 299 | @param[out] err DB_SUCCESS or DB_DECRYPTION_FAILED |
| 300 | @return position on log buffer */ |
| 301 | UNIV_INTERN |
| 302 | byte* |
| 303 | fil_parse_write_crypt_data( |
| 304 | byte* ptr, |
| 305 | const byte* end_ptr, |
| 306 | dberr_t* err) |
| 307 | MY_ATTRIBUTE((warn_unused_result)); |
| 308 | |
| 309 | /** Encrypt a buffer. |
| 310 | @param[in,out] crypt_data Crypt data |
| 311 | @param[in] space space_id |
| 312 | @param[in] offset Page offset |
| 313 | @param[in] lsn Log sequence number |
| 314 | @param[in] src_frame Page to encrypt |
| 315 | @param[in] page_size Page size |
| 316 | @param[in,out] dst_frame Output buffer |
| 317 | @return encrypted buffer or NULL */ |
| 318 | byte* |
| 319 | fil_encrypt_buf( |
| 320 | fil_space_crypt_t* crypt_data, |
| 321 | ulint space, |
| 322 | ulint offset, |
| 323 | lsn_t lsn, |
| 324 | const byte* src_frame, |
| 325 | const page_size_t& page_size, |
| 326 | byte* dst_frame) |
| 327 | MY_ATTRIBUTE((warn_unused_result)); |
| 328 | |
| 329 | /** |
| 330 | Encrypt a page. |
| 331 | |
| 332 | @param[in] space Tablespace |
| 333 | @param[in] offset Page offset |
| 334 | @param[in] lsn Log sequence number |
| 335 | @param[in] src_frame Page to encrypt |
| 336 | @param[in,out] dst_frame Output buffer |
| 337 | @return encrypted buffer or NULL */ |
| 338 | UNIV_INTERN |
| 339 | byte* |
| 340 | fil_space_encrypt( |
| 341 | const fil_space_t* space, |
| 342 | ulint offset, |
| 343 | lsn_t lsn, |
| 344 | byte* src_frame, |
| 345 | byte* dst_frame) |
| 346 | MY_ATTRIBUTE((warn_unused_result)); |
| 347 | |
| 348 | /** |
| 349 | Decrypt a page. |
| 350 | @param[in,out] crypt_data crypt_data |
| 351 | @param[in] tmp_frame Temporary buffer |
| 352 | @param[in] page_size Page size |
| 353 | @param[in,out] src_frame Page to decrypt |
| 354 | @param[out] err DB_SUCCESS or error |
| 355 | @return true if page decrypted, false if not.*/ |
| 356 | UNIV_INTERN |
| 357 | bool |
| 358 | fil_space_decrypt( |
| 359 | fil_space_crypt_t* crypt_data, |
| 360 | byte* tmp_frame, |
| 361 | const page_size_t& page_size, |
| 362 | byte* src_frame, |
| 363 | dberr_t* err); |
| 364 | |
| 365 | /****************************************************************** |
| 366 | Decrypt a page |
| 367 | @param[in] space Tablespace |
| 368 | @param[in] tmp_frame Temporary buffer used for decrypting |
| 369 | @param[in,out] src_frame Page to decrypt |
| 370 | @param[out] decrypted true if page was decrypted |
| 371 | @return decrypted page, or original not encrypted page if decryption is |
| 372 | not needed.*/ |
| 373 | UNIV_INTERN |
| 374 | byte* |
| 375 | fil_space_decrypt( |
| 376 | const fil_space_t* space, |
| 377 | byte* tmp_frame, |
| 378 | byte* src_frame, |
| 379 | bool* decrypted) |
| 380 | MY_ATTRIBUTE((warn_unused_result)); |
| 381 | |
| 382 | /****************************************************************** |
| 383 | Calculate post encryption checksum |
| 384 | @param[in] page_size page size |
| 385 | @param[in] dst_frame Block where checksum is calculated |
| 386 | @return page checksum or BUF_NO_CHECKSUM_MAGIC |
| 387 | not needed. */ |
| 388 | UNIV_INTERN |
| 389 | uint32_t |
| 390 | fil_crypt_calculate_checksum( |
| 391 | const page_size_t& page_size, |
| 392 | const byte* dst_frame) |
| 393 | MY_ATTRIBUTE((warn_unused_result)); |
| 394 | |
| 395 | /********************************************************************* |
| 396 | Adjust thread count for key rotation |
| 397 | @param[in] enw_cnt Number of threads to be used */ |
| 398 | UNIV_INTERN |
| 399 | void |
| 400 | fil_crypt_set_thread_cnt( |
| 401 | uint new_cnt); |
| 402 | |
| 403 | /********************************************************************* |
| 404 | Adjust max key age |
| 405 | @param[in] val New max key age */ |
| 406 | UNIV_INTERN |
| 407 | void |
| 408 | fil_crypt_set_rotate_key_age( |
| 409 | uint val); |
| 410 | |
| 411 | /********************************************************************* |
| 412 | Adjust rotation iops |
| 413 | @param[in] val New max roation iops */ |
| 414 | UNIV_INTERN |
| 415 | void |
| 416 | fil_crypt_set_rotation_iops( |
| 417 | uint val); |
| 418 | |
| 419 | /********************************************************************* |
| 420 | Adjust encrypt tables |
| 421 | @param[in] val New setting for innodb-encrypt-tables */ |
| 422 | UNIV_INTERN |
| 423 | void |
| 424 | fil_crypt_set_encrypt_tables( |
| 425 | uint val); |
| 426 | |
| 427 | /********************************************************************* |
| 428 | Init threads for key rotation */ |
| 429 | UNIV_INTERN |
| 430 | void |
| 431 | fil_crypt_threads_init(); |
| 432 | |
| 433 | /********************************************************************* |
| 434 | Clean up key rotation threads resources */ |
| 435 | UNIV_INTERN |
| 436 | void |
| 437 | fil_crypt_threads_cleanup(); |
| 438 | |
| 439 | /********************************************************************* |
| 440 | Wait for crypt threads to stop accessing space |
| 441 | @param[in] space Tablespace */ |
| 442 | UNIV_INTERN |
| 443 | void |
| 444 | fil_space_crypt_close_tablespace( |
| 445 | const fil_space_t* space); |
| 446 | |
| 447 | /********************************************************************* |
| 448 | Get crypt status for a space (used by information_schema) |
| 449 | @param[in] space Tablespace |
| 450 | @param[out] status Crypt status |
| 451 | return 0 if crypt data present */ |
| 452 | UNIV_INTERN |
| 453 | void |
| 454 | fil_space_crypt_get_status( |
| 455 | const fil_space_t* space, |
| 456 | struct fil_space_crypt_status_t* status); |
| 457 | |
| 458 | /********************************************************************* |
| 459 | Return crypt statistics |
| 460 | @param[out] stat Crypt statistics */ |
| 461 | UNIV_INTERN |
| 462 | void |
| 463 | fil_crypt_total_stat( |
| 464 | fil_crypt_stat_t *stat); |
| 465 | |
| 466 | /** |
| 467 | Get scrub status for a space (used by information_schema) |
| 468 | |
| 469 | @param[in] space Tablespace |
| 470 | @param[out] status Scrub status |
| 471 | return 0 if data found */ |
| 472 | UNIV_INTERN |
| 473 | void |
| 474 | fil_space_get_scrub_status( |
| 475 | const fil_space_t* space, |
| 476 | fil_space_scrub_status_t* status); |
| 477 | |
| 478 | #include "fil0crypt.ic" |
| 479 | #endif /* !UNIV_INNOCHECKSUM */ |
| 480 | |
| 481 | /** |
| 482 | Verify that post encryption checksum match calculated checksum. |
| 483 | This function should be called only if tablespace contains crypt_data |
| 484 | metadata (this is strong indication that tablespace is encrypted). |
| 485 | Function also verifies that traditional checksum does not match |
| 486 | calculated checksum as if it does page could be valid unencrypted, |
| 487 | encrypted, or corrupted. |
| 488 | |
| 489 | @param[in,out] page page frame (checksum is temporarily modified) |
| 490 | @param[in] page_size page size |
| 491 | @param[in] space tablespace identifier |
| 492 | @param[in] offset page number |
| 493 | @return true if page is encrypted AND OK, false otherwise */ |
| 494 | UNIV_INTERN |
| 495 | bool |
| 496 | fil_space_verify_crypt_checksum( |
| 497 | byte* page, |
| 498 | const page_size_t& page_size, |
| 499 | ulint space, |
| 500 | ulint offset) |
| 501 | MY_ATTRIBUTE((warn_unused_result)); |
| 502 | |
| 503 | #endif /* fil0crypt_h */ |
| 504 | |