1 /* Copyright (c) 2014-2016 Percona LLC and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License
5 as published by the Free Software Foundation; version 2 of
6 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 #include <time.h>
18 #include <string.h>
19 #include <stdio.h>
20
21 #include <my_global.h>
22 #include <my_sys.h>
23 #include <m_ctype.h>
24 #include <mysql/plugin.h>
25 #include <mysql/plugin_audit.h>
26 #include <mysql/service_security_context.h>
27 #include <mysqld_error.h>
28 #include <typelib.h>
29 #include <mysql_version.h>
30 #include <mysql_com.h>
31 #include <syslog.h>
32
33 #include "audit_log.h"
34 #include "logger.h"
35 #include "buffer.h"
36 #include "audit_handler.h"
37 #include "filter.h"
38
39 #define PLUGIN_VERSION 0x0002
40
41
42 enum audit_log_policy_t { ALL, NONE, LOGINS, QUERIES };
43 enum audit_log_strategy_t
44 { ASYNCHRONOUS, PERFORMANCE, SEMISYNCHRONOUS, SYNCHRONOUS };
45 enum audit_log_format_t { OLD, NEW, JSON, CSV };
46 enum audit_log_handler_t { HANDLER_FILE, HANDLER_SYSLOG };
47
48 typedef void (*escape_buf_func_t)(const char *, size_t *, char *, size_t *);
49
50 static audit_handler_t *log_handler= NULL;
51 static ulonglong record_id= 0;
52 static time_t log_file_time= 0;
53 static char *audit_log_file;
54 static const char default_audit_log_file[]= "audit.log";
55 static ulong audit_log_policy= ALL;
56 static ulong audit_log_strategy= ASYNCHRONOUS;
57 static ulonglong audit_log_buffer_size= 1048576;
58 static ulonglong audit_log_rotate_on_size= 0;
59 static ulonglong audit_log_rotations= 0;
60 static char audit_log_flush= FALSE;
61 static ulong audit_log_format= OLD;
62 static ulong audit_log_handler= HANDLER_FILE;
63 static char *audit_log_syslog_ident;
64 static const char default_audit_log_syslog_ident[] = "percona-audit";
65 static ulong audit_log_syslog_facility= 0;
66 static ulong audit_log_syslog_priority= 0;
67 static char *audit_log_exclude_accounts= NULL;
68 static char *audit_log_include_accounts= NULL;
69 static char *audit_log_exclude_databases= NULL;
70 static char *audit_log_include_databases= NULL;
71 static char *audit_log_exclude_commands= NULL;
72 static char *audit_log_include_commands= NULL;
73 int64 audit_log_buffer_size_overflow = 0;
74
75 PSI_memory_key key_memory_audit_log_logger_handle;
76 PSI_memory_key key_memory_audit_log_handler;
77 PSI_memory_key key_memory_audit_log_buffer;
78 PSI_memory_key key_memory_audit_log_accounts;
79 PSI_memory_key key_memory_audit_log_databases;
80 PSI_memory_key key_memory_audit_log_commands;
81
82 static PSI_memory_info all_audit_log_memory[]=
83 {
84 {&key_memory_audit_log_logger_handle, "audit_log_logger_handle", 0},
85 {&key_memory_audit_log_handler, "audit_log_handler", 0},
86 {&key_memory_audit_log_buffer, "audit_log_buffer", 0},
87 {&key_memory_audit_log_accounts, "audit_log_accounts", 0},
88 {&key_memory_audit_log_databases, "audit_log_databases", 0},
89 {&key_memory_audit_log_commands, "audit_log_commands", 0},
90 };
91
92 static int audit_log_syslog_facility_codes[]=
93 { LOG_USER, LOG_AUTHPRIV, LOG_CRON, LOG_DAEMON, LOG_FTP,
94 LOG_KERN, LOG_LPR, LOG_MAIL, LOG_NEWS,
95 #if (defined LOG_SECURITY)
96 LOG_SECURITY,
97 #endif
98 LOG_SYSLOG, LOG_AUTH, LOG_UUCP, LOG_LOCAL0, LOG_LOCAL1,
99 LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6,
100 LOG_LOCAL7, 0};
101
102
103 static const char *audit_log_syslog_facility_names[]=
104 { "LOG_USER", "LOG_AUTHPRIV", "LOG_CRON", "LOG_DAEMON", "LOG_FTP",
105 "LOG_KERN", "LOG_LPR", "LOG_MAIL", "LOG_NEWS",
106 #if (defined LOG_SECURITY)
107 "LOG_SECURITY",
108 #endif
109 "LOG_SYSLOG", "LOG_AUTH", "LOG_UUCP", "LOG_LOCAL0", "LOG_LOCAL1",
110 "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4", "LOG_LOCAL5", "LOG_LOCAL6",
111 "LOG_LOCAL7", 0 };
112
113
114 static const int audit_log_syslog_priority_codes[]=
115 { LOG_INFO, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING,
116 LOG_NOTICE, LOG_EMERG, LOG_DEBUG, 0 };
117
118
119 static const char *audit_log_syslog_priority_names[]=
120 { "LOG_INFO", "LOG_ALERT", "LOG_CRIT", "LOG_ERR", "LOG_WARNING",
121 "LOG_NOTICE", "LOG_EMERG", "LOG_DEBUG", 0 };
122
123 static MYSQL_PLUGIN plugin_ptr;
124
125 static
init_record_id(off_t size)126 void init_record_id(off_t size)
127 {
128 record_id= size;
129 }
130
131
132 static
next_record_id()133 ulonglong next_record_id()
134 {
135 return __sync_add_and_fetch(&record_id, 1);
136 }
137
138
139 #define MAX_RECORD_ID_SIZE 50
140 #define MAX_TIMESTAMP_SIZE 25
141
142 void plugin_thdvar_safe_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
143 char **dest, const char *value);
144
145 static
make_timestamp(char * buf,size_t buf_len,time_t t)146 char *make_timestamp(char *buf, size_t buf_len, time_t t)
147 {
148 struct tm tm;
149
150 memset(&tm, 0, sizeof(tm));
151 strftime(buf, buf_len, "%FT%T UTC", gmtime_r(&t, &tm));
152
153 return buf;
154 }
155
156 static
make_record_id(char * buf,size_t buf_len)157 char *make_record_id(char *buf, size_t buf_len)
158 {
159 struct tm tm;
160 size_t len;
161
162 memset(&tm, 0, sizeof(tm));
163 len= my_snprintf(buf, buf_len, "%llu_", next_record_id());
164
165 strftime(buf + len, buf_len - len,
166 "%FT%T", gmtime_r(&log_file_time, &tm));
167
168 return buf;
169 }
170
171 typedef struct
172 {
173 char character;
174 size_t length;
175 const char *replacement;
176 } escape_rule_t;
177
178 static
escape_buf(const char * in,size_t * inlen,char * out,size_t * outlen,const escape_rule_t * control_escape_rules,const escape_rule_t * other_escape_rules)179 void escape_buf(const char *in, size_t *inlen, char *out, size_t *outlen,
180 const escape_rule_t *control_escape_rules,
181 const escape_rule_t *other_escape_rules)
182 {
183 char* outstart = out;
184 const char* base = in;
185 char* outend = out + *outlen;
186 const char* inend;
187 const escape_rule_t *replace_rule = NULL;
188
189 inend = in + (*inlen);
190
191 while ((in < inend) && (out < outend))
192 {
193 replace_rule = NULL;
194 if ((unsigned char)(*in) < 32) {
195 if (control_escape_rules[(unsigned int)*in].character) {
196 replace_rule = &control_escape_rules[(unsigned int)*in];
197 }
198 } else
199 {
200 const escape_rule_t *rule = NULL;
201 for (rule= other_escape_rules; rule->character; rule++)
202 {
203 if (*in == rule->character)
204 {
205 replace_rule = rule;
206 break;
207 }
208 }
209 }
210 if (replace_rule)
211 {
212 if ((outend - out) < (ptrdiff_t) replace_rule->length)
213 break;
214 memcpy(out, replace_rule->replacement, replace_rule->length);
215 out += replace_rule->length;
216 } else
217 {
218 *out++ = *in;
219 }
220 ++in;
221 }
222 *outlen = out - outstart;
223 *inlen = in - base;
224 }
225
226 static
xml_escape(const char * in,size_t * inlen,char * out,size_t * outlen)227 void xml_escape(const char *in, size_t *inlen, char *out, size_t *outlen)
228 {
229 // Most control sequences aren't supported before XML 1.1, and most
230 // tools only support 1.0. Our output is 1.0. Escaping them wouldn't make
231 // the output more valid.
232 static const escape_rule_t control_rules[]=
233 {
234 { 0, 0, NULL },
235 { 0, 0, NULL },
236 { 0, 0, NULL },
237 { 0, 0, NULL },
238 { 0, 0, NULL },
239 { 0, 0, NULL },
240 { 0, 0, NULL },
241 { 0, 0, NULL },
242 { 0, 0, NULL },
243 { '\t', 5, "	" },
244 { '\n', 6, " " },
245 { 0, 0, NULL },
246 { 0, 0, NULL },
247 { '\r', 6, " " },
248 { 0, 0, NULL },
249 { 0, 0, NULL },
250 { 0, 0, NULL },
251 { 0, 0, NULL },
252 { 0, 0, NULL },
253 { 0, 0, NULL },
254 { 0, 0, NULL },
255 { 0, 0, NULL },
256 { 0, 0, NULL },
257 { 0, 0, NULL },
258 { 0, 0, NULL },
259 { 0, 0, NULL },
260 { 0, 0, NULL },
261 { 0, 0, NULL },
262 { 0, 0, NULL },
263 { 0, 0, NULL },
264 { 0, 0, NULL },
265 { 0, 0, NULL },
266 };
267 static const escape_rule_t other_rules[]=
268 {
269 { '<', 4, "<" },
270 { '>', 4, ">" },
271 { '&', 5, "&" },
272 { '"', 6, """ },
273 { 0, 0, NULL }
274 };
275
276 escape_buf(in, inlen, out, outlen, control_rules, other_rules);
277 }
278
279 static
json_escape(const char * in,size_t * inlen,char * out,size_t * outlen)280 void json_escape(const char *in, size_t *inlen, char *out, size_t *outlen)
281 {
282 static const escape_rule_t control_rules[]=
283 {
284 { 0, 6, "\\u0000" },
285 { 1, 6, "\\u0001" },
286 { 2, 6, "\\u0002" },
287 { 3, 6, "\\u0003" },
288 { 4, 6, "\\u0004" },
289 { 5, 6, "\\u0005" },
290 { 6, 6, "\\u0006" },
291 { 7, 6, "\\u0007" },
292 { '\b', 2, "\\b" },
293 { '\t', 2, "\\t" },
294 { '\n', 2, "\\n" },
295 { 11, 6, "\\u000B" },
296 { '\f', 2, "\\f" },
297 { '\r', 2, "\\r" },
298 { 14, 6, "\\u000E" },
299 { 15, 6, "\\u000F" },
300 { 16, 6, "\\u0010" },
301 { 17, 6, "\\u0011" },
302 { 18, 6, "\\u0012" },
303 { 19, 6, "\\u0013" },
304 { 20, 6, "\\u0014" },
305 { 21, 6, "\\u0015" },
306 { 22, 6, "\\u0016" },
307 { 23, 6, "\\u0017" },
308 { 24, 6, "\\u0018" },
309 { 25, 6, "\\u0019" },
310 { 26, 6, "\\u001A" },
311 { 27, 6, "\\u001B" },
312 { 28, 6, "\\u001C" },
313 { 29, 6, "\\u001D" },
314 { 30, 6, "\\u001E" },
315 { 31, 6, "\\u001F" },
316 };
317
318 static const escape_rule_t other_rules[]=
319 {
320 { '\\', 2, "\\\\" },
321 { '"', 2, "\\\"" },
322 { '/', 2, "\\/" },
323 { 0, 0, NULL }
324 };
325
326 escape_buf(in, inlen, out, outlen, control_rules, other_rules);
327 }
328
329 static
csv_escape(const char * in,size_t * inlen,char * out,size_t * outlen)330 void csv_escape(const char *in, size_t *inlen, char *out, size_t *outlen)
331 {
332 // We do not have any standard control escape rules for CSVs
333 static const escape_rule_t control_rules[]=
334 {
335 { 0, 0, NULL },
336 { 0, 0, NULL },
337 { 0, 0, NULL },
338 { 0, 0, NULL },
339 { 0, 0, NULL },
340 { 0, 0, NULL },
341 { 0, 0, NULL },
342 { 0, 0, NULL },
343 { 0, 0, NULL },
344 { 0, 0, NULL },
345 { 0, 0, NULL },
346 { 0, 0, NULL },
347 { 0, 0, NULL },
348 { 0, 0, NULL },
349 { 0, 0, NULL },
350 { 0, 0, NULL },
351 { 0, 0, NULL },
352 { 0, 0, NULL },
353 { 0, 0, NULL },
354 { 0, 0, NULL },
355 { 0, 0, NULL },
356 { 0, 0, NULL },
357 { 0, 0, NULL },
358 { 0, 0, NULL },
359 { 0, 0, NULL },
360 { 0, 0, NULL },
361 { 0, 0, NULL },
362 { 0, 0, NULL },
363 { 0, 0, NULL },
364 { 0, 0, NULL },
365 { 0, 0, NULL },
366 { 0, 0, NULL },
367 };
368
369 static const escape_rule_t other_rules[]=
370 {
371 { '"', 2, "\"\"" },
372 { 0, 0, NULL }
373 };
374
375 escape_buf(in, inlen, out, outlen, control_rules, other_rules);
376 }
377
378 static const escape_buf_func_t format_escape_func[]=
379 { xml_escape, xml_escape, json_escape, csv_escape };
380
381 /*
382 Calculate the size of the otput bufer needed to escape the string.
383
384 @param[in] in Input string
385 @param[in] len Length of the input string
386
387 @return
388 size of the otput bufer including trailing zero
389 */
390 static
calculate_escape_string_buf_len(const char * in,size_t len)391 size_t calculate_escape_string_buf_len(const char *in, size_t len)
392 {
393 char tmp[128];
394 size_t full_outlen= 0;
395
396 while (len > 0)
397 {
398 size_t tmp_size= sizeof(tmp);
399 size_t inlen= len;
400 format_escape_func[audit_log_format](in, &inlen, tmp, &tmp_size);
401 in+= inlen;
402 len-= inlen;
403 full_outlen+= tmp_size;
404 }
405 return full_outlen + 1;
406 }
407
408 /*
409 Escape string according to audit_log_format.
410
411 @param[in] in Input string
412 @param[in] inlen Length of the input string
413 @param[in] out Output buffer
414 @param[in] outlen Length of the output buffer
415 @param[out] endptr A pointer to the character after the
416 last escaped character in the output
417 buffer
418 @param[out] full_outlen Length of the output buffer that would
419 be needed to store complete non-truncated
420 escaped input buffer
421
422 @return
423 pointer to the beginning of the output buffer
424 */
425 static
escape_string(const char * in,size_t inlen,char * out,size_t outlen,char ** endptr,size_t * full_outlen)426 char *escape_string(const char *in, size_t inlen,
427 char *out, size_t outlen,
428 char **endptr, size_t *full_outlen)
429 {
430 if (outlen == 0)
431 {
432 if (endptr)
433 *endptr= out;
434 if (full_outlen)
435 *full_outlen+= calculate_escape_string_buf_len(in, inlen);
436 }
437 else if (in != NULL)
438 {
439 size_t inlen_res= inlen;
440 --outlen;
441 format_escape_func[audit_log_format](in, &inlen_res, out, &outlen);
442 out[outlen]= 0;
443 if (endptr)
444 *endptr= out + outlen + 1;
445 if (full_outlen)
446 {
447 *full_outlen+= outlen;
448 *full_outlen+= calculate_escape_string_buf_len(in + inlen_res,
449 inlen - inlen_res);
450 }
451 }
452 else
453 {
454 *out= 0;
455 if (endptr)
456 *endptr= out + 1;
457 if (full_outlen)
458 ++(*full_outlen);
459 }
460 return out;
461 }
462
463 static
my_plugin_perror(void)464 void my_plugin_perror(void)
465 {
466 char errbuf[MYSYS_STRERROR_SIZE];
467 my_strerror(errbuf, sizeof(errbuf), errno);
468 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Error: %s", errbuf);
469 }
470
471 static
audit_log_write(const char * buf,size_t len)472 void audit_log_write(const char *buf, size_t len)
473 {
474 static int write_error= 0;
475
476 if (audit_handler_write(log_handler, buf, len) < 0)
477 {
478 if (!write_error)
479 {
480 write_error= 1;
481 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL,
482 "Error writing to file %s.", audit_log_file);
483 my_plugin_perror();
484 }
485 }
486 else
487 {
488 write_error= 0;
489 }
490 }
491
492
493 /* Defined in MySQL server */
494 extern int orig_argc;
495 extern char **orig_argv;
496 extern char server_version[SERVER_VERSION_LENGTH];
497
498 static
make_argv(char * buf,size_t len,int argc,char ** argv)499 char *make_argv(char *buf, size_t len, int argc, char **argv)
500 {
501 size_t left= len;
502
503 buf[0]= 0;
504 while (argc > 0 && left > 0)
505 {
506 left-= my_snprintf(buf + len - left, left,
507 "%s%c", *argv, argc > 1 ? ' ' : 0);
508 argc--; argv++;
509 }
510
511 return buf;
512 }
513
514 static
audit_log_audit_record(char * buf,size_t buflen,const char * name,time_t t,size_t * outlen)515 char *audit_log_audit_record(char *buf, size_t buflen,
516 const char *name, time_t t,
517 size_t *outlen)
518 {
519 char id_str[MAX_RECORD_ID_SIZE];
520 char timestamp[MAX_TIMESTAMP_SIZE];
521 char arg_buf[512];
522 const char *format_string[] = {
523 "<AUDIT_RECORD\n"
524 " NAME=\"%s\"\n"
525 " RECORD=\"%s\"\n"
526 " TIMESTAMP=\"%s\"\n"
527 " MYSQL_VERSION=\"%s\"\n"
528 " STARTUP_OPTIONS=\"%s\"\n"
529 " OS_VERSION=\""MACHINE_TYPE"-"SYSTEM_TYPE"\"\n"
530 "/>\n",
531
532 "<AUDIT_RECORD>\n"
533 " <NAME>%s</NAME>\n"
534 " <RECORD>%s</RECORD>\n"
535 " <TIMESTAMP>%s</TIMESTAMP>\n"
536 " <MYSQL_VERSION>%s</MYSQL_VERSION>\n"
537 " <STARTUP_OPTIONS>%s</STARTUP_OPTIONS>\n"
538 " <OS_VERSION>"MACHINE_TYPE"-"SYSTEM_TYPE"</OS_VERSION>\n"
539 "</AUDIT_RECORD>\n",
540
541 "{\"audit_record\":{\"name\":\"%s\",\"record\":\"%s\","
542 "\"timestamp\":\"%s\",\"mysql_version\":\"%s\","
543 "\"startup_optionsi\":\"%s\","
544 "\"os_version\":\""MACHINE_TYPE"-"SYSTEM_TYPE"\"}}\n",
545
546 "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\","
547 "\""MACHINE_TYPE"-"SYSTEM_TYPE"\"\n" };
548
549 *outlen= snprintf(buf, buflen,
550 format_string[audit_log_format],
551 name,
552 make_record_id(id_str, sizeof(id_str)),
553 make_timestamp(timestamp, sizeof(timestamp), t),
554 server_version,
555 make_argv(arg_buf, sizeof(arg_buf),
556 orig_argc - 1, orig_argv + 1));
557
558 /* make sure that record is not truncated */
559 assert(buf + *outlen <= buf + buflen);
560
561 return buf;
562 }
563
564
565 static
audit_log_general_record(char * buf,size_t buflen,const char * name,time_t t,int status,const struct mysql_event_general * event,const char * default_db,size_t * outlen)566 char *audit_log_general_record(char *buf, size_t buflen,
567 const char *name, time_t t, int status,
568 const struct mysql_event_general *event,
569 const char *default_db,
570 size_t *outlen)
571 {
572 char id_str[MAX_RECORD_ID_SIZE];
573 char timestamp[MAX_TIMESTAMP_SIZE];
574 char *query, *user, *host, *external_user, *ip, *db;
575 char *endptr= buf, *endbuf= buf + buflen;
576 size_t full_outlen= 0, buflen_estimated;
577 size_t query_length;
578
579 const char *format_string[] = {
580 "<AUDIT_RECORD\n"
581 " NAME=\"%s\"\n"
582 " RECORD=\"%s\"\n"
583 " TIMESTAMP=\"%s\"\n"
584 " COMMAND_CLASS=\"%s\"\n"
585 " CONNECTION_ID=\"%lu\"\n"
586 " STATUS=\"%d\"\n"
587 " SQLTEXT=\"%s\"\n"
588 " USER=\"%s\"\n"
589 " HOST=\"%s\"\n"
590 " OS_USER=\"%s\"\n"
591 " IP=\"%s\"\n"
592 " DB=\"%s\"\n"
593 "/>\n",
594
595 "<AUDIT_RECORD>\n"
596 " <NAME>%s</NAME>\n"
597 " <RECORD>%s</RECORD>\n"
598 " <TIMESTAMP>%s</TIMESTAMP>\n"
599 " <COMMAND_CLASS>%s</COMMAND_CLASS>\n"
600 " <CONNECTION_ID>%lu</CONNECTION_ID>\n"
601 " <STATUS>%d</STATUS>\n"
602 " <SQLTEXT>%s</SQLTEXT>\n"
603 " <USER>%s</USER>\n"
604 " <HOST>%s</HOST>\n"
605 " <OS_USER>%s</OS_USER>\n"
606 " <IP>%s</IP>\n"
607 " <DB>%s</DB>\n"
608 "</AUDIT_RECORD>\n",
609
610 "{\"audit_record\":"
611 "{\"name\":\"%s\","
612 "\"record\":\"%s\","
613 "\"timestamp\":\"%s\","
614 "\"command_class\":\"%s\","
615 "\"connection_id\":\"%lu\","
616 "\"status\":%d,"
617 "\"sqltext\":\"%s\","
618 "\"user\":\"%s\","
619 "\"host\":\"%s\","
620 "\"os_user\":\"%s\","
621 "\"ip\":\"%s\","
622 "\"db\":\"%s\"}}\n",
623
624 ("\"%s\",\"%s\",\"%s\",\"%s\",\"%lu\",%d,\"%s\",\"%s\","
625 "\"%s\",\"%s\",\"%s\",\"%s\"\n") };
626
627 query_length= my_charset_utf8mb4_general_ci.mbmaxlen *
628 event->general_query.length;
629
630 /* Note: query_length is the maximun size using utf8m4(4 bytes) that
631 * event->general_query_length may use. In the if branch, we convert it to
632 * utf8mb4. We store the recalculated (real size) length to query variable
633 * and use the remaing of the buffer for the output that will be printed to
634 * audit log. Parameter char *buf must be big enough to store
635 * the query (using utf8mb4) + the full output of audit event, which will
636 * contain the query again. At the else branch we estime this size.
637 */
638 if (query_length < (size_t) (endbuf - endptr))
639 {
640 uint errors;
641 query_length= my_convert(endptr, query_length,
642 &my_charset_utf8mb4_general_ci,
643 event->general_query.str,
644 event->general_query.length,
645 event->general_charset, &errors);
646 query= endptr;
647 endptr+= query_length;
648
649 full_outlen+= query_length;
650
651 query= escape_string(query, query_length, endptr, endbuf - endptr,
652 &endptr, &full_outlen);
653 }
654 else
655 {
656 endptr= endbuf;
657 query= escape_string(event->general_query.str, event->general_query.length,
658 endptr, endbuf - endptr, &endptr, &full_outlen);
659 full_outlen*= my_charset_utf8mb4_general_ci.mbmaxlen;
660 full_outlen+= query_length;
661 }
662
663 user= escape_string(event->general_user.str, event->general_user.length,
664 endptr, endbuf - endptr, &endptr, &full_outlen);
665 host= escape_string(event->general_host.str, event->general_host.length,
666 endptr, endbuf - endptr, &endptr, &full_outlen);
667 external_user= escape_string(event->general_external_user.str,
668 event->general_external_user.length,
669 endptr, endbuf - endptr, &endptr, &full_outlen);
670 ip= escape_string(event->general_ip.str, event->general_ip.length,
671 endptr, endbuf - endptr, &endptr, &full_outlen);
672 db= escape_string(default_db, strlen(default_db),
673 endptr, endbuf - endptr, &endptr, &full_outlen);
674
675 buflen_estimated= full_outlen + strlen(format_string[audit_log_format]) +
676 strlen(name) + event->general_sql_command.length +
677 20 + /* general_thread_id */
678 20 + /* status */
679 MAX_RECORD_ID_SIZE + MAX_TIMESTAMP_SIZE;
680 if (buflen_estimated > buflen)
681 {
682 *outlen= buflen_estimated;
683 return NULL;
684 }
685
686 *outlen= snprintf(endptr, endbuf - endptr,
687 format_string[audit_log_format],
688 name,
689 make_record_id(id_str, sizeof(id_str)),
690 make_timestamp(timestamp, sizeof(timestamp), t),
691 event->general_sql_command.str,
692 event->general_thread_id,
693 status, query, user, host, external_user, ip, db);
694
695 /* make sure that record is not truncated */
696 assert(endptr + *outlen <= buf + buflen);
697
698 return endptr;
699 }
700
701 static
audit_log_connection_record(char * buf,size_t buflen,const char * name,time_t t,const struct mysql_event_connection * event,size_t * outlen)702 char *audit_log_connection_record(char *buf, size_t buflen,
703 const char *name, time_t t,
704 const struct mysql_event_connection *event,
705 size_t *outlen)
706 {
707 char id_str[MAX_RECORD_ID_SIZE];
708 char timestamp[MAX_TIMESTAMP_SIZE];
709 char *user, *priv_user, *external_user, *proxy_user, *host, *ip, *database;
710 char *endptr= buf, *endbuf= buf + buflen;
711
712 const char *format_string[] = {
713 "<AUDIT_RECORD\n"
714 " NAME=\"%s\"\n"
715 " RECORD=\"%s\"\n"
716 " TIMESTAMP=\"%s\"\n"
717 " CONNECTION_ID=\"%lu\"\n"
718 " STATUS=\"%d\"\n"
719 " USER=\"%s\"\n"
720 " PRIV_USER=\"%s\"\n"
721 " OS_LOGIN=\"%s\"\n"
722 " PROXY_USER=\"%s\"\n"
723 " HOST=\"%s\"\n"
724 " IP=\"%s\"\n"
725 " DB=\"%s\"\n"
726 "/>\n",
727
728 "<AUDIT_RECORD>\n"
729 " <NAME>%s</NAME>\n"
730 " <RECORD>%s</RECORD>\n"
731 " <TIMESTAMP>%s</TIMESTAMP>\n"
732 " <CONNECTION_ID>%lu</CONNECTION_ID>\n"
733 " <STATUS>%d</STATUS>\n"
734 " <USER>%s</USER>\n"
735 " <PRIV_USER>%s</PRIV_USER>\n"
736 " <OS_LOGIN>%s</OS_LOGIN>\n"
737 " <PROXY_USER>%s</PROXY_USER>\n"
738 " <HOST>%s</HOST>\n"
739 " <IP>%s</IP>\n"
740 " <DB>%s</DB>\n"
741 "</AUDIT_RECORD>\n",
742
743 "{\"audit_record\":"
744 "{\"name\":\"%s\","
745 "\"record\":\"%s\","
746 "\"timestamp\":\"%s\","
747 "\"connection_id\":\"%lu\","
748 "\"status\":%d,"
749 "\"user\":\"%s\","
750 "\"priv_user\":\"%s\","
751 "\"os_login\":\"%s\","
752 "\"proxy_user\":\"%s\","
753 "\"host\":\"%s\","
754 "\"ip\":\"%s\","
755 "\"db\":\"%s\"}}\n",
756
757 ("\"%s\",\"%s\",\"%s\",\"%lu\",%d,\"%s\",\"%s\",\"%s\","
758 "\"%s\",\"%s\",\"%s\",\"%s\"\n") };
759
760 user= escape_string(event->user.str, event->user.length,
761 endptr, endbuf - endptr, &endptr, NULL);
762 priv_user= escape_string(event->priv_user.str,
763 event->priv_user.length,
764 endptr, endbuf - endptr, &endptr, NULL);
765 external_user= escape_string(event->external_user.str,
766 event->external_user.length,
767 endptr, endbuf - endptr, &endptr, NULL);
768 proxy_user= escape_string(event->proxy_user.str, event->proxy_user.length,
769 endptr, endbuf - endptr, &endptr, NULL);
770 host= escape_string(event->host.str, event->host.length,
771 endptr, endbuf - endptr, &endptr, NULL);
772 ip= escape_string(event->ip.str, event->ip.length,
773 endptr, endbuf - endptr, &endptr, NULL);
774 database= escape_string(event->database.str, event->database.length,
775 endptr, endbuf - endptr, &endptr, NULL);
776
777 assert((endptr - buf) * 2 +
778 strlen(format_string[audit_log_format]) +
779 strlen(name) +
780 MAX_RECORD_ID_SIZE +
781 MAX_TIMESTAMP_SIZE +
782 20 + /* event->thread_id */
783 20 /* event->status */
784 < buflen);
785
786 *outlen= snprintf(endptr, endbuf - endptr,
787 format_string[audit_log_format],
788 name,
789 make_record_id(id_str, sizeof(id_str)),
790 make_timestamp(timestamp, sizeof(timestamp), t),
791 event->connection_id,
792 event->status, user, priv_user,external_user,
793 proxy_user, host, ip, database);
794
795 /* make sure that record is not truncated */
796 assert(endptr + *outlen <= buf + buflen);
797
798 return endptr;
799 }
800
801 static
audit_log_header(MY_STAT * stat,char * buf,size_t buflen)802 size_t audit_log_header(MY_STAT *stat, char *buf, size_t buflen)
803 {
804 const char *format_string[] = {
805 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
806 "<AUDIT>\n",
807 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
808 "<AUDIT>\n",
809 "",
810 "" };
811
812 assert(strcmp(system_charset_info->csname, "utf8") == 0);
813
814 log_file_time= stat->st_mtime;
815
816 init_record_id(stat->st_size);
817
818 if (buf == NULL)
819 {
820 return 0;
821 }
822
823 return my_snprintf(buf, buflen, format_string[audit_log_format]);
824 }
825
826
827 static
audit_log_footer(char * buf,size_t buflen)828 size_t audit_log_footer(char *buf, size_t buflen)
829 {
830 const char *format_string[] = {
831 "</AUDIT>\n",
832 "</AUDIT>\n",
833 "",
834 "" };
835
836 if (buf == NULL)
837 {
838 return 0;
839 }
840
841 return my_snprintf(buf, buflen, format_string[audit_log_format]);
842 }
843
844 static
init_new_log_file()845 int init_new_log_file()
846 {
847 if (audit_log_handler == HANDLER_FILE)
848 {
849 audit_handler_file_config_t opts;
850 opts.name= audit_log_file;
851 opts.rotate_on_size= audit_log_rotate_on_size;
852 opts.rotations= audit_log_rotations;
853 opts.sync_on_write= audit_log_strategy == SYNCHRONOUS;
854 opts.use_buffer= audit_log_strategy < SEMISYNCHRONOUS;
855 opts.buffer_size= audit_log_buffer_size;
856 opts.can_drop_data= audit_log_strategy == PERFORMANCE;
857 opts.header= audit_log_header;
858 opts.footer= audit_log_footer;
859
860 log_handler= audit_handler_file_open(&opts);
861 if (log_handler == NULL)
862 {
863 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL,
864 "Cannot open file %s.", audit_log_file);
865 my_plugin_perror();
866 return(1);
867 }
868 }
869 else
870 {
871 audit_handler_syslog_config_t opts;
872 opts.facility= audit_log_syslog_facility_codes[audit_log_syslog_facility];
873 opts.ident= audit_log_syslog_ident;
874 opts.priority= audit_log_syslog_priority_codes[audit_log_syslog_priority];
875 opts.header= audit_log_header;
876 opts.footer= audit_log_footer;
877
878 log_handler= audit_handler_syslog_open(&opts);
879 if (log_handler == NULL)
880 {
881 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL,
882 "Cannot open syslog.");
883 my_plugin_perror();
884 return(1);
885 }
886 }
887
888 return(0);
889 }
890
891
892 static
reopen_log_file()893 int reopen_log_file()
894 {
895 if (audit_handler_flush(log_handler))
896 {
897 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL, "Cannot open file %s.",
898 audit_log_file);
899 my_plugin_perror();
900 return(1);
901 }
902
903 return(0);
904 }
905
906 typedef struct
907 {
908 /* number of included databases */
909 int databases_included;
910 /* number of excluded databases */
911 int databases_excluded;
912 /* number of accessed databases */
913 int databases_accessed;
914 /* query */
915 const char *query;
916 } query_stack_frame;
917
918 typedef struct
919 {
920 size_t size;
921 size_t top;
922 query_stack_frame *frames;
923 } query_stack;
924
925 /*
926 Struct to store various THD specific data
927 */
928 typedef struct
929 {
930 /* size of allocated large buffer for record formatting */
931 size_t record_buffer_size;
932 /* large buffer for record formatting */
933 char *record_buffer;
934 /* skip session logging */
935 my_bool skip_session;
936 /* skip logging for the next query */
937 my_bool skip_query;
938 /* default database */
939 char db[NAME_LEN + 1];
940 /* default database candidate */
941 char init_db_query[NAME_LEN + 1];
942 /* call stack */
943 query_stack stack;
944 } audit_log_thd_local;
945
946 /*
947 Return pointer to THD specific data.
948 */
949 static
950 audit_log_thd_local *get_thd_local(MYSQL_THD thd);
951
952 /*
953 Allocate and return buffer of given size.
954 */
955 static
956 char *get_record_buffer(MYSQL_THD thd, size_t size);
957
958 /*
959 Allocate and return given number of stack frames.
960 */
961 static
962 query_stack_frame *realloc_stack_frames(MYSQL_THD thd, size_t size);
963
964
965 static
audit_log_plugin_init(MYSQL_PLUGIN plugin_info)966 int audit_log_plugin_init(MYSQL_PLUGIN plugin_info)
967 {
968 char buf[1024];
969 size_t len;
970 int count;
971
972 plugin_ptr= plugin_info;
973
974 count= array_elements(all_audit_log_memory);
975 mysql_memory_register(AUDIT_LOG_PSI_CATEGORY, all_audit_log_memory, count);
976 logger_init_mutexes();
977
978 audit_log_filter_init();
979
980 if (audit_log_exclude_accounts != NULL && audit_log_include_accounts != NULL)
981 {
982 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL,
983 "Both 'audit_log_exclude_accounts' and "
984 "'audit_log_include_accounts' are not NULL\n");
985 goto validation_error;
986 }
987
988 if (audit_log_exclude_commands != NULL && audit_log_include_commands != NULL)
989 {
990 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL,
991 "Both 'audit_log_exclude_commands' and "
992 "'audit_log_include_commands' are not NULL\n");
993 goto validation_error;
994 }
995
996 if (audit_log_exclude_databases != NULL
997 && audit_log_include_databases != NULL)
998 {
999 my_plugin_log_message(&plugin_ptr, MY_ERROR_LEVEL,
1000 "Both 'audit_log_exclude_databases' and "
1001 "'audit_log_include_databases' are not NULL\n");
1002 goto validation_error;
1003 }
1004
1005 if (audit_log_exclude_accounts != NULL)
1006 {
1007 audit_log_exclude_accounts= my_strdup(PSI_NOT_INSTRUMENTED,
1008 audit_log_exclude_accounts,
1009 MYF(MY_FAE));
1010 audit_log_set_exclude_accounts(audit_log_exclude_accounts);
1011 }
1012 if (audit_log_include_accounts != NULL)
1013 {
1014 audit_log_include_accounts= my_strdup(PSI_NOT_INSTRUMENTED,
1015 audit_log_include_accounts,
1016 MYF(MY_FAE));
1017 audit_log_set_include_accounts(audit_log_include_accounts);
1018 }
1019 if (audit_log_exclude_commands != NULL)
1020 {
1021 audit_log_exclude_commands= my_strdup(PSI_NOT_INSTRUMENTED,
1022 audit_log_exclude_commands,
1023 MYF(MY_FAE));
1024 audit_log_set_exclude_commands(audit_log_exclude_commands);
1025 }
1026 if (audit_log_include_commands != NULL)
1027 {
1028 audit_log_include_commands= my_strdup(PSI_NOT_INSTRUMENTED,
1029 audit_log_include_commands,
1030 MYF(MY_FAE));
1031 audit_log_set_include_commands(audit_log_include_commands);
1032 }
1033 if (audit_log_exclude_databases != NULL)
1034 {
1035 audit_log_exclude_databases= my_strdup(PSI_NOT_INSTRUMENTED,
1036 audit_log_exclude_databases,
1037 MYF(MY_FAE));
1038 audit_log_set_exclude_databases(audit_log_exclude_databases);
1039 }
1040 if (audit_log_include_databases != NULL)
1041 {
1042 audit_log_include_databases= my_strdup(PSI_NOT_INSTRUMENTED,
1043 audit_log_include_databases,
1044 MYF(MY_FAE));
1045 audit_log_set_include_databases(audit_log_include_databases);
1046 }
1047
1048 if (init_new_log_file())
1049 return(1);
1050
1051 if (audit_log_audit_record(buf, sizeof(buf), "Audit", time(NULL), &len))
1052 audit_log_write(buf, len);
1053
1054 return 0;
1055
1056 validation_error:
1057
1058 audit_log_exclude_accounts= audit_log_include_accounts= NULL;
1059 audit_log_exclude_commands= audit_log_include_commands= NULL;
1060 audit_log_exclude_databases= audit_log_include_databases= NULL;
1061
1062 return 1;
1063 }
1064
1065
1066 static
audit_log_plugin_deinit(void * arg MY_ATTRIBUTE ((unused)))1067 int audit_log_plugin_deinit(void *arg MY_ATTRIBUTE((unused)))
1068 {
1069 char buf[1024];
1070 size_t len;
1071
1072 if (audit_log_audit_record(buf, sizeof(buf), "NoAudit", time(NULL), &len))
1073 audit_log_write(buf, len);
1074
1075 audit_handler_close(log_handler);
1076
1077 audit_log_filter_destroy();
1078
1079 my_free(audit_log_include_accounts);
1080 my_free(audit_log_exclude_accounts);
1081
1082 my_free(audit_log_include_databases);
1083 my_free(audit_log_exclude_databases);
1084
1085 my_free(audit_log_include_commands);
1086 my_free(audit_log_exclude_commands);
1087
1088 return(0);
1089 }
1090
1091
1092 static
is_event_class_allowed_by_policy(mysql_event_class_t class,enum audit_log_policy_t policy)1093 int is_event_class_allowed_by_policy(mysql_event_class_t class,
1094 enum audit_log_policy_t policy)
1095 {
1096 static unsigned int class_mask[]=
1097 {
1098 /* ALL */
1099 (1 << MYSQL_AUDIT_GENERAL_CLASS) | (1 << MYSQL_AUDIT_CONNECTION_CLASS),
1100 0, /* NONE */
1101 (1 << MYSQL_AUDIT_CONNECTION_CLASS), /* LOGINS */
1102 (1 << MYSQL_AUDIT_GENERAL_CLASS), /* QUERIES */
1103 };
1104
1105 return (class_mask[policy] & (1 << class)) != 0;
1106 }
1107
1108 static
next_word(const char * str,size_t * len,const struct charset_info_st * charset)1109 const char *next_word(const char *str, size_t *len,
1110 const struct charset_info_st *charset)
1111 {
1112 while (*str && my_isspace(charset, *str))
1113 {
1114 if (*str == '/' && str[1] == '*' && str[2] == '!')
1115 str+= 3;
1116 else if (*str == '/' && str[1] == '*')
1117 {
1118 while (*str && !(*str == '*' && str[1] == '/'))
1119 str++;
1120 }
1121 else
1122 str++;
1123 }
1124
1125 *len= 0;
1126 while (str[*len] && my_isvar(charset, str[*len]))
1127 (*len)++;
1128
1129 if (*len == 0 && *str == '`')
1130 {
1131 (*len)++;
1132 while (str[*len])
1133 {
1134 if (str[*len] == '`' && str[*len + 1] == '`')
1135 (*len)++;
1136 else if (str[*len] == '`')
1137 break;
1138 (*len)++;
1139 }
1140 (*len)++;
1141 }
1142
1143 return str;
1144 }
1145
1146
1147 static
audit_log_update_thd_local(MYSQL_THD thd,audit_log_thd_local * local,unsigned int event_class,const void * event)1148 my_bool audit_log_update_thd_local(MYSQL_THD thd,
1149 audit_log_thd_local *local,
1150 unsigned int event_class,
1151 const void *event)
1152 {
1153 assert(audit_log_include_accounts == NULL ||
1154 audit_log_exclude_accounts == NULL);
1155
1156 assert(audit_log_include_databases == NULL ||
1157 audit_log_exclude_databases == NULL);
1158
1159 assert(audit_log_include_commands == NULL ||
1160 audit_log_exclude_commands == NULL);
1161
1162 if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
1163 {
1164 const struct mysql_event_connection *event_connection=
1165 (const struct mysql_event_connection *) event;
1166 LEX_STRING priv_user, priv_host;
1167 MYSQL_SECURITY_CONTEXT ctx;
1168
1169 if (thd_get_security_context(thd, &ctx))
1170 {
1171 my_message(ER_AUDIT_API_ABORT, "Error: can not get security context",
1172 MYF(0));
1173 return FALSE;
1174 }
1175
1176 if (security_context_get_option(ctx, "priv_user", &priv_user))
1177 {
1178 my_message(ER_AUDIT_API_ABORT, "Error: can not get priv_user from "
1179 "security context", MYF(0));
1180 return FALSE;
1181 }
1182
1183 if (security_context_get_option(ctx, "priv_host", &priv_host))
1184 {
1185 my_message(ER_AUDIT_API_ABORT, "Error: can not get priv_host from "
1186 "security context", MYF(0));
1187 return FALSE;
1188 }
1189
1190 local->skip_session= FALSE;
1191 if (audit_log_include_accounts != NULL &&
1192 !audit_log_check_account_included(priv_user.str, priv_user.length,
1193 priv_host.str, priv_host.length))
1194 local->skip_session= TRUE;
1195 if (audit_log_exclude_accounts != NULL &&
1196 audit_log_check_account_excluded(priv_user.str, priv_user.length,
1197 priv_host.str, priv_host.length))
1198 local->skip_session= TRUE;
1199
1200 if (event_connection->status == 0)
1201 {
1202 /* track default DB change */
1203 assert(event_connection->database.length <= sizeof(local->db));
1204 memcpy(local->db, event_connection->database.str,
1205 event_connection->database.length);
1206 local->db[event_connection->database.length]= 0;
1207 }
1208 }
1209 else if (event_class == MYSQL_AUDIT_GENERAL_CLASS)
1210 {
1211 const struct mysql_event_general *event_general=
1212 (const struct mysql_event_general *) event;
1213
1214 if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_STATUS)
1215 {
1216 local->skip_query= FALSE;
1217
1218 if (local->stack.frames[local->stack.top].query
1219 == event_general->general_query.str)
1220 {
1221 local->skip_query|= audit_log_include_databases
1222 && local->stack.frames[local->stack.top].databases_accessed > 0
1223 && local->stack.frames[local->stack.top].databases_included == 0;
1224
1225 local->skip_query|= audit_log_exclude_databases
1226 && local->stack.frames[local->stack.top].databases_accessed > 0
1227 && local->stack.frames[local->stack.top].databases_excluded
1228 == local->stack.frames[local->stack.top].databases_accessed;
1229
1230 local->stack.frames[local->stack.top].databases_included= 0;
1231 local->stack.frames[local->stack.top].databases_accessed= 0;
1232 local->stack.frames[local->stack.top].databases_excluded= 0;
1233 local->stack.frames[local->stack.top].query= NULL;
1234
1235 if (local->stack.top > 0)
1236 --local->stack.top;
1237 }
1238
1239 local->skip_query|= audit_log_include_commands
1240 && !audit_log_check_command_included(
1241 event_general->general_sql_command.str,
1242 event_general->general_sql_command.length);
1243
1244 local->skip_query|= audit_log_exclude_commands
1245 && audit_log_check_command_excluded(
1246 event_general->general_sql_command.str,
1247 event_general->general_sql_command.length);
1248
1249 if (!local->skip_query &&
1250 ((event_general->general_command.length == 4 &&
1251 strncmp(event_general->general_command.str, "Quit", 4) == 0) ||
1252 (event_general->general_command.length == 11 &&
1253 strncmp(event_general->general_command.str,
1254 "Change user", 11) == 0)))
1255 local->skip_query= TRUE;
1256 }
1257
1258 if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_LOG &&
1259 event_general->general_command.length == 7 &&
1260 strncmp(event_general->general_command.str, "Init DB", 7) == 0 &&
1261 event_general->general_query.str != NULL &&
1262 strpbrk("\n\r\t ", event_general->general_query.str) == NULL)
1263 {
1264 /* Database is about to be changed. Server doesn't provide database
1265 name in STATUS event, so remember it now. */
1266
1267 assert(event_general->general_query.length <= sizeof(local->db));
1268 memcpy(local->db, event_general->general_query.str,
1269 event_general->general_query.length);
1270 local->db[event_general->general_query.length]= 0;
1271 }
1272 if (event_general->event_subclass == MYSQL_AUDIT_GENERAL_STATUS &&
1273 event_general->general_sql_command.length == 9 &&
1274 strncmp(event_general->general_sql_command.str, "change_db", 9) == 0 &&
1275 event_general->general_command.length == 5 &&
1276 strncmp(event_general->general_command.str, "Query", 5) == 0 &&
1277 event_general->general_error_code == 0)
1278 {
1279 /* it's "use dbname" query */
1280
1281 size_t len;
1282 const char *word;
1283
1284 word= next_word(event_general->general_query.str, &len,
1285 event_general->general_charset);
1286 if (strncasecmp("use", word, len) == 0)
1287 {
1288 uint errors;
1289
1290 word= next_word(word + len, &len, event_general->general_charset);
1291 if (*word == '`')
1292 {
1293 word++;
1294 len-= 2;
1295 }
1296 len= my_convert(local->db, sizeof(local->db) - 1, system_charset_info,
1297 word, len, event_general->general_charset, &errors);
1298 local->db[len]= 0;
1299 }
1300 }
1301 }
1302 else if (event_class == MYSQL_AUDIT_TABLE_ACCESS_CLASS)
1303 {
1304 const struct mysql_event_table_access *event_table=
1305 (const struct mysql_event_table_access *) event;
1306
1307 if (local->stack.frames[local->stack.top].query != event_table->query.str
1308 && local->stack.frames[local->stack.top].query != NULL)
1309 {
1310 if (++local->stack.top >= local->stack.size)
1311 realloc_stack_frames(thd, local->stack.size * 2);
1312 }
1313 local->stack.frames[local->stack.top].query= event_table->query.str;
1314
1315 ++local->stack.frames[local->stack.top].databases_accessed;
1316
1317 if (audit_log_include_databases != NULL &&
1318 audit_log_check_database_included(event_table->table_database.str,
1319 event_table->table_database.length))
1320 ++local->stack.frames[local->stack.top].databases_included;
1321
1322 if (audit_log_exclude_databases != NULL &&
1323 audit_log_check_database_excluded(event_table->table_database.str,
1324 event_table->table_database.length))
1325 ++local->stack.frames[local->stack.top].databases_excluded;
1326 }
1327 return TRUE;
1328 }
1329
1330
1331 static
audit_log_notify(MYSQL_THD thd MY_ATTRIBUTE ((unused)),mysql_event_class_t event_class,const void * event)1332 int audit_log_notify(MYSQL_THD thd MY_ATTRIBUTE((unused)),
1333 mysql_event_class_t event_class,
1334 const void *event)
1335 {
1336 char buf[4096];
1337 char *log_rec = NULL;
1338 char *allocated_buf= get_record_buffer(thd, 0);
1339 size_t len, buflen;
1340 audit_log_thd_local *local= get_thd_local(thd);
1341
1342 if (!audit_log_update_thd_local(thd, local, event_class, event))
1343 return 1;
1344
1345 if (!is_event_class_allowed_by_policy(event_class, audit_log_policy))
1346 return 0;
1347
1348 if (local->skip_session)
1349 return 0;
1350
1351 if (event_class == MYSQL_AUDIT_GENERAL_CLASS)
1352 {
1353 const struct mysql_event_general *event_general=
1354 (const struct mysql_event_general *) event;
1355 switch (event_general->event_subclass)
1356 {
1357 case MYSQL_AUDIT_GENERAL_STATUS:
1358 if (local->skip_query)
1359 break;
1360
1361 /* use allocated buffer if available */
1362 if (allocated_buf != NULL)
1363 {
1364 log_rec= allocated_buf;
1365 buflen= local->record_buffer_size;
1366 }
1367 else
1368 {
1369 log_rec= buf;
1370 buflen= sizeof(buf);
1371 }
1372 log_rec= audit_log_general_record(log_rec, buflen,
1373 event_general->general_command.str,
1374 event_general->general_time,
1375 event_general->general_error_code,
1376 event_general, local->db,
1377 &len);
1378 if (len > buflen)
1379 {
1380 buflen= len + 1024;
1381 log_rec= audit_log_general_record(get_record_buffer(thd, buflen),
1382 buflen,
1383 event_general->general_command.str,
1384 event_general->general_time,
1385 event_general->general_error_code,
1386 event_general, local->db,
1387 &len);
1388 assert(log_rec);
1389 }
1390 if (log_rec)
1391 audit_log_write(log_rec, len);
1392 break;
1393 case MYSQL_AUDIT_GENERAL_LOG:
1394 case MYSQL_AUDIT_GENERAL_ERROR:
1395 case MYSQL_AUDIT_GENERAL_RESULT:
1396 break;
1397 }
1398 }
1399 else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
1400 {
1401 const struct mysql_event_connection *event_connection=
1402 (const struct mysql_event_connection *) event;
1403 switch (event_connection->event_subclass)
1404 {
1405 case MYSQL_AUDIT_CONNECTION_CONNECT:
1406 log_rec= audit_log_connection_record(buf, sizeof(buf), "Connect",
1407 time(NULL), event_connection, &len);
1408 break;
1409 case MYSQL_AUDIT_CONNECTION_DISCONNECT:
1410 log_rec= audit_log_connection_record(buf, sizeof(buf), "Quit",
1411 time(NULL), event_connection, &len);
1412 break;
1413 case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
1414 log_rec= audit_log_connection_record(buf, sizeof(buf), "Change user",
1415 time(NULL), event_connection, &len);
1416 break;
1417 default:
1418 break;
1419 }
1420 if (log_rec)
1421 audit_log_write(log_rec, len);
1422 }
1423 return 0;
1424 }
1425
1426
1427 /*
1428 * Plugin system vars
1429 */
1430
1431 static MYSQL_SYSVAR_STR(file, audit_log_file,
1432 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC,
1433 "The name of the log file.", NULL, NULL, default_audit_log_file);
1434
1435 static const char *audit_log_policy_names[]=
1436 { "ALL", "NONE", "LOGINS", "QUERIES", 0 };
1437
1438 static TYPELIB audit_log_policy_typelib=
1439 {
1440 array_elements(audit_log_policy_names) - 1, "audit_log_policy_typelib",
1441 audit_log_policy_names, NULL
1442 };
1443
1444 static MYSQL_SYSVAR_ENUM(policy, audit_log_policy, PLUGIN_VAR_RQCMDARG,
1445 "The policy controlling the information written by the audit log "
1446 "plugin to its log file.", NULL, NULL, ALL,
1447 &audit_log_policy_typelib);
1448
1449 static const char *audit_log_strategy_names[]=
1450 { "ASYNCHRONOUS", "PERFORMANCE", "SEMISYNCHRONOUS", "SYNCHRONOUS", 0 };
1451 static TYPELIB audit_log_strategy_typelib=
1452 {
1453 array_elements(audit_log_strategy_names) - 1, "audit_log_strategy_typelib",
1454 audit_log_strategy_names, NULL
1455 };
1456
1457 static MYSQL_SYSVAR_ENUM(strategy, audit_log_strategy,
1458 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
1459 "The logging method used by the audit log plugin, "
1460 "if FILE handler is used.", NULL, NULL,
1461 ASYNCHRONOUS, &audit_log_strategy_typelib);
1462
1463 static const char *audit_log_format_names[]=
1464 { "OLD", "NEW", "JSON", "CSV", 0 };
1465 static TYPELIB audit_log_format_typelib=
1466 {
1467 array_elements(audit_log_format_names) - 1, "audit_log_format_typelib",
1468 audit_log_format_names, NULL
1469 };
1470
1471 static MYSQL_SYSVAR_ENUM(format, audit_log_format,
1472 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
1473 "The audit log file format.", NULL, NULL,
1474 ASYNCHRONOUS, &audit_log_format_typelib);
1475
1476 static const char *audit_log_handler_names[]=
1477 { "FILE", "SYSLOG", 0 };
1478 static TYPELIB audit_log_handler_typelib=
1479 {
1480 array_elements(audit_log_handler_names) - 1, "audit_log_handler_typelib",
1481 audit_log_handler_names, NULL
1482 };
1483
1484 static MYSQL_SYSVAR_ENUM(handler, audit_log_handler,
1485 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
1486 "The audit log handler.", NULL, NULL,
1487 HANDLER_FILE, &audit_log_handler_typelib);
1488
1489 static MYSQL_SYSVAR_ULONGLONG(buffer_size, audit_log_buffer_size,
1490 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
1491 "The size of the buffer for asynchronous logging, "
1492 "if FILE handler is used.",
1493 NULL, NULL, 1048576UL, 4096UL, ULLONG_MAX, 4096UL);
1494
1495 static
audit_log_rotate_on_size_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1496 void audit_log_rotate_on_size_update(
1497 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1498 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1499 void *var_ptr MY_ATTRIBUTE((unused)),
1500 const void *save)
1501 {
1502 ulonglong new_val= *(ulonglong *)(save);
1503
1504 audit_handler_set_option(log_handler, OPT_ROTATE_ON_SIZE, &new_val);
1505
1506 audit_log_rotate_on_size= new_val;
1507 }
1508
1509 static MYSQL_SYSVAR_ULONGLONG(rotate_on_size, audit_log_rotate_on_size,
1510 PLUGIN_VAR_RQCMDARG,
1511 "Maximum size of the log to start the rotation, if FILE handler is used.",
1512 NULL, audit_log_rotate_on_size_update, 0UL, 0UL, ULLONG_MAX, 4096UL);
1513
1514 static
audit_log_rotations_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1515 void audit_log_rotations_update(
1516 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1517 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1518 void *var_ptr MY_ATTRIBUTE((unused)),
1519 const void *save)
1520 {
1521 ulonglong new_val= *(ulonglong *)(save);
1522
1523 audit_handler_set_option(log_handler, OPT_ROTATIONS, &new_val);
1524
1525 audit_log_rotations= new_val;
1526 }
1527
1528 static MYSQL_SYSVAR_ULONGLONG(rotations, audit_log_rotations,
1529 PLUGIN_VAR_RQCMDARG,
1530 "Maximum number of rotations to keep, if FILE handler is used.",
1531 NULL, audit_log_rotations_update, 0UL, 0UL, 999UL, 1UL);
1532
1533 static
audit_log_flush_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1534 void audit_log_flush_update(
1535 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1536 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1537 void *var_ptr MY_ATTRIBUTE((unused)),
1538 const void *save)
1539 {
1540 char new_val= *(const char *)(save);
1541
1542 if (new_val != audit_log_flush && new_val)
1543 {
1544 audit_log_flush= TRUE;
1545 reopen_log_file();
1546 audit_log_flush= FALSE;
1547 }
1548 }
1549
1550 static MYSQL_SYSVAR_BOOL(flush, audit_log_flush,
1551 PLUGIN_VAR_OPCMDARG, "Flush the log file.", NULL,
1552 audit_log_flush_update, 0);
1553
1554 static MYSQL_SYSVAR_STR(syslog_ident, audit_log_syslog_ident,
1555 PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC,
1556 "The string that will be prepended to each log message, "
1557 "if SYSLOG handler is used.",
1558 NULL, NULL, default_audit_log_syslog_ident);
1559
1560 static TYPELIB audit_log_syslog_facility_typelib=
1561 {
1562 array_elements(audit_log_syslog_facility_names) - 1,
1563 "audit_log_syslog_facility_typelib",
1564 audit_log_syslog_facility_names, NULL
1565 };
1566
1567 static MYSQL_SYSVAR_ENUM(syslog_facility, audit_log_syslog_facility,
1568 PLUGIN_VAR_RQCMDARG,
1569 "The syslog facility to assign to messages, if SYSLOG handler is used.",
1570 NULL, NULL, 0,
1571 &audit_log_syslog_facility_typelib);
1572
1573 static TYPELIB audit_log_syslog_priority_typelib=
1574 {
1575 array_elements(audit_log_syslog_priority_names) - 1,
1576 "audit_log_syslog_priority_typelib",
1577 audit_log_syslog_priority_names, NULL
1578 };
1579
1580 static MYSQL_SYSVAR_ENUM(syslog_priority, audit_log_syslog_priority,
1581 PLUGIN_VAR_RQCMDARG,
1582 "Priority to be assigned to all messages written to syslog.",
1583 NULL, NULL, 0,
1584 &audit_log_syslog_priority_typelib);
1585
1586 static MYSQL_THDVAR_STR(record_buffer,
1587 PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | \
1588 PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
1589 "Buffer for query formatting.", NULL, NULL, "");
1590
1591 static MYSQL_THDVAR_STR(query_stack,
1592 PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | \
1593 PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
1594 "Query stack.", NULL, NULL, "");
1595
1596 static
1597 int
audit_log_exclude_accounts_validate(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * save,struct st_mysql_value * value)1598 audit_log_exclude_accounts_validate(
1599 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1600 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1601 void *save,
1602 struct st_mysql_value *value)
1603 {
1604 const char *new_val;
1605 char buf[80];
1606 int len= sizeof(buf);
1607
1608 if (audit_log_include_accounts)
1609 return 1;
1610
1611 new_val = value->val_str(value, buf, &len);
1612
1613 *(const char **)(save) = new_val;
1614
1615 return 0;
1616 }
1617
1618 static
audit_log_exclude_accounts_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1619 void audit_log_exclude_accounts_update(
1620 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1621 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1622 void *var_ptr MY_ATTRIBUTE((unused)),
1623 const void *save)
1624 {
1625 const char *new_val= *(const char **)(save);
1626
1627 assert(audit_log_include_accounts == NULL);
1628
1629 my_free(audit_log_exclude_accounts);
1630 audit_log_exclude_accounts= NULL;
1631
1632 if (new_val != NULL)
1633 {
1634 audit_log_exclude_accounts= my_strdup(PSI_NOT_INSTRUMENTED,
1635 new_val, MYF(MY_FAE));
1636 audit_log_set_exclude_accounts(audit_log_exclude_accounts);
1637 }
1638 else
1639 {
1640 audit_log_set_exclude_accounts("");
1641 }
1642 }
1643
1644 static MYSQL_SYSVAR_STR(exclude_accounts, audit_log_exclude_accounts,
1645 PLUGIN_VAR_RQCMDARG,
1646 "Comma separated list of accounts "
1647 "for which events should not be logged.",
1648 audit_log_exclude_accounts_validate,
1649 audit_log_exclude_accounts_update, NULL);
1650
1651 static
1652 int
audit_log_include_accounts_validate(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * save,struct st_mysql_value * value)1653 audit_log_include_accounts_validate(
1654 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1655 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1656 void *save,
1657 struct st_mysql_value *value)
1658 {
1659 const char *new_val;
1660 char buf[80];
1661 int len= sizeof(buf);
1662
1663 if (audit_log_exclude_accounts)
1664 return 1;
1665
1666 new_val = value->val_str(value, buf, &len);
1667
1668 *(const char **)(save) = new_val;
1669
1670 return 0;
1671 }
1672
1673 static
audit_log_include_accounts_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1674 void audit_log_include_accounts_update(
1675 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1676 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1677 void *var_ptr MY_ATTRIBUTE((unused)),
1678 const void *save)
1679 {
1680 const char *new_val= *(const char **)(save);
1681
1682 assert(audit_log_exclude_accounts == NULL);
1683
1684 my_free(audit_log_include_accounts);
1685 audit_log_include_accounts= NULL;
1686
1687 if (new_val != NULL)
1688 {
1689 audit_log_include_accounts= my_strdup(PSI_NOT_INSTRUMENTED,
1690 new_val, MYF(MY_FAE));
1691 audit_log_set_include_accounts(audit_log_include_accounts);
1692 }
1693 else
1694 {
1695 audit_log_set_include_accounts("");
1696 }
1697 }
1698
1699 static MYSQL_SYSVAR_STR(include_accounts, audit_log_include_accounts,
1700 PLUGIN_VAR_RQCMDARG,
1701 "Comma separated list of accounts for which events should be logged.",
1702 audit_log_include_accounts_validate,
1703 audit_log_include_accounts_update, NULL);
1704
1705 static
1706 int
audit_log_exclude_databases_validate(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * save,struct st_mysql_value * value)1707 audit_log_exclude_databases_validate(
1708 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1709 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1710 void *save,
1711 struct st_mysql_value *value)
1712 {
1713 const char *new_val;
1714 char buf[80];
1715 int len= sizeof(buf);
1716
1717 if (audit_log_include_databases)
1718 return 1;
1719
1720 new_val = value->val_str(value, buf, &len);
1721
1722 *(const char **)(save) = new_val;
1723
1724 return 0;
1725 }
1726
1727 static
audit_log_exclude_databases_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1728 void audit_log_exclude_databases_update(
1729 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1730 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1731 void *var_ptr MY_ATTRIBUTE((unused)),
1732 const void *save)
1733 {
1734 const char *new_val= *(const char **)(save);
1735
1736 assert(audit_log_include_databases == NULL);
1737
1738 my_free(audit_log_exclude_databases);
1739 audit_log_exclude_databases= NULL;
1740
1741 if (new_val != NULL)
1742 {
1743 audit_log_exclude_databases= my_strdup(PSI_NOT_INSTRUMENTED,
1744 new_val, MYF(MY_FAE));
1745 audit_log_set_exclude_databases(audit_log_exclude_databases);
1746 }
1747 else
1748 {
1749 audit_log_set_exclude_databases("");
1750 }
1751 }
1752
1753 static MYSQL_SYSVAR_STR(exclude_databases, audit_log_exclude_databases,
1754 PLUGIN_VAR_RQCMDARG,
1755 "Comma separated list of databases "
1756 "for which events should not be logged.",
1757 audit_log_exclude_databases_validate,
1758 audit_log_exclude_databases_update, NULL);
1759
1760 static
1761 int
audit_log_include_databases_validate(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * save,struct st_mysql_value * value)1762 audit_log_include_databases_validate(
1763 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1764 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1765 void *save,
1766 struct st_mysql_value *value)
1767 {
1768 const char *new_val;
1769 char buf[80];
1770 int len= sizeof(buf);
1771
1772 if (audit_log_exclude_databases)
1773 return 1;
1774
1775 new_val = value->val_str(value, buf, &len);
1776
1777 *(const char **)(save) = new_val;
1778
1779 return 0;
1780 }
1781
1782 static
audit_log_include_databases_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1783 void audit_log_include_databases_update(
1784 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1785 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1786 void *var_ptr MY_ATTRIBUTE((unused)),
1787 const void *save)
1788 {
1789 const char *new_val= *(const char **)(save);
1790
1791 assert(audit_log_exclude_databases == NULL);
1792
1793 my_free(audit_log_include_databases);
1794 audit_log_include_databases= NULL;
1795
1796 if (new_val != NULL)
1797 {
1798 audit_log_include_databases= my_strdup(PSI_NOT_INSTRUMENTED,
1799 new_val, MYF(MY_FAE));
1800 audit_log_set_include_databases(audit_log_include_databases);
1801 }
1802 else
1803 {
1804 audit_log_set_include_databases("");
1805 }
1806 }
1807
1808 static MYSQL_SYSVAR_STR(include_databases, audit_log_include_databases,
1809 PLUGIN_VAR_RQCMDARG,
1810 "Comma separated list of databases for which events should be logged.",
1811 audit_log_include_databases_validate,
1812 audit_log_include_databases_update, NULL);
1813
1814 static
1815 int
audit_log_exclude_commands_validate(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * save,struct st_mysql_value * value)1816 audit_log_exclude_commands_validate(
1817 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1818 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1819 void *save,
1820 struct st_mysql_value *value)
1821 {
1822 const char *new_val;
1823 char buf[80];
1824 int len= sizeof(buf);
1825
1826 if (audit_log_include_commands)
1827 return 1;
1828
1829 new_val = value->val_str(value, buf, &len);
1830
1831 *(const char **)(save) = new_val;
1832
1833 return 0;
1834 }
1835
1836 static
audit_log_exclude_commands_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1837 void audit_log_exclude_commands_update(
1838 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1839 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1840 void *var_ptr MY_ATTRIBUTE((unused)),
1841 const void *save)
1842 {
1843 const char *new_val= *(const char **)(save);
1844
1845 assert(audit_log_include_commands == NULL);
1846
1847 my_free(audit_log_exclude_commands);
1848 audit_log_exclude_commands= NULL;
1849
1850 if (new_val != NULL)
1851 {
1852 audit_log_exclude_commands= my_strdup(PSI_NOT_INSTRUMENTED,
1853 new_val, MYF(MY_FAE));
1854 audit_log_set_exclude_commands(audit_log_exclude_commands);
1855 }
1856 else
1857 {
1858 audit_log_set_exclude_commands("");
1859 }
1860 }
1861
1862 static MYSQL_SYSVAR_STR(exclude_commands, audit_log_exclude_commands,
1863 PLUGIN_VAR_RQCMDARG,
1864 "Comma separated list of commands "
1865 "for which events should not be logged.",
1866 audit_log_exclude_commands_validate,
1867 audit_log_exclude_commands_update, NULL);
1868
1869 static
1870 int
audit_log_include_commands_validate(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * save,struct st_mysql_value * value)1871 audit_log_include_commands_validate(
1872 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1873 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1874 void *save,
1875 struct st_mysql_value *value)
1876 {
1877 const char *new_val;
1878 char buf[80];
1879 int len= sizeof(buf);
1880
1881 if (audit_log_exclude_commands)
1882 return 1;
1883
1884 new_val = value->val_str(value, buf, &len);
1885
1886 *(const char **)(save) = new_val;
1887
1888 return 0;
1889 }
1890
1891 static
audit_log_include_commands_update(MYSQL_THD thd MY_ATTRIBUTE ((unused)),struct st_mysql_sys_var * var MY_ATTRIBUTE ((unused)),void * var_ptr MY_ATTRIBUTE ((unused)),const void * save)1892 void audit_log_include_commands_update(
1893 MYSQL_THD thd MY_ATTRIBUTE((unused)),
1894 struct st_mysql_sys_var *var MY_ATTRIBUTE((unused)),
1895 void *var_ptr MY_ATTRIBUTE((unused)),
1896 const void *save)
1897 {
1898 const char *new_val= *(const char **)(save);
1899
1900 assert(audit_log_exclude_commands == NULL);
1901
1902 my_free(audit_log_include_commands);
1903 audit_log_include_commands= NULL;
1904
1905 if (new_val != NULL)
1906 {
1907 audit_log_include_commands= my_strdup(PSI_NOT_INSTRUMENTED,
1908 new_val, MYF(MY_FAE));
1909 audit_log_set_include_commands(audit_log_include_commands);
1910 }
1911 else
1912 {
1913 audit_log_set_include_commands("");
1914 }
1915 }
1916
1917 static MYSQL_SYSVAR_STR(include_commands, audit_log_include_commands,
1918 PLUGIN_VAR_RQCMDARG,
1919 "Comma separated list of commands for which events should be logged.",
1920 audit_log_include_commands_validate,
1921 audit_log_include_commands_update, NULL);
1922
1923 static MYSQL_THDVAR_STR(local,
1924 PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC | \
1925 PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
1926 "Local store.", NULL, NULL, "");
1927
1928 static MYSQL_THDVAR_ULONG(local_ptr,
1929 PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
1930 "Local store ptr.", NULL, NULL, 0, 0, ULONG_MAX, 0);
1931
1932 static struct st_mysql_sys_var* audit_log_system_variables[] =
1933 {
1934 MYSQL_SYSVAR(file),
1935 MYSQL_SYSVAR(policy),
1936 MYSQL_SYSVAR(strategy),
1937 MYSQL_SYSVAR(format),
1938 MYSQL_SYSVAR(buffer_size),
1939 MYSQL_SYSVAR(rotate_on_size),
1940 MYSQL_SYSVAR(rotations),
1941 MYSQL_SYSVAR(flush),
1942 MYSQL_SYSVAR(handler),
1943 MYSQL_SYSVAR(syslog_ident),
1944 MYSQL_SYSVAR(syslog_priority),
1945 MYSQL_SYSVAR(syslog_facility),
1946 MYSQL_SYSVAR(record_buffer),
1947 MYSQL_SYSVAR(query_stack),
1948 MYSQL_SYSVAR(exclude_accounts),
1949 MYSQL_SYSVAR(include_accounts),
1950 MYSQL_SYSVAR(exclude_databases),
1951 MYSQL_SYSVAR(include_databases),
1952 MYSQL_SYSVAR(exclude_commands),
1953 MYSQL_SYSVAR(include_commands),
1954 MYSQL_SYSVAR(local),
1955 MYSQL_SYSVAR(local_ptr),
1956 NULL
1957 };
1958
1959 static char thd_local_init_buf[sizeof(audit_log_thd_local)];
1960
1961 static
audit_log_so_init()1962 void MY_ATTRIBUTE((constructor)) audit_log_so_init()
1963 {
1964 memset(thd_local_init_buf, 1, sizeof(thd_local_init_buf) - 1);
1965 thd_local_init_buf[sizeof(thd_local_init_buf) - 1]= 0;
1966 }
1967
1968 /*
1969 Return pointer to THD specific data.
1970 */
1971 static
get_thd_local(MYSQL_THD thd)1972 audit_log_thd_local *get_thd_local(MYSQL_THD thd)
1973 {
1974 audit_log_thd_local *local= (audit_log_thd_local *) THDVAR(thd, local_ptr);
1975
1976 compile_time_assert(sizeof(THDVAR(thd, local_ptr)) >= sizeof(void *));
1977
1978 if (unlikely(local == NULL))
1979 {
1980 THDVAR_SET(thd, local, thd_local_init_buf);
1981 local= (audit_log_thd_local *) THDVAR(thd, local);
1982 memset(local, 0, sizeof(audit_log_thd_local));
1983 THDVAR(thd, local_ptr)= (ulong) local;
1984
1985 realloc_stack_frames(thd, 4);
1986 }
1987 return local;
1988 }
1989
1990
1991 /*
1992 Allocate and return buffer of given size.
1993 */
1994 static
get_record_buffer(MYSQL_THD thd,size_t size)1995 char *get_record_buffer(MYSQL_THD thd, size_t size)
1996 {
1997 audit_log_thd_local *local= get_thd_local(thd);
1998 char *buf= local->record_buffer;
1999
2000 if (local->record_buffer_size < size)
2001 {
2002 local->record_buffer_size= size;
2003
2004 buf = (char *) my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_FAE));
2005 memset(buf, 1, size - 1);
2006 buf[size - 1]= 0;
2007
2008 THDVAR_SET(thd, record_buffer, buf);
2009
2010 my_free(buf);
2011
2012 buf = (char *) THDVAR(thd, record_buffer);
2013 local->record_buffer = buf;
2014 }
2015
2016 return buf;
2017 }
2018
2019
2020 /*
2021 Allocate and return given number of stack frames.
2022 */
2023 static
realloc_stack_frames(MYSQL_THD thd,size_t size)2024 query_stack_frame *realloc_stack_frames(MYSQL_THD thd, size_t size)
2025 {
2026 audit_log_thd_local *local= get_thd_local(thd);
2027 query_stack_frame *stack= (query_stack_frame *) THDVAR(thd, query_stack);
2028
2029 if (local->stack.size < size)
2030 {
2031 char *buf= (char *) my_malloc(PSI_NOT_INSTRUMENTED,
2032 (local->stack.size + size) *
2033 sizeof(query_stack_frame),
2034 MYF(MY_FAE));
2035 memset(buf + local->stack.size * sizeof(query_stack_frame), 1,
2036 size * sizeof(query_stack_frame) - 1);
2037 buf[(local->stack.size + size) * sizeof(query_stack_frame) - 1]= 0;
2038 if (local->stack.size > 0)
2039 memcpy(buf, stack, local->stack.size * sizeof(query_stack_frame));
2040 THDVAR_SET(thd, query_stack,
2041 buf + local->stack.size * sizeof(query_stack_frame));
2042 stack= (query_stack_frame *) THDVAR(thd, query_stack);
2043 memset(stack, 0, size * sizeof(query_stack_frame));
2044 if (local->stack.size > 0)
2045 memcpy(stack, buf, local->stack.size * sizeof(query_stack_frame));
2046 local->stack.frames= stack;
2047 local->stack.size= size;
2048 my_free(buf);
2049 }
2050
2051 return stack;
2052 }
2053
2054
2055 /*
2056 Plugin type-specific descriptor
2057 */
2058 static struct st_mysql_audit audit_log_descriptor=
2059 {
2060 MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */
2061 NULL, /* release_thd function */
2062 audit_log_notify, /* notify function */
2063 { MYSQL_AUDIT_GENERAL_ALL,
2064 MYSQL_AUDIT_CONNECTION_ALL,
2065 0, 0,
2066 MYSQL_AUDIT_TABLE_ACCESS_ALL,
2067 0, 0, 0, 0, 0 } /* class mask */
2068 };
2069
2070 /*
2071 Plugin status variables for SHOW STATUS
2072 */
2073
2074 static struct st_mysql_show_var audit_log_status_variables[]=
2075 {
2076 {"Audit_log_buffer_size_overflow",
2077 (char*) &audit_log_buffer_size_overflow,
2078 SHOW_LONG, SHOW_SCOPE_GLOBAL},
2079 {NullS, NullS, SHOW_LONG, SHOW_SCOPE_GLOBAL}
2080 };
2081
2082
2083 /*
2084 Plugin library descriptor
2085 */
2086
mysql_declare_plugin(audit_log)2087 mysql_declare_plugin(audit_log)
2088 {
2089 MYSQL_AUDIT_PLUGIN, /* type */
2090 &audit_log_descriptor, /* descriptor */
2091 "audit_log", /* name */
2092 "Percona LLC and/or its affiliates.", /* author */
2093 "Audit log", /* description */
2094 PLUGIN_LICENSE_GPL,
2095 audit_log_plugin_init, /* init function (when loaded) */
2096 audit_log_plugin_deinit, /* deinit function (when unloaded) */
2097 PLUGIN_VERSION, /* version */
2098 audit_log_status_variables, /* status variables */
2099 audit_log_system_variables, /* system variables */
2100 NULL,
2101 0,
2102 }
2103 mysql_declare_plugin_end;
2104
2105