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-1301 USA */
16
17/******************************************************************//**
18 @file Parser.cc
19 A class to parse the key file
20
21How it works...
22The location and usage can be configured via the configuration file.
23Example
24
25[mysqld]
26...
27file_key_management_filename = /home/mdb/keys.enc
28file_key_management_filekey = secret
29...
30
31The keys are read from a file.
32The filename is set up via the file_key_management_filename
33configuration value.
34file_key_management_filename is used to configure the absolute
35path to this file.
36
37Examples:
38file_key_management_filename = \\\\unc\\keys.enc (windows share)
39file_key_management_filename = e:/tmp/keys.enc (windows path)
40file_key_management_filename = /tmp/keys.enc (linux path)
41
42The key file contains AES keys as hex-encoded strings.
43Supported are keys of size 128, 192 or 256 bits.
44Example:
451;F5502320F8429037B8DAEF761B189D12
462;770A8A65DA156D24EE2A093277530142770A8A65DA156D24EE2A093277530142
47
481 is the key identifier which can be used for table creation,
49it is followed by a AES key
50
51The key file could be encrypted and the key to decrypt the file can
52be given with the optional file_key_management_filekey
53parameter.
54
55The file key can also be located if FILE: is prepended to the
56key. Then the following part is interpreted as absolute path to the
57file containing the file key (which must be a text - not binary - string).
58
59Example:
60
61file_key_management_filekey = FILE:y:/secret256.enc
62
63If the key file can not be read at server startup, for example if the
64file key is not present, the plugin will not start
65access to encrypted tables will not be possible.
66
67Open SSL command line utility can be used to create an encrypted key file.
68Example:
69openssl 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
109void 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
147bool 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
168bool 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
199bool 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
234void 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*/
245int 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
313char* 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
402err3:
403 free(decrypted);
404err2:
405 free(buffer);
406err1:
407 close(f);
408err0:
409 return NULL;
410}
411