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