1 /*
2 Copyright (c) 2013 Google Inc.
3 Copyright (c) 2014, 2015 MariaDB Corporation
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
17
18 #include "maria_def.h"
19 #include "ma_blockrec.h"
20 #include <my_crypt.h>
21
22 #define CRYPT_SCHEME_1 1
23 #define CRYPT_SCHEME_1_ID_LEN 4 /* 4 bytes for counter-block */
24 #define CRYPT_SCHEME_1_IV_LEN 16
25 #define CRYPT_SCHEME_1_KEY_VERSION_SIZE 4
26
27 #ifdef HAVE_PSI_INTERFACE
28 PSI_mutex_key key_CRYPT_DATA_lock;
29 #endif
30
31 struct st_crypt_key
32 {
33 uint key_version;
34 uchar key[CRYPT_SCHEME_1_IV_LEN];
35 };
36
37 struct st_maria_crypt_data
38 {
39 struct st_encryption_scheme scheme;
40 uint space;
41 mysql_mutex_t lock; /* protecting keys */
42 };
43
44 /**
45 determine what key id to use for Aria encryption
46
47 Same logic as for tempfiles: if key id 2 exists - use it,
48 otherwise use key id 1.
49
50 Key id 1 is system, it always exists. Key id 2 is optional,
51 it allows to specify fast low-grade encryption for temporary data.
52 */
get_encryption_key_id(MARIA_SHARE * share)53 static uint get_encryption_key_id(MARIA_SHARE *share)
54 {
55 if (share->options & HA_OPTION_TMP_TABLE &&
56 encryption_key_id_exists(ENCRYPTION_KEY_TEMPORARY_DATA))
57 return ENCRYPTION_KEY_TEMPORARY_DATA;
58 else
59 return ENCRYPTION_KEY_SYSTEM_DATA;
60 }
61
62 uint
ma_crypt_get_data_page_header_space()63 ma_crypt_get_data_page_header_space()
64 {
65 return CRYPT_SCHEME_1_KEY_VERSION_SIZE;
66 }
67
68 uint
ma_crypt_get_index_page_header_space(MARIA_SHARE * share)69 ma_crypt_get_index_page_header_space(MARIA_SHARE *share)
70 {
71 if (share->base.born_transactional)
72 {
73 return CRYPT_SCHEME_1_KEY_VERSION_SIZE;
74 }
75 else
76 {
77 /* if the index is not transactional, we add 7 bytes LSN anyway
78 to be used for counter block
79 */
80 return LSN_STORE_SIZE + CRYPT_SCHEME_1_KEY_VERSION_SIZE;
81 }
82 }
83
84 uint
ma_crypt_get_file_length()85 ma_crypt_get_file_length()
86 {
87 return 2 + CRYPT_SCHEME_1_IV_LEN + CRYPT_SCHEME_1_ID_LEN;
88 }
89
crypt_data_scheme_locker(struct st_encryption_scheme * scheme,int unlock)90 static void crypt_data_scheme_locker(struct st_encryption_scheme *scheme,
91 int unlock)
92 {
93 MARIA_CRYPT_DATA *crypt_data = (MARIA_CRYPT_DATA*)scheme;
94 if (unlock)
95 mysql_mutex_unlock(&crypt_data->lock);
96 else
97 mysql_mutex_lock(&crypt_data->lock);
98 }
99
100 int
ma_crypt_create(MARIA_SHARE * share)101 ma_crypt_create(MARIA_SHARE* share)
102 {
103 MARIA_CRYPT_DATA *crypt_data=
104 (MARIA_CRYPT_DATA*)my_malloc(sizeof(MARIA_CRYPT_DATA), MYF(MY_ZEROFILL));
105 crypt_data->scheme.type= CRYPT_SCHEME_1;
106 crypt_data->scheme.locker= crypt_data_scheme_locker;
107 mysql_mutex_init(key_CRYPT_DATA_lock, &crypt_data->lock, MY_MUTEX_INIT_FAST);
108 crypt_data->scheme.key_id= get_encryption_key_id(share);
109 my_random_bytes(crypt_data->scheme.iv, sizeof(crypt_data->scheme.iv));
110 my_random_bytes((uchar*)&crypt_data->space, sizeof(crypt_data->space));
111 share->crypt_data= crypt_data;
112 share->crypt_page_header_space= CRYPT_SCHEME_1_KEY_VERSION_SIZE;
113 return 0;
114 }
115
116 void
ma_crypt_free(MARIA_SHARE * share)117 ma_crypt_free(MARIA_SHARE* share)
118 {
119 if (share->crypt_data != NULL)
120 {
121 mysql_mutex_destroy(&share->crypt_data->lock);
122 my_free(share->crypt_data);
123 share->crypt_data= NULL;
124 }
125 }
126
127 int
ma_crypt_write(MARIA_SHARE * share,File file)128 ma_crypt_write(MARIA_SHARE* share, File file)
129 {
130 MARIA_CRYPT_DATA *crypt_data= share->crypt_data;
131 uchar buff[2 + 4 + sizeof(crypt_data->scheme.iv)];
132 if (crypt_data == 0)
133 return 0;
134
135 buff[0]= crypt_data->scheme.type;
136 buff[1]= sizeof(buff) - 2;
137
138 int4store(buff + 2, crypt_data->space);
139 memcpy(buff + 6, crypt_data->scheme.iv, sizeof(crypt_data->scheme.iv));
140
141 if (mysql_file_write(file, buff, sizeof(buff), MYF(MY_NABP)))
142 return 1;
143
144 return 0;
145 }
146
147 uchar*
ma_crypt_read(MARIA_SHARE * share,uchar * buff)148 ma_crypt_read(MARIA_SHARE* share, uchar *buff)
149 {
150 uchar type= buff[0];
151 uchar iv_length= buff[1];
152
153 /* currently only supported type */
154 if (type != CRYPT_SCHEME_1 ||
155 iv_length != sizeof(((MARIA_CRYPT_DATA*)1)->scheme.iv) + 4)
156 {
157 my_printf_error(HA_ERR_UNSUPPORTED,
158 "Unsupported crypt scheme! type: %d iv_length: %d\n",
159 MYF(ME_FATAL|ME_ERROR_LOG),
160 type, iv_length);
161 return 0;
162 }
163
164 if (share->crypt_data == NULL)
165 {
166 /* opening a table */
167 MARIA_CRYPT_DATA *crypt_data=
168 (MARIA_CRYPT_DATA*)my_malloc(sizeof(MARIA_CRYPT_DATA), MYF(MY_ZEROFILL));
169
170 crypt_data->scheme.type= type;
171 mysql_mutex_init(key_CRYPT_DATA_lock, &crypt_data->lock,
172 MY_MUTEX_INIT_FAST);
173 crypt_data->scheme.locker= crypt_data_scheme_locker;
174 crypt_data->scheme.key_id= get_encryption_key_id(share);
175 crypt_data->space= uint4korr(buff + 2);
176 memcpy(crypt_data->scheme.iv, buff + 6, sizeof(crypt_data->scheme.iv));
177 share->crypt_data= crypt_data;
178 }
179
180 share->crypt_page_header_space= CRYPT_SCHEME_1_KEY_VERSION_SIZE;
181 return buff + 2 + iv_length;
182 }
183
184 static int ma_encrypt(MARIA_SHARE *, MARIA_CRYPT_DATA *, const uchar *,
185 uchar *, uint, uint, LSN, uint *);
186 static int ma_decrypt(MARIA_SHARE *, MARIA_CRYPT_DATA *, const uchar *,
187 uchar *, uint, uint, LSN, uint);
188
ma_crypt_pre_read_hook(PAGECACHE_IO_HOOK_ARGS * args)189 static my_bool ma_crypt_pre_read_hook(PAGECACHE_IO_HOOK_ARGS *args)
190 {
191 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
192 uchar *crypt_buf= my_malloc(share->block_size, MYF(0));
193 if (crypt_buf == NULL)
194 {
195 args->crypt_buf= NULL; /* for post-hook */
196 return 1;
197 }
198
199 /* swap pointers to read into crypt_buf */
200 args->crypt_buf= args->page;
201 args->page= crypt_buf;
202
203 return 0;
204 }
205
ma_crypt_data_post_read_hook(int res,PAGECACHE_IO_HOOK_ARGS * args)206 static my_bool ma_crypt_data_post_read_hook(int res,
207 PAGECACHE_IO_HOOK_ARGS *args)
208 {
209 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
210 const uint size= share->block_size;
211 const uchar page_type= args->page[PAGE_TYPE_OFFSET] & PAGE_TYPE_MASK;
212 const uint32 key_version_offset= (page_type <= TAIL_PAGE) ?
213 KEY_VERSION_OFFSET : FULL_PAGE_KEY_VERSION_OFFSET;
214
215 if (res == 0)
216 {
217 const uchar *src= args->page;
218 uchar* dst= args->crypt_buf;
219 uint pageno= (uint)args->pageno;
220 LSN lsn= lsn_korr(src);
221 const uint head= (page_type <= TAIL_PAGE) ?
222 PAGE_HEADER_SIZE(share) : FULL_PAGE_HEADER_SIZE(share);
223 const uint tail= CRC_SIZE;
224 const uint32 key_version= uint4korr(src + key_version_offset);
225
226 /* 1 - copy head */
227 memcpy(dst, src, head);
228 /* 2 - decrypt page */
229 res= ma_decrypt(share, share->crypt_data,
230 src + head, dst + head, size - (head + tail), pageno, lsn,
231 key_version);
232 /* 3 - copy tail */
233 memcpy(dst + size - tail, src + size - tail, tail);
234 /* 4 clear key version to get correct crc */
235 int4store(dst + key_version_offset, 0);
236 }
237
238 if (args->crypt_buf != NULL)
239 {
240 uchar *tmp= args->page;
241 args->page= args->crypt_buf;
242 args->crypt_buf= NULL;
243 my_free(tmp);
244 }
245
246 return maria_page_crc_check_data(res, args);
247 }
248
store_rand_lsn(uchar * page)249 static void store_rand_lsn(uchar * page)
250 {
251 LSN lsn= 0;
252 lsn+= rand();
253 lsn<<= 32;
254 lsn+= rand();
255 lsn_store(page, lsn);
256 }
257
ma_crypt_data_pre_write_hook(PAGECACHE_IO_HOOK_ARGS * args)258 static my_bool ma_crypt_data_pre_write_hook(PAGECACHE_IO_HOOK_ARGS *args)
259 {
260 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
261 const uint size= share->block_size;
262 uint key_version;
263 uchar *crypt_buf= my_malloc(share->block_size, MYF(0));
264
265 if (crypt_buf == NULL)
266 {
267 args->crypt_buf= NULL; /* for post-hook */
268 return 1;
269 }
270
271 if (!share->base.born_transactional)
272 {
273 /* store a random number instead of LSN (for counter block) */
274 store_rand_lsn(args->page);
275 }
276
277 maria_page_crc_set_normal(args);
278
279 {
280 const uchar *src= args->page;
281 uchar* dst= crypt_buf;
282 uint pageno= (uint)args->pageno;
283 LSN lsn= lsn_korr(src);
284 const uchar page_type= src[PAGE_TYPE_OFFSET] & PAGE_TYPE_MASK;
285 const uint head= (page_type <= TAIL_PAGE) ?
286 PAGE_HEADER_SIZE(share) : FULL_PAGE_HEADER_SIZE(share);
287 const uint tail= CRC_SIZE;
288 const uint32 key_version_offset= (page_type <= TAIL_PAGE) ?
289 KEY_VERSION_OFFSET : FULL_PAGE_KEY_VERSION_OFFSET;
290
291 DBUG_ASSERT(page_type < MAX_PAGE_TYPE);
292
293 /* 1 - copy head */
294 memcpy(dst, src, head);
295 /* 2 - encrypt page */
296 if (ma_encrypt(share, share->crypt_data,
297 src + head, dst + head, size - (head + tail), pageno, lsn,
298 &key_version))
299 return 1;
300 /* 3 - copy tail */
301 memcpy(dst + size - tail, src + size - tail, tail);
302 /* 4 - store key version */
303 int4store(dst + key_version_offset, key_version);
304 }
305
306 /* swap pointers to instead write out the encrypted block */
307 args->crypt_buf= args->page;
308 args->page= crypt_buf;
309
310 return 0;
311 }
312
ma_crypt_post_write_hook(int res,PAGECACHE_IO_HOOK_ARGS * args)313 static void ma_crypt_post_write_hook(int res,
314 PAGECACHE_IO_HOOK_ARGS *args)
315 {
316 if (args->crypt_buf != NULL)
317 {
318 uchar *tmp= args->page;
319 args->page= args->crypt_buf;
320 args->crypt_buf= NULL;
321 my_free(tmp);
322 }
323
324 maria_page_write_failure(res, args);
325 }
326
ma_crypt_set_data_pagecache_callbacks(PAGECACHE_FILE * file,MARIA_SHARE * share)327 void ma_crypt_set_data_pagecache_callbacks(PAGECACHE_FILE *file,
328 MARIA_SHARE *share
329 __attribute__((unused)))
330 {
331 /* Only use encryption if we have defined it */
332 if (encryption_key_id_exists(get_encryption_key_id(share)))
333 {
334 file->pre_read_hook= ma_crypt_pre_read_hook;
335 file->post_read_hook= ma_crypt_data_post_read_hook;
336 file->pre_write_hook= ma_crypt_data_pre_write_hook;
337 file->post_write_hook= ma_crypt_post_write_hook;
338 }
339 }
340
ma_crypt_index_post_read_hook(int res,PAGECACHE_IO_HOOK_ARGS * args)341 static my_bool ma_crypt_index_post_read_hook(int res,
342 PAGECACHE_IO_HOOK_ARGS *args)
343 {
344 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
345 const uint block_size= share->block_size;
346 const uint page_used= _ma_get_page_used(share, args->page);
347
348 if (res ||
349 page_used < share->keypage_header ||
350 page_used >= block_size - CRC_SIZE)
351 {
352 res= 1;
353 my_errno= HA_ERR_DECRYPTION_FAILED;
354 }
355 else
356 {
357 const uchar *src= args->page;
358 uchar* dst= args->crypt_buf;
359 uint pageno= (uint)args->pageno;
360 LSN lsn= lsn_korr(src);
361 const uint head= share->keypage_header;
362 const uint tail= CRC_SIZE;
363 const uint32 key_version= _ma_get_key_version(share, src);
364 /* page_used includes header (but not trailer) */
365 const uint size= page_used - head;
366
367 /* 1 - copy head */
368 memcpy(dst, src, head);
369 /* 2 - decrypt page */
370 res= ma_decrypt(share, share->crypt_data,
371 src + head, dst + head, size, pageno, lsn, key_version);
372 /* 3 - copy tail */
373 memcpy(dst + block_size - tail, src + block_size - tail, tail);
374 /* 4 clear key version to get correct crc */
375 _ma_store_key_version(share, dst, 0);
376 }
377
378 if (args->crypt_buf != NULL)
379 {
380 uchar *tmp= args->page;
381 args->page= args->crypt_buf;
382 args->crypt_buf= NULL;
383 my_free(tmp);
384 }
385
386 return maria_page_crc_check_index(res, args);
387 }
388
ma_crypt_index_pre_write_hook(PAGECACHE_IO_HOOK_ARGS * args)389 static my_bool ma_crypt_index_pre_write_hook(PAGECACHE_IO_HOOK_ARGS *args)
390 {
391 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
392 const uint block_size= share->block_size;
393 const uint page_used= _ma_get_page_used(share, args->page);
394 uint key_version;
395 uchar *crypt_buf= my_malloc(block_size, MYF(0));
396 if (crypt_buf == NULL)
397 {
398 args->crypt_buf= NULL; /* for post-hook */
399 return 1;
400 }
401
402 if (!share->base.born_transactional)
403 {
404 /* store a random number instead of LSN (for counter block) */
405 store_rand_lsn(args->page);
406 }
407
408 maria_page_crc_set_index(args);
409
410 {
411 const uchar *src= args->page;
412 uchar* dst= crypt_buf;
413 uint pageno= (uint)args->pageno;
414 LSN lsn= lsn_korr(src);
415 const uint head= share->keypage_header;
416 const uint tail= CRC_SIZE;
417 /* page_used includes header (but not trailer) */
418 const uint size= page_used - head;
419
420 /* 1 - copy head */
421 memcpy(dst, src, head);
422 /* 2 - encrypt page */
423 if (ma_encrypt(share, share->crypt_data,
424 src + head, dst + head, size, pageno, lsn, &key_version))
425 {
426 my_free(crypt_buf);
427 return 1;
428 }
429 /* 3 - copy tail */
430 memcpy(dst + block_size - tail, src + block_size - tail, tail);
431 /* 4 - store key version */
432 _ma_store_key_version(share, dst, key_version);
433 #ifdef HAVE_valgrind
434 /* 5 - keep valgrind happy by zeroing not used bytes */
435 bzero(dst+head+size, block_size - size - tail - head);
436 #endif
437 }
438
439 /* swap pointers to instead write out the encrypted block */
440 args->crypt_buf= args->page;
441 args->page= crypt_buf;
442
443 return 0;
444 }
445
ma_crypt_set_index_pagecache_callbacks(PAGECACHE_FILE * file,MARIA_SHARE * share)446 void ma_crypt_set_index_pagecache_callbacks(PAGECACHE_FILE *file,
447 MARIA_SHARE *share
448 __attribute__((unused)))
449 {
450 file->pre_read_hook= ma_crypt_pre_read_hook;
451 file->post_read_hook= ma_crypt_index_post_read_hook;
452 file->pre_write_hook= ma_crypt_index_pre_write_hook;
453 file->post_write_hook= ma_crypt_post_write_hook;
454 }
455
ma_encrypt(MARIA_SHARE * share,MARIA_CRYPT_DATA * crypt_data,const uchar * src,uchar * dst,uint size,uint pageno,LSN lsn,uint * key_version)456 static int ma_encrypt(MARIA_SHARE *share, MARIA_CRYPT_DATA *crypt_data,
457 const uchar *src, uchar *dst, uint size,
458 uint pageno, LSN lsn,
459 uint *key_version)
460 {
461 int rc;
462 uint32 dstlen= 0; /* Must be set because of error message */
463
464 *key_version = encryption_key_get_latest_version(crypt_data->scheme.key_id);
465 if (*key_version == ENCRYPTION_KEY_VERSION_INVALID)
466 {
467 /*
468 We use this error for both encryption and decryption, as in normal
469 cases it should be impossible to get an error here.
470 */
471 my_errno= HA_ERR_DECRYPTION_FAILED;
472 my_printf_error(HA_ERR_DECRYPTION_FAILED,
473 "Unknown key id %u. Can't continue!",
474 MYF(ME_FATAL|ME_ERROR_LOG),
475 crypt_data->scheme.key_id);
476 return 1;
477 }
478
479 rc= encryption_scheme_encrypt(src, size, dst, &dstlen,
480 &crypt_data->scheme, *key_version,
481 crypt_data->space, pageno, lsn);
482
483 /* The following can only fail if the encryption key is wrong */
484 DBUG_ASSERT(!my_assert_on_error || rc == MY_AES_OK);
485 DBUG_ASSERT(!my_assert_on_error || dstlen == size);
486 if (! (rc == MY_AES_OK && dstlen == size))
487 {
488 my_errno= HA_ERR_DECRYPTION_FAILED;
489 my_printf_error(HA_ERR_DECRYPTION_FAILED,
490 "failed to encrypt '%s' rc: %d dstlen: %u size: %u\n",
491 MYF(ME_FATAL|ME_ERROR_LOG),
492 share->open_file_name.str, rc, dstlen, size);
493 return 1;
494 }
495
496 return 0;
497 }
498
ma_decrypt(MARIA_SHARE * share,MARIA_CRYPT_DATA * crypt_data,const uchar * src,uchar * dst,uint size,uint pageno,LSN lsn,uint key_version)499 static int ma_decrypt(MARIA_SHARE *share, MARIA_CRYPT_DATA *crypt_data,
500 const uchar *src, uchar *dst, uint size,
501 uint pageno, LSN lsn,
502 uint key_version)
503 {
504 int rc;
505 uint32 dstlen= 0; /* Must be set because of error message */
506
507 rc= encryption_scheme_decrypt(src, size, dst, &dstlen,
508 &crypt_data->scheme, key_version,
509 crypt_data->space, pageno, lsn);
510
511 DBUG_ASSERT(!my_assert_on_error || rc == MY_AES_OK);
512 DBUG_ASSERT(!my_assert_on_error || dstlen == size);
513 if (! (rc == MY_AES_OK && dstlen == size))
514 {
515 my_errno= HA_ERR_DECRYPTION_FAILED;
516 if (!share->silence_encryption_errors)
517 my_printf_error(HA_ERR_DECRYPTION_FAILED,
518 "failed to decrypt '%s' rc: %d dstlen: %u size: %u\n",
519 MYF(ME_FATAL|ME_ERROR_LOG),
520 share->open_file_name.str, rc, dstlen, size);
521 return 1;
522 }
523 return 0;
524 }
525