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 
bytes_to_key(const unsigned char * salt,const char * input,unsigned char * key,unsigned char * iv)101 void Parser::bytes_to_key(const unsigned char *salt, const char *input,
102                           unsigned char *key, unsigned char *iv)
103 {
104   unsigned char digest[MY_SHA1_HASH_SIZE];
105   int key_left   = OpenSSL_key_len;
106   int iv_left    = OpenSSL_iv_len;
107   const size_t ilen= strlen(input);
108   const size_t slen= OpenSSL_salt_len; // either this or explicit (size_t) casts below
109 
110   my_sha1_multi(digest, input, ilen, salt, slen, NullS);
111 
112   while (iv_left)
113   {
114     int left= MY_SHA1_HASH_SIZE;
115     if (key_left)
116     {
117       int store = MY_MIN(key_left, MY_SHA1_HASH_SIZE);
118       memcpy(&key[OpenSSL_key_len - key_left], digest, store);
119 
120       key_left -= store;
121       left     -= store;
122     }
123 
124     if (iv_left && left)
125     {
126       int store= MY_MIN(iv_left, left);
127       memcpy(&iv[OpenSSL_iv_len - iv_left], &digest[MY_SHA1_HASH_SIZE - left], store);
128 
129       iv_left    -= store;
130     }
131 
132     if (iv_left)
133       my_sha1_multi(digest, digest, MY_SHA1_HASH_SIZE,
134                             input, ilen, salt, slen, NullS);
135   }
136 }
137 
138 
parse(std::map<uint,keyentry> * keys)139 bool Parser::parse(std::map<uint,keyentry> *keys)
140 {
141   const char *secret= filekey;
142   char buf[MAX_SECRET_SIZE + 1];
143 
144   //If secret starts with FILE: interpret the secret as a filename.
145   if (strncmp(filekey, FILE_PREFIX,sizeof(FILE_PREFIX) -1) == 0)
146   {
147     if (read_filekey(filekey + sizeof(FILE_PREFIX) - 1, buf))
148       return 1;
149     secret= buf;
150   }
151 
152   return parse_file(keys, secret);
153 }
154 
155 
156 /*
157   secret is limited to MAX_SECRET_SIZE characters
158 */
159 
read_filekey(const char * filekey,char * secret)160 bool Parser::read_filekey(const char *filekey, char *secret)
161 {
162   int f= open(filekey, O_RDONLY|O_BINARY);
163   if (f == -1)
164   {
165     my_error(EE_FILENOTFOUND,ME_ERROR_LOG, filekey, errno);
166     return 1;
167   }
168 
169   int len= read(f, secret, MAX_SECRET_SIZE);
170   if (len <= 0)
171   {
172     my_error(EE_READ,ME_ERROR_LOG, filekey, errno);
173     close(f);
174     return 1;
175   }
176   close(f);
177   while (secret[len - 1] == '\r' || secret[len - 1] == '\n') len--;
178   secret[len]= '\0';
179   return 0;
180 }
181 
182 
183 /**
184    Get the keys from the key file <filename> and decrypt it with the
185    key <secret>.  Store the keys with id smaller then <maxKeyId> in an
186    array of structs keyentry.
187 
188    @return 0 when ok, 1 for an error
189  */
190 
parse_file(std::map<uint,keyentry> * keys,const char * secret)191 bool Parser::parse_file(std::map<uint,keyentry> *keys, const char *secret)
192 {
193   char *buffer= read_and_decrypt_file(secret);
194 
195   if (!buffer)
196     return 1;
197 
198   keyentry key;
199   char *line=buffer;
200 
201   while (*line)
202   {
203     line_number++;
204     switch (parse_line(&line, &key)) {
205     case 1: // comment
206       break;
207     case -1: // error
208       free(buffer);
209       return 1;
210     case 0:
211       (*keys)[key.id] = key;
212       break;
213     }
214   }
215 
216   free(buffer);
217   if (keys->size() == 0 || (*keys)[1].id == 0)
218   {
219     report_error("System key id 1 is missing", 0);
220     return 1;
221   }
222 
223   return 0;
224 }
225 
report_error(const char * reason,size_t position)226 void Parser::report_error(const char *reason, size_t position)
227 {
228   my_printf_error(EE_READ, "%s at %s line %u, column %zu",
229     ME_ERROR_LOG, reason, filename, line_number, position + 1);
230 }
231 
232 /*
233   return 0 - new key
234          1 - comment
235         -1 - error
236 */
parse_line(char ** line_ptr,keyentry * key)237 int Parser::parse_line(char **line_ptr, keyentry *key)
238 {
239   int res= 1;
240   char *p= *line_ptr;
241   while (isspace(*p) && *p != '\n') p++;
242   if (*p != '#' && *p != '\n')
243   {
244     if (!isdigit(*p))
245     {
246       report_error("Syntax error", p - *line_ptr);
247       return -1;
248     }
249 
250     longlong id = 0;
251     while (isdigit(*p))
252     {
253       id = id * 10 + *p - '0';
254       if (id > UINT_MAX32)
255       {
256         report_error("Invalid key id", p - *line_ptr);
257         return -1;
258       }
259       p++;
260     }
261 
262     if (id < 1)
263     {
264       report_error("Invalid key id", p - *line_ptr);
265       return -1;
266     }
267 
268     if (*p != ';')
269     {
270       report_error("Syntax error", p - *line_ptr);
271       return -1;
272     }
273 
274     p++;
275     key->id= (unsigned int)id;
276     key->length=0;
277     while (isxdigit(p[0]) && isxdigit(p[1]) && key->length < sizeof(key->key))
278     {
279       key->key[key->length++] = from_hex(p[0]) * 16 + from_hex(p[1]);
280       p+=2;
281     }
282     if (isxdigit(*p) ||
283         (key->length != 16 && key->length != 24 && key->length != 32))
284     {
285       report_error("Invalid key", p - *line_ptr);
286       return -1;
287     }
288 
289     res= 0;
290   }
291   while (*p && *p != '\n') p++;
292   *line_ptr= *p == '\n' ? p + 1 : p;
293   return res;
294 }
295 
296 /**
297    Decrypt the key file 'filename' if it is encrypted with the key
298    'secret'.  Store the content of the decrypted file in 'buffer'. The
299    buffer has to be freed in the calling function.
300  */
301 #ifdef _WIN32
302 #define lseek _lseeki64
303 #endif
304 
read_and_decrypt_file(const char * secret)305 char* Parser::read_and_decrypt_file(const char *secret)
306 {
307   int f;
308   if (!filename || !filename[0])
309   {
310     my_printf_error(EE_CANT_OPEN_STREAM, "file-key-management-filename is not set",
311                     ME_ERROR_LOG);
312     goto err0;
313   }
314 
315   f= open(filename, O_RDONLY|O_BINARY, 0);
316   if (f < 0)
317   {
318     my_error(EE_FILENOTFOUND, ME_ERROR_LOG, filename, errno);
319     goto err0;
320   }
321 
322   my_off_t file_size;
323   file_size= lseek(f, 0, SEEK_END);
324 
325   if (file_size == MY_FILEPOS_ERROR || (my_off_t)lseek(f, 0, SEEK_SET) == MY_FILEPOS_ERROR)
326   {
327     my_error(EE_CANT_SEEK, MYF(0), filename, errno);
328     goto err1;
329   }
330 
331   if (file_size > MAX_KEY_FILE_SIZE)
332   {
333     my_error(EE_READ, MYF(0), filename, EFBIG);
334     goto err1;
335   }
336 
337   //Read file into buffer
338   uchar *buffer;
339   buffer= (uchar*)malloc((size_t)file_size + 1);
340   if (!buffer)
341   {
342     my_error(EE_OUTOFMEMORY, ME_ERROR_LOG| ME_FATAL, file_size);
343     goto err1;
344   }
345 
346   if (read(f, buffer, (int)file_size) != (int)file_size)
347   {
348     my_printf_error(EE_READ,
349       "read from %s failed, errno %d",
350       MYF(ME_ERROR_LOG|ME_FATAL), filename, errno);
351     goto err2;
352   }
353 
354 // Check for file encryption
355   uchar *decrypted;
356   if (file_size > OpenSSL_prefix_len && strncmp((char*)buffer, OpenSSL_prefix, OpenSSL_prefix_len) == 0)
357   {
358     uchar key[OpenSSL_key_len];
359     uchar iv[OpenSSL_iv_len];
360 
361     decrypted= (uchar*)malloc((size_t)file_size);
362     if (!decrypted)
363     {
364       my_error(EE_OUTOFMEMORY, ME_ERROR_LOG | ME_FATAL, file_size);
365       goto err2;
366     }
367     bytes_to_key(buffer + OpenSSL_prefix_len, secret, key, iv);
368     uint32 d_size;
369     if (my_aes_crypt(MY_AES_CBC, ENCRYPTION_FLAG_DECRYPT,
370                      buffer + OpenSSL_prefix_len + OpenSSL_salt_len,
371                      (unsigned int)file_size - OpenSSL_prefix_len - OpenSSL_salt_len,
372                      decrypted, &d_size, key, OpenSSL_key_len,
373                      iv, OpenSSL_iv_len))
374 
375     {
376       my_printf_error(EE_READ, "Cannot decrypt %s. Wrong key?", ME_ERROR_LOG, filename);
377       goto err3;
378     }
379 
380     free(buffer);
381     buffer= decrypted;
382     file_size= d_size;
383   }
384   else if (*secret)
385   {
386     my_printf_error(EE_READ, "Cannot decrypt %s. Not encrypted", ME_ERROR_LOG, filename);
387     goto err2;
388   }
389 
390   buffer[file_size]= '\0';
391   close(f);
392   return (char*) buffer;
393 
394 err3:
395   free(decrypted);
396 err2:
397   free(buffer);
398 err1:
399   close(f);
400 err0:
401   return NULL;
402 }
403