1 /* Copyright (C) 2014 eperi GmbH.
2    Copyright (C) 2015 MariaDB Corporation
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; version 2 of the License.
7 
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  GNU General Public License for more details.
12 
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
16 
17 /******************************************************************//**
18  @file Parser.cc
19  A class to parse the key file
20 
21 How it works...
22 The location and usage can be configured via the configuration file.
23 Example
24 
25 [mysqld]
26 ...
27 file_key_management_filename = /home/mdb/keys.enc
28 file_key_management_filekey = secret
29 ...
30 
31 The keys are read from a file.
32 The filename is set up via the file_key_management_filename
33 configuration value.
34 file_key_management_filename is used to configure the absolute
35 path to this file.
36 
37 Examples:
38 file_key_management_filename = \\\\unc\\keys.enc        (windows share)
39 file_key_management_filename = e:/tmp/keys.enc          (windows path)
40 file_key_management_filename = /tmp/keys.enc            (linux path)
41 
42 The key file contains AES keys as hex-encoded strings.
43 Supported are keys of size 128, 192 or 256 bits.
44 Example:
45 1;F5502320F8429037B8DAEF761B189D12
46 2;770A8A65DA156D24EE2A093277530142770A8A65DA156D24EE2A093277530142
47 
48 1 is the key identifier which can be used for table creation,
49 it is followed by a AES key
50 
51 The key file could be encrypted and the key to decrypt the file can
52 be given with the optional file_key_management_filekey
53 parameter.
54 
55 The file key can also be located if FILE: is prepended to the
56 key. Then the following part is interpreted as absolute path to the
57 file containing the file key (which must be a text - not binary - string).
58 
59 Example:
60 
61 file_key_management_filekey = FILE:y:/secret256.enc
62 
63 If the key file can not be read at server startup, for example if the
64 file key is not present, the plugin will not start
65 access to encrypted tables will not be possible.
66 
67 Open SSL command line utility can be used to create an encrypted key file.
68 Example:
69 openssl enc -aes-256-cbc -md sha1 -k "secret" -in keys.txt -out keys.enc
70 ***********************************************************************/
71 
72 #include <my_global.h>
73 #include "parser.h"
74 #include <m_string.h>
75 #include <mysys_err.h>
76 
77 #define FILE_PREFIX "FILE:"
78 #define MAX_KEY_FILE_SIZE 1024*1024
79 #define MAX_SECRET_SIZE 256
80 
81 /*
82   The values below are what one gets after
83   openssl enc -aes-256-cbc -md sha1 -k "secret" -in keys.txt -out keys.enc
84 */
85 #define OpenSSL_prefix     "Salted__"
86 #define OpenSSL_prefix_len (sizeof(OpenSSL_prefix) - 1)
87 #define OpenSSL_salt_len   8
88 #define OpenSSL_key_len    32
89 #define OpenSSL_iv_len     16
90 
91 /**
92    Calculate key and iv from a given salt and secret as in the
93    openssl command-line tool
94 
95    @param salt   [in]  the given salt as extracted from the encrypted file
96    @param secret [in]  the given secret as String, provided by the user
97    @param key    [out] 32 Bytes of key are written to this pointer
98    @param iv     [out] 16 Bytes of iv are written to this pointer
99 
100    Note, that in openssl this whole function can be reduced to
101 
102     #include <openssl/evp.h>
103     EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt,
104                    secret, strlen(secret), 1, key, iv);
105 
106    but alas! we want to support yassl too
107 */
108 
bytes_to_key(const unsigned char * salt,const char * input,unsigned char * key,unsigned char * iv)109 void Parser::bytes_to_key(const unsigned char *salt, const char *input,
110                           unsigned char *key, unsigned char *iv)
111 {
112   unsigned char digest[MY_SHA1_HASH_SIZE];
113   int key_left   = OpenSSL_key_len;
114   int iv_left    = OpenSSL_iv_len;
115   const size_t ilen= strlen(input);
116   const size_t slen= OpenSSL_salt_len; // either this or explicit (size_t) casts below
117 
118   my_sha1_multi(digest, input, ilen, salt, slen, NullS);
119 
120   while (iv_left)
121   {
122     int left= MY_SHA1_HASH_SIZE;
123     if (key_left)
124     {
125       int store = MY_MIN(key_left, MY_SHA1_HASH_SIZE);
126       memcpy(&key[OpenSSL_key_len - key_left], digest, store);
127 
128       key_left -= store;
129       left     -= store;
130     }
131 
132     if (iv_left && left)
133     {
134       int store= MY_MIN(iv_left, left);
135       memcpy(&iv[OpenSSL_iv_len - iv_left], &digest[MY_SHA1_HASH_SIZE - left], store);
136 
137       iv_left    -= store;
138     }
139 
140     if (iv_left)
141       my_sha1_multi(digest, digest, MY_SHA1_HASH_SIZE,
142                             input, ilen, salt, slen, NullS);
143   }
144 }
145 
146 
parse(std::map<uint,keyentry> * keys)147 bool Parser::parse(std::map<uint,keyentry> *keys)
148 {
149   const char *secret= filekey;
150   char buf[MAX_SECRET_SIZE + 1];
151 
152   //If secret starts with FILE: interpret the secret as a filename.
153   if (strncmp(filekey, FILE_PREFIX,sizeof(FILE_PREFIX) -1) == 0)
154   {
155     if (read_filekey(filekey + sizeof(FILE_PREFIX) - 1, buf))
156       return 1;
157     secret= buf;
158   }
159 
160   return parse_file(keys, secret);
161 }
162 
163 
164 /*
165   secret is limited to MAX_SECRET_SIZE characters
166 */
167 
read_filekey(const char * filekey,char * secret)168 bool Parser::read_filekey(const char *filekey, char *secret)
169 {
170   int f= open(filekey, O_RDONLY|O_BINARY);
171   if (f == -1)
172   {
173     my_error(EE_FILENOTFOUND,ME_ERROR_LOG, filekey, errno);
174     return 1;
175   }
176 
177   int len= read(f, secret, MAX_SECRET_SIZE);
178   if (len <= 0)
179   {
180     my_error(EE_READ,ME_ERROR_LOG, filekey, errno);
181     close(f);
182     return 1;
183   }
184   close(f);
185   while (secret[len - 1] == '\r' || secret[len - 1] == '\n') len--;
186   secret[len]= '\0';
187   return 0;
188 }
189 
190 
191 /**
192    Get the keys from the key file <filename> and decrypt it with the
193    key <secret>.  Store the keys with id smaller then <maxKeyId> in an
194    array of structs keyentry.
195 
196    @return 0 when ok, 1 for an error
197  */
198 
parse_file(std::map<uint,keyentry> * keys,const char * secret)199 bool Parser::parse_file(std::map<uint,keyentry> *keys, const char *secret)
200 {
201   char *buffer= read_and_decrypt_file(secret);
202 
203   if (!buffer)
204     return 1;
205 
206   keyentry key;
207   char *line=buffer;
208 
209   while (*line)
210   {
211     line_number++;
212     switch (parse_line(&line, &key)) {
213     case 1: // comment
214       break;
215     case -1: // error
216       free(buffer);
217       return 1;
218     case 0:
219       (*keys)[key.id] = key;
220       break;
221     }
222   }
223 
224   free(buffer);
225   if (keys->size() == 0 || (*keys)[1].id == 0)
226   {
227     report_error("System key id 1 is missing", 0);
228     return 1;
229   }
230 
231   return 0;
232 }
233 
report_error(const char * reason,size_t position)234 void Parser::report_error(const char *reason, size_t position)
235 {
236   my_printf_error(EE_READ, "%s at %s line %u, column %zu",
237     ME_ERROR_LOG, reason, filename, line_number, position + 1);
238 }
239 
240 /*
241   return 0 - new key
242          1 - comment
243         -1 - error
244 */
parse_line(char ** line_ptr,keyentry * key)245 int Parser::parse_line(char **line_ptr, keyentry *key)
246 {
247   int res= 1;
248   char *p= *line_ptr;
249   while (isspace(*p) && *p != '\n') p++;
250   if (*p != '#' && *p != '\n')
251   {
252     if (!isdigit(*p))
253     {
254       report_error("Syntax error", p - *line_ptr);
255       return -1;
256     }
257 
258     longlong id = 0;
259     while (isdigit(*p))
260     {
261       id = id * 10 + *p - '0';
262       if (id > UINT_MAX32)
263       {
264         report_error("Invalid key id", p - *line_ptr);
265         return -1;
266       }
267       p++;
268     }
269 
270     if (id < 1)
271     {
272       report_error("Invalid key id", p - *line_ptr);
273       return -1;
274     }
275 
276     if (*p != ';')
277     {
278       report_error("Syntax error", p - *line_ptr);
279       return -1;
280     }
281 
282     p++;
283     key->id= (unsigned int)id;
284     key->length=0;
285     while (isxdigit(p[0]) && isxdigit(p[1]) && key->length < sizeof(key->key))
286     {
287       key->key[key->length++] = from_hex(p[0]) * 16 + from_hex(p[1]);
288       p+=2;
289     }
290     if (isxdigit(*p) ||
291         (key->length != 16 && key->length != 24 && key->length != 32))
292     {
293       report_error("Invalid key", p - *line_ptr);
294       return -1;
295     }
296 
297     res= 0;
298   }
299   while (*p && *p != '\n') p++;
300   *line_ptr= *p == '\n' ? p + 1 : p;
301   return res;
302 }
303 
304 /**
305    Decrypt the key file 'filename' if it is encrypted with the key
306    'secret'.  Store the content of the decrypted file in 'buffer'. The
307    buffer has to be freed in the calling function.
308  */
309 #ifdef _WIN32
310 #define lseek _lseeki64
311 #endif
312 
read_and_decrypt_file(const char * secret)313 char* Parser::read_and_decrypt_file(const char *secret)
314 {
315   int f;
316   if (!filename || !filename[0])
317   {
318     my_printf_error(EE_CANT_OPEN_STREAM, "file-key-management-filename is not set",
319                     ME_ERROR_LOG);
320     goto err0;
321   }
322 
323   f= open(filename, O_RDONLY|O_BINARY, 0);
324   if (f < 0)
325   {
326     my_error(EE_FILENOTFOUND, ME_ERROR_LOG, filename, errno);
327     goto err0;
328   }
329 
330   my_off_t file_size;
331   file_size= lseek(f, 0, SEEK_END);
332 
333   if (file_size == MY_FILEPOS_ERROR || (my_off_t)lseek(f, 0, SEEK_SET) == MY_FILEPOS_ERROR)
334   {
335     my_error(EE_CANT_SEEK, MYF(0), filename, errno);
336     goto err1;
337   }
338 
339   if (file_size > MAX_KEY_FILE_SIZE)
340   {
341     my_error(EE_READ, MYF(0), filename, EFBIG);
342     goto err1;
343   }
344 
345   //Read file into buffer
346   uchar *buffer;
347   buffer= (uchar*)malloc((size_t)file_size + 1);
348   if (!buffer)
349   {
350     my_error(EE_OUTOFMEMORY, ME_ERROR_LOG| ME_FATAL, file_size);
351     goto err1;
352   }
353 
354   if (read(f, buffer, (int)file_size) != (int)file_size)
355   {
356     my_printf_error(EE_READ,
357       "read from %s failed, errno %d",
358       MYF(ME_ERROR_LOG|ME_FATAL), filename, errno);
359     goto err2;
360   }
361 
362 // Check for file encryption
363   uchar *decrypted;
364   if (file_size > OpenSSL_prefix_len && strncmp((char*)buffer, OpenSSL_prefix, OpenSSL_prefix_len) == 0)
365   {
366     uchar key[OpenSSL_key_len];
367     uchar iv[OpenSSL_iv_len];
368 
369     decrypted= (uchar*)malloc((size_t)file_size);
370     if (!decrypted)
371     {
372       my_error(EE_OUTOFMEMORY, ME_ERROR_LOG | ME_FATAL, file_size);
373       goto err2;
374     }
375     bytes_to_key(buffer + OpenSSL_prefix_len, secret, key, iv);
376     uint32 d_size;
377     if (my_aes_crypt(MY_AES_CBC, ENCRYPTION_FLAG_DECRYPT,
378                      buffer + OpenSSL_prefix_len + OpenSSL_salt_len,
379                      (unsigned int)file_size - OpenSSL_prefix_len - OpenSSL_salt_len,
380                      decrypted, &d_size, key, OpenSSL_key_len,
381                      iv, OpenSSL_iv_len))
382 
383     {
384       my_printf_error(EE_READ, "Cannot decrypt %s. Wrong key?", ME_ERROR_LOG, filename);
385       goto err3;
386     }
387 
388     free(buffer);
389     buffer= decrypted;
390     file_size= d_size;
391   }
392   else if (*secret)
393   {
394     my_printf_error(EE_READ, "Cannot decrypt %s. Not encrypted", ME_ERROR_LOG, filename);
395     goto err2;
396   }
397 
398   buffer[file_size]= '\0';
399   close(f);
400   return (char*) buffer;
401 
402 err3:
403   free(decrypted);
404 err2:
405   free(buffer);
406 err1:
407   close(f);
408 err0:
409   return NULL;
410 }
411