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 | |