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